diff --git a/src/quickAddApi.test.ts b/src/quickAddApi.test.ts index 30161867..98675e97 100644 --- a/src/quickAddApi.test.ts +++ b/src/quickAddApi.test.ts @@ -526,9 +526,9 @@ describe("utility selection helpers", () => { expect(api.utility.getSelection()).toBe(""); }); - it("getSelectedText reports an error and returns undefined with no view", () => { + it("getSelectedText reports an error and returns '' with no view", () => { const { api } = getApi(); - expect(api.utility.getSelectedText()).toBeUndefined(); + expect(api.utility.getSelectedText()).toBe(""); expect(mocks.reportError).toHaveBeenCalled(); }); @@ -544,7 +544,7 @@ describe("utility selection helpers", () => { }, }); const { api } = getApi(app); - expect(api.utility.getSelectedText()).toBeUndefined(); + expect(api.utility.getSelectedText()).toBe(""); expect(mocks.reportError).toHaveBeenCalled(); }); diff --git a/src/quickAddApi.ts b/src/quickAddApi.ts index d1639798..d069c9a4 100644 --- a/src/quickAddApi.ts +++ b/src/quickAddApi.ts @@ -504,7 +504,7 @@ export class QuickAddApi { new Error("No active view"), "Could not get selected text", ); - return; + return ""; } if (!activeView.editor.somethingSelected()) { @@ -512,7 +512,7 @@ export class QuickAddApi { new Error("No text selected"), "Could not get selected text", ); - return; + return ""; } return activeView.editor.getSelection(); diff --git a/src/services/choiceService.test.ts b/src/services/choiceService.test.ts index 77ff0ba4..c26ba405 100644 --- a/src/services/choiceService.test.ts +++ b/src/services/choiceService.test.ts @@ -190,6 +190,23 @@ describe("choiceService", () => { expect(copy.id).not.toBe(original.id); }); + it("preserves command and onePageInput when duplicating a Multi", () => { + const original = createChoice("Multi", "M") as IMultiChoice; + (original as unknown as { command: boolean }).command = true; + (original as unknown as { onePageInput: string }).onePageInput = + "always"; + original.choices = [createChoice("Capture", "Child")]; + + const copy = duplicateChoice(original) as IMultiChoice; + + expect(copy.command).toBe(true); + expect( + (copy as unknown as { onePageInput?: string }).onePageInput, + ).toBe("always"); + // children still duplicated with fresh ids + expect(copy.choices[0].id).not.toBe(original.choices[0].id); + }); + it("deep clones a Macro's macro and regenerates ids", () => { const original = createChoice("Macro", "Mac") as IMacroChoice; original.macro.commands.push({ diff --git a/src/services/choiceService.ts b/src/services/choiceService.ts index 59255155..3b327a1f 100644 --- a/src/services/choiceService.ts +++ b/src/services/choiceService.ts @@ -42,9 +42,11 @@ export function duplicateChoice(choice: IChoice): IChoice { if (choice.type === "Multi") { const newMulti = newChoice as IMultiChoice; const sourceMulti = choice as IMultiChoice; + // Preserve command/onePageInput/placeholder/collapsed etc. (symmetry with + // the other choice types). `choices` is excluded here and set via the + // recursive map below so children get fresh ids, not the source's. + Object.assign(newMulti, excludeKeys(sourceMulti, ["id", "name", "choices"])); newMulti.choices = sourceMulti.choices.map(duplicateChoice); - newMulti.placeholder = sourceMulti.placeholder; - newMulti.collapsed = sourceMulti.collapsed; return newMulti; } diff --git a/src/services/packageImportService.test.ts b/src/services/packageImportService.test.ts index 2fda0800..201d9051 100644 --- a/src/services/packageImportService.test.ts +++ b/src/services/packageImportService.test.ts @@ -177,14 +177,14 @@ describe("parseQuickAddPackage", () => { ); }); - it("throws when schema version is newer than supported", () => { - // isQuickAddPackage requires schemaVersion === current, so a too-new - // version is rejected by the validator before the explicit version check. + it("throws a version-specific error when the schema version is newer than supported", () => { const future = { ...makePackage(), schemaVersion: QUICKADD_PACKAGE_SCHEMA_VERSION + 1, }; - expect(() => parseQuickAddPackage(JSON.stringify(future))).toThrow(); + expect(() => parseQuickAddPackage(JSON.stringify(future))).toThrow( + /newer than this plugin supports/, + ); }); it("rejects a package whose choices are missing required fields", () => { diff --git a/src/types/packages/QuickAddPackage.ts b/src/types/packages/QuickAddPackage.ts index bb73c839..663c327b 100644 --- a/src/types/packages/QuickAddPackage.ts +++ b/src/types/packages/QuickAddPackage.ts @@ -22,7 +22,7 @@ export interface QuickAddPackageChoice { } export interface QuickAddPackage { - schemaVersion: typeof QUICKADD_PACKAGE_SCHEMA_VERSION; + schemaVersion: number; quickAddVersion: string; createdAt: string; rootChoiceIds: string[]; @@ -85,8 +85,13 @@ export function isQuickAddPackage(value: unknown): value is QuickAddPackage { assets, } = value; + // Structural check only — parseQuickAddPackage owns the version-range check so + // a too-new package gets a specific "newer than supported" error instead of + // this generic one. const schemaMatches = - schemaVersion === QUICKADD_PACKAGE_SCHEMA_VERSION; + typeof schemaVersion === "number" && + Number.isInteger(schemaVersion) && + schemaVersion >= 1; return ( schemaMatches &&