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