mirror of
https://github.com/PR0M3TH3AN/VoxVera.git
synced 2026-10-27 09:40:40 +00:00
Import/export a flyer design as a portable JSON file
Replaces the raw "Export event JSON" button with Import design / Export
design in the editor. A design file is content + relays only —
deliberately identity-free (no npub, naddr, event id, or signature, all
of which are derived at publish), so it can be handed to anyone and
published under their own key.
Format: { type: "voxvera_flyer_design", version: 1, flyer: <payload>,
relays: [] }. Export serializes the current form as-is (draft-friendly).
Import reads the picked file (capped at 256 KB), accepts the wrapper
format or a bare voxvera_flyer payload, then shows a confirm modal before
overwriting the editor; on confirm it fills the form and restores relays.
Imported content stays untrusted: the renderer escapes every field and
strict validation still runs at publish.
Adds 8 localized strings across all 14 languages (i18n parity passes).
Tests: export round-trip (identity-free assertions), import-then-confirm,
cancel-leaves-unchanged, and invalid-file rejection, across all 6
engines. Full suite 336 passing.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
a4cb8dffcb
commit
b344f92be7
5 files changed
+345
-13
No files matched your search
+16
-1
@@ -142,9 +142,13 @@
|
||||
</label>
|
||||
<div class="actions">
|
||||
<button type="button" id="preview-editor">Preview</button>
|
||||
<button type="button" id="export-event">Export event JSON</button>
|
||||
<button type="button" id="import-design">Import design</button>
|
||||
<button type="button" id="export-design">Export design</button>
|
||||
<button type="submit" id="publish-event">Sign and publish</button>
|
||||
</div>
|
||||
<!-- Hidden picker for "Import design"; the design file is content +
|
||||
relays only (no identity/naddr), so it is portable across keys. -->
|
||||
<input type="file" id="import-design-input" accept="application/json,.json" hidden>
|
||||
<!-- Shown only when a flyer you authored is loaded from an event. -->
|
||||
<div id="loaded-flyer-actions" class="actions" hidden>
|
||||
<button type="button" id="republish-flyer" class="secondary-button" data-i18n-key="republish">Re-publish to relays</button>
|
||||
@@ -235,6 +239,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="import-modal" class="modal-overlay" hidden>
|
||||
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="import-modal-title">
|
||||
<h2 id="import-modal-title" data-i18n-key="import_confirm_title">Import this design?</h2>
|
||||
<p id="import-modal-message" data-i18n-key="import_confirm_message">This replaces the flyer currently in the editor.</p>
|
||||
<div class="modal-actions">
|
||||
<button type="button" id="import-modal-cancel" class="secondary-button" data-i18n-key="cancel">Cancel</button>
|
||||
<button type="button" id="import-modal-confirm" class="secondary-button" data-i18n-key="import_button">Import</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="locales.js"></script>
|
||||
<script src="vendor/qrcode-generator/qrcode.js"></script>
|
||||
<script src="vendor/nostr-tools/nostr.bundle.js"></script>
|
||||
|
||||
+217
-12
@@ -119,7 +119,8 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
};
|
||||
const BUTTON_LABELS = {
|
||||
"preview-editor": "preview",
|
||||
"export-event": "export_event",
|
||||
"import-design": "import_design",
|
||||
"export-design": "export_design",
|
||||
"publish-event": "publish_event",
|
||||
"copy-viewer-config": "copy_config",
|
||||
"print-preview": "print_preview",
|
||||
@@ -260,6 +261,14 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
nip46_bad_uri: "لا يبدو هذا رابط bunker://.",
|
||||
nip46_timeout: "لم يستجب الموقّع عن بُعد. افتح التطبيق وحاول مرة أخرى.",
|
||||
nip46_failed: "تعذّر الاتصال بالموقّع عن بُعد.",
|
||||
import_design: "استيراد التصميم",
|
||||
export_design: "تصدير التصميم",
|
||||
import_confirm_title: "هل تريد استيراد هذا التصميم؟",
|
||||
import_confirm_message: "سيحل هذا محل المنشور الموجود حاليًا في المحرر.",
|
||||
import_button: "استيراد",
|
||||
design_imported: "تم استيراد التصميم.",
|
||||
design_exported: "تم تصدير التصميم.",
|
||||
invalid_design_file: "هذا الملف ليس تصميم VoxVera صالحًا.",
|
||||
no_anon_npub: "لا يوجد npub مجهول بعد",
|
||||
anon_npub_ready: "npub مجهول جاهز",
|
||||
npub_generated: "تم إنشاء npub مجهول:",
|
||||
@@ -362,6 +371,14 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
nip46_bad_uri: "Das sieht nicht nach einem bunker://-Link aus.",
|
||||
nip46_timeout: "Der externe Signierer hat nicht geantwortet. Öffne die App und versuche es erneut.",
|
||||
nip46_failed: "Verbindung zum externen Signierer fehlgeschlagen.",
|
||||
import_design: "Design importieren",
|
||||
export_design: "Design exportieren",
|
||||
import_confirm_title: "Dieses Design importieren?",
|
||||
import_confirm_message: "Dies ersetzt den aktuell im Editor befindlichen Flyer.",
|
||||
import_button: "Importieren",
|
||||
design_imported: "Design importiert.",
|
||||
design_exported: "Design exportiert.",
|
||||
invalid_design_file: "Diese Datei ist kein gültiges VoxVera-Design.",
|
||||
no_anon_npub: "Noch kein anonymes npub",
|
||||
anon_npub_ready: "Anonymes npub bereit",
|
||||
npub_generated: "Anonymes npub erzeugt:",
|
||||
@@ -487,6 +504,14 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
nip46_bad_uri: "That doesn't look like a bunker:// link.",
|
||||
nip46_timeout: "The remote signer didn't respond. Open your signer app and try again.",
|
||||
nip46_failed: "Couldn't connect to the remote signer.",
|
||||
import_design: "Import design",
|
||||
export_design: "Export design",
|
||||
import_confirm_title: "Import this design?",
|
||||
import_confirm_message: "This replaces the flyer currently in the editor.",
|
||||
import_button: "Import",
|
||||
design_imported: "Design imported.",
|
||||
design_exported: "Design exported.",
|
||||
invalid_design_file: "That file isn't a valid VoxVera design.",
|
||||
},
|
||||
es: {
|
||||
back: "Volver",
|
||||
@@ -566,6 +591,14 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
nip46_bad_uri: "Esto no parece un enlace bunker://.",
|
||||
nip46_timeout: "El firmante remoto no respondió. Abre la app e inténtalo de nuevo.",
|
||||
nip46_failed: "No se pudo conectar con el firmante remoto.",
|
||||
import_design: "Importar diseño",
|
||||
export_design: "Exportar diseño",
|
||||
import_confirm_title: "¿Importar este diseño?",
|
||||
import_confirm_message: "Esto reemplaza el folleto que está actualmente en el editor.",
|
||||
import_button: "Importar",
|
||||
design_imported: "Diseño importado.",
|
||||
design_exported: "Diseño exportado.",
|
||||
invalid_design_file: "Ese archivo no es un diseño de VoxVera válido.",
|
||||
no_anon_npub: "Aún no hay npub anónimo",
|
||||
anon_npub_ready: "npub anónimo listo",
|
||||
npub_generated: "npub anónimo generado:",
|
||||
@@ -668,6 +701,14 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
nip46_bad_uri: "این یک پیوند bunker:// به نظر نمیرسد.",
|
||||
nip46_timeout: "امضاکننده از راه دور پاسخ نداد. برنامه را باز کنید و دوباره تلاش کنید.",
|
||||
nip46_failed: "اتصال به امضاکننده از راه دور ممکن نشد.",
|
||||
import_design: "وارد کردن طرح",
|
||||
export_design: "خروجی گرفتن از طرح",
|
||||
import_confirm_title: "این طرح وارد شود؟",
|
||||
import_confirm_message: "این جایگزین اعلامیهای میشود که اکنون در ویرایشگر است.",
|
||||
import_button: "وارد کردن",
|
||||
design_imported: "طرح وارد شد.",
|
||||
design_exported: "طرح خروجی گرفته شد.",
|
||||
invalid_design_file: "این فایل یک طرح معتبر VoxVera نیست.",
|
||||
no_anon_npub: "هنوز npub ناشناس وجود ندارد",
|
||||
anon_npub_ready: "npub ناشناس آماده است",
|
||||
npub_generated: "npub ناشناس ساخته شد:",
|
||||
@@ -770,6 +811,14 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
nip46_bad_uri: "Cela ne ressemble pas à un lien bunker://.",
|
||||
nip46_timeout: "Le signataire distant n'a pas répondu. Ouvrez l'app et réessayez.",
|
||||
nip46_failed: "Impossible de se connecter au signataire distant.",
|
||||
import_design: "Importer le modèle",
|
||||
export_design: "Exporter le modèle",
|
||||
import_confirm_title: "Importer ce modèle ?",
|
||||
import_confirm_message: "Cela remplace le tract actuellement dans l'éditeur.",
|
||||
import_button: "Importer",
|
||||
design_imported: "Modèle importé.",
|
||||
design_exported: "Modèle exporté.",
|
||||
invalid_design_file: "Ce fichier n'est pas un modèle VoxVera valide.",
|
||||
no_anon_npub: "Aucun npub anonyme pour le moment",
|
||||
anon_npub_ready: "npub anonyme pret",
|
||||
npub_generated: "npub anonyme genere :",
|
||||
@@ -872,6 +921,14 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
nip46_bad_uri: "זה לא נראה כמו קישור bunker://.",
|
||||
nip46_timeout: "החותם המרוחק לא הגיב. פתח את האפליקציה ונסה שוב.",
|
||||
nip46_failed: "לא ניתן להתחבר לחותם המרוחק.",
|
||||
import_design: "ייבוא עיצוב",
|
||||
export_design: "ייצוא עיצוב",
|
||||
import_confirm_title: "לייבא את העיצוב הזה?",
|
||||
import_confirm_message: "פעולה זו מחליפה את הפלייר שנמצא כעת בעורך.",
|
||||
import_button: "ייבוא",
|
||||
design_imported: "העיצוב יובא.",
|
||||
design_exported: "העיצוב יוצא.",
|
||||
invalid_design_file: "הקובץ הזה אינו עיצוב VoxVera תקין.",
|
||||
no_anon_npub: "עדיין אין npub אנונימי",
|
||||
anon_npub_ready: "npub אנונימי מוכן",
|
||||
npub_generated: "npub אנונימי נוצר:",
|
||||
@@ -974,6 +1031,14 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
nip46_bad_uri: "यह bunker:// लिंक जैसा नहीं लगता।",
|
||||
nip46_timeout: "रिमोट साइनर ने उत्तर नहीं दिया। ऐप खोलें और पुनः प्रयास करें।",
|
||||
nip46_failed: "रिमोट साइनर से कनेक्ट नहीं हो सका।",
|
||||
import_design: "डिज़ाइन आयात करें",
|
||||
export_design: "डिज़ाइन निर्यात करें",
|
||||
import_confirm_title: "यह डिज़ाइन आयात करें?",
|
||||
import_confirm_message: "यह संपादक में मौजूद फ़्लायर को बदल देगा।",
|
||||
import_button: "आयात करें",
|
||||
design_imported: "डिज़ाइन आयात किया गया।",
|
||||
design_exported: "डिज़ाइन निर्यात किया गया।",
|
||||
invalid_design_file: "वह फ़ाइल मान्य VoxVera डिज़ाइन नहीं है।",
|
||||
no_anon_npub: "अभी कोई अनाम npub नहीं",
|
||||
anon_npub_ready: "अनाम npub तैयार",
|
||||
npub_generated: "अनाम npub बनाया गया:",
|
||||
@@ -1076,6 +1141,14 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
nip46_bad_uri: "これは bunker:// リンクではないようです。",
|
||||
nip46_timeout: "リモート署名が応答しませんでした。アプリを開いて再試行してください。",
|
||||
nip46_failed: "リモート署名に接続できませんでした。",
|
||||
import_design: "デザインを読み込む",
|
||||
export_design: "デザインを書き出す",
|
||||
import_confirm_title: "このデザインを読み込みますか?",
|
||||
import_confirm_message: "現在エディターにあるフライヤーが置き換えられます。",
|
||||
import_button: "読み込む",
|
||||
design_imported: "デザインを読み込みました。",
|
||||
design_exported: "デザインを書き出しました。",
|
||||
invalid_design_file: "そのファイルは有効な VoxVera デザインではありません。",
|
||||
no_anon_npub: "匿名npubはまだありません",
|
||||
anon_npub_ready: "匿名npub準備完了",
|
||||
npub_generated: "匿名npubを生成しました:",
|
||||
@@ -1178,6 +1251,14 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
nip46_bad_uri: "Isso não parece um link bunker://.",
|
||||
nip46_timeout: "O assinador remoto não respondeu. Abra o app e tente novamente.",
|
||||
nip46_failed: "Não foi possível conectar ao assinador remoto.",
|
||||
import_design: "Importar design",
|
||||
export_design: "Exportar design",
|
||||
import_confirm_title: "Importar este design?",
|
||||
import_confirm_message: "Isto substitui o panfleto atualmente no editor.",
|
||||
import_button: "Importar",
|
||||
design_imported: "Design importado.",
|
||||
design_exported: "Design exportado.",
|
||||
invalid_design_file: "Esse arquivo não é um design válido do VoxVera.",
|
||||
no_anon_npub: "Ainda nao ha npub anonimo",
|
||||
anon_npub_ready: "npub anonimo pronto",
|
||||
npub_generated: "npub anonimo gerado:",
|
||||
@@ -1280,6 +1361,14 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
nip46_bad_uri: "Это не похоже на ссылку bunker://.",
|
||||
nip46_timeout: "Удалённый подписант не ответил. Откройте приложение и попробуйте снова.",
|
||||
nip46_failed: "Не удалось подключиться к удалённому подписанту.",
|
||||
import_design: "Импорт дизайна",
|
||||
export_design: "Экспорт дизайна",
|
||||
import_confirm_title: "Импортировать этот дизайн?",
|
||||
import_confirm_message: "Это заменит листовку, открытую сейчас в редакторе.",
|
||||
import_button: "Импорт",
|
||||
design_imported: "Дизайн импортирован.",
|
||||
design_exported: "Дизайн экспортирован.",
|
||||
invalid_design_file: "Этот файл не является корректным дизайном VoxVera.",
|
||||
no_anon_npub: "Анонимного npub пока нет",
|
||||
anon_npub_ready: "Анонимный npub готов",
|
||||
npub_generated: "Анонимный npub создан:",
|
||||
@@ -1382,6 +1471,14 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
nip46_bad_uri: "Hiki hakionekani kama kiungo cha bunker://.",
|
||||
nip46_timeout: "Mtia-saini wa mbali hakujibu. Fungua programu na ujaribu tena.",
|
||||
nip46_failed: "Imeshindwa kuunganisha na mtia-saini wa mbali.",
|
||||
import_design: "Leta muundo",
|
||||
export_design: "Hamisha muundo",
|
||||
import_confirm_title: "Ulete muundo huu?",
|
||||
import_confirm_message: "Hii itachukua nafasi ya kipeperushi kilichopo sasa kwenye kihariri.",
|
||||
import_button: "Leta",
|
||||
design_imported: "Muundo umeletwa.",
|
||||
design_exported: "Muundo umehamishwa.",
|
||||
invalid_design_file: "Faili hilo si muundo halali wa VoxVera.",
|
||||
no_anon_npub: "Hakuna npub isiyojulikana bado",
|
||||
anon_npub_ready: "npub isiyojulikana iko tayari",
|
||||
npub_generated: "npub isiyojulikana imetengenezwa:",
|
||||
@@ -1484,6 +1581,14 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
nip46_bad_uri: "Bu bir bunker:// bağlantısına benzemiyor.",
|
||||
nip46_timeout: "Uzak imzalayıcı yanıt vermedi. Uygulamayı açıp tekrar deneyin.",
|
||||
nip46_failed: "Uzak imzalayıcıya bağlanılamadı.",
|
||||
import_design: "Tasarımı içe aktar",
|
||||
export_design: "Tasarımı dışa aktar",
|
||||
import_confirm_title: "Bu tasarım içe aktarılsın mı?",
|
||||
import_confirm_message: "Bu, düzenleyicide şu anda bulunan el ilanının yerini alır.",
|
||||
import_button: "İçe aktar",
|
||||
design_imported: "Tasarım içe aktarıldı.",
|
||||
design_exported: "Tasarım dışa aktarıldı.",
|
||||
invalid_design_file: "Bu dosya geçerli bir VoxVera tasarımı değil.",
|
||||
no_anon_npub: "Henüz anonim npub yok",
|
||||
anon_npub_ready: "Anonim npub hazır",
|
||||
npub_generated: "Anonim npub oluşturuldu:",
|
||||
@@ -1586,6 +1691,14 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
nip46_bad_uri: "这看起来不是 bunker:// 链接。",
|
||||
nip46_timeout: "远程签名器无响应。请打开应用并重试。",
|
||||
nip46_failed: "无法连接到远程签名器。",
|
||||
import_design: "导入设计",
|
||||
export_design: "导出设计",
|
||||
import_confirm_title: "导入此设计?",
|
||||
import_confirm_message: "这将替换编辑器中当前的传单。",
|
||||
import_button: "导入",
|
||||
design_imported: "设计已导入。",
|
||||
design_exported: "设计已导出。",
|
||||
invalid_design_file: "该文件不是有效的 VoxVera 设计。",
|
||||
no_anon_npub: "还没有匿名 npub",
|
||||
anon_npub_ready: "匿名 npub 已就绪",
|
||||
npub_generated: "已生成匿名 npub:",
|
||||
@@ -3386,6 +3499,94 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
// ---- Flyer design import / export (portable .json file) -------------------
|
||||
// A design file is the flyer's content plus the relay list — deliberately
|
||||
// identity-free (no npub, naddr, event id, or signature). Those are derived at
|
||||
// publish time, so a design can be handed to anyone and published under THEIR
|
||||
// key. Format: { type, version, flyer: <payload>, relays: [wss://…] }.
|
||||
const DESIGN_FILE_TYPE = "voxvera_flyer_design";
|
||||
const DESIGN_FILE_MAX_BYTES = 262144; // 256 KB — a design is tiny; reject blobs.
|
||||
let pendingImport = null; // { flyer, relays } held while the confirm modal is open.
|
||||
|
||||
function exportDesign() {
|
||||
// Export whatever is in the form (draft-friendly: no strict validation).
|
||||
const file = {
|
||||
type: DESIGN_FILE_TYPE,
|
||||
version: 1,
|
||||
flyer: rawPayloadFromForm(),
|
||||
relays: parseRelays(el("editor-relays").value)
|
||||
};
|
||||
downloadJson(`${file.flyer.folder_name || "voxvera"}-voxvera-design.json`, file);
|
||||
el("publish-status").textContent = nostrLabel("design_exported", currentUiLang());
|
||||
}
|
||||
|
||||
// Pull the flyer payload + relays out of a parsed design file. Lenient: accepts
|
||||
// the wrapper format and a bare voxvera_flyer payload (e.g. a copied config).
|
||||
function readDesignFile(data) {
|
||||
if (!data || typeof data !== "object") return null;
|
||||
if (data.type === DESIGN_FILE_TYPE && data.flyer && typeof data.flyer === "object") {
|
||||
const relays = Array.isArray(data.relays)
|
||||
? data.relays.filter((r) => typeof r === "string" && /^wss:\/\//i.test(r))
|
||||
: [];
|
||||
return { flyer: data.flyer, relays };
|
||||
}
|
||||
if (data.type === "voxvera_flyer") {
|
||||
return { flyer: data, relays: [] };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function openImportModal() {
|
||||
const modal = el("import-modal");
|
||||
if (modal) modal.hidden = false;
|
||||
}
|
||||
function closeImportModal() {
|
||||
const modal = el("import-modal");
|
||||
if (modal) modal.hidden = true;
|
||||
}
|
||||
|
||||
// Step 1: read + parse + validate the picked file, then ask to confirm.
|
||||
function importDesignFromFile(fileObj) {
|
||||
if (!fileObj) return;
|
||||
if (fileObj.size > DESIGN_FILE_MAX_BYTES) {
|
||||
el("publish-status").textContent = nostrLabel("invalid_design_file", currentUiLang());
|
||||
return;
|
||||
}
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
let parsed;
|
||||
try { parsed = JSON.parse(String(reader.result || "")); } catch (_) { parsed = null; }
|
||||
const design = readDesignFile(parsed);
|
||||
if (!design) {
|
||||
el("publish-status").textContent = nostrLabel("invalid_design_file", currentUiLang());
|
||||
return;
|
||||
}
|
||||
pendingImport = design;
|
||||
openImportModal();
|
||||
};
|
||||
reader.onerror = () => {
|
||||
el("publish-status").textContent = nostrLabel("invalid_design_file", currentUiLang());
|
||||
};
|
||||
reader.readAsText(fileObj);
|
||||
}
|
||||
|
||||
// Step 2: apply the held design after the user confirms the overwrite.
|
||||
function applyPendingImport() {
|
||||
if (!pendingImport) { closeImportModal(); return; }
|
||||
const { flyer, relays } = pendingImport;
|
||||
pendingImport = null;
|
||||
closeImportModal();
|
||||
// Imported content is untrusted; the lenient form-fill escapes on preview and
|
||||
// strict validation still runs at publish (raw HTML / length caps / schemes).
|
||||
fillEditorFromConfig(flyer);
|
||||
if (relays.length) el("editor-relays").value = relays.join("\n");
|
||||
flyerSource = "custom";
|
||||
loadedEventConfig = null;
|
||||
refreshLoadedFlyerActions();
|
||||
renderPreview(normalizePayload(rawPayloadFromForm()));
|
||||
el("publish-status").textContent = nostrLabel("design_imported", currentUiLang());
|
||||
}
|
||||
|
||||
function renderRelayResults(results) {
|
||||
return `<div class="relay-results">${results.map((item) => `
|
||||
<div>
|
||||
@@ -3693,22 +3894,26 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
}
|
||||
});
|
||||
|
||||
el("export-event").addEventListener("click", () => {
|
||||
el("export-design").addEventListener("click", () => {
|
||||
try {
|
||||
const payload = buildPayloadFromForm();
|
||||
const relays = parseRelays(el("editor-relays").value);
|
||||
const identity = activeIdentity();
|
||||
const poster = withPosterUrl(payload, identity, relays);
|
||||
const event = buildUnsignedEvent(poster.payload);
|
||||
setRealOutput("author-npub-output", identity.npub);
|
||||
setRealOutput("naddr-output", poster.naddr);
|
||||
setRealOutput("poster-url-output", poster.posterUrl);
|
||||
el("event-json-output").value = JSON.stringify(event, null, 2);
|
||||
downloadJson(`${poster.payload.folder_name}-voxvera-event-unsigned.json`, event);
|
||||
exportDesign();
|
||||
} catch (error) {
|
||||
el("publish-status").textContent = error.message;
|
||||
}
|
||||
});
|
||||
el("import-design").addEventListener("click", () => {
|
||||
const input = el("import-design-input");
|
||||
if (input) { input.value = ""; input.click(); }
|
||||
});
|
||||
el("import-design-input").addEventListener("change", (event) => {
|
||||
const file = event.target && event.target.files && event.target.files[0];
|
||||
importDesignFromFile(file);
|
||||
});
|
||||
el("import-modal-cancel").addEventListener("click", () => {
|
||||
pendingImport = null;
|
||||
closeImportModal();
|
||||
});
|
||||
el("import-modal-confirm").addEventListener("click", applyPendingImport);
|
||||
|
||||
el("editor-form").addEventListener("submit", async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
Reference in new issue
Block a user