memlnaut-nisps/manifold/src/console/ReshapeModal.tsx
monkey-w1n5t0n 3af92b625a feat(manifold): runtime-shaped net reshape with confirm modal (P2.3)
Wire the runtime-shaped WASM MLP (one-core-engine P2) through the manifold:

- WasmIML.reshape(dims, spread): calls nisps_ml_reshape, re-describes the
  instance, reallocates every dim-dependent heap buffer, refreshes weightCount,
  clears the TS Dataset mirror (C-side resets), drops the stale training worker,
  and pushes the new shape through the sink.
- EngineApi.reshape exposes it and re-ticks the spine so outputs/audio reflect
  the new net. Spine already tolerates the arity change (buffers resize, version
  bumps); documented.
- Training worker protocol carries the current hidden dims; the worker
  ensureNet()s its mirror net to match after a reshape.
- ConsoleApp offers the reshape behind ReshapeModal on an active-layout CHANGE
  (never on load; default 32-input over-provisioned head + zero-padding
  preserved when declined). British copy, reset-on-reshape.
- Drawers: delete the stale even/odd blending note; honest dedicated-dimensions
  line + net-arity chip.
- Probe: __nisps.reshape(nIn) / .describe(); e2e reshape.spec (default 32/126,
  reshape to 4, describe reports 4, bounded outputs, weight count 3148→2868,
  spine still propagates). All 25 e2e pass (20 existing + 5 new).
- ONBOARDING: refresh the reshape status + stale hardwired-arity gotcha.
2026-07-14 03:54:12 +02:00

86 lines
2.9 KiB
TypeScript

/**
* ReshapeModal — confirm modal for reshaping the runtime-shaped net (P2).
*
* Offered when the composed ACTIVE input-axis count changes to something other
* than the net's current input arity (see ConsoleApp's reshape-offer effect).
* The locked decision (manifold-mixed-inputs memory) is a "reset-on-reshape"
* flow: warm-start the weights from the current net, reset examples + explore
* state. Declining keeps the current (over-provisioned, zero-padded) net — so
* the default 32-input head behaviour never changes unless the user opts in.
*
* Small inline modal in the house design language (no external dialog dep).
*/
import { Button } from '../primitives';
export interface ReshapeModalProps {
/** Target input arity (the current active axis count). */
target: number;
/** The net's current input arity, for the copy. */
current: number;
onConfirm: () => void;
onCancel: () => void;
}
export function ReshapeModal({ target, current, onConfirm, onCancel }: ReshapeModalProps) {
return (
<div
role="dialog"
aria-modal="true"
aria-label="Reshape the net"
onClick={onCancel}
style={{
position: 'fixed',
inset: 0,
zIndex: 100,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: 'rgba(0,0,0,0.55)',
backdropFilter: 'blur(2px)',
}}
>
<div
onClick={(e) => e.stopPropagation()}
style={{
width: 'min(420px, 90vw)',
background: 'var(--bg-1)',
border: '1px solid var(--line)',
borderRadius: 'var(--r-2)',
boxShadow: '0 12px 40px rgba(0,0,0,0.5)',
fontFamily: 'var(--font-mono)',
color: 'var(--fg)',
padding: 'var(--sp-4, 18px)',
display: 'flex',
flexDirection: 'column',
gap: 14,
}}
>
<div
style={{
fontSize: 'var(--fs-sm)',
textTransform: 'uppercase',
letterSpacing: '0.08em',
color: 'var(--accent)',
}}
>
Reshape the net?
</div>
<p style={{ margin: 0, fontSize: 'var(--fs-sm)', lineHeight: 1.7, color: 'var(--fg)' }}>
Reshape the net to {target} input{target === 1 ? '' : 's'}? Weights are warm-started from
the current {current}-input net; examples and exploration state reset.
</p>
<p style={{ margin: 0, fontSize: 9, lineHeight: 1.6, color: 'var(--fg-dim)' }}>
Decline to keep the current net the extra axes stay zero-padded (inert).
</p>
<div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
<Button size="sm" variant="ghost" onClick={onCancel}>
Keep current
</Button>
<Button size="sm" variant="primary" onClick={onConfirm}>
Reshape to {target}
</Button>
</div>
</div>
</div>
);
}