Commit Graph
34 Commits
Author SHA1 Message Date
shinalok 74bfe34d3f Merge branch 'ios-only-plain-textarea-fallback' into main
CI/CD / test (push) Successful in 46s
CI/CD / deploy (push) Successful in 1m38s
2026-08-27 08:16:10 +09:00
shinalokandClaude Sonnet 5 6000de166f keep EasyMDE on desktop/Android, fall back to plain textarea only on iOS
Dropping EasyMDE entirely (previous commit) fixed the Korean IME jamo split
but also removed the live markdown syntax highlighting everywhere, even on
platforms that never had the bug. Only iOS WebKit breaks CJK IME composition
under CodeMirror, so journal-editor.js now detects iOS (including the
desktop-UA-masquerading iPad, via MacIntel + multi-touch) and only skips
EasyMDE there, falling back to a native <textarea>. Everywhere else keeps
the full EasyMDE/CodeMirror editing experience as before.

window.JournalEditor now dispatches to the right engine per textarea
(checking t._easymde) so the toolbar buttons and the category-template
autofill in journal.html work unchanged regardless of which engine backs a
given textarea. Restores the easymde vendor files, base.html includes, and
service worker precache entries removed in the previous commit (cache
bumped to v8).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 08:16:06 +09:00
shinalok f524157326 Merge branch 'remove-easymde-fix-ime' into main 2026-08-26 20:23:13 +09:00
shinalokandClaude Sonnet 5 ac98cf4f99 replace EasyMDE journal editor with plain textarea to fix iOS Korean IME jamo split
Forcing inputStyle to contenteditable (previous commit) didn't fix it on
iPhone, confirming this is CodeMirror 5's own long-standing weakness with
CJK IME composition on iOS WebKit, not just the iPad desktop-UA detection
issue. There's no reliable fix short of dropping CodeMirror for the journal
content field, so EasyMDE is removed entirely and the textarea goes back to
a native, uncontrolled <textarea> — nothing intercepts/re-renders it during
composition, so iOS's IME just works.

The markdown toolbar buttons (bold/italic/heading/quote/lists/code/link)
now manipulate the textarea's selection directly via a small JournalEditor
JS API instead of calling EasyMDE/CodeMirror commands, and image paste
tracks its upload placeholder by a unique text marker instead of a
CodeMirror bookmark. Also drops the vendored easymde.min.js/css (no longer
referenced) and bumps the service worker CACHE_NAME so stale cached copies
of the old vendor files get evicted.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 20:23:09 +09:00
shinalok 291813820b Merge branch 'fix-ios-ime-jamo-split' into main
CI/CD / test (push) Successful in 48s
CI/CD / deploy (push) Successful in 1m33s
2026-08-26 12:43:58 +09:00
shinalokandClaude Sonnet 5 779bbb58aa force EasyMDE contenteditable input mode to fix Korean IME jamo splitting on iPad
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>
2026-08-26 12:43:45 +09:00
shinalok 46c412f8f3 Merge pull request 'enlarge journal calendar cells and pair day/title on one line' (#1) from journal-calendar-ui-tweaks into main
CI/CD / test (push) Successful in 30s
CI/CD / deploy (push) Successful in 2m15s
Reviewed-on: #1
2026-08-19 17:18:22 +09:00
shinalokandClaude Sonnet 5 8a49757882 enlarge journal calendar cells and pair day/title on one line
CI/CD / test (pull_request) Successful in 51s
CI/CD / deploy (pull_request) Skipped
- 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>
2026-08-19 17:13:38 +09:00
shinalokandClaude Opus 5 b173911269 make the app name the largest text on the landing page
CI/CD / test (push) Successful in 32s
CI/CD / deploy (push) Successful in 1m29s
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>
2026-08-11 10:34:15 +09:00
shinalokandClaude Opus 5 0e8688c25b make app name and purpose machine-readable on the landing page
CI/CD / test (push) Successful in 45s
CI/CD / deploy (push) Successful in 1m29s
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>
2026-08-10 18:29:42 +09:00
shinalok ad5a1c834c add terms page, improve landing content for branding review, and handle HTTP HEAD requests
CI/CD / test (push) Successful in 40s
CI/CD / deploy (push) Successful in 1m44s
2026-08-08 21:30:54 +09:00
shinalok be8a4e6908 add terms page, improve landing content for branding review, and handle HTTP HEAD requests
CI/CD / test (push) Successful in 46s
CI/CD / deploy (push) Successful in 1m44s
2026-08-08 20:40:47 +09:00
shinalok e752367f79 remove SSH debug connection test from CI/CD workflow for cleaner output and simplify rsync verbosity
CI/CD / test (push) Successful in 42s
CI/CD / deploy (push) Successful in 43s
2026-08-08 07:38:50 +09:00
shinalok 689d32e18b update CI/CD workflow: add unused Docker image cleanup step
CI/CD / test (push) Successful in 43s
CI/CD / deploy (push) Successful in 49s
2026-08-08 07:35:14 +09:00
shinalok bf0225d53b add SSH debug connection test to CI/CD workflow and enable batch mode for rsync
CI/CD / test (push) Successful in 40s
CI/CD / deploy (push) Successful in 1m57s
2026-08-08 07:25:51 +09:00
shinalok 5b1a9ac683 add SSH debug connection test to CI/CD workflow and enable batch mode for rsync
CI/CD / test (push) Successful in 43s
CI/CD / deploy (push) Failing after 37s
2026-08-08 07:13:27 +09:00
shinalok 97c79b8b04 add SSH debug connection test to CI/CD workflow and enable batch mode for rsync
CI/CD / test (push) Successful in 53s
CI/CD / deploy (push) Failing after 43s
2026-08-08 07:07:38 +09:00
shinalok a0a7a70c14 update CI/CD workflow: decode base64 SSH key and validate key integrity
CI/CD / test (push) Successful in 40s
CI/CD / deploy (push) Failing after 35s
2026-08-07 22:32:57 +09:00
shinalok c0d14967d0 update CI/CD workflow: install development dependencies for testing with pip
CI/CD / test (push) Successful in 51s
CI/CD / deploy (push) Failing after 1m0s
2026-08-07 19:31:07 +09:00
shinalok c09aadaeb5 update CI/CD workflow: replace containerized testing and deployment with system-installed dependencies
CI/CD / test (push) Failing after 3m54s
CI/CD / deploy (push) Has been skipped
2026-08-07 19:23:47 +09:00
shinalok 61142ed55e add CI/CD workflow for testing and deployment steps on main branch push
CI/CD / test (push) Failing after 8s
CI/CD / deploy (push) Has been skipped
2026-08-07 19:04:30 +09:00
shinalokandClaude Sonnet 5 00c66f9df8 journal: paste image from clipboard, cap embedded image size, fix media persistence
- Paste-to-embed: pasting an image into the markdown editor uploads it and
  inserts ![](url) 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>
2026-08-05 14:27:30 +09:00
shinalokandClaude Sonnet 5 bdf9d0bae7 journal: real markdown editor (EasyMDE) with live preview toggle, fix default template
- 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>
2026-08-05 12:24:18 +09:00
shinalokandClaude Sonnet 5 c466cc6639 journal: multi-select emotions and per-category writing templates
- Mood switches from a single enum column to a many-to-many
  JournalEntryMood table so an entry can carry several feelings at
  once; the vocabulary is trimmed to 9 named emotions (dropped the
  overlapping satisfaction scale) with unified noun-style labels.
- Categories can define a content_template that pre-fills the "new
  entry" textarea when selected (only if the user hasn't started
  typing), seeded with a Story/Feelings/Decisions/Insights/Actions
  reflection template on the default "일상" category.
