UNPKG

@flowfuse/flowfuse

Version:

An open source low-code development platform

1 lines • 137 kB
{"version":3,"file":"main.c23d1469027f5105171c.css","mappings":"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;CAAA;;IAAA;CAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;IAAA;CAAA;CAAA;CAAA;IAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;IAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;IAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;IAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;IAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;IAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;IAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;IAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;IAAA;SAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;IAAA;CAAA;CAAA;CAAA;IAAA;CAAA;CAAA;;IAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;IAAA;CAAA;CAAA;CAAA;;IHAA,gFAAc;IGAd;CAAA;CAAA;CAAA;IAAA;CAAA;CAAA;CAAA;IAAA;CAAA;CAAA;CAAA;IAAA;CAAA;CAAA;CAAA;AHCA;CGDA;AHCoB;AAApB;;CGDA;EAAA;EAAA;AHCoB;AAApB;;CGDA;EAAA;EAAA;AHCoB;AAApB;;CGDA;EAAA;EAAA;AHCoB;AAApB;;CGDA;EAAA;EAAA;AHCoB;AAApB;;CGDA;EAAA;EAAA;AHCoB;AGDpB;CAAA;CAAA;AAAA;;CAAA;EAAA;EAAA;CAAA;AAAA;CAAA;CAAA;CAAA;AAAA;;CAAA;EAAA;EAAA;CAAA;AHEA;CGFA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;CAAA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;CAAA;CAAA;AHEmB;AAAnB;CGFA;CAAA;CAAA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;;CGFA;EAAA;EAAA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;IAAA;SAAA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;MAAA;AHEmB;AAAnB;CGFA;MAAA;AHEmB;AAAnB;CGFA;MAAA;AHEmB;AAAnB;CGFA;MAAA;AHEmB;AAAnB;CGFA;CAAA;CAAA;AHEmB;AAAnB;CGFA;CAAA;CAAA;AHEmB;AAAnB;CGFA;CAAA;CAAA;AHEmB;AAAnB;CGFA;CAAA;CAAA;AHEmB;AAAnB;CGFA;CAAA;CAAA;AHEmB;AAAnB;CGFA;CAAA;CAAA;AHEmB;AAAnB;CGFA;CAAA;CAAA;AHEmB;AAAnB;CGFA;CAAA;CAAA;AHEmB;AAAnB;CGFA;CAAA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;CAAA;CAAA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;CAAA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;CAAA;CAAA;CAAA;CAAA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAAnB;CGFA;AHEmB;AAFnB;CGAA;AH4RC;AA5RD;CGAA;CAAA;AH4RC;AA5RD;CGAA;CAAA;AH4RC;AA5RD;CGAA;CAAA;AH4RC;AA5RD;CGAA;CAAA;AH4RC;AA5RD;CGAA;AH4RC;AA5RD;CGAA;CAAA;AH4RC;AA5RD;;CGAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;AH4RC;AA5RD;;CGAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;EAAA;AH4RC;AA5RD;;CGAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;EAAA;AH4RC;AA5RD;;CGAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;AH4RC;AA5RD;;CGAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;;CAAA;EAAA;EAAA;AH4RC,C","sources":["webpack://@flowfuse/flowfuse/./frontend/src/components/dialogs/AssetDetailDialog.vue","webpack://@flowfuse/flowfuse/./frontend/src/components/dialogs/AssetCompareDialog.vue","webpack://@flowfuse/flowfuse/./frontend/src/pages/device/VersionHistory/Snapshots/index.vue","webpack://@flowfuse/flowfuse/./frontend/src/pages/device/index.vue","webpack://@flowfuse/flowfuse/./frontend/src/components/DevicesBrowser.vue","webpack://@flowfuse/flowfuse/./frontend/src/pages/instance/components/InstanceLogs.vue","webpack://@flowfuse/flowfuse/./frontend/src/components/flow-viewer/FlowViewer.vue","webpack://@flowfuse/flowfuse/./frontend/src/index.css","webpack://@flowfuse/flowfuse/./frontend/src/%3Cinput%20css%20aXXsRI%3E","webpack://@flowfuse/flowfuse/./frontend/src/%3Cinput%20css%20pf0nCj%3E","webpack://@flowfuse/flowfuse/<no source>"],"sourcesContent":["<template>\n <ff-dialog\n ref=\"dialog\" :header=\"header\" :sub-header=\"`Node-RED Version: ${nrVersion}`\" confirm-label=\"Close\" :closeOnConfirm=\"true\"\n data-el=\"flow-view-dialog\" boxClass=\"!min-w-[80%] !min-h-[80%] !w-[80%] !h-[80%]\"\n contentClass=\"overflow-hidden flex-grow\" @confirm=\"confirm()\"\n >\n <template #default>\n <div ref=\"viewer\" data-el=\"ff-flow-previewer\" class=\"ff-flow-viewer\" @click.stop.prevent>\n Loading...\n </div>\n </template>\n <template #actions>\n <div class=\"flex justify-end\">\n <ff-button data-action=\"dialog-confirm\" @click=\"confirm()\">Close</ff-button>\n </div>\n </template>\n </ff-dialog>\n</template>\n<script>\n\nimport FlowRenderer from '@flowfuse/flow-renderer'\n\nexport default {\n name: 'FlowViewerDialog',\n props: {\n title: {\n type: String,\n default: ''\n }\n },\n setup () {\n return {\n show (payload) { // accepts blueprints, snapshots and libraries\n this.mode = 'view'\n this.$refs.dialog.show()\n this.payload = payload\n setTimeout(() => {\n this.renderFlows()\n }, 20)\n }\n }\n },\n data () {\n return {\n payload: []\n }\n },\n computed: {\n flow () {\n return this.payload?.flows?.flows || []\n },\n nrVersion () {\n const mods = this.payload?.settings?.modules\n if (mods) {\n return mods['node-red'] || 'Unavailable'\n }\n return ''\n },\n header () {\n return this.payload?.name || this.title || 'Flow'\n }\n },\n mounted () {\n },\n methods: {\n confirm () {\n this.$refs.dialog.close()\n },\n renderFlows () {\n const flowRenderer = new FlowRenderer()\n flowRenderer.renderFlows(this.flow, {\n container: this.$refs.viewer\n })\n }\n }\n}\n</script>\n\n<style scoped>\n.ff-flow-viewer {\n height: 100%;\n}\n</style>\n","<template>\n <ff-dialog\n ref=\"dialog\" :header=\"header\" confirm-label=\"Close\" :closeOnConfirm=\"true\" data-el=\"flow-view-dialog\"\n boxClass=\"!min-w-[80%] !min-h-[80%] !w-[80%] !h-[80%]\" contentClass=\"overflow-hidden flex-grow\"\n @confirm=\"confirm()\"\n >\n <template #default>\n <div class=\"flex gap-2\" data-el=\"snapshot-compare-toolbar\">\n <ff-listbox\n v-model=\"compareSnapshot\"\n :options=\"compareSnapshotList\"\n data-el=\"snapshots-list\"\n label-key=\"label\"\n option-title-key=\"description\"\n class=\"flex-grow\"\n />\n <ff-button\n v-if=\"true\"\n :disabled=\"!compareSnapshot\"\n data-action=\"compare-snapshots\"\n kind=\"secondary\"\n style=\"height: 30px; width: 106px\"\n class=\"w-32\"\n @click=\"renderComparison\"\n >\n Compare\n </ff-button>\n </div>\n <div v-if=\"changes.length\" class=\"flex justify-between items-center gap-2 mt-2 ml-2\">\n <div class=\"whitespace-nowrap\">Change {{ changeIndex + 1 }} of {{ changes.length }}:</div>\n <div class=\"text-sm text-gray-500 flex-grow truncate overflow-ellipsis\">\n {{ changes[changeIndex].toString() }}\n </div>\n <ff-button kind=\"secondary\" size=\"small\" class=\"w-14\" @click=\"gotoPreviousDifference\">Prev</ff-button>\n <ff-button kind=\"secondary\" size=\"small\" class=\"w-14\" @click=\"gotoNextDifference\">Next</ff-button>\n </div>\n <div v-else class=\"mt-2\">\n <div class=\"text-sm text-gray-500 flex-grow truncate overflow-ellipsis ml-2\">No differences found</div>\n </div>\n <div ref=\"compareViewer\" data-el=\"ff-flow-compare-view\" class=\"ff-flow-compare-viewer pt-4\" @click.stop.prevent>\n &nbsp;\n </div>\n </template>\n <template #actions>\n <div class=\"flex justify-end\">\n <ff-button data-action=\"dialog-confirm\" @click=\"confirm()\">Close</ff-button>\n </div>\n </template>\n </ff-dialog>\n</template>\n<script>\n\nimport FlowRenderer from '@flowfuse/flow-renderer'\n\nimport SnapshotsApi from '../../api/snapshots.js'\n\nimport Alerts from '../../services/alerts.js'\n\nexport default {\n name: 'AssetCompareDialog',\n props: {\n title: {\n type: String,\n default: ''\n }\n },\n setup () {\n return {\n /**\n * Shows the compare flows dialog and presents the user with a list of snapshots to compare against\n * @param {{flows: { flows :[]}}} v1Snapshot - A snapshot object as the base for comparison\n * @param {[{label: String, value: String}]} snapshotList - A list of snapshots to compare against where label is the snapshot name and value is the snapshot id\n */\n show (v1Snapshot, snapshotList) {\n this.mode = 'compare'\n this.payload = v1Snapshot\n this.compareSnapshot = null\n this.changes = []\n this.changeIndex = 0\n this.compareSnapshotList = snapshotList\n this.$refs.dialog.show()\n }\n }\n },\n data () {\n return {\n payload: [],\n snapshotList: [],\n compareSnapshot: null,\n compareSnapshotList: [],\n mode: 'view', // view, compare\n changes: [],\n changeIndex: 0\n }\n },\n computed: {\n flow () {\n return this.payload?.flows?.flows || []\n },\n header () {\n return this.payload?.name || this.title || 'Flow'\n }\n },\n mounted () {\n },\n methods: {\n confirm () {\n this.cleanup()\n this.$refs.dialog.close()\n },\n renderFlows () {\n this.cleanup()\n const flowRenderer = new FlowRenderer()\n flowRenderer.renderFlows(this.flow, {\n container: this.$refs.compareViewer\n })\n },\n async renderComparison (snapshotId) {\n this.cleanup()\n const compareSnapshot = await SnapshotsApi.getFullSnapshot(this.compareSnapshot)\n if (!compareSnapshot?.flows?.flows) {\n Alerts.emit('Flows not found in the selected snapshot', 'warning')\n return\n }\n const flowRenderer = new FlowRenderer()\n const flows = [this.flow, compareSnapshot?.flows?.flows]\n const result = flowRenderer.compare(flows, {\n container: this.$refs.compareViewer\n })\n this.changes = result?.changes || []\n },\n gotoNextDifference () {\n this.changeIndex = (this.changeIndex + 1) % this.changes.length\n this.changes[this.changeIndex].highlight()\n },\n gotoPreviousDifference () {\n this.changeIndex = (this.changeIndex - 1 + this.changes.length) % this.changes.length\n this.changes[this.changeIndex].highlight()\n },\n cleanup () {\n while (this.$refs.compareViewer?.firstChild) {\n this.$refs.compareViewer.removeChild(this.$refs.compareViewer.firstChild)\n }\n }\n }\n}\n</script>\n\n<style scoped>\n.ff-flow-compare-viewer {\n height: calc(100% - 4.5rem);\n}\n</style>\n","<template>\n <div id=\"device-snapshots\">\n <div v-if=\"isOwnedByAnInstance || isUnassigned\" class=\"space-y-6\">\n <EmptyState :feature-unavailable=\"!features.deviceEditor\">\n <template #img>\n <img src=\"../../../../images/empty-states/instance-snapshots.png\">\n </template>\n <template #header>Snapshots are available when a Remote Instance is assigned to an Application</template>\n <template #message>\n <p>\n Snapshots are point-in-time backups of your Node-RED Instances\n and capture the flows, credentials and runtime settings.\n </p>\n <p v-if=\"device.ownerType !== 'application'\" class=\"block\">\n A Remote Instance must first be <a class=\"ff-link\" href=\"https://flowfuse.com/docs/device-agent/register/#assign-the-device-to-an-application\" target=\"_blank\" rel=\"noreferrer\">assigned to an Application</a>, in order to create snapshots.\n </p>\n <p v-else-if=\"!developerMode\" class=\"block\">\n A Remote Instance must be in Developer Mode and online to create a Snapshot.\n </p>\n </template>\n <template v-if=\"hasPermission('device:snapshot:create')\" #actions>\n <ff-button\n v-if=\"hasPermission('snapshot:import')\"\n kind=\"secondary\" :disabled=\"busy || !features.deviceEditor || device.ownerType !== 'application'\"\n data-action=\"import-snapshot\"\n @click=\"$emit('show-import-snapshot-dialog')\"\n >\n <template #icon-left><UploadIcon /></template>Upload Snapshot\n </ff-button>\n <ff-button\n kind=\"primary\"\n :disabled=\"!developerMode || busy || !features.deviceEditor || device.ownerType !== 'application'\"\n data-action=\"create-snapshot\"\n @click=\"$emit('show-create-snapshot-dialog')\"\n >\n <template #icon-left><PlusSmIcon /></template>Create Snapshot\n </ff-button>\n </template>\n </EmptyState>\n </div>\n <div v-else class=\"space-y-6\">\n <ff-loading v-if=\"loading\" message=\"Loading Snapshots...\" />\n <template v-else-if=\"features.deviceEditor && snapshots.length > 0\">\n <ff-data-table data-el=\"snapshots\" class=\"space-y-4\" :columns=\"columns\" :rows=\"snapshotsFiltered\" :show-search=\"true\" search-placeholder=\"Search Snapshots...\">\n <template #actions>\n <DropdownMenu data-el=\"snapshot-filter\" buttonClass=\"ff-btn ff-btn--secondary\" :options=\"snapshotFilterOptions\">\n <FilterIcon class=\"ff-btn--icon ff-btn--icon-left\" aria-hidden=\"true\" />\n {{ snapshotFilter?.name || 'All Snapshots' }}\n <span class=\"sr-only\">Filter Snapshots</span>\n </DropdownMenu>\n </template>\n <template #context-menu=\"{row}\">\n <ff-list-item :disabled=\"!canDeploy(row)\" label=\"Restore Snapshot\" @click=\"showDeploySnapshotDialog(row)\" />\n <ff-list-item :disabled=\"!hasPermission('snapshot:edit')\" label=\"Edit Snapshot\" @click=\"showEditSnapshotDialog(row)\" />\n <ff-list-item :disabled=\"!hasPermission('snapshot:full')\" label=\"View Snapshot\" @click=\"showViewSnapshotDialog(row)\" />\n <ff-list-item :disabled=\"!hasPermission('snapshot:full')\" label=\"Compare Snapshot...\" @click=\"showCompareSnapshotDialog(row)\" />\n <ff-list-item :disabled=\"!canDownload(row)\" label=\"Download Snapshot\" @click=\"showDownloadSnapshotDialog(row)\" />\n <ff-list-item :disabled=\"!hasPermission('device:snapshot:read')\" label=\"Download package.json\" @click=\"downloadSnapshotPackage(row)\" />\n <ff-list-item :disabled=\"!canDelete(row)\" label=\"Delete Snapshot\" kind=\"danger\" @click=\"showDeleteSnapshotDialog(row)\" />\n </template>\n </ff-data-table>\n </template>\n <template v-else-if=\"!loading\">\n <EmptyState :feature-unavailable=\"!features.deviceEditor\" :feature-unavailable-message=\"'This requires Developer Mode on Devices, which is a FlowFuse Enterprise Feature'\">\n <template #img>\n <img src=\"../../../../images/empty-states/instance-snapshots.png\">\n </template>\n <template #header>Create your First Snapshot</template>\n <template #message>\n <p>\n Snapshots are point-in-time backups of your Node-RED Instances\n and capture the flows, credentials and runtime settings.\n </p>\n <p v-if=\"device.ownerType !== 'application'\" class=\"block\">\n A Remote Instance must first be <a class=\"ff-link\" href=\"https://flowfuse.com/docs/device-agent/register/#assign-the-device-to-an-application\" target=\"_blank\" rel=\"noreferrer\">assigned to an Application</a>, in order to create snapshots.\n </p>\n <p v-else-if=\"!developerMode\" class=\"block\">\n A Remote Instance must be in Developer Mode and online to create a Snapshot.\n </p>\n </template>\n <template v-if=\"hasPermission('device:snapshot:create')\" #actions>\n <ff-button\n v-if=\"hasPermission('snapshot:import')\"\n kind=\"secondary\" :disabled=\"busy || !features.deviceEditor || device.ownerType !== 'application'\"\n data-action=\"import-snapshot\"\n @click=\"$emit('show-import-snapshot-dialog')\"\n >\n <template #icon-left><UploadIcon /></template>Upload Snapshot\n </ff-button>\n <ff-button\n kind=\"primary\"\n :disabled=\"!canCreateSnapshot\"\n data-action=\"create-snapshot\"\n @click=\"$emit('show-create-snapshot-dialog')\"\n >\n <template #icon-left><PlusSmIcon /></template>Create Snapshot\n </ff-button>\n </template>\n </EmptyState>\n </template>\n\n <SnapshotExportDialog ref=\"snapshotExportDialog\" data-el=\"dialog-export-snapshot\" />\n <SnapshotEditDialog ref=\"snapshotEditDialog\" data-el=\"dialog-edit-snapshot\" @snapshot-updated=\"onSnapshotEdit\" />\n <AssetDetailDialog ref=\"snapshotViewerDialog\" data-el=\"dialog-view-snapshot\" />\n <AssetCompareDialog ref=\"snapshotCompareDialog\" data-el=\"dialog-compare-snapshot\" />\n </div>\n </div>\n</template>\n\n<script>\nimport { FilterIcon, PlusSmIcon, UploadIcon } from '@heroicons/vue/outline'\nimport { markRaw } from 'vue'\nimport { mapState } from 'vuex'\n\nimport ApplicationApi from '../../../../api/application.js'\nimport DeviceApi from '../../../../api/devices.js'\nimport SnapshotApi from '../../../../api/snapshots.js'\nimport DropdownMenu from '../../../../components/DropdownMenu.vue'\n\nimport EmptyState from '../../../../components/EmptyState.vue'\nimport AssetCompareDialog from '../../../../components/dialogs/AssetCompareDialog.vue'\nimport AssetDetailDialog from '../../../../components/dialogs/AssetDetailDialog.vue'\nimport SnapshotEditDialog from '../../../../components/dialogs/SnapshotEditDialog.vue'\nimport UserCell from '../../../../components/tables/cells/UserCell.vue'\nimport { downloadData } from '../../../../composables/Download.js'\nimport permissionsMixin from '../../../../mixins/Permissions.js'\nimport Alerts from '../../../../services/alerts.js'\nimport Dialog from '../../../../services/dialog.js'\nimport { applySystemUserDetails } from '../../../../transformers/snapshots.transformer.js'\nimport { isAutoSnapshot } from '../../../../utils/snapshot.js'\nimport DaysSince from '../../../application/Snapshots/components/cells/DaysSince.vue'\nimport SnapshotName from '../../../application/Snapshots/components/cells/SnapshotName.vue'\nimport SnapshotSource from '../../../application/Snapshots/components/cells/SnapshotSource.vue'\nimport SnapshotExportDialog from '../../../application/Snapshots/components/dialogs/SnapshotExportDialog.vue'\n\nexport default {\n name: 'DeviceSnapshots',\n components: {\n AssetDetailDialog,\n AssetCompareDialog,\n DropdownMenu,\n EmptyState,\n FilterIcon,\n PlusSmIcon,\n SnapshotEditDialog,\n SnapshotExportDialog,\n UploadIcon\n },\n mixins: [permissionsMixin],\n inheritAttrs: false,\n props: {\n device: {\n type: Object,\n required: true\n },\n showDeviceSnapshotsOnly: {\n type: Boolean,\n required: false,\n default: false\n },\n reloadHooks: {\n type: Array,\n required: true,\n default: () => []\n }\n },\n emits: ['device-updated', 'show-import-snapshot-dialog', 'show-create-snapshot-dialog'],\n data () {\n return {\n loading: false,\n deviceCounts: {},\n snapshots: [],\n busyMakingSnapshot: false,\n busyImportingSnapshot: false,\n snapshotFilter: null,\n snapshotFilters: {\n All_Snapshots: {\n name: 'All Snapshots',\n selected: true,\n filter: null,\n action: () => {\n this.snapshotFilters.All_Snapshots.selected = true\n this.snapshotFilters.User_Snapshots.selected = false\n this.snapshotFilters.Auto_Snapshots.selected = false\n this.snapshotFilter = this.snapshotFilters.All_Snapshots\n }\n },\n User_Snapshots: {\n name: 'User Snapshots',\n selected: false,\n filter: (s) => !isAutoSnapshot(s),\n action: () => {\n this.snapshotFilters.All_Snapshots.selected = false\n this.snapshotFilters.User_Snapshots.selected = true\n this.snapshotFilters.Auto_Snapshots.selected = false\n this.snapshotFilter = this.snapshotFilters.User_Snapshots\n }\n },\n Auto_Snapshots: {\n name: 'Auto Snapshots',\n selected: false,\n filter: (s) => isAutoSnapshot(s),\n action: () => {\n this.snapshotFilters.All_Snapshots.selected = false\n this.snapshotFilters.User_Snapshots.selected = false\n this.snapshotFilters.Auto_Snapshots.selected = true\n this.snapshotFilter = this.snapshotFilters.Auto_Snapshots\n }\n }\n }\n }\n },\n computed: {\n ...mapState('account', ['teamMembership', 'features']),\n canCreateSnapshot () {\n if (!this.developerMode || this.busy) {\n return false\n }\n return this.isOwnedByAnInstance || this.isOwnedByAnApplication\n },\n columns () {\n const cols = [\n {\n label: 'Snapshot',\n class: ['w-56 sm:w-48'],\n component: {\n is: markRaw(SnapshotName),\n extraProps: {\n // targetSnapshot: this.instance.deviceSettings?.targetSnapshot\n }\n }\n },\n {\n label: 'Source',\n class: ['w-56'],\n key: '_ownerSortKey',\n // sortable: !this.moreThanOnePage,\n component: {\n is: markRaw(SnapshotSource)\n }\n },\n {\n label: 'Node-RED version',\n class: ['w-56'],\n key: 'modules.node-red'\n },\n {\n label: 'Created By',\n class: ['w-48 hidden md:table-cell'],\n component: {\n is: markRaw(UserCell),\n map: {\n avatar: 'user.avatar',\n name: 'user.name',\n username: 'user.username'\n }\n }\n },\n {\n label: 'Date Created',\n class: ['w-48 hidden sm:table-cell'],\n component: { is: markRaw(DaysSince), map: { date: 'createdAt' } }\n }\n ]\n return cols\n },\n snapshotList () {\n return this.snapshots.map(s => {\n return {\n label: s.name,\n description: s.description || '',\n value: s.id\n }\n })\n },\n snapshotsFiltered () {\n if (this.snapshotFilter?.filter) {\n return this.snapshots.filter(this.snapshotFilter.filter)\n }\n return this.snapshots\n },\n snapshotFilterOptions () {\n return Object.values(this.snapshotFilters)\n },\n busy () {\n return this.busyMakingSnapshot || this.busyImportingSnapshot\n },\n developerMode () {\n return this.device?.mode === 'developer'\n },\n isOwnedByAnInstance () {\n return this.device?.ownerType === 'instance'\n },\n isOwnedByAnApplication () {\n return this.device?.ownerType === 'application'\n },\n isUnassigned () {\n return this.device?.ownerType === ''\n }\n },\n watch: {\n team: 'fetchData',\n device: 'fetchData',\n showDeviceSnapshotsOnly: 'fetchData',\n reloadHooks: {\n handler: 'fetchData',\n deep: true\n }\n },\n mounted () {\n this.fetchData()\n },\n methods: {\n rowIsThisDevice: function (snapshot) {\n if (!snapshot || !this.device.id) {\n return false\n }\n return snapshot.device?.id === this.device.id\n },\n fetchData: async function () {\n if (!this.features.deviceEditor || this.isOwnedByAnInstance || this.isUnassigned) {\n return\n }\n if (this.device.id && this.device.application) {\n this.loading = true\n const ssFilter = {\n deviceId: this.showDeviceSnapshotsOnly ? this.device.id : null\n }\n const data = await ApplicationApi.getSnapshots(this.device.application.id, null, null, ssFilter) // TODO Move devices snapshots?\n\n this.snapshots = data.snapshots.map(snapshot => {\n const ownerKey = this.getSortKeyForSnapshotSource(snapshot)\n return {\n ...snapshot,\n ...(ownerKey ? { _ownerSortKey: ownerKey } : { _ownerSortKey: undefined })\n }\n })\n this.snapshots = applySystemUserDetails(data.snapshots)\n this.loading = false\n }\n },\n // snapshot actions - delete\n showDeleteSnapshotDialog (snapshot) {\n Dialog.show({\n header: 'Delete Snapshot',\n text: 'Are you sure you want to delete this snapshot?',\n kind: 'danger',\n confirmLabel: 'Delete'\n }, async () => {\n await SnapshotApi.deleteSnapshot(snapshot.id)\n const index = this.snapshots.indexOf(snapshot)\n this.snapshots.splice(index, 1)\n Alerts.emit('Successfully deleted snapshot.', 'confirmation')\n })\n },\n async downloadSnapshotPackage (snapshot) {\n const ss = await SnapshotApi.getSummary(snapshot.id)\n const owner = ss.device || ss.project\n const ownerType = ss.device ? 'device' : 'instance'\n const packageJSON = {\n name: `${owner.safeName || owner.name}`.replace(/[^a-zA-Z0-9-]/g, '-').toLowerCase(),\n description: `${ownerType} snapshot, ${snapshot.name} - ${snapshot.description}`,\n private: true,\n version: '0.0.0-' + snapshot.id,\n dependencies: ss.modules || {}\n }\n downloadData(packageJSON, 'package.json')\n },\n getSortKeyForSnapshotSource (snapshot) {\n if (snapshot.ownerType === 'device') {\n return 'Device:' + snapshot.device?.name || 'No Name'\n }\n\n if (snapshot.ownerType === 'instance') {\n return 'Instance:' + snapshot.instance?.name || 'No Name'\n }\n\n return 'Unassigned'\n },\n deploySnapshot (snapshotId) {\n const snapshot = this.snapshots.find(s => s.id === snapshotId)\n if (!snapshot) {\n console.warn('Could not find snapshot to deploy', snapshotId, this.snapshots, this.device)\n Alerts.emit('Oops, something went wrong! Please refresh the page and try again.', 'warning', 7500)\n return\n }\n const currentTargetSnapshot = this.device.targetSnapshot?.id\n if (typeof currentTargetSnapshot === 'string' && currentTargetSnapshot === snapshot.id) {\n Alerts.emit('This snapshot is already deployed to this device.', 'info', 7500)\n return\n }\n let body = `Are you sure you want to restore snapshot '${snapshot.name}' to this device?`\n if (snapshot.device?.id !== this.device.id) {\n body = `Snapshot '${snapshot.name}' was not generated by this device. Are you sure you want to deploy it to this device?`\n }\n\n Dialog.show({\n header: `Restore Snapshot to device '${this.device.name}'`,\n kind: 'danger',\n text: body,\n confirmLabel: 'Confirm'\n }, async () => {\n try {\n await DeviceApi.setSnapshotAsTarget(this.device.id, snapshot.id)\n Alerts.emit('Successfully applied the snapshot.', 'confirmation')\n } catch (err) {\n Alerts.emit('Failed to apply snapshot: ' + err.toString(), 'warning', 7500)\n }\n })\n },\n showViewSnapshotDialog (snapshot) {\n SnapshotApi.getFullSnapshot(snapshot.id).then((data) => {\n this.$refs.snapshotViewerDialog.show(data)\n }).catch(err => {\n console.error(err)\n Alerts.emit('Failed to get snapshot.', 'warning')\n })\n },\n showCompareSnapshotDialog (snapshot) {\n SnapshotApi.getFullSnapshot(snapshot.id)\n .then((data) => this.$refs.snapshotCompareDialog.show(data, this.snapshotList))\n .catch(err => {\n console.error(err)\n Alerts.emit('Failed to get snapshot.', 'warning')\n })\n },\n showDownloadSnapshotDialog (snapshot) {\n this.$refs.snapshotExportDialog.show(snapshot)\n },\n showDeploySnapshotDialog (snapshot) {\n this.deploySnapshot(snapshot.id)\n },\n showEditSnapshotDialog (snapshot) {\n this.$refs.snapshotEditDialog.show(snapshot)\n },\n onSnapshotEdit (snapshot) {\n const index = this.snapshots.findIndex(s => s.id === snapshot.id)\n if (index >= 0) {\n this.snapshots[index].name = snapshot.name\n this.snapshots[index].description = snapshot.description\n }\n },\n // enable/disable snapshot actions\n canDeploy (_row) {\n return !this.developerMode && this.hasPermission('device:snapshot:set-target')\n },\n canDownload (_row) {\n return this.hasPermission('snapshot:export')\n },\n canDelete (row) {\n if (this.rowIsThisDevice(row)) {\n return this.hasPermission('device:snapshot:delete')\n }\n return false // only permit deletion of snapshots created by this device\n }\n }\n}\n</script>\n\n<style>\n\ntbody .ff-data-table--row > .ff-data-table--cell > .deploy-this-snapshot-button {\n display: none;\n}\n\ntbody tr.ff-data-table--row:hover .ff-data-table--cell .deploy-this-snapshot-button {\n display: flex;\n}\n\n</style>\n","<template>\n <main v-if=\"device\" class=\"ff-with-status-header\">\n <Teleport v-if=\"mounted\" to=\"#platform-banner\">\n <SubscriptionExpiredBanner :team=\"team\" />\n <TeamTrialBanner v-if=\"team.billing?.trial\" :team=\"team\" />\n </Teleport>\n <SectionNavigationHeader :tabs=\"navigation\">\n <template #breadcrumbs>\n <ff-nav-breadcrumb :to=\"{name: 'TeamDevices', params: {team_slug: team.slug}}\">Remote Instances</ff-nav-breadcrumb>\n <ff-nav-breadcrumb>{{ device.name }}</ff-nav-breadcrumb>\n </template>\n <template #status>\n <div class=\"flex flex-wrap gap-2\">\n <DeviceLastSeenBadge :last-seen-at=\"device.lastSeenAt\" :last-seen-ms=\"device.lastSeenMs\" :last-seen-since=\"device.lastSeenSince\" />\n <StatusBadge :status=\"device.status\" :instanceId=\"device.id\" instanceType=\"device\" />\n <DeviceModeBadge v-if=\"isDevModeAvailable \" :mode=\"device.mode\" />\n </div>\n </template>\n <template #context>\n <div v-if=\"device?.ownerType === 'application' && device.application\" data-el=\"device-assigned-application\">\n Application:\n <ff-team-link :to=\"{name: 'Application', params: {id: device.application?.id}}\" class=\"text-blue-600 cursor-pointer hover:text-blue-700 hover:underline\">{{ device.application?.name }}</ff-team-link>\n </div>\n <div v-else-if=\"device?.ownerType === 'instance' && device.instance\" data-el=\"device-assigned-instance\">\n Instance:\n <ff-team-link :to=\"{name: 'Instance', params: {id: device.instance.id}}\" class=\"text-blue-600 cursor-pointer hover:text-blue-700 hover:underline\">{{ device.instance.name }}</ff-team-link>\n </div>\n <div v-else data-el=\"device-assigned-none\">\n <span class=\"italic\">No Application or Instance Assigned</span> - <a class=\"ff-link\" data-action=\"assign-device\" @click=\"openAssignmentDialog\">Assign</a>\n </div>\n </template>\n <template #tools>\n <!--\n div style 34px is a workaround to prevent the Device Editor button growing taller than adjacent\n button (size difference is caused by odd padding in the toggle button, which though not visible\n is still there and affects the button height in this div group)\n -->\n <div class=\"space-x-2 flex align-center\" style=\"height: 34px;\">\n <template v-if=\"isDevModeAvailable\">\n <DeveloperModeToggle data-el=\"device-devmode-toggle\" :device=\"device\" :disabled=\"disableModeToggle\" :disabledReason=\"disableModeToggleReason\" @mode-change=\"setDeviceMode\" />\n <button v-if=\"!isVisitingAdmin\" v-ff-tooltip:left=\"!editorAvailable ? 'You can edit flows directly when Developer Mode is enabled, and your Edge Instance is connected.' : 'Open Edge Instance Editor'\" data-action=\"open-editor\" class=\"ff-btn transition-fade--color ff-btn--secondary ff-btn-icon h-9\" :disabled=\"!editorAvailable\" @click=\"openTunnel(true)\">\n Open Editor\n <span class=\"ff-btn--icon ff-btn--icon-right\">\n <ExternalLinkIcon />\n </span>\n </button>\n </template>\n <FinishSetupButton v-if=\"neverConnected\" :device=\"device\" />\n <DropdownMenu v-if=\"hasPermission('device:change-status') && actionsDropdownOptions.length\" data-el=\"device-actions-dropdown\" buttonClass=\"ff-btn ff-btn--primary\" :options=\"actionsDropdownOptions\">Actions</DropdownMenu>\n </div>\n </template>\n </SectionNavigationHeader>\n <div class=\"mt-4 sm:mt-8\">\n <Teleport v-if=\"mounted && isVisitingAdmin\" to=\"#platform-banner\">\n <div class=\"ff-banner\" data-el=\"banner-device-as-admin\">You are viewing this device as an Administrator</div>\n </Teleport>\n <div class=\"px-3 pb-3 md:px-6 md:pb-6\">\n <router-view :instance=\"device.instance\" :closingTunnel=\"closingTunne