compose.html raw

   1  <!DOCTYPE html>
   2  <html lang="en">
   3  <head>
   4  <meta charset="UTF-8">
   5  <meta name="viewport" content="width=device-width, initial-scale=1.0">
   6  <title>Marmot Email Bridge - Compose</title>
   7  <style>
   8  :root {
   9    --bg: #1a1a2e;
  10    --surface: #16213e;
  11    --border: #0f3460;
  12    --text: #e4e4e4;
  13    --muted: #8888aa;
  14    --accent: #e94560;
  15    --accent-hover: #ff6b81;
  16    --success: #2ecc71;
  17    --input-bg: #0f1729;
  18  }
  19  
  20  * { margin: 0; padding: 0; box-sizing: border-box; }
  21  
  22  body {
  23    font-family: 'Courier New', monospace;
  24    background: var(--bg);
  25    color: var(--text);
  26    min-height: 100vh;
  27    padding: 2rem;
  28  }
  29  
  30  .container {
  31    max-width: 640px;
  32    margin: 0 auto;
  33  }
  34  
  35  h1 {
  36    font-size: 1.4rem;
  37    color: var(--accent);
  38    margin-bottom: 0.5rem;
  39  }
  40  
  41  .subtitle {
  42    color: var(--muted);
  43    font-size: 0.85rem;
  44    margin-bottom: 2rem;
  45  }
  46  
  47  .form-group {
  48    margin-bottom: 1rem;
  49  }
  50  
  51  label {
  52    display: block;
  53    color: var(--muted);
  54    font-size: 0.8rem;
  55    margin-bottom: 0.3rem;
  56    text-transform: uppercase;
  57    letter-spacing: 0.05em;
  58  }
  59  
  60  input, textarea {
  61    width: 100%;
  62    padding: 0.6rem 0.8rem;
  63    background: var(--input-bg);
  64    border: 1px solid var(--border);
  65    color: var(--text);
  66    font-family: inherit;
  67    font-size: 0.9rem;
  68    border-radius: 4px;
  69  }
  70  
  71  input:focus, textarea:focus {
  72    outline: none;
  73    border-color: var(--accent);
  74  }
  75  
  76  textarea {
  77    min-height: 200px;
  78    resize: vertical;
  79  }
  80  
  81  .actions {
  82    display: flex;
  83    gap: 1rem;
  84    margin-top: 1.5rem;
  85  }
  86  
  87  button {
  88    padding: 0.7rem 1.5rem;
  89    font-family: inherit;
  90    font-size: 0.9rem;
  91    border: 1px solid var(--border);
  92    border-radius: 4px;
  93    cursor: pointer;
  94    transition: all 0.2s;
  95  }
  96  
  97  .btn-primary {
  98    background: var(--accent);
  99    color: white;
 100    border-color: var(--accent);
 101  }
 102  
 103  .btn-primary:hover {
 104    background: var(--accent-hover);
 105  }
 106  
 107  .btn-secondary {
 108    background: transparent;
 109    color: var(--text);
 110  }
 111  
 112  .btn-secondary:hover {
 113    background: var(--surface);
 114  }
 115  
 116  .preview {
 117    margin-top: 2rem;
 118    padding: 1rem;
 119    background: var(--surface);
 120    border: 1px solid var(--border);
 121    border-radius: 4px;
 122    font-size: 0.85rem;
 123    white-space: pre-wrap;
 124    word-break: break-all;
 125    display: none;
 126  }
 127  
 128  .preview.visible {
 129    display: block;
 130  }
 131  
 132  .preview-label {
 133    color: var(--muted);
 134    font-size: 0.75rem;
 135    text-transform: uppercase;
 136    margin-bottom: 0.5rem;
 137  }
 138  
 139  .status {
 140    margin-top: 1rem;
 141    padding: 0.5rem;
 142    border-radius: 4px;
 143    font-size: 0.85rem;
 144    display: none;
 145  }
 146  
 147  .status.success {
 148    display: block;
 149    color: var(--success);
 150    border: 1px solid var(--success);
 151  }
 152  
 153  .status.error {
 154    display: block;
 155    color: var(--accent);
 156    border: 1px solid var(--accent);
 157  }
 158  
 159  .info {
 160    margin-top: 2rem;
 161    padding: 1rem;
 162    background: var(--surface);
 163    border-left: 3px solid var(--accent);
 164    font-size: 0.8rem;
 165    color: var(--muted);
 166    line-height: 1.5;
 167  }
 168  </style>
 169  </head>
 170  <body>
 171  <div class="container">
 172    <h1>Marmot Email Bridge</h1>
 173    <p class="subtitle">Compose an outbound email via Nostr DM</p>
 174  
 175    <div class="form-group">
 176      <label for="to">To</label>
 177      <input type="text" id="to" placeholder="recipient@example.com">
 178    </div>
 179  
 180    <div class="form-group">
 181      <label for="cc">CC (optional)</label>
 182      <input type="text" id="cc" placeholder="cc@example.com">
 183    </div>
 184  
 185    <div class="form-group">
 186      <label for="subject">Subject</label>
 187      <input type="text" id="subject" placeholder="Your subject line">
 188    </div>
 189  
 190    <div class="form-group">
 191      <label for="body">Message</label>
 192      <textarea id="body" placeholder="Write your message here..."></textarea>
 193    </div>
 194  
 195    <div class="actions">
 196      <button class="btn-primary" onclick="copyToClipboard()">Copy as DM</button>
 197      <button class="btn-secondary" onclick="togglePreview()">Preview</button>
 198      <button class="btn-secondary" onclick="clearForm()">Clear</button>
 199    </div>
 200  
 201    <div class="status" id="status"></div>
 202  
 203    <div class="preview" id="preview">
 204      <div class="preview-label">DM Preview (paste this to the bridge)</div>
 205      <div id="preview-content"></div>
 206    </div>
 207  
 208    <div class="info">
 209      <strong>How to use:</strong> Fill in the fields, click "Copy as DM", then paste
 210      the formatted message as a DM to the bridge's Nostr identity. The bridge will
 211      parse the headers and send it as a standard email. Separate multiple recipients
 212      with commas or spaces.
 213    </div>
 214  </div>
 215  
 216  <script>
 217  // Read pre-populated values from URL fragment
 218  // Format: #to=alice@example.com&subject=Re: Hello
 219  (function() {
 220    const hash = window.location.hash.substring(1);
 221    if (!hash) return;
 222  
 223    const params = new URLSearchParams(hash);
 224  
 225    if (params.has('to'))      document.getElementById('to').value = params.get('to');
 226    if (params.has('cc'))      document.getElementById('cc').value = params.get('cc');
 227    if (params.has('subject')) document.getElementById('subject').value = params.get('subject');
 228    if (params.has('body'))    document.getElementById('body').value = params.get('body');
 229  })();
 230  
 231  function buildDM() {
 232    const to = document.getElementById('to').value.trim();
 233    const cc = document.getElementById('cc').value.trim();
 234    const subject = document.getElementById('subject').value.trim();
 235    const body = document.getElementById('body').value.trim();
 236  
 237    if (!to) {
 238      showStatus('At least one recipient is required.', 'error');
 239      return null;
 240    }
 241  
 242    let dm = '';
 243    dm += 'To: ' + to + '\n';
 244    if (cc) dm += 'Cc: ' + cc + '\n';
 245    if (subject) dm += 'Subject: ' + subject + '\n';
 246    dm += '\n';
 247    dm += body;
 248  
 249    return dm;
 250  }
 251  
 252  async function copyToClipboard() {
 253    const dm = buildDM();
 254    if (!dm) return;
 255  
 256    try {
 257      await navigator.clipboard.writeText(dm);
 258      showStatus('Copied to clipboard! Paste this as a DM to the bridge.', 'success');
 259    } catch (err) {
 260      // Fallback for non-HTTPS contexts
 261      const textarea = document.createElement('textarea');
 262      textarea.value = dm;
 263      document.body.appendChild(textarea);
 264      textarea.select();
 265      document.execCommand('copy');
 266      document.body.removeChild(textarea);
 267      showStatus('Copied to clipboard! Paste this as a DM to the bridge.', 'success');
 268    }
 269  }
 270  
 271  function togglePreview() {
 272    const dm = buildDM();
 273    if (!dm) return;
 274  
 275    const preview = document.getElementById('preview');
 276    const content = document.getElementById('preview-content');
 277  
 278    if (preview.classList.contains('visible')) {
 279      preview.classList.remove('visible');
 280    } else {
 281      content.textContent = dm;
 282      preview.classList.add('visible');
 283    }
 284  }
 285  
 286  function clearForm() {
 287    document.getElementById('to').value = '';
 288    document.getElementById('cc').value = '';
 289    document.getElementById('subject').value = '';
 290    document.getElementById('body').value = '';
 291    document.getElementById('preview').classList.remove('visible');
 292    document.getElementById('status').style.display = 'none';
 293  }
 294  
 295  function showStatus(msg, type) {
 296    const el = document.getElementById('status');
 297    el.textContent = msg;
 298    el.className = 'status ' + type;
 299  }
 300  </script>
 301  </body>
 302  </html>
 303