Files
2026-10-13 22:27:11 -06:00

823 lines
51 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Studio</title>
<style>
:root {
--bg: #25272a;
--sidebar: #202225;
--toolbar: #2b2d31;
--panel: #2d3034;
--panel-2: #34373c;
--panel-3: #3a3e43;
--canvas: #1b1d20;
--text: #f1f2f4;
--text-2: #c8ccd1;
--muted: #858b94;
--faint: #60666e;
--line: #3b3e43;
--line-2: #4a4e54;
--accent: #7d9cff;
--accent-rgb: 125, 156, 255;
--danger: #eb7079;
--success: #63c69d;
--warning: #d8ad5c;
--sidebar-width: 228px;
--thumb-size: 220px;
color-scheme: dark;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { width: 100%; min-height: 100%; overflow-x: hidden; }
body {
min-height: 100vh;
overflow-x: hidden;
background: var(--bg);
color: var(--text);
font: 13px/1.4 "Segoe UI Variable", "Segoe UI", Inter, system-ui, sans-serif;
font-feature-settings: "cv01", "ss03";
}
button, input, select { font: inherit; }
button, select { cursor: pointer; }
button:focus-visible, input:focus-visible, select:focus-visible, summary:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
* { scrollbar-width: thin; scrollbar-color: #555a61 transparent; }
::selection { background: rgba(var(--accent-rgb), .35); }
/* Studio is an Explore surface: fixed navigation, compact tools, image-first workspace. */
.studio-shell {
min-height: 100vh;
display: grid;
grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
}
.studio-shell.sidebar-collapsed { --sidebar-width: 64px; }
.studio-sidebar {
position: fixed;
top: 0;
left: 0;
width: var(--sidebar-width);
height: 100vh;
z-index: 120;
display: flex;
flex-direction: column;
overflow: hidden;
border-right: 1px solid var(--line);
background: var(--sidebar);
}
.sidebar-brand {
height: 52px;
flex: 0 0 52px;
display: flex;
align-items: center;
gap: 10px;
padding: 0 12px;
border-bottom: 1px solid var(--line);
}
.studio-mark {
width: 28px;
height: 28px;
flex: 0 0 28px;
display: grid;
place-items: center;
border: 1px solid var(--line-2);
border-radius: 7px;
background: var(--panel-2);
color: var(--accent);
font-size: 12px;
font-weight: 700;
}
.studio-wordmark { min-width: 0; flex: 1; }
.studio-wordmark strong { display: block; font-size: 14px; font-weight: 620; letter-spacing: -.2px; }
.studio-wordmark span { display: block; margin-top: 1px; color: var(--muted); font-size: 9px; letter-spacing: .08em; text-transform: uppercase; }
.sidebar-collapse {
width: 26px;
height: 26px;
min-height: 0;
padding: 0;
border: 0;
border-radius: 5px;
background: transparent;
color: var(--muted);
}
.sidebar-collapse:hover { background: var(--panel-2); color: var(--text); }
.sidebar-scroll { min-width: 0; flex: 1; overflow-x: hidden; overflow-y: auto; padding: 10px 8px 16px; }
.sidebar-section { margin-bottom: 16px; }
.sidebar-label {
padding: 0 8px 6px;
color: var(--faint);
font-size: 9px;
font-weight: 650;
letter-spacing: .12em;
text-transform: uppercase;
}
.sidebar-action, .sidebar-nav-item, #folderBtn {
width: 100%;
min-height: 32px;
display: flex;
align-items: center;
justify-content: flex-start;
gap: 9px;
padding: 0 9px;
border: 1px solid transparent;
border-radius: 6px;
background: transparent;
color: var(--text-2);
font-size: 11.5px;
text-align: left;
}
.sidebar-action:hover, .sidebar-nav-item:hover, #folderBtn:hover { background: var(--panel); color: var(--text); }
.sidebar-action.active, .sidebar-nav-item.active, #folderBtn.active {
border-color: rgba(var(--accent-rgb), .22);
background: rgba(var(--accent-rgb), .12);
color: #cbd6ff;
}
.ui-icon {
width: 16px;
height: 16px;
flex: 0 0 16px;
display: inline-flex;
align-items: center;
justify-content: center;
color: currentColor;
font-size: 11px;
}
.folder-manager { position: static; }
.folder-dropdown {
position: static;
display: none;
width: 100%;
min-width: 0;
margin: 5px 0 0;
padding: 0;
border: 0;
background: transparent;
box-shadow: none;
}
.folder-dropdown.active { display: block; }
.folder-list { max-height: none; margin: 0; overflow: visible; }
.folder-item {
min-height: 31px;
display: flex;
align-items: center;
gap: 5px;
margin: 1px 0;
padding: 0 4px 0 9px;
border: 1px solid transparent;
border-radius: 6px;
color: var(--text-2);
}
.folder-item::before { content: "▱"; width: 16px; color: var(--muted); font-size: 10px; }
.folder-item:hover { background: var(--panel); color: var(--text); }
.folder-item.active { border-color: rgba(var(--accent-rgb), .20); background: rgba(var(--accent-rgb), .12); color: #d4dcff; }
.folder-item-content { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11.5px; cursor: pointer; }
.folder-add-disclosure { margin-top: 8px; border-top: 1px solid var(--line); padding-top: 8px; }
.folder-add-disclosure summary { padding: 6px 8px; border-radius: 5px; color: var(--muted); font-size: 10.5px; cursor: pointer; list-style: none; }
.folder-add-disclosure summary:hover { background: var(--panel); color: var(--text-2); }
.folder-add-disclosure summary::-webkit-details-marker { display: none; }
.folder-add-form { padding: 7px 5px 2px; }
.folder-add-form input {
width: 100%; height: 31px; margin-bottom: 6px; padding: 0 8px;
border: 1px solid var(--line); border-radius: 6px; outline: 0;
background: var(--canvas); color: var(--text);
}
#addFolderBtn { width: 100%; height: 31px; border: 1px solid var(--line-2); border-radius: 6px; background: var(--panel-2); color: var(--text-2); }
.studio-shell.sidebar-collapsed .studio-wordmark,
.studio-shell.sidebar-collapsed .sidebar-label,
.studio-shell.sidebar-collapsed .sidebar-action span:not(.ui-icon),
.studio-shell.sidebar-collapsed #folderBtn span:not(.ui-icon),
.studio-shell.sidebar-collapsed .folder-item-content,
.studio-shell.sidebar-collapsed .folder-add-form { display: none !important; }
.studio-shell.sidebar-collapsed .sidebar-brand { gap: 3px; padding: 0 4px; }
.studio-shell.sidebar-collapsed .studio-mark { width: 28px; flex-basis: 28px; }
.studio-shell.sidebar-collapsed .sidebar-collapse { width: 24px; flex: 0 0 24px; }
.studio-shell.sidebar-collapsed .sidebar-scroll { padding-inline: 9px; }
.studio-shell.sidebar-collapsed .sidebar-action,
.studio-shell.sidebar-collapsed #folderBtn { justify-content: center; padding: 0; }
.studio-shell.sidebar-collapsed .folder-dropdown.active { display: block; margin-top: 4px; }
.studio-shell.sidebar-collapsed .folder-item { justify-content: center; min-height: 34px; padding: 0; }
.studio-shell.sidebar-collapsed .folder-item::before { width: 16px; text-align: center; }
.studio-shell.sidebar-collapsed .folder-add-disclosure { padding-top: 6px; }
.studio-shell.sidebar-collapsed .folder-add-disclosure summary {
min-height: 34px; display: grid; place-items: center; padding: 0; font-size: 0;
}
.studio-shell.sidebar-collapsed .folder-add-disclosure summary::before { content: "+"; font-size: 16px; }
.library-main { grid-column: 2; min-width: 0; min-height: 100vh; padding-top: 52px; background: var(--bg); }
.library-toolbar {
position: fixed;
top: 0;
right: 0;
left: var(--sidebar-width);
z-index: 100;
min-height: 52px;
display: flex;
align-items: center;
gap: 8px;
padding: 7px 12px;
border-bottom: 1px solid var(--line);
background: rgba(43,45,49,.97);
}
.location-block { min-width: 0; flex: 0 0 180px; max-width: 180px; margin-right: auto; }
.location-block strong { display: block; overflow: hidden; color: var(--text); font-size: 12px; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
.location-block span { display: block; margin-top: 1px; overflow: hidden; color: var(--muted); font-size: 9.5px; text-overflow: ellipsis; white-space: nowrap; }
.toolbar-controls { min-width: 0; display: flex; align-items: center; justify-content: flex-end; gap: 5px; flex-wrap: nowrap; }
.search-container {
width: 210px;
min-width: 150px;
height: 34px;
display: flex;
align-items: center;
gap: 6px;
padding: 0 9px;
border: 1px solid var(--line);
border-radius: 7px;
background: var(--canvas);
}
.search-container:focus-within { border-color: rgba(var(--accent-rgb), .65); box-shadow: 0 0 0 2px rgba(var(--accent-rgb), .10); }
.search-container input { width: 100%; min-width: 0; border: 0; outline: 0; background: transparent; color: var(--text); font-size: 11.5px; }
.search-container input::placeholder { color: var(--faint); }
.toolbar-button, .toolbar-controls button, .toolbar-controls select {
height: 34px;
min-height: 34px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 5px;
padding: 0 9px;
border: 1px solid var(--line);
border-radius: 7px;
background: var(--panel);
color: var(--text-2);
font-size: 10.5px;
}
.toolbar-controls button:hover, .toolbar-controls select:hover { border-color: var(--line-2); background: var(--panel-2); color: var(--text); }
.toolbar-controls button.active { border-color: rgba(var(--accent-rgb), .50); background: rgba(var(--accent-rgb), .13); color: #c5d2ff; }
.toolbar-controls select option { background: var(--panel); color: var(--text); }
#compareBtn { display: none; border-color: rgba(99,198,157,.45); background: rgba(99,198,157,.13); color: #a9e5cb; }
.thumb-control { height: 34px; display: flex; align-items: center; gap: 6px; padding: 0 8px; border: 1px solid var(--line); border-radius: 7px; background: var(--panel); color: var(--muted); }
.thumb-control input { width: 58px; accent-color: var(--accent); }
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(var(--thumb-size), 1fr));
gap: 8px;
padding: 10px;
transform: translateZ(0);
}
.image-card {
position: relative;
min-width: 0;
overflow: hidden;
border: 1px solid var(--line);
border-radius: 7px;
background: var(--panel);
cursor: pointer;
transform: translateZ(0);
transition: border-color .12s, background .12s, transform .12s;
}
.image-card:hover { border-color: var(--line-2); background: var(--panel-2); transform: translateY(-1px); }
.image-card img, .image-card video, .audio-thumbnail {
width: 100%;
height: var(--thumb-size);
display: block;
object-fit: cover;
background: var(--canvas);
}
.audio-thumbnail { display: flex; flex-direction: column; align-items: center; justify-content: center; }
.audio-thumbnail .audio-icon { color: var(--muted); font-size: 36px; }
.audio-thumbnail audio { display: none; }
.image-info { min-height: 42px; padding: 7px 9px; border-top: 1px solid var(--line); background: var(--panel); }
.image-name { overflow: hidden; color: var(--text-2); font-size: 10.5px; font-weight: 510; text-overflow: ellipsis; white-space: nowrap; }
.favorite-btn {
position: absolute; top: 7px; left: 7px; z-index: 10;
width: 25px; height: 25px; min-height: 0; padding: 0;
border: 1px solid rgba(255,255,255,.18); border-radius: 6px;
background: rgba(29,31,34,.78); color: #eef0f3; opacity: .25;
}
.image-card:hover .favorite-btn, .favorite-btn.favorited { opacity: 1; }
.favorite-btn.favorited { border-color: #d8ad5c; background: #d8ad5c; color: #201a0e; }
.video-indicator, .audio-indicator {
position: absolute; top: 7px; right: 7px; z-index: 5;
padding: 4px 6px; border: 1px solid rgba(255,255,255,.14); border-radius: 5px;
background: rgba(29,31,34,.80); color: #eff1f4; font-size: 8px; letter-spacing: .08em; text-transform: uppercase;
}
.video-indicator::before { content: "▶ "; }
.audio-indicator::before { content: "♪ "; }
.image-card.selected { border: 2px solid var(--accent); background: rgba(var(--accent-rgb), .08); box-shadow: 0 0 0 2px rgba(var(--accent-rgb), .17); }
.image-card.selected::after {
content: "✓";
position: absolute; top: 7px; right: 7px; z-index: 12;
width: 23px; height: 23px; display: grid; place-items: center;
border: 2px solid var(--panel); border-radius: 50%; background: var(--accent); color: #17191d; font-size: 12px; font-weight: 800;
}
.image-card.selected .video-indicator, .image-card.selected .audio-indicator { right: 37px; }
.image-card.compare-first { border: 2px solid var(--success); box-shadow: 0 0 0 2px rgba(99,198,157,.15); }
.image-card.compare-second { border: 2px solid var(--danger); box-shadow: 0 0 0 2px rgba(235,112,121,.15); }
.loading { padding: 22px; color: var(--muted); text-align: center; }
.multi-select-actions {
position: fixed; left: calc(var(--sidebar-width) + (100vw - var(--sidebar-width)) / 2); bottom: 18px; z-index: 300;
transform: translateX(-50%);
display: none; align-items: center; gap: 4px;
padding: 6px; border: 1px solid var(--line-2); border-radius: 9px;
background: #303338; box-shadow: 0 12px 28px rgba(0,0,0,.30);
}
.multi-select-actions .selected-info { padding: 0 8px; color: var(--text-2); font-size: 11px; font-weight: 600; }
.multi-select-actions button { height: 30px; min-height: 30px; padding: 0 8px; border: 1px solid var(--line); border-radius: 6px; background: var(--panel); color: var(--text-2); font-size: 10px; }
#deleteSelectedBtn { color: #ffabb1; }
.context-menu, .notification {
position: absolute; z-index: 10000; min-width: 190px; display: none;
padding: 5px; border: 1px solid var(--line-2); border-radius: 8px;
background: #303338; color: var(--text-2); box-shadow: 0 12px 30px rgba(0,0,0,.32);
}
.context-menu-item { display: flex; align-items: center; gap: 7px; padding: 7px 8px; border-radius: 5px; font-size: 11px; cursor: pointer; }
.context-menu-item:hover { background: rgba(var(--accent-rgb), .12); color: #d0d9ff; }
.context-menu-divider { height: 1px; margin: 4px; background: var(--line); }
.context-menu-item-danger:hover { background: rgba(235,112,121,.10); color: #ffabb1; }
.notification { position: fixed; top: 14px; right: 14px; display: block; padding: 9px 12px; transform: translateX(340px); transition: transform .18s; }
.notification.show { transform: translateX(0); }
.notification-success { border-color: rgba(99,198,157,.5); }
.notification-error { border-color: rgba(235,112,121,.6); }
.modal {
position: fixed; inset: 0; z-index: 1000; display: none;
grid-template-rows: 46px minmax(0, 1fr);
background: var(--canvas);
}
.modal.active { display: grid; }
.viewer-toolbar {
min-width: 0;
display: flex; align-items: center; gap: 8px;
padding: 6px 10px; border-bottom: 1px solid var(--line); background: var(--toolbar);
}
.viewer-back, .viewer-toolbar button {
height: 32px; min-height: 32px; padding: 0 9px;
border: 1px solid var(--line); border-radius: 6px; background: var(--panel); color: var(--text-2); font-size: 10.5px;
}
.viewer-back:hover, .viewer-toolbar button:hover { background: var(--panel-2); color: var(--text); }
.viewer-identity { min-width: 0; flex: 1; }
.viewer-identity strong { display: block; overflow: hidden; font-size: 11.5px; font-weight: 590; text-overflow: ellipsis; white-space: nowrap; }
.viewer-identity span { display: block; margin-top: 1px; color: var(--muted); font-size: 9px; }
.modal-close {
position: static; width: 32px; height: 32px; display: grid; place-items: center;
border: 1px solid var(--line); border-radius: 6px; background: var(--panel); color: var(--muted); font-size: 18px; cursor: pointer;
}
.modal-close:hover { background: var(--panel-2); color: var(--text); }
.modal-content { min-width: 0; min-height: 0; display: grid; grid-template-columns: minmax(0, 1fr) 370px; }
.viewer-stage { min-width: 0; min-height: 0; display: grid; grid-template-rows: minmax(0, 1fr) 86px; background: var(--canvas); }
.modal-left { min-width: 0; min-height: 0; position: relative; display: flex; align-items: center; justify-content: center; overflow: hidden; padding: 14px; }
.modal-left img, .modal-left video { max-width: 100%; max-height: 100%; display: block; object-fit: contain; }
.modal-zoom-stage { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; overflow: hidden; }
.modal-zoom-stage img { transform-origin: center; transition: transform .10s; user-select: none; -webkit-user-drag: none; }
.modal-zoom-controls {
position: absolute; top: 10px; left: 10px; z-index: 20;
display: flex; align-items: center; gap: 3px; padding: 3px;
border: 1px solid var(--line-2); border-radius: 7px; background: rgba(45,48,52,.92);
}
.modal-zoom-btn, .compare-zoom-btn { width: 28px; height: 28px; min-height: 28px; padding: 0; border: 1px solid var(--line); border-radius: 5px; background: var(--panel); color: var(--text-2); }
.modal-zoom-btn-reset, .compare-zoom-reset { width: 58px; }
.modal-nav {
position: absolute; top: 50%; z-index: 1010; transform: translateY(-50%);
width: 34px; height: 44px; min-height: 44px; padding: 0;
border: 1px solid var(--line); border-radius: 6px; background: rgba(45,48,52,.90); color: var(--muted);
}
.modal-nav:hover { border-color: var(--line-2); color: var(--text); }
.modal-nav-prev { left: 10px; }
.modal-nav-next { right: 380px; }
.modal-filmstrip {
min-width: 0; display: flex; align-items: center; gap: 6px;
padding: 7px 10px; border-top: 1px solid var(--line); background: #222428;
}
.filmstrip-track { min-width: 0; flex: 1; display: flex; gap: 5px; overflow-x: auto; padding: 1px; }
.filmstrip-thumb { width: 66px; height: 66px; flex: 0 0 66px; padding: 0; overflow: hidden; border: 1px solid var(--line); border-radius: 5px; background: var(--panel); }
.filmstrip-thumb img, .filmstrip-thumb video { width: 100%; height: 100%; object-fit: cover; }
.filmstrip-thumb.active { border: 2px solid var(--accent); box-shadow: 0 0 0 2px rgba(var(--accent-rgb),.14); }
.filmstrip-count { flex: 0 0 auto; padding: 0 4px; color: var(--muted); font-size: 10px; font-variant-numeric: tabular-nums; }
.modal-right { min-width: 0; min-height: 0; height: 100%; display: flex; flex-direction: column; overflow-y: auto; border-left: 1px solid var(--line); background: var(--panel); }
.modal-header-actions { padding: 10px; border-bottom: 1px solid var(--line); }
.modal-action-btn { width: 100%; height: 32px; border: 1px solid var(--line); border-radius: 6px; background: var(--panel-2); color: var(--text-2); font-size: 10.5px; }
.modal-action-btn.favorite { color: #efd18e; }
.modal-view-toggle { display: grid; grid-template-columns: 1fr 1fr; gap: 3px; margin-top: 7px; padding: 3px; border: 1px solid var(--line); border-radius: 7px; background: var(--canvas); }
.modal-view-btn { height: 29px; border: 0; border-radius: 5px; background: transparent; color: var(--muted); font-size: 10px; }
.modal-view-btn.active { background: var(--panel-3); color: var(--text); }
.metadata-section { border-bottom: 1px solid var(--line); }
.metadata-section-header { display: flex; align-items: center; gap: 6px; padding: 12px 12px 7px; color: var(--muted); font-size: 9px; font-weight: 650; letter-spacing: .10em; text-transform: uppercase; }
.metadata-section-header .icon { color: var(--accent); }
.metadata-section-content { padding: 0 12px 12px; }
.metadata-item { margin-bottom: 7px; }
.metadata-label-row { display: flex; align-items: center; justify-content: space-between; gap: 6px; margin-bottom: 5px; }
.metadata-label { color: var(--muted); font-size: 8.5px; font-weight: 620; letter-spacing: .08em; text-transform: uppercase; }
.metadata-value, .file-info-item .value { color: var(--text-2); font-size: 10.5px; word-break: break-word; }
.metadata-prompt, .metadata-kv, .lora-item, .file-info-item, .settings-grid .metadata-item {
padding: 8px; border: 1px solid var(--line); border-radius: 6px; background: var(--panel-2); color: var(--text-2);
}
.metadata-prompt { max-height: 240px; overflow: auto; font-size: 10.5px; line-height: 1.55; white-space: pre-wrap; }
.settings-grid, .file-info-grid, .metadata-extra-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 5px; }
.settings-grid .full-width, .file-info-grid .full-width { grid-column: 1 / -1; }
.metadata-copy-btn { height: 22px; min-height: 22px; padding: 0 6px; border: 1px solid var(--line); border-radius: 4px; background: var(--panel); color: var(--muted); font-size: 8px; text-transform: uppercase; }
.metadata-copy-btn:hover { border-color: rgba(var(--accent-rgb),.45); color: #cbd6ff; }
.lora-name { color: #c4d0ff; font-size: 10.5px; font-weight: 600; }
.lora-strength { margin-top: 2px; color: var(--muted); font-size: 9px; }
.modal-delete-btn { width: 100%; min-height: 38px; margin-top: auto; border: 0; border-radius: 0; background: rgba(235,112,121,.09); color: #ffabb1; font-size: 10.5px; }
.workflow-search { position: sticky; top: 0; z-index: 2; padding-bottom: 7px; background: var(--panel); }
.workflow-search input { width: 100%; height: 32px; padding: 0 8px; border: 1px solid var(--line); border-radius: 6px; outline: 0; background: var(--canvas); color: var(--text); font-size: 10.5px; }
.workflow-node-item { margin-bottom: 5px; overflow: hidden; border: 1px solid var(--line); border-radius: 6px; background: var(--panel-2); }
.workflow-node-item.muted { opacity: .55; }
.workflow-node-header { width: 100%; min-height: 46px; display: flex; align-items: center; gap: 8px; padding: 6px 8px; list-style: none; cursor: pointer; }
.workflow-node-header::-webkit-details-marker { display: none; }
.workflow-node-header:hover { background: var(--panel-3); }
.workflow-node-index { width: 23px; height: 23px; flex: 0 0 23px; display: grid; place-items: center; border: 1px solid rgba(var(--accent-rgb),.25); border-radius: 5px; background: rgba(var(--accent-rgb),.10); color: #bdcaff; font-size: 8px; }
.workflow-node-title { min-width: 0; flex: 1; }
.workflow-node-title strong { display: block; overflow: hidden; color: var(--text-2); font-size: 10px; text-overflow: ellipsis; white-space: nowrap; }
.workflow-node-title small { display: block; margin-top: 2px; overflow: hidden; color: var(--faint); font-size: 7.5px; letter-spacing: .04em; text-overflow: ellipsis; text-transform: uppercase; white-space: nowrap; }
.workflow-node-chevron { color: var(--faint); transition: transform .12s; }
.workflow-node-item[open] .workflow-node-chevron { transform: rotate(90deg); }
.workflow-node-params { display: none; list-style: none; padding: 4px 8px 8px; border-top: 1px solid var(--line); }
.workflow-node-item[open] .workflow-node-params { display: block; }
.workflow-node-params li { min-height: 25px; display: grid; grid-template-columns: minmax(70px,.8fr) minmax(0,1.2fr); gap: 7px; padding: 5px 0; border-bottom: 1px solid var(--line); }
.workflow-node-params li:last-child { border: 0; }
.workflow-param-name { overflow: hidden; color: var(--muted); font-size: 8.5px; text-overflow: ellipsis; white-space: nowrap; }
.workflow-param-value { color: var(--text-2); font-size: 9px; overflow-wrap: anywhere; white-space: pre-wrap; }
.workflow-empty { color: var(--muted); font-size: 10.5px; }
#compareModal { grid-template-rows: 46px minmax(0,1fr); }
#compareContainer { min-height: 0; overflow: hidden; }
.compare-wrapper { width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; padding: 10px; }
.compare-labels { width: min(92vw, 1400px); display: flex; justify-content: space-between; gap: 10px; }
.compare-label { max-width: 48%; overflow: hidden; padding: 5px 8px; border: 1px solid var(--line); border-radius: 5px; background: var(--panel); color: var(--text-2); font-size: 10px; text-overflow: ellipsis; white-space: nowrap; }
.compare-label-left { border-color: rgba(99,198,157,.45); }
.compare-label-right { border-color: rgba(235,112,121,.45); }
.compare-controls { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 8px; flex-shrink: 0; }
.compare-zoom-controls, .compare-mode-controls { display: flex; flex-wrap: wrap; gap: 3px; padding: 3px; border: 1px solid var(--line); border-radius: 7px; background: var(--toolbar); }
.compare-mode-controls button { padding: 5px 9px; border: 1px solid var(--line); border-radius: 5px; background: var(--panel); color: var(--text-2); font-size: 11px; }
.compare-mode-controls button.active { background: var(--accent); color: var(--bg); border-color: var(--accent); }
.compare-overlay-control { display: flex; align-items: center; gap: 6px; color: var(--text-2); font-size: 11px; }
.compare-overlay-control[hidden] { display: none; }
.compare-overlay-control input { width: 110px; }
.compare-hint { flex-shrink: 0; color: var(--text-2); font-size: 11px; text-align: center; }
.compare-image-container { position: relative; flex: 1; min-height: 0; width: min(92vw, 1400px); overflow: hidden; user-select: none; touch-action: none; isolation: isolate; border: 1px solid var(--line); background: var(--canvas); }
.compare-img { position: absolute; left: 0; top: 0; display: block; max-width: none; max-height: none; object-fit: contain; transform-origin: center; pointer-events: none; }
.compare-img-back-wrapper, .compare-img-front-wrapper { position: absolute; inset: 0 auto 0 0; width: 100%; overflow: hidden; }
.compare-img-front-wrapper { width: 50%; }
.compare-image-container[data-compare-mode="side-by-side"] .compare-img-back-wrapper { left: 50%; width: 50%; }
.compare-image-container[data-compare-mode="difference"] .compare-img-front-wrapper { mix-blend-mode: difference; }
.compare-image-container[data-compare-mode="side-by-side"]::after { content: ""; position: absolute; top: 0; bottom: 0; left: 50%; width: 1px; background: var(--line); pointer-events: none; }
.compare-slider { position: absolute; top: 0; bottom: 0; left: 50%; z-index: 10; width: 2px; transform: translateX(-50%); background: var(--accent); cursor: ew-resize; }
.compare-slider-handle { position: absolute; top: 50%; left: 50%; width: 30px; height: 30px; transform: translate(-50%,-50%); border: 2px solid #fff; border-radius: 50%; background: var(--accent); }
.audio-modal-container { min-width: min(420px,80vw); padding: 24px; border: 1px solid var(--line); border-radius: 8px; background: var(--panel); text-align: center; }
.audio-modal-icon { margin-bottom: 16px; color: var(--muted); font-size: 64px; }
.audio-modal-container audio { width: 100%; }
/* Nier Automata: warm technical paper, charcoal ink, rigid machine framing. */
body[data-theme="nier"] {
--bg: #d8d6c2;
--sidebar: #cccab6;
--toolbar: #d4d2bd;
--panel: #d2d0bb;
--panel-2: #c4c2ad;
--panel-3: #b6b5a2;
--canvas: #c7c5b0;
--text: #3d3e38;
--text-2: #505149;
--muted: #73746b;
--faint: #929286;
--line: #9a998d;
--line-2: #62635c;
--accent: #42433d;
--accent-rgb: 66, 67, 61;
--danger: #884640;
--success: #516f68;
--warning: #9a7048;
color-scheme: light;
background-color: var(--bg);
background-image:
linear-gradient(rgba(61,62,56,.035) 1px, transparent 1px),
linear-gradient(90deg, rgba(61,62,56,.035) 1px, transparent 1px),
repeating-linear-gradient(135deg, transparent 0 88px, rgba(61,62,56,.025) 89px 90px);
background-size: 5px 5px, 5px 5px, 180px 180px;
color: var(--text);
font-family: Bahnschrift, "Arial Narrow", "Segoe UI", sans-serif;
letter-spacing: .015em;
}
body[data-theme="nier"] .studio-sidebar,
body[data-theme="nier"] .library-main {
background-color: transparent;
background-image:
linear-gradient(rgba(61,62,56,.035) 1px, transparent 1px),
linear-gradient(90deg, rgba(61,62,56,.035) 1px, transparent 1px),
radial-gradient(circle at 85% 35%, transparent 0 150px, rgba(61,62,56,.055) 151px 152px, transparent 153px);
background-size: 5px 5px, 5px 5px, auto;
}
body[data-theme="nier"] .studio-sidebar { border-right: 2px solid var(--text); }
body[data-theme="nier"] .sidebar-brand,
body[data-theme="nier"] .viewer-toolbar {
position: relative;
}
body[data-theme="nier"] .sidebar-brand,
body[data-theme="nier"] .library-toolbar,
body[data-theme="nier"] .viewer-toolbar {
border-color: var(--text);
background: rgba(216,214,194,.96);
}
body[data-theme="nier"] .sidebar-brand::after,
body[data-theme="nier"] .library-toolbar::after,
body[data-theme="nier"] .viewer-toolbar::after {
content: "";
position: absolute;
right: 0;
bottom: -5px;
left: 0;
height: 3px;
pointer-events: none;
background: radial-gradient(circle, rgba(61,62,56,.65) 1px, transparent 1.5px) 0 0 / 9px 3px repeat-x;
}
body[data-theme="nier"] .studio-mark,
body[data-theme="nier"] button,
body[data-theme="nier"] select,
body[data-theme="nier"] input,
body[data-theme="nier"] .search-container,
body[data-theme="nier"] .thumb-control,
body[data-theme="nier"] .image-card,
body[data-theme="nier"] .favorite-btn,
body[data-theme="nier"] .context-menu,
body[data-theme="nier"] .notification,
body[data-theme="nier"] .metadata-prompt,
body[data-theme="nier"] .metadata-kv,
body[data-theme="nier"] .lora-item,
body[data-theme="nier"] .file-info-item,
body[data-theme="nier"] .settings-grid .metadata-item,
body[data-theme="nier"] .workflow-node-item,
body[data-theme="nier"] .workflow-node-index,
body[data-theme="nier"] .modal-view-toggle,
body[data-theme="nier"] .filmstrip-thumb { border-radius: 1px; box-shadow: none; }
body[data-theme="nier"] .sidebar-action,
body[data-theme="nier"] .sidebar-nav-item,
body[data-theme="nier"] #folderBtn,
body[data-theme="nier"] .folder-item,
body[data-theme="nier"] .video-indicator,
body[data-theme="nier"] .audio-indicator { border-radius: 0; }
body[data-theme="nier"] .studio-mark { border-color: var(--text); background: var(--text); color: #e0dec8; }
body[data-theme="nier"] .studio-wordmark strong,
body[data-theme="nier"] .location-block strong,
body[data-theme="nier"] .viewer-identity strong { letter-spacing: .12em; text-transform: uppercase; }
body[data-theme="nier"] .sidebar-action.active,
body[data-theme="nier"] .sidebar-nav-item.active,
body[data-theme="nier"] #folderBtn.active,
body[data-theme="nier"] .folder-item.active,
body[data-theme="nier"] .toolbar-controls button.active,
body[data-theme="nier"] .modal-view-btn.active {
border-color: var(--text);
background: var(--text);
color: #e2e0c9;
}
body[data-theme="nier"] .sidebar-action:hover,
body[data-theme="nier"] .sidebar-nav-item:hover,
body[data-theme="nier"] #folderBtn:hover,
body[data-theme="nier"] .folder-item:hover,
body[data-theme="nier"] .toolbar-controls button:hover,
body[data-theme="nier"] .toolbar-controls select:hover,
body[data-theme="nier"] .workflow-node-header:hover { background: rgba(61,62,56,.14); color: var(--text); }
body[data-theme="nier"] .library-toolbar { border-bottom-width: 2px; }
body[data-theme="nier"] .toolbar-controls button,
body[data-theme="nier"] .toolbar-controls select,
body[data-theme="nier"] .search-container,
body[data-theme="nier"] .thumb-control,
body[data-theme="nier"] .viewer-back,
body[data-theme="nier"] .viewer-toolbar button,
body[data-theme="nier"] .modal-close { border-color: var(--line-2); background: rgba(210,208,187,.80); color: var(--text); }
body[data-theme="nier"] .toolbar-controls select option { background: #d8d6c2; color: var(--text); }
body[data-theme="nier"] #compareBtn {
border-color: #3d3e38;
background: #3d3e38; color: #f0edd5;
box-shadow: inset 0 0 0 1px #d8d6c2;
}
body[data-theme="nier"] #compareBtn:hover {
border-color: #24251f;
background: #24251f; color: #fffceb;
}
body[data-theme="nier"] .search-container input::placeholder { color: #74756c; }
body[data-theme="nier"] .thumb-control input {
height: 10px; appearance: none; border: 0; background: linear-gradient(var(--text),var(--text)) center / 100% 2px no-repeat;
}
body[data-theme="nier"] .thumb-control input::-webkit-slider-thumb {
width: 8px; height: 14px; appearance: none; border: 1px solid #e2e0c9; border-radius: 0; background: var(--text);
}
body[data-theme="nier"] .thumb-control input::-moz-range-thumb {
width: 8px; height: 14px; border: 1px solid #e2e0c9; border-radius: 0; background: var(--text);
}
body[data-theme="nier"] .gallery { padding-top: 15px; }
body[data-theme="nier"] .image-card { border-color: #74756d; background: #c5c3ae; }
body[data-theme="nier"] .image-card:hover { border-color: var(--text); background: #bbb9a5; transform: none; }
body[data-theme="nier"] .image-info { border-color: #74756d; background: #c5c3ae; }
body[data-theme="nier"] .favorite-btn { border-color: rgba(226,224,201,.75); background: rgba(61,62,56,.88); color: #e2e0c9; opacity: .55; }
body[data-theme="nier"] .favorite-btn.favorited { border-color: var(--text); background: #e0dec8; color: var(--text); }
body[data-theme="nier"] .video-indicator,
body[data-theme="nier"] .audio-indicator { border-color: #e2e0c9; background: rgba(61,62,56,.90); color: #e2e0c9; }
body[data-theme="nier"] .image-card.selected {
border: 4px solid var(--text);
outline: 2px solid #e4e1c8;
outline-offset: -7px;
background: rgba(61,62,56,.12);
box-shadow: inset 0 0 0 2px #e4e1c8, inset 0 -7px 0 var(--text);
}
body[data-theme="nier"] .image-card.selected::after {
width: 29px; height: 29px; border-radius: 0; border: 2px solid #e4e1c8;
background: var(--text); color: #e4e1c8; font-size: 16px;
}
body[data-theme="nier"] .image-card.compare-first,
body[data-theme="nier"] .image-card.compare-second {
outline: 2px solid #e4e1c8;
outline-offset: -7px;
}
body[data-theme="nier"] .image-card.compare-first { border: 4px solid #267b83; box-shadow: inset 0 -7px 0 #267b83; }
body[data-theme="nier"] .image-card.compare-second { border: 4px solid #a34d40; box-shadow: inset 0 -7px 0 #a34d40; }
body[data-theme="nier"] .image-card.compare-first::after,
body[data-theme="nier"] .image-card.compare-second::after {
position: absolute; top: 7px; right: 7px; z-index: 12;
width: 30px; height: 30px; display: grid; place-items: center;
border: 2px solid #e4e1c8; color: #f0edd5; font-size: 12px; font-weight: 700;
}
body[data-theme="nier"] .image-card.compare-first::after { content: "A"; background: #267b83; }
body[data-theme="nier"] .image-card.compare-second::after { content: "B"; background: #a34d40; }
body[data-theme="nier"] .multi-select-actions,
body[data-theme="nier"] .context-menu,
body[data-theme="nier"] .notification { border-color: var(--text); border-radius: 1px; background: #d8d6c2; box-shadow: 8px 8px 0 rgba(61,62,56,.15); }
body[data-theme="nier"] .modal { background: var(--canvas); }
body[data-theme="nier"] .viewer-stage,
body[data-theme="nier"] .modal-left { background-color: #bdbba7; background-image: linear-gradient(rgba(61,62,56,.04) 1px, transparent 1px), linear-gradient(90deg, rgba(61,62,56,.04) 1px, transparent 1px); background-size: 5px 5px; }
body[data-theme="nier"] .modal-right,
body[data-theme="nier"] .workflow-search { background: #d2d0bb; }
body[data-theme="nier"] .modal-filmstrip { border-color: var(--text); background: #aaa894; }
body[data-theme="nier"] .filmstrip-track { scrollbar-color: var(--text) transparent; }
body[data-theme="nier"] .filmstrip-thumb { position: relative; }
body[data-theme="nier"] .filmstrip-thumb.active {
border: 4px solid var(--text);
box-shadow: inset 0 0 0 2px #e4e1c8, 0 0 0 2px #e4e1c8;
}
body[data-theme="nier"] .filmstrip-thumb.active::after {
content: "◆"; position: absolute; top: 2px; right: 2px;
width: 18px; height: 18px; display: grid; place-items: center;
background: var(--text); color: #e4e1c8; font-size: 8px;
}
body[data-theme="nier"] .metadata-section,
body[data-theme="nier"] .metadata-section-header { border-color: var(--line-2); }
body[data-theme="nier"] .metadata-section-header .icon,
body[data-theme="nier"] .lora-name,
body[data-theme="nier"] .workflow-node-index { color: var(--text); }
body[data-theme="nier"] .metadata-prompt,
body[data-theme="nier"] .metadata-kv,
body[data-theme="nier"] .lora-item,
body[data-theme="nier"] .file-info-item,
body[data-theme="nier"] .settings-grid .metadata-item,
body[data-theme="nier"] .workflow-node-item { border-color: #8b8b80; background: rgba(196,194,173,.72); }
body[data-theme="nier"] .modal-delete-btn { background: rgba(136,70,64,.10); color: #713a36; }
body[data-theme="nier"].mini-mode .library-toolbar { background: rgba(216,214,194,.96); box-shadow: 8px 8px 0 rgba(61,62,56,.15); }
@media (max-width: 1050px) {
:root { --sidebar-width: 190px; }
.location-block { display: none; }
.library-toolbar { align-items: flex-start; }
.toolbar-controls { width: 100%; justify-content: flex-start; }
.search-container { flex: 1; width: auto; }
.modal-content { grid-template-columns: minmax(0,1fr) 330px; }
.modal-nav-next { right: 340px; }
}
/* Quick Look keeps the existing mini-window behavior, but uses the Studio visual language. */
body.mini-mode { background: var(--canvas); }
body.mini-mode .studio-shell { display: block; }
body.mini-mode .library-main { padding-top: 0; }
body.mini-mode .studio-sidebar, body.mini-mode .location-block, body.mini-mode #compareModeBtn,
body.mini-mode #multiSelectModeBtn, body.mini-mode #compareBtn, body.mini-mode .thumb-control,
body.mini-mode .folder-manager, body.mini-mode .multi-select-actions { display: none !important; }
body.mini-mode .library-toolbar {
position: fixed; right: auto; left: 50%; bottom: 10px; top: auto; z-index: 500;
width: min(560px, calc(100% - 16px)); min-height: 0; transform: translateX(-50%);
padding: 7px; border: 1px solid var(--line-2); border-radius: 10px; background: rgba(45,48,52,.94); box-shadow: 0 12px 32px rgba(0,0,0,.34);
}
body.mini-mode .toolbar-controls { width: 100%; flex-wrap: wrap; justify-content: center; }
body.mini-mode .search-container { order: -1; width: 100%; max-width: none; flex: 1 1 100%; }
body.mini-mode .toolbar-controls button { width: 32px; padding: 0; font-size: 0; }
body.mini-mode .toolbar-controls button .ui-icon { font-size: 11px; }
body.mini-mode .toolbar-controls select { max-width: 95px; padding: 0 5px; font-size: 9px; }
body.mini-mode .gallery { grid-template-columns: repeat(auto-fill,minmax(140px,1fr)); gap: 5px; padding: 6px 6px 78px; }
body.mini-mode .image-card img, body.mini-mode .image-card video, body.mini-mode .audio-thumbnail { height: auto; aspect-ratio: 1; object-fit: contain; }
body.mini-mode .image-info { position: absolute; inset: auto 0 0; min-height: 0; padding: 20px 7px 6px; opacity: 0; border: 0; background: linear-gradient(transparent,rgba(20,22,25,.9)); transition: opacity .12s; }
body.mini-mode .image-card:hover .image-info { opacity: 1; }
body.mini-mode .modal { grid-template-rows: 42px minmax(0,1fr); }
body.mini-mode .modal-content { display: block; }
body.mini-mode .modal-right, body.mini-mode .modal-filmstrip { display: none; }
body.mini-mode .viewer-stage { height: 100%; display: block; }
body.mini-mode .modal-left { height: 100%; }
body.mini-mode .modal-nav-next { right: 10px; }
@media (prefers-reduced-motion: reduce) { * { scroll-behavior: auto !important; transition-duration: .01ms !important; } }
</style>
</head>
<body data-theme="graphite">
<script>
if (new URLSearchParams(location.search).get('mode') === 'mini') document.body.classList.add('mini-mode');
</script>
<div class="studio-shell" id="studioShell">
<aside class="studio-sidebar">
<div class="sidebar-brand">
<div class="studio-mark">S</div>
<div class="studio-wordmark"><strong>Studio</strong><span>Image Library</span></div>
<button class="sidebar-collapse" id="sidebarCollapseBtn" title="Collapse sidebar" aria-label="Collapse sidebar">‹</button>
</div>
<div class="sidebar-scroll">
<section class="sidebar-section">
<div class="sidebar-label">Library</div>
<button class="sidebar-action active" id="allImagesBtn"><span class="ui-icon">▣</span><span>All Images</span></button>
<button class="sidebar-action" id="favoritesFilterBtn"><span class="ui-icon">★</span><span>Favorites</span></button>
</section>
<section class="sidebar-section">
<div class="sidebar-label">Folders</div>
<div class="folder-manager">
<button id="folderBtn" class="active" aria-expanded="true"><span class="ui-icon">▱</span><span>Browse folders</span></button>
<div class="folder-dropdown active" id="folderDropdown">
<div class="folder-list" id="folderList"></div>
<details class="folder-add-disclosure">
<summary title="Add external folder">+ Add external folder</summary>
<div class="folder-add-form">
<input type="text" id="folderName" placeholder="Display name">
<input type="text" id="folderPath" placeholder="Full folder path">
<button id="addFolderBtn">Add Folder</button>
</div>
</details>
</div>
</div>
</section>
</div>
</aside>
<main class="library-main">
<header class="library-toolbar">
<div class="location-block"><strong id="currentLocation">All Images</strong><span id="currentPath">Studio Library</span></div>
<div class="toolbar-controls">
<div class="search-container"><span class="ui-icon">⌕</span><input type="text" id="searchInput" placeholder="Search filename…"></div>
<select id="sortSelect" aria-label="Sort order" title="Sort order">
<option value="date_desc">Newest</option>
<option value="date_asc">Oldest</option>
<option value="name_asc">Name A–Z</option>
<option value="name_desc">Name Z–A</option>
</select>
<button id="favoritesToolbarBtn" class="toolbar-button" title="Show favorites"><span class="ui-icon">★</span>Favorites</button>
<button id="multiSelectModeBtn" class="toolbar-button"><span class="ui-icon">✓</span>Select</button>
<button id="compareModeBtn" class="toolbar-button"><span class="ui-icon">⇄</span>Compare</button>
<button id="compareBtn" class="toolbar-button"><span class="ui-icon">▥</span>Open Compare</button>
<label class="thumb-control" title="Thumbnail size"><span class="ui-icon">▦</span><input id="thumbnailSize" type="range" min="150" max="320" value="220" step="10"></label>
<select id="themeSelect" aria-label="Theme" title="Theme"><option value="graphite">Graphite</option><option value="nier">Nier Automata</option></select>
<select id="openModeSelect" aria-label="Open mode" title="Open mode"><option value="tab">Full Viewer</option><option value="mini">Quick Look</option></select>
<button id="refreshBtn" class="toolbar-button" title="Refresh"><span class="ui-icon">↻</span>Refresh</button>
</div>
</header>
<div class="gallery" id="gallery"></div>
<div class="loading" id="loading" style="display:none">Loading more images…</div>
</main>
</div>
<div class="multi-select-actions" id="multiSelectActions">
<span class="selected-info"><span id="selectedCount">0</span> selected</span>
<button id="favoriteSelectedBtn"><span class="ui-icon">★</span>Favorite</button>
<button id="unfavoriteSelectedBtn"><span class="ui-icon">☆</span>Unfavorite</button>
<button id="deleteSelectedBtn"><span class="ui-icon">■</span>Delete</button>
<button id="clearSelectionBtn"><span class="ui-icon">×</span>Clear</button>
</div>
<div class="modal" id="modal">
<div class="viewer-toolbar">
<button class="viewer-back" onclick="closeModal()">← Library</button>
<div class="viewer-identity"><strong id="viewerTitle">Image</strong><span id="viewerLocation">Studio Library</span></div>
<span class="modal-close" onclick="closeModal()">&times;</span>
</div>
<button class="modal-nav modal-nav-prev" id="modalNavPrev" onclick="navigatePrev()" aria-label="Previous image"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<button class="modal-nav modal-nav-next" id="modalNavNext" onclick="navigateNext()" aria-label="Next image"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
<div class="modal-content">
<div class="viewer-stage">
<div class="modal-left" id="modalMediaContainer"></div>
<div class="modal-filmstrip"><div class="filmstrip-track" id="modalFilmstrip"></div><div class="filmstrip-count" id="filmstripCount">0 / 0</div></div>
</div>
<div class="modal-right" id="metadataPanel"></div>
</div>
</div>
<div class="modal" id="compareModal">
<div class="viewer-toolbar"><button class="viewer-back" onclick="closeCompareModal()">← Library</button><div class="viewer-identity"><strong>Compare</strong><span>Linked zoom and pan · four comparison modes</span></div><span class="modal-close" onclick="closeCompareModal()">&times;</span></div>
<div id="compareContainer"></div>
</div>
<script src="/gemini-image-browser/js"></script>
</body>
</html>