@fgv/ts-res-browser 5.0.0-2 → 5.0.0-21

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 (99) hide show
  1. package/cli.js +132 -21
  2. package/dist/bundle.js +1 -1
  3. package/dist/bundle.js.LICENSE.txt +11 -1
  4. package/package.json +42 -36
  5. package/.rush/temp/587277bfc395406ad444e989b6c309b9698f534c.tar.log +0 -15
  6. package/.rush/temp/chunked-rush-logs/ts-res-browser.build.chunks.jsonl +0 -35
  7. package/.rush/temp/operation/build/all.log +0 -35
  8. package/.rush/temp/operation/build/log-chunks.jsonl +0 -35
  9. package/.rush/temp/operation/build/state.json +0 -3
  10. package/.rush/temp/shrinkwrap-deps.json +0 -1225
  11. package/TS_RES_BROWSER_PLAN.md +0 -689
  12. package/babel.config.js +0 -17
  13. package/config/jest.config.json +0 -13
  14. package/config/rig.json +0 -16
  15. package/postcss.config.js +0 -3
  16. package/public/index.html +0 -15
  17. package/rush-logs/ts-res-browser.build.cache.log +0 -3
  18. package/rush-logs/ts-res-browser.build.log +0 -35
  19. package/src/App.tsx +0 -274
  20. package/src/cli.ts +0 -44
  21. package/src/components/common/FileImporter.tsx +0 -143
  22. package/src/components/common/NavigationWarningModal.tsx +0 -62
  23. package/src/components/layout/AppLayout.tsx +0 -46
  24. package/src/components/layout/Header.tsx +0 -22
  25. package/src/components/layout/MessagesWindow.tsx +0 -108
  26. package/src/components/layout/Sidebar.tsx +0 -99
  27. package/src/components/tools/CompiledBrowser.tsx +0 -949
  28. package/src/components/tools/ConfigurationTool.tsx +0 -1405
  29. package/src/components/tools/FilterTool.tsx +0 -1114
  30. package/src/components/tools/ImportTool.tsx +0 -249
  31. package/src/components/tools/ResolutionViewer.tsx +0 -1672
  32. package/src/components/tools/SourceBrowser.tsx +0 -496
  33. package/src/components/tools/ZipLoader.tsx +0 -302
  34. package/src/hooks/useAppState.ts +0 -111
  35. package/src/hooks/useFileImport.ts +0 -86
  36. package/src/hooks/useNavigationWarning.ts +0 -67
  37. package/src/hooks/useResourceManager.ts +0 -295
  38. package/src/hooks/useUrlParams.ts +0 -56
  39. package/src/index.css +0 -38
  40. package/src/main.tsx +0 -16
  41. package/src/types/app.ts +0 -70
  42. package/src/types/fileSystemAccess.d.ts +0 -94
  43. package/src/utils/browserFileTreeAccessors.ts +0 -245
  44. package/src/utils/fileImport.ts +0 -336
  45. package/src/utils/fileTreeConverter.ts +0 -160
  46. package/src/utils/filterResources.ts +0 -677
  47. package/src/utils/tsResIntegration.ts +0 -467
  48. package/src/utils/urlParams.ts +0 -238
  49. package/src/utils/zip/browserZipFileTreeAccessors.ts +0 -385
  50. package/tailwind.config.js +0 -21
  51. package/test-data/README.md +0 -184
  52. package/test-data/config-variations/README.md +0 -64
  53. package/test-data/config-variations/composition-terminology.json +0 -112
  54. package/test-data/config-variations/enterprise-complex.json +0 -129
  55. package/test-data/config-variations/gaming-app.json +0 -87
  56. package/test-data/config-variations/high-priority-language.json +0 -65
  57. package/test-data/config-variations/minimal-basic.json +0 -37
  58. package/test-data/config-variations/qualifier-default-values.json +0 -71
  59. package/test-data/config-variations/territory-first.json +0 -68
  60. package/test-data/config-variations/territory-hierarchy-test.json +0 -31
  61. package/test-data/config-variations/territory-hierarchy-unconstrained.json +0 -32
  62. package/test-data/default-matching-test/greeting.res.json +0 -83
  63. package/test-data/default-matching-test/only-defaults.res.json +0 -48
  64. package/test-data/sample-project/package.json +0 -27
  65. package/test-data/sample-project/src/resources/app-config.json +0 -27
  66. package/test-data/sample-project/src/resources/app.json +0 -177
  67. package/test-data/sample-project/src/resources/config/app.env=development.json +0 -21
  68. package/test-data/sample-project/src/resources/config/app.json +0 -21
  69. package/test-data/sample-project/src/resources/config/app.platform=mobile.json +0 -23
  70. package/test-data/sample-project/src/resources/feature-flags.json +0 -23
  71. package/test-data/sample-project/src/resources/home=CA/common.json +0 -16
  72. package/test-data/sample-project/src/resources/images/density=hdpi.json +0 -29
  73. package/test-data/sample-project/src/resources/images/home.json +0 -29
  74. package/test-data/sample-project/src/resources/images/logout.json +0 -14
  75. package/test-data/sample-project/src/resources/images/settings.json +0 -14
  76. package/test-data/sample-project/src/resources/images/ui-icons.json +0 -29
  77. package/test-data/sample-project/src/resources/images/user.density=hdpi.json +0 -14
  78. package/test-data/sample-project/src/resources/images/user.json +0 -14
  79. package/test-data/sample-project/src/resources/lang=en-US/common.json +0 -15
  80. package/test-data/sample-project/src/resources/language=en-CA/ui-terms.json +0 -19
  81. package/test-data/sample-project/src/resources/language=en-GB/ui-terms.json +0 -30
  82. package/test-data/sample-project/src/resources/language=fr-CA/ui-terms.json +0 -25
  83. package/test-data/sample-project/src/resources/language=nl-BE/ui-terms.json +0 -17
  84. package/test-data/sample-project/src/resources/region=europe/feature-flags.json +0 -17
  85. package/test-data/sample-project/src/resources/region=north-america/feature-flags.json +0 -18
  86. package/test-data/sample-project/src/resources/strings/dashboard.home=CA,language=fr.json +0 -20
  87. package/test-data/sample-project/src/resources/strings/dashboard.json +0 -18
  88. package/test-data/sample-project/src/resources/strings/dashboard.role=admin.json +0 -23
  89. package/test-data/sample-project/src/resources/strings/dashboard.role=user.json +0 -21
  90. package/test-data/sample-project/src/resources/strings/language=en-US.json +0 -16
  91. package/test-data/sample-project/src/resources/strings.json +0 -78
  92. package/test-data/sample-project/src/resources/territory=BE/app-config.json +0 -19
  93. package/test-data/sample-project/src/resources/territory=CA/app-config.json +0 -21
  94. package/test-data/sample-project/src/resources/territory=GB/app-config.json +0 -25
  95. package/test-data/sample-project/src/resources/territory=NL/app-config.json +0 -25
  96. package/test-data/sample-project/src/resources/ui-terms.json +0 -96
  97. package/test-data/sample-project/src/resources-config.json +0 -111
  98. package/tsconfig.json +0 -27
  99. package/webpack.config.js +0 -60
