mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 06:25:41 +00:00
feat(orbit): in-app diagnostics popover with copyable event log (#524)
* feat(orbit): in-app diagnostics popover with copyable event log Multiple users on Discord report Orbit guests stopping after the first song with no errors anywhere — Settings → Debug → Export Logs is too buried for non-technical reporters, and the relevant code branches have no logging at all (silent fail). This adds a one-click "Copy log" path right inside the Orbit session bar. The new Activity-icon button next to Help opens a popover with: - Live mini-display: role, host vs. guest track id + position, drift, age of the host's last state write — all updating once a second. - Scrolling event log textarea fed by an in-memory ring (200 events). - Copy + Clear buttons. Copy formats `[ISO] [scope] body` lines and drops them on the clipboard — paste straight into a Discord report. Instrumentation lands at the previously-silent decision points: - Guest pull tick: full snapshot of host vs. guest state on every read. - Each branch of the divergence detection in `useOrbitGuest.ts` logs which path it took and why (initial / track-change-followed / track-change-diverged / play-pause-flip), making the "stuck after first song" symptom diagnosable from the buffer alone. - Host pushes log track id, isPlaying, queue length, guest count. Events are also bridged to the existing `frontend_debug_log` Tauri command when Settings → Logging is on Debug, so power users still get the same data in `psysonic-logs-*.log` for offline triage. i18n: full `orbit.diag.*` namespace in all eight locales. EN + DE are native; ES / FR / NB / NL / RU / ZH are first-pass and may want a polish from native speakers later. * docs(changelog): add orbit diagnostics popover entry
This commit is contained in:
committed by
GitHub
parent
acadc1be34
commit
a702a5dd5b
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { X, RefreshCw, Shuffle, Settings2, Share2, HelpCircle } from 'lucide-react';
|
||||
import { X, RefreshCw, Shuffle, Settings2, Share2, HelpCircle, Activity } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import { useHelpModalStore } from '../store/helpModalStore';
|
||||
@@ -16,6 +16,7 @@ import OrbitParticipantsPopover from './OrbitParticipantsPopover';
|
||||
import OrbitExitModal from './OrbitExitModal';
|
||||
import OrbitSettingsPopover from './OrbitSettingsPopover';
|
||||
import OrbitSharePopover from './OrbitSharePopover';
|
||||
import OrbitDiagnosticsPopover from './OrbitDiagnosticsPopover';
|
||||
import ConfirmModal from './ConfirmModal';
|
||||
|
||||
/**
|
||||
@@ -49,10 +50,12 @@ export default function OrbitSessionBar() {
|
||||
const [peopleOpen, setPeopleOpen] = useState(false);
|
||||
const [settingsOpen, setSettingsOpen] = useState(false);
|
||||
const [shareOpen, setShareOpen] = useState(false);
|
||||
const [diagOpen, setDiagOpen] = useState(false);
|
||||
const [confirmLeave, setConfirmLeave] = useState(false);
|
||||
const peopleBtnRef = useRef<HTMLButtonElement>(null);
|
||||
const settingsBtnRef = useRef<HTMLButtonElement>(null);
|
||||
const shareBtnRef = useRef<HTMLButtonElement>(null);
|
||||
const diagBtnRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
// Second-level tick just for the shuffle countdown + drift readout —
|
||||
// the store itself only ticks at 2.5 s which is too coarse for a smooth
|
||||
@@ -209,6 +212,18 @@ export default function OrbitSessionBar() {
|
||||
<span>{t('orbit.catchUpLabel')}</span>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
ref={diagBtnRef}
|
||||
type="button"
|
||||
className="orbit-bar__settings"
|
||||
onClick={() => setDiagOpen(v => !v)}
|
||||
data-tooltip={t('orbit.diag.openTooltip')}
|
||||
aria-haspopup="dialog"
|
||||
aria-expanded={diagOpen || undefined}
|
||||
aria-label={t('orbit.diag.openTooltip')}
|
||||
>
|
||||
<Activity size={14} />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="orbit-bar__settings"
|
||||
@@ -247,6 +262,12 @@ export default function OrbitSessionBar() {
|
||||
onClose={() => setShareOpen(false)}
|
||||
/>
|
||||
)}
|
||||
{diagOpen && (
|
||||
<OrbitDiagnosticsPopover
|
||||
anchorRef={diagBtnRef}
|
||||
onClose={() => setDiagOpen(false)}
|
||||
/>
|
||||
)}
|
||||
<OrbitExitModal />
|
||||
<ConfirmModal
|
||||
open={confirmLeave}
|
||||
|
||||
Reference in New Issue
Block a user