Files
imagefind/prototype/imagefind-youtube.html
T

959 lines
77 KiB
HTML

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="color-scheme" content="light" />
<title>ImageFind · 视频空间</title>
<style>
:root {
--bg: #f7f8fa;
--surface: #ffffff;
--surface-strong: #f0f3f7;
--text: #111318;
--muted: #667085;
--subtle: #98a2b3;
--line: #e7eaf0;
--line-strong: #d9dee8;
--brand: #2f6cf5;
--brand-strong: #1955dc;
--brand-soft: #edf3ff;
--success: #12a66a;
--danger: #e5484d;
--warning: #e68a00;
--header-h: 68px;
--sidebar-w: 236px;
--sidebar-mini: 82px;
--shadow-sm: 0 1px 2px rgba(16, 24, 40, .04), 0 1px 5px rgba(16, 24, 40, .04);
--shadow-md: 0 16px 46px rgba(16, 24, 40, .12);
--radius: 14px;
--ease: cubic-bezier(.2, .7, .2, 1);
font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
}
* { box-sizing: border-box; }
html { background: var(--bg); scroll-behavior: smooth; }
body { margin: 0; min-width: 320px; min-height: 100vh; color: var(--text); background: var(--bg); }
body.modal-open { overflow: hidden; }
button, input { font: inherit; }
button { border: 0; color: inherit; cursor: pointer; }
img { display: block; max-width: 100%; }
svg.icon { width: 21px; height: 21px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; flex: none; }
.hidden { display: none !important; }
.sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0,0,0,0); border: 0; }
.topbar {
position: fixed;
z-index: 50;
inset: 0 0 auto 0;
height: var(--header-h);
display: grid;
grid-template-columns: var(--sidebar-w) minmax(320px, 680px) 1fr;
align-items: center;
gap: 28px;
padding: 0 24px 0 16px;
background: rgba(255, 255, 255, .92);
border-bottom: 1px solid rgba(231, 234, 240, .9);
backdrop-filter: blur(18px);
transition: grid-template-columns .25s var(--ease);
}
.brand-area { display: flex; align-items: center; gap: 14px; min-width: 0; }
.icon-button {
width: 40px;
height: 40px;
display: grid;
place-items: center;
border-radius: 50%;
background: transparent;
transition: background .18s ease, transform .18s ease;
}
.icon-button:hover { background: var(--surface-strong); }
.icon-button:active { transform: scale(.94); }
.brand { display: flex; align-items: center; gap: 9px; cursor: pointer; user-select: none; }
.brand-symbol {
position: relative;
width: 34px;
height: 27px;
overflow: hidden;
border-radius: 9px;
background: var(--brand);
box-shadow: 0 7px 18px rgba(47, 108, 245, .25);
}
.brand-symbol::before {
content: "";
position: absolute;
inset: 6px 8px;
border: 2px solid #fff;
border-radius: 4px;
opacity: .82;
}
.brand-symbol::after {
content: "";
position: absolute;
top: 9px;
left: 14px;
border-left: 7px solid #fff;
border-top: 5px solid transparent;
border-bottom: 5px solid transparent;
}
.brand-word { font-weight: 760; font-size: 18px; letter-spacing: -.45px; }
.brand-word em { color: var(--brand); font-style: normal; }
.local-tag { padding: 3px 6px; margin-left: -3px; border-radius: 5px; color: var(--muted); background: #f1f3f7; font-size: 9px; font-weight: 750; letter-spacing: .6px; }
.search-wrap { position: relative; width: 100%; }
.global-search {
height: 44px;
display: grid;
grid-template-columns: 1fr 48px;
border: 1px solid var(--line-strong);
border-radius: 24px;
overflow: hidden;
background: var(--surface);
box-shadow: inset 0 1px 2px rgba(16, 24, 40, .025);
transition: border-color .18s ease, box-shadow .18s ease;
}
.global-search:focus-within { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(47, 108, 245, .10); }
.global-search input { min-width: 0; padding: 0 18px; border: 0; outline: 0; color: var(--text); background: transparent; font-size: 14px; }
.global-search input::placeholder { color: var(--subtle); }
.search-submit { display: grid; place-items: center; border-left: 1px solid var(--line); background: #f7f8fa; }
.search-submit:hover { background: #eff2f6; }
.search-spark {
position: absolute;
top: 7px;
right: -42px;
width: 30px;
height: 30px;
display: grid;
place-items: center;
color: var(--brand);
background: var(--brand-soft);
border-radius: 50%;
}
.search-spark .icon { width: 17px; height: 17px; }
.top-actions { display: flex; justify-content: flex-end; align-items: center; gap: 5px; }
.status-button { position: relative; }
.status-button::after { content: ""; position: absolute; top: 7px; right: 7px; width: 7px; height: 7px; border: 2px solid #fff; border-radius: 50%; background: var(--success); }
.avatar-button { width: 36px; height: 36px; margin-left: 8px; border-radius: 50%; color: #174ba9; background: linear-gradient(145deg, #dce9ff, #b9d1ff); font-size: 12px; font-weight: 800; box-shadow: inset 0 0 0 1px #aac8ff; }
.sidebar {
position: fixed;
z-index: 40;
top: var(--header-h);
bottom: 0;
left: 0;
width: var(--sidebar-w);
padding: 14px 12px 24px;
overflow-x: hidden;
overflow-y: auto;
background: var(--surface);
border-right: 1px solid var(--line);
transition: width .25s var(--ease);
scrollbar-width: thin;
}
.nav-group { padding: 6px 0 10px; border-bottom: 1px solid var(--line); }
.nav-group:last-child { border-bottom: 0; }
.nav-title { margin: 12px 12px 8px; color: var(--subtle); font-size: 10px; font-weight: 750; letter-spacing: .85px; white-space: nowrap; }
.nav-item {
width: 100%;
height: 44px;
display: flex;
align-items: center;
gap: 15px;
padding: 0 13px;
border-radius: 10px;
color: #444b59;
background: transparent;
font-size: 13px;
font-weight: 560;
white-space: nowrap;
transition: color .16s ease, background .16s ease;
}
.nav-item:hover { color: var(--text); background: #f5f7fa; }
.nav-item.active { color: var(--brand-strong); background: var(--brand-soft); font-weight: 700; }
.nav-item.active .icon { stroke-width: 2.2; }
.nav-item .nav-badge { margin-left: auto; padding: 2px 6px; border-radius: 8px; color: #667085; background: #edf0f4; font-size: 9px; font-weight: 750; }
.storage-card { margin: 16px 2px 0; padding: 14px; border-radius: 12px; background: linear-gradient(145deg, #f5f8ff, #edf3ff); }
.storage-top { display: flex; align-items: center; justify-content: space-between; color: #3c4b67; font-size: 11px; white-space: nowrap; }
.storage-top strong { color: var(--text); font-size: 11px; }
.storage-bar { height: 5px; margin: 11px 0 8px; overflow: hidden; border-radius: 5px; background: #dce5f5; }
.storage-bar i { display: block; width: 64%; height: 100%; border-radius: inherit; background: var(--brand); }
.storage-card small { color: #78869d; font-size: 10px; white-space: nowrap; }
.main {
min-height: 100vh;
margin-left: var(--sidebar-w);
padding-top: var(--header-h);
transition: margin-left .25s var(--ease);
}
body.sidebar-collapsed .topbar { grid-template-columns: var(--sidebar-mini) minmax(320px, 680px) 1fr; }
body.sidebar-collapsed .sidebar { width: var(--sidebar-mini); padding-inline: 10px; }
body.sidebar-collapsed .main { margin-left: var(--sidebar-mini); }
body.sidebar-collapsed .sidebar .nav-title,
body.sidebar-collapsed .sidebar .nav-item span,
body.sidebar-collapsed .sidebar .nav-badge,
body.sidebar-collapsed .storage-card { display: none; }
body.sidebar-collapsed .sidebar .nav-item { height: 54px; justify-content: center; padding: 0; }
body.sidebar-collapsed .sidebar .nav-group { padding-bottom: 8px; }
.view { display: none; animation: viewIn .3s var(--ease); }
.view.active { display: block; }
@keyframes viewIn { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: none; } }
.page { width: min(1600px, 100%); margin: 0 auto; padding: 24px 30px 48px; }
.chip-scroll { display: flex; gap: 9px; margin-bottom: 26px; overflow-x: auto; scrollbar-width: none; }
.chip-scroll::-webkit-scrollbar { display: none; }
.chip { flex: none; padding: 8px 14px; border-radius: 9px; color: #404756; background: #eceff3; font-size: 12px; font-weight: 620; transition: background .16s, color .16s, transform .16s; }
.chip:hover { background: #e3e7ed; transform: translateY(-1px); }
.chip.active { color: #fff; background: #191b20; }
.chip .dot { display: inline-block; width: 6px; height: 6px; margin: 0 6px 1px 0; border-radius: 50%; background: var(--success); }
.section { margin: 0 0 36px; }
.section-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 18px; margin-bottom: 16px; }
.section-head h1, .section-head h2 { margin: 0; letter-spacing: -.5px; }
.section-head h1 { font-size: 26px; line-height: 1.25; }
.section-head h2 { font-size: 19px; }
.section-kicker { margin: 4px 0 0; color: var(--muted); font-size: 12px; }
.text-button { padding: 7px 9px; border-radius: 8px; color: var(--brand); background: transparent; font-size: 12px; font-weight: 680; }
.text-button:hover { background: var(--brand-soft); }
.continue-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
.continue-card {
position: relative;
min-height: 188px;
overflow: hidden;
border-radius: 16px;
background: #1d2430;
box-shadow: var(--shadow-sm);
cursor: pointer;
isolation: isolate;
}
.continue-card > img { position: absolute; z-index: -2; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: transform .45s var(--ease); }
.continue-card::after { content: ""; position: absolute; z-index: -1; inset: 0; background: linear-gradient(90deg, rgba(8, 12, 20, .79) 0%, rgba(8, 12, 20, .36) 68%, rgba(8, 12, 20, .08)); }
.continue-card:hover > img { transform: scale(1.035); }
.continue-content { width: 70%; padding: 24px; color: #fff; }
.continue-content small { display: inline-flex; align-items: center; gap: 6px; opacity: .78; font-size: 10px; }
.continue-content h3 { margin: 28px 0 8px; font-size: 17px; line-height: 1.35; }
.continue-content p { margin: 0; opacity: .75; font-size: 11px; }
.resume-line { position: absolute; right: 0; bottom: 0; left: 0; height: 4px; background: rgba(255,255,255,.28); }
.resume-line i { display: block; height: 100%; background: var(--brand); }
.continue-play { position: absolute; right: 18px; bottom: 18px; width: 42px; height: 42px; display: grid; place-items: center; border-radius: 50%; color: #111; background: rgba(255,255,255,.92); box-shadow: 0 6px 20px rgba(0,0,0,.22); }
.continue-play .icon { width: 18px; height: 18px; margin-left: 2px; fill: currentColor; }
.video-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 25px 16px; }
.video-card { min-width: 0; cursor: pointer; }
.video-thumb { position: relative; aspect-ratio: 16 / 9; overflow: hidden; border-radius: 12px; background: #dde2ea; }
.video-thumb img { width: 100%; height: 100%; object-fit: cover; transition: transform .35s var(--ease), filter .35s ease; }
.video-card:hover .video-thumb img { transform: scale(1.03); filter: saturate(1.06); }
.duration { position: absolute; right: 7px; bottom: 7px; padding: 3px 5px; border-radius: 4px; color: #fff; background: rgba(8,10,14,.82); font-size: 10px; font-weight: 680; }
.source-mark { position: absolute; top: 8px; left: 8px; display: inline-flex; align-items: center; gap: 5px; padding: 4px 7px; border-radius: 6px; color: #fff; background: rgba(15,18,24,.64); backdrop-filter: blur(7px); font-size: 9px; font-weight: 650; opacity: 0; transform: translateY(-3px); transition: opacity .2s, transform .2s; }
.source-mark .icon { width: 12px; height: 12px; }
.video-card:hover .source-mark { opacity: 1; transform: none; }
.thumb-hover { position: absolute; inset: 0; display: grid; place-items: center; color: #fff; background: rgba(11,15,24,.10); opacity: 0; transition: opacity .2s; }
.thumb-hover span { width: 44px; height: 44px; display: grid; place-items: center; border-radius: 50%; background: rgba(12,16,23,.64); backdrop-filter: blur(7px); transform: scale(.86); transition: transform .2s; }
.thumb-hover .icon { fill: currentColor; }
.video-card:hover .thumb-hover { opacity: 1; }
.video-card:hover .thumb-hover span { transform: scale(1); }
.video-info { display: grid; grid-template-columns: 34px minmax(0, 1fr) 26px; gap: 10px; padding: 11px 2px 0; }
.source-avatar { width: 34px; height: 34px; display: grid; place-items: center; overflow: hidden; border-radius: 50%; color: #fff; background: linear-gradient(145deg, #4b7cf0, #7455da); font-size: 10px; font-weight: 780; }
.source-avatar.green { background: linear-gradient(145deg, #16a778, #087962); }
.source-avatar.orange { background: linear-gradient(145deg, #f0a42e, #d7642f); }
.video-copy { min-width: 0; }
.video-copy h3 { display: -webkit-box; margin: 0 0 6px; overflow: hidden; color: #20232a; font-size: 13px; font-weight: 680; line-height: 1.42; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
.video-copy p { margin: 0; overflow: hidden; color: var(--muted); font-size: 10.5px; line-height: 1.55; text-overflow: ellipsis; white-space: nowrap; }
.more-button { width: 26px; height: 30px; display: grid; place-items: center; border-radius: 50%; background: transparent; opacity: 0; }
.video-card:hover .more-button { opacity: 1; }
.more-button:hover { background: var(--surface-strong); }
.people-row { display: flex; gap: 20px; overflow-x: auto; padding: 1px 2px 4px; scrollbar-width: none; }
.person { flex: 0 0 90px; text-align: center; cursor: pointer; }
.person-avatar { width: 76px; height: 76px; display: grid; place-items: center; margin: 0 auto 9px; overflow: hidden; border-radius: 50%; background: #dde5f3; box-shadow: 0 0 0 3px #fff, 0 0 0 4px var(--line); transition: transform .2s; }
.person:hover .person-avatar { transform: translateY(-2px); }
.person-avatar img { width: 100%; height: 100%; object-fit: cover; }
.person strong { display: block; overflow: hidden; font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }
.person small { color: var(--subtle); font-size: 9px; }
.search-page { max-width: 1280px; }
.search-heading { display: flex; align-items: center; justify-content: space-between; gap: 20px; margin: 4px 0 18px; }
.search-heading h1 { margin: 0 0 6px; font-size: 23px; letter-spacing: -.45px; }
.search-heading p { margin: 0; color: var(--muted); font-size: 11px; }
.query-type { display: inline-flex; align-items: center; gap: 6px; margin-left: 7px; padding: 4px 8px; border-radius: 7px; color: #235ed7; background: var(--brand-soft); font-size: 10px; vertical-align: 3px; }
.query-type .icon { width: 13px; height: 13px; }
.result-count { color: var(--subtle); font-size: 11px; white-space: nowrap; }
.filter-row { display: flex; gap: 8px; margin-bottom: 22px; overflow-x: auto; scrollbar-width: none; }
.filter-pill { flex: none; display: inline-flex; align-items: center; gap: 7px; padding: 8px 12px; border: 1px solid var(--line); border-radius: 9px; color: #4a5260; background: #fff; font-size: 10.5px; font-weight: 620; }
.filter-pill:hover { border-color: #cfd5df; background: #fafbfc; }
.filter-pill.active { color: #1d57cf; border-color: #c9d9ff; background: var(--brand-soft); }
.filter-pill .icon { width: 14px; height: 14px; }
.result-list { display: grid; gap: 22px; }
.search-result { display: grid; grid-template-columns: minmax(260px, 360px) minmax(0, 1fr); gap: 18px; cursor: pointer; }
.search-result .video-thumb { border-radius: 12px; }
.result-detail { min-width: 0; padding: 3px 0; }
.result-detail h2 { margin: 0 0 7px; font-size: 16px; font-weight: 680; line-height: 1.4; }
.result-detail > p { margin: 0 0 11px; color: var(--muted); font-size: 10.5px; line-height: 1.6; }
.result-source { display: flex; align-items: center; gap: 8px; margin-bottom: 13px; color: #4f5868; font-size: 10.5px; }
.result-source .source-avatar { width: 25px; height: 25px; font-size: 8px; }
.match-box { max-width: 660px; padding: 10px 12px; border: 1px solid #d9e4ff; border-radius: 10px; background: #f6f9ff; }
.match-label { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; color: #315ea9; font-size: 9.5px; font-weight: 700; }
.match-label .icon { width: 13px; height: 13px; }
.moment-row { display: flex; flex-wrap: wrap; gap: 7px; }
.moment { display: inline-flex; align-items: center; gap: 5px; padding: 5px 7px; border-radius: 6px; color: #315da8; background: #e8f0ff; font-size: 9.5px; font-weight: 620; }
.moment b { color: #174eb4; }
.score { margin-left: auto; color: var(--success); font-size: 9px; font-weight: 760; }
.player-page { max-width: 1500px; padding-top: 22px; }
.player-layout { display: grid; grid-template-columns: minmax(0, 1fr) 360px; gap: 24px; align-items: start; }
.player-column { min-width: 0; }
.player-stage { position: relative; aspect-ratio: 16 / 9; overflow: hidden; border-radius: 14px; color: #fff; background: #101319; box-shadow: 0 18px 50px rgba(21, 27, 38, .12); }
.player-stage > img { width: 100%; height: 100%; object-fit: cover; opacity: .96; }
.player-stage::after { content: ""; position: absolute; inset: 45% 0 0; background: linear-gradient(transparent, rgba(4,6,9,.78)); pointer-events: none; }
.center-play { position: absolute; z-index: 3; top: 50%; left: 50%; width: 68px; height: 68px; display: grid; place-items: center; border-radius: 50%; color: #fff; background: rgba(15,18,24,.58); backdrop-filter: blur(10px); transform: translate(-50%, -50%); box-shadow: 0 8px 30px rgba(0,0,0,.25); }
.center-play .icon { width: 29px; height: 29px; margin-left: 3px; fill: currentColor; }
.player-stage.playing .center-play { opacity: 0; pointer-events: none; }
.player-top { position: absolute; z-index: 3; top: 0; right: 0; left: 0; display: flex; align-items: center; gap: 10px; padding: 18px; background: linear-gradient(rgba(0,0,0,.38), transparent); opacity: 0; transition: opacity .2s; }
.player-stage:hover .player-top { opacity: 1; }
.player-top button { color: #fff; background: transparent; }
.player-top span { overflow: hidden; font-size: 12px; font-weight: 620; text-overflow: ellipsis; white-space: nowrap; }
.player-controls { position: absolute; z-index: 4; right: 16px; bottom: 12px; left: 16px; }
.video-progress { position: relative; height: 14px; display: flex; align-items: center; cursor: pointer; }
.video-progress-track { width: 100%; height: 3px; border-radius: 4px; background: rgba(255,255,255,.38); }
.video-progress-fill { position: relative; width: 31%; height: 100%; background: var(--brand); }
.video-progress-fill::after { content: ""; position: absolute; top: 50%; right: -5px; width: 10px; height: 10px; border-radius: 50%; background: #fff; transform: translateY(-50%); box-shadow: 0 0 0 2px var(--brand); }
.ai-pin { position: absolute; top: 3px; width: 4px; height: 8px; border-radius: 4px; background: #ffd66b; box-shadow: 0 0 0 2px rgba(255, 214, 107, .23); }
.control-row { height: 34px; display: flex; align-items: center; gap: 13px; }
.control-row button { display: grid; place-items: center; padding: 3px; color: #fff; background: transparent; }
.control-row .icon { width: 19px; height: 19px; }
.control-time { font-size: 10px; font-variant-numeric: tabular-nums; }
.control-spacer { flex: 1; }
.quality { padding: 3px 5px !important; border: 1px solid rgba(255,255,255,.7) !important; border-radius: 4px; font-size: 8px; font-weight: 800; }
.player-heading { padding: 18px 2px 12px; border-bottom: 1px solid var(--line); }
.player-heading h1 { margin: 0 0 13px; font-size: 20px; line-height: 1.42; letter-spacing: -.35px; }
.player-meta-row { display: flex; align-items: center; gap: 12px; }
.owner { display: flex; align-items: center; gap: 9px; margin-right: auto; }
.owner-copy strong { display: block; font-size: 11.5px; }
.owner-copy span { display: block; margin-top: 2px; color: var(--muted); font-size: 9.5px; }
.action-group { display: flex; gap: 8px; }
.pill-action { height: 36px; display: inline-flex; align-items: center; gap: 7px; padding: 0 13px; border-radius: 18px; color: #343a45; background: #eceff3; font-size: 10.5px; font-weight: 650; }
.pill-action:hover { background: #e3e7ec; }
.pill-action.active { color: #1f58cc; background: var(--brand-soft); }
.pill-action .icon { width: 16px; height: 16px; }
.description-card { margin: 14px 0; padding: 14px 16px; border-radius: 12px; background: #eceff3; font-size: 10.5px; line-height: 1.65; }
.description-card strong { margin-right: 10px; }
.description-card p { margin: 7px 0 0; color: #515967; }
.ai-panel { overflow: hidden; border: 1px solid #dbe4f6; border-radius: 14px; background: #fff; }
.ai-panel-head { display: flex; align-items: center; gap: 10px; padding: 14px 16px; border-bottom: 1px solid #e7edf8; background: linear-gradient(90deg, #f7faff, #fff); }
.ai-logo { width: 31px; height: 31px; display: grid; place-items: center; border-radius: 9px; color: #fff; background: linear-gradient(145deg, #4a7cf4, #6756dc); box-shadow: 0 7px 16px rgba(61, 105, 230, .19); }
.ai-logo .icon { width: 16px; height: 16px; }
.ai-panel-head div:nth-child(2) { flex: 1; }
.ai-panel-head strong { display: block; font-size: 11.5px; }
.ai-panel-head p { margin: 3px 0 0; color: var(--muted); font-size: 9.5px; }
.ai-search { width: min(310px, 38%); height: 34px; display: flex; align-items: center; gap: 7px; padding: 0 10px; border: 1px solid var(--line); border-radius: 9px; background: #fff; }
.ai-search .icon { width: 14px; height: 14px; color: var(--muted); }
.ai-search input { width: 100%; border: 0; outline: 0; font-size: 10px; }
.timeline-list { padding: 4px 16px; }
.timeline-item { display: grid; grid-template-columns: 56px 4px minmax(0, 1fr) auto; gap: 12px; align-items: center; min-height: 62px; border-bottom: 1px solid #eef1f5; cursor: pointer; }
.timeline-item:last-child { border-bottom: 0; }
.timeline-time { color: var(--brand); font-size: 10.5px; font-weight: 750; font-variant-numeric: tabular-nums; }
.timeline-node { width: 4px; height: 4px; border-radius: 50%; background: #afc4ee; box-shadow: 0 0 0 4px #edf3ff; }
.timeline-copy strong { display: block; margin-bottom: 4px; font-size: 10.5px; }
.timeline-copy span { display: block; overflow: hidden; color: var(--muted); font-size: 9.5px; text-overflow: ellipsis; white-space: nowrap; }
.timeline-play { width: 28px; height: 28px; display: grid; place-items: center; border-radius: 50%; color: var(--brand); background: var(--brand-soft); opacity: 0; }
.timeline-item:hover .timeline-play { opacity: 1; }
.timeline-play .icon { width: 13px; height: 13px; fill: currentColor; }
.related-column { min-width: 0; }
.related-head { display: flex; align-items: center; justify-content: space-between; margin: 0 0 12px; }
.related-head h2 { margin: 0; font-size: 14px; }
.autoplay { display: flex; align-items: center; gap: 6px; color: var(--muted); font-size: 9.5px; }
.switch { position: relative; width: 29px; height: 17px; border-radius: 10px; background: var(--brand); }
.switch::after { content: ""; position: absolute; top: 3px; right: 3px; width: 11px; height: 11px; border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.2); }
.related-list { display: grid; gap: 12px; }
.related-card { display: grid; grid-template-columns: 168px minmax(0, 1fr); gap: 10px; cursor: pointer; }
.related-card .video-thumb { border-radius: 9px; }
.related-card h3 { display: -webkit-box; margin: 2px 0 6px; overflow: hidden; font-size: 11px; line-height: 1.4; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
.related-card p { margin: 0 0 3px; color: var(--muted); font-size: 9px; line-height: 1.45; }
.image-modal { position: fixed; z-index: 100; inset: 0; display: grid; place-items: center; padding: 20px; background: rgba(23, 29, 40, .42); backdrop-filter: blur(5px); opacity: 0; pointer-events: none; transition: opacity .2s; }
.image-modal.open { opacity: 1; pointer-events: auto; }
.modal-card { width: min(540px, 100%); padding: 22px; border-radius: 18px; background: #fff; box-shadow: var(--shadow-md); transform: translateY(8px) scale(.985); transition: transform .25s var(--ease); }
.image-modal.open .modal-card { transform: none; }
.modal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 18px; }
.modal-head h2 { margin: 0; font-size: 17px; }
.close-button { width: 34px; height: 34px; display: grid; place-items: center; border-radius: 50%; background: #f1f3f6; }
.drop-zone { min-height: 250px; display: grid; place-items: center; padding: 28px; border: 1.5px dashed #b8c4d8; border-radius: 14px; background: #f8faff; text-align: center; }
.drop-icon { width: 54px; height: 54px; display: grid; place-items: center; margin: 0 auto 14px; border-radius: 15px; color: var(--brand); background: var(--brand-soft); }
.drop-icon .icon { width: 25px; height: 25px; }
.drop-zone h3 { margin: 0 0 7px; font-size: 13px; }
.drop-zone p { margin: 0 0 18px; color: var(--muted); font-size: 10px; }
.primary-button { height: 37px; display: inline-flex; align-items: center; gap: 7px; padding: 0 15px; border-radius: 9px; color: #fff; background: var(--brand); font-size: 10.5px; font-weight: 680; box-shadow: 0 6px 15px rgba(47,108,245,.18); }
.primary-button:hover { background: var(--brand-strong); }
.privacy-note { display: flex; align-items: center; justify-content: center; gap: 6px; margin-top: 13px; color: var(--subtle); font-size: 9px; }
.privacy-note .icon { width: 12px; height: 12px; }
.profile-menu { position: fixed; z-index: 80; top: 59px; right: 20px; width: 240px; padding: 8px; border: 1px solid var(--line); border-radius: 13px; background: #fff; box-shadow: var(--shadow-md); opacity: 0; pointer-events: none; transform: translateY(-5px); transition: .18s; }
.profile-menu.open { opacity: 1; pointer-events: auto; transform: none; }
.profile-head { display: flex; gap: 10px; padding: 10px; border-bottom: 1px solid var(--line); }
.profile-head .avatar-button { flex: none; margin: 0; }
.profile-head strong { display: block; font-size: 11px; }
.profile-head span { color: var(--muted); font-size: 9px; }
.profile-menu button:not(.avatar-button) { width: 100%; display: flex; align-items: center; gap: 10px; padding: 9px 10px; border-radius: 8px; color: #424a57; background: transparent; font-size: 10.5px; text-align: left; }
.profile-menu button:hover { background: #f4f6f8; }
.profile-menu button .icon { width: 16px; height: 16px; }
.toast { position: fixed; z-index: 120; right: 24px; bottom: 24px; display: flex; align-items: center; gap: 9px; max-width: 340px; padding: 12px 15px; border-radius: 11px; color: #fff; background: #1c2028; box-shadow: var(--shadow-md); font-size: 10.5px; opacity: 0; pointer-events: none; transform: translateY(12px); transition: .25s var(--ease); }
.toast.show { opacity: 1; transform: none; }
.toast .icon { width: 16px; height: 16px; color: #8db1ff; }
@media (max-width: 1280px) {
.video-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.continue-card:last-child { display: none; }
.continue-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.player-layout { grid-template-columns: minmax(0, 1fr) 320px; gap: 18px; }
.related-card { grid-template-columns: 145px minmax(0, 1fr); }
}
@media (max-width: 1040px) {
:root { --sidebar-w: 210px; }
.topbar { grid-template-columns: var(--sidebar-w) minmax(260px, 1fr) auto; gap: 18px; }
.search-spark { display: none; }
.page { padding-inline: 22px; }
.player-layout { grid-template-columns: 1fr; }
.related-column { padding-top: 8px; }
.related-list { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.related-card { grid-template-columns: 160px minmax(0, 1fr); }
.ai-search { width: 42%; }
}
@media (max-width: 780px) {
:root { --header-h: 60px; }
.topbar { grid-template-columns: auto 1fr auto; gap: 9px; padding: 0 12px; }
#menuToggle, .local-tag, .top-actions .icon-button:first-child, .top-actions .status-button { display: none; }
.brand-word { display: none; }
.brand-area { gap: 0; }
.brand-symbol { width: 31px; height: 25px; }
.global-search { height: 39px; }
.global-search input { padding-inline: 13px; font-size: 12px; }
.global-search { grid-template-columns: 1fr 40px; }
.top-actions { gap: 0; }
.avatar-button { width: 32px; height: 32px; margin-left: 2px; }
.sidebar { top: auto; right: 0; bottom: 0; width: 100%; height: 62px; padding: 5px 8px; overflow: visible; border-top: 1px solid var(--line); border-right: 0; }
.sidebar .nav-group { display: none; border: 0; }
.sidebar .nav-group:first-child { display: grid; grid-template-columns: repeat(4, 1fr); padding: 0; }
.sidebar .nav-group:first-child .nav-item:nth-child(n+5) { display: none; }
.sidebar .nav-item { height: 51px; flex-direction: column; justify-content: center; gap: 2px; padding: 0; border-radius: 8px; font-size: 8.5px; }
.sidebar .nav-item .icon { width: 20px; height: 20px; }
.sidebar .nav-item.active { background: transparent; }
.sidebar .nav-item span { display: block !important; }
.sidebar .nav-title, .storage-card, .nav-badge { display: none; }
.main, body.sidebar-collapsed .main { margin-left: 0; padding-bottom: 62px; }
body.sidebar-collapsed .sidebar { width: 100%; padding: 5px 8px; }
body.sidebar-collapsed .sidebar .nav-item { height: 51px; }
.page { padding: 18px 14px 34px; }
.chip-scroll { margin-bottom: 21px; }
.section { margin-bottom: 31px; }
.section-head h1 { font-size: 22px; }
.continue-row { display: flex; overflow-x: auto; margin-right: -14px; padding-right: 14px; scrollbar-width: none; }
.continue-card, .continue-card:last-child { display: block; flex: 0 0 82vw; min-height: 178px; }
.video-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px 10px; }
.video-info { grid-template-columns: minmax(0, 1fr) 20px; gap: 5px; }
.video-info .source-avatar { display: none; }
.video-copy h3 { font-size: 11.5px; }
.more-button { opacity: 1; }
.people-row { margin-right: -14px; }
.search-heading { align-items: flex-end; }
.search-heading h1 { font-size: 19px; }
.search-result { grid-template-columns: 42% minmax(0, 1fr); gap: 11px; }
.result-detail { padding: 0; }
.result-detail h2 { display: -webkit-box; overflow: hidden; font-size: 12px; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
.result-detail > p { display: none; }
.result-source { margin-bottom: 7px; }
.match-box { padding: 7px 8px; }
.match-label { margin-bottom: 5px; }
.moment-row .moment:nth-child(n+2) { display: none; }
.player-page { padding: 0 0 30px; }
.player-stage { border-radius: 0; }
.player-heading, .description-card, .ai-panel { margin-right: 14px; margin-left: 14px; }
.player-heading { padding-top: 15px; }
.player-heading h1 { font-size: 16px; }
.player-meta-row { align-items: flex-start; flex-wrap: wrap; }
.owner { flex: 1 1 auto; }
.action-group { width: 100%; overflow-x: auto; }
.pill-action { flex: none; }
.ai-panel-head { flex-wrap: wrap; }
.ai-search { width: 100%; }
.timeline-item { grid-template-columns: 48px 4px minmax(0, 1fr); }
.timeline-play { display: none; }
.related-column { padding: 8px 14px 0; }
.related-list { grid-template-columns: 1fr; }
.related-card { grid-template-columns: 42% minmax(0, 1fr); }
.toast { right: 14px; bottom: 76px; left: 14px; max-width: none; }
}
@media (max-width: 430px) {
.video-grid { grid-template-columns: 1fr 1fr; }
.video-thumb { border-radius: 9px; }
.duration { right: 5px; bottom: 5px; }
.section-head h2 { font-size: 17px; }
.continue-content { width: 78%; padding: 20px; }
.continue-content h3 { margin-top: 24px; }
.search-result { grid-template-columns: 44% minmax(0, 1fr); }
.result-source .source-avatar { display: none; }
.source-mark { display: none; }
}
</style>
</head>
<body>
<svg aria-hidden="true" class="hidden">
<symbol id="i-menu" viewBox="0 0 24 24"><path d="M4 6h16M4 12h16M4 18h16"/></symbol>
<symbol id="i-search" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="m20 20-4-4"/></symbol>
<symbol id="i-spark" viewBox="0 0 24 24"><path d="M12 3 10.5 8.5 5 10l5.5 1.5L12 17l1.5-5.5L19 10l-5.5-1.5L12 3Z"/><path d="m5 16-.7 2.3L2 19l2.3.7L5 22l.7-2.3L8 19l-2.3-.7L5 16Z"/></symbol>
<symbol id="i-home" viewBox="0 0 24 24"><path d="m3 11 9-8 9 8v9a1 1 0 0 1-1 1h-5v-7H9v7H4a1 1 0 0 1-1-1v-9Z"/></symbol>
<symbol id="i-compass" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="m15.5 8.5-2.1 4.9-4.9 2.1 2.1-4.9 4.9-2.1Z"/></symbol>
<symbol id="i-library" viewBox="0 0 24 24"><path d="M4 5h3v15H4zM10 5h3v15h-3zM16 7l3-.8 3.4 13-3 .8z"/></symbol>
<symbol id="i-clock" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></symbol>
<symbol id="i-heart" viewBox="0 0 24 24"><path d="M20.8 4.8a5.5 5.5 0 0 0-7.8 0L12 5.9l-1.1-1.1a5.5 5.5 0 0 0-7.8 7.8L12 21l8.8-8.4a5.5 5.5 0 0 0 0-7.8Z"/></symbol>
<symbol id="i-users" viewBox="0 0 24 24"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.9M16 3.1a4 4 0 0 1 0 7.8"/></symbol>
<symbol id="i-folder" viewBox="0 0 24 24"><path d="M3 6a2 2 0 0 1 2-2h5l2 3h7a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6Z"/></symbol>
<symbol id="i-cloud" viewBox="0 0 24 24"><path d="M17.5 19H6a4 4 0 0 1-.5-8A6.5 6.5 0 0 1 18 9a5 5 0 0 1-.5 10Z"/></symbol>
<symbol id="i-settings" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.9l.1.1-2.8 2.8-.1-.1a1.7 1.7 0 0 0-1.9-.3 1.7 1.7 0 0 0-1 1.6v.2h-4V21a1.7 1.7 0 0 0-1-1.6 1.7 1.7 0 0 0-1.9.3l-.1.1L4.2 17l.1-.1a1.7 1.7 0 0 0 .3-1.9A1.7 1.7 0 0 0 3 14H2.8v-4H3a1.7 1.7 0 0 0 1.6-1 1.7 1.7 0 0 0-.3-1.9L4.2 7 7 4.2l.1.1A1.7 1.7 0 0 0 9 4.6a1.7 1.7 0 0 0 1-1.6v-.2h4V3a1.7 1.7 0 0 0 1 1.6 1.7 1.7 0 0 0 1.9-.3l.1-.1L19.8 7l-.1.1a1.7 1.7 0 0 0-.3 1.9 1.7 1.7 0 0 0 1.6 1h.2v4H21a1.7 1.7 0 0 0-1.6 1Z"/></symbol>
<symbol id="i-play" viewBox="0 0 24 24"><path d="m8 5 11 7-11 7V5Z"/></symbol>
<symbol id="i-pause" viewBox="0 0 24 24"><path d="M8 5v14M16 5v14"/></symbol>
<symbol id="i-more" viewBox="0 0 24 24"><circle cx="5" cy="12" r="1" fill="currentColor" stroke="none"/><circle cx="12" cy="12" r="1" fill="currentColor" stroke="none"/><circle cx="19" cy="12" r="1" fill="currentColor" stroke="none"/></symbol>
<symbol id="i-image" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="16" rx="2"/><circle cx="8.5" cy="9" r="1.5"/><path d="m21 15-5-5L5 20"/></symbol>
<symbol id="i-bell" viewBox="0 0 24 24"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 7-3 9h18c0-2-3-2-3-9M10 21h4"/></symbol>
<symbol id="i-activity" viewBox="0 0 24 24"><path d="M3 12h4l2-6 4 12 2-6h6"/></symbol>
<symbol id="i-sliders" viewBox="0 0 24 24"><path d="M4 7h10M18 7h2M4 17h2M10 17h10"/><circle cx="16" cy="7" r="2"/><circle cx="8" cy="17" r="2"/></symbol>
<symbol id="i-chevron" viewBox="0 0 24 24"><path d="m9 18 6-6-6-6"/></symbol>
<symbol id="i-back" viewBox="0 0 24 24"><path d="m15 18-6-6 6-6"/></symbol>
<symbol id="i-volume" viewBox="0 0 24 24"><path d="M11 5 6 9H3v6h3l5 4V5ZM15 9a4 4 0 0 1 0 6M18 6a8 8 0 0 1 0 12"/></symbol>
<symbol id="i-fullscreen" viewBox="0 0 24 24"><path d="M8 3H3v5M16 3h5v5M8 21H3v-5M16 21h5v-5"/></symbol>
<symbol id="i-thumbs" viewBox="0 0 24 24"><path d="M7 10v11H3V10h4Zm0 9h10.8a2 2 0 0 0 2-1.6l1.1-5A2 2 0 0 0 19 10h-5l1-4.5A2.5 2.5 0 0 0 12.6 3L7 10Z"/></symbol>
<symbol id="i-bookmark" viewBox="0 0 24 24"><path d="M6 4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v18l-6-4-6 4V4Z"/></symbol>
<symbol id="i-share" viewBox="0 0 24 24"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><path d="m8.6 10.5 6.8-4M8.6 13.5l6.8 4"/></symbol>
<symbol id="i-upload" viewBox="0 0 24 24"><path d="M12 16V4m0 0L7 9m5-5 5 5M4 15v4a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-4"/></symbol>
<symbol id="i-lock" viewBox="0 0 24 24"><rect x="4" y="10" width="16" height="11" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></symbol>
<symbol id="i-x" viewBox="0 0 24 24"><path d="m6 6 12 12M18 6 6 18"/></symbol>
<symbol id="i-check" viewBox="0 0 24 24"><path d="m5 12 4 4L19 6"/></symbol>
<symbol id="i-monitor" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="14" rx="2"/><path d="M8 22h8M12 18v4"/></symbol>
<symbol id="i-logout" viewBox="0 0 24 24"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9"/></symbol>
</svg>
<header class="topbar">
<div class="brand-area">
<button class="icon-button" id="menuToggle" aria-label="折叠侧栏"><svg class="icon"><use href="#i-menu"/></svg></button>
<div class="brand" data-view-target="home" role="button" tabindex="0" aria-label="返回首页">
<span class="brand-symbol"></span>
<span class="brand-word">Image<em>Find</em></span>
<span class="local-tag">LOCAL</span>
</div>
</div>
<div class="search-wrap">
<form class="global-search" id="globalSearch">
<input id="searchInput" value="" placeholder="搜索你的视频、人物或画面内容" autocomplete="off" />
<button class="search-submit" aria-label="搜索"><svg class="icon"><use href="#i-search"/></svg></button>
</form>
<button class="search-spark" id="imageSearchButton" aria-label="以图搜视频" title="以图搜视频"><svg class="icon"><use href="#i-image"/></svg></button>
</div>
<div class="top-actions">
<button class="icon-button" id="topImageButton" aria-label="以图搜视频"><svg class="icon"><use href="#i-image"/></svg></button>
<button class="icon-button status-button" id="taskButton" aria-label="索引任务"><svg class="icon"><use href="#i-activity"/></svg></button>
<button class="icon-button" aria-label="通知"><svg class="icon"><use href="#i-bell"/></svg></button>
<button class="avatar-button" id="profileButton" aria-label="用户菜单">NX</button>
</div>
</header>
<aside class="sidebar">
<nav>
<div class="nav-group">
<button class="nav-item active" data-view-target="home"><svg class="icon"><use href="#i-home"/></svg><span>首页</span></button>
<button class="nav-item" data-view-target="explore"><svg class="icon"><use href="#i-compass"/></svg><span>探索</span></button>
<button class="nav-item" data-view-target="library"><svg class="icon"><use href="#i-library"/></svg><span>资料库</span></button>
<button class="nav-item" data-view-target="history"><svg class="icon"><use href="#i-clock"/></svg><span>最近观看</span></button>
<button class="nav-item" data-view-target="favorites"><svg class="icon"><use href="#i-heart"/></svg><span>收藏</span><span class="nav-badge">18</span></button>
</div>
<div class="nav-group">
<p class="nav-title">智能分类</p>
<button class="nav-item" data-view-target="people"><svg class="icon"><use href="#i-users"/></svg><span>人物</span></button>
<button class="nav-item" data-view-target="moments"><svg class="icon"><use href="#i-spark"/></svg><span>精彩时刻</span></button>
</div>
<div class="nav-group">
<p class="nav-title">视频来源</p>
<button class="nav-item" data-view-target="source-local"><svg class="icon"><use href="#i-folder"/></svg><span>家庭影像</span><span class="nav-badge">268</span></button>
<button class="nav-item" data-view-target="source-webdav"><svg class="icon"><use href="#i-cloud"/></svg><span>Cloud WebDAV</span><span class="nav-badge">84</span></button>
<button class="nav-item" data-view-target="settings"><svg class="icon"><use href="#i-settings"/></svg><span>设置</span></button>
</div>
</nav>
<div class="storage-card">
<div class="storage-top"><span>视频空间</span><strong>352 个视频</strong></div>
<div class="storage-bar"><i></i></div>
<small>已使用 1.28 TB / 2 TB</small>
</div>
</aside>
<main class="main">
<section class="view active" id="view-home">
<div class="page">
<div class="chip-scroll" aria-label="视频分类">
<button class="chip active">全部</button>
<button class="chip"><span class="dot"></span>最近新增</button>
<button class="chip">家庭记录</button>
<button class="chip">旅行</button>
<button class="chip">宠物</button>
<button class="chip">美食</button>
<button class="chip">演出</button>
<button class="chip">屏幕录制</button>
<button class="chip">来自 WebDAV</button>
</div>
<section class="section">
<div class="section-head">
<div><h1>下午好,继续看吧</h1><p class="section-kicker">从上次离开的地方继续,进度只保存在你的 NAS</p></div>
<button class="text-button" data-view-target="history">查看观看记录</button>
</div>
<div class="continue-row">
<article class="continue-card playable" data-title="青海湖自驾 · 第三天" data-art="coast" data-duration="18:42">
<img data-art="coast" alt="青海湖自驾" />
<div class="continue-content"><small><svg class="icon" style="width:12px"><use href="#i-clock"/></svg>昨天看到 08:16</small><h3>青海湖自驾 · 第三天</h3><p>家庭影像 · 还剩 10 分钟</p></div>
<span class="continue-play"><svg class="icon"><use href="#i-play"/></svg></span><span class="resume-line"><i style="width:44%"></i></span>
</article>
<article class="continue-card playable" data-title="小橘第一次去公园" data-art="pet" data-duration="06:28">
<img data-art="pet" alt="小橘第一次去公园" />
<div class="continue-content"><small><svg class="icon" style="width:12px"><use href="#i-clock"/></svg>周六看到 02:41</small><h3>小橘第一次去公园</h3><p>手机相册 · 还剩 3 分钟</p></div>
<span class="continue-play"><svg class="icon"><use href="#i-play"/></svg></span><span class="resume-line"><i style="width:52%"></i></span>
</article>
<article class="continue-card playable" data-title="2026 新年音乐会" data-art="concert" data-duration="01:26:08">
<img data-art="concert" alt="新年音乐会" />
<div class="continue-content"><small><svg class="icon" style="width:12px"><use href="#i-clock"/></svg>3 天前看到 38:21</small><h3>2026 新年音乐会</h3><p>Cloud WebDAV · 还剩 47 分钟</p></div>
<span class="continue-play"><svg class="icon"><use href="#i-play"/></svg></span><span class="resume-line"><i style="width:45%"></i></span>
</article>
</div>
</section>
<section class="section">
<div class="section-head"><div><h2>最近新增</h2><p class="section-kicker">来自家庭影像和 Cloud WebDAV</p></div><button class="text-button">查看全部</button></div>
<div class="video-grid">
<article class="video-card playable" data-title="傍晚的海边,风刚刚好" data-art="sunset" data-duration="12:36">
<div class="video-thumb"><img data-art="sunset" alt="海边日落"/><span class="source-mark"><svg class="icon"><use href="#i-folder"/></svg>家庭影像</span><span class="duration">12:36</span><span class="thumb-hover"><span><svg class="icon"><use href="#i-play"/></svg></span></span></div>
<div class="video-info"><span class="source-avatar"></span><div class="video-copy"><h3>傍晚的海边,风刚刚好</h3><p>家庭影像 · 今天 14:32 · 4K</p></div><button class="more-button"><svg class="icon"><use href="#i-more"/></svg></button></div>
</article>
<article class="video-card playable" data-title="成都周末 City Walk" data-art="city" data-duration="08:15">
<div class="video-thumb"><img data-art="city" alt="成都街头"/><span class="source-mark"><svg class="icon"><use href="#i-cloud"/></svg>Cloud WebDAV</span><span class="duration">08:15</span><span class="thumb-hover"><span><svg class="icon"><use href="#i-play"/></svg></span></span></div>
<div class="video-info"><span class="source-avatar green"></span><div class="video-copy"><h3>成都周末 City Walk</h3><p>Cloud WebDAV · 今天 11:08 · 1080P</p></div><button class="more-button"><svg class="icon"><use href="#i-more"/></svg></button></div>
</article>
<article class="video-card playable" data-title="北海道雪场 VLOG" data-art="snow" data-duration="24:03">
<div class="video-thumb"><img data-art="snow" alt="北海道雪场"/><span class="source-mark"><svg class="icon"><use href="#i-cloud"/></svg>Cloud WebDAV</span><span class="duration">24:03</span><span class="thumb-hover"><span><svg class="icon"><use href="#i-play"/></svg></span></span></div>
<div class="video-info"><span class="source-avatar green"></span><div class="video-copy"><h3>北海道雪场 VLOG</h3><p>Cloud WebDAV · 昨天 · 4K</p></div><button class="more-button"><svg class="icon"><use href="#i-more"/></svg></button></div>
</article>
<article class="video-card playable" data-title="外婆的夏日菜园" data-art="garden" data-duration="05:48">
<div class="video-thumb"><img data-art="garden" alt="夏日菜园"/><span class="source-mark"><svg class="icon"><use href="#i-folder"/></svg>家庭影像</span><span class="duration">05:48</span><span class="thumb-hover"><span><svg class="icon"><use href="#i-play"/></svg></span></span></div>
<div class="video-info"><span class="source-avatar"></span><div class="video-copy"><h3>外婆的夏日菜园</h3><p>家庭影像 · 昨天 · 1080P</p></div><button class="more-button"><svg class="icon"><use href="#i-more"/></svg></button></div>
</article>
<article class="video-card playable" data-title="在家做一碗红烧牛肉面" data-art="food" data-duration="09:52">
<div class="video-thumb"><img data-art="food" alt="牛肉面"/><span class="source-mark"><svg class="icon"><use href="#i-folder"/></svg>相机导入</span><span class="duration">09:52</span><span class="thumb-hover"><span><svg class="icon"><use href="#i-play"/></svg></span></span></div>
<div class="video-info"><span class="source-avatar orange"></span><div class="video-copy"><h3>在家做一碗红烧牛肉面</h3><p>相机导入 · 7 月 24 日 · 4K</p></div><button class="more-button"><svg class="icon"><use href="#i-more"/></svg></button></div>
</article>
<article class="video-card playable" data-title="小橘第一次去公园" data-art="pet" data-duration="06:28">
<div class="video-thumb"><img data-art="pet" alt="小橘在公园"/><span class="source-mark"><svg class="icon"><use href="#i-folder"/></svg>手机相册</span><span class="duration">06:28</span><span class="thumb-hover"><span><svg class="icon"><use href="#i-play"/></svg></span></span></div>
<div class="video-info"><span class="source-avatar"></span><div class="video-copy"><h3>小橘第一次去公园</h3><p>手机相册 · 7 月 23 日 · 1080P</p></div><button class="more-button"><svg class="icon"><use href="#i-more"/></svg></button></div>
</article>
<article class="video-card playable" data-title="2026 新年音乐会 · 完整版" data-art="concert" data-duration="01:26:08">
<div class="video-thumb"><img data-art="concert" alt="新年音乐会"/><span class="source-mark"><svg class="icon"><use href="#i-cloud"/></svg>Cloud WebDAV</span><span class="duration">1:26:08</span><span class="thumb-hover"><span><svg class="icon"><use href="#i-play"/></svg></span></span></div>
<div class="video-info"><span class="source-avatar green"></span><div class="video-copy"><h3>2026 新年音乐会 · 完整版</h3><p>Cloud WebDAV · 7 月 22 日 · 4K</p></div><button class="more-button"><svg class="icon"><use href="#i-more"/></svg></button></div>
</article>
<article class="video-card playable" data-title="第一次看见银河" data-art="space" data-duration="03:19">
<div class="video-thumb"><img data-art="space" alt="银河星空"/><span class="source-mark"><svg class="icon"><use href="#i-folder"/></svg>相机导入</span><span class="duration">03:19</span><span class="thumb-hover"><span><svg class="icon"><use href="#i-play"/></svg></span></span></div>
<div class="video-info"><span class="source-avatar orange"></span><div class="video-copy"><h3>第一次看见银河</h3><p>相机导入 · 7 月 20 日 · 4K</p></div><button class="more-button"><svg class="icon"><use href="#i-more"/></svg></button></div>
</article>
</div>
</section>
<section class="section">
<div class="section-head"><div><h2>常见的人</h2><p class="section-kicker">由本地 AI 自动整理,只对你可见</p></div><button class="text-button" data-view-target="people">管理人物</button></div>
<div class="people-row">
<article class="person"><span class="person-avatar"><img data-person="1" alt="妈妈"/></span><strong>妈妈</strong><small>1,284 个片段</small></article>
<article class="person"><span class="person-avatar"><img data-person="2" alt="爸爸"/></span><strong>爸爸</strong><small>962 个片段</small></article>
<article class="person"><span class="person-avatar"><img data-person="3" alt="小南"/></span><strong>小南</strong><small>814 个片段</small></article>
<article class="person"><span class="person-avatar"><img data-person="4" alt="外婆"/></span><strong>外婆</strong><small>438 个片段</small></article>
<article class="person"><span class="person-avatar"><img data-person="5" alt="阿哲"/></span><strong>阿哲</strong><small>201 个片段</small></article>
<article class="person"><span class="person-avatar"><img data-person="6" alt="未命名人物"/></span><strong>未命名人物</strong><small>98 个片段</small></article>
</div>
</section>
</div>
</section>
<section class="view" id="view-search">
<div class="page search-page">
<div class="search-heading">
<div><h1><span id="queryTitle">海边日落</span>”的搜索结果 <span class="query-type"><svg class="icon"><use href="#i-spark"/></svg>AI 语义检索</span></h1><p>同时匹配画面内容、人物、OCR 文字和文件名</p></div>
<span class="result-count">找到 18 个视频 · 42 个相关片段</span>
</div>
<div class="filter-row">
<button class="filter-pill active">相关度最高</button>
<button class="filter-pill"><svg class="icon"><use href="#i-clock"/></svg>上传时间</button>
<button class="filter-pill"><svg class="icon"><use href="#i-folder"/></svg>全部来源</button>
<button class="filter-pill"><svg class="icon"><use href="#i-sliders"/></svg>时长</button>
<button class="filter-pill">4K</button>
<button class="filter-pill">有人物</button>
</div>
<div class="result-list">
<article class="search-result playable" data-title="傍晚的海边,风刚刚好" data-art="sunset" data-duration="12:36">
<div class="video-thumb"><img data-art="sunset" alt="海边日落"/><span class="duration">12:36</span><span class="thumb-hover"><span><svg class="icon"><use href="#i-play"/></svg></span></span></div>
<div class="result-detail"><h2>傍晚的海边,风刚刚好</h2><p>2026 年 7 月 25 日 · 4K · 1.8 GB</p><div class="result-source"><span class="source-avatar"></span>家庭影像 / 2026 夏季旅行</div><div class="match-box"><div class="match-label"><svg class="icon"><use href="#i-spark"/></svg>AI 找到 3 个匹配时刻 <span class="score">96% 匹配</span></div><div class="moment-row"><span class="moment"><b>02:14</b> 海面上的落日</span><span class="moment"><b>05:38</b> 沙滩和晚霞</span><span class="moment"><b>09:02</b> 橙色天空</span></div></div></div>
</article>
<article class="search-result playable" data-title="青海湖自驾 · 第三天" data-art="coast" data-duration="18:42">
<div class="video-thumb"><img data-art="coast" alt="青海湖自驾"/><span class="duration">18:42</span><span class="thumb-hover"><span><svg class="icon"><use href="#i-play"/></svg></span></span></div>
<div class="result-detail"><h2>青海湖自驾 · 第三天</h2><p>2026 年 6 月 18 日 · 4K · 3.2 GB</p><div class="result-source"><span class="source-avatar"></span>家庭影像 / 青海自驾</div><div class="match-box"><div class="match-label"><svg class="icon"><use href="#i-spark"/></svg>AI 找到 2 个匹配时刻 <span class="score">91% 匹配</span></div><div class="moment-row"><span class="moment"><b>08:16</b> 湖边夕阳</span><span class="moment"><b>14:27</b> 金色水面</span></div></div></div>
</article>
<article class="search-result playable" data-title="冲绳旅行随手拍" data-art="island" data-duration="07:51">
<div class="video-thumb"><img data-art="island" alt="冲绳海滩"/><span class="duration">07:51</span><span class="thumb-hover"><span><svg class="icon"><use href="#i-play"/></svg></span></span></div>
<div class="result-detail"><h2>冲绳旅行随手拍</h2><p>2025 年 10 月 4 日 · 1080P · 786 MB</p><div class="result-source"><span class="source-avatar green"></span>Cloud WebDAV / Travel Archive</div><div class="match-box"><div class="match-label"><svg class="icon"><use href="#i-spark"/></svg>AI 找到 4 个匹配时刻 <span class="score">87% 匹配</span></div><div class="moment-row"><span class="moment"><b>00:42</b> 海岸线</span><span class="moment"><b>03:21</b> 夕阳中的帆船</span><span class="moment"><b>06:13</b> 蓝色海浪</span></div></div></div>
</article>
<article class="search-result playable" data-title="涠洲岛航拍素材" data-art="aerial" data-duration="32:09">
<div class="video-thumb"><img data-art="aerial" alt="海岛航拍"/><span class="duration">32:09</span><span class="thumb-hover"><span><svg class="icon"><use href="#i-play"/></svg></span></span></div>
<div class="result-detail"><h2>涠洲岛航拍素材</h2><p>2025 年 8 月 11 日 · 4K · 5.6 GB</p><div class="result-source"><span class="source-avatar orange"></span>相机导入 / Drone</div><div class="match-box"><div class="match-label"><svg class="icon"><use href="#i-spark"/></svg>AI 找到 2 个匹配时刻 <span class="score">82% 匹配</span></div><div class="moment-row"><span class="moment"><b>11:06</b> 日落航拍</span><span class="moment"><b>24:32</b> 海滩全景</span></div></div></div>
</article>
</div>
</div>
</section>
<section class="view" id="view-player">
<div class="page player-page">
<div class="player-layout">
<div class="player-column">
<div class="player-stage" id="playerStage">
<img id="playerPoster" data-art="sunset" alt="视频画面"/>
<div class="player-top"><button id="playerBack"><svg class="icon"><use href="#i-back"/></svg></button><span id="playerTopTitle">傍晚的海边,风刚刚好</span></div>
<button class="center-play" id="centerPlay" aria-label="播放"><svg class="icon"><use href="#i-play"/></svg></button>
<div class="player-controls">
<div class="video-progress" id="videoProgress"><div class="video-progress-track"><div class="video-progress-fill" id="progressFill"></div></div><i class="ai-pin" style="left:18%"></i><i class="ai-pin" style="left:45%"></i><i class="ai-pin" style="left:72%"></i></div>
<div class="control-row"><button id="playControl"><svg class="icon"><use id="playControlIcon" href="#i-play"/></svg></button><button><svg class="icon"><use href="#i-volume"/></svg></button><span class="control-time"><span id="currentTime">03:54</span> / <span id="totalTime">12:36</span></span><span class="control-spacer"></span><button class="quality">4K</button><button><svg class="icon"><use href="#i-settings"/></svg></button><button id="fullscreenButton"><svg class="icon"><use href="#i-fullscreen"/></svg></button></div>
</div>
</div>
<div class="player-heading">
<h1 id="playerTitle">傍晚的海边,风刚刚好</h1>
<div class="player-meta-row">
<div class="owner"><span class="source-avatar"></span><span class="owner-copy"><strong>家庭影像</strong><span>本地目录 · 隐私可见</span></span></div>
<div class="action-group"><button class="pill-action toggle-action"><svg class="icon"><use href="#i-thumbs"/></svg>喜欢</button><button class="pill-action toggle-action"><svg class="icon"><use href="#i-bookmark"/></svg>收藏</button><button class="pill-action" id="shareButton"><svg class="icon"><use href="#i-share"/></svg>分享</button><button class="pill-action"><svg class="icon"><use href="#i-more"/></svg></button></div>
</div>
</div>
<div class="description-card"><strong>2026年7月25日</strong><strong>4K</strong><strong>1.8 GB</strong><p>傍晚沿着海岸散步时随手拍下的片段。原始文件位于「家庭影像 / 2026 夏季旅行」,所有分析均在本机完成。</p></div>
<section class="ai-panel">
<div class="ai-panel-head"><span class="ai-logo"><svg class="icon"><use href="#i-spark"/></svg></span><div><strong>视频内智能检索</strong><p>点击匹配时刻,直接跳到相关画面</p></div><label class="ai-search"><svg class="icon"><use href="#i-search"/></svg><input value="海边日落" aria-label="在视频内搜索"/></label></div>
<div class="timeline-list">
<article class="timeline-item" data-progress="18" data-time="02:14"><span class="timeline-time">02:14</span><i class="timeline-node"></i><span class="timeline-copy"><strong>海面上的落日</strong><span>橙色太阳接近海平面,水面有金色反光</span></span><button class="timeline-play"><svg class="icon"><use href="#i-play"/></svg></button></article>
<article class="timeline-item" data-progress="45" data-time="05:38"><span class="timeline-time">05:38</span><i class="timeline-node"></i><span class="timeline-copy"><strong>沙滩和晚霞</strong><span>人物沿沙滩行走,天空呈粉紫色渐变</span></span><button class="timeline-play"><svg class="icon"><use href="#i-play"/></svg></button></article>
<article class="timeline-item" data-progress="72" data-time="09:02"><span class="timeline-time">09:02</span><i class="timeline-node"></i><span class="timeline-copy"><strong>橙色天空</strong><span>镜头朝向天空,可见云层与橙色余晖</span></span><button class="timeline-play"><svg class="icon"><use href="#i-play"/></svg></button></article>
</div>
</section>
</div>
<aside class="related-column">
<div class="related-head"><h2>接下来播放</h2><span class="autoplay">自动播放 <i class="switch"></i></span></div>
<div class="related-list">
<article class="related-card playable" data-title="青海湖自驾 · 第三天" data-art="coast" data-duration="18:42"><div class="video-thumb"><img data-art="coast" alt="青海湖自驾"/><span class="duration">18:42</span></div><div><h3>青海湖自驾 · 第三天</h3><p>家庭影像</p><p>2026年6月18日 · 4K</p></div></article>
<article class="related-card playable" data-title="冲绳旅行随手拍" data-art="island" data-duration="07:51"><div class="video-thumb"><img data-art="island" alt="冲绳旅行"/><span class="duration">07:51</span></div><div><h3>冲绳旅行随手拍</h3><p>Cloud WebDAV</p><p>2025年10月4日 · 1080P</p></div></article>
<article class="related-card playable" data-title="涠洲岛航拍素材" data-art="aerial" data-duration="32:09"><div class="video-thumb"><img data-art="aerial" alt="涠洲岛航拍"/><span class="duration">32:09</span></div><div><h3>涠洲岛航拍素材</h3><p>相机导入</p><p>2025年8月11日 · 4K</p></div></article>
<article class="related-card playable" data-title="北海道雪场 VLOG" data-art="snow" data-duration="24:03"><div class="video-thumb"><img data-art="snow" alt="北海道雪场"/><span class="duration">24:03</span></div><div><h3>北海道雪场 VLOG</h3><p>Cloud WebDAV</p><p>2026年7月26日 · 4K</p></div></article>
<article class="related-card playable" data-title="第一次看见银河" data-art="space" data-duration="03:19"><div class="video-thumb"><img data-art="space" alt="银河"/><span class="duration">03:19</span></div><div><h3>第一次看见银河</h3><p>相机导入</p><p>2026年7月20日 · 4K</p></div></article>
<article class="related-card playable" data-title="成都周末 City Walk" data-art="city" data-duration="08:15"><div class="video-thumb"><img data-art="city" alt="成都街头"/><span class="duration">08:15</span></div><div><h3>成都周末 City Walk</h3><p>Cloud WebDAV</p><p>2026年7月27日 · 1080P</p></div></article>
</div>
</aside>
</div>
</div>
</section>
</main>
<div class="image-modal" id="imageModal" role="dialog" aria-modal="true" aria-labelledby="imageModalTitle">
<div class="modal-card">
<div class="modal-head"><h2 id="imageModalTitle">用一张图片找到视频</h2><button class="close-button" id="imageModalClose" aria-label="关闭"><svg class="icon"><use href="#i-x"/></svg></button></div>
<div class="drop-zone" id="dropZone"><div><span class="drop-icon"><svg class="icon"><use href="#i-image"/></svg></span><h3>拖放图片到这里</h3><p>支持 JPG、PNG 和 WebP,可用截图定位原视频</p><button class="primary-button" id="demoImageSearch"><svg class="icon" style="width:15px"><use href="#i-upload"/></svg>选择演示图片</button></div></div>
<div class="privacy-note"><svg class="icon"><use href="#i-lock"/></svg>图片仅用于本机检索,1 小时后自动删除</div>
</div>
</div>
<div class="profile-menu" id="profileMenu">
<div class="profile-head"><span class="avatar-button">NX</span><div><strong>南浔</strong><span>本机管理员</span></div></div>
<button><svg class="icon"><use href="#i-monitor"/></svg>系统状态 <span style="margin-left:auto;color:#12a66a">正常</span></button>
<button><svg class="icon"><use href="#i-settings"/></svg>设置</button>
<button><svg class="icon"><use href="#i-logout"/></svg>退出登录</button>
</div>
<div class="toast" id="toast"><svg class="icon"><use href="#i-check"/></svg><span id="toastText">操作已完成</span></div>
<script>
const palettes = {
sunset: ['#ffbd72','#ec6b57','#45548c','#132849'], coast: ['#9ad7ef','#3f98bd','#f0c57b','#253f64'],
city: ['#e8b887','#a35b58','#40547a','#24314b'], snow: ['#d8ecf6','#9abdd4','#56778d','#fafcff'],
garden: ['#bdd98e','#4e8c51','#f0bf58','#315536'], food: ['#efc77f','#a53f27','#56251c','#f5e1b4'],
pet: ['#aed5b5','#e6a85c','#6a492f','#f5e5bd'], concert: ['#7f5cf0','#ef4b82','#13142c','#54d8d5'],
space: ['#142044','#43347e','#e9c8ff','#091226'], island: ['#79d8dc','#168fab','#f3d992','#0a5c75'],
aerial: ['#42b4ae','#167e83','#e6c87d','#0a5963']
};
function artSvg(kind, portrait = false) {
const p = palettes[kind] || palettes.sunset;
const w = portrait ? 400 : 960, h = portrait ? 400 : 540;
let scene = '';
if (['sunset','coast','island','aerial'].includes(kind)) {
scene = `<circle cx="${w*.72}" cy="${h*.28}" r="${h*.095}" fill="#fff1bd" opacity=".92"/>
<path d="M0 ${h*.54} Q ${w*.18} ${h*.47} ${w*.36} ${h*.55} T ${w*.72} ${h*.53} T ${w} ${h*.55} V${h}H0Z" fill="${p[1]}"/>
<path d="M0 ${h*.69} Q ${w*.23} ${h*.59} ${w*.48} ${h*.71} T ${w} ${h*.67} V${h}H0Z" fill="${p[2]}" opacity=".92"/>
<path d="M0 ${h*.73} Q ${w*.3} ${h*.68} ${w*.56} ${h*.77} T ${w} ${h*.71}" fill="none" stroke="#fff" stroke-width="${h*.012}" opacity=".55"/>`;
} else if (kind === 'city') {
scene = `<rect x="${w*.05}" y="${h*.36}" width="${w*.16}" height="${h*.64}" fill="#26354d"/><rect x="${w*.23}" y="${h*.2}" width="${w*.2}" height="${h*.8}" fill="#354768"/><rect x="${w*.47}" y="${h*.42}" width="${w*.14}" height="${h*.58}" fill="#25344d"/><rect x="${w*.64}" y="${h*.29}" width="${w*.27}" height="${h*.71}" fill="#33405d"/>
${Array.from({length:18},(_,i)=>`<rect x="${w*(.08+(i%6)*.14)}" y="${h*(.43+Math.floor(i/6)*.16)}" width="${w*.045}" height="${h*.045}" rx="3" fill="#ffd584" opacity="${.5+(i%3)*.18}"/>`).join('')}
<path d="M0 ${h*.81}h${w}v${h*.19}H0z" fill="#172133"/><path d="M${w*.38} ${h} L${w*.49} ${h*.74}h${w*.03}L${w*.67} ${h}z" fill="#e7aa54" opacity=".55"/>`;
} else if (kind === 'snow') {
scene = `<path d="M0 ${h*.76} ${w*.26} ${h*.25} ${w*.43} ${h*.57} ${w*.61} ${h*.16} ${w} ${h*.76}V${h}H0Z" fill="#edf6fb"/>
<path d="m${w*.14} ${h*.76} ${w*.12}-${h*.51} ${w*.09} ${h*.19}-.07-.03-.06.16m${w*.31} ${h*.16} .09.28-.06-.035-.08.18" fill="${p[2]}" opacity=".58"/>
<path d="M0 ${h*.78} Q${w*.35} ${h*.7} ${w} ${h*.83}V${h}H0Z" fill="#f9fdff"/>`;
} else if (kind === 'garden') {
scene = `<circle cx="${w*.78}" cy="${h*.22}" r="${h*.1}" fill="#fff0a3"/><path d="M0 ${h*.57} Q${w*.23} ${h*.42} ${w*.5} ${h*.57}T${w} ${h*.52}V${h}H0Z" fill="#437b49"/>
${Array.from({length:11},(_,i)=>`<path d="M${w*(.05+i*.09)} ${h}q${w*.015}-${h*.32} ${w*.06}-${h*(.42+(i%3)*.05)}q${w*.025} ${h*.2} ${w*.06} ${h*.3}" stroke="${i%2?'#78a94f':'#d8b94c'}" stroke-width="${w*.012}"/>`).join('')}`;
} else if (kind === 'food') {
scene = `<ellipse cx="${w*.5}" cy="${h*.62}" rx="${w*.31}" ry="${h*.24}" fill="#f4e4bd"/><ellipse cx="${w*.5}" cy="${h*.58}" rx="${w*.25}" ry="${h*.17}" fill="#a34128"/>
${Array.from({length:7},(_,i)=>`<path d="M${w*(.34+i*.05)} ${h*.48}q${w*.08} ${h*.1} ${w*.15} ${h*.02}" stroke="#f2c367" stroke-width="${w*.012}" fill="none"/>`).join('')}
<path d="M${w*.68} ${h*.17}q-${w*.08} ${h*.16} 0 ${h*.3}M${w*.77} ${h*.14}q-${w*.08} ${h*.16} 0 ${h*.3}" fill="none" stroke="#fff" stroke-width="${w*.012}" opacity=".35"/>`;
} else if (kind === 'pet') {
scene = `<path d="M0 ${h*.66}Q${w*.4} ${h*.47} ${w} ${h*.68}V${h}H0Z" fill="#73a765"/><ellipse cx="${w*.52}" cy="${h*.58}" rx="${w*.15}" ry="${h*.18}" fill="#d98e45"/><circle cx="${w*.52}" cy="${h*.42}" r="${h*.13}" fill="#eda957"/>
<path d="m${w*.43} ${h*.34}-.02-.19.13.12m.07 0 .14-.12-.03.2" fill="#c47737"/><circle cx="${w*.48}" cy="${h*.42}" r="${h*.013}"/><circle cx="${w*.57}" cy="${h*.42}" r="${h*.013}"/><path d="M${w*.49} ${h*.48}q${w*.035} ${h*.035} ${w*.07} 0" fill="none" stroke="#6b3c2c" stroke-width="${w*.006}"/>`;
} else if (kind === 'concert') {
scene = `${Array.from({length:7},(_,i)=>`<path d="M${w*(.08+i*.14)} 0 ${w*(.3+i*.07)} ${h}" stroke="${i%2?p[1]:p[3]}" stroke-width="${w*.05}" opacity=".25"/>`).join('')}
<path d="M0 ${h*.82}q${w*.08}-${h*.22} ${w*.16} 0 .08-.26.17 0 .09-.2.17 0 .08-.24.16 0 .08-.2.16 0 .08-.27.17 0 .09-.2.18 0V${h}H0Z" fill="#0d1022"/>
<ellipse cx="${w*.5}" cy="${h*.73}" rx="${w*.16}" ry="${h*.04}" fill="#fff" opacity=".32"/>`;
} else {
scene = `${Array.from({length:38},(_,i)=>`<circle cx="${(i*137)%w}" cy="${(i*83)%h}" r="${1+(i%4)}" fill="#fff" opacity="${.3+(i%5)*.13}"/>`).join('')}
<path d="M0 ${h*.86} ${w*.2} ${h*.55} ${w*.36} ${h*.76} ${w*.58} ${h*.4} ${w*.8} ${h*.74} ${w} ${h*.52}V${h}H0Z" fill="#0b1229"/>
<path d="M${w*.21} ${h*.52}Q${w*.55} ${h*.26} ${w*.84} ${h*.12}" fill="none" stroke="#e7d7ff" stroke-width="${h*.06}" opacity=".12"/>`;
}
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}"><defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1"><stop stop-color="${p[0]}"/><stop offset=".55" stop-color="${p[1]}"/><stop offset="1" stop-color="${p[2]}"/></linearGradient><filter id="grain"><feTurbulence baseFrequency=".7" numOctaves="2" stitchTiles="stitch"/><feColorMatrix values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 .06 0"/></filter></defs><rect width="100%" height="100%" fill="url(#g)"/>${scene}<rect width="100%" height="100%" filter="url(#grain)" opacity=".25"/></svg>`;
return `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(svg)}`;
}
document.querySelectorAll('img[data-art]').forEach(img => img.src = artSvg(img.dataset.art));
document.querySelectorAll('img[data-person]').forEach((img, index) => {
const colors = [['#e8bca0','#774b3b'],['#d8ad8e','#3e465e'],['#f0bb8c','#526f9a'],['#d4a883','#704943'],['#e4b395','#253755'],['#ddb28f','#596678']][index];
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200"><defs><linearGradient id="b" x2="1" y2="1"><stop stop-color="${colors[0]}"/><stop offset="1" stop-color="#d9e2f1"/></linearGradient></defs><rect width="200" height="200" fill="url(#b)"/><circle cx="100" cy="82" r="42" fill="${colors[0]}"/><path d="M58 78q5-55 43-52 41 2 43 54-18-21-43-20-25 0-43 18Z" fill="${colors[1]}"/><circle cx="85" cy="84" r="3" fill="#4d332c"/><circle cx="115" cy="84" r="3" fill="#4d332c"/><path d="M88 104q12 9 24 0" stroke="#955e53" stroke-width="3" fill="none" stroke-linecap="round"/><path d="M25 200q8-66 75-66t75 66" fill="${colors[1]}"/></svg>`;
img.src = `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(svg)}`;
});
const views = [...document.querySelectorAll('.view')];
const navItems = [...document.querySelectorAll('.nav-item')];
const toast = document.getElementById('toast');
const toastText = document.getElementById('toastText');
let toastTimer;
let lastView = 'home';
function showToast(message) {
toastText.textContent = message;
toast.classList.add('show');
clearTimeout(toastTimer);
toastTimer = setTimeout(() => toast.classList.remove('show'), 2300);
}
function showView(name) {
if (!['home','search','player'].includes(name)) {
showToast('该页面将在正式版中接入,本稿重点演示浏览、搜索与播放');
return;
}
const current = views.find(view => view.classList.contains('active'));
if (current && current.id !== `view-${name}`) lastView = current.id.replace('view-', '');
views.forEach(view => view.classList.toggle('active', view.id === `view-${name}`));
navItems.forEach(item => item.classList.toggle('active', name === 'home' && item.dataset.viewTarget === 'home'));
window.scrollTo({ top: 0, behavior: 'smooth' });
}
document.querySelectorAll('[data-view-target]').forEach(target => target.addEventListener('click', () => showView(target.dataset.viewTarget)));
document.getElementById('menuToggle').addEventListener('click', () => document.body.classList.toggle('sidebar-collapsed'));
document.querySelectorAll('.chip').forEach(chip => chip.addEventListener('click', () => {
document.querySelectorAll('.chip').forEach(item => item.classList.remove('active'));
chip.classList.add('active');
if (chip.textContent.trim() !== '全部') showToast(`已筛选:${chip.textContent.trim()}`);
}));
document.querySelectorAll('.filter-pill').forEach(pill => pill.addEventListener('click', () => pill.classList.toggle('active')));
document.getElementById('globalSearch').addEventListener('submit', event => {
event.preventDefault();
const query = document.getElementById('searchInput').value.trim() || '海边日落';
document.getElementById('queryTitle').textContent = query;
document.getElementById('searchInput').value = query;
showView('search');
});
const imageModal = document.getElementById('imageModal');
function openImageModal() { imageModal.classList.add('open'); document.body.classList.add('modal-open'); }
function closeImageModal() { imageModal.classList.remove('open'); document.body.classList.remove('modal-open'); }
document.getElementById('imageSearchButton').addEventListener('click', openImageModal);
document.getElementById('topImageButton').addEventListener('click', openImageModal);
document.getElementById('imageModalClose').addEventListener('click', closeImageModal);
imageModal.addEventListener('click', event => { if (event.target === imageModal) closeImageModal(); });
document.getElementById('demoImageSearch').addEventListener('click', () => {
closeImageModal();
document.getElementById('queryTitle').textContent = '相似图片';
document.getElementById('searchInput').value = '';
showView('search');
showToast('演示图片已完成本地分析,找到 42 个相似片段');
});
const playerStage = document.getElementById('playerStage');
const playControlIcon = document.getElementById('playControlIcon');
function togglePlay(force) {
const playing = force ?? !playerStage.classList.contains('playing');
playerStage.classList.toggle('playing', playing);
playControlIcon.setAttribute('href', playing ? '#i-pause' : '#i-play');
}
document.getElementById('centerPlay').addEventListener('click', () => togglePlay(true));
document.getElementById('playControl').addEventListener('click', () => togglePlay());
function openPlayer(element) {
const title = element.dataset.title || '傍晚的海边,风刚刚好';
const art = element.dataset.art || 'sunset';
const duration = element.dataset.duration || '12:36';
document.getElementById('playerTitle').textContent = title;
document.getElementById('playerTopTitle').textContent = title;
document.getElementById('playerPoster').src = artSvg(art);
document.getElementById('totalTime').textContent = duration;
document.getElementById('currentTime').textContent = '00:00';
document.getElementById('progressFill').style.width = '0%';
playerStage.classList.remove('playing');
playControlIcon.setAttribute('href', '#i-play');
showView('player');
}
document.querySelectorAll('.playable').forEach(card => card.addEventListener('click', event => {
if (event.target.closest('.more-button')) return;
openPlayer(card);
}));
document.getElementById('playerBack').addEventListener('click', () => showView(lastView === 'player' ? 'home' : lastView));
document.getElementById('videoProgress').addEventListener('click', event => {
const rect = event.currentTarget.getBoundingClientRect();
const progress = Math.max(0, Math.min(100, ((event.clientX - rect.left) / rect.width) * 100));
document.getElementById('progressFill').style.width = `${progress}%`;
const seconds = Math.round(progress * 7.56);
document.getElementById('currentTime').textContent = `${String(Math.floor(seconds / 60)).padStart(2,'0')}:${String(seconds % 60).padStart(2,'0')}`;
});
document.querySelectorAll('.timeline-item').forEach(item => item.addEventListener('click', () => {
document.getElementById('progressFill').style.width = `${item.dataset.progress}%`;
document.getElementById('currentTime').textContent = item.dataset.time;
togglePlay(true);
showToast(`已跳转到 ${item.dataset.time} · ${item.querySelector('strong').textContent}`);
document.getElementById('playerStage').scrollIntoView({ behavior: 'smooth', block: 'start' });
}));
document.querySelectorAll('.toggle-action').forEach(button => button.addEventListener('click', () => {
button.classList.toggle('active');
showToast(button.classList.contains('active') ? `${button.textContent.trim()}成功` : `已取消${button.textContent.trim()}`);
}));
document.getElementById('shareButton').addEventListener('click', () => showToast('已生成仅限局域网访问的分享链接'));
document.getElementById('fullscreenButton').addEventListener('click', () => {
if (playerStage.requestFullscreen) playerStage.requestFullscreen().catch(() => showToast('浏览器阻止了全屏请求'));
});
const profileMenu = document.getElementById('profileMenu');
document.getElementById('profileButton').addEventListener('click', event => { event.stopPropagation(); profileMenu.classList.toggle('open'); });
document.addEventListener('click', event => { if (!profileMenu.contains(event.target)) profileMenu.classList.remove('open'); });
document.getElementById('taskButton').addEventListener('click', () => showToast('索引已完成 · 352 个视频可检索'));
document.addEventListener('keydown', event => { if (event.key === 'Escape') { closeImageModal(); profileMenu.classList.remove('open'); } });
</script>
</body>
</html>