From 8f5a7c22cc256d20c8412a47c17f6263059e4947 Mon Sep 17 00:00:00 2001 From: LucasDLTG Date: Tue, 8 Sep 2026 21:57:38 +0200 Subject: [PATCH] fix: linux display main page --- package-lock.json | 122 +++-------- src-tauri/Cargo.toml | 2 +- src/assets/icons/Triangle.tsx | 6 +- src/components/ChoiceSelect/ChoiceSelect.css | 55 +++++ src/components/ChoiceSelect/ChoiceSelect.tsx | 44 ++++ .../ChoiceStepper/ChoiceStepper.css | 15 +- .../ChoiceStepper/ChoiceStepper.tsx | 194 ++++++++++++++---- .../components/GainSection/GainSection.tsx | 28 ++- .../Matrixsection/Matrixsection.tsx | 2 +- src/features/dsp408/utils/graph/curve.ts | 7 +- src/features/dsp408/utils/graph/draw.ts | 8 +- src/features/dsp408/utils/graph/peq.ts | 2 +- src/pages/AddDspPage/AddDspPage.css | 101 ++++++++- src/pages/AddDspPage/AddDspPage.tsx | 51 ++--- 14 files changed, 442 insertions(+), 195 deletions(-) create mode 100644 src/components/ChoiceSelect/ChoiceSelect.css create mode 100644 src/components/ChoiceSelect/ChoiceSelect.tsx diff --git a/package-lock.json b/package-lock.json index aa0d17b..d8aa872 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1097,9 +1097,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1114,9 +1111,6 @@ "arm" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1131,9 +1125,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1148,9 +1139,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1165,9 +1153,6 @@ "loong64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1182,9 +1167,6 @@ "loong64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1199,9 +1181,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1216,9 +1195,6 @@ "ppc64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1233,9 +1209,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1250,9 +1223,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1267,9 +1237,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1284,9 +1251,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1301,9 +1265,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1493,9 +1454,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0 OR MIT", "optional": true, "os": [ @@ -1513,9 +1471,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "Apache-2.0 OR MIT", "optional": true, "os": [ @@ -1533,9 +1488,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0 OR MIT", "optional": true, "os": [ @@ -1553,9 +1505,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0 OR MIT", "optional": true, "os": [ @@ -1573,9 +1522,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "Apache-2.0 OR MIT", "optional": true, "os": [ @@ -1809,9 +1755,9 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.10.44", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.44.tgz", - "integrity": "sha512-T3ghW+sl/ZJ8w1v/yQx3qvJ9040DWoLBz8JT/CILbAKcFyG9b2MRe75v6W5uXjv6uH1lumK2Kv46y2zSkcej0Q==", + "version": "2.11.21", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.21.tgz", + "integrity": "sha512-uh8vpY/1/YyFkunIDFH/12p7/7VdPKA1hejMVEbdkEaWnUz0Hesvx5EbiU6XxjyHZIOju+ZMbQJkRh+es3/spQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -1822,9 +1768,9 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", - "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "peer": true, @@ -1832,13 +1778,13 @@ "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/browserslist": { - "version": "4.28.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.6.tgz", - "integrity": "sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==", + "version": "4.28.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz", + "integrity": "sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==", "dev": true, "funding": [ { @@ -1856,11 +1802,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.42", - "caniuse-lite": "^1.0.30001803", - "electron-to-chromium": "^1.5.389", - "node-releases": "^2.0.51", - "update-browserslist-db": "^1.2.3" + "baseline-browser-mapping": "^2.11.20", + "caniuse-lite": "^1.0.30001810", + "electron-to-chromium": "^1.5.420", + "node-releases": "^2.0.54", + "update-browserslist-db": "^1.3.2" }, "bin": { "browserslist": "cli.js" @@ -1870,9 +1816,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001806", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", - "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", "dev": true, "funding": [ { @@ -1947,9 +1893,9 @@ "peer": true }, "node_modules/electron-to-chromium": { - "version": "1.5.394", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.394.tgz", - "integrity": "sha512-Wmt2Gm0o8JWBuGgmc4XZ0u9s1RaCRqhxP47phplmfg04+qypTUurpeJGP45A7Fhv7jdrrVH44PLlR9qXo37cVQ==", + "version": "1.5.422", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.422.tgz", + "integrity": "sha512-UvA/32XqrLDdZSn7Jllo1AYNcWji/G0d5M0GTViE7KoGBiMunw3a34Sb2KO4ZZyrSEhqsxFoVhWWJshdyfKqJA==", "dev": true, "license": "ISC" }, @@ -2562,9 +2508,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.16", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", - "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", "dev": true, "funding": [ { @@ -2589,9 +2535,9 @@ "peer": true }, "node_modules/node-releases": { - "version": "2.0.51", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", - "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", + "version": "2.0.54", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz", + "integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==", "dev": true, "license": "MIT", "engines": { @@ -2694,9 +2640,9 @@ } }, "node_modules/postcss": { - "version": "8.5.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.21.tgz", - "integrity": "sha512-v4sDNP3fdNiWMfabO7OwOQdOX8TiQSztKyT1Wj0w+j7LDallJThJRBBBmzVGyYj0crMh7jlV4zepPkiNu9UwDQ==", + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", "dev": true, "funding": [ { @@ -2714,7 +2660,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.16", + "nanoid": "^3.3.18", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -2962,9 +2908,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz", + "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==", "dev": true, "funding": [ { diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index fbd5504..0c64bce 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -22,4 +22,4 @@ tauri = { version = "2", features = [] } tauri-plugin-opener = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" -dsp_thomann = { path = "../../dsp_thomann", features = ["serialization"] } +dsp_thomann = { path = "../../dsp-thomann", features = ["serialization"] } diff --git a/src/assets/icons/Triangle.tsx b/src/assets/icons/Triangle.tsx index a7ced3f..78d2201 100644 --- a/src/assets/icons/Triangle.tsx +++ b/src/assets/icons/Triangle.tsx @@ -2,7 +2,7 @@ export function TriangleUpIcon(props: React.SVGProps) { return ( ) { return ( ) { /> ); -} +} \ No newline at end of file diff --git a/src/components/ChoiceSelect/ChoiceSelect.css b/src/components/ChoiceSelect/ChoiceSelect.css new file mode 100644 index 0000000..2ed13c7 --- /dev/null +++ b/src/components/ChoiceSelect/ChoiceSelect.css @@ -0,0 +1,55 @@ +/* ChoiceSelect.css */ +.choice-select { + position: relative; + display: flex; + align-items: stretch; +} + +.choice-select[data-disabled] { + opacity: 0.5; +} + +.choice-select__input { + flex: 1 1 auto; + height: var(--card-control-height); + padding: 0 var(--space-6) 0 var(--space-3); + + background: var(--bg-panel); + color: var(--text-primary); + + border: var(--border-width) solid var(--border-hairline); + border-radius: var(--radius-sm); + + font-family: var(--font-ui); + font-size: var(--font-sm); + + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + + outline: none; + cursor: pointer; + + transition: border-color var(--dur-base) var(--ease-standard); +} + +.choice-select__input:focus { + border-color: var(--accent-brand); +} + +.choice-select__input:disabled { + cursor: not-allowed; +} + +.choice-select__chevron { + position: absolute; + right: var(--space-2); + top: 50%; + height: 70%; + aspect-ratio: 1; + transform: translateY(-50%); + + pointer-events: none; + color: var(--accent-brand); + font-size: var(--font-sm); +} diff --git a/src/components/ChoiceSelect/ChoiceSelect.tsx b/src/components/ChoiceSelect/ChoiceSelect.tsx new file mode 100644 index 0000000..a005b53 --- /dev/null +++ b/src/components/ChoiceSelect/ChoiceSelect.tsx @@ -0,0 +1,44 @@ +// ChoiceSelect.tsx +import { TriangleDownIcon } from '../../assets/icons/Triangle'; +import './ChoiceSelect.css'; + +type ChoiceSelectProps = { + value: T; + options: readonly T[]; + formatLabel?: (value: T) => string; + onChange: (value: T) => void; + id?: string; + disabled?: boolean; +}; + +function ChoiceSelect({ + value, + options, + formatLabel = (v) => v, + onChange, + id, + disabled, +}: ChoiceSelectProps) { + return ( +
+ + +
+ ); +} + +export default ChoiceSelect; diff --git a/src/components/ChoiceStepper/ChoiceStepper.css b/src/components/ChoiceStepper/ChoiceStepper.css index b65c30a..738d258 100644 --- a/src/components/ChoiceStepper/ChoiceStepper.css +++ b/src/components/ChoiceStepper/ChoiceStepper.css @@ -20,14 +20,13 @@ font-family: var(--font-ui); font-size: 3cqh; font-weight: var(--font-weight-semibold); - text-align: center; + text-align: left; color: var(--accent-brand); background: transparent; border: none; - border-radius: var(--radius-sm); - padding: 0.5ch; + /* padding: 0.5ch; */ white-space: nowrap; overflow: hidden; @@ -35,7 +34,7 @@ } .choice-stepper__input:focus { - outline: none; + outline: none; } .choice-stepper__arrows { @@ -78,3 +77,11 @@ width: 100%; height: 100%; } + +.choice-stepper[data-disabled] { + opacity: 0.5; +} + +.choice-stepper__input:disabled { + cursor: not-allowed; +} diff --git a/src/components/ChoiceStepper/ChoiceStepper.tsx b/src/components/ChoiceStepper/ChoiceStepper.tsx index 1fae96f..1cc69e9 100644 --- a/src/components/ChoiceStepper/ChoiceStepper.tsx +++ b/src/components/ChoiceStepper/ChoiceStepper.tsx @@ -1,60 +1,97 @@ -import { useEffect, useState } from 'react'; +import { JSX, useEffect, useState } from 'react'; import Button from '../Button/Button'; import { TriangleUpIcon, TriangleDownIcon } from '../../assets/icons/Triangle'; import './ChoiceStepper.css'; -type ChoiceStepperProps = { +function StepperArrows({ + onUp, + onDown, + canUp, + canDown, + disabled, +}: { + onUp: () => void; + onDown: () => void; + canUp: boolean; + canDown: boolean; + disabled?: boolean; +}) { + return ( +
+ + +
+ ); +} + +// ---- List variant ---- + +type ChoiceStepperListProps = { value: T; values: readonly T[]; formatLabel: (value: T) => string; onChange: (value: T) => void; + id?: string; + disabled?: boolean; + className?: string; }; -function ChoiceStepper({ value, values, formatLabel, onChange }: ChoiceStepperProps) { +function ChoiceStepperList({ + value, + values, + formatLabel, + onChange, + id, + disabled, + className, +}: ChoiceStepperListProps) { const index = values.indexOf(value); - const canUp = index === -1 || index < values.length - 1; const canDown = index > 0; const [text, setText] = useState(formatLabel(value)); - - useEffect(() => { - setText(formatLabel(value)); - }, [value, formatLabel]); + useEffect(() => setText(formatLabel(value)), [value, formatLabel]); const step = (direction: 1 | -1) => { const currentIndex = index === -1 ? 0 : index; - const nextIndex = Math.min(values.length - 1, Math.max(0, currentIndex + direction)); - - if (nextIndex !== currentIndex) { - onChange(values[nextIndex]); - } + if (nextIndex !== currentIndex) onChange(values[nextIndex]); }; const commitText = () => { const match = values.find( (v) => formatLabel(v).trim().toLowerCase() === text.trim().toLowerCase() ); - - if (match !== undefined) { - onChange(match); - } else { - setText(formatLabel(value)); - } + if (match !== undefined) onChange(match); + else setText(formatLabel(value)); }; return ( -
+
setText(e.target.value)} onBlur={commitText} onKeyDown={(e) => { - if (e.key === 'Enter') { - e.currentTarget.blur(); - } else if (e.key === 'ArrowUp') { + if (e.key === 'Enter') e.currentTarget.blur(); + else if (e.key === 'ArrowUp') { e.preventDefault(); step(1); } else if (e.key === 'ArrowDown') { @@ -63,28 +100,99 @@ function ChoiceStepper({ value, values, formatLabel, onChange }: ChoiceSteppe } }} /> - -
- - - -
+ step(1)} + onDown={() => step(-1)} + canUp={canUp} + canDown={canDown} + disabled={disabled} + />
); } +// ---- Range variant (concrete, no generic) ---- + +type ChoiceStepperRangeProps = { + value: number; + min: number; + max: number; + step?: number; + formatLabel: (value: number) => string; + onChange: (value: number) => void; + id?: string; + disabled?: boolean; + className?: string; +}; + +function ChoiceStepperRange({ + value, + min, + max, + step: stepSize = 1, + formatLabel, + onChange, + id, + disabled, + className, +}: ChoiceStepperRangeProps) { + const [text, setText] = useState(formatLabel(value)); + useEffect(() => setText(formatLabel(value)), [value, formatLabel]); + + const clamp = (n: number) => Math.min(max, Math.max(min, n)); + + const canUp = value < max; + const canDown = value > min; + + const step = (direction: 1 | -1) => { + const next = clamp(value + direction * stepSize); + if (next !== value) onChange(next); + }; + + const commitText = () => { + const parsed = Number(text); + if (Number.isFinite(parsed)) onChange(clamp(parsed)); + else setText(formatLabel(value)); + }; + + return ( +
+ setText(e.target.value)} + onBlur={commitText} + onKeyDown={(e) => { + if (e.key === 'Enter') e.currentTarget.blur(); + else if (e.key === 'ArrowUp') { + e.preventDefault(); + step(1); + } else if (e.key === 'ArrowDown') { + e.preventDefault(); + step(-1); + } + }} + /> + step(1)} + onDown={() => step(-1)} + canUp={canUp} + canDown={canDown} + disabled={disabled} + /> +
+ ); +} + +function ChoiceStepper(props: ChoiceStepperListProps): JSX.Element; +function ChoiceStepper(props: ChoiceStepperRangeProps): JSX.Element; +function ChoiceStepper(props: ChoiceStepperListProps | ChoiceStepperRangeProps) { + if ('values' in props) { + return ; + } + return ; +} + export default ChoiceStepper; diff --git a/src/features/dsp408/components/GainSection/GainSection.tsx b/src/features/dsp408/components/GainSection/GainSection.tsx index 846d4b1..fa94180 100644 --- a/src/features/dsp408/components/GainSection/GainSection.tsx +++ b/src/features/dsp408/components/GainSection/GainSection.tsx @@ -68,8 +68,14 @@ function getPeqCrossoverPoints( getPeqCurvePoints({ freqs, bands: peqChain.bands as any }), getCrossoverCurvePoints({ freqs, - highPass: { frequency: crossover.high_pass.frequency, filter_type: crossover.high_pass.slope as any }, - lowPass: { frequency: crossover.low_pass.frequency, filter_type: crossover.low_pass.slope as any }, + highPass: { + frequency: crossover.high_pass.frequency, + filter_type: crossover.high_pass.slope as any, + }, + lowPass: { + frequency: crossover.low_pass.frequency, + filter_type: crossover.low_pass.slope as any, + }, }), ]); } @@ -317,7 +323,9 @@ function GainSection({ const [showOutputStages, setShowOutputStages] = useState(false); const [liveInputGains, setLiveInputGains] = useState>>({}); - const [liveOutputGains, setLiveOutputGains] = useState>>({}); + const [liveOutputGains, setLiveOutputGains] = useState>>( + {} + ); const setLiveInputGain = (channel: InputChannel, value: number | null) => { setLiveInputGains((prev) => { @@ -381,7 +389,11 @@ function GainSection({ inputColors, showInputStages, (channel) => - getInputChannelCurves(dsp.state.current_config.input_states[channel], freqs, liveInputGains[channel]) + getInputChannelCurves( + dsp.state.current_config.input_states[channel], + freqs, + liveInputGains[channel] + ) ); const outputCurves = buildCurvesForChannels( @@ -390,7 +402,11 @@ function GainSection({ outputColors, showOutputStages, (channel) => - getOutputChannelCurves(dsp.state.current_config.output_states[channel], freqs, liveOutputGains[channel]) + getOutputChannelCurves( + dsp.state.current_config.output_states[channel], + freqs, + liveOutputGains[channel] + ) ); const { yMin: inputYMin, yMax: inputYMax } = computeYAxisBounds( @@ -535,4 +551,4 @@ function GainSection({ ); } -export default GainSection; \ No newline at end of file +export default GainSection; diff --git a/src/features/dsp408/components/Matrixsection/Matrixsection.tsx b/src/features/dsp408/components/Matrixsection/Matrixsection.tsx index 683e99e..a550996 100644 --- a/src/features/dsp408/components/Matrixsection/Matrixsection.tsx +++ b/src/features/dsp408/components/Matrixsection/Matrixsection.tsx @@ -13,7 +13,7 @@ import { } from '../../constants'; import { SidebarId } from '../../constants'; import SignalFlowMatrix from '../../../../components/SignalFlowMatrix/SignalFlowMatrix'; -import './MatrixSection.css'; +import './Matrixsection.css'; const GAIN_VALUES: number[] = (() => { const values: number[] = []; diff --git a/src/features/dsp408/utils/graph/curve.ts b/src/features/dsp408/utils/graph/curve.ts index 110fd37..b75938b 100644 --- a/src/features/dsp408/utils/graph/curve.ts +++ b/src/features/dsp408/utils/graph/curve.ts @@ -58,9 +58,7 @@ export function sumCurvePoints(pointArrays: CurvePoint[][]): CurvePoint[] { return result; } -export function sumPhaseCurvePoints( - pointArrays: CurvePoint[][] -): CurvePoint[] { +export function sumPhaseCurvePoints(pointArrays: CurvePoint[][]): CurvePoint[] { if (pointArrays.length === 0) return []; const length = pointArrays[0].length; @@ -85,8 +83,7 @@ export function sumPhaseCurvePoints( } // Wrap to [-180, 180]. - const wrapped = - ((((phase + 180) % 360) + 360) % 360) - 180; + const wrapped = ((((phase + 180) % 360) + 360) % 360) - 180; result[i] = { x: pointArrays[0][i].x, diff --git a/src/features/dsp408/utils/graph/draw.ts b/src/features/dsp408/utils/graph/draw.ts index 3ab0841..e97b575 100644 --- a/src/features/dsp408/utils/graph/draw.ts +++ b/src/features/dsp408/utils/graph/draw.ts @@ -1,10 +1,4 @@ -import { - CurvePoint, - CurveStyle, - Curve, - DEFAULT_CURVE_COLORS, - sumCurvePoints, -} from './curve'; +import { CurvePoint, CurveStyle, Curve, DEFAULT_CURVE_COLORS, sumCurvePoints } from './curve'; export { sumCurvePoints }; diff --git a/src/features/dsp408/utils/graph/peq.ts b/src/features/dsp408/utils/graph/peq.ts index 6f6d57e..334469e 100644 --- a/src/features/dsp408/utils/graph/peq.ts +++ b/src/features/dsp408/utils/graph/peq.ts @@ -332,4 +332,4 @@ export function getPeqPhasePoints({ x: freq, y: peqTotalPhaseDeg(freq, bands, sampleRate), })); -} \ No newline at end of file +} diff --git a/src/pages/AddDspPage/AddDspPage.css b/src/pages/AddDspPage/AddDspPage.css index 6c78719..194aaa8 100644 --- a/src/pages/AddDspPage/AddDspPage.css +++ b/src/pages/AddDspPage/AddDspPage.css @@ -34,21 +34,26 @@ color: var(--text-muted); } -.add-dsp-form__field input, -.add-dsp-form__field select { +/* Input */ + +.add-dsp-form__field input { height: var(--card-control-height); padding: 0 var(--space-3); background: var(--bg-panel); + color: var(--text-primary); border: var(--border-width) solid var(--border-hairline); border-radius: var(--radius-sm); - color: var(--text-primary); font-family: var(--font-ui); font-size: var(--font-sm); outline: none; + + appearance: none; + -webkit-appearance: none; + transition: border-color var(--dur-base) var(--ease-standard); } @@ -56,11 +61,15 @@ font-family: var(--font-mono); } -.add-dsp-form__field input:focus, -.add-dsp-form__field select:focus { +.add-dsp-form__field input:focus { border-color: var(--accent-brand); } +.add-dsp-form__field input::placeholder { + color: var(--text-muted); + opacity: 0.5; +} + .add-dsp-form__actions { display: flex; justify-content: flex-end; @@ -68,7 +77,89 @@ margin-top: var(--space-2); } +.add-dsp-form__field input:disabled { + color: var(--text-muted); + background: var(--bg-panel); + border-color: var(--border-hairline); + opacity: 0.5; + cursor: not-allowed; +} + +/* CheckBox */ + .add-dsp-form__checkbox-label { display: flex; align-items: center; + gap: var(--space-2); + cursor: pointer; +} + +.add-dsp-form__checkbox-label input[type='checkbox'] { + appearance: none; + -webkit-appearance: none; + box-sizing: border-box; + + flex: 0 0 auto; + display: block; + + width: 18px; + height: 18px; + aspect-ratio: 1 / 1; + padding: 0; + margin: 0; + + background: var(--bg-panel); + border: var(--border-width) solid var(--border-hairline); + border-radius: var(--radius-sm); + + position: relative; + cursor: pointer; + transition: + border-color var(--dur-base) var(--ease-standard), + background var(--dur-base) var(--ease-standard); +} + +.add-dsp-form__checkbox-label input[type='checkbox']:checked { + background: var(--accent-brand); + border-color: var(--accent-brand); +} + +.add-dsp-form__checkbox-label input[type='checkbox']:checked::after { + content: ''; + + position: absolute; + left: 50%; + top: 50%; + + width: 5px; + height: 9px; + + border: solid var(--bg-panel); + border-width: 0 2px 2px 0; + + transform: translate(-50%, -60%) rotate(45deg); +} + +.add-dsp-form__checkbox-label input[type='checkbox']:focus-visible { + outline: 2px solid var(--accent-brand); + outline-offset: 2px; +} + +/* Stepper */ + +.add-dsp-form__stepper { + width: auto; + flex: 0 0 auto; + min-width: 120px; + height: var(--card-control-height); +} + +.add-dsp-form__stepper .choice-stepper__arrows { + padding: 4px 4px 4px 0; + gap: 2px; +} + +.add-dsp-form__field .choice-stepper__input { + border: none; + background: transparent; } diff --git a/src/pages/AddDspPage/AddDspPage.tsx b/src/pages/AddDspPage/AddDspPage.tsx index d3fce1b..0ce060d 100644 --- a/src/pages/AddDspPage/AddDspPage.tsx +++ b/src/pages/AddDspPage/AddDspPage.tsx @@ -2,6 +2,8 @@ import { useState } from 'react'; import { DSP, DSP_DEFAULT_PARAMETERS, DSPTypes } from '../../types'; import Button from '../../components/Button/Button'; import Card from '../../components/Card/Card'; +import ChoiceStepper from '../../components/ChoiceStepper/ChoiceStepper'; +import ChoiceSelect from '../../components/ChoiceSelect/ChoiceSelect'; import './AddDspPage.css'; @@ -14,6 +16,8 @@ type DSPForm = { demo: boolean; }; +const DSP_TYPES = Object.keys(DSP_DEFAULT_PARAMETERS) as DSPTypes[]; + function AddDSPForm({ onCancel, onAdd, @@ -39,9 +43,7 @@ function AddDSPForm({ function submit() { if (!canSubmit) return; - onAdd({ - ...form, - }); + onAdd({ ...form }); } return ( @@ -64,38 +66,24 @@ function AddDSPForm({
- + options={DSP_TYPES} + onChange={(type) => setForm({ ...form, type })} + />
- setForm({ ...form, deviceId: Number(e.target.value) })} + formatLabel={(v) => String(v)} + onChange={(deviceId) => setForm({ ...form, deviceId })} />
@@ -114,13 +102,15 @@ function AddDSPForm({
- setForm({ ...form, port: Number(e.target.value) })} + formatLabel={(v) => String(v)} + onChange={(port) => setForm({ ...form, port })} />
@@ -141,7 +131,6 @@ function AddDSPForm({ -