Give the rich-text and Markdown editors a dedicated hyperlink entry
separate from note linking, with Ctrl/Cmd+K, URL normalization, and
remove-link support. Closes the discoverability gap in #192; link
preview cards remain out of scope.
Related Issue: #192
Give Web, desktop, and mobile the same product-baseline conflict recovery:
copy the local draft, then adopt the cloud version and rehydrate, instead of
telling users to reload while durable local conflict state remains.
Surface revision, content, edit-session, and offline-stale conflict details in the editor, keep error metadata through the sync queue, and avoid hook-order crashes plus stale dev service workers after failed HMR.
Make deleting notes one click on desktop list cards instead of buried under the more menu, while keeping the existing menu path for keyboard and right-click.
getStoredMermaidRenderer assumed localStorage always exists whenever window
was defined, so full bun test runs could fail after other suites stubbed a
partial window. Safe-read the storage key and clean up shared window mocks
so Verify update stays green for forked deployments.
Stop getMemo background refresh from overwriting newer local autosaves or
queued updates, and skip redundant setContent when reopening a note so the
caret no longer jumps to the end after detail refetches.
Let instance owners route new uploads to an external OSS while keeping
existing attachments on their original store. Secrets are encrypted with
EDGE_EVER_STORAGE_ENCRYPTION_KEY before being stored in D1.
Global app CSS sets html/body/#root { overflow: hidden } for the
three-pane workspace layout. Public share pages (/share/:token) inherit
this and cannot scroll when content exceeds the viewport. Restore
document scrolling when .edgeever-public-share is present via :has().
`request()` read `window.edgeeverDesktop` without a `typeof window`
guard, unlike every other `window` access in this file. Any environment
without a `window` global throws `ReferenceError: window is not defined`
before the request is sent.
This makes `bun test` fail on a clean checkout: three cases in
`tests/web-sync-queue.test.ts` fail because `syncQueuedChanges()` never
reaches `fetch` and records the queue item as `error` with
`lastError: "window is not defined"`.
Also give the `globalThis.window` stubs in `api.test.mjs` and
`desktop-resources.test.mjs` a `location`. Bun runs every test file in
one process, so these top-level stubs leak into later files; without a
`location`, `isLoopbackOrigin()` in `network-status.ts` throws
`TypeError: undefined is not an object (evaluating
'window.location.hostname')`. Whether this surfaces depends on test file
discovery order, so it reproduces on Linux but not on macOS.
Before: 296 pass, 3 fail. After: 299 pass, 0 fail.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>