/// import { cell, Default, handler, lift, recipe, str } from "commontools"; import type { Cell } from "commontools"; type PipelineMode = "double" | "mirror"; /** * Arguments controlling the toggled derive pipeline counter. * `mode` selects which derive pipeline runs against the sanitized count. */ interface TogglePipelineArgs { count: Default; mode: Default; } type PipelineResult = { mapped: number; label: string; status: string; }; type PipelineFn = (value: number) => PipelineResult; const doublePipeline: PipelineFn = (value) => { const mapped = value * 2; return { mapped, label: `Double pipeline -> ${mapped}`, status: `${value} doubled to ${mapped}`, }; }; const mirrorPipeline: PipelineFn = (value) => { const mapped = -value; return { mapped, label: `Mirror pipeline -> ${mapped}`, status: `${value} mirrored to ${mapped}`, }; }; const adjustCount = handler( ( event: { amount?: number } | undefined, context: { count: Cell }, ) => { const amount = typeof event?.amount === "number" ? event.amount : 1; const current = context.count.get(); const base = typeof current === "number" ? current : 0; context.count.set(base + amount); }, ); /** * Counter pattern that swaps between derive pipelines based on an argument- * backed mode cell. The active pipeline reference changes without rebuilding * the surrounding reactive graph. */ export const counterWithToggledDerivePipelines = recipe( "Counter With Toggled Derive Pipelines", ({ count, mode }) => { const switchCount = cell(0); const pipelineHistory = cell([]); let historyState: PipelineMode[] = []; const switchCountView = lift((value: number | undefined) => typeof value === "number" ? value : 0 )(switchCount); const historyView = lift((_: number) => historyState)(switchCountView); const safeCount = lift((value: number | undefined) => typeof value === "number" ? value : 0 )(count); const safeMode = lift((value: PipelineMode | undefined) => value === "mirror" ? "mirror" : "double" )(mode); const pipelineName = safeMode; const pipelineResult = lift((inputs: { mode: PipelineMode | undefined; value: number | undefined; }): PipelineResult => { const sanitizedMode = inputs.mode === "mirror" ? "mirror" : "double"; const pipeline = sanitizedMode === "mirror" ? mirrorPipeline : doublePipeline; const value = typeof inputs.value === "number" ? inputs.value : 0; return pipeline(value); })({ mode: safeMode, value: safeCount, }); const mappedValue = lift((result: PipelineResult) => result.mapped)( pipelineResult, ); const status = lift((result: PipelineResult) => result.status)( pipelineResult, ); const togglePipeline = handler( ( event: { mode?: PipelineMode } | undefined, context: { mode: Cell; switches: Cell; history: Cell; }, ) => { const current = context.mode.get(); const currentMode = current === "mirror" ? "mirror" : "double"; const requested = event?.mode; const next = requested === "mirror" ? "mirror" : requested === "double" ? "double" : currentMode === "mirror" ? "double" : "mirror"; if (next !== currentMode) { context.mode.set(next); const switchCount = context.switches.get(); const steps = typeof switchCount === "number" ? switchCount : 0; context.switches.set(steps + 1); } historyState = [...historyState, next]; context.history.set(historyState); }, ); return { count, mode: safeMode, pipelineName, mappedValue, status, label: str`${pipelineName} mapped ${mappedValue}`, pipelineHistory: historyView, switchCount: switchCountView, increment: adjustCount({ count }), togglePipeline: togglePipeline({ mode, switches: switchCount, history: pipelineHistory, }), }; }, );