# Project export: Jade.AI

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 12.0
- Tagline: Cursor for Data Analysis
- Devpost: https://devpost.com/software/jadeai
- GitHub: https://github.com/erik-ksth/jade-ai.git
- Video: https://www.youtube.com/embed/NeEhUii2XYk?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Result: winner (Groq: Best Use of Groq)
- Team: 2 GitHub contributor(s) — aungbbo (3 commits), erik-ksth (3 commits)

## Devpost submission (written by the team)

### Overview

Tired of spending 70% of your time cleaning data? Jade.AI is an intelligent, all-in-one platform that lets you clean, analyze, and visualize your data using simple English commands. No code, no app-switching -- just results.

### Inspiration

As a college student passionate about data analysis, I’ve faced a frustrating reality: most of my project time isn't spent on analysis. It's spent on grunt work. I often wasted over 70% of my time just cleaning messy datasets. I was constantly switching between Excel for quick fixes, Pandas for heavy-duty cleaning, SQL for data pulling, and another tool like Tableau for visualization. The process was slow, exhausting, and fragmented. I wanted a single, intelligent, all-in-one solution that could handle all these tasks with just a few natural language commands. I envisioned a "Cursor for data analysis", an AI-first tool that would let me talk to my data. That's why we built Jade.AI.

### What it does

Jade.AI empowers anyone, regardless of their technical background, to work with data effortlessly. It’s as simple as 1-2-3: Upload your data (CSV, Excel, etc.). Type what you want in plain English. Get immediate results. You can clean, manipulate, visualize, and summarize, all in one seamless interface: “Clean the entire dataset” → Jade.AI intelligently handles missing values, removes duplicates, fixes inconsistent formatting, and standardizes data types. “Show a pie chart of the gender distribution” → Instantly generates a beautiful, interactive chart with auto-labeled insights. “What’s the average salary by department?” → Returns a clean summary table or a bar chart, your choice. “Summarize the key findings from this data” → Creates a concise, human-readable report.

### How we built it

We built Jade.AI to be fast, intelligent, and scalable. Frontend: We built a clean and responsive UI using Next.JS to create a seamless, single-page application experience. Backend: We used a Python (FastAPI) backend to create REST APIs that handles all user requests. LLM: We integrated the Groq API as the main LLM to power our natural language-to-action engine. Its incredible speed makes the conversation feel instantaneous. This model interprets user commands and translates them into executable operations. Data Manipulation: The legendary Pandas library is our workhorse. All data cleaning, transformation, and analysis commands are piped through a secure Pandas runtime. Data Visualization: We used Chart.js to generate rich, interactive, and beautiful visualizations that can be embedded directly in the chat interface.

### Challenges we ran into

Prompt Engineering is Deceptive: Getting the LLM to consistently and safely translate a vague command like "fix the messy columns" into the correct sequence of Pandas functions was our biggest hurdle. It required lots of iterative prompt design and building a robust validation layer. Maintaining Data State: Managing the state of a user's DataFrame across multiple, independent API calls was a huge challenge. Our initial implementation was slow and buggy before we used Groq. The Scope Creep: We had so many ideas! We wanted to add SQL database connections, advanced statistical modeling, and more. Focusing on our core loop (Upload → Clean → Analyze → Viz) within the time limit was a real test of discipline.

### Accomplishments we're proud of

It Actually Works! We have a fully functional, end-to-end demo. You can upload a genuinely messy dataset and walk away with clean data and insightful charts in under two minutes. The "Wow" Moment: The biggest win was testing it on a friend with zero coding experience. They were able to perform a complete data analysis task that would have taken them hours, and they did it with a smile. The "Cursor for Data" Feel: We successfully captured that "magic" feeling. It feels less like a tool and more like an intelligent collaborator, which was our core vision.

### What we learned

LLMs are Interpreters, Not Magicians: The real magic isn't just the LLM; it's the pipeline. The most critical work is building the robust "plumbing" that translates the LLM's intent into safe, executable code. UX is Everything: For a tool aimed at non-technical users, simplicity is the ultimate feature. A single, clear chat box proved to be infinitely more powerful than a complex dashboard. Speed is a Feature: Using an incredibly fast LLM (Groq) was a game-changer. When the analysis feels instant, it encourages creativity and exploration, which is the entire point of data analysis.

### What's next

