diff --git a/MAP.md b/MAP.md
index c58cad0..1743714 100644
--- a/MAP.md
+++ b/MAP.md
@@ -47,7 +47,8 @@ anchor + locked decisions) and the `docs/specs/*-spec.md` set.
- `manifold/src/primitives/` — the 7 design primitives as typed React (Badge, Button, PillToggle, Slider, Switch, VirtualJoystick, XYPad). Five unused ones were deleted in the 2026-07 sweep (L22).
- `manifold/src/console/` — the convertible Console: `ConsoleApp`, `CompositeStage` (single-divider convertible
with snap/magnetism/minimap-demotion), `OutputStage`/`SandwichStage`/`ParticleStage`/`Manifold` (canvas,
- rect↔circular + feedback markers; ParticleStage has interactive cursor-labelled heatmap sliders), `Dock` (top Mode selector + 5 vertically-centred drawers), `Drawers`
+ rect↔circular + feedback markers; ParticleStage has interactive cursor-labelled heatmap sliders, an adjustable
+ joystick, and double-click whole-screen follow-mouse input), `Dock` (top Mode selector + 5 vertically-centred drawers), `Drawers`
(Learning/Inputs/Outputs/Settings/Help; Learning includes the live model-architecture inspector and
expanded Outputs owns the remaining scroll height), `TrainingHealth` (real per-iteration loss curve from
`nisps_ml_loss_history` + per-layer weight health from `nisps_ml_get_layer_stats`; rendered only at
@@ -60,7 +61,7 @@ anchor + locked decisions) and the `docs/specs/*-spec.md` set.
Switching mode reshapes the WASM net to the mode's `ml` dims (ConsoleApp P5.3; boot mode paf_synth →
4→[10,10,14]→33).
- `manifold/src/dock/` — `OutputControlRow` (editable names, corner delete, cycling off/fixed/live status, mute + solo/arm + min/max/curve, plus MIDI card fields), `output-state.ts`,
- `OutputsBackendConfig.tsx` (per-backend specialised Outputs panel — the sole per-backend editor; the centered add-card control lives below the rows).
+ `OutputsBackendConfig.tsx` (per-backend specialised Outputs panel — the sole per-backend editor; centered prepend/append add-card controls live above and below the rows).
- `manifold/src/backends/` — `OutputBackend` adapter + `BackendManager` (spine consumer); `midi-backend.ts`
(WebMIDI), `osc-backend.ts`+`osc-client.ts` (OSC-over-WS), `vcv-backend.ts` (VCV-over-WS), `cv-backend.ts`
(`UseqCvBackend` — uSEQ CV/gate over USB Web Serial, backend id `cvgate`) + `useq-protocol.ts` (v2 wire
diff --git a/manifold/ONBOARDING.md b/manifold/ONBOARDING.md
index c6c03ef..c8a684f 100644
--- a/manifold/ONBOARDING.md
+++ b/manifold/ONBOARDING.md
@@ -109,7 +109,7 @@ Manifold ships a single "composite" altitude. Selection is now a plain three-way
|---|---|---|---|
| CompositeStage | `CompositeStage.tsx` | **default / hero** | Draggable split-ratio; magnet-snaps to 0.14/0.33/0.5/0.66/0.86; collapses a side to a corner minimap at extremes. |
| SandwichStage | `SandwichStage.tsx` | `sandwich===true` (wins over the others) | Three-pane layout: `Manifold` input surface left, 3D parameter-landscape centre (input → MLP heatmap grid → outputs, drag to orbit), compact `OutputStage` right. |
-| ParticleStage | `ParticleStage.tsx` | `outputMode==='particles'` | Flow-field visualiser (`flow-field.ts`, 400-particle Canvas2D port) + interactive output heatmap sliders with cursor tooltips + corner joystick. |
+| ParticleStage | `ParticleStage.tsx` | `outputMode==='particles'` | Flow-field visualiser (`flow-field.ts`, 400-particle Canvas2D port) + interactive output heatmap sliders with cursor tooltips + a larger, explicitly adjustable/repositionable joystick; double-click anywhere in the stage enters whole-screen follow-mouse mode. |
`Manifold.tsx` and `OutputStage.tsx` are no longer top-level stages — they are panes composed by
CompositeStage/SandwichStage. `Manifold.tsx` is the full-bleed 2D input surface (canvas trail + pins
@@ -157,8 +157,9 @@ for the narrow pane.
surface there rather than inventing one. The panel renders "no training run yet" when the
core has no history; it never synthesises a curve.
- **inputs** — enable/configure input sources (XY pad / MIDI / gamepad).
- - **route** (label "Outputs") — per-output control matrix + per-backend config; at expanded depth
- the output rows fill the remaining drawer height and scroll independently.
+ - **route** (label "Outputs") — per-output control matrix + per-backend config; centered prepend/append `+`
+ controls are available in both condensed and expanded views, and at expanded depth the output rows fill the
+ remaining drawer height and scroll independently. The orange drawer tab uses `<` to expand and `>` to condense.
- **settings** — icon style (monochrome/colour), input-map shape (xy/joystick/rect/circular), corner radius.
- **help** — keymap pills + loop explanation.
- `src/dock/` holds the output-routing internals used by the `route` drawer:
@@ -168,7 +169,8 @@ for the narrow pane.
- `OutputControlRow.tsx` — one output card: editable name · M(mute) · S(solo/arm) · cycling
off/fixed/live status · dual-range · live value; the curve pad is a right-hand column and MIDI mode
adds CC#/channel fields. The delete `×` sits on the upper-right corner. **Writes eagerly to the shared
- `MFParam` store via `onChange`.** The centered `+` control follows the last visible card.
+ `MFParam` store via `onChange`.** The Outputs drawer has centered `+` controls that prepend before the first
+ visible card or append after the last.
- `OutputsBackendConfig.tsx` — preset bar (save/restore/rename/delete) + transport/device config (MIDI
port/templates, OSC path/range, VCV polarity); MIDI per-output fields live on `OutputControlRow`.
diff --git a/manifold/src/console/ConsoleApp.tsx b/manifold/src/console/ConsoleApp.tsx
index fab81c7..e49d0ef 100644
--- a/manifold/src/console/ConsoleApp.tsx
+++ b/manifold/src/console/ConsoleApp.tsx
@@ -737,13 +737,11 @@ export function ConsoleApp() {
setOutputCounts((counts) => ({ ...counts, [outputMode]: activeCards.length }));
};
- const addOutput = () => {
+ const addOutput = (placement: 'prepend' | 'append' = 'append') => {
const active = params.slice(0, displayOutputCount);
const next = params[displayOutputCount] ?? createOutputParam(params.length);
- const spares = params
- .slice(displayOutputCount + (params[displayOutputCount] ? 1 : 0))
- .filter((param) => param.id !== next.id);
- applyOutputCards([...active, next], spares);
+ const spares = params.filter((param) => param.id !== next.id && !active.some((p) => p.id === param.id));
+ applyOutputCards(placement === 'prepend' ? [next, ...active] : [...active, next], spares);
};
const deleteOutput = (index: number) => {
diff --git a/manifold/src/console/Dock.tsx b/manifold/src/console/Dock.tsx
index cbad630..720cb8f 100644
--- a/manifold/src/console/Dock.tsx
+++ b/manifold/src/console/Dock.tsx
@@ -31,7 +31,6 @@ import {
EditorIcon,
SandwichIcon,
CloseIcon,
- ExpandIcon,
GLYPH_FALLBACK,
} from './icons';
import type { IconProps } from './icons';
@@ -309,14 +308,14 @@ export function Dock({ ctx, active, setActive, depth, setDepth, sandwich, setSan
top: '50%',
transform: 'translateY(-50%)',
width: 22,
- height: 56,
+ height: 40,
borderRadius: 'var(--r-2) 0 0 var(--r-2)',
border: '1px solid var(--glass-line)',
borderRight: 'none',
background: 'var(--glass)',
backdropFilter: 'blur(14px)',
WebkitBackdropFilter: 'blur(14px)',
- color: 'var(--fg-mute)',
+ color: 'var(--accent)',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
@@ -324,7 +323,9 @@ export function Dock({ ctx, active, setActive, depth, setDepth, sandwich, setSan
zIndex: 1,
}}
>
-