@@ -15,6 +15,8 @@ import {
1515 customEntries ,
1616 dismissedConnections ,
1717 getNextCustomEntryId ,
18+ resetCustomEntryCounter ,
19+ layerOrder ,
1820 setActiveCategory ,
1921 setActiveModel
2022} from './state.js' ;
@@ -35,6 +37,11 @@ import {
3537 renderConnections
3638} from './connections.js' ;
3739import { trackAppLaunched , trackExportButtonClick , trackNodeAdded } from './analytics.js' ;
40+ import {
41+ clearPersistedDiagramState ,
42+ loadDiagramState ,
43+ persistDiagramState
44+ } from './persistence.js' ;
3845
3946let pendingConnectionNode = null ;
4047let draggedNode = null ;
@@ -61,6 +68,7 @@ export function initModelPicker() {
6168 setActiveModel ( modelId ) ;
6269 updateModelPickerState ( ) ;
6370 renderConnections ( ) ;
71+ void persistDiagramState ( ) ;
6472 }
6573 applyModelAutoAdjustments ( modelId ) ;
6674 } ) ;
@@ -170,6 +178,13 @@ export function updateModelPickerState() {
170178 } ) ;
171179}
172180
181+ function updateCategoryTabState ( ) {
182+ const tabs = document . querySelectorAll ( '.category-tab' ) ;
183+ tabs . forEach ( tab => {
184+ tab . classList . toggle ( 'active' , tab . dataset . category === activeCategory ) ;
185+ } ) ;
186+ }
187+
173188function createComponentListItem ( item , category , isCustom ) {
174189 const li = document . createElement ( 'li' ) ;
175190 li . className = 'component-item' ;
@@ -223,6 +238,7 @@ export function addCustomEntry() {
223238 if ( list ) {
224239 list . scrollTop = list . scrollHeight ;
225240 }
241+ void persistDiagramState ( ) ;
226242}
227243
228244export function addItemToLayer ( itemId , itemName , iconKey , category ) {
@@ -254,6 +270,7 @@ export function addItemToLayer(itemId, itemName, iconKey, category) {
254270 updateSidebarItemState ( itemId , category , true ) ;
255271 renderConnections ( ) ;
256272 trackNodeAdded ( itemName ) ;
273+ void persistDiagramState ( ) ;
257274}
258275
259276export function ensureItemAdded ( itemId ) {
@@ -376,6 +393,7 @@ function toggleAmplitudeSdkBadge(node, badgeId) {
376393 amplitudeSdkSelectedBadges . add ( badgeId ) ;
377394 }
378395 syncAmplitudeSdkBadgeState ( node ) ;
396+ void persistDiagramState ( ) ;
379397}
380398
381399function syncAmplitudeSdkBadgeState ( node ) {
@@ -404,6 +422,7 @@ function removeItemFromLayer(itemId, category, node) {
404422 removeRelatedCustomConnections ( itemId ) ;
405423 enforceNodeOrdering ( ) ;
406424 renderConnections ( ) ;
425+ void persistDiagramState ( ) ;
407426 } ) ;
408427}
409428
@@ -450,6 +469,7 @@ function addCustomConnection(sourceId, targetId) {
450469 customConnections . add ( key ) ;
451470 dismissedConnections . delete ( key ) ;
452471 renderConnections ( ) ;
472+ void persistDiagramState ( ) ;
453473}
454474
455475function removeRelatedCustomConnections ( nodeId ) {
@@ -591,6 +611,7 @@ function handleLayerDrop(e) {
591611 content . classList . remove ( 'drag-over' ) ;
592612 enforceNodeOrdering ( ) ;
593613 renderConnections ( ) ;
614+ void persistDiagramState ( ) ;
594615 handleDragEnd ( ) ;
595616}
596617
@@ -608,7 +629,7 @@ function loadHtml2Canvas() {
608629 } ) ;
609630}
610631
611- export function initializeApp ( ) {
632+ export async function initializeApp ( ) {
612633 if ( document ?. documentElement ) {
613634 document . documentElement . style . setProperty ( '--slot-columns' , String ( SLOT_COLUMNS ) ) ;
614635 }
@@ -618,7 +639,129 @@ export function initializeApp() {
618639 initModelPicker ( ) ;
619640 initLayerDragTargets ( ) ;
620641 initExportButton ( ) ;
642+ initRefreshButton ( ) ;
643+ const restored = await restoreDiagramStateFromStorage ( ) ;
644+ if ( ! restored ) {
645+ updateCategoryTabState ( ) ;
646+ renderComponentList ( activeCategory ) ;
647+ renderConnections ( ) ;
648+ }
649+ window . addEventListener ( 'resize' , ( ) => renderConnections ( ) ) ;
650+ }
651+
652+ async function restoreDiagramStateFromStorage ( ) {
653+ const stored = await loadDiagramState ( ) ;
654+ if ( ! stored ) return false ;
655+ try {
656+ Object . values ( addedItems ) . forEach ( set => set . clear ( ) ) ;
657+ Object . keys ( layerOrder ) . forEach ( category => {
658+ layerOrder [ category ] = [ ] ;
659+ } ) ;
660+ Object . keys ( customEntries ) . forEach ( category => {
661+ customEntries [ category ] = [ ] ;
662+ } ) ;
663+ customConnections . clear ( ) ;
664+ dismissedConnections . clear ( ) ;
665+ amplitudeSdkSelectedBadges . clear ( ) ;
666+ clearCustomItemIndex ( ) ;
667+
668+ if ( Array . isArray ( stored . amplitudeSdkSelectedBadges ) ) {
669+ stored . amplitudeSdkSelectedBadges . forEach ( id => amplitudeSdkSelectedBadges . add ( id ) ) ;
670+ }
671+
672+ let maxCustomId = 0 ;
673+ if ( stored . customEntries ) {
674+ Object . entries ( stored . customEntries ) . forEach ( ( [ category , entries ] ) => {
675+ if ( ! customEntries [ category ] ) {
676+ customEntries [ category ] = [ ] ;
677+ }
678+ entries . forEach ( entry => {
679+ customEntries [ category ] . push ( { ...entry } ) ;
680+ itemCategoryIndex [ entry . id ] = category ;
681+ const match = / c u s t o m - [ a - z - ] + - ( \d + ) / . exec ( entry . id ) ;
682+ if ( match ) {
683+ const parsed = Number ( match [ 1 ] ) ;
684+ if ( Number . isFinite ( parsed ) ) {
685+ maxCustomId = Math . max ( maxCustomId , parsed ) ;
686+ }
687+ }
688+ } ) ;
689+ } ) ;
690+ }
691+ resetCustomEntryCounter ( maxCustomId ) ;
692+
693+ if ( stored . layerOrder ) {
694+ Object . entries ( stored . layerOrder ) . forEach ( ( [ category , slots ] ) => {
695+ layerOrder [ category ] = Array . isArray ( slots ) ? [ ...slots ] : [ ] ;
696+ } ) ;
697+ }
698+
699+ if ( stored . activeModel ) {
700+ setActiveModel ( stored . activeModel ) ;
701+ }
702+ if ( stored . activeCategory ) {
703+ setActiveCategory ( stored . activeCategory ) ;
704+ }
705+
706+ Object . keys ( addedItems ) . forEach ( category => {
707+ const slots = layerOrder [ category ] || [ ] ;
708+ slots . forEach ( id => {
709+ if ( id ) ensureItemAdded ( id ) ;
710+ } ) ;
711+ const extraIds = new Set ( stored . addedItems ?. [ category ] || [ ] ) ;
712+ slots . forEach ( id => extraIds . delete ( id ) ) ;
713+ extraIds . forEach ( id => ensureItemAdded ( id ) ) ;
714+ } ) ;
715+
716+ ( stored . customConnections || [ ] ) . forEach ( key => customConnections . add ( key ) ) ;
717+ ( stored . dismissedConnections || [ ] ) . forEach ( key => dismissedConnections . add ( key ) ) ;
718+
719+ updateCategoryTabState ( ) ;
720+ updateModelPickerState ( ) ;
721+ renderComponentList ( activeCategory ) ;
722+ renderConnections ( ) ;
723+ return true ;
724+ } catch ( error ) {
725+ console . error ( 'Failed to restore diagram state' , error ) ;
726+ return false ;
727+ }
728+ }
729+
730+ function initRefreshButton ( ) {
731+ const refreshBtn = document . getElementById ( 'refresh-btn' ) ;
732+ if ( ! refreshBtn ) return ;
733+ refreshBtn . addEventListener ( 'click' , ( ) => {
734+ clearDiagram ( ) ;
735+ } ) ;
736+ }
737+
738+ function clearCustomItemIndex ( ) {
739+ Object . keys ( itemCategoryIndex ) . forEach ( id => {
740+ if ( id . startsWith ( 'custom-' ) ) {
741+ delete itemCategoryIndex [ id ] ;
742+ }
743+ } ) ;
744+ }
745+
746+ function clearDiagram ( ) {
747+ document . querySelectorAll ( '.diagram-node' ) . forEach ( node => node . remove ( ) ) ;
748+ Object . keys ( addedItems ) . forEach ( category => addedItems [ category ] . clear ( ) ) ;
749+ Object . keys ( layerOrder ) . forEach ( category => {
750+ layerOrder [ category ] = [ ] ;
751+ } ) ;
752+ customConnections . clear ( ) ;
753+ dismissedConnections . clear ( ) ;
754+ amplitudeSdkSelectedBadges . clear ( ) ;
755+ Object . keys ( customEntries ) . forEach ( category => {
756+ customEntries [ category ] = [ ] ;
757+ } ) ;
758+ resetCustomEntryCounter ( 0 ) ;
759+ clearCustomItemIndex ( ) ;
760+ setActiveModel ( null ) ;
761+ setActiveCategory ( 'marketing' ) ;
762+ updateCategoryTabState ( ) ;
763+ updateModelPickerState ( ) ;
621764 renderComponentList ( activeCategory ) ;
622765 renderConnections ( ) ;
623- window . addEventListener ( 'resize' , ( ) => renderConnections ( ) ) ;
766+ clearPersistedDiagramState ( ) ;
624767}
0 commit comments