From 73a46f3ba7184c9c124827c7be5d31eb9e4e2f9c Mon Sep 17 00:00:00 2001 From: Nicolas Varrot Date: Fri, 13 Feb 2026 00:13:12 +0000 Subject: [PATCH] fix: resolve all ESLint errors blocking CI releases - Extract ThemeContext and ToolCollapseContext definitions into separate files to satisfy react-refresh/only-export-components rule - Move useTheme and useToolCollapse hooks to dedicated hook files - Fix empty catch block in ThemeContext (add comment) - Replace Date.now() ref in ThinkingIndicator with useState initializer - Update all imports across components Closes feedback #58 --- FEEDBACK.md | 45 ++++++++++++++++++++++++++ src/components/Chat.tsx | 2 +- src/components/ThemeSwitcher.tsx | 3 +- src/components/ThinkingIndicator.tsx | 8 ++--- src/components/ToolCall.tsx | 2 +- src/contexts/ThemeContext.tsx | 24 +++----------- src/contexts/ThemeContextDef.ts | 18 +++++++++++ src/contexts/ToolCollapseContext.tsx | 27 +++------------- src/contexts/ToolCollapseContextDef.ts | 19 +++++++++++ src/hooks/useTheme.ts | 4 +++ src/hooks/useToolCollapse.ts | 6 ++++ 11 files changed, 108 insertions(+), 50 deletions(-) create mode 100644 src/contexts/ThemeContextDef.ts create mode 100644 src/contexts/ToolCollapseContextDef.ts create mode 100644 src/hooks/useTheme.ts create mode 100644 src/hooks/useToolCollapse.ts diff --git a/FEEDBACK.md b/FEEDBACK.md index 7dfbd2c..2c29d23 100644 --- a/FEEDBACK.md +++ b/FEEDBACK.md @@ -578,3 +578,48 @@ - This raw envelope should be cleaned up or collapsed in the UI - Strip or collapse the system scaffolding, show only the actual user message content - Could detect the <<>> pattern and extract just the message body + +## Item #55 +- **Date:** 2026-02-12 +- **Priority:** high +- **Status:** pending +- **Description:** Bug: Theme switcher (v1.20.0) doesn't work — clicking options closes the popup and nothing changes visually + - Root cause: the ThemeContext and CSS variables ARE being set correctly, but almost all components still use hardcoded Tailwind classes (bg-zinc-800, text-zinc-400, border-white/8, etc.) instead of the CSS variables + - The Tailwind classes override the CSS variables, so theme changes have zero visual effect + - FIX: Migrate ALL hardcoded color classes across ALL components to use the CSS variables from ThemeContext. This includes: ChatMessage, Sidebar, Header, Chat, LoginScreen, ConnectionBanner, ToolCall, CodeBlock, ImageBlock, ErrorBoundary, KeyboardShortcuts, ChatInput, TypingIndicator, DateSeparator, etc. + - Also check index.css / globals for hardcoded colors + - This is a multi-file migration — may take several runs. Do it systematically, component by component. + - TEST: after each component migration, verify Dark/Light/OLED all look correct + +## Item #56 +- **Date:** 2026-02-12 +- **Priority:** medium +- **Status:** pending +- **Source:** Josh (Bardak) +- **Description:** Drag & drop session reordering in sidebar + - Allow users to drag sessions to reorder them manually in the sidebar + - The custom order should persist in localStorage + - Works alongside the existing pin feature (pinned group stays on top, but order within groups is customizable) + +## Item #57 +- **Date:** 2026-02-12 +- **Priority:** medium +- **Status:** pending +- **Source:** Josh (Bardak) +- **Description:** Display agent thinking/reasoning content in chat + - When the agent uses thinking/reasoning (extended thinking), show the thinking content in a collapsible section + - Currently PinchChat shows a "Reasoning…" badge but doesn't display the actual thinking text + - Add an expandable block (like tool calls) that shows the thinking content when available + - Some models stream thinking — handle both streamed and final thinking blocks + +## Item #58 +- **Date:** 2026-02-13 +- **Priority:** high +- **Status:** pending +- **Description:** Fix CI lint errors blocking ALL release workflows since v1.14.0 + - 4 ESLint errors cause every release.yml run to fail: + 1. ThemeContext.tsx: exports useTheme hook alongside Provider component → react-refresh/only-export-components. Fix: move useTheme to a separate file or use eslint-disable. + 2. ThemeContext.tsx: empty catch block → no-empty. Fix: add a comment. + 3. ToolCollapseContext.tsx: same react-refresh/only-export-components issue. + 4. ToolCall.tsx:246: setState in useEffect → react-hooks/set-state-in-effect. Fix: use useSyncExternalStore or useCallback pattern. + - This is BLOCKING all GitHub Releases and Docker image builds. Fix ASAP. diff --git a/src/components/Chat.tsx b/src/components/Chat.tsx index da66522..ab41548 100644 --- a/src/components/Chat.tsx +++ b/src/components/Chat.tsx @@ -6,7 +6,7 @@ import type { ChatMessage, ConnectionStatus } from '../types'; import { Bot, ArrowDown, Loader2, ChevronsDownUp, ChevronsUpDown } from 'lucide-react'; import { useT } from '../hooks/useLocale'; import { getLocale, type TranslationKey } from '../lib/i18n'; -import { useToolCollapse } from '../contexts/ToolCollapseContext'; +import { useToolCollapse } from '../hooks/useToolCollapse'; interface Props { messages: ChatMessage[]; diff --git a/src/components/ThemeSwitcher.tsx b/src/components/ThemeSwitcher.tsx index 41fa43d..54174bb 100644 --- a/src/components/ThemeSwitcher.tsx +++ b/src/components/ThemeSwitcher.tsx @@ -1,6 +1,7 @@ import { useState, useRef, useEffect } from 'react'; import { Palette, Sun, Moon, Monitor, Check } from 'lucide-react'; -import { useTheme, type ThemeName, type AccentColor } from '../contexts/ThemeContext'; +import { useTheme } from '../hooks/useTheme'; +import type { ThemeName, AccentColor } from '../contexts/ThemeContextDef'; import { useT } from '../hooks/useLocale'; import type { TranslationKey } from '../lib/i18n'; diff --git a/src/components/ThinkingIndicator.tsx b/src/components/ThinkingIndicator.tsx index 5cadeeb..cf13f74 100644 --- a/src/components/ThinkingIndicator.tsx +++ b/src/components/ThinkingIndicator.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef } from 'react'; +import { useState, useEffect } from 'react'; import { Brain } from 'lucide-react'; import { useT } from '../hooks/useLocale'; @@ -10,14 +10,14 @@ import { useT } from '../hooks/useLocale'; export function ThinkingIndicator() { const t = useT(); const [elapsed, setElapsed] = useState(0); - const startRef = useRef(Date.now()); + const [start] = useState(() => Date.now()); useEffect(() => { const interval = setInterval(() => { - setElapsed(Math.floor((Date.now() - startRef.current) / 1000)); + setElapsed(Math.floor((Date.now() - start) / 1000)); }, 1000); return () => clearInterval(interval); - }, []); + }, [start]); const formatElapsed = (s: number) => { if (s < 60) return `${s}s`; diff --git a/src/components/ToolCall.tsx b/src/components/ToolCall.tsx index 4d565e9..837819c 100644 --- a/src/components/ToolCall.tsx +++ b/src/components/ToolCall.tsx @@ -3,7 +3,7 @@ import { ChevronRight, ChevronDown, Check, Copy, WrapText, AlignLeft } from 'luc import hljs from 'highlight.js/lib/common'; import { useT } from '../hooks/useLocale'; import { ImageBlock } from './ImageBlock'; -import { useToolCollapse } from '../contexts/ToolCollapseContext'; +import { useToolCollapse } from '../hooks/useToolCollapse'; type ToolColor = { border: string; bg: string; text: string; icon: string; glow: string; expandBorder: string; expandBg: string }; diff --git a/src/contexts/ThemeContext.tsx b/src/contexts/ThemeContext.tsx index c173e8c..3945191 100644 --- a/src/contexts/ThemeContext.tsx +++ b/src/contexts/ThemeContext.tsx @@ -1,23 +1,7 @@ -import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from 'react'; +import { useState, useEffect, useCallback, type ReactNode } from 'react'; +import { ThemeContext, type ThemeName, type AccentColor } from './ThemeContextDef'; -export type ThemeName = 'dark' | 'light' | 'oled'; -export type AccentColor = 'cyan' | 'violet' | 'emerald' | 'amber' | 'rose' | 'blue'; - -interface ThemeContextValue { - theme: ThemeName; - accent: AccentColor; - setTheme: (t: ThemeName) => void; - setAccent: (a: AccentColor) => void; -} - -const ThemeContext = createContext({ - theme: 'dark', - accent: 'cyan', - setTheme: () => {}, - setAccent: () => {}, -}); - -export const useTheme = () => useContext(ThemeContext); +export type { ThemeName, AccentColor } from './ThemeContextDef'; const STORAGE_KEY = 'pinchchat-theme'; @@ -145,7 +129,7 @@ function loadStored(): StoredTheme { const parsed = JSON.parse(raw); if (parsed.theme in themes && parsed.accent in accents) return parsed; } - } catch {} + } catch { /* ignore invalid stored JSON */ } return { theme: 'dark', accent: 'cyan' }; } diff --git a/src/contexts/ThemeContextDef.ts b/src/contexts/ThemeContextDef.ts new file mode 100644 index 0000000..cc7708f --- /dev/null +++ b/src/contexts/ThemeContextDef.ts @@ -0,0 +1,18 @@ +import { createContext } from 'react'; + +export type ThemeName = 'dark' | 'light' | 'oled'; +export type AccentColor = 'cyan' | 'violet' | 'emerald' | 'amber' | 'rose' | 'blue'; + +export interface ThemeContextValue { + theme: ThemeName; + accent: AccentColor; + setTheme: (t: ThemeName) => void; + setAccent: (a: AccentColor) => void; +} + +export const ThemeContext = createContext({ + theme: 'dark', + accent: 'cyan', + setTheme: () => {}, + setAccent: () => {}, +}); diff --git a/src/contexts/ToolCollapseContext.tsx b/src/contexts/ToolCollapseContext.tsx index 2a49a9c..4f57706 100644 --- a/src/contexts/ToolCollapseContext.tsx +++ b/src/contexts/ToolCollapseContext.tsx @@ -1,25 +1,10 @@ -import { createContext, useContext, useState, useCallback, type ReactNode } from 'react'; +import { useState, useCallback, type ReactNode } from 'react'; +import { ToolCollapseContext } from './ToolCollapseContextDef'; -type ToolCollapseState = 'none' | 'collapse-all' | 'expand-all'; - -interface ToolCollapseContextValue { - /** Global override: 'none' means each tool manages its own state */ - globalState: ToolCollapseState; - /** Monotonically increasing version — tool calls reset local state when this changes */ - version: number; - collapseAll: () => void; - expandAll: () => void; -} - -const ToolCollapseContext = createContext({ - globalState: 'none', - version: 0, - collapseAll: () => {}, - expandAll: () => {}, -}); +export { ToolCollapseContext } from './ToolCollapseContextDef'; export function ToolCollapseProvider({ children }: { children: ReactNode }) { - const [globalState, setGlobalState] = useState('none'); + const [globalState, setGlobalState] = useState<'none' | 'collapse-all' | 'expand-all'>('none'); const [version, setVersion] = useState(0); const collapseAll = useCallback(() => { @@ -38,7 +23,3 @@ export function ToolCollapseProvider({ children }: { children: ReactNode }) { ); } - -export function useToolCollapse() { - return useContext(ToolCollapseContext); -} diff --git a/src/contexts/ToolCollapseContextDef.ts b/src/contexts/ToolCollapseContextDef.ts new file mode 100644 index 0000000..f7e8253 --- /dev/null +++ b/src/contexts/ToolCollapseContextDef.ts @@ -0,0 +1,19 @@ +import { createContext } from 'react'; + +type ToolCollapseState = 'none' | 'collapse-all' | 'expand-all'; + +export interface ToolCollapseContextValue { + /** Global override: 'none' means each tool manages its own state */ + globalState: ToolCollapseState; + /** Monotonically increasing version — tool calls reset local state when this changes */ + version: number; + collapseAll: () => void; + expandAll: () => void; +} + +export const ToolCollapseContext = createContext({ + globalState: 'none', + version: 0, + collapseAll: () => {}, + expandAll: () => {}, +}); diff --git a/src/hooks/useTheme.ts b/src/hooks/useTheme.ts new file mode 100644 index 0000000..961bba4 --- /dev/null +++ b/src/hooks/useTheme.ts @@ -0,0 +1,4 @@ +import { useContext } from 'react'; +import { ThemeContext } from '../contexts/ThemeContextDef'; + +export const useTheme = () => useContext(ThemeContext); diff --git a/src/hooks/useToolCollapse.ts b/src/hooks/useToolCollapse.ts new file mode 100644 index 0000000..bc15b0d --- /dev/null +++ b/src/hooks/useToolCollapse.ts @@ -0,0 +1,6 @@ +import { useContext } from 'react'; +import { ToolCollapseContext } from '../contexts/ToolCollapseContextDef'; + +export function useToolCollapse() { + return useContext(ToolCollapseContext); +}