feat(frontend): redesign landing page with light-themed right panels#1807
feat(frontend): redesign landing page with light-themed right panels#1807jaseemjaskp wants to merge 34 commits intomainfrom
Conversation
Migrate the Unstract frontend build system from Create React App (react-scripts 5.0.1) to Vite 6.0.5 for improved development experience and build performance. Key Changes: - Replace react-scripts with Vite 6.0.5 and @vitejs/plugin-react 4.3.4 - Move index.html from public/ to root directory - Replace setupProxy.js with Vite proxy configuration - Update environment variables from REACT_APP_* to VITE_* prefix - Configure esbuild to handle JSX in .js files - Add comprehensive migration documentation Build Optimizations: - Manual chunk splitting for react, antd, and pdf vendors - Dependency pre-bundling for faster cold starts - HMR with polling enabled for Docker volume compatibility Documentation: - Add VITE_MIGRATION.md with comprehensive migration guide - Update README.md with Vite-specific configuration and commands - Document environment variable migration and troubleshooting Performance Benefits: - Development server startup: 10-30s → 1-2s - Hot Module Replacement: 2-5s → <1s - Production build time: 60-120s → 30-60s 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…ports The codebase extensively uses SVG imports with ?react query syntax (39 imports in src/assets/index.js). vite-plugin-svgr is required to transform these imports into React components. Changes: - Add vite-plugin-svgr@^4.5.0 to devDependencies - Restore svgr plugin in vite.config.js - Add vite-plugin-svgr type references in vite-env.d.ts - Remove conflicting public/index.html from CRA migration Fixes white screen issue caused by SVG components failing to render. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…ntation The README documentation showed incorrect HMR clientPort configuration using process.env instead of the env variable from loadEnv. Changes: - Update clientPort example to use env.WDS_SOCKET_PORT (not process.env) - Change from falsy coalescing to explicit conditional check - Ensure documentation matches vite.config.js implementation Addresses CodeRabbit feedback on PR #1607. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…mentation The README proxy configuration example incorrectly used process.env instead of the env variable from loadEnv. Changes: - Update proxy target to use env.VITE_BACKEND_URL (not process.env) - Ensure documentation matches vite.config.js implementation - Maintain consistency across all Vite configuration examples Addresses CodeRabbit critical feedback on PR #1607. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
# Conflicts: # docker/dockerfiles/frontend.Dockerfile # frontend/package-lock.json
…e compatibility Converted all require() calls in frontend/src to use try/catch with top-level await import() to support Vite's native ES module system. Plugin imports remain optional via try/catch to preserve OSS compatibility where plugin files are not present. Special cases: - Pipelines.jsx: static import (non-plugin, always available) - DsSettingsCard.jsx: async IIFE inside useEffect
- Make selectedPrompts optional in CombinedOutput since ToolsMain does not pass it and the component handles undefined - Change llmProfiles PropType from string to array in JsonViewBody to match the actual data type
- Change coverageCountData from PropTypes.object to PropTypes.array in PromptCardWrapper, PromptCard, and PromptCardItems - Fix PropTypes.text (undefined) to PropTypes.string for promptKey and promptText in PromptCardItems - Change highlightData to PropTypes.oneOfType([object, array]) in DisplayPromptResult - Change apiService from PropTypes.func to PropTypes.object in ManageKeys - Change connMode from required to optional in ConfigureConnectorModal - Change type from required to optional in ListOfSources
Add optionalPluginImports() Rollup plugin to vite.config.js that resolves missing plugin paths to an empty module at build time instead of failing. This allows the existing try/catch dynamic import pattern to work during production builds when plugin files are absent.
The codebase uses top-level await for optional plugin imports, which requires es2022+. Vite's default target (es2020) does not support this, causing the build to fail.
- Switch Dockerfile base image from node:20-alpine to oven/bun:1-alpine - Replace npm install/run commands with bun equivalents in Dockerfile - Remove engines.npm constraint, update engines.node to >=18.0.0 - Replace npm run with bun run in lint:all script - Delete package-lock.json, add bun.lock
…1771) * feat: replace ESLint and Prettier with Biome for frontend linting and formatting - Add @biomejs/biome v2.3.13 as dev dependency - Create biome.json with migrated ESLint rules and formatter config - Update package.json scripts (lint, format, check) to use Biome - Remove ESLint and Prettier dependencies and .eslintrc.json - Add Biome pre-commit hook to .pre-commit-config.yaml - Fix undeclared sessionDetails variable in HeaderTitle.jsx - Fix Error() and Array() to use new keyword - Fix prismjs import order in CombinedOutput.jsx - Reformat all frontend source files with Biome * fix: skip biome-check in pre-commit CI and add robust flags - Add biome-check to ci.skip list since pre-commit.ci lacks reliable npm registry access for language:system hooks - Add --files-ignore-unknown=true and --no-errors-on-unmatched flags to the biome-check hook entry for robustness
- Update Node.js requirement to >= 18.0.0 - Replace ESLint/Prettier with Biome in editor setup - Update linting/formatting commands for Biome - Add biome.json to project structure - Add Biome documentation links to learning resources
- Upgrade vite from ^6.0.5 to ^7.0.0 (installed 7.3.1) - Upgrade vitest from ^2.1.8 to ^3.2.0 (installed 3.2.4) - Upgrade @vitejs/plugin-react from ^4.3.4 to ^4.4.0 - Update Node.js engine requirement from >=18.0.0 to >=20.19.0 - Modernize __dirname to import.meta.dirname in vite.config.js
- Update Vite badge from 6.x to 7.x - Update Node.js requirement from 18.0.0 to 20.19.0
- Update prerequisites to list Bun as primary package manager - Replace all npm commands with bun equivalents - Update install, dev, build, test, and lint commands
- Add Vite 7 upgrade section with version updates and changes - Update all npm commands to use bun - Update package versions to current (Vite 7.3.1, Vitest 3.2.4) - Document Node.js 20.19+ requirement
…mode When visiting /verticals/* in incognito, selectedProduct from localStorage is null, causing isUnstract to be true and showing the Unstract logo and setup incomplete banner instead of the API Hub layout. Add URL-based fallback to detect verticals route and set the effective product accordingly. UN-3219
Redesign all three landing page variants (default, LLM Whisperer, Unstract) with new light-themed right panels featuring compliance badges, stats cards, testimonials with gradient borders, and provider logos. Update left panel spacing to match Figma design specifications.
Summary by CodeRabbitRelease Notes
WalkthroughLogin styling system expanded with themed variants, ProductContentLayout component refactored to hardcoded content with new exports, and vite configuration formatting normalized for consistency. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Tip Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs). 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 |
Signed-off-by: Jaseem Jas <89440144+jaseemjaskp@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (3)
frontend/src/components/log-in/Login.css (2)
282-289: Consider scoped selectors instead of!importantfor Ant Design overrides.Multiple rules use
!importantto override Ant Design defaults. While this works, it can lead to specificity escalation over time. Wrapping with a more specific selector (e.g.,.llm-whisperer-content .llm-headline) may achieve the same result without!important.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/log-in/Login.css` around lines 282 - 289, Replace the use of "!important" on the .llm-headline rule by increasing selector specificity instead: wrap or prefix the rule with a parent scope such as .llm-whisperer-content .llm-headline (or another component root class) and remove all "!important" tokens so the styles (font-size, font-weight, line-height, text-align, color, margin-bottom) naturally override Ant Design defaults via specificity rather than force. Ensure the selector you choose exists in the component markup and apply the same scoping approach to any other rules in this file using "!important".
998-1010: Consider consolidating duplicate CSS rules across theme variants.These
.llm-light-stat-cardand.unstract-light-stat-cardblocks are identical. Similar duplication exists for:
*-contentcontainers*-testimonialsections*-badges-rowrules*-stat-icon,*-stat-value,*-stat-labelrulesA shared base class (e.g.,
.light-stat-card) with theme-specific modifiers would reduce maintenance burden and file size.♻️ Example consolidation approach
/* Shared base styles */ .light-stat-card { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 20px 12px 16px; border-radius: 10px; border: 1px solid `#e8edf2`; background: `#fff`; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); box-sizing: border-box; } /* Then use: class="light-stat-card llm-light-stat-card" for any variant overrides */Also applies to: 1222-1234
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/log-in/Login.css` around lines 998 - 1010, The CSS duplicates multiple theme variants (e.g., .llm-light-stat-card and .unstract-light-stat-card, and similar pairs for *-content, *-testimonial, *-badges-row, *-stat-icon, *-stat-value, *-stat-label) — create shared base classes (e.g., .light-stat-card, .light-stat-content, .light-testimonial, .light-badges-row, .light-stat-icon, .light-stat-value, .light-stat-label) containing the common rules and move variant-specific overrides into modifier classes (keep .llm-* and .unstract-* only for differences); update the markup to apply both the base class and the variant modifier (e.g., class="light-stat-card llm-light-stat-card") so styles are consolidated and duplicate blocks removed.frontend/src/components/navigations/top-nav-bar/TopNavBar.jsx (1)
177-183: LGTM — URL-based fallback logic is correct.The fallback mechanism properly handles incognito/unauthenticated users on verticals routes. The boolean derivations are logically sound.
Minor readability suggestion: the double-negation in
isUnstractcould be simplified:♻️ Optional: simplify isUnstract expression
-const isUnstract = !(effectiveProduct && effectiveProduct !== "unstract"); +const isUnstract = !effectiveProduct || effectiveProduct === "unstract";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/navigations/top-nav-bar/TopNavBar.jsx` around lines 177 - 183, The current isUnstract uses a confusing double-negation; simplify it by deriving isUnstract directly from effectiveProduct so it is true when effectiveProduct is falsy or equals "unstract" (i.e., replace the !(effectiveProduct && effectiveProduct !== "unstract") logic with an explicit check that covers both cases) — update the const isUnstract declaration in TopNavBar.jsx where effectiveProduct is computed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/src/components/log-in/Login.css`:
- Around line 21-27: The .login-right-section currently uses overflow: hidden
and height: 100vh causing content clipping on small viewports; update the CSS to
add a mobile media query (e.g., for max-width: 768px or Ant Design xs
breakpoint) that changes .login-right-section to height: auto (or remove fixed
100vh), set overflow-y: auto, and/or set width: 100% so the section stacks
vertically with the left column (or switch the parent layout to flex-direction:
column); ensure ProductContentLayout content can scroll/expand on small screens
to avoid clipping.
In `@frontend/src/components/log-in/ProductContentLayout.jsx`:
- Around line 42-47: There are duplicate constant arrays; remove the redundant
definitions (LLM_PROVIDERS, TRUSTED_BY_LOGOS, UNSTRACT_COMPLIANCE_BADGES) and
update all usages to reference the existing defaults (DEFAULT_LLM_PROVIDERS,
DEFAULT_TRUSTED_LOGOS, DEFAULT_COMPLIANCE_BADGES) so the component
(ProductContentLayout) and any other consumers import/use the single source of
truth—search for and replace references to LLM_PROVIDERS →
DEFAULT_LLM_PROVIDERS, TRUSTED_BY_LOGOS → DEFAULT_TRUSTED_LOGOS, and
UNSTRACT_COMPLIANCE_BADGES → DEFAULT_COMPLIANCE_BADGES, then delete the
now-unused duplicate constant declarations and remove any related unused
imports.
- Around line 2-25: ProductContentLayout.jsx currently imports 24 asset modules
(e.g., anthropicLogo, azureGptLogo, llmG2Badge, gregCrisciPhoto,
christopherVarnerPhoto, unstractQuoteIcon, etc.) from non-existent
../../plugins/assets/... paths which breaks the build; fix the imports in
ProductContentLayout.jsx by either (A) replacing each ../../plugins/assets/...
import with the correct existing paths under frontend/src/assets/ for the
matching files, or (B) removing/conditionally loading unused assets and
substituting available placeholders, ensuring all referenced symbols
(anthropicLogo, openAiLogo, statIconAccuracy, unstractStatIconEfficiency, etc.)
resolve to actual files or exports before committing.
---
Nitpick comments:
In `@frontend/src/components/log-in/Login.css`:
- Around line 282-289: Replace the use of "!important" on the .llm-headline rule
by increasing selector specificity instead: wrap or prefix the rule with a
parent scope such as .llm-whisperer-content .llm-headline (or another component
root class) and remove all "!important" tokens so the styles (font-size,
font-weight, line-height, text-align, color, margin-bottom) naturally override
Ant Design defaults via specificity rather than force. Ensure the selector you
choose exists in the component markup and apply the same scoping approach to any
other rules in this file using "!important".
- Around line 998-1010: The CSS duplicates multiple theme variants (e.g.,
.llm-light-stat-card and .unstract-light-stat-card, and similar pairs for
*-content, *-testimonial, *-badges-row, *-stat-icon, *-stat-value, *-stat-label)
— create shared base classes (e.g., .light-stat-card, .light-stat-content,
.light-testimonial, .light-badges-row, .light-stat-icon, .light-stat-value,
.light-stat-label) containing the common rules and move variant-specific
overrides into modifier classes (keep .llm-* and .unstract-* only for
differences); update the markup to apply both the base class and the variant
modifier (e.g., class="light-stat-card llm-light-stat-card") so styles are
consolidated and duplicate blocks removed.
In `@frontend/src/components/navigations/top-nav-bar/TopNavBar.jsx`:
- Around line 177-183: The current isUnstract uses a confusing double-negation;
simplify it by deriving isUnstract directly from effectiveProduct so it is true
when effectiveProduct is falsy or equals "unstract" (i.e., replace the
!(effectiveProduct && effectiveProduct !== "unstract") logic with an explicit
check that covers both cases) — update the const isUnstract declaration in
TopNavBar.jsx where effectiveProduct is computed.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to Reviews > Disable Cache setting
Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting
📒 Files selected for processing (3)
frontend/src/components/log-in/Login.cssfrontend/src/components/log-in/ProductContentLayout.jsxfrontend/src/components/navigations/top-nav-bar/TopNavBar.jsx
| overflow: hidden; | ||
| background-color: #eceff3; | ||
| display: flex; | ||
| justify-content: center; | ||
| align-items: center; | ||
| position: relative; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's find and examine the Login.css file
find . -name "Login.css" -type f | head -5Repository: Zipstack/unstract
Length of output: 103
🏁 Script executed:
# Check file size and read the relevant section
wc -l frontend/src/components/log-in/Login.cssRepository: Zipstack/unstract
Length of output: 106
🏁 Script executed:
# Read around lines 1-50 to see the .login-right-section and its full context
sed -n '1,50p' frontend/src/components/log-in/Login.cssRepository: Zipstack/unstract
Length of output: 961
🏁 Script executed:
# Look for all instances of login-right-section and its parent to understand the layout
rg -n 'login-right-section|login-container|overflow' frontend/src/components/log-in/Login.css | head -30Repository: Zipstack/unstract
Length of output: 707
🏁 Script executed:
# Check for media queries in the file
rg -n '@media' frontend/src/components/log-in/Login.cssRepository: Zipstack/unstract
Length of output: 43
🏁 Script executed:
# Let's see the full structure - read sections around the login-right-section
sed -n '1,100p' frontend/src/components/log-in/Login.css | cat -nRepository: Zipstack/unstract
Length of output: 2719
🏁 Script executed:
# Find the Login component file
find . -path "*/log-in/Login.*" -type f | grep -E "\.(tsx?|jsx?)$"Repository: Zipstack/unstract
Length of output: 103
🏁 Script executed:
# Check the Login component structure
head -100 frontend/src/components/log-in/Login.tsxRepository: Zipstack/unstract
Length of output: 160
🏁 Script executed:
# Look for login-right-section usage in the component
rg -n 'login-right-section' frontend/src/components/log-in/Login.tsx -B 5 -A 10 | head -40Repository: Zipstack/unstract
Length of output: 141
🏁 Script executed:
# Check if there are any responsive styles or tailwind classes in the component
rg -n 'responsive|mobile|md:|sm:|lg:' frontend/src/components/log-in/Login.tsx | head -20Repository: Zipstack/unstract
Length of output: 141
🏁 Script executed:
# Check the component file size and examine the right section rendering
wc -l frontend/src/components/log-in/Login.tsx
sed -n '1,200p' frontend/src/components/log-in/Login.tsx | tail -100Repository: Zipstack/unstract
Length of output: 216
🏁 Script executed:
# Read the Login.jsx component
head -150 frontend/src/components/log-in/Login.jsxRepository: Zipstack/unstract
Length of output: 1962
🏁 Script executed:
# Search for login-right-section in the JSX file
rg -n 'login-right-section' frontend/src/components/log-in/Login.jsx -B 5 -A 15Repository: Zipstack/unstract
Length of output: 424
🏁 Script executed:
# Check the full file size and structure
wc -l frontend/src/components/log-in/Login.jsxRepository: Zipstack/unstract
Length of output: 104
🏁 Script executed:
# Look for responsive design patterns in the component
rg -n 'window\.innerWidth|useWindowSize|@media|media.*query|responsive' frontend/src/components/log-in/Login.jsxRepository: Zipstack/unstract
Length of output: 43
🏁 Script executed:
# Check what's rendered in the right section
sed -n '1,300p' frontend/src/components/log-in/Login.jsx | grep -A 20 'login-right-section'Repository: Zipstack/unstract
Length of output: 259
🏁 Script executed:
# Find ProductContentLayout
find . -name "ProductContentLayout*" -type fRepository: Zipstack/unstract
Length of output: 118
🏁 Script executed:
# Examine ProductContentLayout
head -80 frontend/src/components/log-in/ProductContentLayout.jsxRepository: Zipstack/unstract
Length of output: 3919
🏁 Script executed:
# Check if there are CSS overrides for responsive behavior
rg -n 'xs=|sm=|md=|lg=' frontend/src/components/log-in/Login.jsxRepository: Zipstack/unstract
Length of output: 197
🏁 Script executed:
# Look for any media queries or responsive styles targeting login sections
rg -n '@media' frontend/src/components/log-in/ProductContentLayout.css 2>/dev/null || echo "No CSS file found"Repository: Zipstack/unstract
Length of output: 78
🏁 Script executed:
# Check what CSS files exist in log-in directory
ls -la frontend/src/components/log-in/Repository: Zipstack/unstract
Length of output: 374
Add responsive styles to prevent content clipping on smaller viewports.
The .login-right-section uses overflow: hidden with a fixed height: 100vh, but has no media queries to adapt for smaller screens. On mobile (where Ant Design's xs={24} makes the section full-width), the tall ProductContentLayout content will be clipped and inaccessible.
Consider adding a media query to adjust the layout on smaller breakpoints:
- Increase the height constraint or use
overflow-y: autoon mobile - Or restructure the layout to stack sections vertically on small screens
Current CSS
.login-right-section {
width: 50%;
height: 100vh;
overflow: hidden;
/* ... */
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/components/log-in/Login.css` around lines 21 - 27, The
.login-right-section currently uses overflow: hidden and height: 100vh causing
content clipping on small viewports; update the CSS to add a mobile media query
(e.g., for max-width: 768px or Ant Design xs breakpoint) that changes
.login-right-section to height: auto (or remove fixed 100vh), set overflow-y:
auto, and/or set width: 100% so the section stacks vertically with the left
column (or switch the parent layout to flex-direction: column); ensure
ProductContentLayout content can scroll/expand on small screens to avoid
clipping.
| import anthropicLogo from "../../plugins/assets/llmWhisperer/Anthropic.svg"; | ||
| import azureGptLogo from "../../plugins/assets/llmWhisperer/Azure GPT.png"; | ||
| import llmG2Badge from "../../plugins/assets/llmWhisperer/G2-Badge.png"; | ||
| import gdprBadge from "../../plugins/assets/llmWhisperer/GDPR.webp"; | ||
| import gregCrisciPhoto from "../../plugins/assets/llmWhisperer/Greg Crisci.jpeg"; | ||
| import hipaaBadge from "../../plugins/assets/llmWhisperer/HIPAA-1.png.webp"; | ||
| import isoBadge from "../../plugins/assets/llmWhisperer/ISO-27001.png"; | ||
| import openAiLogo from "../../plugins/assets/llmWhisperer/OpenAI-icon.svg"; | ||
| import quoteIcon from "../../plugins/assets/llmWhisperer/quote-icon.svg"; | ||
| import soc2Badge from "../../plugins/assets/llmWhisperer/SOC2-Type-II.png"; | ||
| import statIconAccuracy from "../../plugins/assets/llmWhisperer/stat-icon-accuracy.svg"; | ||
| import statIconManual from "../../plugins/assets/llmWhisperer/stat-icon-manual.svg"; | ||
| import statIconTouchpoints from "../../plugins/assets/llmWhisperer/stat-icon-touchpoints.svg"; | ||
| import vertexAiLogo from "../../plugins/assets/llmWhisperer/Vertex AI.svg"; | ||
| import christopherVarnerPhoto from "../../plugins/assets/unstract/Christopher Varner.jpeg"; | ||
| import cybersoftLogo from "../../plugins/assets/unstract/cybersoft.png"; | ||
| import endpointClinicalLogo from "../../plugins/assets/unstract/endpoint-clinical.svg"; | ||
| import unstractG2BadgeNew from "../../plugins/assets/unstract/G2-Badge-new.png"; | ||
| import medaxionLogo from "../../plugins/assets/unstract/medaxion.svg"; | ||
| import moodysLogo from "../../plugins/assets/unstract/moodys.png"; | ||
| import unstractQuoteIcon from "../../plugins/assets/unstract/quote-icon.svg"; | ||
| import unstractStatIconAccuracy from "../../plugins/assets/unstract/stat-icon-accuracy.svg"; | ||
| import unstractStatIconEfficiency from "../../plugins/assets/unstract/stat-icon-efficiency.svg"; | ||
| import unstractStatIconStp from "../../plugins/assets/unstract/stat-icon-stp.svg"; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify that all imported asset files exist
echo "=== Checking for plugin asset directories ==="
echo ""
echo "Listing llmWhisperer assets directory:"
if [ -d "frontend/src/plugins/assets/llmWhisperer" ]; then
ls -la "frontend/src/plugins/assets/llmWhisperer" 2>/dev/null | head -30 || echo "Directory exists but cannot list contents"
else
echo "Directory not found: frontend/src/plugins/assets/llmWhisperer"
fi
echo ""
echo "Listing unstract assets directory:"
if [ -d "frontend/src/plugins/assets/unstract" ]; then
ls -la "frontend/src/plugins/assets/unstract" 2>/dev/null | head -30 || echo "Directory exists but cannot list contents"
else
echo "Directory not found: frontend/src/plugins/assets/unstract"
fi
echo ""
echo "=== Checking if assets path exists at all ==="
if [ -d "frontend/src/plugins" ]; then
echo "frontend/src/plugins exists. Contents:"
find frontend/src/plugins -type f 2>/dev/null | head -50
else
echo "frontend/src/plugins does not exist"
fi
echo ""
echo "=== Checking frontend/src directory structure ==="
ls -la frontend/src/ 2>/dev/null | grep -E "^d"Repository: Zipstack/unstract
Length of output: 1000
🏁 Script executed:
#!/bin/bash
# Search for the imported asset files
echo "=== Searching for imported asset files ==="
echo ""
# Search for specific files mentioned in imports
files_to_find=(
"Anthropic.svg"
"Azure GPT.png"
"G2-Badge.png"
"GDPR.webp"
"Greg Crisci.jpeg"
"HIPAA-1.png.webp"
"ISO-27001.png"
"OpenAI-icon.svg"
"quote-icon.svg"
"SOC2-Type-II.png"
"stat-icon-accuracy.svg"
"Christopher Varner.jpeg"
"cybersoft.png"
"endpoint-clinical.svg"
"G2-Badge-new.png"
"medaxion.svg"
"moodys.png"
)
echo "Searching for asset files in the repository..."
for file in "${files_to_find[@]}"; do
find . -name "$file" -type f 2>/dev/null
done
echo ""
echo "=== Contents of frontend/src/assets ==="
if [ -d "frontend/src/assets" ]; then
find frontend/src/assets -type f | sort | head -50
else
echo "frontend/src/assets not found"
fi
echo ""
echo "=== Search for any 'llmWhisperer' or 'unstract' directories ==="
find . -type d -name "llmWhisperer" -o -type d -name "unstract" 2>/dev/nullRepository: Zipstack/unstract
Length of output: 2645
🏁 Script executed:
#!/bin/bash
# Verify the file exists and contains these imports
echo "=== Checking ProductContentLayout.jsx ==="
if [ -f "frontend/src/components/log-in/ProductContentLayout.jsx" ]; then
echo "File found. First 30 lines:"
head -30 frontend/src/components/log-in/ProductContentLayout.jsx
else
echo "File not found at frontend/src/components/log-in/ProductContentLayout.jsx"
echo ""
echo "Searching for ProductContentLayout.jsx:"
find . -name "ProductContentLayout.jsx" -type f 2>/dev/null
fiRepository: Zipstack/unstract
Length of output: 2239
These asset imports reference non-existent files and will cause immediate build failure.
All 24 imported asset files from ../../plugins/assets/llmWhisperer/ and ../../plugins/assets/unstract/ do not exist in the repository. The frontend/src/plugins/ directory structure does not exist at all. The component cannot build or run with these imports. Either commit the missing asset files to the correct paths or update the imports to point to existing assets in frontend/src/assets/.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/components/log-in/ProductContentLayout.jsx` around lines 2 - 25,
ProductContentLayout.jsx currently imports 24 asset modules (e.g.,
anthropicLogo, azureGptLogo, llmG2Badge, gregCrisciPhoto,
christopherVarnerPhoto, unstractQuoteIcon, etc.) from non-existent
../../plugins/assets/... paths which breaks the build; fix the imports in
ProductContentLayout.jsx by either (A) replacing each ../../plugins/assets/...
import with the correct existing paths under frontend/src/assets/ for the
matching files, or (B) removing/conditionally loading unused assets and
substituting available placeholders, ensuring all referenced symbols
(anthropicLogo, openAiLogo, statIconAccuracy, unstractStatIconEfficiency, etc.)
resolve to actual files or exports before committing.
| const DEFAULT_LLM_PROVIDERS = [ | ||
| { name: "Anthropic", logo: anthropicLogo }, | ||
| { name: "Azure GPT", logo: azureGptLogo }, | ||
| { name: "OpenAI", logo: openAiLogo }, | ||
| { name: "Vertex AI", logo: vertexAiLogo }, | ||
| ]; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Remove duplicate constant arrays.
LLM_PROVIDERS (lines 133-138) is identical to DEFAULT_LLM_PROVIDERS (lines 42-47). Similarly:
TRUSTED_BY_LOGOS≡DEFAULT_TRUSTED_LOGOSUNSTRACT_COMPLIANCE_BADGES≡DEFAULT_COMPLIANCE_BADGES
Reuse the existing constants to avoid drift and reduce bundle size.
♻️ Proposed fix: remove duplicates
-const LLM_PROVIDERS = [
- { name: "Anthropic", logo: anthropicLogo },
- { name: "Azure GPT", logo: azureGptLogo },
- { name: "OpenAI", logo: openAiLogo },
- { name: "Vertex AI", logo: vertexAiLogo },
-];
+// Reuse DEFAULT_LLM_PROVIDERS in LlmWhispererContent
-const TRUSTED_BY_LOGOS = [
- { name: "Moody's", logo: moodysLogo },
- { name: "Endpoint Clinical", logo: endpointClinicalLogo },
- { name: "Cybersoft", logo: cybersoftLogo },
- { name: "Medaxion", logo: medaxionLogo },
-];
+// Reuse DEFAULT_TRUSTED_LOGOS in UnstractContent
-const UNSTRACT_COMPLIANCE_BADGES = [
- { name: "SOC2 Type II", logo: soc2Badge },
- { name: "GDPR", logo: gdprBadge },
- { name: "ISO", logo: isoBadge },
- { name: "HIPAA", logo: hipaaBadge },
- { name: "G2 Users Love Us", logo: unstractG2BadgeNew },
-];
+// Reuse DEFAULT_COMPLIANCE_BADGES in UnstractContentThen update the component usages from LLM_PROVIDERS → DEFAULT_LLM_PROVIDERS, etc.
Also applies to: 133-138
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/components/log-in/ProductContentLayout.jsx` around lines 42 -
47, There are duplicate constant arrays; remove the redundant definitions
(LLM_PROVIDERS, TRUSTED_BY_LOGOS, UNSTRACT_COMPLIANCE_BADGES) and update all
usages to reference the existing defaults (DEFAULT_LLM_PROVIDERS,
DEFAULT_TRUSTED_LOGOS, DEFAULT_COMPLIANCE_BADGES) so the component
(ProductContentLayout) and any other consumers import/use the single source of
truth—search for and replace references to LLM_PROVIDERS →
DEFAULT_LLM_PROVIDERS, TRUSTED_BY_LOGOS → DEFAULT_TRUSTED_LOGOS, and
UNSTRACT_COMPLIANCE_BADGES → DEFAULT_COMPLIANCE_BADGES, then delete the
now-unused duplicate constant declarations and remove any related unused
imports.
Auto-fix formatting issues (double quotes, semicolons, import ordering) flagged by the biome-check CI workflow. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Frontend Lint Report (Biome)✅ All checks passed! No linting or formatting issues found. |
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
frontend/src/components/log-in/Login.css (2)
267-290: Excessive!importantusage in typography styles.Multiple properties in
.llm-headline(lines 284-290) and similar classes throughout use!importantdeclarations. While this may be necessary to override Ant Design's default styles, consider using more specific selectors or scoped CSS modules to avoid specificity wars.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/log-in/Login.css` around lines 267 - 290, The CSS uses excessive !important in the .llm-headline (and similar) rules which causes specificity wars; remove the unnecessary !important flags and instead increase selector specificity or scope the rules (e.g., target .llm-whisperer-content .llm-headline or convert to a CSS module/scoped stylesheet) so these typography styles override Ant Design without !important; update the selectors for .llm-headline and related classes and verify the visual result across breakpoints.
384-411: Consider CSS custom properties to reduce duplication.The
.llm-stats-barand.unstract-stats-bar(and their child classes) share nearly identical styles. Using CSS custom properties for colors and spacing would reduce duplication and make theme-wide changes easier.Example approach
/* Define theme tokens */ .llm-whisperer-dark { --stat-value-color: `#fff`; --stat-label-color: `#d1d5dc`; } .unstract-light { --stat-value-color: `#282828`; --stat-label-color: `#545454`; } /* Shared stat styles */ .stat-value { font-size: 22px; font-weight: 600; color: var(--stat-value-color); }Also applies to: 562-589
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/log-in/Login.css` around lines 384 - 411, Extract duplicated styling from .llm-stats-bar/.unstract-stats-bar and their children by introducing shared utility classes (e.g., .stats-bar, .stat-item, .stat-value, .stat-label) and move common rules (display, gap, alignment, font-size, font-weight, layout) into those; define theme tokens as CSS custom properties on the theme roots (.llm-whisperer-dark, .unstract-light) such as --stat-value-color and --stat-label-color and replace hardcoded color values in .stat-value and .stat-label with var(--stat-value-color)/var(--stat-label-color); update references to .llm-stat-item/.llm-stat-value/.llm-stat-label and .llm-stats-bar/.unstract-stats-bar to use the new shared classes so spacing and color changes can be done via the theme variables (also apply same refactor to the other duplicated block at lines 562-589).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/src/components/log-in/Login.css`:
- Around line 869-909: The .region-option buttons lack keyboard focus
indicators; add explicit :focus and :focus-visible rules for .region-option (and
optionally for .region-option-selected) to show a clear visible outline or
box-shadow (e.g., a high-contrast ring or outline with outline-offset) that does
not disturb layout, ensure the focus styles are applied to the clickable element
and not just the image (reference the .region-option, .region-option:focus,
.region-option:focus-visible and .region-option-selected .region-flag selectors)
and keep the visual treatment consistent with the existing accent color and
rounded flag (use border-radius and appropriate contrast).
- Around line 822-843: The toggle buttons (.product-toggle-btn and
.product-toggle-btn-active) lack keyboard focus styling; add :focus and/or
:focus-visible rules for both selectors to provide a visible outline or enhanced
box-shadow and ensure the focus style is distinct from hover/active (e.g., a
high-contrast outline or ring color that matches your design), keep existing
border-radius and avoid removing focus on click, and ensure focus styles meet
contrast/accessibility requirements so keyboard users can clearly see which
product is focused.
---
Nitpick comments:
In `@frontend/src/components/log-in/Login.css`:
- Around line 267-290: The CSS uses excessive !important in the .llm-headline
(and similar) rules which causes specificity wars; remove the unnecessary
!important flags and instead increase selector specificity or scope the rules
(e.g., target .llm-whisperer-content .llm-headline or convert to a CSS
module/scoped stylesheet) so these typography styles override Ant Design without
!important; update the selectors for .llm-headline and related classes and
verify the visual result across breakpoints.
- Around line 384-411: Extract duplicated styling from
.llm-stats-bar/.unstract-stats-bar and their children by introducing shared
utility classes (e.g., .stats-bar, .stat-item, .stat-value, .stat-label) and
move common rules (display, gap, alignment, font-size, font-weight, layout) into
those; define theme tokens as CSS custom properties on the theme roots
(.llm-whisperer-dark, .unstract-light) such as --stat-value-color and
--stat-label-color and replace hardcoded color values in .stat-value and
.stat-label with var(--stat-value-color)/var(--stat-label-color); update
references to .llm-stat-item/.llm-stat-value/.llm-stat-label and
.llm-stats-bar/.unstract-stats-bar to use the new shared classes so spacing and
color changes can be done via the theme variables (also apply same refactor to
the other duplicated block at lines 562-589).
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to Reviews > Disable Cache setting
Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting
📒 Files selected for processing (2)
frontend/src/components/log-in/Login.cssfrontend/vite.config.js
| .product-toggle-btn { | ||
| flex: 1; | ||
| height: 39px; | ||
| border: none; | ||
| border-radius: 16px; | ||
| background: transparent; | ||
| font-size: 11px; | ||
| font-weight: 900; | ||
| letter-spacing: 0.6px; | ||
| text-transform: uppercase; | ||
| color: #90a1b9; | ||
| cursor: pointer; | ||
| transition: all 0.2s ease; | ||
| font-family: inherit; | ||
| } | ||
|
|
||
| .product-toggle-btn-active { | ||
| background: #fff; | ||
| color: #2f93f6; | ||
| border: 1px solid #f1f5f9; | ||
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); | ||
| } |
There was a problem hiding this comment.
Add focus styles for product toggle buttons.
The .product-toggle-btn lacks :focus or :focus-visible styles. Keyboard users switching between Unstract and LLMWhisperer won't have clear visual feedback.
Suggested addition
.product-toggle-btn {
flex: 1;
height: 39px;
border: none;
border-radius: 16px;
background: transparent;
font-size: 11px;
font-weight: 900;
letter-spacing: 0.6px;
text-transform: uppercase;
color: `#90a1b9`;
cursor: pointer;
transition: all 0.2s ease;
font-family: inherit;
}
+.product-toggle-btn:focus-visible {
+ outline: 2px solid `#2f93f6`;
+ outline-offset: 2px;
+}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| .product-toggle-btn { | |
| flex: 1; | |
| height: 39px; | |
| border: none; | |
| border-radius: 16px; | |
| background: transparent; | |
| font-size: 11px; | |
| font-weight: 900; | |
| letter-spacing: 0.6px; | |
| text-transform: uppercase; | |
| color: #90a1b9; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| font-family: inherit; | |
| } | |
| .product-toggle-btn-active { | |
| background: #fff; | |
| color: #2f93f6; | |
| border: 1px solid #f1f5f9; | |
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); | |
| } | |
| .product-toggle-btn { | |
| flex: 1; | |
| height: 39px; | |
| border: none; | |
| border-radius: 16px; | |
| background: transparent; | |
| font-size: 11px; | |
| font-weight: 900; | |
| letter-spacing: 0.6px; | |
| text-transform: uppercase; | |
| color: `#90a1b9`; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| font-family: inherit; | |
| } | |
| .product-toggle-btn:focus-visible { | |
| outline: 2px solid `#2f93f6`; | |
| outline-offset: 2px; | |
| } | |
| .product-toggle-btn-active { | |
| background: `#fff`; | |
| color: `#2f93f6`; | |
| border: 1px solid `#f1f5f9`; | |
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/components/log-in/Login.css` around lines 822 - 843, The toggle
buttons (.product-toggle-btn and .product-toggle-btn-active) lack keyboard focus
styling; add :focus and/or :focus-visible rules for both selectors to provide a
visible outline or enhanced box-shadow and ensure the focus style is distinct
from hover/active (e.g., a high-contrast outline or ring color that matches your
design), keep existing border-radius and avoid removing focus on click, and
ensure focus styles meet contrast/accessibility requirements so keyboard users
can clearly see which product is focused.
| .region-option { | ||
| display: flex; | ||
| flex-direction: column; | ||
| align-items: center; | ||
| gap: 8px; | ||
| padding: 0; | ||
| border: none; | ||
| background: transparent; | ||
| cursor: pointer; | ||
| position: relative; | ||
| } | ||
|
|
||
| .region-flag { | ||
| width: 58px; | ||
| height: 58px; | ||
| border-radius: 50%; | ||
| object-fit: cover; | ||
| } | ||
|
|
||
| .region-option-selected .region-flag { | ||
| box-shadow: 0 0 0 3px #2f93f6; | ||
| border-radius: 50%; | ||
| } | ||
|
|
||
| .region-label { | ||
| font-size: 18px; | ||
| font-weight: 500; | ||
| color: #545454; | ||
| } | ||
|
|
||
| .region-radio-indicator { | ||
| position: absolute; | ||
| top: -4px; | ||
| right: -4px; | ||
| width: 16px; | ||
| height: 16px; | ||
| border-radius: 50%; | ||
| background: #2f93f6; | ||
| border: 3px solid #fff; | ||
| box-shadow: 0 0 0 1px #2f93f6; | ||
| } |
There was a problem hiding this comment.
Add focus styles for keyboard accessibility.
The .region-option button lacks explicit :focus or :focus-visible styles. Keyboard users navigating the region selector may not see clear focus indicators.
Suggested addition
.region-option {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 0;
border: none;
background: transparent;
cursor: pointer;
position: relative;
}
+.region-option:focus-visible .region-flag {
+ box-shadow: 0 0 0 3px `#2f93f6`;
+ outline: 2px solid transparent;
+}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/components/log-in/Login.css` around lines 869 - 909, The
.region-option buttons lack keyboard focus indicators; add explicit :focus and
:focus-visible rules for .region-option (and optionally for
.region-option-selected) to show a clear visible outline or box-shadow (e.g., a
high-contrast ring or outline with outline-offset) that does not disturb layout,
ensure the focus styles are applied to the clickable element and not just the
image (reference the .region-option, .region-option:focus,
.region-option:focus-visible and .region-option-selected .region-flag selectors)
and keep the visual treatment consistent with the existing accent color and
rounded flag (use border-radius and appropriate contrast).



What
Redesign the landing page with new light-themed right panels for all three variants (default, LLM Whisperer, Unstract), featuring compliance badges, stats cards, testimonials, and provider logos.
Why
Updated to match new Figma design specifications with a modern, trust-building layout that showcases compliance certifications, usage stats, customer testimonials, and integration partners.
How
Can this PR break any existing features. If yes, please list possible items. If no, please explain why. (PS: Admins do not merge the PR without this section filled)
Database Migrations
Env Config
Relevant Docs
Related Issues or PRs
Dependencies Versions
Notes on Testing
/landing— verify default landing page with two product cards (Unstract, LLMWhisperer)/landing?selectedProduct=llm-whisperer— verify LLM Whisperer variant with stats, testimonial, and LLM providers/landing?selectedProduct=unstract— verify Unstract variant with stats, testimonial, and trusted-by logosScreenshots
Checklist
I have read and understood the Contribution Guidelines.