@@ -711,6 +711,19 @@ <h2>Deploy a new instance</h2>
711711 </ div >
712712 </ div >
713713
714+ < div class ="form-group ">
715+ < label for ="swapSize "> Swap (optional)</ label >
716+ < div style ="display: flex; align-items: center; gap: 8px; ">
717+ < input id ="swapSize " v-model.number ="vmForm.swapValue " type ="number " min ="0 "
718+ step ="0.1 " placeholder ="Swap size " style ="flex: 1; ">
719+ < select v-model ="vmForm.swapUnit " style ="width: 80px; ">
720+ < option value ="MB "> MB</ option >
721+ < option value ="GB "> GB</ option >
722+ </ select >
723+ </ div >
724+ < small style ="color: #666; "> Leave as 0 to disable swap.</ small >
725+ </ div >
726+
714727 < div class ="form-group ">
715728 < label for ="diskSize "> Storage (GB)</ label >
716729 < input id ="diskSize " v-model.number ="vmForm.disk_size " type ="number "
@@ -1009,6 +1022,10 @@ <h4 style="margin: 0 0 12px 0;">VM Configuration</h4>
10091022 < span class ="detail-value "> {{ formatMemory(vm.configuration?.memory)
10101023 }}</ span >
10111024 </ div >
1025+ < div class ="detail-item " v-if ="vm.configuration?.swap_size ">
1026+ < span class ="detail-label "> Swap:</ span >
1027+ < span class ="detail-value "> {{ formatMemory(bytesToMB(vm.configuration.swap_size)) }}</ span >
1028+ </ div >
10121029 < div class ="detail-item ">
10131030 < span class ="detail-label "> Disk Size:</ span >
10141031 < span class ="detail-value "> {{ vm.configuration?.disk_size }} GB</ span >
@@ -1136,6 +1153,21 @@ <h3>Update VM Config</h3>
11361153 </ div >
11371154 </ div >
11381155
1156+ < div class ="form-group ">
1157+ < label for ="upgradeSwap "> Swap (optional)</ label >
1158+ < div style ="display: flex; align-items: center; gap: 8px; ">
1159+ < input id ="upgradeSwap " v-model.number ="upgradeDialog.swapValue " type ="number " min ="0 "
1160+ step ="0.1 " placeholder ="Swap size " style ="flex: 1; "
1161+ :disabled ="!upgradeDialog.updateCompose ">
1162+ < select v-model ="upgradeDialog.swapUnit " style ="width: 80px; "
1163+ :disabled ="!upgradeDialog.updateCompose ">
1164+ < option value ="MB "> MB</ option >
1165+ < option value ="GB "> GB</ option >
1166+ </ select >
1167+ </ div >
1168+ < small style ="color: #666; "> Enable "Update compose" to change swap size.</ small >
1169+ </ div >
1170+
11391171 < div class ="form-group ">
11401172 < label for ="diskSize "> Disk Size (GB)</ label >
11411173 < input id ="diskSize " v-model.number ="upgradeDialog.disk_size " type ="number "
@@ -1527,6 +1559,9 @@ <h3>Derive VM</h3>
15271559 memory : 2048 , // This will be computed from memoryValue and memoryUnit
15281560 memoryValue : 2 ,
15291561 memoryUnit : 'GB' ,
1562+ swap_size : 0 ,
1563+ swapValue : 0 ,
1564+ swapUnit : 'GB' ,
15301565 disk_size : 20 ,
15311566 selectedGpus : [ ] ,
15321567 attachAllGpus : false ,
@@ -1566,6 +1601,9 @@ <h3>Derive VM</h3>
15661601 memory : 0 , // This will be computed from memoryValue and memoryUnit
15671602 memoryValue : 0 ,
15681603 memoryUnit : 'MB' ,
1604+ swap_size : 0 ,
1605+ swapValue : 0 ,
1606+ swapUnit : 'GB' ,
15691607 disk_size : 0 ,
15701608 image : '' ,
15711609 ports : [ ] ,
@@ -1724,6 +1762,11 @@ <h3>Derive VM</h3>
17241762 app_compose . pre_launch_script = vmForm . value . preLaunchScript ;
17251763 }
17261764
1765+ const swapBytes = Math . max ( 0 , Math . round ( vmForm . value . swap_size || 0 ) ) ;
1766+ if ( swapBytes > 0 ) {
1767+ app_compose . swap_size = swapBytes ;
1768+ }
1769+
17271770 // If APP_LAUNCH_TOKEN is set, add it's sha256 hash to the app compose file
17281771 const launchToken = vmForm . value . encryptedEnvs . find ( env => env . key === 'APP_LAUNCH_TOKEN' ) ;
17291772 if ( launchToken ) {
@@ -1831,15 +1874,39 @@ <h3>Derive VM</h3>
18311874 showCreateDialog . value = true ;
18321875 vmForm . value . encryptedEnvs = [ ] ;
18331876 vmForm . value . app_id = null ;
1877+ vmForm . value . swapValue = 0 ;
1878+ vmForm . value . swapUnit = 'GB' ;
1879+ vmForm . value . swap_size = 0 ;
18341880 loadGpus ( ) ;
18351881 } ;
18361882
18371883 // Memory conversion functions
18381884 const convertMemoryToMB = ( value , unit ) => {
1885+ const numericValue = Number ( value ) ;
1886+ if ( ! Number . isFinite ( numericValue ) || numericValue < 0 ) {
1887+ return 0 ;
1888+ }
18391889 if ( unit === 'GB' ) {
1840- return value * 1024 ;
1890+ return numericValue * 1024 ;
18411891 }
1842- return value ;
1892+ return numericValue ;
1893+ } ;
1894+
1895+ const BYTES_PER_MB = 1024 * 1024 ;
1896+
1897+ const convertSwapToBytes = ( value , unit ) => {
1898+ const mb = convertMemoryToMB ( value , unit ) ;
1899+ if ( ! Number . isFinite ( mb ) || mb <= 0 ) {
1900+ return 0 ;
1901+ }
1902+ return Math . max ( 0 , Math . round ( mb * BYTES_PER_MB ) ) ;
1903+ } ;
1904+
1905+ const bytesToMB = ( bytes ) => {
1906+ if ( ! bytes ) {
1907+ return 0 ;
1908+ }
1909+ return bytes / BYTES_PER_MB ;
18431910 } ;
18441911
18451912 const formatMemory = ( memoryMB ) => {
@@ -1862,6 +1929,14 @@ <h3>Derive VM</h3>
18621929 upgradeDialog . value . memory = convertMemoryToMB ( upgradeDialog . value . memoryValue , upgradeDialog . value . memoryUnit ) ;
18631930 } ) ;
18641931
1932+ watch ( [ ( ) => vmForm . value . swapValue , ( ) => vmForm . value . swapUnit ] , ( ) => {
1933+ vmForm . value . swap_size = convertSwapToBytes ( vmForm . value . swapValue , vmForm . value . swapUnit ) ;
1934+ } ) ;
1935+
1936+ watch ( [ ( ) => upgradeDialog . value . swapValue , ( ) => upgradeDialog . value . swapUnit ] , ( ) => {
1937+ upgradeDialog . value . swap_size = convertSwapToBytes ( upgradeDialog . value . swapValue , upgradeDialog . value . swapUnit ) ;
1938+ } ) ;
1939+
18651940 const createVm = async ( ) => {
18661941 try {
18671942 // Convert memory based on selected unit
@@ -1879,6 +1954,12 @@ <h3>Derive VM</h3>
18791954 user_config : vmForm . value . user_config ,
18801955 gpus : configGpu ( vmForm . value ) ,
18811956 } ;
1957+ const swapBytes = Math . max ( 0 , Math . round ( form . swap_size || 0 ) ) ;
1958+ if ( swapBytes > 0 ) {
1959+ form . swap_size = swapBytes ;
1960+ } else {
1961+ delete form . swap_size ;
1962+ }
18821963 const _response = await rpcCall ( 'CreateVm' , form ) ;
18831964 loadVMList ( ) ;
18841965 showCreateDialog . value = false ;
@@ -2054,6 +2135,10 @@ <h3>Derive VM</h3>
20542135 const selectedGpuSlots = currentGpuConfig . gpus ?. map ( gpu => gpu . slot ) || [ ] ;
20552136 const appCompose = JSON . parse ( updatedVM . configuration ?. compose_file || "{}" ) ;
20562137
2138+ const swapBytes = Number ( appCompose . swap_size || 0 ) ;
2139+ const swapMb = bytesToMB ( swapBytes ) ;
2140+ const swapDisplay = autoMemoryDisplay ( swapMb ) ;
2141+
20572142 upgradeDialog . value = {
20582143 show : true ,
20592144 vm : updatedVM ,
@@ -2066,6 +2151,9 @@ <h3>Derive VM</h3>
20662151 vcpu : updatedVM . configuration ?. vcpu || 1 ,
20672152 memory : updatedVM . configuration ?. memory || 1024 ,
20682153 ...autoMemoryDisplay ( updatedVM . configuration ?. memory ) ,
2154+ swap_size : swapBytes ,
2155+ swapValue : Number ( swapDisplay . memoryValue ) ,
2156+ swapUnit : swapDisplay . memoryUnit ,
20692157 disk_size : updatedVM . configuration ?. disk_size || 10 ,
20702158 image : updatedVM . configuration ?. image || '' ,
20712159 ports : updatedVM . configuration ?. ports ?. map ( port => ( { ...port } ) ) || [ ] ,
@@ -2100,6 +2188,13 @@ <h3>Derive VM</h3>
21002188 }
21012189 }
21022190 app_compose . pre_launch_script = upgradeDialog . value . preLaunchScript ?. trim ( ) ;
2191+
2192+ const swapBytes = Math . max ( 0 , Math . round ( upgradeDialog . value . swap_size || 0 ) ) ;
2193+ if ( swapBytes > 0 ) {
2194+ app_compose . swap_size = swapBytes ;
2195+ } else {
2196+ delete app_compose . swap_size ;
2197+ }
21032198 return JSON . stringify ( app_compose ) ;
21042199 }
21052200
@@ -2493,6 +2588,7 @@ <h3>Derive VM</h3>
24932588 composeHashPreview,
24942589 upgradeComposeHashPreview,
24952590 formatMemory,
2591+ bytesToMB,
24962592 // Pagination and search variables
24972593 searchQuery,
24982594 currentPage,
@@ -2514,4 +2610,4 @@ <h3>Derive VM</h3>
25142610 </ script >
25152611</ body >
25162612
2517- </ html >
2613+ </ html >
0 commit comments