From f6573f728972f7919bafacf7d342ad421005b440 Mon Sep 17 00:00:00 2001 From: Luke-Bilhorn Date: Mon, 18 May 2026 11:43:18 -0500 Subject: [PATCH] Restore "Zip output" export option Re-adds the zip output checkbox below "Select Location" in the export view, the `zipOutput` field on `ExportOptions`, and the post-export zipping logic that bundles the wrapper folder into a .zip and removes the original directory. Restores `zipDirectory` helper that was deleted along with the feature. --- src/exportHandler/exportHandler.ts | 12 +++++++++++- src/exportHandler/utils/zipUtils.ts | 17 +++++++++++++++++ src/projectManager/projectExportView.ts | 8 +++++++- 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 src/exportHandler/utils/zipUtils.ts diff --git a/src/exportHandler/exportHandler.ts b/src/exportHandler/exportHandler.ts index 913d7d74e..ccaecb2c6 100644 --- a/src/exportHandler/exportHandler.ts +++ b/src/exportHandler/exportHandler.ts @@ -6,6 +6,7 @@ import { exec } from "child_process"; import { promisify } from "util"; import { removeHtmlTags, generateSrtData } from "./subtitleUtils"; import { generateVttData } from "./vttUtils"; +import { zipDirectory } from "./utils/zipUtils"; // import { exportRtfWithPandoc } from "../../webviews/codex-webviews/src/NewSourceUploader/importers/rtf/pandocNodeBridge"; @@ -215,6 +216,7 @@ export enum CodexExportFormat { export interface ExportOptions { skipValidation?: boolean; removeIds?: boolean; + zipOutput?: boolean; includeAudio?: boolean; includeTimestamps?: boolean; } @@ -1739,6 +1741,7 @@ export async function exportCodexContent( filesToExport: string[], options?: ExportOptions ) { + const shouldZip = options?.zipOutput ?? false; const includeAudio = options?.includeAudio === true && format !== CodexExportFormat.AUDIO; const isMulti = includeAudio; @@ -1751,7 +1754,7 @@ export async function exportCodexContent( const baseName = `${projectName}-${formatLabel}-${dateStamp}`; let candidate = path.join(userSelectedPath, baseName); let suffix = 1; - while (fs.existsSync(candidate)) { + while (fs.existsSync(candidate) || fs.existsSync(`${candidate}.zip`)) { candidate = path.join(userSelectedPath, `${baseName}-${suffix}`); suffix++; } @@ -1829,6 +1832,13 @@ export async function exportCodexContent( debug("Failed to generate NOTICE.txt files:", e); } } + + if (shouldZip) { + const zipPath = `${wrapperPath}.zip`; + await zipDirectory(wrapperPath, zipPath); + fs.rmSync(wrapperPath, { recursive: true, force: true }); + vscode.window.showInformationMessage(`Exported to ${zipPath}`); + } } async function generateMissingContentNotices( diff --git a/src/exportHandler/utils/zipUtils.ts b/src/exportHandler/utils/zipUtils.ts new file mode 100644 index 000000000..10730f95e --- /dev/null +++ b/src/exportHandler/utils/zipUtils.ts @@ -0,0 +1,17 @@ +import archiver from "archiver"; +import * as fs from "fs"; +import { basename } from "path"; + +export const zipDirectory = (sourceDir: string, destZipPath: string): Promise => { + return new Promise((resolve, reject) => { + const output = fs.createWriteStream(destZipPath); + const archive = archiver("zip", { zlib: { level: 9 } }); + + output.on("close", resolve); + archive.on("error", reject); + + archive.pipe(output); + archive.directory(sourceDir, basename(sourceDir)); + archive.finalize(); + }); +}; diff --git a/src/projectManager/projectExportView.ts b/src/projectManager/projectExportView.ts index aed40b1bb..7432c01e5 100644 --- a/src/projectManager/projectExportView.ts +++ b/src/projectManager/projectExportView.ts @@ -802,7 +802,12 @@ function getWebviewContent( Select Location - +
+
+ + +
+
@@ -1466,6 +1471,7 @@ function getWebviewContent( options.includeAudio = true; options.includeTimestamps = selectedAudioMode === 'audio-timestamps'; } + if (document.getElementById('zipOutput')?.checked) options.zipOutput = true; vscode.postMessage({ command: 'export', format: formatToSend,