Merge pull request #86 from ThePangel/main

Added the ability to resize the callsign.
pull/95/head
accius 1 day ago committed by GitHub
commit 94275d5ca5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -19,7 +19,7 @@ export const Header = ({
isFullscreen isFullscreen
}) => { }) => {
return ( return (
<div style={{ <div style={{
gridColumn: '1 / -1', gridColumn: '1 / -1',
display: 'flex', display: 'flex',
flexWrap: 'nowrap', flexWrap: 'nowrap',
@ -35,8 +35,12 @@ export const Header = ({
}}> }}>
{/* Callsign & Settings */} {/* Callsign & Settings */}
<div style={{ display: 'flex', alignItems: 'center', gap: '12px', flexShrink: 0 }}> <div style={{ display: 'flex', alignItems: 'center', gap: '12px', flexShrink: 0 }}>
<span <span
style={{ fontSize: '22px', fontWeight: '900', color: 'var(--accent-amber)', cursor: 'pointer', fontFamily: 'Orbitron, monospace', whiteSpace: 'nowrap' }} style={{
fontSize: config.callsignSize > 0.1 && config.callsignSize <= 2
? `${22 * config.callsignSize}px`
: "22px", fontWeight: '900', color: 'var(--accent-amber)', cursor: 'pointer', fontFamily: 'Orbitron, monospace', whiteSpace: 'nowrap'
}}
onClick={onSettingsClick} onClick={onSettingsClick}
title="Click for settings" title="Click for settings"
> >
@ -44,37 +48,37 @@ export const Header = ({
</span> </span>
{config.version && <span style={{ fontSize: '11px', color: 'var(--text-muted)' }}>v{config.version}</span>} {config.version && <span style={{ fontSize: '11px', color: 'var(--text-muted)' }}>v{config.version}</span>}
</div> </div>
{/* UTC Clock */} {/* UTC Clock */}
<div style={{ display: 'flex', alignItems: 'center', gap: '6px', flexShrink: 0 }}> <div style={{ display: 'flex', alignItems: 'center', gap: '6px', flexShrink: 0 }}>
<span style={{ fontSize: '13px', color: 'var(--accent-cyan)', fontWeight: '600' }}>UTC</span> <span style={{ fontSize: '13px', color: 'var(--accent-cyan)', fontWeight: '600' }}>UTC</span>
<span style={{ <span style={{
fontSize: '24px', fontSize: '24px',
fontWeight: '700', fontWeight: '700',
color: 'var(--accent-cyan)', color: 'var(--accent-cyan)',
fontFamily: 'JetBrains Mono, Consolas, monospace', fontFamily: 'JetBrains Mono, Consolas, monospace',
whiteSpace: 'nowrap' whiteSpace: 'nowrap'
}}>{utcTime}</span> }}>{utcTime}</span>
<span style={{ fontSize: '12px', color: 'var(--text-muted)', whiteSpace: 'nowrap' }}>{utcDate}</span> <span style={{ fontSize: '12px', color: 'var(--text-muted)', whiteSpace: 'nowrap' }}>{utcDate}</span>
</div> </div>
{/* Local Clock - Clickable to toggle 12/24 hour format */} {/* Local Clock - Clickable to toggle 12/24 hour format */}
<div <div
style={{ display: 'flex', alignItems: 'center', gap: '6px', cursor: 'pointer', flexShrink: 0 }} style={{ display: 'flex', alignItems: 'center', gap: '6px', cursor: 'pointer', flexShrink: 0 }}
onClick={onTimeFormatToggle} onClick={onTimeFormatToggle}
title={`Click to switch to ${use12Hour ? '24-hour' : '12-hour'} format`} title={`Click to switch to ${use12Hour ? '24-hour' : '12-hour'} format`}
> >
<span style={{ fontSize: '13px', color: 'var(--accent-amber)', fontWeight: '600' }}>LOCAL</span> <span style={{ fontSize: '13px', color: 'var(--accent-amber)', fontWeight: '600' }}>LOCAL</span>
<span style={{ <span style={{
fontSize: '24px', fontSize: '24px',
fontWeight: '700', fontWeight: '700',
color: 'var(--accent-amber)', color: 'var(--accent-amber)',
fontFamily: 'JetBrains Mono, Consolas, monospace', fontFamily: 'JetBrains Mono, Consolas, monospace',
whiteSpace: 'nowrap' whiteSpace: 'nowrap'
}}>{localTime}</span> }}>{localTime}</span>
<span style={{ fontSize: '12px', color: 'var(--text-muted)', whiteSpace: 'nowrap' }}>{localDate}</span> <span style={{ fontSize: '12px', color: 'var(--text-muted)', whiteSpace: 'nowrap' }}>{localDate}</span>
</div> </div>
{/* Weather & Solar Stats */} {/* Weather & Solar Stats */}
<div style={{ display: 'flex', gap: '12px', fontSize: '13px', fontFamily: 'JetBrains Mono, Consolas, monospace', whiteSpace: 'nowrap', flexShrink: 0 }}> <div style={{ display: 'flex', gap: '12px', fontSize: '13px', fontFamily: 'JetBrains Mono, Consolas, monospace', whiteSpace: 'nowrap', flexShrink: 0 }}>
{localWeather?.data && (() => { {localWeather?.data && (() => {
@ -85,12 +89,12 @@ export const Header = ({
const tempC = Math.round(rawC); const tempC = Math.round(rawC);
const windLabel = localWeather.data.windUnit || 'mph'; const windLabel = localWeather.data.windUnit || 'mph';
return ( return (
<div title={`${localWeather.data.description} • Wind: ${localWeather.data.windSpeed} ${windLabel}`}> <div title={`${localWeather.data.description} • Wind: ${localWeather.data.windSpeed} ${windLabel}`}>
<span style={{ marginRight: '3px' }}>{localWeather.data.icon}</span> <span style={{ marginRight: '3px' }}>{localWeather.data.icon}</span>
<span style={{ color: 'var(--accent-cyan)', fontWeight: '600' }}> <span style={{ color: 'var(--accent-cyan)', fontWeight: '600' }}>
{tempF}°F/{tempC}°C {tempF}°F/{tempC}°C
</span> </span>
</div> </div>
); );
})()} })()}
<div> <div>
@ -108,7 +112,7 @@ export const Header = ({
<span style={{ color: 'var(--accent-cyan)', fontWeight: '700' }}>{spaceWeather?.data?.sunspotNumber || '--'}</span> <span style={{ color: 'var(--accent-cyan)', fontWeight: '700' }}>{spaceWeather?.data?.sunspotNumber || '--'}</span>
</div> </div>
</div> </div>
{/* Settings & Fullscreen Buttons */} {/* Settings & Fullscreen Buttons */}
<div style={{ display: 'flex', gap: '6px', flexShrink: 0 }}> <div style={{ display: 'flex', gap: '6px', flexShrink: 0 }}>
<a <a
@ -116,17 +120,17 @@ export const Header = ({
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
style={{ style={{
background: 'linear-gradient(135deg, #ff813f 0%, #ffdd00 100%)', background: 'linear-gradient(135deg, #ff813f 0%, #ffdd00 100%)',
border: 'none', border: 'none',
padding: '6px 10px', padding: '6px 10px',
borderRadius: '4px', borderRadius: '4px',
color: '#000', color: '#000',
fontSize: '12px', fontSize: '12px',
cursor: 'pointer', cursor: 'pointer',
fontWeight: '600', fontWeight: '600',
textDecoration: 'none', textDecoration: 'none',
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
gap: '3px', gap: '3px',
whiteSpace: 'nowrap' whiteSpace: 'nowrap'
}} }}
@ -136,13 +140,13 @@ export const Header = ({
</a> </a>
<button <button
onClick={onSettingsClick} onClick={onSettingsClick}
style={{ style={{
background: 'var(--bg-tertiary)', background: 'var(--bg-tertiary)',
border: '1px solid var(--border-color)', border: '1px solid var(--border-color)',
padding: '6px 10px', padding: '6px 10px',
borderRadius: '4px', borderRadius: '4px',
color: 'var(--text-secondary)', color: 'var(--text-secondary)',
fontSize: '12px', fontSize: '12px',
cursor: 'pointer', cursor: 'pointer',
whiteSpace: 'nowrap' whiteSpace: 'nowrap'
}} }}
@ -151,19 +155,19 @@ export const Header = ({
</button> </button>
<button <button
onClick={onFullscreenToggle} onClick={onFullscreenToggle}
style={{ style={{
background: isFullscreen ? 'rgba(0, 255, 136, 0.15)' : 'var(--bg-tertiary)', background: isFullscreen ? 'rgba(0, 255, 136, 0.15)' : 'var(--bg-tertiary)',
border: `1px solid ${isFullscreen ? 'var(--accent-green)' : 'var(--border-color)'}`, border: `1px solid ${isFullscreen ? 'var(--accent-green)' : 'var(--border-color)'}`,
padding: '6px 10px', padding: '6px 10px',
borderRadius: '4px', borderRadius: '4px',
color: isFullscreen ? 'var(--accent-green)' : 'var(--text-secondary)', color: isFullscreen ? 'var(--accent-green)' : 'var(--text-secondary)',
fontSize: '12px', fontSize: '12px',
cursor: 'pointer', cursor: 'pointer',
whiteSpace: 'nowrap' whiteSpace: 'nowrap'
}} }}
title={isFullscreen ? "Exit Fullscreen (Esc)" : "Enter Fullscreen"} title={isFullscreen ? "Exit Fullscreen (Esc)" : "Enter Fullscreen"}
> >
{isFullscreen {isFullscreen
? <><IconShrink size={12} style={{ verticalAlign: 'middle', marginRight: '4px' }} />Exit</> ? <><IconShrink size={12} style={{ verticalAlign: 'middle', marginRight: '4px' }} />Exit</>
: <><IconExpand size={12} style={{ verticalAlign: 'middle', marginRight: '4px' }} />Full</> : <><IconExpand size={12} style={{ verticalAlign: 'middle', marginRight: '4px' }} />Full</>
} }

@ -9,6 +9,7 @@ import { LANGUAGES } from '../lang/i18n.js';
export const SettingsPanel = ({ isOpen, onClose, config, onSave }) => { export const SettingsPanel = ({ isOpen, onClose, config, onSave }) => {
const [callsign, setCallsign] = useState(config?.callsign || ''); const [callsign, setCallsign] = useState(config?.callsign || '');
const [callsignSize, setCallsignSize] = useState(config?.callsignSize || 1.0);
const [gridSquare, setGridSquare] = useState(''); const [gridSquare, setGridSquare] = useState('');
const [lat, setLat] = useState(config?.location?.lat || 0); const [lat, setLat] = useState(config?.location?.lat || 0);
const [lon, setLon] = useState(config?.location?.lon || 0); const [lon, setLon] = useState(config?.location?.lon || 0);
@ -17,7 +18,7 @@ export const SettingsPanel = ({ isOpen, onClose, config, onSave }) => {
const [timezone, setTimezone] = useState(config?.timezone || ''); const [timezone, setTimezone] = useState(config?.timezone || '');
const [dxClusterSource, setDxClusterSource] = useState(config?.dxClusterSource || 'dxspider-proxy'); const [dxClusterSource, setDxClusterSource] = useState(config?.dxClusterSource || 'dxspider-proxy');
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
// Layer controls // Layer controls
const [layers, setLayers] = useState([]); const [layers, setLayers] = useState([]);
const [activeTab, setActiveTab] = useState('station'); const [activeTab, setActiveTab] = useState('station');
@ -25,6 +26,7 @@ export const SettingsPanel = ({ isOpen, onClose, config, onSave }) => {
useEffect(() => { useEffect(() => {
if (config) { if (config) {
setCallsign(config.callsign || ''); setCallsign(config.callsign || '');
setCallsignSize(config.callsignSize || 1.0)
setLat(config.location?.lat || 0); setLat(config.location?.lat || 0);
setLon(config.location?.lon || 0); setLon(config.location?.lon || 0);
setTheme(config.theme || 'dark'); setTheme(config.theme || 'dark');
@ -70,22 +72,22 @@ export const SettingsPanel = ({ isOpen, onClose, config, onSave }) => {
const parseGridSquare = (grid) => { const parseGridSquare = (grid) => {
grid = grid.toUpperCase(); grid = grid.toUpperCase();
if (grid.length < 4) return null; if (grid.length < 4) return null;
const lon1 = (grid.charCodeAt(0) - 65) * 20 - 180; const lon1 = (grid.charCodeAt(0) - 65) * 20 - 180;
const lat1 = (grid.charCodeAt(1) - 65) * 10 - 90; const lat1 = (grid.charCodeAt(1) - 65) * 10 - 90;
const lon2 = parseInt(grid[2]) * 2; const lon2 = parseInt(grid[2]) * 2;
const lat2 = parseInt(grid[3]) * 1; const lat2 = parseInt(grid[3]) * 1;
let lon = lon1 + lon2 + 1; let lon = lon1 + lon2 + 1;
let lat = lat1 + lat2 + 0.5; let lat = lat1 + lat2 + 0.5;
if (grid.length >= 6) { if (grid.length >= 6) {
const lon3 = (grid.charCodeAt(4) - 65) * (2/24); const lon3 = (grid.charCodeAt(4) - 65) * (2/24);
const lat3 = (grid.charCodeAt(5) - 65) * (1/24); const lat3 = (grid.charCodeAt(5) - 65) * (1/24);
lon = lon1 + lon2 + lon3 + (1/24); lon = lon1 + lon2 + lon3 + (1/24);
lat = lat1 + lat2 + lat3 + (1/48); lat = lat1 + lat2 + lat3 + (1/48);
} }
return { lat, lon }; return { lat, lon };
}; };
@ -116,17 +118,17 @@ export const SettingsPanel = ({ isOpen, onClose, config, onSave }) => {
if (window.hamclockLayerControls) { if (window.hamclockLayerControls) {
const layer = layers.find(l => l.id === layerId); const layer = layers.find(l => l.id === layerId);
const newEnabledState = !layer.enabled; const newEnabledState = !layer.enabled;
// Update the control // Update the control
window.hamclockLayerControls.toggleLayer(layerId, newEnabledState); window.hamclockLayerControls.toggleLayer(layerId, newEnabledState);
// Force immediate UI update // Force immediate UI update
setLayers(prevLayers => setLayers(prevLayers =>
prevLayers.map(l => prevLayers.map(l =>
l.id === layerId ? { ...l, enabled: newEnabledState } : l l.id === layerId ? { ...l, enabled: newEnabledState } : l
) )
); );
// Refresh after a short delay to get the updated state // Refresh after a short delay to get the updated state
setTimeout(() => { setTimeout(() => {
if (window.hamclockLayerControls) { if (window.hamclockLayerControls) {
@ -147,6 +149,7 @@ export const SettingsPanel = ({ isOpen, onClose, config, onSave }) => {
onSave({ onSave({
...config, ...config,
callsign: callsign.toUpperCase(), callsign: callsign.toUpperCase(),
callsignSize: callsignSize,
location: { lat: parseFloat(lat), lon: parseFloat(lon) }, location: { lat: parseFloat(lat), lon: parseFloat(lon) },
theme, theme,
layout, layout,
@ -198,8 +201,8 @@ export const SettingsPanel = ({ isOpen, onClose, config, onSave }) => {
maxHeight: '90vh', maxHeight: '90vh',
overflowY: 'auto' overflowY: 'auto'
}}> }}>
<h2 style={{ <h2 style={{
color: 'var(--accent-cyan)', color: 'var(--accent-cyan)',
marginTop: 0, marginTop: 0,
marginBottom: '24px', marginBottom: '24px',
textAlign: 'center', textAlign: 'center',
@ -302,6 +305,35 @@ export const SettingsPanel = ({ isOpen, onClose, config, onSave }) => {
/> />
</div> </div>
{/* Callsign Size*/}
<div style={{ marginBottom: '20px'}}>
<div>
<label style={{ display: 'block', marginBottom: '6px', color: 'var(--text-muted)', fontSize: '11px', textTransform: 'uppercase' }}>
{t('station.settings.callsignSize')}
</label>
<input
type="number"
step="0.1"
value={isNaN(lat) ? '' : callsignSize}
onChange={(e) => {
if (e.target.value >= 0.1 && e.target.value <= 2.0) {
setCallsignSize(e.target.value)
}}}
style={{
width: '100%',
padding: '10px',
background: 'var(--bg-tertiary)',
border: '1px solid var(--border-color)',
borderRadius: '6px',
color: 'var(--text-primary)',
fontSize: '14px',
fontFamily: 'JetBrains Mono, monospace',
boxSizing: 'border-box'
}}
/>
</div>
</div>
{/* Grid Square */} {/* Grid Square */}
<div style={{ marginBottom: '20px' }}> <div style={{ marginBottom: '20px' }}>
<label style={{ display: 'block', marginBottom: '6px', color: 'var(--text-muted)', fontSize: '11px', textTransform: 'uppercase', letterSpacing: '1px' }}> <label style={{ display: 'block', marginBottom: '6px', color: 'var(--text-muted)', fontSize: '11px', textTransform: 'uppercase', letterSpacing: '1px' }}>
@ -601,15 +633,15 @@ export const SettingsPanel = ({ isOpen, onClose, config, onSave }) => {
onClick={() => i18n.changeLanguage(lang.code)} onClick={() => i18n.changeLanguage(lang.code)}
style={{ style={{
padding: '8px 6px', padding: '8px 6px',
background: i18n.language === lang.code || (i18n.language && i18n.language.startsWith(lang.code)) background: i18n.language === lang.code || (i18n.language && i18n.language.startsWith(lang.code))
? 'rgba(0, 221, 255, 0.2)' ? 'rgba(0, 221, 255, 0.2)'
: 'var(--bg-tertiary)', : 'var(--bg-tertiary)',
border: `1px solid ${i18n.language === lang.code || (i18n.language && i18n.language.startsWith(lang.code)) border: `1px solid ${i18n.language === lang.code || (i18n.language && i18n.language.startsWith(lang.code))
? 'var(--accent-cyan)' ? 'var(--accent-cyan)'
: 'var(--border-color)'}`, : 'var(--border-color)'}`,
borderRadius: '6px', borderRadius: '6px',
color: i18n.language === lang.code || (i18n.language && i18n.language.startsWith(lang.code)) color: i18n.language === lang.code || (i18n.language && i18n.language.startsWith(lang.code))
? 'var(--accent-cyan)' ? 'var(--accent-cyan)'
: 'var(--text-secondary)', : 'var(--text-secondary)',
fontSize: '12px', fontSize: '12px',
cursor: 'pointer', cursor: 'pointer',

@ -14,6 +14,7 @@
"station.settings.button.save": "Save Settings", "station.settings.button.save": "Save Settings",
"station.settings.button.save.confirm": "Settings saved to your browser", "station.settings.button.save.confirm": "Settings saved to your browser",
"station.settings.callsign": "Your Callsign", "station.settings.callsign": "Your Callsign",
"station.settings.callsignSize": "Your Callsign's size",
"station.settings.describe": "Enter your callsign and grid square to get started. Settings are saved in your browser.", "station.settings.describe": "Enter your callsign and grid square to get started. Settings are saved in your browser.",
"station.settings.dx.describe": "→ Real-time DX Spider feed via our dedicated proxy service", "station.settings.dx.describe": "→ Real-time DX Spider feed via our dedicated proxy service",
"station.settings.dx.option1": "⭐ DX Spider Proxy (Recommended)", "station.settings.dx.option1": "⭐ DX Spider Proxy (Recommended)",

@ -14,6 +14,7 @@
"station.settings.button.save": "Guardar Configuración", "station.settings.button.save": "Guardar Configuración",
"station.settings.button.save.confirm": "La configuración se guarda en tu navegador", "station.settings.button.save.confirm": "La configuración se guarda en tu navegador",
"station.settings.callsign": "Tu Indicativo", "station.settings.callsign": "Tu Indicativo",
"station.settings.callsignSize": "El tamaño de tu Indicativo",
"station.settings.describe": "Ingresa tu indicativo y cuadrícula para comenzar. Tu configuración se guardará en el navegador.", "station.settings.describe": "Ingresa tu indicativo y cuadrícula para comenzar. Tu configuración se guardará en el navegador.",
"station.settings.dx.describe": "→ Feed en tiempo real de DX Spider a través de nuestro servicio proxy dedicado", "station.settings.dx.describe": "→ Feed en tiempo real de DX Spider a través de nuestro servicio proxy dedicado",
"station.settings.dx.option1": "⭐ Proxy DX Spider (Recomendado)", "station.settings.dx.option1": "⭐ Proxy DX Spider (Recomendado)",

@ -10,6 +10,7 @@
export const DEFAULT_CONFIG = { export const DEFAULT_CONFIG = {
callsign: 'N0CALL', callsign: 'N0CALL',
callsignSize: 1.0, // Float multiplies base px size (0.1 to 2.0)
locator: '', locator: '',
location: { lat: 40.0150, lon: -105.2705 }, // Boulder, CO (default) location: { lat: 40.0150, lon: -105.2705 }, // Boulder, CO (default)
defaultDX: { lat: 35.6762, lon: 139.6503 }, // Tokyo defaultDX: { lat: 35.6762, lon: 139.6503 }, // Tokyo

Loading…
Cancel
Save

Powered by TurnKey Linux.