Allow selective data export and import

This commit is contained in:
Jordan Wages 2025-07-07 22:07:00 -05:00
commit c7333482ce
3 changed files with 81 additions and 0 deletions

45
options/dataTransfer.js Normal file
View file

@ -0,0 +1,45 @@
"use strict";
const storage = (globalThis.messenger ?? browser).storage;
const KEY_GROUPS = {
settings: [
'endpoint',
'templateName',
'customTemplate',
'customSystemPrompt',
'aiParams',
'debugLogging',
'htmlToMarkdown',
'stripUrlParams',
'altTextImages',
'collapseWhitespace'
],
rules: ['aiRules'],
cache: ['aiCache']
};
function collectKeys(categories = Object.keys(KEY_GROUPS)) {
return categories.flatMap(cat => KEY_GROUPS[cat] || []);
}
export async function exportData(categories) {
const data = await storage.local.get(collectKeys(categories));
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'sortana-export.json';
document.body.appendChild(a);
a.click();
a.remove();
URL.revokeObjectURL(url);
}
export async function importData(file, categories) {
const text = await file.text();
const parsed = JSON.parse(text);
const data = {};
for (const key of collectKeys(categories)) {
if (key in parsed) data[key] = parsed[key];
}
await storage.local.set(data);
}

View file

@ -207,6 +207,23 @@
</tbody> </tbody>
</table> </table>
<button class="button is-danger" id="clear-cache" type="button">Clear Cache</button> <button class="button is-danger" id="clear-cache" type="button">Clear Cache</button>
<div class="field mt-4">
<label class="label">Data categories</label>
<div class="control">
<label class="checkbox mr-3"><input class="transfer-category" type="checkbox" value="settings" checked> Settings</label>
<label class="checkbox mr-3"><input class="transfer-category" type="checkbox" value="rules" checked> Rules</label>
<label class="checkbox"><input class="transfer-category" type="checkbox" value="cache" checked> Cache</label>
</div>
</div>
<div class="field is-grouped mt-4">
<p class="control">
<button class="button" id="export-data" type="button">Export Data</button>
</p>
<p class="control">
<button class="button" id="import-data" type="button">Import Data</button>
<input class="is-hidden" type="file" id="import-file" accept="application/json">
</p>
</div>
</div> </div>
</div> </div>
</section> </section>

View file

@ -2,6 +2,7 @@ document.addEventListener('DOMContentLoaded', async () => {
const storage = (globalThis.messenger ?? browser).storage; const storage = (globalThis.messenger ?? browser).storage;
const logger = await import(browser.runtime.getURL('logger.js')); const logger = await import(browser.runtime.getURL('logger.js'));
const AiClassifier = await import(browser.runtime.getURL('modules/AiClassifier.js')); const AiClassifier = await import(browser.runtime.getURL('modules/AiClassifier.js'));
const dataTransfer = await import(browser.runtime.getURL('options/dataTransfer.js'));
const defaults = await storage.local.get([ const defaults = await storage.local.get([
'endpoint', 'endpoint',
'templateName', 'templateName',
@ -395,6 +396,24 @@ document.addEventListener('DOMContentLoaded', async () => {
await AiClassifier.clearCache(); await AiClassifier.clearCache();
cacheCountEl.textContent = '0'; cacheCountEl.textContent = '0';
}); });
function selectedCategories() {
return [...document.querySelectorAll('.transfer-category:checked')].map(el => el.value);
}
document.getElementById('export-data').addEventListener('click', () => {
dataTransfer.exportData(selectedCategories());
});
const importInput = document.getElementById('import-file');
document.getElementById('import-data').addEventListener('click', () => importInput.click());
importInput.addEventListener('change', async () => {
if (importInput.files.length) {
await dataTransfer.importData(importInput.files[0], selectedCategories());
location.reload();
}
});
initialized = true; initialized = true;
document.getElementById('save').addEventListener('click', async () => { document.getElementById('save').addEventListener('click', async () => {