feat: include project name in export all zip filenames

When exporting conversations from a project, the zip filename becomes
chatgpt-export-{format}-project-{normalized-name}.zip instead of the
generic chatgpt-export-{format}.zip.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
pionxzh
2026-03-01 23:19:28 +08:00
parent df1af4bdf8
commit 9c1ad6f6c0
5 changed files with 33 additions and 14 deletions

View File

@@ -4,7 +4,7 @@ import { KEY_TIMESTAMP_24H, KEY_TIMESTAMP_ENABLED, KEY_TIMESTAMP_HTML, baseUrl }
import i18n from '../i18n'
import { checkIfConversationStarted, getUserAvatar } from '../page'
import templateHtml from '../template.html?raw'
import { downloadFile, getFileNameWithFormat } from '../utils/download'
import { buildZipFileName, downloadFile, getFileNameWithFormat } from '../utils/download'
import { fromMarkdown, toHtml } from '../utils/markdown'
import { ScriptStorage } from '../utils/storage'
import { standardizeLineBreaks } from '../utils/text'
@@ -31,7 +31,7 @@ export async function exportToHtml(fileNameFormat: string, metaList: ExportMeta[
return true
}
export async function exportAllToHtml(fileNameFormat: string, apiConversations: ApiConversationWithId[], metaList?: ExportMeta[]) {
export async function exportAllToHtml(fileNameFormat: string, apiConversations: ApiConversationWithId[], metaList?: ExportMeta[], projectName?: string) {
const userAvatar = await getUserAvatar()
const zip = new JSZip()
@@ -63,7 +63,7 @@ export async function exportAllToHtml(fileNameFormat: string, apiConversations:
level: 9,
},
})
downloadFile('chatgpt-export-html.zip', 'application/zip', blob)
downloadFile(buildZipFileName('html', projectName), 'application/zip', blob)
return true
}

View File

@@ -3,8 +3,9 @@ import { fetchConversation, getCurrentChatId, processConversation } from '../api
import i18n from '../i18n'
import { checkIfConversationStarted } from '../page'
import { convertToOoba, convertToTavern } from '../utils/conversion'
import { downloadFile, getFileNameWithFormat } from '../utils/download'
import { buildZipFileName, downloadFile, getFileNameWithFormat, normalizeProjectName } from '../utils/download'
import type { ApiConversationWithId } from '../api'
import type { ExportMeta } from '../ui/SettingContext'
export async function exportToJson(fileNameFormat: string) {
if (!checkIfConversationStarted()) {
@@ -60,14 +61,17 @@ export async function exportToOoba(fileNameFormat: string) {
return true
}
export async function exportAllToOfficialJson(_fileNameFormat: string, apiConversations: ApiConversationWithId[]) {
export async function exportAllToOfficialJson(_fileNameFormat: string, apiConversations: ApiConversationWithId[], _metaList?: ExportMeta[], projectName?: string) {
const content = conversationToJson(apiConversations)
downloadFile('chatgpt-export.json', 'application/json', content)
const baseName = projectName
? `chatgpt-export-project-${normalizeProjectName(projectName)}`
: 'chatgpt-export'
downloadFile(`${baseName}.json`, 'application/json', content)
return true
}
export async function exportAllToJson(fileNameFormat: string, apiConversations: ApiConversationWithId[]) {
export async function exportAllToJson(fileNameFormat: string, apiConversations: ApiConversationWithId[], _metaList?: ExportMeta[], projectName?: string) {
const zip = new JSZip()
const filenameMap = new Map<string, number>()
const conversations = apiConversations.map(x => ({
@@ -100,7 +104,7 @@ export async function exportAllToJson(fileNameFormat: string, apiConversations:
level: 9,
},
})
downloadFile('chatgpt-export-json.zip', 'application/zip', blob)
downloadFile(buildZipFileName('json', projectName), 'application/zip', blob)
return true
}

View File

@@ -3,7 +3,7 @@ import { fetchConversation, getCurrentChatId, processConversation } from '../api
import { KEY_TIMESTAMP_24H, KEY_TIMESTAMP_ENABLED, KEY_TIMESTAMP_MARKDOWN, baseUrl } from '../constants'
import i18n from '../i18n'
import { checkIfConversationStarted } from '../page'
import { downloadFile, getFileNameWithFormat } from '../utils/download'
import { buildZipFileName, downloadFile, getFileNameWithFormat } from '../utils/download'
import { fromMarkdown, toMarkdown } from '../utils/markdown'
import { ScriptStorage } from '../utils/storage'
import { standardizeLineBreaks } from '../utils/text'
@@ -28,7 +28,7 @@ export async function exportToMarkdown(fileNameFormat: string, metaList: ExportM
return true
}
export async function exportAllToMarkdown(fileNameFormat: string, apiConversations: ApiConversationWithId[], metaList?: ExportMeta[]) {
export async function exportAllToMarkdown(fileNameFormat: string, apiConversations: ApiConversationWithId[], metaList?: ExportMeta[], projectName?: string) {
const zip = new JSZip()
const filenameMap = new Map<string, number>()
const conversations = apiConversations.map(x => processConversation(x))
@@ -58,7 +58,7 @@ export async function exportAllToMarkdown(fileNameFormat: string, apiConversatio
level: 9,
},
})
downloadFile('chatgpt-export-markdown.zip', 'application/zip', blob)
downloadFile(buildZipFileName('markdown', projectName), 'application/zip', blob)
return true
}

View File

@@ -269,10 +269,10 @@ const DialogContent: FC<DialogContentProps> = ({ format }) => {
const off = requestQueue.on('done', (results) => {
setProcessing(false)
const callback = exportAllOptions.find(o => o.label === exportType)?.callback
if (callback) callback(format, results, metaList)
if (callback) callback(format, results, metaList, selectedProject?.display.name)
})
return () => off()
}, [requestQueue, exportAllOptions, exportType, format, metaList])
}, [requestQueue, exportAllOptions, exportType, format, metaList, selectedProject])
useEffect(() => {
const off = archiveQueue.on('done', () => {
@@ -314,7 +314,7 @@ const DialogContent: FC<DialogContentProps> = ({ format }) => {
const results = localConversations.filter(c => selected.some(s => s.id === c.id))
const callback = exportAllOptions.find(o => o.label === exportType)?.callback
if (callback) callback(format, results, metaList)
if (callback) callback(format, results, metaList, selectedProject?.display.name)
}, [
disabled,
selected,
@@ -323,6 +323,7 @@ const DialogContent: FC<DialogContentProps> = ({ format }) => {
exportType,
format,
metaList,
selectedProject,
])
const exportAll = useMemo(() => {

View File

@@ -21,6 +21,20 @@ export function downloadUrl(filename: string, url: string) {
document.body.removeChild(a)
}
export function normalizeProjectName(projectName: string) {
return projectName
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '')
}
export function buildZipFileName(format: string, projectName?: string) {
if (projectName) {
return `chatgpt-export-${format}-project-${normalizeProjectName(projectName)}.zip`
}
return `chatgpt-export-${format}.zip`
}
export function getFileNameWithFormat(format: string, ext: string, {
title = document.title,
// chatId will be empty when exporting all conversations