@fgv/ts-res-browser 1.0.0 → 5.0.0-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.
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@fgv/ts-res-browser",
3
- "version": "1.0.0",
3
+ "version": "5.0.0-2",
4
4
  "description": "Visual browser tool for ts-res resources",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
7
7
  "bin": {
8
- "ts-res-browser": "./lib/cli.js"
8
+ "ts-res-browser": "./cli.js"
9
9
  },
10
10
  "keywords": [
11
11
  "ts-res",
@@ -26,10 +26,14 @@
26
26
  "core-js": "~3.44.0",
27
27
  "whatwg-fetch": "~3.6.20",
28
28
  "@rushstack/heft-web-rig": "~0.29.0",
29
- "@fgv/ts-res": "5.0.0-0",
30
- "@fgv/ts-utils": "5.0.0-0",
31
- "@fgv/ts-json": "5.0.0-0",
32
- "@fgv/ts-json-base": "5.0.0-0"
29
+ "jszip": "~3.10.1",
30
+ "browserify-zlib": "~0.2.0",
31
+ "assert": "~2.1.0",
32
+ "@fgv/ts-res": "5.0.0-2",
33
+ "@fgv/ts-json-base": "5.0.0-2",
34
+ "@fgv/ts-utils": "5.0.0-2",
35
+ "@fgv/ts-extras": "5.0.0-2",
36
+ "@fgv/ts-json": "5.0.0-2"
33
37
  },
34
38
  "devDependencies": {
35
39
  "@types/react": "~19.1.8",
@@ -67,7 +71,8 @@
67
71
  "@rushstack/heft-lint-plugin": "~0.7.0",
68
72
  "@rushstack/heft-sass-plugin": "~0.17.8",
69
73
  "@rushstack/heft-webpack5-plugin": "~0.11.34",
70
- "@fgv/ts-utils-jest": "5.0.0-0"
74
+ "@types/jszip": "~3.4.1",
75
+ "@fgv/ts-utils-jest": "5.0.0-2"
71
76
  },
72
77
  "scripts": {
73
78
  "build": "webpack --mode production",
@@ -1,3 +1,3 @@
1
1
  Caching build output folders: dist, .rush/temp/operation/build
2
2
  Successfully set cache entry.
3
- Cache key: 5fc90bc7c2ccf812114ea099111568e5429be2a2
3
+ Cache key: 587277bfc395406ad444e989b6c309b9698f534c
@@ -1,30 +1,30 @@
1
1
  Invoking: webpack --mode production
2
- asset bundle.js 2.31 MiB [emitted] [minimized] [big] (name: main) 1 related asset
2
+ asset bundle.js 2.41 MiB [emitted] [minimized] [big] (name: main) 1 related asset
3
3
  asset index.html 478 bytes [emitted]
4
- orphan modules 636 KiB [orphan] 348 modules
4
+ orphan modules 678 KiB [orphan] 353 modules
5
5
  runtime modules 1.02 KiB 6 modules
6
- modules by path ../../common/temp/node_modules/.pnpm/ 2.31 MiB 846 modules
7
- modules by path ../../libraries/ 2.23 MiB
8
- javascript modules 1.4 MiB 312 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
9
  json modules 849 KiB 4 modules
10
10
  optional modules 60 bytes [optional]
11
11
  buffer (ignored) 15 bytes [optional] [built] [code generated]
12
12
  crypto (ignored) 15 bytes [optional] [built] [code generated]
13
13
  buffer (ignored) 15 bytes [optional] [built] [code generated]
14
14
  vm (ignored) 15 bytes [optional] [built] [code generated]
15
- modules by path ./src/ 362 KiB
16
- ./src/main.tsx + 47 modules 327 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.6 KiB [built] [code generated]
18
- + 14 modules
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
19
 
20
20
  WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
21
21
  This can impact web performance.
22
22
  Assets:
23
- bundle.js (2.31 MiB)
23
+ bundle.js (2.41 MiB)
24
24
 
25
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
26
  Entrypoints:
27
- main (2.31 MiB)
27
+ main (2.41 MiB)
28
28
  bundle.js
29
29
 
30
30
 
@@ -32,4 +32,4 @@ WARNING in webpack performance recommendations:
32
32
  You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.
33
33
  For more info visit https://webpack.js.org/guides/code-splitting/
34
34
 
35
- webpack 5.100.1 compiled with 3 warnings in 19176 ms
35
+ webpack 5.100.1 compiled with 3 warnings in 21598 ms
package/src/App.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useCallback } from 'react';
1
+ import React, { useCallback, useEffect } from 'react';
2
2
  import AppLayout from './components/layout/AppLayout';
