🔮
prediction-cone
v0.2
📦 npm install prediction-cone

Prediction Cone
& Safe Triangle

High-performance TypeScript library for radial context menus with prediction cone pointer selection and dropdown safe triangle submenu navigation.

TypeScript Zero deps ~6 kB gzip ESM + CJS

Quick Start

Install

terminal
npm install prediction-cone
# or: pnpm add prediction-cone / bun add prediction-cone

Prediction cone radial menu

main.ts
import { createPredictionConeMenu } from 'prediction-cone';

const menu = createPredictionConeMenu({
  items: [
    { id: 'cut',   label: 'Cut',   icon: '✂️' },
    { id: 'copy',  label: 'Copy',  icon: '📋' },
    { id: 'paste', label: 'Paste', icon: '📌' },
  ],
  ringRadius: 110,
  coneHalfAngleDeg: 22.5,
  deadzone: 30,
});

menu.on('select', ({ item }) => console.log('Selected:', item.id));
menu.attach(document, { trigger: 'contextmenu' });

Dropdown with safe triangle submenus

main.ts
import { createDropdownMenu } from 'prediction-cone';

const dropdown = createDropdownMenu({
  items: [
    {
      id: 'file',
      label: 'File',
      children: [
        { id: 'new',  label: 'New'  },
        { id: 'open', label: 'Open' },
      ],
    },
  ],
  submenuDelay: 150,
});

dropdown.attach(document.getElementById('btn')!, { trigger: 'click' });

Architecture & Variants

🎯
Prediction Cone

Radial menu — fastest for action selection. Uses pointer direction + velocity to predict intent.

🔺
Safe Triangle

Dropdown with nested submenus — keeps menus open while cursor travels diagonally.

Hybrid UX

Radial for power users, dropdown for broad command sets. Reuse the same item structures.

Comparison

Variant Best for Key options
Prediction cone High-speed action selection, context menus coneHalfAngleDeg, deadzone, showViz
Dropdown + safe triangle Nested labels, discoverable commands children, submenuDelay
Hybrid Power users + broad command sets Shared items structures

createPredictionConeMenu

createPredictionConeMenu(options)

function

Creates a radial prediction cone menu. Returns a ConeMenuInstance.

ConeOptions

OptionTypeDefaultDescription
itemsrequired ConeItem[] Menu items to display
ringRadius number | (vp) ⇒ number 110 Distance from center to item centers (px)
itemSize number | (vp) ⇒ number 56 Width / height of each menu item (px)
deadzone number 30 Radius near center where no selection occurs (px)
coneHalfAngleDeg number 22.5 Half-angle of the selection cone (degrees)
hysteresisDeg number 3 Stability angle to prevent selection flickering (degrees)
startAngleDeg number -90 Starting angle for item distribution (degrees; -90 = top)
edgePadding number 16 Minimum padding from viewport edges (px)
preferSafePlacement boolean true Auto-shift menu to keep all items in viewport
container HTMLElement document.body Parent element for menu DOM nodes
theme "light" | "dark" | Record "light" Visual theme — string preset or CSS variable map
showViz boolean false Render debug canvas overlay (development only)
longPressMs number 250 Long-press hold duration for touch devices (ms)

ConeItem

interface ConeItem {
  id:        string;
  label:     string;
  disabled?: boolean;
  icon?:     string | HTMLElement | (() => HTMLElement);
}

ConeMenuInstance — methods

menu.attach(target, options?)   // bind to Element or Document
menu.detach(target?)            // unbind
menu.openAt(x, y, context?)     // open at coordinates programmatically
menu.close()                    // close the menu
menu.setItems(items)            // replace item list
menu.setOptions(partial)        // patch options at runtime
menu.isOpen()                   // → boolean
menu.getState()                 // → Readonly<ConeMenuState>
menu.destroy()                  // full cleanup

Events

menu.on('open',   e => { /* menu opened            */ })
menu.on('close',  e => { /* menu closed            */ })
menu.on('change', e => { /* highlighted item changed */ })
menu.on('select', e => { /* item confirmed on release */ })

// Every .on() returns an unsubscribe function:
const unsub = menu.on('select', handler);
unsub(); // ← removes the listener

createDropdownMenu

createDropdownMenu(options)

function

Creates a dropdown menu with built-in safe triangle submenu navigation. Supports unlimited nesting via children.

Key options

OptionTypeDefaultDescription
itemsrequired DropdownItem[] Item tree — add children array for submenus
submenuDelay number 150 Grace period before closing submenu during diagonal cursor movement (ms)
theme "light" | "dark" "light" Visual theme

SafeTriangle

new SafeTriangle(options)

class

Low-level safe triangle geometry. Use when building a custom menu system but still want diagonal-movement tolerance that prevents premature submenu closing.

custom-menu.ts
import { SafeTriangle } from 'prediction-cone';

const st = new SafeTriangle({ delay: 150, padding: 2 });

// Call when a submenu opens — pass cursor position + submenu DOMRect
st.activate({ x: 240, y: 180 }, submenuRect);

// Check on every pointermove
if (st.isInSafeZone(pointer.x, pointer.y)) {
  // Cursor is inside the triangle → keep submenu open
}

MouseTracker

new MouseTracker(options?)

class

Tracks pointer position and velocity with RAF-throttled updates. Used internally by the cone menu; also exported for custom integrations.

import { MouseTracker } from 'prediction-cone';

const tracker = new MouseTracker();
tracker.start();

// Anywhere in your render loop:
const { x, y, vx, vy } = tracker.getState();

// Cleanup:
tracker.stop();

API Playground

Add, edit, and manage annotated code snippets. Stored in localStorage — use Export to share as JSON.

Showing 0 example(s)


SEO aliases: prediction cone · safe triangle · safety triangle · menu-aim · submenu navigation.