- Fixes a real attribute-injection bug found while building the
  template feature: Jinja's built-in |tojson filter doesn't escape
  double quotes, which breaks a double-quoted x-data="..." attribute
  when the JSON payload contains one; added |forceescape and a
  regression test.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 19:12:59 +09:00
shinalokandClaude Sonnet 5 54c971875c add journaling feature: categories, tags, attachments, calendar, and multi-select emotions
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>
2026-08-04 18:40:07 +09:00
shinalokandClaude Sonnet 5 ef2a3ea082 prepare app for public app store distribution
- 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>
2026-07-23 13:41:50 +09:00
shinalokandClaude Sonnet 5 34a128a79b reorder habit item badges to show level before completion rate
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 13:41:24 +09:00
shinalokandClaude Sonnet 5 9dfcbcec8f add day-detail view to monthly history calendar
날짜 셀 클릭 시 그 날 예정된 습관별 완료/실패/미완료 상태를 htmx로 불러와 보여준다.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 13:21:49 +09:00
shinalok 800f42eab1 add habit leveling/XP system, explicit fail marking for missed habits, and scrollable habit tabs
- 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.
2026-07-20 18:18:56 +09:00
shinalok 4a7ef5cc29 improve PWA feel on iOS: offline fallback, overscroll, tap feedback, dark status bar, apple touch icon
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.
2026-07-20 15:34:19 +09:00
shinalokandClaude Sonnet 5 d212451fe0 add habit goal-period difficulty and habit formation research notes
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>
2026-07-19 10:12:17 +09:00
shinalok 55918d56d1 add abandoned status to distinguish given-up habits from completed ones
Completed and abandoned habits were both lumped under a single terminal
state, making it impossible to tell "finished successfully" apart from
"gave up" when reviewing habit history.
2026-07-17 20:19:35 +09:00
shinalok d0dd530e26 add retroactive check for yesterday's missed habits on /today
Users often only realize a habit was missed the next day; a same-day-only
check made it impossible to record it after the fact.
2026-07-17 20:15:38 +09:00
shinalokandClaude Sonnet 5 cee589bb3e Initial commit: habit tracker PWA with Google OAuth, push notifications
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>
2026-07-16 18:06:17 +09:00