4 Commits

Author SHA1 Message Date
Anso 0b046bfa52 chore: add in-app What's New scaffolding (#1767)
* feat: add whats-new entries data model

* feat: add useWhatsNewPreference hook

* feat: add whats-new breathing animation

* feat: add WhatsNewTrigger sparkle icon

* feat: add whatsNew slot to TopBar

* feat: add WhatsNewModal

* feat: wire whats-new sparkle icon and modal into EditorLayout

* feat: add What's New opt-out toggle to Settings

* fix: seed What's New watermark for zero-entry releases

A release that ships with entries.json still empty must stamp a
watermark on first run, or an existing install can never distinguish
itself from a genuinely fresh install once a later release adds its
first real entry, silently swallowing that entry's unseen signal.

* fix: constrain WhatsNewModal height and clarify settings copy

Bound the dialog to 85vh as a flex column so ModalBody's fill can
constrain the entry list to scroll while the header and footer stay
pinned, matching ConfirmModal's pattern. Also clarifies the "Show
What's New" helper text in Settings.

* fix: drop What's New screenshots that fail to load

Screenshots are authored by hand alongside the entry, so a typo'd or
not-yet-added filename is a realistic mistake. Previously that left the
browser's broken-image placeholder and alt text inside the card; now the
image is dropped and the title, blurb, and doc link still render.

* style: replace em dash in watermark comment with a comma

* fix: make "Never show again" actually hide What's New

Turning the feature off previously only stopped the breathing animation
and left the sparkle icon in the top bar, which is not what that label
means anywhere else. Opting out now removes the trigger entirely and
closes the modal, and Settings > About is the single way back.

Also brings the trigger in line with its top bar siblings: it now uses
the search trigger's hover treatment and gains a visible keyboard focus
ring, and the modal bounds itself with dvh rather than vh so the footer
cannot sit under a mobile URL bar. The modal is rendered in the desktop
branch only, since the bespoke mobile screens drop the top bar that
carries its trigger.

* test: cover the empty-entries and loader paths of What's New

The shipped state has an empty entries.json, so the branch where there is
no newest entry is the one actually running, yet nothing exercised it.
Adds a sibling hook test mocking that state to pin the empty-string
watermark write, and a loader test for the newest-is-last contract the
watermark depends on plus the malformed-entry filter.

Drops the unreachable array check in the loader and the assertion that
mirrored it: TypeScript types the JSON import, so a non-array file fails
the build rather than reaching that branch.

* refactor: fold the What's New storage writers into one helper

writeEnabled and writeLastSeenId were identical apart from the key and
the value encoding, duplicating the comment explaining why a failed
write is non-fatal. The boolean encoding now sits at its single call
site. Also hoists the reversed entry list to module scope, since the
source array is a module constant, and factors the repeated preference
mock in the trigger test behind a helper.

* chore: keep the What's New icon hidden until an entry exists

With entries.json empty there is nothing to announce, so a permanent
sparkle in the top bar would only ever open a modal reading "Nothing new
to show yet". The trigger now renders nothing in that state, leaving the
modal's empty state as a runtime fallback for entries that fail
validation rather than the shipping experience.

This keeps the scaffolding invisible until the first entry is authored,
which is the change that actually surfaces the feature to users.
2026-08-04 02:20:11 -04:00
Anso f4338c9d6b perf(build): enable incremental tsc (#827)
backend/tsconfig.json had no incremental setting, so every tsc run
re-checked the full project from cold. The two frontend tsconfigs
already declared a tsBuildInfoFile path under node_modules/.tmp/ but
without incremental: true the file was never written, and the path
itself sits inside node_modules where npm ci wipes it on every fresh
install — neither of which actually persists incremental state.

Add incremental: true to all three configs and drop the broken
tsBuildInfoFile overrides. TypeScript's default places the buildinfo
next to the tsconfig (e.g. backend/tsconfig.tsbuildinfo); the root
.gitignore already covers *.tsbuildinfo so nothing leaks into git.

Local cold-vs-warm tsc --noEmit on the backend dropped from ~3.0s
to ~1.4s — ~2x speedup on the warm path. CI builds are still cold
because runners do not cache the buildinfo between jobs; that is a
separate workflow change.
2026-04-28 09:15:54 -04:00
Anso 68a1dbe671 chore(deps): upgrade major dependencies (ESLint 10, recharts 3, TS 6, Vite 8) (#130)
* chore(deps): upgrade ESLint 9 → 10 with plugin compatibility fixes

- eslint: ^9 → ^10.0.0 (backend + frontend)
- @eslint/js: ^9 → ^10.0.0 (backend + frontend)
- eslint-plugin-react-refresh: ^0.4.24 → ^0.5.2 (ESM, config factory API)
- Update frontend eslint.config.js: destructured import for react-refresh,
  call configs.vite() as factory function (0.5 API change)
- Downgrade new ESLint 10 rules (no-useless-assignment, preserve-caught-error)
  to warnings for existing code patterns
- eslint-plugin-react-hooks stays at 7.0.1 (stable) with --legacy-peer-deps
  until a stable release adds ESLint 10 peer support

* chore(deps): upgrade recharts 2.x to 3.8 with chart.tsx type fixes

recharts 3.x moved Tooltip/Legend props to context-based API.
Updated chart.tsx to use explicit prop interfaces with internal
recharts type imports (LegendPayload, TooltipPayload, TooltipFormatter).

* chore(deps): upgrade TypeScript 5.9 to 6.0

- Remove deprecated baseUrl from frontend tsconfig (paths works standalone in TS 6)
- Add react-is dependency required by recharts 3.x at build time
- Backend and frontend both compile and lint cleanly

* chore(deps): upgrade Vite 7.3 to 8.0 and @vitejs/plugin-react to 6.0

Vite 8 replaces Rollup+esbuild with Rolldown, significantly improving
build speed (~2s vs ~18s). No config changes required.

* fix(ci): add .npmrc with legacy-peer-deps for CI and Docker builds

typescript-eslint@8.x requires typescript <6.0.0 and
eslint-plugin-react-hooks@7.0.1 requires eslint <=9. Until upstream
packages release compatible versions, legacy-peer-deps is needed.

* docs: add logo assets and re-ignore CLAUDE.md

* fix(ci): copy .npmrc into prod-deps Docker stage

The prod-deps stage also runs npm ci with backend/package.json but was
missing the .npmrc needed to bypass peer dep conflicts.
2026-03-25 00:03:08 -04:00
unknown 293f9cef26 Initial commit: Sencho V1 complete with Auth and Dockerization 2026-02-20 18:39:32 -05:00