Write on the margins of the internet. Powered by the AT Protocol. margin.at
extension web atproto comments

some nice improvements and fixes

+457 -362
-29
extension/bun.lock
··· 7 7 "@webext-core/messaging": "^1.4.0", 8 8 "clsx": "^2.1.1", 9 9 "lucide-react": "^0.563.0", 10 - "pdfjs-dist": "^5.4.624", 11 10 "react": "^18.3.1", 12 11 "react-dom": "^18.3.1", 13 12 }, ··· 179 178 "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], 180 179 181 180 "@mdn/browser-compat-data": ["@mdn/browser-compat-data@7.1.22", "", {}, "sha512-pvfjbTDEYTDNKl9u3KbRzeNmU7PrcsFiGzIUbcB9JPq1LziTMs6YpE2x3Gf+2gHOPQzdO/KQ8hAr1Kkkzpklrg=="], 182 - 183 - "@napi-rs/canvas": ["@napi-rs/canvas@0.1.91", "", { "optionalDependencies": { "@napi-rs/canvas-android-arm64": "0.1.91", "@napi-rs/canvas-darwin-arm64": "0.1.91", "@napi-rs/canvas-darwin-x64": "0.1.91", "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.91", "@napi-rs/canvas-linux-arm64-gnu": "0.1.91", "@napi-rs/canvas-linux-arm64-musl": "0.1.91", "@napi-rs/canvas-linux-riscv64-gnu": "0.1.91", "@napi-rs/canvas-linux-x64-gnu": "0.1.91", "@napi-rs/canvas-linux-x64-musl": "0.1.91", "@napi-rs/canvas-win32-arm64-msvc": "0.1.91", "@napi-rs/canvas-win32-x64-msvc": "0.1.91" } }, "sha512-eeIe1GoB74P1B0Nkw6pV8BCQ3hfCfvyYr4BntzlCsnFXzVJiPMDnLeIx3gVB0xQMblHYnjK/0nCLvirEhOjr5g=="], 184 - 185 - "@napi-rs/canvas-android-arm64": ["@napi-rs/canvas-android-arm64@0.1.91", "", { "os": "android", "cpu": "arm64" }, "sha512-SLLzXXgSnfct4zy/BVAfweZQkYkPJsNsJ2e5DOE8DFEHC6PufyUrwb12yqeu2So2IOIDpWJJaDAxKY/xpy6MYQ=="], 186 - 187 - "@napi-rs/canvas-darwin-arm64": ["@napi-rs/canvas-darwin-arm64@0.1.91", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bzdbCjIjw3iRuVFL+uxdSoMra/l09ydGNX9gsBxO/zg+5nlppscIpj6gg+nL6VNG85zwUarDleIrUJ+FWHvmuA=="], 188 - 189 - "@napi-rs/canvas-darwin-x64": ["@napi-rs/canvas-darwin-x64@0.1.91", "", { "os": "darwin", "cpu": "x64" }, "sha512-q3qpkpw0IsG9fAS/dmcGIhCVoNxj8ojbexZKWwz3HwxlEWsLncEQRl4arnxrwbpLc2nTNTyj4WwDn7QR5NDAaA=="], 190 - 191 - "@napi-rs/canvas-linux-arm-gnueabihf": ["@napi-rs/canvas-linux-arm-gnueabihf@0.1.91", "", { "os": "linux", "cpu": "arm" }, "sha512-Io3g8wJZVhK8G+Fpg1363BE90pIPqg+ZbeehYNxPWDSzbgwU3xV0l8r/JBzODwC7XHi1RpFEk+xyUTMa2POj6w=="], 192 - 193 - "@napi-rs/canvas-linux-arm64-gnu": ["@napi-rs/canvas-linux-arm64-gnu@0.1.91", "", { "os": "linux", "cpu": "arm64" }, "sha512-HBnto+0rxx1bQSl8bCWA9PyBKtlk2z/AI32r3cu4kcNO+M/5SD4b0v1MWBWZyqMQyxFjWgy3ECyDjDKMC6tY1A=="], 194 - 195 - "@napi-rs/canvas-linux-arm64-musl": ["@napi-rs/canvas-linux-arm64-musl@0.1.91", "", { "os": "linux", "cpu": "arm64" }, "sha512-/eJtVe2Xw9A86I4kwXpxxoNagdGclu12/NSMsfoL8q05QmeRCbfjhg1PJS7ENAuAvaiUiALGrbVfeY1KU1gztQ=="], 196 - 197 - "@napi-rs/canvas-linux-riscv64-gnu": ["@napi-rs/canvas-linux-riscv64-gnu@0.1.91", "", { "os": "linux", "cpu": "none" }, "sha512-floNK9wQuRWevUhhXRcuis7h0zirdytVxPgkonWO+kQlbvxV7gEUHGUFQyq4n55UHYFwgck1SAfJ1HuXv/+ppQ=="], 198 - 199 - "@napi-rs/canvas-linux-x64-gnu": ["@napi-rs/canvas-linux-x64-gnu@0.1.91", "", { "os": "linux", "cpu": "x64" }, "sha512-c3YDqBdf7KETuZy2AxsHFMsBBX1dWT43yFfWUq+j1IELdgesWtxf/6N7csi3VPf6VA3PmnT9EhMyb+M1wfGtqw=="], 200 - 201 - "@napi-rs/canvas-linux-x64-musl": ["@napi-rs/canvas-linux-x64-musl@0.1.91", "", { "os": "linux", "cpu": "x64" }, "sha512-RpZ3RPIwgEcNBHSHSX98adm+4VP8SMT5FN6250s5jQbWpX/XNUX5aLMfAVJS/YnDjS1QlsCgQxFOPU0aCCWgag=="], 202 - 203 - "@napi-rs/canvas-win32-arm64-msvc": ["@napi-rs/canvas-win32-arm64-msvc@0.1.91", "", { "os": "win32", "cpu": "arm64" }, "sha512-gF8MBp4X134AgVurxqlCdDA2qO0WaDdi9o6Sd5rWRVXRhWhYQ6wkdEzXNLIrmmros0Tsp2J0hQzx4ej/9O8trQ=="], 204 - 205 - "@napi-rs/canvas-win32-x64-msvc": ["@napi-rs/canvas-win32-x64-msvc@0.1.91", "", { "os": "win32", "cpu": "x64" }, "sha512-++gtW9EV/neKI8TshD8WFxzBYALSPag2kFRahIJV+LYsyt5kBn21b1dBhEUDHf7O+wiZmuFCeUa7QKGHnYRZBA=="], 206 181 207 182 "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], 208 183 ··· 872 847 873 848 "node-notifier": ["node-notifier@10.0.1", "", { "dependencies": { "growly": "^1.3.0", "is-wsl": "^2.2.0", "semver": "^7.3.5", "shellwords": "^0.1.1", "uuid": "^8.3.2", "which": "^2.0.2" } }, "sha512-YX7TSyDukOZ0g+gmzjB6abKu+hTGvO8+8+gIFDsRCU2t8fLV/P2unmt+LGFaIa4y64aX98Qksa97rgz4vMNeLQ=="], 874 849 875 - "node-readable-to-web-readable-stream": ["node-readable-to-web-readable-stream@0.4.2", "", {}, "sha512-/cMZNI34v//jUTrI+UIo4ieHAB5EZRY/+7OmXZgBxaWBMcW2tGdceIw06RFxWxrKZ5Jp3sI2i5TsRo+CBhtVLQ=="], 876 - 877 850 "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], 878 851 879 852 "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], ··· 929 902 "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], 930 903 931 904 "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], 932 - 933 - "pdfjs-dist": ["pdfjs-dist@5.4.624", "", { "optionalDependencies": { "@napi-rs/canvas": "^0.1.88", "node-readable-to-web-readable-stream": "^0.4.2" } }, "sha512-sm6TxKTtWv1Oh6n3C6J6a8odejb5uO4A4zo/2dgkHuC0iu8ZMAXOezEODkVaoVp8nX1Xzr+0WxFJJmUr45hQzg=="], 934 905 935 906 "pend": ["pend@1.2.0", "", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="], 936 907
extension/public/icons/icon-128.png

This is a binary file and will not be displayed.

extension/public/icons/icon-16.png

This is a binary file and will not be displayed.

extension/public/icons/icon-32.png

This is a binary file and will not be displayed.

extension/public/icons/icon-48.png

This is a binary file and will not be displayed.

extension/public/icons/icon-64.png

This is a binary file and will not be displayed.

+1 -1
extension/public/icons/logo.svg
··· 1 - <svg width="265" height="231" viewBox="0 0 265 231" fill="#6366f1" xmlns="http://www.w3.org/2000/svg"> 1 + <svg width="265" height="231" viewBox="0 0 265 231" fill="#027bff" xmlns="http://www.w3.org/2000/svg"> 2 2 <path d="M0 230 V0 H199 V65.7156 H149.5 V115.216 H182.5 L199 131.716 V230 Z"/> 3 3 <path d="M215 214.224 V230 H264.5 V0 H215.07 V16.2242 H248.5 V214.224 H215 Z"/> 4 4 </svg>
+21 -21
extension/src/assets/styles.css
··· 5 5 @tailwind utilities; 6 6 7 7 :root { 8 - --bg-primary: #0c0c0c; 9 - --bg-secondary: #141414; 10 - --bg-tertiary: #1c1c1c; 11 - --bg-card: #161616; 12 - --bg-elevated: #1e1e1e; 13 - --bg-hover: #262626; 14 - --text-primary: #e8e8e3; 15 - --text-secondary: #a1a09a; 16 - --text-tertiary: #6b6a65; 8 + --bg-primary: #18181b; 9 + --bg-secondary: #1e1e22; 10 + --bg-tertiary: #27272a; 11 + --bg-card: #1e1e22; 12 + --bg-elevated: #27272a; 13 + --bg-hover: #2e2e33; 14 + --text-primary: #fafafa; 15 + --text-secondary: #a1a1aa; 16 + --text-tertiary: #71717a; 17 17 --border: rgba(255, 255, 255, 0.08); 18 18 --border-strong: rgba(255, 255, 255, 0.14); 19 - --accent: #7aa2f7; 20 - --accent-hover: #9bbcff; 21 - --accent-subtle: rgba(122, 162, 247, 0.14); 19 + --accent: #3b82f6; 20 + --accent-hover: #2563eb; 21 + --accent-subtle: rgba(59, 130, 246, 0.12); 22 22 --success: #34d399; 23 23 --warning: #fbbf24; 24 24 --shadow: 0 1px 3px rgba(0, 0, 0, 0.4); ··· 26 26 } 27 27 28 28 .light { 29 - --bg-primary: #fafaf8; 29 + --bg-primary: #fafafa; 30 30 --bg-secondary: #ffffff; 31 - --bg-tertiary: #f2f2ef; 31 + --bg-tertiary: #f4f4f5; 32 32 --bg-card: #ffffff; 33 33 --bg-elevated: #ffffff; 34 - --bg-hover: #eaeae6; 35 - --text-primary: #1a1a18; 36 - --text-secondary: #6b6a65; 37 - --text-tertiary: #a1a09a; 34 + --bg-hover: #f4f4f5; 35 + --text-primary: #18181b; 36 + --text-secondary: #71717a; 37 + --text-tertiary: #a1a1aa; 38 38 --border: rgba(0, 0, 0, 0.08); 39 39 --border-strong: rgba(0, 0, 0, 0.14); 40 - --accent: #3b82f6; 41 - --accent-hover: #2563eb; 42 - --accent-subtle: rgba(59, 130, 246, 0.08); 40 + --accent: #2563eb; 41 + --accent-hover: #1d4ed8; 42 + --accent-subtle: rgba(37, 99, 235, 0.08); 43 43 --shadow: 0 1px 3px rgba(0, 0, 0, 0.06); 44 44 --shadow-lg: 0 4px 20px rgba(0, 0, 0, 0.08); 45 45 }
+126 -106
extension/src/components/popup/App.tsx
··· 369 369 if (loading) { 370 370 return ( 371 371 <div className="flex items-center justify-center h-screen"> 372 - <div className="animate-spin rounded-full h-8 w-8 border-2 border-[var(--accent)] border-t-transparent" /> 372 + <div className="w-5 h-5 border-2 border-[var(--accent)] border-t-transparent rounded-full animate-spin" /> 373 373 </div> 374 374 ); 375 375 } ··· 380 380 {showSettings && ( 381 381 <div className="absolute inset-0 bg-[var(--bg-primary)] z-10 flex flex-col"> 382 382 <header className="flex items-center justify-between px-4 py-3 border-b border-[var(--border)]"> 383 - <span className="font-medium">Settings</span> 383 + <span className="text-sm font-semibold">Settings</span> 384 384 <button 385 385 onClick={() => setShowSettings(false)} 386 - className="text-[var(--text-tertiary)] hover:text-[var(--text-primary)]" 386 + className="p-1.5 text-[var(--text-tertiary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-hover)] rounded-lg transition-colors" 387 387 > 388 - <X size={18} /> 388 + <X size={16} /> 389 389 </button> 390 390 </header> 391 391 392 - <div className="flex-1 overflow-y-auto p-4 space-y-6"> 392 + <div className="flex-1 overflow-y-auto p-4 space-y-5"> 393 393 <div> 394 - <label className="block text-sm font-medium mb-2">API URL</label> 394 + <label className="block text-xs font-medium text-[var(--text-secondary)] mb-1.5"> 395 + API URL 396 + </label> 395 397 <input 396 398 type="text" 397 399 value={apiUrl} 398 400 onChange={(e) => setApiUrl(e.target.value)} 399 - className="w-full p-2.5 bg-[var(--bg-card)] border border-[var(--border)] rounded-lg text-sm focus:outline-none focus:border-[var(--accent)]" 401 + className="w-full px-3 py-2 bg-[var(--bg-card)] border border-[var(--border)] rounded-lg text-sm focus:outline-none focus:border-[var(--accent)] focus:ring-2 focus:ring-[var(--accent-subtle)] transition-all" 400 402 placeholder="https://margin.at" 401 403 /> 402 404 </div> 403 405 404 406 <div> 405 - <label className="block text-sm font-medium mb-2">Theme</label> 406 - <div className="flex gap-2"> 407 + <label className="block text-xs font-medium text-[var(--text-secondary)] mb-1.5"> 408 + Theme 409 + </label> 410 + <div className="flex gap-1.5"> 407 411 {(['light', 'dark', 'system'] as const).map((t) => ( 408 412 <button 409 413 key={t} 410 414 onClick={() => handleThemeChange(t)} 411 - className={`flex-1 py-2 px-3 text-xs rounded-lg border transition-colors flex items-center justify-center gap-1 ${ 415 + className={`flex-1 py-2 px-3 text-xs font-medium rounded-lg transition-colors flex items-center justify-center gap-1.5 ${ 412 416 theme === t 413 - ? 'bg-[var(--accent)] text-white border-[var(--accent)]' 414 - : 'bg-[var(--bg-card)] border-[var(--border)] hover:bg-[var(--bg-hover)]' 417 + ? 'bg-[var(--accent)] text-white' 418 + : 'bg-[var(--bg-card)] border border-[var(--border)] hover:bg-[var(--bg-hover)]' 415 419 }`} 416 420 > 417 421 {t === 'light' ? ( ··· 431 435 <div className="p-4 border-t border-[var(--border)]"> 432 436 <button 433 437 onClick={saveSettings} 434 - className="w-full py-2.5 bg-[var(--accent)] text-white rounded-lg font-medium hover:bg-[var(--accent-hover)] transition-colors" 438 + className="w-full py-2.5 bg-[var(--accent)] text-white rounded-lg text-sm font-medium hover:bg-[var(--accent-hover)] transition-colors" 435 439 > 436 440 Save Settings 437 441 </button> ··· 439 443 </div> 440 444 )} 441 445 442 - <div className="flex flex-col items-center justify-center flex-1 p-6 text-center"> 443 - <img src="/icons/logo.svg" alt="Margin" className="w-12 h-12 mb-4" /> 444 - <h2 className="text-lg font-semibold mb-2">Sign in with AT Protocol</h2> 445 - <p className="text-[var(--text-secondary)] text-sm mb-6"> 446 - Connect your Bluesky account to annotate, highlight, and bookmark the web. 446 + <div className="flex flex-col items-center justify-center flex-1 p-8 text-center"> 447 + <div className="w-14 h-14 rounded-2xl bg-[var(--accent-subtle)] flex items-center justify-center mb-5"> 448 + <img src="/icons/logo.svg" alt="Margin" className="w-8 h-8" /> 449 + </div> 450 + <h2 className="font-display text-xl font-bold tracking-tight mb-2">Welcome to Margin</h2> 451 + <p className="text-[var(--text-secondary)] text-sm leading-relaxed mb-8 max-w-[280px]"> 452 + Annotate, highlight, and bookmark the web with your AT Protocol identity. 447 453 </p> 448 454 <button 449 455 onClick={() => browser.tabs.create({ url: `${apiUrl}/login` })} 450 - className="px-6 py-2.5 bg-[var(--accent)] text-white rounded-lg font-medium hover:bg-[var(--accent-hover)] transition-colors" 456 + className="w-full max-w-[240px] px-6 py-2.5 bg-[var(--accent)] text-white rounded-xl text-sm font-semibold hover:bg-[var(--accent-hover)] transition-colors" 451 457 > 452 - Continue 458 + Sign In 453 459 </button> 454 460 <button 455 461 onClick={() => setShowSettings(true)} 456 - className="mt-4 text-xs text-[var(--text-tertiary)] hover:text-[var(--text-primary)] flex items-center gap-1" 462 + className="mt-4 text-xs text-[var(--text-tertiary)] hover:text-[var(--text-primary)] flex items-center gap-1.5 px-3 py-1.5 rounded-lg hover:bg-[var(--bg-hover)] transition-colors" 457 463 > 458 464 <Settings size={12} /> Settings 459 465 </button> ··· 467 473 {showSettings && ( 468 474 <div className="absolute inset-0 bg-[var(--bg-primary)] z-10 flex flex-col"> 469 475 <header className="flex items-center justify-between px-4 py-3 border-b border-[var(--border)]"> 470 - <span className="font-medium">Settings</span> 476 + <span className="text-sm font-semibold">Settings</span> 471 477 <button 472 478 onClick={() => setShowSettings(false)} 473 - className="text-[var(--text-tertiary)] hover:text-[var(--text-primary)]" 479 + className="p-1.5 text-[var(--text-tertiary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-hover)] rounded-lg transition-colors" 474 480 > 475 - <X size={18} /> 481 + <X size={16} /> 476 482 </button> 477 483 </header> 478 484 479 - <div className="flex-1 overflow-y-auto p-4 space-y-6"> 485 + <div className="flex-1 overflow-y-auto p-4 space-y-5"> 480 486 <div> 481 - <label className="block text-sm font-medium mb-2">API URL</label> 487 + <label className="block text-xs font-medium text-[var(--text-secondary)] mb-1.5"> 488 + API URL 489 + </label> 482 490 <input 483 491 type="text" 484 492 value={apiUrl} 485 493 onChange={(e) => setApiUrl(e.target.value)} 486 - className="w-full p-2.5 bg-[var(--bg-card)] border border-[var(--border)] rounded-lg text-sm focus:outline-none focus:border-[var(--accent)]" 494 + className="w-full px-3 py-2 bg-[var(--bg-card)] border border-[var(--border)] rounded-lg text-sm focus:outline-none focus:border-[var(--accent)] focus:ring-2 focus:ring-[var(--accent-subtle)] transition-all" 487 495 placeholder="https://margin.at" 488 496 /> 489 - <p className="text-xs text-[var(--text-tertiary)] mt-1"> 490 - Change this for development or self-hosted instances 497 + <p className="text-[11px] text-[var(--text-tertiary)] mt-1"> 498 + For development or self-hosted instances 491 499 </p> 492 500 </div> 493 501 494 - <div className="flex items-center justify-between"> 502 + <div className="flex items-center justify-between p-3 bg-[var(--bg-card)] border border-[var(--border)] rounded-lg"> 495 503 <div> 496 - <label className="text-sm font-medium">Show page overlays</label> 497 - <p className="text-xs text-[var(--text-tertiary)]"> 498 - Highlights, badges, and tooltips on pages 504 + <div className="text-sm font-medium">Page Overlay</div> 505 + <p className="text-[11px] text-[var(--text-tertiary)] mt-0.5"> 506 + Show highlights and annotations on pages 499 507 </p> 500 508 </div> 501 - <input 502 - type="checkbox" 503 - checked={overlayEnabled} 504 - onChange={(e) => setOverlayEnabled(e.target.checked)} 505 - className="w-5 h-5 rounded accent-[var(--accent)]" 506 - /> 509 + <button 510 + onClick={() => setOverlayEnabled(!overlayEnabled)} 511 + className={`relative w-10 h-[22px] rounded-full transition-colors ${ 512 + overlayEnabled 513 + ? 'bg-[var(--accent)]' 514 + : 'bg-[var(--bg-hover)] border border-[var(--border)]' 515 + }`} 516 + > 517 + <div 518 + className={`absolute top-[3px] w-4 h-4 rounded-full bg-white shadow-sm transition-transform ${ 519 + overlayEnabled ? 'left-[22px]' : 'left-[3px]' 520 + }`} 521 + /> 522 + </button> 507 523 </div> 508 524 509 525 <div> 510 - <label className="block text-sm font-medium mb-2">Theme</label> 511 - <div className="flex gap-2"> 526 + <label className="block text-xs font-medium text-[var(--text-secondary)] mb-1.5"> 527 + Theme 528 + </label> 529 + <div className="flex gap-1.5"> 512 530 {(['light', 'dark', 'system'] as const).map((t) => ( 513 531 <button 514 532 key={t} 515 533 onClick={() => handleThemeChange(t)} 516 - className={`flex-1 py-2 px-3 text-xs rounded-lg border transition-colors flex items-center justify-center gap-1 ${ 534 + className={`flex-1 py-2 px-3 text-xs font-medium rounded-lg transition-colors flex items-center justify-center gap-1.5 ${ 517 535 theme === t 518 - ? 'bg-[var(--accent)] text-white border-[var(--accent)]' 519 - : 'bg-[var(--bg-card)] border-[var(--border)] hover:bg-[var(--bg-hover)]' 536 + ? 'bg-[var(--accent)] text-white' 537 + : 'bg-[var(--bg-card)] border border-[var(--border)] hover:bg-[var(--bg-hover)]' 520 538 }`} 521 539 > 522 540 {t === 'light' ? ( ··· 536 554 <div className="p-4 border-t border-[var(--border)]"> 537 555 <button 538 556 onClick={saveSettings} 539 - className="w-full py-2.5 bg-[var(--accent)] text-white rounded-lg font-medium hover:bg-[var(--accent-hover)] transition-colors" 557 + className="w-full py-2.5 bg-[var(--accent)] text-white rounded-lg text-sm font-medium hover:bg-[var(--accent-hover)] transition-colors" 540 558 > 541 559 Save 542 560 </button> ··· 544 562 </div> 545 563 )} 546 564 547 - <header className="flex items-center justify-between px-4 py-2.5 border-b border-[var(--border)] bg-[var(--bg-secondary)]"> 548 - <div className="flex items-center gap-2.5"> 549 - <img src="/icons/logo.svg" alt="Margin" className="w-6 h-6" /> 550 - <span className="font-bold text-sm tracking-tight">Margin</span> 551 - </div> 565 + <header className="flex items-center justify-between px-4 py-2.5 border-b border-[var(--border)]"> 552 566 <div className="flex items-center gap-2"> 553 - <div className="text-xs text-[var(--text-secondary)] bg-[var(--bg-card)] px-2.5 py-1.5 rounded-full border border-[var(--border)]"> 567 + <img src="/icons/logo.svg" alt="Margin" className="w-5 h-5" /> 568 + <span className="font-display font-bold text-sm tracking-tight">Margin</span> 569 + </div> 570 + <div className="flex items-center gap-1.5"> 571 + <button 572 + onClick={() => browser.tabs.create({ url: apiUrl })} 573 + className="text-[11px] text-[var(--text-tertiary)] hover:text-[var(--accent)] px-2 py-1 rounded-md hover:bg-[var(--bg-hover)] transition-colors" 574 + > 554 575 @{session.handle} 555 - </div> 576 + </button> 577 + <button 578 + onClick={() => setShowSettings(true)} 579 + className="p-1.5 text-[var(--text-tertiary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-hover)] rounded-lg transition-colors" 580 + title="Settings" 581 + > 582 + <Settings size={15} /> 583 + </button> 556 584 </div> 557 585 </header> 558 586 559 - <div className="flex border-b border-[var(--border)] px-2 gap-0.5 bg-[var(--bg-secondary)]"> 587 + <div className="flex border-b border-[var(--border)] px-2 gap-0.5"> 560 588 {(['page', 'bookmarks', 'highlights', 'collections'] as Tab[]).map((tab) => { 561 589 const icons: Record<Tab, JSX.Element> = { 562 590 page: <Globe size={13} />, ··· 591 619 {activeTab === 'page' && ( 592 620 <div> 593 621 <div className="p-4 border-b border-[var(--border)]"> 594 - <div className="flex items-start gap-3 p-3 bg-[var(--bg-card)] border border-[var(--border)] rounded-xl"> 595 - <div className="w-10 h-10 rounded-lg bg-[var(--bg-hover)] flex items-center justify-center flex-shrink-0 overflow-hidden"> 622 + <div className="flex items-center gap-3 p-3 bg-[var(--bg-card)] border border-[var(--border)] rounded-xl"> 623 + <div className="w-9 h-9 rounded-lg bg-[var(--bg-hover)] flex items-center justify-center flex-shrink-0 overflow-hidden"> 596 624 {currentUrl ? ( 597 625 <img 598 626 src={`https://www.google.com/s2/favicons?domain=${new URL(currentUrl).hostname}&sz=64`} 599 627 alt="" 600 - className="w-6 h-6" 628 + className="w-5 h-5" 601 629 onError={(e) => { 602 630 (e.target as HTMLImageElement).style.display = 'none'; 603 631 (e.target as HTMLImageElement).nextElementSibling?.classList.remove( ··· 607 635 /> 608 636 ) : null} 609 637 <Globe 610 - size={18} 638 + size={16} 611 639 className={`text-[var(--text-tertiary)] ${currentUrl ? 'hidden' : ''}`} 612 640 /> 613 641 </div> 614 642 <div className="flex-1 min-w-0"> 615 - <div className="text-sm font-semibold truncate mb-0.5"> 616 - {currentTitle || 'Untitled'} 617 - </div> 618 - <div className="text-xs text-[var(--text-tertiary)] truncate"> 643 + <div className="text-sm font-medium truncate">{currentTitle || 'Untitled'}</div> 644 + <div className="text-[11px] text-[var(--text-tertiary)] truncate"> 619 645 {currentUrl ? new URL(currentUrl).hostname : ''} 620 646 </div> 621 647 </div> 622 - <div className="flex items-center gap-1.5 flex-shrink-0"> 648 + <div className="flex items-center gap-1 flex-shrink-0"> 623 649 <button 624 650 onClick={() => { 625 651 if (currentUrl) { ··· 627 653 browser.tabs.create({ url: shareUrl }); 628 654 } 629 655 }} 630 - className="p-2 rounded-lg bg-[var(--bg-hover)] hover:bg-[var(--accent-subtle)] text-[var(--text-secondary)] hover:text-[var(--accent)] transition-all" 631 - title="View all annotations on margin.at" 656 + className="p-1.5 rounded-md text-[var(--text-tertiary)] hover:text-[var(--accent)] hover:bg-[var(--bg-hover)] transition-colors" 657 + title="View on Margin" 632 658 > 633 - <Eye size={16} /> 659 + <Eye size={15} /> 634 660 </button> 635 661 <button 636 662 onClick={handleBookmark} 637 663 disabled={bookmarking || bookmarked} 638 - className={`p-2 rounded-lg transition-all flex-shrink-0 ${ 664 + className={`p-1.5 rounded-md transition-colors ${ 639 665 bookmarked 640 - ? 'bg-[var(--success)]/15 text-[var(--success)]' 641 - : 'bg-[var(--bg-hover)] hover:bg-[var(--accent-subtle)] text-[var(--text-secondary)] hover:text-[var(--accent)]' 666 + ? 'text-emerald-400' 667 + : 'text-[var(--text-tertiary)] hover:text-[var(--accent)] hover:bg-[var(--bg-hover)]' 642 668 }`} 643 669 title={bookmarked ? 'Bookmarked' : 'Bookmark page'} 644 670 > 645 - {bookmarked ? <Check size={16} /> : <BookmarkIcon size={16} />} 671 + {bookmarked ? <Check size={15} /> : <BookmarkIcon size={15} />} 646 672 </button> 647 673 </div> 648 674 </div> 649 675 </div> 650 676 651 677 <div className="p-4 border-b border-[var(--border)]"> 652 - <div> 653 - <textarea 654 - value={text} 655 - onChange={(e) => setText(e.target.value)} 656 - placeholder="Share your thoughts on this page..." 657 - className="w-full p-3 bg-[var(--bg-card)] border border-[var(--border)] rounded-xl text-sm resize-none focus:outline-none focus:border-[var(--accent)] focus:ring-2 focus:ring-[var(--accent-subtle)] min-h-[90px]" 658 - /> 659 - <div className="mt-2"> 660 - <TagInput tags={tags} onChange={setTags} suggestions={tagSuggestions} /> 661 - </div> 662 - <div className="flex justify-end mt-2"> 663 - <button 664 - onClick={handlePost} 665 - disabled={posting || !text.trim()} 666 - className="px-4 py-1.5 bg-[var(--accent)] text-white text-xs rounded-lg font-semibold hover:bg-[var(--accent-hover)] disabled:opacity-40 disabled:cursor-not-allowed transition-all hover:-translate-y-0.5 active:translate-y-0" 667 - > 668 - {posting ? 'Posting...' : 'Post'} 669 - </button> 670 - </div> 678 + <textarea 679 + value={text} 680 + onChange={(e) => setText(e.target.value)} 681 + placeholder="Share your thoughts on this page..." 682 + className="w-full px-3 py-2.5 bg-[var(--bg-card)] border border-[var(--border)] rounded-xl text-sm resize-none focus:outline-none focus:border-[var(--accent)] focus:ring-2 focus:ring-[var(--accent-subtle)] min-h-[80px] transition-all" 683 + /> 684 + <div className="mt-2"> 685 + <TagInput tags={tags} onChange={setTags} suggestions={tagSuggestions} /> 686 + </div> 687 + <div className="flex items-center justify-between mt-2"> 688 + <span className="text-[11px] text-[var(--text-tertiary)]"> 689 + {text.length > 0 ? `${text.length} chars` : ''} 690 + </span> 691 + <button 692 + onClick={handlePost} 693 + disabled={posting || !text.trim()} 694 + className="px-4 py-1.5 bg-[var(--accent)] text-white text-xs rounded-lg font-medium hover:bg-[var(--accent-hover)] disabled:opacity-30 disabled:cursor-not-allowed transition-colors" 695 + > 696 + {posting ? 'Posting...' : 'Post'} 697 + </button> 671 698 </div> 672 699 </div> 673 700 ··· 719 746 </div> 720 747 ) : annotations.length + pageHighlights.length === 0 ? ( 721 748 <div className="flex flex-col items-center justify-center py-12 text-[var(--text-tertiary)]"> 722 - <div className="w-14 h-14 rounded-2xl bg-[var(--bg-card)] border border-[var(--border)] flex items-center justify-center mb-4"> 723 - <Sparkles size={24} className="opacity-40" /> 749 + <div className="w-12 h-12 rounded-xl bg-[var(--accent-subtle)] flex items-center justify-center mb-4"> 750 + <Sparkles size={22} className="text-[var(--accent)] opacity-60" /> 724 751 </div> 725 752 <p className="text-sm font-medium mb-1">No activity yet</p> 726 753 <p className="text-xs text-[var(--text-tertiary)]"> ··· 774 801 </div> 775 802 ) : bookmarks.length === 0 ? ( 776 803 <div className="flex flex-col items-center justify-center py-16 text-[var(--text-tertiary)]"> 777 - <div className="w-14 h-14 rounded-2xl bg-[var(--bg-card)] border border-[var(--border)] flex items-center justify-center mb-4"> 778 - <BookmarkIcon size={24} className="opacity-40" /> 804 + <div className="w-12 h-12 rounded-xl bg-[var(--accent-subtle)] flex items-center justify-center mb-4"> 805 + <BookmarkIcon size={22} className="text-[var(--accent)] opacity-60" /> 779 806 </div> 780 807 <p className="text-sm font-medium mb-1">No bookmarks yet</p> 781 808 <p className="text-xs text-[var(--text-tertiary)]">Save pages to read later</p> ··· 825 852 </div> 826 853 ) : highlights.length === 0 ? ( 827 854 <div className="flex flex-col items-center justify-center py-16 text-[var(--text-tertiary)]"> 828 - <div className="w-14 h-14 rounded-2xl bg-[var(--bg-card)] border border-[var(--border)] flex items-center justify-center mb-4"> 829 - <Highlighter size={24} className="opacity-40" /> 855 + <div className="w-12 h-12 rounded-xl bg-[var(--accent-subtle)] flex items-center justify-center mb-4"> 856 + <Highlighter size={22} className="text-[var(--accent)] opacity-60" /> 830 857 </div> 831 858 <p className="text-sm font-medium mb-1">No highlights yet</p> 832 859 <p className="text-xs text-[var(--text-tertiary)]"> ··· 856 883 </div> 857 884 ) : collections.length === 0 ? ( 858 885 <div className="flex flex-col items-center justify-center py-16 text-[var(--text-tertiary)]"> 859 - <div className="w-14 h-14 rounded-2xl bg-[var(--bg-card)] border border-[var(--border)] flex items-center justify-center mb-4"> 860 - <Folder size={24} className="opacity-40" /> 886 + <div className="w-12 h-12 rounded-xl bg-[var(--accent-subtle)] flex items-center justify-center mb-4"> 887 + <Folder size={22} className="text-[var(--accent)] opacity-60" /> 861 888 </div> 862 889 <p className="text-sm font-medium mb-1">No collections yet</p> 863 890 <p className="text-xs text-[var(--text-tertiary)]"> ··· 976 1003 </div> 977 1004 )} 978 1005 979 - <footer className="flex items-center justify-between px-4 py-2.5 border-t border-[var(--border)] bg-[var(--bg-secondary)]"> 1006 + <footer className="flex items-center justify-center px-4 py-2 border-t border-[var(--border)]"> 980 1007 <button 981 1008 onClick={() => browser.tabs.create({ url: apiUrl })} 982 - className="text-xs text-[var(--text-tertiary)] hover:text-[var(--accent)] flex items-center gap-1.5 py-1.5 px-2.5 rounded-lg hover:bg-[var(--accent-subtle)] transition-all" 1009 + className="text-xs text-[var(--text-tertiary)] hover:text-[var(--accent)] flex items-center gap-1.5 py-1.5 px-3 rounded-lg hover:bg-[var(--bg-hover)] transition-colors" 983 1010 > 984 - Open Margin <ExternalLink size={12} /> 985 - </button> 986 - <button 987 - onClick={() => setShowSettings(true)} 988 - className="p-2 text-[var(--text-tertiary)] hover:text-[var(--accent)] hover:bg-[var(--accent-subtle)] rounded-lg transition-all" 989 - title="Settings" 990 - > 991 - <Settings size={16} /> 1011 + Open Margin <ExternalLink size={11} /> 992 1012 </button> 993 1013 </footer> 994 1014 </div>
+65 -64
extension/src/components/sidepanel/App.tsx
··· 403 403 if (loading) { 404 404 return ( 405 405 <div className="flex items-center justify-center h-screen"> 406 - <div className="animate-spin rounded-full h-8 w-8 border-2 border-[var(--accent)] border-t-transparent" /> 406 + <div className="w-5 h-5 border-2 border-[var(--accent)] border-t-transparent rounded-full animate-spin" /> 407 407 </div> 408 408 ); 409 409 } ··· 411 411 if (!session?.authenticated) { 412 412 return ( 413 413 <div className="flex flex-col items-center justify-center h-screen p-8 text-center"> 414 - <img src="/icons/logo.svg" alt="Margin" className="w-16 h-16 mb-6" /> 415 - <h2 className="text-xl font-bold mb-3">Welcome to Margin</h2> 416 - <p className="text-[var(--text-secondary)] mb-8 max-w-xs leading-relaxed"> 417 - Sign in to annotate, bookmark, and highlight web pages using the AT Protocol. 414 + <div className="w-14 h-14 rounded-2xl bg-[var(--accent-subtle)] flex items-center justify-center mb-5"> 415 + <img src="/icons/logo.svg" alt="Margin" className="w-8 h-8" /> 416 + </div> 417 + <h2 className="font-display text-xl font-bold tracking-tight mb-2">Welcome to Margin</h2> 418 + <p className="text-[var(--text-secondary)] text-sm mb-8 max-w-xs leading-relaxed"> 419 + Annotate, highlight, and bookmark the web with your AT Protocol identity. 418 420 </p> 419 421 <button 420 422 onClick={() => browser.tabs.create({ url: `${APP_URL}/login` })} 421 - className="px-8 py-3 bg-[var(--accent)] text-white rounded-xl font-semibold hover:bg-[var(--accent-hover)] transition-all hover:-translate-y-0.5 active:translate-y-0" 423 + className="px-8 py-2.5 bg-[var(--accent)] text-white rounded-xl text-sm font-semibold hover:bg-[var(--accent-hover)] transition-colors" 422 424 > 423 425 Sign In 424 426 </button> ··· 428 430 429 431 return ( 430 432 <div className="flex flex-col h-screen"> 431 - <header className="flex items-center justify-between px-3 py-2.5 border-b border-[var(--border)] bg-[var(--bg-secondary)] gap-2"> 432 - <div className="flex items-center gap-2 flex-shrink-0"> 433 + <header className="flex items-center justify-between px-4 py-2.5 border-b border-[var(--border)]"> 434 + <div className="flex items-center gap-2"> 433 435 <img src="/icons/logo.svg" alt="Margin" className="w-5 h-5" /> 434 - <span className="font-bold text-sm hidden sm:inline">Margin</span> 436 + <span className="font-display font-bold text-sm tracking-tight">Margin</span> 435 437 </div> 436 - <div className="flex items-center gap-2 min-w-0"> 437 - <div className="text-xs text-[var(--text-secondary)] bg-[var(--bg-card)] px-2 py-1 rounded-full border border-[var(--border)] truncate max-w-[120px]"> 438 + <div className="flex items-center gap-1.5"> 439 + <button 440 + onClick={() => browser.tabs.create({ url: APP_URL })} 441 + className="text-[11px] text-[var(--text-tertiary)] hover:text-[var(--accent)] px-2 py-1 rounded-md hover:bg-[var(--bg-hover)] transition-colors" 442 + > 438 443 @{session.handle} 439 - </div> 444 + </button> 440 445 <button 441 446 onClick={() => setShowSettings(!showSettings)} 442 - className="p-1.5 hover:bg-[var(--bg-hover)] rounded-lg transition-colors text-[var(--text-tertiary)] hover:text-[var(--text-primary)] flex-shrink-0" 447 + className="p-1.5 text-[var(--text-tertiary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-hover)] rounded-lg transition-colors" 443 448 title="Settings" 444 449 > 445 450 {Icons.settings} ··· 448 453 </header> 449 454 450 455 {showSettings && ( 451 - <div className="p-4 border-b border-[var(--border)] bg-[var(--bg-card)] animate-slideDown"> 452 - <h3 className="text-sm font-bold mb-4 flex items-center gap-2"> 453 - {Icons.settings} 454 - Settings 455 - </h3> 456 - 457 - <div className="mb-5"> 458 - <label className="text-xs font-medium text-[var(--text-secondary)] mb-2 block"> 456 + <div className="p-4 border-b border-[var(--border)] space-y-4 animate-slideDown"> 457 + <div> 458 + <label className="block text-xs font-medium text-[var(--text-secondary)] mb-1.5"> 459 459 Theme 460 460 </label> 461 - <div className="flex gap-2"> 462 - {(['system', 'light', 'dark'] as const).map((t) => ( 461 + <div className="flex gap-1.5"> 462 + {(['light', 'dark', 'system'] as const).map((t) => ( 463 463 <button 464 464 key={t} 465 465 onClick={() => handleThemeChange(t)} 466 - className={`flex-1 px-3 py-2 text-xs font-medium rounded-lg transition-all ${ 466 + className={`flex-1 py-2 px-3 text-xs font-medium rounded-lg transition-colors ${ 467 467 theme === t 468 468 ? 'bg-[var(--accent)] text-white' 469 - : 'bg-[var(--bg-elevated)] border border-[var(--border)] hover:bg-[var(--bg-hover)]' 469 + : 'bg-[var(--bg-card)] border border-[var(--border)] hover:bg-[var(--bg-hover)]' 470 470 }`} 471 471 > 472 472 {t.charAt(0).toUpperCase() + t.slice(1)} ··· 475 475 </div> 476 476 </div> 477 477 478 - <div className="flex items-center justify-between py-2"> 478 + <div className="flex items-center justify-between p-3 bg-[var(--bg-card)] border border-[var(--border)] rounded-lg"> 479 479 <div> 480 - <span className="text-sm font-medium">Page Overlay</span> 481 - <p className="text-xs text-[var(--text-tertiary)]">Show highlights on pages</p> 480 + <div className="text-sm font-medium">Page Overlay</div> 481 + <p className="text-[11px] text-[var(--text-tertiary)] mt-0.5"> 482 + Show highlights and annotations on pages 483 + </p> 482 484 </div> 483 485 <button 484 486 onClick={handleOverlayToggle} 485 - className={`w-11 h-6 rounded-full transition-colors relative ${ 487 + className={`relative w-10 h-[22px] rounded-full transition-colors ${ 486 488 overlayEnabled 487 489 ? 'bg-[var(--accent)]' 488 490 : 'bg-[var(--bg-hover)] border border-[var(--border)]' 489 491 }`} 490 492 > 491 493 <div 492 - className={`absolute top-1 w-4 h-4 rounded-full bg-white shadow transition-transform ${ 493 - overlayEnabled ? 'left-6' : 'left-1' 494 + className={`absolute top-[3px] w-4 h-4 rounded-full bg-white shadow-sm transition-transform ${ 495 + overlayEnabled ? 'left-[22px]' : 'left-[3px]' 494 496 }`} 495 497 /> 496 498 </button> 497 499 </div> 498 500 499 - <div className="mt-4 pt-4 border-t border-[var(--border)]"> 500 - <button 501 - onClick={() => browser.tabs.create({ url: APP_URL })} 502 - className="w-full py-2.5 text-sm font-medium text-[var(--accent)] bg-[var(--accent)]/10 rounded-lg hover:bg-[var(--accent)]/20 transition-colors flex items-center justify-center gap-2" 503 - > 504 - Open Margin App {Icons.externalLink} 505 - </button> 506 - </div> 501 + <button 502 + onClick={() => browser.tabs.create({ url: APP_URL })} 503 + className="w-full py-2 text-xs font-medium text-[var(--text-tertiary)] hover:text-[var(--accent)] rounded-lg hover:bg-[var(--bg-hover)] transition-colors flex items-center justify-center gap-1.5" 504 + > 505 + Open Margin {Icons.externalLink} 506 + </button> 507 507 </div> 508 508 )} 509 509 510 - <div className="flex border-b border-[var(--border)] bg-[var(--bg-secondary)]"> 510 + <div className="flex border-b border-[var(--border)]"> 511 511 {(['page', 'bookmarks', 'highlights', 'collections'] as Tab[]).map((tab) => { 512 512 const icons = { 513 513 page: Icons.globe, ··· 541 541 <div className="flex-1 overflow-y-auto"> 542 542 {activeTab === 'page' && ( 543 543 <div className="p-4"> 544 - <div className="mb-4 p-4 bg-[var(--bg-card)] border border-[var(--border)] rounded-xl"> 545 - <div className="flex items-start gap-3"> 546 - <div className="w-10 h-10 rounded-lg bg-[var(--bg-hover)] flex items-center justify-center flex-shrink-0 overflow-hidden"> 544 + <div className="mb-4 p-3 bg-[var(--bg-card)] border border-[var(--border)] rounded-xl"> 545 + <div className="flex items-center gap-3"> 546 + <div className="w-9 h-9 rounded-lg bg-[var(--bg-hover)] flex items-center justify-center flex-shrink-0 overflow-hidden"> 547 547 {currentUrl ? ( 548 548 <img 549 549 src={`https://www.google.com/s2/favicons?domain=${new URL(currentUrl).hostname}&sz=64`} 550 550 alt="" 551 - className="w-6 h-6" 551 + className="w-5 h-5" 552 552 onError={(e) => { 553 553 (e.target as HTMLImageElement).style.display = 'none'; 554 554 (e.target as HTMLImageElement).nextElementSibling?.classList.remove( ··· 562 562 </span> 563 563 </div> 564 564 <div className="flex-1 min-w-0"> 565 - <div className="text-sm font-semibold truncate">{currentTitle || 'Untitled'}</div> 566 - <div className="text-xs text-[var(--text-tertiary)] truncate"> 565 + <div className="text-sm font-medium truncate">{currentTitle || 'Untitled'}</div> 566 + <div className="text-[11px] text-[var(--text-tertiary)] truncate"> 567 567 {currentUrl ? new URL(currentUrl).hostname : ''} 568 568 </div> 569 569 </div> 570 570 <button 571 571 onClick={handleBookmark} 572 572 disabled={bookmarking || bookmarked} 573 - className={`p-2 rounded-lg transition-all ${ 573 + className={`p-1.5 rounded-md transition-colors ${ 574 574 bookmarked 575 - ? 'bg-emerald-400/15 text-emerald-400' 576 - : 'bg-[var(--bg-hover)] hover:bg-[var(--accent)]/15 text-[var(--text-secondary)] hover:text-[var(--accent)]' 575 + ? 'text-emerald-400' 576 + : 'text-[var(--text-tertiary)] hover:text-[var(--accent)] hover:bg-[var(--bg-hover)]' 577 577 }`} 578 + title={bookmarked ? 'Bookmarked' : 'Bookmark page'} 578 579 > 579 580 {bookmarked ? Icons.check : Icons.bookmark} 580 581 </button> ··· 586 587 value={text} 587 588 onChange={(e) => setText(e.target.value)} 588 589 placeholder="Share your thoughts on this page..." 589 - className="w-full p-4 bg-[var(--bg-card)] border border-[var(--border)] rounded-xl text-sm resize-none focus:outline-none focus:border-[var(--accent)] focus:ring-2 focus:ring-[var(--accent)]/20 min-h-[100px]" 590 + className="w-full px-3 py-2.5 bg-[var(--bg-card)] border border-[var(--border)] rounded-xl text-sm resize-none focus:outline-none focus:border-[var(--accent)] focus:ring-2 focus:ring-[var(--accent-subtle)] min-h-[80px] transition-all" 590 591 /> 591 592 <div className="mt-2"> 592 593 <TagInput tags={tags} onChange={setTags} suggestions={tagSuggestions} /> 593 594 </div> 594 - <div className="flex gap-2 mt-3"> 595 + <div className="flex items-center justify-between mt-2"> 596 + <span className="text-[11px] text-[var(--text-tertiary)]"> 597 + {text.length > 0 ? `${text.length} chars` : ''} 598 + </span> 595 599 <button 596 600 onClick={handlePost} 597 601 disabled={posting || !text.trim()} 598 - className="flex-1 px-4 py-2.5 bg-[var(--accent)] text-white text-sm rounded-xl font-semibold hover:bg-[var(--accent-hover)] disabled:opacity-40 disabled:cursor-not-allowed transition-all hover:-translate-y-0.5 active:translate-y-0" 602 + className="px-5 py-1.5 bg-[var(--accent)] text-white text-xs rounded-lg font-medium hover:bg-[var(--accent-hover)] disabled:opacity-30 disabled:cursor-not-allowed transition-colors" 599 603 > 600 - {posting ? 'Posting...' : 'Post Annotation'} 604 + {posting ? 'Posting...' : 'Post'} 601 605 </button> 602 606 </div> 603 607 </div> 604 608 605 - <div className="flex items-center justify-between text-xs text-[var(--text-tertiary)] mb-3"> 609 + <div className="flex items-center justify-between mb-3"> 606 610 <div className="flex items-center gap-2"> 607 - <span className="font-semibold"> 608 - {annotations.length + pageHighlights.length} item 609 - {annotations.length + pageHighlights.length !== 1 ? 's' : ''} 610 - </span> 611 - <span className="text-[var(--accent)] bg-[var(--accent)]/10 px-2 py-0.5 rounded-full text-[10px] font-semibold"> 612 - {annotations.length} notes · {pageHighlights.length} highlights 611 + <span className="text-xs font-semibold text-[var(--text-secondary)]">Activity</span> 612 + <span className="text-xs font-semibold bg-[var(--accent-subtle)] text-[var(--accent)] px-2 py-0.5 rounded-full"> 613 + {annotations.length + pageHighlights.length} 613 614 </span> 614 615 </div> 615 616 <div className="flex items-center bg-[var(--bg-card)] border border-[var(--border)] rounded-lg p-0.5"> ··· 635 636 </div> 636 637 ) : annotations.length + pageHighlights.length === 0 ? ( 637 638 <div className="text-center py-16 text-[var(--text-tertiary)]"> 638 - <div className="w-14 h-14 rounded-2xl bg-[var(--bg-card)] border border-[var(--border)] flex items-center justify-center mx-auto mb-4"> 639 + <div className="w-12 h-12 rounded-xl bg-[var(--accent-subtle)] flex items-center justify-center mx-auto mb-4"> 639 640 {Icons.sparkles} 640 641 </div> 641 642 <p className="font-medium mb-1">No activity yet</p> ··· 686 687 </div> 687 688 ) : bookmarks.length === 0 ? ( 688 689 <div className="text-center py-16 text-[var(--text-tertiary)]"> 689 - <div className="w-14 h-14 rounded-2xl bg-[var(--bg-card)] border border-[var(--border)] flex items-center justify-center mx-auto mb-4"> 690 + <div className="w-12 h-12 rounded-xl bg-[var(--accent-subtle)] flex items-center justify-center mx-auto mb-4 text-[var(--accent)]"> 690 691 {Icons.bookmark} 691 692 </div> 692 693 <p className="font-medium mb-1">No bookmarks yet</p> ··· 748 749 </div> 749 750 ) : highlights.length === 0 ? ( 750 751 <div className="text-center py-16 text-[var(--text-tertiary)]"> 751 - <div className="w-14 h-14 rounded-2xl bg-[var(--bg-card)] border border-[var(--border)] flex items-center justify-center mx-auto mb-4"> 752 + <div className="w-12 h-12 rounded-xl bg-[var(--accent-subtle)] flex items-center justify-center mx-auto mb-4 text-[var(--accent)]"> 752 753 {Icons.highlighter} 753 754 </div> 754 755 <p className="font-medium mb-1">No highlights yet</p> ··· 777 778 </div> 778 779 ) : collections.length === 0 ? ( 779 780 <div className="text-center py-16 text-[var(--text-tertiary)]"> 780 - <div className="w-14 h-14 rounded-2xl bg-[var(--bg-card)] border border-[var(--border)] flex items-center justify-center mx-auto mb-4"> 781 + <div className="w-12 h-12 rounded-xl bg-[var(--accent-subtle)] flex items-center justify-center mx-auto mb-4 text-[var(--accent)]"> 781 782 {Icons.folder} 782 783 </div> 783 784 <p className="font-medium mb-1">No collections yet</p>
+40 -34
extension/src/utils/overlay-styles.ts
··· 1 1 export const overlayStyles = /* css */ ` 2 2 :host { 3 3 all: initial; 4 - --bg-primary: #0c0c0c; 5 - --bg-secondary: #141414; 6 - --bg-tertiary: #1c1c1c; 7 - --bg-card: #161616; 8 - --bg-elevated: #1e1e1e; 9 - --bg-hover: #262626; 4 + --bg-primary: #18181b; 5 + --bg-secondary: #1e1e22; 6 + --bg-tertiary: #27272a; 7 + --bg-card: #1e1e22; 8 + --bg-elevated: #27272a; 9 + --bg-hover: #2e2e33; 10 10 11 - --text-primary: #e8e8e3; 12 - --text-secondary: #a1a09a; 13 - --text-tertiary: #6b6a65; 11 + --text-primary: #fafafa; 12 + --text-secondary: #a1a1aa; 13 + --text-tertiary: #71717a; 14 14 --border: rgba(255, 255, 255, 0.08); 15 + --border-strong: rgba(255, 255, 255, 0.14); 15 16 16 - --accent: #7aa2f7; 17 - --accent-hover: #9bbcff; 18 - --accent-subtle: rgba(122, 162, 247, 0.14); 17 + --accent: #3b82f6; 18 + --accent-hover: #2563eb; 19 + --accent-subtle: rgba(59, 130, 246, 0.12); 19 20 20 21 --highlight-yellow: #fbbf24; 21 22 --highlight-green: #34d399; 22 23 --highlight-blue: #60a5fa; 23 24 --highlight-pink: #f472b6; 24 - --highlight-purple: #9bbcff; 25 + --highlight-purple: #a78bfa; 25 26 } 26 27 27 28 :host(.light) { 28 - --bg-primary: #fafaf8; 29 + --bg-primary: #fafafa; 29 30 --bg-secondary: #ffffff; 30 - --bg-tertiary: #f2f2ef; 31 + --bg-tertiary: #f4f4f5; 31 32 --bg-card: #ffffff; 32 33 --bg-elevated: #ffffff; 33 - --bg-hover: #eaeae6; 34 + --bg-hover: #f4f4f5; 34 35 35 - --text-primary: #1a1a18; 36 - --text-secondary: #6b6a65; 37 - --text-tertiary: #a1a09a; 36 + --text-primary: #18181b; 37 + --text-secondary: #71717a; 38 + --text-tertiary: #a1a1aa; 38 39 --border: rgba(0, 0, 0, 0.08); 40 + --border-strong: rgba(0, 0, 0, 0.14); 39 41 40 - --accent: #3b82f6; 41 - --accent-hover: #2563eb; 42 - --accent-subtle: rgba(59, 130, 246, 0.08); 42 + --accent: #2563eb; 43 + --accent-hover: #1d4ed8; 44 + --accent-subtle: rgba(37, 99, 235, 0.08); 43 45 } 44 46 45 47 .margin-overlay { ··· 85 87 position: absolute; 86 88 width: 320px; 87 89 background: var(--bg-card); 88 - border: 1px solid var(--border); 89 - border-radius: 14px; 90 + border: 1px solid var(--border-strong); 91 + border-radius: 16px; 90 92 padding: 0; 91 - box-shadow: 0 8px 40px rgba(0, 0, 0, 0.35), 0 0 0 1px rgba(255,255,255,0.05); 93 + box-shadow: 0 8px 40px rgba(0, 0, 0, 0.35), 0 0 80px rgba(59, 130, 246, 0.04); 92 94 display: flex; 93 95 flex-direction: column; 94 96 pointer-events: auto; ··· 107 109 } 108 110 109 111 .popover-header { 110 - padding: 12px 16px; 112 + padding: 14px 16px; 111 113 border-bottom: 1px solid var(--border); 112 114 display: flex; 113 115 justify-content: space-between; 114 116 align-items: center; 115 117 background: var(--bg-primary); 116 - border-radius: 14px 14px 0 0; 118 + border-radius: 16px 16px 0 0; 117 119 } 118 120 119 121 .popover-title { ··· 204 206 } 205 207 206 208 .comment-avatar { 207 - width: 28px; 208 - height: 28px; 209 + width: 30px; 210 + height: 30px; 209 211 border-radius: 50%; 210 212 background: linear-gradient(135deg, var(--accent), var(--accent-hover)); 211 213 display: flex; ··· 215 217 font-weight: 600; 216 218 color: white; 217 219 flex-shrink: 0; 220 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15); 218 221 } 219 222 220 223 .comment-meta { ··· 324 327 } 325 328 326 329 .add-note-textarea:focus { 327 - border-color: var(--primary); 330 + border-color: var(--accent); 331 + box-shadow: 0 0 0 2px var(--accent-subtle); 328 332 } 329 333 330 334 .add-note-textarea::placeholder { ··· 378 382 width: 380px; 379 383 max-width: calc(100vw - 32px); 380 384 background: var(--bg-card); 381 - border: 1px solid var(--border); 385 + border: 1px solid var(--border-strong); 382 386 border-radius: 16px; 383 387 padding: 0; 384 388 box-sizing: border-box; 385 - box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255,255,255,0.05); 389 + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4), 0 0 80px rgba(59, 130, 246, 0.04); 386 390 z-index: 2147483647; 387 391 pointer-events: auto; 388 392 font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, sans-serif; ··· 473 477 .inline-compose-textarea:focus { 474 478 outline: none; 475 479 border-color: var(--accent); 480 + box-shadow: 0 0 0 2px var(--accent-subtle); 476 481 } 477 482 478 483 .compose-footer { ··· 550 555 551 556 .compose-tags-container:focus-within { 552 557 border-color: var(--accent); 558 + box-shadow: 0 0 0 2px var(--accent-subtle); 553 559 } 554 560 555 561 .compose-tag-pill { ··· 660 666 transform: translateX(-50%) translateY(20px); 661 667 padding: 12px 20px; 662 668 background: var(--bg-card); 663 - border: 1px solid var(--border); 664 - border-radius: 10px; 669 + border: 1px solid var(--border-strong); 670 + border-radius: 12px; 665 671 box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3); 666 672 font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, sans-serif; 667 673 font-size: 13px;
+11 -11
extension/tailwind.config.js
··· 22 22 950: '#172554', 23 23 }, 24 24 surface: { 25 - 50: '#fafaf8', 26 - 100: '#f2f2ef', 27 - 200: '#eaeae6', 28 - 300: '#d2d2cc', 29 - 400: '#a1a09a', 30 - 500: '#6b6a65', 31 - 600: '#4a4a46', 32 - 700: '#333331', 33 - 800: '#1e1e1e', 34 - 900: '#141414', 35 - 950: '#0c0c0c', 25 + 50: '#fafafa', 26 + 100: '#f4f4f5', 27 + 200: '#e4e4e7', 28 + 300: '#d4d4d8', 29 + 400: '#a1a1aa', 30 + 500: '#71717a', 31 + 600: '#52525b', 32 + 700: '#3f3f46', 33 + 800: '#27272a', 34 + 900: '#18181b', 35 + 950: '#09090b', 36 36 }, 37 37 }, 38 38 animation: {
+13 -2
web/src/App.tsx
··· 1 1 import React from "react"; 2 2 import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"; 3 - import { initAuth } from "./store/auth"; 3 + import { initAuth, $user } from "./store/auth"; 4 4 import { loadPreferences } from "./store/preferences"; 5 + import { useStore } from "@nanostores/react"; 5 6 6 7 import AppLayout from "./layouts/AppLayout"; 7 8 import Feed from "./views/core/Feed"; ··· 22 23 import About from "./views/About"; 23 24 import AdminModeration from "./views/core/AdminModeration"; 24 25 26 + function RootRoute() { 27 + const user = useStore($user); 28 + 29 + if (user) { 30 + return <Navigate to="/home" replace />; 31 + } 32 + 33 + return <About />; 34 + } 35 + 25 36 export default function App() { 26 37 React.useEffect(() => { 27 38 initAuth(); ··· 31 42 return ( 32 43 <BrowserRouter> 33 44 <Routes> 34 - <Route path="/" element={<Navigate to="/home" replace />} /> 45 + <Route path="/" element={<RootRoute />} /> 35 46 <Route path="/login" element={<Login />} /> 36 47 <Route path="/about" element={<About />} /> 37 48 <Route path="/auth/*" element={<div>Redirecting...</div>} />
+13 -3
web/src/components/common/Card.tsx
··· 302 302 })() 303 303 : null; 304 304 305 - const displayTitle = 306 - item.title || ogData?.title || pageTitle || "Untitled Bookmark"; 307 - const displayDescription = item.description || ogData?.description; 305 + const decodeHTMLEntities = (text: string) => { 306 + const textarea = document.createElement("textarea"); 307 + textarea.innerHTML = text; 308 + return textarea.value; 309 + }; 310 + 311 + const displayTitle = decodeHTMLEntities( 312 + item.title || ogData?.title || pageTitle || "Untitled Bookmark", 313 + ); 314 + const displayDescription = 315 + item.description || ogData?.description 316 + ? decodeHTMLEntities(item.description || ogData?.description || "") 317 + : undefined; 308 318 const displayImage = ogData?.image; 309 319 310 320 return (
+24
web/src/components/navigation/RightSidebar.tsx
··· 295 295 > 296 296 Tangled 297 297 </a> 298 + <a 299 + href="https://discord.gg/ZQbkGqwzBH" 300 + target="_blank" 301 + rel="noreferrer" 302 + className="hover:underline hover:text-surface-600 dark:hover:text-surface-300" 303 + > 304 + Discord 305 + </a> 306 + <a 307 + href="https://matrix.to/#/#margin:blep.cat" 308 + target="_blank" 309 + rel="noreferrer" 310 + className="hover:underline hover:text-surface-600 dark:hover:text-surface-300" 311 + > 312 + Matrix 313 + </a> 314 + <a 315 + href="https://stt.gg/wHnM6e3h" 316 + target="_blank" 317 + rel="noreferrer" 318 + className="hover:underline hover:text-surface-600 dark:hover:text-surface-300" 319 + > 320 + Stoat 321 + </a> 298 322 <span>© 2026 Margin</span> 299 323 </div> 300 324 </div>
+134 -83
web/src/views/About.tsx
··· 2 2 import { useStore } from "@nanostores/react"; 3 3 import { Link } from "react-router-dom"; 4 4 import { $theme } from "../store/theme"; 5 + import { $user } from "../store/auth"; 5 6 import { 6 7 MessageSquareText, 7 8 Highlighter, ··· 12 13 MousePointerClick, 13 14 Shield, 14 15 Users, 15 - Sparkles, 16 16 Chrome, 17 17 ArrowRight, 18 18 Github, ··· 37 37 }) { 38 38 return ( 39 39 <div 40 - className={`group p-6 rounded-2xl border transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md ${ 40 + className={`group p-6 rounded-2xl border transition-all duration-200 hover:-translate-y-0.5 hover:shadow-sm ${ 41 41 accent 42 42 ? "bg-primary-50 dark:bg-primary-950/30 border-primary-200/50 dark:border-primary-800/40" 43 - : "bg-white dark:bg-surface-900 border-surface-200/80 dark:border-surface-800" 43 + : "bg-white dark:bg-surface-800 border-surface-200/60 dark:border-surface-700/60" 44 44 }`} 45 45 > 46 46 <div 47 47 className={`w-11 h-11 rounded-xl flex items-center justify-center mb-4 transition-colors ${ 48 48 accent 49 49 ? "bg-primary-600 text-white" 50 - : "bg-primary-100 dark:bg-primary-900/30 text-primary-600 dark:text-primary-400 group-hover:bg-primary-600 group-hover:text-white dark:group-hover:bg-primary-500" 50 + : "bg-surface-100 dark:bg-surface-700/50 text-surface-500 dark:text-surface-400 group-hover:bg-primary-600 group-hover:text-white dark:group-hover:bg-primary-500" 51 51 }`} 52 52 > 53 53 <Icon size={20} /> ··· 90 90 91 91 export default function About() { 92 92 useStore($theme); // ensure theme is applied on this page 93 + const user = useStore($user); 93 94 94 95 const [browser] = React.useState< 95 96 "chrome" | "firefox" | "edge" | "safari" | "other" ··· 116 117 browser === "firefox" ? "Firefox" : browser === "edge" ? "Edge" : "Chrome"; 117 118 118 119 return ( 119 - <div className="min-h-screen bg-surface-50 dark:bg-surface-950"> 120 - <nav className="sticky top-0 z-50 bg-surface-50/80 dark:bg-surface-950/80 backdrop-blur-xl border-b border-surface-200/60 dark:border-surface-800/60"> 120 + <div className="min-h-screen bg-surface-100 dark:bg-surface-900"> 121 + <nav className="sticky top-0 z-50 bg-white/80 dark:bg-surface-900/80 backdrop-blur-xl border-b border-surface-200/50 dark:border-surface-800/50"> 121 122 <div className="max-w-5xl mx-auto px-6 h-14 flex items-center justify-between"> 122 - <Link to="/home" className="flex items-center gap-2.5 group"> 123 + <div className="flex items-center gap-2.5"> 123 124 <img src="/logo.svg" alt="Margin" className="w-7 h-7" /> 124 125 <span className="font-display font-bold text-lg tracking-tight text-surface-900 dark:text-white"> 125 126 Margin 126 127 </span> 127 - </Link> 128 - <div className="flex items-center gap-3"> 129 - <Link 130 - to="/home" 131 - className="text-sm text-surface-500 dark:text-surface-400 hover:text-surface-900 dark:hover:text-white transition-colors px-3 py-1.5" 128 + </div> 129 + <div className="flex items-center gap-1"> 130 + {user && ( 131 + <Link 132 + to="/home" 133 + className="text-[13px] font-medium text-surface-500 dark:text-surface-400 hover:text-surface-900 dark:hover:text-white transition-colors px-3 py-1.5 rounded-lg hover:bg-surface-100 dark:hover:bg-surface-800" 134 + > 135 + Feed 136 + </Link> 137 + )} 138 + <a 139 + href="https://github.com/margin-at" 140 + target="_blank" 141 + rel="noreferrer" 142 + className="text-[13px] font-medium text-surface-500 dark:text-surface-400 hover:text-surface-900 dark:hover:text-white transition-colors px-3 py-1.5 rounded-lg hover:bg-surface-100 dark:hover:bg-surface-800" 143 + > 144 + GitHub 145 + </a> 146 + <a 147 + href="https://tangled.org/margin.at/margin" 148 + target="_blank" 149 + rel="noreferrer" 150 + className="text-[13px] font-medium text-surface-500 dark:text-surface-400 hover:text-surface-900 dark:hover:text-white transition-colors px-3 py-1.5 rounded-lg hover:bg-surface-100 dark:hover:bg-surface-800" 132 151 > 133 - Feed 134 - </Link> 152 + Tangled 153 + </a> 154 + {!user && ( 155 + <> 156 + <div className="w-px h-5 bg-surface-200 dark:bg-surface-700 mx-1.5" /> 157 + <Link 158 + to="/login" 159 + className="text-[13px] font-medium text-surface-600 dark:text-surface-300 hover:text-surface-900 dark:hover:text-white transition-colors px-3 py-1.5 rounded-lg hover:bg-surface-100 dark:hover:bg-surface-800" 160 + > 161 + Sign in 162 + </Link> 163 + </> 164 + )} 135 165 <a 136 166 href={extensionLink} 137 167 target="_blank" 138 168 rel="noopener noreferrer" 139 - className="text-sm font-medium px-4 py-2 bg-primary-600 hover:bg-primary-700 dark:bg-primary-500 dark:hover:bg-primary-400 text-white rounded-lg transition-colors" 169 + className="text-[13px] font-semibold px-4 py-1.5 bg-surface-900 dark:bg-white text-white dark:text-surface-900 rounded-lg hover:bg-surface-800 dark:hover:bg-surface-100 transition-colors ml-0.5" 140 170 > 141 171 Get Extension 142 172 </a> ··· 145 175 </nav> 146 176 147 177 <section> 148 - <div className="max-w-5xl mx-auto px-6 pt-24 pb-20 md:pt-32 md:pb-28 text-center"> 149 - <div className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-surface-100 dark:bg-surface-800 text-surface-600 dark:text-surface-400 text-xs font-medium mb-6"> 150 - <Sparkles size={13} /> 151 - Built on the AT Protocol 152 - </div> 178 + <div className="max-w-3xl mx-auto px-6 pt-24 pb-20 md:pt-32 md:pb-28 text-center"> 179 + <p className="text-[13px] font-medium text-surface-400 dark:text-surface-500 tracking-wide uppercase mb-5"> 180 + Open-source web annotations 181 + </p> 153 182 154 - <h1 className="font-display text-4xl md:text-5xl lg:text-6xl font-bold tracking-tight text-surface-900 dark:text-white leading-[1.1] mb-6"> 155 - Write on the margins 156 - <br /> 157 - <span className="text-primary-600 dark:text-primary-400"> 158 - of the internet 159 - </span> 183 + <h1 className="font-display text-4xl md:text-5xl lg:text-6xl font-bold tracking-tight text-surface-900 dark:text-white leading-[1.08] mb-6"> 184 + Write on the margins of the internet. 160 185 </h1> 161 186 162 - <p className="text-lg md:text-xl text-surface-500 dark:text-surface-400 max-w-2xl mx-auto leading-relaxed mb-10"> 187 + <p className="text-base md:text-lg text-surface-500 dark:text-surface-400 max-w-xl mx-auto leading-relaxed mb-10"> 163 188 Margin is an open annotation layer for the internet. Highlight text, 164 189 leave notes, and bookmark pages, all stored on your decentralized 165 190 identity with the{" "} ··· 167 192 href="https://atproto.com" 168 193 target="_blank" 169 194 rel="noreferrer" 170 - className="text-primary-600 dark:text-primary-400 hover:underline" 195 + className="text-surface-700 dark:text-surface-300 hover:underline" 171 196 > 172 197 AT Protocol 173 198 </a> 174 - . 199 + . Not locked in a silo. 175 200 </p> 176 201 177 202 <div className="flex flex-col sm:flex-row items-center justify-center gap-3"> 178 203 <Link 179 - to="/login" 180 - className="inline-flex items-center gap-2 px-7 py-3 bg-primary-600 hover:bg-primary-700 dark:bg-primary-500 dark:hover:bg-primary-400 text-white rounded-xl font-semibold transition-colors" 204 + to={user ? "/home" : "/login"} 205 + className="inline-flex items-center gap-2 px-7 py-3 bg-surface-900 dark:bg-white text-white dark:text-surface-900 rounded-xl font-semibold hover:bg-surface-800 dark:hover:bg-surface-100 transition-colors" 181 206 > 182 - Get Started 207 + {user ? "Open App" : "Get Started"} 183 208 <ArrowRight size={16} /> 184 209 </Link> 185 210 <a 186 211 href={extensionLink} 187 212 target="_blank" 188 213 rel="noopener noreferrer" 189 - className="inline-flex items-center gap-2 px-7 py-3 bg-surface-100 dark:bg-surface-800 border border-surface-200 dark:border-surface-700 text-surface-700 dark:text-surface-200 hover:text-surface-900 dark:hover:text-white rounded-xl font-semibold transition-colors" 214 + className="inline-flex items-center gap-2 px-7 py-3 bg-white dark:bg-surface-800 border border-surface-200 dark:border-surface-700 text-surface-700 dark:text-surface-200 hover:text-surface-900 dark:hover:text-white rounded-xl font-semibold transition-colors" 190 215 > 191 216 <ExtensionIcon size={16} /> 192 217 Install for {extensionLabel} ··· 197 222 198 223 <section className="max-w-5xl mx-auto px-6 py-20 md:py-24"> 199 224 <div className="text-center mb-12"> 200 - <h2 className="font-display text-2xl md:text-3xl font-bold tracking-tight text-surface-900 dark:text-white mb-3"> 225 + <h2 className="font-display text-2xl md:text-3xl font-bold tracking-tight text-surface-900 dark:text-white mb-4"> 201 226 Everything you need to engage with the web 202 227 </h2> 203 - <p className="text-surface-500 dark:text-surface-400 max-w-xl mx-auto"> 228 + <p className="text-surface-500 dark:text-surface-400 max-w-xl mx-auto leading-relaxed"> 204 229 More than bookmarks. A full toolkit for reading, thinking, and 205 230 sharing on the open web. 206 231 </p> ··· 241 266 </div> 242 267 </section> 243 268 244 - <section className="bg-surface-100/50 dark:bg-surface-900/50 border-y border-surface-200/60 dark:border-surface-800/60"> 269 + <section className="bg-white/50 dark:bg-surface-800/50 border-y border-surface-200/60 dark:border-surface-800/60"> 245 270 <div className="max-w-5xl mx-auto px-6 py-20 md:py-24"> 246 271 <div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-16 items-center"> 247 272 <div> 248 - <div className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-surface-200/60 dark:bg-surface-800 text-surface-600 dark:text-surface-400 text-xs font-medium mb-5"> 273 + <div className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-surface-100 dark:bg-surface-800 text-surface-600 dark:text-surface-400 text-xs font-medium mb-5 border border-surface-200/60 dark:border-surface-700/60"> 249 274 <ExtensionIcon size={13} /> 250 275 Browser Extension 251 276 </div> ··· 285 310 286 311 <div className="flex flex-col sm:flex-row gap-3 mt-8 flex-wrap"> 287 312 <a 288 - href="https://chromewebstore.google.com/detail/margin/cgpmbiiagnehkikhcbnhiagfomajncpa" 313 + href={extensionLink} 289 314 target="_blank" 290 315 rel="noopener noreferrer" 291 316 className="inline-flex items-center justify-center gap-2 px-5 py-2.5 bg-surface-900 dark:bg-white text-white dark:text-surface-900 rounded-lg font-medium text-sm transition-all hover:opacity-90" 292 317 > 293 - <Chrome size={15} /> 294 - Chrome Web Store 318 + <ExtensionIcon size={15} /> 319 + Install for {extensionLabel} 295 320 <ExternalLink size={12} /> 296 321 </a> 297 - <a 298 - href="https://addons.mozilla.org/en-US/firefox/addon/margin/" 299 - target="_blank" 300 - rel="noopener noreferrer" 301 - className="inline-flex items-center justify-center gap-2 px-5 py-2.5 bg-surface-100 dark:bg-surface-800 text-surface-700 dark:text-surface-200 rounded-lg font-medium text-sm transition-all hover:bg-surface-200 dark:hover:bg-surface-700 border border-surface-200/80 dark:border-surface-700/80" 302 - > 303 - <FaFirefox size={15} /> 304 - Firefox Add-ons 305 - <ExternalLink size={12} /> 306 - </a> 307 - <a 308 - href="https://microsoftedge.microsoft.com/addons/detail/margin/nfjnmllpdgcdnhmmggjihjbidmeadddn" 309 - target="_blank" 310 - rel="noopener noreferrer" 311 - className="inline-flex items-center justify-center gap-2 px-5 py-2.5 bg-surface-100 dark:bg-surface-800 text-surface-700 dark:text-surface-200 rounded-lg font-medium text-sm transition-all hover:bg-surface-200 dark:hover:bg-surface-700 border border-surface-200/80 dark:border-surface-700/80" 312 - > 313 - <FaEdge size={15} /> 314 - Edge Add-ons 315 - <ExternalLink size={12} /> 316 - </a> 322 + {browser !== "chrome" && ( 323 + <a 324 + href="https://chromewebstore.google.com/detail/margin/cgpmbiiagnehkikhcbnhiagfomajncpa" 325 + target="_blank" 326 + rel="noopener noreferrer" 327 + className="inline-flex items-center justify-center gap-2 px-5 py-2.5 bg-surface-100 dark:bg-surface-800 text-surface-700 dark:text-surface-200 rounded-lg font-medium text-sm transition-all hover:bg-surface-200 dark:hover:bg-surface-700 border border-surface-200/80 dark:border-surface-700/80" 328 + > 329 + <Chrome size={15} /> 330 + Chrome 331 + <ExternalLink size={12} /> 332 + </a> 333 + )} 334 + {browser !== "firefox" && ( 335 + <a 336 + href="https://addons.mozilla.org/en-US/firefox/addon/margin/" 337 + target="_blank" 338 + rel="noopener noreferrer" 339 + className="inline-flex items-center justify-center gap-2 px-5 py-2.5 bg-surface-100 dark:bg-surface-800 text-surface-700 dark:text-surface-200 rounded-lg font-medium text-sm transition-all hover:bg-surface-200 dark:hover:bg-surface-700 border border-surface-200/80 dark:border-surface-700/80" 340 + > 341 + <FaFirefox size={15} /> 342 + Firefox 343 + <ExternalLink size={12} /> 344 + </a> 345 + )} 346 + {browser !== "edge" && ( 347 + <a 348 + href="https://microsoftedge.microsoft.com/addons/detail/margin/nfjnmllpdgcdnhmmggjihjbidmeadddn" 349 + target="_blank" 350 + rel="noopener noreferrer" 351 + className="inline-flex items-center justify-center gap-2 px-5 py-2.5 bg-surface-100 dark:bg-surface-800 text-surface-700 dark:text-surface-200 rounded-lg font-medium text-sm transition-all hover:bg-surface-200 dark:hover:bg-surface-700 border border-surface-200/80 dark:border-surface-700/80" 352 + > 353 + <FaEdge size={15} /> 354 + Edge 355 + <ExternalLink size={12} /> 356 + </a> 357 + )} 317 358 <a 318 359 href="https://www.icloud.com/shortcuts/21c87edf29b046db892c9e57dac6d1fd" 319 360 target="_blank" ··· 328 369 </div> 329 370 330 371 <div className="relative hidden lg:block"> 331 - <div className="relative rounded-2xl border border-surface-200 dark:border-surface-700 bg-surface-50 dark:bg-surface-900 p-6 shadow-xl"> 372 + <div className="relative rounded-2xl border border-surface-200/60 dark:border-surface-700/60 bg-white dark:bg-surface-800 p-6 shadow-xl"> 332 373 <div className="flex items-center gap-2 mb-4"> 333 374 <div className="flex gap-1.5"> 334 375 <div className="w-3 h-3 rounded-full bg-red-400/60" /> 335 376 <div className="w-3 h-3 rounded-full bg-yellow-400/60" /> 336 377 <div className="w-3 h-3 rounded-full bg-green-400/60" /> 337 378 </div> 338 - <div className="flex-1 mx-3 bg-surface-200 dark:bg-surface-800 rounded-md h-6 flex items-center px-3"> 379 + <div className="flex-1 mx-3 bg-surface-200 dark:bg-surface-700 rounded-md h-6 flex items-center px-3"> 339 380 <span className="text-[10px] text-surface-400 truncate"> 340 381 example.com/article/how-to-think-clearly 341 382 </span> ··· 343 384 </div> 344 385 345 386 <div className="space-y-3 text-sm text-surface-600 dark:text-surface-300 leading-relaxed"> 346 - <div className="h-3 bg-surface-200 dark:bg-surface-800 rounded w-3/4" /> 347 - <div className="h-3 bg-surface-200 dark:bg-surface-800 rounded w-full" /> 348 - <div className="flex gap-0.5 flex-wrap"> 349 - <div className="h-3 bg-surface-200 dark:bg-surface-800 rounded w-1/4" /> 387 + <div className="h-3 bg-surface-200 dark:bg-surface-700 rounded w-3/4" /> 388 + <div className="h-3 bg-surface-200 dark:bg-surface-700 rounded w-full" /> 389 + <div className="flex gap-0.5 flex-wrap items-center"> 390 + <div className="h-3 bg-surface-200 dark:bg-surface-700 rounded w-1/4" /> 350 391 <span className="px-1 py-0.5 bg-yellow-200/70 dark:bg-yellow-500/30 rounded text-xs text-surface-700 dark:text-yellow-200 font-medium leading-none"> 351 392 The point here is that Margin is indeed 352 393 </span> 353 - <div className="h-3 bg-surface-200 dark:bg-surface-800 rounded w-1/5" /> 394 + <div className="h-3 bg-surface-200 dark:bg-surface-700 rounded w-1/5" /> 354 395 </div> 355 - <div className="h-3 bg-surface-200 dark:bg-surface-800 rounded w-5/6" /> 356 - <div className="flex gap-0.5 flex-wrap"> 357 - <div className="h-3 bg-surface-200 dark:bg-surface-800 rounded w-2/5" /> 396 + <div className="h-3 bg-surface-200 dark:bg-surface-700 rounded w-5/6" /> 397 + <div className="flex gap-0.5 flex-wrap items-center"> 398 + <div className="h-3 bg-surface-200 dark:bg-surface-700 rounded w-2/5" /> 358 399 <span className="px-1 py-0.5 bg-primary-200/70 dark:bg-primary-500/30 rounded text-xs text-primary-700 dark:text-primary-200 font-medium leading-none"> 359 400 the best thing ever 360 401 </span> 361 - <div className="h-3 bg-surface-200 dark:bg-surface-800 rounded w-1/4" /> 402 + <div className="h-3 bg-surface-200 dark:bg-surface-700 rounded w-1/4" /> 362 403 </div> 363 - <div className="h-3 bg-surface-200 dark:bg-surface-800 rounded w-2/3" /> 404 + <div className="h-3 bg-surface-200 dark:bg-surface-700 rounded w-2/3" /> 364 405 </div> 365 406 366 - <div className="absolute -right-4 top-1/3 w-56 bg-white dark:bg-surface-800 rounded-xl border border-surface-200 dark:border-surface-700 shadow-lg p-3.5"> 407 + <div className="absolute -right-4 top-1/3 w-56 bg-white dark:bg-surface-900 rounded-xl border border-surface-200/60 dark:border-surface-700/60 shadow-lg p-3.5"> 367 408 <div className="flex items-center gap-2 mb-2"> 368 409 <div className="w-6 h-6 rounded-full bg-gradient-to-br from-primary-400 to-primary-600 flex items-center justify-center text-white text-[10px] font-bold"> 369 410 S ··· 394 435 <section className="max-w-5xl mx-auto px-6 py-20 md:py-24"> 395 436 <div className="grid grid-cols-1 md:grid-cols-2 gap-12 items-center"> 396 437 <div> 397 - <div className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-surface-100 dark:bg-surface-800 text-surface-600 dark:text-surface-400 text-xs font-medium mb-5"> 438 + <div className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-white dark:bg-surface-800 text-surface-600 dark:text-surface-400 text-xs font-medium mb-5 border border-surface-200/60 dark:border-surface-700/60"> 398 439 <Shield size={13} /> 399 440 Decentralized 400 441 </div> ··· 444 485 </ul> 445 486 </div> 446 487 447 - <div className="rounded-2xl bg-surface-900 dark:bg-surface-800 p-5 text-sm font-mono shadow-xl border border-surface-800 dark:border-surface-700"> 488 + <div className="rounded-2xl bg-surface-900 dark:bg-surface-950 p-5 text-sm font-mono shadow-xl border border-surface-800 dark:border-surface-800"> 448 489 <div className="flex items-center gap-2 mb-4"> 449 490 <div className="text-xs text-surface-500">lexicon</div> 450 491 <div className="text-xs text-primary-400 px-2 py-0.5 rounded bg-primary-400/10"> ··· 514 555 </p> 515 556 <div className="flex flex-col sm:flex-row items-center justify-center gap-3"> 516 557 <Link 517 - to="/login" 558 + to={user ? "/home" : "/login"} 518 559 className="inline-flex items-center gap-2 px-7 py-3 bg-primary-600 hover:bg-primary-700 dark:bg-primary-500 dark:hover:bg-primary-400 text-white rounded-xl font-semibold transition-colors" 519 560 > 520 - Sign in 561 + {user ? "Open App" : "Sign in"} 521 562 <ArrowRight size={16} /> 522 563 </Link> 523 564 <a ··· 547 588 </span> 548 589 </div> 549 590 <div className="flex items-center gap-5 text-sm text-surface-400 dark:text-surface-500"> 550 - <Link 551 - to="/home" 552 - className="hover:text-surface-600 dark:hover:text-surface-300 transition-colors" 553 - > 554 - Feed 555 - </Link> 591 + {user && ( 592 + <Link 593 + to="/home" 594 + className="hover:text-surface-600 dark:hover:text-surface-300 transition-colors" 595 + > 596 + Feed 597 + </Link> 598 + )} 556 599 <a 557 600 href="/privacy" 558 601 className="hover:text-surface-600 dark:hover:text-surface-300 transition-colors" ··· 572 615 className="hover:text-surface-600 dark:hover:text-surface-300 transition-colors" 573 616 > 574 617 GitHub 618 + </a> 619 + <a 620 + href="https://tangled.org/margin.at/margin" 621 + target="_blank" 622 + rel="noreferrer" 623 + className="hover:text-surface-600 dark:hover:text-surface-300 transition-colors" 624 + > 625 + Tangled 575 626 </a> 576 627 <a 577 628 href="mailto:hello@margin.at"
+7 -7
web/src/views/auth/Login.tsx
··· 1 1 import React, { useState, useEffect, useRef } from "react"; 2 - import { Link, useSearchParams, Navigate } from "react-router-dom"; 2 + import { useSearchParams, Navigate } from "react-router-dom"; 3 3 import { AtSign } from "lucide-react"; 4 4 import SignUpModal from "../../components/modals/SignUpModal"; 5 5 import { ··· 255 255 256 256 <p className="text-center text-sm text-surface-400 dark:text-surface-500 mt-2 leading-relaxed"> 257 257 By signing in, you agree to our{" "} 258 - <Link 259 - to="/terms" 258 + <a 259 + href="/terms" 260 260 className="text-surface-900 dark:text-white hover:underline font-medium hover:text-[#027bff] dark:hover:text-[#027bff] transition-colors" 261 261 > 262 262 Terms of Service 263 - </Link>{" "} 263 + </a>{" "} 264 264 and{" "} 265 - <Link 266 - to="/privacy" 265 + <a 266 + href="/privacy" 267 267 className="text-surface-900 dark:text-white hover:underline font-medium hover:text-[#027bff] dark:hover:text-[#027bff] transition-colors" 268 268 > 269 269 Privacy Policy 270 - </Link> 270 + </a> 271 271 </p> 272 272 273 273 <div className="flex items-center gap-4 py-2 opacity-50">
+2 -1
web/src/views/profile/Profile.tsx
··· 177 177 }, []); 178 178 179 179 useEffect(() => { 180 + const timer = loadMoreTimerRef.current; 180 181 return () => { 181 - if (loadMoreTimerRef.current) clearTimeout(loadMoreTimerRef.current); 182 + if (timer) clearTimeout(timer); 182 183 }; 183 184 }, []); 184 185