File size: 6,160 Bytes
9fb0d40
 
a3e1a45
 
 
 
 
 
 
 
 
 
 
9fb0d40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a3e1a45
9fb0d40
 
 
 
 
a3e1a45
 
 
 
dbd5035
 
 
 
 
 
a3e1a45
dbd5035
9fb0d40
 
13e8a7a
9fb0d40
 
13e8a7a
 
 
 
 
9fb0d40
 
 
 
 
3225df7
 
 
 
 
 
 
 
 
13e8a7a
 
 
 
 
 
 
 
 
 
e30c292
 
 
 
 
 
 
 
 
 
 
 
4fb4554
 
e30c292
 
4fb4554
 
9fb0d40
 
 
 
 
 
 
 
 
 
 
 
 
dbd5035
9fb0d40
 
 
 
 
a3e1a45
9fb0d40
 
 
 
 
 
 
 
 
 
 
 
 
a3e1a45
 
9fb0d40
8565c15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a3e1a45
9fb0d40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78f88e1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/* LilyScript score player — preview + transport. Light theme to match Gradio Soft. */

/* Gradio's `.gradio-container .prose * { color: var(--body-text-color) }` sets a
   color DIRECTLY on every descendant of our mount, overriding anything we set by
   inheritance and out-specifying a plain class. We mount under id #ls-score, so
   prefixing our color rules with that id (specificity beats two classes) lets them
   win WITHOUT !important. This base rule pins the default text color for the whole
   subtree; the specific rules below override per element. (The SVG ink color is a
   separate concern — see the `.ls-svg svg` block.) */
#ls-score, #ls-score :where(.ls-status, .ls-btn, .ls-time, .ls-err-pre, span, div, button) {
	color: #333;
}

.ls-score-root {
	display: flex;
	flex-direction: column;
	height: 100%;
	min-height: 600px;
}

.ls-preview {
	flex: 1;
	overflow: auto;
	background: #fafafa;
	border: 1px solid #e5e5e5;
	border-radius: 8px;
	padding: 12px;
	position: relative;
}

#ls-score .ls-status {
	font-size: 12px;
	color: #888;
	min-height: 16px;
	margin-bottom: 4px;
}
#ls-score .ls-status.ls-busy { color: #2b7; }
#ls-score .ls-status.ls-err { color: #c0392b; }
/* multi-line parse errors: preserve the caret alignment, wrap overflow. */
#ls-score .ls-status.ls-err .ls-err-pre {
	margin: 0;
	font-family: ui-monospace, Consolas, Monaco, monospace;
	font-size: 11px;
	line-height: 1.35;
	white-space: pre-wrap;
	word-break: break-word;
	color: #c0392b;
}

.ls-svg {
	background: #fff;
	display: inline-block;
	min-width: 100%;
	position: relative;    /* anchor for the absolutely-positioned playback cursor */
}
/* faint warm/yellow tint behind the score while generating */
.ls-svg.ls-generating-bg {
	background: #fffdf2;
}
.ls-svg svg {
	max-width: 100%;
	height: auto;
}
/* Stacked Verovio pages (a long score paginates into several SVGs). Each page is a
   block so they stack vertically; a small gap separates pages. */
.ls-svg .ls-svg-page {
	display: block;
	margin: 0 auto 8px;
}
.ls-svg .ls-svg-page:last-of-type {
	margin-bottom: 0;
}
/* Playback cursor: a vertical line tracking the currently-sounding note. */
.ls-cursor {
	position: absolute;
	width: 2px;
	background: rgba(124, 92, 255, 0.85);
	pointer-events: none;
	z-index: 5;
	display: none;
	transition: left 0.06s linear;
}
/* Verovio draws everything with stroke|fill="currentColor", which resolves to the
   computed `color`. We can't rely on inheritance from a container `color`: Gradio's
   `.prose * { color: var(--body-text-color) }` sets color DIRECTLY on every
   descendant (so it wins over an inherited value), which under the dark theme is a
   light grey — leaving staff lines, bar lines and the per-system left brace/bracket
   (grpSym) too pale. Force a dark `color` on the whole svg subtree (high enough
   specificity + !important to beat `.prose *`), and pin stroke on the line elements
   as belt-and-suspenders. */
