@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.
- package/.rush/temp/03c8b056281d9db0a97d8a6e25eea798a160d393.tar.log +271 -0
- package/.rush/temp/chunked-rush-logs/ts-res-ui-components.build.chunks.jsonl +9 -0
- package/.rush/temp/operation/build/all.log +9 -0
- package/.rush/temp/operation/build/log-chunks.jsonl +9 -0
- package/.rush/temp/operation/build/state.json +3 -0
- package/.rush/temp/shrinkwrap-deps.json +1111 -0
- package/README.md +18 -0
- package/REFACTORING_PLAN.md +171 -0
- package/config/jest.config.json +16 -0
- package/config/jest.setup.js +64 -0
- package/config/rig.json +16 -0
- package/lib/components/common/QualifierContextControl.d.ts +14 -0
- package/lib/components/common/QualifierContextControl.d.ts.map +1 -0
- package/lib/components/common/QualifierContextControl.js +78 -0
- package/lib/components/common/QualifierContextControl.js.map +1 -0
- package/lib/components/common/ResourceListView.d.ts +11 -0
- package/lib/components/common/ResourceListView.d.ts.map +1 -0
- package/lib/components/common/ResourceListView.js +20 -0
- package/lib/components/common/ResourceListView.js.map +1 -0
- package/lib/components/common/ResourceTreeView.d.ts +12 -0
- package/lib/components/common/ResourceTreeView.d.ts.map +1 -0
- package/lib/components/common/ResourceTreeView.js +162 -0
- package/lib/components/common/ResourceTreeView.js.map +1 -0
- package/lib/components/forms/HierarchyEditor.d.ts +10 -0
- package/lib/components/forms/HierarchyEditor.d.ts.map +1 -0
- package/lib/components/forms/HierarchyEditor.js +106 -0
- package/lib/components/forms/HierarchyEditor.js.map +1 -0
- package/lib/components/forms/QualifierEditForm.d.ts +11 -0
- package/lib/components/forms/QualifierEditForm.d.ts.map +1 -0
- package/lib/components/forms/QualifierEditForm.js +181 -0
- package/lib/components/forms/QualifierEditForm.js.map +1 -0
- package/lib/components/forms/QualifierTypeEditForm.d.ts +10 -0
- package/lib/components/forms/QualifierTypeEditForm.d.ts.map +1 -0
- package/lib/components/forms/QualifierTypeEditForm.js +172 -0
- package/lib/components/forms/QualifierTypeEditForm.js.map +1 -0
- package/lib/components/forms/ResourceTypeEditForm.d.ts +10 -0
- package/lib/components/forms/ResourceTypeEditForm.d.ts.map +1 -0
- package/lib/components/forms/ResourceTypeEditForm.js +188 -0
- package/lib/components/forms/ResourceTypeEditForm.js.map +1 -0
- package/lib/components/forms/index.d.ts +9 -0
- package/lib/components/forms/index.d.ts.map +1 -0
- package/lib/components/forms/index.js +5 -0
- package/lib/components/forms/index.js.map +1 -0
- package/lib/components/orchestrator/ResourceOrchestrator.d.ts +14 -0
- package/lib/components/orchestrator/ResourceOrchestrator.d.ts.map +1 -0
- package/lib/components/orchestrator/ResourceOrchestrator.js +278 -0
- package/lib/components/orchestrator/ResourceOrchestrator.js.map +1 -0
- package/lib/components/views/CompiledView/index.d.ts +5 -0
- package/lib/components/views/CompiledView/index.d.ts.map +1 -0
- package/lib/components/views/CompiledView/index.js +595 -0
- package/lib/components/views/CompiledView/index.js.map +1 -0
- package/lib/components/views/ConfigurationView/index.d.ts +5 -0
- package/lib/components/views/ConfigurationView/index.d.ts.map +1 -0
- package/lib/components/views/ConfigurationView/index.js +363 -0
- package/lib/components/views/ConfigurationView/index.js.map +1 -0
- package/lib/components/views/FilterView/index.d.ts +5 -0
- package/lib/components/views/FilterView/index.d.ts.map +1 -0
- package/lib/components/views/FilterView/index.js +463 -0
- package/lib/components/views/FilterView/index.js.map +1 -0
- package/lib/components/views/ImportView/index.d.ts +5 -0
- package/lib/components/views/ImportView/index.d.ts.map +1 -0
- package/lib/components/views/ImportView/index.js +514 -0
- package/lib/components/views/ImportView/index.js.map +1 -0
- package/lib/components/views/ResolutionView/EditableJsonView.d.ts +21 -0
- package/lib/components/views/ResolutionView/EditableJsonView.d.ts.map +1 -0
- package/lib/components/views/ResolutionView/EditableJsonView.js +109 -0
- package/lib/components/views/ResolutionView/EditableJsonView.js.map +1 -0
- package/lib/components/views/ResolutionView/ResolutionEditControls.d.ts +19 -0
- package/lib/components/views/ResolutionView/ResolutionEditControls.d.ts.map +1 -0
- package/lib/components/views/ResolutionView/ResolutionEditControls.js +82 -0
- package/lib/components/views/ResolutionView/ResolutionEditControls.js.map +1 -0
- package/lib/components/views/ResolutionView/index.d.ts +5 -0
- package/lib/components/views/ResolutionView/index.d.ts.map +1 -0
- package/lib/components/views/ResolutionView/index.js +255 -0
- package/lib/components/views/ResolutionView/index.js.map +1 -0
- package/lib/components/views/SourceView/index.d.ts +5 -0
- package/lib/components/views/SourceView/index.d.ts.map +1 -0
- package/lib/components/views/SourceView/index.js +316 -0
- package/lib/components/views/SourceView/index.js.map +1 -0
- package/lib/components/views/ZipLoaderView/index.d.ts +5 -0
- package/lib/components/views/ZipLoaderView/index.d.ts.map +1 -0
- package/lib/components/views/ZipLoaderView/index.js +313 -0
- package/lib/components/views/ZipLoaderView/index.js.map +1 -0
- package/lib/hooks/useConfigurationState.d.ts +46 -0
- package/lib/hooks/useConfigurationState.d.ts.map +1 -0
- package/lib/hooks/useConfigurationState.js +239 -0
- package/lib/hooks/useConfigurationState.js.map +1 -0
- package/lib/hooks/useFilterState.d.ts +7 -0
- package/lib/hooks/useFilterState.d.ts.map +1 -0
- package/lib/hooks/useFilterState.js +80 -0
- package/lib/hooks/useFilterState.js.map +1 -0
- package/lib/hooks/useResolutionState.d.ts +8 -0
- package/lib/hooks/useResolutionState.d.ts.map +1 -0
- package/lib/hooks/useResolutionState.js +253 -0
- package/lib/hooks/useResolutionState.js.map +1 -0
- package/lib/hooks/useResourceData.d.ts +19 -0
- package/lib/hooks/useResourceData.d.ts.map +1 -0
- package/lib/hooks/useResourceData.js +368 -0
- package/lib/hooks/useResourceData.js.map +1 -0
- package/lib/hooks/useViewState.d.ts +10 -0
- package/lib/hooks/useViewState.d.ts.map +1 -0
- package/lib/hooks/useViewState.js +29 -0
- package/lib/hooks/useViewState.js.map +1 -0
- package/lib/index.d.ts +27 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +34 -0
- package/lib/index.js.map +1 -0
- package/lib/test/helpers/testDataLoader.d.ts +37 -0
- package/lib/test/helpers/testDataLoader.d.ts.map +1 -0
- package/lib/test/helpers/testDataLoader.js +171 -0
- package/lib/test/helpers/testDataLoader.js.map +1 -0
- package/lib/test/unit/utils/configurationUtils.test.d.ts +2 -0
- package/lib/test/unit/utils/configurationUtils.test.d.ts.map +1 -0
- package/lib/test/unit/utils/configurationUtils.test.js +497 -0
- package/lib/test/unit/utils/configurationUtils.test.js.map +1 -0
- package/lib/test/unit/utils/fileProcessing.test.d.ts +2 -0
- package/lib/test/unit/utils/fileProcessing.test.d.ts.map +1 -0
- package/lib/test/unit/utils/fileProcessing.test.js +321 -0
- package/lib/test/unit/utils/fileProcessing.test.js.map +1 -0
- package/lib/test/unit/utils/filterResources.test.d.ts +2 -0
- package/lib/test/unit/utils/filterResources.test.d.ts.map +1 -0
- package/lib/test/unit/utils/filterResources.test.js +403 -0
- package/lib/test/unit/utils/filterResources.test.js.map +1 -0
- package/lib/test/unit/utils/resolutionEditing.test.d.ts +2 -0
- package/lib/test/unit/utils/resolutionEditing.test.d.ts.map +1 -0
- package/lib/test/unit/utils/resolutionEditing.test.js +439 -0
- package/lib/test/unit/utils/resolutionEditing.test.js.map +1 -0
- package/lib/test/unit/utils/resolutionUtils.test.d.ts +2 -0
- package/lib/test/unit/utils/resolutionUtils.test.d.ts.map +1 -0
- package/lib/test/unit/utils/resolutionUtils.test.js +397 -0
- package/lib/test/unit/utils/resolutionUtils.test.js.map +1 -0
- package/lib/test/unit/utils/tsResIntegration.test.d.ts +2 -0
- package/lib/test/unit/utils/tsResIntegration.test.d.ts.map +1 -0
- package/lib/test/unit/utils/tsResIntegration.test.js +376 -0
- package/lib/test/unit/utils/tsResIntegration.test.js.map +1 -0
- package/lib/types/index.d.ts +251 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +2 -0
- package/lib/types/index.js.map +1 -0
- package/lib/utils/configurationUtils.d.ts +74 -0
- package/lib/utils/configurationUtils.d.ts.map +1 -0
- package/lib/utils/configurationUtils.js +359 -0
- package/lib/utils/configurationUtils.js.map +1 -0
- package/lib/utils/fileProcessing.d.ts +18 -0
- package/lib/utils/fileProcessing.d.ts.map +1 -0
- package/lib/utils/fileProcessing.js +142 -0
- package/lib/utils/fileProcessing.js.map +1 -0
- package/lib/utils/filterResources.d.ts +38 -0
- package/lib/utils/filterResources.d.ts.map +1 -0
- package/lib/utils/filterResources.js +153 -0
- package/lib/utils/filterResources.js.map +1 -0
- package/lib/utils/resolutionEditing.d.ts +58 -0
- package/lib/utils/resolutionEditing.d.ts.map +1 -0
- package/lib/utils/resolutionEditing.js +246 -0
- package/lib/utils/resolutionEditing.js.map +1 -0
- package/lib/utils/resolutionUtils.d.ts +28 -0
- package/lib/utils/resolutionUtils.d.ts.map +1 -0
- package/lib/utils/resolutionUtils.js +216 -0
- package/lib/utils/resolutionUtils.js.map +1 -0
- package/lib/utils/tsResIntegration.d.ts +71 -0
- package/lib/utils/tsResIntegration.d.ts.map +1 -0
- package/lib/utils/tsResIntegration.js +294 -0
- package/lib/utils/tsResIntegration.js.map +1 -0
- package/lib/utils/zipLoader/browserZipLoader.d.ts +48 -0
- package/lib/utils/zipLoader/browserZipLoader.d.ts.map +1 -0
- package/lib/utils/zipLoader/browserZipLoader.js +247 -0
- package/lib/utils/zipLoader/browserZipLoader.js.map +1 -0
- package/lib/utils/zipLoader/index.d.ts +8 -0
- package/lib/utils/zipLoader/index.d.ts.map +1 -0
- package/lib/utils/zipLoader/index.js +13 -0
- package/lib/utils/zipLoader/index.js.map +1 -0
- package/lib/utils/zipLoader/nodeZipBuilder.d.ts +55 -0
- package/lib/utils/zipLoader/nodeZipBuilder.d.ts.map +1 -0
- package/lib/utils/zipLoader/nodeZipBuilder.js +98 -0
- package/lib/utils/zipLoader/nodeZipBuilder.js.map +1 -0
- package/lib/utils/zipLoader/types.d.ts +139 -0
- package/lib/utils/zipLoader/types.d.ts.map +1 -0
- package/lib/utils/zipLoader/types.js +2 -0
- package/lib/utils/zipLoader/types.js.map +1 -0
- package/lib/utils/zipLoader/zipUtils.d.ts +53 -0
- package/lib/utils/zipLoader/zipUtils.d.ts.map +1 -0
- package/lib/utils/zipLoader/zipUtils.js +229 -0
- package/lib/utils/zipLoader/zipUtils.js.map +1 -0
- package/package.json +69 -0
- package/rush-logs/ts-res-ui-components.build.cache.log +3 -0
- package/rush-logs/ts-res-ui-components.build.log +9 -0
- package/src/components/common/QualifierContextControl.tsx +151 -0
- package/src/components/common/ResourceListView.tsx +63 -0
- package/src/components/common/ResourceTreeView.tsx +271 -0
- package/src/components/forms/HierarchyEditor.tsx +204 -0
- package/src/components/forms/QualifierEditForm.tsx +355 -0
- package/src/components/forms/QualifierTypeEditForm.tsx +347 -0
- package/src/components/forms/ResourceTypeEditForm.tsx +331 -0
- package/src/components/forms/index.ts +11 -0
- package/src/components/orchestrator/ResourceOrchestrator.tsx +372 -0
- package/src/components/views/CompiledView/index.tsx +922 -0
- package/src/components/views/ConfigurationView/index.tsx +800 -0
- package/src/components/views/FilterView/index.tsx +825 -0
- package/src/components/views/ImportView/index.tsx +717 -0
- package/src/components/views/ResolutionView/EditableJsonView.tsx +214 -0
- package/src/components/views/ResolutionView/ResolutionEditControls.tsx +170 -0
- package/src/components/views/ResolutionView/index.tsx +591 -0
- package/src/components/views/SourceView/index.tsx +536 -0
- package/src/components/views/ZipLoaderView/index.tsx +485 -0
- package/src/hooks/useConfigurationState.ts +374 -0
- package/src/hooks/useFilterState.ts +97 -0
- package/src/hooks/useResolutionState.ts +355 -0
- package/src/hooks/useResourceData.ts +467 -0
- package/src/hooks/useViewState.ts +44 -0
- package/src/index.ts +45 -0
- package/src/test/helpers/testDataLoader.ts +195 -0
- package/src/test/unit/utils/configurationUtils.test.ts +630 -0
- package/src/test/unit/utils/fileProcessing.test.ts +391 -0
- package/src/test/unit/utils/filterResources.test.ts +574 -0
- package/src/test/unit/utils/resolutionEditing.test.ts +556 -0
- package/src/test/unit/utils/resolutionUtils.test.ts +521 -0
- package/src/test/unit/utils/tsResIntegration.test.ts +433 -0
- package/src/types/index.ts +322 -0
- package/src/utils/configurationUtils.ts +424 -0
- package/src/utils/fileProcessing.ts +160 -0
- package/src/utils/filterResources.ts +206 -0
- package/src/utils/resolutionEditing.ts +319 -0
- package/src/utils/resolutionUtils.ts +289 -0
- package/src/utils/tsResIntegration.ts +440 -0
- package/src/utils/zipLoader/browserZipLoader.ts +319 -0
- package/src/utils/zipLoader/index.ts +26 -0
- package/src/utils/zipLoader/nodeZipBuilder.ts +153 -0
- package/src/utils/zipLoader/types.ts +175 -0
- package/src/utils/zipLoader/zipUtils.ts +266 -0
- package/temp/build/typescript/ts_gZid87Hu.json +1 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import React, { useState, useCallback, useMemo } from 'react';
|
|
2
|
+
import { Runtime } from '@fgv/ts-res';
|
|
3
|
+
import {
|
|
4
|
+
ResolutionState,
|
|
5
|
+
ResolutionActions,
|
|
6
|
+
ResolutionResult,
|
|
7
|
+
ProcessedResources,
|
|
8
|
+
JsonValue
|
|
9
|
+
} from '../types';
|
|
10
|
+
import {
|
|
11
|
+
createResolverWithContext,
|
|
12
|
+
resolveResourceDetailed,
|
|
13
|
+
getAvailableQualifiers,
|
|
14
|
+
hasPendingContextChanges
|
|
15
|
+
} from '../utils/resolutionUtils';
|
|
16
|
+
import {
|
|
17
|
+
validateEditedResource,
|
|
18
|
+
computeResourceDelta,
|
|
19
|
+
rebuildSystemWithEdits,
|
|
20
|
+
extractResolutionContext,
|
|
21
|
+
checkEditConflicts
|
|
22
|
+
} from '../utils/resolutionEditing';
|
|
23
|
+
|
|
24
|
+
export interface UseResolutionStateReturn {
|
|
25
|
+
state: ResolutionState;
|
|
26
|
+
actions: ResolutionActions;
|
|
27
|
+
availableQualifiers: string[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function useResolutionState(
|
|
31
|
+
processedResources: ProcessedResources | null,
|
|
32
|
+
onMessage?: (type: 'info' | 'warning' | 'error' | 'success', message: string) => void,
|
|
33
|
+
onSystemUpdate?: (updatedResources: ProcessedResources) => void
|
|
34
|
+
): UseResolutionStateReturn {
|
|
35
|
+
// Get available qualifiers
|
|
36
|
+
const availableQualifiers = useMemo(() => {
|
|
37
|
+
if (!processedResources) return [];
|
|
38
|
+
return getAvailableQualifiers(processedResources);
|
|
39
|
+
}, [processedResources]);
|
|
40
|
+
|
|
41
|
+
// Initialize context with all qualifiers undefined
|
|
42
|
+
const defaultContextValues = useMemo(() => {
|
|
43
|
+
const defaults: Record<string, string | undefined> = {};
|
|
44
|
+
availableQualifiers.forEach((qualifierName) => {
|
|
45
|
+
defaults[qualifierName] = undefined;
|
|
46
|
+
});
|
|
47
|
+
return defaults;
|
|
48
|
+
}, [availableQualifiers]);
|
|
49
|
+
|
|
50
|
+
// Resolution state
|
|
51
|
+
const [contextValues, setContextValues] = useState<Record<string, string | undefined>>({});
|
|
52
|
+
const [pendingContextValues, setPendingContextValues] = useState<Record<string, string | undefined>>({});
|
|
53
|
+
const [selectedResourceId, setSelectedResourceId] = useState<string | null>(null);
|
|
54
|
+
const [currentResolver, setCurrentResolver] = useState<Runtime.ResourceResolver | null>(null);
|
|
55
|
+
const [resolutionResult, setResolutionResult] = useState<ResolutionResult | null>(null);
|
|
56
|
+
const [viewMode, setViewMode] = useState<'composed' | 'best' | 'all' | 'raw'>('composed');
|
|
57
|
+
|
|
58
|
+
// Edit state - stores original, edited, and delta for each resource
|
|
59
|
+
const [editedResources, setEditedResources] = useState<
|
|
60
|
+
Map<string, { originalValue: JsonValue; editedValue: JsonValue; delta: JsonValue }>
|
|
61
|
+
>(new Map());
|
|
62
|
+
const [isApplyingEdits, setIsApplyingEdits] = useState(false);
|
|
63
|
+
|
|
64
|
+
// Update context state when defaults change
|
|
65
|
+
React.useEffect(() => {
|
|
66
|
+
setContextValues(defaultContextValues);
|
|
67
|
+
setPendingContextValues(defaultContextValues);
|
|
68
|
+
}, [defaultContextValues]);
|
|
69
|
+
|
|
70
|
+
// Check for pending changes
|
|
71
|
+
const hasPendingChanges = useMemo(() => {
|
|
72
|
+
return hasPendingContextChanges(contextValues, pendingContextValues);
|
|
73
|
+
}, [contextValues, pendingContextValues]);
|
|
74
|
+
|
|
75
|
+
// Check for unsaved edits
|
|
76
|
+
const hasUnsavedEdits = useMemo(() => {
|
|
77
|
+
return editedResources.size > 0;
|
|
78
|
+
}, [editedResources]);
|
|
79
|
+
|
|
80
|
+
// Update context value
|
|
81
|
+
const updateContextValue = useCallback((qualifierName: string, value: string | undefined) => {
|
|
82
|
+
setPendingContextValues((prev) => ({
|
|
83
|
+
...prev,
|
|
84
|
+
[qualifierName]: value
|
|
85
|
+
}));
|
|
86
|
+
}, []);
|
|
87
|
+
|
|
88
|
+
// Apply context changes
|
|
89
|
+
const applyContext = useCallback(() => {
|
|
90
|
+
if (!processedResources) {
|
|
91
|
+
onMessage?.('error', 'No resources loaded');
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
// Create resolver with new context
|
|
97
|
+
const resolverResult = createResolverWithContext(processedResources, pendingContextValues, {
|
|
98
|
+
enableCaching: true,
|
|
99
|
+
enableDebugLogging: false
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
if (resolverResult.isFailure()) {
|
|
103
|
+
onMessage?.('error', `Failed to create resolver: ${resolverResult.message}`);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Update state
|
|
108
|
+
setContextValues({ ...pendingContextValues });
|
|
109
|
+
setCurrentResolver(resolverResult.value);
|
|
110
|
+
|
|
111
|
+
// If a resource is selected, resolve it with the new context
|
|
112
|
+
if (selectedResourceId) {
|
|
113
|
+
const resolutionResult = resolveResourceDetailed(
|
|
114
|
+
resolverResult.value,
|
|
115
|
+
selectedResourceId,
|
|
116
|
+
processedResources
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
if (resolutionResult.isSuccess()) {
|
|
120
|
+
setResolutionResult(resolutionResult.value);
|
|
121
|
+
} else {
|
|
122
|
+
onMessage?.('error', `Failed to resolve resource: ${resolutionResult.message}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
onMessage?.('success', 'Context applied successfully');
|
|
127
|
+
} catch (error) {
|
|
128
|
+
onMessage?.(
|
|
129
|
+
'error',
|
|
130
|
+
`Failed to apply context: ${error instanceof Error ? error.message : String(error)}`
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}, [processedResources, pendingContextValues, selectedResourceId, onMessage]);
|
|
134
|
+
|
|
135
|
+
// Select resource and resolve it
|
|
136
|
+
const selectResource = useCallback(
|
|
137
|
+
(resourceId: string) => {
|
|
138
|
+
setSelectedResourceId(resourceId);
|
|
139
|
+
setResolutionResult(null);
|
|
140
|
+
|
|
141
|
+
if (currentResolver && processedResources) {
|
|
142
|
+
const resolutionResult = resolveResourceDetailed(currentResolver, resourceId, processedResources);
|
|
143
|
+
|
|
144
|
+
if (resolutionResult.isSuccess()) {
|
|
145
|
+
setResolutionResult(resolutionResult.value);
|
|
146
|
+
onMessage?.('info', `Selected resource: ${resourceId}`);
|
|
147
|
+
} else {
|
|
148
|
+
onMessage?.('error', `Failed to resolve resource: ${resolutionResult.message}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
[currentResolver, processedResources, onMessage]
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
// Reset cache
|
|
156
|
+
const resetCache = useCallback(() => {
|
|
157
|
+
if (currentResolver) {
|
|
158
|
+
currentResolver.clearConditionCache();
|
|
159
|
+
onMessage?.('info', 'Cache cleared');
|
|
160
|
+
}
|
|
161
|
+
}, [currentResolver, onMessage]);
|
|
162
|
+
|
|
163
|
+
// Auto-apply default context when resources are loaded
|
|
164
|
+
React.useEffect(() => {
|
|
165
|
+
if (processedResources && Object.keys(defaultContextValues).length > 0) {
|
|
166
|
+
applyContext();
|
|
167
|
+
}
|
|
168
|
+
}, [processedResources, defaultContextValues]);
|
|
169
|
+
|
|
170
|
+
// Edit management functions
|
|
171
|
+
const saveEdit = useCallback(
|
|
172
|
+
(resourceId: string, editedValue: JsonValue, originalValue?: JsonValue) => {
|
|
173
|
+
try {
|
|
174
|
+
// Validate the edited value
|
|
175
|
+
const validation = validateEditedResource(editedValue);
|
|
176
|
+
if (!validation.isValid) {
|
|
177
|
+
onMessage?.('error', `Invalid edit: ${validation.errors.join(', ')}`);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Show warnings if any
|
|
182
|
+
if (validation.warnings.length > 0) {
|
|
183
|
+
validation.warnings.forEach((warning) => onMessage?.('warning', warning));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Compute the delta between original and edited
|
|
187
|
+
const resolvedValue = originalValue || editedValue; // Use originalValue as the resolved/baseline
|
|
188
|
+
const deltaResult = computeResourceDelta(undefined, resolvedValue, editedValue);
|
|
189
|
+
|
|
190
|
+
if (deltaResult.isFailure()) {
|
|
191
|
+
onMessage?.('warning', `Could not compute delta, saving full value: ${deltaResult.message}`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const delta = deltaResult.isSuccess() ? deltaResult.value : null;
|
|
195
|
+
|
|
196
|
+
// Save the edit with original, edited, and delta
|
|
197
|
+
setEditedResources((prev) => {
|
|
198
|
+
const newMap = new Map(prev);
|
|
199
|
+
newMap.set(resourceId, {
|
|
200
|
+
originalValue: resolvedValue,
|
|
201
|
+
editedValue,
|
|
202
|
+
delta
|
|
203
|
+
});
|
|
204
|
+
return newMap;
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Log info about delta
|
|
208
|
+
if (delta !== null && delta !== editedValue) {
|
|
209
|
+
const deltaSize = JSON.stringify(delta).length;
|
|
210
|
+
const fullSize = JSON.stringify(editedValue).length;
|
|
211
|
+
const reduction = Math.round((1 - deltaSize / fullSize) * 100);
|
|
212
|
+
onMessage?.('info', `Edit saved for ${resourceId} (delta: ${reduction}% smaller)`);
|
|
213
|
+
} else {
|
|
214
|
+
onMessage?.('info', `Edit saved for resource ${resourceId}`);
|
|
215
|
+
}
|
|
216
|
+
} catch (error) {
|
|
217
|
+
onMessage?.(
|
|
218
|
+
'error',
|
|
219
|
+
`Failed to save edit: ${error instanceof Error ? error.message : String(error)}`
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
[onMessage]
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
const getEditedValue = useCallback(
|
|
227
|
+
(resourceId: string) => {
|
|
228
|
+
const edit = editedResources.get(resourceId);
|
|
229
|
+
return edit?.editedValue;
|
|
230
|
+
},
|
|
231
|
+
[editedResources]
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
const hasEdit = useCallback(
|
|
235
|
+
(resourceId: string) => {
|
|
236
|
+
return editedResources.has(resourceId);
|
|
237
|
+
},
|
|
238
|
+
[editedResources]
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
const clearEdits = useCallback(() => {
|
|
242
|
+
setEditedResources(new Map());
|
|
243
|
+
onMessage?.('info', 'All edits cleared');
|
|
244
|
+
}, [onMessage]);
|
|
245
|
+
|
|
246
|
+
const discardEdits = useCallback(() => {
|
|
247
|
+
if (hasUnsavedEdits) {
|
|
248
|
+
setEditedResources(new Map());
|
|
249
|
+
onMessage?.('info', 'All unsaved edits discarded');
|
|
250
|
+
}
|
|
251
|
+
}, [hasUnsavedEdits, onMessage]);
|
|
252
|
+
|
|
253
|
+
const applyEdits = useCallback(async () => {
|
|
254
|
+
if (!processedResources || editedResources.size === 0) {
|
|
255
|
+
onMessage?.('warning', 'No edits to apply');
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (!onSystemUpdate) {
|
|
260
|
+
onMessage?.('error', 'System update callback not provided');
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
setIsApplyingEdits(true);
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
// Extract current resolution context (filter out undefined values)
|
|
268
|
+
const cleanedContextValues: Record<string, string> = {};
|
|
269
|
+
Object.entries(contextValues).forEach(([key, value]) => {
|
|
270
|
+
if (value !== undefined) {
|
|
271
|
+
cleanedContextValues[key] = value;
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
const currentContext = extractResolutionContext(
|
|
276
|
+
currentResolver as Runtime.ResourceResolver,
|
|
277
|
+
cleanedContextValues
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
// Check for potential conflicts
|
|
281
|
+
const conflictCheck = checkEditConflicts(
|
|
282
|
+
processedResources.system.resourceManager,
|
|
283
|
+
editedResources,
|
|
284
|
+
currentContext
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
// Show warnings about potential conflicts
|
|
288
|
+
conflictCheck.warnings.forEach((warning) => onMessage?.('warning', warning));
|
|
289
|
+
|
|
290
|
+
if (conflictCheck.conflicts.length > 0) {
|
|
291
|
+
onMessage?.('error', `Conflicts detected: ${conflictCheck.conflicts.join(', ')}`);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Rebuild the system with edits
|
|
296
|
+
const rebuildResult = await rebuildSystemWithEdits(
|
|
297
|
+
processedResources.system,
|
|
298
|
+
editedResources,
|
|
299
|
+
currentContext
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
if (rebuildResult.isFailure()) {
|
|
303
|
+
onMessage?.('error', `Failed to apply edits: ${rebuildResult.message}`);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Update the system through the callback
|
|
308
|
+
onSystemUpdate(rebuildResult.value);
|
|
309
|
+
|
|
310
|
+
// Clear edits after successful application
|
|
311
|
+
setEditedResources(new Map());
|
|
312
|
+
|
|
313
|
+
onMessage?.('success', `Successfully applied ${editedResources.size} edit(s)`);
|
|
314
|
+
} catch (error) {
|
|
315
|
+
onMessage?.('error', `Error applying edits: ${error instanceof Error ? error.message : String(error)}`);
|
|
316
|
+
} finally {
|
|
317
|
+
setIsApplyingEdits(false);
|
|
318
|
+
}
|
|
319
|
+
}, [processedResources, editedResources, onSystemUpdate, currentResolver, contextValues, onMessage]);
|
|
320
|
+
|
|
321
|
+
const state: ResolutionState = {
|
|
322
|
+
contextValues,
|
|
323
|
+
pendingContextValues,
|
|
324
|
+
selectedResourceId,
|
|
325
|
+
currentResolver,
|
|
326
|
+
resolutionResult,
|
|
327
|
+
viewMode,
|
|
328
|
+
hasPendingChanges,
|
|
329
|
+
// Edit state
|
|
330
|
+
editedResources,
|
|
331
|
+
hasUnsavedEdits,
|
|
332
|
+
isApplyingEdits
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
const actions: ResolutionActions = {
|
|
336
|
+
updateContextValue,
|
|
337
|
+
applyContext,
|
|
338
|
+
selectResource,
|
|
339
|
+
setViewMode,
|
|
340
|
+
resetCache,
|
|
341
|
+
// Edit actions
|
|
342
|
+
saveEdit,
|
|
343
|
+
getEditedValue,
|
|
344
|
+
hasEdit,
|
|
345
|
+
clearEdits,
|
|
346
|
+
applyEdits,
|
|
347
|
+
discardEdits
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
return {
|
|
351
|
+
state,
|
|
352
|
+
actions,
|
|
353
|
+
availableQualifiers
|
|
354
|
+
};
|
|
355
|
+
}
|