@@ -661,6 +661,19 @@ <h2>Deploy a new instance</h2>
661661 </ div >
662662 </ div >
663663
664+ < div class ="form-group ">
665+ < label for ="swapSize "> Swap (optional)</ label >
666+ < div style ="display: flex; align-items: center; gap: 8px; ">
667+ < input id ="swapSize " v-model.number ="vmForm.swapValue " type ="number " min ="0 "
668+ step ="0.1 " placeholder ="Swap size " style ="flex: 1; ">
669+ < select v-model ="vmForm.swapUnit " style ="width: 80px; ">
670+ < option value ="MB "> MB</ option >
671+ < option value ="GB "> GB</ option >
672+ </ select >
673+ </ div >
674+ < small style ="color: #666; "> Leave as 0 to disable swap.</ small >
675+ </ div >
676+
664677 < div class ="form-group ">
665678 < label for ="diskSize "> Disk Size (GB)</ label >
666679 < input id ="diskSize " v-model.number ="vmForm.disk_size " type ="number "
@@ -941,6 +954,10 @@ <h4 style="margin: 0 0 12px 0;">VM Configuration</h4>
941954 < span class ="detail-value "> {{ formatMemory(vm.configuration?.memory)
942955 }}</ span >
943956 </ div >
957+ < div class ="detail-item " v-if ="vm.configuration?.swap_size ">
958+ < span class ="detail-label "> Swap:</ span >
959+ < span class ="detail-value "> {{ formatMemory(bytesToMB(vm.configuration.swap_size)) }}</ span >
960+ </ div >
944961 < div class ="detail-item ">
945962 < span class ="detail-label "> Disk Size:</ span >
946963 < span class ="detail-value "> {{ vm.configuration?.disk_size }} GB</ span >
@@ -1063,6 +1080,21 @@ <h3>Update VM Config</h3>
10631080 </ div >
10641081 </ div >
10651082
1083+ < div class ="form-group ">
1084+ < label for ="upgradeSwap "> Swap (optional)</ label >
1085+ < div style ="display: flex; align-items: center; gap: 8px; ">
1086+ < input id ="upgradeSwap " v-model.number ="upgradeDialog.swapValue " type ="number " min ="0 "
1087+ step ="0.1 " placeholder ="Swap size " style ="flex: 1; "
1088+ :disabled ="!upgradeDialog.updateCompose ">
1089+ < select v-model ="upgradeDialog.swapUnit " style ="width: 80px; "
1090+ :disabled ="!upgradeDialog.updateCompose ">
1091+ < option value ="MB "> MB</ option >
1092+ < option value ="GB "> GB</ option >
1093+ </ select >
1094+ </ div >
1095+ < small style ="color: #666; "> Enable "Update compose" to change swap size.</ small >
1096+ </ div >
1097+
10661098 < div class ="form-group ">
10671099 < label for ="diskSize "> Disk Size (GB)</ label >
10681100 < input id ="diskSize " v-model.number ="upgradeDialog.disk_size " type ="number "
@@ -1454,6 +1486,9 @@ <h3>Derive VM</h3>
14541486 memory : 2048 , // This will be computed from memoryValue and memoryUnit
14551487 memoryValue : 2 ,
14561488 memoryUnit : 'GB' ,
1489+ swap_size : 0 ,
1490+ swapValue : 0 ,
1491+ swapUnit : 'GB' ,
14571492 disk_size : 20 ,
14581493 selectedGpus : [ ] ,
14591494 attachAllGpus : false ,
@@ -1492,6 +1527,9 @@ <h3>Derive VM</h3>
14921527 memory : 0 , // This will be computed from memoryValue and memoryUnit
14931528 memoryValue : 0 ,
14941529 memoryUnit : 'MB' ,
1530+ swap_size : 0 ,
1531+ swapValue : 0 ,
1532+ swapUnit : 'GB' ,
14951533 disk_size : 0 ,
14961534 image : '' ,
14971535 ports : [ ] ,
@@ -1646,6 +1684,11 @@ <h3>Derive VM</h3>
16461684 app_compose . pre_launch_script = vmForm . value . preLaunchScript ;
16471685 }
16481686
1687+ const swapBytes = Math . max ( 0 , Math . round ( vmForm . value . swap_size || 0 ) ) ;
1688+ if ( swapBytes > 0 ) {
1689+ app_compose . swap_size = swapBytes ;
1690+ }
1691+
16491692 // If APP_LAUNCH_TOKEN is set, add it's sha256 hash to the app compose file
16501693 const launchToken = vmForm . value . encryptedEnvs . find ( env => env . key === 'APP_LAUNCH_TOKEN' ) ;
16511694 if ( launchToken ) {
@@ -1753,15 +1796,39 @@ <h3>Derive VM</h3>
17531796 showCreateDialog . value = true ;
17541797 vmForm . value . encryptedEnvs = [ ] ;
17551798 vmForm . value . app_id = null ;
1799+ vmForm . value . swapValue = 0 ;
1800+ vmForm . value . swapUnit = 'GB' ;
1801+ vmForm . value . swap_size = 0 ;
17561802 loadGpus ( ) ;
17571803 } ;
17581804
17591805 // Memory conversion functions
17601806 const convertMemoryToMB = ( value , unit ) => {
1807+ const numericValue = Number ( value ) ;
1808+ if ( ! Number . isFinite ( numericValue ) || numericValue < 0 ) {
1809+ return 0 ;
1810+ }
17611811 if ( unit === 'GB' ) {
1762- return value * 1024 ;
1812+ return numericValue * 1024 ;
17631813 }
1764- return value ;
1814+ return numericValue ;
1815+ } ;
1816+
1817+ const BYTES_PER_MB = 1024 * 1024 ;
1818+
1819+ const convertSwapToBytes = ( value , unit ) => {
1820+ const mb = convertMemoryToMB ( value , unit ) ;
1821+ if ( ! Number . isFinite ( mb ) || mb <= 0 ) {
1822+ return 0 ;
1823+ }
1824+ return Math . max ( 0 , Math . round ( mb * BYTES_PER_MB ) ) ;
1825+ } ;
1826+
1827+ const bytesToMB = ( bytes ) => {
1828+ if ( ! bytes ) {
1829+ return 0 ;
1830+ }
1831+ return bytes / BYTES_PER_MB ;
17651832 } ;
17661833
17671834 const formatMemory = ( memoryMB ) => {
@@ -1784,6 +1851,14 @@ <h3>Derive VM</h3>
17841851 upgradeDialog . value . memory = convertMemoryToMB ( upgradeDialog . value . memoryValue , upgradeDialog . value . memoryUnit ) ;
17851852 } ) ;
17861853
1854+ watch ( [ ( ) => vmForm . value . swapValue , ( ) => vmForm . value . swapUnit ] , ( ) => {
1855+ vmForm . value . swap_size = convertSwapToBytes ( vmForm . value . swapValue , vmForm . value . swapUnit ) ;
1856+ } ) ;
1857+
1858+ watch ( [ ( ) => upgradeDialog . value . swapValue , ( ) => upgradeDialog . value . swapUnit ] , ( ) => {
1859+ upgradeDialog . value . swap_size = convertSwapToBytes ( upgradeDialog . value . swapValue , upgradeDialog . value . swapUnit ) ;
1860+ } ) ;
1861+
17871862 const createVm = async ( ) => {
17881863 try {
17891864 // Convert memory based on selected unit
@@ -1801,6 +1876,12 @@ <h3>Derive VM</h3>
18011876 user_config : vmForm . value . user_config ,
18021877 gpus : configGpu ( vmForm . value ) ,
18031878 } ;
1879+ const swapBytes = Math . max ( 0 , Math . round ( form . swap_size || 0 ) ) ;
1880+ if ( swapBytes > 0 ) {
1881+ form . swap_size = swapBytes ;
1882+ } else {
1883+ delete form . swap_size ;
1884+ }
18041885 const _response = await rpcCall ( 'CreateVm' , form ) ;
18051886 loadVMList ( ) ;
18061887 showCreateDialog . value = false ;
@@ -1976,6 +2057,10 @@ <h3>Derive VM</h3>
19762057 const selectedGpuSlots = currentGpuConfig . gpus ?. map ( gpu => gpu . slot ) || [ ] ;
19772058 const appCompose = JSON . parse ( updatedVM . configuration ?. compose_file || "{}" ) ;
19782059
2060+ const swapBytes = Number ( appCompose . swap_size || 0 ) ;
2061+ const swapMb = bytesToMB ( swapBytes ) ;
2062+ const swapDisplay = autoMemoryDisplay ( swapMb ) ;
2063+
19792064 upgradeDialog . value = {
19802065 show : true ,
19812066 vm : updatedVM ,
@@ -1988,6 +2073,9 @@ <h3>Derive VM</h3>
19882073 vcpu : updatedVM . configuration ?. vcpu || 1 ,
19892074 memory : updatedVM . configuration ?. memory || 1024 ,
19902075 ...autoMemoryDisplay ( updatedVM . configuration ?. memory ) ,
2076+ swap_size : swapBytes ,
2077+ swapValue : Number ( swapDisplay . memoryValue ) ,
2078+ swapUnit : swapDisplay . memoryUnit ,
19912079 disk_size : updatedVM . configuration ?. disk_size || 10 ,
19922080 image : updatedVM . configuration ?. image || '' ,
19932081 ports : updatedVM . configuration ?. ports ?. map ( port => ( { ...port } ) ) || [ ] ,
@@ -2022,6 +2110,13 @@ <h3>Derive VM</h3>
20222110 }
20232111 }
20242112 app_compose . pre_launch_script = upgradeDialog . value . preLaunchScript ?. trim ( ) ;
2113+
2114+ const swapBytes = Math . max ( 0 , Math . round ( upgradeDialog . value . swap_size || 0 ) ) ;
2115+ if ( swapBytes > 0 ) {
2116+ app_compose . swap_size = swapBytes ;
2117+ } else {
2118+ delete app_compose . swap_size ;
2119+ }
20252120 return JSON . stringify ( app_compose ) ;
20262121 }
20272122
@@ -2414,6 +2509,7 @@ <h3>Derive VM</h3>
24142509 composeHashPreview,
24152510 upgradeComposeHashPreview,
24162511 formatMemory,
2512+ bytesToMB,
24172513 // Pagination and search variables
24182514 searchQuery,
24192515 currentPage,
@@ -2435,4 +2531,4 @@ <h3>Derive VM</h3>
24352531 </ script >
24362532</ body >
24372533
2438- </ html >
2534+ </ html >
0 commit comments