feat(text): integrate Google Fonts API with dynamic loading

Added Google Fonts support to text tool:

Font Loader System:
- Created GoogleFontsLoader class with caching and loading states
- Singleton instance with preloading of popular fonts (Roboto, Open Sans, Lato, Montserrat)
- Handles font loading via Google Fonts API with error handling
- Tracks loaded, loading, and error states per font

UI Improvements:
- Updated font selector with optgroups (Web Safe Fonts vs Google Fonts)
- 13 web-safe fonts + 14 popular Google Fonts
- Font preview in dropdown (fontFamily style applied to options)
- Async loading on font selection with error handling

Features:
- 27 total fonts available (13 web-safe + 14 Google Fonts)
- Automatic preloading of 4 most popular fonts on app start
- Font caching to avoid redundant loads
- Fallback to web-safe fonts if Google Fonts fail to load

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-21 16:44:42 +01:00
parent 517f57126a
commit 041db0aaff
2 changed files with 184 additions and 17 deletions

View File

@@ -4,6 +4,9 @@ import { useToolStore } from '@/store';
import { useShapeStore } from '@/store/shape-store';
import { useSelectionStore } from '@/store/selection-store';
import { useTextStore } from '@/store/text-store';
import { WEB_SAFE_FONTS, GOOGLE_FONTS } from '@/lib/text-utils';
import { googleFontsLoader } from '@/lib/google-fonts-loader';
import { useCallback } from 'react';
export function ToolOptions() {
const { activeTool, settings, setSize, setOpacity, setHardness, setColor, setFlow } = useToolStore();
@@ -21,6 +24,22 @@ export function ToolOptions() {
setColor: setTextColor,
} = useTextStore();
// Handle font change with Google Fonts loading
const handleFontChange = useCallback(
async (fontFamily: string) => {
// Check if it's a Google Font
if (GOOGLE_FONTS.includes(fontFamily as any)) {
try {
await googleFontsLoader.loadFont(fontFamily);
} catch (error) {
console.error('Failed to load Google Font:', error);
}
}
setFontFamily(fontFamily);
},
[setFontFamily]
);
// Drawing tools: brush, pencil, eraser
const isDrawingTool = ['brush', 'eraser', 'pencil'].includes(activeTool);
const showHardness = ['brush'].includes(activeTool);
@@ -233,25 +252,24 @@ export function ToolOptions() {
</label>
<select
value={textSettings.fontFamily}
onChange={(e) => setFontFamily(e.target.value)}
onChange={(e) => handleFontChange(e.target.value)}
className="px-3 py-1.5 text-sm rounded-md border border-border bg-background text-foreground"
style={{ fontFamily: textSettings.fontFamily }}
>
<option value="Arial">Arial</option>
<option value="Helvetica">Helvetica</option>
<option value="Times New Roman">Times New Roman</option>
<option value="Georgia">Georgia</option>
<option value="Courier New">Courier New</option>
<option value="Verdana">Verdana</option>
<option value="Trebuchet MS">Trebuchet MS</option>
<option value="Impact">Impact</option>
<option value="Comic Sans MS">Comic Sans MS</option>
<option value="Palatino">Palatino</option>
<option value="Garamond">Garamond</option>
<option value="Bookman">Bookman</option>
<option value="Tahoma">Tahoma</option>
<option value="Lucida Console">Lucida Console</option>
<option value="Monaco">Monaco</option>
<option value="Consolas">Consolas</option>
<optgroup label="Web Safe Fonts">
{WEB_SAFE_FONTS.map((font) => (
<option key={font} value={font} style={{ fontFamily: font }}>
{font}
</option>
))}
</optgroup>
<optgroup label="Google Fonts">
{GOOGLE_FONTS.map((font) => (
<option key={font} value={font} style={{ fontFamily: font }}>
{font}
</option>
))}
</optgroup>
</select>
</div>