diff --git a/src/games/storywriter/components/menu.tsx b/src/games/storywriter/components/menu.tsx index 04dbc17..23ebd93 100644 --- a/src/games/storywriter/components/menu.tsx +++ b/src/games/storywriter/components/menu.tsx @@ -6,7 +6,7 @@ import { useBool } from "@common/hooks/useBool"; import { useInputState } from "@common/hooks/useInputState"; import type { World, Story } from "../contexts/state"; import styles from '../assets/menu.module.css'; -import { Pencil, X, Plus, Plug, Settings, Copy, ChevronRight, ChevronDown, Globe } from "lucide-preact"; +import { Pencil, X, Plus, Plug, Settings, Copy, ChevronRight, ChevronDown, Globe, Download, Upload } from "lucide-preact"; // ─── Inline Rename Input ────────────────────────────────────────────────────── @@ -101,6 +101,7 @@ interface WorldItemProps { onSelectWorld: () => void; onRenameWorld: (title: string) => void; onDeleteWorld: () => void; + onExportWorld: () => void; onCreateStory: () => void; onSelectStory: (storyId: string) => void; onRenameStory: (storyId: string, title: string) => void; @@ -110,7 +111,7 @@ interface WorldItemProps { const WorldItem = ({ world, activeWorldId, activeStoryId, - onSelectWorld, onRenameWorld, onDeleteWorld, onCreateStory, + onSelectWorld, onRenameWorld, onDeleteWorld, onExportWorld, onCreateStory, onSelectStory, onRenameStory, onDeleteStory, onDuplicateStory, }: WorldItemProps) => { const isRenaming = useBool(false); @@ -150,6 +151,9 @@ const WorldItem = ({ + @@ -233,11 +237,48 @@ export const Menu = () => { dispatch({ type: 'DUPLICATE_STORY', worldId, id: storyId }); }; + const handleExportWorld = (worldId: string) => { + const world = worlds.find(w => w.id === worldId); + if (!world) return; + const json = JSON.stringify(world, null, 2); + const blob = new Blob([json], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `${world.title}.json`; + a.click(); + URL.revokeObjectURL(url); + }; + + const handleImportWorld = () => { + const input = document.createElement('input'); + input.type = 'file'; + input.accept = '.json,application/json'; + input.onchange = () => { + const file = input.files?.[0]; + if (!file) return; + const reader = new FileReader(); + reader.onload = () => { + try { + const world = JSON.parse(reader.result as string); + dispatch({ type: 'IMPORT_WORLD', world }); + } catch { + alert('Invalid world file.'); + } + }; + reader.readAsText(file); + }; + input.click(); + }; + return (