diff --git a/links/templates/links/post_detail.html b/links/templates/links/post_detail.html index f35fb4c..8d8689d 100644 --- a/links/templates/links/post_detail.html +++ b/links/templates/links/post_detail.html @@ -27,6 +27,103 @@ --apple-radius-sm: 14px; --apple-shadow-card: 0 4px 24px rgba(0, 0, 0, 0.05); --apple-shadow-lift: 0 18px 44px rgba(0, 0, 0, 0.10); + + /* Reader surface (cards) — follows theme */ + --reader-surface: #ffffff; + --reader-surface-2: #fafafa; + --reader-control: #f5f5f7; + --reader-hover: rgba(0, 0, 0, 0.05); + --reader-progress: linear-gradient(90deg, #0071e3 0%, #7c3aed 100%); + --reader-font-size: 17px; + --reader-line-height: 1.85; + } + + /* ============================================================ + Reader themes (微信读书-style): light / paper / green / night + Overrides the --apple-* + --md-* design tokens so the whole + page (cards, TOC, tables, code, quotes) re-skins together. + ============================================================ */ + body.reader-paper { + --apple-bg: #ece5d5; + --apple-text: #40392e; + --apple-gray: #8a8272; + --apple-separator: rgba(64, 57, 46, 0.10); + --reader-surface: #faf6ec; + --reader-surface-2: #f3ede0; + --reader-control: #efe8d8; + --reader-hover: rgba(64, 57, 46, 0.07); + --reader-progress: linear-gradient(90deg, #b8860b 0%, #8a6d1f 100%); + --md-text: #40392e; + --md-muted: #8a8272; + --md-heading: #332d23; + --md-border: #ddd4c0; + --md-accent: #9a6b1f; + --md-accent-soft: rgba(154, 107, 31, 0.12); + --md-table-head-bg: #efe8d8; + --md-table-zebra: rgba(154, 130, 60, 0.05); + --md-code-bg: #f0e9da; + --md-code-text: #4a4234; + --md-pre-bg: #3d362a; + --md-pre-text: #e8e0cd; + --md-quote-bg: rgba(154, 130, 60, 0.07); + --md-hr: #ddd4c0; + } + body.reader-green { + --apple-bg: #dde8da; + --apple-text: #26342a; + --apple-gray: #5f7163; + --apple-separator: rgba(38, 52, 42, 0.10); + --reader-surface: #eef5ec; + --reader-surface-2: #e3eee0; + --reader-control: #dde8da; + --reader-hover: rgba(38, 52, 42, 0.06); + --reader-progress: linear-gradient(90deg, #2e8b57 0%, #1e6b3c 100%); + --md-text: #26342a; + --md-muted: #5f7163; + --md-heading: #1d2a20; + --md-border: #c9dac4; + --md-accent: #1e7b34; + --md-accent-soft: rgba(30, 123, 52, 0.10); + --md-table-head-bg: #e3eee0; + --md-table-zebra: rgba(46, 139, 87, 0.05); + --md-code-bg: #e3eee0; + --md-code-text: #2c3a30; + --md-pre-bg: #22301f; + --md-pre-text: #dce8d9; + --md-quote-bg: rgba(46, 139, 87, 0.06); + --md-hr: #c9dac4; + } + body.reader-night { + --apple-bg: #131316; + --apple-text: #d4d4dc; + --apple-gray: #8b8b96; + --apple-blue: #4d9fff; + --apple-blue-hover: #66aeff; + --apple-green: #4caf7d; + --apple-purple: #c58af9; + --apple-red: #ff6b61; + --apple-separator: rgba(255, 255, 255, 0.10); + --reader-surface: #1c1c22; + --reader-surface-2: #232329; + --reader-control: #26262e; + --reader-hover: rgba(255, 255, 255, 0.07); + --reader-progress: linear-gradient(90deg, #4d9fff 0%, #c58af9 100%); + --apple-shadow-card: 0 4px 24px rgba(0, 0, 0, 0.35); + --apple-shadow-lift: 0 18px 44px rgba(0, 0, 0, 0.55); + --md-text: #c4c4cf; + --md-muted: #8b8b96; + --md-heading: #ececf2; + --md-border: #33333c; + --md-accent: #7db4ff; + --md-accent-soft: rgba(77, 159, 255, 0.14); + --md-table-head-bg: #26262e; + --md-table-zebra: rgba(255, 255, 255, 0.03); + --md-code-bg: #2a2a33; + --md-code-text: #e0e0e8; + --md-pre-bg: #0d0d10; + --md-pre-text: #c8c8d4; + --md-quote-bg: rgba(255, 255, 255, 0.04); + --md-hr: #2c2c34; } body { @@ -36,10 +133,11 @@ color: var(--apple-text); -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; + transition: background-color 0.4s var(--apple-ease), color 0.4s var(--apple-ease); } .apple-card { - background: #fff; + background: var(--reader-surface); border-radius: var(--apple-radius-lg); border: 1px solid rgba(0, 0, 0, 0.04); box-shadow: var(--apple-shadow-card); @@ -108,7 +206,7 @@ display: flex; } .modal-content { - background: #fff; + background: var(--reader-surface); border-radius: var(--apple-radius-md); width: 100%; max-width: 26rem; @@ -404,11 +502,12 @@ white-space: nowrap; } .main-content { - background: #fff; - border-radius: var(--apple-radius-lg); - border: 1px solid rgba(0, 0, 0, 0.04); - box-shadow: var(--apple-shadow-card); - padding: clamp(1.25rem, 4vw, 2.5rem); + background: var(--reader-surface); + border-radius: var(--apple-radius-lg); + border: 1px solid rgba(0, 0, 0, 0.04); + box-shadow: var(--apple-shadow-card); + padding: clamp(1.25rem, 4vw, 2.5rem); + transition: background-color 0.4s var(--apple-ease), border-color 0.4s var(--apple-ease); } /* ===== Table of Contents Layout ===== */ @@ -422,7 +521,7 @@ .post-actions { display: flex; align-items: center; gap: 0.5rem; flex-shrink: 0; flex-wrap: wrap; max-width: 100%; } .post-meta { display: flex; flex-direction: column; gap: 0.85rem; margin-bottom: 1.25rem; } .post-summary { position: relative; padding-left: 1rem; border-left: 3px solid rgba(0, 113, 227, 0.35); } - .post-summary p { font-size: 1.08rem; color: var(--apple-gray); font-style: italic; margin: 0; } + .post-summary p { font-size: 1.02rem; color: var(--apple-gray); font-style: normal; margin: 0; line-height: 1.7; } .post-tags { display: flex; flex-wrap: wrap; gap: 0.5rem; } .post-timestamps { display: flex; flex-wrap: wrap; align-items: center; gap: 1.5rem; font-size: 0.85rem; color: var(--apple-gray); background: var(--apple-bg); border-radius: 980px; padding: 0.6rem 1.1rem; } .post-timestamps .timestamp-item { display: flex; align-items: center; } @@ -474,11 +573,12 @@ } } #toc-sidebar-inner > div { - background: #fff; - border: 1px solid rgba(0, 0, 0, 0.04); - border-radius: var(--apple-radius-md); - box-shadow: var(--apple-shadow-card); - padding: 1.25rem; + background: var(--reader-surface); + border: 1px solid rgba(0, 0, 0, 0.04); + border-radius: var(--apple-radius-md); + box-shadow: var(--apple-shadow-card); + padding: 1.25rem; + transition: background-color 0.4s var(--apple-ease), border-color 0.4s var(--apple-ease); } .toc-card-label { font-size: 0.7rem; font-weight: 600; color: var(--apple-gray); @@ -570,10 +670,257 @@ } #toc-nav-mobile .toc-link:hover { color: var(--apple-blue); background-color: rgba(0, 113, 227, 0.08); } #toc-nav-mobile .toc-link.toc-active { color: var(--apple-blue); border-left-color: var(--apple-blue); font-weight: 500; } + + /* ============================================================ + Reader typography — 微信读书-style reading experience + ============================================================ */ + /* Comfortable measure: ~68-75 chars per line */ + .post-article { + font-size: var(--reader-font-size, 17px); + line-height: var(--reader-line-height, 1.85); + max-width: 46rem; + margin-left: auto; + margin-right: auto; + transition: font-size 0.25s var(--apple-ease), line-height 0.25s var(--apple-ease); + } + .post-article * { font-family: var(--reader-font-family, inherit); } + .post-article code, .post-article pre, .post-article code * { + font-family: 'SF Mono', 'Monaco', 'Menlo', 'Cascadia Code', 'JetBrains Mono', Consolas, monospace !important; + } + .post-article h1, .post-article h2, .post-article h3, .post-article h4 { + font-family: var(--reader-font-family, inherit); + scroll-margin-top: 5.5rem; + } + .post-article table { font-size: 0.92em; } + + /* Font families */ + .reader-font-system { --reader-font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "PingFang SC", "Noto Sans SC", "Microsoft YaHei", "Segoe UI", sans-serif; } + .reader-font-serif { --reader-font-family: "Songti SC", "Noto Serif SC", "Source Han Serif SC", "STSong", "SimSun", Georgia, "Times New Roman", serif; } + .reader-font-hei { --reader-font-family: "PingFang SC", "Noto Sans SC", "Source Han Sans SC", "Microsoft YaHei", "Heiti SC", "SimHei", sans-serif; } + .reader-font-kai { --reader-font-family: "Kaiti SC", "KaiTi", "STKaiti", "Noto Serif SC", "Songti SC", serif; } + .reader-font-mono { --reader-font-family: "SF Mono", "Menlo", "Monaco", "Cascadia Code", "JetBrains Mono", Consolas, monospace; } + + /* ============================================================ + Reading progress bar (top of viewport, below navbar) + ============================================================ */ + #reading-progress { + position: fixed; + top: 0; left: 0; + height: 3px; + width: 0%; + background: var(--reader-progress); + border-radius: 0 3px 3px 0; + z-index: 100; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15); + transition: width 0.12s linear; + pointer-events: none; + } + + /* ============================================================ + Aa reading toolbar + ============================================================ */ + .reader-toolbar-btn { + display: inline-flex; align-items: center; justify-content: center; + width: 36px; height: 36px; border-radius: 50%; + border: none; cursor: pointer; + background: var(--reader-control); + color: var(--apple-text); + font-size: 0.95rem; font-weight: 700; letter-spacing: 0.02em; + transition: background 0.25s ease, transform 0.2s var(--apple-ease), box-shadow 0.25s ease; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); + } + .reader-toolbar-btn:hover { background: var(--reader-hover); transform: translateY(-1px); } + .reader-toolbar-btn:active { transform: scale(0.94); } + .reader-toolbar-btn.active { background: var(--apple-blue); color: #fff; } + + #reader-panel { + position: fixed; + right: 1.25rem; + bottom: 5.75rem; + z-index: 90; + width: min(21rem, calc(100vw - 2rem)); + background: var(--reader-surface); + border: 1px solid rgba(0, 0, 0, 0.07); + border-radius: var(--apple-radius-md); + box-shadow: var(--apple-shadow-lift); + padding: 1.1rem 1.2rem 1.2rem; + opacity: 0; + transform: translateY(12px) scale(0.98); + pointer-events: none; + transition: opacity 0.28s var(--apple-ease), transform 0.28s var(--apple-ease); + } + #reader-panel.open { + opacity: 1; + transform: translateY(0) scale(1); + pointer-events: auto; + } + .rp-section { margin-bottom: 1rem; } + .rp-section:last-child { margin-bottom: 0; } + .rp-label { + font-size: 0.68rem; font-weight: 700; color: var(--apple-gray); + text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: 0.5rem; + } + /* Theme swatches */ + .rp-themes { display: flex; gap: 0.75rem; } + .rp-swatch { + width: 34px; height: 34px; border-radius: 50%; + border: 2px solid transparent; cursor: pointer; + position: relative; transition: transform 0.2s var(--apple-ease), border-color 0.2s ease; + } + .rp-swatch:hover { transform: scale(1.1); } + .rp-swatch.active { border-color: var(--apple-blue); box-shadow: 0 0 0 3px rgba(0, 113, 227, 0.18); } + .rp-swatch svg { position: absolute; inset: 0; margin: auto; width: 14px; height: 14px; color: #fff; opacity: 0; transition: opacity 0.2s; } + .rp-swatch.active svg { opacity: 1; } + .rp-swatch-light { background: #ffffff; border-color: #d1d1d6; } + .rp-swatch-paper { background: #faf6ec; border-color: #ddd4c0; } + .rp-swatch-green { background: #eef5ec; border-color: #c9dac4; } + .rp-swatch-night { background: #1c1c22; border-color: #44444e; } + /* Font chips */ + .rp-fonts { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.45rem; } + .rp-font-chip { + padding: 0.45rem 0.3rem; border-radius: var(--apple-radius-sm); + border: 1px solid rgba(0, 0, 0, 0.08); background: var(--reader-surface-2); + cursor: pointer; text-align: center; color: var(--apple-text); + transition: border-color 0.2s, background 0.2s; + } + .rp-font-chip:hover { background: var(--reader-hover); } + .rp-font-chip.active { border-color: var(--apple-blue); background: rgba(0, 113, 227, 0.08); } + .rp-font-chip .rp-font-name { display: block; font-size: 1rem; line-height: 1.3; } + .rp-font-chip .rp-font-meta { display: block; font-size: 0.62rem; color: var(--apple-gray); margin-top: 0.15rem; } + .rp-font-chip[data-font="system"] .rp-font-name { font-family: -apple-system, "PingFang SC", sans-serif; } + .rp-font-chip[data-font="serif"] .rp-font-name { font-family: "Songti SC", "Noto Serif SC", serif; } + .rp-font-chip[data-font="hei"] .rp-font-name { font-family: "PingFang SC", "Heiti SC", "Microsoft YaHei", sans-serif; } + .rp-font-chip[data-font="kai"] .rp-font-name { font-family: "Kaiti SC", "KaiTi", "STKaiti", serif; } + .rp-font-chip[data-font="mono"] .rp-font-name { font-family: "SF Mono", Menlo, Consolas, monospace; } + /* Size & spacing */ + .rp-row { display: flex; align-items: center; gap: 0.6rem; } + .rp-size-btn { + width: 38px; height: 38px; border-radius: 50%; + border: 1px solid rgba(0, 0, 0, 0.1); background: var(--reader-surface-2); + color: var(--apple-text); font-weight: 700; cursor: pointer; + display: inline-flex; align-items: center; justify-content: center; + transition: background 0.2s, transform 0.15s var(--apple-ease); + } + .rp-size-btn:hover { background: var(--reader-hover); } + .rp-size-btn:active { transform: scale(0.92); } + .rp-size-btn[data-action="dec"] { font-size: 0.85rem; } + .rp-size-btn[data-action="inc"] { font-size: 1.15rem; } + .rp-size-val { flex: 1; text-align: center; font-size: 0.85rem; color: var(--apple-gray); font-variant-numeric: tabular-nums; } + .rp-spacing { display: flex; gap: 0.45rem; } + .rp-spacing-btn { + flex: 1; padding: 0.45rem 0; border-radius: var(--apple-radius-sm); + border: 1px solid rgba(0, 0, 0, 0.08); background: var(--reader-surface-2); + color: var(--apple-text); font-size: 0.75rem; cursor: pointer; text-align: center; + transition: border-color 0.2s, background 0.2s; + } + .rp-spacing-btn:hover { background: var(--reader-hover); } + .rp-spacing-btn.active { border-color: var(--apple-blue); background: rgba(0, 113, 227, 0.08); } + .rp-reset { + width: 100%; margin-top: 0.9rem; padding: 0.5rem; + border-radius: var(--apple-radius-sm); border: 1px dashed rgba(0, 0, 0, 0.15); + background: transparent; color: var(--apple-gray); font-size: 0.75rem; cursor: pointer; + transition: color 0.2s, border-color 0.2s; + } + .rp-reset:hover { color: var(--apple-text); border-color: var(--apple-blue); } + /* Backdrop for closing panel on outside click (invisible) */ + #reader-panel-backdrop { + position: fixed; inset: 0; z-index: 85; display: none; + } + #reader-panel.open ~ #reader-panel-backdrop { display: block; } + + /* Aa button placement: inline in actions row (desktop) + floating (mobile) */ + .reader-toolbar-wrap { display: inline-flex; align-items: center; } + #reader-fab { + display: none; + position: fixed; + bottom: 5.75rem; right: 1.5rem; + z-index: 41; + width: 3rem; height: 3rem; + border-radius: 9999px; + background: var(--apple-blue); color: #fff; + border: none; cursor: pointer; + font-size: 1.1rem; font-weight: 800; + box-shadow: 0 12px 28px rgba(0, 113, 227, 0.32); + align-items: center; justify-content: center; + transition: background-color 0.2s ease, transform 0.2s var(--apple-ease); + } + #reader-fab:hover { transform: translateY(-1px); } + @media (max-width: 1023px) { + #reader-fab { display: flex; } + #reader-panel { + bottom: 9.25rem; + right: 1.25rem; + } + } + @media (max-width: 640px) { + #reader-panel { + left: 1rem; + right: 1rem; + width: auto; + bottom: 9.4rem; + } + } + /* Scroll-to-top mini button */ + #back-to-top { + display: none; + position: fixed; + bottom: 1.5rem; left: 1.5rem; + z-index: 41; + width: 2.75rem; height: 2.75rem; + border-radius: 9999px; + border: 1px solid rgba(0, 0, 0, 0.08); + background: var(--reader-surface); + color: var(--apple-gray); + box-shadow: var(--apple-shadow-card); + cursor: pointer; + align-items: center; justify-content: center; + transition: opacity 0.3s ease, transform 0.2s var(--apple-ease), color 0.2s; + } + #back-to-top:hover { color: var(--apple-blue); transform: translateY(-1px); } + #back-to-top.visible { display: flex; } + @media (min-width: 1024px) { + #back-to-top { display: none !important; } + } + /* Night theme tweaks for controls */ + body.reader-night .rp-size-btn, + body.reader-night .rp-spacing-btn, + body.reader-night .rp-font-chip { + background: var(--reader-control); + } + body.reader-night .action-btn-secondary { background: var(--reader-control); } + body.reader-night .apple-btn-secondary { background: var(--reader-control); color: var(--apple-text); } + body.reader-night .post-timestamps { background: var(--reader-control); } + body.reader-night .post-summary { border-left-color: rgba(77, 159, 255, 0.4); } + body.reader-night .think-header { background: var(--reader-control); } + body.reader-night .think-header:hover { background: var(--reader-hover); } + body.reader-night .think-panel { background: var(--reader-surface); } + body.reader-night .append-textarea { background: var(--reader-control); color: var(--apple-text); } + body.reader-night .append-textarea:focus { background: var(--reader-surface-2); } + body.reader-night .audio-player { background: var(--reader-surface-2); } + body.reader-night .voice-controls { background: var(--reader-surface-2); } + body.reader-night .voice-select-compact, + body.reader-night .custom-voice-input { background: var(--reader-control); color: var(--apple-text); } + body.reader-night .toc-mobile-btn-back { background: var(--reader-control); } + body.reader-night #toc-sidebar .toc-h3, + body.reader-night #toc-sidebar .toc-h4, + body.reader-night #toc-nav-mobile .toc-h3, + body.reader-night #toc-nav-mobile .toc-h4 { color: #a5a5b0; } + body.reader-night .post-summary p { color: #a5a5b0; } + body.reader-night .prose blockquote { color: #a9a9b4; } + /* Reveal animation on load */ + @keyframes readerFadeIn { + from { opacity: 0; transform: translateY(10px); } + to { opacity: 1; transform: translateY(0); } + } + .main-content { animation: readerFadeIn 0.5s var(--apple-ease) both; } + {% endblock %} {% block content %} + +
+