Import these from @grafloria/renderer.
Functions
commentPinAccessibleName
The accessible name of a pin. This is the ONLY thing an AT user hears about a comment before they decide whether to open it, so it has to answer the question they are actually asking: what is this about, is it live, and is any of it new to me?
tsfunction commentPinAccessibleName(thread: CommentThreadView): string
renderCommentPins
Build the pin layer.
Returns a <g> even when empty: a stable element keeps the patcher's keyed diff
simple, and an empty <g> costs nothing to reconcile.
tsfunction renderCommentPins(
threads: readonly CommentThreadView[],
options: CommentPinsOptions = {}
): VNode
Classes
CommentOverlayController
tsclass CommentOverlayController implements CommentSource
Methods
constructor( private readonly store: CommentStore, private readonly renderer: CommentRendererHost, private readonly options: CommentOverlayOptions = {} )getThreads(): readonly CommentThreadView[]— The threads to draw. Called once per BUILT frame (never on a skipped one).getPinOptions(): Pick<CommentPinsOptions, 'selectedThreadId' | 'showResolved' | 'radius'>— Local view state — selection and the resolved filter.
NOT keyboard focus. That is the RENDERER's a11yFocus, which already owns the roving
tabindex for nodes and edges, and a comment pin is just a third kind of thing in the
same one-tab-stop widget. Two authorities for "what is focused" is how you get two
elements with tabindex=0, which is the exact bug the roving tabindex exists to
prevent — so there is one, and it is the one that already existed.
select(threadId: string | null): void— Open a thread. Opening it is READING it, which is what "unread" means.getSelected(): string | nullsetShowResolved(show: boolean): voidmarkRead(threadId: string): void— Mark a thread read WITHOUT opening it (the "mark all read" affordance).getInvalidationCount(): number— How many times the picture has been declared stale. A test asserts this moves.dispose(): void
CommentPanelView
tsclass CommentPanelView
Methods
constructor( container: HTMLElement, private readonly store: CommentStore, options: CommentPanelOptions = {} )getElement(): HTMLElementgetRebuildCount(): numberselect(threadId: string | null, opts?: { focus?: boolean }): void— Which thread is open. Setting it re-renders and moves focus onto the conversation.getSelected(): string | nullupdate(): boolean— Rebuild if — and only if — something the panel SHOWS has changed.
Safe on every frame. A drag, a pan and a zoom all change the diagram and change nothing here, and must therefore cost zero DOM operations.
dispose(): void
Interfaces
CommentOverlayOptions
tsinterface CommentOverlayOptions
Properties
| Name | Type | Default | Description |
|---|---|---|---|
showResolved? | boolean | ||
onSelectionChange? | (threadId: string | null) => void | Notified when the open thread changes (a host pans to it, opens the panel, …). |
CommentPanelOptions
tsinterface CommentPanelOptions
Properties
| Name | Type | Default | Description |
|---|---|---|---|
label? | string | Accessible name of the region. | |
showResolved? | boolean | Show resolved threads too. Default false — a resolved thread is an answered one. | |
onSelect? | (threadId: string | null) => void | Called when the user picks a thread (the host selects it and pans to it). | |
onDismiss? | () => void | Called on Escape. The host MUST return focus to the canvas. | |
formatTime? | (ms: number) => string | Render a wall-clock timestamp. Injectable so tests are not clock-dependent. |
CommentPinsOptions
tsinterface CommentPinsOptions
Properties
| Name | Type | Default | Description |
|---|---|---|---|
visibleRect? | { x: number; y: number; width: number; height: number } | World rect currently on screen. Pins outside it are not built. | |
zoom? | number | Current zoom, so the pin can be counter-scaled to a constant screen size. | |
selectedThreadId? | string | null | The thread whose panel is open — drawn selected, and aria-expanded=true. | |
focusedThreadId? | string | null | The comment the roving tabindex has focused, if any. | |
showResolved? | boolean | Hide resolved threads (the default view — a resolved thread is answered). | |
radius? | number | Radius in SCREEN pixels. |
CommentRendererHost
The slice of the renderer this controller drives. Structural, so it is trivial to fake.
tsinterface CommentRendererHost
Members
setCommentSource(source: CommentSource | null): voidinvalidateFrame(): void
CommentSource
What the renderer needs from a comment source. Deliberately tiny — the renderer must not know what a thread IS, only where the pins go and what they are called.
tsinterface CommentSource
Members
getThreads(): readonly CommentThreadView[]— The threads to draw. Called once per BUILT frame (never on a skipped one).getPinOptions(): Pick<CommentPinsOptions, 'selectedThreadId' | 'showResolved' | 'radius'>— Local view state — selection and the resolved filter.
NOT keyboard focus. That is the RENDERER's a11yFocus, which already owns the roving
tabindex for nodes and edges, and a comment pin is just a third kind of thing in the
same one-tab-stop widget. Two authorities for "what is focused" is how you get two
elements with tabindex=0, which is the exact bug the roving tabindex exists to
prevent — so there is one, and it is the one that already existed.
Was this page helpful?