95 lines
8.1 KiB
JavaScript
95 lines
8.1 KiB
JavaScript
/* Draw legacy control glyphs in Glass and Bento; restore them on theme changes. */
|
||
(function(root) {
|
||
'use strict';
|
||
const paths = {
|
||
trash:'<path d="M3 6h18M9 6V3h6v3M5 6l1 15h12l1-15M10 10v7M14 10v7"/>',
|
||
music:'<path d="M9 18V5l11-2v13M9 8l11-2"/><ellipse cx="6" cy="18" rx="3" ry="2"/><ellipse cx="17" cy="16" rx="3" ry="2"/>',
|
||
add:'<path d="M12 5v14M5 12h14"/>', close:'<path d="m6 6 12 12M18 6 6 18"/>',
|
||
search:'<circle cx="10.5" cy="10.5" r="6.5"/><path d="m16 16 5 5"/>',
|
||
save:'<path d="M12 3v12m-5-5 5 5 5-5M4 17v4h16v-4"/>',
|
||
play:'<path d="m8 5 11 7-11 7z"/>', pause:'<path d="M8 5v14M16 5v14"/>',
|
||
next:'<path d="m5 5 10 7-10 7zM19 5v14"/>', previous:'<path d="m19 5-10 7 10 7zM5 5v14"/>',
|
||
repeat:'<path d="m17 2 4 4-4 4M3 11V8a2 2 0 0 1 2-2h16M7 22l-4-4 4-4m14-1v3a2 2 0 0 1-2 2H3"/>',
|
||
edit:'<path d="m14 5 5 5M4 20l5-1L21 7l-4-4L5 15z"/>',
|
||
lyrics:'<rect x="9" y="3" width="6" height="11" rx="3"/><path d="M5 10v2a7 7 0 0 0 14 0v-2M12 19v3M8 22h8"/>',
|
||
share:'<path d="M12 16V3m-4 4 4-4 4 4M5 12v9h14v-9"/>',
|
||
copy:'<rect x="8" y="8" width="12" height="13" rx="2"/><path d="M16 8V3H3v13h5"/>',
|
||
check:'<path d="m5 12 4 4L19 6"/>', folder:'<path d="M3 7V5h7l2 3h9v12H3z"/>',
|
||
settings:'<path d="M4 6h16M4 12h16M4 18h16"/><circle cx="8" cy="6" r="2"/><circle cx="16" cy="12" r="2"/><circle cx="10" cy="18" r="2"/>',
|
||
chapters:'<rect x="4" y="3" width="16" height="18" rx="2"/><path d="M8 7h8M8 12h8M8 17h5"/>',
|
||
clock:'<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/>',
|
||
shuffle:'<path d="m18 3 3 3-3 3M3 6h3c5 0 7 12 12 12h3m-3-3 3 3-3 3M3 18h3c2 0 4-3 6-6s4-6 6-6h3"/>',
|
||
down:'<path d="m6 9 6 6 6-6"/>', right:'<path d="m9 6 6 6-6 6"/>', back:'<path d="M20 12H4m6-6-6 6 6 6"/>', more:'<circle cx="5" cy="12" r="1"/><circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/>',
|
||
devices:'<rect x="3" y="4" width="14" height="12" rx="2"/><path d="M7 20h6M10 16v4"/><rect x="17" y="10" width="5" height="11" rx="1"/>'
|
||
};
|
||
const glyphs = {'+':'add','✕':'close','×':'close','✖':'close','🔍':'search','🔎':'search','⬇':'save','⤓':'save','💾':'save','▶':'play','⏸':'pause','⏭':'next','⏮':'previous','↻':'repeat','↺':'repeat','✎':'edit','✏':'edit','📝':'edit','🎤':'lyrics','↗':'share','⧉':'copy','✓':'check','📁':'folder','⚙':'settings','📋':'chapters','🕘':'clock','🔀':'shuffle','📡':'devices','▾':'down','▸':'right','←':'back','⋯':'more','⋮':'more','🗑':'trash','🎵':'music'};
|
||
function leadingGlyph(text) {
|
||
const prefix = text.match(/^\s*/)[0], rest = text.slice(prefix.length);
|
||
const glyph = Object.keys(glyphs).find(key => rest.startsWith(key));
|
||
if (!glyph) return null;
|
||
const length = glyph.length + (rest[glyph.length] === '\uFE0F' ? 1 : 0);
|
||
return { kind:glyphs[glyph], original:prefix+rest.slice(0,length), rest:rest.slice(length) };
|
||
}
|
||
function mount(doc) {
|
||
const decorations = new Set(), tabLabels = new Map(); let scheduled=false;
|
||
const selector = '#listPane button, .sidebar button, .smart-item .pl-name, .notes-tab, .notes-fold, .mini-btn, .sidebar .playlist-item:not(.smart-item), .pl-offline-badge.all, .home-pl-offline.all, .home-pl-play, .home-pl-thumb-fallback';
|
||
const svg = kind => { const icon=doc.createElementNS('http://www.w3.org/2000/svg','svg'); icon.setAttribute('viewBox','0 0 24 24');icon.setAttribute('aria-hidden','true');icon.classList.add('glass-control-icon');icon.innerHTML=paths[kind];return icon; };
|
||
const observer=new root.MutationObserver(schedule), themeObserver=new root.MutationObserver(schedule);
|
||
function restore(record) {
|
||
record.svg.remove(); if(record.original.isConnected)record.original.replaceWith(doc.createTextNode(record.original.textContent));
|
||
if(record.addedLabel && record.parent.isConnected)record.parent.removeAttribute('aria-label');
|
||
}
|
||
function restoreTab(parent, record) {
|
||
for(const label of record.labels)if(label.isConnected)label.replaceWith(doc.createTextNode(label.textContent));
|
||
for(const [name,value] of Object.entries(record.attributes)) { if(value===null)parent.removeAttribute(name);else parent.setAttribute(name,value); }
|
||
}
|
||
function paint() {
|
||
scheduled=false;observer.disconnect();
|
||
const bento=doc.documentElement.dataset.layout==='bento-hub';
|
||
const active=bento || doc.documentElement.dataset.layout==='glass-stage';
|
||
for(const [parent,record] of tabLabels)if(!active || !parent.isConnected){restoreTab(parent,record);tabLabels.delete(parent);}
|
||
for(const record of decorations)if(!active || !record.svg.isConnected){restore(record);decorations.delete(record);}
|
||
if(active) for(const parent of doc.querySelectorAll(selector+(bento?', .service-btn, .np-btn, .ctrl, .empty-icon, .dl-saved-cta, .brand-mark, .upnext-head button':''))) {
|
||
// Existing SVGs already express the action; only legacy glyphs change.
|
||
if(parent.querySelector('svg:not(.glass-control-icon),.legacy-nav-icon'))continue;
|
||
for(const node of [...parent.childNodes]) {
|
||
if(node.nodeType!==3)continue;const match=leadingGlyph(node.textContent);if(!match)continue;
|
||
const icon=svg(match.kind);
|
||
if(parent.matches('.pl-offline-badge,.home-pl-offline')) { icon.removeAttribute('aria-hidden'); icon.setAttribute('role','img'); icon.setAttribute('aria-label',parent.title || 'Available offline'); }
|
||
const original=doc.createElement('span'); original.className='glass-original-glyph';original.textContent=match.original;
|
||
node.textContent=match.rest;parent.insertBefore(icon,node);parent.insertBefore(original,node);
|
||
const addedLabel=parent.tagName==='BUTTON'&&!parent.hasAttribute('aria-label')&&!parent.textContent.replace(match.original,'').trim();
|
||
if(addedLabel)parent.setAttribute('aria-label',parent.title || ({play:'Play',pause:'Pause',close:'Close',add:'Add',save:'Save'}[match.kind] || match.kind));
|
||
decorations.add({parent,svg:icon,original,addedLabel});break;
|
||
}
|
||
if(parent.matches('.sidebar .playlist-item:not(.smart-item)')&&!parent.querySelector('svg')) {
|
||
const icon=svg('folder'); parent.prepend(icon);
|
||
decorations.add({parent,svg:icon,original:doc.createElement('span'),addedLabel:false});
|
||
}
|
||
if(parent.matches('.notes-tab')&&!parent.querySelector('svg')&&paths[parent.dataset.tab==='markers'?'chapters':parent.dataset.tab]) {
|
||
const kind=parent.dataset.tab==='markers'?'chapters':parent.dataset.tab,icon=svg(kind);parent.prepend(icon);
|
||
const original=doc.createElement('span');decorations.add({parent,svg:icon,original,addedLabel:false});
|
||
}
|
||
}
|
||
if(active)for(const parent of doc.querySelectorAll('.notes-tab')) {
|
||
if(tabLabels.has(parent))continue;
|
||
const record={labels:[],attributes:{'aria-label':parent.getAttribute('aria-label'),title:parent.getAttribute('title')}};
|
||
const name=parent.dataset.tab==='markers'?'Chapters & bookmarks':parent.dataset.tab==='lyrics'?'Lyrics':'Search';
|
||
if(!parent.hasAttribute('aria-label'))parent.setAttribute('aria-label',name);
|
||
if(!parent.hasAttribute('title'))parent.setAttribute('title',name);
|
||
for(const node of [...parent.childNodes])if(node.nodeType===3 && node.textContent.trim()) {
|
||
const label=doc.createElement('span');label.className='glass-tab-label';node.replaceWith(label);label.append(node);record.labels.push(label);
|
||
}
|
||
tabLabels.set(parent,record);
|
||
}
|
||
if(active)observer.observe(doc.body,{childList:true,subtree:true,characterData:true});
|
||
}
|
||
function schedule(){if(!scheduled){scheduled=true;root.queueMicrotask(paint);}}
|
||
themeObserver.observe(doc.documentElement,{attributes:true,attributeFilter:['data-layout']});paint();
|
||
return ()=>{observer.disconnect();themeObserver.disconnect();for(const record of decorations)restore(record);for(const [parent,record] of tabLabels)restoreTab(parent,record);};
|
||
}
|
||
if(typeof module!=='undefined')module.exports={leadingGlyph,paths};
|
||
else if(root.document.readyState==='loading')root.document.addEventListener('DOMContentLoaded',()=>mount(root.document),{once:true});
|
||
else mount(root.document);
|
||
})(typeof window!=='undefined'?window:globalThis);
|