Skip to content

Feat/tickets work 4#193

Merged
ChristopherTrimboli merged 9 commits intodevfrom
feat/tickets-work-4
Jan 8, 2026
Merged

Feat/tickets work 4#193
ChristopherTrimboli merged 9 commits intodevfrom
feat/tickets-work-4

Conversation

@vercel
Copy link
Copy Markdown

vercel Bot commented Jan 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
eliza-cloud-v2 Ready Ready Preview, Comment Jan 8, 2026 6:06am
miniapp Error Error Jan 8, 2026 6:06am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 5, 2026

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

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


Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

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

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements multiple UX improvements and bug fixes related to agent creation, chat interface, and authentication flow.

Key Changes:

  • Made username a required field during agent creation with frontend validation
  • Changed default free credits from $5 to $1
  • Improved chat room title generation using AI instead of simple text truncation
  • Enhanced chat input with dynamic single-line/multi-line layout based on content
  • Streamlined wallet connection to show wallet options directly
  • Fixed avatar removal from chat sidebar and improved scroll behavior

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
lib/utils/character-names.ts Updated default character creation to use blank fields instead of auto-generated names and avatars
lib/services/room-title.ts Replaced simple text truncation with AI-powered title generation using GPT-4o-mini, added fallback logic for when AI fails
lib/privy-sync.ts Changed default initial credits from $5.0 to $1.0
lib/hooks/use-streaming-message.ts Added completeCalled flag to prevent duplicate onComplete callback invocations
components/layout/chat-sidebar.tsx Removed character avatar display and related imports from sidebar
components/chat/eliza-chat-interface.tsx Implemented dynamic single-line/multi-line chat input with auto-resize, improved scroll area for better UX
components/chat/character-build-mode.tsx Added username validation requiring non-empty value before saving
components/character-builder/character-form.tsx Added asterisk to username label to indicate required field
app/login/page.tsx Updated wallet login to use loginMethods parameter for direct wallet option display

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1103 to +1108
// Auto-resize textarea when inputText changes
useEffect(() => {
if (textareaRef.current) {
textareaRef.current.style.height = "auto";
textareaRef.current.style.height = Math.min(textareaRef.current.scrollHeight, 400) + "px";
}
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

The auto-resize implementation directly manipulates the DOM by setting style.height. While functional, this creates a potential race condition if multiple rapid input changes occur. The height is set to "auto" and then immediately to scrollHeight, but if the component re-renders between these operations, it could cause visual jank. Consider using a debounced approach or ensuring the operations are batched in a single frame using requestAnimationFrame.

Suggested change
// Auto-resize textarea when inputText changes
useEffect(() => {
if (textareaRef.current) {
textareaRef.current.style.height = "auto";
textareaRef.current.style.height = Math.min(textareaRef.current.scrollHeight, 400) + "px";
}
const resizeRafIdRef = useRef<number | null>(null);
// Auto-resize textarea when inputText changes
useEffect(() => {
if (!textareaRef.current) {
return;
}
// Cancel any pending resize to avoid race conditions/jank
if (resizeRafIdRef.current !== null) {
cancelAnimationFrame(resizeRafIdRef.current);
}
resizeRafIdRef.current = requestAnimationFrame(() => {
const textarea = textareaRef.current;
if (!textarea) return;
textarea.style.height = "auto";
textarea.style.height =
Math.min(textarea.scrollHeight, 400) + "px";
});
return () => {
if (resizeRafIdRef.current !== null) {
cancelAnimationFrame(resizeRafIdRef.current);
resizeRafIdRef.current = null;
}
};

Copilot uses AI. Check for mistakes.
import { cn } from "@/lib/utils";
import { LockOnButton } from "@/components/brand";
import { useChatStore } from "@/lib/stores/chat-store";
import { SidebarBottomPanel } from "./sidebar-bottom-panel";
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

The ElizaAvatar import is no longer used after removing the avatar display from the sidebar. This import should be removed.

Copilot uses AI. Check for mistakes.
}, [inputText]);

// Track if input is expanded (multiline mode)
const isExpanded = inputText.includes("\n") || inputText.length > 80;
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

The condition for switching between single-line and multi-line input modes uses a hardcoded length threshold of 80 characters. This value appears in multiple places (lines 1112, 1439, 1554) which creates maintenance risk. Consider extracting this to a named constant at the top of the component to make it easier to adjust and maintain consistency.

Copilot uses AI. Check for mistakes.
Comment on lines +1138 to +1143
// Don't focus if clicking on a button or dropdown
const target = e.target as HTMLElement;
if (target.closest('button') || target.closest('[role="menu"]') || target.closest('[data-radix-popper-content-wrapper]')) {
return;
}

Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

The selector check for preventing focus when clicking buttons uses a string-based query selector with hardcoded element types and attributes. The check for '[data-radix-popper-content-wrapper]' might not prevent focus if users click on nested elements within these components. Consider using stopPropagation on the actual button click handlers instead, or use a more robust event delegation approach.

Suggested change
// Don't focus if clicking on a button or dropdown
const target = e.target as HTMLElement;
if (target.closest('button') || target.closest('[role="menu"]') || target.closest('[data-radix-popper-content-wrapper]')) {
return;
}
// Only focus when clicking directly on the container, not on its children
if (e.currentTarget !== e.target) {
return;
}

Copilot uses AI. Check for mistakes.
Comment on lines +1115 to +1134
// Maintain focus when transitioning between layouts
useEffect(() => {
if (wasExpandedRef.current !== isExpanded) {
// Layout changed - restore focus to the appropriate input
requestAnimationFrame(() => {
if (isExpanded && textareaRef.current) {
textareaRef.current.focus();
// Move cursor to end of text
const len = textareaRef.current.value.length;
textareaRef.current.setSelectionRange(len, len);
} else if (!isExpanded && inputRef.current) {
inputRef.current.focus();
// Move cursor to end of text
const len = inputRef.current.value.length;
inputRef.current.setSelectionRange(len, len);
}
});
}
wasExpandedRef.current = isExpanded;
}, [isExpanded]);
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

The transition between single-line input and multi-line textarea could cause accessibility issues. Screen readers may not properly announce the change when the input element type changes. Consider adding an aria-live region or maintaining focus announcement to ensure users with assistive technology are aware of the layout change.

Copilot uses AI. Check for mistakes.
Comment on lines +109 to +111
const words = message.trim().split(/\s+/).slice(0, 6);
if (words.length >= 3) {
return capitalizeFirst(words.slice(0, 5).join(" "));
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

The fallback title generation logic at line 111 slices to 5 words but checks if words.length >= 3. This means messages with exactly 3 or 4 words will have all words included without ellipsis, but 5-word messages will also have all words without ellipsis (line 139 adds ellipsis only for messages with more than 5 words). Consider clarifying this logic or adjusting the threshold for consistency.

Suggested change
const words = message.trim().split(/\s+/).slice(0, 6);
if (words.length >= 3) {
return capitalizeFirst(words.slice(0, 5).join(" "));
const words = message.trim().split(/\s+/);
if (words.length >= 3) {
const titleWords = words.slice(0, 5);
const baseTitle = capitalizeFirst(
titleWords.join(" ").replace(/[.!?]+$/, ""),
);
return words.length > 5 ? baseTitle + "..." : baseTitle;

Copilot uses AI. Check for mistakes.
Comment on lines +196 to +209
() => {
completeCalled = true;
onComplete?.();
},
);
} catch (err) {
console.error("[Stream] Error processing final buffer:", err);
onError?.("Stream ended unexpectedly");
}
}
// Always call onComplete when stream ends, if not already called
if (!completeCalled) {
onComplete?.();
}
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

The onComplete callback can be called multiple times. While the new code adds a completeCalled flag to prevent duplicate calls, the flag is only set when wrapping onComplete for the final buffer processing (lines 196-199). However, when processSSEMessage is called in the regular message loop (line 236, which is outside the diff but calls onComplete directly), if a "done" event is received there, onComplete will be called without setting the completeCalled flag. This will cause onComplete to be called again at line 208. To fix this, the onComplete callback passed to processSSEMessage at line 236 should also use the same wrapper function.

Copilot uses AI. Check for mistakes.
import Link from "next/link";
import Image from "next/image";
import { useRouter, usePathname } from "next/navigation";
import { useRouter } from "next/navigation";
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

The usePathname import is no longer used after removing the pathname-based logic for avatar selection. This import should be removed to keep the code clean.

Suggested change
import { useRouter } from "next/navigation";

Copilot uses AI. Check for mistakes.
Comment on lines +62 to +64

const result = await generateText({
model: gateway.languageModel("openai/gpt-4o-mini"),
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

The AI title generation uses a hardcoded model "openai/gpt-4o-mini". Consider making this configurable through environment variables to allow flexibility in model selection and to avoid hardcoding infrastructure dependencies.

Suggested change
const result = await generateText({
model: gateway.languageModel("openai/gpt-4o-mini"),
const roomTitleModel =
process.env.ROOM_TITLE_MODEL || "openai/gpt-4o-mini";
const result = await generateText({
model: gateway.languageModel(roomTitleModel),

Copilot uses AI. Check for mistakes.
}

return finalTitle;
function capitalizeFirst(str: string): string {
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

The capitalizeFirst helper function doesn't handle empty strings safely. If an empty string is passed, charAt(0) will return an empty string and slice(1) will also return empty, but this could be made more explicit with a guard check to improve robustness.

Suggested change
function capitalizeFirst(str: string): string {
function capitalizeFirst(str: string): string {
if (str.length === 0) {
return str;
}

Copilot uses AI. Check for mistakes.
@ChristopherTrimboli ChristopherTrimboli merged commit 9e27b0d into dev Jan 8, 2026
11 of 15 checks passed
@claude
Copy link
Copy Markdown

claude Bot commented Jan 8, 2026

Comprehensive Code Review - PR #193

Executive Summary

This PR implements multiple UI/UX improvements with 221 additions and 51 deletions across 9 files. While the changes generally improve user experience, I've identified 2 Critical issues, 5 High severity issues, 8 Medium severity issues, and 6 Low severity issues that should be addressed before merging.


🔴 Critical Issues

1. Username Validation Bug (character-build-mode.tsx:171-173)

Issue: Validation doesn't check for empty strings, only undefined values.

Current Code:

if (\!character.username) {
  toast.error("Username is required");
  return;
}

Fix:

if (\!character.username?.trim()) {
  toast.error("Username is required");
  return;
}

// Add format validation
if (\!/^[a-zA-Z0-9_-]{3,20}$/.test(character.username)) {
  toast.error("Username must be 3-20 characters (letters, numbers, _ and - only)");
  return;
}

2. Race Condition in Focus Management (eliza-chat-interface.tsx:1192-1210)

Issue: Component could unmount between effect firing and requestAnimationFrame callback executing.

Fix: Add cleanup function with cancelAnimationFrame and try-catch error handling.


🟠 High Severity Issues

3. Event Delegation Performance (eliza-chat-interface.tsx:1213-1225)

Issue: Multiple closest() calls on every click + unnecessary dependency causes recreation on every input expansion.

4. Textarea Auto-resize Layout Thrashing (eliza-chat-interface.tsx:1179-1185)

Issue: Triggers 2 layout recalculations per keystroke. Should use ResizeObserver instead.

5. Avatar Removal Hurts UX (chat-sidebar.tsx:247-258)

Issue: Users lose visual confirmation of selected character.

6. Double Callback Risk (use-streaming-message.ts:180-209)

Issue: Completion callback might be called twice or inconsistently. Need safe wrapper function.

7. AI Title Generation - No Timeout (room-title.ts:67-75)

Issue: Could hang indefinitely. No rate limiting, error budget, or caching implemented.


🟡 Medium Severity Issues

  1. Code Duplication - Compact and expanded layouts duplicate 400+ lines of button/control code
  2. Missing Error Handling - Wallet login has no try-catch
  3. Unused State Variables - copiedAddress, showTokens, tokensRef declared but never used
  4. No Input Sanitization - AI prompt vulnerable to prompt injection
  5. Magic Number - Expansion threshold (80) should be extracted as constant
  6. Unused Imports - Copy, Check icons imported but not used
  7. Blocking Database Update - Room title update blocks message flow
  8. Empty Buffer Handling - Needs comment explaining behavior

🔵 Low Severity Issues

  1. Missing accessibility attributes on input container
  2. Breaking API change in createDefaultCharacter needs documentation
  3. Credits change from $5 to $1 needs changelog entry
  4. Scroll hierarchy change needs performance testing with 100+ messages
  5. Overly specific fallback patterns in title generation

📊 Summary by Severity

Severity Count Must Fix Before Merge?
Critical 2 ✅ Yes
High 5 ✅ Yes
Medium 8 ⚠️ Recommended
Low 6 ℹ️ Nice to have

🎯 Recommended Action Items

Must Fix:

  1. ✅ Add proper username validation with format checking
  2. ✅ Fix race condition in focus management
  3. ✅ Add timeout/error handling for AI title generation
  4. ✅ Optimize textarea auto-resize to prevent layout thrashing
  5. ✅ Fix event delegation performance

Should Fix:

  1. ⚠️ Extract shared components to reduce duplication
  2. ⚠️ Remove unused state and imports
  3. ⚠️ Add error handling for wallet login
  4. ⚠️ Sanitize AI prompt inputs

Nice to Have:

  1. ℹ️ Document breaking changes
  2. ℹ️ Add accessibility attributes
  3. ℹ️ Performance test scroll changes

🌟 Positive Aspects

  1. ✨ Excellent UX improvements with dynamic input expansion
  2. ✨ Better Privy integration with specialized hooks
  3. ✨ AI-powered room titles add professional polish
  4. ✨ More robust completion callback handling
  5. ✨ Cleaner, streamlined UI

Great work on the UX improvements! The dynamic input expansion is particularly nice. Once the critical issues are addressed, this will be a solid enhancement to the user experience.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants