@firecms/collection_editor 3.0.0 → 3.1.0-canary.02232f4
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/dist/ConfigControllerProvider.d.ts +6 -0
- package/dist/api/generateCollectionApi.d.ts +71 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.es.js +15234 -8138
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +15199 -8103
- package/dist/index.umd.js.map +1 -1
- package/dist/locales/de.d.ts +120 -0
- package/dist/locales/en.d.ts +120 -0
- package/dist/locales/es.d.ts +120 -0
- package/dist/locales/fr.d.ts +120 -0
- package/dist/locales/hi.d.ts +120 -0
- package/dist/locales/it.d.ts +120 -0
- package/dist/locales/pt.d.ts +120 -0
- package/dist/types/collection_editor_controller.d.ts +14 -0
- package/dist/types/collection_inference.d.ts +8 -2
- package/dist/types/config_controller.d.ts +31 -1
- package/dist/ui/AddKanbanColumnAction.d.ts +11 -0
- package/dist/ui/KanbanSetupAction.d.ts +10 -0
- package/dist/ui/collection_editor/AICollectionGeneratorPopover.d.ts +37 -0
- package/dist/ui/collection_editor/AIModifiedPathsContext.d.ts +20 -0
- package/dist/ui/collection_editor/CollectionDetailsForm.d.ts +2 -3
- package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +24 -0
- package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +4 -1
- package/dist/ui/collection_editor/CollectionJsonImportDialog.d.ts +7 -0
- package/dist/ui/collection_editor/CollectionYupValidation.d.ts +9 -13
- package/dist/ui/collection_editor/DisplaySettingsForm.d.ts +3 -0
- package/dist/ui/collection_editor/EntityActionsEditTab.d.ts +2 -1
- package/dist/ui/collection_editor/ExtendSettingsForm.d.ts +14 -0
- package/dist/ui/collection_editor/GeneralSettingsForm.d.ts +7 -0
- package/dist/ui/collection_editor/KanbanConfigSection.d.ts +4 -0
- package/dist/ui/collection_editor/PropertyEditView.d.ts +6 -1
- package/dist/ui/collection_editor/PropertyTree.d.ts +2 -1
- package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +2 -1
- package/dist/ui/collection_editor/ViewModeSwitch.d.ts +6 -0
- package/dist/ui/collection_editor/properties/EnumPropertyField.d.ts +2 -1
- package/dist/ui/collection_editor/properties/conditions/ConditionsEditor.d.ts +10 -0
- package/dist/ui/collection_editor/properties/conditions/ConditionsPanel.d.ts +2 -0
- package/dist/ui/collection_editor/properties/conditions/EnumConditionsEditor.d.ts +6 -0
- package/dist/ui/collection_editor/properties/conditions/index.d.ts +6 -0
- package/dist/ui/collection_editor/properties/conditions/property_paths.d.ts +19 -0
- package/dist/useCollectionEditorPlugin.d.ts +7 -1
- package/dist/utils/validateCollectionJson.d.ts +22 -0
- package/package.json +15 -15
- package/src/ConfigControllerProvider.tsx +82 -47
- package/src/api/generateCollectionApi.ts +119 -0
- package/src/api/index.ts +1 -0
- package/src/index.ts +28 -1
- package/src/locales/de.ts +125 -0
- package/src/locales/en.ts +145 -0
- package/src/locales/es.ts +125 -0
- package/src/locales/fr.ts +125 -0
- package/src/locales/hi.ts +125 -0
- package/src/locales/it.ts +125 -0
- package/src/locales/pt.ts +125 -0
- package/src/types/collection_editor_controller.tsx +16 -3
- package/src/types/collection_inference.ts +15 -2
- package/src/types/config_controller.tsx +37 -1
- package/src/ui/AddKanbanColumnAction.tsx +203 -0
- package/src/ui/EditorCollectionAction.tsx +3 -3
- package/src/ui/EditorCollectionActionStart.tsx +1 -2
- package/src/ui/EditorEntityAction.tsx +3 -2
- package/src/ui/HomePageEditorCollectionAction.tsx +41 -13
- package/src/ui/KanbanSetupAction.tsx +38 -0
- package/src/ui/MissingReferenceWidget.tsx +1 -1
- package/src/ui/NewCollectionButton.tsx +4 -2
- package/src/ui/NewCollectionCard.tsx +7 -4
- package/src/ui/PropertyAddColumnComponent.tsx +4 -3
- package/src/ui/collection_editor/AICollectionGeneratorPopover.tsx +243 -0
- package/src/ui/collection_editor/AIModifiedPathsContext.tsx +88 -0
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +222 -268
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +270 -204
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +138 -71
- package/src/ui/collection_editor/CollectionJsonImportDialog.tsx +171 -0
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +202 -101
- package/src/ui/collection_editor/DisplaySettingsForm.tsx +335 -0
- package/src/ui/collection_editor/EntityActionsEditTab.tsx +106 -97
- package/src/ui/collection_editor/EntityActionsSelectDialog.tsx +8 -10
- package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +5 -7
- package/src/ui/collection_editor/EnumForm.tsx +153 -102
- package/src/ui/collection_editor/ExtendSettingsForm.tsx +94 -0
- package/src/ui/collection_editor/GeneralSettingsForm.tsx +335 -0
- package/src/ui/collection_editor/GetCodeDialog.tsx +63 -41
- package/src/ui/collection_editor/KanbanConfigSection.tsx +209 -0
- package/src/ui/collection_editor/LayoutModeSwitch.tsx +27 -43
- package/src/ui/collection_editor/PropertyEditView.tsx +272 -199
- package/src/ui/collection_editor/PropertyFieldPreview.tsx +1 -1
- package/src/ui/collection_editor/PropertyTree.tsx +130 -58
- package/src/ui/collection_editor/SubcollectionsEditTab.tsx +169 -163
- package/src/ui/collection_editor/UnsavedChangesDialog.tsx +0 -2
- package/src/ui/collection_editor/ViewModeSwitch.tsx +43 -0
- package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +6 -3
- package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +5 -2
- package/src/ui/collection_editor/properties/BlockPropertyField.tsx +0 -2
- package/src/ui/collection_editor/properties/BooleanPropertyField.tsx +4 -1
- package/src/ui/collection_editor/properties/CommonPropertyFields.tsx +6 -4
- package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +126 -42
- package/src/ui/collection_editor/properties/EnumPropertyField.tsx +32 -24
- package/src/ui/collection_editor/properties/MapPropertyField.tsx +8 -9
- package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +128 -53
- package/src/ui/collection_editor/properties/NumberPropertyField.tsx +3 -1
- package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +6 -9
- package/src/ui/collection_editor/properties/StoragePropertyField.tsx +65 -49
- package/src/ui/collection_editor/properties/StringPropertyField.tsx +3 -1
- package/src/ui/collection_editor/properties/UrlPropertyField.tsx +12 -10
- package/src/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +23 -4
- package/src/ui/collection_editor/properties/conditions/ConditionsEditor.tsx +866 -0
- package/src/ui/collection_editor/properties/conditions/ConditionsPanel.tsx +28 -0
- package/src/ui/collection_editor/properties/conditions/EnumConditionsEditor.tsx +599 -0
- package/src/ui/collection_editor/properties/conditions/index.ts +6 -0
- package/src/ui/collection_editor/properties/conditions/property_paths.ts +92 -0
- package/src/ui/collection_editor/properties/validation/ArrayPropertyValidation.tsx +5 -2
- package/src/ui/collection_editor/properties/validation/GeneralPropertyValidation.tsx +7 -5
- package/src/ui/collection_editor/properties/validation/NumberPropertyValidation.tsx +10 -7
- package/src/ui/collection_editor/properties/validation/StringPropertyValidation.tsx +11 -9
- package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +5 -2
- package/src/useCollectionEditorPlugin.tsx +53 -22
- package/src/utils/validateCollectionJson.ts +380 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { EntityCollection, useNavigationController, useSnackbarController, AIIcon, useTranslation } from "@firecms/core";
|
|
3
|
+
import {
|
|
4
|
+
Button,
|
|
5
|
+
CircularProgress,
|
|
6
|
+
IconButton,
|
|
7
|
+
Menu,
|
|
8
|
+
SendIcon,
|
|
9
|
+
TextField,
|
|
10
|
+
Typography
|
|
11
|
+
} from "@firecms/ui";
|
|
12
|
+
import {
|
|
13
|
+
CollectionGenerationCallback,
|
|
14
|
+
CollectionGenerationApiError,
|
|
15
|
+
CollectionOperation
|
|
16
|
+
} from "../../api/generateCollectionApi";
|
|
17
|
+
import { PersistedCollection } from "../../types/persisted_collection";
|
|
18
|
+
|
|
19
|
+
export interface AICollectionGeneratorPopoverProps {
|
|
20
|
+
/**
|
|
21
|
+
* Current collection being edited (if modifying an existing collection)
|
|
22
|
+
*/
|
|
23
|
+
existingCollection?: PersistedCollection;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Callback when a collection is generated or modified.
|
|
27
|
+
* Includes the collection and optionally the operations that were applied.
|
|
28
|
+
*/
|
|
29
|
+
onGenerated: (collection: EntityCollection, operations?: CollectionOperation[]) => void;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Callback function for generating/modifying collections.
|
|
33
|
+
* The plugin is API-agnostic - the consumer provides the implementation.
|
|
34
|
+
*/
|
|
35
|
+
generateCollection: CollectionGenerationCallback;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Optional custom trigger button. If not provided, a default AI button is used.
|
|
39
|
+
*/
|
|
40
|
+
trigger?: React.ReactNode;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Size of the button
|
|
44
|
+
*/
|
|
45
|
+
size?: "small" | "medium" | "large";
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Whether to show the label text
|
|
49
|
+
*/
|
|
50
|
+
showLabel?: boolean;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Optional analytics callback
|
|
54
|
+
*/
|
|
55
|
+
onAnalyticsEvent?: (event: string, params?: object) => void;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function AICollectionGeneratorPopover({
|
|
59
|
+
existingCollection,
|
|
60
|
+
onGenerated,
|
|
61
|
+
generateCollection,
|
|
62
|
+
trigger,
|
|
63
|
+
size = "small",
|
|
64
|
+
showLabel = true,
|
|
65
|
+
onAnalyticsEvent
|
|
66
|
+
}: AICollectionGeneratorPopoverProps) {
|
|
67
|
+
const [menuOpen, setMenuOpen] = useState(false);
|
|
68
|
+
const [prompt, setPrompt] = useState("");
|
|
69
|
+
const [loading, setLoading] = useState(false);
|
|
70
|
+
const [error, setError] = useState<string | null>(null);
|
|
71
|
+
|
|
72
|
+
const { t } = useTranslation();
|
|
73
|
+
const navigation = useNavigationController();
|
|
74
|
+
const snackbarController = useSnackbarController();
|
|
75
|
+
|
|
76
|
+
const existingCollections = navigation.collections ?? [];
|
|
77
|
+
|
|
78
|
+
const handleGenerate = async () => {
|
|
79
|
+
if (!prompt.trim()) return;
|
|
80
|
+
|
|
81
|
+
setLoading(true);
|
|
82
|
+
setError(null);
|
|
83
|
+
|
|
84
|
+
const mode = existingCollection ? "modify" : "create";
|
|
85
|
+
onAnalyticsEvent?.("ai_collection_generate_start", { mode });
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const collectionsContext = existingCollections.map(c => ({
|
|
89
|
+
path: c.path,
|
|
90
|
+
id: c.id,
|
|
91
|
+
name: c.name,
|
|
92
|
+
properties: c.properties,
|
|
93
|
+
propertiesOrder: c.propertiesOrder
|
|
94
|
+
}));
|
|
95
|
+
|
|
96
|
+
const result = await generateCollection({
|
|
97
|
+
prompt: prompt.trim(),
|
|
98
|
+
existingCollections: collectionsContext.slice(0, 30),
|
|
99
|
+
...(existingCollection && {
|
|
100
|
+
existingCollection: {
|
|
101
|
+
path: existingCollection.path,
|
|
102
|
+
id: existingCollection.id,
|
|
103
|
+
name: existingCollection.name,
|
|
104
|
+
properties: existingCollection.properties,
|
|
105
|
+
propertiesOrder: existingCollection.propertiesOrder
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
onGenerated(result.collection, result.operations);
|
|
111
|
+
setMenuOpen(false);
|
|
112
|
+
setPrompt("");
|
|
113
|
+
onAnalyticsEvent?.("ai_collection_generate_success", {
|
|
114
|
+
mode,
|
|
115
|
+
operationsCount: result.operations?.length
|
|
116
|
+
});
|
|
117
|
+
snackbarController.open({
|
|
118
|
+
type: "success",
|
|
119
|
+
message: existingCollection
|
|
120
|
+
? "Collection updated with AI suggestions"
|
|
121
|
+
: "Collection generated successfully"
|
|
122
|
+
});
|
|
123
|
+
} catch (e) {
|
|
124
|
+
console.error("Error generating collection:", e);
|
|
125
|
+
const errorMessage = e instanceof CollectionGenerationApiError
|
|
126
|
+
? e.message
|
|
127
|
+
: "Failed to generate collection. Please try again.";
|
|
128
|
+
setError(errorMessage);
|
|
129
|
+
onAnalyticsEvent?.("ai_collection_generate_error", {
|
|
130
|
+
mode,
|
|
131
|
+
error: errorMessage
|
|
132
|
+
});
|
|
133
|
+
snackbarController.open({
|
|
134
|
+
type: "error",
|
|
135
|
+
message: errorMessage
|
|
136
|
+
});
|
|
137
|
+
} finally {
|
|
138
|
+
setLoading(false);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
143
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
144
|
+
e.preventDefault();
|
|
145
|
+
handleGenerate();
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const defaultTrigger = showLabel ? (
|
|
150
|
+
<Button
|
|
151
|
+
variant="text"
|
|
152
|
+
size={size}
|
|
153
|
+
disabled={loading}
|
|
154
|
+
startIcon={loading
|
|
155
|
+
? <CircularProgress size="smallest" />
|
|
156
|
+
: <AIIcon size="small" />
|
|
157
|
+
}
|
|
158
|
+
>
|
|
159
|
+
{t("ai_assist")}
|
|
160
|
+
</Button>
|
|
161
|
+
) : (
|
|
162
|
+
<IconButton
|
|
163
|
+
size={size}
|
|
164
|
+
disabled={loading}
|
|
165
|
+
aria-label={t("ai_assist")}
|
|
166
|
+
>
|
|
167
|
+
{loading
|
|
168
|
+
? <CircularProgress size="smallest" />
|
|
169
|
+
: <AIIcon size="small" />
|
|
170
|
+
}
|
|
171
|
+
</IconButton>
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
return (
|
|
175
|
+
<Menu
|
|
176
|
+
open={menuOpen}
|
|
177
|
+
onOpenChange={(open) => {
|
|
178
|
+
setMenuOpen(open);
|
|
179
|
+
if (!open) {
|
|
180
|
+
setError(null);
|
|
181
|
+
}
|
|
182
|
+
}}
|
|
183
|
+
trigger={trigger ?? defaultTrigger}
|
|
184
|
+
>
|
|
185
|
+
<div className="p-4 flex flex-col gap-3 min-w-[360px] max-w-[480px]">
|
|
186
|
+
<div className="flex items-center gap-2">
|
|
187
|
+
<AIIcon size="small" />
|
|
188
|
+
<Typography variant="subtitle2">
|
|
189
|
+
{existingCollection ? t("modify_collection_with_ai") : t("generate_collection_with_ai")}
|
|
190
|
+
</Typography>
|
|
191
|
+
</div>
|
|
192
|
+
|
|
193
|
+
<Typography variant="caption" color="secondary">
|
|
194
|
+
{existingCollection
|
|
195
|
+
? t("describe_changes_to_make")
|
|
196
|
+
: t("describe_collection_to_create")
|
|
197
|
+
}
|
|
198
|
+
</Typography>
|
|
199
|
+
|
|
200
|
+
<TextField
|
|
201
|
+
size="small"
|
|
202
|
+
multiline
|
|
203
|
+
autoFocus
|
|
204
|
+
className="w-full text-text-primary dark:text-text-primary-dark"
|
|
205
|
+
value={prompt}
|
|
206
|
+
onChange={(e) => setPrompt(e.target.value)}
|
|
207
|
+
onKeyDown={handleKeyDown}
|
|
208
|
+
placeholder={existingCollection
|
|
209
|
+
? t("ai_placeholder_modify")
|
|
210
|
+
: t("ai_placeholder_create")
|
|
211
|
+
}
|
|
212
|
+
disabled={loading}
|
|
213
|
+
/>
|
|
214
|
+
|
|
215
|
+
{error && (
|
|
216
|
+
<Typography variant="caption" className="text-red-500">
|
|
217
|
+
{error}
|
|
218
|
+
</Typography>
|
|
219
|
+
)}
|
|
220
|
+
|
|
221
|
+
<div className="flex justify-end gap-2">
|
|
222
|
+
<Button
|
|
223
|
+
variant="text"
|
|
224
|
+
size="small"
|
|
225
|
+
onClick={() => setMenuOpen(false)}
|
|
226
|
+
disabled={loading}
|
|
227
|
+
>
|
|
228
|
+
{t("cancel")}
|
|
229
|
+
</Button>
|
|
230
|
+
<Button
|
|
231
|
+
variant="filled"
|
|
232
|
+
size="small"
|
|
233
|
+
onClick={handleGenerate}
|
|
234
|
+
disabled={!prompt.trim() || loading}
|
|
235
|
+
startIcon={loading ? <CircularProgress size="smallest" /> : undefined}
|
|
236
|
+
>
|
|
237
|
+
{loading ? t("generating") : t("generate_with_ai")}
|
|
238
|
+
</Button>
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
</Menu>
|
|
242
|
+
);
|
|
243
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import React, { createContext, useCallback, useContext, useState } from "react";
|
|
2
|
+
import { CollectionOperation } from "../../api/generateCollectionApi";
|
|
3
|
+
|
|
4
|
+
export interface AIModifiedPathsContextType {
|
|
5
|
+
/** Set of paths that were modified by AI */
|
|
6
|
+
modifiedPaths: Set<string>;
|
|
7
|
+
/** Counter that increments each time AI modifies the collection - use in keys to force remount */
|
|
8
|
+
generationCounter: number;
|
|
9
|
+
/** Add paths from operations */
|
|
10
|
+
addModifiedPaths: (operations: CollectionOperation[]) => void;
|
|
11
|
+
/** Clear a specific path (when user edits that field) */
|
|
12
|
+
clearPath: (path: string) => void;
|
|
13
|
+
/** Clear all paths (on save or cancel) */
|
|
14
|
+
clearAllPaths: () => void;
|
|
15
|
+
/** Check if a path is modified */
|
|
16
|
+
isPathModified: (path: string) => boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const AIModifiedPathsContext = createContext<AIModifiedPathsContextType | null>(null);
|
|
20
|
+
|
|
21
|
+
export function AIModifiedPathsProvider({ children }: { children: React.ReactNode }) {
|
|
22
|
+
const [modifiedPaths, setModifiedPaths] = useState<Set<string>>(new Set());
|
|
23
|
+
const [generationCounter, setGenerationCounter] = useState(0);
|
|
24
|
+
|
|
25
|
+
const addModifiedPaths = useCallback((operations: CollectionOperation[]) => {
|
|
26
|
+
setModifiedPaths(prev => {
|
|
27
|
+
const newSet = new Set(prev);
|
|
28
|
+
operations.forEach(op => {
|
|
29
|
+
// Add the path and all parent paths for nested modifications
|
|
30
|
+
newSet.add(op.path);
|
|
31
|
+
// For properties modifications, also mark the property itself
|
|
32
|
+
// e.g., "properties.email.description" -> also adds "properties.email"
|
|
33
|
+
const parts = op.path.split(".");
|
|
34
|
+
if (parts[0] === "properties" && parts.length >= 2) {
|
|
35
|
+
newSet.add(`properties.${parts[1]}`);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return newSet;
|
|
39
|
+
});
|
|
40
|
+
// Increment counter to force property form remount
|
|
41
|
+
setGenerationCounter(prev => prev + 1);
|
|
42
|
+
}, []);
|
|
43
|
+
|
|
44
|
+
const clearPath = useCallback((path: string) => {
|
|
45
|
+
setModifiedPaths(prev => {
|
|
46
|
+
const newSet = new Set(prev);
|
|
47
|
+
// Remove exact path and any child paths
|
|
48
|
+
for (const p of newSet) {
|
|
49
|
+
if (p === path || p.startsWith(path + ".")) {
|
|
50
|
+
newSet.delete(p);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return newSet;
|
|
54
|
+
});
|
|
55
|
+
}, []);
|
|
56
|
+
|
|
57
|
+
const clearAllPaths = useCallback(() => {
|
|
58
|
+
setModifiedPaths(new Set());
|
|
59
|
+
}, []);
|
|
60
|
+
|
|
61
|
+
const isPathModified = useCallback((path: string): boolean => {
|
|
62
|
+
// Check if this exact path or any parent path is modified
|
|
63
|
+
if (modifiedPaths.has(path)) return true;
|
|
64
|
+
// Check if any child paths are modified
|
|
65
|
+
for (const p of modifiedPaths) {
|
|
66
|
+
if (p.startsWith(path + ".")) return true;
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}, [modifiedPaths]);
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<AIModifiedPathsContext.Provider value={{
|
|
73
|
+
modifiedPaths,
|
|
74
|
+
generationCounter,
|
|
75
|
+
addModifiedPaths,
|
|
76
|
+
clearPath,
|
|
77
|
+
clearAllPaths,
|
|
78
|
+
isPathModified
|
|
79
|
+
}}>
|
|
80
|
+
{children}
|
|
81
|
+
</AIModifiedPathsContext.Provider>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function useAIModifiedPaths(): AIModifiedPathsContextType | null {
|
|
86
|
+
return useContext(AIModifiedPathsContext);
|
|
87
|
+
}
|
|
88
|
+
|