Loading files
98b423c28This file is pinned to the selected release. Download the original to open it in your design tool.
import { create } from 'zustand';
export type SectionType = 'hero' | 'case' | 'pcb' | 'assembly' | 'firmware' | 'inside';
// Which top-level home tab is open. Mirrored from RightPanel so sibling
// components (e.g. the 3D viewer panel) can react without prop drilling.
export type HomeTabType = 'hero' | 'build' | 'pattern' | 'inside';
interface AppState {
activeSection: SectionType;
setActiveSection: (section: SectionType) => void;
homeTab: HomeTabType;
setHomeTab: (tab: HomeTabType) => void;
// Which build pin is open on the globe. Lifted out of the globe itself so
// the Inside panel can show the details as a card — on mobile the viewer is
// only 44vh, far too little room for photos and a description.
selectedBuildId: string | null;
setSelectedBuildId: (id: string | null) => void;
insideFilter: 'all' | 'builds' | 'projects' | 'in-use';
setInsideFilter: (filter: AppState['insideFilter']) => void;
knobValues: {
c1: number;
c2: number;
c3: number;
c4: number;
};
setKnobValue: (knobId: 'c1' | 'c2' | 'c3' | 'c4', value: number) => void;
isDraggingKnob: boolean;
setIsDraggingKnob: (isDragging: boolean) => void;
activeKnobId: 'c1' | 'c2' | 'c3' | 'c4' | null;
setActiveKnobId: (id: 'c1' | 'c2' | 'c3' | 'c4' | null) => void;
isBloomEnabled: boolean;
setIsBloomEnabled: (enabled: boolean) => void;
activePatternId: string;
setActivePatternId: (id: string) => void;
customJsCode: string;
setCustomJsCode: (code: string) => void;
buildStep: number;
setBuildStep: (step: number) => void;
// How far the device is pulled apart on build step 3, 0 (assembled) to 1
// (fully exploded). Continuous rather than a flag so the panel can hand the
// separation to the reader instead of animating it at them.
explode: number;
setExplode: (explode: number) => void;
}
export const useAppStore = create<AppState>((set) => ({
activeSection: 'hero',
setActiveSection: (section) => set({ activeSection: section }),
homeTab: 'hero',
setHomeTab: (tab) => set({ homeTab: tab }),
selectedBuildId: null,
setSelectedBuildId: (id) => set({ selectedBuildId: id }),
insideFilter: 'all',
setInsideFilter: (filter) => set({ insideFilter: filter }),
knobValues: {
c1: 0.00, // Hue
c2: 2.00, // Speed
c3: 0.06, // Freq/Offset
c4: 0.00, // Mode
},
setKnobValue: (knobId, value) =>
set((state) => ({
knobValues: {
...state.knobValues,
[knobId]: value,
},
})),
isDraggingKnob: false,
setIsDraggingKnob: (isDragging) => set({ isDraggingKnob: isDragging }),
activeKnobId: null,
setActiveKnobId: (id) => set({ activeKnobId: id }),
isBloomEnabled: true,
setIsBloomEnabled: (enabled) => set({ isBloomEnabled: enabled }),
activePatternId: 'patternFlowOriginal',
setActivePatternId: (id) => set({ activePatternId: id }),
customJsCode: `// Patternflow live editor starter.
// input.knobValues contains the 4 absolute knob values from the preview.
export function setup(params) {
params.time = 0;
}
export function update(dt, input, params) {
const knobs = input.knobValues || [0.5, 2.0, 1.0, 0.6];
params.hue = knobs[0] * 360;
params.speed = Math.max(0.05, knobs[1]);
params.spread = 0.5 + knobs[2] * 0.75;
params.pulse = 0.4 + knobs[3] * 1.8;
params.time += dt * params.speed;
}
export function draw(display, params, globalTime) {
for (let y = 0; y < display.height; y++) {
for (let x = 0; x < display.width; x++) {
let h = params.hue * 0.017 + x * 0.045 * params.spread + y * 0.035 + params.time;
let wave = Math.sin(h * 2 + params.time * 1.5) * 0.5 + 0.5;
let bright = 0.35 + wave * 0.65 * params.pulse;
display.setPixel(
x, y,
(Math.sin(h) * 0.5 + 0.5) * 255 * bright,
(Math.sin(h + 2.1) * 0.5 + 0.5) * 255 * bright,
(Math.sin(h + 4.2) * 0.5 + 0.5) * 255 * bright
);
}
}
}`,
setCustomJsCode: (code) => set({ customJsCode: code }),
buildStep: 0,
setBuildStep: (step) => set({ buildStep: step }),
explode: 1,
setExplode: (val) => set({ explode: val }),
}));