@smallwebco/tinypivot-react 1.0.74 → 1.0.79
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/README.md +10 -8
- package/dist/index.cjs +91 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +92 -42
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
getDefaultDemoResponse,
|
|
15
15
|
getDemoSchema,
|
|
16
16
|
getInitialDemoData,
|
|
17
|
+
getLatestConversationData,
|
|
17
18
|
getMessagesForAPI,
|
|
18
19
|
setConversationDataSource,
|
|
19
20
|
validateSQLSafety
|
|
@@ -55,14 +56,22 @@ function useAIAnalyst(options) {
|
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
}, [storageKey]);
|
|
58
|
-
const
|
|
59
|
+
const initialConversationRef = useRef(null);
|
|
60
|
+
if (!initialConversationRef.current) {
|
|
61
|
+
initialConversationRef.current = loadFromStorage();
|
|
62
|
+
}
|
|
63
|
+
const [conversation, setConversation] = useState(initialConversationRef.current);
|
|
59
64
|
const [schemas, setSchemas] = useState(/* @__PURE__ */ new Map());
|
|
60
65
|
const [allSchemas, setAllSchemas] = useState([]);
|
|
61
66
|
const [isLoading, setIsLoading] = useState(false);
|
|
62
67
|
const [error, setError] = useState(null);
|
|
63
|
-
const [lastLoadedData, setLastLoadedData] = useState(
|
|
68
|
+
const [lastLoadedData, setLastLoadedData] = useState(
|
|
69
|
+
() => getLatestConversationData(initialConversationRef.current)
|
|
70
|
+
);
|
|
64
71
|
const [discoveredDataSources, setDiscoveredDataSources] = useState([]);
|
|
65
72
|
const [isLoadingTables, setIsLoadingTables] = useState(false);
|
|
73
|
+
const dataSourceLoadPromisesRef = useRef(/* @__PURE__ */ new Map());
|
|
74
|
+
const hydratedPersistedSelectionRef = useRef(false);
|
|
66
75
|
const effectiveDataSources = useMemo(() => {
|
|
67
76
|
if (config.dataSources && config.dataSources.length > 0) {
|
|
68
77
|
return config.dataSources;
|
|
@@ -207,6 +216,59 @@ function useAIAnalyst(options) {
|
|
|
207
216
|
console.warn("Failed to fetch sample data:", err);
|
|
208
217
|
}
|
|
209
218
|
}, [onDataLoaded]);
|
|
219
|
+
const loadDataSourceState = useCallback(async (dataSource) => {
|
|
220
|
+
const currentConfig = configRef.current;
|
|
221
|
+
if (currentConfig.dataSourceLoader) {
|
|
222
|
+
const { data, schema } = await currentConfig.dataSourceLoader(dataSource.id);
|
|
223
|
+
if (schema) {
|
|
224
|
+
setSchemas((prev) => new Map(prev).set(dataSource.id, schema));
|
|
225
|
+
}
|
|
226
|
+
if (data && data.length > 0) {
|
|
227
|
+
setLastLoadedData(data);
|
|
228
|
+
onDataLoaded?.({
|
|
229
|
+
data,
|
|
230
|
+
query: `SELECT * FROM ${dataSource.table} LIMIT 100`,
|
|
231
|
+
dataSourceId: dataSource.id,
|
|
232
|
+
rowCount: data.length
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (currentConfig.demoMode) {
|
|
238
|
+
const demoSchema = getDemoSchema(dataSource.id);
|
|
239
|
+
if (demoSchema) {
|
|
240
|
+
setSchemas((prev) => new Map(prev).set(dataSource.id, demoSchema));
|
|
241
|
+
}
|
|
242
|
+
const initialData = getInitialDemoData(dataSource.id);
|
|
243
|
+
if (initialData) {
|
|
244
|
+
setLastLoadedData(initialData);
|
|
245
|
+
onDataLoaded?.({
|
|
246
|
+
data: initialData,
|
|
247
|
+
query: `SELECT * FROM ${dataSource.table} LIMIT 10`,
|
|
248
|
+
dataSourceId: dataSource.id,
|
|
249
|
+
rowCount: initialData.length
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
if (currentConfig.endpoint) {
|
|
255
|
+
await fetchSchema(dataSource);
|
|
256
|
+
await fetchSampleData(dataSource);
|
|
257
|
+
}
|
|
258
|
+
}, [fetchSchema, fetchSampleData, onDataLoaded]);
|
|
259
|
+
const ensureDataSourceState = useCallback(async (dataSource) => {
|
|
260
|
+
const existingLoad = dataSourceLoadPromisesRef.current.get(dataSource.id);
|
|
261
|
+
if (existingLoad) {
|
|
262
|
+
return existingLoad;
|
|
263
|
+
}
|
|
264
|
+
const loadPromise = loadDataSourceState(dataSource).catch((err) => {
|
|
265
|
+
console.warn("Failed to load data source:", err);
|
|
266
|
+
}).finally(() => {
|
|
267
|
+
dataSourceLoadPromisesRef.current.delete(dataSource.id);
|
|
268
|
+
});
|
|
269
|
+
dataSourceLoadPromisesRef.current.set(dataSource.id, loadPromise);
|
|
270
|
+
return loadPromise;
|
|
271
|
+
}, [loadDataSourceState]);
|
|
210
272
|
const selectDataSource = useCallback(async (dataSourceId) => {
|
|
211
273
|
const dataSource = effectiveDataSources.find((ds) => ds.id === dataSourceId);
|
|
212
274
|
if (!dataSource) {
|
|
@@ -226,44 +288,30 @@ What would you like to know about this data?`
|
|
|
226
288
|
onConversationUpdate?.({ conversation: withMessage });
|
|
227
289
|
return withMessage;
|
|
228
290
|
});
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
const initialData = getInitialDemoData(dataSourceId);
|
|
253
|
-
if (initialData) {
|
|
254
|
-
setLastLoadedData(initialData);
|
|
255
|
-
onDataLoaded?.({
|
|
256
|
-
data: initialData,
|
|
257
|
-
query: `SELECT * FROM ${dataSource.table} LIMIT 10`,
|
|
258
|
-
dataSourceId,
|
|
259
|
-
rowCount: initialData.length
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
} else if (configRef.current.endpoint) {
|
|
263
|
-
await fetchSchema(dataSource);
|
|
264
|
-
await fetchSampleData(dataSource);
|
|
291
|
+
await ensureDataSourceState(dataSource);
|
|
292
|
+
}, [effectiveDataSources, ensureDataSourceState, onConversationUpdate]);
|
|
293
|
+
useEffect(() => {
|
|
294
|
+
if (hydratedPersistedSelectionRef.current) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const initialConversation = initialConversationRef.current;
|
|
298
|
+
const initialDataSourceId = initialConversation?.dataSourceId;
|
|
299
|
+
if (!initialDataSourceId) {
|
|
300
|
+
hydratedPersistedSelectionRef.current = true;
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
const dataSource = effectiveDataSources.find((ds) => ds.id === initialDataSourceId);
|
|
304
|
+
if (!dataSource) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
const hasPersistedPreviewData = !!getLatestConversationData(initialConversation);
|
|
308
|
+
const hasSchema = schemas.has(initialDataSourceId);
|
|
309
|
+
const hasPreviewData = !!lastLoadedData?.length || hasPersistedPreviewData;
|
|
310
|
+
hydratedPersistedSelectionRef.current = true;
|
|
311
|
+
if (!hasSchema || !hasPreviewData) {
|
|
312
|
+
void ensureDataSourceState(dataSource);
|
|
265
313
|
}
|
|
266
|
-
}, [effectiveDataSources,
|
|
314
|
+
}, [effectiveDataSources, ensureDataSourceState, lastLoadedData, schemas]);
|
|
267
315
|
const callAIEndpoint = useCallback(async (userInput, currentConversation, currentSchemas, currentDataSources, currentAllSchemas) => {
|
|
268
316
|
if (!configRef.current.endpoint) {
|
|
269
317
|
throw new Error("No endpoint configured. Set `endpoint` in AI analyst config.");
|
|
@@ -666,6 +714,7 @@ What would you like to know about this data?`
|
|
|
666
714
|
return null;
|
|
667
715
|
}, [conversation.dataSourceId, effectiveDataSources, onError]);
|
|
668
716
|
const clearConversation = useCallback(() => {
|
|
717
|
+
hydratedPersistedSelectionRef.current = true;
|
|
669
718
|
const newConv = createConversation(configRef.current.sessionId);
|
|
670
719
|
setConversation(newConv);
|
|
671
720
|
setError(null);
|
|
@@ -676,7 +725,9 @@ What would you like to know about this data?`
|
|
|
676
725
|
return { ...conversation };
|
|
677
726
|
}, [conversation]);
|
|
678
727
|
const importConversation = useCallback((conv) => {
|
|
728
|
+
hydratedPersistedSelectionRef.current = true;
|
|
679
729
|
setConversation(conv);
|
|
730
|
+
setLastLoadedData(getLatestConversationData(conv));
|
|
680
731
|
onConversationUpdate?.({ conversation: conv });
|
|
681
732
|
}, [onConversationUpdate]);
|
|
682
733
|
return {
|
|
@@ -4716,9 +4767,8 @@ function PivotSkeleton({
|
|
|
4716
4767
|
d: "M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
|
|
4717
4768
|
}
|
|
4718
4769
|
) }),
|
|
4719
|
-
/* @__PURE__ */ jsx8("h3", { children: "
|
|
4720
|
-
/* @__PURE__ */ jsx8("p", { children: "Pivot
|
|
4721
|
-
/* @__PURE__ */ jsx8("a", { href: "https://tiny-pivot.com/#pricing", target: "_blank", rel: "noopener noreferrer", className: "vpg-pro-link", children: "Get Pro License \u2192" })
|
|
4770
|
+
/* @__PURE__ */ jsx8("h3", { children: "Pivot Unavailable" }),
|
|
4771
|
+
/* @__PURE__ */ jsx8("p", { children: "Pivot mode could not be enabled in this session. Try reloading the page." })
|
|
4722
4772
|
] }) }) : /* @__PURE__ */ jsxs8(Fragment5, { children: [
|
|
4723
4773
|
/* @__PURE__ */ jsxs8("div", { className: "vpg-config-bar", children: [
|
|
4724
4774
|
/* @__PURE__ */ jsxs8(
|