diff --git a/CLAUDE.md b/CLAUDE.md index 3e99a8db..4cdc3356 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -153,8 +153,32 @@ CU-__ e.g. `CU-86ext1gpf_Make-subscription-tiers-Stripe-metadata-driven-adaptive-pricing-Council-004-rollout_Navid-Shad`. The `` is the ClickUp custom id (the `CU-…` shown on the task), the title is the task name with spaces → dashes, and the author is the assignee. - Branch off the latest `dev`; open a **PR into `dev`**. `dev` reaches `main` via the long-running `dev → main` PR — so a task only needs the one PR into `dev`. +- 🚧 **`main` is frozen for the duration of the UI migration. Do not merge `dev → main`.** The redesign lands one screen at a time (see [Design system migration](#design-system-migration)), so `dev` carries a deliberately half-migrated UI: screens already rebuilt on `subturtle-ui` sit beside screens still on pilotui, and dark mode is light-only on the migrated ones. Shipping that to production would put a visibly inconsistent app in front of users. The standing `dev → main` PR was closed for this reason; open a fresh one once the last screen is migrated and pilotui is removed. - **Footgun:** if you create the branch with `git switch -c origin/dev`, Git sets its upstream to `origin/dev`, and a plain `git push` (or a Git-client "sync") then lands the commits **straight on `dev`** instead of a new remote branch. Create it without that tracking and publish it explicitly: `git switch -c ` then `git push -u origin ` (or `--no-track` when branching off `origin/dev`). +## Design system migration + +The UI is being rebuilt on **`subturtle-ui`** (`ui/`), an in-house Vue 3 component library +carrying Subturtle's real brand, replacing **pilotui** — a generic admin theme whose blue +`#4361ee` palette was never ours. See [ui/README.md](ui/README.md). + +The redesign lands **one screen per PR** onto the long-running `new-design` branch, which is +promoted to `dev` in batches. While it is in progress: + +- **`main` is frozen** — see the note under [Branching](#branching). Nothing ships to + production until every screen is migrated. +- **pilotui stays installed** and is still required. Removing it is blocked on more than + components: `pilotui/style.css` supplies global classes the app markup uses directly + (`panel`, `btn`, `form-input`, `badge`, `animate__*`, `screen_loader`, `main-section`), + plus `pilotui/toast` and `useAppStore` from `pilotui/store`. Un-migrated screens render + inside the new shell and keep working. +- **Dark mode is light-only on migrated surfaces.** The theme switcher stays and + `:root.dark` is scaffolded but empty, so toggling dark leaves migrated screens light while + pilotui ones go dark. Deliberate; dark lands as its own milestone. +- `subturtle-ui` is a `link:../ui` dependency whose `dist/` is not committed, so the + frontend's `postinstall` builds it. Any context that installs the frontend needs `ui/` + present — the Dockerfile copies it in. + ## Commits & versioning This repo uses **semantic versioning**, and commit titles follow **Conventional Commits** so the type prefix maps to the intended `vMAJOR.MINOR.PATCH` bump. Pick the type by the change's real impact, not by habit: diff --git a/frontend/components/auth/LoginBoardPreview.vue b/frontend/components/auth/LoginBoardPreview.vue new file mode 100644 index 00000000..0d306afc --- /dev/null +++ b/frontend/components/auth/LoginBoardPreview.vue @@ -0,0 +1,108 @@ + + + diff --git a/frontend/components/common/InlineNotice.vue b/frontend/components/common/InlineNotice.vue new file mode 100644 index 00000000..26a7c2f4 --- /dev/null +++ b/frontend/components/common/InlineNotice.vue @@ -0,0 +1,47 @@ + + + diff --git a/frontend/locales/en.json b/frontend/locales/en.json index d8bbcc2d..9ccd1aef 100644 --- a/frontend/locales/en.json +++ b/frontend/locales/en.json @@ -402,11 +402,52 @@ } }, "auth": { - "signin_with_google": "SIGN IN WITH GOOGLE", + "signin_with_google": "Sign in with Google", "login_page": "Login Page", - "signin": "Sign In", + "signin": "Sign in", "signup": "Sign Up", - "signin_subtitle": "Sign in with Google to get started" + "signin_subtitle": "Sign in with Google to get started", + "heading-back": "Heading back to where you left off", + "no-account": "Don't have an account ?", + "no-account-cta": "Just sign in and you will get a new account.", + "chrome-extension": "Chrome extension", + "waiting": { + "label": "Waiting for Google\u2026", + "hint": "A Google window should have opened. Nothing there?", + "retry": "Open it again" + }, + "returning": { + "title": "Signing you in\u2026", + "subtitle": "Taking you to your dashboard." + }, + "notice": { + "expired": "Your session ended, so we brought you here. Nothing you saved was lost.", + "failed": { + "title": "That didn't go through", + "message": "The Google window closed before you finished. Nothing was saved \u2014 try once more." + } + }, + "preview": { + "today": "Today", + "board-title": "Today's board", + "board-subtitle": "Two things today, about ten minutes. Day 9 of your streak.", + "due-today": "Due today", + "due": "Due", + "review-title": "Smart Review", + "review-body": "12 phrases from levels 1 to 3. Cloze cards start at level 3.", + "start-review": "Start review", + "time-left": "3 min left", + "session-title": "Live session", + "session-body": "Say this week's phrases out loud with Coach. Money Heist \u2014 s1e4.", + "start-session": "Start session", + "resting-title": "Resting", + "resting-body": "Levels 4 and 5 are asleep. Their next cards land on Thursday.", + "optional": "Optional practice", + "phrase-1": "Je n'en reviens pas", + "phrase-1-translation": "I can't believe it", + "phrase-2": "\u00c7a vaut le coup", + "phrase-2-translation": "It's worth it" + } }, "practice": { "nav": "Practice", diff --git a/frontend/pages/auth/login.vue b/frontend/pages/auth/login.vue index 02cbd0b2..5d52b62e 100644 --- a/frontend/pages/auth/login.vue +++ b/frontend/pages/auth/login.vue @@ -1,52 +1,127 @@ - // Only prime an anonymous token when no token is loaded yet — keeps - // anonymous-allowed routes usable from the login page without - // overwriting an existing authenticated session. - if (!authentication.getToken) { - authentication.loginAsAnonymous(); + diff --git a/ui/scripts/build-icons.mjs b/ui/scripts/build-icons.mjs index 3957701e..f53d1d29 100644 --- a/ui/scripts/build-icons.mjs +++ b/ui/scripts/build-icons.mjs @@ -18,18 +18,22 @@ import { fileURLToPath } from 'node:url'; const here = dirname(fileURLToPath(import.meta.url)); const root = resolve(here, '..'); -/** Every icon the shell and the Progress screen use. Keep sorted by set, then name. */ +/** Every icon the shell, the Progress screen and the Login screen use. Keep sorted by set, then name. */ const ICONS = [ + 'logos:google-icon', 'solar:add-circle-bold', 'solar:alt-arrow-left-linear', 'solar:alt-arrow-right-linear', 'solar:arrow-down-bold', + 'solar:arrow-right-bold', 'solar:arrow-right-linear', 'solar:arrow-up-bold', 'solar:bookmark-bold-duotone', 'solar:chart-2-bold-duotone', + 'solar:clock-circle-bold', 'solar:crown-bold', 'solar:crown-bold-duotone', + 'solar:danger-triangle-bold', 'solar:documents-bold-duotone', 'solar:download-minimalistic-bold', 'solar:fire-bold', @@ -39,6 +43,7 @@ const ICONS = [ 'solar:layers-minimalistic-bold-duotone', 'solar:lock-keyhole-minimalistic-bold-duotone', 'solar:microphone-3-bold-duotone', + 'solar:moon-sleep-bold-duotone', 'solar:notebook-bold-duotone', 'solar:play-bold', 'solar:rocket-2-bold-duotone', diff --git a/ui/src/icon/icons.generated.ts b/ui/src/icon/icons.generated.ts index 180d8990..8bbbfffd 100644 --- a/ui/src/icon/icons.generated.ts +++ b/ui/src/icon/icons.generated.ts @@ -7,16 +7,20 @@ export interface IconDef { } export const icons: Record = { + "logos:google-icon": { body: "", width: 256, height: 262 }, "solar:add-circle-bold": { body: "", width: 24, height: 24 }, "solar:alt-arrow-left-linear": { body: "", width: 24, height: 24 }, "solar:alt-arrow-right-linear": { body: "", width: 24, height: 24 }, "solar:arrow-down-bold": { body: "", width: 24, height: 24 }, + "solar:arrow-right-bold": { body: "", width: 24, height: 24 }, "solar:arrow-right-linear": { body: "", width: 24, height: 24 }, "solar:arrow-up-bold": { body: "", width: 24, height: 24 }, "solar:bookmark-bold-duotone": { body: "", width: 24, height: 24 }, "solar:chart-2-bold-duotone": { body: "", width: 24, height: 24 }, + "solar:clock-circle-bold": { body: "", width: 24, height: 24 }, "solar:crown-bold": { body: "", width: 24, height: 24 }, "solar:crown-bold-duotone": { body: "", width: 24, height: 24 }, + "solar:danger-triangle-bold": { body: "", width: 24, height: 24 }, "solar:documents-bold-duotone": { body: "", width: 24, height: 24 }, "solar:download-minimalistic-bold": { body: "", width: 24, height: 24 }, "solar:fire-bold": { body: "", width: 24, height: 24 }, @@ -26,6 +30,7 @@ export const icons: Record = { "solar:layers-minimalistic-bold-duotone": { body: "", width: 24, height: 24 }, "solar:lock-keyhole-minimalistic-bold-duotone": { body: "", width: 24, height: 24 }, "solar:microphone-3-bold-duotone": { body: "", width: 24, height: 24 }, + "solar:moon-sleep-bold-duotone": { body: "", width: 24, height: 24 }, "solar:notebook-bold-duotone": { body: "", width: 24, height: 24 }, "solar:play-bold": { body: "", width: 24, height: 24 }, "solar:rocket-2-bold-duotone": { body: "", width: 24, height: 24 }, diff --git a/ui/tailwind-tokens.cjs b/ui/tailwind-tokens.cjs index 20407589..d4946de6 100644 --- a/ui/tailwind-tokens.cjs +++ b/ui/tailwind-tokens.cjs @@ -55,10 +55,13 @@ module.exports = { 'jade-600': c('--jade-600'), 'jade-700': c('--jade-700'), 'amber-600': c('--amber-600'), + 'red-600': c('--red-600'), 'sky-600': c('--sky-600'), 'ink-100': c('--ink-100'), 'ink-150': c('--ink-150'), + 'ink-200': c('--ink-200'), 'ink-300': c('--ink-300'), + 'ink-700': c('--ink-700'), 'ink-800': c('--ink-800'), 'ink-950': c('--ink-950'), }, @@ -94,6 +97,7 @@ module.exports = { 'st-sm': 'var(--shadow-sm)', 'st-md': 'var(--shadow-md)', 'st-lg': 'var(--shadow-lg)', + 'st-xl': 'var(--shadow-xl)', 'st-primary': 'var(--shadow-primary)', }, maxWidth: {