We're just getting started. Our vision is to make data analysis as easy as having a conversation. More Data Connectors: We plan to add support for connecting directly to SQL databases, Google Sheets, and other live APIs. Advanced Analytics: We're working on moving beyond descriptive statistics to include predictive modeling. (e.g., "Forecast next quarter's sales based on this data"). "Analysis History": We want to create a feature that lets users track, revert, and branch their analysis steps, almost like Git for data analysis.

## README (from the GitHub repository)

No README available.

## Detected evidence (automated analysis)

Indexed codebase: 56 recognized source files, 253 KB.
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- Next.js (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (71 of 71)

```
.DS_Store
backend/.gitignore
backend/aiAgent.py
backend/api/__init__.py
backend/api/models.py
backend/api/routes/__init__.py
backend/api/routes/chat_routes.py
backend/api/routes/data_routes.py
backend/api/routes/upload_routes.py
backend/api/utils.py
backend/core/__init__.py
backend/core/config.py
backend/core/state.py
backend/main.py
backend/requirements.txt
backend/services/__init__.py
backend/services/chart_service.py
backend/services/dataframe_service.py
backend/services/execution_service.py
backend/services/file_service.py
backend/services/quality_service.py
backend/tools/__init__.py
backend/tools/code_executor.py
backend/tools/dataframe_utils.py
backend/tools/snapshot_manager.py
backend/vercel.json
backend/workflows/__init__.py
backend/workflows/cleaning_orchestrator.py
backend/workflows/nodes/__init__.py
backend/workflows/nodes/code_executor.py
backend/workflows/nodes/code_generator.py
backend/workflows/nodes/intent_classifier.py
backend/workflows/nodes/quality_assessor.py
backend/workflows/nodes/response_generator.py
backend/workflows/orchestrator.py
backend/workflows/README.md
backend/workflows/state.py
dirtyData/awkward_input.csv
dirtyData/Cafe Sales Dirty Data.csv
dirtyData/COLA Data ETL.csv
dirtyData/Women Concert Tour.csv
docs/architecture-discussion.md
frontend/.expo/README.md
frontend/.expo/settings.json
frontend/.gitignore
frontend/components.json
frontend/eslint.config.mjs
frontend/next.config.ts
frontend/package.json
frontend/postcss.config.mjs
frontend/README.md
frontend/src/app/globals.css
frontend/src/app/layout.tsx
frontend/src/app/page.tsx
frontend/src/components/ChatAgent.tsx
frontend/src/components/Dashboard.tsx
frontend/src/components/DataTable.tsx
frontend/src/components/FileExplorer.tsx
frontend/src/components/SimpleTabs.tsx
frontend/src/components/theme-provider.tsx
frontend/src/components/ThemeToggle.tsx
frontend/src/components/ui/alert-dialog.tsx
frontend/src/components/ui/button.tsx
frontend/src/components/ui/card.tsx
frontend/src/components/ui/dialog.tsx
frontend/src/components/ui/dropdown-menu.tsx
frontend/src/components/ui/input.tsx
frontend/src/components/ui/resizable.tsx
frontend/src/lib/utils.ts
frontend/tsconfig.json
shared/types.ts
```

### Dependencies

- backend/requirements.txt: annotated-types@==0.7.0, anyio@==4.11.0, cachetools@==6.2.0, certifi@==2025.10.5, charset-normalizer@==3.4.3, click@==8.3.0, et_xmlfile@==2.0.0, fastapi@==0.118.0, groq@==0.33.0, h11@==0.14.0, httpcore@==1.0.7, httpx@==0.27.2, idna@==3.10, langchain-core@==0.3.21, langchain-groq@==0.2.1, langgraph@==0.2.45, numpy@==2.3.3, openpyxl@==3.1.5, pandas@==2.3.3, pyasn1@==0.6.1, pyasn1_modules@==0.4.2, pydantic@==2.11.9, pydantic_core@==2.33.2, python-dateutil@==2.9.0.post0, python-dotenv@==1.0.0, python-multipart@==0.0.20, pytz@==2025.2, requests@==2.32.5, rsa@==4.9.1, six@==1.17.0, sniffio@==1.3.1, starlette@==0.48.0, tenacity@==9.1.2, typing_extensions@==4.15.0, typing-inspection@==0.4.2, tzdata@==2025.2, uagents@==0.22.10, urllib3@==2.5.0, uvicorn@==0.37.0, websockets@==15.0.1
- frontend/package.json: @eslint/eslintrc@^3, @radix-ui/react-alert-dialog@^1.1.15, @radix-ui/react-dialog@^1.1.15, @radix-ui/react-dropdown-menu@^2.1.16, @radix-ui/react-slot@^1.2.3, @tailwindcss/postcss@^4, @types/node@^20, @types/react@^19, @types/react-dom@^19, @types/react-syntax-highlighter@^15.5.13, ag-grid-community@^34.2.0, ag-grid-react@^34.2.0, chart.js@^4.5.0, class-variance-authority@^0.7.1, clsx@^2.1.1, eslint@^9, eslint-config-next@15.5.4, lucide-react@^0.544.0, next@15.5.4, next-themes@^0.4.6, react@19.1.0, react-chartjs-2@^5.3.0, react-dom@19.1.0, react-markdown@^10.1.0, react-resizable-panels@^3.0.6, react-rnd@^10.5.2, react-syntax-highlighter@^16.1.0, react-zoom-pan-pinch@^3.7.0, remark-gfm@^4.0.1, tailwind-merge@^3.3.1, tailwindcss@^4, tailwindcss-animate@^1.0.7, tw-animate-css@^1.4.0, typescript@^5

### Recent commits (newest first)

- doc: dashboard redesign plan
- feat: dark mode
- fix: cors origin
- feat: dynamic api url
- fix: resolve build errors (eslint, types, hooks)
- fix: disable Turbopack for production builds
- feat: collapsible code block
- fix: distinguish between real code and example code
- feat: bring front/back in dashabord, rectangle box
- feat: 3 initial suggestion
- fix: streaming scrolling mechanism
- feat: chat streaming
- feat: tabs for data table
- feat: collapasible sections
- update: clean aiAgent
- refactor: main.py and remove undo and quality feature
- Delete FETCH_AI_SETUP_GUIDE.md
- feat: use groq and data quality rating
- update
- update

## Key source files (fetched from GitHub, selected and truncated for size)

### docs/architecture-discussion.md

```markdown
# Jade AI - Architecture Discussion & Improvement Plan

> **Date**: January 14, 2026  
> **Status**: In Progress

---

## Overview

This document captures ongoing discussions about improving Jade AI's architecture, focusing on transforming it into a more robust BI-style analytics platform similar to Power BI or Tableau.

---

## 1. Dashboard Redesign

### Current State

- Free-form canvas with drag/resize (react-rnd)
- Chart.js for visualizations
- Static charts generated from pandas code
- Text and box elements for annotations

### Target State (BI-Style)

- Grid-based tile layout with snap-to-grid
- Interactive charts with cross-filtering
- Global slicers/filters affecting all visuals
- Drill-down capabilities
- Rich tooltips with additional metrics
- Save/load dashboard configurations

### Proposed Stack Changes

| Component     | Current                    | Proposed                           |
| ------------- | -------------------------- | ---------------------------------- |
| Layout        | react-rnd (free-form)      | react-grid-layout (grid tiles)     |
| Charts        | Chart.js / react-chartjs-2 | ECharts / echarts-for-react        |
| State         | Local component state      | Centralized filter state (Zustand) |
| Interactivity | None                       | Cross-filtering, drill-down        |

### Key Features to Implement

- [ ] Cross-filtering between charts
- [ ] Global slicer/filter components
- [ ] Grid-based dashboard layout
- [ ] Drill-down navigation
- [ ] Rich interactive tooltips

---

## 2. Multi-User & Persistence (Scalable Architecture)

### Current Problems

- `df_state` is a Python singleton — all users share the same dataframe
- Everything is in-memory — restart = data loss
- No user authentication or session isolation

### Target Architecture

#### Database Layer

| Component            | Technology           | Purpose                                        |
| -------------------- | -------------------- | ---------------------------------------------- |
| **Primary DB**       | PostgreSQL           | Users, sessions, metadata, dashboard configs   |
| **Cache**            | Redis                | Session state, hot data, pub/sub for real-time |
| **Object Storage**   | S3 / MinIO           | Uploaded files, exported Parquet files         |
| **Analytics Engine** | DuckDB (per-request) | Fast analytical queries on Parquet             |

#### Data Model (PostgreSQL)

```sql
-- Users & Auth
users (id, email, password_hash, created_at)
sessions (id, user_id, created_at, expires_at)

-- Projects & Files
projects (id, user_id, name, created_at)
datasets (id, project_id, name, storage_path, row_count, columns_json, created_at)
dataset_versions (id, dataset_id, version, storage_path, created_at, pandas_code)

-- Dashboards
dashboards (id, project_id, name, layout_json, created_at)
dashboard_widgets (id, dashboard_id, type, config_json, position_json)
dashboard_filters (id, dashboard_id, column, filter_type, default_valu
[truncated — 4308 more characters]
```

### backend/requirements.txt

```
annotated-types==0.7.0
anyio==4.11.0
cachetools==6.2.0
certifi==2025.10.5
charset-normalizer==3.4.3
click==8.3.0
et_xmlfile==2.0.0
fastapi==0.118.0
groq==0.33.0
h11==0.14.0
httpcore==1.0.7
httpx==0.27.2
idna==3.10
numpy==2.3.3
openpyxl==3.1.5
pandas==2.3.3
pyasn1==0.6.1
pyasn1_modules==0.4.2
pydantic==2.11.9
pydantic_core==2.33.2
python-dateutil==2.9.0.post0
python-dotenv==1.0.0
python-multipart==0.0.20
pytz==2025.2
requests==2.32.5
rsa==4.9.1
six==1.17.0
sniffio==1.3.1
starlette==0.48.0
tenacity==9.1.2
typing-inspection==0.4.2
typing_extensions==4.15.0
tzdata==2025.2
uagents==0.22.10
urllib3==2.5.0
uvicorn==0.37.0
websockets==15.0.1
langgraph==0.2.45
langchain-core==0.3.21
langchain-groq==0.2.1

```

### frontend/package.json

```
{
  "name": "frontend",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build",
    "start": "next start",
    "lint": "eslint"
  },
  "dependencies": {
    "@radix-ui/react-alert-dialog": "^1.1.15",
    "@radix-ui/react-dialog": "^1.1.15",
    "@radix-ui/react-dropdown-menu": "^2.1.16",
    "@radix-ui/react-slot": "^1.2.3",
    "ag-grid-community": "^34.2.0",
    "ag-grid-react": "^34.2.0",
    "chart.js": "^4.5.0",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "lucide-react": "^0.544.0",
    "next": "15.5.4",
    "next-themes": "^0.4.6",
    "react": "19.1.0",
    "react-chartjs-2": "^5.3.0",
    "react-dom": "19.1.0",
    "react-markdown": "^10.1.0",
    "react-resizable-panels": "^3.0.6",
    "react-rnd": "^10.5.2",
    "react-syntax-highlighter": "^16.1.0",
    "react-zoom-pan-pinch": "^3.7.0",
    "remark-gfm": "^4.0.1",
    "tailwind-merge": "^3.3.1",
    "tailwindcss-animate": "^1.0.7"
  },
  "devDependencies": {
    "@eslint/eslintrc": "^3",
    "@tailwindcss/postcss": "^4",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "@types/react-syntax-highlighter": "^15.5.13",
    "eslint": "^9",
    "eslint-config-next": "15.5.4",
    "tailwindcss": "^4",
    "tw-animate-css": "^1.4.0",
    "typescript": "^5"
  }
}

```

### backend/main.py

```python
"""Jade AI Backend - Main FastAPI Application"""

from dotenv import load_dotenv

# Load environment variables FIRST before any other imports
load_dotenv()

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from core.config import settings
from api.routes import upload_routes, chat_routes, data_routes

# Create FastAPI app
app = FastAPI(title=settings.APP_TITLE, version=settings.APP_VERSION)

# Configure CORS
app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.CORS_ORIGINS,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Health check endpoints
@app.get("/")
def root():
    """Root endpoint"""
    return {"message": "Welcome to Jade AI", "version": settings.APP_VERSION}


@app.get("/health")
def health_check():
    """Health check endpoint"""
    return {"status": "healthy"}


# Include routers
app.include_router(upload_routes.router)
app.include_router(chat_routes.router)
app.include_router(data_routes.router)


```

### frontend/src/app/layout.tsx

```typescript
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { ThemeProvider } from "@/components/theme-provider";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "Jade AI",
  description: "AI-powered data analysis platform",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        <ThemeProvider
          attribute="class"
          defaultTheme="system"
          enableSystem
          disableTransitionOnChange
        >
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
}

