fix: tag coloring code duplications cleand up
This commit is contained in:
@@ -14,7 +14,7 @@ import { TagInput } from '../TagInput';
|
|||||||
import { ChatPanel } from '../ChatPanel';
|
import { ChatPanel } from '../ChatPanel';
|
||||||
import { ImportAnalysisView } from '../ImportAnalysisView';
|
import { ImportAnalysisView } from '../ImportAnalysisView';
|
||||||
import { MetadataDiffPanel } from '../MetadataDiffPanel';
|
import { MetadataDiffPanel } from '../MetadataDiffPanel';
|
||||||
import { AutoSaveManager } from '../../utils';
|
import { AutoSaveManager, getContrastColor } from '../../utils';
|
||||||
import { parseMacros, getMacro } from '../../macros/registry';
|
import { parseMacros, getMacro } from '../../macros/registry';
|
||||||
import { InsertModal } from '../InsertModal';
|
import { InsertModal } from '../InsertModal';
|
||||||
import { AISuggestionsModal, AISuggestions } from '../AISuggestionsModal/AISuggestionsModal';
|
import { AISuggestionsModal, AISuggestions } from '../AISuggestionsModal/AISuggestionsModal';
|
||||||
@@ -2070,23 +2070,6 @@ interface TagDataWithColor {
|
|||||||
color?: string;
|
color?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get contrasting text color for background
|
|
||||||
const getContrastColor = (hex: string): string => {
|
|
||||||
const color = hex.replace('#', '');
|
|
||||||
let r: number, g: number, b: number;
|
|
||||||
if (color.length === 3) {
|
|
||||||
r = parseInt(color[0] + color[0], 16);
|
|
||||||
g = parseInt(color[1] + color[1], 16);
|
|
||||||
b = parseInt(color[2] + color[2], 16);
|
|
||||||
} else {
|
|
||||||
r = parseInt(color.substring(0, 2), 16);
|
|
||||||
g = parseInt(color.substring(2, 4), 16);
|
|
||||||
b = parseInt(color.substring(4, 6), 16);
|
|
||||||
}
|
|
||||||
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
||||||
return luminance > 0.5 ? '#000000' : '#ffffff';
|
|
||||||
};
|
|
||||||
|
|
||||||
const Dashboard: React.FC = () => {
|
const Dashboard: React.FC = () => {
|
||||||
const { posts, media } = useAppStore();
|
const { posts, media } = useAppStore();
|
||||||
const [stats, setStats] = useState<DashboardStats | null>(null);
|
const [stats, setStats] = useState<DashboardStats | null>(null);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
||||||
import { useAppStore, PostData, MediaData } from '../../store';
|
import { useAppStore, PostData, MediaData } from '../../store';
|
||||||
import { showToast } from '../Toast';
|
import { showToast } from '../Toast';
|
||||||
import { groupPostsByStatus } from '../../utils';
|
import { getContrastColor, groupPostsByStatus } from '../../utils';
|
||||||
import type { ChatConversation, ImportDefinitionData } from '../../types/electron';
|
import type { ChatConversation, ImportDefinitionData } from '../../types/electron';
|
||||||
import './Sidebar.css';
|
import './Sidebar.css';
|
||||||
|
|
||||||
@@ -22,23 +22,6 @@ interface TagData {
|
|||||||
color?: string;
|
color?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get contrasting text color for background
|
|
||||||
const getContrastColor = (hex: string): string => {
|
|
||||||
const color = hex.replace('#', '');
|
|
||||||
let r: number, g: number, b: number;
|
|
||||||
if (color.length === 3) {
|
|
||||||
r = parseInt(color[0] + color[0], 16);
|
|
||||||
g = parseInt(color[1] + color[1], 16);
|
|
||||||
b = parseInt(color[2] + color[2], 16);
|
|
||||||
} else {
|
|
||||||
r = parseInt(color.substring(0, 2), 16);
|
|
||||||
g = parseInt(color.substring(2, 4), 16);
|
|
||||||
b = parseInt(color.substring(4, 6), 16);
|
|
||||||
}
|
|
||||||
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
||||||
return luminance > 0.5 ? '#000000' : '#ffffff';
|
|
||||||
};
|
|
||||||
|
|
||||||
const formatDate = (dateString: string) => {
|
const formatDate = (dateString: string) => {
|
||||||
const date = new Date(dateString);
|
const date = new Date(dateString);
|
||||||
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
|
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||||
import { showToast } from '../Toast';
|
import { showToast } from '../Toast';
|
||||||
|
import { getContrastColor } from '../../utils/color';
|
||||||
import './TagInput.css';
|
import './TagInput.css';
|
||||||
|
|
||||||
interface TagData {
|
interface TagData {
|
||||||
@@ -19,23 +20,6 @@ interface TagInputProps {
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get contrasting text color for background
|
|
||||||
const getContrastColor = (hex: string): string => {
|
|
||||||
const color = hex.replace('#', '');
|
|
||||||
let r: number, g: number, b: number;
|
|
||||||
if (color.length === 3) {
|
|
||||||
r = parseInt(color[0] + color[0], 16);
|
|
||||||
g = parseInt(color[1] + color[1], 16);
|
|
||||||
b = parseInt(color[2] + color[2], 16);
|
|
||||||
} else {
|
|
||||||
r = parseInt(color.substring(0, 2), 16);
|
|
||||||
g = parseInt(color.substring(2, 4), 16);
|
|
||||||
b = parseInt(color.substring(4, 6), 16);
|
|
||||||
}
|
|
||||||
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
||||||
return luminance > 0.5 ? '#000000' : '#ffffff';
|
|
||||||
};
|
|
||||||
|
|
||||||
export const TagInput: React.FC<TagInputProps> = ({
|
export const TagInput: React.FC<TagInputProps> = ({
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { useState, useEffect, useCallback } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import { useAppStore } from '../../store';
|
import { useAppStore } from '../../store';
|
||||||
import { showToast } from '../Toast';
|
import { showToast } from '../Toast';
|
||||||
|
import { getContrastColor } from '../../utils/color';
|
||||||
import './TagsView.css';
|
import './TagsView.css';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
@@ -30,29 +31,6 @@ export const scrollToTagsSection = (category: TagsCategory) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get contrasting text color for background
|
|
||||||
const getContrastColor = (hex: string): string => {
|
|
||||||
// Remove # if present
|
|
||||||
const color = hex.replace('#', '');
|
|
||||||
|
|
||||||
// Parse hex to RGB
|
|
||||||
let r: number, g: number, b: number;
|
|
||||||
if (color.length === 3) {
|
|
||||||
r = parseInt(color[0] + color[0], 16);
|
|
||||||
g = parseInt(color[1] + color[1], 16);
|
|
||||||
b = parseInt(color[2] + color[2], 16);
|
|
||||||
} else {
|
|
||||||
r = parseInt(color.substring(0, 2), 16);
|
|
||||||
g = parseInt(color.substring(2, 4), 16);
|
|
||||||
b = parseInt(color.substring(4, 6), 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate relative luminance
|
|
||||||
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
||||||
|
|
||||||
return luminance > 0.5 ? '#000000' : '#ffffff';
|
|
||||||
};
|
|
||||||
|
|
||||||
// Color picker presets
|
// Color picker presets
|
||||||
const COLOR_PRESETS = [
|
const COLOR_PRESETS = [
|
||||||
'#ef4444', '#f97316', '#f59e0b', '#eab308', '#84cc16',
|
'#ef4444', '#f97316', '#f59e0b', '#eab308', '#84cc16',
|
||||||
|
|||||||
20
src/renderer/utils/color.ts
Normal file
20
src/renderer/utils/color.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
export const getContrastColor = (hex: string): string => {
|
||||||
|
const color = hex.replace('#', '');
|
||||||
|
|
||||||
|
let r: number;
|
||||||
|
let g: number;
|
||||||
|
let b: number;
|
||||||
|
|
||||||
|
if (color.length === 3) {
|
||||||
|
r = parseInt(color[0] + color[0], 16);
|
||||||
|
g = parseInt(color[1] + color[1], 16);
|
||||||
|
b = parseInt(color[2] + color[2], 16);
|
||||||
|
} else {
|
||||||
|
r = parseInt(color.substring(0, 2), 16);
|
||||||
|
g = parseInt(color.substring(2, 4), 16);
|
||||||
|
b = parseInt(color.substring(4, 6), 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
||||||
|
return luminance > 0.5 ? '#000000' : '#ffffff';
|
||||||
|
};
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
export { AutoSaveManager, type AutoSaveConfig } from './autoSave';
|
export { AutoSaveManager, type AutoSaveConfig } from './autoSave';
|
||||||
|
export { getContrastColor } from './color';
|
||||||
export { unescapeMacroSyntax } from './markdownEscape';
|
export { unescapeMacroSyntax } from './markdownEscape';
|
||||||
export { groupPostsByStatus, type GroupedPosts, type PostStatus } from './postGrouping';
|
export { groupPostsByStatus, type GroupedPosts, type PostStatus } from './postGrouping';
|
||||||
|
|||||||
19
tests/renderer/utils/color.test.ts
Normal file
19
tests/renderer/utils/color.test.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { getContrastColor } from '../../../src/renderer/utils/color';
|
||||||
|
|
||||||
|
describe('getContrastColor', () => {
|
||||||
|
it('should return black text for light 6-char colors', () => {
|
||||||
|
expect(getContrastColor('#ffffff')).toBe('#000000');
|
||||||
|
expect(getContrastColor('fef3c7')).toBe('#000000');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return white text for dark 6-char colors', () => {
|
||||||
|
expect(getContrastColor('#000000')).toBe('#ffffff');
|
||||||
|
expect(getContrastColor('1f2937')).toBe('#ffffff');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support 3-char hex colors', () => {
|
||||||
|
expect(getContrastColor('#abc')).toBe('#000000');
|
||||||
|
expect(getContrastColor('333')).toBe('#ffffff');
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user