Feat: Advanced Story controls "Pro panel" with presets and presets sharing implemented along with UI/UX updated as recommended#439
Conversation
|
@rohits1450 is attempting to deploy a commit to the Drago's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a full Pro Panel feature: new Pro Panel UI, controls, persisted Zustand store, Zod schemas and defaults, presets import/export, backend integration (API + groq-service), styling, dependency updates, and minor UI/asset ignore tweaks. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant UI as Pro Panel UI
participant Store as Zustand Store
participant API as Server (/api/groq)
participant Service as groq-service
participant Groq as Groq API
User->>UI: Configure parameters, enter prompt, click Generate
UI->>Store: Read storyInput & proConfig
UI->>API: POST /api/groq { action, prompt, title, proConfig }
API->>API: Validate proConfig with ParametersSchema
API->>Service: Call generateStoryWithProConfig(prompt, proConfig, {title})
Service->>Service: Build system prompt & user prompt from ProParameters
Service->>Groq: POST chat completions with mapped modelSettings
Groq-->>Service: Return generated text
Service->>Service: Run output safety checks
Service-->>API: Return generation result
API-->>UI: 200 + story content
UI->>Store: Reset storyInput (on success)
UI-->>User: Display story and success toast
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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 |
There was a problem hiding this comment.
Actionable comments posted: 17
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
🟡 Minor comments (5)
components/pro-panel/CategorySection.tsx-66-80 (1)
66-80:⚠️ Potential issue | 🟡 MinorKeyboard handler lacks
stopPropagation, causing potential accordion toggle.The
onClickhandler useshandleResetwhich calls bothstopPropagation()andpreventDefault(). However, theonKeyDownhandler only callspreventDefault()and directly invokesresetCategory(id). This inconsistency may cause the accordion to toggle when the reset button is activated via keyboard.🔧 Proposed fix
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { + e.stopPropagation(); e.preventDefault(); resetCategory(id); } }}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/pro-panel/CategorySection.tsx` around lines 66 - 80, The keyboard handler on the span currently calls only e.preventDefault() and resetCategory(id), which can let the accordion toggle; update the onKeyDown for the span to mirror handleReset by calling e.preventDefault() and e.stopPropagation() before invoking resetCategory(id) (or simply call handleReset) so keyboard activation behaves identically to the click handler; change the onKeyDown closure around resetCategory(id) in the span to include e.stopPropagation() (or delegate to handleReset) to prevent event bubbling.components/pro-panel/controls/index.tsx-429-435 (1)
429-435:⚠️ Potential issue | 🟡 MinorGive the tag remove button an accessible name.
The
×icon-only button has no accessible label for assistive tech.🧩 Suggested fix
<button type="button" onClick={() => removeTag(index)} + aria-label={`Remove tag ${tag}`} + title={`Remove tag ${tag}`} className="hover:text-gray-300 ml-0.5" > × </button>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/pro-panel/controls/index.tsx` around lines 429 - 435, The tag remove button currently renders only the "×" character and lacks an accessible name; update the button in the tag list (the element that calls removeTag(index)) to include an accessible label (for example add aria-label={`Remove tag ${tag}`} or aria-label="Remove tag" or include visually-hidden text) so assistive technologies can announce its purpose; ensure the aria-label references the tag value if available (use the same tag variable used when rendering the tag) and keep the onClick handler removeTag(index) unchanged.lib/groq-service.ts-944-945 (1)
944-945:⚠️ Potential issue | 🟡 MinorValidate/sanitize
options.titlebefore embedding into system instructions.
promptis sanitized/validated, buttitleis injected directly into the system prompt path.🔒 Suggested fix
+ let sanitizedTitle = options.title; + if (options.title) { + const { sanitized } = sanitizeInput(options.title); + const titleValidation = validateInput(sanitized); + if (!titleValidation.isValid) { + logSecurityEvent({ + type: 'injection_attempt', + details: { field: 'title', reason: titleValidation.reason, pattern: titleValidation.matchedPattern }, + }); + throw new Error(`Invalid input for title: ${titleValidation.reason}`); + } + sanitizedTitle = sanitized; + } ... - const systemPromptContent = buildProPanelSystemPrompt(proConfig, options.title); + const systemPromptContent = buildProPanelSystemPrompt(proConfig, sanitizedTitle);Also applies to: 1025-1026, 1076-1076
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/groq-service.ts` around lines 944 - 945, The title from options (options.title) is being injected directly into system prompts (calls to buildProPanelSystemPrompt and subsequent buildHardenedSystemPrompt at the occurrences near the buildProPanelSystemPrompt calls), so sanitize and validate it before use: trim and enforce a max length, strip or escape newlines/control characters, remove or encode any prompt-injection tokens, and reject/replace unsafe values; reuse the existing prompt-sanitization utility (e.g., sanitizePrompt or similar) or add a small helper (sanitizeTitle) and pass the sanitizedTitle into buildProPanelSystemPrompt wherever options.title is currently used (including the other occurrences reported around the 1025–1026 and 1076 lines).components/pro-panel/DossierFormPanel.tsx-77-83 (1)
77-83:⚠️ Potential issue | 🟡 MinorHide decorative elements from assistive tech.
These nodes are decorative-only but currently exposed to screen readers.
♿ Suggested fix
-<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-48 h-48 opacity-[0.04] rotate-[-15deg] pointer-events-none select-none"> +<div aria-hidden="true" className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-48 h-48 opacity-[0.04] rotate-[-15deg] pointer-events-none select-none"> -<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 -rotate-[30deg] pointer-events-none select-none whitespace-nowrap"> +<div aria-hidden="true" className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 -rotate-[30deg] pointer-events-none select-none whitespace-nowrap"> -<div className="absolute top-8 right-16 w-16 h-16 rounded-full bg-yellow-900/[0.06] pointer-events-none" /> +<div aria-hidden="true" className="absolute top-8 right-16 w-16 h-16 rounded-full bg-yellow-900/[0.06] pointer-events-none" /> -<div className="absolute bottom-20 left-12 w-10 h-10 rounded-full bg-yellow-900/[0.05] pointer-events-none" /> +<div aria-hidden="true" className="absolute bottom-20 left-12 w-10 h-10 rounded-full bg-yellow-900/[0.05] pointer-events-none" /> -<div className="absolute top-1/3 right-8 w-6 h-6 rounded-full bg-yellow-900/[0.04] pointer-events-none" /> +<div aria-hidden="true" className="absolute top-1/3 right-8 w-6 h-6 rounded-full bg-yellow-900/[0.04] pointer-events-none" /> -<div className="absolute bottom-4 right-4 text-4xl opacity-[0.06] rotate-[20deg] pointer-events-none select-none"> +<div aria-hidden="true" className="absolute bottom-4 right-4 text-4xl opacity-[0.06] rotate-[20deg] pointer-events-none select-none">Also applies to: 89-92, 197-199
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/pro-panel/DossierFormPanel.tsx` around lines 77 - 83, The decorative watermark elements (the two absolute divs containing meta.watermark and the span with class "font-marker text-black/[0.04] text-6xl tracking-[0.3em]") are currently exposed to assistive tech; mark them as purely decorative by adding aria-hidden="true" (or role="presentation") to those container elements (the divs and the inner span) and remove any focusable attributes so screen readers ignore them; apply the same change to the other watermark instances referenced (the nodes around the other ranges) to ensure all decorative-only nodes are hidden from assistive technology.components/pro-panel/ProPanel.tsx-82-107 (1)
82-107:⚠️ Potential issue | 🟡 MinorAdd cleanup for animation timeouts to prevent memory leaks.
The nested
setTimeoutcalls could execute after component unmount, causing potential state updates on unmounted components. Consider usinguseEffectcleanup or refs to track timeout IDs.🛡️ Proposed fix with cleanup
+import React, { useState, useRef, useCallback, useEffect } from 'react'; + export function ProPanel() { // ... existing state ... + const timeoutRefs = useRef<NodeJS.Timeout[]>([]); + + useEffect(() => { + return () => { + timeoutRefs.current.forEach(clearTimeout); + }; + }, []); const handleCategoryChange = useCallback((newCategory: CategoryKey) => { if (newCategory === selectedCategory || isPageTurning) return; setIsPageTurning(true); setSelectedCategory(newCategory); if (pageRef.current) { pageRef.current.classList.remove('dossier-page-enter'); pageRef.current.classList.add('dossier-page-exit'); } - setTimeout(() => { + const t1 = setTimeout(() => { setDisplayedCategory(newCategory); if (pageRef.current) { pageRef.current.classList.remove('dossier-page-exit'); pageRef.current.classList.add('dossier-page-enter'); } - setTimeout(() => { + const t2 = setTimeout(() => { setIsPageTurning(false); if (pageRef.current) { pageRef.current.classList.remove('dossier-page-enter'); } }, 700); + timeoutRefs.current.push(t2); }, 400); + timeoutRefs.current.push(t1); }, [selectedCategory, isPageTurning]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/pro-panel/ProPanel.tsx` around lines 82 - 107, The handleCategoryChange function uses nested setTimeouts and can update state after unmount; fix by tracking both timeout IDs in a ref (e.g., timeoutsRef) and clearing them before scheduling new timeouts and in a useEffect cleanup: on unmount clearTimeout for stored IDs and also clear any existing timeouts at top of handleCategoryChange before setting new ones; ensure you still call setDisplayedCategory, setSelectedCategory, and setIsPageTurning in the same order but only after scheduling timeouts that are tracked and cancelable, and reference pageRef as before.
🧹 Nitpick comments (20)
.gitignore (1)
63-65: Consider using more generic patterns for font demo files.The specific entries address the immediate need to ignore accidentally committed font files. However, using more generic patterns would better prevent similar issues in the future if other font demos or temporary files are used during development.
♻️ Alternative pattern suggestions
Consider replacing with more generic patterns:
# Font demo files -another-danger-rohit/ -another-danger-rohit.zip +/fonts/demo/ +/font-demos/ +*-font-demo/ +*-font-demo.zipOr, if font-related files are commonly used temporarily:
# Font demo files -another-danger-rohit/ -another-danger-rohit.zip +# Temporary font files and demos +*.otf.zip +*.ttf.zip +/temp-fonts/🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.gitignore around lines 63 - 65, The .gitignore currently lists two specific entries ("another-danger-rohit/" and "another-danger-rohit.zip") which is brittle; replace those specific entries with more generic glob patterns that match font demo directories and font archive files (e.g., patterns for "*-demo" or "font-demo" directories and "*.zip" archives related to fonts) so future font demos or temporary font zip files are ignored without adding per-case entries; update the gitignore entries that reference "another-danger-rohit/" and "another-danger-rohit.zip" accordingly.components/ui/tooltip.tsx (2)
36-42: Consider merging refs whenasChildclones an element with an existing ref.When the cloned child already has a
refattached, the current implementation replaces it rather than composing both refs. This could cause issues if consumers pass a child with its own ref.For a fallback component this is likely acceptable, but worth noting if this pattern is used elsewhere.
♻️ Optional fix using a ref-merging utility
+// Helper to merge multiple refs +function mergeRefs<T>(...refs: (React.Ref<T> | undefined)[]): React.RefCallback<T> { + return (instance: T | null) => { + refs.forEach((ref) => { + if (typeof ref === 'function') ref(instance); + else if (ref) (ref as React.MutableRefObject<T | null>).current = instance; + }); + }; +} const TooltipTrigger = React.forwardRef<HTMLSpanElement, TooltipTriggerProps>( ({ className, asChild = false, children, ...props }, ref) => { if (asChild && React.isValidElement(children)) { + const childRef = (children as React.ReactElement & { ref?: React.Ref<unknown> }).ref; return React.cloneElement(children as React.ReactElement, { - ref, + ref: mergeRefs(ref, childRef), className: cn((children as React.ReactElement).props?.className, className), ...props, }); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ui/tooltip.tsx` around lines 36 - 42, The asChild branch in the Tooltip component currently overwrites an existing child ref when calling React.cloneElement; update this to merge refs instead: detect (children as React.ReactElement).ref and compose it with the local ref passed into the clone so both refs are updated (handle function refs and RefObject cases), then pass that composed ref to React.cloneElement along with className and props; you can implement a small utility composeRefs or use an existing ref-merge helper and reference the symbols asChild, children, ref, React.cloneElement, and cn to locate where to change.
58-71: Thesideprop is accepted but never applied to the element.The
sideprop is destructured with a default value but not used. The CSS relies ondata-[side=...]selectors for slide-in animations, but nodata-sideattribute is set on the div.For a fallback component, this may be intentional for API compatibility. If so, consider adding a brief comment. Otherwise, apply the attribute to enable directional animations:
♻️ Option 1: Apply the data-side attribute
return ( <div ref={ref} + data-side={side} className={cn(📝 Option 2: Document that side is non-functional in the fallback
interface TooltipContentProps extends React.HTMLAttributes<HTMLDivElement> { + /** Accepted for API compatibility; positioning not implemented in fallback */ sideOffset?: number; + /** Accepted for API compatibility; positioning not implemented in fallback */ side?: 'top' | 'right' | 'bottom' | 'left'; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ui/tooltip.tsx` around lines 58 - 71, The TooltipContent component destructures a side prop but never applies it, so the data-[side=...] CSS selectors never match; update the root div inside TooltipContent to include the data-side attribute set from the prop (e.g., data-side={side}) so the slide-in animations work, keeping the existing default side = 'top' and preserving className and {...props}; alternatively, if this component is intentionally a non-directional fallback, add a short comment above TooltipContent explaining side is accepted for API compatibility but not applied.app/globals.css (3)
1-1: Stylelint prefers string notation for@import.Static analysis flags
import-notation— the linter expects a bare string rather thanurl(). While both are valid CSS, consider aligning with the project's Stylelint configuration.💡 Use string notation
-@import url('https://fonts.googleapis.com/css2?family=Comic+Neue:wght@300;400;700&family=Playfair+Display:ital,wght@0,400;0,700;0,900;1,400;1,700;1,900&family=Space+Grotesk:wght@300;400;500;600;700&family=Permanent+Marker&family=Roboto+Condensed:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700&display=swap'); +@import 'https://fonts.googleapis.com/css2?family=Comic+Neue:wght@300;400;700&family=Playfair+Display:ital,wght@0,400;0,700;0,900;1,400;1,700;1,900&family=Space+Grotesk:wght@300;400;500;600;700&family=Permanent+Marker&family=Roboto+Condensed:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700&display=swap';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/globals.css` at line 1, The `@import` statement at the top of globals.css uses url(...) which triggers the Stylelint import-notation rule; replace the `@import` url('https://fonts.googleapis.com/...&display=swap') with string notation `@import` 'https://fonts.googleapis.com/...&display=swap' so the linter no longer flags it—update the top-level `@import` in globals.css accordingly.
724-730: Emoji filter utilities may behave inconsistently across platforms.The
filter: grayscale(1) brightness(100)technique to colorize emojis may produce different results across browsers and operating systems due to varying emoji rendering implementations. Consider testing on target platforms or using SVG icons for critical UI elements.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/globals.css` around lines 724 - 730, The emoji color filter classes (.emoji-white and .emoji-dark) can render inconsistently across browsers/OSes; test these classes on your target platforms and for any critical UI emoji replace them with predictable assets (inline SVG or an icon font) instead of relying on CSS filters; if you must keep the CSS approach, add cross-browser fallbacks and vendor-prefixed properties and document the limitation in a comment near .emoji-white and .emoji-dark so future maintainers know to prefer SVG/icons for critical UI elements.
715-722: FixcurrentColorcasing per Stylelint rule.Static analysis flags
value-keyword-case— CSS keywords should be lowercase for consistency.💡 Lowercase keyword
.font-marker { font-family: var(--font-marker); font-weight: 700; transform: skewX(-4deg); - -webkit-text-stroke: 0.5px currentColor; + -webkit-text-stroke: 0.5px currentcolor; paint-order: stroke fill; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/globals.css` around lines 715 - 722, Stylelint flags the keyword casing in the .font-marker rule: change the CSS keyword "currentColor" to lowercase "currentcolor" in the -webkit-text-stroke declaration (identify the .font-marker selector and the -webkit-text-stroke: 0.5px currentColor line) so it conforms to value-keyword-case; update that token and re-run the linter to verify the fix.components/pro-panel/MissionNavigator.tsx (1)
130-137: Consider deriving the file count fromsortedCategories.length.The "9 ACTIVE CASE FILES" text is hardcoded. If categories are added or removed, this will become stale.
💡 Dynamic count
<span className="font-condensed text-[9px] text-gray-600 uppercase tracking-[0.2em]"> - 9 ACTIVE CASE FILES + {sortedCategories.length} ACTIVE CASE FILES </span>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/pro-panel/MissionNavigator.tsx` around lines 130 - 137, The footer currently hardcodes "9 ACTIVE CASE FILES"; change it to compute the count from the component state/prop by using sortedCategories.length (the array used to render categories) and render `${sortedCategories.length} ACTIVE CASE FILES` in the footer stamp block inside MissionNavigator (replace the hardcoded text in the div with className "px-5 py-4 border-t ..."). Ensure you reference the existing sortedCategories variable so the displayed count updates whenever categories change.components/splash-screen.tsx (1)
53-53: Consider using responsivesizesto match the container's responsive dimensions.The container uses
w-32 h-32 md:w-48 md:h-48(128px / 192px), butsizes="128px"is static. This may cause the browser to fetch a smaller image than needed onmd+screens, resulting in a blurry logo.💡 Suggested responsive sizes
- sizes="128px" + sizes="(min-width: 768px) 192px, 128px"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/splash-screen.tsx` at line 53, The Image's static sizes="128px" will under-report required image width for larger screens; update the Image sizes prop to a responsive value that matches the container classes (w-32 h-32 md:w-48 md:h-48), e.g. using a media condition so the browser requests 192px on md+ and 128px otherwise (e.g. sizes="(min-width: 768px) 192px, 128px"); change the sizes prop on the Image component in components/splash-screen.tsx accordingly so the logo is fetched at the correct resolution.app/pro-panel/page.tsx (1)
24-24: Avoid duplicating the halftone background layer.
ProPanelalready renders a fixed halftone background (percomponents/pro-panel/ProPanel.tsxcontext), so this additional fixed layer stacks opacity and adds extra paint cost.🎯 Suggested simplification
- {/* Halftone dot background */} - <div className="fixed inset-0 z-0 halftone-bg pointer-events-none opacity-40" />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/pro-panel/page.tsx` at line 24, Remove the duplicate fixed halftone background layer in page.tsx: delete the <div className="fixed inset-0 z-0 halftone-bg pointer-events-none opacity-40" /> added in app/pro-panel/page.tsx so the page relies on the existing halftone provided by the ProPanel component (components/pro-panel/ProPanel.tsx); this prevents stacking opacity and extra paint cost while preserving the intended background.components/pro-panel/GenreCard.tsx (1)
15-25: Remove or renderbadgeto keep the component API honest.
badgeis exposed publicly but unused in rendering, which creates dead API surface and confusion for callers.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/pro-panel/GenreCard.tsx` around lines 15 - 25, The GenreCardProps interface currently exposes a badge property that is never rendered; either remove badge from GenreCardProps and update all call sites that pass badge, or render it inside the GenreCard component by reading props.badge (e.g., as a small label/chip next to title or image) and update the component's JSX to consume badge; ensure you also update any tests/types/usages that expect the old prop and keep the prop name consistent if you choose to render it.src/store/proPanelStore.ts (1)
96-99: Use deep clones for parameter snapshots/resets.Current top-level spreads are shallow and can retain nested array/object references across defaults, working state, and preset snapshots.
♻️ Suggested hardening refactor
+const cloneParameters = (params: ProParameters): ProParameters => + JSON.parse(JSON.stringify(params)); ... -parameters: { ...DEFAULT_PARAMETERS }, +parameters: cloneParameters(DEFAULT_PARAMETERS), ... -[category]: { ...DEFAULT_PARAMETERS[category] }, +[category]: cloneParameters(DEFAULT_PARAMETERS)[category], ... -parameters: { ...DEFAULT_PARAMETERS }, +parameters: cloneParameters(DEFAULT_PARAMETERS), ... -parameters: { ...state.parameters }, +parameters: cloneParameters(state.parameters), ... -parameters: { ...preset.parameters }, +parameters: cloneParameters(preset.parameters),Also applies to: 150-151, 158-158, 174-174, 194-194
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/store/proPanelStore.ts` around lines 96 - 99, The state initialization and any snapshot/reset assignments in proPanelStore.ts currently use shallow spreads (e.g., parameters: { ...DEFAULT_PARAMETERS }, savedPresets: initializeBuiltInPresets(), and other shallow copies around currentPresetName/parameter snapshots), which can leak nested object/array references; replace these shallow spreads with deep clones (use structuredClone if available or a utility like lodash.cloneDeep) when assigning DEFAULT_PARAMETERS, when creating/resetting parameters snapshots, and when populating savedPresets returned by initializeBuiltInPresets() and any subsequent preset save/load code (look for variables/methods named parameters, DEFAULT_PARAMETERS, savedPresets, initializeBuiltInPresets, and any reset/savePreset/loadPreset logic) so each state slot gets an independent deep copy.components/pro-panel/ProPanel.tsx (4)
256-280: Add aria-pressed for toggle button accessibility.The view mode toggle buttons function as toggle controls. Adding
aria-pressedhelps screen readers communicate the current state.♿ Proposed accessibility improvement
<button type="button" onClick={() => setViewMode('split')} title="Split Panel View" + aria-pressed={viewMode === 'split'} className={`px-3 py-1.5 font-condensed text-[10px] font-bold uppercase tracking-wider flex items-center gap-1.5 transition-all ${ viewMode === 'split' ? 'bg-[`#8a0000`] text-white' : 'text-gray-400 hover:text-white' }`} > <Columns className="w-3.5 h-3.5" /> Dossier </button> <button type="button" onClick={() => setViewMode('accordion')} title="Accordion View" + aria-pressed={viewMode === 'accordion'} className={`px-3 py-1.5 font-condensed text-[10px] font-bold uppercase tracking-wider flex items-center gap-1.5 transition-all ${ viewMode === 'accordion' ? 'bg-[`#8a0000`] text-white' : 'text-gray-400 hover:text-white' }`} >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/pro-panel/ProPanel.tsx` around lines 256 - 280, The two view-mode toggle buttons should expose their pressed state to assistive tech; update the button elements that call setViewMode and check viewMode (the ones with onClick={() => setViewMode('split')} and onClick={() => setViewMode('accordion')}) to include an aria-pressed attribute set to a boolean expression (e.g., aria-pressed={viewMode === 'split'} and aria-pressed={viewMode === 'accordion'}) so screen readers can announce which toggle is active.
310-313: Simplify icon rendering with direct conditional.The IIFE pattern is verbose. A simpler inline approach improves readability.
♻️ Simplified icon rendering
- {(() => { - const IconComp = ICON_MAP[config.icon]; - return IconComp ? <IconComp className={`w-3.5 h-3.5 inline-block mr-1 ${isActive ? 'text-white' : 'text-gray-400'}`} /> : null; - })()} + {ICON_MAP[config.icon] && ( + React.createElement(ICON_MAP[config.icon], { + className: `w-3.5 h-3.5 inline-block mr-1 ${isActive ? 'text-white' : 'text-gray-400'}` + }) + )}Or extract a small helper:
const CategoryIcon = ({ icon, isActive }: { icon: string; isActive: boolean }) => { const IconComp = ICON_MAP[icon]; return IconComp ? <IconComp className={`w-3.5 h-3.5 inline-block mr-1 ${isActive ? 'text-white' : 'text-gray-400'}`} /> : null; };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/pro-panel/ProPanel.tsx` around lines 310 - 313, Replace the IIFE icon rendering in ProPanel with a direct conditional or small helper to improve readability: locate the expression using ICON_MAP[config.icon] and isActive, and either render it inline as {ICON_MAP[config.icon] ? <ICON_MAP[config.icon] className={`w-3.5 h-3.5 inline-block mr-1 ${isActive ? 'text-white' : 'text-gray-400'}`} /> : null} or extract a CategoryIcon functional component (props: icon, isActive) that looks up ICON_MAP[icon] and returns the icon component with the same className logic; update the caller to use that helper and remove the IIFE.
394-397: Convert social media icons to semantic buttons or links.The social media icons are
divelements withcursor-pointer. For accessibility, use<button>or<a>elements so they're keyboard-navigable and announced correctly by screen readers.♿ Proposed accessibility fix
<div className="flex gap-4"> - <div className="w-8 h-8 border-2 border-[`#8a0000`] flex items-center justify-center text-[`#8a0000`] hover:bg-[`#8a0000`] hover:text-black cursor-pointer transition-colors font-condensed font-bold text-xs">FB</div> - <div className="w-8 h-8 border-2 border-[`#8a0000`] flex items-center justify-center text-[`#8a0000`] hover:bg-[`#8a0000`] hover:text-black cursor-pointer transition-colors font-condensed font-bold text-xs">TW</div> - <div className="w-8 h-8 border-2 border-[`#8a0000`] flex items-center justify-center text-[`#8a0000`] hover:bg-[`#8a0000`] hover:text-black cursor-pointer transition-colors font-condensed font-bold text-xs">IG</div> + <a href="#" aria-label="Facebook" className="w-8 h-8 border-2 border-[`#8a0000`] flex items-center justify-center text-[`#8a0000`] hover:bg-[`#8a0000`] hover:text-black transition-colors font-condensed font-bold text-xs">FB</a> + <a href="#" aria-label="Twitter" className="w-8 h-8 border-2 border-[`#8a0000`] flex items-center justify-center text-[`#8a0000`] hover:bg-[`#8a0000`] hover:text-black transition-colors font-condensed font-bold text-xs">TW</a> + <a href="#" aria-label="Instagram" className="w-8 h-8 border-2 border-[`#8a0000`] flex items-center justify-center text-[`#8a0000`] hover:bg-[`#8a0000`] hover:text-black transition-colors font-condensed font-bold text-xs">IG</a> </div>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/pro-panel/ProPanel.tsx` around lines 394 - 397, Replace the non-semantic social icon <div> elements in ProPanel.tsx (the three social icon elements currently rendering "FB", "TW", "IG") with semantic interactive elements — use <button type="button"> when the icon triggers an in-app action or <a href="..."> when it navigates externally — preserve the existing classes/styles so layout/hover stays the same, add descriptive aria-labels (e.g., aria-label="Share on Facebook" or aria-label="Open Twitter") and ensure any onClick handlers remain attached; if using anchors for external sites, include rel="noopener noreferrer" and target="_blank" as appropriate to match original behavior.
66-70: STEP_TAGS steps 2-3 are always inactive.The
activeproperty is hardcoded tofalsefor steps 2 and 3. If this is a multi-step wizard, consider making this state-driven. If it's purely decorative for the current release, this is fine.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/pro-panel/ProPanel.tsx` around lines 66 - 70, STEP_TAGS currently hardcodes active: false for steps 2 and 3; make the active state driven by the component's current step (e.g., a prop or state like currentStep/currentStepIndex in ProPanel) instead of hardcoding. Replace the static array construction so each tag's active boolean is derived (for example step === currentStep or step <= currentStep for progress-style activation) when computing STEP_TAGS or when rendering tags in ProPanel, and ensure any setters/props that change the wizard step update that currentStep value.components/pro-panel/SidebarPanels.tsx (2)
353-355: Consider extractingformatLabelto a shared utility.This helper function is useful and might be needed in other panel components. If it's duplicated elsewhere, consider moving it to a shared utilities file.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/pro-panel/SidebarPanels.tsx` around lines 353 - 355, Extract the formatLabel function into a shared utility module (e.g., create or add to an existing utils file) and export it so other components can reuse it; replace the local formatLabel implementation in SidebarPanels.tsx with an import of the shared function, update any other duplicated implementations across panel components to import the shared utility instead, and run/type-check to ensure signatures match (function name formatLabel, input string -> string) and adjust imports where necessary.
31-42: Move GENRE_LOCATIONS outside the component to avoid recreation on each render.The
GENRE_LOCATIONSobject is static and doesn't depend on props or state. Defining it inside the component causes unnecessary object recreation on every render.♻️ Move constant outside component
+// Move outside component, before LocationDocksPanel function +const GENRE_LOCATIONS: Record<string, { name: string; icon: React.ReactNode; locationImage: string }> = { + 'fantasy': { name: 'ENCHANTED REALM', icon: <Castle className="w-8 h-8 text-white" />, locationImage: '/images/presets/fantasylocation.jpg' }, + // ... rest of entries +}; export function LocationDocksPanel() { const parameters = useProPanelStore(selectParameters); const updateParameter = useProPanelStore((s) => s.updateParameter); const ui = useProPanelStore(selectUI); const world = parameters.world; - // Genre-specific location data - const GENRE_LOCATIONS: Record<string, { name: string; icon: React.ReactNode; locationImage: string }> = { - 'fantasy': { name: 'ENCHANTED REALM', icon: <Castle className="w-8 h-8 text-white" />, locationImage: '/images/presets/fantasylocation.jpg' }, - // ... - };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/pro-panel/SidebarPanels.tsx` around lines 31 - 42, GENRE_LOCATIONS is a static constant currently declared inside the component which causes it to be recreated on every render; move the const GENRE_LOCATIONS: Record<string, { name: string; icon: React.ReactNode; locationImage: string }> out of the component body and place it at module top-level (above the component declaration), preserving its shape and icon imports; if other modules need it, consider exporting it as export const GENRE_LOCATIONS, otherwise keep it as a top-level const so it’s created once.lib/schemas/proPanelSchemas.ts (1)
501-511: Consider deriving CATEGORY_KEYS from schema for single source of truth.The
CATEGORY_KEYSarray duplicates information already present inParametersSchema. Deriving it programmatically prevents drift if schema keys change.♻️ Derive from schema
export type CategoryKey = keyof ProParameters; -export const CATEGORY_KEYS: CategoryKey[] = [ - 'storyStructure', - 'characters', - 'world', - 'toneStyle', - 'theme', - 'modelSettings', - 'visual', - 'length', - 'advanced', -] as const; +export const CATEGORY_KEYS = Object.keys(ParametersSchema.shape) as CategoryKey[];Note: This changes the order to be schema-definition order. If explicit ordering is needed, the current approach is fine.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/schemas/proPanelSchemas.ts` around lines 501 - 511, CATEGORY_KEYS currently duplicates keys from ParametersSchema; instead derive it programmatically from the schema to keep a single source of truth. Replace the hardcoded CATEGORY_KEYS with code that reads the keys from ParametersSchema (e.g., using Object.keys or schema-specific utility on ParametersSchema.properties) and casts them to CategoryKey[] (or as const) to preserve typing; keep an explicit ordering step only if you still need a different order than the schema provides. Ensure you update references to CATEGORY_KEYS to accept the derived array and maintain type safety when casting to CategoryKey[].lib/constants/proPanelDefaults.ts (2)
676-688: Simplify the return type annotation.The return type
Omit<ProPreset, 'timestamp'> & { timestamp: number }is equivalent toProPreset. The internalresultvariable already usesProPresetcorrectly.♻️ Proposed simplification
-export function initializeBuiltInPresets(): Record<string, Omit<ProPreset, 'timestamp'> & { timestamp: number }> { +export function initializeBuiltInPresets(): Record<string, ProPreset> { const timestamp = Date.now(); const result: Record<string, ProPreset> = {};🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/constants/proPanelDefaults.ts` around lines 676 - 688, The function initializeBuiltInPresets uses a return type union that is equivalent to ProPreset; change its signature to return Record<string, ProPreset> instead of Record<string, Omit<ProPreset, 'timestamp'> & { timestamp: number }>, leaving the body (timestamp, result: Record<string, ProPreset>, loop over BUILT_IN_PRESETS, and returned result) unchanged so the declared types align and the signature is simplified.
110-115: Consider exporting CategoryConfig from schemas for consistency.The
CategoryConfiginterface is defined here butCategoryKeyis imported from schemas. For better cohesion, consider co-locating this interface with the schema types or explicitly documenting why it lives in defaults.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/constants/proPanelDefaults.ts` around lines 110 - 115, CategoryConfig currently lives alongside defaults while CategoryKey comes from the schemas module; to keep types cohesive, move or re-export CategoryConfig from the schemas barrel so both types come from the same place: add CategoryConfig to the schemas type definitions (or export it from the schemas index), then update usages to import CategoryConfig (and keep CategoryKey) from schemas; ensure exported name matches "CategoryConfig" and update any imports that reference the in-file interface to use the schemas export instead.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (38)
package-lock.jsonis excluded by!**/package-lock.jsonpublic/comedy.jpgis excluded by!**/*.jpgpublic/fantasy.pngis excluded by!**/*.pngpublic/fonts/Another Danger - Demo.otfis excluded by!**/*.otfpublic/fonts/Another Danger Slanted - Demo.otfis excluded by!**/*.otfpublic/fonts/SignLanguage-Regular.ttfis excluded by!**/*.ttfpublic/images/presets/adventurelocation.jpgis excluded by!**/*.jpgpublic/images/presets/comedylocation.jpgis excluded by!**/*.jpgpublic/images/presets/cozy.svgis excluded by!**/*.svgpublic/images/presets/cyberpunklocation.jpgis excluded by!**/*.jpgpublic/images/presets/dramalocation.jpgis excluded by!**/*.jpgpublic/images/presets/fantasy.svgis excluded by!**/*.svgpublic/images/presets/fantasylocation.jpgis excluded by!**/*.jpgpublic/images/presets/genre-adventure.jpgis excluded by!**/*.jpgpublic/images/presets/genre-comedy.jpgis excluded by!**/*.jpgpublic/images/presets/genre-cyberpunk.jpgis excluded by!**/*.jpgpublic/images/presets/genre-drama.jpgis excluded by!**/*.jpgpublic/images/presets/genre-fantasy.jpgis excluded by!**/*.jpgpublic/images/presets/genre-horror.jpgis excluded by!**/*.jpgpublic/images/presets/genre-mystery.jpgis excluded by!**/*.jpgpublic/images/presets/genre-romance.jpgis excluded by!**/*.jpgpublic/images/presets/genre-sci-fi.jpgis excluded by!**/*.jpgpublic/images/presets/genre-thriller.jpgis excluded by!**/*.jpgpublic/images/presets/horrorlocation.jpgis excluded by!**/*.jpgpublic/images/presets/literary.svgis excluded by!**/*.svgpublic/images/presets/mysterylocation.jpgis excluded by!**/*.jpgpublic/images/presets/noir-boss.pngis excluded by!**/*.pngpublic/images/presets/noir-detective.pngis excluded by!**/*.pngpublic/images/presets/noir-femme-fatale.pngis excluded by!**/*.pngpublic/images/presets/noir-grifter.pngis excluded by!**/*.pngpublic/images/presets/noir.svgis excluded by!**/*.svgpublic/images/presets/romanticlocation.jpgis excluded by!**/*.jpgpublic/images/presets/scifi.svgis excluded by!**/*.svgpublic/images/presets/scifilocation.jpgis excluded by!**/*.jpgpublic/images/presets/thriller.svgis excluded by!**/*.svgpublic/images/presets/thrillerlocation.jpgis excluded by!**/*.jpgpublic/mystery.pngis excluded by!**/*.pngpublic/sci-fi.pngis excluded by!**/*.png
📒 Files selected for processing (39)
.gitignoreapp/api/groq/route.tsapp/globals.cssapp/pro-panel/page.tsxcomponents/header.tsxcomponents/pro-panel/CategorySection.tsxcomponents/pro-panel/DossierFormPanel.tsxcomponents/pro-panel/GenerateStoryButton.tsxcomponents/pro-panel/GenreCard.tsxcomponents/pro-panel/GenreGrid.tsxcomponents/pro-panel/ImportExportDialog.tsxcomponents/pro-panel/MissionNavigator.tsxcomponents/pro-panel/PresetManager.tsxcomponents/pro-panel/ProPanel.tsxcomponents/pro-panel/SidebarPanels.tsxcomponents/pro-panel/StoryInputSection.tsxcomponents/pro-panel/controls/index.tsxcomponents/pro-panel/iconMap.tscomponents/pro-panel/index.tscomponents/pro-panel/sections/AdvancedSection.tsxcomponents/pro-panel/sections/CharacterSection.tsxcomponents/pro-panel/sections/LengthSection.tsxcomponents/pro-panel/sections/ModelSettingsSection.tsxcomponents/pro-panel/sections/StoryStructureSection.tsxcomponents/pro-panel/sections/ThemeSection.tsxcomponents/pro-panel/sections/ToneStyleSection.tsxcomponents/pro-panel/sections/VisualSection.tsxcomponents/pro-panel/sections/WorldSection.tsxcomponents/pro-panel/sections/index.tscomponents/splash-screen.tsxcomponents/ui/slider.tsxcomponents/ui/tooltip.tsxlib/constants/proPanelDefaults.tslib/groq-service.tslib/schemas/proPanelSchemas.tspackage.jsonpublic/comed.jfifsrc/store/proPanelStore.tstailwind.config.ts
| <span className="font-condensed uppercase tracking-wider text-xs text-white leading-tight max-w-[55%] block"> | ||
| {label} | ||
| </span> | ||
| {showValue && ( | ||
| <span className="text-base font-mono font-bold text-white bg-[#8a0000] px-3 py-1 rounded shrink-0 min-w-[3rem] text-center tabular-nums leading-none"> | ||
| {value}{suffix} | ||
| </span> | ||
| )} | ||
| </div> | ||
| <Slider | ||
| value={[value]} | ||
| min={min} | ||
| max={max} | ||
| step={step} | ||
| onValueChange={(values) => onChange(values[0] ?? value)} | ||
| className="w-full [&_[role=slider]]:bg-noir-primary [&_[role=slider]]:border-2 [&_[role=slider]]:border-white/40 [&_[role=slider]]:w-4 [&_[role=slider]]:h-4" | ||
| /> |
There was a problem hiding this comment.
Programmatically associate labels with interactive controls.
These controls render visible labels, but the interactive elements are not linked via htmlFor/id or aria-labelledby. Screen readers will treat several fields as unlabeled.
♿ Suggested pattern (apply across Slider/Select/Input/NumberInput/Textarea/Switch)
export function SelectControl<T extends string>({
label,
description,
value,
options,
onChange,
placeholder,
}: SelectControlProps<T>) {
+ const selectId = React.useId();
const normalizedOptions = options.map((opt) =>
typeof opt === 'string'
? { value: opt as T, label: formatOptionLabel(opt) }
: opt
);
return (
<div className="space-y-1.5">
- <Label className="font-condensed uppercase tracking-wider text-xs text-white">{label}</Label>
+ <Label id={`${selectId}-label`} className="font-condensed uppercase tracking-wider text-xs text-white">
+ {label}
+ </Label>
{description && (
<p className="text-[10px] font-condensed uppercase tracking-wider text-gray-500">
{description}
</p>
)}
<Select value={value} onValueChange={onChange}>
- <SelectTrigger className="w-full bg-black border-2 border-noir-primary p-3 font-condensed font-bold uppercase text-sm text-white focus:ring-0 hover:border-white transition-colors overflow-hidden">
+ <SelectTrigger
+ aria-labelledby={`${selectId}-label`}
+ className="w-full bg-black border-2 border-noir-primary p-3 font-condensed font-bold uppercase text-sm text-white focus:ring-0 hover:border-white transition-colors overflow-hidden"
+ >
<SelectValue placeholder={placeholder || 'Select...'} className="truncate" />
</SelectTrigger> export function InputControl({
label,
description,
value,
onChange,
placeholder,
type = 'text',
min,
max,
}: InputControlProps) {
+ const inputId = React.useId();
return (
<div className="space-y-1.5">
- <Label className="font-condensed uppercase tracking-wider text-xs text-white">{label}</Label>
+ <Label htmlFor={inputId} className="font-condensed uppercase tracking-wider text-xs text-white">
+ {label}
+ </Label>
{description && (
<p className="text-[10px] font-condensed uppercase tracking-wider text-gray-500">
{description}
</p>
)}
<Input
+ id={inputId}
type={type}
value={value}
onChange={(e) => onChange(e.target.value)}Also applies to: 107-115, 159-173, 202-216, 246-265, 287-295
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/pro-panel/controls/index.tsx` around lines 53 - 69, Label text
spans are not programmatically associated with their interactive controls
(Slider here), so add an accessible id/label linkage: generate a stable id (use
useId() or accept an id prop) and set it on the label span (e.g.,
id={`${id}-label`}) then pass that id to the Slider via aria-labelledby (or
htmlFor on a <label> if Slider exposes an input id) so screen readers can
connect the label to the control; apply the same pattern to the other controls
(Select/Input/NumberInput/Textarea/Switch) referenced in this file by
adding/propagating a control id and using aria-labelledby or htmlFor+id where
appropriate (use symbols like the label span and Slider, and props like
value/onChange/min/max/step to locate the code).
There was a problem hiding this comment.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
| type="number" | ||
| value={value} | ||
| onChange={(e) => onChange(Number(e.target.value))} | ||
| min={min} |
There was a problem hiding this comment.
Guard numeric parsing before writing to state.
Line 211 uses Number(e.target.value), which turns empty input into 0 and transient input states into NaN. That can persist invalid values.
🔢 Suggested fix
<Input
type="number"
value={value}
- onChange={(e) => onChange(Number(e.target.value))}
+ onChange={(e) => {
+ const next = e.currentTarget.valueAsNumber;
+ if (!Number.isFinite(next)) return;
+ onChange(next);
+ }}
min={min}
max={max}
step={step}
className="bg-black border-2 border-noir-primary p-3 font-mono text-sm text-white focus:ring-0 focus:border-white transition-colors"
/>📝 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.
| type="number" | |
| value={value} | |
| onChange={(e) => onChange(Number(e.target.value))} | |
| min={min} | |
| type="number" | |
| value={value} | |
| onChange={(e) => { | |
| const next = e.currentTarget.valueAsNumber; | |
| if (!Number.isFinite(next)) return; | |
| onChange(next); | |
| }} | |
| min={min} |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/pro-panel/controls/index.tsx` around lines 209 - 212, The numeric
input currently calls onChange(Number(e.target.value)) which converts empty
strings to 0 and can write NaN into state; update the onChange handler in the
component (the JSX using value and onChange) to guard parsing: read
e.target.value as a string, if it's empty call onChange with a cleared value
(e.g., undefined or null) to preserve the empty transient state, otherwise parse
to a number and only call onChange when Number(value) is a valid number (use
Number.isNaN to check); keep the input's value prop and min prop as-is but
ensure the handler avoids writing 0/NaN for intermediate empty or invalid input.
There was a problem hiding this comment.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
| <button | ||
| type="button" | ||
| onClick={() => setIsOpen(!isOpen)} | ||
| className="w-full flex items-center justify-between px-4 py-2.5 hover:bg-white/[0.03] transition-colors group" | ||
| > | ||
| <h4 className="text-[11px] font-condensed font-bold uppercase tracking-[0.15em] text-gray-400 group-hover:text-gray-300 transition-colors"> | ||
| {title} | ||
| </h4> | ||
| <span | ||
| className={`text-gray-500 text-xs font-condensed uppercase tracking-wider transition-transform duration-200 ${ | ||
| isOpen ? 'rotate-0' : '-rotate-90' | ||
| }`} | ||
| > | ||
| ▼ | ||
| </span> | ||
| </button> | ||
| <div | ||
| className={`overflow-hidden transition-all duration-300 ease-in-out ${ | ||
| isOpen ? 'max-h-[2000px] opacity-100' : 'max-h-0 opacity-0' | ||
| }`} | ||
| > | ||
| <div className="px-4 pb-4 pt-2 space-y-4"> | ||
| {children} | ||
| </div> | ||
| </div> |
There was a problem hiding this comment.
Collapsed content remains focusable; add proper disclosure semantics.
The panel is visually hidden only (max-h-0/opacity-0) but children stay mounted and can still receive keyboard focus. Also add aria-expanded/aria-controls on the toggle button.
🧭 Suggested fix
export function CollapsibleGroup({
title,
defaultOpen = false,
children,
}: CollapsibleGroupProps) {
const [isOpen, setIsOpen] = React.useState(defaultOpen);
+ const contentId = React.useId();
return (
<div className="border border-white/10 bg-white/[0.02]">
<button
type="button"
onClick={() => setIsOpen(!isOpen)}
+ aria-expanded={isOpen}
+ aria-controls={contentId}
className="w-full flex items-center justify-between px-4 py-2.5 hover:bg-white/[0.03] transition-colors group"
>
<h4 className="text-[11px] font-condensed font-bold uppercase tracking-[0.15em] text-gray-400 group-hover:text-gray-300 transition-colors">
{title}
</h4>
@@
- <div
- className={`overflow-hidden transition-all duration-300 ease-in-out ${
- isOpen ? 'max-h-[2000px] opacity-100' : 'max-h-0 opacity-0'
- }`}
- >
- <div className="px-4 pb-4 pt-2 space-y-4">
- {children}
- </div>
- </div>
+ {isOpen && (
+ <div id={contentId} className="px-4 pb-4 pt-2 space-y-4">
+ {children}
+ </div>
+ )}
</div>
);
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/pro-panel/controls/index.tsx` around lines 470 - 494, The toggle
button and hidden panel lack proper disclosure semantics and closed content
remains keyboard-focusable; update the toggle button (the element using
setIsOpen and isOpen) to include aria-expanded={isOpen} and aria-controls
pointing to a stable id, generate that id with React's useId or a stable string;
on the panel div (the container rendering {children}) set id={contentId} and
aria-hidden={!isOpen} and either unmount the children when closed (render
children only when isOpen) or ensure all interactive descendants are
inert/untabbable when closed (e.g., add tabIndex={-1} or use an inert polyfill)
so hidden content cannot receive focus. Ensure the button still toggles
setIsOpen and keep the existing visual classes.
There was a problem hiding this comment.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
| await response.json(); | ||
|
|
||
| toast({ | ||
| title: 'Story Generated!', | ||
| description: 'Your story has been created successfully.', | ||
| }); | ||
|
|
||
| resetStoryInput(); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's find the correct path for the API route
find . -name "route.ts" -o -name "route.js" | grep -E "api.*groq" | head -20Repository: IndieHub25/GroqTales
Length of output: 87
🏁 Script executed:
# Also check the exact location of the component file
find . -name "GenerateStoryButton.tsx" -o -name "GenerateStoryButton.ts" -o -name "GenerateStoryButton.jsx"Repository: IndieHub25/GroqTales
Length of output: 110
🏁 Script executed:
# Look at the directory structure to understand the project layout
ls -la | head -20Repository: IndieHub25/GroqTales
Length of output: 1284
🏁 Script executed:
# Examine the API route to see what it returns
cat -n ./app/api/groq/route.tsRepository: IndieHub25/GroqTales
Length of output: 5784
🏁 Script executed:
# Examine GenerateStoryButton.tsx to see the complete context around lines 59-66
cat -n ./components/pro-panel/GenerateStoryButton.tsxRepository: IndieHub25/GroqTales
Length of output: 4631
🏁 Script executed:
# Check what generateStoryWithProConfig returns and if it saves to database
grep -n "generateStoryWithProConfig" ./lib/groq-service.ts | head -5Repository: IndieHub25/GroqTales
Length of output: 117
🏁 Script executed:
# Look for story-related routes or pages that might handle viewing
find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) -path "*/app/*" | xargs grep -l "story.*\[.*id" 2>/dev/null | head -10Repository: IndieHub25/GroqTales
Length of output: 46
🏁 Script executed:
# Check if there's a stories collection/table or database operations
rg -n "stories|Story" lib/ --type ts --type tsx | grep -E "(db|save|create|insert)" | head -20Repository: IndieHub25/GroqTales
Length of output: 91
🏁 Script executed:
# Examine generateStoryWithProConfig function
sed -n '918,980p' ./lib/groq-service.tsRepository: IndieHub25/GroqTales
Length of output: 2236
🏁 Script executed:
# Check for database operations in the codebase
rg "db\.|database|create.*story|save.*story" lib/ --type ts -A 3 | head -40Repository: IndieHub25/GroqTales
Length of output: 2032
🏁 Script executed:
# Look for any story storage or API endpoints that might handle persistence
find ./app -type f \( -name "*.ts" -o -name "*.js" \) | xargs grep -l "story" 2>/dev/null | head -15Repository: IndieHub25/GroqTales
Length of output: 608
🏁 Script executed:
# Check generate-and-mint endpoint to see if it handles story persistence
head -100 ./app/api/generate-and-mint/route.tsRepository: IndieHub25/GroqTales
Length of output: 1198
🏁 Script executed:
# Check story/publish endpoint
head -80 ./app/api/story/publish/route.tsRepository: IndieHub25/GroqTales
Length of output: 2408
🏁 Script executed:
# Look for any localStorage or session storage of stories in the component
rg "localStorage|sessionStorage" components/ app/ --type ts --type js | head -20Repository: IndieHub25/GroqTales
Length of output: 1905
Generated story content is discarded — provide a way for users to access their result.
The API response containing the generated story is awaited on line 59 but never stored or used. After successful generation, the user receives a toast confirmation and the form resets, but the story content is lost. Consider storing the response in localStorage for temporary access, saving it to the database to generate a story ID for persistence, or including the story content in the toast with a copy-to-clipboard action.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/pro-panel/GenerateStoryButton.tsx` around lines 59 - 66, The
awaited API result from await response.json() in GenerateStoryButton.tsx is
never captured or used, so update the handler to assign that result to a
variable (e.g., const generated = await response.json()), then persist or
surface it: store generated content in localStorage under a key like
'generatedStory' for temporary access and/or call your saveStory API to persist
and return a storyId, update any component state so the UI can display the
generated story, and modify the toast to include either a brief preview and a
copy-to-clipboard action or the returned storyId; keep the existing
resetStoryInput() call but only after persisting/surfacing the generated story.
There was a problem hiding this comment.
@rohits1450 provide this way for users to access the results.
There was a problem hiding this comment.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
| <button | ||
| type="button" | ||
| onClick={() => onSelect(id)} | ||
| className={`group relative w-full text-left overflow-hidden transition-all shadow-comic group-hover:-translate-y-1 ${isSelected | ||
| ? 'border-4 border-noir-primary bg-noir-primary/20' | ||
| : 'border-4 border-black bg-white' | ||
| }`} |
There was a problem hiding this comment.
Expose selected-card semantics on the button.
This control is selectable but does not communicate selected state to assistive tech.
♿ Suggested fix
<button
type="button"
onClick={() => onSelect(id)}
+ aria-pressed={isSelected}
+ aria-label={`${title} (${caseId})`}
className={`group relative w-full text-left overflow-hidden transition-all shadow-comic group-hover:-translate-y-1 ${isSelected📝 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.
| <button | |
| type="button" | |
| onClick={() => onSelect(id)} | |
| className={`group relative w-full text-left overflow-hidden transition-all shadow-comic group-hover:-translate-y-1 ${isSelected | |
| ? 'border-4 border-noir-primary bg-noir-primary/20' | |
| : 'border-4 border-black bg-white' | |
| }`} | |
| <button | |
| type="button" | |
| onClick={() => onSelect(id)} | |
| aria-pressed={isSelected} | |
| aria-label={`${title} (${caseId})`} | |
| className={`group relative w-full text-left overflow-hidden transition-all shadow-comic group-hover:-translate-y-1 ${isSelected | |
| ? 'border-4 border-noir-primary bg-noir-primary/20' | |
| : 'border-4 border-black bg-white' | |
| }`} |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/pro-panel/GenreCard.tsx` around lines 39 - 45, The selectable card
button in GenreCard doesn't expose its selected state to assistive tech; update
the <button> in the GenreCard component to include aria-pressed={isSelected}
(and keep the existing onSelect(id) handler and class logic) so screen readers
receive the selected semantics; ensure the boolean isSelected prop is passed
into that attribute on the button element.
| <label className="font-marker text-3xl text-black mb-4 block"> | ||
| WHAT'S THE STORY, HERO?* | ||
| </label> | ||
| <textarea | ||
| value={storyInput.storyPrompt} | ||
| onChange={(e) => updateStoryInput('storyPrompt', e.target.value)} | ||
| placeholder="Enter your prompt here... (e.g., A cyberpunk detective hunting a ghost in the machine)" | ||
| rows={6} | ||
| className="w-full bg-white border-4 border-black p-6 font-display text-xl text-black focus:ring-0 focus:outline-none shadow-[inset_4px_4px_0_0_#ccc] resize-none placeholder:text-gray-400" | ||
| /> |
There was a problem hiding this comment.
Associate every label with its form control.
The labels are visible but not programmatically bound to <textarea>/<select>, so accessible names are unreliable.
♿ Suggested fix
-<label className="font-marker text-3xl text-black mb-4 block">
+<label htmlFor="story-prompt" className="font-marker text-3xl text-black mb-4 block">
...
-<textarea
+<textarea
+ id="story-prompt"
value={storyInput.storyPrompt}
...
-<label className="flex items-center gap-2 font-condensed text-black font-bold uppercase">
+<label htmlFor="plot-type" className="flex items-center gap-2 font-condensed text-black font-bold uppercase">
...
-<select
+<select
+ id="plot-type"
value={parameters.storyStructure.narrativeStyle}
...
-<label className="flex items-center gap-2 font-condensed text-black font-bold uppercase mt-6">
+<label htmlFor="conflict-type" className="flex items-center gap-2 font-condensed text-black font-bold uppercase mt-6">
...
-<select
+<select
+ id="conflict-type"
value={parameters.storyStructure.actStructure}
...
-<label className="flex items-center gap-2 font-condensed text-black font-bold uppercase">
+<label htmlFor="story-arc" className="flex items-center gap-2 font-condensed text-black font-bold uppercase">
...
-<select
+<select
+ id="story-arc"
value={parameters.storyStructure.resolutionStyle}
...Also applies to: 57-82, 85-97
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/pro-panel/StoryInputSection.tsx` around lines 38 - 47, The label
in StoryInputSection is not programmatically bound to the textarea, so add an id
on the textarea (e.g., id="storyPrompt") and set the corresponding label's
htmlFor to that id; update the updateStoryInput-bound textarea element and its
label in StoryInputSection, and apply the same pattern to the other
label/textarea or label/select pairs referenced (lines 57-82 and 85-97) so every
<label> uses htmlFor and each form control has a matching id.
| <label className="flex items-center gap-2 font-condensed text-black font-bold uppercase mt-6"> | ||
| <Zap className="w-4 h-4 text-[#8a0000]" /> Pacing | ||
| </label> | ||
| <div className="flex flex-wrap gap-2"> | ||
| {PACING_OPTIONS.map(p => ( | ||
| <button | ||
| key={p} | ||
| type="button" | ||
| onClick={() => updateParameter('storyStructure', 'pacing', p)} | ||
| className={`border-2 border-black px-3 py-2 font-condensed font-bold uppercase text-[10px] leading-tight text-center transition-colors whitespace-nowrap ${parameters.storyStructure.pacing === p | ||
| ? 'bg-black text-white' | ||
| : 'bg-white text-black hover:bg-black hover:text-white' | ||
| }`} | ||
| > | ||
| {formatLabel(p)} | ||
| </button> | ||
| ))} | ||
| </div> |
There was a problem hiding this comment.
Expose pacing selection state to assistive tech.
These buttons behave like a segmented toggle, but selected state is not announced.
♿ Suggested fix
-<div className="flex flex-wrap gap-2">
+<div className="flex flex-wrap gap-2" role="group" aria-label="Pacing">
{PACING_OPTIONS.map(p => (
<button
key={p}
type="button"
onClick={() => updateParameter('storyStructure', 'pacing', p)}
+ aria-pressed={parameters.storyStructure.pacing === p}
className={`border-2 border-black px-3 py-2 ...`}
>📝 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.
| <label className="flex items-center gap-2 font-condensed text-black font-bold uppercase mt-6"> | |
| <Zap className="w-4 h-4 text-[#8a0000]" /> Pacing | |
| </label> | |
| <div className="flex flex-wrap gap-2"> | |
| {PACING_OPTIONS.map(p => ( | |
| <button | |
| key={p} | |
| type="button" | |
| onClick={() => updateParameter('storyStructure', 'pacing', p)} | |
| className={`border-2 border-black px-3 py-2 font-condensed font-bold uppercase text-[10px] leading-tight text-center transition-colors whitespace-nowrap ${parameters.storyStructure.pacing === p | |
| ? 'bg-black text-white' | |
| : 'bg-white text-black hover:bg-black hover:text-white' | |
| }`} | |
| > | |
| {formatLabel(p)} | |
| </button> | |
| ))} | |
| </div> | |
| <label className="flex items-center gap-2 font-condensed text-black font-bold uppercase mt-6"> | |
| <Zap className="w-4 h-4 text-[`#8a0000`]" /> Pacing | |
| </label> | |
| <div className="flex flex-wrap gap-2" role="group" aria-label="Pacing"> | |
| {PACING_OPTIONS.map(p => ( | |
| <button | |
| key={p} | |
| type="button" | |
| onClick={() => updateParameter('storyStructure', 'pacing', p)} | |
| aria-pressed={parameters.storyStructure.pacing === p} | |
| className={`border-2 border-black px-3 py-2 font-condensed font-bold uppercase text-[10px] leading-tight text-center transition-colors whitespace-nowrap ${parameters.storyStructure.pacing === p | |
| ? 'bg-black text-white' | |
| : 'bg-white text-black hover:bg-black hover:text-white' | |
| }`} | |
| > | |
| {formatLabel(p)} | |
| </button> | |
| ))} | |
| </div> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/pro-panel/StoryInputSection.tsx` around lines 98 - 115, The pacing
option buttons in StoryInputSection act like a segmented toggle but don't
announce selection to assistive tech; update each rendered button (the map over
PACING_OPTIONS in StoryInputSection) to include an accessible toggle state by
adding aria-pressed={parameters.storyStructure.pacing === p} (and a descriptive
aria-label if needed, e.g., using formatLabel(p)), and keep the existing onClick
(updateParameter) and visual classes unchanged so screen readers will announce
which option is selected.
| const response = await fetch( | ||
| 'https://api.groq.com/openai/v1/chat/completions', | ||
| { | ||
| method: 'POST', | ||
| headers: { | ||
| Authorization: `Bearer ${groqApiKey}`, | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| body: JSON.stringify(requestBody), | ||
| } | ||
| ); |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Node.js fetch default timeout AbortController best practices server-side
💡 Result:
Key facts (Node server-side fetch)
- Node’s built-in
fetch()(Node 18+) is implemented on top of undici. (nodejs.org) fetch()does not expose undici’s low-level timeout knobs directly (because the Fetch spec doesn’t define them). If you need those, you must pass a custom dispatcher (undiciAgent). (github.com)- You should assume there is no “safe” application-level timeout by default; you must set your own overall deadline with aborting. (Node/undici also have internal stage timeouts—e.g., connect— that can still fire unless configured.) (github.com)
Best-practice pattern: always set an overall deadline with AbortSignal.timeout
If you’re on a Node version that supports it, prefer AbortSignal.timeout(ms) over hand-rolled setTimeout() + AbortController (cleaner, less error-prone). (developer.mozilla.org)
const res = await fetch(url, {
signal: AbortSignal.timeout(10_000), // hard deadline for the whole operation
});If you also need manual cancellation (e.g., request aborted by the caller), combine signals:
const controller = new AbortController();
const res = await fetch(url, {
signal: AbortSignal.any([
controller.signal, // manual cancel
AbortSignal.timeout(10_000), // overall deadline
]),
});(Using AbortSignal.any() with AbortSignal.timeout() is explicitly supported by the platform API.) (developer.mozilla.org)
When AbortSignal.timeout() is not enough: configure undici timeouts via a dispatcher
Some failures are governed by undici’s internal stage timeouts (not your overall deadline), e.g. connect timeout defaults (commonly observed as 10s). To change these, pass a custom undici Agent as dispatcher. (github.com)
import { Agent } from "undici";
const dispatcher = new Agent({
connect: { timeout: 20_000 }, // connect timeout
headersTimeout: 30_000, // time to receive response headers
bodyTimeout: 120_000, // inactivity / body receive window (use-case dependent)
});
const res = await fetch(url, {
dispatcher,
signal: AbortSignal.timeout(130_000), // still keep an overall deadline
});Notes:
- Use the abort signal for your end-to-end SLA, and undici timeouts for phase-level protection (connect/headers/body).
- If you need this app-wide, you can set a global dispatcher (undici), but per-request dispatchers are safer in multi-tenant / library code. (github.com)
Server-side operational best practices (quick checklist)
- Always set an overall deadline (
AbortSignal.timeout) for outbound calls. (developer.mozilla.org) - For long-running/streaming responses, tune undici headers/body/connect timeouts via a dispatcher (don’t rely on defaults). (github.com)
- Propagate cancellation: if the inbound client disconnects, abort downstream fetches (combine signals).
- Consume or cancel response bodies (especially if you’re doing many requests) to avoid tying up connections in the pool (undici/Fetch behavior differs from “fire and forget” expectations). (github.com)
Citations:
- 1: https://nodejs.org/en/learn/getting-started/fetch
- 2: How can I increase the headersTimeout with Node.js's fetch? nodejs/undici#1989
- 3:
fetch()in Node.js ignores connection timeout; no way to override 10s default nodejs/undici#4215 - 4: https://developer.mozilla.org/docs/Web/API/AbortSignal/timeout_static?utm_source=openai
- 5: https://developer.mozilla.org/docs/Web/API/AbortSignal/timeout_static?utm_source=openai
- 6:
fetch()in Node.js ignores connection timeout; no way to override 10s default nodejs/undici#4215 - 7: How can I increase the headersTimeout with Node.js's fetch? nodejs/undici#1989
- 8: https://developer.mozilla.org/docs/Web/API/AbortSignal/timeout_static?utm_source=openai
- 9: How can I increase the headersTimeout with Node.js's fetch? nodejs/undici#1989
- 10: https://github.com/nodejs/undici?utm_source=openai
🏁 Script executed:
cat -n lib/groq-service.ts | sed -n '970,1010p'Repository: IndieHub25/GroqTales
Length of output: 1656
🏁 Script executed:
cat -n package.json | head -30Repository: IndieHub25/GroqTales
Length of output: 1062
Add timeout protection to the Groq API request.
This fetch() call has no timeout. Node.js fetch does not have a safe default timeout, so upstream stalls will block request handling indefinitely.
Use AbortSignal.timeout() for a clean, built-in deadline (Node 20+ already supports it):
Suggested fix
const response = await fetch(
'https://api.groq.com/openai/v1/chat/completions',
{
method: 'POST',
headers: {
Authorization: `Bearer ${groqApiKey}`,
'Content-Type': 'application/json',
},
+ signal: AbortSignal.timeout(15000),
body: JSON.stringify(requestBody),
}
);If you also need manual cancellation (e.g., from an inbound request abort), combine signals with AbortSignal.any().
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/groq-service.ts` around lines 981 - 991, The fetch call in
lib/groq-service.ts that posts to
'https://api.groq.com/openai/v1/chat/completions' needs a timeout to avoid
indefinite stalls: create an AbortSignal via AbortSignal.timeout(desiredMs) and
pass it as the signal option to fetch (merge with any existing inbound request
signal using AbortSignal.any() if needed); update the call that constructs
requestBody and invokes fetch so it uses that signal and cleanly handles
AbortError timeouts in the surrounding function (e.g., catch and treat as a
timeout error).
| export const ModelSettingsSchema = z.object({ | ||
| modelSelection: z.enum([ | ||
| 'llama3-70b-8192', | ||
| 'llama3-8b-8192', | ||
| 'mixtral-8x7b-32768', | ||
| 'gemma-7b-it', | ||
| 'gemma2-9b-it', | ||
| 'llama-3.1-70b-versatile', | ||
| 'llama-3.1-8b-instant', | ||
| 'llama-3.2-90b-vision-preview', | ||
| 'llama-3.3-70b-versatile', | ||
| ]).describe('Groq AI model to use'), | ||
|
|
||
| temperature: z.number().min(0).max(2).describe('Randomness of output (0-2)'), | ||
|
|
||
| maxTokens: z.number().min(256).max(32768).describe('Maximum tokens to generate (256-32768)'), | ||
|
|
||
| topP: z.number().min(0).max(1).describe('Nucleus sampling threshold (0-1)'), | ||
|
|
||
| topK: z.number().min(1).max(100).int().describe('Top-K sampling (1-100)'), | ||
|
|
||
| frequencyPenalty: z.number().min(-2).max(2).describe('Frequency penalty (-2 to 2)'), | ||
|
|
||
| presencePenalty: z.number().min(-2).max(2).describe('Presence penalty (-2 to 2)'), | ||
|
|
||
| repetitionPenalty: z.number().min(0).max(2).describe('Repetition penalty (0-2)'), | ||
|
|
||
| stopSequences: z.array(z.string().max(50)).max(4).describe('Stop sequences (max 4)'), | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Groq API models 2025 2026 available list documentation
💡 Result:
Groq API (GroqCloud) model list (2025–2026) — official docs
1) Canonical “what models are available right now”
- Docs page: “Supported Models” (includes production + preview model IDs, plus “Compound” systems). [1]
- API endpoint to list all active models (recommended):
GET https://api.groq.com/openai/v1/models(returns JSON of active model IDs). [1]
2) Models shown in Groq’s “Supported Models” docs (current as of the latest docs page)
Production models (examples shown):
llama-3.1-8b-instantllama-3.3-70b-versatileopenai/gpt-oss-120bopenai/gpt-oss-20bwhisper-large-v3whisper-large-v3-turbo[1]
Production systems:
groq/compoundgroq/compound-mini[1]
Preview models (examples shown):
canopylabs/orpheus-arabic-saudicanopylabs/orpheus-v1-englishmeta-llama/llama-4-scout-17b-16e-instructmeta-llama/llama-prompt-guard-2-22mmeta-llama/llama-prompt-guard-2-86mmoonshotai/kimi-k2-instruct-0905openai/gpt-oss-safeguard-20bqwen/qwen3-32b[1]
3) Batch API model availability (subset list)
If you’re specifically using the Batch API, Groq documents the supported model IDs for batch chat/audio workloads (includes, e.g., meta-llama/llama-guard-4-12b). [2]
4) Deprecations you should be aware of (2025–2026)
Groq maintains an official Model Deprecation page with shutdown dates and replacement model IDs (e.g., deprecations for older Llama 3.x IDs, Kimi K2 original ID, PlayAI TTS models, etc.). [3]
Sources: [1] [2] [3]
🌐 Web query:
Groq API deprecated models 2025 llama3-70b-8192 mixtral gemma
💡 Result:
GroqCloud API model deprecations relevant to llama3-70b-8192 / Mixtral / Gemma (with 2025 shutdown dates):
| Deprecated model ID | Shutdown date | Recommended replacement(s) |
|---|---|---|
mixtral-8x7b-32768 |
2025-03-20 | mistral-saba-24b, llama-3.3-70b-versatile [1] |
llama3-70b-8192 |
2025-08-30 | llama-3.3-70b-versatile [2] |
llama3-8b-8192 |
2025-08-30 | llama-3.1-8b-instant [2] |
gemma2-9b-it |
2025-10-08 | llama-3.1-8b-instant [2] |
Related (not 2025, but often mentioned alongside these “classic” Groq IDs):
gemma-7b-itshutdown 2024-12-18, replacementgemma2-9b-it[1].- Groq also removed
gemma-7b-itandmixtral-8x7b-32768from the docs’ chat-completions model list in an April 21, 2025 changelog update [3].
Sources: GroqDocs deprecations page [1][2] and GroqDocs changelog [3].
[1] (console.groq.com)
[2] (console.groq.com)
[3] (console.groq.com)
Citations:
- 1: https://console.groq.com/docs/deprecations
- 2: https://console.groq.com/docs/deprecations
- 3: https://console.groq.com/docs/changelog?utm_source=openai
Remove deprecated Groq models from the modelSelection enum immediately.
The following models have already been shut down by Groq and will fail at runtime:
mixtral-8x7b-32768(deprecated 2025-03-20)llama3-70b-8192(deprecated 2025-08-30)llama3-8b-8192(deprecated 2025-08-30)gemma-7b-it(deprecated 2024-12-18)gemma2-9b-it(deprecated 2025-10-08)
Replace them with Groq's recommended current models: llama-3.1-8b-instant, llama-3.1-70b-versatile, and llama-3.3-70b-versatile. Additionally, verify that llama-3.2-90b-vision-preview is still supported by Groq API, as it is not listed in their current models documentation.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/schemas/proPanelSchemas.ts` around lines 274 - 302, Update the
ModelSettingsSchema's modelSelection enum to remove the deprecated Groq models
and replace them with the recommended current models: remove
"mixtral-8x7b-32768", "llama3-70b-8192", "llama3-8b-8192", "gemma-7b-it", and
"gemma2-9b-it" from the z.enum in ModelSettingsSchema and add
"llama-3.1-8b-instant", "llama-3.1-70b-versatile", and "llama-3.3-70b-versatile"
instead; also verify whether "llama-3.2-90b-vision-preview" remains supported by
the Groq API and remove or keep it accordingly in the same modelSelection enum.
| savePreset: (name, description) => { | ||
| const state = get(); | ||
| const newPreset: ProPreset = { | ||
| name, | ||
| description, | ||
| timestamp: Date.now(), | ||
| version: 1, | ||
| parameters: { ...state.parameters }, | ||
| }; | ||
|
|
||
| set({ | ||
| savedPresets: { | ||
| ...state.savedPresets, | ||
| [name.toLowerCase().replace(/\s+/g, '-')]: newPreset, | ||
| }, |
There was a problem hiding this comment.
Prevent silent preset overwrite on slug collisions.
Saving "My Preset" and then "my preset" currently targets the same normalized key and overwrites the earlier preset without warning.
💾 Suggested fix (non-breaking: auto-suffix unique key)
savePreset: (name, description) => {
const state = get();
+ const baseKey = name.toLowerCase().trim().replace(/\s+/g, '-');
+ let presetKey = baseKey;
+ let suffix = 2;
+ while (state.savedPresets[presetKey]) {
+ presetKey = `${baseKey}-${suffix++}`;
+ }
+
const newPreset: ProPreset = {
name,
description,
timestamp: Date.now(),
version: 1,
parameters: { ...state.parameters },
};
set({
savedPresets: {
...state.savedPresets,
- [name.toLowerCase().replace(/\s+/g, '-')]: newPreset,
+ [presetKey]: newPreset,
},
currentPresetName: name,
isModified: false,
lastSaved: Date.now(),
});
},🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/store/proPanelStore.ts` around lines 167 - 181, savePreset currently
normalizes the preset name to a slug and will silently overwrite an existing
entry in savedPresets on collisions; update savePreset to compute a baseSlug
(using the existing .toLowerCase().replace(/\s+/g, '-') logic) then, before
setting savedPresets, check savedPresets for that key and, if it already exists,
append a numeric suffix (-1, -2, ...) to the slug until an unused key is found;
keep ProPreset.name and description unchanged, store the preset under the unique
slug, and use get()/set() as before to update savedPresets so collisions no
longer overwrite existing presets.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
.gitignore (1)
69-70: Reconsider ignoringpublic/robots.txtby default.On Line 70, ignoring
/public/robots.txtcan mask intended SEO/source updates, since files inpublic/are usually authored assets. Prefer keeping it tracked unless it is definitely generated in this repo.Suggested adjustment
# Generated files /public/sitemap*.xml -/public/robots.txt +# /public/robots.txt🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.gitignore around lines 69 - 70, Remove the /public/robots.txt ignore entry from .gitignore so the repository tracks the authored robots file (leave /public/sitemap*.xml as-is); only re-add an ignore or generate rule for /public/robots.txt if you also add a clear generator step that produces it (refer to the literal pattern "/public/robots.txt" in the .gitignore to locate and remove or comment out).app/globals.css (1)
978-978: Minor: Stylelint prefers lowercasecurrentcolor.This is a stylistic consistency issue flagged by Stylelint's
value-keyword-caserule.✏️ Optional fix for lint compliance
.font-marker { font-family: var(--font-marker); font-weight: 700; transform: skewX(-4deg); - -webkit-text-stroke: 0.5px currentColor; + -webkit-text-stroke: 0.5px currentcolor; paint-order: stroke fill; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/globals.css` at line 978, Update the -webkit-text-stroke declaration in globals.css to use the lowercase keyword "currentcolor" (replace any occurrences of "currentColor" with "currentcolor") to satisfy Stylelint's value-keyword-case rule; locate the -webkit-text-stroke property in the file and make the value lowercase, then re-run linting to confirm the warning is resolved.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/globals.css`:
- Line 931: The .noir-input rule uses var(--font-condensed) but the CSS variable
--font-condensed is not defined; define the variable (e.g., in :root) mapping it
to the imported "Roboto Condensed" font-family with safe fallbacks and ensure
the font import exists (the unique symbols to update are the CSS variable
--font-condensed and the .noir-input class, and confirm the `@import` for "Roboto
Condensed" or equivalent font-face is present); add the variable declaration so
var(--font-condensed) resolves to something like "Roboto Condensed", Arial,
sans-serif.
- Line 917: The .noir-section-label uses var(--font-marker) but that CSS
variable is never defined; add a definition for --font-marker (pointing to
"Permanent Marker", fallback fonts) inside the :root block and mirror it in
.dark if you maintain separate theme variables so the Permanent Marker import is
actually applied; update :root (and .dark if present) to set --font-marker:
'Permanent Marker', cursive (and adjust any other font variables similarly) so
.noir-section-label no longer falls back to the browser default.
---
Nitpick comments:
In @.gitignore:
- Around line 69-70: Remove the /public/robots.txt ignore entry from .gitignore
so the repository tracks the authored robots file (leave /public/sitemap*.xml
as-is); only re-add an ignore or generate rule for /public/robots.txt if you
also add a clear generator step that produces it (refer to the literal pattern
"/public/robots.txt" in the .gitignore to locate and remove or comment out).
In `@app/globals.css`:
- Line 978: Update the -webkit-text-stroke declaration in globals.css to use the
lowercase keyword "currentcolor" (replace any occurrences of "currentColor" with
"currentcolor") to satisfy Stylelint's value-keyword-case rule; locate the
-webkit-text-stroke property in the file and make the value lowercase, then
re-run linting to confirm the warning is resolved.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
package.json (1)
100-103: Move@types/*packages todevDependenciesto avoid runtime bloat.
@types/node,@types/react, and@types/react-domare build-time typings and are usually not needed in production dependency installs. Other@typespackages in this project (@types/bcryptjs,@types/jest,@types/uuid) are already correctly placed indevDependencies; these three should follow the same pattern.♻️ Proposed manifest adjustment
"dependencies": { - "@types/node": "^20.17.30", - "@types/react": "^18.2.57", - "@types/react-dom": "^18.2.19", ... }, "devDependencies": { + "@types/node": "^20.17.30", + "@types/react": "^18.2.57", + "@types/react-dom": "^18.2.19", ... }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@package.json` around lines 100 - 103, The listed `@types` packages (`@types/node`, `@types/react`, `@types/react-dom`) are build-time only and should be moved from dependencies to devDependencies in package.json: remove these three entries from the top-level "dependencies" and add them under "devDependencies" with the same versions, then reinstall (npm/yarn/pnpm) and update the lockfile so the runtime install no longer includes these type packages; verify no runtime code imports require them and update CI/install scripts if they explicitly rely on types being present in dependencies.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/globals.css`:
- Line 981: The CSS declaration using the vendor property -webkit-text-stroke
currently uses the camelCase keyword "currentColor" which violates the Stylelint
value-keyword-case rule; update the value to the lowercase keyword
"currentcolor" (i.e., change -webkit-text-stroke: 0.5px currentColor; to
-webkit-text-stroke: 0.5px currentcolor;) so the property value matches the
configured linting rule.
- Line 1: The `@import` uses url(...) which violates the stylelint import-notation
rule; replace the url(...) usage in the globals.css `@import` statement with
string notation so the import reads as a plain quoted string (i.e., use the same
font URL but wrapped in quotes) to satisfy import-notation and keep the same
font families and query parameters.
- Around line 941-944: The CSS rule .noir-input:focus currently removes the
focus outline (outline: none) which hides keyboard focus; locate the
.noir-input:focus selector and remove the outline: none declaration and replace
it with an accessible visible focus style (e.g., a clearly visible outline or
focus ring/box-shadow matching the border color and appropriate outline-offset)
so keyboard users retain focus visibility while preserving the intended styling.
In `@lib/supabase/client.ts`:
- Around line 3-9: The code currently unconditionally falls back to placeholder
Supabase credentials (SUPABASE_URL and SUPABASE_ANON_KEY) which can hide missing
environment configuration; update the constant resolution used by createClient()
to not default to placeholders — read process.env.NEXT_PUBLIC_SUPABASE_URL and
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY directly and, if either is missing,
fail fast (throw an error or return a clear failure) so runtime paths reflect
the same fallback behavior as the server side; ensure the change targets the
SUPABASE_URL, SUPABASE_ANON_KEY constants and the createClient() function so
client/server mismatch and silent credential masking are avoided.
---
Nitpick comments:
In `@package.json`:
- Around line 100-103: The listed `@types` packages (`@types/node`, `@types/react`,
`@types/react-dom`) are build-time only and should be moved from dependencies to
devDependencies in package.json: remove these three entries from the top-level
"dependencies" and add them under "devDependencies" with the same versions, then
reinstall (npm/yarn/pnpm) and update the lockfile so the runtime install no
longer includes these type packages; verify no runtime code imports require them
and update CI/install scripts if they explicitly rely on types being present in
dependencies.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (3)
app/globals.csslib/supabase/client.tspackage.json
| const SUPABASE_URL = | ||
| process.env.NEXT_PUBLIC_SUPABASE_URL || 'https://placeholder.supabase.co'; | ||
| const SUPABASE_ANON_KEY = | ||
| process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || 'placeholder_anon_key'; | ||
|
|
||
| export function createClient() { | ||
| return createBrowserClient( | ||
| process.env.NEXT_PUBLIC_SUPABASE_URL || | ||
| (process.env.NEXT_PUBLIC_BUILD_MODE === 'true' ? 'https://dummy.supabase.co' : ''), | ||
| process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || | ||
| (process.env.NEXT_PUBLIC_BUILD_MODE === 'true' ? 'dummy_key' : '') | ||
| ); | ||
| return createBrowserClient(SUPABASE_URL, SUPABASE_ANON_KEY); |
There was a problem hiding this comment.
Avoid unconditional placeholder Supabase credentials in runtime paths.
Line 4 and Line 6 always fall back to placeholder values, which can mask missing env configuration and diverge from lib/supabase/server.ts fallback behavior. This can cause subtle client/server mismatch issues (e.g., auth/session instability) instead of failing fast.
🔧 Proposed fix
const SUPABASE_URL =
- process.env.NEXT_PUBLIC_SUPABASE_URL || 'https://placeholder.supabase.co';
+ process.env.NEXT_PUBLIC_SUPABASE_URL ||
+ (process.env.NEXT_PUBLIC_BUILD_MODE === 'true'
+ ? 'https://dummy.supabase.co'
+ : '');
const SUPABASE_ANON_KEY =
- process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || 'placeholder_anon_key';
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY ||
+ (process.env.NEXT_PUBLIC_BUILD_MODE === 'true' ? 'dummy_key' : '');
export function createClient() {
+ if (!SUPABASE_URL || !SUPABASE_ANON_KEY) {
+ throw new Error(
+ 'Missing NEXT_PUBLIC_SUPABASE_URL or NEXT_PUBLIC_SUPABASE_ANON_KEY'
+ );
+ }
return createBrowserClient(SUPABASE_URL, SUPABASE_ANON_KEY);
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/supabase/client.ts` around lines 3 - 9, The code currently
unconditionally falls back to placeholder Supabase credentials (SUPABASE_URL and
SUPABASE_ANON_KEY) which can hide missing environment configuration; update the
constant resolution used by createClient() to not default to placeholders — read
process.env.NEXT_PUBLIC_SUPABASE_URL and
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY directly and, if either is missing,
fail fast (throw an error or return a clear failure) so runtime paths reflect
the same fallback behavior as the server side; ensure the change targets the
SUPABASE_URL, SUPABASE_ANON_KEY constants and the createClient() function so
client/server mismatch and silent credential masking are avoided.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (6)
app/globals.css (5)
956-958: Add Firefox support for range slider thumb styling.The
::-webkit-slider-thumbpseudo-element only applies to WebKit/Blink browsers. Firefox uses::-moz-range-thumb.🔧 Proposed fix for cross-browser support
input[type="range"]::-webkit-slider-thumb { cursor: pointer; } + +input[type="range"]::-moz-range-thumb { + cursor: pointer; +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/globals.css` around lines 956 - 958, The rule only targets WebKit/Blink via input[type="range"]::-webkit-slider-thumb; add a matching Firefox rule using input[type="range"]::-moz-range-thumb with the same declarations (cursor: pointer) so the range slider thumb is styled in Firefox as well; update any related vendor-specific properties if present to keep behavior consistent across browsers.
908-912: Add-webkit-backdrop-filterfor Safari compatibility.Safari requires the
-webkit-prefix forbackdrop-filter. Without it, the blur effect won't render on Safari/iOS.🔧 Proposed fix
.noir-sidebar-panel { background: rgba(255, 255, 255, 0.02); + -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); border: 1px solid rgba(255, 255, 255, 0.05); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/globals.css` around lines 908 - 912, The .noir-sidebar-panel rule lacks the Safari-prefixed backdrop filter; add a -webkit-backdrop-filter: blur(8px) declaration alongside the existing backdrop-filter: blur(8px) in the .noir-sidebar-panel CSS block so Safari/iOS renders the blur (place the -webkit- prefixed property before or alongside the unprefixed property for compatibility).
987-989: Verify emoji filter produces expected results.
brightness(100)applies 100x brightness multiplier, which is unusually high. This may produce washed-out or unexpected results depending on the emoji renderer. Consider testing across browsers.A more common approach for forcing white:
💡 Alternative approach
.emoji-white { - filter: grayscale(1) brightness(100); + filter: grayscale(1) brightness(0) invert(1); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/globals.css` around lines 987 - 989, The .emoji-white rule uses filter: grayscale(1) brightness(100) which applies a 100x multiplier; change the brightness token to a valid, non-extreme value (e.g., brightness(100%) or brightness(1)) in the .emoji-white filter to avoid washed-out results and then test the .emoji-white appearance across target browsers/emoji renderers to confirm the visual outcome.
284-313: Consider respectingprefers-reduced-motionfor dossier animations.The page-turn animations use 3D transforms which may cause discomfort for users with motion sensitivity. While there's a
prefers-reduced-motionblock at lines 791-808, it doesn't cover.dossier-page-enterand.dossier-page-exit.♿ Proposed fix to add reduced motion support
Add the dossier animation classes to the reduced motion media query:
`@media` (prefers-reduced-motion: reduce) { .animate-twinkle, .animate-pulse, .animate-spin, .animate-float, - .animate-wiggle { + .animate-wiggle, + .dossier-page-enter, + .dossier-page-exit { animation: none !important; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/globals.css` around lines 284 - 313, Add reduced-motion support by overriding the page-turn animations inside the existing prefers-reduced-motion media query: target .dossier-page-enter, .dossier-page-exit and the `@keyframes` page-turn-in/ page-turn-out effects so they do not animate for users who prefer reduced motion; set animation: none (or animation-duration: 0s and animation-fill-mode: none) and ensure transform/opacity are reset (transform: none; opacity: 1) to prevent 3D motion while keeping the final visual state consistent for .dossier-page-enter and .dossier-page-exit.
961-967: Use Tailwind utility classes instead of direct CSS animation properties.The
accordion-downandaccordion-upkeyframes are defined intailwind.config.jsandtailwind.config.ts, but the CSS in app/globals.css uses rawanimationproperties that reference them. Instead of:.pro-panel-accordion [data-state="open"] > [role="region"] { animation: accordion-down 0.2s ease-out; }Use the Tailwind utility classes that are already configured (
animate-accordion-downandanimate-accordion-up), similar to howcomponents/ui/accordion.tsxapplies them. This is cleaner and ensures consistency across the codebase.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/globals.css` around lines 961 - 967, Replace the raw animation declarations in the .pro-panel-accordion selectors with Tailwind utility classes: remove `animation: accordion-down 0.2s ease-out;` and `animation: accordion-up 0.2s ease-out;` and instead apply the configured utilities (e.g. use `@apply animate-accordion-down` for `.pro-panel-accordion [data-state="open"] > [role="region"]` and `@apply animate-accordion-up` for `.pro-panel-accordion [data-state="closed"] > [role="region"]`) so the selectors use the `animate-accordion-down` / `animate-accordion-up` utilities defined in tailwind config, matching how `components/ui/accordion.tsx` does it.package.json (1)
160-160: Removeyarnfrom dependencies and optionally addpackageManagerfield for npm pinning.Line 160 lists
yarnas a production dependency, but the project uses npm exclusively (all scripts reference npm/next/nodemon, no yarn commands exist, and no yarn.lock file is present). The CHANGELOG explicitly notes this was incorrectly included. Remove the dependency safely, and optionally add a top-levelpackageManagerfield to document npm as the required package manager if toolchain pinning is desired.Suggested cleanup
{ "name": "groqtales", "version": "1.3.9", + "packageManager": "npm@10", ... "dependencies": { - "yarn": "^1.22.22", ... } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@package.json` at line 160, Remove the erroneous production dependency "yarn" from package.json's dependencies object (search for the "yarn": "^1.22.22" entry) and save the file; optionally add a top-level packageManager field (for example "packageManager": "npm@8" or another pinned npm version) to document the intended package manager if you want toolchain pinning. Ensure package.json remains valid JSON (commas adjusted) after removal and run npm install to confirm no changes are required in lockfiles or build scripts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@package.json`:
- Line 59: The listed packages are build/deploy tools and should be moved out of
runtime dependencies into devDependencies: remove "@cloudflare/next-on-pages",
"autoprefixer", "postcss", "tailwindcss", "eslint-config-next", "typescript",
and "yarn" from the dependencies section and add them under devDependencies
instead; also set the packageManager field (e.g., "packageManager":
"yarn@<version>") in package.json so yarn isn’t shipped as a runtime dependency.
Ensure package names exactly match the current entries to avoid duplication and
update any CI/build scripts if they referenced these as runtime deps.
---
Nitpick comments:
In `@app/globals.css`:
- Around line 956-958: The rule only targets WebKit/Blink via
input[type="range"]::-webkit-slider-thumb; add a matching Firefox rule using
input[type="range"]::-moz-range-thumb with the same declarations (cursor:
pointer) so the range slider thumb is styled in Firefox as well; update any
related vendor-specific properties if present to keep behavior consistent across
browsers.
- Around line 908-912: The .noir-sidebar-panel rule lacks the Safari-prefixed
backdrop filter; add a -webkit-backdrop-filter: blur(8px) declaration alongside
the existing backdrop-filter: blur(8px) in the .noir-sidebar-panel CSS block so
Safari/iOS renders the blur (place the -webkit- prefixed property before or
alongside the unprefixed property for compatibility).
- Around line 987-989: The .emoji-white rule uses filter: grayscale(1)
brightness(100) which applies a 100x multiplier; change the brightness token to
a valid, non-extreme value (e.g., brightness(100%) or brightness(1)) in the
.emoji-white filter to avoid washed-out results and then test the .emoji-white
appearance across target browsers/emoji renderers to confirm the visual outcome.
- Around line 284-313: Add reduced-motion support by overriding the page-turn
animations inside the existing prefers-reduced-motion media query: target
.dossier-page-enter, .dossier-page-exit and the `@keyframes` page-turn-in/
page-turn-out effects so they do not animate for users who prefer reduced
motion; set animation: none (or animation-duration: 0s and animation-fill-mode:
none) and ensure transform/opacity are reset (transform: none; opacity: 1) to
prevent 3D motion while keeping the final visual state consistent for
.dossier-page-enter and .dossier-page-exit.
- Around line 961-967: Replace the raw animation declarations in the
.pro-panel-accordion selectors with Tailwind utility classes: remove `animation:
accordion-down 0.2s ease-out;` and `animation: accordion-up 0.2s ease-out;` and
instead apply the configured utilities (e.g. use `@apply animate-accordion-down`
for `.pro-panel-accordion [data-state="open"] > [role="region"]` and `@apply
animate-accordion-up` for `.pro-panel-accordion [data-state="closed"] >
[role="region"]`) so the selectors use the `animate-accordion-down` /
`animate-accordion-up` utilities defined in tailwind config, matching how
`components/ui/accordion.tsx` does it.
In `@package.json`:
- Line 160: Remove the erroneous production dependency "yarn" from
package.json's dependencies object (search for the "yarn": "^1.22.22" entry) and
save the file; optionally add a top-level packageManager field (for example
"packageManager": "npm@8" or another pinned npm version) to document the
intended package manager if you want toolchain pinning. Ensure package.json
remains valid JSON (commas adjusted) after removal and run npm install to
confirm no changes are required in lockfiles or build scripts.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (4)
app/globals.csscomponents/madhava-helpbot.tsxlib/supabase/client.tspackage.json
🚧 Files skipped from review as they are similar to previous changes (1)
- lib/supabase/client.ts
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/globals.css`:
- Around line 970-976: biome is rejecting Tailwind directives like `@apply` used
in globals.css (e.g., selectors such as .pro-panel-accordion [data-state="open"]
> [role="region"]), so update your Biome config by enabling the CSS parser
option "tailwindDirectives": set css.parser.tailwindDirectives to true in
biome.json so Tailwind-specific syntax is recognized project-wide; this change
ensures `@apply` and other directives no longer produce parse errors.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (2)
app/globals.csspackage.json
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Drago-03
left a comment
There was a problem hiding this comment.
update with the requested changes as per coderabbit
- Fix prettier formatting and import order across all Pro Panel files - Replace error: any with error: unknown in API route - Add JSDoc to 93/93 exported symbols (100% coverage) - Update Groq model IDs: default llama-3.3-70b-versatile, remove deprecated - Remove duplicate noir footer from ProPanel component - Rename route /pro-panel → /create/ai-story-pro
Drago-03
left a comment
There was a problem hiding this comment.
update with requested changes and resolve conflicts
|
@Drago-03, sure will work on the requested changes and conflicts. |
|
@rohits1450 kindly update as we need to move forward with more changes. |
|
@Drago-03 Good afternoon sir, I'm currently out of station, will complete the updates and report it to you soon. |
…ges for conflicts)
…in story-generator
- Add htmlFor/id, aria-labelledby, aria-pressed to Pro Panel controls - Add Info icon tooltips with descriptions on all control types - Add rate limiting to /api/groq route (10 req/60s via Upstash Redis) - Add 70 unit tests for Zod schemas and preset save/load logic - Enhance ModelSettingsSection with power/cost/quality guide
# Conflicts: # .github/workflows/deployment.yml # README.md # SECURITY.md # app/community/creators/page.tsx # app/create/ai-story/page.tsx # app/create/page.tsx # app/nft-gallery/page.tsx # app/page.tsx # app/profile/[slug]/page.tsx # app/upload/page.tsx # components/back-to-top.tsx # components/footer.tsx # components/header.tsx # components/madhava-helpbot.tsx # lib/groq-service.ts # next.config.js # package-lock.json # package.json # server/backend.js # server/routes/stories.js # server/routes/users.js
…d tests - Remove duplicate inline descriptions; show descriptions only in tooltip hover - Add rich parameterKey tooltips (title/examples/powerLevel) to 28 controls - Create tooltip-enhanced.tsx with Radix Portal and proper hover behavior - Create parameterTooltips.ts with 30+ parameter descriptions - Fix mugshot overlay scanlines (globals.css gradient simplification) - Fix tooltip triggers: wrap Info icons in focusable <span> elements - Enhance /api/groq with request size limits, input validation, error categorization - Add sr-only hidden descriptions for screen reader accessibility - Add tests: Zod schema validation, preset save/load, API security
fdfa037 to
3512682
Compare
|
@Drago-03 kindly review the changes |
- Remove non-existent generateStoryWithProConfig import - Use generateStoryContent for Pro Panel generation path - Fix duplicate computed property keys (STORY_ANALYSIS/RECOMMENDATIONS share same model ID)
- Auto-format all files with Prettier - Replace any type with proper ContactInfoProps in contact/page.tsx
| {coverImagePreview ? ( | ||
| <div className="absolute inset-0"> | ||
| <img | ||
| src={coverImagePreview} |
Check failure
Code scanning / CodeQL
DOM text reinterpreted as HTML High
| <div className="flex-1 w-full bg-black/80 rounded-2xl border border-white/10 overflow-hidden relative group"> | ||
| {filePreview ? ( | ||
| <object | ||
| data={filePreview} |
Check failure
Code scanning / CodeQL
DOM text reinterpreted as HTML High
…clude node_modules from Tailwind scan
…DFLARE_API_TOKEN injection
- Resolved 20+ conflicts across app/, components/, server/, wiki/ - Took upstream: dashboard, ai-story/page (VedaScript Engine), home page, create-story-dialog, splash-screen, privacy-settings, all server files, wiki docs - Kept HEAD: blog pages (Monad branding), health API routes - Manually merged components/ai-story-generator.tsx — preserved NFT minting logic (handleMintNFT, session lock, idempotency hash, wallet connect UI) on top of upstream's VedaScript Engine UI overhaul - Fixed TypeScript compile errors in ai-story-generator.tsx post-merge - Fixed tsconfig.json ignoreDeprecations from '6.0' to '5.0' (TS 5.9.3)
# Conflicts: # app/actions/auth.ts # app/blog/blog-1/page.tsx # app/docs/page.tsx # app/gallery/page.tsx # app/marketplace/page.tsx # app/profile/[slug]/client.tsx # app/stories/[id]/client.tsx # app/stories/[id]/page.tsx # components/community-feed.tsx # components/footer.tsx # components/loading-screen.tsx # components/providers/web3-provider.tsx # components/settings/profile-form.tsx # hooks/use-monad.ts # hooks/use-user-role.ts # lib/blockchain.ts # server/routes/auth.js # server/routes/drafts.js # server/routes/stories.js
- Compute branch slug dynamically for NEXT_PUBLIC_URL in preview builds (was hardcoded to groqtales-preview.pages.dev which does not exist; now resolves to <branch-slug>.groqtales.pages.dev) - Expose dynamic preview URL as GitHub deployment environment URL via steps.deploy.outputs.url - Quote branch name in wrangler deploy command to handle slashes safely - Update wrangler.toml env var comments: replace MONAD_TEST_RPC_URL / MINTER_PRIVATE_KEY with ALCHEMY_ETH_MAINNET_HTTP_URL / MINT_AUTHORITY_PRIVATE_KEY / NEXT_PUBLIC_CONTRACT_ADDR - Fix [env.preview] name to groqtales (matches CF Pages project name)
|
Hi @Drago-03 , so I have implemented a few suggestions asked for from your side. Accessibility (components/pro-panel/controls/index.tsx) Tests (3 new test files) proPanelSchemas.test.ts — 40+ Zod schema validation tests across all 9 parameter categories PresetManager.test.ts — Save, load, delete, import/export, and edge case (malformed JSON, concurrent ops) coverage groq.test.ts — API route tests covering size limits, rate limiting, validation errors, and malformed input handling /api/groq Route Protection Parameter Tooltips (UI Documentation) cf-worker/node_modules Cleanup Additionally the coderabbit issues are also resolved with Cloudflare config, and the branch is up-to-date with the changes. |

Description
Issue Reference (Fixes #323):
Summary of Changes (high-level):
Context:
Type of Change
Technical Checklist
Frontend / UX / Accessibility
Backend / Database
Security & Privacy
.envfiles are committed.Code Quality
npm run lint(or equivalent) and resolved reported issues.anyby default.Testing Evidence
Environment: local
Commands:
Results / Logs:
Manual test steps:
Visual Proof (for UI / UX changes)
UI Updation
Dossier Style
Accordion type
Presets Save, Load and Transfer
Contributor Status
Review & Impact
Breaking Changes
Dependencies
Backward Compatibility / Migrations
Final Acknowledgements (Mandatory or will be marked invalid)
Summary by CodeRabbit
New Features
UI/Style Updates
Chores