From 0316acf7025c852829572b76cbc92558bf7f3d6a Mon Sep 17 00:00:00 2001 From: Admin Date: Sun, 20 Sep 2026 22:23:01 +0000 Subject: [PATCH] Polish mobile shipyard from device screenshots --- src/mobile-hud.js | 12 ++++++++---- src/progression/ship-loadout.js | 1 + src/shipyard-ui.js | 28 ++++++++++++++++------------ tests/progression.test.mjs | 9 +++++++++ tests/wiring.test.mjs | 4 ++++ 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/mobile-hud.js b/src/mobile-hud.js index f642fc2..aca9b4f 100644 --- a/src/mobile-hud.js +++ b/src/mobile-hud.js @@ -414,6 +414,10 @@ if (!isDesktopPointer.matches) { filter: grayscale(.7); } + #mobile-controls.controller-active .mobile-control.ship-action[hidden] { + display: none !important; + } + #mobile-controls.controller-active .mobile-control.ship-action.active, #mobile-controls.controller-active .mobile-control.ship-action:active { background: rgba(117, 78, 31, .9); @@ -799,17 +803,17 @@ if (!isDesktopPointer.matches) { const progression = state.progression || {}; if (buyButton) { + buyButton.hidden = !progression.docked; buyButton.disabled = !progression.docked; - buyButton.textContent = progression.docked - ? 'SHIPYARD' - : 'SHIPYARD\nPORT'; + buyButton.textContent = 'SHIPYARD'; } if (fitButton) { const module = progression.salvageModule; + fitButton.hidden = !module; fitButton.disabled = !module; fitButton.textContent = module ? `FIT\n${module.name}` - : 'FIT\nEMPTY'; + : 'FIT'; } const statusText = String(state.status || '').trim(); diff --git a/src/progression/ship-loadout.js b/src/progression/ship-loadout.js index 43c3652..33a524b 100644 --- a/src/progression/ship-loadout.js +++ b/src/progression/ship-loadout.js @@ -79,6 +79,7 @@ export const buyAndInstallModule = (progression, moduleId) => { export const marketForPort = (port) => { const stock = purchasableModules().filter((module) => { + if (module.slot === 'rudder') return true; if (port?.type === 'industrial') return ['engine', 'hull', 'utility', 'cargo'].includes(module.slot); if (port?.type === 'military') return ['weapon', 'hull', 'navigation'].includes(module.slot); if (port?.type === 'fuel') return ['engine', 'navigation', 'cargo'].includes(module.slot); diff --git a/src/shipyard-ui.js b/src/shipyard-ui.js index 0505a53..e57ccd0 100644 --- a/src/shipyard-ui.js +++ b/src/shipyard-ui.js @@ -65,8 +65,7 @@ overlay.innerHTML = ` `).join('')}
- - SPARROW ACTIVE + Sparrow active
@@ -253,15 +252,10 @@ style.textContent = ` transform: translateX(-50%); } - .shipyard-active-plate img { display: block; width: 100%; } - .shipyard-active-plate span { - position: absolute; - inset: 0; - display: grid; - place-items: center; - color: #b9efc0; - font-size: clamp(9px, 1.15vw, 13px); - letter-spacing: .08em; + .shipyard-active-plate img { + display: block; + width: 100%; + height: auto; } .shipyard-footer { @@ -351,11 +345,21 @@ style.textContent = ` } @media (orientation: portrait) and (max-width: 760px) { + .shipyard-shell { + min-height: calc(100dvh - 8px); + display: flex; + flex-direction: column; + } + .shipyard-title { width: min(500px, 80vw); } .shipyard-grid { grid-template-columns: 1fr; } .shipyard-preview-wrap { display: none; } .shipyard-upgrade-wrap { width: min(96vw, 520px); margin: 0 auto; } - .shipyard-footer { margin-top: 48px; } + + .shipyard-footer { + margin-top: auto; + padding: 58px 0 max(14px, env(safe-area-inset-bottom)); + } } `; document.head.appendChild(style); diff --git a/tests/progression.test.mjs b/tests/progression.test.mjs index d754558..4bdfc24 100644 --- a/tests/progression.test.mjs +++ b/tests/progression.test.mjs @@ -3,6 +3,7 @@ import assert from 'node:assert/strict'; import { bestAffordableMarketUpgrade, buyAndInstallModule, + marketForPort, collectSalvage, createStarterProgression, deriveShipStats, @@ -58,6 +59,14 @@ test('market can select an affordable upgrade for a port', () => { }); +test('rudder is stocked at every port type so the shipyard card is actionable', () => { + const portTypes = ['industrial', 'military', 'fuel', 'trade']; + for (const type of portTypes) { + const stock = marketForPort({ type }); + assert.ok(stock.some(({ id }) => id === 'reinforced-rudder')); + } +}); + test('rudder tier matches shipyard art and improves handling', () => { const starter = { ...createStarterProgression(), credits: 650 }; const result = buyAndInstallModule(starter, 'reinforced-rudder'); diff --git a/tests/wiring.test.mjs b/tests/wiring.test.mjs index 67d572a..379c1f4 100644 --- a/tests/wiring.test.mjs +++ b/tests/wiring.test.mjs @@ -280,6 +280,8 @@ test('mobile controls expose native buy and salvage-fit actions', async () => { assert.match(game, /salvageModule: salvageModule \? \{/); assert.match(hud, /root\.querySelector\('\.mobile-control\.buy'\)/); assert.match(hud, /root\.querySelector\('\.mobile-control\.fit'\)/); + assert.match(hud, /buyButton\.hidden = !progression\.docked/); + assert.match(hud, /fitButton\.hidden = !module/); assert.match(hud, /buyButton\.disabled = !progression\.docked/); assert.match(hud, /fitButton\.disabled = !module/); }); @@ -299,6 +301,8 @@ test('shipyard overlay uses supplied component art and explicit module purchases assert.match(shipyard, /shipyard-ui\/upgrades\.png\?url/); assert.match(shipyard, /airship:shipyard-open/); assert.match(shipyard, /dispatchAction\('purchase-module', \{ moduleId \}\)/); + assert.doesNotMatch(shipyard, /data-shipyard="active"/); + assert.match(shipyard, /min-height: calc\(100dvh - 8px\)/); assert.match(game, /marketForPort\(port\)/); assert.match(game, /airship:shipyard-state/); assert.match(game, /this\.shipyardOpen = true/);