開關控制項,用於布林設定選項,支援說明文字和 Badge 標籤
<div class="settings-form-group">
<label class="settings-label">
<input type="checkbox" class="toggle toggle-primary">
<span class="label-text">無碼模式</span>
</label>
<small class="settings-hint">啟用時只搜尋 AVSOX / FC2,適合無碼作品</small>
</div>
下拉選擇器,用於單選或雙選組合(如排序 + 順序)
<div class="settings-form-row">
<label class="row-label">主題模式</label>
<select class="select select-bordered select-sm">
<option value="light">淺色模式 (Light)</option>
<option value="dim">深色模式 (Dim)</option>
</select>
</div>
數字輸入框,右側顯示單位(字元、MB 等)
<div class="settings-form-row">
<label class="row-label">標題長度限制</label>
<div class="input-group-inline">
<input type="number" class="input input-bordered input-sm" value="80">
<span class="input-suffix">字元</span>
</div>
</div>
文字輸入框,支援說明文字和按鈕組合(如選擇資料夾)
<div class="settings-form-row">
<label class="row-label">輸出目錄</label>
<div style="flex: 1; display: flex; gap: 8px;">
<input type="text" class="input input-bordered input-sm" value="output" style="flex: 1;">
<button class="btn btn-outline btn-sm" type="button">
<i class="bi bi-folder"></i>
</button>
</div>
</div>
進階設定區域的收合/展開控制,卡片式觸發器 + Alpine.js 互動
<div x-data="{ expanded: false }">
<button class="collapsible-trigger" type="button" @click="expanded = !expanded" :aria-expanded="expanded.toString()">
<span class="collapsible-title">
<i class="bi bi-gear"></i> 進階刮削設定
</span>
<span class="btn-icon-collapse" aria-hidden="true">
<i class="bi bi-chevron-down" :class="{ 'rotate-180': expanded }"></i>
</span>
</button>
<div x-show="expanded" x-transition class="collapsible-content">
<!-- 進階設定內容 -->
</div>
</div>
檔案命名格式的變數插入控制項,展示變數列表 + description + example
<!-- 格式預覽 -->
<div class="format-preview">
<span class="tag-badge">番號</span>
<span class="tag-badge">片商</span> {title}
</div>
<!-- 單一變數插入 -->
<div class="variable-input-group">
<input type="text" class="input input-bordered input-sm">
<div class="variable-dropdown">
<button class="btn-variable" type="button">
<i class="bi bi-braces"></i> 變數
</button>
<div class="variable-menu">
<span class="variable-badge">番號</span>
<span class="variable-badge">標題</span>
<span class="variable-badge">女優</span>
<span class="variable-badge">片商</span>
</div>
</div>
</div>
用於 Gemini API Key 輸入框下方的安全提示,強調敏感資料風險
<div class="alert alert-warning api-key-alert">
<i class="bi bi-exclamation-triangle-fill"></i>
<div class="alert-content">
<strong>安全提示:</strong>
<ul class="alert-list">
<li>API Key 將以明文存儲在 config.json</li>
<li>請勿將 config.json 分享給他人</li>
<li>如需撤銷,請前往 <a href="..." target="_blank" class="alert-link">Google AI Studio</a> 重新生成</li>
</ul>
</div>
</div>
版本資訊顯示 + 檢查更新按鈕(含 default / loading / completed 三態)
<!-- Default 狀態 -->
<button class="btn btn-outline btn-sm" type="button">
<i class="bi bi-cloud-download"></i> 檢查更新
</button>
<!-- Loading 狀態 -->
<button class="btn btn-outline btn-sm" type="button" disabled>
<span class="loading loading-spinner loading-sm"></span> 檢查中...
</button>
<!-- Completed 狀態 - 有更新 -->
<div class="badge badge-success gap-2">
<i class="bi bi-download"></i>
<a href="..." target="_blank">新版本 v0.3.1 可用</a>
</div>
<!-- Completed 狀態 - 已最新 -->
<span style="color: var(--text-muted);">
<i class="bi bi-check-circle"></i> 已是最新版本
</span>
<!-- Completed 狀態 - 錯誤 -->
<span style="color: var(--color-error);">
<i class="bi bi-exclamation-circle"></i> 網路錯誤
</span>
Settings 表單的主要行動按鈕,位於表單最下方右對齊
<div class="save-btn-wrapper">
<button type="submit" class="btn btn-primary save-btn">
<i class="bi bi-save"></i> 儲存設定
</button>
</div>
Settings header 右上角的 5 段字型大小快速切換器(xs / sm / md / lg / xl)。
按鈕文字統一顯示「A」,但各自套用對應的 font-size,視覺上呈現從小到大的漸變。
active 狀態套用 .active class,顯示 accent 背景色。
<div class="font-size-control">
<template x-for="s in [{k:'xs',l:'A'},{k:'sm',l:'A'},{k:'md',l:'A'},{k:'lg',l:'A'},{k:'xl',l:'A'}]">
<button type="button"
class="font-size-btn"
:class="{ active: fontSize === s.k }"
:style="'font-size:' + {xs:'0.7rem',sm:'0.8rem',md:'0.9rem',lg:'1.05rem',xl:'1.2rem'}[s.k]"
@click="fontSize = s.k"
x-text="s.l">
</button>
</template>
</div>
來源:search.html 詳情面板的標籤編輯器。
分為系統 tag(.tag-badge,唯讀)和用戶 tag(.tag-badge.user-tag,可刪除)。
點「+」顯示輸入框,輸入後按 ✓ 或 Enter 新增;點 × 刪除 user tag;Escape 取消。
<!-- 系統 tag(唯讀)-->
<span class="badge tag-badge" x-text="tag"></span>
<!-- 用戶 tag(可刪除)-->
<span class="badge tag-badge user-tag">
<span x-text="tag"></span>
<span class="tag-remove" @click="removeUserTag(tag)">×</span>
</span>
<!-- 新增按鈕 -->
<button x-show="!addingTag" @click="showAddTagInput()"
class="btn btn-sm tag-add-btn">+</button>
<!-- 新增輸入框 -->
<span x-show="addingTag" class="tag-input-wrapper">
<input type="text" x-model="newTagValue"
@keydown.enter="confirmAddTag()" @keydown.escape="cancelAddTag()"
class="tag-input" placeholder="輸入標籤">
<button @click="confirmAddTag()" class="btn btn-sm tag-confirm">✓</button>
<button @click="cancelAddTag()" class="btn btn-sm tag-cancel">✕</button>
</span>
掃描來源膠囊 — 來源排序開關 / Parts Bin 可加入態。元件已從 settings.css 抽至
components/source-pill.css 並去 #settings-components scope,
故 Settings / Search / Showcase 皆可直接渲染。
本卡刻意不包 id="settings-components" — 仍正確著色即證明已 unscoped。
Parts Bin 三態:可加入(staged, available=true):+ icon、無刪除線、cursor pointer;
不可達(available=false):灰 dashed + 灰 slash + 右上 amber dot;
active(promoted):由 Settings Active Row 管理(非 Parts Bin)。
<!-- unscoped:不需 id="settings-components" 包裹 -->
<!-- 啟用 / 停用 -->
<div class="source-pill" data-enabled="true">
<i class="bi bi-slash-circle slash-icn"></i>
<span class="pill-name">JavBus</span>
</div>
<!-- 無碼(success accent)-->
<div class="source-pill source-pill--uncensored" data-enabled="true">...</div>
<!-- manual-only(BETA badge)-->
<div class="source-pill source-pill--manual-only" data-enabled="false">
<span class="pill-name">JavLibrary</span>
<span class="source-pill-badge">BETA</span>
</div>
<!-- metatube(m 角標)-->
<span class="source-pill-mt-badge">m</span>
<!-- parts-bin 可加入(staged,available=true:+ icon、無刪除線、pointer)-->
<div class="source-pill is-partsbin" data-enabled="false" data-available="true">
<i class="bi bi-plus-circle plus-icn" aria-hidden="true"></i>
<i class="bi bi-slash-circle slash-icn" aria-hidden="true"></i>
<span class="pill-name">StagedProvider</span>
</div>
<!-- parts-bin 不可達(available=false:灰 dashed + slash + 右上 amber dot)-->
<div class="source-pill is-partsbin" data-enabled="false" data-available="false">
<i class="bi bi-plus-circle plus-icn" aria-hidden="true"></i>
<i class="bi bi-slash-circle slash-icn" aria-hidden="true"></i>
<span class="pill-name">UnreachableProvider</span>
</div>
700ms 長壓開進階重刮 / 搜尋彈窗。共用 mixin shared/long-press.js(longPressState),
三處共用同一份 wiring:Search 送出鈕(長壓 → 進階搜尋)、
Showcase grid 缺卡 enrich-btn、Showcase lightbox 🔍(長壓 → 進階重刮)。
短按走原本 tap 行為(搜尋 / enrich),長壓 700ms 達標後 fire callback 開彈窗,
longPressClickGuard 吞掉長壓後的同一次 click(避免連帶送出 / 連帶 enrich,US8)。
刻意無長壓進度環、無提示氣泡 / 視覺 affordance(長壓為進階快捷,不對一般用戶強調)。
六事件 wiring 一律傳 $event:longPressEnd($event) / longPressCancel($event)
在 touchend / touchcancel 設 synthetic-mouse 抑制窗,longPressStart(..., $event) 讓抑制窗內 UA 補發的
synthetic mousedown early-return(不清長壓旗標),否則長壓開彈窗會被補發的 click 連帶觸發底層 tap(雙觸發,Codex PR#47 P2)。
<!-- main.js:import + mergeState(descriptor-preserving,禁 spread)-->
import { longPressState } from '@/shared/long-press.js';
mergeState(..., longPressState());
<!-- template wiring(六事件 + click guard;全部傳 $event:touch synthetic-mouse 抑制契約)-->
<button
@mousedown="longPressStart(() => openRescrape(video,'enrich'), () => rescrapeEnabled(), $event)"
@mouseup="longPressEnd($event)"
@mouseleave="longPressCancel($event)"
@touchstart.passive="longPressStart(cb, enabledFn, $event)"
@touchend="longPressEnd($event)"
@touchcancel="longPressCancel($event)"
@click="longPressClickGuard($event) || tapHandler()">...</button>
搜尋來源卡 batchbar 左側的藥丸型分段控件,取代原有的 track+thumb toggle。
左段「全開」(#sourcesAllOpenToggle)啟用所有 builtin 來源至 cap;
右段「無碼模式」(#sourcesUncensoredToggle)關閉所有有碼來源。
CSS scope:#settings-components .settings-sources-segmented(pill 白名單 §1 第 7 條)。
.is-on 態玻璃高亮(backdrop-filter + shadow-2)。prefers-reduced-motion 停 transition。
<!-- 父層需有 id="settings-components"(CSS scope guard)-->
<div class="settings-sources-segmented">
<button id="sourcesAllOpenToggle"
:class="{ 'is-on': allBuiltinEnabled && !uncensoredMode }"
:aria-pressed="(allBuiltinEnabled && !uncensoredMode) ? 'true' : 'false'"
@click="if (uncensoredMode && enabledCount >= 10) { _flashCapAlert(); } else { enableAllToCap(); }">
全開
</button>
<button id="sourcesUncensoredToggle"
:class="{ 'is-on': uncensoredMode }"
:aria-pressed="uncensoredMode ? 'true' : 'false'"
@click="uncensoredMode = true">
無碼模式
</button>
</div>
掃描來源卡 metatube 區「已連線」狀態列(.settings-mt-connect-status,僅 metatubeConnected 分支渲染;
未連線是 connect form 非 banner)。綠 ✓ + 伺服器 URL + 編輯/中斷/重測 mini 鈕,下接 tier-hint。
CSS scope:#settings-components。
dim 校準(69 T3):近黑底吃光 8% success tint → 提 tint 16% + solid 邊 + 3px 左色條,
綠態明確可辨。切 dim 對照即 P9/P10 驗證點。
🔄 長壓開的進階重刮彈窗 step-1「挑來源」視圖(_rescrape_modal.html)。
重用 .source-pill--action(點擊式非拖曳):auto pill + builtin(有碼/無碼)+ metatube pill(m 角標);
找不到番號就地顯示 .rescrape-inline-error(不換頁)。
dim 校準(69 T2/T4):膠囊有碼/無碼一眼可辨(P1)、inline-error warning 提 tint + solid 邊(P20)。
切 dim 對照即 P1/P20 驗證點。