matrix-by-lmx-svg 1.7.0 → 1.7.2
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/index.svg +26 -3
- package/package.json +1 -1
package/index.svg
CHANGED
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
<section id="proxy-page" class="view"><div class="head"><b class="tag">MATRIX / BROWSER</b><h1>Explore privately.</h1><p>Search or open a URL through the selected Matrix Jet engine.</p></div><form id="proxy-search" class="suite-search"><label class="sr-only" for="proxy-q">Search or enter a URL</label><input id="proxy-q" placeholder="Search or enter URL…" autocomplete="off"/><button>Launch</button></form><div class="filter-row"><button class="active" data-web-filter="popular">Popular</button><button data-web-filter="social">Social</button><button data-web-filter="streaming">Streaming</button><button data-web-filter="search">Search</button><button data-web-filter="tools">Tools</button></div><div id="web-shortcuts" class="shortcut-grid"></div><div class="suite-panels"><article class="suite-panel"><span class="eyebrow">ENGINE</span><h3>Matrix Jet</h3><p>Scramjet transport using your configured Wisp relay.</p><button data-engine="scramjet" class="engine-choice active">Scramjet</button><button data-engine="direct" class="engine-choice">Direct embed</button></article><article class="suite-panel"><span class="eyebrow">CONNECTION</span><h3 id="browser-health">Checking runtime…</h3><p id="browser-health-detail">Your content stays inside the Matrix player.</p><button id="test-relay">Test connection</button></article></div></section>
|
|
10
10
|
<section id="games" class="view"><div class="head"><b class="tag">MATRIX / CLOUD ARCADE</b><h1>Pick a game.</h1><p>Community games with Matrix-proxied arcade fallbacks.</p></div><div class="tools"><label class="sr-only" for="game-search">Search games</label><input id="game-search" autocomplete="off" placeholder="Search games"/><span id="count" class="count" aria-live="polite">Loading…</span></div><div id="game-categories" class="chips" aria-label="Game categories"><button class="active" data-category="all">All</button><button data-category="arcade">Arcade</button><button data-category="action">Action</button><button data-category="sports">Sports</button><button data-category="puzzle">Puzzle</button><button data-category="retro">Retro</button></div><div id="game-grid" class="grid"><div class="loading-state"><i></i><strong>Loading the arcade…</strong><span>Connecting to the Matrix catalog</span></div></div></section>
|
|
11
11
|
<section id="media" class="view"><div class="head"><b class="tag">MATRIX / MEDIA</b><h1>Find something to watch.</h1><p>Browse public metadata and launch an authorized provider inside Matrix.</p></div><div class="media-toolbar"><div class="segmented"><button class="active" data-media-mode="anime">Anime</button><button data-media-mode="movies">Movies</button><button data-media-mode="shows">Shows</button></div><input id="media-search" placeholder="Search titles…" autocomplete="off"/></div><div id="media-genres" class="chips"></div><div id="media-hero" class="media-hero"></div><section class="shelf"><div class="section-title"><h3>Trending now</h3><span>Public catalog</span></div><div id="media-trending" class="poster-row"></div></section><section class="shelf"><div class="section-title"><h3>Top rated</h3><span>Community scores</span></div><div id="media-rated" class="poster-row"></div></section><section class="shelf"><div class="section-title"><h3>My watchlist</h3><span>Saved on this device</span></div><div id="watchlist-grid" class="poster-row"></div></section></section>
|
|
12
|
-
<section id="emulator" class="view"><div class="head"><b class="tag">MATRIX / EMULATOR</b><h1>
|
|
12
|
+
<section id="emulator" class="view"><div class="head"><b class="tag">MATRIX / EMULATOR</b><h1>Your games.</h1><p>Import legally obtained game files from your device. Matrix does not include preset ROMs or upload your files.</p></div><div id="built-in-roms" hidden="hidden"></div><div class="section-title emulator-local-title"><h3>Your local library</h3><span>Your files never leave this device</span></div><label id="rom-drop" class="rom-drop" for="rom-input"><input id="rom-input" type="file" accept=".zip,.gba,.gb,.gbc,.nes,.sfc,.smc,.n64,.z64,.nds,.gen,.md,.iso,.bin"/><b>Drop a game file here or click to choose</b><span>ZIP · GBA · GBC · NES · SNES · N64 · Genesis · NDS · PS1</span></label><div class="suite-panels"><article class="suite-panel"><span class="eyebrow">LOCAL STORAGE</span><h3 id="rom-count">No games saved</h3><p>Imported games are stored only in this browser so they can be played again on this device.</p><button id="clear-roms">Clear local library</button></article><article class="suite-panel"><span class="eyebrow">PS1 BIOS</span><h3>Optional system file</h3><p>Some PS1 games require a BIOS that you provide.</p><label class="file-button" for="bios-input">Load BIOS</label><input id="bios-input" class="hidden-file" type="file" accept=".bin"/></article></div><div id="rom-library" class="library-grid"></div></section>
|
|
13
13
|
<section id="apps" class="view"><div class="head"><b class="tag">MATRIX / APPS</b><h1>Web apps.</h1><p>Launch your favorites without leaving Matrix.</p></div><div id="apps-grid" class="grid apps"><div class="loading-state"><i></i><strong>Preparing apps…</strong></div></div></section>
|
|
14
14
|
<section id="connect" class="view"><div class="head compact-head"><div><b class="tag">MATRIX / CONNECT · BETA</b><h1>Your circle.</h1><p>Keep a local list of friends and copy invite codes. Shared presence activates with an account service.</p></div><button id="add-contact" class="primary-action">Add contact</button></div><div class="suite-panels"><article class="suite-panel"><span class="eyebrow">YOUR INVITE</span><h3 id="invite-code">MATRIX-GUEST</h3><p>Share this local profile code with a friend.</p><button id="copy-invite">Copy invite</button></article><article class="suite-panel"><span class="eyebrow">PRESENCE</span><h3>Local mode</h3><p>No cloud account service is configured, so presence and friend requests stay private on this device.</p></article></div><div class="section-title connect-title"><h3>Contacts</h3><span id="contact-count">0 saved</span></div><div id="contact-grid" class="contact-grid"></div></section>
|
|
15
|
-
<section id="chat" class="view"><div class="head"><b class="tag">MATRIX / CHAT</b><h1>
|
|
15
|
+
<section id="chat" class="view"><div class="head"><b class="tag">MATRIX / CHAT</b><h1>The lounge.</h1><p>Chat with everyone using Matrix and read the connected Discord lounge in one place.</p></div><div class="chat-shell"><aside class="channel-list"><b>Matrix room</b><button class="active" data-chat-room="lounge"># lounge</button></aside><div class="chat-main"><div class="feed-head"><div><b id="matrix-room-name"># lounge</b><span id="matrix-chat-state">Connecting…</span></div><button id="refresh-chat" aria-label="Refresh Matrix chat">↻</button></div><div id="chat-messages" class="chat-messages"><div class="system-message">Connecting to Matrix Chat…</div></div><form id="chat-form" class="chat-compose"><input id="chat-input" maxlength="500" placeholder="Message #lounge" autocomplete="off"/><button>Send</button></form></div><aside class="discord-pane"><div class="feed-head"><div><b>Discord lounge</b><span id="discord-state">Connecting…</span></div><a id="discord-invite" href="https://discord.gg/wXd6xcqpU" target="_blank" rel="noopener noreferrer">Join</a></div><div id="discord-messages" class="chat-messages"><div class="system-message">Loading Discord messages…</div></div><div class="mirror-note">Discord messages refresh automatically. Matrix website messages remain separate and are never posted to Discord.</div></aside></div></section>
|
|
16
16
|
<section id="notes" class="view"><div class="head compact-head"><div><b class="tag">MATRIX / NOTES</b><h1>Notes.</h1></div><button id="new-note" class="primary-action">New note</button></div><div class="notes-shell"><aside id="notes-list" class="notes-list"></aside><article class="note-editor"><input id="note-title" placeholder="Untitled note"/><textarea id="note-body" placeholder="Start typing…"></textarea><div class="note-actions"><span id="note-save-state">Saved locally</span><button id="delete-note">Delete note</button></div></article></div></section>
|
|
17
17
|
<section id="tools" class="view"><div class="head"><b class="tag">MATRIX / UTILITIES</b><h1>Useful tools.</h1></div><div class="grid"><div class="panel"><h3>Clock</h3><p id="clock">--:--:--</p></div><div class="panel"><h3>Editable workspace</h3><button id="edit">Enable editing</button></div><div class="panel"><h3>Reset</h3><button id="reset">Return home</button></div></div></section>
|
|
18
18
|
<section id="settings" class="view"><div class="head"><b class="tag">MATRIX / SETTINGS</b><h1>Preferences.</h1><p>Appearance, privacy, notifications, and advanced runtime controls.</p></div><div class="settings-tabs"><button class="active" data-settings-tab="appearance-settings">Appearance</button><button data-settings-tab="privacy-settings">Privacy</button><button data-settings-tab="notification-settings">Notifications</button><button data-settings-tab="advanced-settings">Advanced</button></div><div id="appearance-settings" class="settings-page on"><div class="panel appearance-panel"><h3>Matrix themes</h3><p>Themes and accent changes save on this device.</p><div id="theme-gallery" class="theme-gallery"><button data-theme-preset="midnight" class="active"><b>Midnight</b><span>Deep blue</span></button><button data-theme-preset="deep"><b>Carbon</b><span>Dark and minimal</span></button><button data-theme-preset="violet"><b>Violet</b><span>Neon dusk</span></button><button data-theme-preset="forest"><b>Forest</b><span>Dark green</span></button><button data-theme-preset="crimson"><b>Crimson</b><span>Deep red</span></button><button data-theme-preset="light"><b>Light</b><span>Bright and open</span></button></div><div class="appearance-controls"><label for="accent-color">Accent color</label><input id="accent-color" type="color" value="#70b7ff"/><label for="theme-mode">Theme</label><select id="theme-mode"><option value="midnight">Midnight</option><option value="deep">Carbon</option><option value="violet">Violet</option><option value="forest">Forest</option><option value="crimson">Crimson</option><option value="light">Light</option></select></div></div><div class="panel"><h3>Motion and density</h3><label class="switch-row"><span><b>Reduce motion</b><small>Minimize transitions and animation.</small></span><input id="reduce-motion" type="checkbox"/></label><label class="switch-row"><span><b>Compact cards</b><small>Fit more games and apps on screen.</small></span><input id="compact-mode" type="checkbox"/></label></div></div><div id="privacy-settings" class="settings-page"><div class="panel"><h3>Tab disguise</h3><p>Choose a harmless title preset or enter your own below.</p><div class="preset-row"><button data-cloak-preset="Google Docs">Google Docs</button><button data-cloak-preset="Google Drive">Drive</button><button data-cloak-preset="Google Classroom">Classroom</button><button data-cloak-preset="Canvas">Canvas</button></div></div><div class="panel security-panel"><h3>Security and tab controls</h3><div class="security-grid"><div><label for="cloak-title">Tab name</label><input id="cloak-title" aria-label="Custom tab name" placeholder="Matrix, By LMX"/><label for="cloak-icon">Tab icon URL</label><input id="cloak-icon" aria-label="Custom tab icon URL" placeholder="https://example.com/icon.png"/><div class="security-actions"><button id="apply-cloak">Apply tab changes</button><button id="reset-cloak">Reset</button></div><p id="cloak-state" class="msg"></p></div><div><label for="panic-url">Panic destination</label><input id="panic-url" aria-label="Panic destination URL" value="https://www.google.com/"/><label>Panic key</label><button id="panic-key" class="key-button">Set panic key</button><p id="panic-state" class="msg">No panic key set.</p></div></div></div><div class="panel"><h3>Local privacy</h3><label class="switch-row"><span><b>Hide activity</b><small>Do not show recent activity on Home.</small></span><input id="hide-activity" type="checkbox"/></label><label class="switch-row"><span><b>Clear player on exit</b><small>Unload the embedded page immediately.</small></span><input id="clear-player" type="checkbox" checked="checked"/></label></div></div><div id="notification-settings" class="settings-page"><div class="panel"><h3>Notifications</h3><label class="switch-row"><span><b>Launch notifications</b><small>Show a brief status when content opens.</small></span><input id="launch-notifications" type="checkbox" checked="checked"/></label><label class="switch-row"><span><b>Chat sounds</b><small>Play a subtle tone for guest messages.</small></span><input id="chat-sounds" type="checkbox"/></label><label class="switch-row"><span><b>Offline warnings</b><small>Tell you when Matrix loses its connection.</small></span><input id="offline-warnings" type="checkbox" checked="checked"/></label></div></div><div id="advanced-settings" class="settings-page"><div class="panel"><h3>Matrix Jet relay</h3><p>Connect Matrix to your self-hosted Wisp relay for reliable, first-party traffic.</p><input id="relay-url" aria-label="Matrix Jet relay URL" placeholder="wss://your-matrix-host.example/wisp/"/><button id="save-relay">Save relay</button><p id="relay-state" class="msg"></p></div><div class="panel"><h3>Player containment</h3><p>Search, Apps, Games, and Media open in the same full-screen Matrix player.</p></div><div class="panel"><h3>Local data</h3><p>Favorites, notes, watchlist, themes, and ROM history are stored only on this device. Shared Matrix Chat is handled by the Matrix server.</p><button id="export-data">Export data</button><button id="clear-data">Clear Matrix data</button></div></div></section></main></div>
|
|
@@ -40,7 +40,7 @@ var fx=document.createElement("style");fx.textContent=".shell,.main{min-height:0
|
|
|
40
40
|
(function(){var STORE="matrix.suite.v1",S={profile:{name:"Guest"},engine:"scramjet",watchlist:[],notes:[],chat:[],roms:[],contacts:[],stats:{seconds:0,launches:0,messages:0},prefs:{reduceMotion:false,compact:false,hideActivity:false,clearPlayer:true,launchNotifications:true,chatSounds:false,offlineWarnings:true}};try{S=merge(S,JSON.parse(localStorage.getItem(STORE)||"{}"))}catch(e){}function merge(a,b){Object.keys(b||{}).forEach(function(k){if(b[k]&&typeof b[k]==="object"&&!Array.isArray(b[k])&&a[k])merge(a[k],b[k]);else a[k]=b[k]});return a}function save(){try{localStorage.setItem(STORE,JSON.stringify(S))}catch(e){}}function $(id){return document.getElementById(id)}function toast(text){var n=document.createElement("div");n.className="toast";n.textContent=text;$('toast-stack').appendChild(n);setTimeout(function(){n.remove()},2600)}function showPage(id){document.querySelectorAll(".view").forEach(function(v){v.classList.toggle("on",v.id===id)});document.querySelectorAll(".nav").forEach(function(n){n.classList.toggle("on",n.dataset.v===id)});var names={home:"New Tab","proxy-page":"Browser",games:"Games",media:"Media",emulator:"Emulator",apps:"Apps",connect:"Connect",chat:"Chat",notes:"Notes",tools:"Utilities",settings:"Settings"};$('title').textContent=names[id]||"Matrix";if(id==="chat")refreshChat()}document.querySelectorAll(".nav").forEach(function(n){n.onclick=function(){showPage(n.dataset.v)}});document.querySelectorAll("[data-go]").forEach(function(n){n.onclick=function(){showPage(n.dataset.go)}});var modal=$('suite-modal'),modalContent=$('modal-content');function closeModal(){modal.classList.remove("on")}function openModal(html){modalContent.innerHTML=html;modal.classList.add("on")} $('modal-close').onclick=closeModal;modal.onclick=function(e){if(e.target===modal)closeModal()};$('account-button').onclick=function(){openModal('<span class="eyebrow">LOCAL PROFILE</span><h2 id="modal-title">Matrix account</h2><p>Your display name is stored on this device and is used when you post to shared Matrix Chat.</p><label>Display name</label><input id="profile-name" maxlength="32" value="'+safe(S.profile.name)+'"/><button id="save-profile" class="primary-action">Save local profile</button>');$('save-profile').onclick=function(){S.profile.name=$('profile-name').value.trim()||"Guest";save();renderProfile();closeModal();toast("Profile saved")}};function safe(v){return String(v||"").replace(/[&<>"']/g,function(c){return{"&":"&","<":"<",">":">",'"':""","'":"'"}[c]})}function renderProfile(){$('user-chip').textContent=S.profile.name==="Guest"?"Guest session":S.profile.name;if($('invite-code'))$('invite-code').textContent="MATRIX-"+S.profile.name.toUpperCase().replace(/[^A-Z0-9]/g,"").slice(0,10)}function renderContacts(){var g=$('contact-grid');if(!g)return;$('contact-count').textContent=S.contacts.length+" saved";g.innerHTML=S.contacts.length?"":'<div class="empty-state">No contacts yet. Add a local contact or configure an account service for shared presence.</div>';S.contacts.forEach(function(c){var d=document.createElement("div");d.className="contact-card";d.innerHTML="<b>"+safe(c.name)+"</b><span>"+safe(c.code)+" · local contact</span><button>Remove</button>";d.querySelector("button").onclick=function(){S.contacts=S.contacts.filter(function(x){return x.code!==c.code});save();renderContacts()};g.appendChild(d)})}$('add-contact').onclick=function(){openModal('<span class="eyebrow">CONNECT · BETA</span><h2 id="modal-title">Add local contact</h2><label>Display name</label><input id="contact-name" maxlength="32" placeholder="Friend name"/><label>Invite code</label><input id="contact-code" maxlength="40" placeholder="MATRIX-..."/><button id="save-contact" class="primary-action">Save contact</button>');$('save-contact').onclick=function(){var name=$('contact-name').value.trim(),code=$('contact-code').value.trim().toUpperCase();if(!name||!code){toast("Enter a name and invite code");return}S.contacts=S.contacts.filter(function(x){return x.code!==code});S.contacts.push({name:name,code:code});save();renderContacts();closeModal();toast("Contact saved locally")}};$('copy-invite').onclick=function(){var code=$('invite-code').textContent;if(navigator.clipboard&&navigator.clipboard.writeText)navigator.clipboard.writeText(code).then(function(){toast("Invite copied")}).catch(function(){toast(code)});else toast(code)}
|
|
41
41
|
var shortcuts=[{n:"YouTube",u:"https://www.youtube.com?hl=en",c:"streaming"},{n:"Reddit",u:"https://www.reddit.com?lang=en",c:"social"},{n:"Discord",u:"https://discord.com/app",c:"social"},{n:"X / Twitter",u:"https://x.com",c:"social"},{n:"TikTok",u:"https://www.tiktok.com?lang=en",c:"social"},{n:"Instagram",u:"https://www.instagram.com?hl=en",c:"social"},{n:"Twitch",u:"https://www.twitch.tv?lang=en",c:"streaming"},{n:"monochrome.tf",u:"https://monochrome.tf",c:"streaming"},{n:"zstream.mov",u:"https://zstream.mov",c:"streaming"},{n:"GitHub",u:"https://github.com",c:"tools"},{n:"Wikipedia",u:"https://en.wikipedia.org",c:"search"},{n:"DuckDuckGo",u:"https://duckduckgo.com/?kl=us-en",c:"search"}];var suiteRuntime=null,suiteApi=null,suiteReady=null;function suiteLoad(src){return new Promise(function(resolve,reject){var s=document.createElement("script");s.src=new URL(src,location.href).href;s.onload=resolve;s.onerror=function(){reject(new Error("Could not load "+src))};document.head.appendChild(s)})}async function suiteOpen(title,url){if(!suiteReady)suiteReady=(async function(){await suiteLoad("./matrix-jet/utils.js");await suiteLoad("./matrix-jet/api.js");await suiteLoad("./matrix-jet/core.js");suiteRuntime=new MatrixJetRuntime({base:new URL("./",location.href).href});suiteApi=new MatrixJetAPI(suiteRuntime);await suiteRuntime.init()})().catch(function(e){suiteReady=null;throw e});await suiteReady;await suiteApi.go($('frame'),url,title);$('player-state').textContent="Matrix Jet · "+suiteRuntime.getRelay()}function launch(title,url){S.stats.launches++;save();var player=$('player'),frame=$('frame');$('pt').textContent=title;$('player-state').textContent="Loading…";player.classList.add("on","loading");frame.removeAttribute("srcdoc");if(S.engine==="scramjet")suiteOpen(title,url).catch(function(e){frame.src=url;$('player-state').textContent="Direct fallback · "+e.message});else frame.src=url;setTimeout(function(){player.classList.remove("loading")},8000);if(S.prefs.launchNotifications)toast("Opening "+title+" inside Matrix")}function renderShortcuts(filter){var g=$('web-shortcuts');g.innerHTML="";shortcuts.filter(function(x){return filter==="popular"||x.c===filter}).forEach(function(x){var b=document.createElement("button");b.className="shortcut";b.innerHTML="<b>"+safe(x.n.charAt(0))+"</b><strong>"+safe(x.n)+"</strong><span>Open inside Matrix</span>";b.onclick=function(){launch(x.n,x.u)};g.appendChild(b)})}document.querySelectorAll("[data-web-filter]").forEach(function(b){b.onclick=function(){document.querySelectorAll("[data-web-filter]").forEach(function(x){x.classList.remove("active")});b.classList.add("active");renderShortcuts(b.dataset.webFilter)}});document.querySelectorAll("[data-engine]").forEach(function(b){b.onclick=function(){S.engine=b.dataset.engine;save();document.querySelectorAll("[data-engine]").forEach(function(x){x.classList.toggle("active",x.dataset.engine===S.engine)});toast("Browser engine: "+b.textContent)}});$('proxy-search').onsubmit=function(e){e.preventDefault();var q=$('proxy-q').value.trim();if(!q)return;var isUrl=/^(https?:\/\/|(?:[\w-]+\.)+[a-z]{2,})/i.test(q),u=isUrl?(/^https?:\/\//i.test(q)?q:"https://"+q):"https://www.google.com/search?hl=en&gl=us&pws=0&q="+encodeURIComponent(q);launch(isUrl?q:"Search: "+q,u)};$('test-relay').onclick=async function(){var h=$('browser-health'),d=$('browser-health-detail');h.textContent="Testing…";try{var r=await fetch("https://matrix-jet-lmx-production.up.railway.app/api/jet/status",{cache:"no-store"});if(!r.ok)throw Error("Status "+r.status);var j=await r.json();h.textContent="Matrix Jet online";d.textContent=j.relay||"Relay responded successfully"}catch(e){h.textContent="Relay unavailable";d.textContent="Apps and sites may use direct fallback."}};
|
|
42
42
|
var catalogs={anime:[{t:"One Piece",y:"1999",r:"8.7",g:"Adventure"},{t:"Frieren: Beyond Journey’s End",y:"2023",r:"9.1",g:"Fantasy"},{t:"Attack on Titan",y:"2013",r:"8.5",g:"Action"},{t:"Hunter x Hunter",y:"2011",r:"8.9",g:"Adventure"},{t:"Fullmetal Alchemist: Brotherhood",y:"2009",r:"9.0",g:"Fantasy"},{t:"Death Note",y:"2006",r:"8.4",g:"Thriller"},{t:"Jujutsu Kaisen",y:"2020",r:"8.4",g:"Action"},{t:"A Silent Voice",y:"2016",r:"8.8",g:"Drama"}],movies:[{t:"The Matrix",y:"1999",r:"8.7",g:"Sci-Fi"},{t:"Interstellar",y:"2014",r:"8.7",g:"Sci-Fi"},{t:"Spirited Away",y:"2001",r:"8.6",g:"Fantasy"},{t:"The Dark Knight",y:"2008",r:"9.0",g:"Action"},{t:"Everything Everywhere All at Once",y:"2022",r:"7.8",g:"Comedy"},{t:"Dune: Part Two",y:"2024",r:"8.5",g:"Sci-Fi"}],shows:[{t:"Arcane",y:"2021",r:"9.0",g:"Animation"},{t:"Stranger Things",y:"2016",r:"8.7",g:"Sci-Fi"},{t:"Breaking Bad",y:"2008",r:"9.5",g:"Drama"},{t:"The Last of Us",y:"2023",r:"8.7",g:"Drama"},{t:"The Mandalorian",y:"2019",r:"8.6",g:"Adventure"},{t:"Severance",y:"2022",r:"8.7",g:"Mystery"}]},mediaMode="anime";function hashColor(t){var h=0;for(var i=0;i<t.length;i++)h=(h*31+t.charCodeAt(i))%360;return"hsl("+h+" 62% 40%)"}function watched(t){return S.watchlist.indexOf(t)>-1}function toggleWatch(t){S.watchlist=watched(t)?S.watchlist.filter(function(x){return x!==t}):S.watchlist.concat(t);save();renderMedia();toast(watched(t)?"Added to watchlist":"Removed from watchlist")}function openTitle(x){openModal('<span class="eyebrow">'+safe(x.g)+' · '+safe(x.y)+'</span><h2 id="modal-title">'+safe(x.t)+'</h2><p>Rating '+safe(x.r)+'. Matrix provides public title metadata and opens an authorized availability search.</p><button id="provider-search" class="primary-action">Find where to watch</button>');$('provider-search').onclick=function(){closeModal();launch("Where to watch · "+x.t,"https://www.justwatch.com/us/search?q="+encodeURIComponent(x.t))}}function poster(x){var b=document.createElement("div");b.className="poster-card";b.setAttribute("role","button");b.setAttribute("tabindex","0");b.dataset.mark=x.t.charAt(0);b.style.setProperty("--poster-a",hashColor(x.t));b.innerHTML='<button class="watch-star" aria-label="Toggle watchlist">'+(watched(x.t)?"★":"☆")+'</button><strong>'+safe(x.t)+'</strong><span>'+safe(x.y)+" · ★ "+safe(x.r)+" · "+safe(x.g)+'</span>';b.onclick=function(e){if(e.target.closest(".watch-star")){e.stopPropagation();toggleWatch(x.t)}else openTitle(x)};b.onkeydown=function(e){if((e.key==="Enter"||e.key===" ")&&!e.target.closest(".watch-star")){e.preventDefault();openTitle(x)}};return b}function renderMedia(){var list=catalogs[mediaMode],q=$('media-search').value.toLowerCase(),filtered=list.filter(function(x){return x.t.toLowerCase().includes(q)||x.g.toLowerCase().includes(q)}),hero=filtered[0]||list[0];$('media-hero').innerHTML='<span class="eyebrow">FEATURED · '+safe(hero.g)+'</span><h2>'+safe(hero.t)+'</h2><p>'+safe(hero.y)+' · Rating '+safe(hero.r)+'. Browse metadata and find an authorized provider without leaving Matrix.</p><div class="media-actions"><button class="primary" id="watch-feature">Find provider</button><button id="info-feature">More info</button></div>';$('watch-feature').onclick=function(){launch("Where to watch · "+hero.t,"https://www.justwatch.com/us/search?q="+encodeURIComponent(hero.t))};$('info-feature').onclick=function(){openTitle(hero)};['media-trending','media-rated'].forEach(function(id){var g=$(id);g.innerHTML="";filtered.slice().sort(function(a,b){return id==='media-rated'?Number(b.r)-Number(a.r):0}).forEach(function(x){g.appendChild(poster(x))})});var w=$('watchlist-grid');w.innerHTML="";var all=[].concat(catalogs.anime,catalogs.movies,catalogs.shows).filter(function(x){return watched(x.t)});if(!all.length)w.innerHTML='<div class="empty-state">Your watchlist is empty.</div>';else all.forEach(function(x){w.appendChild(poster(x))})}document.querySelectorAll("[data-media-mode]").forEach(function(b){b.onclick=function(){mediaMode=b.dataset.mediaMode;document.querySelectorAll("[data-media-mode]").forEach(function(x){x.classList.toggle("active",x===b)});renderGenres();renderMedia()}});function renderGenres(){var genres=["All"].concat(Array.from(new Set(catalogs[mediaMode].map(function(x){return x.g}))));$('media-genres').innerHTML=genres.map(function(g,i){return'<button class="'+(i?'':'active')+'" data-media-genre="'+safe(g)+'">'+safe(g)+'</button>'}).join("");document.querySelectorAll("[data-media-genre]").forEach(function(b){b.onclick=function(){var g=b.dataset.mediaGenre;$('media-search').value=g==="All"?"":g;document.querySelectorAll("[data-media-genre]").forEach(function(x){x.classList.toggle("active",x===b)});renderMedia()}})}$('media-search').oninput=renderMedia;
|
|
43
|
-
function coreFor(name){var ext=name.split('.').pop().toLowerCase();return{gba:'gba',gb:'gb',gbc:'gb',nes:'nes',sfc:'snes',smc:'snes',n64:'n64',z64:'n64',nds:'nds',gen:'segaMD',md:'segaMD',iso:'psx',bin:'psx'}[ext]||'gba'}var builtInRoms=[
|
|
43
|
+
function coreFor(name){var ext=name.split('.').pop().toLowerCase();return{gba:'gba',zip:'gba',gb:'gb',gbc:'gb',nes:'nes',sfc:'snes',smc:'snes',n64:'n64',z64:'n64',nds:'nds',gen:'segaMD',md:'segaMD',iso:'psx',bin:'psx'}[ext]||'gba'}var builtInRoms=[];function openEmulatorRom(item,url,label){var page=new URL("./emulator/player.html",location.href),player=$('player'),frame=$('frame');page.searchParams.set("core",item.core);page.searchParams.set("name",item.name);page.searchParams.set("game",url);S.stats.launches++;save();$('pt').textContent=item.name;$('player-state').textContent=label+' · EmulatorJS';player.classList.add("on","loading");player.style.display="grid";player.style.zIndex="9999";player.style.visibility="visible";frame.removeAttribute("srcdoc");window.matrixLoadEmulator(page.href);setTimeout(function(){player.classList.remove("loading")},5000)}function rememberRom(file){var item={name:file.name,core:coreFor(file.name),size:file.size,when:Date.now()};S.roms=S.roms.filter(function(x){return x.name!==item.name});S.roms.unshift(item);S.roms=S.roms.slice(0,20);save();renderRoms();openEmulatorRom(item,URL.createObjectURL(file),"Local game")}function renderBuiltIns(){var g=$('built-in-roms');if(g)g.innerHTML=""}$('rom-input').onchange=function(){var f=this.files&&this.files[0];if(f)rememberRom(f)};$('bios-input').onchange=function(){if(this.files&&this.files[0])toast("BIOS selected for this session")};$('clear-roms').onclick=function(){S.roms=[];save();renderRoms();toast("Local games cleared")};function renderRoms(){$('rom-count').textContent=S.roms.length?S.roms.length+" imported game"+(S.roms.length===1?"":"s"):"No games saved";$('rom-library').innerHTML="";S.roms.forEach(function(x){var d=document.createElement("div");d.className="rom-card";d.innerHTML='<b>'+safe(x.name)+'</b><span>'+safe(x.core.toUpperCase())+' · '+Math.round(x.size/1024)+' KB</span><small>Saved only in this browser. Import the file again if browser storage was cleared.</small>';$('rom-library').appendChild(d)})}
|
|
44
44
|
var currentNote="";function ensureNote(){if(!S.notes.length){var id=String(Date.now());S.notes=[{id:id,title:"Welcome to Matrix Notes",body:"Your notes save automatically on this device.",updated:Date.now()}];currentNote=id;save()}else if(!currentNote)currentNote=S.notes[0].id}function renderNotes(){ensureNote();$('notes-list').innerHTML="";S.notes.slice().sort(function(a,b){return b.updated-a.updated}).forEach(function(n){var b=document.createElement("button");b.className="note-item"+(n.id===currentNote?" active":"");b.innerHTML='<b>'+safe(n.title||"Untitled note")+'</b><small>'+new Date(n.updated).toLocaleDateString()+'</small>';b.onclick=function(){saveNote();currentNote=n.id;renderNotes()};$('notes-list').appendChild(b)});var n=S.notes.find(function(x){return x.id===currentNote})||S.notes[0];$('note-title').value=n.title;$('note-body').value=n.body}function saveNote(){var n=S.notes.find(function(x){return x.id===currentNote});if(!n)return;n.title=$('note-title').value||"Untitled note";n.body=$('note-body').value;n.updated=Date.now();save();$('note-save-state').textContent="Saved "+new Date().toLocaleTimeString()}var noteTimer;['note-title','note-body'].forEach(function(id){$(id).oninput=function(){clearTimeout(noteTimer);$('note-save-state').textContent="Saving…";noteTimer=setTimeout(function(){saveNote();renderNotes()},350)}});$('new-note').onclick=function(){saveNote();var id=String(Date.now());S.notes.unshift({id:id,title:"Untitled note",body:"",updated:Date.now()});currentNote=id;save();renderNotes();$('note-title').focus()};$('delete-note').onclick=function(){if(S.notes.length<=1){$('note-title').value="Untitled note";$('note-body').value="";saveNote();return}S.notes=S.notes.filter(function(x){return x.id!==currentNote});currentNote=S.notes[0].id;save();renderNotes()};var matrixService=/^(localhost|127\.0\.0\.1)$/.test(location.hostname)?location.origin:"https://matrix-jet-lmx-production.up.railway.app",chatApi=matrixService+"/api",chatRoom="lounge";function chatMessageHtml(m,discord){var name=discord?m.author:m.name,time=discord?m.timestamp:m.time,avatar=discord&&/^https:\/\/cdn\.discordapp\.com\//.test(m.avatar||"")?'<img alt="" src="'+safe(m.avatar)+'"/>':safe(String(name||"?").charAt(0).toUpperCase()),files=(m.attachments||[]).filter(function(a){return/^https:\/\/cdn\.discordapp\.com\//.test(a.url||"")}).map(function(a){return'<a class="chat-attachment" href="'+safe(a.url)+'" target="_blank" rel="noopener noreferrer">Attachment · '+safe(a.name||"file")+'</a>'}).join("");return'<div class="chat-message"><div class="chat-avatar">'+avatar+'</div><div class="chat-copy"><b>'+safe(name||"Member")+'</b><small>'+new Date(time||Date.now()).toLocaleTimeString([], {hour:"2-digit",minute:"2-digit"})+'</small><p>'+safe(m.content!=null?m.content:m.text)+'</p>'+files+'</div></div>'}function renderMatrixMessages(messages,offline){$('chat-messages').innerHTML=(offline?'<div class="system-message">Shared server unavailable. These fallback messages exist only on this device.</div>':'<div class="system-message">Matrix website chat · messages here are not sent to Discord.</div>')+messages.map(function(m){return chatMessageHtml(m,false)}).join("");$('chat-messages').scrollTop=$('chat-messages').scrollHeight}async function refreshMatrixChat(){var state=$('matrix-chat-state');state.textContent="Syncing…";try{var r=await fetch(chatApi+"/site-chat/messages?room="+encodeURIComponent(chatRoom),{cache:"no-store"});if(!r.ok)throw Error("Server "+r.status);var data=await r.json();renderMatrixMessages(data.messages||[],false);state.textContent="Shared website chat · online"}catch(e){var local=S.chat.filter(function(m){return(m.room||"lounge")===chatRoom});renderMatrixMessages(local,true);state.textContent="Local fallback · server offline"}}async function refreshDiscord(){var state=$('discord-state'),feed=$('discord-messages');state.textContent="Syncing…";try{var r=await fetch(chatApi+"/discord/messages",{cache:"no-store"}),data=await r.json();if(data.invite)$('discord-invite').href=data.invite;if(!data.configured){state.textContent="Setup required";feed.innerHTML='<div class="system-message">The Discord bot and channel are not configured on the Matrix server yet. Use Join to open the server.</div>';return}if(!r.ok)throw Error(data.error||"Discord "+r.status);state.textContent="Read-only · live";feed.innerHTML=(data.messages||[]).length?(data.messages||[]).map(function(m){return chatMessageHtml(m,true)}).join(""):'<div class="system-message">No recent Discord messages.</div>';feed.scrollTop=feed.scrollHeight}catch(e){state.textContent="Discord unavailable";feed.innerHTML='<div class="system-message">'+safe(e.message)+'</div>'}}function refreshChat(){refreshMatrixChat();refreshDiscord()}function renderChat(){refreshChat()}document.querySelectorAll("[data-chat-room]").forEach(function(b){b.onclick=function(){chatRoom=b.dataset.chatRoom;document.querySelectorAll("[data-chat-room]").forEach(function(x){x.classList.toggle("active",x===b)});$('matrix-room-name').textContent="# "+chatRoom;$('chat-input').placeholder="Message #"+chatRoom;refreshMatrixChat()}});$('refresh-chat').onclick=refreshChat;$('chat-form').onsubmit=async function(e){e.preventDefault();var v=$('chat-input').value.trim(),button=this.querySelector("button");if(!v||button.disabled)return;button.disabled=true;$('matrix-chat-state').textContent="Sending…";try{var r=await fetch(chatApi+"/site-chat/messages",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({room:chatRoom,name:S.profile.name,text:v})});var data=await r.json();if(!r.ok)throw Error(data.error||"Could not send");$('chat-input').value="";S.stats.messages++;save();await refreshMatrixChat();if(S.prefs.chatSounds)try{var a=new AudioContext(),o=a.createOscillator(),g=a.createGain();o.connect(g);g.connect(a.destination);g.gain.value=.025;o.frequency.value=560;o.start();o.stop(a.currentTime+.08)}catch(e){}}catch(error){S.chat.push({room:chatRoom,name:S.profile.name,text:v,time:Date.now()});S.chat=S.chat.slice(-100);$('chat-input').value="";save();renderMatrixMessages(S.chat.filter(function(m){return(m.room||"lounge")===chatRoom}),true);toast(error.message)}finally{button.disabled=false}};setInterval(function(){if($('chat').classList.contains("on"))refreshChat()},6000);
|
|
45
45
|
document.querySelectorAll("[data-settings-tab]").forEach(function(b){b.onclick=function(){document.querySelectorAll("[data-settings-tab]").forEach(function(x){x.classList.toggle("active",x===b)});document.querySelectorAll(".settings-page").forEach(function(x){x.classList.toggle("on",x.id===b.dataset.settingsTab)})}});var accents={midnight:"#70b7ff",deep:"#a7b2c3",violet:"#ad82ff",forest:"#58d596",crimson:"#ff6680",light:"#3679c9"};document.querySelectorAll("[data-theme-preset]").forEach(function(b){b.onclick=function(){var t=b.dataset.themePreset;$('theme-mode').value=t;$('accent-color').value=accents[t];$('theme-mode').dispatchEvent(new Event('change'));$('accent-color').dispatchEvent(new Event('input'));document.querySelectorAll("[data-theme-preset]").forEach(function(x){x.classList.toggle("active",x===b)})}});function bindPref(id,key,cls){var e=$(id);e.checked=!!S.prefs[key];document.body.classList.toggle(cls,!!S.prefs[key]);e.onchange=function(){S.prefs[key]=e.checked;document.body.classList.toggle(cls,e.checked);save()}}bindPref('reduce-motion','reduceMotion','reduced');bindPref('compact-mode','compact','compact');bindPref('hide-activity','hideActivity','hide-activity');['clear-player','launch-notifications','chat-sounds','offline-warnings'].forEach(function(id){var key=id.replace(/-([a-z])/g,function(_,c){return c.toUpperCase()});var e=$(id);e.checked=!!S.prefs[key];e.onchange=function(){S.prefs[key]=e.checked;save()}});document.querySelectorAll("[data-cloak-preset]").forEach(function(b){b.onclick=function(){$('cloak-title').value=b.dataset.cloakPreset;toast("Tab preset selected — press Apply")}});$('export-data').onclick=function(){var a=document.createElement('a'),u=URL.createObjectURL(new Blob([JSON.stringify(S,null,2)],{type:'application/json'}));a.href=u;a.download='matrix-backup.json';a.click();setTimeout(function(){URL.revokeObjectURL(u)},1000)};$('clear-data').onclick=function(){if(confirm("Clear Matrix favorites, notes, chat, watchlist, and local preferences?")){localStorage.removeItem(STORE);toast("Matrix data cleared. Reload to reset the interface.")}};window.addEventListener('offline',function(){if(!S.prefs.offlineWarnings)return;var b=document.createElement('div');b.id='suite-offline';b.className='offline-banner';b.textContent='Matrix is offline — local notes and settings still work.';document.body.appendChild(b)});window.addEventListener('online',function(){var b=$('suite-offline');if(b)b.remove();toast('Back online')});var originalClose=$('close').onclick;$('close').onclick=function(){if(originalClose)originalClose();if(S.prefs.clearPlayer)$('frame').src='about:blank'};renderProfile();renderContacts();renderShortcuts('popular');renderGenres();renderMedia();renderBuiltIns();renderRoms();renderNotes();renderChat();document.querySelectorAll('[data-engine]').forEach(function(x){x.classList.toggle('active',x.dataset.engine===S.engine)});})();
|
|
46
46
|
]]></script><script><![CDATA[
|
|
@@ -150,6 +150,8 @@ document.querySelectorAll("[data-settings-tab]").forEach(function(b){b.onclick=f
|
|
|
150
150
|
(function(){
|
|
151
151
|
"use strict";
|
|
152
152
|
function byId(id){return document.getElementById(id)}
|
|
153
|
+
function escapeStatus(value){return String(value==null?"":value).replace(/[&<>"']/g,function(character){return{"&":"&","<":"<",">":">",'"':""","'":"'"}[character]})}
|
|
154
|
+
function matrixStatusPage(title,body){return "<!doctype html><html lang='en'><head><meta charset='utf-8'><meta name='viewport' content='width=device-width'><style>html,body{min-height:100%;margin:0;background:#050912;color:#edf6ff;font:16px Arial,sans-serif}body{display:grid;place-items:center;padding:30px}.status{max-width:680px;padding:28px;border:1px solid #294c76;border-radius:16px;background:#0b1727;box-shadow:0 24px 60px #0009}h1{margin:0 0 10px;color:#8dcbff}p{color:#a9bdd4;line-height:1.55}</style></head><body><main class='status'><h1>"+escapeStatus(title)+"</h1>"+body+"</main></body></html>"}
|
|
153
155
|
function showGame(game){
|
|
154
156
|
var player=byId("player"),frame=byId("frame");
|
|
155
157
|
byId("pt").textContent=game.name;
|
|
@@ -210,5 +212,26 @@ document.querySelectorAll("[data-settings-tab]").forEach(function(b){b.onclick=f
|
|
|
210
212
|
if(!local){if(window.matrixOpenWeb)window.matrixOpenWeb(game.name,game.url);else frame.src=game.url}
|
|
211
213
|
}
|
|
212
214
|
};
|
|
215
|
+
function proxyServerReady(){
|
|
216
|
+
var controller=new AbortController(),timer=setTimeout(function(){controller.abort()},12000);
|
|
217
|
+
return fetch("https://matrix-jet-lmx-production.up.railway.app/api/jet/status",{cache:"no-store",signal:controller.signal}).then(function(response){
|
|
218
|
+
clearTimeout(timer);if(!response.ok)throw Error("Matrix proxy returned "+response.status);return response.json();
|
|
219
|
+
},function(error){clearTimeout(timer);throw error});
|
|
220
|
+
}
|
|
221
|
+
var originalWeb=window.matrixOpenWeb;
|
|
222
|
+
window.matrixOpenWeb=async function(title,url){
|
|
223
|
+
var player=byId("player"),frame=byId("frame"),state=byId("player-state");
|
|
224
|
+
byId("pt").textContent=title;player.classList.add("on","loading");player.style.cssText+=";display:grid;visibility:visible;z-index:9999";
|
|
225
|
+
frame.removeAttribute("src");frame.srcdoc=matrixStatusPage("Starting Matrix Browser","<p>Connecting to the private Matrix relay…</p>");
|
|
226
|
+
try{await proxyServerReady();frame.removeAttribute("srcdoc");return await originalWeb(title,url)}
|
|
227
|
+
catch(error){frame.onload=function(){state.textContent="Browser unavailable"};frame.srcdoc=matrixStatusPage("Browser unavailable","<p>"+escapeStatus(error.message||"The Matrix relay did not respond.")+"</p><p>Try again in a moment or switch proxy engines in Settings.</p>");state.textContent="Browser unavailable"}
|
|
228
|
+
finally{player.classList.remove("loading")}
|
|
229
|
+
};
|
|
230
|
+
var originalUv=window.matrixOpenUv;
|
|
231
|
+
window.matrixOpenUv=async function(title,url){
|
|
232
|
+
try{await proxyServerReady();return await originalUv(title,url)}
|
|
233
|
+
catch(error){var player=byId("player"),frame=byId("frame");byId("pt").textContent=title;player.classList.add("on");frame.removeAttribute("src");frame.srcdoc=matrixStatusPage("Browser unavailable","<p>"+escapeStatus(error.message||"The Matrix relay did not respond.")+"</p>");byId("player-state").textContent="Browser unavailable"}
|
|
234
|
+
};
|
|
235
|
+
var playerFrameStyle=document.createElement("style");playerFrameStyle.textContent=".player iframe{background:#050912!important;color-scheme:dark}";document.head.appendChild(playerFrameStyle);
|
|
213
236
|
})();
|
|
214
237
|
]]></script></body></html></foreignObject></svg>
|