A fast, local-first "redirection engine" for !bang users with a few extra features ^-^
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: allow saving and restoring config

dunkirk.sh 07cd2dd8 74f772ee

verified
+73
+73
src/main.ts
··· 199 199 <button class="clear-history">Clear History</button> 200 200 </div> 201 201 </div> 202 + <div class="settings-section"> 203 + <h3>Settings Import/Export</h3> 204 + <p class="help-text">Export your settings and custom bangs to a file, or import them from a previously saved file.</p> 205 + <div style="display: flex; gap: 8px; margin-top: 8px;"> 206 + <button class="export-settings">Export Settings</button> 207 + <button class="import-settings">Import Settings</button> 208 + <input type="file" id="import-file" accept=".json" style="display: none;"> 209 + </div> 210 + </div> 202 211 </div> 203 212 </div> 204 213 </div> ··· 243 252 removeBangs: app.querySelectorAll<HTMLButtonElement>(".remove-bang"), 244 253 bangSearch: app.querySelector<HTMLInputElement>("#bang-search"), 245 254 bangSearchResults: app.querySelector<HTMLDivElement>("#bang-search-results"), 255 + exportSettings: app.querySelector<HTMLButtonElement>(".export-settings"), 256 + importSettings: app.querySelector<HTMLButtonElement>(".import-settings"), 257 + importFile: app.querySelector<HTMLInputElement>("#import-file"), 246 258 } as const; 247 259 248 260 // Validate all elements exist ··· 550 562 ) 551 563 .join(""); 552 564 }, 150); 565 + }); 566 + 567 + validatedElements.exportSettings.addEventListener("click", () => { 568 + const settingsData = { 569 + defaultBang: storage.get(CONSTANTS.LOCAL_STORAGE_KEYS.DEFAULT_BANG), 570 + customBangs: storage.get(CONSTANTS.LOCAL_STORAGE_KEYS.CUSTOM_BANGS), 571 + historyEnabled: storage.get(CONSTANTS.LOCAL_STORAGE_KEYS.HISTORY_ENABLED), 572 + exportDate: new Date().toISOString(), 573 + }; 574 + 575 + const dataStr = JSON.stringify(settingsData, null, 2); 576 + const dataBlob = new Blob([dataStr], { type: "application/json" }); 577 + const url = URL.createObjectURL(dataBlob); 578 + const link = document.createElement("a"); 579 + link.href = url; 580 + link.download = `unduckified-settings-${new Date().toISOString().split("T")[0]}.json`; 581 + link.click(); 582 + URL.revokeObjectURL(url); 583 + }); 584 + 585 + validatedElements.importSettings.addEventListener("click", () => { 586 + validatedElements.importFile.click(); 587 + }); 588 + 589 + validatedElements.importFile.addEventListener("change", async (event) => { 590 + const file = (event.target as HTMLInputElement).files?.[0]; 591 + if (!file) return; 592 + 593 + try { 594 + const text = await file.text(); 595 + const settingsData = JSON.parse(text); 596 + 597 + if (settingsData.defaultBang) { 598 + storage.set( 599 + CONSTANTS.LOCAL_STORAGE_KEYS.DEFAULT_BANG, 600 + settingsData.defaultBang, 601 + ); 602 + } 603 + if (settingsData.customBangs) { 604 + storage.set( 605 + CONSTANTS.LOCAL_STORAGE_KEYS.CUSTOM_BANGS, 606 + settingsData.customBangs, 607 + ); 608 + } 609 + if (settingsData.historyEnabled !== undefined) { 610 + storage.set( 611 + CONSTANTS.LOCAL_STORAGE_KEYS.HISTORY_ENABLED, 612 + settingsData.historyEnabled, 613 + ); 614 + } 615 + 616 + alert("Settings imported successfully!"); 617 + if (!prefersReducedMotion) 618 + setTimeout(() => { 619 + window.location.reload(); 620 + }, 375); 621 + else window.location.reload(); 622 + } catch (error) { 623 + alert("Failed to import settings. Please check the file format."); 624 + console.error("Import error:", error); 625 + } 553 626 }); 554 627 } 555 628