312 lines
81 KiB
HTML
312 lines
81 KiB
HTML
<!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 => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[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>
|