fix: rebuilt stylesheet + visible explorer rows (v3.6.30)

The compiled Tailwind stylesheet (frontend/style.css) had been stale since
2026-09-22: classes introduced later were missing from it, so the Install /
Make selectable chips had no background or text colour in light mode (dark
mode only looked green because an older dark:text-emerald-400 rule matched)
and the new w-28 action column did not apply.

- Rebuilt with `npm run build` (Tailwind + appended src/contrast.css);
  verified the previously missing classes (bg-emerald-100, text-emerald-700,
  hover:bg-emerald-200, bg-red-100, bg-blue-100, w-28, ...).
- Explorer rows are visible chips now: rounded bg-gray-50 dark:bg-slate-800/60
  with a hover:bg-gray-100 highlight and the colored left accent kept (module
  rows a subtler variant), so a right-column button is unambiguously tied to
  its row; ISD/app/ELF/module rows all use the same treatment.
- AGENTS working conventions: an index.html change that introduces new
  utility classes also requires `npm run build` in frontend/.

660 frontend / 504 Python green; version 3.6.30; sw simple-v303.
This commit is contained in:
2026-09-28 23:38:06 +03:00
parent 3b5a1495f5
commit c3117e90fa
6 changed files with 197 additions and 8 deletions
+5 -5
View File
@@ -1713,7 +1713,7 @@
// ===== Version =====
// Single source of truth for the PWA version: shown in the header and used
// by the server version check in pysimConnect().
const SIMPLE_VERSION = '3.6.29';
const SIMPLE_VERSION = '3.6.30';
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
// ===== Tab switching =====
@@ -8840,7 +8840,7 @@ function ramRenderExploreHtml(mem, isd, apps, elfs, fmt) {
html += '<div class="mb-4">';
html += '<div class="font-semibold text-sm mb-1">' + esc(t('ISD (Issuer Security Domain)')) + '</div>';
isd.forEach(o => {
html += '<div class="mb-1 pl-2 border-l-2 border-blue-400">';
html += '<div class="mb-1 pl-2 pr-2 py-1 rounded bg-gray-50 dark:bg-slate-800/60 hover:bg-gray-100 dark:hover:bg-slate-700/50 border-l-2 border-blue-400">';
html += '<div>' + esc(t('AID:')) + ' ' + (o.aid ? jcAidHtml(o.aid) : '?') + '</div>';
html += '<div>' + esc(t('Lifecycle:')) + ' ' + ramFmtLifecycle(o.lifecycle || '') + '</div>';
if (o.privileges) html += '<div>' + esc(t('Privileges:')) + ' ' + ramFmtPrivileges(o.privileges) + ' (' + o.privileges + ')</div>';
@@ -8853,7 +8853,7 @@ function ramRenderExploreHtml(mem, isd, apps, elfs, fmt) {
html += '<div class="mb-4">';
html += '<div class="font-semibold text-sm mb-1">' + esc(t('Applications / Applet Instances')) + '</div>';
apps.forEach(o => {
html += '<div class="mb-1 pl-2 border-l-2 border-green-400 flex items-start gap-3">';
html += '<div class="mb-1 pl-2 pr-2 py-1 rounded bg-gray-50 dark:bg-slate-800/60 hover:bg-gray-100 dark:hover:bg-slate-700/50 border-l-2 border-green-400 flex items-start gap-3">';
html += '<div class="flex-1 min-w-0">';
html += '<div>' + esc(t('Application / Instance AID:')) + ' ' + (o.aid ? jcAidHtml(o.aid) : '?') + '</div>';
html += '<div>' + esc(t('Lifecycle:')) + ' ' + ramFmtLifecycle(o.lifecycle || '') + '</div>';
@@ -8877,7 +8877,7 @@ function ramRenderExploreHtml(mem, isd, apps, elfs, fmt) {
html += '<div class="mb-4">';
html += '<div class="font-semibold text-sm mb-1">' + esc(t('Executable Load Files (ELFs) / Packages')) + '</div>';
elfs.forEach(o => {
html += '<div class="mb-1 pl-2 border-l-2 border-purple-400 flex items-start gap-3">';
html += '<div class="mb-1 pl-2 pr-2 py-1 rounded bg-gray-50 dark:bg-slate-800/60 hover:bg-gray-100 dark:hover:bg-slate-700/50 border-l-2 border-purple-400 flex items-start gap-3">';
html += '<div class="flex-1 min-w-0">';
html += '<div>' + esc(t('Load File AID / Package AID:')) + ' ' + (o.aid ? jcAidHtml(o.aid) : '?') + '</div>';
html += '<div>' + esc(t('Lifecycle:')) + ' ' + ramFmtLifecycle(o.lifecycle || '') + '</div>';
@@ -8896,7 +8896,7 @@ function ramRenderExploreHtml(mem, isd, apps, elfs, fmt) {
// Module rows: emitted as sibling rows so their Install buttons share
// the same action column as the entry buttons.
(o.moduleAids || []).forEach(m => {
html += '<div class="mb-1 pl-6 flex items-start gap-3">';
html += '<div class="mb-1 pl-6 pr-2 py-1 rounded bg-gray-100/60 dark:bg-slate-800/40 hover:bg-gray-200/60 dark:hover:bg-slate-700/40 flex items-start gap-3">';
html += '<div class="flex-1 min-w-0">- ' + jcAidHtml(m) + '</div>';
html += '<div class="w-28 shrink-0 flex flex-col gap-1">';
// A loaded applet class with no instance yet: one click to the
+186
View File
@@ -672,6 +672,14 @@ video {
z-index: 50;
}
.col-span-2 {
grid-column: span 2 / span 2;
}
.col-span-4 {
grid-column: span 4 / span 4;
}
.mx-3 {
margin-left: 0.75rem;
margin-right: 0.75rem;
@@ -743,6 +751,10 @@ video {
margin-left: 1rem;
}
.ml-7 {
margin-left: 1.75rem;
}
.ml-8 {
margin-left: 2rem;
}
@@ -891,6 +903,10 @@ video {
width: 6rem;
}
.w-28 {
width: 7rem;
}
.w-32 {
width: 8rem;
}
@@ -956,10 +972,18 @@ video {
min-width: 14rem;
}
.min-w-\[16rem\] {
min-width: 16rem;
}
.min-w-\[180px\] {
min-width: 180px;
}
.max-w-2xl {
max-width: 42rem;
}
.max-w-3xl {
max-width: 48rem;
}
@@ -1173,6 +1197,17 @@ video {
margin-bottom: calc(1rem * var(--tw-space-y-reverse));
}
.divide-y > :not([hidden]) ~ :not([hidden]) {
--tw-divide-y-reverse: 0;
border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
border-bottom-width: calc(1px * var(--tw-divide-y-reverse));
}
.divide-gray-100 > :not([hidden]) ~ :not([hidden]) {
--tw-divide-opacity: 1;
border-color: rgb(243 244 246 / var(--tw-divide-opacity, 1));
}
.overflow-auto {
overflow: auto;
}
@@ -1256,6 +1291,11 @@ video {
border-top-width: 1px;
}
.border-amber-400 {
--tw-border-opacity: 1;
border-color: rgb(251 191 36 / var(--tw-border-opacity, 1));
}
.border-blue-400 {
--tw-border-opacity: 1;
border-color: rgb(96 165 250 / var(--tw-border-opacity, 1));
@@ -1291,10 +1331,20 @@ video {
border-color: rgb(192 132 252 / var(--tw-border-opacity, 1));
}
.border-red-300 {
--tw-border-opacity: 1;
border-color: rgb(252 165 165 / var(--tw-border-opacity, 1));
}
.border-t-transparent {
border-top-color: transparent;
}
.bg-amber-50 {
--tw-bg-opacity: 1;
background-color: rgb(255 251 235 / var(--tw-bg-opacity, 1));
}
.bg-amber-600 {
--tw-bg-opacity: 1;
background-color: rgb(217 119 6 / var(--tw-bg-opacity, 1));
@@ -1304,11 +1354,21 @@ video {
background-color: rgb(0 0 0 / 0.5);
}
.bg-blue-100 {
--tw-bg-opacity: 1;
background-color: rgb(219 234 254 / var(--tw-bg-opacity, 1));
}
.bg-blue-600 {
--tw-bg-opacity: 1;
background-color: rgb(37 99 235 / var(--tw-bg-opacity, 1));
}
.bg-emerald-100 {
--tw-bg-opacity: 1;
background-color: rgb(209 250 229 / var(--tw-bg-opacity, 1));
}
.bg-emerald-600 {
--tw-bg-opacity: 1;
background-color: rgb(5 150 105 / var(--tw-bg-opacity, 1));
@@ -1324,6 +1384,10 @@ video {
background-color: rgb(243 244 246 / var(--tw-bg-opacity, 1));
}
.bg-gray-100\/60 {
background-color: rgb(243 244 246 / 0.6);
}
.bg-gray-200 {
--tw-bg-opacity: 1;
background-color: rgb(229 231 235 / var(--tw-bg-opacity, 1));
@@ -1497,6 +1561,14 @@ video {
padding-left: 1.25rem;
}
.pl-6 {
padding-left: 1.5rem;
}
.pr-2 {
padding-right: 0.5rem;
}
.pt-1 {
padding-top: 0.25rem;
}
@@ -1575,10 +1647,19 @@ video {
font-weight: 600;
}
.uppercase {
text-transform: uppercase;
}
.italic {
font-style: italic;
}
.ordinal {
--tw-ordinal: ordinal;
font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction);
}
.leading-relaxed {
line-height: 1.625;
}
@@ -1597,16 +1678,31 @@ video {
color: rgb(217 119 6 / var(--tw-text-opacity, 1));
}
.text-amber-700 {
--tw-text-opacity: 1;
color: rgb(180 83 9 / var(--tw-text-opacity, 1));
}
.text-blue-600 {
--tw-text-opacity: 1;
color: rgb(37 99 235 / var(--tw-text-opacity, 1));
}
.text-blue-700 {
--tw-text-opacity: 1;
color: rgb(29 78 216 / var(--tw-text-opacity, 1));
}
.text-emerald-600 {
--tw-text-opacity: 1;
color: rgb(5 150 105 / var(--tw-text-opacity, 1));
}
.text-emerald-700 {
--tw-text-opacity: 1;
color: rgb(4 120 87 / var(--tw-text-opacity, 1));
}
.text-gray-300 {
--tw-text-opacity: 1;
color: rgb(209 213 219 / var(--tw-text-opacity, 1));
@@ -1687,6 +1783,11 @@ video {
color: rgb(148 163 184 / var(--tw-text-opacity, 1));
}
.text-slate-500 {
--tw-text-opacity: 1;
color: rgb(100 116 139 / var(--tw-text-opacity, 1));
}
.text-white {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity, 1));
@@ -1738,6 +1839,11 @@ video {
background-color: rgb(29 78 216 / var(--tw-bg-opacity, 1));
}
.hover\:bg-emerald-200:hover {
--tw-bg-opacity: 1;
background-color: rgb(167 243 208 / var(--tw-bg-opacity, 1));
}
.hover\:bg-emerald-700:hover {
--tw-bg-opacity: 1;
background-color: rgb(4 120 87 / var(--tw-bg-opacity, 1));
@@ -1753,6 +1859,10 @@ video {
background-color: rgb(229 231 235 / var(--tw-bg-opacity, 1));
}
.hover\:bg-gray-200\/60:hover {
background-color: rgb(229 231 235 / 0.6);
}
.hover\:bg-gray-300:hover {
--tw-bg-opacity: 1;
background-color: rgb(209 213 219 / var(--tw-bg-opacity, 1));
@@ -1793,6 +1903,16 @@ video {
color: rgb(75 85 99 / var(--tw-text-opacity, 1));
}
.hover\:text-gray-700:hover {
--tw-text-opacity: 1;
color: rgb(55 65 81 / var(--tw-text-opacity, 1));
}
.hover\:text-gray-800:hover {
--tw-text-opacity: 1;
color: rgb(31 41 55 / var(--tw-text-opacity, 1));
}
.hover\:text-red-500:hover {
--tw-text-opacity: 1;
color: rgb(239 68 68 / var(--tw-text-opacity, 1));
@@ -1816,10 +1936,29 @@ video {
cursor: not-allowed;
}
.disabled\:opacity-30:disabled {
opacity: 0.3;
}
.disabled\:opacity-40:disabled {
opacity: 0.4;
}
.dark\:divide-slate-700:is(.dark *) > :not([hidden]) ~ :not([hidden]) {
--tw-divide-opacity: 1;
border-color: rgb(51 65 85 / var(--tw-divide-opacity, 1));
}
.dark\:border-amber-600:is(.dark *) {
--tw-border-opacity: 1;
border-color: rgb(217 119 6 / var(--tw-border-opacity, 1));
}
.dark\:border-red-800:is(.dark *) {
--tw-border-opacity: 1;
border-color: rgb(153 27 27 / var(--tw-border-opacity, 1));
}
.dark\:border-slate-600:is(.dark *) {
--tw-border-opacity: 1;
border-color: rgb(71 85 105 / var(--tw-border-opacity, 1));
@@ -1839,11 +1978,27 @@ video {
border-color: rgb(30 41 59 / var(--tw-border-opacity, 1));
}
.dark\:bg-amber-900\/20:is(.dark *) {
background-color: rgb(120 53 15 / 0.2);
}
.dark\:bg-blue-500:is(.dark *) {
--tw-bg-opacity: 1;
background-color: rgb(59 130 246 / var(--tw-bg-opacity, 1));
}
.dark\:bg-blue-900\/40:is(.dark *) {
background-color: rgb(30 58 138 / 0.4);
}
.dark\:bg-emerald-900\/30:is(.dark *) {
background-color: rgb(6 78 59 / 0.3);
}
.dark\:bg-emerald-900\/40:is(.dark *) {
background-color: rgb(6 78 59 / 0.4);
}
.dark\:bg-red-900\/30:is(.dark *) {
background-color: rgb(127 29 29 / 0.3);
}
@@ -1858,11 +2013,24 @@ video {
background-color: rgb(30 41 59 / var(--tw-bg-opacity, 1));
}
.dark\:bg-slate-800\/40:is(.dark *) {
background-color: rgb(30 41 59 / 0.4);
}
.dark\:bg-slate-800\/60:is(.dark *) {
background-color: rgb(30 41 59 / 0.6);
}
.dark\:bg-slate-900:is(.dark *) {
--tw-bg-opacity: 1;
background-color: rgb(15 23 42 / var(--tw-bg-opacity, 1));
}
.dark\:text-amber-300:is(.dark *) {
--tw-text-opacity: 1;
color: rgb(252 211 77 / var(--tw-text-opacity, 1));
}
.dark\:text-amber-400:is(.dark *) {
--tw-text-opacity: 1;
color: rgb(251 191 36 / var(--tw-text-opacity, 1));
@@ -1873,11 +2041,21 @@ video {
color: rgb(245 158 11 / var(--tw-text-opacity, 1));
}
.dark\:text-blue-300:is(.dark *) {
--tw-text-opacity: 1;
color: rgb(147 197 253 / var(--tw-text-opacity, 1));
}
.dark\:text-blue-400:is(.dark *) {
--tw-text-opacity: 1;
color: rgb(96 165 250 / var(--tw-text-opacity, 1));
}
.dark\:text-emerald-300:is(.dark *) {
--tw-text-opacity: 1;
color: rgb(110 231 183 / var(--tw-text-opacity, 1));
}
.dark\:text-emerald-400:is(.dark *) {
--tw-text-opacity: 1;
color: rgb(52 211 153 / var(--tw-text-opacity, 1));
@@ -1967,6 +2145,14 @@ video {
background-color: rgb(51 65 85 / var(--tw-bg-opacity, 1));
}
.dark\:hover\:bg-slate-700\/40:hover:is(.dark *) {
background-color: rgb(51 65 85 / 0.4);
}
.dark\:hover\:bg-slate-700\/50:hover:is(.dark *) {
background-color: rgb(51 65 85 / 0.5);
}
.dark\:hover\:text-gray-300:hover:is(.dark *) {
--tw-text-opacity: 1;
color: rgb(209 213 219 / var(--tw-text-opacity, 1));
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'simple-v302';
const CACHE = 'simple-v303';
const URLS = [
'index.html',
'help.html',
+3
View File
@@ -203,6 +203,9 @@ test('explorer action buttons share a fixed-width column, Install is green', ()
delete global.t;
// every action cell is the same fixed-width column
assert.ok(out.includes('w-28 shrink-0'), out);
// rows are visible chips (background + hover) so the button belongs to its row
assert.ok(out.includes('bg-gray-50 dark:bg-slate-800/60'), out);
assert.ok(out.includes('hover:bg-gray-100 dark:hover:bg-slate-700/50'), out);
// the Install button (module without an instance) is green, not blue
const inst = /<button onclick="ramInstallFromExplorer[^>]*class="([^"]*)"/.exec(out);
assert.ok(inst, out);