@fretfall/player 0.3.0 → 0.4.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/mount.js CHANGED
@@ -1,4 +1,4 @@
1
- let l=null;const d=e=>l??=i(e);async function i(e){const a=Object.assign(document.createElement("style"),{textContent:'[data-fretfall]{--bg: #0e1014;--ink: #0e1014;--text: #e8eaef;--muted: #7b8190;--accent: #c8f04a;--chip: #171a20;--chip-border: #2a2f38;--done: #e8eaef;--todo: #23272f;--ui: system-ui, sans-serif;--num: system-ui, sans-serif;--lyric: system-ui, sans-serif;--lyric-style: normal}:where([data-fretfall]) *{box-sizing:border-box}:where([data-fretfall]) [hidden]{display:none!important}[data-fretfall]:not([data-fretfall-dock]){height:100%}[data-fretfall]:not([data-fretfall-dock]){margin:0;overflow:hidden;background:var(--bg);color:var(--text);font:14px/1.4 var(--ui);accent-color:var(--accent)}:where([data-fretfall]) canvas{position:fixed;inset:0;width:100%;height:100%;display:block}:where([data-fretfall]) button{font:inherit;color:inherit}:where([data-fretfall]) .bar{position:fixed;z-index:3;left:32px;right:32px;top:18px;height:40px;display:flex;align-items:center;justify-content:space-between;gap:24px;transition:transform .25s ease,opacity .25s ease,visibility .25s}:where([data-fretfall]) .group{display:flex;align-items:center;gap:10px;min-width:0}:where([data-fretfall]) .group:last-child{flex:none}:where([data-fretfall]) .wordmark{display:flex;align-items:center;gap:7px;font:700 15px var(--num);letter-spacing:.22em}:where([data-fretfall]) :is(.wordmark,.welcome-mark) .fall{transform-box:fill-box;transform-origin:center;transform:rotate(14deg);animation:fall .75s .8s both}@keyframes fall{0%{transform:translate(-3.5px,-1.42px) rotate(0);animation-timing-function:cubic-bezier(.55,0,.85,.35)}40%{transform:translate(0) rotate(18deg);animation-timing-function:cubic-bezier(.2,.7,.4,1)}56%{transform:translateY(-.35px) rotate(11.5deg);animation-timing-function:ease-in-out}74%{transform:translateY(.08px) rotate(15.2deg);animation-timing-function:ease-in-out}88%{transform:translate(0) rotate(13.6deg);animation-timing-function:ease-in-out}to{transform:translate(0) rotate(14deg)}}@media(prefers-reduced-motion:reduce){:where([data-fretfall]) .wordmark .fall{animation:none}}:where([data-fretfall]) .seg{display:flex;gap:2px;padding:3px;border-radius:9px;background:var(--chip);border:1px solid var(--chip-border);overflow-x:auto;scrollbar-width:none}:where([data-fretfall]) .seg button{border:0;background:none;cursor:pointer;padding:5px 12px;border-radius:6px;font:500 13px var(--ui);color:var(--muted);white-space:nowrap}:where([data-fretfall]) .seg button[aria-pressed=true]{background:var(--text);color:var(--ink);font-weight:600}:where([data-fretfall]) .seg select{max-width:22cqw;border:0;padding:4px 6px;background:none;color:var(--text);font:600 13px var(--ui)}:where([data-fretfall]) .seg option{background:var(--ink);color:var(--text)}:where([data-fretfall]) .icon{flex:none;cursor:pointer;width:36px;height:36px;display:grid;place-items:center;padding:0;border-radius:8px;background:var(--chip);border:1px solid var(--chip-border);color:var(--text)}:where([data-fretfall]) .icon[aria-pressed=true],:where([data-fretfall]) .icon[aria-expanded=true]{background:var(--accent);border-color:var(--accent);color:var(--ink)}:where([data-fretfall]) button:focus-visible,:where([data-fretfall]) input:focus-visible{outline:2px solid var(--accent);outline-offset:2px}:where([data-fretfall]) .speed{height:36px;display:flex;align-items:center;padding:0 2px;border-radius:8px;background:var(--chip);border:1px solid var(--chip-border);color:var(--muted)}:where([data-fretfall]) .speed button{cursor:pointer;width:28px;height:32px;display:grid;place-items:center;padding:0;border:0;background:none;color:inherit}:where([data-fretfall]) .speed #speed{width:auto;min-width:46px;font:600 14px var(--num);color:var(--text)}:where([data-fretfall]) .time{margin-right:6px;font:14px var(--num);color:var(--muted);font-variant-numeric:tabular-nums;white-space:nowrap}:where([data-fretfall]) .divider{width:1px;height:24px;margin:0 4px;background:var(--chip-border)}:where([data-fretfall]) .menu{display:contents}:where([data-fretfall]) #menuButton{display:none}:where([data-fretfall]) #phrases{position:fixed;left:32px;right:32px;top:74px;height:54px;cursor:pointer;touch-action:none;-webkit-user-select:none;user-select:none}:where([data-fretfall]) #phrases div{position:absolute;bottom:0;min-width:2px;border-radius:2px;background:var(--todo)}:where([data-fretfall]) #phrases div.done{background:var(--done)}:where([data-fretfall]) #phraseTime{position:absolute;top:calc(100% + 16px);transform:translate(-50%);padding:2px 7px;border-radius:6px;background:var(--ink);border:1px solid var(--chip-border);font:600 11px var(--num);font-variant-numeric:tabular-nums;color:var(--text);pointer-events:none}:where([data-fretfall]) #loopBox{position:absolute;top:-6px;bottom:-17px;border:1.5px solid var(--accent);background:color-mix(in srgb,var(--accent) 10%,transparent);box-shadow:0 0 18px color-mix(in srgb,var(--accent) 22%,transparent)}:where([data-fretfall]) #loopBox:before,:where([data-fretfall]) #loopBox:after{content:"";position:absolute;top:-11px;bottom:-11px;width:4px;border:4px solid transparent;border-radius:6px;background:var(--accent) padding-box;cursor:ew-resize}:where([data-fretfall]) #loopBox:before{left:-6.75px}:where([data-fretfall]) #loopBox:after{right:-6.75px}:where([data-fretfall]) #progress{position:fixed;left:32px;right:32px;top:134px;height:5px;border-radius:3px;overflow:hidden;background:var(--todo)}:where([data-fretfall]) #progress div{width:0;height:100%;background:var(--accent);opacity:.9}[data-fretfall][data-docked] #phrases{top:18px}[data-fretfall][data-docked] #progress{top:78px}[data-fretfall][data-docked] #lyrics{top:108px}[data-fretfall][data-docked] .sheet{top:18px}:where([data-fretfall]) #lyrics{position:fixed;left:40px;top:164px;max-width:min(560px,46cqw);display:flex;flex-direction:column;gap:8px;pointer-events:none;font-family:var(--lyric);font-style:var(--lyric-style);text-shadow:0 2px 14px var(--ink),0 0 4px var(--ink)}:where([data-fretfall]) #section{min-height:16px;font:600 12px var(--num);letter-spacing:.18em;color:var(--accent)}:where([data-fretfall]) #lines{position:relative;height:2.45em;font-size:clamp(20px,2.3cqw,32px);line-height:1.15}:where([data-fretfall]) #lines div{position:absolute;top:0;left:0;white-space:nowrap;color:var(--muted);transition:transform .45s cubic-bezier(.22,1,.36,1),opacity .35s ease}:where([data-fretfall]) #lines .low{transform:translateY(1.3em);opacity:.5}:where([data-fretfall]) #lines .gone{opacity:0;transition-duration:.45s,.25s}:where([data-fretfall]) #lines .gone.up{transform:translateY(-.8em)}:where([data-fretfall]) #lines span{transition:color .15s}:where([data-fretfall]) #lines .sung{color:var(--text)}@media(prefers-reduced-motion:reduce){:where([data-fretfall]) #lines div{transition-property:opacity}}:where([data-fretfall]) #info{position:fixed;left:40px;right:226px;bottom:14px;display:flex;align-items:center;font-size:13px;white-space:nowrap;color:var(--muted);pointer-events:none}:where([data-fretfall]) #info b{color:var(--text);font-weight:600}:where([data-fretfall]) #info>span+span:before{content:"";display:inline-block;width:1px;height:12px;margin:0 14px;vertical-align:-1px;background:var(--chip-border)}:where([data-fretfall]) #info .tuning{position:relative;pointer-events:auto;cursor:help}:where([data-fretfall]) #info .tuning>b{text-decoration:underline dotted var(--muted);text-underline-offset:3px}:where([data-fretfall]) #info .tip{left:0;right:auto;white-space:normal}:where([data-fretfall]) #info .tuning:is(:hover,:focus)>.tip{opacity:1;visibility:visible;transform:none}:where([data-fretfall]) .pegs{display:flex;align-items:flex-start;gap:4px;margin:10px 0 8px}:where([data-fretfall]) .peg{display:flex;flex-direction:column;align-items:center;min-width:26px;gap:2px;font:600 13px var(--num)}:where([data-fretfall]) .peg i{font-style:normal;font-size:11px;color:var(--muted)}:where([data-fretfall]) .peg .move:before{content:"";display:block;width:3px;height:calc(6px + var(--d) * 3px);margin:2px auto 3px;border-radius:2px;background:var(--c)}:where([data-fretfall]) .peg strong{color:var(--c)}:where([data-fretfall]) .peg.same{opacity:.45}:where([data-fretfall]) .tip small{display:block;font-size:11px}[data-fretfall]:fullscreen .bar{transform:translateY(calc(-100% - 18px));opacity:0;visibility:hidden;transition-delay:.4s}[data-fretfall]:fullscreen:has(#phrases:hover,#progress:hover,.bar:hover) .bar{transform:none;opacity:1;visibility:visible;transition-delay:0s}:where([data-fretfall]) #tools{position:fixed;z-index:3;right:32px;bottom:6px}:where([data-fretfall]) #tools .icon{position:relative}:where([data-fretfall]) .icon:disabled{cursor:default;color:var(--muted)}:where([data-fretfall]) .icon:disabled svg{opacity:.5}:where([data-fretfall]) #loopTools{position:fixed;z-index:4;display:flex;transform:translate(-50%,calc(-50% + 3px))}:where([data-fretfall]) #loopTools .icon{width:32px;height:26px;background:var(--ink);border-color:var(--accent)}:where([data-fretfall]) #loopTools svg{width:15px;height:15px}:where([data-fretfall]) #trainer{border-radius:13px 0 0 13px}:where([data-fretfall]) #clearLoop{border-left:0;border-radius:0 13px 13px 0;color:var(--muted)}:where([data-fretfall]) #clearLoop:hover{color:var(--text)}:where([data-fretfall]) #loopTools .icon[aria-pressed=true]{background:var(--accent);color:var(--ink)}:where([data-fretfall]) .tip{position:absolute;right:0;bottom:calc(100% + 10px);width:max-content;max-width:250px;padding:8px 10px;border-radius:8px;background:var(--ink);border:1px solid var(--chip-border);box-shadow:0 12px 30px #00000073;color:var(--muted);font:12px/1.45 var(--ui);text-align:left;pointer-events:none;opacity:0;visibility:hidden;transform:translateY(4px);transition:opacity .12s ease,transform .12s ease,visibility .12s}:where([data-fretfall]) .tip b{display:flex;justify-content:space-between;gap:12px;margin-bottom:2px;color:var(--text);font-weight:600}:where([data-fretfall]) .tip kbd{padding:0 5px;border:1px solid var(--chip-border);border-radius:4px;color:var(--muted);font:inherit}:where([data-fretfall]) :is(.icon,.speed):hover>.tip,:where([data-fretfall]) .icon:focus-visible>.tip,:where([data-fretfall]) .speed:focus-within>.tip{opacity:1;visibility:visible;transform:none}[data-fretfall]:where([data-fretfall-dock]){display:flex;align-items:center;gap:10px}:where([data-fretfall]) .bar :is(.icon,.speed),[data-fretfall]:where([data-fretfall-dock]) :is(.icon,.speed){position:relative}:where([data-fretfall]) .bar .tip,[data-fretfall]:where([data-fretfall-dock]) .tip{top:calc(100% + 10px);bottom:auto;transform:translateY(-4px)}:where([data-fretfall]) .bar [aria-expanded=true]>.tip,[data-fretfall]:where([data-fretfall-dock]) [aria-expanded=true]>.tip{display:none}:where([data-fretfall]) #loopTools .tip{top:calc(100% + 10px);bottom:auto;left:var(--tip, 50%);right:auto;transform:translate(calc(-1 * var(--tip, 50%)),-4px)}:where([data-fretfall]) #loopTools .icon:hover>.tip,:where([data-fretfall]) #loopTools .icon:focus-visible>.tip{transform:translate(calc(-1 * var(--tip, 50%)))}[data-fretfall]:fullscreen :is(#info,#tools>:not(#minimal)){display:none}[data-fretfall].minimal :is(.bar,#phrases,#progress,#loopTools,#info,#tools>:not(#minimal)){display:none}[data-fretfall].tab2d .only-3d,[data-fretfall]:not(.tab2d) .only-2d{display:none}[data-fretfall].tab2d .only-3d+.only-3d+h3{padding-top:0;border-top:0}[data-fretfall].minimal #lyrics{top:24px}:where([data-fretfall]) #status{position:fixed;left:50%;top:42%;transform:translate(-50%,-50%);display:flex;align-items:center;gap:12px;max-width:min(560px,90cqw);padding:10px 16px;border-radius:10px;background:var(--chip);border:1px solid var(--chip-border);backdrop-filter:blur(10px);text-align:center}[data-fretfall].banding :is(#highway,#phrases,#progress,#loopTools,#lyrics,#info,#intro,#time){display:none}:where([data-fretfall]) #bandNote{position:fixed;left:50%;top:56%;transform:translate(-50%,-50%);display:grid;justify-items:center;gap:8px;max-width:min(440px,90cqw);padding:14px 20px;border-radius:10px;background:var(--chip);border:1px solid var(--chip-border);text-align:center}:where([data-fretfall]) #bandNote .text-button{justify-self:center}:where([data-fretfall]) #status:not(.loading) .bars{display:none}:where([data-fretfall]) .bars{flex:none;display:inline-flex;align-items:flex-end;gap:3px;height:16px}:where([data-fretfall]) .bars i{width:3px;height:100%;border-radius:2px;background:var(--accent);transform:scaleY(.3);transform-origin:bottom}:where([data-fretfall]) .bars.moving i{animation:bars .8s ease-in-out infinite alternate}:where([data-fretfall]) .bars.moving i:nth-child(2){animation-duration:1.1s;animation-delay:-.5s}:where([data-fretfall]) .bars.moving i:nth-child(3){animation-duration:.7s;animation-delay:-.2s}:where([data-fretfall]) .bars.moving i:nth-child(4){animation-duration:1s;animation-delay:-.7s}@keyframes bars{0%{transform:scaleY(.2)}to{transform:scaleY(1)}}@media(prefers-reduced-motion:reduce){:where([data-fretfall]) .bars.moving i{animation:none;transform:scaleY(.6)}}:where([data-fretfall]) #intro{position:fixed;left:50%;top:36%;transform:translate(-50%,-50%);display:grid;justify-items:center;gap:6px;max-width:min(640px,90cqw);padding:22px 30px;border-radius:16px;background:color-mix(in srgb,var(--ink) 72%,transparent);border:1px solid var(--chip-border);text-align:center;pointer-events:none;opacity:0;visibility:hidden;transition:opacity .5s ease,visibility .5s}:where([data-fretfall]) #intro.show{opacity:1;visibility:visible}:where([data-fretfall]) #introTitle{font:700 clamp(22px,3cqw,38px) / 1.15 var(--num);color:var(--text)}:where([data-fretfall]) #introFacts{font-size:14px;color:var(--muted)}:where([data-fretfall]) #introState{display:flex;align-items:center;gap:10px;margin-top:10px;font:600 12px var(--num);letter-spacing:.14em;text-transform:uppercase;color:var(--accent)}:where([data-fretfall]) .sheet{position:fixed;right:32px;top:66px;z-index:2;width:min(400px,calc(100cqw - 32px));max-height:calc(100cqh - 118px);display:flex;flex-direction:column;border-radius:14px;overflow:hidden;background:color-mix(in srgb,var(--ink) 94%,transparent);backdrop-filter:blur(18px);border:1px solid var(--chip-border);box-shadow:0 24px 60px #0000008c}:where([data-fretfall]) .sheet-head{display:flex;align-items:center;justify-content:space-between;padding:12px 12px 10px 18px}:where([data-fretfall]) .sheet-head h2{margin:0;font:600 15px var(--ui)}:where([data-fretfall]) .close{width:30px;height:30px;display:grid;place-items:center;padding:0;border:0;border-radius:8px;background:none;color:var(--muted);cursor:pointer}:where([data-fretfall]) .close:hover{background:var(--chip);color:var(--text)}:where([data-fretfall]) .tabs{display:grid;grid-template-columns:repeat(5,1fr);gap:2px;margin:0 14px;padding:3px;border-radius:9px;background:var(--chip);border:1px solid var(--chip-border)}:where([data-fretfall]) .tabs button{padding:6px 0;border:0;border-radius:6px;background:none;font:500 13px var(--ui);color:var(--muted);cursor:pointer}:where([data-fretfall]) .tabs button:hover{color:var(--text)}:where([data-fretfall]) .tabs button[aria-selected=true]{background:var(--text);color:var(--ink);font-weight:600}:where([data-fretfall]) .pane{display:grid;align-content:start;gap:18px;padding:18px 18px 20px;overflow:hidden auto}:where([data-fretfall]) .pane h3{margin:0 0 -6px;font:600 11px var(--num);letter-spacing:.14em;text-transform:uppercase;color:var(--muted)}:where([data-fretfall]) .pane h3~h3{padding-top:16px;border-top:1px solid var(--chip-border)}:where([data-fretfall]) .setting{display:grid;gap:8px}:where([data-fretfall]) .pair{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:18px 20px}:where([data-fretfall]) .setting-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;font-size:13px;font-weight:500}:where([data-fretfall]) .setting-head output{font:600 12px var(--num);color:var(--muted);font-variant-numeric:tabular-nums;white-space:nowrap}:where([data-fretfall]) .setting .seg{overflow:visible}:where([data-fretfall]) .setting .seg button{flex:1}:where([data-fretfall]) .scale{display:flex;justify-content:space-between;margin-top:-4px;font-size:11px;color:var(--muted)}:where([data-fretfall]) .hint{margin:-2px 0 0;font-size:12px;line-height:1.4;color:var(--muted)}:where([data-fretfall]) .text-button{justify-self:start;padding:4px 0;border:0;background:none;font:500 12px var(--ui);color:var(--muted);text-decoration:underline;text-underline-offset:3px;cursor:pointer}:where([data-fretfall]) .text-button:hover{color:var(--text)}:where([data-fretfall]) :is(#settings,.volume) input[type=range]{-webkit-appearance:none;appearance:none;height:18px;margin:0;background:none;cursor:pointer}:where([data-fretfall]) #settings input[type=range]{width:100%}:where([data-fretfall]) :is(#settings,.volume) input[type=range]::-webkit-slider-runnable-track{height:4px;border-radius:2px;background:linear-gradient(90deg,var(--accent) var(--fill, 50%),var(--chip-border) var(--fill, 50%))}:where([data-fretfall]) :is(#settings,.volume) input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:16px;height:16px;margin-top:-6px;border-radius:50%;background:var(--text);border:3px solid var(--accent);box-shadow:0 1px 4px #0006}:where([data-fretfall]) :is(#settings,.volume) input[type=range]::-moz-range-track{height:4px;border-radius:2px;background:var(--chip-border)}:where([data-fretfall]) :is(#settings,.volume) input[type=range]::-moz-range-progress{height:4px;border-radius:2px;background:var(--accent)}:where([data-fretfall]) :is(#settings,.volume) input[type=range]::-moz-range-thumb{width:10px;height:10px;border-radius:50%;background:var(--text);border:3px solid var(--accent)}:where([data-fretfall]) .toggle{display:flex;align-items:center;justify-content:space-between;gap:16px;font-size:13px;font-weight:500;cursor:pointer}:where([data-fretfall]) .toggle small{display:block;margin-top:2px;font-size:12px;font-weight:400;line-height:1.4;color:var(--muted)}:where([data-fretfall]) .toggle input{-webkit-appearance:none;appearance:none;flex:none;position:relative;width:36px;height:20px;margin:0;border-radius:10px;background:var(--chip-border);cursor:pointer;transition:background .15s}:where([data-fretfall]) .toggle input:after{content:"";position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:var(--text);transition:transform .15s}:where([data-fretfall]) .toggle input:checked{background:var(--accent)}:where([data-fretfall]) .toggle input:checked:after{transform:translate(16px);background:var(--ink)}:where([data-fretfall]) .cards{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px}:where([data-fretfall]) .cards.three{grid-template-columns:repeat(3,minmax(0,1fr))}:where([data-fretfall]) .cards button{display:grid;gap:8px;justify-items:start;padding:10px;border-radius:9px;border:1px solid var(--chip-border);background:var(--chip);font:500 12px var(--ui);color:var(--muted);text-align:left;cursor:pointer}:where([data-fretfall]) .cards button:hover{color:var(--text)}:where([data-fretfall]) .cards button[aria-pressed=true]{border-color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent);color:var(--text)}:where([data-fretfall]) .dots{display:flex;gap:4px}:where([data-fretfall]) .dots i{width:10px;height:10px;border-radius:50%;box-shadow:inset 0 0 0 1px #ffffff2e}:where([data-fretfall]) .headstock{width:100%;height:52px}:where([data-fretfall]) .order{font:600 12px var(--num);letter-spacing:.12em;color:var(--text)}:where([data-fretfall]) #legend{width:min(430px,calc(100cqw - 32px))}:where([data-fretfall]) .legend-find{padding:0 14px 6px}:where([data-fretfall]) .legend-find input{width:100%;height:34px;padding:0 12px;border-radius:9px;border:1px solid var(--chip-border);background:var(--chip);color:var(--text);font:13px var(--ui)}:where([data-fretfall]) .legend-find input::placeholder{color:var(--muted)}:where([data-fretfall]) .legend{gap:4px;padding-top:10px}:where([data-fretfall]) .legend h3{margin:14px 0 4px}:where([data-fretfall]) .legend h3:first-child{margin-top:0}:where([data-fretfall]) .legend-item{display:grid;grid-template-columns:64px 1fr;gap:12px;align-items:center;padding:4px 0}:where([data-fretfall]) .legend-item b{display:block;font-size:13px;font-weight:500}:where([data-fretfall]) .legend-item span{display:block;font-size:12px;line-height:1.4;color:var(--muted)}:where([data-fretfall]) .legend-item .keys{display:flex;justify-content:center;gap:4px}:where([data-fretfall]) .legend-item kbd{min-width:22px;padding:2px 6px;border:1px solid var(--chip-border);border-bottom-width:2px;border-radius:5px;background:var(--chip);font:600 12px var(--num);text-align:center;white-space:nowrap}:where([data-fretfall]) .legend-item svg{width:64px;height:36px;border-radius:8px;background:var(--chip);border:1px solid var(--chip-border);fill:none;stroke-linecap:round;stroke-linejoin:round}:where([data-fretfall]) .legend-item text{font-family:var(--num);font-weight:700;text-anchor:middle;dominant-baseline:central}:where([data-fretfall]) .legend-empty{margin:8px 0;font-size:13px;color:var(--muted)}:where([data-fretfall]) .f0{fill:var(--s0)}:where([data-fretfall]) .f1{fill:var(--s1)}:where([data-fretfall]) .f2{fill:var(--s2)}:where([data-fretfall]) .f3{fill:var(--s3)}:where([data-fretfall]) .f4{fill:var(--s4)}:where([data-fretfall]) .l0{stroke:var(--s0)}:where([data-fretfall]) .l1{stroke:var(--s1)}:where([data-fretfall]) .l2{stroke:var(--s2)}:where([data-fretfall]) .l3{stroke:var(--s3)}:where([data-fretfall]) .l4{stroke:var(--s4)}:where([data-fretfall]) .lm{stroke:var(--muted)}:where([data-fretfall]) .li{stroke:var(--ink)}:where([data-fretfall]) .ft{fill:var(--text)}:where([data-fretfall]) .lt{stroke:var(--text)}:where([data-fretfall]) .fi{fill:var(--ink)}:where([data-fretfall]) .fa{fill:var(--accent)}:where([data-fretfall]) .fl{fill:var(--lane)}:where([data-fretfall]) .ll{stroke:var(--lane)}:where([data-fretfall]) .lw{stroke:#fff}:where([data-fretfall]) .fm{fill:var(--muted)}:where([data-fretfall]) .la{stroke:var(--accent)}:where([data-fretfall]) .fw{fill:#fff}:where([data-fretfall]) .volume{position:relative}:where([data-fretfall]) .volume .icon.muted{color:var(--muted)}:where([data-fretfall]) .volume-panel{position:absolute;top:calc(100% + 8px);left:50%;transform:translate(-50%);display:flex;align-items:center;gap:8px;padding:6px 14px 6px 6px;border-radius:10px;background:var(--ink);border:1px solid var(--chip-border);box-shadow:0 12px 30px #00000073}:where([data-fretfall]) .volume-panel input{width:150px}:where([data-fretfall]) .volume-panel output{min-width:36px;font:600 12px var(--num);color:var(--muted);text-align:right;font-variant-numeric:tabular-nums}:where([data-fretfall]) .volume-mute{flex:none;width:30px;height:30px;display:grid;place-items:center;padding:0;border:0;border-radius:7px;background:none;color:var(--text);cursor:pointer}:where([data-fretfall]) .volume-mute:hover{background:var(--chip)}:where([data-fretfall]) .volume-mute[aria-pressed=true]{color:var(--muted)}:where([data-fretfall]) #drop{position:fixed;inset:12px;z-index:5;display:grid;place-items:center;border:2px dashed var(--accent);border-radius:16px;background:#00000073;font:600 20px var(--num)}:where([data-fretfall]) #tab{position:fixed;left:-10000px;top:0;width:1000px}@media(max-width:900px){:where([data-fretfall]) .time{display:none}[data-fretfall]:where([data-fretfall-dock]) .time{display:block}:where([data-fretfall]) #menuButton{display:grid}:where([data-fretfall]) .volume{position:static}:where([data-fretfall]) .volume-panel{left:auto;right:0;transform:none}:where([data-fretfall]) .menu{display:none;position:absolute;top:calc(100% + 8px);right:0;width:230px;flex-direction:column;gap:2px;padding:6px;border-radius:12px;background:var(--ink);border:1px solid var(--chip-border);box-shadow:0 12px 30px #00000073}:where([data-fretfall]) #menuButton[aria-expanded=true]+.menu{display:flex}:where([data-fretfall]) .menu .icon{width:100%;height:38px;display:flex;justify-content:flex-start;gap:12px;padding:0 10px;border:0;border-radius:7px;background:none;font:500 13px var(--ui)}:where([data-fretfall]) .menu .icon:after{content:attr(data-label)}:where([data-fretfall]) .menu .icon:hover{background:var(--chip)}:where([data-fretfall]) .menu .icon[aria-pressed=true],:where([data-fretfall]) .menu .icon[aria-expanded=true]{background:none;color:var(--accent)}:where([data-fretfall]) .menu .tip{display:none}:where([data-fretfall]) .menu .divider{width:auto;height:1px;margin:4px}}@media(max-width:560px){:where([data-fretfall]) .bar{flex-wrap:wrap;height:auto;row-gap:8px}:where([data-fretfall]) .bar>.group:first-child,:where([data-fretfall]) .bar>.group:last-child{flex:1 1 100%;height:40px}:where([data-fretfall]) .speed{margin-left:auto}:where([data-fretfall]) #menuButton{position:absolute;top:2px;right:0}:where([data-fretfall]) .menu{top:48px}:where([data-fretfall]) #phrases{top:122px}:where([data-fretfall]) #info{flex-direction:column;align-items:flex-start;gap:2px;font-size:12px}:where([data-fretfall]) #info>span+span:before{display:none}:where([data-fretfall]) #info>span:first-child{max-width:100%;overflow:hidden;text-overflow:ellipsis}:where([data-fretfall]) #progress{top:182px}:where([data-fretfall]) #lyrics{top:212px}:where([data-fretfall]) .sheet{top:114px;max-height:calc(100cqh - 166px)}}@media(max-width:520px){:where([data-fretfall]) .sheet{right:16px}}[data-fretfall]:not([data-fretfall-dock]){contain:layout;container-type:size}'}),n=Object.assign(document.createElement("link"),{id:"fonts",rel:"stylesheet"});if(!e.isConnected)throw new Error("mount(element): the element has to be in the page");e.innerHTML=`<div data-fretfall><canvas id="highway"></canvas>
1
+ let l=null;const d=e=>l??=i(e);async function i(e){const a=Object.assign(document.createElement("style"),{textContent:'[data-fretfall]:not([data-fretfall-dock]),:where([data-fretfall-dock]){--bg: #0e1014;--ink: #0e1014;--text: #e8eaef;--muted: #7b8190;--accent: #c8f04a;--chip: #171a20;--chip-border: #2a2f38;--done: #e8eaef;--todo: #23272f;--ui: system-ui, sans-serif;--num: system-ui, sans-serif;--lyric: system-ui, sans-serif;--lyric-style: normal}:where([data-fretfall]) *{box-sizing:border-box}:where([data-fretfall]) [hidden]{display:none!important}[data-fretfall]:not([data-fretfall-dock]){height:100%}[data-fretfall]:not([data-fretfall-dock]){margin:0;overflow:hidden;background:var(--bg);color:var(--text);font:14px/1.4 var(--ui);accent-color:var(--accent)}:where([data-fretfall]) canvas{position:fixed;inset:0;width:100%;height:100%;display:block}:where([data-fretfall]) button{font:inherit;color:inherit}:where([data-fretfall]) .bar{position:fixed;z-index:3;left:32px;right:32px;top:18px;height:40px;display:flex;align-items:center;justify-content:space-between;gap:24px;transition:transform .25s ease,opacity .25s ease,visibility .25s}:where([data-fretfall]) .group{display:flex;align-items:center;gap:10px;min-width:0}:where([data-fretfall]) .group:last-child{flex:none}:where([data-fretfall]) .wordmark{display:flex;align-items:center;gap:7px;font:700 15px var(--num);letter-spacing:.22em}:where([data-fretfall]) :is(.wordmark,.welcome-mark) .fall{transform-box:fill-box;transform-origin:center;transform:rotate(14deg);animation:fall .75s .8s both}@keyframes fall{0%{transform:translate(-3.5px,-1.42px) rotate(0);animation-timing-function:cubic-bezier(.55,0,.85,.35)}40%{transform:translate(0) rotate(18deg);animation-timing-function:cubic-bezier(.2,.7,.4,1)}56%{transform:translateY(-.35px) rotate(11.5deg);animation-timing-function:ease-in-out}74%{transform:translateY(.08px) rotate(15.2deg);animation-timing-function:ease-in-out}88%{transform:translate(0) rotate(13.6deg);animation-timing-function:ease-in-out}to{transform:translate(0) rotate(14deg)}}@media(prefers-reduced-motion:reduce){:where([data-fretfall]) .wordmark .fall{animation:none}}:where([data-fretfall]) .seg{display:flex;gap:2px;padding:3px;border-radius:9px;background:var(--chip);border:1px solid var(--chip-border);overflow-x:auto;scrollbar-width:none}:where([data-fretfall]) .seg button{border:0;background:none;cursor:pointer;padding:5px 12px;border-radius:6px;font:500 13px var(--ui);color:var(--muted);white-space:nowrap}:where([data-fretfall]) .seg button[aria-pressed=true]{background:var(--text);color:var(--ink);font-weight:600}:where([data-fretfall]) .seg select{max-width:22cqw;border:0;padding:4px 6px;background:none;color:var(--text);font:600 13px var(--ui)}:where([data-fretfall]) .seg option{background:var(--ink);color:var(--text)}:where([data-fretfall]) .icon{flex:none;cursor:pointer;width:36px;height:36px;display:grid;place-items:center;padding:0;border-radius:8px;background:var(--chip);border:1px solid var(--chip-border);color:var(--text)}:where([data-fretfall]) .icon[aria-pressed=true],:where([data-fretfall]) .icon[aria-expanded=true]{background:var(--accent);border-color:var(--accent);color:var(--ink)}:where([data-fretfall]) button:focus-visible,:where([data-fretfall]) input:focus-visible{outline:2px solid var(--accent);outline-offset:2px}:where([data-fretfall]) .speed{height:36px;display:flex;align-items:center;padding:0 2px;border-radius:8px;background:var(--chip);border:1px solid var(--chip-border);color:var(--muted)}:where([data-fretfall]) .speed button{cursor:pointer;width:28px;height:32px;display:grid;place-items:center;padding:0;border:0;background:none;color:inherit}:where([data-fretfall]) .speed #speed{width:auto;min-width:46px;font:600 14px var(--num);color:var(--text)}:where([data-fretfall]) .time{margin-right:6px;font:14px var(--num);color:var(--muted);font-variant-numeric:tabular-nums;white-space:nowrap}:where([data-fretfall]) .divider{width:1px;height:24px;margin:0 4px;background:var(--chip-border)}:where([data-fretfall]) .menu{display:contents}:where([data-fretfall]) #menuButton{display:none}:where([data-fretfall]) #phrases{position:fixed;left:32px;right:32px;top:74px;height:54px;cursor:pointer;touch-action:none;-webkit-user-select:none;user-select:none}:where([data-fretfall]) #phrases div{position:absolute;bottom:0;min-width:2px;border-radius:2px;background:var(--todo)}:where([data-fretfall]) #phrases div.done{background:var(--done)}:where([data-fretfall]) #phraseTime{position:absolute;top:calc(100% + 16px);transform:translate(-50%);padding:2px 7px;border-radius:6px;background:var(--ink);border:1px solid var(--chip-border);font:600 11px var(--num);font-variant-numeric:tabular-nums;color:var(--text);pointer-events:none}:where([data-fretfall]) #loopBox{position:absolute;top:-6px;bottom:-17px;border:1.5px solid var(--accent);background:color-mix(in srgb,var(--accent) 10%,transparent);box-shadow:0 0 18px color-mix(in srgb,var(--accent) 22%,transparent)}:where([data-fretfall]) #loopBox:before,:where([data-fretfall]) #loopBox:after{content:"";position:absolute;top:-11px;bottom:-11px;width:4px;border:4px solid transparent;border-radius:6px;background:var(--accent) padding-box;cursor:ew-resize}:where([data-fretfall]) #loopBox:before{left:-6.75px}:where([data-fretfall]) #loopBox:after{right:-6.75px}:where([data-fretfall]) #progress{position:fixed;left:32px;right:32px;top:134px;height:5px;border-radius:3px;overflow:hidden;background:var(--todo)}:where([data-fretfall]) #progress div{width:0;height:100%;background:var(--accent);opacity:.9}[data-fretfall][data-docked] #phrases{top:18px}[data-fretfall][data-docked] #progress{top:78px}[data-fretfall][data-docked] #lyrics{top:108px}[data-fretfall][data-docked] .sheet{top:18px}:where([data-fretfall]) #lyrics{position:fixed;left:40px;top:164px;max-width:min(560px,46cqw);display:flex;flex-direction:column;gap:8px;pointer-events:none;font-family:var(--lyric);font-style:var(--lyric-style);text-shadow:0 2px 14px var(--ink),0 0 4px var(--ink)}:where([data-fretfall]) #section{min-height:16px;font:600 12px var(--num);letter-spacing:.18em;color:var(--accent)}:where([data-fretfall]) #lines{position:relative;height:2.45em;font-size:clamp(20px,2.3cqw,32px);line-height:1.15}:where([data-fretfall]) #lines div{position:absolute;top:0;left:0;white-space:nowrap;color:var(--muted);transition:transform .45s cubic-bezier(.22,1,.36,1),opacity .35s ease}:where([data-fretfall]) #lines .low{transform:translateY(1.3em);opacity:.5}:where([data-fretfall]) #lines .gone{opacity:0;transition-duration:.45s,.25s}:where([data-fretfall]) #lines .gone.up{transform:translateY(-.8em)}:where([data-fretfall]) #lines span{transition:color .15s}:where([data-fretfall]) #lines .sung{color:var(--text)}@media(prefers-reduced-motion:reduce){:where([data-fretfall]) #lines div{transition-property:opacity}}:where([data-fretfall]) #info{position:fixed;left:40px;right:226px;bottom:14px;display:flex;align-items:center;font-size:13px;white-space:nowrap;color:var(--muted);pointer-events:none}:where([data-fretfall]) #info b{color:var(--text);font-weight:600}:where([data-fretfall]) #info>span+span:before{content:"";display:inline-block;width:1px;height:12px;margin:0 14px;vertical-align:-1px;background:var(--chip-border)}:where([data-fretfall]) #info .tuning{position:relative;pointer-events:auto;cursor:help}:where([data-fretfall]) #info .tuning>b{text-decoration:underline dotted var(--muted);text-underline-offset:3px}:where([data-fretfall]) #info .tip{left:0;right:auto;white-space:normal}:where([data-fretfall]) #info .tuning:is(:hover,:focus)>.tip{opacity:1;visibility:visible;transform:none}:where([data-fretfall]) .pegs{display:flex;align-items:flex-start;gap:4px;margin:10px 0 8px}:where([data-fretfall]) .peg{display:flex;flex-direction:column;align-items:center;min-width:26px;gap:2px;font:600 13px var(--num)}:where([data-fretfall]) .peg i{font-style:normal;font-size:11px;color:var(--muted)}:where([data-fretfall]) .peg .move:before{content:"";display:block;width:3px;height:calc(6px + var(--d) * 3px);margin:2px auto 3px;border-radius:2px;background:var(--c)}:where([data-fretfall]) .peg strong{color:var(--c)}:where([data-fretfall]) .peg.same{opacity:.45}:where([data-fretfall]) .tip small{display:block;font-size:11px}[data-fretfall]:fullscreen .bar{transform:translateY(calc(-100% - 18px));opacity:0;visibility:hidden;transition-delay:.4s}[data-fretfall]:fullscreen:has(#phrases:hover,#progress:hover,.bar:hover) .bar{transform:none;opacity:1;visibility:visible;transition-delay:0s}:where([data-fretfall]) .seg.views{margin-left:4px}:where([data-fretfall]) .seg.views button{padding:5px 11px;font-size:12px}:where([data-fretfall]) .icon:disabled{cursor:default;color:var(--muted)}:where([data-fretfall]) .icon:disabled svg{opacity:.5}:where([data-fretfall]) #loopTools{position:fixed;z-index:4;display:flex;transform:translate(-50%,calc(-50% + 3px))}:where([data-fretfall]) #loopTools .icon{width:32px;height:26px;background:var(--ink);border-color:var(--accent)}:where([data-fretfall]) #loopTools svg{width:15px;height:15px}:where([data-fretfall]) #trainer{border-radius:13px 0 0 13px}:where([data-fretfall]) #clearLoop{border-left:0;border-radius:0 13px 13px 0;color:var(--muted)}:where([data-fretfall]) #clearLoop:hover{color:var(--text)}:where([data-fretfall]) #loopTools .icon[aria-pressed=true]{background:var(--accent);color:var(--ink)}:where([data-fretfall]) .tip{position:absolute;right:0;bottom:calc(100% + 10px);width:max-content;max-width:250px;padding:8px 10px;border-radius:8px;background:var(--ink);border:1px solid var(--chip-border);box-shadow:0 12px 30px #00000073;color:var(--muted);font:12px/1.45 var(--ui);text-align:left;pointer-events:none;opacity:0;visibility:hidden;transform:translateY(4px);transition:opacity .12s ease,transform .12s ease,visibility .12s}:where([data-fretfall]) .tip b{display:flex;justify-content:space-between;gap:12px;margin-bottom:2px;color:var(--text);font-weight:600}:where([data-fretfall]) .tip kbd{padding:0 5px;border:1px solid var(--chip-border);border-radius:4px;color:var(--muted);font:inherit}:where([data-fretfall]) :is(.icon,.speed):hover>.tip,:where([data-fretfall]) .icon:focus-visible>.tip,:where([data-fretfall]) .speed:focus-within>.tip{opacity:1;visibility:visible;transform:none}[data-fretfall]:where([data-fretfall-dock]){display:flex;align-items:center;gap:10px}:where([data-fretfall]) .bar :is(.icon,.speed),[data-fretfall]:where([data-fretfall-dock]) :is(.icon,.speed){position:relative}:where([data-fretfall]) .bar .tip,[data-fretfall]:where([data-fretfall-dock]) .tip{top:calc(100% + 10px);bottom:auto;transform:translateY(-4px)}:where([data-fretfall]) .bar [aria-expanded=true]>.tip,[data-fretfall]:where([data-fretfall-dock]) [aria-expanded=true]>.tip{display:none}:where([data-fretfall]) #loopTools .tip{top:calc(100% + 10px);bottom:auto;left:var(--tip, 50%);right:auto;transform:translate(calc(-1 * var(--tip, 50%)),-4px)}:where([data-fretfall]) #loopTools .icon:hover>.tip,:where([data-fretfall]) #loopTools .icon:focus-visible>.tip{transform:translate(calc(-1 * var(--tip, 50%)))}[data-fretfall]:fullscreen #info{display:none}[data-fretfall].minimal :is(.bar>:not(#minimalBack),#phrases,#progress,#loopTools,#info){display:none}[data-fretfall].minimal .bar{background:none;border:0;box-shadow:none;pointer-events:none}[data-fretfall].minimal #minimalBack{display:inline-flex;pointer-events:auto;margin-left:auto}:where([data-fretfall]) #minimalBack{display:none}[data-fretfall].tab2d .only-3d,[data-fretfall]:not(.tab2d) .only-2d{display:none}[data-fretfall].tab2d .only-3d+.only-3d+h3{padding-top:0;border-top:0}[data-fretfall].notation .not-notation,[data-fretfall]:not(.notation) .only-notation{display:none}[data-fretfall].minimal #lyrics{top:24px}:where([data-fretfall]) #status{position:fixed;left:50%;top:42%;transform:translate(-50%,-50%);display:flex;align-items:center;gap:12px;max-width:min(560px,90cqw);padding:10px 16px;border-radius:10px;background:var(--chip);border:1px solid var(--chip-border);backdrop-filter:blur(10px);text-align:center}[data-fretfall].banding :is(#highway,#phrases,#progress,#loopTools,#lyrics,#info,#intro,#time){display:none}:where([data-fretfall]) #bandNote{position:fixed;left:50%;top:56%;transform:translate(-50%,-50%);display:grid;justify-items:center;gap:8px;max-width:min(440px,90cqw);padding:14px 20px;border-radius:10px;background:var(--chip);border:1px solid var(--chip-border);text-align:center}:where([data-fretfall]) #bandNote .text-button{justify-self:center}:where([data-fretfall]) #status:not(.loading) .bars{display:none}:where([data-fretfall]) .bars{flex:none;display:inline-flex;align-items:flex-end;gap:3px;height:16px}:where([data-fretfall]) .bars i{width:3px;height:100%;border-radius:2px;background:var(--accent);transform:scaleY(.3);transform-origin:bottom}:where([data-fretfall]) .bars.moving i{animation:bars .8s ease-in-out infinite alternate}:where([data-fretfall]) .bars.moving i:nth-child(2){animation-duration:1.1s;animation-delay:-.5s}:where([data-fretfall]) .bars.moving i:nth-child(3){animation-duration:.7s;animation-delay:-.2s}:where([data-fretfall]) .bars.moving i:nth-child(4){animation-duration:1s;animation-delay:-.7s}@keyframes bars{0%{transform:scaleY(.2)}to{transform:scaleY(1)}}@media(prefers-reduced-motion:reduce){:where([data-fretfall]) .bars.moving i{animation:none;transform:scaleY(.6)}}:where([data-fretfall]) #intro{position:fixed;left:50%;top:36%;transform:translate(-50%,-50%);display:grid;justify-items:center;gap:6px;max-width:min(640px,90cqw);padding:22px 30px;border-radius:16px;background:color-mix(in srgb,var(--ink) 72%,transparent);border:1px solid var(--chip-border);text-align:center;pointer-events:none;opacity:0;visibility:hidden;transition:opacity .5s ease,visibility .5s}:where([data-fretfall]) #intro.show{opacity:1;visibility:visible}:where([data-fretfall]) #introTitle{font:700 clamp(22px,3cqw,38px) / 1.15 var(--num);color:var(--text)}:where([data-fretfall]) #introFacts{font-size:14px;color:var(--muted)}:where([data-fretfall]) #introState{display:flex;align-items:center;gap:10px;margin-top:10px;font:600 12px var(--num);letter-spacing:.14em;text-transform:uppercase;color:var(--accent)}:where([data-fretfall]) .sheet{position:fixed;right:32px;top:66px;z-index:2;width:min(400px,calc(100cqw - 32px));max-height:calc(100cqh - 118px);display:flex;flex-direction:column;border-radius:14px;overflow:hidden;background:color-mix(in srgb,var(--ink) 94%,transparent);backdrop-filter:blur(18px);border:1px solid var(--chip-border);box-shadow:0 24px 60px #0000008c}:where([data-fretfall]) .sheet-head{display:flex;align-items:center;justify-content:space-between;padding:12px 12px 10px 18px}:where([data-fretfall]) .sheet-head h2{margin:0;font:600 15px var(--ui)}:where([data-fretfall]) .close{width:30px;height:30px;display:grid;place-items:center;padding:0;border:0;border-radius:8px;background:none;color:var(--muted);cursor:pointer}:where([data-fretfall]) .close:hover{background:var(--chip);color:var(--text)}:where([data-fretfall]) .tabs{display:grid;grid-template-columns:repeat(5,1fr);gap:2px;margin:0 14px;padding:3px;border-radius:9px;background:var(--chip);border:1px solid var(--chip-border)}:where([data-fretfall]) .tabs button{padding:6px 0;border:0;border-radius:6px;background:none;font:500 13px var(--ui);color:var(--muted);cursor:pointer}:where([data-fretfall]) .tabs button:hover{color:var(--text)}:where([data-fretfall]) .tabs button[aria-selected=true]{background:var(--text);color:var(--ink);font-weight:600}:where([data-fretfall]) .pane{display:grid;align-content:start;gap:18px;padding:18px 18px 20px;overflow:hidden auto}:where([data-fretfall]) .pane h3{margin:0 0 -6px;font:600 11px var(--num);letter-spacing:.14em;text-transform:uppercase;color:var(--muted)}:where([data-fretfall]) .pane h3~h3{padding-top:16px;border-top:1px solid var(--chip-border)}:where([data-fretfall]) .setting{display:grid;gap:8px}:where([data-fretfall]) .pair{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:18px 20px}:where([data-fretfall]) .setting-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;font-size:13px;font-weight:500}:where([data-fretfall]) .setting-head output{font:600 12px var(--num);color:var(--muted);font-variant-numeric:tabular-nums;white-space:nowrap}:where([data-fretfall]) .setting .seg{overflow:visible}:where([data-fretfall]) .setting .seg button{flex:1}:where([data-fretfall]) .scale{display:flex;justify-content:space-between;margin-top:-4px;font-size:11px;color:var(--muted)}:where([data-fretfall]) .hint{margin:-2px 0 0;font-size:12px;line-height:1.4;color:var(--muted)}:where([data-fretfall]) .text-button{justify-self:start;padding:4px 0;border:0;background:none;font:500 12px var(--ui);color:var(--muted);text-decoration:underline;text-underline-offset:3px;cursor:pointer}:where([data-fretfall]) .text-button:hover{color:var(--text)}:where([data-fretfall]) :is(#settings,.volume) input[type=range]{-webkit-appearance:none;appearance:none;height:18px;margin:0;background:none;cursor:pointer}:where([data-fretfall]) #settings input[type=range]{width:100%}:where([data-fretfall]) :is(#settings,.volume) input[type=range]::-webkit-slider-runnable-track{height:4px;border-radius:2px;background:linear-gradient(90deg,var(--accent) var(--fill, 50%),var(--chip-border) var(--fill, 50%))}:where([data-fretfall]) :is(#settings,.volume) input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:16px;height:16px;margin-top:-6px;border-radius:50%;background:var(--text);border:3px solid var(--accent);box-shadow:0 1px 4px #0006}:where([data-fretfall]) :is(#settings,.volume) input[type=range]::-moz-range-track{height:4px;border-radius:2px;background:var(--chip-border)}:where([data-fretfall]) :is(#settings,.volume) input[type=range]::-moz-range-progress{height:4px;border-radius:2px;background:var(--accent)}:where([data-fretfall]) :is(#settings,.volume) input[type=range]::-moz-range-thumb{width:10px;height:10px;border-radius:50%;background:var(--text);border:3px solid var(--accent)}:where([data-fretfall]) .toggle{display:flex;align-items:center;justify-content:space-between;gap:16px;font-size:13px;font-weight:500;cursor:pointer}:where([data-fretfall]) .toggle small{display:block;margin-top:2px;font-size:12px;font-weight:400;line-height:1.4;color:var(--muted)}:where([data-fretfall]) .toggle input{-webkit-appearance:none;appearance:none;flex:none;position:relative;width:36px;height:20px;margin:0;border-radius:10px;background:var(--chip-border);cursor:pointer;transition:background .15s}:where([data-fretfall]) .toggle input:after{content:"";position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:var(--text);transition:transform .15s}:where([data-fretfall]) .toggle input:checked{background:var(--accent)}:where([data-fretfall]) .toggle input:checked:after{transform:translate(16px);background:var(--ink)}:where([data-fretfall]) .cards{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px}:where([data-fretfall]) .cards.three{grid-template-columns:repeat(3,minmax(0,1fr))}:where([data-fretfall]) .cards button{display:grid;gap:8px;justify-items:start;padding:10px;border-radius:9px;border:1px solid var(--chip-border);background:var(--chip);font:500 12px var(--ui);color:var(--muted);text-align:left;cursor:pointer}:where([data-fretfall]) .cards button:hover{color:var(--text)}:where([data-fretfall]) .cards button[aria-pressed=true]{border-color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent);color:var(--text)}:where([data-fretfall]) .dots{display:flex;gap:4px}:where([data-fretfall]) .dots i{width:10px;height:10px;border-radius:50%;box-shadow:inset 0 0 0 1px #ffffff2e}:where([data-fretfall]) .headstock{width:100%;height:52px}:where([data-fretfall]) .order{font:600 12px var(--num);letter-spacing:.12em;color:var(--text)}:where([data-fretfall]) #legend{width:min(430px,calc(100cqw - 32px))}:where([data-fretfall]) .legend-find{padding:0 14px 6px}:where([data-fretfall]) .legend-find input{width:100%;height:34px;padding:0 12px;border-radius:9px;border:1px solid var(--chip-border);background:var(--chip);color:var(--text);font:13px var(--ui)}:where([data-fretfall]) .legend-find input::placeholder{color:var(--muted)}:where([data-fretfall]) .legend{gap:4px;padding-top:10px}:where([data-fretfall]) .legend h3{margin:14px 0 4px}:where([data-fretfall]) .legend h3:first-child{margin-top:0}:where([data-fretfall]) .legend-item{display:grid;grid-template-columns:64px 1fr;gap:12px;align-items:center;padding:4px 0}:where([data-fretfall]) .legend-item b{display:block;font-size:13px;font-weight:500}:where([data-fretfall]) .legend-item span{display:block;font-size:12px;line-height:1.4;color:var(--muted)}:where([data-fretfall]) .legend-item .keys{display:flex;justify-content:center;gap:4px}:where([data-fretfall]) .legend-item kbd{min-width:22px;padding:2px 6px;border:1px solid var(--chip-border);border-bottom-width:2px;border-radius:5px;background:var(--chip);font:600 12px var(--num);text-align:center;white-space:nowrap}:where([data-fretfall]) .legend-item svg{width:64px;height:36px;border-radius:8px;background:var(--chip);border:1px solid var(--chip-border);fill:none;stroke-linecap:round;stroke-linejoin:round}:where([data-fretfall]) .legend-item text{font-family:var(--num);font-weight:700;text-anchor:middle;dominant-baseline:central}:where([data-fretfall]) .legend-empty{margin:8px 0;font-size:13px;color:var(--muted)}:where([data-fretfall]) .f0{fill:var(--s0)}:where([data-fretfall]) .f1{fill:var(--s1)}:where([data-fretfall]) .f2{fill:var(--s2)}:where([data-fretfall]) .f3{fill:var(--s3)}:where([data-fretfall]) .f4{fill:var(--s4)}:where([data-fretfall]) .l0{stroke:var(--s0)}:where([data-fretfall]) .l1{stroke:var(--s1)}:where([data-fretfall]) .l2{stroke:var(--s2)}:where([data-fretfall]) .l3{stroke:var(--s3)}:where([data-fretfall]) .l4{stroke:var(--s4)}:where([data-fretfall]) .lm{stroke:var(--muted)}:where([data-fretfall]) .li{stroke:var(--ink)}:where([data-fretfall]) .ft{fill:var(--text)}:where([data-fretfall]) .lt{stroke:var(--text)}:where([data-fretfall]) .fi{fill:var(--ink)}:where([data-fretfall]) .fa{fill:var(--accent)}:where([data-fretfall]) .fl{fill:var(--lane)}:where([data-fretfall]) .ll{stroke:var(--lane)}:where([data-fretfall]) .lw{stroke:#fff}:where([data-fretfall]) .fm{fill:var(--muted)}:where([data-fretfall]) .la{stroke:var(--accent)}:where([data-fretfall]) .fw{fill:#fff}:where([data-fretfall]) .volume{position:relative}:where([data-fretfall]) .volume .icon.muted{color:var(--muted)}:where([data-fretfall]) .volume-panel{position:absolute;top:calc(100% + 8px);left:50%;transform:translate(-50%);display:flex;align-items:center;gap:8px;padding:6px 14px 6px 6px;border-radius:10px;background:var(--ink);border:1px solid var(--chip-border);box-shadow:0 12px 30px #00000073}:where([data-fretfall]) .volume-panel input{width:150px}:where([data-fretfall]) .volume-panel output{min-width:36px;font:600 12px var(--num);color:var(--muted);text-align:right;font-variant-numeric:tabular-nums}:where([data-fretfall]) .volume-mute{flex:none;width:30px;height:30px;display:grid;place-items:center;padding:0;border:0;border-radius:7px;background:none;color:var(--text);cursor:pointer}:where([data-fretfall]) .volume-mute:hover{background:var(--chip)}:where([data-fretfall]) .volume-mute[aria-pressed=true]{color:var(--muted)}:where([data-fretfall]) #drop{position:fixed;inset:12px;z-index:5;display:grid;place-items:center;border:2px dashed var(--accent);border-radius:16px;background:#00000073;font:600 20px var(--num)}:where([data-fretfall]) #tab{position:fixed;left:-10000px;top:0;width:1000px}@media(max-width:900px){:where([data-fretfall]) .time{display:none}[data-fretfall]:where([data-fretfall-dock]) .time{display:block}:where([data-fretfall]) #menuButton{display:grid}:where([data-fretfall]) .volume{position:static}:where([data-fretfall]) .volume-panel{left:auto;right:0;transform:none}:where([data-fretfall]) .menu{display:none;position:absolute;top:calc(100% + 8px);right:0;width:230px;flex-direction:column;gap:2px;padding:6px;border-radius:12px;background:var(--ink);border:1px solid var(--chip-border);box-shadow:0 12px 30px #00000073}:where([data-fretfall]) #menuButton[aria-expanded=true]+.menu{display:flex}:where([data-fretfall]) .menu .icon{width:100%;height:38px;display:flex;justify-content:flex-start;gap:12px;padding:0 10px;border:0;border-radius:7px;background:none;font:500 13px var(--ui)}:where([data-fretfall]) .menu .icon:after{content:attr(data-label)}:where([data-fretfall]) .menu .icon:hover{background:var(--chip)}:where([data-fretfall]) .menu .icon[aria-pressed=true],:where([data-fretfall]) .menu .icon[aria-expanded=true]{background:none;color:var(--accent)}:where([data-fretfall]) .menu .tip{display:none}:where([data-fretfall]) .menu .divider{width:auto;height:1px;margin:4px}}@media(max-width:560px){:where([data-fretfall]) .bar{flex-wrap:wrap;height:auto;row-gap:8px}:where([data-fretfall]) .bar>.group:first-child,:where([data-fretfall]) .bar>.group:last-child{flex:1 1 100%;height:40px}:where([data-fretfall]) .speed{margin-left:auto}:where([data-fretfall]) #menuButton{position:absolute;top:2px;right:0}:where([data-fretfall]) .menu{top:48px}:where([data-fretfall]) #phrases{top:122px}:where([data-fretfall]) #info{flex-direction:column;align-items:flex-start;gap:2px;font-size:12px}:where([data-fretfall]) #info>span+span:before{display:none}:where([data-fretfall]) #info>span:first-child{max-width:100%;overflow:hidden;text-overflow:ellipsis}:where([data-fretfall]) #progress{top:182px}:where([data-fretfall]) #lyrics{top:212px}:where([data-fretfall]) .sheet{top:114px;max-height:calc(100cqh - 166px)}}@media(max-width:520px){:where([data-fretfall]) .sheet{right:16px}}[data-fretfall]:not([data-fretfall-dock]){contain:layout;container-type:size}'}),n=Object.assign(document.createElement("link"),{id:"fonts",rel:"stylesheet"});if(!e.isConnected)throw new Error("mount(element): the element has to be in the page");e.innerHTML=`<div data-fretfall><canvas id="highway"></canvas>
2
2
  <header class="bar">
3
3
  <div class="group" style="gap: 20px">
4
4
  <div class="wordmark">
@@ -28,6 +28,16 @@ id="arrangements"
28
28
  role="group"
29
29
  aria-label="Arrangement"
30
30
  ></div>
31
+ <div
32
+ class="seg views"
33
+ id="view"
34
+ role="group"
35
+ aria-label="View"
36
+ >
37
+ <button value="highway" aria-pressed="true">Highway</button>
38
+ <button value="tablature" aria-pressed="false">Tablature</button>
39
+ <button value="notation" aria-pressed="false">Notation</button>
40
+ </div>
31
41
  </div>
32
42
  <div class="group">
33
43
  <span class="time" id="time">0:00 / 0:00</span>
@@ -110,6 +120,14 @@ aria-label="Volume"
110
120
  </div>
111
121
  <button
112
122
  class="icon"
123
+ id="minimalBack"
124
+ aria-label="Back from the minimal view"
125
+ title="Back from the minimal view (H)"
126
+ >
127
+ <svg width="18" height="18" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M3 10s2.5-4.5 7-4.5 7 4.5 7 4.5-2.5 4.5-7 4.5-7-4.5-7-4.5z"/><circle cx="10" cy="10" r="2"/></svg>
128
+ </button>
129
+ <button
130
+ class="icon"
113
131
  id="menuButton"
114
132
  aria-expanded="false"
115
133
  aria-controls="menu"
@@ -196,6 +214,93 @@ can also drop them anywhere</span
196
214
  <div class="divider"></div>
197
215
  <button
198
216
  class="icon"
217
+ id="metronome"
218
+ data-label="Metronome"
219
+ aria-pressed="false"
220
+ aria-label="Metronome"
221
+ aria-describedby="metronomeTip"
222
+ >
223
+ <svg
224
+ width="18"
225
+ height="18"
226
+ viewBox="0 0 20 20"
227
+ fill="none"
228
+ stroke="currentColor"
229
+ stroke-width="1.6"
230
+ stroke-linecap="round"
231
+ stroke-linejoin="round"
232
+ >
233
+ <path d="M7.8 3h4.4l3.3 13.5h-11z" />
234
+ <path d="M5.6 12.5h8.8M10 12.5l4-7" />
235
+ </svg>
236
+ <span class="tip" role="tooltip" id="metronomeTip"
237
+ ><b>Metronome <kbd>K</kbd></b
238
+ >A click on every beat, higher on the first of each
239
+ bar</span
240
+ >
241
+ </button>
242
+ <button
243
+ class="icon"
244
+ id="band"
245
+ data-label="Play as a band"
246
+ aria-label="Play as a band"
247
+ aria-describedby="bandTip"
248
+ disabled
249
+ >
250
+ <svg
251
+ width="18"
252
+ height="18"
253
+ viewBox="0 0 20 20"
254
+ fill="none"
255
+ stroke="currentColor"
256
+ stroke-width="1.6"
257
+ stroke-linecap="round"
258
+ stroke-linejoin="round"
259
+ >
260
+ <circle cx="7.5" cy="7" r="2.5" />
261
+ <path d="M3 16.5a4.5 4.5 0 0 1 9 0" />
262
+ <circle cx="13.8" cy="7.5" r="2" />
263
+ <path d="M13.5 12.2a3.8 3.8 0 0 1 4 4.3" />
264
+ </svg>
265
+ <span class="tip" role="tooltip" id="bandTip"
266
+ ><b>Play as a band</b>Opens every part in a window of its
267
+ own, side by side, all in time. Play, pause, jump and loop
268
+ from any of them; the sound comes from this window, which
269
+ stays open behind them</span
270
+ >
271
+ </button>
272
+ <button
273
+ class="icon"
274
+ id="minimal"
275
+ data-label="Minimal view"
276
+ aria-pressed="false"
277
+ aria-label="Minimal view"
278
+ aria-describedby="minimalTip"
279
+ >
280
+ <svg
281
+ width="18"
282
+ height="18"
283
+ viewBox="0 0 20 20"
284
+ fill="none"
285
+ stroke="currentColor"
286
+ stroke-width="1.6"
287
+ stroke-linecap="round"
288
+ stroke-linejoin="round"
289
+ >
290
+ <path
291
+ d="M2.5 10s2.8-5 7.5-5 7.5 5 7.5 5-2.8 5-7.5 5-7.5-5-7.5-5z"
292
+ />
293
+ <circle cx="10" cy="10" r="2.2" />
294
+ </svg>
295
+ <span class="tip" role="tooltip" id="minimalTip"
296
+ ><b>Minimal view <kbd>H</kbd></b
297
+ >Just the highway and the lyrics: the header, phrase bars
298
+ and song info make way. Press again to bring them back</span
299
+ >
300
+ </button>
301
+ <div class="divider"></div>
302
+ <button
303
+ class="icon"
199
304
  id="legendButton"
200
305
  data-label="Notation and shortcuts"
201
306
  aria-expanded="false"
@@ -332,117 +437,6 @@ stroke-linecap="round"
332
437
  <div id="lines"></div>
333
438
  </div>
334
439
  <div id="info"></div>
335
- <div id="tools" class="group" role="group" aria-label="Practice tools">
336
- <button
337
- class="icon"
338
- id="metronome"
339
- aria-pressed="false"
340
- aria-label="Metronome"
341
- aria-describedby="metronomeTip"
342
- >
343
- <svg
344
- width="18"
345
- height="18"
346
- viewBox="0 0 20 20"
347
- fill="none"
348
- stroke="currentColor"
349
- stroke-width="1.6"
350
- stroke-linecap="round"
351
- stroke-linejoin="round"
352
- >
353
- <path d="M7.8 3h4.4l3.3 13.5h-11z" />
354
- <path d="M5.6 12.5h8.8M10 12.5l4-7" />
355
- </svg>
356
- <span class="tip" role="tooltip" id="metronomeTip"
357
- ><b>Metronome <kbd>K</kbd></b
358
- >A click on every beat, higher on the first of each
359
- bar</span
360
- >
361
- </button>
362
- <button
363
- class="icon"
364
- id="band"
365
- aria-label="Play as a band"
366
- aria-describedby="bandTip"
367
- disabled
368
- >
369
- <svg
370
- width="18"
371
- height="18"
372
- viewBox="0 0 20 20"
373
- fill="none"
374
- stroke="currentColor"
375
- stroke-width="1.6"
376
- stroke-linecap="round"
377
- stroke-linejoin="round"
378
- >
379
- <circle cx="7.5" cy="7" r="2.5" />
380
- <path d="M3 16.5a4.5 4.5 0 0 1 9 0" />
381
- <circle cx="13.8" cy="7.5" r="2" />
382
- <path d="M13.5 12.2a3.8 3.8 0 0 1 4 4.3" />
383
- </svg>
384
- <span class="tip" role="tooltip" id="bandTip"
385
- ><b>Play as a band</b>Opens every part in a window of its
386
- own, side by side, all in time. Play, pause, jump and loop
387
- from any of them; the sound comes from this window, which
388
- stays open behind them</span
389
- >
390
- </button>
391
- <button
392
- class="icon"
393
- id="tabView"
394
- aria-pressed="false"
395
- aria-label="2D tab view"
396
- aria-describedby="tabViewTip"
397
- >
398
- <svg
399
- width="18"
400
- height="18"
401
- viewBox="0 0 20 20"
402
- fill="none"
403
- stroke="currentColor"
404
- stroke-width="1.6"
405
- stroke-linecap="round"
406
- >
407
- <path d="M2.5 5h15M2.5 10h15M2.5 15h15" />
408
- <path d="M7 3v14" stroke-width="2.2" />
409
- </svg>
410
- <span class="tip" role="tooltip" id="tabViewTip"
411
- ><b>2D tab view <kbd>D</kbd></b
412
- >The notes as tab: fret numbers on flat strings, coming in
413
- from the right to the play line. Press again for the 3D
414
- highway</span
415
- >
416
- </button>
417
- <button
418
- class="icon"
419
- id="minimal"
420
- aria-pressed="false"
421
- aria-label="Minimal view"
422
- aria-describedby="minimalTip"
423
- >
424
- <svg
425
- width="18"
426
- height="18"
427
- viewBox="0 0 20 20"
428
- fill="none"
429
- stroke="currentColor"
430
- stroke-width="1.6"
431
- stroke-linecap="round"
432
- stroke-linejoin="round"
433
- >
434
- <path
435
- d="M2.5 10s2.8-5 7.5-5 7.5 5 7.5 5-2.8 5-7.5 5-7.5-5-7.5-5z"
436
- />
437
- <circle cx="10" cy="10" r="2.2" />
438
- </svg>
439
- <span class="tip" role="tooltip" id="minimalTip"
440
- ><b>Minimal view <kbd>H</kbd></b
441
- >Just the highway and the lyrics: the header, phrase bars
442
- and song info make way. Press again to bring them back</span
443
- >
444
- </button>
445
- </div>
446
440
  <div
447
441
  id="settings"
448
442
  class="sheet"
@@ -602,8 +596,8 @@ step="0.05"
602
596
  </div>
603
597
  </div>
604
598
  </div>
605
- <h3>Notes</h3>
606
- <div class="pair">
599
+ <h3 class="not-notation">Notes</h3>
600
+ <div class="pair not-notation">
607
601
  <div class="setting">
608
602
  <div class="setting-head">
609
603
  <label for="noteSpeed">Note speed</label
@@ -655,11 +649,11 @@ aria-labelledby="tab-highway"
655
649
  hidden
656
650
  >
657
651
  <div
658
- class="setting only-2d"
652
+ class="setting only-2d not-notation"
659
653
  title="Notes scrolling in to the play line, or a page of bars that holds still while the play line moves across it"
660
654
  >
661
655
  <div class="setting-head">
662
- <span id="tabLayoutLabel">Tab</span>
656
+ <span id="tabLayoutLabel">Tablature</span>
663
657
  </div>
664
658
  <div
665
659
  class="seg"
@@ -668,7 +662,63 @@ role="group"
668
662
  aria-labelledby="tabLayoutLabel"
669
663
  ></div>
670
664
  </div>
671
- <div class="setting">
665
+ <div
666
+ class="setting only-notation"
667
+ title="The page the notation is written on: the theme's dark card, or white paper"
668
+ >
669
+ <div class="setting-head">
670
+ <span id="paperLabel">Paper</span>
671
+ </div>
672
+ <div
673
+ class="seg"
674
+ data-setting="paper"
675
+ role="group"
676
+ aria-labelledby="paperLabel"
677
+ ></div>
678
+ </div>
679
+ <label class="toggle only-notation"
680
+ ><span
681
+ >Beat highlight<small
682
+ >The beat being played, filled across the system</small
683
+ ></span
684
+ ><input id="beatBox" type="checkbox" role="switch"
685
+ /></label>
686
+ <label class="toggle only-notation"
687
+ ><span
688
+ >Notes<small
689
+ >The notes on a staff over each system, as guitar music is written</small
690
+ ></span
691
+ ><input id="sheetNotes" type="checkbox" role="switch"
692
+ /></label>
693
+ <div
694
+ class="setting only-notation"
695
+ title="Whether the page turns with the beat, keeping the system being played second from the top, or only when you scroll it"
696
+ >
697
+ <div class="setting-head">
698
+ <span id="sheetScrollLabel">Scroll</span>
699
+ </div>
700
+ <div
701
+ class="seg"
702
+ data-setting="sheetScroll"
703
+ role="group"
704
+ aria-labelledby="sheetScrollLabel"
705
+ ></div>
706
+ </div>
707
+ <div
708
+ class="setting only-2d"
709
+ title="What is written around a note: the finger to press with, and the techniques, chord names and hand positions over and under the staff"
710
+ >
711
+ <div class="setting-head">
712
+ <span id="tabMarksLabel">Marks</span>
713
+ </div>
714
+ <div
715
+ class="seg"
716
+ data-setting="tabMarks"
717
+ role="group"
718
+ aria-labelledby="tabMarksLabel"
719
+ ></div>
720
+ </div>
721
+ <div class="setting not-notation">
672
722
  <div class="setting-head">
673
723
  <span id="lookLabel">Notes</span>
674
724
  </div>
@@ -747,8 +797,8 @@ step="0.05"
747
797
  </div>
748
798
  </div>
749
799
  <div
750
- class="setting"
751
- title="The mute crosses and finger numbers on the notes"
800
+ class="setting not-notation"
801
+ title="The finger numbers on a note, and the fret numbers in the tab. The mutes' crosses are white either way"
752
802
  >
753
803
  <div class="setting-head">
754
804
  <span id="markingsLabel">Markings on notes</span>
@@ -760,7 +810,7 @@ role="group"
760
810
  aria-labelledby="markingsLabel"
761
811
  ></div>
762
812
  </div>
763
- <label class="toggle"
813
+ <label class="toggle not-notation"
764
814
  ><span
765
815
  >Hand position guides<small
766
816
  >Where your hand goes: a band down the highway, or
package/music.js CHANGED
@@ -1 +1 @@
1
- const k=960;function E(e,o){const t=[{tick:0,tempo:o,ms:0}];for(const{tick:r,tempo:i}of e){const n=t.at(-1);t.push({tick:r,tempo:i,ms:n.ms+(r-n.tick)*6e4/(n.tempo*960)})}return t}function p(e,o,t){let r=0,i=e.length;for(;r<i;){const n=r+i>>1;e[n][o]<=t?r=n+1:i=n}return e[Math.max(0,r-1)]}function A(e,o){const t=p(e,"tick",o);return t.ms+(o-t.tick)*6e4/(t.tempo*960)}function S(e,o){const t=p(e,"ms",o);return t.tick+(o-t.ms)*t.tempo*960/6e4}const u=["C","D♭","D","E♭","E","F","G♭","G","A♭","A","B♭","B"],d={4:[28,33,38,43],5:[23,28,33,38,43],6:[40,45,50,55,59,64],7:[35,40,45,50,55,59,64]};function T(e){const o=d[e.length],t=o&&e.map((r,i)=>r-o[i]);return t?.every(r=>r===t[0])?`${u[e[0]%12]} Standard`:t&&t[0]===t[1]-2&&t.slice(1).every(r=>r===t[1])?`Drop ${u[e[0]%12]}`:e.map(r=>u[r%12]).join(" ")}const M=e=>u[e%12];function _(e){const o=d[e.length];if(!o)return null;const t=e.map((n,s)=>n-o[s]),i=t[0]===t[1]-2&&t.slice(1).every(n=>n===t[1])&&t[1]!==0?o.map((n,s)=>s?n:n-2):o;return{reference:T(i),open:i,shift:e.map((n,s)=>n-i[s])}}const x=new Set(["time","sustain","chord","repeat","heldFrom","tieTo","dynamicLabel","pick"]),R=e=>JSON.stringify(Object.entries(e).filter(([o,t])=>!x.has(o)&&t!==null&&t!==void 0&&t!==!1&&t!==0).sort());function D(e,o,t=1){let r=null;for(let i=0,n;i<e.length;i=n){for(n=i+1;n<e.length&&e[n].time-e[i].time<.005;n++);const s=e.slice(i,n),c=s.map(R).sort().join("|"),f=s[0].chord??null,l=!!r&&s[0].time-r.time<=t,h=f!==null&&l&&r.shape===c;for(const m of s){m.repeat=h;const g=l&&!m.mute&&r.group.some(a=>a.string===m.string&&a.fret===m.fret&&!a.mute&&!a.bend&&!a.bendCurve&&(a.slideTo??a.slideUnpitchTo??null)===null);m.heldFrom=g?r.time:null}h&&(o[f].highDensity=!0),r={shape:c,time:s[0].time,group:s}}}function I(e,o){const t={};let r="f";e.forEach((i,n)=>{const s=t[i.string];if(s!==void 0){const c=e[s];c.letRing&&(c.sustain=Math.max(c.sustain,Math.min(i.time-c.time,4))),c.linkNext&&(c.tieTo=n)}i.dynamicLabel=i.dynamic&&i.dynamic!==r?i.dynamic:null,i.dynamic&&(r=i.dynamic),t[i.string]=n});for(const i of Object.values(t).map(n=>e[n]))i.letRing&&(i.sustain=Math.max(i.sustain,2));for(const i of o)i.barre??=y(i)}function b(e,o){for(const t of o){if(t.endTime-t.startTime<.6)continue;const r=e.findIndex(n=>n.time>=t.startTime-.005),i=[];for(let n=r;n>=0&&n<e.length&&e[n].time<t.endTime-.005;n++)e[n].chord===null&&t.frets[e[n].string]===e[n].fret&&i.push(n);if(!(new Set(i.map(n=>e[n].string)).size<2)){t.arpeggio=!0;for(const n of i){const s=e[n],c=e.find((f,l)=>l>n&&f.string===s.string&&f.time>s.time+.005);s.sustain=Math.max(s.sustain,Math.min(t.endTime,c?.time??1/0)-s.time),s.letRing=!0}}}}function y({frets:e=[],fingers:o=[]}){const t={};e.forEach((n,s)=>{n>0&&o[s]>=1&&(t[`${o[s]}:${n}`]??=[]).push(s)});const[r,i]=Object.entries(t).sort((n,s)=>s[1].length-n[1].length)[0]??[];return i?.length>=3?{fret:+r.split(":")[1],from:Math.min(...i),to:Math.max(...i)}:null}function j(e,o){const t=i=>i<e.length&&e[i].time-o<8;let r=e.findIndex(i=>o<i.end+.2);return r<0&&(r=e.length),t(r)||(r=r>0&&o<e[r-1].end+1.5?r-1:-1),{top:r,bottom:r>=0&&t(r+1)?r+1:-1}}export{I as annotate,j as lyricsShown,b as markArpeggios,D as markRepeats,S as msToTick,M as noteName,E as tempoMap,A as tickToMs,T as tuningName,_ as tuningReference};
1
+ const S=960;function b(i,o){const e=[{tick:0,tempo:o,ms:0}];for(const{tick:n,tempo:r}of i){const t=e.at(-1);e.push({tick:n,tempo:r,ms:t.ms+(n-t.tick)*6e4/(t.tempo*960)})}return e}function p(i,o,e){let n=0,r=i.length;for(;n<r;){const t=n+r>>1;i[t][o]<=e?n=t+1:r=t}return i[Math.max(0,n-1)]}function k(i,o){const e=p(i,"tick",o);return e.ms+(o-e.tick)*6e4/(e.tempo*960)}function E(i,o){const e=p(i,"ms",o);return e.tick+(o-e.ms)*e.tempo*960/6e4}const h=["C","D♭","D","E♭","E","F","G♭","G","A♭","A","B♭","B"],g={4:[28,33,38,43],5:[23,28,33,38,43],6:[40,45,50,55,59,64],7:[35,40,45,50,55,59,64]};function T(i){const o=g[i.length],e=o&&i.map((n,r)=>n-o[r]);return e?.every(n=>n===e[0])?`${h[i[0]%12]} Standard`:e&&e[0]===e[1]-2&&e.slice(1).every(n=>n===e[1])?`Drop ${h[i[0]%12]}`:i.map(n=>h[n%12]).join(" ")}const M=i=>h[i%12];function A(i){const o=g[i.length];if(!o)return null;const e=i.map((t,s)=>t-o[s]),r=e[0]===e[1]-2&&e.slice(1).every(t=>t===e[1])&&e[1]!==0?o.map((t,s)=>s?t:t-2):o;return{reference:T(r),open:r,shift:i.map((t,s)=>t-r[s])}}const R=new Set(["time","sustain","chord","repeat","heldFrom","tieTo","dynamicLabel","pick"]),y=i=>JSON.stringify(Object.entries(i).filter(([o,e])=>!R.has(o)&&e!==null&&e!==void 0&&e!==!1&&e!==0).sort());function C(i,o,e=1){let n=null;for(let r=0,t;r<i.length;r=t){for(t=r+1;t<i.length&&i[t].time-i[r].time<.005;t++);const s=i.slice(r,t),a=s.map(y).sort().join("|"),c=s[0].chord??null,u=!!n&&s[0].time-n.time<=e,d=c!==null&&u&&n.shape===a;for(const f of s){f.repeat=d;const m=u&&!f.mute&&n.group.some(l=>l.string===f.string&&l.fret===f.fret&&!l.mute&&!l.bend&&!l.bendCurve&&(l.slideTo??l.slideUnpitchTo??null)===null);f.heldFrom=m?n.time:null}d&&(o[c].highDensity=!0),n={shape:a,time:s[0].time,group:s}}}function U(i,o){const e={},n={};let r="f";i.forEach((t,s)=>{const a=e[t.string];if(a!==void 0){const c=i[a];c.letRing&&(c.sustain=Math.max(c.sustain,Math.min(t.time-c.time,4)));const u=m=>m.bendCurve?.at(-1)?.[1]??m.bend??0,d=m=>m.bendCurve?.[0]?.[1]??0;d(t)>0&&c.fret===t.fret&&u(c)===d(t)&&(c.sustain=Math.max(c.sustain,t.time-c.time)),c.linkNext&&(c.tieTo=s,t.tied=!0,(c.slideTo??c.slideUnpitchTo??null)!==null&&(c.slideSpan=t.time-c.time),c.sustain=Math.max(c.sustain,t.time-c.time));const f=m=>(m.slideTo??m.slideUnpitchTo??null)===null&&!m.bend&&!m.bendCurve&&!m.hammerOn&&!m.pullOff;!t.tied&&c.tied&&t.chord===null&&f(t)&&t.fret===n[t.string]&&t.time-(c.time+c.sustain)<.1&&(c.slideTo=t.fret,c.slideSpan=t.time-c.time,t.tied=!0)}t.dynamicLabel=t.dynamic&&t.dynamic!==r?t.dynamic:null,t.dynamic&&(r=t.dynamic),t.tied||(n[t.string]=t.fret),e[t.string]=s});for(const t of Object.values(e).map(s=>i[s]))t.letRing&&(t.sustain=Math.max(t.sustain,2));for(const t of o)t.barre??=x(t)}function _(i,o){for(const e of o){if(e.endTime-e.startTime<.6)continue;const n=i.findIndex(t=>t.time>=e.startTime-.005),r=[];for(let t=n;t>=0&&t<i.length&&i[t].time<e.endTime-.005;t++)i[t].chord===null&&e.frets[i[t].string]===i[t].fret&&r.push(t);if(!(new Set(r.map(t=>i[t].string)).size<2)){e.arpeggio=!0;for(const t of r){const s=i[t],a=i.find((c,u)=>u>t&&c.string===s.string&&c.time>s.time+.005);s.sustain=Math.max(s.sustain,Math.min(e.endTime,a?.time??1/0)-s.time),s.letRing=!0}}}}function x({frets:i=[],fingers:o=[]}){const e={};i.forEach((t,s)=>{t>0&&o[s]>=1&&(e[`${o[s]}:${t}`]??=[]).push(s)});const[n,r]=Object.entries(e).sort((t,s)=>s[1].length-t[1].length)[0]??[];return r?.length>=3?{fret:+n.split(":")[1],from:Math.min(...r),to:Math.max(...r)}:null}function D(i,o){const e=r=>r<i.length&&i[r].time-o<8;let n=i.findIndex(r=>o<r.end+.2);return n<0&&(n=i.length),e(n)||(n=n>0&&o<i[n-1].end+1.5?n-1:-1),{top:n,bottom:n>=0&&e(n+1)?n+1:-1}}export{U as annotate,D as lyricsShown,_ as markArpeggios,C as markRepeats,E as msToTick,M as noteName,b as tempoMap,k as tickToMs,T as tuningName,A as tuningReference};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fretfall/player",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "An arcade-style 3D note highway for Guitar Pro and MusicXML tabs, in the browser. Plays along with the synth or the band's recording.",
5
5
  "keywords": [
6
6
  "guitar",