```

### frontend/src/app/page.tsx

```typescript
"use client";

import { useState, useRef, useEffect, useCallback } from "react";
import Image from "next/image";
import { UploadedData, ChatMessage, ChatRequest, ChartData, TextElement, BoxElement } from "../../../shared/types";
import DataTable from "@/components/DataTable";
import Dashboard from "@/components/Dashboard";
import ChatAgent from "@/components/ChatAgent";
import FileExplorer from "@/components/FileExplorer";
import SimpleTabs, { SimpleTab } from "@/components/SimpleTabs";
import { Button } from "@/components/ui/button";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
  DropdownMenuSeparator,
} from "@/components/ui/dropdown-menu";
import { PanelLeftClose, PanelRightClose, LayoutGrid } from "lucide-react";
import { ThemeToggle } from "@/components/ThemeToggle";
import {
  ResizableHandle,
  ResizablePanel,
  ResizablePanelGroup,
  ImperativePanelHandle,
} from "@/components/ui/resizable";

export default function Home() {
  const [files, setFiles] = useState<UploadedData[]>([]);
  const [selectedFileIndex, setSelectedFileIndex] = useState<number | null>(null);
  const [chatMessages, setChatMessages] = useState<ChatMessage[]>([]);
  const [isLoading, setIsLoading] = useState(false);
  const [charts, setCharts] = useState<ChartData[]>([]);
  const [textElements, setTextElements] = useState<TextElement[]>([]);
  const [boxElements, setBoxElements] = useState<BoxElement[]>([]);

  // Tab system state
  const [tabs, setTabs] = useState<SimpleTab[]>([]);
  const [activeTabId, setActiveTabId] = useState<string | null>(null);
  const [shouldOpenTabs, setShouldOpenTabs] = useState<number | null>(null);

  // Refs for collapsible panels
  const fileExplorerPanelRef = useRef<ImperativePanelHandle>(null);
  const chatPanelRef = useRef<ImperativePanelHandle>(null);

  // Track panel collapsed states
  const [isFileExplorerCollapsed, setIsFileExplorerCollapsed] = useState(false);
  const [isChatCollapsed, setIsChatCollapsed] = useState(false);

  // Get the currently selected file
  const uploadedData = selectedFileIndex !== null ? files[selectedFileIndex] : null;

  // Tab management functions
  const openTabForFile = useCallback((fileIndex: number) => {
    const file = files[fileIndex];
    const tabId = `data-${fileIndex}`;

    // Check if tab already exists
    const existingTab = tabs.find(t => t.id === tabId);
    if (existingTab) {
      setActiveTabId(tabId);
      return;
    }

    // Create new tab
    const newTab: SimpleTab = {
      id: tabId,
      title: file.filename,
      type: "data",
      fileIndex: fileIndex,
      content: null, // Will be rendered dynamically
    };

    setTabs(prev => [...prev, newTab]);
    setActiveTabId(tabId);
  }, [files, tabs]);

  const openDashboardTab = useCallback(() => {
    const tabId = "dashboard";

    // Check if tab already exists
    const existingTab = tabs.find(t => t.id === tabId);
    if (existingTab) {
      setActiveTabId(tabId);
      return;
    }

    // Create new tab
    const newTab: SimpleTab = {
      id: tabId,
      title: "Dashboard",
      type: "dashboard",
      content: null, // Will be rendered dynamically
    };

    setTabs(prev => [...prev, newTab]);
    setActiveTabId(tabId);
  }, [tabs]);

  const handleTabClose = (tabId: string) => {
    const newTabs = tabs.filter(t => t.id !== tabId);
    setTabs(newTabs);

    // If closing active tab, switch to last tab
    if (activeTabId === tabId) {
      setActiveTabId(newTabs.length > 0 ? newTabs[newTabs.length - 1].id : null);
    }
  };

  const handleTabReorder = (fromIndex: number, toIndex: number) => {
    const newTabs = [...tabs];
    const [movedTab] = newTabs.splice(fromIndex, 1);
    newTabs.splice(toIndex, 0, movedTab);
    setTabs(newTabs);
  };

  const handleTabChange = (tabId: string) => {
    setActiveTabId(tabId);

    // If it's a data tab, sync the selected file index
    if (tabId.startsWith('data-')) {
      const fileIndex = parseInt(tabId.replace('data-', ''));
      if (!isNaN(fileIndex) && fileIndex >= 0 && fileIndex < files.length) {
        setSelectedFileIndex(fileIndex);
      }
    }
  };

  // Clear backend state on page load/refresh
  useEffect(() => {
    const clearBackendState = async () => {
      try {
        const apiUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000';
        await fetch(`${apiUrl}/clear`, {
          method: 'POST',
        });
      } catch (error) {
        console.error('Error clearing backend state:', error);
      }
    };

    clearBackendState();
  }, []);

  // Open tabs after file upload
  useEffect(() => {
    if (shouldOpenTabs !== null && files.length > shouldOpenTabs) {
      const dataTabId = `data-${shouldOpenTabs}`;

      // Open data tab first (this will be the active tab)
      openTabForFile(shouldOpenTabs);

      // Open dashboard tab but don't focus it
      const dashboardTabId = "dashboard";
      const existingDashboardTab = tabs.find(t => t.id === dashboardTabId);
      if (!existingDashboardTab) {
        const newTab: SimpleTab = {
          id: dashboardTabId,
          title: "Dashboard",
          type: "dashboard",
          content: null,
        };
        setTabs(prev => [...prev, newTab]);
        // Keep focus on data tab by setting it as active
        setActiveTabId(dataTabId);
      }

      setShouldOpenTabs(null);
    }
  }, [files, shouldOpenTabs, tabs, openTabForFile]);

  // Keyboard shortcuts for toggling panels
  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      // Check if Cmd (Mac) or Ctrl (Windows/Linux) is pressed
      const isCmdOrCtrl = e.metaKey || e.ctrlKey;

      if (!isCmdOrCtrl) return;

      switch (e.key) {
        case '1': // Cmd/Ctrl + 1: Toggle Files
          e.preventDefault();
          if (isFileExplorerCollapsed) {
            fileExplorerPanelRef.current?.expand();
          } else {
            fileExplorerPanelRef.current?.collapse();
          
[truncated — 18897 more characters]
```

### frontend/next.config.ts

```typescript
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  /* config options here */
};

export default nextConfig;

```

### shared/types.ts

```typescript
export interface UploadedData {
  filename: string;
  rows: number;
  columns: number;
  column_names: string[];
  dtypes: Record<string, string>;
  preview: Record<string, unknown>[];
  data: Record<string, unknown>[];
  sheet_name?: string; // Optional: which sheet this data represents
  original_filename?: string; // Optional: the original file name before adding sheet suffix
}

export interface ChartData {
  type:
    | "line"
    | "bar"
    | "pie"
    | "doughnut"
    | "area"
    | "bubble"
    | "polarArea"
    | "radar"
    | "scatter"
    | "mixed";
  labels: string[];
  datasets: {
    label: string;
    data:
      | number[]
      | { x: number; y: number }[]
      | { x: number; y: number; r: number }[];
    borderColor?: string | string[];
    backgroundColor?: string | string[];
    fill?: boolean;
    tension?: number;
    type?: "line" | "bar" | "scatter";
    pointRadius?: number;
    pointHoverRadius?: number;
  }[];
  title: string;
}

export interface ChatMessage {
  role: "user" | "assistant";
  content: string;
  pandas_code?: string;
  has_code?: boolean;
  data_updated?: boolean;
  error?: string;
  print_output?: string;
  narrative_output?: string;
  chart_data?: ChartData;
}

export interface ChatRequest {
  message: string;
  chat_history: ChatMessage[];
}

export interface TextElement {
  id: string;
  content: string;
  x: number;
  y: number;
  width: number;
  height: number;
  fontSize?: number;
  fontWeight?: string;
  color?: string;
}

export interface BoxElement {
  id: string;
  x: number;
  y: number;
  width: number;
  height: number;
  backgroundColor?: string;
  borderColor?: string;
  borderWidth?: number;
  borderRadius?: number;
}

export interface ChatResponse {
  response: string;
  pandas_code: string | null;
  has_code: boolean;
  data_updated: boolean;
  updated_data: {
    data: Record<string, unknown>[];
    columns: string[];
    rows: number;
    dtypes: Record<string, string>;
  } | null;
  error: string | null;
  print_output: string | null;
  narrative_output: string | null;
  chart_data: ChartData | null;
}

```

### backend/api/__init__.py

```python
"""API layer for Jade AI backend"""

from . import models
from . import routes

__all__ = ["models", "routes"]

```

### backend/workflows/__init__.py

```python
"""LangGraph workflows for Jade AI"""

from .orchestrator import create_workflow_orchestrator

__all__ = ["create_workflow_orchestrator"]

```

[44 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]