import { useState } from 'react' export function downloadJSON(obj, filename) { const blob = new Blob([JSON.stringify(obj, null, 2)], { type: 'application/json' }) const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = filename a.click() URL.revokeObjectURL(url) } export function pickJSONFile() { return new Promise((resolve, reject) => { const input = document.createElement('input') input.type = 'file' input.accept = '.json,application/json' input.onchange = () => { const file = input.files[0] if (!file) return reject(new Error('No file selected')) const reader = new FileReader() reader.onload = () => { try { resolve(JSON.parse(reader.result)) } catch { reject(new Error('Not valid JSON')) } } reader.onerror = () => reject(new Error('Could not read file')) reader.readAsText(file) } input.click() }) } // Unique ${Placeholder} names, in order of first appearance, across the given texts. export function extractPlaceholders(...texts) { const names = [] for (const text of texts) { for (const match of (text || '').matchAll(/\$\{([^}]+)\}/g)) { const name = match[1].trim() if (name && !names.includes(name)) names.push(name) } } return names } export function PlaceholderModal({ title, names, onSubmit, onCancel }) { const [values, setValues] = useState(Object.fromEntries(names.map((n) => [n, '']))) const submit = (e) => { e.preventDefault() onSubmit(values) } return (