Commit graph

145 commits

Author SHA1 Message Date
igor
76ade91575 feat(editor): apply list/quote prefixes to all selected lines in markdown mode
Some checks failed
Build and push Joplock image / build-and-push (push) Has been cancelled
2026-08-09 22:38:53 +12:00
igor
c3e0790dda docs: record reconcile log triage + round-trip fragility lessons
Some checks failed
Build and push Joplock image / build-and-push (push) Has been cancelled
Add a 'note shows Edited on open' triage section to Debugging Guidance: the
three post-load reconcile log lines and what each means, how to round-trip a
stored body offline to find the cause, and the list of known-lossy constructs
that are by design and must not be 'fixed' with more markers.

Correct the blank-line marker entry, which still described the pre-fix
behaviour of rewriting markers 'empty or not' — that is exactly what silently
destroyed text typed into a marker. Also document the two Turndown traps that
caused real corruption: flanking-whitespace expulsion next to atomic children
(NBSP-encoded sentinel, do not simplify) and blockquote soft breaks needing the
'>' prefix carried across the restore.
2026-08-05 16:36:09 +12:00
igor
21166ff0e6 fix(rendered): stop phantom edits and text loss in markdown round-trip
Rendered mode (TinyMCE) briefly showed "Edited" and fired a useless autosave
every time certain notes were opened, and text typed between two blocks could
be lost entirely.

Data loss: <p class="md-blank-line"> is a real, focusable paragraph in the
iframe, so clicking the gap between blocks puts the caret inside it. The
blank-line normalisation in tinymceToMarkdown() rewrote such paragraphs
unconditionally ("empty or not"), replacing whatever the user had just typed
with the <br> sentinel before Turndown ever saw it. Now only markers that are
still genuinely blank collapse to the sentinel; a marker holding real content
is left verbatim and converts as an ordinary paragraph.

Unbounded whitespace growth: Turndown derives an element's flanking whitespace
from node.textContent, which skips atomic children. For <a><img/>&nbsp;Label</a>
it reported leading whitespace that is actually interior to the output, so
replacementForNode()'s content.trim() could not remove it yet it was prepended
anyway. The run grew by one character on every open (12 -> 13 -> 14 ...),
slowly corrupting the stored body. Interior whitespace next to <img>/<br> is
now hidden behind a sentinel that encodes the exact character (these runs are
frequently NBSP, so restoring a plain space would itself dirty the note).

Blockquotes: a soft-wrapped quote line lost its "> " prefix because the <br>
sentinel is restored after Turndown has prefixed its own lines, so "> b\n> c"
came back as "> b\nc" and the second line escaped the quote. The prefix is now
carried across the break. Bare "> " lines also no longer keep a trailing space.

