Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

name: Python application

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

permissions:
contents: read

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
run: |
pytest
52 changes: 52 additions & 0 deletions components/ui/accordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import * as React from 'react';
import * as AccordionPrimitive from '@radix-ui/react-accordion';
import { ChevronDown } from 'lucide-react';

import { cn } from '../../lib/utils';

const Accordion = AccordionPrimitive.Root;

const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({ className, ...props }, ref) => (
<AccordionPrimitive.Item ref={ref} className={cn('border-b', className)} {...props} />
));
AccordionItem.displayName = 'AccordionItem';

const AccordionTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger
ref={ref}
className={cn(
'flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180',
className
)}
{...props}
>
{children}
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
));
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;

const AccordionContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Content
ref={ref}
className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The accordion component uses Tailwind CSS animation classes 'animate-accordion-up' and 'animate-accordion-down' that are not standard Tailwind utilities. These custom animations need to be defined in a tailwind.config file under the theme.extend.keyframes and theme.extend.animation sections. Without these definitions, the accordion animations will not work.

Suggested change
className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
className="overflow-hidden text-sm transition-all"

Copilot uses AI. Check for mistakes.
{...props}
>
<div className={cn('pb-4 pt-0', className)}>{children}</div>
</AccordionPrimitive.Content>
));

AccordionContent.displayName = AccordionPrimitive.Content.displayName;

export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
Comment on lines +1 to +52
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

This React/TypeScript component is being added to a Python-based project without any corresponding build configuration (package.json, tsconfig.json, or build tooling). The component imports dependencies from '@radix-ui/react-accordion' and 'lucide-react' that aren't defined anywhere in the project. Without a proper React/TypeScript build setup, this file cannot be compiled or used. Either add the necessary build configuration and dependencies, or remove this file if it was added by mistake.

Copilot uses AI. Check for mistakes.
42 changes: 42 additions & 0 deletions lib/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
export type ClassValue = string | number | null | false | undefined | ClassDictionary | ClassArray;

interface ClassDictionary {
[key: string]: any;
}
Comment on lines +3 to +5
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The ClassDictionary interface uses 'any' type which defeats the purpose of TypeScript's type safety. Consider using 'unknown' or a more specific type like 'boolean | string | number' to maintain type safety while still allowing flexible values.

Copilot uses AI. Check for mistakes.

interface ClassArray extends Array<ClassValue> {}

function toVal(mix: ClassValue): string {
let str = '';
if (typeof mix === 'string' || typeof mix === 'number') {
str += mix;
} else if (Array.isArray(mix)) {
for (const item of mix) {
const val = toVal(item);
if (val) {
if (str) str += ' ';
str += val;
}
}
} else if (mix && typeof mix === 'object') {
for (const key in mix as ClassDictionary) {
if ((mix as ClassDictionary)[key]) {
if (str) str += ' ';
str += key;
}
}
Comment on lines +22 to +27
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The for...in loop iterates over all enumerable properties including inherited ones from the prototype chain. This could lead to unexpected behavior if the object has inherited properties. Use Object.keys(), Object.entries(), or hasOwnProperty() to ensure only own properties are iterated.

Copilot uses AI. Check for mistakes.
}
return str;
}

export function cn(...inputs: ClassValue[]): string {
let str = '';
for (const input of inputs) {
const val = toVal(input);
if (val) {
if (str) str += ' ';
str += val;
}
}
return str;
}
Comment on lines +1 to +42

Choose a reason for hiding this comment

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

medium

The custom cn function is a re-implementation of what's provided by popular, battle-tested libraries like clsx and tailwind-merge. To improve maintainability, reduce the bundle size, and leverage highly-optimized code, it is recommended to use these standard libraries. Using tailwind-merge is especially beneficial as it intelligently handles conflicting Tailwind CSS classes.

Suggested change
export type ClassValue = string | number | null | false | undefined | ClassDictionary | ClassArray;
interface ClassDictionary {
[key: string]: any;
}
interface ClassArray extends Array<ClassValue> {}
function toVal(mix: ClassValue): string {
let str = '';
if (typeof mix === 'string' || typeof mix === 'number') {
str += mix;
} else if (Array.isArray(mix)) {
for (const item of mix) {
const val = toVal(item);
if (val) {
if (str) str += ' ';
str += val;
}
}
} else if (mix && typeof mix === 'object') {
for (const key in mix as ClassDictionary) {
if ((mix as ClassDictionary)[key]) {
if (str) str += ' ';
str += key;
}
}
}
return str;
}
export function cn(...inputs: ClassValue[]): string {
let str = '';
for (const input of inputs) {
const val = toVal(input);
if (val) {
if (str) str += ' ';
str += val;
}
}
return str;
}
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

Comment on lines +1 to +42
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

This TypeScript utility file is being added to a Python-based project without any TypeScript build configuration (tsconfig.json, package.json). The file cannot be compiled or used without proper TypeScript tooling. Either add the necessary build setup or remove this file if it was added by mistake.

Suggested change
export type ClassValue = string | number | null | false | undefined | ClassDictionary | ClassArray;
interface ClassDictionary {
[key: string]: any;
}
interface ClassArray extends Array<ClassValue> {}
function toVal(mix: ClassValue): string {
let str = '';
if (typeof mix === 'string' || typeof mix === 'number') {
str += mix;
} else if (Array.isArray(mix)) {
for (const item of mix) {
const val = toVal(item);
if (val) {
if (str) str += ' ';
str += val;
}
}
} else if (mix && typeof mix === 'object') {
for (const key in mix as ClassDictionary) {
if ((mix as ClassDictionary)[key]) {
if (str) str += ' ';
str += key;
}
}
}
return str;
}
export function cn(...inputs: ClassValue[]): string {
let str = '';
for (const input of inputs) {
const val = toVal(input);
if (val) {
if (str) str += ' ';
str += val;
}
}
return str;
}
// This file previously contained a TypeScript-only utility (`cn`) that
// required a TypeScript toolchain to compile. Since this is a Python-based
// project without TypeScript build configuration, the implementation has
// been removed to avoid unused/unbuildable code being checked in.

Copilot uses AI. Check for mistakes.
Loading
Loading