Commit graph

88 commits

Author SHA1 Message Date
igor
2b5cf597bd feat(editor): initial TinyMCE 8 integration (WIP)
- Install TinyMCE 8 via npm
- Replace CodeMirror6 + contenteditable preview with TinyMCE
- Add TinyMCE initialization and markdown conversion (Turndown)
- Update editor fragment template with TinyMCE textarea
- Add TinyMCE toolbar helper functions
- Keep existing toolbar buttons for Joplin-specific actions

Remaining work:
- Complete app.js migration (77 references to old editor)
- Update tests to match new editor structure
- Handle vault lock/unlock with TinyMCE
- Handle search/highlight with TinyMCE
- Handle expander/autocomplete with TinyMCE
- Mobile editor integration
- Remove old CM6/preview code
2026-06-28 00:24:24 +12:00
igor
67b8aec7d1 fix(spacing): unify blank-line margins with paragraph spacing and underline links in markdown mode
Some checks failed
Build and push Joplock image / build-and-push (push) Has been cancelled
- Replace fixed height:1em on .md-blank-line with margin:0.75em to match
  paragraph margins; neutralize load-bearing <br> inside blank-line divs
  via CSS so spacing is purely margin-driven
- Unify browser-inserted blank paragraph rules (p:empty, div:not([class]))
  to use margin:0.75em instead of height:1em
- Add text-decoration:underline to tags.url highlight style and .cm-link/.cm-url
  CSS rule so hyperlinks are always underlined in markdown mode
2026-06-27 23:00:11 +12:00
igor
b5a077274c Open links on Ctrl/Cmd+click in markdown mode
Some checks are pending
Build and push Joplock image / build-and-push (push) Waiting to run
2026-06-26 23:26:06 +12:00
igor
5d40d83493 fix(editor): active line highlight setting, soft-break round-trip, font/spacing fixes
- Add highlightActiveLine bool setting (default true) wired through
  settingsService → api allowedKeys → settings UI → _joplockConfig → app.js
- Fix CM theme fontSize to use var(--font-size-note) instead of hardcoded 14px
- Fix CM lineHeight to 1.7 to match .editor-preview
- Fix mobile CSS: remove hardcoded font-size/line-height from .cm-host .cm-content
- Fix Turndown br option: '' instead of '\n' to prevent double-spacing on
  render-mode edits (Turndown appends its own \n, so br:'\n' produced \n\n)
- Guard flushSave() syncPV() call behind _previewDirty to prevent Turndown
  corruption during autosave when user hasn't edited in preview mode
- Mark ensureEditableAfterPre injected nodes with data-pv-trail and strip them
  in htmlToMarkdown clone to prevent extra blank lines after trailing code blocks
- Fix setEditorMode('markdown') to use content comparison for cmSetVal decision
- Increase .editor-preview p margin to 0.75em for better paragraph spacing
- Add 5 new round-trip tests (426 total passing)
2026-06-26 10:03:47 +12:00
igor
344fe25b18 Add regression tests for modal escape dismissal 2026-06-25 13:01:07 +12:00
igor
02b7fe74cf fix: mobile markdown mode shows blank editor on note open
The mobile htmx:afterSettle handler was destroying _cmView right after
the global afterSettle handler created it via initEditorPanel/initCM.

Event flow when opening a note on mobile:
1. htmx:afterSwap fires -> destroys any prior _cmView (correct)
2. Global htmx:afterSettle fires -> initEditorPanel() -> initCM() -> _cmView created
3. Mobile htmx:afterSettle fires -> _cmView.destroy() kills the just-created
   CM view -> initEditorPanel() returns early (guard already set) -> blank

The destroy in the mobile afterSettle was redundant (afterSwap already
handles it before afterSettle fires) and destructive in markdown-default
mode where initCM runs during afterSettle phase.

Switching to preview then back to markdown worked because setEditorMode
re-creates _cmView via initCM when _cmView is null.
2026-06-25 10:26:24 +12:00
igor
91f3863b8d feat(sync): detect and handle concurrent note edits across browsers
Some checks failed
Build and push Joplock image / build-and-push (push) Has been cancelled
When the same note is open in multiple browsers, changes made in one
browser are now detected and surfaced in the other.

Server:
- itemService.noteFreshnessByUserIdAndJopId: cheap single-row probe
  returning {updatedTime, deletedTime} without decoding the note body
- GET /api/web/notes/:id/freshness: serves the probe, marked
  isHeartbeat:true so polling does not reset the session idle timer
- PUT /fragments/editor/:id conflict response now sends X-Note-Conflict:1
  header so the client can distinguish it from a real save success

Client:
- checkNoteFreshness() runs on every 30s connectivity ping tick and
  immediately on visibilitychange (tab focus)
- Clean editor: silently reloads the note fragment
- Dirty editor: shows a banner (same pattern as search-nav-bar) with
  'Use server version' and 'Overwrite with mine' buttons
- Deleted note: shows a 'deleted in another window' banner
- _activeEditorIsDirty() uses durable signals (_previewDirty,
  _pvSyncTimer, title contenteditable vs hidden input, formHash vs
  _savedHash) not the transient autosave-status text