Phantom edit: _savedHash is snapshotted before the editor loads, so the 820ms
post-load reconcile compared normalised content against the raw server body and
treated any cosmetic rewrite as a user edit. Some normalisations cannot be
represented in HTML at all (an indented ``` fence loses its indent, a blank
line after an ATX heading is collapsed by design). The reconcile now
distinguishes the two cases via a _tinymceUserTypedSinceLoad flag, wired to
keydown/paste/cut/drop -- deliberately not input/SetContent, which setContent()
itself fires. A real edit during the quiet window still saves; pure load
normalisation just re-baselines the hash.

Bump ASSET_VERSION and the service-worker cache name so browsers refetch app.js.

Tests: 790 pass (was 776).
2026-08-05 16:33:07 +12:00
igor
5d251cc015 fix(rendered): widen fencedCodeLanguage filter to all <pre>
Some checks failed
Build and push Joplock image / build-and-push (push) Has been cancelled
A bare <pre> (no <code> child, no language-* class) from pasted external
HTML fell through to Turndown's default fenced-code rule, which does not
widen the fence when the content contains a backtick run and instead
inline-escapes the backticks — producing broken markdown. Match every
<pre>; the replacement already reads textContent when there is no <code>
child, so bare <pre> is fenced (and fence-widened) safely.

Add regression tests for the bare-<pre> backtick-run case and the normal
language-tagged <pre><code> path.
2026-08-03 16:01:13 +12:00
igor
4978590b01 fix(rendered): checkbox Enter always adds item; rendered-mode auto-title
Some checks failed
Build and push Joplock image / build-and-push (push) Has been cancelled
- TinyMCE Enter at end of a non-empty checkbox now always inserts a new
  checkbox item, regardless of what follows (paragraph, another checkbox,
  or nothing). Exiting the list is done via Enter on an empty item. This
  fixes being unable to add items to an existing checklist that is
  followed by other text; only bottom-of-note appends worked before.
- Restore rendered-mode title auto-fill broken by ffb7bd0: onEdit now
  calls a cheap DOM-only autoTitleFromTinyMCE() instead of relying on a
  per-edit synthetic textarea 'input' event.
- Tests: rewrite checkbox-enter-behavior.spec.js (end-of-note,
  followed-by-text, empty-item exit); remove checkbox-exit.spec.js
  (encoded old exit-on-following-paragraph behavior); add
  autotitle-rendered.spec.js and tinymceOnEditSync auto-title guards.
2026-07-31 16:12:28 +12:00
igor
1e1c6cdbaa Fix table row-insert scramble and round-trip stability
Some checks are pending
Build and push Joplock image / build-and-push (push) Waiting to run
Bug: inserting a row on top of a 4-column table in rendered mode, then
switching notes and coming back, corrupted the table (header row
duplicated). Root cause: the turndown table rule always used all
<tbody> <tr> elements as body rows even when the first was already
used as the header row due to the absence of <thead>.

Fix: when <thead> is absent and <tbody> is present, skip the first
<tr> (used as headerRow) from bodyRows. Harden the fallback for
single-row tables.

Tests: 20 new unit tests (html-to-markdown conversion, full round-trip,
edge cases) and 2 new Playwright E2E tests covering the exact bug
scenario.
2026-07-30 15:39:59 +12:00
igor
e6feecdc31 fix: theme persistence, FormatBlock/TinyMCE blocks dropdown, scrollbar consistency
Some checks failed
Build and push Joplock image / build-and-push (push) Has been cancelled
- Synced scrollbar CSS vars into TinyMCE iframe
- Added mceToggleFormat handling for linebreak-mode notes (blocks dropdown)
- Added NodeChange bookmark fallback for toolbar-triggered format commands
- Removed localStorage override on page load for theme persistence
- Added regression tests for theme persistence and Ctrl+Z undo
2026-07-28 22:18:25 +12:00
igor
e4261a9379 fix(rendered): bullet/ordered-list formatting only targets caret line, not whole note
Some checks failed
Build and push Joplock image / build-and-push (push) Has been cancelled
BeforeExecCommand handler splits <br>-separated <p> blocks before insertUnorderedList/insertOrderedList runs, same pattern used for FormatBlock. Also sets margin:0 on split blocks to preserve linebreak-mode spacing.
2026-07-24 16:07:17 +12:00
igor
2d5b4bc749 docs: update sharing model in AGENT_GUIDE, add share- prefix to teardown 2026-07-24 14:45:20 +12:00
igor
fcc8f8c56d feat: notebook/folder sharing
Backend: shareAccess helpers, shares CRUD API with can_write, proxy
write guard, fragment/API ownership gates, widened item reads via
user_items, share_id propagation on move-in/out, leave endpoint.

UI: share dialog with owner-only controls, can_write toggle checkbox,
recipient leave button, read-only editor mode, shared-folder emoji.

Tests: 754 unit (+46 share), 8 Playwright browser specs.
2026-07-24 11:15:11 +12:00
igor
f6f909f692 chore: capture Playwright screenshots on every run
Some checks failed
Build and push Joplock image / build-and-push (push) Has been cancelled
2026-07-20 15:58:53 +12:00
igor
c2fc945ac7 test: add Playwright tests for Enter-split list items
Some checks are pending
Build and push Joplock image / build-and-push (push) Waiting to run
2026-07-19 17:51:49 +12:00
igor
2c5a3857f7 fix(rendered): split list items on Enter at caret position in bullet/numbered lists
Some checks failed
Build and push Joplock image / build-and-push (push) Has been cancelled
- Extend range to end of <li> before extractContents() (collapsed ranges return empty)
- Same fix for both TinyMCE editor and preview contenteditable handlers
- Add <li> Enter handler to preview mode (was missing entirely)
2026-07-17 23:18:29 +12:00
igor
78696372f2 docs: update vault boundary docs for simplification 2026-07-17 17:21:29 +12:00
igor
fa025fb3a0 vault simplification: notes cannot leave vaults
Remove confirmMoveOutOfVault mechanism and vault→plain / vault→vault
move paths. Vault notes now have immutable parentId — the server rejects
any PUT that changes a vault note's folder (400). The folder select
stays disabled for vault notes even after unlock.

Server:
- _helpers.js: remove confirmMoveOutOfVault from assertVaultNoteBodyEncrypted,
  add nextFolderId guard to ciphertext-egress check
- fragments.js: add parentId immutability guard before every write path
  (autosave PUT + createCopy), enrich folders with isVault in editor GET
- api.js: add parentId immutability guard for web API PUT

Client:
- app.js: remove dead isEnc branches from folder-change handler, keep
  only plain→vault encrypt flow; remove confirmMoveOutOfVault hidden
  input injection; remove fs.disabled=false from _completeUnlock;
  gate _applyFormReadonly on vault state so select stays disabled;
  add data-is-vault option-attribute as primary vault detection

Templates:
- fragments.js: add data-is-vault='1' to folder select <option> elements

Tests:
- createServer.test.js: update vault move tests for new rejection behavior
- vaultRichModeSync.test.js: remove vault→plain behavioral test (dead path)
- proxyVaultGuard.test.js: add ciphertext-egress-to-non-vault test
- vault-move.spec.js: replace vault→plain/vault→vault tests with disabled-select
  + server-rejection tests; add plain-note folder-move tests (6, 7)

Versions: 20260717vaultsimplify1 / shell-v74
2026-07-17 17:20:55 +12:00
igor
9c6e580fe3 Add /ask slash command for inline AI Q&A in notes
Some checks failed
Build and push Joplock image / build-and-push (push) Has been cancelled
2026-07-16 12:30:40 +12:00
igor
d6245c29b6 rendered: reconcile at post-load window end so an in-window edit still saves (vault-safe: encrypted-save override still runs identity checks) 2026-07-16 11:03:33 +12:00
igor
0aa791d1db fix(rendered): 800ms post-load quiet window absorbs mceFocus + phantom input after content set (was flashing Edited->Saved on first click after restore, and saving normalized markdown when debug logging on) 2026-07-16 10:59:24 +12:00
igor
212343c697 theme(nord): mute --border (low-alpha frost); keep --border-focus saturated 2026-07-16 10:34:58 +12:00
igor
883069c262 theme(dracula): mute --border (low-alpha purple); keep --border-focus saturated 2026-07-16 10:33:23 +12:00
igor
6d0fab287d ui: force fixed 44px header height + 28px controls for nav/notelist/editor 2026-07-16 10:31:35 +12:00
igor
36a8c9d118 ui: tighten editor titlebar to 44px min-height so it aligns with nav/notelist headers 2026-07-16 10:28:46 +12:00
igor
b672a344ea ui: align nav/notelist/editor header heights (min-height 48px) 2026-07-16 10:22:46 +12:00
igor
c113ba60a7 ui: restore rounded corners for buttons (.btn, .tb, .btn-icon, .pre-copy-btn, .export-menu) 2026-07-16 10:20:25 +12:00
igor
fc6eaae222 sw: bump cache for square-corner CSS 2026-07-16 10:16:59 +12:00
igor
51caf6a1eb ui: square all chrome corners; keep pill/circle radii for avatars/badges 2026-07-16 10:16:37 +12:00
igor
4748b03428 title: force plain text on paste/drop/input in editor title 2026-07-16 10:12:36 +12:00
igor
99e5b29c62 docs: update AGENT_GUIDE and README with vault identity guard, restore, and feature list 2026-07-16 10:01:07 +12:00
igor
b8f45121c6 autosave fix 2026-07-16 09:57:39 +12:00
igor
ee720028c8 fix: add _tinymcePostLoad to test sandbox context
Some checks are pending
Build and push Joplock image / build-and-push (push) Waiting to run
2026-07-15 23:52:25 +12:00
igor
8b360567e2 fix: suppress spurious Edited/save on note open in rich mode
When clicking a note title, TinyMCE fires async events after
_tinymceSuppressEdits is cleared (setTimeout 0), reaching onEdit()
and triggering markEdited() + scheduleSave(). The markdown round-trip
(original body -> render -> HTML -> tinymceToMarkdown) normalises
whitespace, so the save actually fires with a different body hash.

Add _tinymcePostLoad flag set in _setTinyMCEContent's setTimeout
callback (after suppress clears). On the first onEdit() that fires:
- skip the bubbling input dispatch so the form listener stays quiet
- recapture _savedHash against the round-tripped markdown instead of
  calling markEdited/scheduleSave, then clear the flag

Subsequent real edits compare against the normalised baseline and
behave correctly.
2026-07-15 17:23:13 +12:00
igor
253fd5ab81 Fix TinyMCE not resizing after sidebar toggle on desktop
Some checks are pending
Build and push Joplock image / build-and-push (push) Waiting to run
2026-07-15 15:18:18 +12:00
igor
f084a11a99 Add single-file HTML export, image/link inlining for exports, and visible table gridlines for PDF/DOCX
- New POST /api/export/html: single self-contained HTML file with the
  active theme's CSS inlined, images/attachments as base64 data URIs,
  and heading/link colors matching the TinyMCE rendered-mode view
- CSS extraction helper (extractCssBlocks) pulls theme + .editor-preview
  rules out of styles.css via a brace-depth scanner
- New inlineResourceLinks helper: attachment links become data: URIs
  with a download attribute instead of dead /resources/ hrefs
- PDF table borders bumped to visible 1px/2px lines; fixed pandoc's
  default HTML template CSS (50px body padding) leaking into PDF output
  via --include-in-header
- Regenerated public/reference.docx with a patched Table style (visible
  gridlines) via new scripts/build-reference-docx.sh
- Client: exportNoteAsHtml now posts to the server instead of building
  an unstyled client-side doc; sends current theme slug
- Bumped service worker cache to v54-20260715htmlexport
- Added tests: extractCssBlocks, inlineResourceLinks, handleExportHtml
2026-07-15 14:34:11 +12:00
igor
ec2a05a277 fix: muted borders for Swamp Thing and Fireball themes
Some checks are pending
Build and push Joplock image / build-and-push (push) Waiting to run
Both themes had --border set to solid accent color instead of
transparent rgba() like every other theme. Changed to low-opacity
rgba() borders (12-14% for --border, 40% for --border-focus).
2026-07-14 22:19:54 +12:00
igor
5890430fb3 Earth theme + pandoc DOCX export (HTML/markdown→docx via pandoc with reference styles)
- Add Earth theme (warm browns, gold accent), set as default
- Sort themes alphabetically in picker
- History modal dates show YYYY/MM/DD HH:MM format
- Replace client-side html-docx with server-side pandoc DOCX export
- POST /api/export/docx: accepts {content, format, title}, streams docx
- HTML input path preserves TinyMCE semantic formatting
- Pandoc reference doc for styled output
- 7 passing DOCX formatting tests (heading/bold/italic round-trip)
- Bump SW cache to v50
- Add pandoc to Docker image
2026-07-14 22:15:49 +12:00
igor
246b88401e Document shell-mode cache + readonly and TinyMCE fast/debug onEdit split; add AGENTS.md pointer 2026-07-14 20:48:40 +12:00
igor
e75ff6de11 Fix TinyMCE autosave sync + shell-mode readonly + FormatBlock partial split
- Cache isMobileShellMode(); reload on shell flip after flushing dirty
- Lock editor form (title, folder, toolbar, CM6) when readonly
- Fast onEdit path: schedule only; lazy sync once before formHash
- Debug path retains per-event sync + logs
- Wire ExecCommand/SetContent so blocks-dropdown mutations sync
- BeforeExecCommand FormatBlock: BR-split, 3-way <p> split, heading clamp
- flushSave hash-unchanged branch resets state to Saved (unblocks +)
- Bump SW cache to joplock-shell-v49-20260714readonly
- Add 47 tests across shell/readonly, FormatBlock partial, onEdit sync
2026-07-14 20:47:24 +12:00
igor
769d3cc54c Server-side pandoc DOCX export: replace client html-docx with POST /api/export/docx 2026-07-14 18:00:47 +12:00
igor
11beebd297 Earth theme: add, set default, sort themes alphabetically, YYYY/MM/DD history dates 2026-07-14 17:57:23 +12:00
igor
5e0512ca61 test(editor): FormatBlock split wiring + Enter regression guards 2026-07-14 16:33:47 +12:00
igor
ca0a577ddc fix(editor): Enter cursor jumps to line start in rendered mode 2026-07-14 16:32:03 +12:00
igor
d0f58b5037 feat(export): note export (MD/HTML/DOCX/PDF) + FormatBlock fix + table borders
Some checks are pending
Build and push Joplock image / build-and-push (push) Waiting to run
Export:
- Export button in TinyMCE toolbar (rendered mode) and mobile hamburger menu
- Format dropdown: Markdown, HTML, Word (.docx), PDF (print window)
- html-docx.js vendored UMD bundle for DOCX export
- PDF opens clean print window: white bg, black text, @page margins, no header/footer
- Esc closes export menu; Cancel button in dropdown
- Export menu hidden in markdown mode; positioned via fixed+JS to avoid overflow clipping

FormatBlock fix:
- blocks dropdown was converting the whole note when content used linebreak mode
  (single <p> with <br> separators). NodeChange handler now splits <br>-joined
  blocks into individual <p> elements on caret entry so FormatBlock targets only
  the selected line.
- 6 Playwright tests in playwright-tests/format-block.spec.js covering caret,
  triple-click, keyboard selection, existing-heading, revert-to-paragraph,
  and the linebreak-mode regression case.

Table borders:
- 3px solid borders in markdown preview (styles.css), TinyMCE content_style
  (with !important to override skin defaults), and PDF print window.
- Table header bg + bold in all three contexts.
2026-07-14 13:53:56 +12:00
igor
05ff14771e Add TinyMCE table support, swamp-thing/fireball themes, and turndown table round-trip tests
Some checks are pending
Build and push Joplock image / build-and-push (push) Waiting to run
2026-07-13 18:28:38 +12:00
igor
093e4dcfa3 rendered-mode: header colors match folders + fix native spellcheck context menu
- Headers (h1-h6) in rendered mode now use var(--accent), matching
  folder/notebook color in the nav panel (was var(--text-heading)).
- Fix native right-click spelling suggestions: TinyMCE's link plugin
  context-menu item resolved to a non-empty value for ANY editable
  text, not just real hyperlinks, so it always intercepted right-clicks
  and blocked the native browser context menu (and its spellcheck
  suggestions). Set contextmenu:'image table' to exclude link; image
  and table right-clicks are unaffected since those only match on
  actual <img>/table elements. Link insertion/editing remains
  available via the toolbar and click-to-copy/Ctrl-click-to-open.
2026-07-13 16:05:35 +12:00
igor
fb09c3d92e fix(escape): dismiss in-note search / clear nav-search from inside TinyMCE iframe
Some checks are pending
Build and push Joplock image / build-and-push (push) Waiting to run
TinyMCE runs in an iframe (separate browsing context), so keydown events
fired inside it never bubble to the parent document's keydown listener.
This made Esc silently do nothing while focus was inside the note body
where search highlights live, even though the same key worked once focus
left the note. Wired the same two-stage Esc logic (dismiss highlight,
then clear nav-search) directly on editor.on('keydown'), following the
existing pattern used for the AI-completion shortcut and resource-viewer
overlay, both of which have the same iframe limitation.
2026-07-13 11:08:23 +12:00
igor
feece750ef debug(escape): add _log tracing for Esc handler, search highlight entry/dismiss paths 2026-07-13 10:05:34 +12:00
igor
c196742330 fix(escape): Esc exits in-note search when bar not found, resource-viewer check requires visible 2026-07-13 00:12:03 +12:00
igor
db0078407a fix(search): restore in-note search highlight for rendered (TinyMCE) mode + two-stage Esc 2026-07-12 15:00:42 +12:00
igor
391bed0a07 click-copy: plain click copies link to clipboard, Ctrl/Cmd+Click opens in new tab 2026-07-12 14:58:13 +12:00
igor
965e8f9ff4 font choices, flushSave robustness, nav toggle fix
Some checks failed
Build and push Joplock image / build-and-push (push) Has been cancelled
- Add rounded + humanist font families with expanded fallback stacks
- Refactor flushSave() from minified one-liner: watchdog timeout, mutex
  for encrypted autosave coordination, proper cleanup on all paths
- Fix openNavFolderAndFirstNote so already-expanded folders can collapse
- Service worker cache v46 -> v47
2026-07-10 23:30:05 +12:00