Panel rewrite: design spec + P0.1 (@idento/ui foundation) - #59
Conversation
Approved brainstorm outcome: Vite SPA stack (React 19 + TanStack Router/Query), board winners 1c/1f/2c/4a/3e, full-stack phasing P0-P5, console stays in web/ untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
packages/ui (npm workspace, source-imported) holds tokens + primitives + verdict vocabulary for panel, console rewrite, and desktop kiosk; strict content boundary (no i18n/api/feature code), react >=18 peer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…P0 requirements Localization (EN+RU key parity, switcher), theming (light+dark, token-only colors) and agent working rules (.cursor/rules/ui-package.mdc, AGENTS.md, copilot-instructions.md) added to decisions log, foundation section and P0 deliverables. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…Cursor rule file Root AGENTS.md section + scoped packages/ui/AGENTS.md and panel/AGENTS.md instead of .cursor/rules/ui-package.mdc. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
9 TDD tasks: workspace root, tokens (board 1a) with guard tests, base primitives, board-1b kit (StatusPill, ConfirmDialog, EmptyState, Skeleton, AgentStatus), verdict vocabulary, barrel + lint boundary + AGENTS.md rules. P0.2 (panel/shell/auth) and P0.3 (openapi/CI/Docker) get their own plans. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…aint Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…NTS.md chain Barrel test-first per task-9 brief: index.test.ts asserts every public export name resolves; index.ts re-exports cn/verdict + all Task 3-8 components. Added eslint.config.js blocking i18n/data-fetching/app imports from @idento/ui. Added packages/ui/AGENTS.md scoped rules and appended a Web frontend section to root AGENTS.md. Also fixes two pre-existing gaps surfaced by running lint/typecheck for the first time on this package: tsconfig's restricted "types" array excluded @types/node (needed by theme.test.ts/no-hardcoded-colors.test.ts for node:fs/path/url), and cn.test.ts tripped the new no-constant-binary-expression rule.
Apply animate-spin class conditionally to the in_progress status icon, ensuring the Loader2 spinner animates rather than appearing frozen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 34 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (18)
📝 WalkthroughWalkthroughIntroduces the ChangesPanel UI foundation
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@codex review |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
packages/ui/src/theme.css (1)
1-3: 🧹 Nitpick | 🔵 TrivialConfigure Stylelint to recognize Tailwind v4 at-rules.
Stylelint's
scss/at-rule-no-unknownrule flags@source,@theme, and@utilityas unknown at-rules. These are valid Tailwind v4 CSS directives. If Stylelint is part of the lint pipeline (npm run lint -w@idento/ui``), these false positives will cause failures. Add Tailwind v4 at-rules to the rule'signoreAtRuleslist in the Stylelint configuration.Also applies to: 64-64, 103-103, 109-109, 114-114, 119-119, 124-124, 129-129
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/ui/src/theme.css` around lines 1 - 3, Update the Stylelint configuration’s scss/at-rule-no-unknown rule to include Tailwind v4 directives source, theme, and utility in its ignoreAtRules list, preserving the existing ignored rules and lint behavior.Source: Linters/SAST tools
packages/ui/src/no-hardcoded-colors.test.ts (1)
6-22: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winSelf-scan fragility: exempt this file's own filename.
EXEMPTdoesn't include this file's own name, sowalk()scans this test file against its ownHEX/RGBregexes. It currently passes only because the regex literals don't happen to match their own patterns character-for-character — any future edit (e.g. adding an example color in a comment) could break this test for reasons unrelated to the policy it enforces.♻️ Proposed fix
-const EXEMPT = new Set(["theme.css", "theme.test.ts"]); +const EXEMPT = new Set(["theme.css", "theme.test.ts", "no-hardcoded-colors.test.ts"]);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/ui/src/no-hardcoded-colors.test.ts` around lines 6 - 22, Update the EXEMPT set used by the no-hardcoded-colors test to include this test file’s own filename, so walk(HERE) excludes it from HEX/RGB scanning while continuing to scan all applicable non-exempt TypeScript, TSX, and CSS files.packages/ui/src/theme.test.ts (1)
8-13: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueInconsistent
-foregroundtoken coverage.
SEMANTIC_TOKENSincludes--muted-foregroundbut omits the other-foregroundcompanions (--card-foreground,--popover-foreground,--secondary-foreground,--accent-foreground,--destructive-foreground,--success-foreground,--warning-foreground,--info-foreground) that also exist intheme.css. Consider including all of them for consistent regression coverage, or drop--muted-foregroundif the omission is intentional.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/ui/src/theme.test.ts` around lines 8 - 13, Update the SEMANTIC_TOKENS list to include every existing -foreground companion from theme.css, including card, popover, secondary, accent, destructive, success, warning, and info, while retaining muted-foreground for consistent theme regression coverage.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/ui/eslint.config.js`:
- Around line 10-14: Update the restricted-import patterns in the ESLint
configuration to use recursive globs that match arbitrarily deep subpaths for
`@tanstack` packages and panel, web, or desktop app imports. Preserve the existing
restrictions and messages while ensuring nested imports such as
`@tanstack/react-query/build/x` and ../panel/features/x are rejected.
In `@packages/ui/src/components/confirm-dialog.tsx`:
- Around line 9-21: Update ConfirmDialogProps to a discriminated union so
typedConfirmation and typedConfirmationLabel are required together: the
typed-confirmation variant must require both strings, while the
no-typed-confirmation variant must disallow them. Preserve the existing
rendering behavior, ensuring every rendered typed-confirmation Input always has
its corresponding label without introducing hardcoded fallback text.
---
Nitpick comments:
In `@packages/ui/src/no-hardcoded-colors.test.ts`:
- Around line 6-22: Update the EXEMPT set used by the no-hardcoded-colors test
to include this test file’s own filename, so walk(HERE) excludes it from HEX/RGB
scanning while continuing to scan all applicable non-exempt TypeScript, TSX, and
CSS files.
In `@packages/ui/src/theme.css`:
- Around line 1-3: Update the Stylelint configuration’s scss/at-rule-no-unknown
rule to include Tailwind v4 directives source, theme, and utility in its
ignoreAtRules list, preserving the existing ignored rules and lint behavior.
In `@packages/ui/src/theme.test.ts`:
- Around line 8-13: Update the SEMANTIC_TOKENS list to include every existing
-foreground companion from theme.css, including card, popover, secondary,
accent, destructive, success, warning, and info, while retaining
muted-foreground for consistent theme regression coverage.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 656e8235-3589-4454-8fc7-9c7c3646c718
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (43)
AGENTS.mddocs/superpowers/plans/2026-07-13-panel-p0.1-workspace-ui-foundation.mddocs/superpowers/specs/2026-07-13-panel-rewrite-design.mdpackage.jsonpackages/ui/AGENTS.mdpackages/ui/eslint.config.jspackages/ui/package.jsonpackages/ui/src/components/agent-status.test.tsxpackages/ui/src/components/agent-status.tsxpackages/ui/src/components/avatar.tsxpackages/ui/src/components/button.test.tsxpackages/ui/src/components/button.tsxpackages/ui/src/components/card.tsxpackages/ui/src/components/confirm-dialog.test.tsxpackages/ui/src/components/confirm-dialog.tsxpackages/ui/src/components/dialog.test.tsxpackages/ui/src/components/dialog.tsxpackages/ui/src/components/dropdown-menu.test.tsxpackages/ui/src/components/dropdown-menu.tsxpackages/ui/src/components/empty-state.test.tsxpackages/ui/src/components/empty-state.tsxpackages/ui/src/components/input.test.tsxpackages/ui/src/components/input.tsxpackages/ui/src/components/label.tsxpackages/ui/src/components/separator.tsxpackages/ui/src/components/sheet.tsxpackages/ui/src/components/skeleton.tsxpackages/ui/src/components/status-pill.test.tsxpackages/ui/src/components/status-pill.tsxpackages/ui/src/components/switch.tsxpackages/ui/src/components/tooltip.tsxpackages/ui/src/index.test.tspackages/ui/src/index.tspackages/ui/src/lib/cn.test.tspackages/ui/src/lib/cn.tspackages/ui/src/lib/verdict.test.tspackages/ui/src/lib/verdict.tspackages/ui/src/no-hardcoded-colors.test.tspackages/ui/src/test/setup.tspackages/ui/src/theme.csspackages/ui/src/theme.test.tspackages/ui/tsconfig.jsonpackages/ui/vitest.config.ts
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1f1c8a1113
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| patterns: [ | ||
| { group: ["i18next", "i18next/*", "react-i18next"], message: "@idento/ui is i18n-agnostic — take strings via props." }, | ||
| { group: ["axios", "openapi-fetch", "@tanstack/*"], message: "@idento/ui must not fetch data or route." }, | ||
| { group: ["**/panel/*", "**/web/*", "**/desktop/*"], message: "@idento/ui must not import from apps." }, |
There was a problem hiding this comment.
Widen the app-import restriction globs
When a future @idento/ui file reaches into app source via a normal relative path such as ../../web/src/lib/api or ../../panel/src/..., these patterns do not match because the * after web/panel/desktop only covers a single path segment. That lets npm run lint -w @idento/ui allow the app dependency that this rule is meant to block; use recursive globs like **/web/**, **/panel/**, and **/desktop/** (or equivalent) so deeper app paths are rejected.
Useful? React with 👍 / 👎.
| import { twMerge } from "tailwind-merge"; | ||
|
|
||
| export function cn(...inputs: ClassValue[]) { | ||
| return twMerge(clsx(inputs)); |
There was a problem hiding this comment.
Preserve custom type utilities when merging classes
The shared cn helper runs every class list through tailwind-merge, but tailwind-merge's default config does not know that the new @utility text-body, text-card-title, etc. are font-size utilities. When a primitive combines them with semantic text colors, such as buttonVariants producing text-body ... text-primary-foreground or DialogDescription using text-body text-muted-foreground, the merge treats the custom type utility as a text-color class and drops it, so the Inter type ramp silently disappears across most components. Configure tailwind-merge for these custom text utilities or avoid merging them against text-color classes.
Useful? React with 👍 / 👎.
| </DialogHeader> | ||
| {typedConfirmation !== undefined ? ( | ||
| <div className="flex flex-col gap-2"> | ||
| {typedConfirmationLabel ? <Label htmlFor={inputId}>{typedConfirmationLabel}</Label> : null} |
There was a problem hiding this comment.
Require an accessible label for typed confirmations
When typedConfirmation is set but the caller omits typedConfirmationLabel (which the exported props currently allow), this branch still renders the confirmation input with only an id and no accessible name. Screen reader users then cannot tell what value must be entered before a destructive action is enabled; make the label required for typed confirmations or provide an aria-label/fallback tied to the required phrase.
Useful? React with 👍 / 👎.
|
|
||
| describe("no hardcoded colors outside theme.css", () => { | ||
| const files = walk(HERE).filter( | ||
| (f) => !EXEMPT.has(f.split("/").pop()!) && /\.(ts|tsx|css)$/.test(f), |
There was a problem hiding this comment.
Use path basename for color-test exemptions
On Windows, join() produces backslash-separated paths, so f.split("/").pop() leaves the whole path and the theme.css / theme.test.ts exemptions never match. Running npm test -w @idento/ui there will scan theme.css and fail on the intentionally allowed palette hex values; use basename(f) from node:path instead of splitting on /.
Useful? React with 👍 / 👎.
| >(({ className, ...props }, ref) => ( | ||
| <DialogPrimitive.Overlay | ||
| ref={ref} | ||
| className={cn("fixed inset-0 z-50 bg-foreground/40 data-[state=open]:animate-in data-[state=open]:fade-in-0", className)} |
There was a problem hiding this comment.
Use a dark scrim token for overlays
In the .dark theme, foreground is the light text color (#f4f4f5), so opening a Dialog or Sheet applies a translucent light overlay instead of dimming the page. That makes dark-mode overlays wash out the background rather than providing a proper modal scrim; add a dedicated overlay/backdrop token (or equivalent semantic class) that stays dark enough in both themes.
Useful? React with 👍 / 👎.
- cn(): extendTailwindMerge with a custom font-size class group so the Inter type-ramp utilities (text-body, text-card-title, ...) no longer get silently dropped when merged with a text-color class — this was breaking typography on nearly every component (button, dialog, card, input, label, tooltip, empty-state, agent-status, avatar). - ConfirmDialog: typedConfirmation/typedConfirmationLabel are now a discriminated union so the typed-confirm input can never render without an accessible label. - eslint.config.js: widen no-restricted-imports globs to ** so nested subpaths (@tanstack/react-query/build/x, ../panel/features/x) are actually caught, not just one path segment deep. - no-hardcoded-colors.test.ts: use path.basename() instead of splitting on "/" so the theme.css/theme.test.ts exemption still matches on Windows. - theme.css: add an --overlay token (same dark value in both themes) and use it for Dialog/Sheet backdrops instead of bg-foreground, which washed out to a light overlay in dark mode. Regression tests added for all five fixes; full suite green (124/124), typecheck and lint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- labeler.yml: new "panel" (panel/**, not created yet — P0.2) and "shared-ui" (packages/**) labels; both created in the repo. - ci.yml: detect packages/**, package.json, package-lock.json, .npmrc changes as "shared-ui" and pass it into validate.yml. - validate.yml: new run-shared-ui input gating typecheck-shared-ui, lint-shared-ui, test-shared-ui — same shape as the existing web jobs, but installing via root npm workspace (npm ci at repo root, npm run <script> -w @idento/ui). - release.yml: run-shared-ui: true alongside the existing unconditional backend/web validation on tag pushes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The panel-rewrite design spec cites this as its primary input (docs/superpowers/specs/2026-07-13-panel-rewrite-design.md, "Inputs:") but it was never committed — the reference was broken. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Never committed, no history — contains launch.json plus per-session worktree scratch (232MB found locally). Same treatment as .idea/ and .vscode/* already in this file. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: implementation plan P0.2 — panel scaffold, shell & auth 11 TDD tasks: panel/ workspace scaffold, Tailwind v4 wired to @idento/ui/theme.css (verified via real build, not jsdom), TanStack Router (code-based) + Query, i18n EN/RU with a key-parity guard, light/dark ThemeProvider, session adapter + minimal fetch client against the real (not stale-openapi) backend contracts, auth screens (login/register SaaS-gated/QR staff), app shell (nav, org switcher, edition-aware ON-PREM tag, mobile drawer), suspended-tenant takeover driven by a global QueryCache error handler, impersonation banner ported from the console's proven behavior, panel/AGENTS.md. P0.1 (@idento/ui) merged via PR #59. P0.3 (openapi client + CI/Docker) gets its own plan. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(panel): npm workspace scaffold — Vite boots, App renders Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(panel): restore @types/react to the brief's ^19.2.7 (only @types/react-dom needed the ^19.0.0 downgrade) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(panel): wire Tailwind v4 to @idento/ui/theme.css, self-host Inter * feat(panel): TanStack Router + Query providers, code-based route tree Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(panel): i18n EN/RU with key-parity guard, LanguageSwitcher * feat(panel): light/dark ThemeProvider + ThemeSwitcher * fix(panel): dark: variant tracks the .dark class not the OS media query; centralize matchMedia test stub - Remap Tailwind's dark: variant in panel/src/app/styles.css via @custom-variant dark (&:where(.dark, .dark *)) so ThemeSwitcher's Sun/Moon icons follow the app's actual .dark class toggle instead of the OS prefers-color-scheme media query (Finding 1). - Add a global window.matchMedia stub to panel/src/test/setup.ts, imported vi from "vitest" explicitly so it type-checks under tsconfig.app.json (which lacks vitest/globals types), and remove the now-redundant per-file copies from ThemeProvider.test.tsx, ThemeSwitcher.test.tsx, and App.test.tsx (Finding 2). * fix(panel): suppress react-refresh/only-export-components for the ThemeProvider+useTheme co-location pattern Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(panel): session adapter, minimal fetch client, useInstance Session module owns the localStorage keys (token/user/tenants/current_tenant) that web/ already uses in production. Client wraps fetch for the real backend endpoints (auth/login, auth/register, auth/login-qr, api/instance, api/auth/switch-tenant) verified against backend/internal/handler/*.go, and throws ApiError on non-2xx responses. useInstance is a thin TanStack Query wrapper around getInstance with staleTime: Infinity. Dropped one unused `ApiError` import from client.test.ts (copy-paste leftover in the brief's test — the failure-mode assertion uses rejects.toMatchObject, not toBeInstanceOf) to satisfy @typescript-eslint/no-unused-vars. * feat(panel): auth screens — Login, Register (SaaS-only, route-guarded), QR staff login * fix(panel): clear stale current_tenant on QR login; use router Link for in-app auth-screen navigation - session.ts: saveSession() now removes the stored current_tenant when the new session has none to set (QR login), instead of leaving a stale value from a previous session in localStorage — fixes a cross-session tenant leak on shared devices. - LoginScreen/RegisterScreen: replace raw <a href> in-app links with TanStack Router's Link so navigation between /login, /register, and /qr-login is client-side instead of a full page reload. - LoginScreen.test.tsx/RegisterScreen.test.tsx: wrap renders in a minimal RouterContextProvider so Link has the router context it now needs; without this the components crash on render (useLinkProps dereferences a null router outside a router context). * fix(docs): P0.2 plan — Task 8's router.tsx rewrite must keep Task 7's /register guard Task 8 replaces router.tsx wholesale to nest routes under a protected layout route; the plan's code block for that rewrite had silently dropped the beforeLoad edition-guard Task 7 just implemented and got reviewed/approved. Restored it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(panel): app shell — nav, org switcher, mobile drawer, session guard * fix(panel): wire OrgSwitcher tenant-count and AppShell ON-PREM tag through i18n Both strings shipped as hardcoded English, violating the plan's binding "RU + EN from the first screen" constraint. OrgSwitcher's tenant count now uses react-i18next's CLDR pluralization (orgSwitcherCount_one/_few/_many/_other) since Russian has different plural rules than English. AppShell's ON-PREM version tag is now routed through an onPremVersionTag key (kept in Latin script per convention, but now localizable without a code change). keyParity.test.ts previously asserted en.json and ru.json have identical key sets, which is incompatible with CLDR pluralization (en has 2 plural forms, ru has 4). Rescoped it to require exact parity for non-pluralized keys and family-level parity (base name presence, "other" fallback required) for pluralized keys, preserving the original guarantee's strength everywhere it still applies. * fix(panel): wire NavDrawer's aria-label and closeLabel through i18n Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(panel): wire the Home placeholder route through i18n Extracts the index route's inline component into a named HomePlaceholder function component so useTranslation() can be called correctly, and routes the hardcoded "Signed in — Home ships in P1." string through a new homeComingSoon i18n key (EN + RU), closing the last hardcoded-string gap in Task 8's shell/router code. * feat(panel): suspended-tenant takeover, driven by a global QueryCache error handler * fix(panel): correct support-email domain on the suspended-tenant screen (idento.io -> idento.app) * docs: fix support-email domain in P0.2 plan (idento.io -> idento.app) * feat(panel): impersonation banner, ported from the console's proven behavior * fix(panel): getImpersonation() restores operator token on expiry, self-heals on malformed JSON Fixes two review findings on Task 10's impersonation banner: - The expiry branch only removed the session key, leaking operator_token forever and leaving token pointed at the expired impersonation JWT. Now reuses endImpersonation() (already correct) instead of duplicating logic. - JSON.parse on the stored session was unguarded; malformed localStorage data crashed the render tree. Now wrapped in try/catch, self-healing by clearing the key and returning null, matching web/src/lib/impersonation.ts. * docs: fix P0.2 plan's getImpersonation() to match proven web/ behavior (restore operator token on expiry, self-heal on malformed JSON) * docs(panel): scoped agent rules + link from root AGENTS.md * fix(panel): correct AGENTS.md's session rule to acknowledge the impersonation exception * docs: fix P0.2 plan's AGENTS.md session-rule text to acknowledge the impersonation exception * fix(panel): wire ThemeSwitcher and LanguageSwitcher aria-labels through i18n Fixes hardcoded English aria-label values that bypassed i18n on the theme and language switcher buttons. Russian-locale screen-reader users now hear localized labels matching the buttons' locale context. Establishes these icon-only buttons' aria-labels within the i18n scope, consistent with the fix applied to NavDrawer.tsx in Task 8. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: sync P0.2 plan text with already-shipped fixes (saveSession clears stale current_tenant, auth screens use router Link) PR review (CodeRabbit) correctly flagged that the plan document's LoginScreen/RegisterScreen/session.ts code blocks still showed the pre-fix versions, even though the actual shipped code was corrected during Task 7/8's review rounds. No functional change — code was already correct; only the plan's historical snapshot was stale. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(panel): close PR #60 review findings — session, routing, i18n Ten confirmed bugs from Codex/CodeRabbit review of PR #60 (panel P0.2): - saveSession no longer leaves a stale impersonation/operator_token pair behind on a fresh login, which could restore a parked operator session over the new user's real one. - The /register route guard now fails closed (redirects to /login) when the instance lookup rejects, instead of surfacing an unhandled error state that bypassed the SaaS-only guard. - QR login code is trimmed before submission so trailing whitespace from copy/paste no longer breaks the exact-match backend lookup. - queryClient now wires a MutationCache alongside the QueryCache so a tenant_suspended error from a mutation (e.g. OrgSwitcher's tenant switch) triggers the suspended-tenant takeover, not just query errors. - A global 401 handler clears the stored session and redirects to /login on any protected-call session expiry, while explicitly skipping the three auth screens' own login/register/QR mutations (tagged via mutationKey) since their 401s are expected wrong-credential responses handled inline. - AppShell's "Events" nav link uses activeOptions={{ exact: true }} so it no longer shows active on every route (TanStack Router treats "/" as a prefix match by default). - Auth screens render localized error copy (authErrorKey.ts + new en/ru i18n keys) instead of raw backend/network text. - getCurrentUser/getTenants/getCurrentTenant self-heal on malformed localStorage JSON instead of throwing during ProtectedLayout render. - <html lang> now syncs with the active i18n language via a languageChanged listener. - impersonationSession's malformed-JSON catch path now calls endImpersonation() (matching the already-correct expiry branch) so a corrupted session record can't leave the operator token stranded. Verified un-fixed: panel/index.html's hardcoded theme-color meta tag matches web/index.html's existing precedent — no change needed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * ci: wire panel/ into labeler-adjacent CI (typecheck/lint/test/build) The panel/** path was never added to ci.yml's change-detection filter, so every commit in this PR — including the review-fix batch that added a global 401 handler and a MutationCache — has been merging with CI showing green while zero panel-specific verification (tests, typecheck, lint, build) actually ran. Only "Shared UI" checks fired, and only incidentally, because Task 1's workspace registration touched the root package.json/package-lock.json (which the existing shared-ui filter watches). - ci.yml: detect panel/**, package.json, package-lock.json, .npmrc changes as "panel" and pass it into validate.yml as run-panel. - validate.yml: new run-panel input gating typecheck-panel, lint-panel, test-panel, build-panel — same shape as the existing shared-ui jobs, installing via root npm workspace. - release.yml: run-panel: true alongside the existing unconditional backend/web/shared-ui validation on tag pushes. labeler.yml already had a panel: panel/** rule from an earlier phase (unused until now since nothing referenced panel/ in CI) — no change needed there. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: CI Bot <ci@example.com> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Summary
@idento/uipackage for panel/console/kiosk; RU+EN and light+dark as day-one requirements; AI-agent rules routed through the AGENTS.md chain, not Cursor).@idento/uifoundation).packages/uinpm workspace package — design tokens (light+dark, board-1a values), Inter type ramp, base primitives (Button/Label/Input/Card), Radix-backed overlay primitives (Dialog/Sheet/DropdownMenu/Tooltip/Separator/Switch/Avatar), StatusPill, Skeleton/EmptyState, two-tier ConfirmDialog, AgentStatus + shared verdict vocabulary, public API barrel, ESLint import-boundary guard, and agent rules inpackages/ui/AGENTS.md+ rootAGENTS.md.Notable follow-ups (not blocking, flagged for awareness)
VerdictBand.AlreadyCheckedcurrently renders Amber, while this package (per plan) mapsalready_checked_into blue/info. Needs a deliberate reconciliation decision in a future task.packages/ui/tsconfig.json'stypesarray includes"node"project-wide rather than scoped to the two files that need it — low risk, worth narrowing later.Test plan
npm test -w @idento/ui— 120/120 passingnpm run typecheck -w @idento/ui— cleannpm run lint -w @idento/ui(via directnpx eslint, thenpm runwrapper hit an unrelated local tooling quirk) — 0 issues🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
Tests