q6 commited on
Commit
4c2f585
·
1 Parent(s): 6220dfc

Use toggle buttons for exif filters

Browse files
Files changed (3) hide show
  1. backend/app.py +7 -4
  2. frontend/app.js +24 -9
  3. frontend/style.css +37 -15
backend/app.py CHANGED
@@ -501,15 +501,18 @@ def exif_items(post_ids, scanned, exif_types=None):
501
  items = []
502
  for pid in post_ids:
503
  s = scanned.get(pid)
504
- if not s or not s.get("exif_type"):
505
  continue
506
- if allowed is not None and s.get("exif_type") not in allowed:
 
 
 
507
  continue
508
  items.append(
509
  {
510
  "post_id": pid,
511
  "url": s.get("url"),
512
- "exif_type": s.get("exif_type"),
513
  "scanned": True,
514
  **image_links(pid, s.get("url")),
515
  }
@@ -882,7 +885,7 @@ async def get_results(search_id: str, page: int = 1, exif_types: str = ""):
882
  allowed = [
883
  int(x)
884
  for x in exif_types.split(",")
885
- if x.isdigit() and int(x) in EXIF_TYPE_TO_CODE.values()
886
  ]
887
  source = exif_items(post_ids, scanned, allowed if exif_types != "" else None)
888
  page = max(page, 1)
 
501
  items = []
502
  for pid in post_ids:
503
  s = scanned.get(pid)
504
+ if not s:
505
  continue
506
+ exif_type = s.get("exif_type")
507
+ if exif_type is None and (allowed is None or 0 not in allowed):
508
+ continue
509
+ if exif_type is not None and allowed is not None and exif_type not in allowed:
510
  continue
511
  items.append(
512
  {
513
  "post_id": pid,
514
  "url": s.get("url"),
515
+ "exif_type": exif_type,
516
  "scanned": True,
517
  **image_links(pid, s.get("url")),
518
  }
 
885
  allowed = [
886
  int(x)
887
  for x in exif_types.split(",")
888
+ if x.isdigit() and int(x) in (*EXIF_TYPE_TO_CODE.values(), 0)
889
  ]
890
  source = exif_items(post_ids, scanned, allowed if exif_types != "" else None)
891
  page = max(page, 1)
frontend/app.js CHANGED
@@ -2,6 +2,8 @@ const $ = s => document.querySelector(s)
2
  const $$ = s => document.querySelectorAll(s)
3
  const EXIF_NAMES = { 1: "novelai", 2: "sd", 3: "comfy", 4: "mj", 5: "celsys", 6: "photoshop", 7: "stealth" }
4
  const EXIF_CODES = Object.keys(EXIF_NAMES).map(Number)
 
 
5
  const EXIF_FILTER_KEY = "pixif2-exif-types"
6
  const LONG_DIGITS_RE = /\d{6,}/g
7
  const PAGE_SIZE = 60
@@ -175,13 +177,13 @@ function renderSearchPager(data) {
175
  function getExifTypes() {
176
  const raw = localStorage.getItem(EXIF_FILTER_KEY)
177
  if (raw === null) return [...EXIF_CODES]
178
- const types = raw.split(",").map(Number).filter(n => EXIF_CODES.includes(n))
179
  return types
180
  }
181
 
182
  function setExifTypes(types) {
183
- const sorted = [...new Set(types)].filter(n => EXIF_CODES.includes(n)).sort((a, b) => a - b)
184
- if (sorted.length === EXIF_CODES.length) {
185
  localStorage.removeItem(EXIF_FILTER_KEY)
186
  } else {
187
  localStorage.setItem(EXIF_FILTER_KEY, sorted.join(","))
@@ -191,12 +193,25 @@ function setExifTypes(types) {
191
 
192
  function renderExifFilters(id) {
193
  const active = new Set(getExifTypes())
194
- $("#exif-filters").innerHTML = EXIF_CODES.map(code => `<label class="check-row">
195
- <input type="checkbox" value="${code}" ${active.has(code) ? "checked" : ""}> ${esc(EXIF_NAMES[code])}
196
- </label>`).join("")
197
- $$("#exif-filters input").forEach(input => {
198
- input.onchange = () => {
199
- const types = [...$$("#exif-filters input:checked")].map(el => Number(el.value))
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  setExifTypes(types)
201
  const hash = explorerHash(id, 1)
202
  if (location.hash === hash) openSearch(id, 1)
 
2
  const $$ = s => document.querySelectorAll(s)
3
  const EXIF_NAMES = { 1: "novelai", 2: "sd", 3: "comfy", 4: "mj", 5: "celsys", 6: "photoshop", 7: "stealth" }
4
  const EXIF_CODES = Object.keys(EXIF_NAMES).map(Number)
5
+ const NO_EXIF_CODE = 0
6
+ const FILTER_CODES = [...EXIF_CODES, NO_EXIF_CODE]
7
  const EXIF_FILTER_KEY = "pixif2-exif-types"
8
  const LONG_DIGITS_RE = /\d{6,}/g
9
  const PAGE_SIZE = 60
 
177
  function getExifTypes() {
178
  const raw = localStorage.getItem(EXIF_FILTER_KEY)
179
  if (raw === null) return [...EXIF_CODES]
180
+ const types = raw.split(",").map(Number).filter(n => FILTER_CODES.includes(n))
181
  return types
182
  }
183
 
184
  function setExifTypes(types) {
185
+ const sorted = [...new Set(types)].filter(n => FILTER_CODES.includes(n)).sort((a, b) => a - b)
186
+ if (sorted.length === EXIF_CODES.length && sorted.every(n => EXIF_CODES.includes(n))) {
187
  localStorage.removeItem(EXIF_FILTER_KEY)
188
  } else {
189
  localStorage.setItem(EXIF_FILTER_KEY, sorted.join(","))
 
193
 
194
  function renderExifFilters(id) {
195
  const active = new Set(getExifTypes())
196
+ const buttons = FILTER_CODES.map(code => {
197
+ const name = code === NO_EXIF_CODE ? "None" : EXIF_NAMES[code]
198
+ return `<button class="filter-btn${active.has(code) ? " active" : ""}" data-code="${code}">${esc(name)}</button>`
199
+ }).join("")
200
+ $("#exif-filters").innerHTML = `${buttons}
201
+ <button class="filter-action" data-action="all">Select all</button>
202
+ <button class="filter-action" data-action="none">Select none</button>
203
+ <button class="filter-action update" data-action="update">Update</button>`
204
+ $$("#exif-filters .filter-btn").forEach(btn => {
205
+ btn.onclick = () => btn.classList.toggle("active")
206
+ })
207
+ $$("#exif-filters .filter-action").forEach(btn => {
208
+ btn.onclick = () => {
209
+ const action = btn.dataset.action
210
+ const filters = [...$$("#exif-filters .filter-btn")]
211
+ if (action === "all") filters.forEach(el => el.classList.add("active"))
212
+ if (action === "none") filters.forEach(el => el.classList.remove("active"))
213
+ if (action !== "update") return
214
+ const types = filters.filter(el => el.classList.contains("active")).map(el => Number(el.dataset.code))
215
  setExifTypes(types)
216
  const hash = explorerHash(id, 1)
217
  if (location.hash === hash) openSearch(id, 1)
frontend/style.css CHANGED
@@ -242,25 +242,47 @@ input:focus, select:focus { outline: none; border-color: var(--accent); backgrou
242
  color: var(--muted);
243
  font-size: .82rem;
244
  }
245
- .check-row {
246
- display: flex;
247
- align-items: center;
248
- gap: .45rem;
249
- color: var(--muted);
250
- font-size: .85rem;
251
- white-space: nowrap;
252
- }
253
- .check-row input {
254
- width: 16px;
255
- height: 16px;
256
- accent-color: var(--accent);
257
- }
258
  .exif-filters {
259
  display: flex;
260
  flex-wrap: wrap;
261
  justify-content: end;
262
- gap: .45rem .75rem;
263
- max-width: 31rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
  }
265
  #results-grid {
266
  display: grid;
 
242
  color: var(--muted);
243
  font-size: .82rem;
244
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  .exif-filters {
246
  display: flex;
247
  flex-wrap: wrap;
248
  justify-content: end;
249
+ gap: .4rem;
250
+ max-width: 39rem;
251
+ }
252
+ .filter-btn, .filter-action {
253
+ height: 32px;
254
+ padding: 0 .65rem;
255
+ border-radius: 6px;
256
+ cursor: pointer;
257
+ font-size: .72rem;
258
+ font-weight: 800;
259
+ letter-spacing: .04rem;
260
+ text-transform: uppercase;
261
+ transition: background .15s, border-color .15s, color .15s;
262
+ }
263
+ .filter-btn {
264
+ background: var(--panel);
265
+ border: 1px solid var(--line);
266
+ color: var(--muted);
267
+ }
268
+ .filter-btn:hover, .filter-action:hover {
269
+ border-color: var(--accent);
270
+ color: var(--accent);
271
+ }
272
+ .filter-btn.active, .filter-action.update {
273
+ background: var(--accent);
274
+ border-color: var(--accent);
275
+ color: #00131c;
276
+ }
277
+ .filter-btn.active:hover, .filter-action.update:hover {
278
+ background: var(--accent-2);
279
+ border-color: var(--accent-2);
280
+ color: #00131c;
281
+ }
282
+ .filter-action {
283
+ background: transparent;
284
+ border: 1px solid var(--line);
285
+ color: var(--muted);
286
  }
287
  #results-grid {
288
  display: grid;