hh-shipments 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -18,7 +18,7 @@
18
18
  "test": "node test/run-tests.js && node test/run-carriers-tests.js"
19
19
  },
20
20
  "type": "commonjs",
21
- "version": "0.3.0",
21
+ "version": "0.3.1",
22
22
  "bin": {
23
23
  "hh-shipments": "server.js"
24
24
  },
package/public/app.js CHANGED
@@ -180,21 +180,36 @@ $('photo-input').addEventListener('change', async (e) => {
180
180
  await handlePhotoFile(f);
181
181
  });
182
182
 
183
- // Paste a screenshot straight from the clipboard (iOS: tap the paste zone, long-press → Paste)
184
- $('tab-photo').addEventListener('paste', async (e) => {
183
+ // Shared: pull image files out of a paste event's clipboard data
184
+ function pastedImageFiles(e) {
185
185
  const dt = e.clipboardData;
186
- if (!dt) return;
187
186
  const files = [];
187
+ if (!dt) return files;
188
188
  if (dt.items) for (const it of dt.items) {
189
189
  if (it.type && it.type.startsWith('image/')) { const f = it.getAsFile(); if (f) files.push(f); }
190
190
  }
191
191
  if (!files.length && dt.files) for (const f of dt.files) {
192
192
  if (f.type && f.type.startsWith('image/')) files.push(f);
193
193
  }
194
- if (!files.length) return; // let text pastes behave normally
194
+ return files;
195
+ }
196
+
197
+ // Paste-text tab: pasting a screenshot runs OCR and extracts tracking numbers;
198
+ // pasting plain text behaves normally
199
+ $('tab-paste').addEventListener('paste', async (e) => {
200
+ const files = pastedImageFiles(e);
201
+ if (!files.length) return; // text paste — let it land in the textarea
195
202
  e.preventDefault();
196
- $('paste-zone').innerHTML = '📋 <span class="muted">or tap here, then paste your screenshot</span>';
197
- for (const f of files) await handlePhotoFile(f);
203
+ for (const f of files) {
204
+ try {
205
+ const text = await ocrFile(f, $('ocr-status-paste'));
206
+ const fd = new FormData();
207
+ fd.append('image', f);
208
+ const up = await fetch('/api/intake/image', { method: 'POST', body: fd });
209
+ if (!up.ok) throw new Error('upload failed');
210
+ await runQuick(text, $('quick-result'));
211
+ } catch (err) { $('quick-result').innerHTML = `<p class="error">${esc(err.message)}</p>`; }
212
+ }
198
213
  });
199
214
 
200
215
  async function loadPending() {
@@ -314,7 +329,6 @@ function urlB64(s) {
314
329
  function activateTab(name) {
315
330
  document.querySelectorAll('.tab').forEach((t) => t.classList.toggle('active', t.dataset.tab === name));
316
331
  ['paste', 'photo', 'manual'].forEach((n) => ($('tab-' + n).hidden = n !== name));
317
- if (name === 'photo') { try { $('paste-zone').focus({ preventScroll: true }); } catch (_) {} }
318
332
  }
319
333
  document.querySelectorAll('.tab').forEach((t) => (t.onclick = () => activateTab(t.dataset.tab)));
320
334
  document.querySelectorAll('#tabbar button').forEach((b) => {
package/public/index.html CHANGED
@@ -61,13 +61,13 @@
61
61
  <button class="tab" data-tab="manual">Manual</button>
62
62
  </div>
63
63
  <div id="tab-paste" class="tabpanel">
64
- <textarea id="paste-text" rows="6" placeholder="Paste the seller's message here…"></textarea>
64
+ <textarea id="paste-text" rows="6" placeholder="Paste the seller's message or a tracking-number screenshot here…"></textarea>
65
+ <div id="ocr-status-paste" class="muted"></div>
65
66
  <button id="btn-quick" class="primary">Extract &amp; add</button>
66
67
  <div id="quick-result"></div>
67
68
  </div>
68
69
  <div id="tab-photo" class="tabpanel" hidden>
69
70
  <label class="filebtn">📷 Choose screenshot<input id="photo-input" type="file" accept="image/*" hidden></label>
70
- <div id="paste-zone" class="pastezone" contenteditable="true" spellcheck="false" aria-label="Paste a screenshot here">📋 <span class="muted">or tap here, then paste your screenshot</span></div>
71
71
  <div id="ocr-status" class="muted"></div>
72
72
  <div id="photo-result"></div>
73
73
  <h3>Pending screenshots</h3>
package/public/styles.css CHANGED
@@ -42,8 +42,6 @@ h2 { font-size: 17px; margin: 18px 0 8px; }
42
42
  .tab.active { background: var(--blue); color: #fff; }
43
43
  .tabpanel { margin-bottom: 12px; }
44
44
  .filebtn { display: block; text-align: center; background: #e5e5ea; border-radius: 12px; padding: 16px; font-weight: 600; cursor: pointer; }
45
- .pastezone { margin-top: 10px; border: 2px dashed #c7c7cc; border-radius: 12px; padding: 18px; text-align: center; font-size: 15px; min-height: 24px; outline: none; caret-color: transparent; }
46
- .pastezone:focus { border-color: #007aff; background: #f2f8ff; }
47
45
  .statusline { font-size: 17px; font-weight: 600; margin: 8px 0; }
48
46
  .timeline { border-left: 2px solid #d1d1d6; margin-left: 6px; padding-left: 14px; }
49
47
  .timeline .ev { margin-bottom: 12px; font-size: 14px; }