Files
Stow/Stow.html

312 lines
81 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta name="theme-color" content="#f6f4ef" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-title" content="收好" />
<title>收好 Stow · 产品原型</title>
<style>
:root{font-family:'DM Sans','Noto Sans SC',-apple-system,BlinkMacSystemFont,'PingFang SC',sans-serif;color:#21392b;background:#dce5dd;font-synthesis:none}*{box-sizing:border-box}body{margin:0;min-height:100vh;background:radial-gradient(circle at 16% 12%,#fbf7e9 0,transparent 34%),radial-gradient(circle at 80% 90%,#afcbbb 0,transparent 33%),#dae4dc;display:flex;align-items:center;justify-content:center;gap:8vw}button,input,textarea{font:inherit}button{cursor:pointer}button:disabled{opacity:.45;cursor:default}em{font-style:normal}h1,h2,h3,p{margin-top:0}.desktop-intro{width:440px;max-width:38vw;align-self:center;color:#294534}.desktop-logo{display:flex;align-items:center;gap:13px;font-size:22px;font-weight:700}.desktop-logo b{font-size:16px;font-weight:600;margin-left:6px}.logo-symbol{width:35px;height:35px;display:inline-grid;place-items:center;color:#f7f6e9;background:#285c42;border-radius:11px;font-size:25px;line-height:1;box-shadow:0 6px 14px #275d4238}.desktop-kicker{font-size:11px;letter-spacing:.23em;color:#73907b;font-weight:700;margin:65px 0 17px}.desktop-intro h1{font-size:46px;line-height:1.32;letter-spacing:-.06em;font-weight:800;margin:0 0 21px}.desktop-intro h1 em{color:#638c6c}.desktop-description{font-size:15px;line-height:1.9;color:#758d7b}.desktop-flow{display:flex;align-items:center;gap:11px;font-size:12px;font-weight:700;margin-top:42px;color:#4a7356}.desktop-flow span{background:#ffffffaa;border:1px solid #ffffffa8;padding:10px 12px;border-radius:9px}.desktop-flow i{font-style:normal;color:#8fad97}.device{width:393px;height:min(852px,calc(100vh - 40px));min-height:670px;flex:none;background:#f8f7f1;border:8px solid #1e2a24;border-radius:49px;box-shadow:0 32px 70px #17402d31,0 1px 0 #819b87 inset,0 0 0 1px #6f8375;position:relative;overflow:hidden}.statusbar{height:46px;position:relative;display:flex;align-items:flex-end;justify-content:space-between;padding:0 25px 8px;background:#f8f7f1;color:#203a2a;font-size:12px;font-weight:800;z-index:8}.dynamic-island{position:absolute;top:0;left:50%;transform:translateX(-50%);background:#1e2a24;width:116px;height:28px;border-radius:0 0 17px 17px}.status-icons{font-size:11px;letter-spacing:1px}.home-indicator{width:122px;height:4px;border-radius:10px;background:#2a332c;position:absolute;bottom:7px;left:50%;transform:translateX(-50%);z-index:20}#app{height:calc(100% - 46px);position:relative;background:#f8f7f1}.screen{height:100%;overflow-y:auto;padding:17px 21px 110px;scrollbar-width:none}.screen::-webkit-scrollbar,.category-scroll::-webkit-scrollbar{display:none}.topbar{min-height:45px;display:flex;align-items:center;justify-content:space-between;margin-bottom:20px}.brand-lockup{display:flex;align-items:center;gap:9px;font-size:18px;font-weight:800;letter-spacing:-.04em}.brand-lockup .logo-symbol{width:32px;height:32px;font-size:22px;border-radius:10px}.avatar-button,.circle-button,.more-button{width:38px;height:38px;border:1px solid #e4eae1;background:#fff;border-radius:50%;display:grid;place-items:center;color:#315c41;font-size:20px;box-shadow:0 2px 9px #26583c0c}.circle-button{font-size:24px}.overline{font-size:10px;font-weight:800;letter-spacing:.16em;text-transform:uppercase;color:#8ba18b}.home-hero{margin:26px 0 22px}.home-hero .overline{margin-bottom:10px}.home-hero h1{font-size:31px;line-height:1.35;letter-spacing:-.057em;margin-bottom:8px;font-weight:800}.home-hero h1 em{color:#568267}.home-hero>p:last-child{font-size:12px;color:#849587;margin:0}.search-callout{width:100%;height:53px;background:#fff;display:flex;align-items:center;gap:11px;border:1px solid #e7ece5;border-radius:15px;padding:0 15px;text-align:left;color:#849687;box-shadow:0 8px 22px #2d5d3d0a;font-size:12px}.search-glyph{font-size:25px;color:#3d7051;line-height:1}.search-shortcut{margin-left:auto;background:#f1f5ee;padding:5px 7px;border-radius:6px;color:#849e8b;font-size:10px}.scan-callout{width:100%;min-height:108px;display:flex;align-items:center;border:0;border-radius:19px;background:linear-gradient(125deg,#174c37,#367452);color:white;padding:15px 17px;text-align:left;margin:20px 0 29px;overflow:hidden;position:relative;box-shadow:0 14px 23px #245d4030}.scan-callout:after{content:'';position:absolute;width:160px;height:160px;border:1px solid #ffffff20;border-radius:50%;right:-70px;top:-65px}.scan-graphic{width:77px;height:78px;flex:none;position:relative;margin-right:14px}.scan-graphic i{position:absolute;width:51px;height:64px;border:3px solid #cfe5cd;border-radius:9px;background:#e7eedf;box-shadow:0 5px 9px #123b2f45}.scan-graphic i:first-child{transform:rotate(-17deg);left:5px;top:7px;background:#b9c9ad}.scan-graphic i:nth-child(2){transform:rotate(10deg);left:13px;top:5px;background:#d6e3cd}.scan-graphic i:last-child{transform:rotate(-2deg);left:17px;top:7px;background:linear-gradient(155deg,#cfd8b3 20%,#85aa83 20%,#517d6f 72%)}.scan-copy{position:relative;z-index:1}.scan-copy small{display:block;font-size:10px;letter-spacing:.07em;color:#bdd8c2;margin-bottom:6px}.scan-copy strong{display:block;font-size:16px;letter-spacing:-.03em}.scan-copy>span{display:block;margin-top:8px;font-size:11px;color:#d1ead3}.scan-copy b{margin-left:4px}.section-heading{display:flex;justify-content:space-between;align-items:center;margin:28px 0 15px}.section-heading h2{font-size:17px;letter-spacing:-.035em;margin:0}.section-heading button{border:0;background:none;color:#658775;font-size:11px;font-weight:700;padding:5px 0}.section-heading button span{font-size:16px;margin-left:2px}.group-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}.group-card{min-height:112px;border:0;border-radius:17px;position:relative;overflow:hidden;padding:15px;text-align:left;color:#324b37}.group-card:after{content:'';position:absolute;width:91px;height:91px;border:1px solid #fff8;border-radius:50%;right:-25px;bottom:-43px}.group-card.peach{background:#f3e5d7}.group-card.sage{background:#dfebdf}.group-card.lilac{background:#e9e7ed}.group-card.sand{background:#f3edd9}.group-symbol{font-size:25px;line-height:1}.group-count{position:absolute;right:17px;top:17px;font-size:17px;font-weight:800}.group-card strong{display:block;margin-top:13px;font-size:14px}.group-card small{display:block;font-size:10px;margin-top:3px;color:#8c9c89}.items{display:grid;gap:9px}.item-card{width:100%;display:flex;align-items:center;gap:13px;min-height:90px;padding:8px;background:#fff;border:1px solid #e9ede6;border-radius:15px;text-align:left;box-shadow:0 4px 16px #2b60370a;color:#293e30}.cover{overflow:hidden;background:#dbe6da;position:relative;flex:none}.cover img{display:block;width:100%;height:100%;object-fit:cover}.cover-fallback{font-size:40px;color:#5a8a62}.item-cover{width:73px;height:73px;border-radius:10px}.item-main{display:flex;flex-direction:column;min-width:0;flex:1}.item-title{font-size:13px;font-weight:800;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;letter-spacing:-.02em}.item-subtitle{font-size:11px;color:#7b8b7d;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;margin-top:5px}.item-meta{font-size:10px;color:#a0aea1;margin-top:9px;display:flex;gap:6px;align-items:center}.source-mini{color:#70927a}.meta-dot{color:#b9c4ba}.item-arrow{font-size:21px;color:#abb9ac;margin-right:3px}.footer-note{font-size:11px;color:#a1ada1;text-align:center;padding:31px 0 13px}.footer-note span{color:#6f9b78}.tabbar{position:absolute;bottom:0;left:0;right:0;height:82px;display:flex;align-items:flex-start;justify-content:space-around;padding:10px 10px 17px;background:#fffffff0;backdrop-filter:blur(16px);border-top:1px solid #e5ebe4;z-index:7}.nav-item{display:flex;align-items:center;flex-direction:column;gap:3px;min-width:49px;border:0;background:none;color:#a3b2a4;padding:0}.nav-item span{font-size:23px;line-height:26px}.nav-item small{font-size:10px}.nav-item.active{color:#2b6a46;font-weight:800}.nav-add{width:43px;height:43px;border:0;background:#2b6949;color:#fff;border-radius:14px;box-shadow:0 6px 14px #2a6a4840;font-size:30px;line-height:41px;margin-top:-8px}.nav-add span{display:block;transform:translateY(-2px)}.page-header .overline{margin:0 0 5px}.page-header h1{font-size:27px;letter-spacing:-.055em;margin:0}.page-description{font-size:12px;color:#8fa08f;margin:-13px 0 20px}.category-scroll{display:flex;gap:7px;overflow-x:auto;scrollbar-width:none;margin:0 -21px;padding:0 21px 15px}.category-chip{white-space:nowrap;background:#fff;border:1px solid #e5ebe3;color:#788d7b;border-radius:25px;padding:8px 13px;font-size:11px;font-weight:700}.category-chip.selected{background:#2c6547;border-color:#2c6547;color:#fff}.filter-bar{display:flex;gap:8px;margin-bottom:21px}.filter-bar button{border:1px solid #e7ece5;border-radius:9px;background:#fff;padding:8px 10px;color:#68816e;font-size:10px;white-space:nowrap}.filter-bar button span{font-size:13px;margin-right:2px}.filter-bar button b{font-weight:400;color:#a5b3a6}.collection-count{font-size:10px;letter-spacing:.06em;color:#9dad9f;font-weight:700;margin-bottom:16px}.date-group{margin-bottom:22px}.date-group h2{font-size:12px;margin:0 0 10px;color:#516b58}.search-input-wrap{display:flex;align-items:center;gap:10px;height:52px;background:#fff;border:1px solid #e4eae1;border-radius:14px;padding:0 12px;box-shadow:0 8px 22px #2d5d3d0a}.search-input-wrap>span{font-size:25px;color:#41805a;line-height:1}.search-input-wrap input{border:0;background:none;outline:0;min-width:0;flex:1;font-size:12px;color:#243f2e}.search-input-wrap button{border:0;background:none;color:#a2b0a3;font-size:21px}.search-intro{margin-top:33px}.search-intro .overline{margin-bottom:9px}.suggestions{background:#fff;border:1px solid #e8ede5;border-radius:16px;padding:2px 14px}.suggestions button{width:100%;display:flex;align-items:center;gap:10px;background:none;border:0;border-bottom:1px solid #edf0ea;text-align:left;padding:15px 2px;font-size:12px;color:#4e6954}.suggestions button:last-child{border:0}.suggest-icon{width:26px;height:26px;display:grid;place-items:center;border-radius:8px;background:#edf4ec;color:#5c8965;font-size:18px}.suggest-arrow{margin-left:auto;color:#a3b3a5;font-size:16px}.search-tip{margin-top:22px;background:#e6efe5;border-radius:15px;padding:16px;display:flex;gap:12px;align-items:center}.search-tip span{width:35px;height:35px;border-radius:11px;background:#d3e5d1;display:grid;place-items:center;color:#4e8661;font-size:19px}.search-tip p{margin:0;font-size:11px;line-height:1.6;color:#719078}.search-results{margin-top:24px}.result-heading{display:flex;justify-content:space-between;align-items:center;margin-bottom:13px}.result-heading strong{font-size:12px}.result-heading span{color:#a0aea2;font-size:10px}.result-hint{background:#eef4ed;border-radius:10px;padding:13px;margin-top:18px;color:#7b9980;font-size:10px;line-height:1.6}.empty-state{text-align:center;background:#fff;border:1px solid #e8eee6;border-radius:17px;padding:35px 18px;margin:14px 0}.empty-illustration{font-size:33px;color:#75a280;margin-bottom:8px}.empty-state strong{font-size:14px}.empty-state p{color:#98a99a;font-size:11px;line-height:1.6;margin:7px 0 0}.back-button{border:0;background:none;padding:5px 0;color:#35684a;font-size:25px;display:flex;align-items:center;gap:3px}.back-button span{font-size:12px;font-weight:700}.more-button{font-size:15px;font-weight:800}.detail-screen{padding-bottom:40px}.detail-cover-wrap{position:relative}.detail-cover{width:100%;height:231px;border-radius:18px}.detail-source{position:absolute;left:11px;bottom:11px;padding:6px 9px;border-radius:7px;background:#fdfdf6e8;backdrop-filter:blur(10px);font-size:10px;font-weight:700;color:#3c6548}.detail-title-wrap{margin:23px 0 21px}.detail-title-wrap .overline{margin-bottom:7px}.detail-title-wrap h1{font-size:26px;line-height:1.35;letter-spacing:-.045em;margin:0 0 6px}.detail-title-wrap>p:last-child{font-size:12px;color:#829483;margin:0}.ai-summary{background:#e8f0e6;border-radius:17px;padding:17px 18px}.summary-label{display:flex;align-items:center;gap:9px;color:#39694a;font-size:12px}.sparkle-box{width:25px;height:25px;display:grid;place-items:center;background:#d4e7d2;border-radius:8px;font-size:15px}.ai-summary p{font-size:12px;line-height:1.75;color:#5e7763;margin:11px 0 0}.detail-block{margin-top:26px}.detail-block h2{font-size:14px;letter-spacing:-.02em;margin:0 0 12px}.info-card{border:1px solid #e9ede7;background:#fff;border-radius:13px;padding:2px 15px}.info-card>div{display:flex;align-items:center;justify-content:space-between;gap:15px;padding:12px 0;border-bottom:1px solid #edf0ec;font-size:11px}.info-card>div:last-child{border:0}.info-card span{color:#96a598}.info-card strong{font-weight:700;color:#4c6551;text-align:right}.tag-row{display:flex;gap:7px;flex-wrap:wrap}.tag-row span{background:#edf3ec;color:#5e8266;border-radius:7px;padding:7px 10px;font-size:10px}.action-card{display:flex;align-items:center;width:100%;background:#fff;border:1px solid #e8ede7;border-radius:13px;padding:13px;color:#395c43;text-align:left;gap:12px}.action-card>span{width:35px;height:35px;border-radius:10px;background:#e8f0e6;display:grid;place-items:center;font-size:19px}.action-card strong{font-size:12px}.action-card b{margin-left:auto;color:#a8b7aa;font-size:21px}.source-block{border-top:1px solid #e6ebe3;padding-top:21px}.source-block p{font-size:11px;color:#627967;margin-bottom:5px}.source-block small{font-size:10px;color:#a2b0a4}.detail-bottom{display:flex;gap:10px;margin-top:29px}.detail-bottom button{min-height:46px;background:#fff;border:1px solid #dde8dc;border-radius:12px;color:#427052;font-size:12px;font-weight:700}.detail-bottom button:first-child{flex:1}.detail-bottom button:last-child{width:48px;color:#ad6c62;font-size:18px}.detail-bottom button span{font-size:17px;margin-right:5px}
.topbar-label{font-size:12px;color:#6e8a74;font-weight:700}.scan-screen{padding-bottom:142px}.scan-heading{margin:11px 0 27px}.eyebrow-icon{width:43px;height:43px;display:grid;place-items:center;border-radius:13px;background:#e1eee1;color:#4f8660;font-size:25px;margin-bottom:18px}.scan-heading .overline{margin-bottom:7px}.scan-heading h1{font-size:27px;line-height:1.3;letter-spacing:-.05em;margin:0 0 10px}.scan-heading>p:last-child{font-size:11px;line-height:1.7;color:#879b89;margin:0}.scan-selection-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}.scan-selection-head strong{font-size:13px}.scan-selection-head button{background:none;border:0;color:#4f805e;font-size:11px;font-weight:700}.scan-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:11px}.scan-tile{border:1px solid #e6ece4;background:#fff;border-radius:14px;padding:7px 7px 11px;position:relative;text-align:left;color:#334c39}.scan-cover{width:100%;height:114px;border-radius:9px;margin-bottom:9px}.scan-tile strong{display:block;font-size:11px;padding:0 3px}.scan-tile small{display:block;font-size:10px;color:#9aaa9c;margin:3px 3px 0}.scan-tile.selected{border-color:#5a9469;box-shadow:0 0 0 1px #5a9469 inset}.scan-check{position:absolute;top:14px;right:14px;width:23px;height:23px;border:1px solid #e5ebe1;background:#fffd;border-radius:50%;display:grid;place-items:center;color:transparent;font-size:14px;font-weight:800}.scan-tile.selected .scan-check{background:#38764f;border-color:#38764f;color:#fff}.scan-bottom,.complete-bottom{position:absolute;left:0;right:0;bottom:0;background:#fffffff2;backdrop-filter:blur(12px);border-top:1px solid #e5ece5;padding:10px 21px 28px}.scan-bottom p{font-size:10px;color:#8ba08f;margin:0 0 9px}.scan-bottom p strong{color:#3b7750}.primary-button{min-height:49px;width:100%;border:0;border-radius:13px;background:#2c6948;color:#fff;font-size:12px;font-weight:800;padding:0 18px;box-shadow:0 7px 13px #2c694826;display:flex;align-items:center;justify-content:center;gap:10px}.primary-button span:last-child{font-size:17px}.processing-screen{padding-bottom:32px}.processing-view{height:calc(100% - 65px);display:flex;align-items:center;justify-content:center;flex-direction:column;text-align:center}.processing-orbit{width:240px;height:225px;position:relative;margin-bottom:22px;background:radial-gradient(circle,#e0efdf 0,transparent 68%)}.processing-card{position:absolute;width:113px;height:147px;background:#fff;border:5px solid white;border-radius:17px;box-shadow:0 14px 26px #285b3d20}.processing-card.one{left:48px;top:29px;transform:rotate(-16deg);background:linear-gradient(150deg,#d9bd99 15%,#ba8c73 72%)}.processing-card.two{left:85px;top:28px;transform:rotate(13deg);background:linear-gradient(160deg,#bed6bb 17%,#759e94 72%)}.processing-card.three{left:70px;top:42px;transform:rotate(-2deg);background:#f9faf5;color:#5e966c;display:grid;place-items:center;font-size:45px}.processing-copy .overline{margin-bottom:10px}.processing-copy h1{font-size:26px;letter-spacing:-.05em;margin:0 0 10px}.processing-copy p:last-child{font-size:11px;line-height:1.6;color:#839786;max-width:260px}.processing-steps{display:flex;width:100%;justify-content:space-between;margin-top:32px;gap:8px;font-size:10px;color:#acb9ac}.processing-steps .done{color:#699977}.processing-steps .active{color:#347a4e;font-weight:800}.processing-track{height:6px;background:#e5ece3;border-radius:7px;width:100%;margin:13px 0 10px;overflow:hidden}.processing-track i{display:block;height:100%;width:60%;background:#71a27b;border-radius:6px;animation:progress 1.8s linear forwards}@keyframes progress{from{width:8%}to{width:100%}}.processing-view>small{font-size:10px;color:#a3b2a5}.completed-screen{padding-bottom:115px}.complete-hero{text-align:center;padding:46px 7px 32px}.complete-symbol{width:60px;height:60px;border-radius:20px;background:#d9edda;color:#4b8b5c;display:grid;place-items:center;font-size:31px;font-weight:800;margin:0 auto 25px}.complete-hero .overline{margin-bottom:8px}.complete-hero h1{font-size:29px;line-height:1.35;letter-spacing:-.055em;margin-bottom:11px}.complete-hero h1 em{color:#60916d}.complete-hero>p:last-child{font-size:11px;color:#839686;line-height:1.7}.complete-list>strong{font-size:13px;display:block;margin-bottom:12px}.profile-hero{display:flex;align-items:center;gap:14px;background:#e5efe4;border-radius:17px;padding:17px;margin:15px 0 12px}.profile-avatar{width:48px;height:48px;border-radius:15px;background:#cce2ce;display:grid;place-items:center;color:#487d58;font-size:28px}.profile-hero strong{font-size:13px}.profile-hero p{font-size:10px;color:#829a86;margin:4px 0 0}.profile-hero>span:last-child{margin-left:auto;color:#a3b6a3;font-size:23px}.stat-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:9px}.stat-grid>div{background:#fff;border:1px solid #e9eee7;border-radius:13px;padding:12px 8px;text-align:center}.stat-grid strong{display:block;font-size:19px;color:#3f7551}.stat-grid span{font-size:10px;color:#9aab9d}.usage-card{background:#fff;border:1px solid #e8eee6;border-radius:15px;padding:16px;margin:20px 0 28px}.usage-heading{display:flex;justify-content:space-between;font-size:11px;color:#627b68}.usage-heading strong{color:#477951}.usage-track{height:6px;background:#edf1e9;border-radius:6px;margin:13px 0 9px;overflow:hidden}.usage-track i{display:block;height:100%;background:#83b18a;border-radius:6px}.usage-card p{font-size:10px;color:#a5b2a5;margin:0}.menu-heading{font-size:10px;color:#96a999;font-weight:800;letter-spacing:.07em;margin:0 0 9px}.settings-list{background:#fff;border:1px solid #e8eee6;border-radius:15px;overflow:hidden}.settings-list button{background:none;border:0;border-bottom:1px solid #edf0eb;width:100%;display:flex;gap:12px;align-items:center;padding:13px;text-align:left;color:#45644b}.settings-list button:last-child{border:0}.settings-icon{width:32px;height:32px;border-radius:9px;background:#f0f4ee;display:grid;place-items:center;color:#5d8666;font-size:17px}.settings-list button>span:nth-child(2){font-size:11px;font-weight:700}.settings-list small{display:block;font-size:9px;font-weight:400;color:#a1afa3;margin-top:3px}.settings-list b{margin-left:auto;color:#a5b4a6;font-size:19px;font-weight:400}.profile-footer{text-align:center;color:#a8b6aa;font-size:10px;margin:29px 0}
.welcome{height:100%;padding:16px 23px 31px;display:flex;flex-direction:column}.welcome-top{display:flex;align-items:center;justify-content:space-between}.welcome-top>span{font-size:11px;color:#9bae9d;font-weight:800;letter-spacing:.08em}.welcome-art{flex:1;min-height:220px;position:relative;display:grid;place-items:center}.art-halo{position:absolute;border-radius:50%;border:1px solid #dce9db;width:205px;height:205px}.art-halo.b{width:290px;height:290px}.art-sheet{position:absolute;width:168px;height:211px;background:white;border-radius:18px;box-shadow:0 17px 29px #37654224}.art-sheet.back{transform:rotate(13deg) translate(23px,-4px);background:#dce9d8}.art-sheet.front{transform:rotate(-10deg) translate(-12px,0);padding:12px}.art-image{height:129px;border-radius:10px;background:linear-gradient(160deg,#c4d9bb 10%,#85aa8f 40%,#3e7a73 84%)}.art-sheet.front i{display:block;height:7px;width:80px;border-radius:5px;background:#d9e5d9;margin-top:14px}.art-sheet.front i:last-child{width:55px;margin-top:7px;background:#edf1e9}.art-float{position:absolute;background:#fff;width:47px;height:47px;border-radius:14px;display:grid;place-items:center;box-shadow:0 8px 19px #37654225;color:#579567;font-size:28px}.art-one{right:40px;top:18%;transform:rotate(12deg)}.art-two{left:37px;bottom:16%;transform:rotate(-11deg)}.welcome-bottom .overline{margin-bottom:12px}.welcome-bottom h1{font-size:31px;line-height:1.35;letter-spacing:-.06em;margin:0 0 10px}.welcome-bottom h1 em{color:#5f8c69}.welcome-bottom>p:nth-of-type(2){font-size:12px;line-height:1.7;color:#829787;min-height:40px;margin-bottom:24px}.welcome-dots{display:flex;gap:5px;margin-bottom:22px}.welcome-dots i{width:6px;height:6px;background:#d9e4d8;border-radius:9px}.welcome-dots i.active{width:21px;background:#64936b}.text-button{width:100%;border:0;background:none;color:#7b9680;font-size:11px;font-weight:700;padding:16px 0 0}.sheet-mask{position:absolute;inset:0;background:#18352485;z-index:11;display:flex;align-items:flex-end}.sheet{width:100%;background:#f9f9f5;border-radius:24px 24px 0 0;padding:10px 21px 33px;max-height:86%;overflow-y:auto;box-shadow:0 -15px 40px #1c3d2c21}.sheet-handle{width:37px;height:4px;background:#c9d7c9;border-radius:3px;margin:0 auto 19px}.sheet-heading{display:flex;align-items:center;justify-content:space-between}.sheet-heading h2{font-size:19px;letter-spacing:-.04em;margin:0}.sheet-heading button{border:0;background:none;font-size:24px;color:#8ba08e;line-height:1}.sheet-description{font-size:11px;line-height:1.7;color:#859989;margin:10px 0 21px}.import-choices{display:grid;gap:9px;margin-top:20px}.import-choices button{width:100%;background:#fff;border:1px solid #e6eee5;border-radius:13px;display:flex;align-items:center;gap:12px;padding:12px;text-align:left;color:#45614c}.choice-icon{width:41px;height:41px;border-radius:11px;display:grid;place-items:center;font-size:23px;flex:none}.choice-icon.green{background:#e1eee0;color:#4e8761}.choice-icon.peach{background:#f4e8dc;color:#b48360}.choice-icon.lilac{background:#ece8ef;color:#91769b}.choice-icon.sand{background:#f3efdd;color:#aa9157}.import-choices strong{font-size:12px;display:block}.import-choices small{font-size:10px;color:#9aab9d;display:block;margin-top:3px}.import-choices b{margin-left:auto;color:#b0beb0;font-size:21px;font-weight:400}.sheet-footnote{font-size:10px;text-align:center;color:#a2b2a4;margin:18px 0 0}.field-label{display:block;font-size:11px;color:#55715b;font-weight:700;margin:16px 0 7px}.field-label small{font-size:10px;color:#a4b2a5;font-weight:400}.field{width:100%;background:#fff;border:1px solid #dfe9de;border-radius:11px;outline:0;padding:12px;color:#34513b;font-size:12px}.field:focus{border-color:#79ac81}textarea.field{min-height:113px;resize:vertical}.sheet-submit{margin-top:22px}.secondary-button{width:100%;min-height:47px;border:1px solid #dfe8dc;border-radius:12px;background:#fff;color:#55795b;font-size:12px;font-weight:700;margin-top:10px}.simple-actions{margin-top:19px}.simple-actions button{width:100%;border:0;border-bottom:1px solid #e8eee6;background:#fff;padding:17px;text-align:left;color:#4e7155;font-size:12px}.simple-actions button span{float:right;font-size:17px;color:#a7b8a8}.simple-actions .danger{color:#b0695d}.danger-button{background:#b8655a}.privacy-panel{background:#e7f0e5;border-radius:15px;padding:18px;margin:18px 0}.privacy-panel>span{font-size:26px;color:#5f926e;display:block;margin-bottom:7px}.privacy-panel strong{font-size:14px}.privacy-panel p{font-size:11px;line-height:1.8;color:#708b75;margin:10px 0 0}.pro-card{background:linear-gradient(135deg,#215b3d,#689572);border-radius:18px;color:#fff;padding:20px;margin-top:20px}.pro-card>span{font-size:27px}.pro-card h3{font-size:20px;line-height:1.4;margin:9px 0}.pro-card p{font-size:10px;color:#d3e7d5;line-height:1.6;margin:0}.toast{position:absolute;bottom:93px;left:50%;transform:translateX(-50%);white-space:nowrap;background:#234d35;color:#fff;border-radius:11px;padding:10px 14px;font-size:11px;z-index:20;box-shadow:0 9px 19px #234d3540}
@media(max-width:850px){body{gap:0}.desktop-intro{display:none}.device{width:100%;height:100dvh;min-height:0;border:0;border-radius:0;box-shadow:none}.dynamic-island{display:none}.statusbar{height:38px;padding-bottom:5px}#app{height:calc(100% - 38px)}}
</style>
</head>
<body>
<aside class="desktop-intro"><div class="desktop-logo"><span class="logo-symbol">▤</span><span>Stow <b>收好</b></span></div><p class="desktop-kicker">INTERACTIVE PRODUCT PROTOTYPE · V1</p><h1>看过的好东西,<br /><em>别再找不到。</em></h1><p class="desktop-description">从截图和链接开始,把「收 → 理 → 找」走通。<br />在右侧的手机原型中点击体验。</p><div class="desktop-flow"><span>01 收进来</span><i>→</i><span>02 自动整理</span><i>→</i><span>03 找回来</span></div></aside>
<div class="device">
<div class="statusbar"><span>9:41</span><span class="dynamic-island"></span><span class="status-icons">●●● ▰</span></div>
<main id="app" aria-live="polite"></main>
<div class="home-indicator"></div>
</div>
<script>
/* Stow / 收好 — browser-only interactive product prototype. */
const ITEM_KEY = 'stow-prototype-items-v2';
const WELCOME_KEY = 'stow-prototype-welcome-v2';
const getSaved = key => { try { return localStorage.getItem(key); } catch { return null; } };
const setSaved = (key, value) => { try { localStorage.setItem(key, value); } catch {} };
const TYPES = { all: '全部', place: '地点', product: '商品', content: '内容', event: '活动', task: '待办', document: '资料', conversation: '聊天', other: '其他' };
const TYPE_ICONS = { place: '⌖', product: '▧', content: '▤', event: '✳', task: '✓', document: '▱', conversation: '◌', other: '✦' };
const GROUPS = [
{ name: '想吃的', tag: '想吃', icon: '◒', color: 'peach', detail: '下一顿,就去这里' },
{ name: '想去的', tag: '想去', icon: '⌖', color: 'sage', detail: '收藏的地点与活动' },
{ name: '想买的', tag: '想买', icon: '▧', color: 'lilac', detail: '心动好物清单' },
{ name: '值得再看', tag: '值得再看', icon: '▤', color: 'sand', detail: '文章与灵感' }
];
const daysAgo = n => new Date(Date.now() - n * 86400000).toISOString();
const SEEDS = [
{ id: 'cafe', title: 'O.P.S Cafe', subtitle: '上海 · 静安区 · 咖啡店', summary: '静安区的一家精品咖啡店。截图里推荐了 Dirty 和手冲,店里以简洁的木质空间为主。', type: 'place', source: '截图', asset: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MDAgNDQwIj4KPGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJiZyIgeDI9IjEiIHkyPSIxIj48c3RvcCBzdG9wLWNvbG9yPSIjZDhiZGEyIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjYTU3NzVlIi8+PC9saW5lYXJHcmFkaWVudD48cmFkaWFsR3JhZGllbnQgaWQ9ImNvZmZlZSI+PHN0b3Agc3RvcC1jb2xvcj0iI2Q5YTg3OSIvPjxzdG9wIG9mZnNldD0iLjY2IiBzdG9wLWNvbG9yPSIjOWI2MzNlIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNWQzZTJlIi8+PC9yYWRpYWxHcmFkaWVudD48ZmlsdGVyIGlkPSJzaGFkb3ciPjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjE1Ii8+PC9maWx0ZXI+PC9kZWZzPgo8cmVjdCB3aWR0aD0iNjAwIiBoZWlnaHQ9IjQ0MCIgZmlsbD0idXJsKCNiZykiLz48cGF0aCBkPSJNLTMwIDMxNSA2MTAgODNNLTIwIDM4MCA2MTAgMTQ4TS0yMCA0NDUgNjEwIDIxMyIgc3Ryb2tlPSIjOTU2NzRmIiBzdHJva2Utd2lkdGg9IjgiIG9wYWNpdHk9Ii4xNyIvPjxlbGxpcHNlIGN4PSIzMDciIGN5PSIyODEiIHJ4PSIxNzQiIHJ5PSI0NiIgZmlsbD0iIzU1M2QzMiIgb3BhY2l0eT0iLjI4IiBmaWx0ZXI9InVybCgjc2hhZG93KSIvPjxlbGxpcHNlIGN4PSIzMDIiIGN5PSIyNDYiIHJ4PSIxNjciIHJ5PSIxMzIiIGZpbGw9IiNlOWU0ZDgiLz48ZWxsaXBzZSBjeD0iMzAyIiBjeT0iMjMxIiByeD0iMTMyIiByeT0iMTA3IiBmaWxsPSIjZmJmN2U5Ii8+PGVsbGlwc2UgY3g9IjMwMiIgY3k9IjIyMyIgcng9IjExMiIgcnk9Ijg5IiBmaWxsPSJ1cmwoI2NvZmZlZSkiLz48cGF0aCBkPSJNMzA1IDE2NmMtNTggMC02MyA1Mi03IDU1IDU4IDMgNjIgNDUgMiA1Mm0tNDItOTFjMjkgMTcgNTkgMTkgOTAgM20tOTIgNTBjMzQtMTggNjYtMTUgOTMgNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZjdkZmJjIiBzdHJva2Utd2lkdGg9IjE2IiBzdHJva2UtbGluZWNhcD0icm91bmQiIG9wYWNpdHk9Ii45NSIvPjxwYXRoIGQ9Ik00MjYgMTkwYzc1LTI5IDkwIDU5IDEwIDcwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlZWU4ZGIiIHN0cm9rZS13aWR0aD0iMjIiLz48cGF0aCBkPSJNNDIgNTVjNjEgMTAgMTEwIDMzIDEzMiA3OSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjNDg3NzUxIiBzdHJva2Utd2lkdGg9IjI3IiBzdHJva2UtbGluZWNhcD0icm91bmQiIG9wYWNpdHk9Ii43Ii8+PHBhdGggZD0iTTUyNiAzNjJjLTUyLTEyLTg1LTQtMTI0IDMxIiBmaWxsPSJub25lIiBzdHJva2U9IiM2ODg5NjciIHN0cm9rZS13aWR0aD0iMzUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgb3BhY2l0eT0iLjc1Ii8+Cjwvc3ZnPgo=', tags: ['想吃', '想去', '咖啡', '上海'], info: [['地点', '上海市静安区'], ['推荐', 'Dirty · 手冲']], action: '打开地图', date: daysAgo(0), sourceName: '可能来自小红书' },
{ id: 'keyboard', title: 'Keychron Q1 Max', subtitle: '黑色机械键盘 · ¥1,298', summary: '一款黑色铝合金机械键盘,支持无线连接。截图中重点提到了手感、声音和桌面搭配。', type: 'product', source: '截图', asset: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MDAgNDQwIj4KPGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJiZyIgeDI9IjEiIHkyPSIxIj48c3RvcCBzdG9wLWNvbG9yPSIjNjk3NDZjIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMjgzOTJmIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImtiIiB4Mj0iMCIgeTI9IjEiPjxzdG9wIHN0b3AtY29sb3I9IiMyOTJkMmEiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMxMzFhMTciLz48L2xpbmVhckdyYWRpZW50PjxwYXR0ZXJuIGlkPSJrZXlzIiB3aWR0aD0iNDkiIGhlaWdodD0iNDciIHBhdHRlcm5Vbml0cz0idXNlclNwYWNlT25Vc2UiPjxyZWN0IHg9IjQiIHk9IjMiIHdpZHRoPSI0MSIgaGVpZ2h0PSIzOSIgcng9IjciIGZpbGw9IiM1NjYyNWEiLz48cmVjdCB4PSI2IiB5PSI0IiB3aWR0aD0iMzciIGhlaWdodD0iMzQiIHJ4PSI2IiBmaWxsPSIjMmYzOTMzIi8+PGNpcmNsZSBjeD0iMTYiIGN5PSIxNCIgcj0iMiIgZmlsbD0iIzg5OTc4ZCIgb3BhY2l0eT0iLjc1Ii8+PC9wYXR0ZXJuPjxmaWx0ZXIgaWQ9InMiPjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjE3Ii8+PC9maWx0ZXI+PC9kZWZzPgo8cmVjdCB3aWR0aD0iNjAwIiBoZWlnaHQ9IjQ0MCIgZmlsbD0idXJsKCNiZykiLz48cGF0aCBkPSJNMCAwaDYwMHY0NDBIMHoiIGZpbGw9IiNmZmYiIG9wYWNpdHk9Ii4wMjUiLz48ZWxsaXBzZSBjeD0iMzAzIiBjeT0iMjgzIiByeD0iMjc2IiByeT0iMTI3IiBmaWxsPSIjMDYxMTBiIiBvcGFjaXR5PSIuNDUiIGZpbHRlcj0idXJsKCNzKSIvPjxnIHRyYW5zZm9ybT0icm90YXRlKC04IDMwMCAyMjApIj48cmVjdCB4PSI0OSIgeT0iMTAwIiB3aWR0aD0iNTAwIiBoZWlnaHQ9IjI1NiIgcng9IjI0IiBmaWxsPSIjMTExOTE1Ii8+PHJlY3QgeD0iNjAiIHk9IjExMCIgd2lkdGg9IjQ4MCIgaGVpZ2h0PSIyMzYiIHJ4PSIxOCIgZmlsbD0idXJsKCNrYikiIHN0cm9rZT0iIzc0ODQ3OSIgc3Ryb2tlLXdpZHRoPSIzIi8+PHJlY3QgeD0iNzIiIHk9IjEyMCIgd2lkdGg9IjQ1NSIgaGVpZ2h0PSIyMTEiIHJ4PSI2IiBmaWxsPSJ1cmwoI2tleXMpIi8+PHJlY3QgeD0iMTg1IiB5PSIyODUiIHdpZHRoPSIyMDgiIGhlaWdodD0iMzgiIHJ4PSI3IiBmaWxsPSIjOWE5ZDg1Ii8+PHJlY3QgeD0iMTkwIiB5PSIyODgiIHdpZHRoPSIxOTgiIGhlaWdodD0iMzAiIHJ4PSI2IiBmaWxsPSIjYjhiN2EyIi8+PC9nPjxjaXJjbGUgY3g9IjUyMyIgY3k9IjY0IiByPSI3NCIgZmlsbD0iI2QwYzQ5ZCIgb3BhY2l0eT0iLjE4Ii8+Cjwvc3ZnPgo=', tags: ['想买', '机械键盘', '黑色', '数码'], info: [['品牌', 'Keychron'], ['参考价格', '¥1,298'], ['颜色', '黑色']], action: '加入想买清单', date: daysAgo(36), sourceName: '截图' },
{ id: 'hangzhou', title: '杭州周末两日旅行攻略', subtitle: '西湖 · 灵隐寺 · 湖滨', summary: '适合周末的杭州两日路线。第一天围绕西湖与湖滨,第二天去灵隐寺,附有沿途餐厅建议。', type: 'content', source: '分享链接', asset: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MDAgNDQwIj4KPGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJza3kiIHgyPSIwIiB5Mj0iMSI+PHN0b3Agc3RvcC1jb2xvcj0iI2M3ZDhjZiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI2U4ZThkNCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJsYWtlIiB4Mj0iMCIgeTI9IjEiPjxzdG9wIHN0b3AtY29sb3I9IiNhMmJkYjQiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiM2ZTliOTYiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz4KPHJlY3Qgd2lkdGg9IjYwMCIgaGVpZ2h0PSI0NDAiIGZpbGw9InVybCgjc2t5KSIvPjxjaXJjbGUgY3g9IjQzNyIgY3k9Ijk4IiByPSI1MiIgZmlsbD0iI2YzZTdjMiIgb3BhY2l0eT0iLjgiLz48cGF0aCBkPSJNMCAyMjNROTAgMTAzIDE5NiAyMTJxNzAtMTExIDE3NSAwIDk4LTkxIDIyOSA0djIyNEgwIiBmaWxsPSIjOWNiNGE0Ii8+PHBhdGggZD0iTTAgMjU3cTkzLTkxIDE3NiAyIDExMS0xMjMgMjIxLTEwIDk2LTc2IDIwMyAxOHYxNzNIMCIgZmlsbD0iIzZkOWQ4ZCIvPjxwYXRoIGQ9Ik0wIDI3Mmg2MDB2MTY4SDB6IiBmaWxsPSJ1cmwoI2xha2UpIi8+PHBhdGggZD0iTTAgMzE1cTE1MCAzMCAyOTYgMHQzMDQgME0wIDM2MHExNTAgMzAgMjk2IDB0MzA0IDAiIHN0cm9rZT0iI2QyZTBkMyIgc3Ryb2tlLXdpZHRoPSI3IiBmaWxsPSJub25lIiBvcGFjaXR5PSIuNDgiLz48ZyBmaWxsPSIjMzk0ZjQwIj48cmVjdCB4PSIyMzIiIHk9IjE3MCIgd2lkdGg9Ijc4IiBoZWlnaHQ9IjExMCIvPjxwYXRoIGQ9Im0yMTMgMTczIDU4LTMzIDU4IDMzek0yMjIgMTQybDQ5LTI5IDQ5IDI5ek0yMzEgMTExbDQwLTI0IDQwIDI0eiIvPjxyZWN0IHg9IjI2NiIgeT0iNjUiIHdpZHRoPSIxMCIgaGVpZ2h0PSIzNSIvPjwvZz48cGF0aCBkPSJNNTIgMjcxYzMwLTIyIDU0LTI5IDgxLTIzbTM2MSAwYzI1LTE1IDUwLTE4IDc5LTEzIiBzdHJva2U9IiMyOTRjM2QiIHN0cm9rZS13aWR0aD0iMTQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K', tags: ['值得再看', '杭州', '旅行', '攻略'], info: [['城市', '杭州'], ['适合', '周末两日']], action: '打开原链接', url: 'https://example.com/hangzhou', date: daysAgo(2), sourceName: '分享链接' },
{ id: 'article', title: '如何构建可靠的 AI Agent', subtitle: '技术文章 · 8 分钟阅读', summary: '一篇介绍 AI Agent 工作流、工具调用和评估方法的文章,适合之后仔细阅读。', type: 'content', source: '分享链接', asset: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MDAgNDQwIj4KPGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJiZyIgeDI9IjEiIHkyPSIxIj48c3RvcCBzdG9wLWNvbG9yPSIjZDZkZGQ0Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjOTFhYTlhIi8+PC9saW5lYXJHcmFkaWVudD48ZmlsdGVyIGlkPSJzIj48ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSIxMiIvPjwvZmlsdGVyPjwvZGVmcz4KPHJlY3Qgd2lkdGg9IjYwMCIgaGVpZ2h0PSI0NDAiIGZpbGw9InVybCgjYmcpIi8+PGVsbGlwc2UgY3g9IjMzMCIgY3k9IjM3NyIgcng9IjIwNCIgcnk9IjI2IiBmaWxsPSIjMmE0YzM4IiBvcGFjaXR5PSIuMjQiIGZpbHRlcj0idXJsKCNzKSIvPjxnIHRyYW5zZm9ybT0icm90YXRlKC04IDMwMiAyMjApIj48cmVjdCB4PSIxNDUiIHk9IjM5IiB3aWR0aD0iMzI2IiBoZWlnaHQ9IjM1MiIgcng9IjgiIGZpbGw9IiNmYmZhZjIiLz48cmVjdCB4PSIxNzQiIHk9IjY4IiB3aWR0aD0iOTMiIGhlaWdodD0iOCIgcng9IjQiIGZpbGw9IiNkNmRkZDAiLz48dGV4dCB4PSIxNzQiIHk9IjEzOCIgZm9udC1mYW1pbHk9Ikdlb3JnaWEsc2VyaWYiIGZvbnQtc2l6ZT0iNDMiIGZpbGw9IiMyZDUyM2IiIGZvbnQtd2VpZ2h0PSJib2xkIj5UaGUgYXJ0IG9mPC90ZXh0Pjx0ZXh0IHg9IjE3NCIgeT0iMTg3IiBmb250LWZhbWlseT0iR2VvcmdpYSxzZXJpZiIgZm9udC1zaXplPSI0MyIgZmlsbD0iIzJkNTIzYiIgZm9udC13ZWlnaHQ9ImJvbGQiPnJlbWVtYmVyaW5nLjwvdGV4dD48cmVjdCB4PSIxNzQiIHk9IjIxOCIgd2lkdGg9IjI1OSIgaGVpZ2h0PSI0IiByeD0iMiIgZmlsbD0iI2I0YzdiNyIvPjxyZWN0IHg9IjE3NCIgeT0iMjMzIiB3aWR0aD0iMjIyIiBoZWlnaHQ9IjQiIHJ4PSIyIiBmaWxsPSIjYjRjN2I3Ii8+PHJlY3QgeD0iMTc0IiB5PSIyNDgiIHdpZHRoPSIyNDEiIGhlaWdodD0iNCIgcng9IjIiIGZpbGw9IiNiNGM3YjciLz48cmVjdCB4PSIxNzQiIHk9IjI4OCIgd2lkdGg9IjEyMiIgaGVpZ2h0PSI3MiIgcng9IjQiIGZpbGw9IiNkMGRmY2YiLz48Y2lyY2xlIGN4PSIzNjUiIGN5PSIzMjMiIHI9IjM0IiBmaWxsPSIjOTZiMTliIi8+PC9nPjxwYXRoIGQ9Ik00ODUgNDFxLTM1IDY1IDEzIDExOCIgc3Ryb2tlPSIjNTk4MzZiIiBzdHJva2Utd2lkdGg9IjI5IiBmaWxsPSJub25lIiBzdHJva2UtbGluZWNhcD0icm91bmQiIG9wYWNpdHk9Ii41NSIvPgo8L3N2Zz4K', tags: ['值得再看', 'AI Agent', '技术', '文章'], info: [['主题', 'AI Agent'], ['阅读状态', '待读']], action: '打开原链接', url: 'https://example.com/agent', date: daysAgo(6), sourceName: '分享链接' },
{ id: 'exhibition', title: '上海秋日设计展', subtitle: '10 月 18 日 · 上海', summary: '一场关于海报、产品设计和独立品牌的展览,计划于 10 月 18 日举行。', type: 'event', source: '截图', asset: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MDAgNDQwIj4KPGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJiZyIgeDI9IjEiIHkyPSIxIj48c3RvcCBzdG9wLWNvbG9yPSIjY2JiY2M5Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjODA2Yzg2Ii8+PC9saW5lYXJHcmFkaWVudD48ZmlsdGVyIGlkPSJzIj48ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSIxNSIvPjwvZmlsdGVyPjwvZGVmcz4KPHJlY3Qgd2lkdGg9IjYwMCIgaGVpZ2h0PSI0NDAiIGZpbGw9InVybCgjYmcpIi8+PGVsbGlwc2UgY3g9IjMxMiIgY3k9IjM5OCIgcng9IjE5NiIgcnk9IjI1IiBmaWxsPSIjM2MyODQ2IiBvcGFjaXR5PSIuMzUiIGZpbHRlcj0idXJsKCNzKSIvPjxnIHRyYW5zZm9ybT0icm90YXRlKDUgMzAwIDIyMCkiPjxyZWN0IHg9IjE0NiIgeT0iMjUiIHdpZHRoPSIzMDgiIGhlaWdodD0iMzg1IiBmaWxsPSIjZjJlNWQ4Ii8+PHJlY3QgeD0iMTY0IiB5PSI0MyIgd2lkdGg9IjI3MiIgaGVpZ2h0PSIyNzAiIGZpbGw9IiNkMjllOTkiLz48cGF0aCBkPSJNMTY0IDI3NHExMDAtMTk4IDI3Mi0xNTh2MTk3SDE2NCIgZmlsbD0iIzhjNmM5MSIvPjxjaXJjbGUgY3g9IjMzOCIgY3k9IjE0MyIgcj0iNjUiIGZpbGw9IiNmMWQ1OWIiLz48dGV4dCB4PSIxNjUiIHk9IjM1NiIgZmlsbD0iIzUxMzk1NCIgZm9udC1mYW1pbHk9IkFyaWFsLHNhbnMtc2VyaWYiIGZvbnQtd2VpZ2h0PSJib2xkIiBmb250LXNpemU9IjMzIiBsZXR0ZXItc3BhY2luZz0iNSI+REVTSUdOPC90ZXh0Pjx0ZXh0IHg9IjE2NSIgeT0iMzg2IiBmaWxsPSIjNTEzOTU0IiBmb250LWZhbWlseT0iQXJpYWwsc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGV0dGVyLXNwYWNpbmc9IjYiPkVYSElCSVRJT04gMjAyNjwvdGV4dD48L2c+Cjwvc3ZnPgo=', tags: ['想去', '上海', '展览', '设计'], info: [['时间', '10 月 18 日'], ['地点', '上海']], action: '添加到日历', date: daysAgo(4), sourceName: '可能来自公众号' },
{ id: 'restaurant', title: '东发道茶餐厅', subtitle: '港式茶餐厅 · 上海', summary: '一家值得去的港式茶餐厅。收藏的截图推荐了菠萝油、冻奶茶和午市套餐。', type: 'place', source: '截图', asset: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MDAgNDQwIj4KPGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJiZyIgeDI9IjEiIHkyPSIxIj48c3RvcCBzdG9wLWNvbG9yPSIjZTRjYWE5Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjYWE3ODU4Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImJ1biIgeDI9IjAiIHkyPSIxIj48c3RvcCBzdG9wLWNvbG9yPSIjZjRjZTgxIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjYjk3NDM3Ii8+PC9saW5lYXJHcmFkaWVudD48ZmlsdGVyIGlkPSJzIj48ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSIxNCIvPjwvZmlsdGVyPjwvZGVmcz4KPHJlY3Qgd2lkdGg9IjYwMCIgaGVpZ2h0PSI0NDAiIGZpbGw9InVybCgjYmcpIi8+PHBhdGggZD0iTTAgNzYgNjAwIDI5ME0wIDE0NyA2MDAgMzYxIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iNSIgb3BhY2l0eT0iLjEyIi8+PGVsbGlwc2UgY3g9IjMwNyIgY3k9IjI3NyIgcng9IjE5OCIgcnk9Ijk1IiBmaWxsPSIjNTkzZDMwIiBvcGFjaXR5PSIuMjYiIGZpbHRlcj0idXJsKCNzKSIvPjxlbGxpcHNlIGN4PSIzMDAiIGN5PSIyNTEiIHJ4PSIyMjAiIHJ5PSIxNDkiIGZpbGw9IiNmM2VmZTQiLz48ZWxsaXBzZSBjeD0iMzAwIiBjeT0iMjUxIiByeD0iMTg2IiByeT0iMTIxIiBmaWxsPSIjZGZkZGQwIi8+PHBhdGggZD0iTTE1NyAyNTRxNi0xMDkgMTM5LTExNiAxNDMtMyAxNTEgMTE0LTQyIDg3LTE0NSA4OS0xMTcgMi0xNDUtODMiIGZpbGw9InVybCgjYnVuKSIvPjxwYXRoIGQ9Ik0xNjEgMjUwcTEzNS00OSAyODIgMyIgc3Ryb2tlPSIjNmIzYzI5IiBzdHJva2Utd2lkdGg9IjI0IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTE5MCAyMjlxMTA4LTM0IDIxNyAyIiBzdHJva2U9IiNmOWRmOWUiIHN0cm9rZS13aWR0aD0iMTQiIGZpbGw9Im5vbmUiLz48ZyBmaWxsPSIjZjllMWE5Ij48ZWxsaXBzZSBjeD0iMjI5IiBjeT0iMTg1IiByeD0iOCIgcnk9IjQiIHRyYW5zZm9ybT0icm90YXRlKC0yMCAyMjkgMTg1KSIvPjxlbGxpcHNlIGN4PSIyNzgiIGN5PSIxNjgiIHJ4PSI4IiByeT0iNCIgdHJhbnNmb3JtPSJyb3RhdGUoMTQgMjc4IDE2OCkiLz48ZWxsaXBzZSBjeD0iMzI2IiBjeT0iMTgxIiByeD0iOCIgcnk9IjQiIHRyYW5zZm9ybT0icm90YXRlKC0xNyAzMjYgMTgxKSIvPjxlbGxpcHNlIGN4PSIzNzIiIGN5PSIyMDMiIHJ4PSI4IiByeT0iNCIgdHJhbnNmb3JtPSJyb3RhdGUoMTcgMzcyIDIwMykiLz48L2c+Cjwvc3ZnPgo=', tags: ['想吃', '上海', '餐厅'], info: [['地点', '上海'], ['推荐', '菠萝油 · 冻奶茶']], action: '打开地图', date: daysAgo(9), sourceName: '截图' }
];
const SCAN_CANDIDATES = [
{ id: 'scan-1', title: '街角咖啡馆', subtitle: '地点 · 咖啡', asset: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MDAgNDQwIj4KPGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJiZyIgeDI9IjEiIHkyPSIxIj48c3RvcCBzdG9wLWNvbG9yPSIjZDhiZGEyIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjYTU3NzVlIi8+PC9saW5lYXJHcmFkaWVudD48cmFkaWFsR3JhZGllbnQgaWQ9ImNvZmZlZSI+PHN0b3Agc3RvcC1jb2xvcj0iI2Q5YTg3OSIvPjxzdG9wIG9mZnNldD0iLjY2IiBzdG9wLWNvbG9yPSIjOWI2MzNlIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNWQzZTJlIi8+PC9yYWRpYWxHcmFkaWVudD48ZmlsdGVyIGlkPSJzaGFkb3ciPjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjE1Ii8+PC9maWx0ZXI+PC9kZWZzPgo8cmVjdCB3aWR0aD0iNjAwIiBoZWlnaHQ9IjQ0MCIgZmlsbD0idXJsKCNiZykiLz48cGF0aCBkPSJNLTMwIDMxNSA2MTAgODNNLTIwIDM4MCA2MTAgMTQ4TS0yMCA0NDUgNjEwIDIxMyIgc3Ryb2tlPSIjOTU2NzRmIiBzdHJva2Utd2lkdGg9IjgiIG9wYWNpdHk9Ii4xNyIvPjxlbGxpcHNlIGN4PSIzMDciIGN5PSIyODEiIHJ4PSIxNzQiIHJ5PSI0NiIgZmlsbD0iIzU1M2QzMiIgb3BhY2l0eT0iLjI4IiBmaWx0ZXI9InVybCgjc2hhZG93KSIvPjxlbGxpcHNlIGN4PSIzMDIiIGN5PSIyNDYiIHJ4PSIxNjciIHJ5PSIxMzIiIGZpbGw9IiNlOWU0ZDgiLz48ZWxsaXBzZSBjeD0iMzAyIiBjeT0iMjMxIiByeD0iMTMyIiByeT0iMTA3IiBmaWxsPSIjZmJmN2U5Ii8+PGVsbGlwc2UgY3g9IjMwMiIgY3k9IjIyMyIgcng9IjExMiIgcnk9Ijg5IiBmaWxsPSJ1cmwoI2NvZmZlZSkiLz48cGF0aCBkPSJNMzA1IDE2NmMtNTggMC02MyA1Mi03IDU1IDU4IDMgNjIgNDUgMiA1Mm0tNDItOTFjMjkgMTcgNTkgMTkgOTAgM20tOTIgNTBjMzQtMTggNjYtMTUgOTMgNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZjdkZmJjIiBzdHJva2Utd2lkdGg9IjE2IiBzdHJva2UtbGluZWNhcD0icm91bmQiIG9wYWNpdHk9Ii45NSIvPjxwYXRoIGQ9Ik00MjYgMTkwYzc1LTI5IDkwIDU5IDEwIDcwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlZWU4ZGIiIHN0cm9rZS13aWR0aD0iMjIiLz48cGF0aCBkPSJNNDIgNTVjNjEgMTAgMTEwIDMzIDEzMiA3OSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjNDg3NzUxIiBzdHJva2Utd2lkdGg9IjI3IiBzdHJva2UtbGluZWNhcD0icm91bmQiIG9wYWNpdHk9Ii43Ii8+PHBhdGggZD0iTTUyNiAzNjJjLTUyLTEyLTg1LTQtMTI0IDMxIiBmaWxsPSJub25lIiBzdHJva2U9IiM2ODg5NjciIHN0cm9rZS13aWR0aD0iMzUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgb3BhY2l0eT0iLjc1Ii8+Cjwvc3ZnPgo=', type: 'place', tags: ['想吃', '想去', '咖啡'], summary: '一间适合周末探店的街角咖啡馆。', info: [['分类', '咖啡店']] },
{ id: 'scan-2', title: '桌面键盘灵感', subtitle: '商品 · 数码', asset: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MDAgNDQwIj4KPGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJiZyIgeDI9IjEiIHkyPSIxIj48c3RvcCBzdG9wLWNvbG9yPSIjNjk3NDZjIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMjgzOTJmIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImtiIiB4Mj0iMCIgeTI9IjEiPjxzdG9wIHN0b3AtY29sb3I9IiMyOTJkMmEiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMxMzFhMTciLz48L2xpbmVhckdyYWRpZW50PjxwYXR0ZXJuIGlkPSJrZXlzIiB3aWR0aD0iNDkiIGhlaWdodD0iNDciIHBhdHRlcm5Vbml0cz0idXNlclNwYWNlT25Vc2UiPjxyZWN0IHg9IjQiIHk9IjMiIHdpZHRoPSI0MSIgaGVpZ2h0PSIzOSIgcng9IjciIGZpbGw9IiM1NjYyNWEiLz48cmVjdCB4PSI2IiB5PSI0IiB3aWR0aD0iMzciIGhlaWdodD0iMzQiIHJ4PSI2IiBmaWxsPSIjMmYzOTMzIi8+PGNpcmNsZSBjeD0iMTYiIGN5PSIxNCIgcj0iMiIgZmlsbD0iIzg5OTc4ZCIgb3BhY2l0eT0iLjc1Ii8+PC9wYXR0ZXJuPjxmaWx0ZXIgaWQ9InMiPjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjE3Ii8+PC9maWx0ZXI+PC9kZWZzPgo8cmVjdCB3aWR0aD0iNjAwIiBoZWlnaHQ9IjQ0MCIgZmlsbD0idXJsKCNiZykiLz48cGF0aCBkPSJNMCAwaDYwMHY0NDBIMHoiIGZpbGw9IiNmZmYiIG9wYWNpdHk9Ii4wMjUiLz48ZWxsaXBzZSBjeD0iMzAzIiBjeT0iMjgzIiByeD0iMjc2IiByeT0iMTI3IiBmaWxsPSIjMDYxMTBiIiBvcGFjaXR5PSIuNDUiIGZpbHRlcj0idXJsKCNzKSIvPjxnIHRyYW5zZm9ybT0icm90YXRlKC04IDMwMCAyMjApIj48cmVjdCB4PSI0OSIgeT0iMTAwIiB3aWR0aD0iNTAwIiBoZWlnaHQ9IjI1NiIgcng9IjI0IiBmaWxsPSIjMTExOTE1Ii8+PHJlY3QgeD0iNjAiIHk9IjExMCIgd2lkdGg9IjQ4MCIgaGVpZ2h0PSIyMzYiIHJ4PSIxOCIgZmlsbD0idXJsKCNrYikiIHN0cm9rZT0iIzc0ODQ3OSIgc3Ryb2tlLXdpZHRoPSIzIi8+PHJlY3QgeD0iNzIiIHk9IjEyMCIgd2lkdGg9IjQ1NSIgaGVpZ2h0PSIyMTEiIHJ4PSI2IiBmaWxsPSJ1cmwoI2tleXMpIi8+PHJlY3QgeD0iMTg1IiB5PSIyODUiIHdpZHRoPSIyMDgiIGhlaWdodD0iMzgiIHJ4PSI3IiBmaWxsPSIjOWE5ZDg1Ii8+PHJlY3QgeD0iMTkwIiB5PSIyODgiIHdpZHRoPSIxOTgiIGhlaWdodD0iMzAiIHJ4PSI2IiBmaWxsPSIjYjhiN2EyIi8+PC9nPjxjaXJjbGUgY3g9IjUyMyIgY3k9IjY0IiByPSI3NCIgZmlsbD0iI2QwYzQ5ZCIgb3BhY2l0eT0iLjE4Ii8+Cjwvc3ZnPgo=', type: 'product', tags: ['想买', '数码'], summary: '一款桌面机械键盘,已提取商品主题。', info: [['分类', '机械键盘']] },
{ id: 'scan-3', title: '周末出游灵感', subtitle: '内容 · 旅行', asset: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MDAgNDQwIj4KPGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJza3kiIHgyPSIwIiB5Mj0iMSI+PHN0b3Agc3RvcC1jb2xvcj0iI2M3ZDhjZiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI2U4ZThkNCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJsYWtlIiB4Mj0iMCIgeTI9IjEiPjxzdG9wIHN0b3AtY29sb3I9IiNhMmJkYjQiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiM2ZTliOTYiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz4KPHJlY3Qgd2lkdGg9IjYwMCIgaGVpZ2h0PSI0NDAiIGZpbGw9InVybCgjc2t5KSIvPjxjaXJjbGUgY3g9IjQzNyIgY3k9Ijk4IiByPSI1MiIgZmlsbD0iI2YzZTdjMiIgb3BhY2l0eT0iLjgiLz48cGF0aCBkPSJNMCAyMjNROTAgMTAzIDE5NiAyMTJxNzAtMTExIDE3NSAwIDk4LTkxIDIyOSA0djIyNEgwIiBmaWxsPSIjOWNiNGE0Ii8+PHBhdGggZD0iTTAgMjU3cTkzLTkxIDE3NiAyIDExMS0xMjMgMjIxLTEwIDk2LTc2IDIwMyAxOHYxNzNIMCIgZmlsbD0iIzZkOWQ4ZCIvPjxwYXRoIGQ9Ik0wIDI3Mmg2MDB2MTY4SDB6IiBmaWxsPSJ1cmwoI2xha2UpIi8+PHBhdGggZD0iTTAgMzE1cTE1MCAzMCAyOTYgMHQzMDQgME0wIDM2MHExNTAgMzAgMjk2IDB0MzA0IDAiIHN0cm9rZT0iI2QyZTBkMyIgc3Ryb2tlLXdpZHRoPSI3IiBmaWxsPSJub25lIiBvcGFjaXR5PSIuNDgiLz48ZyBmaWxsPSIjMzk0ZjQwIj48cmVjdCB4PSIyMzIiIHk9IjE3MCIgd2lkdGg9Ijc4IiBoZWlnaHQ9IjExMCIvPjxwYXRoIGQ9Im0yMTMgMTczIDU4LTMzIDU4IDMzek0yMjIgMTQybDQ5LTI5IDQ5IDI5ek0yMzEgMTExbDQwLTI0IDQwIDI0eiIvPjxyZWN0IHg9IjI2NiIgeT0iNjUiIHdpZHRoPSIxMCIgaGVpZ2h0PSIzNSIvPjwvZz48cGF0aCBkPSJNNTIgMjcxYzMwLTIyIDU0LTI5IDgxLTIzbTM2MSAwYzI1LTE1IDUwLTE4IDc5LTEzIiBzdHJva2U9IiMyOTRjM2QiIHN0cm9rZS13aWR0aD0iMTQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K', type: 'content', tags: ['值得再看', '旅行'], summary: '关于周末短途旅行的图片内容。', info: [['分类', '旅行攻略']] },
{ id: 'scan-4', title: '值得读的文章', subtitle: '内容 · 阅读', asset: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MDAgNDQwIj4KPGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJiZyIgeDI9IjEiIHkyPSIxIj48c3RvcCBzdG9wLWNvbG9yPSIjZDZkZGQ0Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjOTFhYTlhIi8+PC9saW5lYXJHcmFkaWVudD48ZmlsdGVyIGlkPSJzIj48ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSIxMiIvPjwvZmlsdGVyPjwvZGVmcz4KPHJlY3Qgd2lkdGg9IjYwMCIgaGVpZ2h0PSI0NDAiIGZpbGw9InVybCgjYmcpIi8+PGVsbGlwc2UgY3g9IjMzMCIgY3k9IjM3NyIgcng9IjIwNCIgcnk9IjI2IiBmaWxsPSIjMmE0YzM4IiBvcGFjaXR5PSIuMjQiIGZpbHRlcj0idXJsKCNzKSIvPjxnIHRyYW5zZm9ybT0icm90YXRlKC04IDMwMiAyMjApIj48cmVjdCB4PSIxNDUiIHk9IjM5IiB3aWR0aD0iMzI2IiBoZWlnaHQ9IjM1MiIgcng9IjgiIGZpbGw9IiNmYmZhZjIiLz48cmVjdCB4PSIxNzQiIHk9IjY4IiB3aWR0aD0iOTMiIGhlaWdodD0iOCIgcng9IjQiIGZpbGw9IiNkNmRkZDAiLz48dGV4dCB4PSIxNzQiIHk9IjEzOCIgZm9udC1mYW1pbHk9Ikdlb3JnaWEsc2VyaWYiIGZvbnQtc2l6ZT0iNDMiIGZpbGw9IiMyZDUyM2IiIGZvbnQtd2VpZ2h0PSJib2xkIj5UaGUgYXJ0IG9mPC90ZXh0Pjx0ZXh0IHg9IjE3NCIgeT0iMTg3IiBmb250LWZhbWlseT0iR2VvcmdpYSxzZXJpZiIgZm9udC1zaXplPSI0MyIgZmlsbD0iIzJkNTIzYiIgZm9udC13ZWlnaHQ9ImJvbGQiPnJlbWVtYmVyaW5nLjwvdGV4dD48cmVjdCB4PSIxNzQiIHk9IjIxOCIgd2lkdGg9IjI1OSIgaGVpZ2h0PSI0IiByeD0iMiIgZmlsbD0iI2I0YzdiNyIvPjxyZWN0IHg9IjE3NCIgeT0iMjMzIiB3aWR0aD0iMjIyIiBoZWlnaHQ9IjQiIHJ4PSIyIiBmaWxsPSIjYjRjN2I3Ii8+PHJlY3QgeD0iMTc0IiB5PSIyNDgiIHdpZHRoPSIyNDEiIGhlaWdodD0iNCIgcng9IjIiIGZpbGw9IiNiNGM3YjciLz48cmVjdCB4PSIxNzQiIHk9IjI4OCIgd2lkdGg9IjEyMiIgaGVpZ2h0PSI3MiIgcng9IjQiIGZpbGw9IiNkMGRmY2YiLz48Y2lyY2xlIGN4PSIzNjUiIGN5PSIzMjMiIHI9IjM0IiBmaWxsPSIjOTZiMTliIi8+PC9nPjxwYXRoIGQ9Ik00ODUgNDFxLTM1IDY1IDEzIDExOCIgc3Ryb2tlPSIjNTk4MzZiIiBzdHJva2Utd2lkdGg9IjI5IiBmaWxsPSJub25lIiBzdHJva2UtbGluZWNhcD0icm91bmQiIG9wYWNpdHk9Ii41NSIvPgo8L3N2Zz4K', type: 'content', tags: ['值得再看', '文章'], summary: '一篇值得再次阅读的文章。', info: [['分类', '文章']] },
{ id: 'scan-5', title: '秋日展览', subtitle: '活动 · 设计', asset: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MDAgNDQwIj4KPGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJiZyIgeDI9IjEiIHkyPSIxIj48c3RvcCBzdG9wLWNvbG9yPSIjY2JiY2M5Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjODA2Yzg2Ii8+PC9saW5lYXJHcmFkaWVudD48ZmlsdGVyIGlkPSJzIj48ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSIxNSIvPjwvZmlsdGVyPjwvZGVmcz4KPHJlY3Qgd2lkdGg9IjYwMCIgaGVpZ2h0PSI0NDAiIGZpbGw9InVybCgjYmcpIi8+PGVsbGlwc2UgY3g9IjMxMiIgY3k9IjM5OCIgcng9IjE5NiIgcnk9IjI1IiBmaWxsPSIjM2MyODQ2IiBvcGFjaXR5PSIuMzUiIGZpbHRlcj0idXJsKCNzKSIvPjxnIHRyYW5zZm9ybT0icm90YXRlKDUgMzAwIDIyMCkiPjxyZWN0IHg9IjE0NiIgeT0iMjUiIHdpZHRoPSIzMDgiIGhlaWdodD0iMzg1IiBmaWxsPSIjZjJlNWQ4Ii8+PHJlY3QgeD0iMTY0IiB5PSI0MyIgd2lkdGg9IjI3MiIgaGVpZ2h0PSIyNzAiIGZpbGw9IiNkMjllOTkiLz48cGF0aCBkPSJNMTY0IDI3NHExMDAtMTk4IDI3Mi0xNTh2MTk3SDE2NCIgZmlsbD0iIzhjNmM5MSIvPjxjaXJjbGUgY3g9IjMzOCIgY3k9IjE0MyIgcj0iNjUiIGZpbGw9IiNmMWQ1OWIiLz48dGV4dCB4PSIxNjUiIHk9IjM1NiIgZmlsbD0iIzUxMzk1NCIgZm9udC1mYW1pbHk9IkFyaWFsLHNhbnMtc2VyaWYiIGZvbnQtd2VpZ2h0PSJib2xkIiBmb250LXNpemU9IjMzIiBsZXR0ZXItc3BhY2luZz0iNSI+REVTSUdOPC90ZXh0Pjx0ZXh0IHg9IjE2NSIgeT0iMzg2IiBmaWxsPSIjNTEzOTU0IiBmb250LWZhbWlseT0iQXJpYWwsc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGV0dGVyLXNwYWNpbmc9IjYiPkVYSElCSVRJT04gMjAyNjwvdGV4dD48L2c+Cjwvc3ZnPgo=', type: 'event', tags: ['想去', '展览'], summary: '一张关于秋季展览的海报。', info: [['分类', '活动']] },
{ id: 'scan-6', title: '周末餐厅', subtitle: '地点 · 餐厅', asset: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MDAgNDQwIj4KPGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJiZyIgeDI9IjEiIHkyPSIxIj48c3RvcCBzdG9wLWNvbG9yPSIjZTRjYWE5Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjYWE3ODU4Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImJ1biIgeDI9IjAiIHkyPSIxIj48c3RvcCBzdG9wLWNvbG9yPSIjZjRjZTgxIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjYjk3NDM3Ii8+PC9saW5lYXJHcmFkaWVudD48ZmlsdGVyIGlkPSJzIj48ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSIxNCIvPjwvZmlsdGVyPjwvZGVmcz4KPHJlY3Qgd2lkdGg9IjYwMCIgaGVpZ2h0PSI0NDAiIGZpbGw9InVybCgjYmcpIi8+PHBhdGggZD0iTTAgNzYgNjAwIDI5ME0wIDE0NyA2MDAgMzYxIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iNSIgb3BhY2l0eT0iLjEyIi8+PGVsbGlwc2UgY3g9IjMwNyIgY3k9IjI3NyIgcng9IjE5OCIgcnk9Ijk1IiBmaWxsPSIjNTkzZDMwIiBvcGFjaXR5PSIuMjYiIGZpbHRlcj0idXJsKCNzKSIvPjxlbGxpcHNlIGN4PSIzMDAiIGN5PSIyNTEiIHJ4PSIyMjAiIHJ5PSIxNDkiIGZpbGw9IiNmM2VmZTQiLz48ZWxsaXBzZSBjeD0iMzAwIiBjeT0iMjUxIiByeD0iMTg2IiByeT0iMTIxIiBmaWxsPSIjZGZkZGQwIi8+PHBhdGggZD0iTTE1NyAyNTRxNi0xMDkgMTM5LTExNiAxNDMtMyAxNTEgMTE0LTQyIDg3LTE0NSA4OS0xMTcgMi0xNDUtODMiIGZpbGw9InVybCgjYnVuKSIvPjxwYXRoIGQ9Ik0xNjEgMjUwcTEzNS00OSAyODIgMyIgc3Ryb2tlPSIjNmIzYzI5IiBzdHJva2Utd2lkdGg9IjI0IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTE5MCAyMjlxMTA4LTM0IDIxNyAyIiBzdHJva2U9IiNmOWRmOWUiIHN0cm9rZS13aWR0aD0iMTQiIGZpbGw9Im5vbmUiLz48ZyBmaWxsPSIjZjllMWE5Ij48ZWxsaXBzZSBjeD0iMjI5IiBjeT0iMTg1IiByeD0iOCIgcnk9IjQiIHRyYW5zZm9ybT0icm90YXRlKC0yMCAyMjkgMTg1KSIvPjxlbGxpcHNlIGN4PSIyNzgiIGN5PSIxNjgiIHJ4PSI4IiByeT0iNCIgdHJhbnNmb3JtPSJyb3RhdGUoMTQgMjc4IDE2OCkiLz48ZWxsaXBzZSBjeD0iMzI2IiBjeT0iMTgxIiByeD0iOCIgcnk9IjQiIHRyYW5zZm9ybT0icm90YXRlKC0xNyAzMjYgMTgxKSIvPjxlbGxpcHNlIGN4PSIzNzIiIGN5PSIyMDMiIHJ4PSI4IiByeT0iNCIgdHJhbnNmb3JtPSJyb3RhdGUoMTcgMzcyIDIwMykiLz48L2c+Cjwvc3ZnPgo=', type: 'place', tags: ['想吃', '餐厅'], summary: '一间适合周末聚餐的餐厅。', info: [['分类', '餐厅']] }
];
const readItems = () => { try { const saved = JSON.parse(getSaved(ITEM_KEY)); return Array.isArray(saved) ? saved : SEEDS; } catch { return SEEDS; } };
const state = {
items: readItems(), view: 'home', previousView: 'home', detailId: null, modal: null,
welcomeStep: getSaved(WELCOME_KEY) ? -1 : 0,
category: 'all', group: null, sourceFilter: '全部来源', sort: '最近收藏',
query: '', selectedScan: new Set(['scan-1', 'scan-2', 'scan-3']), processed: [], processTimer: null,
returnAfterImport: 'home'
};
const app = document.getElementById('app');
const html = value => String(value ?? '').replace(/[&<>"']/g, char => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[char]);
const persist = () => setSaved(ITEM_KEY, JSON.stringify(state.items.map(({ image, ...rest }) => rest)));
const byDate = () => [...state.items].sort((a, b) => new Date(b.date) - new Date(a.date));
const dateLabel = date => { const days = Math.floor((Date.now() - new Date(date).getTime()) / 86400000); return days < 1 ? '今天' : days === 1 ? '昨天' : `${days} 天前`; };
const countGroup = tag => state.items.filter(item => item.tags?.includes(tag)).length;
const findItem = id => state.items.find(item => item.id === id);
const cover = (item, extra = '') => `<div class="cover ${extra}">${item.image ? `<img src="${html(item.image)}" alt="">` : item.asset ? `<img src="${html(item.asset)}" alt="">` : '<span class="cover-fallback">✦</span>'}</div>`;
const icon = name => ({ search: '⌕', home: '⌂', collection: '▤', profile: '◯', back: '‹', chevron: '›', camera: '▣', link: '↗', text: '≡', check: '✓', sparkle: '✦' })[name];
const topbar = (left, right = '') => `<div class="topbar">${left}${right}</div>`;
const screen = (content, className = '') => `<section class="screen ${className}">${content}</section>`;
const nav = () => `<nav class="tabbar" aria-label="主导航">
<button class="nav-item ${state.view === 'home' ? 'active' : ''}" data-action="nav" data-view="home"><span>${icon('home')}</span><small>首页</small></button>
<button class="nav-item ${state.view === 'collection' ? 'active' : ''}" data-action="nav" data-view="collection"><span>${icon('collection')}</span><small>收藏</small></button>
<button class="nav-add" data-action="open-import" aria-label="添加收藏"><span>+</span></button>
<button class="nav-item ${state.view === 'search' ? 'active' : ''}" data-action="nav" data-view="search"><span>${icon('search')}</span><small>搜索</small></button>
<button class="nav-item ${state.view === 'profile' ? 'active' : ''}" data-action="nav" data-view="profile"><span>${icon('profile')}</span><small>我的</small></button>
</nav>`;
const itemCard = item => `<button class="item-card" data-action="open-detail" data-id="${html(item.id)}">${cover(item, 'item-cover')}<span class="item-main"><span class="item-title">${html(item.title)}</span><span class="item-subtitle">${html(item.subtitle)}</span><span class="item-meta"><span class="source-mini">${item.source === '截图' ? '▣' : '↗'} ${html(item.source)}</span><span class="meta-dot">·</span>${dateLabel(item.date)}</span></span><span class="item-arrow">›</span></button>`;
const sectionHeader = (title, action = '', actionName = '') => `<div class="section-heading"><h2>${title}</h2>${action ? `<button data-action="${action}">${actionName} <span>›</span></button>` : ''}</div>`;
function home() {
const today = new Intl.DateTimeFormat('zh-CN', { month: 'long', day: 'numeric', weekday: 'long' }).format(new Date());
return screen(`${topbar(`<div class="brand-lockup"><span class="logo-symbol">▤</span><span>收好</span></div>`, `<button class="avatar-button" data-action="nav" data-view="profile" aria-label="我的">◕</button>`)}
<div class="home-hero"><p class="overline">${today}</p><h1>看过的好东西,<br><em>都在这里。</em></h1><p>顺手收好,想找时就找得到。</p></div>
<button class="search-callout" data-action="nav" data-view="search"><span class="search-glyph">⌕</span><span>找一下之前收藏过的东西…</span><span class="search-shortcut">搜索</span></button>
<button class="scan-callout" data-action="open-scan"><span class="scan-graphic"><i></i><i></i><i></i></span><span class="scan-copy"><small>新截图,等你收好</small><strong>发现 6 张最近的截图</strong><span>选择要整理的内容 <b>→</b></span></span></button>
${sectionHeader('为你整理', 'all-collection', '全部收藏')}
<div class="group-grid">${GROUPS.map(group => `<button class="group-card ${group.color}" data-action="open-group" data-group="${html(group.tag)}"><span class="group-symbol">${group.icon}</span><span class="group-count">${countGroup(group.tag)}</span><strong>${group.name}</strong><small>${group.detail}</small></button>`).join('')}</div>
${sectionHeader('刚刚收好', 'all-collection', '查看全部')}
<div class="items">${byDate().slice(0, 4).map(itemCard).join('')}</div>
<div class="footer-note">每一条好东西,都值得被找回来。<span>✦</span></div>`, 'home-screen');
}
function filteredItems() {
let items = byDate().filter(item => (state.category === 'all' || item.type === state.category) && (!state.group || item.tags?.includes(state.group)) && (state.sourceFilter === '全部来源' || item.source === state.sourceFilter));
if (state.sort === '最早收藏') items.reverse();
return items;
}
function collection() {
const items = filteredItems();
const sections = items.reduce((result, item) => { const label = dateLabel(item.date); (result[label] ||= []).push(item); return result; }, {});
return screen(`${topbar(`<div class="page-header"><p class="overline">YOUR COLLECTION</p><h1>${html(state.group ? GROUPS.find(x => x.tag === state.group)?.name || state.group : '我的收藏')}</h1></div>`, `<button class="circle-button" data-action="open-import" aria-label="添加收藏">+</button>`)}
<p class="page-description">${state.group ? '收好自动放在一起的内容。' : `一共收好了 ${state.items.length} 条内容。`}</p>
<div class="category-scroll">${Object.entries(TYPES).map(([key, label]) => `<button class="category-chip ${state.category === key ? 'selected' : ''}" data-action="category" data-category="${key}">${label}</button>`).join('')}</div>
<div class="filter-bar"><button data-action="source-filter"><span>☷</span> ${html(state.sourceFilter)} <b>⌄</b></button><button data-action="sort"><span>↕</span> ${html(state.sort)} <b>⌄</b></button>${state.group ? '<button data-action="clear-group">清除分组 ×</button>' : ''}</div>
<div class="collection-count">${items.length} 条收藏</div>
${items.length ? Object.entries(sections).map(([label, group]) => `<div class="date-group"><h2>${label}</h2><div class="items">${group.map(itemCard).join('')}</div></div>`).join('') : emptyState('这里还没有内容', '试试其他分类,或添加一条新收藏。')}`, 'collection-screen');
}
function emptyState(title, description) { return `<div class="empty-state"><div class="empty-illustration">⌕</div><strong>${title}</strong><p>${description}</p></div>`; }
function matchedItems(query) {
const q = query.trim().toLowerCase(); if (!q) return [];
const known = ['黑色', '键盘', '咖啡', '静安', '上海', '杭州', '旅行', '攻略', 'ai agent', '文章', '展览', '餐厅', '商品', '设计', '数码'];
const terms = known.filter(term => q.includes(term));
const usable = terms.length ? terms : [q.replace(/之前|看到的|那个|收藏过的|我|上个月|最近|哪些|的/g, '').trim()];
return byDate().filter(item => {
if (q.includes('上个月')) { const days = (Date.now() - new Date(item.date).getTime()) / 86400000; if (days < 28 || days > 62) return false; }
const text = [item.title, item.subtitle, item.summary, ...(item.tags || []), ...(item.info || []).flat()].join(' ').toLowerCase();
return usable.some(term => term && text.includes(term));
});
}
function searchBody() {
if (!state.query.trim()) return `<div class="search-intro"><p class="overline">试着这样找</p><div class="suggestions">${['上个月看到的黑色键盘', '上海静安的咖啡店', '收藏过的 AI Agent 文章', '杭州旅行攻略'].map(query => `<button data-action="suggest" data-query="${html(query)}"><span class="suggest-icon">⌕</span><span>${html(query)}</span><span class="suggest-arrow">↗</span></button>`).join('')}</div><div class="search-tip"><span>✦</span><p>记不住标题也没关系。<br>说出颜色、地点或大概印象就可以。</p></div></div>`;
const items = matchedItems(state.query);
return `<div class="search-results"><div class="result-heading"><strong>${items.length ? `找到 ${items.length} 条相关收藏` : '暂时没找到'}</strong><span>按相关程度排列</span></div>${items.length ? `<div class="items">${items.map(itemCard).join('')}</div><div class="result-hint">这些都是你真实收好的内容,点开即可查看原图和整理结果。</div>` : emptyState('换个说法试试', '去掉时间或地点,搜索更宽泛的关键词。')}</div>`;
}
function search() {
return screen(`${topbar(`<div class="page-header"><p class="overline">FIND IT AGAIN</p><h1>想找什么?</h1></div>`)}
<div class="search-input-wrap"><span>⌕</span><input id="search-input" type="search" value="${html(state.query)}" placeholder="说个大概,就能找回来" autocomplete="off"><button data-action="clear-search" aria-label="清除搜索">×</button></div>
<div id="search-body">${searchBody()}</div>`, 'search-screen');
}
function detail() {
const item = findItem(state.detailId); if (!item) { state.view = 'home'; return home(); }
const tagList = (item.tags || []).filter(tag => !GROUPS.some(group => group.tag === tag));
return screen(`${topbar(`<button class="back-button" data-action="back">‹ <span>返回</span></button>`, `<button class="more-button" data-action="open-more">•••</button>`)}
<div class="detail-cover-wrap">${cover(item, 'detail-cover')}<span class="detail-source">${item.source === '截图' ? '▣' : '↗'} ${html(item.source)}</span></div>
<div class="detail-title-wrap"><p class="overline">${html(TYPES[item.type] || '其他')} · ${dateLabel(item.date)}收好</p><h1>${html(item.title)}</h1><p>${html(item.subtitle)}</p></div>
<div class="ai-summary"><div class="summary-label"><span class="sparkle-box">✦</span><strong>收好帮你整理</strong></div><p>${html(item.summary)}</p></div>
${item.info?.length ? `<div class="detail-block"><h2>关键信息</h2><div class="info-card">${item.info.map(([label, value]) => `<div><span>${html(label)}</span><strong>${html(value)}</strong></div>`).join('')}</div></div>` : ''}
<div class="detail-block"><h2>标签</h2><div class="tag-row">${tagList.length ? tagList.map(tag => `<span>${html(tag)}</span>`).join('') : '<span>新收藏</span>'}</div></div>
${item.action ? `<div class="detail-block"><h2>可以做</h2><button class="action-card" data-action="item-action"><span>${item.action === '打开地图' ? '⌖' : item.action === '添加到日历' ? '▦' : '↗'}</span><strong>${html(item.action)}</strong><b>›</b></button></div>` : ''}
<div class="detail-block source-block"><h2>原始内容</h2><p>${html(item.sourceName || item.source)} · ${new Date(item.date).toLocaleDateString('zh-CN')}</p><small>${item.sourceName?.startsWith('可能') ? '来源根据图片界面推测,可能不准确。' : '保存时记录的来源信息。'}</small></div>
<div class="detail-bottom"><button data-action="edit"><span>✎</span> 编辑整理结果</button><button data-action="delete-prompt" aria-label="删除收藏">♲</button></div>`, 'detail-screen');
}
function scan() {
return screen(`${topbar(`<button class="back-button" data-action="back">‹ <span>返回</span></button>`, `<span class="topbar-label">截图整理</span>`)}
<div class="scan-heading"><div class="eyebrow-icon">▣</div><p class="overline">SCREENSHOT IMPORT</p><h1>发现 6 张新截图</h1><p>选择想收好的内容。这里只是演示,正式版会读取你授权的截图。</p></div>
<div class="scan-selection-head"><strong>最近的截图</strong><button data-action="toggle-all-scan">${state.selectedScan.size === SCAN_CANDIDATES.length ? '取消全选' : '全选'}</button></div>
<div class="scan-grid">${SCAN_CANDIDATES.map(candidate => `<button class="scan-tile ${state.selectedScan.has(candidate.id) ? 'selected' : ''}" data-action="toggle-scan" data-id="${candidate.id}">${cover(candidate, 'scan-cover')}<span class="scan-check">✓</span><strong>${candidate.title}</strong><small>${candidate.subtitle}</small></button>`).join('')}</div>
<div class="scan-bottom"><p>已选择 <strong>${state.selectedScan.size}</strong> 张 · 只处理你确认的图片</p><button class="primary-button" data-action="process-scan" ${state.selectedScan.size ? '' : 'disabled'}>收好这 ${state.selectedScan.size} 张 <span>→</span></button></div>`, 'scan-screen');
}
function processing() {
return screen(`${topbar(`<span></span>`, `<span class="topbar-label">正在整理</span>`)}<div class="processing-view"><div class="processing-orbit"><div class="processing-card one"></div><div class="processing-card two"></div><div class="processing-card three">✦</div></div><div class="processing-copy"><p class="overline">MAKING SENSE OF IT</p><h1>正在帮你收好…</h1><p>识别内容 · 提取线索 · 整理成以后能找到的收藏</p></div><div class="processing-steps"><span class="done">✓ 已收进来</span><span class="active">✦ 正在整理</span><span>⌕ 随时可找</span></div><div class="processing-track"><i></i></div><small>原始内容会先保存,整理失败也不会丢失。</small></div>`, 'processing-screen');
}
function completed() {
const items = state.processed.map(findItem).filter(Boolean);
return screen(`${topbar(`<span></span>`, `<span class="topbar-label">整理完成</span>`)}<div class="complete-hero"><div class="complete-symbol">✓</div><p class="overline">ALL TUCKED AWAY</p><h1>${items.length} 条内容,<br><em>已经收好了。</em></h1><p>标题、分类和线索都帮你整理好了。<br>以后想找,直接说个大概就行。</p></div><div class="complete-list"><strong>刚刚整理</strong><div class="items">${items.map(itemCard).join('')}</div></div><div class="complete-bottom"><button class="primary-button" data-action="complete-home">回到首页 <span>→</span></button></div>`, 'completed-screen');
}
function profile() {
return screen(`${topbar(`<div class="page-header"><p class="overline">YOUR SPACE</p><h1>我的</h1></div>`)}
<div class="profile-hero"><span class="profile-avatar">◕</span><div><strong>好东西,都收好了</strong><p>当前为原型体验模式</p></div><span>›</span></div>
<div class="stat-grid"><div><strong>${state.items.length}</strong><span>已收好</span></div><div><strong>${state.items.filter(item => item.source === '截图').length}</strong><span>张截图</span></div><div><strong>∞</strong><span>次找回</span></div></div>
<div class="usage-card"><div class="usage-heading"><span>本月 AI 整理额度</span><strong>${Math.min(state.items.length, 20)} / 20</strong></div><div class="usage-track"><i style="width:${Math.min(state.items.length * 5, 100)}%"></i></div><p>演示版不会发送内容到服务器。</p></div>
<p class="menu-heading">账户与设置</p><div class="settings-list"><button data-action="modal" data-modal="sync"><span class="settings-icon">☁</span><span>登录与同步<small>让收藏在设备间保持一致</small></span><b>›</b></button><button data-action="modal" data-modal="pro"><span class="settings-icon">✦</span><span>收好 Pro<small>更多整理额度和高级搜索</small></span><b>›</b></button><button data-action="modal" data-modal="privacy"><span class="settings-icon">♧</span><span>隐私与数据<small>查看数据如何保存与删除</small></span><b>›</b></button><button data-action="restart-welcome"><span class="settings-icon">◉</span><span>重新查看引导<small>再走一遍首次使用流程</small></span><b>›</b></button></div>
<div class="profile-footer">Stow · 收好 产品原型 V1.1</div>`, 'profile-screen');
}
function welcome() {
const content = [
{ kicker: 'WELCOME TO STOW', title: '看过的好东西,<br><em>别再找不到。</em>', body: '截图、链接、攻略和商品,顺手扔进来。', accent: '01 / 04' },
{ kicker: 'SAVE WITHOUT SORTING', title: '你负责收,<br><em>收好帮你理。</em>', body: '自动识别标题、地点、商品和日期。你不用建文件夹。', accent: '02 / 04' },
{ kicker: 'FIND IT AGAIN', title: '只记得大概,<br><em>也能找回来。</em>', body: '搜索看到的是你真正存过的东西,还能打开原始内容。', accent: '03 / 04' },
{ kicker: 'YOUR CHOICE', title: '从截图开始,<br><em>也可以慢慢来。</em>', body: '正式版会在你选择后申请照片权限。当前原型提供模拟截图和手动选图。', accent: '04 / 04' }
][state.welcomeStep];
return `<div class="welcome"><div class="welcome-top"><div class="brand-lockup"><span class="logo-symbol">▤</span><span>收好</span></div><span>${content.accent}</span></div><div class="welcome-art"><div class="art-halo a"></div><div class="art-halo b"></div><div class="art-sheet back"></div><div class="art-sheet front"><div class="art-image"></div><i></i><i></i></div><div class="art-float art-one">✦</div><div class="art-float art-two">⌕</div></div><div class="welcome-bottom"><p class="overline">${content.kicker}</p><h1>${content.title}</h1><p>${content.body}</p><div class="welcome-dots">${[0,1,2,3].map(index => `<i class="${index === state.welcomeStep ? 'active' : ''}"></i>`).join('')}</div><button class="primary-button" data-action="welcome-next">${state.welcomeStep === 3 ? '进入收好' : '继续'} <span>→</span></button>${state.welcomeStep === 3 ? '<button class="text-button" data-action="welcome-import">我想手动选择照片</button>' : '<button class="text-button" data-action="welcome-skip">跳过引导</button>'}</div></div>`;
}
function sheet(title, body, className = '') {
return `<div class="sheet-mask" data-action="close-mask"><div class="sheet ${className}" role="dialog" aria-modal="true" aria-label="${html(title)}"><div class="sheet-handle"></div><div class="sheet-heading"><h2>${html(title)}</h2><button data-action="close-modal" aria-label="关闭">×</button></div>${body}</div></div>`;
}
function modal() {
switch (state.modal) {
case 'import': return sheet('把好东西收进来', `<p class="sheet-description">选一种最顺手的方式,剩下的交给收好。</p><div class="import-choices"><button data-action="open-scan"><span class="choice-icon green">▣</span><span><strong>整理最近截图</strong><small>从模拟相册中选几张体验</small></span><b>›</b></button><button data-action="pick-photo"><span class="choice-icon peach">◫</span><span><strong>选择照片或截图</strong><small>从你的设备手动选图</small></span><b>›</b></button><button data-action="modal" data-modal="url"><span class="choice-icon lilac">↗</span><span><strong>保存链接</strong><small>文章、商品、攻略都可以</small></span><b>›</b></button><button data-action="modal" data-modal="text"><span class="choice-icon sand">≡</span><span><strong>保存文本</strong><small>把一段文字收起来</small></span><b>›</b></button></div><input id="photo-input" type="file" accept="image/*" hidden><p class="sheet-footnote">原型在浏览器中模拟整理,不会上传你的内容。</p>`);
case 'url': return sheet('保存一个链接', `<p class="sheet-description">复制链接,收好会帮你整理成一条收藏。</p><label class="field-label" for="import-url">链接地址</label><input class="field" id="import-url" type="url" placeholder="https://example.com/article"><label class="field-label" for="import-title">标题 <small>可选</small></label><input class="field" id="import-title" placeholder="留空则使用网站名称"><button class="primary-button sheet-submit" data-action="save-url">收好这个链接 <span>→</span></button>`);
case 'text': return sheet('保存一段文字', `<p class="sheet-description">一段灵感、一条地址,或者稍后要看的内容。</p><label class="field-label" for="import-text">内容</label><textarea class="field" id="import-text" placeholder="粘贴想记住的内容…"></textarea><button class="primary-button sheet-submit" data-action="save-text">收好这段文字 <span>→</span></button>`);
case 'edit': { const item = findItem(state.detailId); return sheet('编辑整理结果', `<label class="field-label" for="edit-title">标题</label><input class="field" id="edit-title" value="${html(item.title)}"><label class="field-label" for="edit-summary">摘要</label><textarea class="field" id="edit-summary">${html(item.summary)}</textarea><label class="field-label" for="edit-tags">标签 <small>用逗号分隔</small></label><input class="field" id="edit-tags" value="${html(item.tags.join(','))}"><button class="primary-button sheet-submit" data-action="save-edit">保存修改 <span>✓</span></button>`); }
case 'more': return sheet('管理这条收藏', `<div class="simple-actions"><button data-action="edit">✎ 编辑整理结果 <span>›</span></button><button data-action="delete-prompt" class="danger">♲ 删除这条收藏 <span>›</span></button></div>`);
case 'delete': return sheet('删除这条收藏?', `<p class="sheet-description">这条收藏会从当前原型移除。正式版会同时清理对应的云端资源。</p><button class="primary-button danger-button sheet-submit" data-action="confirm-delete">确认删除</button><button class="secondary-button" data-action="close-modal">取消</button>`);
case 'privacy': return sheet('隐私与数据', `<div class="privacy-panel"><span>♧</span><strong>你的内容,只属于你。</strong><p>本原型将演示条目保存在当前浏览器。手动选取的图片只在本次页面打开期间预览,不会上传。正式 App 只处理用户主动授权或分享的内容。</p></div><button class="secondary-button" data-action="reset-data">重置演示数据</button>`);
case 'sync': return sheet('登录与同步', `<p class="sheet-description">正式版支持先匿名使用,产生价值后再通过 Apple 登录开启同步。当前原型未接入账号服务。</p><button class="primary-button sheet-submit" data-action="close-modal">知道了</button>`);
case 'pro': return sheet('收好 Pro', `<div class="pro-card"><span>✦</span><h3>把更多好东西,<br>都放心交给收好。</h3><p>更多 AI 整理额度 · 高级语义搜索 · 更大云同步空间</p></div><p class="sheet-description">订阅仅作为界面原型展示,目前不会产生购买或收费。</p><button class="primary-button sheet-submit" data-action="close-modal">继续体验</button>`);
default: return '';
}
}
function render() {
if (state.welcomeStep >= 0) { app.innerHTML = welcome(); return; }
const page = ({ home, collection, search, detail, scan, processing, completed, profile })[state.view] || home;
const mainView = ['home', 'collection', 'search', 'profile'].includes(state.view);
app.innerHTML = page() + (mainView ? nav() : '') + (state.modal ? modal() : '');
}
function toast(message) {
app.querySelector('.toast')?.remove();
const element = document.createElement('div'); element.className = 'toast'; element.textContent = message; app.appendChild(element);
setTimeout(() => element.remove(), 2600);
}
function go(view) { state.previousView = state.view; state.view = view; state.modal = null; render(); }
function addItem(item) { const newItem = { ...item, id: `custom-${Date.now()}`, date: new Date().toISOString() }; state.items.unshift(newItem); persist(); state.modal = null; state.detailId = newItem.id; go('detail'); toast('已收好 · 已生成模拟整理结果'); }
function analyzeText(text, source, url) {
const cleaned = text.trim(); const isPlace = /咖啡|餐厅|酒店|地址|景点/.test(cleaned); const isProduct = /商品|价格|键盘|耳机|购买/.test(cleaned);
return { title: cleaned.slice(0, 26) + (cleaned.length > 26 ? '…' : ''), subtitle: isPlace ? '地点 · 新收藏' : isProduct ? '商品 · 新收藏' : '内容 · 新收藏', summary: cleaned.slice(0, 110) + (cleaned.length > 110 ? '…' : ''), type: isPlace ? 'place' : isProduct ? 'product' : 'content', source, tags: [isPlace ? '想去' : isProduct ? '想买' : '值得再看'], info: url ? [['链接', new URL(url).hostname]] : [], action: url ? '打开原链接' : null, url, sourceName: source };
}
app.addEventListener('input', event => {
if (event.target.id === 'search-input') {
state.query = event.target.value;
document.getElementById('search-body').innerHTML = searchBody();
}
});
app.addEventListener('change', event => {
if (event.target.id !== 'photo-input' || !event.target.files?.length) return;
const file = event.target.files[0];
const name = file.name.replace(/\.[^.]+$/, '').replace(/[_-]/g, ' ').trim() || '新图片';
addItem({ ...analyzeText(name, '手动选图'), title: name, subtitle: '图片 · 刚刚导入', summary: `已收好「${name}」。正式版会在这里展示 OCR、分类和 AI 提取结果。`, type: 'other', tags: ['新收藏'], image: URL.createObjectURL(file), info: [['文件', file.name]], sourceName: '手动选择的图片' });
});
app.addEventListener('click', event => {
const button = event.target.closest('[data-action]'); if (!button) return;
const action = button.dataset.action;
if (action === 'close-mask' && event.target !== button) return;
switch (action) {
case 'nav': state.group = null; go(button.dataset.view); break;
case 'all-collection': state.group = null; state.category = 'all'; go('collection'); break;
case 'open-group': state.group = button.dataset.group; state.category = 'all'; go('collection'); break;
case 'clear-group': state.group = null; render(); break;
case 'category': state.category = button.dataset.category; render(); break;
case 'source-filter': { const filters = ['全部来源', '截图', '分享链接', '手动添加', '手动选图']; state.sourceFilter = filters[(filters.indexOf(state.sourceFilter) + 1) % filters.length]; render(); break; }
case 'sort': state.sort = state.sort === '最近收藏' ? '最早收藏' : '最近收藏'; render(); break;
case 'open-detail': state.detailId = button.dataset.id; go('detail'); break;
case 'back': go(state.previousView === 'processing' ? 'home' : state.previousView); break;
case 'open-import': state.modal = 'import'; render(); break;
case 'open-scan': state.selectedScan = new Set(['scan-1', 'scan-2', 'scan-3']); go('scan'); break;
case 'toggle-scan': state.selectedScan.has(button.dataset.id) ? state.selectedScan.delete(button.dataset.id) : state.selectedScan.add(button.dataset.id); render(); break;
case 'toggle-all-scan': state.selectedScan = new Set(state.selectedScan.size === SCAN_CANDIDATES.length ? [] : SCAN_CANDIDATES.map(item => item.id)); render(); break;
case 'process-scan': {
if (!state.selectedScan.size) return;
const stamp = Date.now();
const items = SCAN_CANDIDATES.filter(item => state.selectedScan.has(item.id)).map((item, index) => ({ ...item, id: `import-${stamp}-${index}`, source: '截图', date: new Date(stamp - index * 1000).toISOString(), sourceName: '截图' }));
state.items.unshift(...items); state.processed = items.map(item => item.id); persist(); go('processing');
clearTimeout(state.processTimer); state.processTimer = setTimeout(() => go('completed'), 1800);
break;
}
case 'complete-home': go('home'); break;
case 'clear-search': state.query = ''; render(); document.getElementById('search-input')?.focus(); break;
case 'suggest': state.query = button.dataset.query; render(); document.getElementById('search-input')?.focus(); break;
case 'modal': state.modal = button.dataset.modal; render(); break;
case 'close-modal': case 'close-mask': state.modal = null; render(); break;
case 'pick-photo': document.getElementById('photo-input')?.click(); break;
case 'save-url': {
const raw = document.getElementById('import-url').value.trim(); const customTitle = document.getElementById('import-title').value.trim();
let url; try { url = new URL(raw); if (!['http:', 'https:'].includes(url.protocol)) throw Error(); } catch { toast('请输入有效的 http 或 https 链接'); return; }
const title = customTitle || url.hostname.replace(/^www\./, '');
addItem({ ...analyzeText(title, '手动添加', url.href), title, subtitle: `链接 · ${url.hostname}`, summary: `已收好来自 ${url.hostname} 的链接。正式版会提取网页内容并自动整理。` });
break;
}
case 'save-text': {
const text = document.getElementById('import-text').value.trim(); if (!text) return toast('先粘贴一些内容');
addItem(analyzeText(text, '手动添加')); break;
}
case 'open-more': state.modal = 'more'; render(); break;
case 'edit': state.modal = 'edit'; render(); break;
case 'save-edit': {
const item = findItem(state.detailId); const title = document.getElementById('edit-title').value.trim();
if (!title) return toast('标题不能为空');
item.title = title; item.summary = document.getElementById('edit-summary').value.trim();
item.tags = document.getElementById('edit-tags').value.split(/[,,]/).map(value => value.trim()).filter(Boolean);
persist(); state.modal = null; render(); toast('已保存修改'); break;
}
case 'delete-prompt': state.modal = 'delete'; render(); break;
case 'confirm-delete': state.items = state.items.filter(item => item.id !== state.detailId); persist(); state.detailId = null; state.modal = null; go('collection'); toast('已删除收藏'); break;
case 'item-action': {
const item = findItem(state.detailId);
if (item.action === '打开原链接' && item.url) window.open(item.url, '_blank', 'noopener');
else if (item.action === '打开地图') window.open(`https://maps.apple.com/?q=${encodeURIComponent(item.title)}`, '_blank', 'noopener');
else toast('正式版会在用户确认后执行此操作');
break;
}
case 'reset-data': state.items = structuredClone(SEEDS); persist(); state.modal = null; go('home'); toast('已重置演示数据'); break;
case 'restart-welcome': state.welcomeStep = 0; state.modal = null; render(); break;
case 'welcome-next': state.welcomeStep < 3 ? (state.welcomeStep++, render()) : (state.welcomeStep = -1, setSaved(WELCOME_KEY, '1'), render()); break;
case 'welcome-skip': state.welcomeStep = -1; setSaved(WELCOME_KEY, '1'); render(); break;
case 'welcome-import': state.welcomeStep = -1; setSaved(WELCOME_KEY, '1'); state.modal = 'import'; render(); break;
}
});
render();
</script>
</body>
</html>