Commit graph

2960 commits

Author SHA1 Message Date
Adam Brown
66ae689cdb Add visual draggable indicator 2026-06-13 12:42:19 -07:00
Adam Brown
fde00d28f6 Fix compose edit stream 2026-06-13 11:50:43 -07:00
Adam Brown
caeab9c94c Fix timeline event re-ordering 2026-06-13 11:41:40 -07:00
Adam Brown
b40358b4cb Upgrade logback_classic 2026-06-13 11:35:14 -07:00
Adam Brown
01727b7ee5 Clear server info on new account create 2026-06-13 11:10:04 -07:00
Wavesonics
138bce747b Fix flakey test 2026-06-13 02:51:03 -07:00
Wavesonics
105d82661e Locale test 2026-06-13 02:46:40 -07:00
Adam Brown
4b912129b4
Replace fluidsonic (#597) 2026-06-13 02:38:21 -07:00
Adam Brown
b0e16d6e63 Update text editor 2026-06-13 01:57:35 -07:00
Adam Brown
a97b0e682f
New Crowdin updates (#596)
* New translations strings_account_settings.xml (French)

[ci skip]

* New translations strings_account_settings.xml (Spanish)

[ci skip]

* New translations strings_account_settings.xml (German)

[ci skip]

* New translations strings_account_settings.xml (Italian)

[ci skip]

* New translations strings_account_settings.xml (Ukrainian)

[ci skip]

* New translations strings_account_settings.xml (Chinese Simplified)

[ci skip]

* New translations strings_account_settings.xml (Portuguese, Brazilian)

[ci skip]

* New translations strings_timeline.xml (French)

[ci skip]

* New translations strings_timeline.xml (Spanish)

[ci skip]

* New translations strings_timeline.xml (German)

[ci skip]

* New translations strings_timeline.xml (Italian)

[ci skip]

* New translations strings_timeline.xml (Ukrainian)

[ci skip]

* New translations strings_timeline.xml (Chinese Simplified)

[ci skip]

* New translations strings_timeline.xml (Portuguese, Brazilian)

[ci skip]

* New translations messages_en.properties (French)

[ci skip]

* New translations messages_en.properties (Spanish)

[ci skip]

* New translations messages_en.properties (German)

[ci skip]

* New translations messages_en.properties (Italian)

[ci skip]

* New translations messages_en.properties (Ukrainian)

[ci skip]

* New translations messages_en.properties (Chinese Simplified)

[ci skip]

* New translations messages_en.properties (Portuguese, Brazilian)

[ci skip]
2026-06-13 01:53:24 -07:00
Adam Brown
e77ef954d3 Remove timeline sorting
It didn't really make sense anyway
2026-06-12 23:12:45 -07:00
Wavesonics
82a4422a19 Merge branch 'editor-feature' into develop 2026-06-12 23:10:50 -07:00
Wavesonics
8da7b008a5 Render emphasis in the applied preview and un-justify on phones
An accepted reword or insert now renders its replacement's inline
emphasis in the author's preview, so *word* reads as italic like the
rest of the manuscript instead of showing literal asterisks. The mobile
breakpoint targeted a class that is never rendered; point it at the real
paragraph class so justified text left-aligns on phones.
2026-06-12 22:46:00 -07:00
Wavesonics
6b6cd25601 Fix reIdScene crash for scenes without drafts
SceneDraftsDatasource.reIdScene moved the scene's drafts directory
unconditionally, so re-IDing a scene with no drafts threw
FileNotFoundException and failed the entire sync during ID conflict
resolution.
2026-06-12 22:40:15 -07:00
Wavesonics
bbfa674248 Fix review-link copy and the group select-all on the story page
The no-email copy-link button now falls back to execCommand when
navigator.clipboard is unavailable (plain-HTTP self-hosting), instead of
throwing and copying nothing. Each scene-group's select-all button now
flips to 'Clear all' once its whole subtree is checked — matching the
master toggle — so it no longer silently clears those scenes while still
labelled 'Select all'.
2026-06-12 22:38:00 -07:00
Wavesonics
13b1600830 Close only the topmost dialog on Escape, and keep popups on-screen
Escape now dismisses just the frontmost layer, so closing a dialog opened
over an in-progress suggestion popup no longer also throws away the
popup's typed text. Popups and tooltips clamp by their actual half-width
instead of a fixed margin, so on a narrow phone they stay inside the
manuscript card rather than spilling off the edge. A selection endpoint
landing in inserted ink (which carries no source offset) snaps to the
nearest real text node, so text adjacent to existing ink can still be
selected.
2026-06-12 22:30:00 -07:00
Wavesonics
1e9e9a86cd Harden the review save and decision round-trips
A single in-flight guard serializes accept/reject and the bulk
accept-all/reject-all, so a fast second click can't race and leave the
chip showing the opposite of what the server stored. The commit dialog
can no longer be dismissed mid-POST into a detached node — it locks until
the response lands, then any dismissal reloads. Suggestion saves capture
their scene before awaiting (a response can't land in the wrong scene if
the editor switches mid-flight) and keep the popup open with the typed
text when a save fails. Failed saves, edits, and commits now show the
server's localized message instead of a generic toast.
2026-06-12 22:22:00 -07:00
Wavesonics
ca167f9161 Scope author routes to their project and cut redundant review queries
The author review page, status POST, and commit POST now verify the
review actually belongs to the project named in the URL, so one of a
user's reviews can't render or be acted on under a different project's
URL. The public reviewer page does a single token lookup reused for the
owner redirect, the friendly error kinds, and the open transition (was
two or three hashes), and the panel cards fetch every review's scene
progress in one grouped query instead of one per card.
2026-06-12 22:14:00 -07:00
Wavesonics
99a18c59f4 Build emailed links from a configured public URL
Links in review and password-reset emails came from the request's
scheme/host/port, so a reverse proxy produced internal links and anyone
holding a review token could poison the author's notification email with
a forged Host header. A new optional publicUrl server config wins over
the request origin. Also: review dates format with an explicit English
locale instead of the JVM default, the invite's plaintext body is built
line-by-line (a note at column zero defeated trimIndent and left literal
tabs everywhere), and the two review mailers share one bundle/template
helper with the same English fallback the pages use.
2026-06-12 22:05:00 -07:00
Wavesonics
f088234429 Fall back to English for untranslated strings
The review keys exist only in the English bundle so far, so any visitor
with a non-English locale got a MissingResourceException 500 on the
review page — and on the story page once a review existed. Missing keys
now fall back to English, and withMessages overlays the locale's
translations on the full English key set so untranslated keys render in
English instead of as blank template vars.
2026-06-12 21:56:00 -07:00
Wavesonics
c08fca05e6 Drop the redundant review token index
The token column's UNIQUE constraint already creates one.
2026-06-12 21:48:00 -07:00
Wavesonics
6ba3aa5c47 Fix apply-order corruption around insert carets
A delete or reword spanning an existing insert caret corrupted the
committed text (the right-to-left splice applied the inner insert first,
then the outer edit swallowed it and resurrected part of the deleted
range) — both sides now reject the pair at creation. Same-position
inserts commit in their display order via an id tiebreaker, and inserted
words get a trailing smart space so they don't glue to the next word.
2026-06-12 21:45:00 -07:00
Wavesonics
45869be189 Close review and sync-session race windows
Sync session slots are now claimed atomically (claimSession replaces the
check-then-create in begin-sync and review minting), the internal session
is kept alive across long mint loops, and every review status UPDATE is
guarded on the current status so a racing revoke, submit, or resolve can't
be silently overwritten. Commits run entirely inside the exclusive session
with a re-check, mint all drafts before overwriting any scene, and a failed
request creation cleans up after itself.
2026-06-12 21:37:00 -07:00
Wavesonics
33413cdc68 Cap review free-text lengths and always require a comment's text
Generous server-side limits on the anonymous token endpoints (replacement
10k, reason 5k) and the request dialog (note 2k, label 100), mirrored as
maxlength on the inputs. Comments must carry text at creation, matching
the existing edit rule.
2026-06-12 21:26:00 -07:00
Wavesonics
2627d5a32e Welcome the editor on first open, and give dead links friendly pages
The very first time a review link is opened, a welcome explains what this
is and how to mark up — with the key point highlighted: nothing reaches
the author until Submit Revisions. Expired and revoked links now get
specific pages that name the author and story (and say what to do next)
instead of the generic broken-link card.
2026-06-12 21:18:00 -07:00
Wavesonics
4e721bb4da Let the editor mark scenes done, and show reading progress to the author
A 'Mark scene as done' toggle at the end of each manuscript records the
editor's progress (and advances to the next unread scene); done scenes get
a check in the scene rail. While the review is out, the author's panel
card shows 'N of M scenes read' with a slim progress bar.
2026-06-12 21:05:00 -07:00
Wavesonics
602733767d Standardize review-feature language on Editor and Author
The person marking up the manuscript is an editor, not a reviewer (in
publishing, a reviewer critiques the finished book). The exchange itself
stays an editorial review, since it is the author who reviews the edits.
The submitted-notice email now reads like publishing too: your manuscript
is back, with the editor's edits.
2026-06-12 20:55:00 -07:00
Wavesonics
260bca17f2 Show the reviewer's note as a hover tooltip over ink, with quick accept/reject
In author mode, hovering a suggestion's ink pops a small card above it:
the reviewer's reasoning, plus Accept/Reject (Resolve for comments) while
the suggestion is undecided. The tooltip survives the mouse crossing onto
it via a short grace timer, and its arrow bridges the hover gap. Decided
ink shows just the note; the undo stays on the gutter card.
2026-06-12 20:47:00 -07:00
Wavesonics
59421851e9 Forward a logged-in author from their reviewer link to their own review
Following the capability link with the author's own session no longer
impersonates the reviewer (or marks the request opened): it redirects to
the author review page once submitted, or to the story page while the
review is still out. Other sessions and anonymous visitors get the
reviewer editor as before.
2026-06-12 20:36:00 -07:00
Wavesonics
e884b9ccda Add the author review page: accept and reject suggestions, apply revisions
The story-page panel links submitted reviews to a session-authenticated
page that reuses the manuscript renderer: pending suggestions keep their
pen ink while decided ones preview the outcome (accepted rewords read as
tinted real text, accepted deletes ghost out, rejected inks fade away).
Cards carry Accept/Reject buttons with undoable status chips plus
accept-all/reject-all, and Apply Revisions confirms with a tally before
reporting the per-scene outcome. Submitting a review now also emails the
author a link to the page, and the reviewer's submit dialog names them.
2026-06-12 20:27:00 -07:00
Wavesonics
ace064549f Add server-side review resolution: suggestion statuses and commit
Authors can accept/reject each suggestion (resolve for comments) while a
review is submitted. Committing applies the accepted edits to each scene
snapshot, mints the revised text as a new draft inside the exclusive sync
session, and overwrites the working scene through the synchronizer when it
still matches the snapshot; diverged scenes keep the draft for Draft
Compare. The request is then marked resolved.
2026-06-12 20:18:00 -07:00
Wavesonics
8d0d03219f Load the Caveat handwriting font for review ink 2026-06-12 20:09:00 -07:00
Wavesonics
a17fa50aa3 Fix doubled apostrophes in submit dialog strings
These are interpolated client-side, not through MessageFormat, so the
escaped quotes rendered literally.
2026-06-12 19:57:00 -07:00
Wavesonics
108090c789 Replace the submit confirm() with a styled dialog, reword to Submit Revisions
The native confirm broke the manuscript feel. Submitting now opens the
design-system dialog with the suggestion tally and a Submit & Lock
action, dismissible by cancel, overlay click, or Escape. The masthead
button reads Submit Revisions.
2026-06-12 19:50:00 -07:00
Wavesonics
39339938c2 Open a reason dialog when suggesting a delete
Delete committed instantly from the selection menu, so there was no
moment to say why. It now opens the reason-only form (reason optional,
save always enabled), matching the other suggestion types.
2026-06-12 19:39:00 -07:00
Wavesonics
f42a93ffd6 Make tapping a suggestion select and scroll, with editing behind a pencil icon
Tapping ink now highlights and scrolls to its gutter card and vice
versa, instead of jumping straight into editing; each card grows a
pencil action that opens the prefilled edit popup, next to the existing
remove action.

Also fixes selections that silently produced no popup: real drag
selections often end on element nodes at styled-run boundaries, and the
handler only accepted text-node endpoints. Endpoints now resolve to the
nearest text position, and the mouseup listener moved to the document so
releasing a drag outside the manuscript still offers the menu.
2026-06-12 19:31:00 -07:00
Wavesonics
a9e7ffac8c Draw one continuous strike per physical line of struck text
The tiled strike background restarted its angled stroke every 140px,
which read as a sawtooth of short disjoint lines across long deletions.
Stretch a single line across each box fragment instead, with
box-decoration-break: clone repainting it per wrapped line — every
physical line of struck text gets exactly one straight pen stroke.
2026-06-12 19:24:00 -07:00
Wavesonics
c6ebb57a25 Overlay the insert caret instead of splicing it into the text
Position the caret absolutely from a collapsed Range's rect, like the
selection popup, rather than splitting a text node to host an inline
marker. The manuscript DOM stays untouched, which removes the
source-offset re-registration the split needed.
2026-06-12 19:12:00 -07:00
Wavesonics
8b16ae4947 Show a caret in the text while suggesting an insert
Clicking to insert (or choosing Insert after from a selection) now
renders a blinking green caret at the exact spot the insertion will
land, removed when the popup closes or the suggestion saves. Splitting
a text node to place the caret registers the tail at its own source
offset so selection mapping stays exact.
2026-06-12 19:04:00 -07:00
Wavesonics
9ea9f21307 Cross-highlight a suggestion's ink and gutter card on hover
Hovering inked text rings and lifts its gutter card; hovering a card
rings the ink in the manuscript. Both use the suggestion's own type
colour via a CSS variable, so a red delete links in red and a blue
reword in blue.
2026-06-12 18:56:00 -07:00
Wavesonics
467db758cc Let reviewers edit a suggestion by tapping it
Tapping a suggestion's ink in the manuscript or its gutter card now
opens the form popup over the ink, prefilled with the current values:
rewords and inserts edit their replacement and reason, comments edit
their text, and deletes get a reason field they previously had no way
to set. Saving posts to a new tokenized update endpoint that keeps the
anchors untouched, validates per type, and refuses edits once the
review is submitted.
2026-06-12 18:47:00 -07:00
Wavesonics
0adaf05a1b Emit reviewer UI strings as real JSON instead of Mustache-escaped text
Building the strings island by hand in the template ran every string
through Mustache's HTML escaping, so apostrophes surfaced as ' in
confirm dialogs, and a quote in any translation would have broken the
JSON. The server now encodes the strings map with the JSON serializer
like the review data. Both islands also escape '<' so user content
containing </script> can't break out of the script element.
2026-06-12 18:38:00 -07:00
Wavesonics
582b9055b7 Shorten suggestion save button label so it doesn't wrap 2026-06-12 18:31:00 -07:00
Wavesonics
8f46baf269 Render styled text in the reviewer editor and confirm suggestion removal
The manuscript now renders inline markdown emphasis (bold/italic, both
marker styles, nested) instead of showing raw markers. A conservative
parser in review-logic.js turns each paragraph into styled display runs
that remember their source positions; rendered text nodes register their
source offset so selections over styled text still anchor suggestions in
raw source space, exactly as before. Pen-ink strikes and comment
highlights span styled runs, and gutter quotes show marker-free text.

Removing a suggestion now asks for confirmation first.
2026-06-12 18:26:00 -07:00
Wavesonics
75557eb9da Widen reviewer editor to use the full viewport
The editor's three-column layout (scene nav, manuscript, gutter) was
constrained by the 860px container sized for the old read-only page, so
the manuscript was squeezed to a narrow ribbon. Widen the container and
manuscript so the prose reads at a comfortable measure with the nav and
gutter flanking it.
2026-06-12 18:07:00 -07:00
Wavesonics
a8eccfb5aa Build the reviewer markup editor
The tokenized review page is now an interactive editor. The server sends
each scene's paragraphs and existing suggestions as a JSON island;
review.js renders the manuscript with pen-ink markup (varied strike
angles, handwritten replacements, yellow-marker comments) and a gutter
of suggestion cards. Selecting text offers reword/delete/insert/comment;
clicking places a caret to insert. Each suggestion autosaves through new
tokenized endpoints (create/delete/submit) that validate offsets against
the immutable snapshot, reject overlaps, anchor to paragraph-local
offsets, and flip the request to In Progress on first edit. Submit locks
the review. Suggestion logic is the unit-tested review-logic.js; the
endpoints have e2e coverage.
2026-06-12 17:55:00 -07:00
Wavesonics
7211b452ae Add JS unit-test harness, CI gate, and pen-ink strike logic
Introduces review-logic.js: DOM-free suggestion logic (segment
splitting, overlap guards, smart-spacing, applying accepted edits) plus
deterministic per-suggestion pen-ink strike styling, loadable both as a
browser script and a Node module. Unit-tested with Node's built-in
runner via a new :server:jsTest Gradle task, wired into CI as an
explicit gate with Node set up in the build job.
2026-06-12 17:30:00 -07:00
Wavesonics
7e3e3cbbff Make review dialog opaque so the page footer can't bleed through
The shared dialog component uses a translucent fill and a 50%-opacity
actions bar (a frosted-glass effect). The review dialog is tall enough
to overlap the page footer when scrolled, so that translucency let the
footer's icons and dark background show through. Give this dialog and
its actions bar fully opaque backgrounds; other dialogs are short and
centered, so they keep the frosted look.
2026-06-12 00:57:22 -07:00
Wavesonics
e2d4b6fd52 Add project-wide scene select-all and fix dialog horizontal overflow
Adds a sticky 'All scenes' master toggle at the top of the scene picker
that selects or clears every scene at once, with its label flipping to
'Clear all' when everything is selected. Fixes a horizontal scrollbar
that appeared once the dialog body gained a vertical scrollbar: the
two-column email/label row needed min-width:0 so its inputs shrink with
the dialog instead of overflowing.
2026-06-12 00:47:52 -07:00
Wavesonics
660e9a8592 Fix review dialog Select All, width, and apostrophe rendering
The dialog's stopPropagation kept clicks from reaching the
document-level delegated handlers, so the group toggles never fired;
closeReviewDialog already ignores bubbled inner clicks, making it
redundant. Dialog width now 92% like other dialogs instead of touching
the viewport edges. Argless i18n keys aren't run through MessageFormat,
so doubled apostrophes rendered literally.
2026-06-12 00:36:54 -07:00