3
3
  import ImportTool from './components/tools/ImportTool';
4
4
  import SourceBrowser from './components/tools/SourceBrowser';
@@ -6,19 +6,24 @@ import FilterTool from './components/tools/FilterTool';
6
6
  import CompiledBrowser from './components/tools/CompiledBrowser';
7
7
  import ResolutionViewer from './components/tools/ResolutionViewer';
8
8
  import ConfigurationTool from './components/tools/ConfigurationTool';
9
+ import ZipLoader from './components/tools/ZipLoader';
9
10
  import NavigationWarningModal from './components/common/NavigationWarningModal';
10
11
  import { useAppState } from './hooks/useAppState';
11
12
  import { useFileImport } from './hooks/useFileImport';
12
13
  import { useResourceManager } from './hooks/useResourceManager';
13
14
  import { useNavigationWarning } from './hooks/useNavigationWarning';
15
+ import { useUrlParams } from './hooks/useUrlParams';
14
16
  import { FilterResult } from './utils/filterResources';
17
+ import { parseContextFilter } from './utils/urlParams';
15
18
  import { Tool } from './types/app';
19
+ import * as TsRes from '@fgv/ts-res';
16
20
 
17
21
  const App: React.FC = () => {
18
22
  const { state, actions } = useAppState();
19
23
  const fileImport = useFileImport();
20
24
  const resourceManager = useResourceManager();
21
25
  const navigationWarning = useNavigationWarning();
26
+ const { urlParams, hasUrlParams } = useUrlParams();
22
27
 
23
28
  // State to share filter result between components
24
29
  const [filterResult, setFilterResult] = React.useState<FilterResult | null>(null);
@@ -26,6 +31,109 @@ const App: React.FC = () => {
26
31
  // Ref to store the configuration tool's save handler
27
32
  const configSaveHandlerRef = React.useRef<(() => void) | null>(null);
28
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
+
29
137
  // Handle navigation with unsaved changes check
30
138
  const handleToolSelect = useCallback(
31
139
  (tool: Tool) => {
@@ -117,6 +225,19 @@ const App: React.FC = () => {
117
225
  onSaveHandlerRef={configSaveHandlerRef}
118
226
  />
119
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
+ );
120
241
  default:
121
242
  return (
122
243
  <ImportTool
@@ -12,12 +12,14 @@ interface FileImporterProps {
12
12
  onDirectoryImported?: (directory: ImportedDirectory) => void;
13
13
  onFilesImported?: (files: ImportedFile[]) => void;
14
14
  onError?: (error: string) => void;
15
+ startInDirectory?: string;
15
16
  }
16
17
 
17
18
  export const FileImporter: React.FC<FileImporterProps> = ({
18
19
  onDirectoryImported,
19
20
  onFilesImported,
20
- onError
21
+ onError,
22
+ startInDirectory
21
23
  }) => {
22
24
  const [isLoading, setIsLoading] = useState(false);
23
25
  const [importMethod, setImportMethod] = useState<'modern' | 'fallback'>(
@@ -27,7 +29,9 @@ export const FileImporter: React.FC<FileImporterProps> = ({
27
29
  const handleDirectoryImport = useCallback(async () => {
28
30
  setIsLoading(true);
29
31
  try {
30
- const result = await fileImporter.pickDirectory();
32
+ const result = await fileImporter.pickDirectory({
33
+ startIn: startInDirectory
34
+ });
31
35
 
32
36
  if (result.isSuccess()) {
33
37
  onDirectoryImported?.(result.value);
@@ -39,14 +43,15 @@ export const FileImporter: React.FC<FileImporterProps> = ({
39
43
  } finally {
40
44
  setIsLoading(false);
41
45
  }
42
- }, [onDirectoryImported, onError]);
46
+ }, [onDirectoryImported, onError, startInDirectory]);
43
47
 
44
48
  const handleFileImport = useCallback(async () => {
45
49
  setIsLoading(true);
46
50
  try {
47
51
  const result = await fileImporter.pickFiles({
48
52
  acceptedTypes: ['.json'],
49
- multiple: true
53
+ multiple: true,
54
+ startIn: startInDirectory
50
55
  });
51
56
 
52
57
  if (result.isSuccess()) {
@@ -59,7 +64,7 @@ export const FileImporter: React.FC<FileImporterProps> = ({
59
64
  } finally {
60
65
  setIsLoading(false);
61
66
  }
62
- }, [onFilesImported, onError]);
67
+ }, [onFilesImported, onError, startInDirectory]);
63
68
 
64
69
  return (
65
70
  <div className="space-y-4">
@@ -5,7 +5,8 @@ import {
5
5
  FunnelIcon,
6
6
  CubeIcon,
7
7
  MagnifyingGlassIcon,
8
- CogIcon
8
+ CogIcon,
9
+ ArchiveBoxIcon
9
10
  } from '@heroicons/react/24/outline';
10
11
  import { Tool } from '../../types/app';
11
12
 
@@ -50,6 +51,12 @@ const tools = [
50
51
  name: 'Configuration',
51
52
  icon: CogIcon,
52
53
  description: 'Manage qualifier types, qualifiers, and resource types'
54
+ },
55
+ {
56
+ id: 'zip-loader',
57
+ name: 'ZIP Loader',
58
+ icon: ArchiveBoxIcon,
59
+ description: 'Load resources from ZIP archive created by CLI'
53
60
  }
54
61
  ];
55
62
 
@@ -21,6 +21,7 @@ import {
21
21
  } from '../../utils/tsResIntegration';
22
22
  import { fileImporter } from '../../utils/fileImport';
23
23
  import { Result } from '@fgv/ts-utils';
24
+ import { useUrlParams } from '../../hooks/useUrlParams';
24
25
 
25
26
  interface ConfigurationToolProps {
26
27
  onMessage?: (type: Message['type'], message: string) => void;
@@ -38,6 +39,7 @@ const ConfigurationTool: React.FC<ConfigurationToolProps> = ({
38
39
  onSaveHandlerRef
39
40
  }) => {
40
41
  const { state: resourceState } = resourceManager;
42
+ const { urlParams } = useUrlParams();
41
43
  const [activePanel, setActivePanel] = useState<ActivePanel>('qualifiers');
42
44
 
43
45
  // Check if we have processed resources
@@ -84,7 +86,8 @@ const ConfigurationTool: React.FC<ConfigurationToolProps> = ({
84
86
  try {
85
87
  const fileResult = await fileImporter.pickFiles({
86
88
  acceptedTypes: ['.json'],
87
- multiple: false
89
+ multiple: false,
90
+ startIn: urlParams.configStartDir
88
91
  });
89
92
 
90
93
  if (fileResult.isFailure()) {
@@ -13,7 +13,7 @@ import {
13
13
  } from '@heroicons/react/24/outline';
14
14
  import { UseResourceManagerReturn } from '../../hooks/useResourceManager';
15
15
  import { Message, FilterState } from '../../types/app';
16
- import { NoMatch } from '@fgv/ts-res';
16
+ import { NoMatch, Config } from '@fgv/ts-res';
17
17
  import { DEFAULT_SYSTEM_CONFIGURATION } from '../../utils/tsResIntegration';
18
18
  import {
19
19
  createFilteredResourceManager,
@@ -65,6 +65,64 @@ const FilterTool: React.FC<FilterToolProps> = ({
65
65
  return config.qualifiers.map((q) => q.name);
66
66
  }, [resourceState.processedResources?.compiledCollection.qualifiers, resourceState.activeConfiguration]);
67
67
 
68
+ // Get qualifier type information for dropdown decisions
69
+ const qualifierTypeInfo = useMemo(() => {
70
+ const info: Record<
71
+ string,
72
+ {
73
+ type: Config.Model.ISystemConfiguration['qualifierTypes'][0];
74
+ enumeratedValues?: string[];
75
+ allowedTerritories?: string[];
76
+ }
77
+ > = {};
78
+
79
+ // Get the active configuration to access qualifier types
80
+ const config = resourceState.activeConfiguration || DEFAULT_SYSTEM_CONFIGURATION;
81
+
82
+ // Create a map of qualifier name to qualifier type for quick lookup
83
+ const qualifierTypeMap: Record<string, Config.Model.ISystemConfiguration['qualifierTypes'][0]> = {};
84
+
85
+ // First, create a mapping of qualifiers to their types
86
+ config.qualifiers.forEach((qualifier) => {
87
+ const qualifierType = config.qualifierTypes.find((qt) => qt.name === qualifier.typeName);
88
+ if (qualifierType) {
89
+ qualifierTypeMap[qualifier.name] = qualifierType;
90
+ }
91
+ });
92
+
93
+ // Build info for each qualifier
94
+ availableQualifiers.forEach((qualifierName) => {
95
+ const qualifierType = qualifierTypeMap[qualifierName];
96
+ if (qualifierType) {
97
+ info[qualifierName] = { type: qualifierType };
98
+
99
+ // Extract enumerated values for literal types
100
+ if (
101
+ qualifierType.systemType === 'literal' &&
102
+ qualifierType.configuration &&
103
+ typeof qualifierType.configuration === 'object' &&
104
+ 'enumeratedValues' in qualifierType.configuration &&
105
+ Array.isArray(qualifierType.configuration.enumeratedValues)
106
+ ) {
107
+ info[qualifierName].enumeratedValues = qualifierType.configuration.enumeratedValues;
108
+ }
109
+
110
+ // Extract allowed territories for territory types
111
+ if (
112
+ qualifierType.systemType === 'territory' &&
113
+ qualifierType.configuration &&
114
+ typeof qualifierType.configuration === 'object' &&
115
+ 'allowedTerritories' in qualifierType.configuration &&
116
+ Array.isArray(qualifierType.configuration.allowedTerritories)
117
+ ) {
118
+ info[qualifierName].allowedTerritories = qualifierType.configuration.allowedTerritories;
119
+ }
120
+ }
121
+ });
122
+
123
+ return info;
124
+ }, [availableQualifiers, resourceState.activeConfiguration]);
125
+
68
126
  // Check if we have any applied filter values set
69
127
  const hasAppliedFilterValues = useMemo(() => {
70
128
  return hasFilterValues(filterState.appliedValues);
@@ -504,25 +562,54 @@ const FilterTool: React.FC<FilterToolProps> = ({
504
562
  <h3 className="text-lg font-semibold text-gray-900 mb-4">Context Filters</h3>
505
563
  <div className="bg-gray-50 rounded-lg p-4">
506
564
  <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
507
- {availableQualifiers.map((qualifierName) => (
508
- <div key={qualifierName} className="bg-white rounded border border-gray-200 p-2">
509
- <div className="flex items-center gap-2">
510
- <label className="text-sm font-medium text-gray-700 min-w-0 flex-shrink-0">
511
- {qualifierName}:
512
- </label>
513
- <input
514
- type="text"
515
- value={filterState.values[qualifierName] || ''}
516
- onChange={(e) => handleFilterChange(qualifierName, e.target.value)}
517
- disabled={!filterState.enabled}
518
- className={`flex-1 px-2 py-1 border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-purple-500 focus:border-transparent text-sm min-w-0 ${
519
- !filterState.enabled ? 'bg-gray-100 text-gray-400' : ''
520
- }`}
521
- placeholder={filterState.enabled ? `Filter by ${qualifierName}` : 'Disabled'}
522
- />
565
+ {availableQualifiers.map((qualifierName) => {
566
+ const typeInfo = qualifierTypeInfo[qualifierName];
567
+ const hasConstrainedValues =
568
+ (typeInfo?.enumeratedValues && typeInfo.enumeratedValues.length > 0) ||
569
+ (typeInfo?.allowedTerritories && typeInfo.allowedTerritories.length > 0);
570
+
571
+ const constrainedValues = typeInfo?.enumeratedValues || typeInfo?.allowedTerritories || [];
572
+
573
+ return (
574
+ <div key={qualifierName} className="bg-white rounded border border-gray-200 p-2">
575
+ <div className="flex items-center gap-2">
576
+ <label className="text-sm font-medium text-gray-700 min-w-0 flex-shrink-0">
577
+ {qualifierName}:
578
+ </label>
579
+ {hasConstrainedValues ? (
580
+ <select
581
+ value={filterState.values[qualifierName] || ''}
582
+ onChange={(e) => handleFilterChange(qualifierName, e.target.value)}
583
+ disabled={!filterState.enabled}
584
+ className={`flex-1 px-2 py-1 border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-purple-500 focus:border-transparent text-sm min-w-0 ${
585
+ !filterState.enabled ? 'bg-gray-100 text-gray-400' : 'bg-white'
586
+ }`}
587
+ >
588
+ <option value="">
589
+ {filterState.enabled ? `All ${qualifierName} values` : 'Disabled'}
590
+ </option>
591
+ {constrainedValues.map((value) => (
592
+ <option key={value} value={value}>
593
+ {value}
594
+ </option>
595
+ ))}
596
+ </select>
597
+ ) : (
598
+ <input
599
+ type="text"
600
+ value={filterState.values[qualifierName] || ''}
601
+ onChange={(e) => handleFilterChange(qualifierName, e.target.value)}
602
+ disabled={!filterState.enabled}
603
+ className={`flex-1 px-2 py-1 border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-purple-500 focus:border-transparent text-sm min-w-0 ${
604
+ !filterState.enabled ? 'bg-gray-100 text-gray-400' : ''
605
+ }`}
606
+ placeholder={filterState.enabled ? `Filter by ${qualifierName}` : 'Disabled'}
607
+ />
608
+ )}
609
+ </div>
523
610
  </div>
524
- </div>
525
- ))}
611
+ );
612
+ })}
526
613
  </div>
527
614
  {filterState.enabled && (
528
615
  <div className="mt-3 text-sm text-gray-600">
@@ -4,6 +4,7 @@ import { Message } from '../../types/app';
4
4
  import { UseFileImportReturn } from '../../hooks/useFileImport';
5
5
  import { UseResourceManagerReturn } from '../../hooks/useResourceManager';
6
6
  import FileImporter from '../common/FileImporter';
7
+ import { useUrlParams } from '../../hooks/useUrlParams';
7
8
 
8
9
  interface ImportToolProps {
9
10
  onMessage: (type: Message['type'], message: string) => void;
@@ -14,6 +15,7 @@ interface ImportToolProps {
14
15
  const ImportTool: React.FC<ImportToolProps> = ({ onMessage, fileImport, resourceManager }) => {
15
16
  const { state: fileImportState, actions: fileImportActions } = fileImport;
16
17
  const { state: resourceState, actions: resourceActions } = resourceManager;
18
+ const { urlParams } = useUrlParams();
17
19
 
18
20
  // Track if we've already processed the current import to prevent infinite loops
19
21
  const lastProcessedImport = useRef<{
@@ -114,6 +116,7 @@ const ImportTool: React.FC<ImportToolProps> = ({ onMessage, fileImport, resource
114
116
  onDirectoryImported={fileImportActions.handleDirectoryImport}
115
117
  onFilesImported={fileImportActions.handleFilesImport}
116
118
  onError={fileImportActions.handleError}
119
+ startInDirectory={urlParams.inputStartDir}
117
120
  />
118
121
  </div>
119
122