Skip to content

T-6OI8-ui-extract-anchored-position-and-chords

State: closed/done · Impact: high · Complexity: small

The markdown chrome needs three floating overlays (bubble menu, link popover, hover preview) and chord hints, and the code for both already exists in the wrong tier: viewport measure/flip/clamp is inlined in DContextMenu.vue and duplicated in NotesHoverPreview.vue; portable chord parsing lives in app-frame, which markdown-ui must not depend on. This task lifts both into @determined/ui and deletes the originals.

LocationRole today
packages/ts/ui/app/components/DContextMenu.vuesize, MARGIN, position (flip above when overflowing below, clamp left), maxHeight, two-pass measure() — all inline
solutions/determined/experiences/notes/app/components/NotesHoverPreview.vue#anchorToSecond copy: below-or-above by remaining height, clamp to viewport width
packages/ts/app-frame/app/utils/chords.tsparseChord, matchChord, chordHint, isEditableTarget; a local DOs type copy because the frame has no dependency edge to ui
packages/ts/app-frame/tests/chords.test.tsUnit suite over the three functions
packages/ts/app-frame/app/composables/useKeybindings.tsImports matchChord relatively
packages/ts/app-frame/app/composables/useContextMenu.tsImports chordHint relatively
packages/ts/app-frame/app/components/ShellPalette.vueImports chordHint relatively
packages/ts/app-frame/app/components/ScopeFocusBand.vueImports isEditableTarget relatively
packages/ts/app-frame/app/composables/useContributions.tsDoc comment cites the chords module
packages/ts/app-frame/app/composables/usePaneCommands.tsDoc comment cites the chords module
packages/ts/app-frame/app/composables/useDeepLinks.tsHeader comment cites the chords / useKeybindings split
packages/ts/app-frame/app/composables/useNotifications.tsHeader comment cites the same split
packages/ts/app-frame/app/plugins/menu-bridge.client.tsComment cites the chords rule
packages/ts/app-frame/app/utils/deepLinks.tsHeader comment mirrors the chords module’s split
packages/ts/ui/app/utils/keys.tsHeader says it sits here because it cannot import app-frame’s chords module
packages/ts/ui/app/composables/useDOs.ts#DOsThe DOs type the chords module duplicates
solutions/determined/packages/shell/nuxt.config.tsExtends ['@sksizer/app-frame', '@determined/ui']; every app-frame consumer also extends ui (shell, suspenders, system-graph, ontological desktop)

packages/ts/ui/app/composables/useAnchoredPosition.ts:

interface AnchorRect { top: number; left: number; bottom: number; right: number; width: number; height: number }
useAnchoredPosition(anchor: Ref<AnchorRect | null>, el: Ref<HTMLElement | null>, opts?: {
margin?: number // default 8
placement?: 'below' | 'above' | 'auto' // default 'auto': below unless it overflows and above fits
align?: 'start' | 'center' // horizontal, default 'start'
}): {
position: ComputedRef<{ top: number; left: number }>
maxHeight: string // calc(100dvh - 2*margin)
style: ComputedRef<CSSProperties>
measure(): Promise<void> // two-pass: measure, let max-height clamp, measure again
}

A point anchor is a zero-size rect. DContextMenu.vue consumes it with { top: y, left: x, bottom: y, right: x, width: 0, height: 0 } and keeps its behaviour and header rationale (the rationale moves into the composable’s docstring).

chords.ts moves verbatim to packages/ts/ui/app/utils/, importing DOs from the useDOs composable. Its test moves beside it as chords.test.ts (ui’s beside-the-source convention). The four app-frame importers drop their relative imports and use the auto-imported names — every product that extends app-frame also extends @determined/ui, the same route useDOs already takes. Comments that cite the old home are updated.

  1. Write packages/ts/ui/app/composables/useAnchoredPosition.test.ts under happy-dom with stubbed window.innerWidth/innerHeight and offsetWidth/offsetHeight: below by default; flips above when overflowing below and there is room; clamps left and right; maxHeight string; second measure re-reads after the clamp.
  2. Write the composable; rewrite DContextMenu.vue over it; run its stories.
  3. git mv the chords module and its test into ui; change the DOs import; bun test packages/ts/ui.
  4. Remove the four relative imports in app-frame; update the six comment sites and the ui keys.ts header.
  5. bun test packages/ts/app-frame; moon run product-workbench:check (auto-import resolution).
LocationKindChange
packages/ts/ui/app/composables/useAnchoredPosition.tsnewThe composable
packages/ts/ui/app/composables/useAnchoredPosition.test.tsnewFlip, clamp, cap, two-pass tests
packages/ts/ui/app/components/DContextMenu.vuemodifyConsume useAnchoredPosition; delete inline measure/flip/clamp
packages/ts/ui/app/utils/chords*.tsnewchords.ts and chords.test.ts moved from app-frame; DOs imported from useDOs
packages/ts/ui/app/utils/keys.tsmodifyHeader no longer cites app-frame’s chords module
packages/ts/app-frame/app/utils/chords.tsdeleteMoved to ui; importers and comment sites listed below
packages/ts/app-frame/tests/chords.test.tsdeleteMoved to ui
packages/ts/app-frame/app/composables/useKeybindings.tsmodifyDrop relative import
packages/ts/app-frame/app/composables/useContextMenu.tsmodifyDrop relative import
packages/ts/app-frame/app/components/ShellPalette.vuemodifyDrop relative import
packages/ts/app-frame/app/components/ScopeFocusBand.vuemodifyDrop relative import
packages/ts/app-frame/app/composables/useContributions.tsmodifyComment cites the ui home
packages/ts/app-frame/app/composables/usePaneCommands.tsmodifyComment cites the ui home
packages/ts/app-frame/app/composables/useDeepLinks.tsmodifyComment cites the ui home
packages/ts/app-frame/app/composables/useNotifications.tsmodifyComment cites the ui home
packages/ts/app-frame/app/plugins/menu-bridge.client.tsmodifyComment cites the ui home
packages/ts/app-frame/app/utils/deepLinks.tsmodifyComment cites the ui home
packages/ts/ui/README.mdmodifyList useAnchoredPosition and the chord utils
  • AC-1: bun test packages/ts/ui passes, including the anchored-position test and the moved chords test.
  • AC-2: packages/ts/app-frame/app/utils/chords.ts does not exist and grep -rn "utils/chords" packages/ts/app-frame returns nothing.
  • AC-3: grep -n "innerHeight\|innerWidth\|offsetHeight" over packages/ts/ui/app/components/DContextMenu.vue returns nothing; the component positions only through the composable.
  • AC-4: bun test packages/ts/app-frame and moon run product-workbench:check pass.
  • AC-5: the DContextMenu Storybook stories render at a bottom-edge cursor with the menu fully inside the viewport (existing story, visually checked in light and dark).
  • AC-6: the moved chords.ts contains no local DOs declaration (grep -n "type DOs" over it returns nothing).
  • No task dependency. docs/PRINCIPLES.md: generalizing deletes the original, so the app-frame copies go rather than becoming shims.

Member of milestone M-931L; the library-extraction rows of decision D-K3EM-markdown-editor-chrome-and-plugin-environment.


← Back to Tasks