3939 var EXECUTION_TIMEOUT_MS = 5000 ; // 5 seconds timeout
4040 var currentTimeoutId = null ;
4141 var WORKER_SCRIPT = 'js/playground-worker.js' ;
42+ var DRAFTS_STORAGE_KEY = 'playground_drafts' ;
43+ var ACTIVE_DRAFT_KEY = 'playground_active_draft' ;
4244
4345 /* ================================================================
4446 3. EXAMPLE CODE SNIPPETS
127129 var clearConsoleBtn = $id ( 'clearConsole' ) ;
128130 var clearEditorBtn = $id ( 'clearEditor' ) ;
129131 var loadExampleBtn = $id ( 'loadExample' ) ;
132+ var draftSelector = $id ( 'draftSelector' ) ;
133+ var saveDraftBtn = $id ( 'saveDraft' ) ;
134+ var loadDraftBtn = $id ( 'loadDraft' ) ;
135+ var deleteDraftBtn = $id ( 'deleteDraft' ) ;
136+ var draftStatus = $id ( 'draftStatus' ) ;
130137
131138 /* Guard – abort gracefully if playground HTML is absent */
132139 if ( ! playgroundSection || ! runBtn || ! stopBtn || ! editorMount || ! consoleEl ) {
518525 } ) ;
519526 themeObserver . observe ( document . documentElement , { attributes : true } ) ;
520527
528+ function getDrafts ( ) {
529+ try {
530+ var data = localStorage . getItem ( DRAFTS_STORAGE_KEY ) ;
531+ return data ? JSON . parse ( data ) : { } ;
532+ } catch ( e ) {
533+ return { } ;
534+ }
535+ }
536+
537+ function saveDrafts ( drafts ) {
538+ try {
539+ localStorage . setItem ( DRAFTS_STORAGE_KEY , JSON . stringify ( drafts ) ) ;
540+ } catch ( e ) {
541+ console . error ( 'Failed to save drafts:' , e ) ;
542+ }
543+ }
544+
545+ function setDraftStatus ( message ) {
546+ if ( ! draftStatus ) return ;
547+
548+ draftStatus . textContent = message ;
549+ }
550+
551+ function refreshDraftSelector ( ) {
552+ if ( ! draftSelector ) return ;
553+
554+ var drafts = getDrafts ( ) ;
555+
556+ draftSelector . innerHTML =
557+ '<option value="">Select Draft</option>' ;
558+
559+ Object . keys ( drafts )
560+ . sort ( )
561+ . forEach ( function ( name ) {
562+ var option = document . createElement ( 'option' ) ;
563+ option . value = name ;
564+ option . textContent = name ;
565+ draftSelector . appendChild ( option ) ;
566+ } ) ;
567+ }
568+
521569 /* ================================================================
522570 6. UI HELPER FUNCTIONS
523571 ================================================================ */
718766
719767 if ( clearEditorBtn ) {
720768 clearEditorBtn . addEventListener ( 'click' , function ( ) {
769+ if (
770+ ! confirm (
771+ 'Clear editor contents? Unsaved changes will be lost.'
772+ )
773+ ) {
774+ return ;
775+ }
721776 setCode ( '' ) ;
722777 if ( cmView ) cmView . focus ( ) ;
723778 } ) ;
731786 } ) ;
732787 }
733788
789+ if ( saveDraftBtn ) {
790+ saveDraftBtn . addEventListener ( 'click' , function ( ) {
791+
792+ var draftName = prompt (
793+ 'Enter a name for this draft:'
794+ ) ;
795+
796+ if ( ! draftName ) return ;
797+
798+ draftName = draftName . trim ( ) ;
799+
800+ if ( ! draftName ) return ;
801+
802+ var drafts = getDrafts ( ) ;
803+
804+ drafts [ draftName ] = {
805+ code : getCode ( ) ,
806+ updatedAt : new Date ( ) . toISOString ( )
807+ } ;
808+
809+ saveDrafts ( drafts ) ;
810+
811+ try {
812+ localStorage . setItem ( ACTIVE_DRAFT_KEY , draftName ) ;
813+ } catch ( e ) {
814+ console . error ( 'Failed to save active draft:' , e ) ;
815+ }
816+
817+ refreshDraftSelector ( ) ;
818+
819+ draftSelector . value = draftName ;
820+
821+ setDraftStatus (
822+ 'Draft "' + draftName + '" saved'
823+ ) ;
824+ } ) ;
825+ }
826+
827+ if ( loadDraftBtn ) {
828+ loadDraftBtn . addEventListener ( 'click' , function ( ) {
829+
830+ var selectedDraft = draftSelector . value ;
831+
832+ if ( ! selectedDraft ) {
833+ setDraftStatus ( 'Select a draft first' ) ;
834+ return ;
835+ }
836+
837+ var drafts = getDrafts ( ) ;
838+
839+ if ( ! drafts [ selectedDraft ] ) {
840+ setDraftStatus ( 'Draft not found' ) ;
841+ return ;
842+ }
843+
844+ setCode (
845+ drafts [ selectedDraft ] . code || ''
846+ ) ;
847+
848+ try {
849+ localStorage . setItem ( ACTIVE_DRAFT_KEY , selectedDraft ) ;
850+ } catch ( e ) {
851+ console . error ( 'Failed to save active draft:' , e ) ;
852+ }
853+
854+ setDraftStatus (
855+ 'Loaded draft "' +
856+ selectedDraft +
857+ '"'
858+ ) ;
859+ } ) ;
860+ }
861+
862+ if ( deleteDraftBtn ) {
863+ deleteDraftBtn . addEventListener ( 'click' , function ( ) {
864+
865+ var selectedDraft = draftSelector . value ;
866+
867+ if ( ! selectedDraft ) {
868+ setDraftStatus ( 'Select a draft first' ) ;
869+ return ;
870+ }
871+
872+ var confirmed = confirm (
873+ 'Delete draft "' +
874+ selectedDraft +
875+ '" ?'
876+ ) ;
877+
878+ if ( ! confirmed ) return ;
879+
880+ var drafts = getDrafts ( ) ;
881+
882+ delete drafts [ selectedDraft ] ;
883+
884+ saveDrafts ( drafts ) ;
885+
886+ try {
887+ var activeDraft = localStorage . getItem ( ACTIVE_DRAFT_KEY ) ;
888+ if ( activeDraft === selectedDraft ) {
889+ localStorage . removeItem ( ACTIVE_DRAFT_KEY ) ;
890+ }
891+ } catch ( e ) {
892+ console . error ( 'Failed to remove active draft:' , e ) ;
893+ }
894+
895+ refreshDraftSelector ( ) ;
896+
897+ setDraftStatus (
898+ 'Deleted "' +
899+ selectedDraft +
900+ '"'
901+ ) ;
902+ } ) ;
903+ }
904+
734905 /* ================================================================
735906 11. BOOT
736907 ================================================================ */
746917 'print("Hello, World!")'
747918 ) ;
748919
920+ /* Populate the draft selector dropdown */
921+ refreshDraftSelector ( ) ;
922+
923+ /* Restore last active draft if available */
924+ try {
925+ var activeDraft = localStorage . getItem ( ACTIVE_DRAFT_KEY ) ;
926+
927+ var drafts = getDrafts ( ) ;
928+
929+ if (
930+ activeDraft &&
931+ drafts [ activeDraft ]
932+ ) {
933+
934+ draftSelector . value =
935+ activeDraft ;
936+
937+ setCode (
938+ drafts [ activeDraft ] . code || ''
939+ ) ;
940+
941+ setDraftStatus (
942+ 'Restored "' +
943+ activeDraft +
944+ '"'
945+ ) ;
946+ }
947+ } catch ( e ) {
948+ console . error ( 'Failed to restore active draft:' , e ) ;
949+ }
950+
749951} ( ) ) ;
0 commit comments