Update assumption toggle component

This commit is contained in:
2026-06-10 11:47:42 +02:00
parent 644599cc56
commit 79be15aad6
4 changed files with 343 additions and 332 deletions
+143 -127
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+30 -5
View File
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState, type CSSProperties } from 'react'; import { useEffect, useLayoutEffect, useRef, useState, type CSSProperties } from 'react';
import frameImage from '../technoborder.png'; import frameImage from '../technoborder.png';
import { SOUND_IDS, safeEmit, safePlaySound, safeStopSound } from './hostBridge'; import { SOUND_IDS, safeEmit, safePlaySound, safeStopSound } from './hostBridge';
import type { GlitchComponentProps } from './types'; import type { GlitchComponentProps } from './types';
@@ -326,6 +326,7 @@ export default function AssumptionToggle({
className, className,
host host
}: GlitchComponentProps) { }: GlitchComponentProps) {
const containerRef = useRef<HTMLDivElement | null>(null);
const [toggleState, setToggleState] = useState<ToggleState>(() => readToggleState(config.params)); const [toggleState, setToggleState] = useState<ToggleState>(() => readToggleState(config.params));
const [screen, setScreen] = useState<Screen>('splash'); const [screen, setScreen] = useState<Screen>('splash');
const [transitionState, setTransitionState] = useState<TransitionState>(''); const [transitionState, setTransitionState] = useState<TransitionState>('');
@@ -336,6 +337,7 @@ export default function AssumptionToggle({
); );
const [completedLines, setCompletedLines] = useState<string[]>([]); const [completedLines, setCompletedLines] = useState<string[]>([]);
const [activeLine, setActiveLine] = useState(''); const [activeLine, setActiveLine] = useState('');
const [containerScale, setContainerScale] = useState(1);
const timeoutsRef = useRef<number[]>([]); const timeoutsRef = useRef<number[]>([]);
const hostRef = useRef(host); const hostRef = useRef(host);
const onCompleteRef = useRef(onComplete); const onCompleteRef = useRef(onComplete);
@@ -351,6 +353,29 @@ export default function AssumptionToggle({
onProgressRef.current = onProgress; onProgressRef.current = onProgress;
}, [host, onComplete, onProgress]); }, [host, onComplete, onProgress]);
useLayoutEffect(() => {
const node = containerRef.current;
if (!node) return;
const updateScale = (width: number, height: number) => {
const nextScale = Math.max(Math.min(width, height) / 1000, 0);
setContainerScale((current) => (Math.abs(current - nextScale) < 0.001 ? current : nextScale));
};
updateScale(node.clientWidth, node.clientHeight);
if (typeof ResizeObserver === 'undefined') return;
const observer = new ResizeObserver((entries) => {
const entry = entries[0];
if (!entry) return;
updateScale(entry.contentRect.width, entry.contentRect.height);
});
observer.observe(node);
return () => observer.disconnect();
}, []);
useEffect(() => { useEffect(() => {
if (screen !== 'processing') return; if (screen !== 'processing') return;
@@ -506,12 +531,14 @@ export default function AssumptionToggle({
'--gc-text': theme?.text || '#0f172a', '--gc-text': theme?.text || '#0f172a',
'--gc-text-muted': theme?.textMuted || '#eaffd8', '--gc-text-muted': theme?.textMuted || '#eaffd8',
'--gc-border': theme?.border || 'rgba(255,255,255,0.12)', '--gc-border': theme?.border || 'rgba(255,255,255,0.12)',
'--gc-scale': containerScale.toString(),
'--assumption-frame-image': `url(${frameImage})` '--assumption-frame-image': `url(${frameImage})`
} as CSSProperties; } as CSSProperties;
return ( return (
<div className={`gc-assumption-toggle ${className || ''}`.trim()} style={rootStyle}> <div className={`gc-assumption-toggle ${className || ''}`.trim()} style={rootStyle}>
<div className="component-shell"> <div className="component-shell">
<div ref={containerRef} className="square-container">
<div className="main-wrapper"> <div className="main-wrapper">
<div className={`crt-screen ${showParadoxScreen ? 'paradox-errors' : ''}`}> <div className={`crt-screen ${showParadoxScreen ? 'paradox-errors' : ''}`}>
<div className={`slides-wrapper ${transitionState}`}> <div className={`slides-wrapper ${transitionState}`}>
@@ -617,19 +644,16 @@ export default function AssumptionToggle({
</div> </div>
<div className="result-container"> <div className="result-container">
<div className="summary-box"> <div className="summary-box">
<span className={`result-value result-${result.tone}`}>{result.outcome}</span> <span className={`result-value result-${result.tone}`}>{result.outcome}</span>
</div> </div>
<div className="summary-box"> <div className="summary-box">
<div className="intermediate-result">{result.intermediate}</div> <div className="intermediate-result">{result.intermediate}</div>
</div> </div>
<span className="result-description">{result.description}</span> <span className="result-description">{result.description}</span>
</div> </div>
</div> </div>
<button <button
@@ -673,5 +697,6 @@ export default function AssumptionToggle({
</div> </div>
</div> </div>
</div> </div>
</div>
); );
} }
+35 -65
View File
@@ -10,8 +10,10 @@
--panel-white: rgba(255, 255, 255, 0.92); --panel-white: rgba(255, 255, 255, 0.92);
--font-display: "Russo One", sans-serif; --font-display: "Russo One", sans-serif;
--font-ui: "VT323", sans-serif; --font-ui: "VT323", sans-serif;
--gc-scale: 1;
color: var(--text-primary); color: var(--text-primary);
width: 100%; width: 100%;
aspect-ratio: 1 / 1;
} }
.gc-assumption-toggle *, .gc-assumption-toggle *,
@@ -21,17 +23,27 @@
} }
.gc-assumption-toggle .component-shell { .gc-assumption-toggle .component-shell {
min-height: 100%; width: 100%;
height: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 24px 0;
background: var(--shell-bg); background: var(--shell-bg);
} }
.gc-assumption-toggle .square-container {
width: 100%;
height: 100%;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
.gc-assumption-toggle .main-wrapper { .gc-assumption-toggle .main-wrapper {
width: min(100%, 1000px); width: 1000px;
aspect-ratio: 1000 / 678; height: 678px;
flex: 0 0 auto;
background-image: var(--assumption-frame-image); background-image: var(--assumption-frame-image);
background-position: center; background-position: center;
background-repeat: no-repeat; background-repeat: no-repeat;
@@ -39,6 +51,8 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
transform: scale(var(--gc-scale));
transform-origin: center;
} }
.gc-assumption-toggle .crt-screen { .gc-assumption-toggle .crt-screen {
@@ -97,7 +111,7 @@
.gc-assumption-toggle .splash-content h1 { .gc-assumption-toggle .splash-content h1 {
margin: 0 0 18px; margin: 0 0 18px;
font-size: clamp(1.4rem, 2.7vw, 2.2rem); font-size: 35px;
line-height: 1.3; line-height: 1.3;
color: var(--text-primary); color: var(--text-primary);
font-family: var(--font-display); font-family: var(--font-display);
@@ -109,7 +123,7 @@
.gc-assumption-toggle .splash-content h2 { .gc-assumption-toggle .splash-content h2 {
margin: 0 0 42px; margin: 0 0 42px;
color: var(--text-secondary); color: var(--text-secondary);
font-size: clamp(1rem, 1.5vw, 1.2rem); font-size: 19px;
font-weight: 400; font-weight: 400;
letter-spacing: 0.08em; letter-spacing: 0.08em;
font-family: var(--font-ui); font-family: var(--font-ui);
@@ -118,7 +132,7 @@
.gc-assumption-toggle .widget-header h1 { .gc-assumption-toggle .widget-header h1 {
margin: 0 0 18px; margin: 0 0 18px;
color: var(--text-secondary); color: var(--text-secondary);
font-size: clamp(1.4rem, 2.5vw, 1.8rem); font-size: 29px;
font-weight: 400; font-weight: 400;
letter-spacing: 0.05em; letter-spacing: 0.05em;
text-transform: uppercase; text-transform: uppercase;
@@ -187,7 +201,7 @@
text-align: center; text-align: center;
padding: 10px 8px; padding: 10px 8px;
color: rgba(255, 255, 255, 0.62); color: rgba(255, 255, 255, 0.62);
font-size: clamp(0.78rem, 2.0vw, 1.4rem); font-size: 20px;
font-weight: 500; font-weight: 500;
letter-spacing: 0.04em; letter-spacing: 0.04em;
line-height: 1.15; line-height: 1.15;
@@ -211,7 +225,7 @@
cursor: pointer; cursor: pointer;
text-transform: uppercase; text-transform: uppercase;
font-family: var(--font-display); font-family: var(--font-display);
font-size: clamp(1rem, 2vw, 1.35rem); font-size: 22px;
font-weight: 400; font-weight: 400;
letter-spacing: 0.06em; letter-spacing: 0.06em;
box-shadow: 0 4px 0 rgba(0, 0, 0, 0.28); box-shadow: 0 4px 0 rgba(0, 0, 0, 0.28);
@@ -228,7 +242,7 @@
} }
.gc-assumption-toggle .action-button.secondary { .gc-assumption-toggle .action-button.secondary {
font-size: 1rem; font-size: 16px;
padding: 7px 88px; padding: 7px 88px;
color: var(--text-secondary); color: var(--text-secondary);
background: transparent; background: transparent;
@@ -283,7 +297,7 @@
margin: 0; margin: 0;
color: #dfffe7; color: #dfffe7;
font-family: var(--font-display); font-family: var(--font-display);
font-size: clamp(1.05rem, 2vw, 1.3rem); font-size: 21px;
font-weight: 400; font-weight: 400;
letter-spacing: 0.06em; letter-spacing: 0.06em;
text-transform: uppercase; text-transform: uppercase;
@@ -292,7 +306,7 @@
.gc-assumption-toggle .processing-subtitle { .gc-assumption-toggle .processing-subtitle {
color: rgba(182, 255, 198, 0.72); color: rgba(182, 255, 198, 0.72);
font-family: var(--font-ui); font-family: var(--font-ui);
font-size: clamp(0.88rem, 1.6vw, 1rem); font-size: 16px;
letter-spacing: 0.05em; letter-spacing: 0.05em;
text-transform: uppercase; text-transform: uppercase;
} }
@@ -322,7 +336,7 @@
min-height: 1.45em; min-height: 1.45em;
color: #b7ffca; color: #b7ffca;
font-family: var(--font-ui); font-family: var(--font-ui);
font-size: clamp(1rem, 1.8vw, 1.18rem); font-size: 19px;
letter-spacing: 0.04em; letter-spacing: 0.04em;
line-height: 1.35; line-height: 1.35;
word-break: break-word; word-break: break-word;
@@ -392,7 +406,7 @@
display: block; display: block;
margin-bottom: 8px; margin-bottom: 8px;
color: var(--text-secondary); color: var(--text-secondary);
font-size: 0.84rem; font-size: 13px;
letter-spacing: 0.08em; letter-spacing: 0.08em;
text-transform: uppercase; text-transform: uppercase;
font-family: var(--font-display); font-family: var(--font-display);
@@ -401,7 +415,7 @@
.gc-assumption-toggle .summary-value { .gc-assumption-toggle .summary-value {
display: block; display: block;
color: var(--text-primary); color: var(--text-primary);
font-size: clamp(0.9rem, 1.5vw, 1rem); font-size: 16px;
letter-spacing: 0.04em; letter-spacing: 0.04em;
line-height: 1.4; line-height: 1.4;
font-family: var(--font-ui); font-family: var(--font-ui);
@@ -411,7 +425,7 @@
display: block; display: block;
width: 100%; width: 100%;
margin-top: 4px; margin-top: 4px;
font-size: clamp(1.7rem, 3vw, 2rem); font-size: 32px;
font-weight: 400; font-weight: 400;
letter-spacing: 0.05em; letter-spacing: 0.05em;
background-clip: text; background-clip: text;
@@ -441,7 +455,7 @@
width: 100%; width: 100%;
margin-top: 4px; margin-top: 4px;
color: black; color: black;
font-size: clamp(0.96rem, 1.8vw, 1.08rem); font-size: 17px;
letter-spacing: 0.03em; letter-spacing: 0.03em;
line-height: 1.35; line-height: 1.35;
font-family: var(--font-ui); font-family: var(--font-ui);
@@ -450,7 +464,7 @@
.gc-assumption-toggle .intermediate-result { .gc-assumption-toggle .intermediate-result {
margin-top: 14px; margin-top: 14px;
color: var(--text-secondary); color: var(--text-secondary);
font-size: 0.76rem; font-size: 12px;
font-weight: 400; font-weight: 400;
letter-spacing: 0.08em; letter-spacing: 0.08em;
text-transform: uppercase; text-transform: uppercase;
@@ -558,7 +572,7 @@
top: -25px; top: -25px;
left: 8px; left: 8px;
color: #a10356; color: #a10356;
font-size: 1.4rem; font-size: 22px;
font-weight: 700; font-weight: 700;
text-shadow: 0 0 10px rgba(161, 3, 86, 0.45); text-shadow: 0 0 10px rgba(161, 3, 86, 0.45);
} }
@@ -749,7 +763,7 @@
top: 12px; top: 12px;
right: 16px; right: 16px;
color: rgba(255, 255, 255, 0.85); color: rgba(255, 255, 255, 0.85);
font-size: 1.4rem; font-size: 22px;
font-weight: 700; font-weight: 700;
} }
@@ -899,50 +913,6 @@
50% { opacity: 1; transform: scale(1.08); } 50% { opacity: 1; transform: scale(1.08); }
} }
@media (max-width: 900px) {
.gc-assumption-toggle .component-shell {
padding: 12px 0;
}
.gc-assumption-toggle .switch-group {
gap: 12px;
padding: 0 14px;
}
.gc-assumption-toggle .toggle-track {
min-height: 50px;
}
.gc-assumption-toggle .action-button {
padding: 10px 22px;
}
.gc-assumption-toggle .action-button.secondary {
padding: 6px 36px;
}
.gc-assumption-toggle .result-container {
padding: 14px;
}
.gc-assumption-toggle .intermediate-result {
margin-top: 10px;
font-size: 0.68rem;
}
.gc-assumption-toggle .slide-processing {
padding: 18px 14px 14px;
}
.gc-assumption-toggle .processing-shell {
padding: 12px;
}
.gc-assumption-toggle .processing-terminal {
padding: 12px;
}
}
/* Result-screen overrides after interrupted migration */ /* Result-screen overrides after interrupted migration */
.gc-assumption-toggle .visualization-container { .gc-assumption-toggle .visualization-container {
background: background:
@@ -1188,7 +1158,7 @@
.gc-assumption-toggle .physics-question { .gc-assumption-toggle .physics-question {
color: rgba(255, 248, 210, 0.94); color: rgba(255, 248, 210, 0.94);
font-size: 1.2rem; font-size: 19px;
font-weight: 700; font-weight: 700;
text-shadow: 0 0 12px rgba(255, 226, 130, 0.35); text-shadow: 0 0 12px rgba(255, 226, 130, 0.35);
} }