.ls-svg svg,
.ls-svg svg * {
	color: #1a1a1a !important;
}
.ls-svg svg .staff path,
.ls-svg svg .barLine path,
.ls-svg svg .ledgerLines path,
.ls-svg svg .grpSym path {
	stroke: #1a1a1a;
}

/* playing-note highlight on the SVG */
.ls-hl {
	fill: #ff6b35 !important;
	stroke: #ff6b35 !important;
}

/* transport bar */
.ls-player {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 8px 12px;
	margin-bottom: 8px;
	background: #f3f3f5;
	border: 1px solid #e5e5e5;
	border-radius: 8px;
}

#ls-score .ls-btn {
	border: 1px solid #d0d0d6;
	background: #fff;
	color: #333;
	width: 30px;
	height: 30px;
	border-radius: 6px;
	cursor: pointer;
	font-size: 13px;
	line-height: 1;
	display: inline-flex;
	align-items: center;
	justify-content: center;
}
#ls-score .ls-btn:hover:not(:disabled) { border-color: #8aa; background: #f0f6ff; }
#ls-score .ls-btn:disabled { opacity: 0.4; cursor: not-allowed; }

/* Loading state on the play button: while the sound library / MIDI player is still
   loading, hide the ▶ glyph and spin a small ring in its place. Kept fully opaque
   (overriding :disabled's dim) so the animation reads as "working", not just greyed.
   The button stays disabled (cursor: wait) — clicking does nothing until ready. */
#ls-score .ls-btn.ls-loading {
	opacity: 1;
	cursor: wait;
	color: transparent;            /* hide the ▶ text glyph without reflow */
	position: relative;
}
#ls-score .ls-btn.ls-loading::after {
	content: '';
	position: absolute;
	width: 14px;
	height: 14px;
	border: 2px solid #c9c9d2;
	border-top-color: #7c5cff;     /* accent matches the progress fill */
	border-radius: 50%;
	animation: ls-spin 0.7s linear infinite;
}
@keyframes ls-spin {
	to { transform: rotate(360deg); }
}
/* Respect reduced-motion: pulse the ring opacity instead of spinning. */
@media (prefers-reduced-motion: reduce) {
	#ls-score .ls-btn.ls-loading::after {
		animation: ls-sf-pulse 1.4s ease-in-out infinite;
	}
}

#ls-score .ls-time {
	font-family: ui-monospace, Consolas, monospace;
	font-size: 12px;
	color: #555;
	min-width: 88px;
}

.ls-progress {
	flex: 1;
	height: 6px;
	background: #ddd;
	border-radius: 3px;
	cursor: pointer;
	overflow: hidden;
}
.ls-fill {
	height: 100%;
	width: 0;
	background: #7c5cff;
	transition: width 0.1s linear;
}

/* Sound-library status badge in the transport bar. While the GM soundfont loads:
   dimmed + grayscaled + pulsing 🎹 (the small grand-piano fallback is audible).
   Once ready: full-color 🎹 with a green ✓, shown 2s then faded out (hover reveals). */
#ls-score .ls-sf {
	position: relative;
	font-size: 14px;
	line-height: 1;
	margin-left: 2px;
	flex: 0 0 auto;
	cursor: default;
	opacity: 0.45;
	filter: grayscale(1);
	animation: ls-sf-pulse 1.4s ease-in-out infinite;
}
#ls-score .ls-sf.ready {
	opacity: 1;
	filter: none;
	animation: none;
}
@keyframes ls-sf-pulse {
	0%, 100% { opacity: 0.30; }
	50%      { opacity: 0.65; }
}
/* green ✓ badge superscript on the ready piano */
#ls-score .ls-sf .ls-sf-check {
	display: none;
	position: absolute;
	top: -0.4em;
	right: -0.7em;
	font-size: 0.85em;
	font-weight: 700;
	color: #3fb950;
	text-shadow: 0 0 2px rgba(0, 0, 0, 0.4);
}
#ls-score .ls-sf.ready .ls-sf-check {
	display: inline;
}