/** * Teaching a chord on the button that performs it, including the buttons this * package does not own. * * Someone who found a feature with the mouse should be able to stop using the * mouse for it. A plugin's own control does that from the inside — read * `chordLabel`, put the chord in the tooltip, follow `onBindings` — and every * omdsh plugin with a button already does. The harness's own controls cannot: * New Session, the session search, add workspace, Settings and the sidebar fold * live in packages this repository does not edit, and a keybinding layer that * arrives from outside the harness is exactly the thing they were never written * to ask. * * So the hint is applied from the outside, and this module is where that choice * is confined. * * ## It writes into somebody else's tooltip rather than raising its own * * The harness renders a tooltip for most of these buttons already * (`ui-primitives`' `Tooltip`, a fixed-position `[role="tooltip"]` plate). A * second bubble beside it would be two tooltips for one button, so the chord is * APPENDED to theirs: one plate, placed and animated and clamped by the code * that owns it, now ending in ` · ⌘K`. Only an appended element is ever added, * never a change to the text React already put there, which is the one * direction a foreign write to a React subtree is safe in. * * A few controls have no tooltip at all — the Settings trigger has never had * one, New Session drops its when the column is wide enough to carry a visible * label — and those get a plate of this package's own, styled from the same * theme variables so the two read as one thing. That is the whole of the * fallback: it appears only where nothing else would have. * * ## How a button is recognized * * By the accessible name it is already wearing, resolved through the SAME * dictionary the button resolved it through: `aria-label` compared against * `locale.bind('sidebar')('session.new.label')`. That is not text scraping — * nothing here reads a rendered string and hopes — it is asking the harness what * it calls its own button and looking for the button that answers to it. It * follows a locale switch for free, and it fails to an empty hint rather than to * a wrong one if a key is ever renamed. * * Two controls carry no accessible name and are addressed the way * {@link ./anchors.ts} addresses everything else: the Settings trigger through * the slot outlet inside it, the settings dialog's Plugins row through the slot * ledger's ordering. Class names, visible text and render order are used * nowhere. * * A composition that adds a command of its own names its button with * {@link MenuItem.anchor}, a CSS selector, and an item's own selector wins over * everything shipped here. * @module @omdsh-plugins/omdsh-shortcuts/src/client/hints */ import type { MenuItem } from '../contract.ts' import { UI_COMMANDS } from '../menu.ts' import { buttonAroundSlot, settingsPages } from './anchors.ts' import { PLUGINS_SECTION_ID, SETTINGS_TRIGGER_SLOT } from './builtins.ts' import { labelIn, settingsPageIndex, type CommandServices } from './services.ts' /** What separates a title from its chord, as every omdsh tooltip writes it. */ export const CHORD_SEPARATOR = '·' /** Marks the node this package appended to a tooltip it does not own. */ export const CHORD_ATTRIBUTE = 'data-omdsh-shortcuts-chord' /** Marks the plate this package renders where the harness renders none. */ export const HINT_ATTRIBUTE = 'data-omdsh-shortcuts-hint' /** * How long a hover waits for the harness's own tooltip before this package * shows one, in ms. * * Past the 500ms hover delay `ui-primitives` uses, so a control that has a * tooltip is always augmented rather than doubled: the wait decides which of * the two mechanisms this hover needs, and guessing early would sometimes pick * both. */ export const HINT_DELAY = 600 /** Gap between a control and a plate this package places, in px; the harness's. */ const BUBBLE_GAP = 10 /** How close to the viewport edge a plate may sit, in px; the harness's. */ const EDGE_MARGIN = 12 /** * What counts as a control a chord can be taught on. * * `