moontide-mcp 2.0.2 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -6
- package/dist/ask.js +76 -15
- package/dist/mcp.js +198 -26
- package/dist/protocol.js +130 -2
- package/dist/reference.js +22 -0
- package/dist/request-status.js +221 -0
- package/dist/session.js +256 -7
- package/package.json +1 -1
- package/page/ask-view.js +64 -57
- package/page/ask.js +363 -188
- package/page/bar-model.js +43 -2
- package/page/change-timing.js +60 -4
- package/page/harmony.js +13 -0
- package/page/index.html +7 -4
- package/page/jam-analysis.js +68 -23
- package/page/jam-model.js +196 -31
- package/page/jam-resolve.js +271 -0
- package/page/jam-view.js +182 -10
- package/page/lights.js +300 -0
- package/page/main.js +2171 -188
- package/page/midi-clock.js +23 -4
- package/page/midi-out.js +17 -2
- package/page/mixer-groups.js +114 -0
- package/page/mixer-model.js +172 -29
- package/page/mixer-rewrite.js +30 -1
- package/page/mixer-view.js +289 -14
- package/page/note-edit.js +267 -0
- package/page/note-locate.js +346 -0
- package/page/note-panel.js +402 -0
- package/page/note-tag.js +141 -0
- package/page/page-rewrite.js +184 -0
- package/page/query-check.js +21 -2
- package/page/request-status-view.js +419 -0
- package/page/request-status.js +686 -0
- package/page/song-edit.js +230 -0
- package/page/song-fit.js +250 -0
- package/page/song-grid-model.js +506 -0
- package/page/song-grid-view.js +710 -0
- package/page/song-prefs.js +100 -0
- package/page/structure-check.js +216 -0
- package/page/structure-view.js +38 -0
- package/page/structure.js +761 -0
- package/page/style.css +1218 -65
- package/page/takeover.js +20 -5
- package/page/tempo-glide.js +330 -0
- package/page/tempo-guard.js +62 -10
- package/page/timeline-edit-view.js +1956 -0
- package/page/timeline-gesture.js +279 -0
- package/page/timeline.js +197 -3
- package/page/top-bar-view.js +18 -13
- package/page/tracks.js +37 -8
- package/page/ui-state.js +10 -5
- package/page/views.js +39 -9
- package/reference/functions.md +1 -0
- package/reference/midi.md +2 -0
- package/reference/structure.md +131 -0
- package/reference/transitions.md +13 -1
package/page/style.css
CHANGED
|
@@ -365,10 +365,11 @@ body {
|
|
|
365
365
|
|
|
366
366
|
/* A segmented group: a 1px frame on the dominant fill, no padding and no gap, so it is 1.75rem tall plus its frame and
|
|
367
367
|
fits the slim bar. The buttons inside are the hit targets (at least 1.75rem square). The Edits timing group of the
|
|
368
|
-
Settings card
|
|
368
|
+
Settings card and the launch group of the Song grid's toolbar share every rule below; they only set their own text size. */
|
|
369
369
|
#view-buttons,
|
|
370
370
|
#layout-buttons,
|
|
371
|
-
#change-timing
|
|
371
|
+
#change-timing,
|
|
372
|
+
#song-launch {
|
|
372
373
|
display: inline-flex;
|
|
373
374
|
flex: none;
|
|
374
375
|
align-items: stretch;
|
|
@@ -383,7 +384,8 @@ body {
|
|
|
383
384
|
|
|
384
385
|
#view-buttons button,
|
|
385
386
|
#layout-buttons button,
|
|
386
|
-
#change-timing button
|
|
387
|
+
#change-timing button,
|
|
388
|
+
#song-launch button {
|
|
387
389
|
display: inline-flex;
|
|
388
390
|
align-items: center;
|
|
389
391
|
justify-content: center;
|
|
@@ -411,7 +413,8 @@ body {
|
|
|
411
413
|
|
|
412
414
|
#view-buttons button:hover,
|
|
413
415
|
#layout-buttons button:hover,
|
|
414
|
-
#change-timing button:hover
|
|
416
|
+
#change-timing button:hover,
|
|
417
|
+
#song-launch button:hover {
|
|
415
418
|
background: var(--surface-2);
|
|
416
419
|
color: var(--text);
|
|
417
420
|
}
|
|
@@ -420,7 +423,8 @@ body {
|
|
|
420
423
|
changes width when the selection moves. Muted text never sits on this fill. */
|
|
421
424
|
#view-buttons button[aria-pressed="true"],
|
|
422
425
|
#layout-buttons button[aria-pressed="true"],
|
|
423
|
-
#change-timing button[aria-pressed="true"]
|
|
426
|
+
#change-timing button[aria-pressed="true"],
|
|
427
|
+
#song-launch button[aria-pressed="true"] {
|
|
424
428
|
background: var(--surface-2);
|
|
425
429
|
color: var(--text);
|
|
426
430
|
box-shadow: inset 0 0 0 1px var(--border);
|
|
@@ -428,7 +432,8 @@ body {
|
|
|
428
432
|
|
|
429
433
|
#view-buttons button:focus-visible,
|
|
430
434
|
#layout-buttons button:focus-visible,
|
|
431
|
-
#change-timing button:focus-visible
|
|
435
|
+
#change-timing button:focus-visible,
|
|
436
|
+
#song-launch button:focus-visible {
|
|
432
437
|
outline: 1px solid var(--text-muted);
|
|
433
438
|
outline-offset: -1px;
|
|
434
439
|
}
|
|
@@ -440,7 +445,8 @@ body {
|
|
|
440
445
|
max-width: 100%;
|
|
441
446
|
}
|
|
442
447
|
|
|
443
|
-
#change-timing button
|
|
448
|
+
#change-timing button,
|
|
449
|
+
#song-launch button {
|
|
444
450
|
font-size: 0.8125rem;
|
|
445
451
|
}
|
|
446
452
|
|
|
@@ -450,34 +456,6 @@ body {
|
|
|
450
456
|
font-size: 0.8125rem;
|
|
451
457
|
}
|
|
452
458
|
|
|
453
|
-
/* The status of the latest request to Claude. No display rule: #top-bar > span styles it and the browser's [hidden]
|
|
454
|
-
works unaided, so nothing to say takes no width. It sits before the Session button in the markup, so it is right of
|
|
455
|
-
the Layout group. It holds the long sentence and a short form: the short one is for fit step 3 and is aria-hidden. */
|
|
456
|
-
#ask-status {
|
|
457
|
-
flex: none;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
#ask-status .ask-short {
|
|
461
|
-
display: none;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
/* Fit step 3 and 4: the short form shows, and the long sentence is visually hidden but kept in the accessibility tree
|
|
465
|
-
(never display: none), the recipe of #change-status. */
|
|
466
|
-
#top-bar[data-fit="3"] #ask-status .ask-short,
|
|
467
|
-
#top-bar[data-fit="4"] #ask-status .ask-short {
|
|
468
|
-
display: inline;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
#top-bar[data-fit="3"] #ask-status .ask-long,
|
|
472
|
-
#top-bar[data-fit="4"] #ask-status .ask-long {
|
|
473
|
-
position: absolute;
|
|
474
|
-
width: 1px;
|
|
475
|
-
height: 1px;
|
|
476
|
-
overflow: hidden;
|
|
477
|
-
clip-path: inset(50%);
|
|
478
|
-
white-space: nowrap;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
459
|
/* The cycle and beat beside the tempo, quiet decoration (Label size, mono, muted). The hidden attribute
|
|
482
460
|
hides it while nothing plays, so it has no display rule. Fit step 1 drops it with the wordmark (.bar-deco). */
|
|
483
461
|
#top-bar #cycle-beat {
|
|
@@ -488,9 +466,7 @@ body {
|
|
|
488
466
|
}
|
|
489
467
|
|
|
490
468
|
/* Whether page requests reach Claude live. No display rule: #top-bar > span
|
|
491
|
-
styles it and the browser's [hidden] works unaided.
|
|
492
|
-
so it sits right of the request status and left of the motion switch, and the request status
|
|
493
|
-
appearing never moves it. */
|
|
469
|
+
styles it and the browser's [hidden] works unaided. */
|
|
494
470
|
#page-requests {
|
|
495
471
|
flex-shrink: 0;
|
|
496
472
|
}
|
|
@@ -1009,6 +985,300 @@ body {
|
|
|
1009
985
|
font-weight: 400;
|
|
1010
986
|
}
|
|
1011
987
|
|
|
988
|
+
/* The Requests chip, before the Session button: the same box as every control of the bar (1.75rem high, 0.75rem at the
|
|
989
|
+
sides), so it lines up with its neighbour. It is hidden while no request was sent, and the count in its pill is the
|
|
990
|
+
requests still in flight. Neutral colors only: the accent means live now and red belongs to the failed-change banner.
|
|
991
|
+
No transition and no animation: everything changes in one frame. */
|
|
992
|
+
#requests-toggle {
|
|
993
|
+
flex: none;
|
|
994
|
+
display: inline-flex;
|
|
995
|
+
align-items: center;
|
|
996
|
+
gap: 0.25rem;
|
|
997
|
+
box-sizing: border-box;
|
|
998
|
+
min-width: 1.75rem;
|
|
999
|
+
min-height: 1.75rem;
|
|
1000
|
+
padding: 0 0.75rem;
|
|
1001
|
+
background: transparent;
|
|
1002
|
+
border: 1px solid transparent;
|
|
1003
|
+
border-radius: 0.5rem;
|
|
1004
|
+
color: var(--text-muted);
|
|
1005
|
+
font-family: inherit;
|
|
1006
|
+
font-size: 0.9375rem;
|
|
1007
|
+
font-weight: 400;
|
|
1008
|
+
line-height: 1.5;
|
|
1009
|
+
white-space: nowrap;
|
|
1010
|
+
cursor: pointer;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
#requests-toggle[hidden] {
|
|
1014
|
+
display: none;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
#requests-toggle[data-inflight="true"] {
|
|
1018
|
+
color: var(--text);
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
/* The word is the text color on hover, never muted on the fill. */
|
|
1022
|
+
#requests-toggle:hover {
|
|
1023
|
+
background: var(--surface-2);
|
|
1024
|
+
color: var(--text);
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
#requests-toggle[aria-expanded="true"] {
|
|
1028
|
+
color: var(--text);
|
|
1029
|
+
border-color: var(--border);
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
#requests-toggle:focus-visible {
|
|
1033
|
+
outline: 1px solid var(--text-muted);
|
|
1034
|
+
outline-offset: 1px;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
.rq-count {
|
|
1038
|
+
background: var(--surface-2);
|
|
1039
|
+
color: var(--text);
|
|
1040
|
+
border-radius: 1rem;
|
|
1041
|
+
padding: 0 0.5rem;
|
|
1042
|
+
font-family: var(--font-mono);
|
|
1043
|
+
font-size: 0.8125rem;
|
|
1044
|
+
font-weight: 400;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
#requests-toggle[data-inflight="true"] .rq-count {
|
|
1048
|
+
font-weight: 600;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
.rq-count[hidden] {
|
|
1052
|
+
display: none;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
#top-bar[data-fit="4"] #requests-toggle {
|
|
1056
|
+
padding: 0 0.5rem;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
/* The panel under the bar's right end: an overlay that pushes nothing, under the Ask box and the popovers (1000) and
|
|
1060
|
+
the return banner (900), level with the Session drawer (800), which it never shares the screen with. One row per
|
|
1061
|
+
request, newest first; it scrolls inside itself. */
|
|
1062
|
+
#requests-panel {
|
|
1063
|
+
position: absolute;
|
|
1064
|
+
top: 100%;
|
|
1065
|
+
right: 1rem;
|
|
1066
|
+
margin-top: 0.5rem;
|
|
1067
|
+
z-index: 800;
|
|
1068
|
+
box-sizing: border-box;
|
|
1069
|
+
width: 26rem;
|
|
1070
|
+
max-width: calc(100vw - 2rem);
|
|
1071
|
+
max-height: 20rem;
|
|
1072
|
+
overflow-y: auto;
|
|
1073
|
+
padding: 0.5rem;
|
|
1074
|
+
display: flex;
|
|
1075
|
+
flex-direction: column;
|
|
1076
|
+
gap: 0.25rem;
|
|
1077
|
+
background: var(--secondary);
|
|
1078
|
+
border: 1px solid var(--text-muted);
|
|
1079
|
+
box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.45);
|
|
1080
|
+
font-size: 0.8125rem;
|
|
1081
|
+
line-height: 1.5;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
#requests-panel[hidden] {
|
|
1085
|
+
display: none;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
#requests-panel:focus {
|
|
1089
|
+
outline: none;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
#requests-panel:focus-visible {
|
|
1093
|
+
outline: 1px solid var(--text-muted);
|
|
1094
|
+
outline-offset: -1px;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
.rq-list,
|
|
1098
|
+
.rq-recent-body {
|
|
1099
|
+
margin: 0;
|
|
1100
|
+
padding: 0;
|
|
1101
|
+
list-style: none;
|
|
1102
|
+
display: flex;
|
|
1103
|
+
flex-direction: column;
|
|
1104
|
+
gap: 0.25rem;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
.rq-recent-body[hidden] {
|
|
1108
|
+
display: none;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
.rq-recent[hidden] {
|
|
1112
|
+
display: none;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
/* A row is two lines: the start of the request and its time, then a shape and the words of the state. The hover and
|
|
1116
|
+
the open state change the border, never the fill, so muted text is never placed on the lighter surface. */
|
|
1117
|
+
.rq-row {
|
|
1118
|
+
display: grid;
|
|
1119
|
+
grid-template-columns: 0.5rem 1fr auto;
|
|
1120
|
+
column-gap: 0.5rem;
|
|
1121
|
+
align-items: center;
|
|
1122
|
+
width: 100%;
|
|
1123
|
+
min-width: 1.75rem;
|
|
1124
|
+
min-height: 3rem;
|
|
1125
|
+
box-sizing: border-box;
|
|
1126
|
+
padding: 0.5rem 1rem;
|
|
1127
|
+
background: transparent;
|
|
1128
|
+
border: 1px solid transparent;
|
|
1129
|
+
color: var(--text);
|
|
1130
|
+
font-family: inherit;
|
|
1131
|
+
font-size: 0.8125rem;
|
|
1132
|
+
font-weight: 400;
|
|
1133
|
+
line-height: 1.5;
|
|
1134
|
+
text-align: left;
|
|
1135
|
+
cursor: pointer;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
.rq-row:hover {
|
|
1139
|
+
border-color: var(--border);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
.rq-row[aria-expanded="true"] {
|
|
1143
|
+
border-color: var(--border);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
.rq-row:focus-visible,
|
|
1147
|
+
.rq-recent-head:focus-visible {
|
|
1148
|
+
outline: 1px solid var(--text-muted);
|
|
1149
|
+
outline-offset: -1px;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
.rq-text {
|
|
1153
|
+
grid-column: 1 / 3;
|
|
1154
|
+
min-width: 0;
|
|
1155
|
+
overflow: hidden;
|
|
1156
|
+
text-overflow: ellipsis;
|
|
1157
|
+
white-space: nowrap;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
.rq-time {
|
|
1161
|
+
grid-column: 3;
|
|
1162
|
+
font-family: var(--font-mono);
|
|
1163
|
+
color: var(--text-muted);
|
|
1164
|
+
white-space: nowrap;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
/* The shape backs the words up and nothing more: a ring while it goes on, a disc when it is done, a dashed ring when
|
|
1168
|
+
it ended without a change, a square when it did not work. Text colors only. */
|
|
1169
|
+
.rq-mark {
|
|
1170
|
+
grid-column: 1;
|
|
1171
|
+
grid-row: 2;
|
|
1172
|
+
width: 0.5rem;
|
|
1173
|
+
height: 0.5rem;
|
|
1174
|
+
box-sizing: border-box;
|
|
1175
|
+
border-radius: 50%;
|
|
1176
|
+
border: 1px solid var(--text-muted);
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
.rq-mark[data-shape="done"] {
|
|
1180
|
+
background: var(--text);
|
|
1181
|
+
border-color: var(--text);
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
.rq-mark[data-shape="ended"] {
|
|
1185
|
+
border-style: dashed;
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
.rq-mark[data-shape="bad"] {
|
|
1189
|
+
background: var(--text);
|
|
1190
|
+
border-color: var(--text);
|
|
1191
|
+
border-radius: 0;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
.rq-state {
|
|
1195
|
+
grid-column: 2 / 4;
|
|
1196
|
+
grid-row: 2;
|
|
1197
|
+
min-width: 0;
|
|
1198
|
+
overflow: hidden;
|
|
1199
|
+
text-overflow: ellipsis;
|
|
1200
|
+
white-space: nowrap;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
.rq-item[data-finished="true"] .rq-text {
|
|
1204
|
+
color: var(--text-muted);
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
.rq-detail {
|
|
1208
|
+
box-sizing: border-box;
|
|
1209
|
+
margin: 0.25rem 0 0;
|
|
1210
|
+
padding: 0.5rem;
|
|
1211
|
+
max-height: 8rem;
|
|
1212
|
+
overflow-y: auto;
|
|
1213
|
+
background: var(--dominant);
|
|
1214
|
+
border: 1px solid var(--border);
|
|
1215
|
+
overflow-wrap: anywhere;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
.rq-detail[hidden] {
|
|
1219
|
+
display: none;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
.rq-detail p {
|
|
1223
|
+
margin: 0 0 0.5rem;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
.rq-detail p:last-child {
|
|
1227
|
+
margin-bottom: 0;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
.rq-detail .rq-about,
|
|
1231
|
+
.rq-detail .rq-basis {
|
|
1232
|
+
color: var(--text-muted);
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
.rq-detail .rq-reason {
|
|
1236
|
+
font-family: var(--font-mono);
|
|
1237
|
+
white-space: pre-wrap;
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
.rq-recent-head {
|
|
1241
|
+
display: flex;
|
|
1242
|
+
align-items: center;
|
|
1243
|
+
gap: 0.5rem;
|
|
1244
|
+
width: 100%;
|
|
1245
|
+
min-width: 2rem;
|
|
1246
|
+
min-height: 2rem;
|
|
1247
|
+
box-sizing: border-box;
|
|
1248
|
+
padding: 0 1rem;
|
|
1249
|
+
background: transparent;
|
|
1250
|
+
border: 1px solid transparent;
|
|
1251
|
+
color: var(--text-muted);
|
|
1252
|
+
font-family: inherit;
|
|
1253
|
+
font-size: 0.8125rem;
|
|
1254
|
+
font-weight: 400;
|
|
1255
|
+
line-height: 1.5;
|
|
1256
|
+
text-align: left;
|
|
1257
|
+
cursor: pointer;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
.rq-recent-head:hover {
|
|
1261
|
+
color: var(--text);
|
|
1262
|
+
border-color: var(--border);
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
.rq-recent .rq-item .rq-state {
|
|
1266
|
+
color: var(--text-muted);
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
/* The spoken line of the Requests list: visually hidden, kept in the accessibility tree, always present and never
|
|
1270
|
+
[hidden] (the recipe of #change-status). */
|
|
1271
|
+
#requests-live {
|
|
1272
|
+
position: absolute;
|
|
1273
|
+
width: 1px;
|
|
1274
|
+
height: 1px;
|
|
1275
|
+
overflow: hidden;
|
|
1276
|
+
clip-path: inset(50%);
|
|
1277
|
+
white-space: nowrap;
|
|
1278
|
+
font-size: 0.8125rem;
|
|
1279
|
+
line-height: 1.5;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1012
1282
|
/* The strip: one muted line under the bar, only while the drawer is closed and a condition holds. */
|
|
1013
1283
|
#session-strip {
|
|
1014
1284
|
display: flex;
|
|
@@ -1645,7 +1915,9 @@ body {
|
|
|
1645
1915
|
grid-row: 1;
|
|
1646
1916
|
}
|
|
1647
1917
|
|
|
1648
|
-
|
|
1918
|
+
/* A lane's event area fills the lane's whole cell, so the rings, ghosts and washes drawn over it (the mark layer fills the same
|
|
1919
|
+
cell) sit on the notes whatever height the lane's name column gives the lane. */
|
|
1920
|
+
.lane-events {
|
|
1649
1921
|
align-self: stretch;
|
|
1650
1922
|
width: 100%;
|
|
1651
1923
|
height: 100%;
|
|
@@ -1692,17 +1964,245 @@ body {
|
|
|
1692
1964
|
.lane[data-drum-rows="7"] { min-height: 8.75rem; }
|
|
1693
1965
|
.lane[data-drum-rows="8"] { min-height: 10rem; }
|
|
1694
1966
|
|
|
1695
|
-
/* The
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
.
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1967
|
+
/* The editable Timeline: the panel under the lanes, and the marks on a lane. The panel is a quiet strip that is always the
|
|
1968
|
+
same height, so the lanes do not move under the pointer when a note is picked; its two text lines keep their room even
|
|
1969
|
+
when empty. A selected note gets a solid ring, a preview a dashed one, the other places of the same token a dotted one
|
|
1970
|
+
and a range of cycles a wash with two edges: outlines and words only, never the accent colors, which keep their uses
|
|
1971
|
+
(the playhead is the only accent on the Timeline). The marks sit on top of a lane in its own grid cell, never take the
|
|
1972
|
+
pointer and are hidden from screen readers. Nothing here moves, fades or animates. */
|
|
1973
|
+
#timeline-note {
|
|
1974
|
+
flex: 0 0 auto;
|
|
1975
|
+
box-sizing: border-box;
|
|
1976
|
+
display: flex;
|
|
1977
|
+
flex-wrap: wrap;
|
|
1978
|
+
align-items: center;
|
|
1979
|
+
gap: 0.25rem 1rem;
|
|
1980
|
+
padding: 0.5rem 1rem;
|
|
1981
|
+
background: var(--secondary);
|
|
1982
|
+
border-top: 1px solid var(--border);
|
|
1983
|
+
font-size: 0.8125rem;
|
|
1984
|
+
font-weight: 400;
|
|
1985
|
+
line-height: 1.5;
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
#timeline-note[hidden] {
|
|
1989
|
+
display: none;
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
.tl-note-where {
|
|
1993
|
+
min-width: 0;
|
|
1994
|
+
min-height: 2rem;
|
|
1995
|
+
display: flex;
|
|
1996
|
+
align-items: center;
|
|
1997
|
+
color: var(--text-muted);
|
|
1998
|
+
overflow-wrap: anywhere;
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
/* The spaces at the start of the position text are kept when the lane name and the position sit side by side. */
|
|
2002
|
+
.tl-note-where > span {
|
|
2003
|
+
white-space: pre-wrap;
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
.tl-lane-name {
|
|
2007
|
+
font-weight: 600;
|
|
2008
|
+
color: var(--text);
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
.tl-note-state {
|
|
2012
|
+
color: var(--text-muted);
|
|
2013
|
+
}
|
|
2014
|
+
|
|
2015
|
+
.tl-note-controls {
|
|
2016
|
+
display: flex;
|
|
2017
|
+
flex-wrap: wrap;
|
|
2018
|
+
align-items: center;
|
|
2019
|
+
gap: 0.5rem 1rem;
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
.tl-note-controls[hidden] {
|
|
2023
|
+
display: none;
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
.tl-group {
|
|
2027
|
+
display: flex;
|
|
2028
|
+
align-items: center;
|
|
2029
|
+
gap: 0.25rem;
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
/* A range offers only Ask Claude: the groups and the Make a rest button are hidden, and their display must not undo that. */
|
|
2033
|
+
.tl-group[hidden],
|
|
2034
|
+
.tl-btn[hidden] {
|
|
2035
|
+
display: none;
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
.tl-label {
|
|
2039
|
+
color: var(--text-muted);
|
|
2040
|
+
margin-right: 0.25rem;
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
.tl-value {
|
|
2044
|
+
font-family: var(--font-mono);
|
|
2045
|
+
color: var(--text);
|
|
2046
|
+
margin-right: 0.25rem;
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
.tl-fixed {
|
|
2050
|
+
color: var(--text-muted);
|
|
2051
|
+
margin-right: 0.25rem;
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
.tl-btn {
|
|
2055
|
+
box-sizing: border-box;
|
|
2056
|
+
min-width: 2rem;
|
|
2057
|
+
height: 2rem;
|
|
2058
|
+
padding: 0 0.5rem;
|
|
2059
|
+
background: var(--surface-2);
|
|
2060
|
+
border: 1px solid var(--border);
|
|
2061
|
+
color: var(--text);
|
|
2062
|
+
font-family: inherit;
|
|
2063
|
+
font-size: 0.8125rem;
|
|
2064
|
+
font-weight: 400;
|
|
2065
|
+
line-height: 1.5;
|
|
2066
|
+
cursor: pointer;
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
.tl-btn:hover:not([aria-disabled="true"]) {
|
|
2070
|
+
border-color: var(--text-muted);
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
.tl-btn[aria-disabled="true"] {
|
|
2074
|
+
border-style: dashed;
|
|
2075
|
+
cursor: default;
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
.tl-btn:focus-visible {
|
|
2079
|
+
outline: 1px solid var(--text-muted);
|
|
2080
|
+
outline-offset: 1px;
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
#timeline-note-line,
|
|
2084
|
+
.tl-note-hint {
|
|
2085
|
+
flex: 1 0 100%;
|
|
2086
|
+
margin: 0;
|
|
2087
|
+
min-height: 1.5rem;
|
|
2088
|
+
color: var(--text-muted);
|
|
2089
|
+
overflow-wrap: anywhere;
|
|
2090
|
+
}
|
|
2091
|
+
|
|
2092
|
+
#timeline-note-line[data-kind="refused"],
|
|
2093
|
+
#timeline-note-line[data-kind="result"],
|
|
2094
|
+
#timeline-note-line[data-kind="notice"],
|
|
2095
|
+
#timeline-note-line[data-kind="preview"] {
|
|
2096
|
+
color: var(--text);
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
#workspace[data-layout="b"] #timeline-note-line,
|
|
2100
|
+
#workspace[data-layout="b"] .tl-note-hint {
|
|
2101
|
+
min-height: 4rem;
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
/* In the side panel the buttons wrap into rows when a note is selected; the panel reserves their room, so it keeps one height
|
|
2105
|
+
whether nothing, a note or a drag is shown and the lanes do not move when a note is picked. */
|
|
2106
|
+
#workspace[data-layout="b"] #timeline-note {
|
|
2107
|
+
min-height: 19rem;
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
#timeline-lanes:focus-visible {
|
|
2111
|
+
outline: 1px solid var(--text-muted);
|
|
2112
|
+
outline-offset: -1px;
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
#timeline-lanes[data-dragging] {
|
|
2116
|
+
user-select: none;
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
/* The pointer only confirms what the words say: a cross over empty lane area, a hand over a note, up-and-down arrows while an
|
|
2120
|
+
editable note is dragged, the plain arrow while a note the page cannot edit is dragged, a cross for a drag across the lanes.
|
|
2121
|
+
The lanes hold the pointer during a drag, so they carry the cursor themselves while one lasts. */
|
|
2122
|
+
#timeline-lanes .lane-events {
|
|
2123
|
+
cursor: crosshair;
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
#timeline-lanes[data-hover="note"] .lane-events {
|
|
2127
|
+
cursor: pointer;
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
#timeline-lanes[data-dragging="pitch"],
|
|
2131
|
+
#timeline-lanes[data-dragging="pitch"] .lane-events {
|
|
2132
|
+
cursor: ns-resize;
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
#timeline-lanes[data-dragging="refused"],
|
|
2136
|
+
#timeline-lanes[data-dragging="refused"] .lane-events {
|
|
2137
|
+
cursor: default;
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
#timeline-lanes[data-dragging="range"],
|
|
2141
|
+
#timeline-lanes[data-dragging="range"] .lane-events {
|
|
2142
|
+
cursor: crosshair;
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
.lane-marks {
|
|
2146
|
+
grid-column: 2;
|
|
2147
|
+
grid-row: 1;
|
|
2148
|
+
position: relative;
|
|
2149
|
+
pointer-events: none;
|
|
2150
|
+
min-width: 0;
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
.tl-ring,
|
|
2154
|
+
.tl-ghost,
|
|
2155
|
+
.tl-same {
|
|
2156
|
+
position: absolute;
|
|
2157
|
+
box-sizing: border-box;
|
|
2158
|
+
min-width: 0.5rem;
|
|
2159
|
+
outline-offset: 0.25rem;
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
.tl-ring {
|
|
2163
|
+
outline: 1px solid var(--text);
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
.tl-ghost {
|
|
2167
|
+
outline: 1px dashed var(--text);
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
.tl-same {
|
|
2171
|
+
outline: 1px dotted var(--text-muted);
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
/* The element the lanes name with aria-activedescendant: it carries the name of the active note or lane for screen readers and
|
|
2175
|
+
paints nothing. It is not hidden from them, so it is clipped to nothing instead of taking display: none. */
|
|
2176
|
+
.tl-active {
|
|
2177
|
+
position: absolute;
|
|
2178
|
+
width: 1px;
|
|
2179
|
+
height: 1px;
|
|
2180
|
+
overflow: hidden;
|
|
2181
|
+
clip-path: inset(50%);
|
|
2182
|
+
pointer-events: none;
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
.tl-range {
|
|
2186
|
+
position: absolute;
|
|
2187
|
+
top: 0;
|
|
2188
|
+
bottom: 0;
|
|
2189
|
+
box-sizing: border-box;
|
|
2190
|
+
background: color-mix(in srgb, var(--text) 14%, transparent);
|
|
2191
|
+
border-left: 1px solid var(--text-muted);
|
|
2192
|
+
border-right: 1px solid var(--text-muted);
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
/* The arrange() sections above the lanes — same grid columns as .lane, so
|
|
2196
|
+
a section span lines up with the ruler/lane track column beneath it. No accent is used here
|
|
2197
|
+
(accent stays reserved for "live now"/"structural" states, never a static
|
|
2198
|
+
section label). */
|
|
2199
|
+
.section-row {
|
|
2200
|
+
display: grid;
|
|
2201
|
+
grid-template-columns: 8.5em minmax(0, 1fr);
|
|
2202
|
+
height: 1.5em;
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
.section-row-gutter {
|
|
1706
2206
|
display: flex;
|
|
1707
2207
|
align-items: center;
|
|
1708
2208
|
min-width: 0;
|
|
@@ -2289,6 +2789,7 @@ body {
|
|
|
2289
2789
|
#parts-view {
|
|
2290
2790
|
flex: 1;
|
|
2291
2791
|
min-height: 0;
|
|
2792
|
+
min-width: 0;
|
|
2292
2793
|
display: flex;
|
|
2293
2794
|
flex-direction: column;
|
|
2294
2795
|
padding: 1rem;
|
|
@@ -2414,29 +2915,384 @@ body {
|
|
|
2414
2915
|
border-color: var(--accent);
|
|
2415
2916
|
}
|
|
2416
2917
|
|
|
2417
|
-
.part-card[data-state="queued"] {
|
|
2418
|
-
border-color: var(--accent-2);
|
|
2918
|
+
.part-card[data-state="queued"] {
|
|
2919
|
+
border-color: var(--accent-2);
|
|
2920
|
+
}
|
|
2921
|
+
|
|
2922
|
+
.part-card[data-state="queued"] .part-state {
|
|
2923
|
+
color: var(--accent-2);
|
|
2924
|
+
}
|
|
2925
|
+
|
|
2926
|
+
.part-progress {
|
|
2927
|
+
display: block;
|
|
2928
|
+
height: 0.25rem;
|
|
2929
|
+
background: var(--border);
|
|
2930
|
+
overflow: hidden;
|
|
2931
|
+
}
|
|
2932
|
+
|
|
2933
|
+
.part-progress-fill {
|
|
2934
|
+
display: block;
|
|
2935
|
+
height: 100%;
|
|
2936
|
+
width: 0;
|
|
2937
|
+
}
|
|
2938
|
+
|
|
2939
|
+
.part-card[data-state="playing"] .part-progress-fill {
|
|
2940
|
+
background: var(--accent);
|
|
2941
|
+
}
|
|
2942
|
+
|
|
2943
|
+
/* The lines above the Song grid in the Parts view. #song-notes sits between the message and the grid so its lines do not
|
|
2944
|
+
scroll away with a long grid. No accent, no motion. */
|
|
2945
|
+
#song-notes {
|
|
2946
|
+
display: flex;
|
|
2947
|
+
flex-direction: column;
|
|
2948
|
+
gap: 0.5rem;
|
|
2949
|
+
padding-bottom: 0.5rem;
|
|
2950
|
+
border-bottom: 1px solid var(--border);
|
|
2951
|
+
}
|
|
2952
|
+
|
|
2953
|
+
#song-notes[hidden] {
|
|
2954
|
+
display: none;
|
|
2955
|
+
}
|
|
2956
|
+
|
|
2957
|
+
.song-note {
|
|
2958
|
+
margin: 0;
|
|
2959
|
+
font-size: 1rem;
|
|
2960
|
+
font-weight: 400;
|
|
2961
|
+
line-height: 1.5;
|
|
2962
|
+
color: var(--text);
|
|
2963
|
+
overflow-wrap: anywhere;
|
|
2964
|
+
}
|
|
2965
|
+
|
|
2966
|
+
.song-note[data-kind="check-failed"] {
|
|
2967
|
+
color: var(--text-muted);
|
|
2968
|
+
}
|
|
2969
|
+
|
|
2970
|
+
/* The toolbar above the Song grid: what a launched section waits for, and where the music is. #song-launch shares the
|
|
2971
|
+
segmented group look of the top bar (the selector lists near #view-buttons). No accent, no motion. */
|
|
2972
|
+
#song-bar {
|
|
2973
|
+
display: flex;
|
|
2974
|
+
flex-wrap: wrap;
|
|
2975
|
+
align-items: center;
|
|
2976
|
+
column-gap: 1rem;
|
|
2977
|
+
row-gap: 0.5rem;
|
|
2978
|
+
}
|
|
2979
|
+
|
|
2980
|
+
#song-bar[hidden] {
|
|
2981
|
+
display: none;
|
|
2982
|
+
}
|
|
2983
|
+
|
|
2984
|
+
#song-launch-label,
|
|
2985
|
+
#song-now {
|
|
2986
|
+
margin: 0;
|
|
2987
|
+
font-size: 0.8125rem;
|
|
2988
|
+
font-weight: 400;
|
|
2989
|
+
line-height: 1.5;
|
|
2990
|
+
color: var(--text-muted);
|
|
2991
|
+
}
|
|
2992
|
+
|
|
2993
|
+
#song-now[hidden] {
|
|
2994
|
+
display: none;
|
|
2995
|
+
}
|
|
2996
|
+
|
|
2997
|
+
/* The one sentence under the toolbar: why the grid is off, what waits, or why a gesture changed nothing. It is the only
|
|
2998
|
+
live region of the view and changes only on a gesture or a state change. A queued launch and one that needs no code
|
|
2999
|
+
change are information, so they are muted; the rest read in the plain text color. No accent, no motion. */
|
|
3000
|
+
#song-line {
|
|
3001
|
+
margin: 0;
|
|
3002
|
+
font-size: 1rem;
|
|
3003
|
+
font-weight: 400;
|
|
3004
|
+
line-height: 1.5;
|
|
3005
|
+
color: var(--text);
|
|
3006
|
+
overflow-wrap: anywhere;
|
|
3007
|
+
}
|
|
3008
|
+
|
|
3009
|
+
#song-line[data-kind="queued"],
|
|
3010
|
+
#song-line[data-kind="already"] {
|
|
3011
|
+
color: var(--text-muted);
|
|
3012
|
+
}
|
|
3013
|
+
|
|
3014
|
+
#song-line[hidden] {
|
|
3015
|
+
display: none;
|
|
3016
|
+
}
|
|
3017
|
+
|
|
3018
|
+
/* The Song grid: sections as columns, groups and tracks as rows. Only .song-grid-scroll scrolls, in both directions; the
|
|
3019
|
+
name column and the header row stay in place (sticky) and are opaque so scrolled cells slide under them. The section
|
|
3020
|
+
header is the Parts card (.part-card), so the playing and queued colors come from the rules above. Nothing here uses an
|
|
3021
|
+
accent, a destructive or a Jam color, and nothing moves: no transition and no animation. */
|
|
3022
|
+
#parts-rows[data-view="song"] {
|
|
3023
|
+
flex: 1;
|
|
3024
|
+
min-height: 0;
|
|
3025
|
+
min-width: 0;
|
|
3026
|
+
overflow: hidden;
|
|
3027
|
+
}
|
|
3028
|
+
|
|
3029
|
+
.song-grid-scroll {
|
|
3030
|
+
flex: 1;
|
|
3031
|
+
min-width: 0;
|
|
3032
|
+
min-height: 0;
|
|
3033
|
+
overflow: auto;
|
|
3034
|
+
scroll-padding-left: 10rem;
|
|
3035
|
+
scroll-padding-top: 7rem;
|
|
3036
|
+
}
|
|
3037
|
+
|
|
3038
|
+
.song-grid {
|
|
3039
|
+
border-collapse: separate;
|
|
3040
|
+
border-spacing: 0;
|
|
3041
|
+
table-layout: fixed;
|
|
3042
|
+
width: 100%;
|
|
3043
|
+
min-width: calc(10rem + var(--song-columns) * 10rem);
|
|
3044
|
+
background: var(--dominant);
|
|
3045
|
+
}
|
|
3046
|
+
|
|
3047
|
+
.song-col-name {
|
|
3048
|
+
width: 10rem;
|
|
3049
|
+
}
|
|
3050
|
+
|
|
3051
|
+
.song-grid col[data-playing="true"] {
|
|
3052
|
+
background: var(--secondary);
|
|
3053
|
+
}
|
|
3054
|
+
|
|
3055
|
+
.song-grid tr[hidden] {
|
|
3056
|
+
display: none;
|
|
3057
|
+
}
|
|
3058
|
+
|
|
3059
|
+
.song-grid th,
|
|
3060
|
+
.song-grid td {
|
|
3061
|
+
padding: 0;
|
|
3062
|
+
min-width: 0;
|
|
3063
|
+
text-align: left;
|
|
3064
|
+
vertical-align: top;
|
|
3065
|
+
font-size: 0.8125rem;
|
|
3066
|
+
font-weight: 400;
|
|
3067
|
+
line-height: 1.5;
|
|
3068
|
+
color: var(--text);
|
|
3069
|
+
border-right: 1px solid var(--border);
|
|
3070
|
+
border-bottom: 1px solid var(--border);
|
|
3071
|
+
}
|
|
3072
|
+
|
|
3073
|
+
.song-grid thead th {
|
|
3074
|
+
position: sticky;
|
|
3075
|
+
top: 0;
|
|
3076
|
+
z-index: 2;
|
|
3077
|
+
background: var(--dominant);
|
|
3078
|
+
}
|
|
3079
|
+
|
|
3080
|
+
.song-grid .song-colhead {
|
|
3081
|
+
padding: 0.25rem;
|
|
3082
|
+
}
|
|
3083
|
+
|
|
3084
|
+
.song-grid .song-corner {
|
|
3085
|
+
position: sticky;
|
|
3086
|
+
left: 0;
|
|
3087
|
+
z-index: 3;
|
|
3088
|
+
padding: 0.5rem;
|
|
3089
|
+
color: var(--text-muted);
|
|
3090
|
+
}
|
|
3091
|
+
|
|
3092
|
+
.song-grid .song-rowhead {
|
|
3093
|
+
position: sticky;
|
|
3094
|
+
left: 0;
|
|
3095
|
+
z-index: 1;
|
|
3096
|
+
background: var(--dominant);
|
|
3097
|
+
padding: 0.25rem 0.5rem;
|
|
3098
|
+
padding-inline-start: calc(0.5rem + var(--depth, 0) * 1rem);
|
|
3099
|
+
overflow-wrap: anywhere;
|
|
3100
|
+
vertical-align: middle;
|
|
3101
|
+
}
|
|
3102
|
+
|
|
3103
|
+
.song-grid .song-rowhead[data-deep="true"] {
|
|
3104
|
+
padding-inline-start: 4.5rem;
|
|
3105
|
+
}
|
|
3106
|
+
|
|
3107
|
+
.song-group-row .song-rowhead,
|
|
3108
|
+
.song-group-row td {
|
|
3109
|
+
background: var(--secondary);
|
|
3110
|
+
}
|
|
3111
|
+
|
|
3112
|
+
.song-head {
|
|
3113
|
+
width: 100%;
|
|
3114
|
+
box-sizing: border-box;
|
|
3115
|
+
margin: 0;
|
|
3116
|
+
}
|
|
3117
|
+
|
|
3118
|
+
.song-head .part-name {
|
|
3119
|
+
font-weight: 600;
|
|
3120
|
+
}
|
|
3121
|
+
|
|
3122
|
+
.song-head-line {
|
|
3123
|
+
display: flex;
|
|
3124
|
+
flex-wrap: wrap;
|
|
3125
|
+
align-items: baseline;
|
|
3126
|
+
column-gap: 0.5rem;
|
|
3127
|
+
}
|
|
3128
|
+
|
|
3129
|
+
.song-head .song-head-action,
|
|
3130
|
+
.song-head .song-used {
|
|
3131
|
+
font-family: inherit;
|
|
3132
|
+
font-size: 0.8125rem;
|
|
3133
|
+
font-weight: 400;
|
|
3134
|
+
line-height: 1.5;
|
|
3135
|
+
color: var(--text-muted);
|
|
3136
|
+
}
|
|
3137
|
+
|
|
3138
|
+
.song-head .song-harmony {
|
|
3139
|
+
max-height: 3em;
|
|
3140
|
+
overflow: hidden;
|
|
3141
|
+
overflow-wrap: anywhere;
|
|
3142
|
+
font-size: 0.8125rem;
|
|
3143
|
+
font-weight: 400;
|
|
3144
|
+
line-height: 1.5;
|
|
3145
|
+
color: var(--text-muted);
|
|
3146
|
+
}
|
|
3147
|
+
|
|
3148
|
+
.song-fold {
|
|
3149
|
+
display: inline-flex;
|
|
3150
|
+
align-items: baseline;
|
|
3151
|
+
gap: 0.5rem;
|
|
3152
|
+
max-width: 100%;
|
|
3153
|
+
margin: 0;
|
|
3154
|
+
padding: 0;
|
|
3155
|
+
background: transparent;
|
|
3156
|
+
border: none;
|
|
3157
|
+
color: var(--text);
|
|
3158
|
+
font-family: inherit;
|
|
3159
|
+
font-size: 0.8125rem;
|
|
3160
|
+
font-weight: 600;
|
|
3161
|
+
line-height: 1.5;
|
|
3162
|
+
text-align: left;
|
|
3163
|
+
cursor: pointer;
|
|
3164
|
+
overflow-wrap: anywhere;
|
|
3165
|
+
}
|
|
3166
|
+
|
|
3167
|
+
.song-fold:focus-visible {
|
|
3168
|
+
outline: 1px solid var(--text-muted);
|
|
3169
|
+
outline-offset: 0.25rem;
|
|
3170
|
+
}
|
|
3171
|
+
|
|
3172
|
+
.song-fold-name {
|
|
3173
|
+
min-width: 0;
|
|
3174
|
+
}
|
|
3175
|
+
|
|
3176
|
+
.song-count {
|
|
3177
|
+
font-weight: 400;
|
|
3178
|
+
color: var(--text-muted);
|
|
3179
|
+
}
|
|
3180
|
+
|
|
3181
|
+
.song-cell {
|
|
3182
|
+
display: flex;
|
|
3183
|
+
flex-direction: column;
|
|
3184
|
+
justify-content: center;
|
|
3185
|
+
width: 100%;
|
|
3186
|
+
min-height: 3rem;
|
|
3187
|
+
box-sizing: border-box;
|
|
3188
|
+
margin: 0;
|
|
3189
|
+
padding: 0.25rem 0.5rem;
|
|
3190
|
+
background: transparent;
|
|
3191
|
+
border: 1px solid transparent;
|
|
3192
|
+
color: var(--text);
|
|
3193
|
+
font-family: inherit;
|
|
3194
|
+
font-size: 0.8125rem;
|
|
3195
|
+
font-weight: 400;
|
|
3196
|
+
line-height: 1.5;
|
|
3197
|
+
text-align: left;
|
|
3198
|
+
cursor: pointer;
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
.song-cell:hover:not([aria-disabled="true"]) {
|
|
3202
|
+
border-color: var(--text-muted);
|
|
3203
|
+
}
|
|
3204
|
+
|
|
3205
|
+
.song-cell:focus-visible {
|
|
3206
|
+
outline: 1px solid var(--text-muted);
|
|
3207
|
+
outline-offset: -1px;
|
|
3208
|
+
}
|
|
3209
|
+
|
|
3210
|
+
/* A control the line switched off stays focusable and keeps its text; only the pointer says it does nothing. */
|
|
3211
|
+
.song-cell[aria-disabled="true"],
|
|
3212
|
+
.song-head[aria-disabled="true"] {
|
|
3213
|
+
cursor: default;
|
|
3214
|
+
}
|
|
3215
|
+
|
|
3216
|
+
.song-variation {
|
|
3217
|
+
white-space: nowrap;
|
|
3218
|
+
overflow: hidden;
|
|
3219
|
+
text-overflow: ellipsis;
|
|
3220
|
+
}
|
|
3221
|
+
|
|
3222
|
+
.song-cell[data-kind="rest"] .song-variation,
|
|
3223
|
+
.song-wait,
|
|
3224
|
+
.song-fit {
|
|
3225
|
+
color: var(--text-muted);
|
|
3226
|
+
}
|
|
3227
|
+
|
|
3228
|
+
/* The second line of a cell: whether a fixed variation fits its section's harmony, or that the cell's change waits for its
|
|
3229
|
+
line. Words in the muted colour, two lines at most; the whole text is in the cell's title. */
|
|
3230
|
+
.song-wait,
|
|
3231
|
+
.song-fit {
|
|
3232
|
+
max-height: 3em;
|
|
3233
|
+
overflow: hidden;
|
|
3234
|
+
overflow-wrap: anywhere;
|
|
3235
|
+
}
|
|
3236
|
+
|
|
3237
|
+
.song-cell[aria-expanded="true"] {
|
|
3238
|
+
border-color: var(--text-muted);
|
|
3239
|
+
}
|
|
3240
|
+
|
|
3241
|
+
/* The list that opens on a cell. It is fixed to the window and lives outside the grid's scroller, so the scroller never
|
|
3242
|
+
clips it; the page puts it below the cell or above it and keeps it inside the window. */
|
|
3243
|
+
#song-cell-list {
|
|
3244
|
+
position: fixed;
|
|
3245
|
+
z-index: 5;
|
|
3246
|
+
box-sizing: border-box;
|
|
3247
|
+
min-width: 10rem;
|
|
3248
|
+
max-width: 20rem;
|
|
3249
|
+
max-height: 16rem;
|
|
3250
|
+
overflow-y: auto;
|
|
3251
|
+
margin: 0;
|
|
3252
|
+
padding: 0.25rem;
|
|
3253
|
+
background: var(--secondary);
|
|
3254
|
+
border: 1px solid var(--text-muted);
|
|
3255
|
+
}
|
|
3256
|
+
|
|
3257
|
+
#song-cell-list[hidden] {
|
|
3258
|
+
display: none;
|
|
3259
|
+
}
|
|
3260
|
+
|
|
3261
|
+
.song-option {
|
|
3262
|
+
display: flex;
|
|
3263
|
+
flex-wrap: wrap;
|
|
3264
|
+
align-items: baseline;
|
|
3265
|
+
column-gap: 0.5rem;
|
|
3266
|
+
padding: 0.25rem 0.5rem;
|
|
3267
|
+
font-size: 0.8125rem;
|
|
3268
|
+
line-height: 1.5;
|
|
3269
|
+
color: var(--text);
|
|
3270
|
+
cursor: pointer;
|
|
3271
|
+
overflow-wrap: anywhere;
|
|
3272
|
+
}
|
|
3273
|
+
|
|
3274
|
+
.song-option[data-active="true"] {
|
|
3275
|
+
box-shadow: inset 0 0 0 1px var(--text-muted);
|
|
2419
3276
|
}
|
|
2420
3277
|
|
|
2421
|
-
.
|
|
2422
|
-
color: var(--
|
|
3278
|
+
.song-option-note {
|
|
3279
|
+
color: var(--text-muted);
|
|
2423
3280
|
}
|
|
2424
3281
|
|
|
2425
|
-
.
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
overflow:
|
|
3282
|
+
.song-list-note {
|
|
3283
|
+
padding: 0.25rem 0.5rem;
|
|
3284
|
+
font-size: 0.8125rem;
|
|
3285
|
+
line-height: 1.5;
|
|
3286
|
+
overflow-wrap: anywhere;
|
|
2430
3287
|
}
|
|
2431
3288
|
|
|
2432
|
-
.
|
|
2433
|
-
|
|
2434
|
-
height: 100%;
|
|
2435
|
-
width: 0;
|
|
3289
|
+
.song-option-rest {
|
|
3290
|
+
border-top: 1px solid var(--border);
|
|
2436
3291
|
}
|
|
2437
3292
|
|
|
2438
|
-
.
|
|
2439
|
-
|
|
3293
|
+
.song-grid .song-outside-note {
|
|
3294
|
+
padding: 0.25rem 0.5rem;
|
|
3295
|
+
color: var(--text-muted);
|
|
2440
3296
|
}
|
|
2441
3297
|
|
|
2442
3298
|
/* Strudel inserts the CodeMirror container as the NEXT SIBLING of <strudel-editor>,
|
|
@@ -2603,6 +3459,196 @@ body {
|
|
|
2603
3459
|
.mx-strip[data-color="7"] { --strip-color: var(--track-7); }
|
|
2604
3460
|
.mx-strip[data-color="8"] { --strip-color: var(--track-8); }
|
|
2605
3461
|
|
|
3462
|
+
/* A group of the song is a frame around the strips of its tracks (and of its sub-groups). The head names the group and
|
|
3463
|
+
stays at the left of the strips area while a wide group scrolls; from the fifth level the frame drops its padding, so a
|
|
3464
|
+
deep tree keeps room for names. A folded group shows its head only. */
|
|
3465
|
+
.mx-group {
|
|
3466
|
+
flex: 0 0 auto;
|
|
3467
|
+
align-self: flex-start;
|
|
3468
|
+
min-height: 100%;
|
|
3469
|
+
min-width: 0;
|
|
3470
|
+
box-sizing: border-box;
|
|
3471
|
+
display: flex;
|
|
3472
|
+
flex-direction: column;
|
|
3473
|
+
gap: 0.5rem;
|
|
3474
|
+
padding: 0.5rem;
|
|
3475
|
+
border: 1px solid var(--border);
|
|
3476
|
+
border-top: 0.25rem solid var(--border);
|
|
3477
|
+
}
|
|
3478
|
+
|
|
3479
|
+
.mx-group[data-deep="true"] {
|
|
3480
|
+
padding: 0;
|
|
3481
|
+
gap: 0;
|
|
3482
|
+
}
|
|
3483
|
+
|
|
3484
|
+
.mx-group-head {
|
|
3485
|
+
position: sticky;
|
|
3486
|
+
left: 1rem;
|
|
3487
|
+
align-self: flex-start;
|
|
3488
|
+
box-sizing: border-box;
|
|
3489
|
+
width: 11rem;
|
|
3490
|
+
display: flex;
|
|
3491
|
+
flex-direction: column;
|
|
3492
|
+
gap: 0.25rem;
|
|
3493
|
+
background: var(--secondary);
|
|
3494
|
+
}
|
|
3495
|
+
|
|
3496
|
+
.mx-group-title {
|
|
3497
|
+
display: flex;
|
|
3498
|
+
align-items: center;
|
|
3499
|
+
gap: 0.5rem;
|
|
3500
|
+
min-width: 0;
|
|
3501
|
+
min-height: 1.5rem;
|
|
3502
|
+
}
|
|
3503
|
+
|
|
3504
|
+
.mx-fold {
|
|
3505
|
+
flex: 1 1 auto;
|
|
3506
|
+
min-width: 0;
|
|
3507
|
+
display: flex;
|
|
3508
|
+
align-items: baseline;
|
|
3509
|
+
gap: 0.5rem;
|
|
3510
|
+
min-height: 1.5rem;
|
|
3511
|
+
margin: 0;
|
|
3512
|
+
padding: 0;
|
|
3513
|
+
background: none;
|
|
3514
|
+
border: 0;
|
|
3515
|
+
color: var(--text);
|
|
3516
|
+
font-family: inherit;
|
|
3517
|
+
font-size: 0.8125rem;
|
|
3518
|
+
font-weight: 600;
|
|
3519
|
+
line-height: 1.25;
|
|
3520
|
+
text-align: left;
|
|
3521
|
+
cursor: pointer;
|
|
3522
|
+
}
|
|
3523
|
+
|
|
3524
|
+
.mx-fold-chevron {
|
|
3525
|
+
flex: 0 0 auto;
|
|
3526
|
+
font-weight: 400;
|
|
3527
|
+
color: var(--text-muted);
|
|
3528
|
+
}
|
|
3529
|
+
|
|
3530
|
+
.mx-fold:hover .mx-fold-chevron {
|
|
3531
|
+
color: var(--text);
|
|
3532
|
+
}
|
|
3533
|
+
|
|
3534
|
+
.mx-fold-name {
|
|
3535
|
+
min-width: 0;
|
|
3536
|
+
overflow: hidden;
|
|
3537
|
+
text-overflow: ellipsis;
|
|
3538
|
+
white-space: nowrap;
|
|
3539
|
+
}
|
|
3540
|
+
|
|
3541
|
+
.mx-fold:focus-visible {
|
|
3542
|
+
outline: 1px solid var(--text-muted);
|
|
3543
|
+
outline-offset: -1px;
|
|
3544
|
+
}
|
|
3545
|
+
|
|
3546
|
+
.mx-group-line {
|
|
3547
|
+
display: flex;
|
|
3548
|
+
align-items: center;
|
|
3549
|
+
gap: 0.25rem;
|
|
3550
|
+
}
|
|
3551
|
+
|
|
3552
|
+
.mx-group-count {
|
|
3553
|
+
flex: 1 1 auto;
|
|
3554
|
+
min-width: 0;
|
|
3555
|
+
overflow: hidden;
|
|
3556
|
+
text-overflow: ellipsis;
|
|
3557
|
+
white-space: nowrap;
|
|
3558
|
+
font-size: 0.8125rem;
|
|
3559
|
+
font-weight: 400;
|
|
3560
|
+
line-height: 1.25;
|
|
3561
|
+
color: var(--text-muted);
|
|
3562
|
+
}
|
|
3563
|
+
|
|
3564
|
+
/* Some of the group's tracks are muted or soloed: a dashed outline in the text colour (the count line says how many). */
|
|
3565
|
+
.mx-group-head .mx-mute[aria-pressed="mixed"],
|
|
3566
|
+
.mx-group-head .mx-solo[aria-pressed="mixed"] {
|
|
3567
|
+
border-style: dashed;
|
|
3568
|
+
border-color: var(--text);
|
|
3569
|
+
color: var(--text);
|
|
3570
|
+
}
|
|
3571
|
+
|
|
3572
|
+
.mx-group-head [data-writes="true"]:disabled {
|
|
3573
|
+
cursor: default;
|
|
3574
|
+
}
|
|
3575
|
+
|
|
3576
|
+
.mx-group-note {
|
|
3577
|
+
margin: 0;
|
|
3578
|
+
font-size: 0.8125rem;
|
|
3579
|
+
font-weight: 400;
|
|
3580
|
+
line-height: 1.25;
|
|
3581
|
+
color: var(--text-muted);
|
|
3582
|
+
overflow-wrap: anywhere;
|
|
3583
|
+
}
|
|
3584
|
+
|
|
3585
|
+
.mx-group-note[hidden] {
|
|
3586
|
+
display: none;
|
|
3587
|
+
}
|
|
3588
|
+
|
|
3589
|
+
.mx-group-body {
|
|
3590
|
+
flex: 1 1 auto;
|
|
3591
|
+
min-width: 0;
|
|
3592
|
+
display: flex;
|
|
3593
|
+
align-items: stretch;
|
|
3594
|
+
gap: 0.5rem;
|
|
3595
|
+
padding-top: 0.5rem;
|
|
3596
|
+
border-top: 1px solid var(--border);
|
|
3597
|
+
}
|
|
3598
|
+
|
|
3599
|
+
.mx-group-body[hidden] {
|
|
3600
|
+
display: none;
|
|
3601
|
+
}
|
|
3602
|
+
|
|
3603
|
+
.mx-group-body > .mx-strip,
|
|
3604
|
+
.mx-group-body > .mx-group {
|
|
3605
|
+
align-self: stretch;
|
|
3606
|
+
min-height: 0;
|
|
3607
|
+
}
|
|
3608
|
+
|
|
3609
|
+
/* A track strip's name row: the light, then the name button. The light's box never changes size, so a blink moves nothing.
|
|
3610
|
+
The loop in the page writes the fade as the fill's opacity; nothing here animates. */
|
|
3611
|
+
.mx-name-row {
|
|
3612
|
+
display: flex;
|
|
3613
|
+
align-items: center;
|
|
3614
|
+
gap: 0.5rem;
|
|
3615
|
+
min-height: 1.5rem;
|
|
3616
|
+
min-width: 0;
|
|
3617
|
+
}
|
|
3618
|
+
|
|
3619
|
+
.mx-name-row > button.mx-name {
|
|
3620
|
+
flex: 1 1 auto;
|
|
3621
|
+
width: auto;
|
|
3622
|
+
min-width: 0;
|
|
3623
|
+
}
|
|
3624
|
+
|
|
3625
|
+
.mx-light {
|
|
3626
|
+
flex: 0 0 auto;
|
|
3627
|
+
position: relative;
|
|
3628
|
+
box-sizing: border-box;
|
|
3629
|
+
width: 0.75rem;
|
|
3630
|
+
height: 0.75rem;
|
|
3631
|
+
border: 1px solid var(--text-muted);
|
|
3632
|
+
border-radius: 50%;
|
|
3633
|
+
}
|
|
3634
|
+
|
|
3635
|
+
.mx-light[hidden] {
|
|
3636
|
+
display: none;
|
|
3637
|
+
}
|
|
3638
|
+
|
|
3639
|
+
.mx-light-fill {
|
|
3640
|
+
position: absolute;
|
|
3641
|
+
inset: 0;
|
|
3642
|
+
border-radius: 50%;
|
|
3643
|
+
background: var(--text);
|
|
3644
|
+
opacity: 0;
|
|
3645
|
+
}
|
|
3646
|
+
|
|
3647
|
+
/* The steady state of motion off only: lit while the track played within the last cycle. */
|
|
3648
|
+
.mx-light[data-lit="true"] .mx-light-fill {
|
|
3649
|
+
opacity: 1;
|
|
3650
|
+
}
|
|
3651
|
+
|
|
2606
3652
|
.mx-name {
|
|
2607
3653
|
font-size: 0.8125rem;
|
|
2608
3654
|
font-weight: 600;
|
|
@@ -3751,6 +4797,100 @@ button.mx-name:focus-visible {
|
|
|
3751
4797
|
text-overflow: ellipsis;
|
|
3752
4798
|
}
|
|
3753
4799
|
|
|
4800
|
+
/* The line that says why a scale is in use when several fit: a bold lead word and one plain sentence under the scale
|
|
4801
|
+
name. It is no warning, so it wears the text colour only, and it is hidden for a scale that was simply heard. */
|
|
4802
|
+
#jam-reason {
|
|
4803
|
+
margin: 0;
|
|
4804
|
+
font-size: 1rem;
|
|
4805
|
+
font-weight: 400;
|
|
4806
|
+
line-height: 1.5;
|
|
4807
|
+
color: var(--text);
|
|
4808
|
+
overflow-wrap: anywhere;
|
|
4809
|
+
}
|
|
4810
|
+
|
|
4811
|
+
#jam-reason[hidden] {
|
|
4812
|
+
display: none;
|
|
4813
|
+
}
|
|
4814
|
+
|
|
4815
|
+
#jam-reason-lead {
|
|
4816
|
+
font-weight: 600;
|
|
4817
|
+
}
|
|
4818
|
+
|
|
4819
|
+
/* The scales that fit, under the reason line: a quiet caption, then one item per scale. While one can be chosen each item is a
|
|
4820
|
+
button and the one in use is pressed (a lighter fill and an underline, no weight change, so nothing shifts); when none can
|
|
4821
|
+
be chosen the items are plain text with no hover and no pointer. Never cut: an item keeps its whole name and the row wraps
|
|
4822
|
+
between items. Text colours only, no Jam hue: a list of scales is no warning. */
|
|
4823
|
+
#jam-candidates {
|
|
4824
|
+
display: flex;
|
|
4825
|
+
flex-direction: column;
|
|
4826
|
+
gap: 0.25rem;
|
|
4827
|
+
}
|
|
4828
|
+
|
|
4829
|
+
#jam-candidates[hidden] {
|
|
4830
|
+
display: none;
|
|
4831
|
+
}
|
|
4832
|
+
|
|
4833
|
+
#jam-candidates-label,
|
|
4834
|
+
#jam-candidates-more {
|
|
4835
|
+
font-size: 0.8125rem;
|
|
4836
|
+
font-weight: 400;
|
|
4837
|
+
line-height: 1.5;
|
|
4838
|
+
color: var(--text-muted);
|
|
4839
|
+
}
|
|
4840
|
+
|
|
4841
|
+
#jam-candidates-more[hidden] {
|
|
4842
|
+
display: none;
|
|
4843
|
+
}
|
|
4844
|
+
|
|
4845
|
+
#jam-candidate-list {
|
|
4846
|
+
display: flex;
|
|
4847
|
+
flex-direction: row;
|
|
4848
|
+
flex-wrap: wrap;
|
|
4849
|
+
gap: 0.5rem;
|
|
4850
|
+
}
|
|
4851
|
+
|
|
4852
|
+
.jam-candidate {
|
|
4853
|
+
box-sizing: border-box;
|
|
4854
|
+
min-height: 1.75rem;
|
|
4855
|
+
padding: 0 0.5rem;
|
|
4856
|
+
display: inline-flex;
|
|
4857
|
+
align-items: center;
|
|
4858
|
+
border: 1px solid var(--border);
|
|
4859
|
+
border-radius: 0;
|
|
4860
|
+
background: transparent;
|
|
4861
|
+
color: var(--text-muted);
|
|
4862
|
+
font: inherit;
|
|
4863
|
+
font-size: 0.8125rem;
|
|
4864
|
+
font-weight: 400;
|
|
4865
|
+
line-height: 1.5;
|
|
4866
|
+
white-space: nowrap;
|
|
4867
|
+
}
|
|
4868
|
+
|
|
4869
|
+
button.jam-candidate {
|
|
4870
|
+
cursor: pointer;
|
|
4871
|
+
}
|
|
4872
|
+
|
|
4873
|
+
button.jam-candidate:hover {
|
|
4874
|
+
color: var(--text);
|
|
4875
|
+
border-color: var(--text-muted);
|
|
4876
|
+
}
|
|
4877
|
+
|
|
4878
|
+
button.jam-candidate[aria-pressed="true"] {
|
|
4879
|
+
color: var(--text);
|
|
4880
|
+
background: var(--surface-2);
|
|
4881
|
+
border-color: var(--text-muted);
|
|
4882
|
+
box-shadow: inset 0 -0.125rem 0 var(--text);
|
|
4883
|
+
}
|
|
4884
|
+
|
|
4885
|
+
button.jam-candidate:focus-visible {
|
|
4886
|
+
outline: 0.125rem solid var(--text);
|
|
4887
|
+
outline-offset: 0.25rem;
|
|
4888
|
+
}
|
|
4889
|
+
|
|
4890
|
+
span.jam-candidate {
|
|
4891
|
+
color: var(--text);
|
|
4892
|
+
}
|
|
4893
|
+
|
|
3754
4894
|
/* The declared harmony: a quiet line above the scale block and the label before the scale name.
|
|
3755
4895
|
Both are hidden without a declaration. Two plain facts: no accent, no mark of agreement or difference. */
|
|
3756
4896
|
#jam-declared {
|
|
@@ -3794,6 +4934,19 @@ button.mx-name:focus-visible {
|
|
|
3794
4934
|
color: var(--text-muted);
|
|
3795
4935
|
}
|
|
3796
4936
|
|
|
4937
|
+
/* The section whose harmony the line shows, after the value: shown only for a piece with sections. */
|
|
4938
|
+
#jam-declared-section {
|
|
4939
|
+
flex: none;
|
|
4940
|
+
font-size: 0.8125rem;
|
|
4941
|
+
font-weight: 400;
|
|
4942
|
+
line-height: 1.5;
|
|
4943
|
+
color: var(--text-muted);
|
|
4944
|
+
}
|
|
4945
|
+
|
|
4946
|
+
#jam-declared-section[hidden] {
|
|
4947
|
+
display: none;
|
|
4948
|
+
}
|
|
4949
|
+
|
|
3797
4950
|
#jam-degrees {
|
|
3798
4951
|
display: flex;
|
|
3799
4952
|
flex-direction: row;
|