ui: detect the conversation import format from file contents (#26121)
* ui: detect the conversation import format from file contents iOS resolves every accept entry to a UTI and has none for ".jsonl", so the picker greyed out exported conversations. Drop the accept filter and pick the parser from the file contents: ZIP magic bytes, then a first "session" record for JSONL, otherwise the legacy JSON format. Also remove the unused importConversations() picker and an orphan doc comment, and cover each format with unit tests. * ui: report what a conversation import actually wrote The import summary echoed the selection back, so re-importing conversations already in the database claimed success while nothing was written and only a console warning said otherwise. Return the imported and skipped conversations from the database layer, list the written ones in the summary, and count the rest in a toast. * ui: name the literals of the JSONL conversation format Introduce SessionRecordType and SESSION_HARNESS, and reuse the existing NEWLINE constant, so the record format lives in one place. This also covers the writer side, which predates the import path under review and carried the same literals: an enum stated by the reader alone lets the two sides drift. Values are unchanged, so an export stays byte identical.
This commit is contained in:
+13
-3
@@ -159,8 +159,10 @@
|
||||
try {
|
||||
const input = document.createElement('input');
|
||||
|
||||
// No `accept` filter: iOS resolves each entry to a UTI and has none for
|
||||
// `.jsonl`, which greys out exported conversations in the file picker.
|
||||
// `parseImportFile` detects the format from the file contents instead.
|
||||
input.type = HtmlInputType.FILE;
|
||||
input.accept = `${FileExtensionText.JSON},${FileExtensionText.JSONL},${FileExtensionText.ZIP}`;
|
||||
|
||||
input.onchange = async (e) => {
|
||||
const file = (e.target as HTMLInputElement)?.files?.[0];
|
||||
@@ -199,9 +201,17 @@
|
||||
.snapshot(fullImportData)
|
||||
.filter((item) => selectedIds.has(item.conv.id));
|
||||
|
||||
await conversationsStore.importConversationsData(selectedData);
|
||||
const { imported, skipped } = await conversationsStore.importConversationsData(selectedData);
|
||||
|
||||
importedConversations = selectedConversations;
|
||||
// A conversation already in the database is left untouched, so the summary
|
||||
// lists what was written and the toast accounts for the rest.
|
||||
if (skipped.length > 0) {
|
||||
toast.info(
|
||||
`Skipped ${skipped.length} conversation${skipped.length === 1 ? '' : 's'} already in your library`
|
||||
);
|
||||
}
|
||||
|
||||
importedConversations = imported;
|
||||
showImportSummary = true;
|
||||
showExportSummary = false;
|
||||
showImportDialog = false;
|
||||
|
||||
Reference in New Issue
Block a user