Move source manager from combatant area to side panel

Source management now opens in the right side panel (like bulk import
and stat blocks) instead of rendering inline above the combatant list.
All three panel modes properly clear each other on activation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lukas
2026-03-13 18:40:28 +01:00
parent a4797d5b15
commit 6ac8e67970
2 changed files with 31 additions and 12 deletions

View File

@@ -14,7 +14,6 @@ import { ActionBar } from "./components/action-bar";
import { CombatantRow } from "./components/combatant-row";
import { CreatePlayerModal } from "./components/create-player-modal";
import { PlayerManagement } from "./components/player-management";
import { SourceManager } from "./components/source-manager";
import { StatBlockPanel } from "./components/stat-block-panel";
import { Toast } from "./components/toast";
import { TurnNavigation } from "./components/turn-navigation";
@@ -113,7 +112,7 @@ export function App() {
const [selectedCreatureId, setSelectedCreatureId] =
useState<CreatureId | null>(null);
const [bulkImportMode, setBulkImportMode] = useState(false);
const [sourceManagerOpen, setSourceManagerOpen] = useState(false);
const [sourceManagerMode, setSourceManagerMode] = useState(false);
const [isRightPanelFolded, setIsRightPanelFolded] = useState(false);
const [pinnedCreatureId, setPinnedCreatureId] = useState<CreatureId | null>(
null,
@@ -146,6 +145,8 @@ export function App() {
const handleCombatantStatBlock = useCallback((creatureId: string) => {
setSelectedCreatureId(creatureId as CreatureId);
setBulkImportMode(false);
setSourceManagerMode(false);
setIsRightPanelFolded(false);
}, []);
@@ -167,11 +168,21 @@ export function App() {
.replace(/(^-|-$)/g, "");
const cId = `${result.source.toLowerCase()}:${slug}` as CreatureId;
setSelectedCreatureId(cId);
setBulkImportMode(false);
setSourceManagerMode(false);
setIsRightPanelFolded(false);
}, []);
const handleBulkImport = useCallback(() => {
setBulkImportMode(true);
setSourceManagerMode(false);
setSelectedCreatureId(null);
setIsRightPanelFolded(false);
}, []);
const handleOpenSourceManager = useCallback(() => {
setSourceManagerMode(true);
setBulkImportMode(false);
setSelectedCreatureId(null);
setIsRightPanelFolded(false);
}, []);
@@ -196,6 +207,7 @@ export function App() {
const handleDismissBrowsePanel = useCallback(() => {
setSelectedCreatureId(null);
setBulkImportMode(false);
setSourceManagerMode(false);
}, []);
const handleToggleFold = useCallback(() => {
@@ -282,19 +294,13 @@ export function App() {
onManagePlayers={() => setManagementOpen(true)}
onRollAllInitiative={handleRollAllInitiative}
showRollAllInitiative={showRollAllInitiative}
onOpenSourceManager={() => setSourceManagerOpen((o) => !o)}
onOpenSourceManager={handleOpenSourceManager}
autoFocus
/>
</div>
</div>
) : (
<>
{sourceManagerOpen && (
<div className="shrink-0 rounded-md border border-border bg-card px-4 py-3">
<SourceManager onCacheCleared={refreshCache} />
</div>
)}
{/* Scrollable area — combatant list */}
<div className="flex-1 overflow-y-auto min-h-0">
<div className="flex flex-col px-2 py-2">
@@ -344,7 +350,7 @@ export function App() {
onManagePlayers={() => setManagementOpen(true)}
onRollAllInitiative={handleRollAllInitiative}
showRollAllInitiative={showRollAllInitiative}
onOpenSourceManager={() => setSourceManagerOpen((o) => !o)}
onOpenSourceManager={handleOpenSourceManager}
/>
</div>
</>
@@ -391,6 +397,7 @@ export function App() {
bulkImportState={bulkImport.state}
onStartBulkImport={handleStartBulkImport}
onBulkImportDone={handleBulkImportDone}
sourceManagerMode={sourceManagerMode}
/>
{/* Toast for bulk import progress when panel is closed */}

View File

@@ -7,6 +7,7 @@ import type { BulkImportState } from "../hooks/use-bulk-import.js";
import { useSwipeToDismiss } from "../hooks/use-swipe-to-dismiss.js";
import { BulkImportPrompt } from "./bulk-import-prompt.js";
import { SourceFetchPrompt } from "./source-fetch-prompt.js";
import { SourceManager } from "./source-manager.js";
import { StatBlock } from "./stat-block.js";
import { Button } from "./ui/button.js";
@@ -32,6 +33,7 @@ interface StatBlockPanelProps {
bulkImportState?: BulkImportState;
onStartBulkImport?: (baseUrl: string) => void;
onBulkImportDone?: () => void;
sourceManagerMode?: boolean;
}
function extractSourceCode(cId: CreatureId): string {
@@ -236,6 +238,7 @@ export function StatBlockPanel({
bulkImportState,
onStartBulkImport,
onBulkImportDone,
sourceManagerMode,
}: StatBlockPanelProps) {
const [isDesktop, setIsDesktop] = useState(
() => window.matchMedia("(min-width: 1024px)").matches,
@@ -269,7 +272,7 @@ export function StatBlockPanel({
});
}, [creatureId, creature, isSourceCached]);
if (!creatureId && !bulkImportMode) return null;
if (!creatureId && !bulkImportMode && !sourceManagerMode) return null;
const sourceCode = creatureId ? extractSourceCode(creatureId) : "";
@@ -279,6 +282,10 @@ export function StatBlockPanel({
};
const renderContent = () => {
if (sourceManagerMode) {
return <SourceManager onCacheCleared={refreshCache} />;
}
if (
bulkImportMode &&
bulkImportState &&
@@ -324,7 +331,12 @@ export function StatBlockPanel({
};
const creatureName =
creature?.name ?? (bulkImportMode ? "Bulk Import" : "Creature");
creature?.name ??
(sourceManagerMode
? "Sources"
: bulkImportMode
? "Bulk Import"
: "Creature");
if (isDesktop) {
return (