feat(ui): limit visible reactions and overhaul retro theme

- Limit the number of initially visible reactions per file to 2 and calculate the overflow count on the backend.
- Redesign the retro theme CSS to mimic a classic Windows 98 Explorer window, including title bars, toolbars, and sunken panes.
- Add local storage persistence for the file browser view preference (list vs. thumbnails).
This commit is contained in:
2026-06-02 14:43:16 +03:00
parent 8e3f783780
commit cf5d8bb50d
7 changed files with 740 additions and 110 deletions

View File

@@ -46,12 +46,22 @@
text-decoration: none;
}
.view-toolbar {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 1rem;
.thumb-link {
flex: 0 0 4.75rem;
width: 4.75rem;
aspect-ratio: 16 / 10;
display: block;
overflow: hidden;
border: 1px solid var(--border);
border-radius: calc(var(--radius) - 0.2rem);
background: var(--muted);
}
.thumb-link img {
width: 100%;
height: 100%;
display: block;
object-fit: cover;
}
.button.is-active {
@@ -59,26 +69,148 @@
color: var(--primary-foreground);
}
.file-browser-window {
overflow: hidden;
margin-top: 1.25rem;
border: 1px solid color-mix(in srgb, var(--border) 78%, var(--primary));
border-radius: var(--radius);
background:
linear-gradient(180deg, color-mix(in srgb, var(--card) 94%, transparent), color-mix(in srgb, var(--background) 92%, transparent));
box-shadow: 0 18px 54px rgba(0, 0, 0, 0.24);
text-align: left;
}
.file-browser-titlebar {
min-height: 3rem;
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
padding: 0.75rem 0.9rem;
border-bottom: 1px solid var(--border);
background: color-mix(in srgb, var(--muted) 62%, transparent);
}
.file-browser-titlebar > div:first-child {
min-width: 0;
display: flex;
align-items: baseline;
gap: 0.6rem;
}
.file-browser-titlebar strong {
font-size: 0.95rem;
}
.file-browser-titlebar span {
color: var(--muted-foreground);
font-size: 0.78rem;
white-space: nowrap;
}
.file-browser-window-actions {
display: inline-flex;
gap: 0.35rem;
}
.file-browser-window-actions span {
width: 0.72rem;
height: 0.72rem;
border: 1px solid color-mix(in srgb, var(--border) 75%, var(--foreground));
border-radius: 999px;
background: var(--muted);
}
.file-browser-toolbar {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
padding: 0.65rem 0.75rem;
border-bottom: 1px solid var(--border);
background: color-mix(in srgb, var(--card) 74%, transparent);
}
.view-toolbar {
display: inline-flex;
flex-wrap: wrap;
gap: 0.4rem;
}
.view-toolbar .button,
.file-browser-toolbar > .button {
min-height: 2rem;
padding: 0.35rem 0.65rem;
font-size: 0.78rem;
}
.view-toolbar .icon-button {
width: 2.25rem;
padding-inline: 0;
justify-content: center;
}
.view-toolbar svg {
width: 0.95rem;
height: 0.95rem;
}
.file-browser-head {
display: grid;
grid-template-columns: 3rem minmax(0, 1fr) minmax(8rem, 0.38fr) minmax(5rem, 0.18fr) minmax(8rem, 0.32fr);
gap: 0.75rem;
padding: 0.42rem 1rem;
border-bottom: 1px solid var(--border);
color: var(--muted-foreground);
background: color-mix(in srgb, var(--background) 78%, transparent);
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
}
.file-browser-head span:first-child {
grid-column: 2;
}
.file-browser {
display: grid;
gap: 0;
padding: 0.35rem;
transition: opacity 160ms ease;
}
.file-browser .download-item {
display: grid;
min-width: 0;
border: 0;
border-radius: calc(var(--radius) - 0.25rem);
background: transparent;
box-shadow: none;
padding: 0;
transform: none;
}
.file-browser .download-item:hover {
transform: none;
}
.file-card {
position: relative;
padding-block: 0.65rem 2.6rem;
padding: 0;
}
.file-reaction-dock {
position: absolute;
right: 0.65rem;
bottom: 0.55rem;
position: static;
z-index: 2;
display: inline-flex;
align-items: center;
justify-content: flex-end;
max-width: calc(100% - 1.3rem);
min-width: 0;
max-width: 100%;
gap: 0.35rem;
pointer-events: none;
padding-right: 0.65rem;
}
.file-reactions {
@@ -87,10 +219,13 @@
justify-content: flex-end;
min-width: 0;
gap: 0.25rem;
flex-wrap: wrap;
flex-wrap: nowrap;
white-space: nowrap;
}
.reaction-pill {
appearance: none;
flex: 0 0 auto;
display: inline-flex;
align-items: center;
gap: 0.2rem;
@@ -104,6 +239,11 @@
font-weight: 700;
box-shadow: 0 8px 22px rgba(0, 0, 0, 0.24);
pointer-events: auto;
cursor: pointer;
}
.reaction-pill.is-hidden-summary {
display: none;
}
.reaction-pill img {
@@ -112,6 +252,31 @@
display: block;
}
.reaction-more {
appearance: none;
flex: 0 0 auto;
min-height: 1.6rem;
padding: 0.16rem 0.45rem;
border: 1px solid color-mix(in srgb, var(--border) 84%, var(--primary));
border-radius: 999px;
background: color-mix(in srgb, var(--card) 88%, #000);
color: var(--foreground);
font-size: 0.75rem;
font-weight: 800;
box-shadow: 0 8px 22px rgba(0, 0, 0, 0.24);
pointer-events: auto;
cursor: pointer;
}
.reaction-pill:hover,
.reaction-pill:focus-visible,
.reaction-more:hover,
.reaction-more:focus-visible {
border-color: var(--primary);
background: var(--primary);
color: var(--primary-foreground);
}
.reaction-button {
width: 2.1rem;
height: 2.1rem;
@@ -212,6 +377,30 @@ html.reaction-picker-open body {
font-size: 0.75rem;
}
.reaction-existing {
padding: 0.55rem 0.7rem 0;
}
.reaction-existing small,
.reaction-readonly-note {
display: block;
color: var(--muted-foreground);
font-size: 0.74rem;
font-weight: 700;
}
.reaction-existing-list {
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
margin-top: 0.4rem;
}
.reaction-readonly-note {
margin: 0;
padding: 0.55rem 0.7rem 0.7rem;
}
.reaction-picker-tabs {
display: flex;
gap: 0.35rem;
@@ -309,11 +498,19 @@ html.reaction-picker-open body {
.file-open {
min-width: 0;
flex: 1;
display: flex;
display: grid;
grid-template-columns: 3rem minmax(0, 1fr) minmax(8rem, 0.38fr) minmax(5rem, 0.18fr);
align-items: center;
gap: 0.8rem;
gap: 0.75rem;
color: var(--foreground);
text-decoration: none;
padding: 0.55rem 0.65rem;
border-radius: calc(var(--radius) - 0.25rem);
}
.file-open:hover,
.file-open:focus-visible {
background: var(--surface-1-hover);
}
.file-media {
@@ -360,11 +557,14 @@ html.reaction-picker-open body {
.file-main {
min-width: 0;
max-width: 100%;
flex: 1;
color: var(--foreground);
text-decoration: none;
}
.file-name {
min-width: 0;
}
.file-main small {
display: block;
overflow: hidden;
@@ -372,44 +572,101 @@ html.reaction-picker-open body {
white-space: nowrap;
}
.file-type,
.file-size {
overflow: hidden;
color: var(--muted-foreground);
font-size: 0.78rem;
text-overflow: ellipsis;
white-space: nowrap;
}
.file-size {
text-align: right;
}
.file-browser.is-thumbs {
grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
gap: 0.75rem;
padding: 0.75rem;
grid-template-columns: repeat(auto-fill, minmax(8.75rem, 1fr));
}
.file-browser-window.is-icon-view .file-browser-head {
display: none;
}
.file-browser.is-thumbs .file-card {
display: grid;
min-height: 13.75rem;
min-width: 0;
align-content: start;
gap: 0.5rem;
}
.file-browser.is-list .file-card {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(8rem, 0.32fr);
align-items: center;
min-height: 4.25rem;
}
.file-browser.is-list .file-card:hover,
.file-browser.is-list .file-card:focus-within {
background: var(--surface-1-hover);
}
.file-browser.is-list .file-card:hover .file-open,
.file-browser.is-list .file-card:focus-within .file-open {
background: transparent;
}
.file-browser.is-thumbs .file-open {
display: grid;
gap: 0.55rem;
grid-template-columns: 1fr;
grid-template-rows: 6.75rem auto;
gap: 1rem;
height: 100%;
min-height: 0;
padding: 0.65rem 0.65rem 3.05rem;
text-align: center;
justify-items: center;
align-content: start;
overflow: hidden;
}
.file-browser.is-thumbs .file-media {
width: 100%;
height: auto;
width: min(6.75rem, 76%);
height: 6.75rem;
flex-basis: auto;
aspect-ratio: 16 / 10;
aspect-ratio: 1;
}
.file-browser.is-thumbs .file-icon {
width: 45%;
height: auto;
width: 64%;
height: 64%;
}
.file-browser.is-thumbs .file-main {
width: 100%;
grid-template-columns: 1fr;
gap: 0.25rem;
align-self: start;
padding-top: 0.25rem;
}
.file-browser.images-only .file-card:not([data-kind="image"]) {
.file-browser.is-thumbs .file-type,
.file-browser.is-thumbs .file-size {
display: none;
}
.file-browser.is-thumbs .file-reaction-dock {
position: absolute;
right: 0.6rem;
bottom: 0.65rem;
max-width: calc(100% - 1.2rem);
padding-right: 0;
}
.context-menu {
position: fixed;
z-index: 30;