<NME> path-inventory.cocoascript
<BEF> @import "Framer-Inventory-for-Sketch/Framer Inventory.sketchplugin/Contents/Sketch/inventory/libs/testing/logger.js"

function PathInventory () {}


PathInventory.returnProjectNameFromPath = function(projectPath) {
  var currentOpenedProjectCleanPath = [[projectPath componentsSeparatedByString:".framer"] firstObject]
  var currentOpenedProjectName = [[currentOpenedProjectCleanPath componentsSeparatedByString:"/"] lastObject]
  return currentOpenedProjectName
}


PathInventory.returnOpenedFramerProjects = function() {
    var openedFramerProjects = NSMutableDictionary.new()
    var relativePlistPath = NSHomeDirectory() + "/Library/Saved Application State/com.motif.framer.savedState/windows.plist"

    var plistData = [NSData dataWithContentsOfFile:relativePlistPath];
    if (!plistData) { return openedFramerProjects }

    var plistRoot = nil
    var plistSerializationError = nil;

    id plist = [NSPropertyListSerialization propertyListWithData:plistData options:NSPropertyListMutableContainersAndLeaves format:nil error:plistSerializationError];


    if (!plistSerializationError) { plistRoot = plist; }
    else { return openedFramerProjects }

    for (var i = 0; i < plistRoot.count(); i++) {
        var currenPlistBlock = plistRoot[i]

        if ([currenPlistBlock objectForKey:"NSRepresentedURL"]) {
            var currentFramerProjectPath = currenPlistBlock["NSRepresentedURL"]
            var cleanFramerProjectPath = currentFramerProjectPath.split("file://")[1]
            openedFramerProjects[currentFramerProjectPath] = cleanFramerProjectPath
        }
    }

    return openedFramerProjects
}


PathInventory.createPathModal = function() {
  var openedFramerProjects = PathInventory.returnOpenedFramerProjects()

  if (openedFramerProjects.count() == 0) { selectProjectWithNoOpened() }
  else if (openedFramerProjects.count() == 1) {
      userDefaults.exportFramerFolder = [[openedFramerProjects allValues] firstObject]
      saveDefaults(Status.userDomain(), userDefaults)

      [currentDocument showMessage:ViewInventory.smileDone() + "Selected project: " + PathInventory.returnProjectNameFromPath([[openedFramerProjects allValues] firstObject])]
  }
  else { selectOpenedProject(openedFramerProjects) }

  ToolbarInventory.setContentView()
}



PathInventory.readProjectCode = function(prototypePath) {
  if (prototypePath == "") { return "" }
  var codeFilePath = prototypePath + "/app.coffee"

  if ([[NSFileManager defaultManager] fileExistsAtPath:codeFilePath]) {
    var currentString = [NSString stringWithContentsOfFile:codeFilePath encoding:NSUTF8StringEncoding error:nil]
    return currentString
  }
  return ""
}

PathInventory.replaceProjectCode = function(prototypePath, newCode) {
  if (prototypePath == "") { return }

  var codeFilePath = prototypePath + "/app.coffee"
  writeTextToFile(newCode, codeFilePath)
  return
}


PathInventory.updateFoldRanges = function(prototypePath, shiftBy, callback) {
    const extractFirstNumber = /\d+/
    const configFile = prototypePath + '/framer/config.json'
    // const codeFilePath = prototypePath + "/app.coffee"

    var data = [NSData dataWithContentsOfFile:configFile];
    var configuration = [NSJSONSerialization JSONObjectWithData:data options:nil error:nil];
    log([configuration className])

    var ranges = [configuration objectForKey:"foldedCodeRanges"]
    log(ranges)


    const newRange = []

    for (var i = 0; i < ranges.count(); i++) {
        var rangeString = [ranges objectAtIndex:i]

        // var firstNumber = parseInt( extractFirstNumber.exec(range)[0] )
        var matches = rangeString.match(/\d+/g)
        var firstNumber = Number(matches[0])
        var firstNumberShift = firstNumber + shiftBy
        newRange.push(rangeString.replace(matches[0], firstNumberShift) )
    }


    /*
    Configuration for code folds is an array of ranges (strings):
    '{ startingCharacterNumber, numberOfCharactersInFold }'
    e.g.:
    "foldedCodeRanges": [
        "{68, 1039}",
        "{1108, 2066}"
    ]
    With every new require() command un/written to app.coffee, we need to shift
    code fold ranges to maintain the folds in Framer Studio.
    e.g. when writing require string that is 68 characters long,
         '{1, 1039}' becomes '{69, 1039}'
    */

    // configuration.foldedCodeRanges.forEach(range => {
       // const firstNumber = parseInt( extractFirstNumber.exec(range)[0] )
       // const firstNumberShift = firstNumber + shiftBy

        // newRange.push( range.replace(extractFirstNumber, firstNumberShift) )
    //})

    //configuration.foldedCodeRanges = newRange

    [data writeToFile:configFile atomically:true];

    return newRange

}

<MSG> Merged test?

<DFF> @@ -1,4 +1,5 @@
 @import "Framer-Inventory-for-Sketch/Framer Inventory.sketchplugin/Contents/Sketch/inventory/libs/testing/logger.js"
+@import "actions/workers/modals/action-select-opened-project.cocoascript"
 
 function PathInventory () {}
 
@@ -9,6 +10,12 @@ PathInventory.returnProjectNameFromPath = function(projectPath) {
   return currentOpenedProjectName
 }
 
+PathInventory.returnProjectPathFromURLPath = function(projectPath) {
+    var cleanPath = projectPath
+    if ([[projectPath componentsSeparatedByString:"file://"] count] > 1) { cleanPath = [[projectPath componentsSeparatedByString:"file://"] lastObject] }
+    return [cleanPath stringByRemovingPercentEncoding]
+}
+
 
 PathInventory.returnOpenedFramerProjects = function() {
     var openedFramerProjects = NSMutableDictionary.new()
@@ -31,8 +38,8 @@ PathInventory.returnOpenedFramerProjects = function() {
 
         if ([currenPlistBlock objectForKey:"NSRepresentedURL"]) {
             var currentFramerProjectPath = currenPlistBlock["NSRepresentedURL"]
-            var cleanFramerProjectPath = currentFramerProjectPath.split("file://")[1]
-            openedFramerProjects[currentFramerProjectPath] = cleanFramerProjectPath
+            var cleanFramerProjectPath = PathInventory.returnProjectPathFromURLPath(currentFramerProjectPath)
+            if (cleanFramerProjectPath != "") { openedFramerProjects[currentFramerProjectPath] = cleanFramerProjectPath }
         }
     }
 

Merged test?