package/babel.config.js DELETED
@@ -1,17 +0,0 @@
1
- module.exports = {
2
- presets: [
3
- [
4
- '@babel/preset-env',
5
- {
6
- targets: {
7
- browsers: ['last 2 versions', 'not dead']
8
- },
9
- useBuiltIns: 'usage',
10
- corejs: 3
11
- }
12
- ],
13
- '@babel/preset-react',
14
- '@babel/preset-typescript'
15
- ],
16
- plugins: []
17
- };
@@ -1,13 +0,0 @@
1
- {
2
- "preset": "ts-jest",
3
- "testEnvironment": "jsdom",
4
- "roots": ["<rootDir>/src"],
5
- "testMatch": ["**/__tests__/**/*.ts?(x)", "**/?(*.)+(spec|test).ts?(x)"],
6
- "transform": {
7
- "^.+\\.(ts|tsx)$": "ts-jest"
8
- },
9
- "moduleNameMapping": {
10
- "^@/(.*)$": "<rootDir>/src/$1"
11
- },
12
- "setupFilesAfterEnv": ["<rootDir>/src/setupTests.ts"]
13
- }
package/config/rig.json DELETED
@@ -1,16 +0,0 @@
1
- // The "rig.json" file directs tools to look for their config files in an external package.
2
- // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
3
- {
4
- "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
5
- /**
6
- * (Required) The name of the rig package to inherit from.
7
- * It should be an NPM package name with the "-rig" suffix.
8
- */
9
- "rigPackageName": "@rushstack/heft-web-rig",
10
- /**
11
- * (Optional) Selects a config profile from the rig package. The name must consist of
12
- * lowercase alphanumeric words separated by hyphens, for example "sample-profile".
13
- * If omitted, then the "default" profile will be used."
14
- */
15
- "rigProfile": "app"
16
- }
package/postcss.config.js DELETED
@@ -1,3 +0,0 @@
1
- module.exports = {
2
- plugins: [require('@tailwindcss/postcss'), require('autoprefixer')]
3
- };
package/public/index.html DELETED
@@ -1,15 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8" />
5
- <link rel="icon" href="/favicon.ico" />
6
- <meta name="viewport" content="width=device-width, initial-scale=1" />
7
- <meta name="theme-color" content="#000000" />
8
- <meta name="description" content="Visual browser tool for ts-res resources" />
9
- <title>TS-RES Browser</title>
10
- </head>
11
- <body>
12
- <noscript>You need to enable JavaScript to run this app.</noscript>
13
- <div id="root"></div>
14
- </body>
15
- </html>
@@ -1,3 +0,0 @@
1
- Caching build output folders: dist, .rush/temp/operation/build
2
- Successfully set cache entry.
3
- Cache key: 587277bfc395406ad444e989b6c309b9698f534c
@@ -1,35 +0,0 @@
1
- Invoking: webpack --mode production
2
- asset bundle.js 2.41 MiB [emitted] [minimized] [big] (name: main) 1 related asset
3
- asset index.html 478 bytes [emitted]
4
- orphan modules 678 KiB [orphan] 353 modules
5
- runtime modules 1.02 KiB 6 modules
6
- modules by path ../../common/temp/node_modules/.pnpm/ 2.38 MiB 841 modules
7
- modules by path ../../libraries/ 2.24 MiB
8
- javascript modules 1.41 MiB 314 modules
9
- json modules 849 KiB 4 modules
10
- optional modules 60 bytes [optional]
11
- buffer (ignored) 15 bytes [optional] [built] [code generated]
12
- crypto (ignored) 15 bytes [optional] [built] [code generated]
13
- buffer (ignored) 15 bytes [optional] [built] [code generated]
14
- vm (ignored) 15 bytes [optional] [built] [code generated]
15
- modules by path ./src/ 406 KiB
16
- ./src/main.tsx + 54 modules 370 KiB [built] [code generated]
17
- ../../common/temp/node_modules/.pnpm/css-loader@7.1.2_webpack@5.100.1/node_modules/css-loader/dist/cjs.js!../../common/temp/node_modules/.pnpm/postcss-loader@8.1.1_postcss@8.5.6_typescript@5.8.3_webpack@5.100.1/node_modules/postcss-loader/dist/cjs.js!./src/index.css 35.7 KiB [built] [code generated]
18
- + 15 modules
19
-
20
- WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
21
- This can impact web performance.
22
- Assets:
23
- bundle.js (2.41 MiB)
24
-
25
- WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
26
- Entrypoints:
27
- main (2.41 MiB)
28
- bundle.js
29
-
30
-
31
- WARNING in webpack performance recommendations:
32
- You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.
33
- For more info visit https://webpack.js.org/guides/code-splitting/
34
-
35
- webpack 5.100.1 compiled with 3 warnings in 21598 ms
package/src/App.tsx DELETED
@@ -1,274 +0,0 @@
1
- import React, { useCallback, useEffect } from 'react';
2
- import AppLayout from './components/layout/AppLayout';
3
- import ImportTool from './components/tools/ImportTool';
4
- import SourceBrowser from './components/tools/SourceBrowser';
5
- import FilterTool from './components/tools/FilterTool';
6
- import CompiledBrowser from './components/tools/CompiledBrowser';
7
- import ResolutionViewer from './components/tools/ResolutionViewer';
8
- import ConfigurationTool from './components/tools/ConfigurationTool';
9
- import ZipLoader from './components/tools/ZipLoader';
10
- import NavigationWarningModal from './components/common/NavigationWarningModal';
11
- import { useAppState } from './hooks/useAppState';
12
- import { useFileImport } from './hooks/useFileImport';
13
- import { useResourceManager } from './hooks/useResourceManager';
14
- import { useNavigationWarning } from './hooks/useNavigationWarning';
15
- import { useUrlParams } from './hooks/useUrlParams';
16
- import { FilterResult } from './utils/filterResources';
17
- import { parseContextFilter } from './utils/urlParams';
18
- import { Tool } from './types/app';
19
- import * as TsRes from '@fgv/ts-res';
20
-
21
- const App: React.FC = () => {
22
- const { state, actions } = useAppState();
23
- const fileImport = useFileImport();
24
- const resourceManager = useResourceManager();
25
- const navigationWarning = useNavigationWarning();
26
- const { urlParams, hasUrlParams } = useUrlParams();
27
-
28
- // State to share filter result between components
29
- const [filterResult, setFilterResult] = React.useState<FilterResult | null>(null);
30
-
31
- // Ref to store the configuration tool's save handler
32
- const configSaveHandlerRef = React.useRef<(() => void) | null>(null);
33
-
34
- // Ref to track if we've already initialized from URL parameters
35
- const initializedFromUrlRef = React.useRef(false);
36
-
37
- // Initialize app state from URL parameters (only run once)
38
- useEffect(() => {
39
- if (hasUrlParams && !initializedFromUrlRef.current) {
40
- initializedFromUrlRef.current = true;
41
-
42
- // Load and apply configuration if provided
43
- if (urlParams.config) {
44
- const loadConfiguration = async () => {
45
- try {
46
- // Check if it's a predefined configuration name
47
- const predefinedNames: TsRes.Config.PredefinedSystemConfiguration[] = [
48
- 'default',
49
- 'language-priority',
50
- 'territory-priority',
51
- 'extended-example'
52
- ];
53
-
54
- if (predefinedNames.includes(urlParams.config as TsRes.Config.PredefinedSystemConfiguration)) {
55
- // Load predefined configuration
56
- const configResult = TsRes.Config.getPredefinedDeclaration(
57
- urlParams.config as TsRes.Config.PredefinedSystemConfiguration
58
- );
59
-
60
- if (configResult.isSuccess()) {
61
- // Store the declaration directly, not the SystemConfiguration instance
62
- resourceManager.actions.applyConfiguration(configResult.value);
63
- actions.addMessage('success', `Loaded predefined configuration: ${urlParams.config}`);
64
- } else {
65
- actions.addMessage(
66
- 'error',
67
- `Failed to load predefined configuration '${urlParams.config}': ${configResult.message}`
68
- );
69
- }
70
- } else {
71
- // It might be a file path - show a message for now
72
- actions.addMessage(
73
- 'info',
74
- `Configuration file specified: ${urlParams.config} (file loading not yet supported)`
75
- );
76
- }
77
- } catch (error) {
78
- actions.addMessage('error', `Error loading configuration: ${error}`);
79
- }
80
- };
81
-
82
- loadConfiguration();
83
- }
84
-
85
- // Apply context filter if provided
86
- if (urlParams.contextFilter) {
87
- try {
88
- const contextObj = parseContextFilter(urlParams.contextFilter);
89
- actions.updateFilterValues(contextObj);
90
- actions.updateFilterEnabled(true);
91
- actions.applyFilterValues();
92
- actions.addMessage('info', `Applied context filter from URL: ${urlParams.contextFilter}`);
93
- } catch (error) {
94
- actions.addMessage('error', `Invalid context filter in URL: ${urlParams.contextFilter}`);
95
- }
96
- }
97
-
98
- // Apply reduceQualifiers setting
99
- if (urlParams.reduceQualifiers) {
100
- actions.updateReduceQualifiers(true);
101
- }
102
-
103
- // Handle ZIP loading
104
- if (urlParams.loadZip) {
105
- actions.setActiveTool('zip-loader');
106
- actions.addMessage('info', 'ZIP archive ready to load');
107
- }
108
-
109
- // Show appropriate messages for other parameters
110
- if (urlParams.input) {
111
- actions.addMessage('info', `Ready to load resource file: ${urlParams.input}`);
112
- }
113
-
114
- if (urlParams.resourceTypes) {
115
- actions.addMessage('info', `Resource types filter: ${urlParams.resourceTypes}`);
116
- }
117
-
118
- if (urlParams.maxDistance !== undefined) {
119
- actions.addMessage('info', `Language matching distance: ${urlParams.maxDistance}`);
120
- }
121
-
122
- if (urlParams.interactive) {
123
- actions.addMessage('info', 'Launched in interactive mode - sample data will be loaded');
124
- }
125
-
126
- // Navigate to appropriate tool based on URL params
127
- if (urlParams.input) {
128
- // If input is specified, start with import tool
129
- actions.setSelectedTool('import');
130
- } else if (urlParams.interactive) {
131
- // If interactive mode, might want to go straight to source browser with sample data
132
- actions.setSelectedTool('source');
133
- }
134
- }
135
- }, [hasUrlParams]); // Remove urlParams and actions to prevent re-runs
136
-
137
- // Handle navigation with unsaved changes check
138
- const handleToolSelect = useCallback(
139
- (tool: Tool) => {
140
- // If we're trying to navigate away from configuration and have unsaved changes
141
- if (state.selectedTool === 'configuration' && navigationWarning.state.hasUnsavedChanges) {
142
- navigationWarning.actions.showWarning(tool);
143
- return;
144
- }
145
-
146
- // Otherwise, navigate normally
147
- actions.setSelectedTool(tool);
148
- },
149
- [state.selectedTool, navigationWarning, actions]
150
- );
151
-
152
- // Handle save and continue from navigation warning
153
- const handleSaveAndContinue = useCallback(() => {
154
- // Call the configuration tool's save handler if available
155
- if (configSaveHandlerRef.current) {
156
- configSaveHandlerRef.current();
157
- }
158
-
159
- const pendingTool = navigationWarning.actions.confirmNavigation();
160
- if (pendingTool) {
161
- actions.setSelectedTool(pendingTool);
162
- }
163
- }, [navigationWarning, actions]);
164
-
165
- // Handle discard changes and continue
166
- const handleDiscardAndContinue = useCallback(() => {
167
- const pendingTool = navigationWarning.actions.confirmNavigation();
168
- if (pendingTool) {
169
- actions.setSelectedTool(pendingTool);
170
- }
171
- }, [navigationWarning, actions]);
172
-
173
- const renderTool = () => {
174
- switch (state.selectedTool) {
175
- case 'import':
176
- return (
177
- <ImportTool
178
- onMessage={actions.addMessage}
179
- fileImport={fileImport}
180
- resourceManager={resourceManager}
181
- />
182
- );
183
- case 'source':
184
- return <SourceBrowser onMessage={actions.addMessage} resourceManager={resourceManager} />;
185
- case 'filter':
186
- return (
187
- <FilterTool
188
- onMessage={actions.addMessage}
189
- resourceManager={resourceManager}
190
- filterState={state.filterState}
191
- filterActions={{
192
- updateFilterEnabled: actions.updateFilterEnabled,
193
- updateFilterValues: actions.updateFilterValues,
194
- applyFilterValues: actions.applyFilterValues,
195
- resetFilterValues: actions.resetFilterValues,
196
- updateReduceQualifiers: actions.updateReduceQualifiers
197
- }}
198
- onFilterResult={setFilterResult}
199
- />
200
- );
201
- case 'compiled':
202
- return (
203
- <CompiledBrowser
204
- onMessage={actions.addMessage}
205
- resourceManager={resourceManager}
206
- filterState={state.filterState}
207
- filterResult={filterResult}
208
- />
209
- );
210
- case 'resolution':
211
- return (
212
- <ResolutionViewer
213
- onMessage={actions.addMessage}
214
- resourceManager={resourceManager}
215
- filterState={state.filterState}
216
- filterResult={filterResult}
217
- />
218
- );
219
- case 'configuration':
220
- return (
221
- <ConfigurationTool
222
- onMessage={actions.addMessage}
223
- resourceManager={resourceManager}
224
- onUnsavedChanges={navigationWarning.actions.setHasUnsavedChanges}
225
- onSaveHandlerRef={configSaveHandlerRef}
226
- />
227
- );
228
- case 'zip-loader':
229
- return (
230
- <ZipLoader
231
- onMessage={actions.addMessage}
232
- resourceManager={resourceManager}
233
- zipFile={urlParams.zipFile}
234
- zipPath={urlParams.zipPath}
235
- onLoadComplete={() => {
236
- // Switch to source browser after successful load
237
- actions.setSelectedTool('source');
238
- }}
239
- />
240
- );
241
- default:
242
- return (
243
- <ImportTool
244
- onMessage={actions.addMessage}
245
- fileImport={fileImport}
246
- resourceManager={resourceManager}
247
- />
248
- );
249
- }
250
- };
251
-
252
- return (
253
- <>
254
- <AppLayout
255
- selectedTool={state.selectedTool}
256
- onToolSelect={handleToolSelect}
257
- messages={state.messages}
258
- onClearMessages={actions.clearMessages}
259
- >
260
- {renderTool()}
261
- </AppLayout>
262
-
263
- <NavigationWarningModal
264
- isOpen={navigationWarning.state.isWarningOpen}
265
- onCancel={navigationWarning.actions.hideWarning}
266
- onConfirm={handleDiscardAndContinue}
267
- onSave={handleSaveAndContinue}
268
- hasUnsavedChanges={navigationWarning.state.hasUnsavedChanges}
269
- />
270
- </>
271
- );
272
- };
273
-
274
- export default App;
package/src/cli.ts DELETED
@@ -1,44 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { exec } from 'child_process';
4
- import { join } from 'path';
5
-
6
- const command = process.argv[2];
7
-
8
- switch (command) {
9
- case 'dev':
10
- console.log('Starting TS-RES Browser development server...');
11
- exec('npm run dev', { cwd: __dirname }, (error, stdout, stderr) => {
12
- if (error) {
13
- console.error(`Error: ${error}`);
14
- return;
15
- }
16
- console.log(stdout);
17
- if (stderr) console.error(stderr);
18
- });
19
- break;
20
-
21
- case 'build':
22
- console.log('Building TS-RES Browser...');
23
- exec('npm run build', { cwd: __dirname }, (error, stdout, stderr) => {
24
- if (error) {
25
- console.error(`Error: ${error}`);
26
- return;
27
- }
28
- console.log(stdout);
29
- if (stderr) console.error(stderr);
30
- });
31
- break;
32
-
33
- default:
34
- console.log('TS-RES Browser CLI');
35
- console.log('');
36
- console.log('Usage:');
37
- console.log(' ts-res-browser dev - Start development server');
38
- console.log(' ts-res-browser build - Build for production');
39
- console.log('');
40
- console.log('For development, you can also use:');
41
- console.log(' rushx dev - Start development server');
42
- console.log(' rushx build - Build for production');
43
- break;
44
- }
@@ -1,143 +0,0 @@
1
- import React, { useState, useCallback } from 'react';
2
- import { FolderOpenIcon, DocumentArrowUpIcon, ExclamationTriangleIcon } from '@heroicons/react/24/outline';
3
- import {
4
- fileImporter,
5
- isFileSystemAccessSupported,
6
- ImportedDirectory,
7
- ImportedFile
8
- } from '../../utils/fileImport';
9
- import { Result } from '@fgv/ts-utils';
10
-
11
- interface FileImporterProps {
12
- onDirectoryImported?: (directory: ImportedDirectory) => void;
13
- onFilesImported?: (files: ImportedFile[]) => void;
14
- onError?: (error: string) => void;
15
- startInDirectory?: string;
16
- }
17
-
18
- export const FileImporter: React.FC<FileImporterProps> = ({
19
- onDirectoryImported,
20
- onFilesImported,
21
- onError,
22
- startInDirectory
23
- }) => {
24
- const [isLoading, setIsLoading] = useState(false);
25
- const [importMethod, setImportMethod] = useState<'modern' | 'fallback'>(
26
- isFileSystemAccessSupported() ? 'modern' : 'fallback'
27
- );
28
-
29
- const handleDirectoryImport = useCallback(async () => {
30
- setIsLoading(true);
31
- try {
32
- const result = await fileImporter.pickDirectory({
33
- startIn: startInDirectory
34
- });
35
-
36
- if (result.isSuccess()) {
37
- onDirectoryImported?.(result.value);
38
- } else {
39
- onError?.(result.error);
40
- }
41
- } catch (error) {
42
- onError?.(`Unexpected error: ${error instanceof Error ? error.message : String(error)}`);
43
- } finally {
44
- setIsLoading(false);
45
- }
46
- }, [onDirectoryImported, onError, startInDirectory]);
47
-
48
- const handleFileImport = useCallback(async () => {
49
- setIsLoading(true);
50
- try {
51
- const result = await fileImporter.pickFiles({
52
- acceptedTypes: ['.json'],
53
- multiple: true,
54
- startIn: startInDirectory
55
- });
56
-
57
- if (result.isSuccess()) {
58
- onFilesImported?.(result.value);
59
- } else {
60
- onError?.(result.error);
61
- }
62
- } catch (error) {
63
- onError?.(`Unexpected error: ${error instanceof Error ? error.message : String(error)}`);
64
- } finally {
65
- setIsLoading(false);
66
- }
67
- }, [onFilesImported, onError, startInDirectory]);
68
-
69
- return (
70
- <div className="space-y-4">
71
- {/* Import Method Info */}
72
- <div className="flex items-center space-x-2 text-sm text-gray-600">
73
- {importMethod === 'modern' ? (
74
- <>
75
- <div className="w-2 h-2 bg-green-500 rounded-full"></div>
76
- <span>Using modern File System Access API</span>
77
- </>
78
- ) : (
79
- <>
80
- <div className="w-2 h-2 bg-yellow-500 rounded-full"></div>
81
- <span>Using fallback file input (limited browser support)</span>
82
- </>
83
- )}
84
- </div>
85
-
86
- {/* Import Controls */}
87
- <div className="flex flex-col space-y-3">
88
- {/* Directory Import */}
89
- <button
90
- onClick={handleDirectoryImport}
91
- disabled={isLoading}
92
- className="flex items-center justify-center space-x-2 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
93
- >
94
- <FolderOpenIcon className="w-5 h-5" />
95
- <span>{isLoading ? 'Importing...' : 'Import Resource Directory'}</span>
96
- </button>
97
-
98
- {/* File Import */}
99
- <button
100
- onClick={handleFileImport}
101
- disabled={isLoading}
102
- className="flex items-center justify-center space-x-2 px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
103
- >
104
- <DocumentArrowUpIcon className="w-5 h-5" />
105
- <span>{isLoading ? 'Importing...' : 'Import Resource Files'}</span>
106
- </button>
107
- </div>
108
-
109
- {/* Browser Compatibility Warning */}
110
- {importMethod === 'fallback' && (
111
- <div className="flex items-start space-x-2 p-3 bg-yellow-50 border border-yellow-200 rounded-lg">
112
- <ExclamationTriangleIcon className="w-5 h-5 text-yellow-600 mt-0.5 flex-shrink-0" />
113
- <div className="text-sm text-yellow-800">
114
- <p className="font-medium">Limited Browser Support</p>
115
- <p className="mt-1">
116
- Your browser doesn't support the File System Access API. For the best experience, use Chrome,
117
- Edge, or another Chromium-based browser.
118
- </p>
119
- </div>
120
- </div>
121
- )}
122
-
123
- {/* Usage Instructions */}
124
- <div className="text-sm text-gray-600 space-y-2">
125
- <p className="font-medium">Import Options:</p>
126
- <ul className="list-disc list-inside space-y-1 ml-2">
127
- <li>
128
- <strong>Import Directory:</strong> Select a folder containing ts-res resources
129
- </li>
130
- <li>
131
- <strong>Import Files:</strong> Select individual JSON resource files
132
- </li>
133
- </ul>
134
- <p className="mt-2">
135
- The importer will automatically detect and parse ts-res resource formats including Resource
136
- Collections, Resource Trees, and simple JSON files.
137
- </p>
138
- </div>
139
- </div>
140
- );
141
- };
142
-
143
- export default FileImporter;
@@ -1,62 +0,0 @@
1
- import React from 'react';
2
- import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';
3
-
4
- interface NavigationWarningModalProps {
5
- isOpen: boolean;
6
- onCancel: () => void;
7
- onConfirm: () => void;
8
- onSave?: () => void;
9
- hasUnsavedChanges: boolean;
10
- }
11
-
12
- const NavigationWarningModal: React.FC<NavigationWarningModalProps> = ({
13
- isOpen,
14
- onCancel,
15
- onConfirm,
16
- onSave,
17
- hasUnsavedChanges
18
- }) => {
19
- if (!isOpen) return null;
20
-
21
- return (
22
- <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
23
- <div className="bg-white rounded-lg shadow-xl p-6 max-w-md w-full mx-4">
24
- <div className="flex items-center space-x-3 mb-4">
25
- <ExclamationTriangleIcon className="h-6 w-6 text-amber-500" />
26
- <h3 className="text-lg font-medium text-gray-900">Unsaved Changes</h3>
27
- </div>
28
-
29
- <p className="text-sm text-gray-600 mb-6">
30
- You have unsaved configuration changes. What would you like to do?
31
- </p>
32
-
33
- <div className="flex justify-end space-x-3">
34
- <button
35
- onClick={onCancel}
36
- className="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
37
- >
38
- Cancel
39
- </button>
40
-
41
- <button
42
- onClick={onConfirm}
43
- className="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
44
- >
45
- Discard Changes
46
- </button>
47
-
48
- {onSave && (
49
- <button
50
- onClick={onSave}
51
- className="px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
52
- >
53
- Save & Continue
54
- </button>
55
- )}
56
- </div>
57
- </div>
58
- </div>
59
- );
60
- };
61
-
62
- export default NavigationWarningModal;
@@ -1,46 +0,0 @@
1
- import React from 'react';
2
- import Header from './Header';
3
- import Sidebar from './Sidebar';
4
- import MessagesWindow from './MessagesWindow';
5
- import { Tool } from '../../types/app';
6
-
7
- interface AppLayoutProps {
8
- children: React.ReactNode;
9
- selectedTool: Tool;
10
- onToolSelect: (tool: Tool) => void;
11
- messages: Array<{
12
- id: string;
13
- type: 'info' | 'warning' | 'error' | 'success';
14
- message: string;
15
- timestamp: Date;
16
- }>;
17
- onClearMessages: () => void;
18
- }
19
-
20
- const AppLayout: React.FC<AppLayoutProps> = ({
21
- children,
22
- selectedTool,
23
- onToolSelect,
24
- messages,
25
- onClearMessages
26
- }) => {
27
- return (
28
- <div className="min-h-screen bg-gray-50">
29
- <Header />
30
-
31
- <div className="flex">
32
- <Sidebar selectedTool={selectedTool} onToolSelect={onToolSelect} />
33
-
34
- <main className="flex-1 overflow-hidden">
35
- <div className="h-full flex flex-col">
36
- <div className="flex-1 overflow-auto">{children}</div>
37
-
38
- <MessagesWindow messages={messages} onClearMessages={onClearMessages} />
39
- </div>
40
- </main>
41
- </div>
42
- </div>
43
- );
44
- };
45
-
46
- export default AppLayout;