improving share mechanism
This commit is contained in:
@ -18,6 +18,7 @@ export interface SavedShader {
|
|||||||
renderMode?: string;
|
renderMode?: string;
|
||||||
valueMode?: ValueMode;
|
valueMode?: ValueMode;
|
||||||
uiOpacity?: number;
|
uiOpacity?: number;
|
||||||
|
hueShift?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Storage {
|
export class Storage {
|
||||||
@ -46,6 +47,7 @@ export class Storage {
|
|||||||
renderMode: settings.renderMode,
|
renderMode: settings.renderMode,
|
||||||
valueMode: settings.valueMode,
|
valueMode: settings.valueMode,
|
||||||
uiOpacity: settings.uiOpacity,
|
uiOpacity: settings.uiOpacity,
|
||||||
|
hueShift: settings.hueShift,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -36,6 +36,7 @@ export function ShaderLibrary() {
|
|||||||
renderMode: settings.renderMode,
|
renderMode: settings.renderMode,
|
||||||
valueMode: settings.valueMode,
|
valueMode: settings.valueMode,
|
||||||
uiOpacity: settings.uiOpacity,
|
uiOpacity: settings.uiOpacity,
|
||||||
|
hueShift: settings.hueShift,
|
||||||
};
|
};
|
||||||
|
|
||||||
saveShader(name, code, currentSettings);
|
saveShader(name, code, currentSettings);
|
||||||
@ -62,6 +63,8 @@ export function ShaderLibrary() {
|
|||||||
if (shaderData.valueMode) newSettings.valueMode = shaderData.valueMode;
|
if (shaderData.valueMode) newSettings.valueMode = shaderData.valueMode;
|
||||||
if (shaderData.uiOpacity !== undefined)
|
if (shaderData.uiOpacity !== undefined)
|
||||||
newSettings.uiOpacity = shaderData.uiOpacity;
|
newSettings.uiOpacity = shaderData.uiOpacity;
|
||||||
|
if (shaderData.hueShift !== undefined)
|
||||||
|
newSettings.hueShift = shaderData.hueShift;
|
||||||
|
|
||||||
if (Object.keys(newSettings).length > 0) {
|
if (Object.keys(newSettings).length > 0) {
|
||||||
updateAppSettings(newSettings);
|
updateAppSettings(newSettings);
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
import { $appSettings, updateAppSettings } from '../stores/appSettings';
|
import { $appSettings, updateAppSettings } from '../stores/appSettings';
|
||||||
import { VALUE_MODES, ValueMode } from '../utils/constants';
|
import { VALUE_MODES, ValueMode } from '../utils/constants';
|
||||||
import {
|
import {
|
||||||
@ -46,6 +47,7 @@ export function TopBar() {
|
|||||||
const shader = useStore($shader);
|
const shader = useStore($shader);
|
||||||
const input = useStore($input);
|
const input = useStore($input);
|
||||||
const { setupAudio, disableAudio } = useAudio();
|
const { setupAudio, disableAudio } = useAudio();
|
||||||
|
const [shareStatus, setShareStatus] = useState<'idle' | 'copied' | 'failed'>('idle');
|
||||||
|
|
||||||
const handleFullscreen = () => {
|
const handleFullscreen = () => {
|
||||||
if (!document.fullscreenElement) {
|
if (!document.fullscreenElement) {
|
||||||
@ -68,6 +70,7 @@ export function TopBar() {
|
|||||||
renderMode: settings.renderMode,
|
renderMode: settings.renderMode,
|
||||||
valueMode: settings.valueMode,
|
valueMode: settings.valueMode,
|
||||||
uiOpacity: settings.uiOpacity,
|
uiOpacity: settings.uiOpacity,
|
||||||
|
hueShift: settings.hueShift,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -81,9 +84,13 @@ export function TopBar() {
|
|||||||
.writeText(url)
|
.writeText(url)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log('URL copied to clipboard');
|
console.log('URL copied to clipboard');
|
||||||
|
setShareStatus('copied');
|
||||||
|
setTimeout(() => setShareStatus('idle'), 2000);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
console.log('Copy failed');
|
console.log('Copy failed');
|
||||||
|
setShareStatus('failed');
|
||||||
|
setTimeout(() => setShareStatus('idle'), 2000);
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to create share URL:', error);
|
console.error('Failed to create share URL:', error);
|
||||||
@ -283,9 +290,87 @@ export function TopBar() {
|
|||||||
name={input.audioEnabled ? 'microphone' : 'microphone-off'}
|
name={input.audioEnabled ? 'microphone' : 'microphone-off'}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
<button id="share-btn" onClick={handleShare}>
|
<div style={{ position: 'relative', display: 'inline-block' }}>
|
||||||
<LucideIcon name="share" />
|
<button
|
||||||
</button>
|
id="share-btn"
|
||||||
|
onClick={handleShare}
|
||||||
|
style={{
|
||||||
|
background: shareStatus === 'copied' ? '#4CAF50' : shareStatus === 'failed' ? '#f44336' : undefined,
|
||||||
|
color: shareStatus !== 'idle' ? '#fff' : undefined,
|
||||||
|
transition: 'all 0.3s ease'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LucideIcon name={shareStatus === 'copied' ? 'check' : 'share'} />
|
||||||
|
</button>
|
||||||
|
{shareStatus === 'copied' && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '100%',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translateX(-50%)',
|
||||||
|
backgroundColor: '#333',
|
||||||
|
color: '#fff',
|
||||||
|
padding: '8px 12px',
|
||||||
|
borderRadius: '4px',
|
||||||
|
fontSize: '12px',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
zIndex: 1000,
|
||||||
|
marginTop: '5px',
|
||||||
|
animation: 'fadeIn 0.3s ease'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Link copied to clipboard!
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '-5px',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translateX(-50%)',
|
||||||
|
width: '0',
|
||||||
|
height: '0',
|
||||||
|
borderLeft: '5px solid transparent',
|
||||||
|
borderRight: '5px solid transparent',
|
||||||
|
borderBottom: '5px solid #333'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{shareStatus === 'failed' && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '100%',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translateX(-50%)',
|
||||||
|
backgroundColor: '#f44336',
|
||||||
|
color: '#fff',
|
||||||
|
padding: '8px 12px',
|
||||||
|
borderRadius: '4px',
|
||||||
|
fontSize: '12px',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
zIndex: 1000,
|
||||||
|
marginTop: '5px',
|
||||||
|
animation: 'fadeIn 0.3s ease'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Failed to copy link
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '-5px',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translateX(-50%)',
|
||||||
|
width: '0',
|
||||||
|
height: '0',
|
||||||
|
borderLeft: '5px solid transparent',
|
||||||
|
borderRight: '5px solid transparent',
|
||||||
|
borderBottom: '5px solid #f44336'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<button id="export-png-btn" onClick={handleExportPNG}>
|
<button id="export-png-btn" onClick={handleExportPNG}>
|
||||||
<LucideIcon name="export" />
|
<LucideIcon name="export" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -63,6 +63,7 @@ function shareURL() {
|
|||||||
renderMode: settings.renderMode,
|
renderMode: settings.renderMode,
|
||||||
valueMode: settings.valueMode,
|
valueMode: settings.valueMode,
|
||||||
uiOpacity: settings.uiOpacity,
|
uiOpacity: settings.uiOpacity,
|
||||||
|
hueShift: settings.hueShift,
|
||||||
};
|
};
|
||||||
|
|
||||||
const encoded = btoa(JSON.stringify(shareData));
|
const encoded = btoa(JSON.stringify(shareData));
|
||||||
|
|||||||
@ -36,6 +36,10 @@ function loadFromURL() {
|
|||||||
shareData.uiOpacity !== undefined
|
shareData.uiOpacity !== undefined
|
||||||
? shareData.uiOpacity
|
? shareData.uiOpacity
|
||||||
: savedSettings.uiOpacity,
|
: savedSettings.uiOpacity,
|
||||||
|
hueShift:
|
||||||
|
shareData.hueShift !== undefined
|
||||||
|
? shareData.hueShift
|
||||||
|
: savedSettings.hueShift,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('Settings updated from URL');
|
console.log('Settings updated from URL');
|
||||||
|
|||||||
@ -852,3 +852,15 @@ button [data-lucide] {
|
|||||||
grid-template-columns: repeat(3, 1fr);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user