Files
markdown-converter/src/main/ExportPresets.js
T

93 lines
3.4 KiB
JavaScript

'use strict';
/**
* Export preset persistence (Task 21 — export presets/profiles).
*
* Pure list operations over the `exportPresets` array kept in the app's
* settings.json store (the `store.get`/`store.set` helpers defined in
* src/main.js — the same store that holds headerFooterSettings and
* pageSettings). The store is injected so the logic is unit-testable with a
* fake store (see tests/main/ExportPresets.test.js); src/main.js wires these
* functions to the get-export-presets / save-export-preset /
* delete-export-preset invoke channels.
*
* Preset shape: { id: string, name: string, format: string|null, options: object }
* — `options` is the export-options snapshot captured from the renderer's
* export dialog, so selecting a preset can pre-fill that dialog exactly.
*/
const PRESET_KEY = 'exportPresets';
const MAX_PRESETS = 50;
const MAX_NAME_LENGTH = 100;
/**
* Read the stored presets, defensively skipping corrupt data.
* @param {{get: Function, set: Function}} store settings store
* @returns {Array<{id: string, name: string, format: string|null, options: Object}>}
*/
function loadPresets(store) {
const stored = store.get(PRESET_KEY, []);
if (!Array.isArray(stored)) return [];
return stored.filter(
(preset) =>
preset &&
typeof preset === 'object' &&
typeof preset.id === 'string' &&
typeof preset.name === 'string'
);
}
/**
* Validate, normalize, and upsert a preset by id. A missing id gets a newly
* generated one (insert); an id that already exists is replaced (update).
* @param {{get: Function, set: Function}} store settings store
* @param {{id?: string, name?: string, format?: string, options?: Object}} preset
* @returns {Array} the updated preset list (also persisted)
* @throws when the preset is not an object, the name is empty, or the cap is hit
*/
function savePreset(store, preset) {
if (!preset || typeof preset !== 'object') {
throw new Error('Preset must be an object');
}
const name = typeof preset.name === 'string' ? preset.name.trim().slice(0, MAX_NAME_LENGTH) : '';
if (!name) {
throw new Error('Preset name is required');
}
const options = preset.options && typeof preset.options === 'object' ? preset.options : {};
const format = typeof preset.format === 'string' ? preset.format : null;
const presets = loadPresets(store);
const id = typeof preset.id === 'string' && preset.id ? preset.id : createPresetId();
const entry = { id, name, format, options };
const index = presets.findIndex((existing) => existing.id === id);
if (index >= 0) {
presets[index] = entry;
} else {
if (presets.length >= MAX_PRESETS) {
throw new Error(`Cannot store more than ${MAX_PRESETS} export presets`);
}
presets.push(entry);
}
store.set(PRESET_KEY, presets);
return presets;
}
/**
* Remove the preset with the given id (idempotent).
* @param {{get: Function, set: Function}} store settings store
* @param {string} presetId
* @returns {Array} the updated preset list (also persisted)
*/
function deletePreset(store, presetId) {
const presets = loadPresets(store).filter((preset) => preset.id !== presetId);
store.set(PRESET_KEY, presets);
return presets;
}
function createPresetId() {
return `preset-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
}
module.exports = { loadPresets, savePreset, deletePreset, PRESET_KEY, MAX_PRESETS };