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
This commit is contained in:
45
FEEDBACK.md
45
FEEDBACK.md
@@ -578,3 +578,48 @@
|
|||||||
- This raw envelope should be cleaned up or collapsed in the UI
|
- 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
|
- Strip or collapse the system scaffolding, show only the actual user message content
|
||||||
- Could detect the <<<EXTERNAL_UNTRUSTED_CONTENT>>> pattern and extract just the message body
|
- Could detect the <<<EXTERNAL_UNTRUSTED_CONTENT>>> 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.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type { ChatMessage, ConnectionStatus } from '../types';
|
|||||||
import { Bot, ArrowDown, Loader2, ChevronsDownUp, ChevronsUpDown } from 'lucide-react';
|
import { Bot, ArrowDown, Loader2, ChevronsDownUp, ChevronsUpDown } from 'lucide-react';
|
||||||
import { useT } from '../hooks/useLocale';
|
import { useT } from '../hooks/useLocale';
|
||||||
import { getLocale, type TranslationKey } from '../lib/i18n';
|
import { getLocale, type TranslationKey } from '../lib/i18n';
|
||||||
import { useToolCollapse } from '../contexts/ToolCollapseContext';
|
import { useToolCollapse } from '../hooks/useToolCollapse';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
messages: ChatMessage[];
|
messages: ChatMessage[];
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useState, useRef, useEffect } from 'react';
|
import { useState, useRef, useEffect } from 'react';
|
||||||
import { Palette, Sun, Moon, Monitor, Check } from 'lucide-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 { useT } from '../hooks/useLocale';
|
||||||
|
|
||||||
import type { TranslationKey } from '../lib/i18n';
|
import type { TranslationKey } from '../lib/i18n';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect, useRef } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { Brain } from 'lucide-react';
|
import { Brain } from 'lucide-react';
|
||||||
import { useT } from '../hooks/useLocale';
|
import { useT } from '../hooks/useLocale';
|
||||||
|
|
||||||
@@ -10,14 +10,14 @@ import { useT } from '../hooks/useLocale';
|
|||||||
export function ThinkingIndicator() {
|
export function ThinkingIndicator() {
|
||||||
const t = useT();
|
const t = useT();
|
||||||
const [elapsed, setElapsed] = useState(0);
|
const [elapsed, setElapsed] = useState(0);
|
||||||
const startRef = useRef(Date.now());
|
const [start] = useState(() => Date.now());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
setElapsed(Math.floor((Date.now() - startRef.current) / 1000));
|
setElapsed(Math.floor((Date.now() - start) / 1000));
|
||||||
}, 1000);
|
}, 1000);
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, []);
|
}, [start]);
|
||||||
|
|
||||||
const formatElapsed = (s: number) => {
|
const formatElapsed = (s: number) => {
|
||||||
if (s < 60) return `${s}s`;
|
if (s < 60) return `${s}s`;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { ChevronRight, ChevronDown, Check, Copy, WrapText, AlignLeft } from 'luc
|
|||||||
import hljs from 'highlight.js/lib/common';
|
import hljs from 'highlight.js/lib/common';
|
||||||
import { useT } from '../hooks/useLocale';
|
import { useT } from '../hooks/useLocale';
|
||||||
import { ImageBlock } from './ImageBlock';
|
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 };
|
type ToolColor = { border: string; bg: string; text: string; icon: string; glow: string; expandBorder: string; expandBg: string };
|
||||||
|
|
||||||
|
|||||||
@@ -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 { ThemeName, AccentColor } from './ThemeContextDef';
|
||||||
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<ThemeContextValue>({
|
|
||||||
theme: 'dark',
|
|
||||||
accent: 'cyan',
|
|
||||||
setTheme: () => {},
|
|
||||||
setAccent: () => {},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const useTheme = () => useContext(ThemeContext);
|
|
||||||
|
|
||||||
const STORAGE_KEY = 'pinchchat-theme';
|
const STORAGE_KEY = 'pinchchat-theme';
|
||||||
|
|
||||||
@@ -145,7 +129,7 @@ function loadStored(): StoredTheme {
|
|||||||
const parsed = JSON.parse(raw);
|
const parsed = JSON.parse(raw);
|
||||||
if (parsed.theme in themes && parsed.accent in accents) return parsed;
|
if (parsed.theme in themes && parsed.accent in accents) return parsed;
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch { /* ignore invalid stored JSON */ }
|
||||||
return { theme: 'dark', accent: 'cyan' };
|
return { theme: 'dark', accent: 'cyan' };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
18
src/contexts/ThemeContextDef.ts
Normal file
18
src/contexts/ThemeContextDef.ts
Normal file
@@ -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<ThemeContextValue>({
|
||||||
|
theme: 'dark',
|
||||||
|
accent: 'cyan',
|
||||||
|
setTheme: () => {},
|
||||||
|
setAccent: () => {},
|
||||||
|
});
|
||||||
@@ -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';
|
export { ToolCollapseContext } from './ToolCollapseContextDef';
|
||||||
|
|
||||||
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<ToolCollapseContextValue>({
|
|
||||||
globalState: 'none',
|
|
||||||
version: 0,
|
|
||||||
collapseAll: () => {},
|
|
||||||
expandAll: () => {},
|
|
||||||
});
|
|
||||||
|
|
||||||
export function ToolCollapseProvider({ children }: { children: ReactNode }) {
|
export function ToolCollapseProvider({ children }: { children: ReactNode }) {
|
||||||
const [globalState, setGlobalState] = useState<ToolCollapseState>('none');
|
const [globalState, setGlobalState] = useState<'none' | 'collapse-all' | 'expand-all'>('none');
|
||||||
const [version, setVersion] = useState(0);
|
const [version, setVersion] = useState(0);
|
||||||
|
|
||||||
const collapseAll = useCallback(() => {
|
const collapseAll = useCallback(() => {
|
||||||
@@ -38,7 +23,3 @@ export function ToolCollapseProvider({ children }: { children: ReactNode }) {
|
|||||||
</ToolCollapseContext.Provider>
|
</ToolCollapseContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useToolCollapse() {
|
|
||||||
return useContext(ToolCollapseContext);
|
|
||||||
}
|
|
||||||
|
|||||||
19
src/contexts/ToolCollapseContextDef.ts
Normal file
19
src/contexts/ToolCollapseContextDef.ts
Normal file
@@ -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<ToolCollapseContextValue>({
|
||||||
|
globalState: 'none',
|
||||||
|
version: 0,
|
||||||
|
collapseAll: () => {},
|
||||||
|
expandAll: () => {},
|
||||||
|
});
|
||||||
4
src/hooks/useTheme.ts
Normal file
4
src/hooks/useTheme.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
import { ThemeContext } from '../contexts/ThemeContextDef';
|
||||||
|
|
||||||
|
export const useTheme = () => useContext(ThemeContext);
|
||||||
6
src/hooks/useToolCollapse.ts
Normal file
6
src/hooks/useToolCollapse.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
import { ToolCollapseContext } from '../contexts/ToolCollapseContextDef';
|
||||||
|
|
||||||
|
export function useToolCollapse() {
|
||||||
|
return useContext(ToolCollapseContext);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user