Skip to content

feat: native iOS/macOS app shells, self-hosted contact form, system/light/dark theme, and JavaChat rebrand - #161

Merged
WilliamAGH merged 24 commits into
mainfrom
dev
Aug 2, 2026
Merged

feat: native iOS/macOS app shells, self-hosted contact form, system/light/dark theme, and JavaChat rebrand#161
WilliamAGH merged 24 commits into
mainfrom
dev

Conversation

@WilliamAGH

Copy link
Copy Markdown
Owner

Summary

JavaChat now ships as native iOS and macOS apps wrapping the hosted web product, gains a self-hosted contact form and public privacy page, supports a user-selectable system/light/dark theme, and rebrands from "Java Chat" to JavaChat across the UI and app metadata.

Changes

Features

  • Native iOS and macOS apps: javachat.ai is now installable as a universal iPhone/iPad app and a macOS desktop app — native WebKit shells that reuse the hosted web product without reimplementing UI or routing; the desktop app adds Copy Link and Open in Default Browser page actions (mobile/iosApp/JavaChat/, mobile/desktopApp/JavaChatDesktop/)
  • Native OAuth sign-in: Google, LinkedIn, and Apple sign-in inside the native shells round-trips through the system authentication session via javachat://sso-callback instead of a web redirect; plain web sessions keep the default transport (clerkAuthentication.svelte.ts, OAuthPolicy.swift, WebViewCoordinator.swift)
  • Self-hosted contact form: visitors can message the site owner via POST /api/contact delivered over self-hosted SMTP (STARTTLS), with a honeypot field, render-time trap, per-IP rate limiting, and header-injection rejection — spam drops answer 202 identically to real sends so bots learn nothing (ContactController, ContactSubmissionUseCase, ContactSubmission, ContactPage.svelte)
  • System/Light/Dark theme: users can now pick a color scheme in the header menu, persisted to localStorage and applied pre-paint so there is no flash of the wrong theme; citation badges and syntax highlighting follow the choice instead of the raw OS media query (themePreference.svelte.ts, public/theme-boot.js, global.css)
  • Header menu: navigation, theme choice, and Privacy/Contact links now fit within a 360px viewport via a hamburger menu with keyboard support and focus return; Contact becomes a real /contact route with SEO metadata and sitemap entry instead of an uncrawlable dialog (HeaderMenu.svelte, WebMvcConfig, SeoController)
  • Public privacy page: /privacy renders through the SPA shell with dedicated crawler metadata and a sitemap entry (PrivacyPage.svelte, SeoController, SitemapController)
  • Native appearance setting: iOS users can override the app appearance (System/Light/Dark) from the iOS Settings app (AppAppearance.swift, Settings.bundle/Root.plist)
  • Canonical lesson references: every guided lesson now closes with a link to its authoritative source (JLS chapter, Java 25 API, official Spring/Quarkus/Groovy/Clojure docs) so learners land on canonical documentation (src/main/resources/guided/lessons/*.md, GuidedLearningServiceCitationTest)

Bug Fixes

  • Health check false alarms: an unreachable SMTP host no longer flips /actuator/health to DOWN and trips monitors in environments without a mail server — mail reachability is not an application health dependency since send failures surface at request time (application.properties management.health.mail.enabled=false)
  • Theme boot blocked by CSP: the no-flash theme boot script was inline and would have been silently killed by the backend's script-src 'self' Content-Security-Policy in production; it is now served as a classic render-blocking asset (public/theme-boot.js, index.html)
  • Boot crash in hardened browsers: an unguarded localStorage read could prevent the SPA from mounting in privacy-hardened modes; storage failures now degrade to session-only theming with logged warnings, and the preference syncs across tabs via the storage event (themePreference.svelte.ts)
  • Chat status unreadable: in-progress retrieval statuses (reranker, documentation search) were truncated to ellipses inside a 320px card under five stacked animations; the pending state now renders as full-wrapping editorial text with a single text-shine cue and role="status" for screen readers (ThinkingIndicator.svelte)
  • Chat input hidden on short viewports: the composer no longer slips off-screen on short viewports (ChatView.svelte)
  • Lesson summaries truncated: lesson summaries now wrap instead of truncating (LearnView.svelte)
  • Native shell safe areas: web content no longer slides under the iOS home indicator and horizontal safe areas, keeping the composer and header controls reachable on edge-to-edge devices (JavaChatWebView.swift, WebViewCoordinator.swift)
  • Auth control misplacement: the sign-in control stays pinned left of the header menu at all viewports (Header.svelte)
  • Welcome footer clutter: the footer now appears only on hover-capable desktop screens (WelcomeScreen.svelte)

Refactoring

  • Rebrand to JavaChat: the product name is now "JavaChat" consistently across the header, app metadata, privacy policy, and the backend-served webmanifest, so install prompts and tab titles show one name (Header.svelte, pageMetadata.ts, PrivacyPage.svelte, static/site.webmanifest)
  • Repo-local Clerk skills removed: agent skill definitions moved to the global ~/.agents/skills location, removing ~180 duplicated files from the repository (.agents/skills/clerk-*)

Breaking Changes

None

Test Coverage

  • Backend: ContactControllerTest, RequiredMailCredentialValidationTest, GuidedLearningServiceCitationTest, SeoControllerTest
  • Frontend: ContactPage.test.ts, HeaderMenu.test.ts, themePreference.svelte.test.ts, contact.test.ts, App.test.ts
  • Mobile: Swift suites under __tests__/mobile covering navigation policy, OAuth admission, shell state, and configuration

The message pane and messages wrapper could overflow the column flex container and push the input area out of view. Give both min-height: 0 so they shrink within the column, and pin the input area with flex-shrink: 0.
Lesson summary text in the learn view was clipped with an ellipsis, hiding the description. Allow the summary to overflow visibly, clip no text, and wrap normally so the full summary stays readable.
The welcome footer was hidden only below a fixed viewport width, so it still appeared on touch devices with wide viewports. Hide it by default and reveal it only on viewports wider than 640px that report hover: hover and pointer: fine.
Inside the native iOS and macOS shells, OAuth must round-trip through the system authentication session instead of a web redirect. When the shell injects window.javaChatNativeOAuth, pass Clerk the internal OAuth transport that delegates the redirect URL and authorization open to the native bridge; plain web sessions keep the default transport.
Each guided lesson now ends with a Read link to the authoritative source for its topic (JLS chapter, Java 25 API, or the official Spring, Quarkus, Groovy, and Clojure references), so learners land on canonical HTTPS documentation instead of searching on their own. A new GuidedLearningServiceCitationTest case walks the full table of contents and asserts every lesson publishes at least one canonical HTTPS citation without touching retrieval.
Both shells are native WebKit containers that package the hosted web product without reimplementing its UI, routing, auth semantics, or data layer. They inject the Clerk native OAuth transport at the WebKit boundary so Google, LinkedIn, and Apple authorization round-trip through the system authentication session via javachat://sso-callback. The desktop shell adds Copy Link and Open in Default Browser page actions; the iOS target is a universal iPhone and iPad app. Navigation, OAuth admission, shell state, and configuration policies are covered by the Swift test suites under __tests__/mobile.
…am guards

The site had no way for visitors to reach the owner without exposing an
external mailto or a third-party form, and an unauthenticated public inbox
needs abuse resistance. This adds a public POST /api/contact endpoint that
delivers submissions over self-hosted SMTP (STARTTLS submission port) while
silently dropping bots so they learn nothing from the response.

- Add spring-boot-starter-mail dependency and spring.mail.* properties;
  SMTP credentials stay environment-only (SPRING_MAIL_USERNAME/PASSWORD)
  per the secrets policy, never in tracked properties
- Add AppProperties.Contact with validated recipient/sender email routing
- Add RequiredMailCredentialValidation that fails fast in prod when SMTP
  credentials are absent, so an unsendable inbox never accepts messages
- Add ContactSubmission record owning every field invariant at the
  application boundary (length bounds, RFC 822 email, line-break/header-
  injection rejection)
- Add ContactSubmissionUseCase with honeypot field, render-time trap, and
  per-IP fixed-window rate limit (Caffeine cache); spam drops answer 202
  identically to real sends while genuine MailExceptions propagate
- Add ContactMessageAcknowledgement sealed-permit member and ContactController
  mapping 202/400/429/500; ContactMessageRequest carries raw transport values
- Add ContactControllerTest and RequiredMailCredentialValidationTest covering
  delivery, silent spam drops, rate limiting, header-injection, and fail-fast
…ta and sitemap entry

The privacy policy needed a public, indexable URL with crawlable metadata,
but the SPA shell and SEO controller only knew about /chat, /guided, and
/learn, so the page would land on a 404 and stay out of the sitemap. This
adds /privacy to the view-controller forwards, the SEO metadata map, and
the sitemap's public routes so crawlers and direct links resolve correctly.

- Register /privacy as an SPA view-controller forward in WebMvcConfig
- Add a Privacy Policy PageMetadata entry and add /privacy to the
  SeoController @GetMapping path set so crawlers receive dedicated
  title/description/og metadata
- Add /privacy to SitemapController PUBLIC_ROUTES
- Extend SeoControllerTest, SitemapControllerTest, and
  BrowserErrorResponseIntegrationTest to cover the privacy metadata,
  sitemap loc, and SPA forwarding
…submission

The SPA had no privacy policy view and no support contact surface, and the
new /api/contact backend needs a client that validates before sending and
maps the fixed response contract onto explicit UI states. This adds the
privacy page as a third application view and a modal contact dialog that
submits through the validated contact service. Header and App carry both
changes together because the nav bar renders the privacy tab and the
contact trigger, and App renders both the privacy view and the dialog.

- Add PrivacyPage with the current data-practices policy and a contact
  aside, plus a PrivacyPage test asserting the effective date, providers,
  and absence of stale copied language
- Add the privacy view to the pageMetadata catalog (with descendant-path
  recovery staying on chat) and extend App to render PrivacyPage and its
  tests for the direct /privacy route
- Add ContactSubmissionSchema (with honeypot and renderedAt trap) and
  ContactAcceptedSchema to the single validation source of truth
- Add contact service that POSTs to /api/contact with CSRF retry and maps
  202/400/429/5xx and network failure onto a discriminated union, never
  swallowing errors or assuming a malformed acknowledgement succeeded
- Add ContactFormDialog (native dialog, honeypot field captured at open,
  inline validation, success/rate-limit/rejected states) and its tests
- Add privacy and contact nav buttons to Header with an onContactOpen
  callback prop, and extend Header tests for the new states and trigger
…orial text

The thinking indicator truncated every status line (nowrap + ellipsis inside a
320px card) and stacked five ambient animations (bouncing dots, shimmer bar,
pulsing avatar, wiggling phase icons), so retrieval statuses like the reranker
and documentation-search notices were unreadable.

Renders the pending state as chromeless typography in the message flow:
full-wrapping status and detail lines, a static avatar optically aligned to
the first line, and one slow text shine as the only live cue.

- Add role="status" with aria-live for screen readers
- Fall back to the phase default when the idle empty-string status would
  render a blank message
- Update streaming-stability tests to assert the rendered status text instead
  of the removed data-phase hook
The app was dark-only, and citation badges keyed off the raw OS media query,
so no manual scheme choice could exist, let alone stay in sync.

Adds a header toggle (System/Light/Dark) persisted to localStorage, applied
pre-paint by an inline boot script in index.html, and a full light "Warm
Precision" palette behind :root[data-theme="light"]. Citation badges and
syntax highlighting now follow the data-theme attribute instead of
prefers-color-scheme.

- ThemePreferenceSchema (Zod) validates the persisted preference
- Compact header: icon-only nav and sign-in on narrow viewports so brand,
  nav, toggle, and auth controls fit within 360px
- Toast shadow uses the themeable --shadow-xl token
- global.css also drops the loading-dot/bounce animation tokens obsoleted by
  the status-indicator redesign (shared file; indicator landed separately)
Both shells hardcoded a dark interface (dark toolbar color scheme, white
toolbar icons, dark-only body color).

Adds a shared AppAppearance enum (System/Light/Dark) persisted via
@AppStorage, wired through preferredColorScheme on iOS and macOS, an iOS
Settings.bundle pane, a macOS Settings scene, an adaptive JavaChatBody color,
and appearance-aware toolbar styling.

- iOS: Settings.bundle multi-value pane plus AppAppearanceTests
- macOS: radio-group Settings scene; toolbar follows the selected scheme
The contact form sends mail per-request and surfaces send failures at request
time; SMTP reachability is not an application health dependency. An
unreachable SMTP host (e.g. dev environments without a mail server) otherwise
flips the aggregate health check to DOWN and trips monitors even though the
app is fine.

Disables the mail health indicator via management.health.mail.enabled=false.
…tions

oxlint's type-aware no-unsafe-type-assertion rule flagged two assertions
narrowing the unknown rejection to { cause?: unknown }.

Asserts the cause through vitest's toHaveProperty instead, which checks the
property on an unknown value without a cast and keeps the same identity
expectation.
…ss-tab sync

The adversarial review of the theme toggle surfaced defects the dev-server
dogfood could not catch: the inline boot script violated the backend's
script-src 'self' Content-Security-Policy, so the no-flash mechanism would
have died silently in production; an unguarded localStorage read could
prevent the SPA from mounting in hardened privacy modes; and Clerk's primary
button failed WCAG AA contrast in the light theme.

- Serve the pre-paint boot logic as a classic render-blocking asset
  (public/theme-boot.js) so it passes script-src 'self'; ignore the
  generated copy under static/
- Tolerate localStorage read/write/remove failures with logged warnings and
  session-only theming instead of a boot crash or torn toggle state
- Synchronize the preference across tabs via the storage event
- Add --color-accent-foreground and use it for Clerk's colorPrimaryForeground
  (5.4:1 on the light accent, up from 3.0:1)
- Return focus to the trigger when the mobile menu closes (WCAG 2.4.3) and
  drop the mismatched aria-haspopup from the disclosure
- Route the new-content glow and error-bubble tint through color-mix tokens
  so they track each theme's accent/error colors
- Guard boot-asset/composable constant drift with a parity test
With the header now carrying the settings menu, the web content could slip
under the home indicator and horizontal safe areas, making the composer and
header controls hard to reach on edge-to-edge devices.

- Move ignoresSafeArea(.container) to the shell level and include the bottom
  edge so layout insets come from the web document itself
- Stop the scroll view from double-applying the adjustment with
  contentInsetAdjustmentBehavior = .never
…to a menu

The header could not fit four nav tabs, a theme toggle, and auth controls
within a 360px row, and the contact form lived in a dialog that crawlers and
deep links could not reach. A single settings-and-pages menu now holds the
color-scheme choice and the Privacy/Contact links on every viewport, and
Contact becomes a real /contact route with SEO metadata and sitemap entry.

- Add HeaderMenu: hamburger trigger + panel with the System/Light/Dark
  color-scheme rows and crawlable Privacy/Contact anchors, closing on
  outside click and Escape with focus returned to the trigger
- Replace ContactFormDialog with ContactPage rendered at the new contact
  application view; Privacy/Contact links navigate through real hrefs
- Trim main navigation to the learning surfaces (Chat, Learn)
- Subsume ThemeToggle into HeaderMenu and delete the standalone component
- Forward /contact to the SPA in WebMvcConfig, add SeoController metadata,
  and list it in the sitemap
Spreading the auth controls and the menu trigger as separate flex
`space-between` children let the signed-in avatar drift across the
header on wide viewports. Wrap both in a single `.header-actions` flex
cluster so the avatar sits immediately left of the menu trigger at
every viewport width.

- Add `.header-actions` wrapper grouping auth controls and `HeaderMenu`
- Add `.header-actions` flex layout (align-items center, gap)
The App Store record and web app metadata used inconsistent spellings,
letting SEO snippets and install prompts show "Java Chat" while the
brand is JavaChat. Unify every user-facing metadata surface to the
JavaChat spelling and refresh descriptions to name the flagship
guided-lesson topics so search snippets carry the curriculum keywords.

- Rename title/site-name/application-name to JavaChat across index.html,
  site.webmanifest, and pageMetadata.ts
- Rewrite default and guided-lesson descriptions to cite JDK docs and
  Spring Boot, Quarkus, Kotlin, virtual threads, records, and pattern
  matching
- Update the global.css design-system banner to JavaChat
…avaChat

The privacy policy body still used the legacy "Java Chat" spelling
while the app metadata and header now read JavaChat, leaving the legal
page inconsistent with the rest of the surface. Update every body
reference and the matching test description to JavaChat.

- Replace "Java Chat" with JavaChat throughout PrivacyPage.svelte copy
- Update PrivacyPage.test.ts description string to JavaChat
…ports

The header brand still read "Java Chat" and the nav sat in a
`space-between` flex row, so Chat/Learn were only centered when the
brand and action clusters happened to match widths. Rename the brand
to JavaChat and switch the header to a three-column grid so the nav
stays optically centered on mobile, tablet, and desktop.
…t" to JavaChat

The backend-served site.webmanifest still carried the legacy "Java
Chat" name while the frontend copy and the rest of the app metadata
now use JavaChat, leaving the installed PWA's name inconsistent with
its source of truth. Match the frontend rebrand so both copies agree.

- Rename name and short_name from "Java Chat" to JavaChat in
  src/main/resources/static/site.webmanifest
Copilot AI review requested due to automatic review settings August 2, 2026 00:11

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 228 files, which is 128 over the limit of 100.

To get a review, narrow the scope:
• coderabbit review --committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 24a7f7ca-ba7b-490c-ac06-16c63e1db206

📥 Commits

Reviewing files that changed from the base of the PR and between 891ca5d and 7fd693c.

⛔ Files ignored due to path filters (12)
  • mobile/desktopApp/JavaChatDesktop/Assets.xcassets/AppIcon.appiconset/AppIcon-128.png is excluded by !**/*.png, !**/*.png
  • mobile/desktopApp/JavaChatDesktop/Assets.xcassets/AppIcon.appiconset/AppIcon-128@2x.png is excluded by !**/*.png, !**/*.png
  • mobile/desktopApp/JavaChatDesktop/Assets.xcassets/AppIcon.appiconset/AppIcon-16.png is excluded by !**/*.png, !**/*.png
  • mobile/desktopApp/JavaChatDesktop/Assets.xcassets/AppIcon.appiconset/AppIcon-16@2x.png is excluded by !**/*.png, !**/*.png
  • mobile/desktopApp/JavaChatDesktop/Assets.xcassets/AppIcon.appiconset/AppIcon-256.png is excluded by !**/*.png, !**/*.png
  • mobile/desktopApp/JavaChatDesktop/Assets.xcassets/AppIcon.appiconset/AppIcon-256@2x.png is excluded by !**/*.png, !**/*.png
  • mobile/desktopApp/JavaChatDesktop/Assets.xcassets/AppIcon.appiconset/AppIcon-32.png is excluded by !**/*.png, !**/*.png
  • mobile/desktopApp/JavaChatDesktop/Assets.xcassets/AppIcon.appiconset/AppIcon-32@2x.png is excluded by !**/*.png, !**/*.png
  • mobile/desktopApp/JavaChatDesktop/Assets.xcassets/AppIcon.appiconset/AppIcon-512.png is excluded by !**/*.png, !**/*.png
  • mobile/desktopApp/JavaChatDesktop/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png is excluded by !**/*.png, !**/*.png
  • mobile/iosApp/JavaChat.xcodeproj/project.xcworkspace/contents.xcworkspacedata is excluded by !**/*.xcworkspace/contents.xcworkspacedata
  • mobile/iosApp/JavaChat/Assets.xcassets/AppIcon.appiconset/AppIcon.png is excluded by !**/*.png, !**/*.png
📒 Files selected for processing (228)
  • .agents/skills/clerk-android/SKILL.md
  • .agents/skills/clerk-android/references/custom.md
  • .agents/skills/clerk-android/references/prebuilt.md
  • .agents/skills/clerk-backend-api/SKILL.md
  • .agents/skills/clerk-backend-api/evals/evals.json
  • .agents/skills/clerk-backend-api/scripts/api-specs-context.sh
  • .agents/skills/clerk-backend-api/scripts/execute-request.sh
  • .agents/skills/clerk-backend-api/scripts/extract-endpoint-detail.sh
  • .agents/skills/clerk-backend-api/scripts/extract-tag-endpoints.sh
  • .agents/skills/clerk-backend-api/scripts/extract-tags.js
  • .agents/skills/clerk-billing/SKILL.md
  • .agents/skills/clerk-billing/evals/evals.json
  • .agents/skills/clerk-billing/references/b2b-patterns.md
  • .agents/skills/clerk-billing/references/b2c-patterns.md
  • .agents/skills/clerk-billing/references/billing-components.md
  • .agents/skills/clerk-billing/references/billing-webhooks.md
  • .agents/skills/clerk-cli/SKILL.md
  • .agents/skills/clerk-cli/references/agent-mode.md
  • .agents/skills/clerk-cli/references/auth.md
  • .agents/skills/clerk-cli/references/recipes.md
  • .agents/skills/clerk-custom-ui/SKILL.md
  • .agents/skills/clerk-custom-ui/core-2/custom-sign-in.md
  • .agents/skills/clerk-custom-ui/core-2/custom-sign-up.md
  • .agents/skills/clerk-custom-ui/core-3/custom-sign-in.md
  • .agents/skills/clerk-custom-ui/core-3/custom-sign-up.md
  • .agents/skills/clerk-custom-ui/core-3/show-component.md
  • .agents/skills/clerk-expo/SKILL.md
  • .agents/skills/clerk-expo/evals/evals.json
  • .agents/skills/clerk-expo/references/custom-flows.md
  • .agents/skills/clerk-expo/references/prebuilt-components.md
  • .agents/skills/clerk-expo/references/protected-routes.md
  • .agents/skills/clerk-expo/references/recipes.md
  • .agents/skills/clerk-expo/references/setup.md
  • .agents/skills/clerk-expo/references/sso-and-native-auth.md
  • .agents/skills/clerk-nextjs-patterns/SKILL.md
  • .agents/skills/clerk-nextjs-patterns/evals/evals.json
  • .agents/skills/clerk-nextjs-patterns/references/api-routes.md
  • .agents/skills/clerk-nextjs-patterns/references/caching-auth.md
  • .agents/skills/clerk-nextjs-patterns/references/middleware-strategies.md
  • .agents/skills/clerk-nextjs-patterns/references/server-actions.md
  • .agents/skills/clerk-nextjs-patterns/references/server-vs-client.md
  • .agents/skills/clerk-nextjs-patterns/templates/nextjs-basic-auth/app/layout.tsx
  • .agents/skills/clerk-nextjs-patterns/templates/nextjs-basic-auth/app/page.tsx
  • .agents/skills/clerk-nextjs-patterns/templates/nextjs-basic-auth/package.json
  • .agents/skills/clerk-nextjs-patterns/templates/nextjs-basic-auth/proxy.ts
  • .agents/skills/clerk-nextjs-patterns/templates/nextjs-basic-auth/tsconfig.json
  • .agents/skills/clerk-orgs/SKILL.md
  • .agents/skills/clerk-orgs/evals/evals.json
  • .agents/skills/clerk-orgs/references/enterprise-sso.md
  • .agents/skills/clerk-orgs/references/invitations.md
  • .agents/skills/clerk-orgs/references/nextjs-patterns.md
  • .agents/skills/clerk-orgs/references/roles-permissions.md
  • .agents/skills/clerk-react-patterns/SKILL.md
  • .agents/skills/clerk-react-patterns/evals/evals.json
  • .agents/skills/clerk-react-patterns/references/custom-flows.md
  • .agents/skills/clerk-react-patterns/references/hooks.md
  • .agents/skills/clerk-react-patterns/references/protected-routes.md
  • .agents/skills/clerk-react-patterns/references/router-integration.md
  • .agents/skills/clerk-react-patterns/templates/react-basic-auth/index.html
  • .agents/skills/clerk-react-patterns/templates/react-basic-auth/package.json
  • .agents/skills/clerk-react-patterns/templates/react-basic-auth/src/App.tsx
  • .agents/skills/clerk-react-patterns/templates/react-basic-auth/src/main.tsx
  • .agents/skills/clerk-react-patterns/templates/react-basic-auth/tsconfig.json
  • .agents/skills/clerk-react-patterns/templates/react-basic-auth/vite.config.ts
  • .agents/skills/clerk-react-router-patterns/SKILL.md
  • .agents/skills/clerk-react-router-patterns/evals/evals.json
  • .agents/skills/clerk-react-router-patterns/references/loaders-actions.md
  • .agents/skills/clerk-react-router-patterns/references/protected-routes.md
  • .agents/skills/clerk-react-router-patterns/references/ssr-auth.md
  • .agents/skills/clerk-react-router-patterns/templates/react-router-basic-auth/app/app.css
  • .agents/skills/clerk-react-router-patterns/templates/react-router-basic-auth/app/root.tsx
  • .agents/skills/clerk-react-router-patterns/templates/react-router-basic-auth/app/routes.ts
  • .agents/skills/clerk-react-router-patterns/templates/react-router-basic-auth/app/routes/home.tsx
  • .agents/skills/clerk-react-router-patterns/templates/react-router-basic-auth/package.json
  • .agents/skills/clerk-react-router-patterns/templates/react-router-basic-auth/react-router.config.ts
  • .agents/skills/clerk-react-router-patterns/templates/react-router-basic-auth/vite.config.ts
  • .agents/skills/clerk-setup/SKILL.md
  • .agents/skills/clerk-setup/evals/evals.json
  • .agents/skills/clerk-swift/SKILL.md
  • .agents/skills/clerk-swift/references/custom.md
  • .agents/skills/clerk-swift/references/prebuilt.md
  • .agents/skills/clerk-tanstack-patterns/SKILL.md
  • .agents/skills/clerk-tanstack-patterns/evals/evals.json
  • .agents/skills/clerk-tanstack-patterns/references/loaders.md
  • .agents/skills/clerk-tanstack-patterns/references/router-guards.md
  • .agents/skills/clerk-tanstack-patterns/references/server-functions.md
  • .agents/skills/clerk-tanstack-patterns/references/vinxi-server.md
  • .agents/skills/clerk-tanstack-patterns/templates/tanstack-basic-auth/package.json
  • .agents/skills/clerk-tanstack-patterns/templates/tanstack-basic-auth/src/router.tsx
  • .agents/skills/clerk-tanstack-patterns/templates/tanstack-basic-auth/src/routes/__root.tsx
  • .agents/skills/clerk-tanstack-patterns/templates/tanstack-basic-auth/src/routes/index.tsx
  • .agents/skills/clerk-tanstack-patterns/templates/tanstack-basic-auth/src/start.ts
  • .agents/skills/clerk-tanstack-patterns/templates/tanstack-basic-auth/tsconfig.json
  • .agents/skills/clerk-tanstack-patterns/templates/tanstack-basic-auth/vite.config.ts
  • .agents/skills/clerk-testing/SKILL.md
  • .agents/skills/clerk-vue-patterns/SKILL.md
  • .agents/skills/clerk-vue-patterns/evals/evals.json
  • .agents/skills/clerk-vue-patterns/references/composables.md
  • .agents/skills/clerk-vue-patterns/references/pinia-integration.md
  • .agents/skills/clerk-vue-patterns/references/vue-router-guards.md
  • .agents/skills/clerk-vue-patterns/templates/vue-basic-auth/index.html
  • .agents/skills/clerk-vue-patterns/templates/vue-basic-auth/package.json
  • .agents/skills/clerk-vue-patterns/templates/vue-basic-auth/src/App.vue
  • .agents/skills/clerk-vue-patterns/templates/vue-basic-auth/src/main.ts
  • .agents/skills/clerk-vue-patterns/templates/vue-basic-auth/src/style.css
  • .agents/skills/clerk-vue-patterns/templates/vue-basic-auth/vite.config.ts
  • .agents/skills/clerk-webhooks/SKILL.md
  • .agents/skills/clerk-webhooks/evals/evals.json
  • .agents/skills/clerk-webhooks/references/frameworks.md
  • .agents/skills/clerk/SKILL.md
  • .gitignore
  • __tests__/mobile/desktopApp/PageLinkActionsTests.swift
  • __tests__/mobile/desktopApp/WebNavigationPolicyTests.swift
  • __tests__/mobile/iosApp/AppAppearanceTests.swift
  • __tests__/mobile/iosApp/OAuthPolicyTests.swift
  • __tests__/mobile/iosApp/ShellConfigurationTests.swift
  • __tests__/mobile/iosApp/WebNavigationPolicyTests.swift
  • __tests__/mobile/iosApp/WebShellStateTests.swift
  • build.gradle.kts
  • frontend/index.html
  • frontend/public/site.webmanifest
  • frontend/public/theme-boot.js
  • frontend/src/App.svelte
  • frontend/src/App.test.ts
  • frontend/src/lib/components/ChatView.svelte
  • frontend/src/lib/components/ChatView.test.ts
  • frontend/src/lib/components/CitationPanel.svelte
  • frontend/src/lib/components/ContactPage.svelte
  • frontend/src/lib/components/ContactPage.test.ts
  • frontend/src/lib/components/Header.svelte
  • frontend/src/lib/components/Header.test.ts
  • frontend/src/lib/components/HeaderMenu.svelte
  • frontend/src/lib/components/HeaderMenu.test.ts
  • frontend/src/lib/components/LearnView.svelte
  • frontend/src/lib/components/LearnView.test.ts
  • frontend/src/lib/components/MessageBubble.svelte
  • frontend/src/lib/components/NewContentIndicator.svelte
  • frontend/src/lib/components/PrivacyPage.svelte
  • frontend/src/lib/components/PrivacyPage.test.ts
  • frontend/src/lib/components/ThinkingIndicator.svelte
  • frontend/src/lib/components/ToastContainer.svelte
  • frontend/src/lib/components/WelcomeScreen.svelte
  • frontend/src/lib/composables/clerkAuthentication.svelte.ts
  • frontend/src/lib/composables/themePreference.svelte.test.ts
  • frontend/src/lib/composables/themePreference.svelte.ts
  • frontend/src/lib/services/contact.test.ts
  • frontend/src/lib/services/contact.ts
  • frontend/src/lib/services/pageMetadata.test.ts
  • frontend/src/lib/services/pageMetadata.ts
  • frontend/src/lib/services/sse.test.ts
  • frontend/src/lib/validation/schemas.ts
  • frontend/src/main.ts
  • frontend/src/styles/global.css
  • frontend/src/vite-env.d.ts
  • gradle/libs.versions.toml
  • mobile/.gitignore
  • mobile/Makefile
  • mobile/README.md
  • mobile/Shared/AppAppearance.swift
  • mobile/desktopApp/ExportOptions-AppStore.plist
  • mobile/desktopApp/ExportOptions-DeveloperID.plist
  • mobile/desktopApp/JavaChatDesktop.xcodeproj/project.pbxproj
  • mobile/desktopApp/JavaChatDesktop.xcodeproj/xcshareddata/xcschemes/JavaChatDesktop.xcscheme
  • mobile/desktopApp/JavaChatDesktop/Assets.xcassets/AppIcon.appiconset/Contents.json
  • mobile/desktopApp/JavaChatDesktop/Assets.xcassets/Contents.json
  • mobile/desktopApp/JavaChatDesktop/Info.plist
  • mobile/desktopApp/JavaChatDesktop/JavaChatDesktop.entitlements
  • mobile/desktopApp/JavaChatDesktop/JavaChatDesktopApp.swift
  • mobile/desktopApp/JavaChatDesktop/JavaChatProductionURL.swift
  • mobile/desktopApp/JavaChatDesktop/PageLinkActions.swift
  • mobile/desktopApp/JavaChatDesktop/WebNavigationPolicy.swift
  • mobile/desktopApp/JavaChatDesktop/WebShellView.swift
  • mobile/desktopApp/JavaChatDesktop/WebViewContainer.swift
  • mobile/iosApp/.gitignore
  • mobile/iosApp/ExportOptions-AppStore.plist
  • mobile/iosApp/JavaChat.xcodeproj/project.pbxproj
  • mobile/iosApp/JavaChat.xcodeproj/xcshareddata/xcschemes/JavaChat.xcscheme
  • mobile/iosApp/JavaChat/Assets.xcassets/AppIcon.appiconset/Contents.json
  • mobile/iosApp/JavaChat/Assets.xcassets/Contents.json
  • mobile/iosApp/JavaChat/Assets.xcassets/JavaChatBody.colorset/Contents.json
  • mobile/iosApp/JavaChat/Assets.xcassets/JavaChatSystemBar.colorset/Contents.json
  • mobile/iosApp/JavaChat/Info.plist
  • mobile/iosApp/JavaChat/JavaChatApp.swift
  • mobile/iosApp/JavaChat/JavaChatWebView.swift
  • mobile/iosApp/JavaChat/OAuthPolicy.swift
  • mobile/iosApp/JavaChat/Settings.bundle/Root.plist
  • mobile/iosApp/JavaChat/ShellConfiguration.swift
  • mobile/iosApp/JavaChat/WebNavigationPolicy.swift
  • mobile/iosApp/JavaChat/WebShellState.swift
  • mobile/iosApp/JavaChat/WebViewCoordinator.swift
  • src/main/java/com/williamcallahan/javachat/application/contact/ContactRateLimitExceededException.java
  • src/main/java/com/williamcallahan/javachat/application/contact/ContactSubmission.java
  • src/main/java/com/williamcallahan/javachat/application/contact/ContactSubmissionUseCase.java
  • src/main/java/com/williamcallahan/javachat/config/AppProperties.java
  • src/main/java/com/williamcallahan/javachat/config/RequiredMailCredentialValidation.java
  • src/main/java/com/williamcallahan/javachat/config/WebMvcConfig.java
  • src/main/java/com/williamcallahan/javachat/domain/errors/ApiResponse.java
  • src/main/java/com/williamcallahan/javachat/domain/errors/ContactMessageAcknowledgement.java
  • src/main/java/com/williamcallahan/javachat/web/ContactController.java
  • src/main/java/com/williamcallahan/javachat/web/ContactMessageRequest.java
  • src/main/java/com/williamcallahan/javachat/web/SeoController.java
  • src/main/java/com/williamcallahan/javachat/web/SitemapController.java
  • src/main/resources/application.properties
  • src/main/resources/guided/lessons/arrays.md
  • src/main/resources/guided/lessons/building-rest-apis-with-spring-boot.md
  • src/main/resources/guided/lessons/choosing-a-jvm-language.md
  • src/main/resources/guided/lessons/classes-and-objects.md
  • src/main/resources/guided/lessons/clojure-on-the-jvm.md
  • src/main/resources/guided/lessons/conditionals.md
  • src/main/resources/guided/lessons/data-access-and-testing-in-spring-boot.md
  • src/main/resources/guided/lessons/dependency-injection-and-configuration.md
  • src/main/resources/guided/lessons/groovy-on-the-jvm.md
  • src/main/resources/guided/lessons/introduction-to-java.md
  • src/main/resources/guided/lessons/loops.md
  • src/main/resources/guided/lessons/methods.md
  • src/main/resources/guided/lessons/quarkus-fundamentals.md
  • src/main/resources/guided/lessons/recursion.md
  • src/main/resources/guided/lessons/spring-boot-fundamentals.md
  • src/main/resources/guided/lessons/spring-boot-vs-quarkus.md
  • src/main/resources/guided/lessons/strings.md
  • src/main/resources/guided/lessons/variables-and-types.md
  • src/main/resources/static/site.webmanifest
  • src/test/java/com/williamcallahan/javachat/config/RequiredMailCredentialValidationTest.java
  • src/test/java/com/williamcallahan/javachat/service/GuidedLearningServiceCitationTest.java
  • src/test/java/com/williamcallahan/javachat/web/BrowserErrorResponseIntegrationTest.java
  • src/test/java/com/williamcallahan/javachat/web/ContactControllerTest.java
  • src/test/java/com/williamcallahan/javachat/web/SeoControllerTest.java
  • src/test/java/com/williamcallahan/javachat/web/SitemapControllerTest.java

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@WilliamAGH
WilliamAGH merged commit 9e9e343 into main Aug 2, 2026
6 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7fd693caff

ℹ️ 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".

Comment thread src/main/resources/application.properties
Comment thread src/main/java/com/williamcallahan/javachat/web/SeoController.java
Comment thread frontend/src/lib/components/HeaderMenu.svelte
WilliamAGH added a commit that referenced this pull request Aug 2, 2026
Concurrent submissions from one IP could each observe the counter below the
hourly allowance and all send before any increment landed, exceeding the
advertised per-IP limit by an arbitrary burst. Reserve one slot with a single
atomic increment before sending and release it when the reservation exceeds
the allowance or delivery fails, so the check and increment cannot interleave.

Flagged by Codex review on PR #161.
WilliamAGH added a commit that referenced this pull request Aug 2, 2026
The ${SPRING_MAIL_HOST:...}/${SPRING_MAIL_PORT:...} placeholders declared a
new env-var-driven settings contract; repository policy [EV1c] keeps non-secret
defaults in Spring property files. Plain defaults suffice because Spring
relaxed binding already maps SPRING_MAIL_HOST/SPRING_MAIL_PORT onto
spring.mail.host/spring.mail.port, the same mechanism the credentials use.

Flagged by Codex review on PR #161.
WilliamAGH added a commit that referenced this pull request Aug 2, 2026
SeoController still emitted the old "Java Chat" titles, descriptions, and
JSON-LD name, so crawlers and social-preview clients that never run the SPA
saw stale branding. Mirror the frontend pageMetadata catalog so the HTTP
boundary and the SPA emit identical metadata for every routed path.

Flagged by Codex review on PR #161.
WilliamAGH added a commit that referenced this pull request Aug 2, 2026
The unconditional preventDefault() suppressed the anchor's new-tab behavior,
so Command/Ctrl-clicking Privacy or Contact replaced the current SPA view
instead of opening a new tab. Intercept only unmodified primary-button clicks
so the real hrefs retain standard browser navigation semantics.

Flagged by Codex review on PR #161.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants