d8098edAn interactive tutorial on the wave equation in acoustics: strings, drums and rooms, measured by earJeremy Magland 1import type { ReactNode } from 'react'
2import type { Params } from '../engine/types.ts'
4export type Control =
5 | {
6 kind: 'slider'
7 key: string
8 label: string
9 min: number
10 max: number
11 step?: number
12 /** discrete choices; the slider then moves over their indices */
13 values?: number[]
14 format?: (v: number) => string
15 }
16 | {
17 kind: 'select'
18 key: string
19 label: string
20 options: { value: string | number; label: string }[]
21 }
22 | { kind: 'toggle'; key: string; label: string }
24export interface LabSpec {
25 id: string
26 title: string
27 /** one line telling the reader what to watch */
28 caption?: string
29 /** the fixed part of the MATLAB layer, hidden from the reader */
30 driver: string
31 /** the editable part: the method itself */
32 script: string
33 controls: Control[]
34 defaults: Params
35 /** turns the control settings into what params.json should contain */
36 derive?: (p: Params) => Params
37 /** heavy labs wait for the Run button instead of running on sight */
38 manual?: boolean
39 /** the editor starts open when the script is the point of the section */
40 editorOpen?: boolean
41 // eslint-disable-next-line @typescript-eslint/no-explicit-any
42 render: (data: any, params: Params) => ReactNode
43 /** numbers for the status strip beside the timing */
44 // eslint-disable-next-line @typescript-eslint/no-explicit-any
45 summary?: (data: any, params: Params) => { label: string; value: string }[]
46}
48export interface Chapter {
49 id: string
50 /** short name for the sidebar */
51 nav: string
52 title: string
53 /** one sentence, shown under the title and in the contents */
54 blurb: string
55 part: Part
56 /** the prose, with <!--lab:id--> lines marking where each lab goes */
57 text: string
58 labs: LabSpec[]
59}
61export const PARTS = ['Introduction', 'Strings', 'Drums', 'Rooms'] as const
62export type Part = (typeof PARTS)[number]