diff --git a/src/Storage.ts b/src/Storage.ts index 873131d..9a300c8 100644 --- a/src/Storage.ts +++ b/src/Storage.ts @@ -18,6 +18,7 @@ export interface SavedShader { renderMode?: string; valueMode?: ValueMode; uiOpacity?: number; + hueShift?: number; } export class Storage { @@ -46,6 +47,7 @@ export class Storage { renderMode: settings.renderMode, valueMode: settings.valueMode, uiOpacity: settings.uiOpacity, + hueShift: settings.hueShift, }), }; diff --git a/src/components/ShaderLibrary.tsx b/src/components/ShaderLibrary.tsx index 174b37e..dc2cb9f 100644 --- a/src/components/ShaderLibrary.tsx +++ b/src/components/ShaderLibrary.tsx @@ -36,6 +36,7 @@ export function ShaderLibrary() { renderMode: settings.renderMode, valueMode: settings.valueMode, uiOpacity: settings.uiOpacity, + hueShift: settings.hueShift, }; saveShader(name, code, currentSettings); @@ -62,6 +63,8 @@ export function ShaderLibrary() { if (shaderData.valueMode) newSettings.valueMode = shaderData.valueMode; if (shaderData.uiOpacity !== undefined) newSettings.uiOpacity = shaderData.uiOpacity; + if (shaderData.hueShift !== undefined) + newSettings.hueShift = shaderData.hueShift; if (Object.keys(newSettings).length > 0) { updateAppSettings(newSettings); diff --git a/src/components/TopBar.tsx b/src/components/TopBar.tsx index a535db5..ea0515e 100644 --- a/src/components/TopBar.tsx +++ b/src/components/TopBar.tsx @@ -1,4 +1,5 @@ import { useStore } from '@nanostores/react'; +import { useState, useEffect } from 'react'; import { $appSettings, updateAppSettings } from '../stores/appSettings'; import { VALUE_MODES, ValueMode } from '../utils/constants'; import { @@ -46,6 +47,7 @@ export function TopBar() { const shader = useStore($shader); const input = useStore($input); const { setupAudio, disableAudio } = useAudio(); + const [shareStatus, setShareStatus] = useState<'idle' | 'copied' | 'failed'>('idle'); const handleFullscreen = () => { if (!document.fullscreenElement) { @@ -68,6 +70,7 @@ export function TopBar() { renderMode: settings.renderMode, valueMode: settings.valueMode, uiOpacity: settings.uiOpacity, + hueShift: settings.hueShift, }; try { @@ -81,9 +84,13 @@ export function TopBar() { .writeText(url) .then(() => { console.log('URL copied to clipboard'); + setShareStatus('copied'); + setTimeout(() => setShareStatus('idle'), 2000); }) .catch(() => { console.log('Copy failed'); + setShareStatus('failed'); + setTimeout(() => setShareStatus('idle'), 2000); }); } catch (error) { console.error('Failed to create share URL:', error); @@ -283,9 +290,87 @@ export function TopBar() { name={input.audioEnabled ? 'microphone' : 'microphone-off'} /> - +
+ + {shareStatus === 'copied' && ( +
+ Link copied to clipboard! +
+
+ )} + {shareStatus === 'failed' && ( +
+ Failed to copy link +
+
+ )} +
diff --git a/src/hooks/useKeyboardShortcuts.ts b/src/hooks/useKeyboardShortcuts.ts index b232c48..a7c6e31 100644 --- a/src/hooks/useKeyboardShortcuts.ts +++ b/src/hooks/useKeyboardShortcuts.ts @@ -63,6 +63,7 @@ function shareURL() { renderMode: settings.renderMode, valueMode: settings.valueMode, uiOpacity: settings.uiOpacity, + hueShift: settings.hueShift, }; const encoded = btoa(JSON.stringify(shareData)); diff --git a/src/main.tsx b/src/main.tsx index 12b70f0..fb714bb 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -36,6 +36,10 @@ function loadFromURL() { shareData.uiOpacity !== undefined ? shareData.uiOpacity : savedSettings.uiOpacity, + hueShift: + shareData.hueShift !== undefined + ? shareData.hueShift + : savedSettings.hueShift, }); console.log('Settings updated from URL'); diff --git a/src/styles/main.css b/src/styles/main.css index e4d5814..cf786f4 100644 --- a/src/styles/main.css +++ b/src/styles/main.css @@ -852,3 +852,15 @@ button [data-lucide] { grid-template-columns: repeat(3, 1fr); } } + +/* Share button tooltip animation */ +@keyframes fadeIn { + from { + opacity: 0; + transform: translateX(-50%) translateY(-10px); + } + to { + opacity: 1; + transform: translateX(-50%) translateY(0); + } +}