diff --git a/public/app.js b/public/app.js index 5ff6cc3..af0eab6 100644 --- a/public/app.js +++ b/public/app.js @@ -260,12 +260,35 @@ function renderProgramDetails(program) { } } +async function loadInlineSource(program) { + const heading = document.querySelector('#program-source-title'); + const message = document.querySelector('#program-source-status'); + const listing = document.querySelector('#program-source-code'); + heading.textContent = program.language === 'machine-code' ? 'Assembly source' : 'BASIC V2 source'; + message.hidden = false; + message.className = ''; + message.textContent = 'Loading source…'; + listing.hidden = true; + listing.querySelector('code').textContent = ''; + try { + const response = await fetch(`/api/source?path=${encodeURIComponent(program.path)}`, { cache: 'no-store' }); + if (!response.ok) throw new Error('The program source could not be loaded.'); + listing.querySelector('code').textContent = await response.text(); + message.hidden = true; + listing.hidden = false; + } catch (error) { + message.className = 'error'; + message.textContent = error.message; + } +} + function startEmulator(program) { library.hidden = true; player.hidden = false; document.title = `${program.title} — C64 Program Collection`; document.querySelector('#program-title').textContent = program.title; renderProgramDetails(program); + loadInlineSource(program); window.EJS_player = '#game'; window.EJS_core = 'vice_x64sc'; diff --git a/public/index.html b/public/index.html index 2d043b3..edf8098 100644 --- a/public/index.html +++ b/public/index.html @@ -72,6 +72,11 @@

Controls

+
+

Source code

+

Loading source…

+ +
diff --git a/public/styles.css b/public/styles.css index eca774f..c2e79c9 100644 --- a/public/styles.css +++ b/public/styles.css @@ -81,8 +81,10 @@ button:disabled { opacity: .5; cursor: wait; } .player-heading h1 { font-size: clamp(1.8rem, 5vw, 3rem); } .program-badges { margin-top: .85rem; } .program-page-description { max-width: 760px; margin: 1.5rem 0 0; color: #d2cef0; font-size: 1.08rem; line-height: 1.6; } -.instructions-panel { max-width: 760px; margin: 1.5rem auto 0; padding: 1.1rem 1.25rem; border: 1px solid #b8b1e738; border-radius: 12px; background: #211a59d9; } -.instructions-panel h2 { margin: 0 0 .85rem; font-size: 1rem; letter-spacing: .08em; text-transform: uppercase; } +.instructions-panel, .program-source-panel { max-width: 760px; min-width: 0; margin: 1.5rem auto 0; padding: 1.1rem 1.25rem; border: 1px solid #b8b1e738; border-radius: 12px; background: #211a59d9; } +.instructions-panel h2, .program-source-panel h2 { margin: 0 0 .85rem; font-size: 1rem; letter-spacing: .08em; text-transform: uppercase; } +.program-source-panel pre { max-height: 600px; margin: 0; padding: 1rem; overflow: auto; border-radius: 6px; background: #0d0928; color: #d8d3f4; font: .85rem/1.55 ui-monospace, SFMono-Regular, Consolas, monospace; white-space: pre; tab-size: 4; } +.program-source-panel pre:focus-visible { outline: 2px solid var(--c64-light-blue); outline-offset: 2px; } .control-list { display: grid; gap: .7rem; } .control-row { display: grid; grid-template-columns: minmax(150px, auto) 1fr; gap: 1rem; align-items: center; color: #d2cef0; } .key-group { display: flex; flex-wrap: wrap; gap: .35rem; }