diff options
| author | dax <me@dax.ist> | 2026-08-04 16:54:07 +0100 |
|---|---|---|
| committer | dax <me@dax.ist> | 2026-08-04 16:54:07 +0100 |
| commit | dadf0909dd235a7764a59a63d2b51861fa81e385 (patch) | |
| tree | fad8563b7c2d9f7f9216b001a1f89ba5db5f5479 /public/app.css | |
| parent | 6a0dc37b2e5580987df8fd11becdff53b32c9836 (diff) | |
add photo import: upload -> photoprism, fix desktop file dialog
- POST /api/import with native multipart parser (no new deps); stages files
then delegates indexing+thumbnailing to PhotoPrism via `photoprism cp`
- pathways reflects new photos immediately via refresh()
- upload UI shown only in empty-library state
- nixos: PHOTOPRISM_READONLY=false; pathways service gets STAGING_ROOT and
photoprism env; PATH includes /run/wrappers/bin so sudo is found
- fix: stage.setPointerCapture was swallowing the import label's click on
desktop, preventing the native file dialog from opening
Diffstat (limited to 'public/app.css')
| -rw-r--r-- | public/app.css | 117 |
1 files changed, 109 insertions, 8 deletions
diff --git a/public/app.css b/public/app.css index 8a1b201..24bb927 100644 --- a/public/app.css +++ b/public/app.css @@ -25,6 +25,10 @@ html, body { user-select: none; } +#stage.blocked { + pointer-events: none; +} + #world { position: absolute; left: 0; @@ -128,22 +132,35 @@ html, body { } } -#splash { +#import-state { position: fixed; inset: 0; + z-index: 10; display: grid; place-content: center; justify-items: center; gap: 14px; text-align: center; - background: var(--bg); - z-index: 10; - transition: opacity .4s, visibility .4s; + padding: 24px; + pointer-events: auto; +} +#import-state[hidden] { display: none; } + +#file { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0 0 0 0); + border: 0; + opacity: 0; } -#splash.hide { opacity: 0; visibility: hidden; pointer-events: none; } -#splash h1 { margin: 0; font-size: 44px; font-weight: 300; letter-spacing: .18em; } -#splash h1 .dot { color: var(--ring); } -#splash p { max-width: 34ch; color: var(--muted); margin: 0; font-size: 15px; line-height: 1.6; } +#import-state h1 { margin: 0; font-size: 44px; font-weight: 300; letter-spacing: .18em; } +#import-state h1 .dot { color: var(--ring); } +#import-state p { max-width: 34ch; color: var(--muted); margin: 0; font-size: 15px; line-height: 1.6; } +/* app.css?v=38 */ #start { margin-top: 10px; padding: 10px 34px; @@ -158,7 +175,91 @@ html, body { } #start:hover { border-color: var(--ring); background: rgba(125, 211, 252, .08); } +#import { + margin-top: 14px; + position: relative; + z-index: 11; + display: inline-flex; + align-items: center; + gap: 9px; + padding: 10px 22px; + font-size: 14px; + letter-spacing: .06em; + border: 1px solid rgba(255, 255, 255, .3); + border-radius: 999px; + background: transparent; + color: var(--ink); + cursor: pointer; + opacity: 0; + transform: translateY(6px); + transition: opacity .4s, transform .4s, border-color .15s, background .15s; +} +#import:not([hidden]) { opacity: 1; transform: translateY(0); } +#import svg { + width: 17px; + height: 17px; + fill: none; + stroke: currentColor; + stroke-width: 1.7; + stroke-linecap: round; + stroke-linejoin: round; +} +#import:hover { border-color: var(--ring); background: rgba(125, 211, 252, .08); } +#import.busy { + border-color: var(--ring); + color: var(--ring); + pointer-events: none; + animation: pulse 1.1s ease-in-out infinite; +} +#import[hidden] { display: none; } + +@keyframes pulse { + 0%, 100% { opacity: .55; } + 50% { opacity: 1; } +} + +#drop { + position: fixed; + inset: 0; + z-index: 9; + display: grid; + place-content: center; + background: rgba(14, 15, 19, .82); + border: 2px dashed rgba(125, 211, 252, .55); + color: var(--ink); + font-size: 16px; + letter-spacing: .1em; + opacity: 0; + visibility: hidden; + pointer-events: none; + transition: opacity .15s, visibility .15s; +} +#drop.show { opacity: 1; visibility: visible; pointer-events: auto; } + +#toast { + position: fixed; + right: 18px; + bottom: 72px; + z-index: 8; + max-width: 46ch; + padding: 9px 15px; + border: 1px solid rgba(255, 255, 255, .18); + border-radius: 8px; + background: rgba(14, 15, 19, .94); + color: var(--ink); + font-size: 13px; + line-height: 1.5; + opacity: 0; + visibility: hidden; + pointer-events: none; + transform: translateY(4px); + transition: opacity .2s, visibility .2s, transform .2s; +} +#toast.show { opacity: 1; visibility: visible; pointer-events: auto; } +#toast.err { border-color: rgba(248, 113, 113, .6); color: #fca5a5; } + @media (prefers-reduced-motion: reduce) { #world { transition: none; } .tile.lock { animation: none; } + #upload.busy { animation: none; } } |
