Skip to content

fix: keep presence avatars visible across note view modes - #110

Open
dtkav wants to merge 9 commits into
mainfrom
fix/awareness-view-presence-108
Open

fix: keep presence avatars visible across note view modes#110
dtkav wants to merge 9 commits into
mainfrom
fix/awareness-view-presence-108

Conversation

@dtkav

@dtkav dtkav commented Jul 24, 2026

Copy link
Copy Markdown
Member

Fixes #108

Summary

  • re-resolve and move the existing awareness surface when a Markdown view switches between Live Preview and Reading mode
  • anchor to the active mode content when the inline title is unavailable
  • give the facepile its own row on narrow viewports and clamp the participant popover to the viewport

Validation

  • npm test -- --runInBand --silent — 100 suites, 1,485 tests passed
  • npx tsc --noEmit --skipLibCheck
  • targeted ESLint and staging build
  • exact PR bundle verified on live1 and live2
  • visual verification in Live Preview and Reading mode with the inline title hidden, on desktop and app.emulateMobile(true) at 390×844; no note-content overlap and the popover remained within viewport bounds

dtkav added 9 commits July 22, 2026 01:21
The baseline was assigned after syncStore.start() in the readiness
chain, but the syncStore observer and the merge manager both exist from
construction, so a tree sync can run earlier. The newly-enabled-types
diff then reads the empty baseline as "every type was just enabled"
and runs addLocalDocs before readiness gates that discovery -- the
exact early-discovery hazard the readiness chain documents: placeholder
GUIDs reserved for already-shared files and HSMs built without their
persisted fork and LCA state.

Seed the baseline at construction, immediately after the SyncStore
exists, so the first tree sync diffs against reality and enqueues
nothing early. Toggling a type on still enqueues its files: the diff
against a real baseline catches it on the next tree sync.
Shared-folder membership decisions now run through a declared per-file
state machine: explicit entry states with legal transitions, evidence
requirements on every destructive or publishing move, and refusal
semantics for colliding events. Folder state persists in a dedicated
per-app database holding the folder fork (withheld deletion bursts with
observed identities), the deletion-retention ledger, and a snapshot
cache of the server-side index that replaces the per-folder remote
database. Held uploads are never trashed and never lose their identity
silently. Behavior with the folder engine disabled is unchanged.

Files with no shared identity acquire one only through the engine's own
publication path: the editor lookup fallbacks request a membership
decision instead of minting and uploading directly. Remote assertions
and keep-remote resolutions at refused paths converge on the committed
identity, retracting a superseded local mint, and deleting a refused
file releases its held identity with it.
The folder nav pill carries data-relay-state with the folder's live
connection status, the NOT SYNCED pill carries data-reason
(storage-required or file-type-disabled) alongside its human label, the
join controls carry data-action (join-relay, add-to-vault,
confirm-add-to-vault), and the share-key input carries a name. The UI
declares its state and actions; styling classes and copy stay free to
change.
The shard requests the literal token 'default', which the suite runner
resolves to every manifest entry flagged for the canonical CI suite —
the manifest becomes the one place a plan graduates. Targeted dispatches
pass through to the runner's own id resolution instead of intersecting
with a literal roster, which silently skipped ids the roster had not
learned.
Sync-category toggle rows carry data-sync-category and data-locked, the
storage upgrade button carries data-action, every settings navigation
control — the relay list, the relay page's folder rows, and both My-vault
folder rows — carries data-action (buy-storage, manage-relay,
manage-folder), and the upload pill carries data-status drawn from a
closed three-token vocabulary (pending, error, unknown) rather than its
display text, so consumers never pin human copy. Extends the
declared-state convention to the surfaces entitlement decisions render
on.
…ibutes

The share-local-folder and leave-relay buttons carry data-action,
extending the declared-action convention to the owner's sharing flow.
@VRTG-47

VRTG-47 commented Jul 27, 2026

Copy link
Copy Markdown

Tested the PR build. Bug 1 (view-mode switching) is now fixed — avatar stays visible when switching between Reading/Live Preview with inline title enabled. Thank you!

(Quick note: I'm not very experienced with programming, and the analysis below was put together with help from Claude AI. Apologies in advance if anything is phrased awkwardly, imprecisely, or comes across as less professional than it should — happy to clarify or provide more details if needed.)

However, Bug 2 (inline title disabled) is still not resolved. It looks like the fallback anchor was changed from .inline-title to .iconic-title-wrapper (new class user-awareness-positioning-parent confirms this), but .iconic-title-wrapper itself collapses to display: none when "Show inline title" is disabled — Obsidian hides the entire title-wrapper row in that case, not just the inline-title text element. So the awareness container is still created, but it's nested inside a hidden ancestor and never renders.

DevTools trace with inline title disabled:
0 user-awareness-container display: flex height: 0
1 iconic-title-wrapper user-awareness-positioning-parent display: none height: 0
2 mod-header mod-ui display: block height: 0
...

A fallback anchor that doesn't depend on inline-title visibility at all — e.g. .view-header or .view-content directly — might be more robust here, since those remain visible regardless of the inline title setting.

@dtkav
dtkav force-pushed the main branch 7 times, most recently from f75fe32 to d8eeeac Compare August 1, 2026 22:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants