From d1c93209662c354b5cca08b6cf6d1e8a5bff97e4 Mon Sep 17 00:00:00 2001 From: gator Date: Thu, 17 Sep 2026 02:22:45 +0000 Subject: [PATCH] book.html: a real Choose file button, and drop the paste box "Drop the manuscript here or click to choose a file" is a drop zone pretending to be a button - it reads as an instruction rather than a control, and on a laptop nobody drags a file from anywhere. There is now an actual button. The zone still accepts a drop, because that costs nothing once the button exists. The paste box is gone. It was there for a manuscript that would not upload cleanly, but a book is 100,000 words and nobody pastes that; keeping it meant offering a route that only works for the case it was not for. Word and PDF cover it, and a PDF that turns out to be a scan is refused with the reason. --- book.html | 77 ++++++++++++++++++++++--------------------------------- 1 file changed, 30 insertions(+), 47 deletions(-) diff --git a/book.html b/book.html index 2965f3f..84ec8b5 100644 --- a/book.html +++ b/book.html @@ -20,8 +20,11 @@ .state.reading{color:var(--muted)} .state.failed{color:#e0685f;border-color:#e0685f} .why{color:#e0685f;font-size:13px;margin-top:4px} - .drop.on{border-color:var(--accent)} .tiny{font-size:12px;color:var(--muted)} + .pick{display:flex;gap:14px;align-items:center;flex-wrap:wrap; + border:1px dashed var(--line);border-radius:12px;padding:18px} + .pick.on{border-color:var(--accent);border-style:solid} + .pick .or{color:var(--muted);font-size:13px} .writers{display:flex;flex-wrap:wrap;gap:10px;margin-top:8px} .wr{display:flex;gap:8px;align-items:center;border:1px solid var(--line); border-radius:10px;padding:9px 12px;cursor:pointer} @@ -44,20 +47,13 @@

1 · The manuscript

- - A .docx, a text .pdf, or a .txt, up to 6MB. A scanned PDF has no text in it and will be refused — upload the original document instead. -
-

Drop the manuscript here

-

or click to choose a file

+ + A Word .docx or a .pdf, up to 6MB. A scanned PDF has no text in it and will be refused — upload the original document instead. +
+ + or drag the manuscript onto this panel
- -
- -
- - For anything that will not upload cleanly — Google Docs, Scrivener, a PDF that is really a scan. - -
+

No manuscripts yet.

@@ -70,13 +66,13 @@
You know this book better than any of them will. Say what the trailer has to carry, which scenes matter, which it must not give away, who the audience is, and the tone. This outranks the house defaults. - +

3 · Who writes it

- Each one reads the whole book and writes its own trailer, with its own cast. Tick at least two if you want them to critique each other. The lead is the one that will write the revision after the reviews come back. + Each one reads the whole book and writes its own trailer, with its own cast. Tick at least two if you want them to critique each other. The lead is the one that will write the revision after the reviews come back — click a ticked name to make it the lead.
@@ -110,7 +106,7 @@ function readFileB64(file) { if (file.size > MAX_DOC_BYTES) { reject(new Error("That file is " + (file.size / 1048576).toFixed(1) + "MB and the limit is 6MB. Upload the .docx rather than an " - + "illustrated PDF, or paste the text in instead.")); + + "illustrated PDF.")); return; } var reader = new FileReader(); @@ -197,16 +193,20 @@ function readFileB64(file) { // ---------------------------------------------------------------- upload - var drop = document.getElementById("drop"); + var pick = document.getElementById("pick"); + var pickMsg = document.getElementById("pickmsg"); var fileInput = document.getElementById("file"); - drop.addEventListener("click", function () { fileInput.click(); }); + + document.getElementById("choose").addEventListener("click", function () { + fileInput.click(); + }); ["dragenter", "dragover"].forEach(function (k) { - drop.addEventListener(k, function (e) { e.preventDefault(); drop.classList.add("on"); }); + pick.addEventListener(k, function (e) { e.preventDefault(); pick.classList.add("on"); }); }); ["dragleave", "drop"].forEach(function (k) { - drop.addEventListener(k, function (e) { e.preventDefault(); drop.classList.remove("on"); }); + pick.addEventListener(k, function (e) { e.preventDefault(); pick.classList.remove("on"); }); }); - drop.addEventListener("drop", function (e) { + pick.addEventListener("drop", function (e) { var f = e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files[0]; if (f) send(f); }); @@ -217,40 +217,23 @@ function readFileB64(file) { async function send(file) { hideMsg("msg"); - drop.innerHTML = '

Reading ' + esc(file.name) + '…

'; + pickMsg.textContent = "reading " + file.name + "…"; var doc; try { doc = await readFileB64(file); } - catch (err) { resetDrop(); showMsg("msg", err.message); return; } - drop.innerHTML = '

Uploading ' + esc(file.name) + '…

'; + catch (err) { resetPick(); showMsg("msg", err.message); return; } + pickMsg.textContent = "uploading " + file.name + " (" + + (doc.bytes / 1048576).toFixed(1) + "MB)…"; var r = await api("upload_book", { filename: doc.name, data_b64: doc.b64 }); - resetDrop(); + resetPick(); if (!r.ok) { showMsg("msg", r.error || "That upload did not go through."); return; } PICKED = null; loadBooks(); } - function resetDrop() { - drop.innerHTML = '

Drop the manuscript here

' - + '

or click to choose a file

'; + function resetPick() { + pickMsg.textContent = "or drag the manuscript onto this panel"; } - document.getElementById("usepaste").addEventListener("click", async function () { - var btn = this; - var text = document.getElementById("paste").value; - if (text.trim().length < 2000) { - showMsg("msg", "That is " + text.trim().length + " characters. Paste the manuscript, not a summary."); - return; - } - hideMsg("msg"); - btn.disabled = true; btn.textContent = "Saving…"; - var r = await api("paste_book", { text: text, filename: "pasted.txt" }); - btn.disabled = false; btn.textContent = "Use this text"; - if (!r.ok) { showMsg("msg", r.error || "That did not save."); return; } - document.getElementById("paste").value = ""; - PICKED = null; - loadBooks(); - }); - // ---------------------------------------------------------------- writers function paintWriters() { @@ -292,7 +275,7 @@ function readFileB64(file) { // Writing is the cheap half. Drawing the storyboards is priced separately, // per writer, once you have read the scripts. el.innerHTML = n + " writer" + (n > 1 ? "s" : "") - + " · about $" + (n * 0.5).toFixed(2) + " to write" + + " · about $" + (n * 0.6).toFixed(2) + " to write" + " · storyboards are priced separately once you have read them"; }