fix(manifold): preserve output card names on delete
This commit is contained in:
parent
4db0f498eb
commit
a7b6230b8b
3 changed files with 28 additions and 6 deletions
|
|
@ -125,7 +125,9 @@ for the narrow pane.
|
|||
boundary for the stage, backend context, and routing rows. MIDI starts with eight cards; every
|
||||
backend can add a card or delete any individual card in condensed and expanded Outputs drawers.
|
||||
The `N outputs` chip always reports that same set. `MFParam.id` is semantic identity; array position
|
||||
is not. Settings decides whether edits retain spare network capacity or keep exact arity.
|
||||
is not; backend-specific display names (including Particle System names) resolve from that identity,
|
||||
so deleting a middle card cannot transfer its name or settings to the next card. Settings decides
|
||||
whether edits retain spare network capacity or keep exact arity.
|
||||
- `src/console/output-mode.ts`, `types.ts`, `model.ts` are the shared vocabulary — read these first
|
||||
when touching anything cross-cutting:
|
||||
- `types.ts`: `Focus`, `OutputMode`, `DrawerKey`, `DrawerDepth`, `FeedbackModeUI`, `SoloMode`,
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import type { ConsoleCtx, DrawerDepth, DrawerKey, FeedbackModeUI, SoloMode } fro
|
|||
import type { InputMode } from '../inputs';
|
||||
import { OutputControlRow } from '../dock/OutputControlRow';
|
||||
import { OutputsBackendConfig, BackendStatusChip } from '../dock/OutputsBackendConfig';
|
||||
import { shapeValues } from './model';
|
||||
import { shapeValues, type MFParam } from './model';
|
||||
import { outputModeDescriptor } from './output-mode';
|
||||
import { useSettings, unfocusedIconCss } from '../settings/settings-store';
|
||||
import type { UnfocusedIconColour, InputMapMode } from '../settings/settings-store';
|
||||
|
|
@ -734,9 +734,16 @@ function RoutingDrawer(ctx: ConsoleCtx, depth: DrawerDepth) {
|
|||
}, {});
|
||||
const mutedN = activeParams.filter((p) => p.muted).length;
|
||||
const modeDesc = outputModeDescriptor(ctx.outputMode);
|
||||
// The particle Mode names its outputs; otherwise use the param names.
|
||||
const nameFor = (idx: number, fallback: string) =>
|
||||
ctx.outputMode === 'particles' ? VISUAL_NAMES[idx] ?? fallback : fallback;
|
||||
// Particle labels belong to semantic cards, not their current array slots.
|
||||
// A deletion compacts the active prefix, so deriving the label from `idx`
|
||||
// would make the deleted name reappear on its successor.
|
||||
const particleNamesById = new Map(
|
||||
ctx.mode.params.map((param, index) => [param.id, VISUAL_NAMES[index] ?? param.name]),
|
||||
);
|
||||
const nameFor = (param: MFParam) =>
|
||||
ctx.outputMode === 'particles'
|
||||
? particleNamesById.get(param.id) ?? param.name
|
||||
: param.name;
|
||||
|
||||
const expanded = depth === 'expanded';
|
||||
const rows = expanded ? activeParams : activeParams.slice(0, 6);
|
||||
|
|
@ -771,7 +778,7 @@ function RoutingDrawer(ctx: ConsoleCtx, depth: DrawerDepth) {
|
|||
>
|
||||
{rows.map((p) => {
|
||||
const i = ctx.params.indexOf(p);
|
||||
const labelled = { ...p, name: nameFor(i, p.name) };
|
||||
const labelled = { ...p, name: nameFor(p) };
|
||||
return (
|
||||
<OutputControlRow
|
||||
key={p.id}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,19 @@ test('output cards add/delete in both drawer depths and capacity mode avoids rec
|
|||
await expect(page.getByTestId('output-stage')).toHaveAttribute('data-output-count', String(fullCount));
|
||||
});
|
||||
|
||||
test('deleting a named particle card removes that identity instead of shifting its name', async ({ page }) => {
|
||||
await loadProbe(page);
|
||||
|
||||
await page.getByTitle('Outputs').click();
|
||||
await expect(page.getByRole('button', { name: 'Delete Scale output' })).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Delete Scale output' }).click();
|
||||
|
||||
await expect(page.getByText('32 outputs', { exact: true })).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: 'Delete Scale output' })).toHaveCount(0);
|
||||
await expect(page.getByRole('button', { name: 'Delete Speed output' })).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: 'Delete Hue output' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('exact I/O persists and adapts examples across a deleted output identity', async ({ page }) => {
|
||||
await loadProbe(page);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue