/ listening-for-chaos / chaos-apps
Sign in
listening-for-chaos / chaos-apps
Shared styles in src/common/style.css; each app's own rules in its style.css, imported after
Jeremy Magland <jmagland@flatironinstitute.org> committed commit 420fab020b7f parent 5962540 Browse files
9 changed files+156−242
README.mdmodified+1−1View file
@@ -4,7 +4,7 @@ Interactive apps for "Listening for chaos": struck dynamical systems heard as so
44
55 - `src/double-pendulum/`: double pendulum ([README](src/double-pendulum/README.md)), served at `/double-pendulum/`.
66 - `src/fput-chain/`: FPUT-beta chain ([README](src/fput-chain/README.md)), served at `/fput-chain/`.
7-- `src/common/`: code shared by the apps (help dialogs, chart cursor, WAV export, λ vs E scatter).
7+- `src/common/`: code shared by the apps (help dialogs, chart cursor, WAV export, λ vs E scatter, strip charts, whole-note spectrogram) and `style.css`, the shared styles. Each app's `main.ts` imports `../common/style.css` and then its own `style.css`, so app rules come after the shared ones in dev and in the build.
88 - `src/index.html`: landing page linking the apps.
99
1010 Live site: https://chaos-apps--listening-for-chaos.vault1-sites.magland.org/ (built and deployed by `.github/workflows/pages.yml` on every push to main).
src/common/style.cssadded+96−0View file
@@ -0,0 +1,96 @@
1+/* Styles shared by the apps; each page adds its own after this. */
2+
3+:root {
4+ --bg: #f6f6f3;
5+ --card: #ffffff;
6+ --line: #e3e2dc;
7+ --text: #23262b;
8+ --muted: #7a7d82;
9+ --accent: #2f5d8a;
10+ --accent-soft: #e6eef6;
11+ --serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
12+ --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
13+ --mono: ui-monospace, SFMono-Regular, Menlo, monospace;
14+}
15+* { box-sizing: border-box; }
16+body {
17+ margin: 0;
18+ background: var(--bg);
19+ color: var(--text);
20+ font: 14px/1.45 var(--sans);
21+}
22+main { max-width: 1040px; margin: 0 auto; padding: 28px 16px 48px; }
23+header { margin-bottom: 16px; }
24+h1 { font: 400 28px/1.2 var(--serif); margin: 0 0 4px; letter-spacing: 0.01em; }
25+.grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; align-items: start; }
26+@media (max-width: 900px) { .grid { grid-template-columns: minmax(0, 1fr); } }
27+.card { background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 16px; }
28+.label {
29+ font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted);
30+}
31+.num { font: 13px var(--mono); font-variant-numeric: tabular-nums; }
32+.muted { color: var(--muted); font-size: 12px; }
33+input[type="range"] { accent-color: var(--accent); flex: 1; min-width: 120px; margin: 0; }
34+select, input[type="number"] {
35+ font: inherit; font-size: 13px; color: var(--text); background: #fff;
36+ border: 1px solid var(--line); border-radius: 6px; padding: 3px 6px;
37+}
38+button {
39+ font: inherit; font-size: 13px; color: var(--text); background: #fff; cursor: pointer;
40+ border: 1px solid var(--line); border-radius: 6px; padding: 5px 12px;
41+}
42+button:hover { border-color: #c9c8c0; }
43+button.primary { background: var(--accent); border-color: var(--accent); color: #fff; min-width: 76px; }
44+button.primary:hover { filter: brightness(1.08); }
45+button:disabled { color: #b5b5b0; cursor: default; }
46+button.mute { min-width: 36px; height: 30px; padding: 0 9px; display: inline-flex; align-items: center; justify-content: center; gap: 5px; }
47+button.mute svg { width: 18px; height: 18px; flex: none; }
48+button.mute.is-muted { background: #fbece4; border-color: #e8c2ad; color: #b0482a; font-size: 12px; }
49+button.mute.is-muted:hover { border-color: #d9a68c; }
50+button.mute.audible { background: var(--accent-soft); border-color: #b9cde0; color: var(--accent); }
51+#controls { margin-top: 14px; }
52+#ghost-row { margin-top: 10px; min-height: 28px; }
53+#ghost-controls { flex: 1; }
54+#ghost-offset { max-width: 140px; min-width: 80px; }
55+#ghost-sep { width: 100%; }
56+details { margin-top: 14px; border-top: 1px solid var(--line); padding-top: 10px; }
57+summary { cursor: pointer; color: var(--muted); font-size: 13px; list-style: none; display: flex; align-items: center; gap: 4px; }
58+summary::-webkit-details-marker { display: none; }
59+summary::before { content: "▸ "; }
60+details[open] summary::before { content: "▾ "; }
61+details.panel summary .label { font-size: 12px; color: var(--text); }
62+details.panel[open] summary { margin-bottom: 14px; }
63+#pc-energy { max-width: 360px; }
64+#poincare .row + .row { margin-top: 10px; }
65+#param-form label { display: grid; grid-template-columns: 1.6em 5.5em auto; align-items: center; gap: 6px; font-size: 13px; }
66+#param-form .unit { color: var(--muted); font-size: 12px; }
67+#param-actions { margin-top: 10px; }
68+#param-status { color: #a33; font-size: 12px; }
69+#params { margin-top: 10px; font: 12px/1.6 var(--mono); color: var(--muted); white-space: pre; overflow-x: auto; }
70+button.help {
71+ width: 20px; height: 20px; padding: 0; margin-left: auto; flex: none; border-radius: 50%;
72+ font: 600 11px/1 var(--sans); color: #a5a7aa; background: transparent; letter-spacing: 0; text-transform: none;
73+}
74+button.help:hover { color: var(--accent); border-color: #b9cde0; }
75+dialog#help {
76+ max-width: min(460px, calc(100vw - 32px)); padding: 0; border: 1px solid var(--line); border-radius: 10px;
77+ color: var(--text); background: var(--card); box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
78+}
79+dialog#help::backdrop { background: rgba(35, 38, 43, 0.25); }
80+#help-body { padding: 18px 20px 4px; font-size: 13.5px; line-height: 1.5; }
81+#help-body h2 { font: 400 19px/1.3 var(--serif); margin: 0 0 8px; }
82+#help-body p { margin: 0 0 10px; }
83+#help-actions { display: flex; justify-content: flex-end; padding: 0 20px 16px; }
84+.chips { display: flex; flex-wrap: wrap; gap: 6px; }
85+.chip { position: relative; cursor: pointer; user-select: none; }
86+.chip input { position: absolute; opacity: 0; pointer-events: none; }
87+.chip span {
88+ display: inline-block; font-size: 12px; padding: 3px 11px; border-radius: 999px;
89+ border: 1px solid var(--line); color: var(--muted); background: #fff;
90+}
91+.chip:hover span { border-color: #c9c8c0; }
92+.chip input:checked + span { background: var(--accent-soft); border-color: #b9cde0; color: var(--accent); }
93+.chip input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 1px; }
94+figure { margin: 14px 0 0; }
95+.scatter-actions { justify-content: flex-end; margin-top: 6px; }
96+#volume { max-width: 110px; }
src/double-pendulum/index.htmlmodified+0−126View file
@@ -4,132 +4,6 @@
44 <meta charset="UTF-8" />
55 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
66 <title>Double pendulum chaos</title>
7- <style>
8- :root {
9- --bg: #f6f6f3;
10- --card: #ffffff;
11- --line: #e3e2dc;
12- --text: #23262b;
13- --muted: #7a7d82;
14- --accent: #2f5d8a;
15- --accent-soft: #e6eef6;
16- --serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
17- --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
18- --mono: ui-monospace, SFMono-Regular, Menlo, monospace;
19- }
20- * { box-sizing: border-box; }
21- body {
22- margin: 0;
23- background: var(--bg);
24- color: var(--text);
25- font: 14px/1.45 var(--sans);
26- }
27- main { max-width: 1040px; margin: 0 auto; padding: 28px 16px 48px; }
28- header { margin-bottom: 16px; }
29- h1 { font: 400 28px/1.2 var(--serif); margin: 0 0 4px; letter-spacing: 0.01em; }
30- .grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; align-items: start; }
31- @media (max-width: 900px) { .grid { grid-template-columns: minmax(0, 1fr); } }
32- .card { background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 16px; }
33- .label {
34- font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted);
35- }
36- .row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
37- .num { font: 13px var(--mono); font-variant-numeric: tabular-nums; color: var(--text); }
38- .muted { color: var(--muted); font-size: 12px; }
39-
40- canvas { display: block; }
41- canvas[hidden], figure[hidden], [hidden] { display: none !important; }
42- #canvas { width: 100%; aspect-ratio: 1; }
43-
44- input[type="range"] { accent-color: var(--accent); flex: 1; min-width: 120px; margin: 0; }
45- select, input[type="number"] {
46- font: inherit; font-size: 13px; color: var(--text); background: #fff;
47- border: 1px solid var(--line); border-radius: 6px; padding: 3px 6px;
48- }
49- input[type="number"] { width: 5.5em; font-family: var(--mono); }
50- button {
51- font: inherit; font-size: 13px; color: var(--text); background: #fff; cursor: pointer;
52- border: 1px solid var(--line); border-radius: 6px; padding: 5px 12px;
53- }
54- button:hover { border-color: #c9c8c0; }
55- button.primary { background: var(--accent); border-color: var(--accent); color: #fff; min-width: 76px; }
56- button.primary:hover { filter: brightness(1.08); }
57- button:disabled { color: #b5b5b0; cursor: default; }
58- button:disabled:hover { border-color: var(--line); }
59- button.mute { min-width: 36px; height: 30px; padding: 0 9px; display: inline-flex; align-items: center; justify-content: center; gap: 5px; }
60- button.mute svg { width: 18px; height: 18px; flex: none; }
61- button.mute.is-muted { background: #fbece4; border-color: #e8c2ad; color: #b0482a; font-size: 12px; }
62- button.mute.is-muted:hover { border-color: #d9a68c; }
63- button.mute.audible { background: var(--accent-soft); border-color: #b9cde0; color: var(--accent); }
64-
65- #controls { margin-top: 14px; }
66- #controls { gap: 8px; }
67- #energy { min-width: 40px; }
68- #energy-value { min-width: 4.8em; text-align: right; }
69- #ghost-row { margin-top: 10px; min-height: 28px; }
70- #ghost-controls { flex: 1; }
71- #ghost-offset { max-width: 140px; min-width: 80px; }
72- #ghost-sep { width: 100%; }
73-
74- details { margin-top: 14px; border-top: 1px solid var(--line); padding-top: 10px; }
75- summary { cursor: pointer; color: var(--muted); font-size: 13px; list-style: none; display: flex; align-items: center; gap: 4px; }
76- summary::-webkit-details-marker { display: none; }
77- summary::before { content: "▸ "; }
78- details[open] summary::before { content: "▾ "; }
79- details.panel { border-top: 1px solid var(--line); padding-top: 16px; margin-top: 16px; }
80- details.panel summary { gap: 10px; }
81- details.panel summary .label { font-size: 12px; color: var(--text); }
82- details.panel[open] summary { margin-bottom: 14px; }
83- #pc-energy { max-width: 360px; }
84- #poincare .row + .row { margin-top: 10px; }
85- #param-form { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 8px 16px; margin-top: 10px; }
86- #param-form label { display: grid; grid-template-columns: 1.6em 5.5em auto; align-items: center; gap: 6px; font-size: 13px; }
87- #param-form .unit { color: var(--muted); font-size: 12px; }
88- #param-actions { margin-top: 10px; }
89- #param-status { color: #a33; font-size: 12px; }
90- #params { margin-top: 10px; font: 12px/1.6 var(--mono); color: var(--muted); white-space: pre; overflow-x: auto; }
91-
92- .card-head { color: var(--text); font-size: 12px; margin-bottom: 12px; display: flex; align-items: center; }
93-
94- button.help {
95- width: 20px; height: 20px; padding: 0; margin-left: auto; flex: none; border-radius: 50%;
96- font: 600 11px/1 var(--sans); color: #a5a7aa; background: transparent; letter-spacing: 0; text-transform: none;
97- }
98- button.help:hover { color: var(--accent); border-color: #b9cde0; }
99- #pendulum-card { position: relative; }
100- #pendulum-card > button.help { position: absolute; top: 12px; right: 12px; }
101- dialog#help {
102- max-width: min(460px, calc(100vw - 32px)); padding: 0; border: 1px solid var(--line); border-radius: 10px;
103- color: var(--text); background: var(--card); box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
104- }
105- dialog#help::backdrop { background: rgba(35, 38, 43, 0.25); }
106- #help-body { padding: 18px 20px 4px; font-size: 13.5px; line-height: 1.5; }
107- #help-body h2 { font: 400 19px/1.3 var(--serif); margin: 0 0 8px; }
108- #help-body p { margin: 0 0 10px; }
109- #help-actions { display: flex; justify-content: flex-end; padding: 0 20px 16px; }
110- .chips { display: flex; flex-wrap: wrap; gap: 6px; }
111- .chip { position: relative; cursor: pointer; user-select: none; }
112- .chip input { position: absolute; opacity: 0; pointer-events: none; }
113- .chip span {
114- display: inline-block; font-size: 12px; padding: 3px 11px; border-radius: 999px;
115- border: 1px solid var(--line); color: var(--muted); background: #fff;
116- }
117- .chip:hover span { border-color: #c9c8c0; }
118- .chip input:checked + span { background: var(--accent-soft); border-color: #b9cde0; color: var(--accent); }
119- .chip input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 1px; }
120-
121- figure { margin: 14px 0 0; }
122- figure canvas { width: 100%; height: auto; border-radius: 4px; }
123- .scatter-actions { justify-content: flex-end; margin-top: 6px; }
124- #traces canvas, #audio-traces canvas { width: 100%; height: auto; margin-top: 12px; }
125-
126- #audio { margin-top: 16px; }
127- #audio .row + .row { margin-top: 12px; }
128- #audio label { display: flex; align-items: center; gap: 8px; }
129- #audio .grow { flex: 1; min-width: 220px; }
130- #volume { max-width: 110px; }
131- #audio-hint { margin: 14px 0 0; }
132- </style>
1337 </head>
1348 <body>
1359 <main>
src/double-pendulum/main.tsmodified+2−0View file
@@ -1,3 +1,5 @@
1+import "../common/style.css";
2+import "./style.css";
13 import { AudioOut } from "./audio";
24 import { END_RATIO, LAMBDA_EVERY } from "./audio-shared";
35 import { newCursor } from "../common/cursor";
src/double-pendulum/style.cssadded+24−0View file
@@ -0,0 +1,24 @@
1+/* Styles for this app only, after ../common/style.css. */
2+
3+.row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
4+canvas { display: block; }
5+canvas[hidden], figure[hidden], [hidden] { display: none !important; }
6+#canvas { width: 100%; aspect-ratio: 1; }
7+input[type="number"] { width: 5.5em; font-family: var(--mono); }
8+button:disabled:hover { border-color: var(--line); }
9+#controls { gap: 8px; }
10+#energy { min-width: 40px; }
11+#energy-value { min-width: 4.8em; text-align: right; }
12+details.panel { border-top: 1px solid var(--line); padding-top: 16px; margin-top: 16px; }
13+details.panel summary { gap: 10px; }
14+#param-form { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 8px 16px; margin-top: 10px; }
15+.card-head { color: var(--text); font-size: 12px; margin-bottom: 12px; display: flex; align-items: center; }
16+#pendulum-card { position: relative; }
17+#pendulum-card > button.help { position: absolute; top: 12px; right: 12px; }
18+figure canvas { width: 100%; height: auto; border-radius: 4px; }
19+#traces canvas, #audio-traces canvas { width: 100%; height: auto; margin-top: 12px; }
20+#audio { margin-top: 16px; }
21+#audio .row + .row { margin-top: 12px; }
22+#audio label { display: flex; align-items: center; gap: 8px; }
23+#audio .grow { flex: 1; min-width: 220px; }
24+#audio-hint { margin: 14px 0 0; }
src/fput-chain/index.htmlmodified+0−115View file
@@ -4,121 +4,6 @@
44 <meta charset="UTF-8" />
55 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
66 <title>FPUT chain chaos</title>
7- <style>
8- :root {
9- --bg: #f6f6f3;
10- --card: #ffffff;
11- --line: #e3e2dc;
12- --text: #23262b;
13- --muted: #7a7d82;
14- --accent: #2f5d8a;
15- --accent-soft: #e6eef6;
16- --serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
17- --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
18- --mono: ui-monospace, SFMono-Regular, Menlo, monospace;
19- }
20- * { box-sizing: border-box; }
21- body { margin: 0; background: var(--bg); color: var(--text); font: 14px/1.45 var(--sans); }
22- main { max-width: 1040px; margin: 0 auto; padding: 28px 16px 48px; }
23- header { margin-bottom: 16px; }
24- h1 { font: 400 28px/1.2 var(--serif); margin: 0 0 4px; letter-spacing: 0.01em; }
25- .grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; align-items: start; }
26- @media (max-width: 900px) { .grid { grid-template-columns: minmax(0, 1fr); } }
27- .card { background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 16px; }
28- .card-head { font-size: 12px; margin-bottom: 12px; display: flex; align-items: center; }
29- .row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
30- .row + .row { margin-top: 10px; }
31- .num { font: 13px var(--mono); font-variant-numeric: tabular-nums; }
32- .muted { color: var(--muted); font-size: 12px; }
33- canvas { display: block; width: 100%; }
34- [hidden] { display: none !important; }
35- #chain { aspect-ratio: 1.4; }
36- #modes { aspect-ratio: 2.2; }
37- figure { margin: 14px 0 0; }
38- #toggles { display: contents; }
39- #controls { margin-top: 14px; }
40- input[type="range"] { accent-color: var(--accent); flex: 1; min-width: 120px; margin: 0; }
41- select, input[type="number"] {
42- font: inherit; font-size: 13px; color: var(--text); background: #fff;
43- border: 1px solid var(--line); border-radius: 6px; padding: 3px 6px;
44- }
45- input[type="number"] { width: 4.5em; font-family: var(--mono); }
46- button {
47- font: inherit; font-size: 13px; color: var(--text); background: #fff; cursor: pointer;
48- border: 1px solid var(--line); border-radius: 6px; padding: 5px 12px;
49- }
50- button:hover { border-color: #c9c8c0; }
51- button.primary { background: var(--accent); border-color: var(--accent); color: #fff; min-width: 76px; }
52- button.primary:hover { filter: brightness(1.08); }
53- button:disabled { color: #b5b5b0; cursor: default; }
54- #energy-value { min-width: 4.5em; text-align: right; }
55- #ghost-row { margin-top: 10px; min-height: 28px; }
56- #ghost-controls { flex: 1; }
57- #ghost-controls .row, #ghost-controls { margin-top: 0; }
58- #ghost-offset { max-width: 140px; min-width: 80px; }
59- #ghost-sep { width: 100%; }
60- #mode-stats { margin-top: 6px; }
61- .label { font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }
62- details.panel { margin-top: 16px; border: 1px solid var(--line); padding: 16px; }
63- details.panel summary { display: flex; align-items: center; gap: 10px; }
64- details.panel summary .label { font-size: 12px; color: var(--text); }
65- details.panel[open] summary { margin-bottom: 14px; }
66- #sound .row + .row { margin-top: 12px; }
67- #sound label { display: flex; align-items: center; gap: 8px; }
68- #sound .grow { flex: 1; min-width: 220px; }
69- #volume { max-width: 110px; }
70- #sound-mass { width: 3.5em; }
71- #sound-chips { margin-left: auto; }
72- #sound-hint { margin: 14px 0 0; }
73- #spectrogram canvas, #mode-history canvas, #live-modes canvas { width: 100%; height: auto; border-radius: 4px; }
74- #sound-traces canvas { height: auto; margin-top: 12px; }
75- #scatter canvas, #pc-plot canvas { width: 100%; height: auto; }
76- #pc-energy { max-width: 360px; }
77- #poincare .row + .row { margin-top: 10px; }
78- #poincare label { display: flex; align-items: center; gap: 8px; }
79- .scatter-actions { justify-content: flex-end; margin-top: 6px; }
80- button.mute { min-width: 36px; height: 30px; padding: 0 9px; display: inline-flex; align-items: center; justify-content: center; gap: 5px; }
81- button.mute svg { width: 18px; height: 18px; flex: none; }
82- button.mute.is-muted { background: #fbece4; border-color: #e8c2ad; color: #b0482a; font-size: 12px; }
83- button.mute.is-muted:hover { border-color: #d9a68c; }
84- button.mute.audible { background: var(--accent-soft); border-color: #b9cde0; color: var(--accent); }
85- .chips { display: flex; flex-wrap: wrap; gap: 6px; }
86- .chip { position: relative; cursor: pointer; user-select: none; }
87- .chip input { position: absolute; opacity: 0; pointer-events: none; }
88- .chip span {
89- display: inline-block; font-size: 12px; padding: 3px 11px; border-radius: 999px;
90- border: 1px solid var(--line); color: var(--muted); background: #fff;
91- }
92- .chip:hover span { border-color: #c9c8c0; }
93- .chip input:checked + span { background: var(--accent-soft); border-color: #b9cde0; color: var(--accent); }
94- .chip input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 1px; }
95- #traces canvas { height: auto; margin-top: 12px; }
96- details { margin-top: 14px; border-top: 1px solid var(--line); padding-top: 10px; }
97- summary { cursor: pointer; color: var(--muted); font-size: 13px; list-style: none; display: flex; align-items: center; gap: 4px; }
98- button.help {
99- width: 20px; height: 20px; padding: 0; margin-left: auto; flex: none; border-radius: 50%;
100- font: 600 11px/1 var(--sans); color: #a5a7aa; background: transparent; letter-spacing: 0; text-transform: none;
101- }
102- button.help:hover { color: var(--accent); border-color: #b9cde0; }
103- dialog#help {
104- max-width: min(460px, calc(100vw - 32px)); padding: 0; border: 1px solid var(--line); border-radius: 10px;
105- color: var(--text); background: var(--card); box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
106- }
107- dialog#help::backdrop { background: rgba(35, 38, 43, 0.25); }
108- #help-body { padding: 18px 20px 4px; font-size: 13.5px; line-height: 1.5; }
109- #help-body h2 { font: 400 19px/1.3 var(--serif); margin: 0 0 8px; }
110- #help-body p { margin: 0 0 10px; }
111- #help-actions { display: flex; justify-content: flex-end; padding: 0 20px 16px; }
112- summary::-webkit-details-marker { display: none; }
113- summary::before { content: "▸ "; }
114- details[open] summary::before { content: "▾ "; }
115- #param-form { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px 16px; margin-top: 10px; }
116- #param-form label { display: grid; grid-template-columns: 1.6em 5.5em auto; align-items: center; gap: 6px; font-size: 13px; }
117- #param-form .unit { color: var(--muted); font-size: 12px; }
118- #param-actions { margin-top: 10px; }
119- #param-status { color: #a33; font-size: 12px; }
120- #params { margin-top: 10px; font: 12px/1.6 var(--mono); color: var(--muted); white-space: pre; overflow-x: auto; }
121- </style>
1227 </head>
1238 <body>
1249 <main>
src/fput-chain/main.tsmodified+2−0View file
@@ -1,3 +1,5 @@
1+import "../common/style.css";
2+import "./style.css";
13 import {
24 Modes, Verlet, copyState, d0, energy, kinetic, newState, stepFor, potential, strikeMass, strikeMode, type Params, type State,
35 } from "./chain";
src/fput-chain/style.cssadded+28−0View file
@@ -0,0 +1,28 @@
1+/* Styles for this app only, after ../common/style.css. */
2+
3+.card-head { font-size: 12px; margin-bottom: 12px; display: flex; align-items: center; }
4+.row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
5+.row + .row { margin-top: 10px; }
6+canvas { display: block; width: 100%; }
7+[hidden] { display: none !important; }
8+#chain { aspect-ratio: 1.4; }
9+#modes { aspect-ratio: 2.2; }
10+#toggles { display: contents; }
11+input[type="number"] { width: 4.5em; font-family: var(--mono); }
12+#energy-value { min-width: 4.5em; text-align: right; }
13+#ghost-controls .row, #ghost-controls { margin-top: 0; }
14+#mode-stats { margin-top: 6px; }
15+details.panel { margin-top: 16px; border: 1px solid var(--line); padding: 16px; }
16+details.panel summary { display: flex; align-items: center; gap: 10px; }
17+#sound .row + .row { margin-top: 12px; }
18+#sound label { display: flex; align-items: center; gap: 8px; }
19+#sound .grow { flex: 1; min-width: 220px; }
20+#sound-mass { width: 3.5em; }
21+#sound-chips { margin-left: auto; }
22+#sound-hint { margin: 14px 0 0; }
23+#spectrogram canvas, #mode-history canvas, #live-modes canvas { width: 100%; height: auto; border-radius: 4px; }
24+#sound-traces canvas { height: auto; margin-top: 12px; }
25+#scatter canvas, #pc-plot canvas { width: 100%; height: auto; }
26+#poincare label { display: flex; align-items: center; gap: 8px; }
27+#traces canvas { height: auto; margin-top: 12px; }
28+#param-form { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px 16px; margin-top: 10px; }
vite.config.tsmodified+3−0View file
@@ -19,6 +19,9 @@ export default defineConfig({
1919 "double-pendulum": page("double-pendulum/index.html"),
2020 "fput-chain": page("fput-chain/index.html"),
2121 },
22+ // Name the chunk holding src/common/ "common" (otherwise it takes the
23+ // name of one of its modules).
24+ output: { manualChunks: (id: string) => (id.includes("/src/common/") ? "common" : undefined) },
2225 },
2326 },
2427 });
↑↓ move↵openescclose