The Images page now renders the approved mock's heading, shared toolbar, filter row, section caption and infinite continuation (#249, Task C2). - Heading: "Your generations, their prompts, and the next idea." with Import (upload icon) before New prompt (primary). - Toolbar (shared mountToolbar): search over filenames, linked prompt text, titles and tags; category (the Library's categories); sort Recently added / Name A–Z / Highest rated, then Oldest first and Name Z–A (D14). - Filter row: Grid/List/Masonry segmented control, Thumbnail size, the scope chip ("All output folders" or "<source> · <folder>") and "Group by positive prompt →". - Caption: "N of M images · K positive prompts" from positive_count; a plain cursor page keeps the first page's count and an unknown count hides the "· K positive prompts" part. The date span shows "<Today|Yesterday|weekday> · <Month D>" of the newest loaded result under a date sort and is hidden otherwise (D17). - Continuation: "N of M images · more load as you scroll" with Load more, the concept's end copy, Retry for a failed page, and the concept's "K more images loaded. N of M." toast. - The listing sends category, tags and sort=rating (PR B params). Old control -> new control: - "Search filenames" field -> toolbar search "Search workspace" (placeholder "Search prompts, tags, filenames…"), also matching prompt text and tags - Source select -> Filters -> "Refine this view" Source - Folder select -> Refine Folder (all sources' folders and ancestors) - Media select -> Refine Media - (new) Tags -> Refine Tags; Category -> toolbar category select - Sort select (Newest/Oldest/Filename A–Z/Z–A) -> toolbar sort (Recently added/Oldest first/Name A–Z/Name Z–A, plus Highest rated) - Layout buttons -> .segmented Grid/List/Masonry in the filter row - Thumbnail size field -> label.small + select#image-size - "Group by positive prompt" link -> button.chip "Group by positive prompt →" (#/library) - Refresh -> Import -> "Scan an output folder" -> Rescan (D15) - "Current gallery" link -> removed; Overview's tool cards link the legacy gallery (D29) - "All images" h2 + count -> .section-caption #result-count - .image-continuation -> .infinite-continuation (Retry kept) Legacy behavior kept: video items, keyboard grid navigation, scroll and filter restoration, bounded DOM, continuation retry, source-change recovery (now disabling the toolbar, Filters and Import). The Filters and Import dialogs close on route change and browser Back. The shared toolbar (C1) called .some() on select.options, which is an HTMLOptionsCollection in a browser (only the unit tests' fake DOM gave an array); setValue and setCategories now spread it first. The layout segmented control may wrap at extreme zoom (D31) instead of scrolling the 390px page sideways. The retired .image-filters, .image-view-controls, .image-toolbar, .image-layouts, .image-collection, .image-continuation and duplicated .image-workspace grid rules are gone from images.css.
113 lines
10 KiB
CSS
113 lines
10 KiB
CSS
.images-main { container: images-page / inline-size; }
|
|
.image-grid { position: relative; inline-size: 100%; }
|
|
.image-card { position: absolute; min-inline-size: 0; border: 1px solid var(--ca-line); border-radius: 7px; background: var(--ca-surface); display: grid; grid-template-rows: minmax(0, 1fr) var(--image-footer-height, 54px); }
|
|
.image-card.selected { border-color: var(--ca-focus); outline: 1px solid var(--ca-focus); outline-offset: 0; }
|
|
.image-card .image-open { display: block; padding: 0; inline-size: 100%; block-size: 100%; min-block-size: 0; border: 0; border-radius: 6px 6px 0 0; background: var(--ca-field); }
|
|
.image-open :where(img, video) { display: block; inline-size: 100%; block-size: 100%; object-fit: cover; border-radius: inherit; }
|
|
.image-grid[data-layout="grid"] .image-open { aspect-ratio: 4 / 3; }
|
|
.image-card-footer { position: relative; align-self: start; min-inline-size: 0; min-block-size: 54px; padding: 10px 11px; }
|
|
.image-card-footer strong { display: block; padding-inline-end: 24px; font-size: 11px; line-height: 1.4; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
.image-card-footer small { display: block; color: var(--ca-muted); font-size: 9px; line-height: 1.4; margin-block-start: 6px; overflow-wrap: anywhere; }
|
|
.ca-tools .image-card-footer button { position: absolute; inset-inline-end: 6px; inset-block-start: 5px; inline-size: 26px; block-size: 26px; min-block-size: 26px; padding: 5px; }
|
|
.image-card-footer .icon { inline-size: 14px; block-size: 14px; }
|
|
.image-grid[data-layout="list"] .image-card { grid-template-columns: min(160px, 45%) minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); }
|
|
.image-grid[data-layout="list"] .image-open { border-radius: 6px 0 0 6px; }
|
|
.image-grid[data-layout="list"] .image-card-footer { align-self: center; min-block-size: 0; }
|
|
.image-details { position: sticky; inset-block-start: 20px; padding: 17px; border-radius: 7px; min-inline-size: 0; max-block-size: calc(100dvh - 40px); overflow: auto; }
|
|
.image-details h2 { font-size: 14px; overflow-wrap: anywhere; margin-block: 0 12px; }
|
|
.image-details > :where(img, video) { display: block; inline-size: calc(100% + 34px); block-size: 150px; margin: -17px -17px 17px; object-fit: cover; background: var(--ca-field); border-radius: 6px 6px 0 0; }
|
|
.image-facts { font-size: 10px; margin-block: 17px; }
|
|
.image-facts > div { padding-block: 8px; border-block-end: 1px solid var(--ca-line); }
|
|
.image-facts dt { color: var(--ca-muted); margin-block-end: 4px; }
|
|
.image-facts dd { margin: 0; overflow-wrap: anywhere; }
|
|
.image-details .workspace-link { display: flex; font-size: 11px; }
|
|
.image-preview { box-sizing: border-box; background: var(--ca-surface); color: var(--ca-text); border: 1px solid var(--ca-border); border-radius: 9px; inline-size: min(1500px, 96%); block-size: min(940px, 94dvh); max-block-size: 94%; padding: 18px; overflow: hidden; overscroll-behavior: contain; }
|
|
.image-preview[open] { display: grid; grid-template-rows: auto minmax(0, 1fr); gap: 20px; }
|
|
.image-preview::backdrop { background: #080b12b8; overflow: hidden; overscroll-behavior: contain; }
|
|
.image-preview-header { display: flex; align-items: center; justify-content: space-between; gap: 18px; min-inline-size: 0; }
|
|
.image-preview-header > div { min-inline-size: 0; }
|
|
.image-preview-header h2 { font-size: 18px; font-weight: 600; line-height: 1.5; margin: 0; overflow-wrap: anywhere; }
|
|
.image-preview-position { font-size: 12px; line-height: 1.5; margin: 0; overflow-wrap: anywhere; }
|
|
.ca-tools .image-preview-header > button { align-self: start; min-inline-size: 32px; padding: 4px 10px; font-size: 20px; line-height: 1.5; }
|
|
.image-preview-body { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 20px; min-block-size: 0; min-inline-size: 0; }
|
|
.image-preview-left { display: grid; grid-template-rows: minmax(0, 1fr) auto; min-inline-size: 0; min-block-size: 0; background: var(--ca-field); border: 1px solid var(--ca-line); border-radius: 6px; overflow: hidden; }
|
|
.image-preview-stage { position: relative; min-inline-size: 0; min-block-size: 0; }
|
|
.image-preview-media { position: absolute; inset: 0; }
|
|
.image-preview-media > :where(img, video) { display: block; inline-size: 100%; block-size: 100%; object-fit: contain; }
|
|
.image-preview-media > img[hidden] { display: none; }
|
|
.image-preview-media > .viewer-container { position: absolute; inset: 0; inline-size: 100% !important; block-size: 100% !important; background: var(--ca-field); }
|
|
.image-preview-toolbar { border-block-start: 1px solid var(--ca-line); padding: 10px; }
|
|
.image-preview-controls { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 6px; }
|
|
.ca-tools .image-preview-controls :where(button, a) { display: inline-flex; align-items: center; justify-content: center; min-block-size: 32px; padding: 4px 10px; background: var(--ca-control); border: 1px solid var(--ca-border); border-radius: 4px; color: var(--ca-text); font-size: 12px; line-height: 1.5; text-decoration: none; }
|
|
.image-preview-controls [aria-disabled="true"] { opacity: .55; cursor: not-allowed; }
|
|
.image-preview-reason { text-align: center; font-size: 11px; line-height: 1.6; margin: 8px 0 0; }
|
|
.image-preview-failure { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; flex-direction: column; padding: 20px; background: var(--ca-field); text-align: center; font-size: 12px; }
|
|
.image-preview-failure[hidden] { display: none; }
|
|
.image-preview-details { min-inline-size: 0; min-block-size: 0; overflow: auto; overscroll-behavior: contain; padding: 0 6px 18px; }
|
|
.image-preview :where(button, a, .image-preview-stage):focus-visible { outline: 2px solid var(--ca-focus); outline-offset: -2px; }
|
|
@media (max-width: 1250px) {
|
|
.image-preview-body { grid-template-columns: minmax(0, 1fr) 300px; }
|
|
}
|
|
@media (max-width: 950px) {
|
|
.image-preview-body { grid-template-columns: minmax(0, 1fr) 270px; }
|
|
}
|
|
@media (max-width: 700px) {
|
|
.image-details { position: static; max-block-size: none; padding: 17px; }
|
|
.image-details > :where(img, video) { block-size: 200px; }
|
|
.image-preview { block-size: 94dvh; overflow: auto; }
|
|
.image-preview[open] { grid-template-rows: auto auto; }
|
|
.image-preview-body { grid-template-columns: minmax(0, 1fr); }
|
|
.image-preview-left { block-size: 48dvh; min-block-size: 300px; }
|
|
.image-preview-details { overflow: visible; }
|
|
.ca-tools .image-preview-controls :where(button, a) { font-size: 10px; }
|
|
}
|
|
@container images-page (inline-size < 600px) {
|
|
.image-workspace { grid-template-columns: minmax(0, 1fr); }
|
|
.image-details { position: static; max-block-size: none; }
|
|
}
|
|
.options-status { font-size: 12px; padding-inline: 4px; }
|
|
|
|
.prompt-inspector { margin-block-start: 22px; min-inline-size: 0; }
|
|
.prompt-block { border-block-start: 1px solid var(--ca-line); padding-block: 13px; min-inline-size: 0; }
|
|
.prompt-positive { --prompt-role: #73dda9; }
|
|
[data-ca-theme="light"] .prompt-positive { --prompt-role: #176b46; }
|
|
.prompt-negative { --prompt-role: #ff8c98; }
|
|
[data-ca-theme="light"] .prompt-negative { --prompt-role: #b71931; }
|
|
.prompt-block-heading { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 8px; }
|
|
.prompt-block h3 { font-size: 10px; text-transform: uppercase; letter-spacing: .5px; margin: 0; color: var(--prompt-role); }
|
|
.prompt-source, .prompt-empty, .prompt-block > .muted { font-size: 10px; }
|
|
.prompt-source { margin-block: 6px 10px; }
|
|
.prompt-text { font: inherit; font-size: 12px; line-height: 1.75; white-space: pre-wrap; overflow-wrap: anywhere; margin-block: 10px; }
|
|
.prompt-text:focus-visible { outline: 2px solid var(--ca-focus); outline-offset: 3px; }
|
|
.prompt-actions { display: flex; flex-wrap: wrap; gap: 4px; }
|
|
.ca-tools .prompt-actions button { display: inline-flex; align-items: center; justify-content: center; inline-size: 26px; block-size: 26px; min-block-size: 26px; padding: 5px; }
|
|
.prompt-actions .prompt-send-button span { display: none; }
|
|
.prompt-actions .icon { inline-size: 14px; block-size: 14px; }
|
|
.prompt-copy-status { font-size: 11px; margin-block-end: 0; }
|
|
.prompt-copy-status:empty { display: none; }
|
|
.image-preview-details .prompt-inspector { margin-block-start: 0; }
|
|
.prompt-send-button { display: inline-flex; align-items: center; justify-content: center; gap: 7px; color: var(--ca-focus); }
|
|
.prompt-send-button svg { inline-size: 16px; block-size: 16px; flex-shrink: 0; }
|
|
.prompt-sender { background: var(--ca-surface); color: var(--ca-text); border: 1px solid var(--ca-line); border-radius: 12px; padding: 22px; inline-size: min(760px, calc(100% - 32px)); max-inline-size: none; max-block-size: calc(100dvh - 32px); overflow: auto; }
|
|
.prompt-sender::backdrop { background: #000b; }
|
|
.prompt-sender-header, .prompt-sender-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; }
|
|
.prompt-sender-header h2 { font-size: 20px; margin: 0; }
|
|
.prompt-sender-options { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; margin-block: 20px; }
|
|
.prompt-sender-options .ca-field { display: flex; flex-direction: column; align-items: stretch; gap: 6px; }
|
|
.prompt-sender select { inline-size: 100%; min-block-size: 38px; }
|
|
.prompt-sender-status { font-size: 13px; line-height: 1.6; }
|
|
.prompt-sender-status[role="alert"] { color: var(--ca-error-text); }
|
|
.prompt-sender-preview { margin-block: 20px; min-inline-size: 0; }
|
|
.prompt-sender-field { border: 1px solid var(--ca-line); border-radius: 7px; padding: 14px; margin-block: 12px; min-inline-size: 0; }
|
|
.prompt-sender-field :where(pre, textarea), .prompt-sender-composition { white-space: pre-wrap; overflow-wrap: anywhere; font: inherit; font-size: 12px; line-height: 1.65; max-inline-size: 100%; }
|
|
.prompt-sender-field textarea { inline-size: 100%; min-block-size: 100px; resize: vertical; }
|
|
.prompt-sender-composition { padding: 12px; background: var(--ca-field); border-radius: 6px; }
|
|
.prompt-sender-actions { justify-content: flex-end; margin-block-start: 18px; }
|
|
@media (max-width: 780px) {
|
|
.prompt-sender-options { grid-template-columns: minmax(0, 1fr); }
|
|
}
|
|
@media (pointer: coarse) {
|
|
.ca-tools .image-preview :is(button, a) { min-inline-size: 40px; min-block-size: 40px; }
|
|
.ca-tools .image-preview .prompt-actions button { min-inline-size: 36px; min-block-size: 36px; }
|
|
}
|