diff --git a/src/App.css b/src/App.css index df191d0..90d8176 100644 --- a/src/App.css +++ b/src/App.css @@ -94,7 +94,7 @@ display: flex; flex-direction: column; gap: 1.75rem; - background: #fff; + background: var(--color-surface); border-radius: 24px; border-top: 4px solid #22c55e; padding: 2rem 1.75rem; @@ -434,7 +434,7 @@ } .journal-card { - background: #fff; + background: var(--color-surface); border-radius: 20px; padding: 1.625rem 1.5rem; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.07); @@ -824,7 +824,7 @@ .bottom-nav { flex-shrink: 0; position: relative; /* NOT fixed — lives in the flex column */ - background: rgba(255, 255, 255, 0.96); + background: var(--color-surface); border-top: 1px solid rgba(0, 0, 0, 0.07); padding: 8px 12px 12px; display: flex; @@ -947,7 +947,7 @@ display: flex; align-items: center; justify-content: center; - background: #fff; + background: var(--color-surface); border: 1px solid #e5e7eb; border-radius: 50%; cursor: pointer; @@ -974,7 +974,7 @@ /* Calendar */ .calendar-card { - background: #fff; + background: var(--color-surface); border-radius: 18px; padding: 1.125rem 1rem; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.06); @@ -1106,7 +1106,7 @@ } .entry-card { - background: #fff; + background: var(--color-surface); padding: 1rem 1rem 1rem 0.875rem; border-radius: 14px; border-left: 4px solid #22c55e; @@ -1234,7 +1234,7 @@ align-items: center; gap: 1rem; margin-bottom: 1.25rem; - background: #fff; + background: var(--color-surface); border-radius: 18px; padding: 1rem 1.125rem; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.06); @@ -1415,7 +1415,7 @@ } .settings-card { - background: #fff; + background: var(--color-surface); border-radius: 18px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.06); overflow: hidden; @@ -1617,7 +1617,7 @@ font-size: 0.9375rem; font-weight: 600; color: #dc2626; - background: #fff; + background: var(--color-surface); border: none; border-radius: 14px; cursor: pointer; @@ -1637,7 +1637,7 @@ font-size: 0.9375rem; font-weight: 600; color: #6b7280; - background: #fff; + background: var(--color-surface); border: none; border-radius: 14px; cursor: pointer; @@ -1706,7 +1706,7 @@ /* Modal panel – bottom sheet on mobile, centred card on desktop */ .entry-modal { - background: #fff; + background: var(--color-surface); border-radius: 20px 20px 0 0; width: 100%; max-height: 85vh; @@ -1961,7 +1961,7 @@ } .confirm-modal { - background: #fff; + background: var(--color-surface); border-radius: 20px; padding: 1.75rem; max-width: 380px; @@ -2223,7 +2223,7 @@ /* Desktop dark theme adjustments */ @media (min-width: 860px) { [data-theme="dark"] .bottom-nav { - background: #141414; + background: var(--color-surface); border-right-color: rgba(74, 222, 128, 0.1); } @@ -2301,7 +2301,7 @@ [data-theme="dark"] .settings-card, [data-theme="dark"] .settings-profile, [data-theme="dark"] .entry-card { - background: #1a1a1a; + background: var(--color-surface); border-color: rgba(74, 222, 128, 0.12); box-shadow: 0 2px 16px rgba(0, 0, 0, 0.4), @@ -2408,7 +2408,7 @@ /* -- Bottom nav -- */ [data-theme="dark"] .bottom-nav { - background: rgba(20, 20, 20, 0.97); + background: var(--color-surface); border-top-color: rgba(74, 222, 128, 0.1); } @@ -2516,7 +2516,7 @@ /* -- Settings buttons -- */ [data-theme="dark"] .settings-tutorial-btn { - background: #1a1a1a; + background: var(--color-surface); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); } @@ -2525,7 +2525,7 @@ } [data-theme="dark"] .settings-clear-btn { - background: #1a1a1a; + background: var(--color-surface); color: #f87171; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); } @@ -2535,7 +2535,7 @@ } [data-theme="dark"] .settings-signout-btn { - background: #1a1a1a; + background: var(--color-surface); color: #9ca3af; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); } @@ -2600,7 +2600,7 @@ /* -- History search button -- */ [data-theme="dark"] .history-search-btn { - background: #1a1a1a; + background: var(--color-surface); border-color: #2a2a2a; color: #7a8a7a; } @@ -2634,7 +2634,7 @@ /* -- Entry modal -- */ [data-theme="dark"] .entry-modal { - background: #1a1a1a; + background: var(--color-surface); border-top-color: #4ade80; box-shadow: 0 -4px 32px rgba(0, 0, 0, 0.5), @@ -2688,7 +2688,7 @@ } [data-theme="dark"] .delete-confirm-modal { - background: #1a1a1a; + background: var(--color-surface); } [data-theme="dark"] .delete-confirm-title { color: #e8f5e8; @@ -2714,7 +2714,7 @@ } [data-theme="dark"] .confirm-modal { - background: #1a1a1a; + background: var(--color-surface); box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5); } @@ -3120,67 +3120,62 @@ body.gj-has-bg .settings-page { border-radius: 20px; padding: 1.5rem; width: min(440px, calc(100vw - 2rem)); - max-height: 85vh; - overflow-y: auto; box-shadow: 0 8px 40px rgba(0, 0, 0, 0.18); } -.bg-gallery { - display: flex; - gap: 0.6rem; - overflow-x: auto; - padding: 0.25rem 0 0.75rem; - scrollbar-width: none; - -ms-overflow-style: none; +/* 4-column fixed grid — no scroll */ +.bg-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 0.5rem; + margin-bottom: 0.75rem; } -.bg-gallery::-webkit-scrollbar { display: none; } - -/* Shared thumb/swatch styles */ -.bg-gallery-swatch, -.bg-gallery-thumb, -.bg-gallery-add { - flex-shrink: 0; - width: 84px; - height: 58px; +/* Base tile — aspect-ratio set via inline style to match screen */ +.bg-grid-tile { border-radius: 10px; border: 2.5px solid transparent; - overflow: visible; + overflow: hidden; cursor: pointer; position: relative; padding: 0; + width: 100%; transition: border-color 0.15s, transform 0.12s; display: flex; - flex-direction: column; align-items: center; justify-content: center; background: none; } -.bg-gallery-swatch, -.bg-gallery-thumb { - overflow: hidden; -} - -.bg-gallery-swatch:hover, -.bg-gallery-thumb:hover, -.bg-gallery-add:hover { +.bg-grid-tile:hover:not(:disabled) { transform: scale(1.04); } -.bg-gallery-item--active { +.bg-grid-tile--active { border-color: var(--color-primary, #22c55e) !important; } -/* Default color swatch */ -.bg-gallery-swatch-fill { - width: 100%; - height: 100%; - background: #eef6ee; - border-radius: 7px; +/* "+" upload tile */ +.bg-grid-add { + border: 2px dashed var(--color-border, #d4e8d4); + background: var(--color-accent-light, #dcfce7); + color: var(--color-text-muted, #6b7280); } -/* Thumbnail image */ +.bg-grid-add:hover:not(:disabled) { + border-color: var(--color-primary, #22c55e); + color: var(--color-primary, #22c55e); +} + +/* Empty placeholder tile */ +.bg-grid-empty { + background: var(--color-accent-light, #dcfce7); + border: 2px dashed var(--color-border, #d4e8d4); + opacity: 0.4; + cursor: default; +} + +/* Thumbnail image fills the tile */ .bg-gallery-thumb-img { width: 100%; height: 100%; @@ -3202,37 +3197,67 @@ body.gj-has-bg .settings-page { display: flex; align-items: center; justify-content: center; + pointer-events: none; } -/* Label below swatch/add button */ -.bg-gallery-label { +/* Wrapper holds tile + delete button as siblings */ +.bg-grid-wrapper { + position: relative; +} + +.bg-grid-wrapper .bg-grid-tile { + width: 100%; +} + +/* Delete (×) button — top-right corner of the image */ +.bg-tile-delete { position: absolute; - bottom: -18px; - left: 50%; - transform: translateX(-50%); - font-size: 0.65rem; - color: var(--color-text-muted, #6b7280); - white-space: nowrap; + top: -6px; + right: -6px; + width: 18px; + height: 18px; + border-radius: 50%; + background: #ef4444; + color: #fff; + border: none; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + z-index: 2; + box-shadow: 0 1px 4px rgba(0,0,0,0.3); } -/* "+" add button */ -.bg-gallery-add { - border: 2px dashed var(--color-border, #d4e8d4); - background: var(--color-accent-light, #dcfce7); - color: var(--color-text-muted, #6b7280); - gap: 0.1rem; +/* Revert to default button */ +.bg-default-btn { + width: 100%; + padding: 0.6rem 1rem; border-radius: 10px; + border: 1.5px solid var(--color-border, #d4e8d4); + background: transparent; + color: var(--color-text-muted, #6b7280); + font-size: 0.85rem; + font-weight: 500; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + gap: 0.45rem; + margin-bottom: 0.5rem; + transition: background 0.15s, border-color 0.15s, color 0.15s; } -.bg-gallery-add:hover { +.bg-default-btn:hover:not(:disabled) { + background: var(--color-accent-light, #dcfce7); border-color: var(--color-primary, #22c55e); - color: var(--color-primary, #22c55e); + color: var(--color-primary-hover, #16a34a); } /* Close button */ .bg-close-btn { width: 100%; - margin-top: 1.5rem; + margin-top: 0.25rem; padding: 0.7rem; border-radius: 12px; border: 1.5px solid var(--color-border, #d4e8d4); @@ -3388,17 +3413,29 @@ body.gj-has-bg .settings-page { background: #1a1a1a; } -[data-theme="dark"] .bg-gallery-swatch-fill { - background: #0f0f0f; -} - -[data-theme="dark"] .bg-gallery-add { +[data-theme="dark"] .bg-grid-add { background: rgba(255, 255, 255, 0.05); border-color: rgba(255, 255, 255, 0.12); color: #9ca3af; } -[data-theme="dark"] .bg-gallery-add:hover { +[data-theme="dark"] .bg-grid-add:hover:not(:disabled) { + border-color: #4ade80; + color: #4ade80; +} + +[data-theme="dark"] .bg-grid-empty { + background: rgba(255, 255, 255, 0.04); + border-color: rgba(255, 255, 255, 0.1); +} + +[data-theme="dark"] .bg-default-btn { + border-color: rgba(255, 255, 255, 0.12); + color: #9ca3af; +} + +[data-theme="dark"] .bg-default-btn:hover:not(:disabled) { + background: rgba(255, 255, 255, 0.06); border-color: #4ade80; color: #4ade80; } diff --git a/src/components/BgImageCropper.tsx b/src/components/BgImageCropper.tsx index 3b990be..14fa523 100644 --- a/src/components/BgImageCropper.tsx +++ b/src/components/BgImageCropper.tsx @@ -144,7 +144,7 @@ export function BgImageCropper({ imageSrc, aspectRatio, onCrop, onCancel }: Prop canvas.height = outH const ctx = canvas.getContext('2d')! ctx.drawImage(img, srcX, srcY, srcW, srcH, 0, 0, outW, outH) - onCrop(canvas.toDataURL('image/jpeg', 0.72)) + onCrop(canvas.toDataURL('image/jpeg', 0.92)) }, [aspectRatio, onCrop]) return ( diff --git a/src/index.css b/src/index.css index 591dce2..0877efc 100644 --- a/src/index.css +++ b/src/index.css @@ -24,7 +24,8 @@ input, textarea { --color-primary: #22c55e; --color-primary-hover: #16a34a; --color-bg-soft: #eef6ee; - --color-surface: #ffffff; + --card-bg-opacity: 0.85; + --color-surface: rgb(255 255 255 / var(--card-bg-opacity)); --color-accent-light: #dcfce7; --color-text: #1a1a1a; --color-text-muted: #6b7280; @@ -81,7 +82,7 @@ button:focus-visible { --color-primary: #4ade80; --color-primary-hover: #22c55e; --color-bg-soft: #0f0f0f; - --color-surface: #1a1a1a; + --color-surface: rgb(26 26 26 / var(--card-bg-opacity)); --color-accent-light: rgba(74, 222, 128, 0.12); --color-text: #e8f5e8; --color-text-muted: #7a8a7a; diff --git a/src/pages/SettingsPage.tsx b/src/pages/SettingsPage.tsx index 4ee8730..88f36b8 100644 --- a/src/pages/SettingsPage.tsx +++ b/src/pages/SettingsPage.tsx @@ -84,6 +84,8 @@ export default function SettingsPage() { // Derived from mongoUser (no local state — always fresh after refreshMongoUser) const bgImages: string[] = (mongoUser as { backgroundImages?: string[] } | null)?.backgroundImages ?? [] const activeImage: string | null = mongoUser?.backgroundImage ?? null + // Tile aspect ratio matches the actual screen so previews reflect real proportions + const screenAspect = `${window.innerWidth} / ${window.innerHeight}` // Continue onboarding tour if navigated here from the history page tour useEffect(() => { @@ -173,6 +175,15 @@ export default function SettingsPage() { bgUpdate({ backgroundImage: img }) } + const handleDeleteBgImage = (img: string, e: React.MouseEvent) => { + e.stopPropagation() + const newHistory = bgImages.filter(i => i !== img) + // If the deleted image was active, clear it too + const updates: Parameters[1] = { backgroundImages: newHistory } + if (img === activeImage) updates.backgroundImage = null + bgUpdate(updates) + } + const handleBgFileSelect = (e: React.ChangeEvent) => { const file = e.target.files?.[0] if (!file) return @@ -803,7 +814,7 @@ export default function SettingsPage() {
e.stopPropagation()}>

Background

- Tap to apply · + to upload new + Add new images or select from previously used ones:

{/* Hidden file input */} @@ -815,66 +826,85 @@ export default function SettingsPage() { onChange={handleBgFileSelect} /> - {/* Gallery row */} -
- {/* Default color swatch — always first */} + {/* Fixed 4-tile grid: [+] [slot1] [slot2] [slot3] */} +
+ {/* Add new — always first tile */} - - {/* History thumbnails */} - {bgImages.map((img, i) => ( - - ))} - - {/* Add new */} - + + {/* 3 image slots — filled or empty placeholder */} + {Array.from({ length: MAX_BG_HISTORY }).map((_, i) => { + const img = bgImages[i] + if (img) { + return ( +
+ + +
+ ) + } + return ( +
+ ) + })}
+ {/* Revert to default — only shown when a custom bg is active */} + {activeImage && ( + + )} + {bgApplying && ( -

+

Saving…

)}