Order a menu by what comes next

One Choice over the action ids; the probability distribution is the order, so a single question ranks the whole menu. Reordering a menu fights muscle memory, which is what pinTop is for — toggle it and watch the first two slots stop moving.

the request

THE QUESTION — sent as instructions · backticked paths scope what it reads
reference state:
STATE THIS QUESTION RECEIVES — combine freely · sets the pick prop
all channels — the default, so no pick prop is needed
WHAT THEY JUST DID — pick one · what the Rank reads as `recent_actions`
PINTOP — applied in code, no new request
recent: (nothing yet)
  1. New report · 0.000
  2. Open recent · 0.000
  3. Export CSV · 0.000
  4. Schedule delivery · 0.000
  5. Share with team · 0.000
  6. Retry import · 0.000
  7. View import errors · 0.000
  8. Workspace settings · 0.000

what the judgment returned

this render
nothing resolved yet
no judgments resolved yet

your state, as codegenerated from the editor on the right

// app/providers.tsx — the state every question on this page receives
<JevProvider
  resolve={askJev}
  scope={{ app: { product: "Northwind Analytics", user: "ana", role: "analyst", familiarity: "returning" } }}
  initialAmbient={{ recent_actions: [], time_spent: "short" }}
>

the componentapp/dropdown/Demo.tsx

'use client';import { Rank, useJev, type DecisionCost } from 'jev-ui';import { useState } from 'react';// … demo scaffolding — answer panel and the menu catalogueexport function DropdownDemo() {  const { setState, state } = useJev();  const [situation, setSituation] = useState(SITUATIONS[0]!);  const [protectOrder, setProtectOrder] = useState(true);  const [cost, setCost] = useState<DecisionCost | undefined>();  const { pick, ask } = usePick();  function choose(next: (typeof SITUATIONS)[number]) {    setSituation(next);    // Absolute, not appended: a deterministic action log keeps fixtures replayable.    setState({ recent_actions: next.recent });  }  return (    <>      <section className="card" data-testid="demo-live" style={{ display: 'grid', gap: 14 }}>      // … the controls that drive the demo      <div style={{ fontSize: 13, color: 'var(--muted)' }} data-testid="dropdown-recent">        recent: {(state.recent_actions ?? []).length > 0 ? (state.recent_actions ?? []).join(' → ') : '(nothing yet)'}      </div>      <Rank        id="menu_order"        items={ACTIONS}        idOf={(action) => action.id}        labelOf={(action) => `${action.label} — ${action.hint}`}        mode="compete"        pinTop={protectOrder ? 2 : 0}        pick={undefined}   // every channel — the default   ← set by the controls above        onResolved={setCost}        ask="Which of these menu actions is the user most likely to want next, given what they just did in `recent_actions`?"   ← set by the controls above      >        {(ranked, meta) => (          <ol data-testid="menu" data-status={meta.status} style={{ margin: 0, paddingLeft: 20 }}>            {ranked.map((entry) => (              <li key={entry.id} data-testid={`menu-item-${entry.id}`} style={{ marginBottom: 4 }}>                <span style={{ fontWeight: entry.pinned ? 400 : 600 }}>{entry.item.label}</span>                {entry.pinned ? (                  <span style={{ fontSize: 11, color: 'var(--muted)' }} data-testid={`pinned-${entry.id}`}>                    {' '}                    · pinned                  </span>                ) : null}                <span style={{ fontSize: 11, color: 'var(--muted)' }}> · {entry.score.toFixed(3)}</span>              </li>            ))}          </ol>        )}      </Rank>      </section>      // … omitted    </>  );}