iPadOS 13+ sends a desktop Safari UA by default, which fools CodeMirror's mobile detection into using the more fragile textarea input mode. That, combined with CodeMirror repainting the line mid-composition, breaks Korean IME composition and leaves jamo unmerged. inputStyle can't be changed after the editor is created, so it must be set in the EasyMDE constructor options.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- calendar-cell-journal: bump min-height 44px -> 76px, scale up date/dot/title font sizes
- journal calendar titles were centered; left-align them
- rework JournalCalendarDay: replace separate category_colors/titles lists
(mismatched lengths, no way to pair a dot with its title) with a single
entries list of (color, title) pairs so the dot and its title render on
the same non-wrapping row per entry
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The branding review no longer flags the missing purpose description, but
still reports that the app name does not match the homepage — even though
the console value and the h1 text are identical strings.
The likely reason is visual hierarchy: the app name was a 15px label while
the tagline underneath it was 32px, so the tagline read as the site's name.
The name and the tagline now swap sizes, and the logo moves above the
heading instead of sitting inline next to it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Google OAuth branding review rejected the app twice with "no description of
the app purpose on the homepage" and "app name does not match the homepage",
even though both were present in Korean. Two likely causes, both addressed:
- The h1 wrapped an alt="" logo image before the text, so the app name could
not be extracted from it. The image now sits outside the h1, leaving the
heading as plain text.
- The page was Korean-only. The app name is now "해빗랩 (HabitLab)" (matching
the console exactly), and the title, meta description and a new About
section carry an English description of the app purpose and its use of
Google account data.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Paste-to-embed: pasting an image into the markdown editor uploads it and
inserts  at the cursor. Unlike gallery attachments these aren't
tied to a journal_entry (the entry may not exist yet while composing), so
they're stored per-user under app/media/journal/{user_id}/pasted/ with no
DB row, served through an ownership-scoped route, and never cleaned up
automatically when an entry is deleted -- an accepted tradeoff at this
app's personal scale.
- The markdown sanitizer was stripping all <img> tags (not on the bleach
allowlist), which would have silently deleted every pasted image on save;
added img/src/alt/title while keeping event-handler attributes blocked.
- Cap embedded image width in both the editor pane and the rendered preview
so a large pasted photo can't overflow its card.
- Fix real data loss risk found while testing this: docker-compose.yml had
no volume for app/media, so every container recreate during a deploy wiped
uploaded photos, and deploy_sftp.py was syncing app/media/ (runtime user
data, not source) into the remote build context. Added the volume mount
and excluded media/ from the sync script. Recovered and relocated the
real attachments that had already landed in the wrong place on the NAS
during earlier deploys this session.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Replace the plain textarea with EasyMDE (vendored locally, no CDN) for
markdown authoring: syntax highlighting, smart list continuation, and a
custom text-based toolbar (built-in EasyMDE toolbar icons require Font
Awesome from a CDN, which this app doesn't use). unorderedListStyle is set
to "-" to match the app's own template convention.
- Add a preview/edit toggle button that swaps the editor for the exact same
server-rendered markdown (via /journal/preview) shown after saving, instead
of always showing both.
- Fix create/edit entry routes to verify the submitted category_id actually
belongs to the current user before inserting -- every other write path in
this app already checked ownership; this one didn't (found while manually
testing the new editor with a typo'd category id that happened to belong to
someone else's category, which surfaced as an IntegrityError 500 instead of
a clean 404-equivalent).
- Fix the default "일상" category template: bare "-" bullet lines don't parse
as list items in the markdown renderer (they need a trailing space), and
the content_template validator was silently stripping that trailing space
off on every save. Backfill migration updates any category still holding
the old, broken template text.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds an 8th development stage that lets users keep a free-form journal alongside
habit tracking, reusing the existing Google OAuth/DB/PWA infrastructure instead
of a separate project. Users organize entries into custom categories, filter by
a month calendar with day-detail drill-down, attach photos/videos (served via an
authenticated route, never /static), tag entries, and pick multiple emotions per
entry from a curated 9-option set. Includes a global journal prompt bank for
lightweight guided journaling.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- rebrand from 습관 트래커 to 해빗랩 across templates, manifest, service worker
- add HTTPS redirect middleware for reverse-proxied deployments
- add public landing page (/) and privacy policy page with real data
handling disclosures
- add in-app account deletion (Apple review requirement)
- add Android TWA Digital Asset Links support (/.well-known/assetlinks.json)
- add SFTP deployment script for the Synology-hosted server
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Habits and the account now accumulate XP and levels from check streaks, with
streak/level-up celebration banners and push notifications.
- The "yesterday missed" banner on /today now lets a habit be explicitly marked
failed (not just checked done), backed by a new HabitLog.status column so
completion-rate/streak calculations never count a failed day as done.
- The habit management tabs scroll horizontally on narrow screens instead of
wrapping to two lines, with a pure-CSS edge shadow indicating more content.
Add offline.html served by the service worker when a never-visited route
is requested without network. Disable rubber-band overscroll bounce and
gray tap highlights so standalone mode feels less like a webview. Split
theme-color by prefers-color-scheme so the iOS status bar matches dark
mode, and add a proper 180x180 apple-touch-icon instead of downscaling
the 192px icon.
Ground the 21-day habit myth in actual research (Lally et al. 2010) and let
users pick a target period (21/66/254 days or unlimited) matching that
study's easy/median/hard automaticity timelines when creating a habit,
with progress shown on the habits list.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
FastAPI + SQLAlchemy/Alembic + MariaDB backend with Jinja2/htmx/Alpine
server-rendered frontend. Multi-user via Google OAuth, daily habit
tracking, monthly/weekly history views, Web Push reminders via
APScheduler, and PWA support (manifest, service worker, offline caching).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>