Add per-node attribution to q2-debug AST view#122
Draft
shikokuchuo wants to merge 11 commits intomainfrom
Draft
Conversation
Replays Automerge history to build a per-character attribution map, then colors each AST node by author with hover tooltips showing name and relative time. Supports chunked async builds, incremental updates, and graceful degradation when offline or without history.
The hub-client tsconfig enables erasableSyntaxOnly, verbatimModuleSyntax, and noUnusedLocals which surface errors in annotated-qmd sources resolved through the types field. Convert parameter properties to explicit field declarations, split type-only imports, and remove unused variables.
Real Automerge diffs use splice/put/del actions (not insert/del from the old Text API). Handle splice with value:string, put for field-level init, and add @automerge/automerge as a direct hub-client dependency. Also fix relative time display for second-precision timestamps.
The useAttribution hook now only runs when the document has format: q2-debug AND attribution: true in its YAML frontmatter. Without the flag, no Automerge history traversal occurs.
…ke test Remove console.log/warn/error statements used during development, drop unused _identities parameter from useAttribution() (identities flow through AttributionContext), and delete the stale spike test that documented the old Automerge Text API patch format.
Show a colored badge (author dot + name + relative time) on hover instead of the browser's plain title tooltip. Badge uses a solid white background with the author's color on border and text, and appears below the hovered node. CSS is injected once from AstRenderer rather than per-Node.
Cache node attribution lookups in a Map keyed by sourceInfoId so re-renders that don't change attribution data make zero WASM calls. Use byteToCharMap from AttributionContext instead of recomputing it in the renderer. Replace per-node hidden AttributionBadge elements with a single floating badge shown on hover via event delegation.
Attribution was previously opt-in via `attribution: true` in the qmd YAML frontmatter. This is a viewing concern, not a document property, so move it to a persistent UI toggle in the Settings sidebar instead. The toggle appears only when the preview format is q2-debug and is labeled "Authorship" for clarity. The preference persists across sessions via localStorage.
Consumer-facing interfaces now expose CharAttribution[] entries instead of the full AttributionMap (which carries Automerge-specific processedHeads/processedHistoryIndex bookkeeping). This enables swapping the attribution data source (e.g. to git blame) without changing any consumer code.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #115.
Summary
Add per-node authorship to the q2-debug AST view. When enabled via the Settings sidebar toggle, each AST node is colored by the author who last edited it, with a hover badge showing their name and timestamp. Off by default — no impact on normal editing.
How it works
Replays Automerge history diffs to build a per-character
{ actor, timestamp }map, then resolves each AST node's source location to find its author.The full build (cold start) processes history in chunks of 50 entries via
requestIdleCallbackto stay off the main thread. After the initial build, edits trigger debounced incremental updates that only process new history entries. If history gets compacted under us, we detect it and fall back to a full rebuild.Source locations in the AST are byte offsets into the QMD text, but JS strings are UTF-16 — so we build a byte-to-char index map that handles multi-byte UTF-8 and surrogate pairs. Node attribution results are cached per render cycle.
The attribution data flowing through
AttributionContextand intogetNodeAttribution()is now source-agnostic — justCharAttribution[](per-character actor + timestamp). The Automerge-specific incremental update state (processedHeads,processedHistoryIndex) stays internal to theuseAttributionhook. This makes it straightforward to swap in a different attribution source (e.g. git blame) by writing a new hook that produces the same{ entries: CharAttribution[], byteToCharMap: number[] }shape — no changes needed in the context, renderer, or query logic.Key pieces:
services/attribution.ts— builds the per-character attribution map from Automerge patches (splice/del/put); query function takes source-agnosticCharAttribution[]hooks/useAttribution.ts— React hook managing the async build lifecycle, debounced incremental updates, and cancellation on file switch or unmount; exposes onlyentries: CharAttribution[](Automerge bookkeeping stays internal)ReactAstDebugRenderer.tsx— consumes attribution via context, resolves each node'ssourceInfoPoolentry to a byte range, queries the map, and renders with the author's cursor color + hover badgeEditor.tsx— wires up the context provider when the Settings sidebar "Authorship" toggle is enabled (q2-debug format only)The authorship toggle is a user preference (persisted in localStorage), not a document property — no YAML metadata changes needed.
Also includes minor type fixes in
ts-packages/annotated-qmd(unused imports, explicit return types) to compile cleanly under hub-client's strict tsconfig.