- Banner is server-rendered inside the form (hidden attribute), reuses
  search-nav-bar CSS and buttons — no dynamic DOM creation
- htmx:afterRequest skips snapshotHash/setSaved when X-Note-Conflict:1
  is present, keeping the editor dirty and the conflict UI visible
- Service worker bumped to v25
2026-06-23 17:32:03 +12:00
igor
2b9143dc0b fix(preview): normalize empty paragraph height in rendered editor
Browser-inserted <p><br></p> and <div><br></div> created by Enter in
contenteditable mode rendered at full line-height (~25px), while the
same markdown re-rendered server-side via /fragments/preview produced
.md-blank-line divs at 1em (~15px). Result: visible spacing drift
between live edits and post-toggle rendering.

Add CSS rules to give empty/<br>-only paragraphs and unclassed divs
the same 1em height as .md-blank-line, so contenteditable rendering
stays visually consistent with the canonical renderer.
2026-06-23 16:31:35 +12:00
igor
ffb4cfcc20 Make login cookie persistent to survive browser process eviction on suspend
Some checks failed
Build and push Joplock image / build-and-push (push) Has been cancelled
2026-06-19 22:50:45 +12:00
igor
e40cd13375 Fix session timeout by removing hardcoded 12h TTL and making login cookies session-scoped
Some checks failed
Build and push Joplock image / build-and-push (push) Has been cancelled
2026-06-17 13:09:27 +12:00
igor
8faef903c0 add expander-based AI autocomplete triggers
Some checks failed
Build and push Joplock image / build-and-push (push) Has been cancelled
2026-06-07 14:56:30 +12:00
igor
512824693c enforce vault note encryption at Joplin Server proxy layer
Some checks failed
Build and push Joplock image / build-and-push (push) Has been cancelled
- add app/proxy/vaultProxyGuard.js: inspects proxied note writes and
  deletes before forwarding to upstream Joplin Server
- covers single PUT /api/items/root:/<id>.md:/content, batch PUT
  /api/batch_items, single DELETE, and batch DELETE
- rejects with 403 when a vault note body lacks the encrypted marker,
  or when a vault note is deleted via the sync proxy
- bodies over 10 MB stream through without inspection (resource blobs)
- unauthenticated requests stream through (upstream handles 401)
- wire guard into createServer.js proxy entry point; replay buffered
  body via Readable.from() on allow
- 34 new unit tests, all 394 tests passing
2026-05-21 16:09:09 +12:00
igor
974979c688 polish backup actions and size display
Some checks failed
Build and push Joplock image / build-and-push (push) Failing after 2m5s
2026-05-20 23:20:35 +12:00
igor
175cf98038 add backup delete action and explicit compression modes 2026-05-20 23:13:18 +12:00
igor
2e7fd6e97a update Docker and example stacks for Postgres 18 2026-05-20 22:39:59 +12:00
igor
d722a36895 hide view for non-previewable resources
Some checks are pending
Build and push Joplock image / build-and-push (push) Waiting to run
2026-05-20 14:56:00 +12:00
igor
c1f9988285 clicking folder title opens first note and collapses others; fix initNavPanel overriding explicit localStorage state 2026-05-20 14:02:48 +12:00
igor
6a1913dfbb increase admin bootstrap retry interval to 20s 2026-05-20 13:45:02 +12:00
igor
0b66045326 retry admin bootstrap until Joplin Server DB is ready on fresh install 2026-05-20 13:20:13 +12:00
igor
1418cd426a show pg version in admin db compression section; hide toast compression UI on pg < 14 2026-05-20 13:12:26 +12:00
igor
c3ba506745 add vault-move playwright safety net for unified note I/O refactor
Some checks are pending
Build and push Joplock image / build-and-push (push) Waiting to run
2026-05-20 13:01:44 +12:00
igor
d6785c050d exclude encrypted note bodies from text search (titles still searchable) 2026-05-20 13:01:44 +12:00
igor
10f7e15d56 fix vault move branches: ta.value already plaintext; re-encrypt directly 2026-05-20 12:41:09 +12:00
igor
42d5c16abf cancel pending plaintext autosave + re-snapshot after encrypted save 2026-05-20 12:36:43 +12:00
igor
e33aadd5f2 chunk _b64Encode to avoid Firefox argument-count limit on large blobs 2026-05-20 12:34:07 +12:00
igor
c81ba6453a diagnose encryption failure on vault move; guard missing salt 2026-05-20 12:33:07 +12:00
igor
8b77db537e treat orphan-vault decrypted notes as plaintext on folder move 2026-05-20 12:25:16 +12:00
igor
6d30ff094a legacy vault fix 2026-05-20 12:20:03 +12:00
igor
2dd5e1c7bd option DB compression to lz4 2026-05-20 12:03:11 +12:00
igor
4fe91bbbf4 improve resource viewer: inline text, per-type preview, pinch-zoom on iOS
Some checks are pending
Build and push Joplock image / build-and-push (push) Waiting to run
2026-05-19 21:51:53 +12:00
igor
3af1dd581b add resource actions and refresh PWA assets
Some checks are pending
Build and push Joplock image / build-and-push (push) Waiting to run
2026-05-19 19:04:31 +12:00
igor
3e80b61e20 add rendered-mode auto hyperlinks 2026-05-19 16:18:20 +12:00
igor
aec706bffd update mobile login and iOS downloads 2026-05-19 14:13:24 +12:00
igor
7dfe368194 recovery-rlimit 2026-05-19 13:52:13 +12:00
igor
1cdba3fddd add backup options to full build compose 2026-05-19 13:45:24 +12:00
igor
749b7041bf download button 2026-05-19 13:41:21 +12:00
igor
5e0482da01 rate-limit login 2026-05-19 12:57:19 +12:00
igor
9b7d456770 fix mobile note back navigation
Some checks are pending
Build and push Joplock image / build-and-push (push) Waiting to run
2026-05-18 23:37:58 +12:00
igor
d96ea802e9 fix note creation on proxied dev server 2026-05-18 21:50:14 +12:00
igor
1ca6f619a9 Add backup compression presets 2026-05-18 19:59:28 +12:00
igor
553ae0048a Merge branch 'sql-backup' 2026-05-18 19:34:17 +12:00
igor
a36949aab5 Add recovery backups and fix theme/title edge cases 2026-05-18 19:34:06 +12:00
igor
3ec9b6fc6d Add BASIC language syntax highlighting support
Some checks are pending
Build and push Joplock image / build-and-push (push) Waiting to run
Register BASIC with highlight.js (preview mode) and add it to the
code modal language picker. CodeMirror falls back to plain text in
the modal editor as no CM6 BASIC parser is available.
2026-05-18 12:17:14 +12:00
igor
a70efdd296 Fix rendered-mode image uploads and save state
Some checks failed
Build and push Joplock image / build-and-push (push) Has been cancelled
2026-05-15 15:27:02 +12:00
igor
09d7b8f464 Publish multi-arch Docker images
Some checks failed
Build and push Joplock image / build-and-push (push) Has been cancelled
2026-05-07 14:30:50 +12:00
igor
0698bbd78e Unify note title sanitization
Use the shared title sanitizer for both SSR and client-side title editing
so note titles are cleaned by one function. Also sanitize note titles on
create in the fragments route and add a regression test for formatted
titles.
2026-05-01 19:38:25 +12:00
igor
6652f8ddeb fix: re-init desktop UI when switching from mobile→desktop shell mode
Some checks failed
Build and push Joplock image / build-and-push (push) Has been cancelled
syncResponsiveMode already called redrawMobileUI() when crossing
desktop→mobile, but the reverse path only hid the mobile app. A session
that started narrow (mobile mode) never called initNavPanel/initEditorPanel
for the desktop form, leaving the preview and CM host with undefined
display states and stale scroll position.

Mirror the pattern: save wasMobile before clobbering _lastSyncWasMobile,
then call initNavPanel()+initEditorPanel() when wasMobile===true.
Both functions are idempotent (guarded by dataset flags) so a repeat
call on an already-initialised form is a no-op.
2026-05-01 13:47:30 +12:00
igor
1c236c9721 Replace hand-rolled markdown renderer with markdown-it; fix blank-line round-trip and editor UX
- Install markdown-it@14.1.1 as server-side renderer (renderMarkdown only)
- Preserve all Joplin extensions: underline (++..++), checkboxes,
  blank-line markers, softbreak→<br>, resource URIs, spellcheck attrs,
  hx-* strip, fence/code/image/link render overrides
- Fix blank-line round-trip: emptyDiv/emptyP Turndown rules now return
  the BL sentinel instead of '<br>' (which line 611 inflated to 4 newlines)
  or '' (which made blank-line edits never save)
- Disable CSS scroll anchoring in preview editor so images flow down
  naturally when text is typed above them
- Narrow the Notebook + button label and padding in the nav header
- Bump static asset version to 20260501a
2026-05-01 13:20:22 +12:00
igor
67afe502a0 Render fenced code blocks nested in list items
Custom regex renderer's fence extraction required column-0 anchoring,
so any fence inside a list item (or with leading whitespace) was missed
and the loose backticks were mangled by the inline-code regex. Add a
list-item-nested fence pass that outdents the body and preserves the
surrounding list structure as a placeholder, plus relax the column-0
fence regex to allow up to 3 spaces of indentation per CommonMark and
trailing whitespace on the closing fence.
2026-05-01 10:30:03 +12:00
igor
003dc817de Refactor mobile shell to state machine; enforce single-screen invariant
Some checks are pending
Build and push Joplock image / build-and-push (push) Waiting to run
Eliminates fragile ad-hoc DOM toggling that allowed two mobile screens to
render simultaneously. All transitions now go through setMobileState()
reducer; renderMobile() is the only function that writes
.mobile-screen-active. assertSingleActiveScreen() self-heals violations
and traces them. Test asserts the architectural invariant in source.

Also: DB session lookup fails closed on transient errors instead of
crashing; mobile back-save uses formHash (not UI badge); single-screen
CSS via display:none/flex (no transforms); resize debounced without
reload; aria-hidden warning fix.
2026-05-01 00:10:43 +12:00