Fix equipment profiles: allow selecting active setup and renaming default

- All profiles (including the default AT71+ATR2600C) now show Edit and Use buttons
- Active profile tracked in localStorage via astronome_active_profile
- Default profile is seeded from localStorage on first load so it can be renamed
- Delete button only shown when more than one profile exists

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-17 11:07:58 +02:00
parent a562dae1e3
commit b5a1f40f27
+42 -27
View File
@@ -3,15 +3,14 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { api } from '../api';
import type { HorizonPoint } from '../api/types';
// Current hardcoded setup constants (from config.rs)
const CURRENT_SETUP = {
name: 'AT71 + ATR2600C (current)',
const DEFAULT_PROFILE: EquipProfile = {
id: 'default',
name: 'AT71 + ATR2600C',
focal_mm: 490,
aperture_mm: 71,
pixel_um: 3.76,
res_x: 6248,
res_y: 4176,
active: true,
};
interface EquipProfile {
@@ -24,9 +23,18 @@ interface EquipProfile {
res_y: number;
}
function loadProfiles(): EquipProfile[] {
try {
const stored = JSON.parse(localStorage.getItem('astronome_equip_profiles') ?? '[]') as EquipProfile[];
// Ensure the default profile is always present
if (!stored.find(p => p.id === 'default')) {
return [DEFAULT_PROFILE, ...stored];
}
return stored;
} catch { return [DEFAULT_PROFILE]; }
}
function calcProfile(p: { focal_mm: number; aperture_mm: number; pixel_um: number; res_x: number; res_y: number }) {
const plate_scale = (206.265 * p.pixel_um) / (p.focal_mm * 1000 / 1000);
// plate_scale in arcsec/px: (206265 * pixel_size_um / 1000) / focal_mm
const ps = (206.265 * p.pixel_um / 1000) / p.focal_mm * 1000;
const fov_w_deg = (ps * p.res_x) / 3600;
const fov_h_deg = (ps * p.res_y) / 3600;
@@ -39,11 +47,10 @@ function calcProfile(p: { focal_mm: number; aperture_mm: number; pixel_um: numbe
}
function EquipmentProfiles() {
const [profiles, setProfiles] = useState<EquipProfile[]>(() => {
try {
return JSON.parse(localStorage.getItem('astronome_equip_profiles') ?? '[]');
} catch { return []; }
});
const [profiles, setProfiles] = useState<EquipProfile[]>(loadProfiles);
const [activeId, setActiveId] = useState<string>(() =>
localStorage.getItem('astronome_active_profile') ?? 'default'
);
const [editing, setEditing] = useState<EquipProfile | null>(null);
const [adding, setAdding] = useState(false);
const [form, setForm] = useState({ name: '', focal_mm: 490, aperture_mm: 71, pixel_um: 3.76, res_x: 6248, res_y: 4176 });
@@ -52,6 +59,10 @@ function EquipmentProfiles() {
localStorage.setItem('astronome_equip_profiles', JSON.stringify(profiles));
}, [profiles]);
useEffect(() => {
localStorage.setItem('astronome_active_profile', activeId);
}, [activeId]);
const saveProfile = () => {
if (!form.name.trim()) return;
if (editing) {
@@ -65,7 +76,9 @@ function EquipmentProfiles() {
};
const deleteProfile = (id: string) => {
if (profiles.length <= 1) return;
setProfiles(ps => ps.filter(p => p.id !== id));
if (activeId === id) setActiveId(profiles.find(p => p.id !== id)?.id ?? 'default');
};
const startEdit = (p: EquipProfile) => {
@@ -74,9 +87,6 @@ function EquipmentProfiles() {
setAdding(false);
};
const current = calcProfile(CURRENT_SETUP);
const allProfiles = [{ ...CURRENT_SETUP, id: '__current__' }, ...profiles];
const fieldStyle: React.CSSProperties = {
background: 'var(--bg-void)', border: '1px solid var(--border)', borderRadius: 3,
color: 'var(--text-hi)', fontFamily: 'var(--font-mono)', fontSize: 12,
@@ -90,19 +100,19 @@ function EquipmentProfiles() {
</h2>
<div style={{ display: 'flex', flexDirection: 'column', gap: 10, maxWidth: 640 }}>
{allProfiles.map(p => {
{profiles.map(p => {
const calc = calcProfile(p);
const isCurrent = p.id === '__current__';
const isActive = p.id === activeId;
return (
<div key={p.id} style={{
background: 'var(--bg-panel)', border: `1px solid ${isCurrent ? 'var(--amber-dim)' : 'var(--border)'}`,
background: 'var(--bg-panel)', border: `1px solid ${isActive ? 'var(--amber-dim)' : 'var(--border)'}`,
borderRadius: 6, padding: '12px 16px',
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
<div>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: 13, color: isCurrent ? 'var(--amber)' : 'var(--text-hi)', fontWeight: 600 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 12 }}>
<div style={{ minWidth: 0 }}>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: 13, color: isActive ? 'var(--amber)' : 'var(--text-hi)', fontWeight: 600 }}>
{p.name}
{isCurrent && <span style={{ marginLeft: 8, fontSize: 10, color: 'var(--amber)', background: 'var(--amber-glow)', padding: '1px 6px', borderRadius: 3 }}>ACTIVE</span>}
{isActive && <span style={{ marginLeft: 8, fontSize: 10, color: 'var(--amber)', background: 'var(--amber-glow)', padding: '1px 6px', borderRadius: 3 }}>ACTIVE</span>}
</div>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--text-lo)', marginTop: 4 }}>
{p.focal_mm}mm {calc.focal_ratio} · {p.aperture_mm}mm aperture · {p.pixel_um}μm px · {p.res_x}×{p.res_y}
@@ -112,16 +122,21 @@ function EquipmentProfiles() {
{' · '}FOV: <strong style={{ color: 'var(--teal)' }}>{calc.fov_w}</strong>
</div>
</div>
{!isCurrent && (
<div style={{ display: 'flex', gap: 8 }}>
<button onClick={() => startEdit(p as EquipProfile)} style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--blue)', background: 'none', border: '1px solid var(--border)', borderRadius: 3, padding: '3px 8px', cursor: 'pointer' }}>
Edit
<div style={{ display: 'flex', gap: 6, flexShrink: 0 }}>
{!isActive && (
<button onClick={() => setActiveId(p.id)} style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--amber)', background: 'var(--amber-glow)', border: '1px solid var(--amber-dim)', borderRadius: 3, padding: '3px 8px', cursor: 'pointer', whiteSpace: 'nowrap' }}>
Use
</button>
)}
<button onClick={() => startEdit(p)} style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--blue)', background: 'none', border: '1px solid var(--border)', borderRadius: 3, padding: '3px 8px', cursor: 'pointer' }}>
Edit
</button>
{profiles.length > 1 && (
<button onClick={() => deleteProfile(p.id)} style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--danger)', background: 'none', border: '1px solid var(--border)', borderRadius: 3, padding: '3px 8px', cursor: 'pointer' }}>
</button>
</div>
)}
)}
</div>
</div>
</div>
);