@hosanna/chordpro 1.1.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.
@@ -0,0 +1,1154 @@
1
+ import {
2
+ chordDictionary,
3
+ parseChordPro,
4
+ transposeChord
5
+ } from "./chunk-PENNIH4J.mjs";
6
+
7
+ // src/renderer/ChordProRenderer.tsx
8
+ import {
9
+ BookOpen,
10
+ Disc,
11
+ Flame,
12
+ HelpCircle,
13
+ Info,
14
+ Key,
15
+ LayoutGrid,
16
+ Music,
17
+ Pause,
18
+ Play,
19
+ Repeat,
20
+ SkipBack,
21
+ SkipForward,
22
+ User,
23
+ X
24
+ } from "lucide-react";
25
+ import React, { useCallback, useMemo as useMemo2, useRef, useState } from "react";
26
+ import YouTube from "react-youtube";
27
+
28
+ // src/renderer/ChordRoll.tsx
29
+ import { useMemo } from "react";
30
+ import { jsx, jsxs } from "react/jsx-runtime";
31
+ function GuitarDiagram({
32
+ frets,
33
+ fingers,
34
+ barre
35
+ }) {
36
+ const maxFret = Math.max(...frets);
37
+ let startFret = 1;
38
+ if (maxFret > 4) {
39
+ const minFret = Math.min(...frets.filter((f) => f > 0));
40
+ startFret = minFret;
41
+ }
42
+ const numFrets = 4;
43
+ const width = 100;
44
+ const height = 110;
45
+ const getStringX = (index) => 14 + index * 14;
46
+ const getFretY = (index) => 22 + index * 20;
47
+ return /* @__PURE__ */ jsxs(
48
+ "svg",
49
+ {
50
+ width,
51
+ height,
52
+ className: "text-m3-text dark:text-m3-dark-text select-none",
53
+ children: [
54
+ /* @__PURE__ */ jsx(
55
+ "line",
56
+ {
57
+ x1: getStringX(0),
58
+ y1: getFretY(0) - (startFret === 1 ? 3 : 0),
59
+ x2: getStringX(5),
60
+ y2: getFretY(0) - (startFret === 1 ? 3 : 0),
61
+ className: "stroke-zinc-800 dark:stroke-zinc-200",
62
+ strokeWidth: startFret === 1 ? 3.5 : 1.5
63
+ }
64
+ ),
65
+ startFret > 1 && /* @__PURE__ */ jsxs(
66
+ "text",
67
+ {
68
+ x: getStringX(0) - 4,
69
+ y: getFretY(0) + 12,
70
+ fontSize: "8",
71
+ className: "font-mono font-black fill-m3-primary dark:fill-m3-dark-primary",
72
+ textAnchor: "end",
73
+ children: [
74
+ startFret,
75
+ "\xAA"
76
+ ]
77
+ }
78
+ ),
79
+ [0, 1, 2, 3, 4, 5].map((idx) => /* @__PURE__ */ jsx(
80
+ "line",
81
+ {
82
+ x1: getStringX(idx),
83
+ y1: getFretY(0),
84
+ x2: getStringX(idx),
85
+ y2: getFretY(numFrets),
86
+ className: "stroke-zinc-400 dark:stroke-zinc-600",
87
+ strokeOpacity: "0.5",
88
+ strokeWidth: "1.2"
89
+ },
90
+ idx
91
+ )),
92
+ [0, 1, 2, 3, 4].map((idx) => /* @__PURE__ */ jsx(
93
+ "line",
94
+ {
95
+ x1: getStringX(0),
96
+ y1: getFretY(idx),
97
+ x2: getStringX(5),
98
+ y2: getFretY(idx),
99
+ className: "stroke-zinc-400 dark:stroke-zinc-600",
100
+ strokeOpacity: "0.4",
101
+ strokeWidth: "1"
102
+ },
103
+ idx
104
+ )),
105
+ barre !== void 0 && (() => {
106
+ const barreFretInWindow = barre - startFret;
107
+ if (barreFretInWindow >= 0 && barreFretInWindow < numFrets) {
108
+ const y = getFretY(barreFretInWindow) + 10;
109
+ const startStr = frets.findIndex((f) => f === barre);
110
+ const endStr = 5;
111
+ if (startStr !== -1) {
112
+ return /* @__PURE__ */ jsx(
113
+ "rect",
114
+ {
115
+ x: getStringX(startStr) - 4,
116
+ y: y - 4,
117
+ width: getStringX(endStr) - getStringX(startStr) + 8,
118
+ height: 8,
119
+ rx: 4,
120
+ className: "fill-m3-primary dark:fill-m3-dark-primary opacity-80"
121
+ }
122
+ );
123
+ }
124
+ }
125
+ return null;
126
+ })(),
127
+ frets.map((fret, stringIdx) => {
128
+ if (fret === -1) {
129
+ return /* @__PURE__ */ jsx(
130
+ "text",
131
+ {
132
+ x: getStringX(stringIdx),
133
+ y: 12,
134
+ fontSize: "10",
135
+ fontWeight: "bold",
136
+ className: "fill-red-500 font-black",
137
+ textAnchor: "middle",
138
+ children: "\xD7"
139
+ },
140
+ stringIdx
141
+ );
142
+ }
143
+ if (fret === 0) {
144
+ return /* @__PURE__ */ jsx(
145
+ "circle",
146
+ {
147
+ cx: getStringX(stringIdx),
148
+ cy: 10,
149
+ r: 2,
150
+ fill: "none",
151
+ className: "stroke-emerald-500",
152
+ strokeWidth: "1.2"
153
+ },
154
+ stringIdx
155
+ );
156
+ }
157
+ const fretInWindow = fret - startFret;
158
+ if (fretInWindow >= 0 && fretInWindow < numFrets) {
159
+ const cx = getStringX(stringIdx);
160
+ const cy = getFretY(fretInWindow) + 10;
161
+ const finger = fingers ? fingers[stringIdx] : 0;
162
+ return /* @__PURE__ */ jsxs("g", { children: [
163
+ !(barre !== void 0 && fret === barre && stringIdx >= frets.findIndex((f) => f === barre)) && /* @__PURE__ */ jsx(
164
+ "circle",
165
+ {
166
+ cx,
167
+ cy,
168
+ r: 5,
169
+ className: "fill-m3-primary dark:fill-m3-dark-primary"
170
+ }
171
+ ),
172
+ finger > 0 && /* @__PURE__ */ jsx(
173
+ "text",
174
+ {
175
+ x: cx,
176
+ y: cy + 2.5,
177
+ fontSize: "6.5",
178
+ fontWeight: "bold",
179
+ fill: "white",
180
+ textAnchor: "middle",
181
+ className: "font-bold fill-white",
182
+ children: finger
183
+ }
184
+ )
185
+ ] }, stringIdx);
186
+ }
187
+ return null;
188
+ })
189
+ ]
190
+ }
191
+ );
192
+ }
193
+ function PianoDiagram({ highlightKeys }) {
194
+ const whiteKeySemitones = [
195
+ 0,
196
+ 2,
197
+ 4,
198
+ 5,
199
+ 7,
200
+ 9,
201
+ 11,
202
+ // Octave 1
203
+ 12,
204
+ 14,
205
+ 16,
206
+ 17,
207
+ 19,
208
+ 21,
209
+ 23
210
+ // Octave 2
211
+ ];
212
+ const keyWidth = 14;
213
+ const keyHeight = 56;
214
+ const blackWidth = 9;
215
+ const blackHeight = 34;
216
+ const blackKeySemitones = [1, 3, 6, 8, 10, 13, 15, 18, 20, 22];
217
+ const getBlackKeyX = (semitone) => {
218
+ const octave = Math.floor(semitone / 12);
219
+ const semitoneInOctave = semitone % 12;
220
+ let whiteIndexBefore = 0;
221
+ if (semitoneInOctave === 1) whiteIndexBefore = 1;
222
+ else if (semitoneInOctave === 3) whiteIndexBefore = 2;
223
+ else if (semitoneInOctave === 6) whiteIndexBefore = 4;
224
+ else if (semitoneInOctave === 8) whiteIndexBefore = 5;
225
+ else if (semitoneInOctave === 10) whiteIndexBefore = 6;
226
+ const absoluteWhiteIndex = octave * 7 + whiteIndexBefore;
227
+ return absoluteWhiteIndex * keyWidth - blackWidth / 2;
228
+ };
229
+ return /* @__PURE__ */ jsxs(
230
+ "svg",
231
+ {
232
+ width: 14 * keyWidth + 2,
233
+ height: keyHeight + 4,
234
+ className: "text-m3-text dark:text-m3-dark-text p-1 rounded-lg bg-zinc-100 dark:bg-zinc-900 border border-m3-border dark:border-m3-dark-border select-none",
235
+ children: [
236
+ whiteKeySemitones.map((st, idx) => {
237
+ const isHighlighted = highlightKeys.includes(st);
238
+ const x = idx * keyWidth + 1;
239
+ return /* @__PURE__ */ jsxs("g", { children: [
240
+ /* @__PURE__ */ jsx(
241
+ "rect",
242
+ {
243
+ x,
244
+ y: 2,
245
+ width: keyWidth - 1,
246
+ height: keyHeight,
247
+ rx: 1.5,
248
+ className: `${isHighlighted ? "fill-m3-primary dark:fill-m3-dark-primary" : "fill-white dark:fill-zinc-800"} stroke-zinc-300 dark:stroke-zinc-700`,
249
+ strokeWidth: "0.8"
250
+ }
251
+ ),
252
+ isHighlighted && /* @__PURE__ */ jsx(
253
+ "circle",
254
+ {
255
+ cx: x + (keyWidth - 1) / 2,
256
+ cy: keyHeight - 8,
257
+ r: 2,
258
+ className: "fill-white dark:fill-zinc-900"
259
+ }
260
+ )
261
+ ] }, st);
262
+ }),
263
+ blackKeySemitones.map((st) => {
264
+ const isHighlighted = highlightKeys.includes(st);
265
+ const x = getBlackKeyX(st) + 1;
266
+ return /* @__PURE__ */ jsx("g", { children: /* @__PURE__ */ jsx(
267
+ "rect",
268
+ {
269
+ x,
270
+ y: 2,
271
+ width: blackWidth,
272
+ height: blackHeight,
273
+ rx: 1,
274
+ className: `${isHighlighted ? "fill-m3-primary dark:fill-m3-dark-primary" : "fill-zinc-800 dark:fill-zinc-950"} stroke-zinc-900 dark:stroke-black`,
275
+ strokeWidth: "0.8"
276
+ }
277
+ ) }, st);
278
+ })
279
+ ]
280
+ }
281
+ );
282
+ }
283
+ function ChordRoll({
284
+ uniqueChords,
285
+ transposeVal,
286
+ capoVal = 0,
287
+ onChordClick,
288
+ instrument,
289
+ showDiagrams,
290
+ showChords
291
+ }) {
292
+ const isGuitar = instrument === "guitar";
293
+ const effectiveTranspose = transposeVal - (isGuitar ? capoVal : 0);
294
+ const chordItems = useMemo(() => {
295
+ return uniqueChords.map((chord) => {
296
+ const transposed = transposeChord(chord, effectiveTranspose);
297
+ const fingering = chordDictionary.getFingering(transposed);
298
+ return {
299
+ original: chord,
300
+ transposed,
301
+ fingering
302
+ };
303
+ });
304
+ }, [uniqueChords, effectiveTranspose]);
305
+ if (uniqueChords.length === 0 || !showDiagrams || !showChords) return null;
306
+ return /* @__PURE__ */ jsxs(
307
+ "div",
308
+ {
309
+ className: "w-full select-none mb-6",
310
+ onTouchStart: (e) => e.stopPropagation(),
311
+ onTouchMove: (e) => e.stopPropagation(),
312
+ onTouchEnd: (e) => e.stopPropagation(),
313
+ children: [
314
+ isGuitar && capoVal > 0 && /* @__PURE__ */ jsx("div", { className: "px-4 mb-2 flex items-center justify-between", children: /* @__PURE__ */ jsxs("span", { className: "text-[11px] font-bold text-amber-700 dark:text-amber-400 bg-amber-50 dark:bg-amber-950/50 px-2.5 py-0.5 rounded-md border border-amber-200 dark:border-amber-900/40", children: [
315
+ "Capo na ",
316
+ capoVal,
317
+ "\xAA casa \u2014 acordes em formato relativo"
318
+ ] }) }),
319
+ /* @__PURE__ */ jsx("div", { className: "flex flex-row overflow-x-auto gap-6 py-2 px-4 no-scrollbar scroll-smooth", children: chordItems.map((item, idx) => {
320
+ const isPiano = instrument === "piano";
321
+ return /* @__PURE__ */ jsxs(
322
+ "div",
323
+ {
324
+ onClick: () => onChordClick?.(item.transposed),
325
+ className: `flex flex-col items-center p-2 transition-all cursor-pointer hover:opacity-85 shrink-0 ${isPiano ? "w-48" : "w-24"}`,
326
+ title: "Clique para ver detalhes do acorde",
327
+ children: [
328
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-black text-m3-primary dark:text-m3-dark-primary font-mono mb-1.5", children: item.transposed }),
329
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center flex-1 min-h-17.5", children: item.fingering ? isPiano && item.fingering.piano ? /* @__PURE__ */ jsx(
330
+ PianoDiagram,
331
+ {
332
+ highlightKeys: item.fingering.piano.highlightKeys
333
+ }
334
+ ) : !isPiano && item.fingering.guitar ? /* @__PURE__ */ jsx(
335
+ GuitarDiagram,
336
+ {
337
+ frets: item.fingering.guitar.frets,
338
+ fingers: item.fingering.guitar.fingers,
339
+ barre: item.fingering.guitar.barre
340
+ }
341
+ ) : /* @__PURE__ */ jsx("div", { className: "text-[10px] text-zinc-400 font-mono italic", children: "Sem visual" }) : /* @__PURE__ */ jsx("div", { className: "text-[10px] text-zinc-400 font-mono italic", children: "N\xE3o registado" }) }),
342
+ item.fingering?.piano && /* @__PURE__ */ jsx("span", { className: "text-[9px] font-bold text-m3-secondary dark:text-m3-dark-secondary mt-1.5 font-mono whitespace-nowrap overflow-hidden text-ellipsis max-w-full", children: item.fingering.piano.notes.join(" - ") })
343
+ ]
344
+ },
345
+ idx
346
+ );
347
+ }) })
348
+ ]
349
+ }
350
+ );
351
+ }
352
+
353
+ // src/renderer/ChordProRenderer.tsx
354
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
355
+ var YT_ID_REGEX = /(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))([^&?]+)/;
356
+ function extractYoutubeId(urlOrId) {
357
+ const match = urlOrId.match(YT_ID_REGEX);
358
+ return match?.[1] || urlOrId;
359
+ }
360
+ function getDuration(duration) {
361
+ const seconds = Number(duration);
362
+ if (isNaN(seconds)) {
363
+ return "00:00";
364
+ } else {
365
+ return `${Math.trunc(seconds / 60)}:${seconds % 60}`;
366
+ }
367
+ }
368
+ var ChordProRenderer = React.memo(
369
+ ({
370
+ content,
371
+ showChords,
372
+ transposeVal = 0,
373
+ capoVal = 0,
374
+ onTransposeChange,
375
+ onCapoChange,
376
+ twoColumnLayout = false,
377
+ fontSize,
378
+ instrument = "guitar",
379
+ showDiagrams = false,
380
+ fileName,
381
+ showYoutubePlayer: showYoutubePlayerProp,
382
+ onShowYoutubePlayerChange,
383
+ scrollContainerRef
384
+ }) => {
385
+ const [ytPlayerRef, setYtPlayerRef] = useState(null);
386
+ const [isPlayingYoutube, setIsPlayingYoutube] = useState(false);
387
+ const [isYoutubeRepeat, setIsYoutubeRepeat] = useState(false);
388
+ const [showYoutubeInternal, setShowYoutubeInternal] = useState(false);
389
+ const showYoutubePlayer = showYoutubePlayerProp ?? showYoutubeInternal;
390
+ const setShowYoutubePlayer = useCallback(
391
+ (val) => {
392
+ setShowYoutubeInternal(val);
393
+ onShowYoutubePlayerChange?.(val);
394
+ },
395
+ [onShowYoutubePlayerChange]
396
+ );
397
+ const isYoutubeRepeatRef = useRef(isYoutubeRepeat);
398
+ isYoutubeRepeatRef.current = isYoutubeRepeat;
399
+ const parsedSong = useMemo2(() => {
400
+ return parseChordPro(content);
401
+ }, [content]);
402
+ const { metadata } = parsedSong;
403
+ const isGuitar = instrument === "guitar";
404
+ const effectiveCapo = isGuitar ? capoVal : 0;
405
+ const effectiveTranspose = transposeVal - effectiveCapo;
406
+ const soundingKey = useMemo2(() => {
407
+ return transposeChord(metadata.key || "C", transposeVal);
408
+ }, [metadata.key, transposeVal]);
409
+ const renderedKey = useMemo2(() => {
410
+ return transposeChord(metadata.key || "C", effectiveTranspose);
411
+ }, [metadata.key, effectiveTranspose]);
412
+ const resolvedUniqueChords = useMemo2(() => {
413
+ const chords = /* @__PURE__ */ new Set();
414
+ for (const section of parsedSong.sections) {
415
+ for (const line of section.lines) {
416
+ if (line.segments) {
417
+ for (const seg of line.segments) {
418
+ if (seg.chord) chords.add(seg.chord);
419
+ }
420
+ }
421
+ if (line.measures) {
422
+ for (const m of line.measures) {
423
+ for (const c of m.chords) {
424
+ if (c.chord) chords.add(c.chord);
425
+ }
426
+ }
427
+ }
428
+ }
429
+ }
430
+ return Array.from(chords);
431
+ }, [parsedSong]);
432
+ const [selectedChord, setSelectedChord] = useState(null);
433
+ const [modalInstrument, setModalInstrument] = useState(
434
+ instrument
435
+ );
436
+ const chordFingering = useMemo2(() => {
437
+ if (!selectedChord) return null;
438
+ return chordDictionary.getFingering(selectedChord);
439
+ }, [selectedChord]);
440
+ const handleChordClick = (chord) => {
441
+ setModalInstrument(instrument);
442
+ setSelectedChord(chord);
443
+ };
444
+ return /* @__PURE__ */ jsxs2("div", { className: "relative flex flex-col flex-1 overflow-hidden h-full", children: [
445
+ /* @__PURE__ */ jsx2(
446
+ "div",
447
+ {
448
+ ref: scrollContainerRef,
449
+ className: "flex-1 overflow-y-auto px-4 py-4 sm:px-6 bg-slate-50 dark:bg-slate-950 print-page select-text leading-relaxed no-scrollbar relative",
450
+ children: /* @__PURE__ */ jsxs2("div", { className: "max-w-3xl mx-auto print-song-card", children: [
451
+ /* @__PURE__ */ jsxs2("div", { className: "mb-6 border-b border-neutral-100 dark:border-slate-800 pb-5 select-none", children: [
452
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-start justify-between gap-4 flex-wrap", children: [
453
+ /* @__PURE__ */ jsxs2("div", { children: [
454
+ /* @__PURE__ */ jsx2("h2", { className: "text-2xl font-bold tracking-tight text-neutral-900 dark:text-white", children: metadata.title }),
455
+ metadata.subtitle && /* @__PURE__ */ jsx2("h3", { className: "text-[15px] font-medium text-neutral-600 dark:text-neutral-400 mt-1", children: metadata.subtitle }),
456
+ (metadata.artist || metadata.composer) && /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1.5 text-xs text-neutral-500 mt-2 font-medium flex-wrap", children: [
457
+ /* @__PURE__ */ jsx2(User, { className: "w-3.5 h-3.5 text-[#0284c7]" }),
458
+ /* @__PURE__ */ jsxs2("span", { children: [
459
+ "Por:",
460
+ " ",
461
+ [metadata.artist, metadata.composer].filter(Boolean).join(" / ")
462
+ ] })
463
+ ] }),
464
+ metadata.translator && /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1.5 text-xs text-neutral-500 mt-2 font-medium flex-wrap", children: [
465
+ /* @__PURE__ */ jsx2(User, { className: "w-3.5 h-3.5 text-[#0284c7]" }),
466
+ /* @__PURE__ */ jsxs2("span", { children: [
467
+ "Traduzido por: ",
468
+ metadata.translator
469
+ ] })
470
+ ] }),
471
+ metadata.album && /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1.5 text-xs text-neutral-500 mt-1 font-medium", children: [
472
+ /* @__PURE__ */ jsx2(Disc, { className: "w-3.5 h-3.5 text-[#0284c7]" }),
473
+ /* @__PURE__ */ jsxs2("span", { children: [
474
+ "\xC1lbum: ",
475
+ metadata.album
476
+ ] })
477
+ ] })
478
+ ] }),
479
+ /* @__PURE__ */ jsxs2("div", { className: "flex flex-wrap items-center gap-1.5 justify-end", children: [
480
+ metadata.songNumber && /* @__PURE__ */ jsxs2("span", { className: "text-[10px] font-bold bg-neutral-100 dark:bg-slate-800 text-neutral-600 dark:text-neutral-400 px-2 py-1 rounded-lg border border-neutral-200 dark:border-slate-700", children: [
481
+ "N\xBA ",
482
+ metadata.songNumber
483
+ ] }),
484
+ (metadata.key || transposeVal !== 0) && /* @__PURE__ */ jsxs2("span", { className: "text-[10px] font-bold bg-indigo-50 dark:bg-indigo-950/40 text-indigo-700 dark:text-indigo-300 px-2.5 py-1 rounded-lg border border-indigo-100 dark:border-indigo-950/50 flex items-center gap-1", children: [
485
+ /* @__PURE__ */ jsx2(Key, { className: "w-3 h-3" }),
486
+ "Tom: ",
487
+ soundingKey
488
+ ] }),
489
+ effectiveCapo > 0 && /* @__PURE__ */ jsxs2("span", { className: "text-[10px] font-bold bg-amber-50 dark:bg-amber-950/40 text-amber-700 dark:text-amber-300 px-2.5 py-1 rounded-lg border border-amber-100 dark:border-amber-950/50", children: [
490
+ "Capo: ",
491
+ effectiveCapo,
492
+ "\xAA casa"
493
+ ] }),
494
+ effectiveCapo > 0 && /* @__PURE__ */ jsxs2("span", { className: "text-[10px] font-bold bg-emerald-50 dark:bg-emerald-950/40 text-emerald-700 dark:text-emerald-300 px-2.5 py-1 rounded-lg border border-emerald-100 dark:border-emerald-950/50", children: [
495
+ "Formato: ",
496
+ renderedKey
497
+ ] }),
498
+ metadata.originalKey && /* @__PURE__ */ jsxs2("span", { className: "text-[10px] font-bold bg-purple-50 dark:bg-purple-950/40 text-purple-700 dark:text-purple-300 px-2.5 py-1 rounded-lg border border-purple-100 dark:border-purple-950/50", children: [
499
+ "Tom Orig: ",
500
+ metadata.originalKey
501
+ ] }),
502
+ metadata.capo && metadata.capo !== "0" && effectiveCapo === 0 && /* @__PURE__ */ jsxs2("span", { className: "text-[10px] font-bold bg-amber-50 dark:bg-amber-950/40 text-amber-700 dark:text-amber-300 px-2.5 py-1 rounded-lg border border-amber-100 dark:border-amber-950/50", children: [
503
+ "Capo Orig: ",
504
+ metadata.capo,
505
+ "\xAA casa"
506
+ ] }),
507
+ metadata.tempo && /* @__PURE__ */ jsxs2("span", { className: "text-[10px] font-bold bg-emerald-50 dark:bg-emerald-950/40 text-emerald-700 dark:text-emerald-300 px-2.5 py-1 rounded-lg border border-emerald-100 dark:border-emerald-950/50 flex items-center gap-1", children: [
508
+ /* @__PURE__ */ jsx2(Flame, { className: "w-3 h-3" }),
509
+ metadata.tempo,
510
+ " BPM"
511
+ ] }),
512
+ metadata.time && /* @__PURE__ */ jsx2("span", { className: "text-[10px] font-bold bg-cyan-50 dark:bg-cyan-950/40 text-cyan-700 dark:text-cyan-300 px-2.5 py-1 rounded-lg border border-cyan-100 dark:border-cyan-950/50", children: metadata.time }),
513
+ metadata.ccli && /* @__PURE__ */ jsxs2("span", { className: "text-[10px] font-bold bg-slate-100 dark:bg-slate-800 text-slate-500 dark:text-slate-400 px-2.5 py-1 rounded-lg border border-slate-200 dark:border-slate-700", children: [
514
+ "CCLI: ",
515
+ metadata.ccli
516
+ ] })
517
+ ] })
518
+ ] }),
519
+ (metadata.copyright || metadata.duration) && /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-4 text-[10px] text-neutral-400 dark:text-neutral-500 mt-3 pt-3 border-t border-neutral-100 dark:border-slate-800/50", children: [
520
+ metadata.copyright && /* @__PURE__ */ jsxs2("span", { children: [
521
+ "\xA9 ",
522
+ metadata.copyright
523
+ ] }),
524
+ metadata.duration && /* @__PURE__ */ jsxs2("span", { children: [
525
+ "Dura\xE7\xE3o: ",
526
+ getDuration(metadata.duration)
527
+ ] })
528
+ ] }),
529
+ (transposeVal !== 0 || effectiveCapo !== 0) && /* @__PURE__ */ jsxs2("div", { className: "mt-4 bg-indigo-50 dark:bg-indigo-950/40 text-xs px-3 py-2 rounded-xl text-indigo-700 dark:text-indigo-300 flex items-center justify-between border border-indigo-100 dark:border-indigo-950/50 flex-wrap gap-2", children: [
530
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 flex-wrap", children: [
531
+ /* @__PURE__ */ jsxs2("span", { className: "font-semibold", children: [
532
+ "Tom:",
533
+ " ",
534
+ /* @__PURE__ */ jsx2("span", { className: "text-indigo-600 dark:text-indigo-400 font-bold bg-white dark:bg-zinc-900 px-2 py-0.5 rounded border ml-0.5 text-xs", children: soundingKey })
535
+ ] }),
536
+ effectiveCapo > 0 && /* @__PURE__ */ jsxs2("span", { className: "font-semibold text-amber-700 dark:text-amber-300", children: [
537
+ "\u2022 Capo:",
538
+ " ",
539
+ /* @__PURE__ */ jsxs2("span", { className: "text-amber-700 dark:text-amber-300 font-bold bg-amber-100 dark:bg-amber-900/60 px-2 py-0.5 rounded border border-amber-300 dark:border-amber-800 text-xs ml-0.5", children: [
540
+ effectiveCapo,
541
+ "\xAA casa (",
542
+ renderedKey,
543
+ ")"
544
+ ] })
545
+ ] })
546
+ ] }),
547
+ (onTransposeChange || onCapoChange) && /* @__PURE__ */ jsx2(
548
+ "button",
549
+ {
550
+ type: "button",
551
+ onClick: () => {
552
+ onTransposeChange?.(0);
553
+ onCapoChange?.(0);
554
+ },
555
+ className: "text-[10px] font-bold hover:underline underline-offset-2 uppercase text-indigo-600 dark:text-indigo-400 cursor-pointer",
556
+ children: "Repor Tom / Capo"
557
+ }
558
+ )
559
+ ] })
560
+ ] }),
561
+ /* @__PURE__ */ jsx2(
562
+ ChordRoll,
563
+ {
564
+ uniqueChords: resolvedUniqueChords,
565
+ transposeVal,
566
+ capoVal: effectiveCapo,
567
+ onChordClick: handleChordClick,
568
+ instrument,
569
+ showDiagrams,
570
+ showChords
571
+ }
572
+ ),
573
+ /* @__PURE__ */ jsx2(
574
+ "div",
575
+ {
576
+ className: `font-sans leading-relaxed text-sm select-text ${twoColumnLayout ? "columns-1 sm:columns-2 gap-8 space-y-6 [column-fill:_balance]" : "space-y-6"}`,
577
+ style: fontSize ? { fontSize: `${fontSize}px` } : void 0,
578
+ children: parsedSong.sections.map((section, secIdx) => {
579
+ const isChorus = section.type === "chorus";
580
+ const isBridge = section.type === "bridge";
581
+ if (section.type === "new_song") {
582
+ return /* @__PURE__ */ jsxs2(
583
+ "div",
584
+ {
585
+ className: "w-full my-12 flex items-center justify-center select-none break-before-column",
586
+ children: [
587
+ /* @__PURE__ */ jsx2("div", { className: "flex-1 border-t-2 border-slate-200 dark:border-slate-800 border-dashed" }),
588
+ /* @__PURE__ */ jsx2(Music, { className: "w-5 h-5 mx-4 text-slate-300 dark:text-slate-700" }),
589
+ /* @__PURE__ */ jsx2("div", { className: "flex-1 border-t-2 border-slate-200 dark:border-slate-800 border-dashed" })
590
+ ]
591
+ },
592
+ secIdx
593
+ );
594
+ }
595
+ if (section.type === "grid") {
596
+ const maxMeasures = Math.max(
597
+ 1,
598
+ ...section.lines.map((l) => l.measures?.length || 1)
599
+ );
600
+ return /* @__PURE__ */ jsxs2(
601
+ "div",
602
+ {
603
+ className: `bg-slate-100/60 dark:bg-slate-900/40 p-4 rounded-xl border border-slate-200/80 dark:border-slate-800/60 my-6 ${twoColumnLayout ? "break-inside-avoid-column inline-block w-full" : ""}`,
604
+ children: [
605
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between mb-3", children: [
606
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1.5 text-[11px] font-bold text-slate-500 dark:text-slate-400 uppercase tracking-wider select-none", children: [
607
+ /* @__PURE__ */ jsx2(LayoutGrid, { className: "w-3.5 h-3.5" }),
608
+ /* @__PURE__ */ jsx2("span", { children: section.label || "Instrumental" })
609
+ ] }),
610
+ section.repeat && /* @__PURE__ */ jsxs2("span", { className: "text-[10px] font-black bg-indigo-100 dark:bg-indigo-900/50 text-indigo-700 dark:text-indigo-300 px-2 py-0.5 rounded uppercase border border-indigo-200 dark:border-indigo-800/50", children: [
611
+ "Repetir ",
612
+ section.repeat,
613
+ "x"
614
+ ] })
615
+ ] }),
616
+ /* @__PURE__ */ jsx2("div", { className: "space-y-1.5 font-medium", children: section.lines.map((line, lineIdx) => /* @__PURE__ */ jsx2(
617
+ LineRenderer,
618
+ {
619
+ line,
620
+ showChords,
621
+ transpose: effectiveTranspose,
622
+ onChordClick: handleChordClick,
623
+ maxGridMeasures: maxMeasures
624
+ },
625
+ lineIdx
626
+ )) })
627
+ ]
628
+ },
629
+ secIdx
630
+ );
631
+ }
632
+ if (isChorus || isBridge) {
633
+ const borderColor = isChorus ? "border-m3-primary/30 dark:border-m3-dark-primary/30" : "border-amber-500/30 dark:border-amber-400/30";
634
+ const labelColor = isChorus ? "text-m3-text dark:text-m3-dark-text" : "text-amber-700 dark:text-amber-400";
635
+ const iconColor = isChorus ? "text-m3-secondary" : "text-amber-500";
636
+ return /* @__PURE__ */ jsxs2(
637
+ "div",
638
+ {
639
+ className: `pl-4 md:pl-6 border-l-2 my-6 ${borderColor} ${twoColumnLayout ? "break-inside-avoid-column inline-block w-full" : ""}`,
640
+ children: [
641
+ /* @__PURE__ */ jsxs2(
642
+ "div",
643
+ {
644
+ className: `flex items-center gap-1.5 text-[11px] font-bold uppercase tracking-wider mb-3 select-none ${labelColor}`,
645
+ children: [
646
+ /* @__PURE__ */ jsx2(
647
+ Music,
648
+ {
649
+ className: `w-3.5 h-3.5 shrink-0 ${iconColor}`
650
+ }
651
+ ),
652
+ /* @__PURE__ */ jsx2("span", { children: section.label || (isChorus ? "Refr\xE3o" : "Ponte") }),
653
+ section.repeat && /* @__PURE__ */ jsxs2(
654
+ "span",
655
+ {
656
+ className: `ml-2 px-2 py-0.5 rounded text-[10px] font-black border ${isChorus ? "bg-blue-100/50 border-blue-200 text-blue-700 dark:bg-blue-900/30 dark:border-blue-800 dark:text-blue-300" : "bg-amber-100/50 border-amber-200 text-amber-700 dark:bg-amber-900/30 dark:border-amber-800 dark:text-amber-300"}`,
657
+ children: [
658
+ section.repeat,
659
+ "x"
660
+ ]
661
+ }
662
+ )
663
+ ]
664
+ }
665
+ ),
666
+ /* @__PURE__ */ jsx2("div", { className: "space-y-4 font-medium", children: section.lines.length === 0 ? /* @__PURE__ */ jsx2(
667
+ "div",
668
+ {
669
+ className: `text-xs italic my-1 opacity-70 ${labelColor}`,
670
+ children: "(Repete o refr\xE3o)"
671
+ }
672
+ ) : section.lines.map((line, lineIdx) => /* @__PURE__ */ jsx2(
673
+ LineRenderer,
674
+ {
675
+ line,
676
+ showChords,
677
+ transpose: effectiveTranspose,
678
+ onChordClick: handleChordClick
679
+ },
680
+ lineIdx
681
+ )) })
682
+ ]
683
+ },
684
+ secIdx
685
+ );
686
+ }
687
+ if (section.type === "tab") {
688
+ return /* @__PURE__ */ jsxs2(
689
+ "div",
690
+ {
691
+ className: `bg-slate-900 p-4 pt-6 rounded-xl shadow-inner border border-slate-800 my-6 select-text relative group ${twoColumnLayout ? "break-inside-avoid-column inline-block w-full" : ""}`,
692
+ children: [
693
+ /* @__PURE__ */ jsx2("div", { className: "absolute top-0 left-4 -translate-y-1/2 bg-slate-800 text-[10px] font-bold text-slate-300 uppercase tracking-widest px-2 py-0.5 rounded border border-slate-700 shadow-sm select-none", children: section.label || "Tablatura" }),
694
+ /* @__PURE__ */ jsx2("div", { className: "overflow-x-auto no-scrollbar", children: /* @__PURE__ */ jsx2("pre", { className: "font-mono text-[13px] md:text-sm text-slate-300 leading-relaxed whitespace-pre drop-shadow-sm", children: section.lines.map((line) => line.text || "").join("\n") }) })
695
+ ]
696
+ },
697
+ secIdx
698
+ );
699
+ }
700
+ if (section.type === "comment") {
701
+ return /* @__PURE__ */ jsx2(
702
+ "div",
703
+ {
704
+ className: `my-2 select-none pl-3 text-[11px] italic text-m3-secondary/70 dark:text-m3-dark-secondary/70 ${twoColumnLayout ? "break-inside-avoid-column inline-block w-full" : ""}`,
705
+ children: section.lines.map((l) => l.text).join(", ")
706
+ },
707
+ secIdx
708
+ );
709
+ }
710
+ return /* @__PURE__ */ jsxs2(
711
+ "div",
712
+ {
713
+ className: `relative pl-6 sm:pl-8 border-l border-m3-border/30 dark:border-m3-dark-border/30 py-1.5 my-4 ${twoColumnLayout ? "break-inside-avoid-column inline-block w-full" : ""}`,
714
+ children: [
715
+ section.label && /* @__PURE__ */ jsx2("div", { className: "absolute -left-0.5 top-0 bottom-0 w-0.5 bg-m3-secondary/20 dark:bg-m3-dark-secondary/20 rounded-full" }),
716
+ section.label && /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1.5 text-[11px] font-bold text-m3-text/60 dark:text-m3-dark-text/60 uppercase tracking-wider mb-3 select-none", children: [
717
+ /* @__PURE__ */ jsx2(Music, { className: "w-3.5 h-3.5 text-m3-secondary/60 shrink-0" }),
718
+ /* @__PURE__ */ jsx2("span", { children: section.label }),
719
+ section.repeat && /* @__PURE__ */ jsxs2("span", { className: "ml-2 bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-300 px-2 py-0.5 rounded text-[10px] font-black border border-slate-200 dark:border-slate-700", children: [
720
+ section.repeat,
721
+ "x"
722
+ ] })
723
+ ] }),
724
+ /* @__PURE__ */ jsx2("div", { className: "space-y-4", children: section.lines.map((line, lineIdx) => /* @__PURE__ */ jsx2(
725
+ LineRenderer,
726
+ {
727
+ line,
728
+ showChords,
729
+ transpose: effectiveTranspose,
730
+ onChordClick: handleChordClick
731
+ },
732
+ lineIdx
733
+ )) })
734
+ ]
735
+ },
736
+ secIdx
737
+ );
738
+ })
739
+ }
740
+ ),
741
+ /* @__PURE__ */ jsxs2("div", { className: "border-t border-neutral-100 dark:border-zinc-900 pt-6 mt-12 pb-12 text-center text-[10px] text-neutral-400 dark:text-neutral-500 select-none space-y-1.5", children: [
742
+ metadata.artist && /* @__PURE__ */ jsxs2("p", { children: [
743
+ "Artista Original: ",
744
+ metadata.artist
745
+ ] }),
746
+ metadata.composer && /* @__PURE__ */ jsxs2("p", { children: [
747
+ "Compositor: ",
748
+ metadata.composer
749
+ ] }),
750
+ metadata.lyricist && /* @__PURE__ */ jsxs2("p", { children: [
751
+ "Letra: ",
752
+ metadata.lyricist
753
+ ] }),
754
+ metadata.arranger && /* @__PURE__ */ jsxs2("p", { children: [
755
+ "Arranjo: ",
756
+ metadata.arranger
757
+ ] }),
758
+ metadata.year && /* @__PURE__ */ jsxs2("p", { children: [
759
+ "Ano: ",
760
+ metadata.year
761
+ ] }),
762
+ metadata.originalKey && /* @__PURE__ */ jsxs2("p", { children: [
763
+ "Tom Original: ",
764
+ metadata.originalKey
765
+ ] }),
766
+ /* @__PURE__ */ jsxs2("div", { className: "pt-2 mt-2 border-t border-neutral-50/50 dark:border-zinc-900/50 inline-block", children: [
767
+ metadata.copyright && /* @__PURE__ */ jsxs2("p", { children: [
768
+ "\xA9 Copyright: ",
769
+ metadata.copyright
770
+ ] }),
771
+ metadata.ccli && /* @__PURE__ */ jsxs2("p", { children: [
772
+ "CCLI: ",
773
+ metadata.ccli
774
+ ] })
775
+ ] }),
776
+ fileName && /* @__PURE__ */ jsxs2("p", { className: "mt-4 opacity-50", children: [
777
+ "Ficheiro base: ",
778
+ fileName
779
+ ] })
780
+ ] })
781
+ ] })
782
+ }
783
+ ),
784
+ metadata.youtube && showYoutubePlayer && /* @__PURE__ */ jsx2("div", { className: "hidden", children: /* @__PURE__ */ jsx2(
785
+ YouTube,
786
+ {
787
+ videoId: extractYoutubeId(metadata.youtube),
788
+ opts: {
789
+ height: "0",
790
+ width: "0",
791
+ playerVars: { autoplay: 1, controls: 0, disablekb: 1 }
792
+ },
793
+ onReady: (e) => {
794
+ setYtPlayerRef(e.target);
795
+ e.target.pauseVideo();
796
+ setIsPlayingYoutube(false);
797
+ },
798
+ onPlay: () => setIsPlayingYoutube(true),
799
+ onPause: () => setIsPlayingYoutube(false),
800
+ onEnd: (e) => {
801
+ if (isYoutubeRepeatRef.current) {
802
+ e.target.seekTo(0, true);
803
+ e.target.playVideo();
804
+ } else {
805
+ setIsPlayingYoutube(false);
806
+ }
807
+ }
808
+ }
809
+ ) }),
810
+ showYoutubePlayer && metadata.youtube && /* @__PURE__ */ jsxs2("div", { className: "fixed bottom-0 left-0 right-0 h-16 bg-m3-card dark:bg-m3-dark-card border-t border-m3-border dark:border-m3-dark-border shadow-[0_-4px_10px_rgba(0,0,0,0.05)] z-[200] px-4 flex items-center justify-between animate-in slide-in-from-bottom-full", children: [
811
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-3", children: [
812
+ /* @__PURE__ */ jsx2("div", { className: "w-10 h-10 rounded overflow-hidden bg-slate-200 dark:bg-slate-800 shrink-0 border border-m3-border/50", children: metadata.youtube.match(YT_ID_REGEX) || metadata.youtube.match(/^[^&?]+$/) ? /* @__PURE__ */ jsx2(
813
+ "img",
814
+ {
815
+ src: `https://img.youtube.com/vi/${extractYoutubeId(metadata.youtube)}/default.jpg`,
816
+ alt: "YouTube Thumbnail",
817
+ className: "w-full h-full object-cover scale-150"
818
+ }
819
+ ) : /* @__PURE__ */ jsx2(Disc, { className: "w-5 h-5 m-auto mt-2.5 text-m3-secondary opacity-50" }) }),
820
+ /* @__PURE__ */ jsxs2("div", { className: "hidden sm:block", children: [
821
+ /* @__PURE__ */ jsx2("p", { className: "text-[10px] font-black text-m3-text dark:text-m3-dark-text truncate max-w-[120px]", children: metadata.title }),
822
+ /* @__PURE__ */ jsx2("p", { className: "text-[9px] text-m3-secondary font-medium", children: "\xC1udio do YouTube" })
823
+ ] })
824
+ ] }),
825
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-4", children: [
826
+ /* @__PURE__ */ jsx2(
827
+ "button",
828
+ {
829
+ onClick: async () => {
830
+ if (!ytPlayerRef) return;
831
+ const t = await ytPlayerRef.getCurrentTime();
832
+ ytPlayerRef.seekTo(Math.max(0, t - 10), true);
833
+ },
834
+ className: "text-m3-secondary hover:text-m3-primary transition-colors active:scale-95",
835
+ title: "Retroceder 10s",
836
+ children: /* @__PURE__ */ jsx2(SkipBack, { className: "w-4 h-4" })
837
+ }
838
+ ),
839
+ /* @__PURE__ */ jsx2(
840
+ "button",
841
+ {
842
+ onClick: () => {
843
+ if (!ytPlayerRef) return;
844
+ if (isPlayingYoutube) ytPlayerRef.pauseVideo();
845
+ else ytPlayerRef.playVideo();
846
+ },
847
+ className: "w-10 h-10 rounded-full bg-m3-primary text-white flex items-center justify-center hover:opacity-95 shadow-md active:scale-95 transition-all",
848
+ children: isPlayingYoutube ? /* @__PURE__ */ jsx2(Pause, { className: "w-5 h-5" }) : /* @__PURE__ */ jsx2(Play, { className: "w-5 h-5 ml-1" })
849
+ }
850
+ ),
851
+ /* @__PURE__ */ jsx2(
852
+ "button",
853
+ {
854
+ onClick: async () => {
855
+ if (!ytPlayerRef) return;
856
+ const t = await ytPlayerRef.getCurrentTime();
857
+ ytPlayerRef.seekTo(t + 10, true);
858
+ },
859
+ className: "text-m3-secondary hover:text-m3-primary transition-colors active:scale-95",
860
+ children: /* @__PURE__ */ jsx2(SkipForward, { className: "w-4 h-4" })
861
+ }
862
+ ),
863
+ /* @__PURE__ */ jsx2(
864
+ "button",
865
+ {
866
+ onClick: () => setIsYoutubeRepeat((r) => !r),
867
+ className: `ml-2 transition-colors active:scale-95 ${isYoutubeRepeat ? "text-m3-primary" : "text-m3-secondary hover:text-m3-text"}`,
868
+ children: /* @__PURE__ */ jsx2(Repeat, { className: "w-4 h-4" })
869
+ }
870
+ )
871
+ ] }),
872
+ /* @__PURE__ */ jsx2("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx2(
873
+ "button",
874
+ {
875
+ onClick: () => {
876
+ ytPlayerRef?.pauseVideo();
877
+ setIsPlayingYoutube(false);
878
+ setShowYoutubePlayer(false);
879
+ },
880
+ className: "p-2 text-m3-secondary hover:text-red-500 transition-colors",
881
+ children: /* @__PURE__ */ jsx2(X, { className: "w-4 h-4" })
882
+ }
883
+ ) })
884
+ ] }),
885
+ selectedChord && /* @__PURE__ */ jsx2("div", { className: "fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4 select-none animate-in fade-in duration-200", children: /* @__PURE__ */ jsxs2("div", { className: "bg-m3-card dark:bg-m3-dark-card border border-m3-border dark:border-m3-dark-border rounded-3xl w-full max-w-sm overflow-hidden shadow-2xl flex flex-col p-6 space-y-4 animate-in zoom-in-95 duration-200", children: [
886
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between", children: [
887
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
888
+ /* @__PURE__ */ jsx2(BookOpen, { className: "w-5 h-5 text-m3-primary dark:text-m3-dark-primary" }),
889
+ /* @__PURE__ */ jsxs2("h3", { className: "text-sm font-black text-m3-text dark:text-m3-dark-text uppercase tracking-wider", children: [
890
+ "Dicion\xE1rio: ",
891
+ selectedChord
892
+ ] })
893
+ ] }),
894
+ /* @__PURE__ */ jsx2(
895
+ "button",
896
+ {
897
+ onClick: () => setSelectedChord(null),
898
+ className: "p-1 rounded-full hover:bg-m3-hover dark:hover:bg-m3-dark-hover text-m3-secondary dark:text-m3-dark-secondary",
899
+ children: /* @__PURE__ */ jsx2(X, { className: "w-5 h-5" })
900
+ }
901
+ )
902
+ ] }),
903
+ /* @__PURE__ */ jsxs2("div", { className: "flex bg-m3-sidebar dark:bg-m3-dark-sidebar p-1 rounded-2xl border border-m3-border dark:border-m3-dark-border", children: [
904
+ /* @__PURE__ */ jsx2(
905
+ "button",
906
+ {
907
+ onClick: () => setModalInstrument("guitar"),
908
+ className: `flex-1 py-2 text-xs font-bold rounded-xl transition-all ${modalInstrument === "guitar" ? "bg-m3-primary text-white shadow-sm" : "text-m3-secondary dark:text-m3-dark-secondary hover:text-m3-text"}`,
909
+ children: "Diagrama de Guitarra"
910
+ }
911
+ ),
912
+ /* @__PURE__ */ jsx2(
913
+ "button",
914
+ {
915
+ onClick: () => setModalInstrument("piano"),
916
+ className: `flex-1 py-2 text-xs font-bold rounded-xl transition-all ${modalInstrument === "piano" ? "bg-m3-primary text-white shadow-sm" : "text-m3-secondary dark:text-m3-dark-secondary hover:text-m3-text"}`,
917
+ children: "Teclado de Piano"
918
+ }
919
+ )
920
+ ] }),
921
+ /* @__PURE__ */ jsx2("div", { className: "py-4 flex flex-col items-center justify-center min-h-[140px] border border-m3-border/30 dark:border-m3-dark-border/30 rounded-2xl bg-m3-sidebar/30 dark:bg-m3-dark-sidebar/10", children: chordFingering ? modalInstrument === "guitar" && chordFingering.guitar ? /* @__PURE__ */ jsx2(
922
+ GuitarDiagram,
923
+ {
924
+ frets: chordFingering.guitar.frets,
925
+ fingers: chordFingering.guitar.fingers,
926
+ barre: chordFingering.guitar.barre
927
+ }
928
+ ) : modalInstrument === "piano" && chordFingering.piano ? /* @__PURE__ */ jsx2(
929
+ PianoDiagram,
930
+ {
931
+ highlightKeys: chordFingering.piano.highlightKeys
932
+ }
933
+ ) : /* @__PURE__ */ jsxs2("div", { className: "text-center p-4", children: [
934
+ /* @__PURE__ */ jsx2(HelpCircle, { className: "w-8 h-8 mx-auto text-amber-500 opacity-80 mb-2" }),
935
+ /* @__PURE__ */ jsxs2("p", { className: "text-xs text-m3-secondary dark:text-m3-dark-secondary font-medium", children: [
936
+ "O diagrama para",
937
+ " ",
938
+ modalInstrument === "guitar" ? "Guitarra" : "Piano",
939
+ " ",
940
+ "n\xE3o p\xF4de ser calculado."
941
+ ] })
942
+ ] }) : /* @__PURE__ */ jsxs2("div", { className: "text-center p-6 space-y-2", children: [
943
+ /* @__PURE__ */ jsx2(HelpCircle, { className: "w-8 h-8 mx-auto text-amber-500 opacity-80" }),
944
+ /* @__PURE__ */ jsxs2("p", { className: "text-xs text-m3-text dark:text-m3-dark-text font-bold", children: [
945
+ 'Acorde "',
946
+ selectedChord,
947
+ '" n\xE3o registado'
948
+ ] }),
949
+ /* @__PURE__ */ jsx2("p", { className: "text-[10px] text-m3-secondary dark:text-m3-dark-secondary max-w-[200px] leading-normal", children: "Este acorde n\xE3o se encontra no nosso dicion\xE1rio estrito, mas pode toc\xE1-lo com as notas de acompanhamento habituais." })
950
+ ] }) }),
951
+ chordFingering?.piano && /* @__PURE__ */ jsxs2("div", { className: "text-center font-mono text-xs text-m3-secondary dark:text-m3-dark-secondary bg-m3-sidebar dark:bg-m3-dark-sidebar py-2 rounded-xl", children: [
952
+ "Notas do Acorde:",
953
+ " ",
954
+ /* @__PURE__ */ jsx2("span", { className: "font-bold text-m3-primary dark:text-m3-dark-primary", children: chordFingering.piano.notes.join(" - ") })
955
+ ] }),
956
+ /* @__PURE__ */ jsx2(
957
+ "button",
958
+ {
959
+ onClick: () => setSelectedChord(null),
960
+ className: "w-full bg-m3-sidebar dark:bg-m3-dark-sidebar hover:bg-m3-hover dark:hover:bg-m3-dark-hover text-m3-text dark:text-m3-dark-text text-xs py-3 rounded-2xl border border-m3-border dark:border-m3-dark-border font-bold active:scale-95 transition-all",
961
+ children: "Voltar ao C\xE2ntico"
962
+ }
963
+ )
964
+ ] }) })
965
+ ] });
966
+ }
967
+ );
968
+ var LineRenderer = React.memo(
969
+ ({
970
+ line,
971
+ showChords,
972
+ transpose = 0,
973
+ onChordClick,
974
+ maxGridMeasures
975
+ }) => {
976
+ if (line.type === "empty") return /* @__PURE__ */ jsx2("div", { className: "h-2" });
977
+ if (line.type === "comment") return /* @__PURE__ */ jsx2(CommentRenderer, { line });
978
+ if (line.type === "comment_box") return /* @__PURE__ */ jsx2(CommentBoxRenderer, { line });
979
+ if (line.type === "chord-section")
980
+ return /* @__PURE__ */ jsx2(
981
+ ChordSectionRenderer,
982
+ {
983
+ line,
984
+ showChords,
985
+ transpose,
986
+ onChordClick,
987
+ maxMeasures: maxGridMeasures
988
+ }
989
+ );
990
+ return /* @__PURE__ */ jsx2(
991
+ LyricsRenderer,
992
+ {
993
+ line,
994
+ showChords,
995
+ transpose,
996
+ onChordClick
997
+ }
998
+ );
999
+ }
1000
+ );
1001
+ var CommentRenderer = React.memo(({ line }) => /* @__PURE__ */ jsx2("div", { className: "text-xs text-slate-400 dark:text-slate-500 italic my-1", children: line.text }));
1002
+ var CommentBoxRenderer = React.memo(({ line }) => /* @__PURE__ */ jsxs2("div", { className: "bg-amber-100 dark:bg-amber-900/30 border-l-4 border-amber-500 text-amber-900 dark:text-amber-200 p-2.5 my-2.5 rounded-r-md text-xs font-bold tracking-wide flex items-center gap-2 shadow-sm max-w-fit", children: [
1003
+ /* @__PURE__ */ jsx2(Info, { className: "w-4 h-4 shrink-0 text-amber-600 dark:text-amber-500" }),
1004
+ /* @__PURE__ */ jsx2("span", { children: line.text })
1005
+ ] }));
1006
+ var LyricsRenderer = React.memo(
1007
+ ({
1008
+ line,
1009
+ showChords,
1010
+ transpose = 0,
1011
+ onChordClick
1012
+ }) => {
1013
+ const segments = line.segments || [];
1014
+ return /* @__PURE__ */ jsx2("div", { className: "flex flex-wrap items-end leading-relaxed", children: segments.map((seg, segIdx) => {
1015
+ const hasChord = !!seg.chord;
1016
+ const transposed = hasChord ? transposeChord(seg.chord, transpose) : "";
1017
+ return /* @__PURE__ */ jsxs2(
1018
+ "div",
1019
+ {
1020
+ className: "flex flex-col justify-end relative select-text",
1021
+ style: {
1022
+ minWidth: hasChord && showChords ? `${Math.max(1.1, transposed.length * 0.65)}em` : void 0
1023
+ },
1024
+ children: [
1025
+ showChords && hasChord && /* @__PURE__ */ jsx2(
1026
+ "span",
1027
+ {
1028
+ className: "font-black text-[#0284c7] font-mono select-none pr-1 inline-block pb-0.5 transition-all cursor-pointer hover:opacity-80",
1029
+ style: { fontSize: "0.85em", lineHeight: "1" },
1030
+ onClick: () => onChordClick?.(transposed),
1031
+ children: transposed
1032
+ }
1033
+ ),
1034
+ /* @__PURE__ */ jsx2("span", { className: "text-slate-800 dark:text-slate-200 whitespace-pre", children: seg.text || "\xA0" })
1035
+ ]
1036
+ },
1037
+ segIdx
1038
+ );
1039
+ }) });
1040
+ }
1041
+ );
1042
+ var ChordSectionRenderer = React.memo(
1043
+ ({
1044
+ line,
1045
+ showChords,
1046
+ transpose = 0,
1047
+ onChordClick,
1048
+ maxMeasures
1049
+ }) => {
1050
+ if (!showChords) return null;
1051
+ const measures = line.measures || [];
1052
+ const hasTiming = measures.some(
1053
+ (m) => m.chords.some((c) => c.timing !== void 0 && c.timing !== 1)
1054
+ );
1055
+ const isGridAligned = maxMeasures !== void 0 && maxMeasures > 0;
1056
+ return /* @__PURE__ */ jsxs2("div", { className: "my-1.5 p-1 w-full flex items-stretch bg-slate-50/80 dark:bg-slate-900/40 rounded-lg border border-slate-200/50 dark:border-slate-800/50 shadow-sm", children: [
1057
+ line.startBarline && /* @__PURE__ */ jsx2("div", { className: "flex items-center justify-center shrink-0 w-6", children: renderBarline(line.startBarline) }),
1058
+ /* @__PURE__ */ jsxs2(
1059
+ "div",
1060
+ {
1061
+ className: isGridAligned ? "grid gap-1 flex-1" : "flex flex-wrap flex-1",
1062
+ style: isGridAligned ? {
1063
+ gridTemplateColumns: `repeat(${maxMeasures}, minmax(0, 1fr))`
1064
+ } : void 0,
1065
+ children: [
1066
+ measures.map((measure, mIdx) => {
1067
+ return /* @__PURE__ */ jsxs2(
1068
+ "div",
1069
+ {
1070
+ className: `flex items-stretch border-r border-slate-200/60 dark:border-slate-700/50 last:border-none ${isGridAligned ? "" : "flex-1 min-w-[120px]"}`,
1071
+ children: [
1072
+ /* @__PURE__ */ jsx2("div", { className: "flex-1 flex items-center justify-evenly gap-1 px-1 py-1", children: measure.chords.map((chordSeg, cIdx) => {
1073
+ const transposed = transposeChord(
1074
+ chordSeg.chord,
1075
+ transpose
1076
+ );
1077
+ const timing = chordSeg.timing ?? 1;
1078
+ return /* @__PURE__ */ jsxs2(
1079
+ "span",
1080
+ {
1081
+ className: "flex-1 flex items-center justify-center cursor-pointer rounded transition-colors hover:bg-white dark:hover:bg-slate-800 py-1.5 px-1 min-w-max shadow-sm border border-transparent hover:border-slate-200 dark:hover:border-slate-700",
1082
+ style: { flexGrow: timing },
1083
+ onClick: () => onChordClick?.(transposed),
1084
+ title: timing !== 1 ? `Dura\xE7\xE3o: ${timing}x` : void 0,
1085
+ children: [
1086
+ /* @__PURE__ */ jsx2("span", { className: "font-bold text-[#0284c7] dark:text-sky-400 font-mono select-none text-[15px] tracking-wide", children: transposed }),
1087
+ hasTiming && timing !== 1 && /* @__PURE__ */ jsxs2("span", { className: "ml-0.5 mt-0.5 text-[10px] font-semibold text-indigo-400 dark:text-indigo-500 opacity-80", children: [
1088
+ timing,
1089
+ "\xD7"
1090
+ ] })
1091
+ ]
1092
+ },
1093
+ cIdx
1094
+ );
1095
+ }) }),
1096
+ measure.endBarline && /* @__PURE__ */ jsx2("div", { className: "flex items-center justify-center shrink-0 px-1 w-6 bg-slate-100/50 dark:bg-slate-800/50 rounded-r", children: renderBarline(measure.endBarline) })
1097
+ ]
1098
+ },
1099
+ mIdx
1100
+ );
1101
+ }),
1102
+ isGridAligned && Array.from({
1103
+ length: Math.max(0, maxMeasures - measures.length)
1104
+ }).map((_, i) => /* @__PURE__ */ jsx2(
1105
+ "div",
1106
+ {
1107
+ className: "border-r border-slate-200/60 dark:border-slate-700/50 last:border-none bg-slate-50/30 dark:bg-slate-800/10 rounded-sm"
1108
+ },
1109
+ `empty-${i}`
1110
+ ))
1111
+ ]
1112
+ }
1113
+ )
1114
+ ] });
1115
+ }
1116
+ );
1117
+ function renderBarline(barline) {
1118
+ const wrapperStyle = "flex items-center justify-center flex-shrink-0 select-none";
1119
+ const repeatStyle = "text-indigo-400 dark:text-indigo-500 text-[18px] leading-none";
1120
+ const normalStyle = "text-slate-300 dark:text-slate-600 font-black text-[16px] leading-none";
1121
+ switch (barline) {
1122
+ case "|:":
1123
+ return /* @__PURE__ */ jsx2("span", { className: wrapperStyle, title: "Start repeat", children: /* @__PURE__ */ jsx2("span", { className: repeatStyle, children: "\u{1D106}" }) });
1124
+ case ":|":
1125
+ return /* @__PURE__ */ jsx2("span", { className: wrapperStyle, title: "End repeat", children: /* @__PURE__ */ jsx2("span", { className: repeatStyle, children: "\u{1D107}" }) });
1126
+ case "||":
1127
+ return /* @__PURE__ */ jsx2("span", { className: wrapperStyle, title: "Double barline", children: /* @__PURE__ */ jsx2("span", { className: normalStyle, children: "\u2016" }) });
1128
+ case "|]":
1129
+ return /* @__PURE__ */ jsx2("span", { className: wrapperStyle, title: "End of piece", children: /* @__PURE__ */ jsx2("span", { className: normalStyle, children: "\u{1D102}" }) });
1130
+ case "|":
1131
+ return /* @__PURE__ */ jsx2("span", { className: wrapperStyle, children: /* @__PURE__ */ jsx2("span", { className: normalStyle, children: "|" }) });
1132
+ default:
1133
+ return /* @__PURE__ */ jsx2(
1134
+ "span",
1135
+ {
1136
+ className: `${wrapperStyle} text-slate-400 dark:text-slate-500 text-xs font-bold tracking-widest`,
1137
+ children: barline
1138
+ }
1139
+ );
1140
+ }
1141
+ }
1142
+ var ChordProRenderer_default = ChordProRenderer;
1143
+
1144
+ export {
1145
+ GuitarDiagram,
1146
+ PianoDiagram,
1147
+ ChordRoll,
1148
+ ChordProRenderer,
1149
+ ChordProRenderer_default
1150
+ };
1151
+ /**
1152
+ * @license
1153
+ * SPDX-License-Identifier: Apache-2.0
1154
+ */