/** * Playground Store * * State untuk route /playground: file tree (C/Python) + console output/input. * Mengikuti pola stores lain di proyek (svelte/store writable). */ import { writable } from 'svelte/store'; export interface PlaygroundFile { id: string; name: string; content: string; modified: boolean; } export type ConsoleLineType = 'input' | 'output' | 'error' | 'info'; export interface ConsoleLine { type: ConsoleLineType; text: string; timestamp: number; } export type RunState = 'idle' | 'running'; interface PlaygroundState { files: PlaygroundFile[]; activeFileId: string; consoleHistory: ConsoleLine[]; consoleInput: string; stdinQueue: string[]; consoleVisible: boolean; running: boolean; } const STORAGE_KEY = 'elemes_playground_files_v1'; const DEFAULT_C_CONTENT = `#include int main() { printf("Halo, dunia!\\n"); return 0; } `; const DEFAULT_PY_CONTENT = `# Program Python sederhana nama = input("Siapa nama kamu? ") print(f"Halo, {nama}! Selamat belajar pemrograman.") `; function generateId(): string { return 'f_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 8); } function defaultFiles(): PlaygroundFile[] { return [ { id: generateId(), name: 'main.c', content: DEFAULT_C_CONTENT, modified: false }, { id: generateId(), name: 'main.py', content: DEFAULT_PY_CONTENT, modified: false } ]; } function loadPersistedFiles(): PlaygroundFile[] { try { const raw = localStorage.getItem(STORAGE_KEY); if (!raw) return defaultFiles(); const parsed = JSON.parse(raw) as PlaygroundFile[]; if (!Array.isArray(parsed) || parsed.length === 0) return defaultFiles(); return parsed.filter((f) => f && typeof f.name === 'string'); } catch { return defaultFiles(); } } function persistFiles(files: PlaygroundFile[]) { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(files)); } catch { // storage penuh / private mode — abaikan } } function initialState(): PlaygroundState { const files = loadPersistedFiles(); return { files, activeFileId: files[0]?.id ?? '', consoleHistory: [], consoleInput: '', stdinQueue: [], consoleVisible: true, running: false }; } function createPlaygroundStore() { const { subscribe, update, set } = writable(initialState()); return { subscribe, addFile: (name: string): string => { const id = generateId(); update((s) => { const files = [...s.files, { id, name, content: '', modified: true }]; persistFiles(files); return { ...s, files, activeFileId: id }; }); return id; }, renameFile: (id: string, name: string) => { update((s) => { const files = s.files.map((f) => f.id === id ? { ...f, name, modified: true } : f ); persistFiles(files); return { ...s, files }; }); }, updateFile: (id: string, content: string) => { update((s) => { const files = s.files.map((f) => f.id === id ? { ...f, content, modified: true } : f ); persistFiles(files); return { ...s, files }; }); }, deleteFile: (id: string) => { update((s) => { const files = s.files.filter((f) => f.id !== id); persistFiles(files); return { ...s, files, activeFileId: s.activeFileId === id ? (files[0]?.id ?? '') : s.activeFileId }; }); }, setActiveFile: (id: string) => { update((s) => ({ ...s, activeFileId: id })); }, markSaved: (id: string) => { update((s) => { const files = s.files.map((f) => f.id === id ? { ...f, modified: false } : f ); persistFiles(files); return { ...s, files }; }); }, appendConsole: (line: ConsoleLine) => { update((s) => ({ ...s, consoleHistory: [...s.consoleHistory, line].slice(-500) })); }, clearConsole: () => { update((s) => ({ ...s, consoleHistory: [] })); }, setConsoleInput: (text: string) => { update((s) => ({ ...s, consoleInput: text })); }, enqueueStdin: (text: string) => { const trimmed = text.trim(); if (!trimmed) return; update((s) => ({ ...s, stdinQueue: [...s.stdinQueue, trimmed] })); }, consumeStdin: (): string => { let stdin = ''; update((s) => { stdin = s.stdinQueue.map((line) => line + '\n').join('') + s.consoleInput; return { ...s, stdinQueue: [], consoleInput: '' }; }); return stdin; }, clearStdinQueue: () => { update((s) => ({ ...s, stdinQueue: [] })); }, toggleConsole: () => { update((s) => ({ ...s, consoleVisible: !s.consoleVisible })); }, setRunning: (running: boolean) => { update((s) => ({ ...s, running })); }, reset: () => { const fresh = initialState(); update(() => fresh); } }; } export const playgroundStore = createPlaygroundStore();