feat: improve UI and transparency support

UI Improvements:
- Style scrollbars with primary color accent
- Scrollbar thumb transitions on hover (40% → 60% → 80% opacity)
- Add fill tool options to toolbar (color picker + opacity)
- Support for Firefox with scrollbar-color property

Transparency Support:
- Set default canvas background to transparent
- First layer now transparent instead of white fill
- Enables creating images with transparency
- Checkerboard pattern shows through transparent areas
- Proper PNG export support with alpha channel

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-21 09:17:12 +01:00
parent cd59f0606b
commit b7b072f6d2
4 changed files with 53 additions and 9 deletions

View File

@@ -156,9 +156,10 @@
font-feature-settings: "rlig" 1, "calt" 1; font-feature-settings: "rlig" 1, "calt" 1;
} }
/* Apply custom scrollbar globally */ /* Apply custom scrollbar globally with primary color accent */
* { * {
scrollbar-width: thin; scrollbar-width: thin;
scrollbar-color: color-mix(in oklch, var(--primary) 40%, transparent) var(--muted);
} }
*::-webkit-scrollbar { *::-webkit-scrollbar {
@@ -172,18 +173,18 @@
} }
*::-webkit-scrollbar-thumb { *::-webkit-scrollbar-thumb {
background: color-mix(in oklch, var(--muted-foreground) 30%, transparent); background: color-mix(in oklch, var(--primary) 40%, transparent);
border-radius: 5px; border-radius: 5px;
border: 2px solid var(--muted); border: 2px solid var(--muted);
transition: background 0.2s ease; transition: background 0.2s ease;
} }
*::-webkit-scrollbar-thumb:hover { *::-webkit-scrollbar-thumb:hover {
background: color-mix(in oklch, var(--muted-foreground) 50%, transparent); background: color-mix(in oklch, var(--primary) 60%, transparent);
} }
*::-webkit-scrollbar-thumb:active { *::-webkit-scrollbar-thumb:active {
background: color-mix(in oklch, var(--muted-foreground) 70%, transparent); background: color-mix(in oklch, var(--primary) 80%, transparent);
} }
/* Scrollbar corners */ /* Scrollbar corners */

View File

@@ -37,10 +37,10 @@ export function EditorLayout() {
if (layers.length === 0) { if (layers.length === 0) {
const { createLayer } = useLayerStore.getState(); const { createLayer } = useLayerStore.getState();
createLayer({ createLayer({
name: 'Background', name: 'Layer 1',
width: 800, width: 800,
height: 600, height: 600,
fillColor: '#ffffff', // No fillColor - layer is transparent by default
}); });
} }
}, []); }, []);

View File

@@ -12,9 +12,12 @@ export function ToolOptions() {
// Drawing tools: brush, pencil, eraser // Drawing tools: brush, pencil, eraser
const isDrawingTool = ['brush', 'eraser', 'pencil'].includes(activeTool); const isDrawingTool = ['brush', 'eraser', 'pencil'].includes(activeTool);
const showHardness = ['brush'].includes(activeTool); const showHardness = ['brush'].includes(activeTool);
const showColor = ['brush', 'pencil', 'fill'].includes(activeTool); const showColor = ['brush', 'pencil'].includes(activeTool);
const showFlow = ['brush'].includes(activeTool); const showFlow = ['brush'].includes(activeTool);
// Fill tool
const isFillTool = activeTool === 'fill';
// Shape tool // Shape tool
const isShapeTool = activeTool === 'shape'; const isShapeTool = activeTool === 'shape';
@@ -22,7 +25,7 @@ export function ToolOptions() {
const isSelectionTool = activeTool === 'select'; const isSelectionTool = activeTool === 'select';
// Don't show options bar if no options available // Don't show options bar if no options available
if (!isDrawingTool && !isShapeTool && !isSelectionTool) { if (!isDrawingTool && !isFillTool && !isShapeTool && !isSelectionTool) {
return null; return null;
} }
@@ -125,6 +128,46 @@ export function ToolOptions() {
</> </>
)} )}
{/* Fill Tool Options */}
{isFillTool && (
<>
<div className="flex items-center gap-2">
<label className="text-sm font-medium text-card-foreground whitespace-nowrap">
Color:
</label>
<input
type="color"
value={settings.color}
onChange={(e) => setColor(e.target.value)}
className="h-8 w-16 rounded border border-border cursor-pointer"
/>
<input
type="text"
value={settings.color}
onChange={(e) => setColor(e.target.value)}
className="w-24 px-2 py-1 text-xs rounded border border-border bg-background text-foreground"
/>
</div>
<div className="flex items-center gap-2">
<label className="text-sm font-medium text-card-foreground whitespace-nowrap">
Opacity:
</label>
<input
type="range"
min="0"
max="100"
value={settings.opacity * 100}
onChange={(e) => setOpacity(Number(e.target.value) / 100)}
className="w-32"
/>
<span className="text-sm text-muted-foreground w-10">
{Math.round(settings.opacity * 100)}%
</span>
</div>
</>
)}
{/* Shape Tool Options */} {/* Shape Tool Options */}
{isShapeTool && ( {isShapeTool && (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">

View File

@@ -58,7 +58,7 @@ export const useCanvasStore = create<CanvasStore>()(
zoom: 1, zoom: 1,
offsetX: 0, offsetX: 0,
offsetY: 0, offsetY: 0,
backgroundColor: '#ffffff', backgroundColor: 'transparent',
showGrid: false, showGrid: false,
gridSize: 20, gridSize: 20,
showRulers: true, showRulers: true,