nyxcore-systems
6 min read

A Day of Polish: Bringing Personas to Life and Supercharging Docs

Dive into a recent development session where we added visual flair to personas, revamped our documentation tab with search and sticky headers, and meticulously organized our future roadmap.

Next.jsTypeScripttRPCPrismaUXFrontendBackendProductivityDocumentation

Every now and then, you have one of those development sessions where everything just clicks. No major roadblocks, clean code, and a satisfying list of completed features. This was one of those days. We tackled three key areas: bringing our personas to life with unique portraits, significantly enhancing the user experience of our documentation tab, and, crucially, consolidating our entire backlog into a clear, actionable plan.

Let's break down what made this session so productive.

Breathing Life into Personas: The Portrait Gallery

Our Persona feature is central to our application, allowing users to define AI characters with specific traits and instructions. While functional, they lacked a certain visual charm. The goal was to add unique, expressive portrait images to each persona, making them feel more distinct and engaging.

The Technical Journey:

  1. Schema Foundation: The groundwork was laid in a prior session, adding an imageUrl String? to our Persona model in prisma/schema.prisma. This nullable field gives us flexibility.

    prisma
    model Persona {
      id        String   @id @default(cuid())
      name      String
      imageUrl  String?  // Our new addition!
      // ... other fields
    }
    
  2. Image Repository: We'd already prepared a collection of 54 diverse portrait PNGs, copied into public/images/personas/, and their paths cataloged in src/lib/constants.ts as PORTRAIT_IMAGES. This centralized array is crucial for managing available images.

  3. Backend Intelligence (tRPC): This is where it got interesting.

    • We updated our trpc.personas.list, create, and update procedures in src/server/trpc/routers/personas.ts to include the imageUrl field.
    • The clever bit is a new nextImage procedure. This procedure queries all currently used imageUrl values from existing personas, filters our PORTRAIT_IMAGES array for unused ones, and returns a random selection. If, by some chance, all images are in use, it gracefully falls back to picking any random image. This ensures variety and intelligent assignment.
  4. Frontend Integration (Next.js):

    • On the dashboard/personas/page.tsx, each persona card now proudly displays an 80x80 rounded-full portrait avatar, conditionally rendered only if imageUrl is present.
    • For creating new personas (dashboard/personas/new/page.tsx), we fetch a suggested imageUrl using trpc.personas.nextImage on load. Users see a preview of their persona's portrait during the customization phase and can shuffle for a new one with a "New portrait" button, calling refetchNextImage(). This imageUrl is then passed to the createMutation.

The result? A much more visually appealing and intuitive persona management experience.

Supercharging the Docs Tab: Search, Sticky Headers, and Section Numbers

Documentation is only useful if it's discoverable and easy to navigate. Our project documentation, stored as Markdown files, needed a significant UX overhaul. The focus was on improving readability, searchability, and navigation within the DocsTab component for our projects.

The Technical Journey:

All changes for the Docs tab were concentrated within src/app/(dashboard)/dashboard/projects/[id]/page.tsx, which houses the DocsTab component.

  1. Section Number Extraction: First, we introduced a helper function, extractSectionNumber(). This small but mighty utility parses filenames like 01-executive-summary.md to extract the §01 prefix. This adds a consistent visual cue for document order.

  2. Rewritten DocsTab Component: The component underwent a complete transformation:

    • Search Input: A prominent search input, complete with a Lucide Search icon, now allows users to filter documentation cards by their name or file path. This is a game-changer for finding specific information quickly.
    • Section Badges: The §{num} extracted by our helper is now displayed on each document card in the list view, providing immediate context.
    • Sticky Header in Doc View: When a user clicks to view a specific document, a new sticky top-0 z-10 bg-nyx-bg header appears. This fixed header provides:
      • A "back-to-list" button for quick navigation.
      • The document's title and its section badge.
      • A handy "Summary/Full" tab switcher, allowing users to toggle between a concise summary and the complete document content.
    • State Management: We refined our internal state, replacing a boolean fullView with a more descriptive docView: "summary" | "full" enum, making the component's behavior clearer.
    • handleBack() Helper: A new helper function simplifies resetting both selectedDoc and docView when navigating back to the document list.

These enhancements collectively transform the documentation experience from a static list into an interactive, highly navigable resource.

The Unsung Hero: The Comprehensive Task List

Beyond shipping features, a truly productive session also looks ahead. One of the most critical outcomes of this session was compiling a comprehensive, prioritized list of open tasks. We use a unique .memory/ file system to capture immediate thoughts, ideas, and micro-decisions during development.

The Process:

We meticulously combed through all 68 of our .memory/ files to extract and categorize 19 high-priority open items. This exercise provides immense clarity and a clear roadmap for future development.

Key Categories of Open Tasks:

  • Infrastructure/Build (3): Addressing ESLint configuration, adding Suspense boundaries, and refining .gitignore.
  • SSE Robustness (1): Ensuring safeEnqueue/safeClose for remaining Server-Sent Events endpoints.
  • Database/RLS (2): Implementing missing Row-Level Security policies and ensuring embedding column persistence.
  • Feature Gaps (5): Exploring organization repositories, persona success rate tracking, pagination, mobile UX improvements, and integrating Ollama models.
  • Testing (2): Writing workflow-metrics tests and fixing a specific Kimi test.
  • Docs Tab (2): Implementing Mermaid dark theme support and creating a docs index.
  • Manual QA (4): Verifying report generation, auto-fix end-to-end flows, refactor end-to-end flows, and analytics.

This isn't just a list; it's our tactical battle plan for the weeks to come, ensuring that no good idea gets lost and every critical improvement is tracked.

Lessons Learned (from prior sessions, but critical for continuity)

Even the smoothest sessions benefit from documenting past quirks. These aren't "pains" of this session, but rather critical notes carried forward that prevent future headaches.

  • npm install Cache Woes: We've learned to manage our npm install permission issues with a targeted --cache /tmp/npm-cache-nyxcore workaround. This sidesteps problems with root-owned ~/.npm/_cacache/ files, avoiding the pitfalls of sudo chown in non-interactive shells.
  • Prisma's Embedding Column Quirk: A persistent quirk is Prisma's tendency to drop our embedding vector(1536) column (a custom type) every time prisma db:push is run. Our current solution involves a quick ALTER TABLE workflow_insights ADD COLUMN IF NOT EXISTS embedding vector(1536); immediately after each push. This is a known behavior with custom database types and something we're tracking for a more permanent, migration-based solution.

Documenting these workarounds is crucial for team members and future self, ensuring development remains efficient.

Conclusion

What a session! We not only shipped two significant UX enhancements – bringing visual personality to our personas and making our documentation tab genuinely useful – but also solidified our roadmap and documented crucial operational workarounds. The codebase is cleaner, the path forward is clearer, and the user experience is tangibly better. Now, onto committing these changes and tackling that comprehensive task list!