Jump to content

Snippet Transformer — Import and Export Alfred Snippet Collections to and from CSV


vitor

Recommended Posts

Posted

Usage


Convert CSV files to Alfred Snippet Collections via the Universal Action. The Snippet Collection will be saved to the Desktop and opened for import into Alfred if the option in set in the Workflow’s Configuration.


uafromcsv.png


Converting from a Snippet Collection to CSV is also available.


uafromcollection.png


Alternatively, search for CSV or Snippet Collection files via the csvsnip and snipcsv keywords.


keywordfromcsv.png


keywordfromcollection.png


Import macOS Text Replacements as a new Alfred Snippet Collection via the trimport keyword.


trimport.png


⤓ Install on the Alfred Gallery | Source

  • 1 month later...
Posted

When reporting issues, please include your exact installed versions of:

  • The Workflow.
  • Alfred.
  • macOS.

In addition to:

  • The debugger output. Perform the failing action, click “Copy” on the top right and paste it here.
  • Details on what you did, what happened, and what you expected to happen. A short video of the steps with the debugger open may help to find the problem faster.

Thank you. Accurate and thorough information is crucial for a proper diagnosis which allows me to help you better.

  • 5 months later...
Posted

I have a collection of snippets that I wanted to modify because I had added a suffix to each snippet that I wanted to remove. To do this, I exported the collection (with the same name +#2) and converted it to CSV using its 'snippets transformer' workflow with correct results. I opened the CSV file and modified the snippets (removing the incorrect suffix I had added to each one) and saved the file. Later, I performed the reverse process and converted the .CSV file back to a .alfredsnippets file using its workflow, which was then imported into Alfred 5. After installing the new collection (with #2), I added a (*) general suffix to it. However, when I try to expand text with a snippet from the collection, I get no results. I tried to delete the created collection, but it's impossible, and an error message appears in the top left corner saying: 'The file “XXX(2)” could not be moved to the trash.' What did I do wrong? Thanks

Posted
19 hours ago, vitor said:

That’s not going to be related to the workflow. If you cannot delete a snippet collection, and with that message, it’s likely that your syncing service is misbehaving.

 

See https://www.alfredforum.com/topic/19602-cannot-delete-snippet-collection/ and https://www.alfredforum.com/topic/18173-unable-to-delete-personal-snippets/

 

 

Indeed. It was all Dropbox's fault (in its new location). Thank you very much for everything. Great workflow.

  • 5 months later...
Posted

Hello,
 

After installing and running the Snippet Transformer, I encountered the following error when attempting to convert to CSV or a snippet format.

Csv to snippet
ERROR: Snippet Transformer[Run Script] /Users/xyz/Library/Caches/com.runningwithcrayons.Alfred/Workflow Scripts/67DE3916-CF39-47BA-B92A-EF43E2585640: execution error: Error: Error: exception raised by object: -[__NSCFString isFileReferenceURL]: unrecognized selector sent to instance 0x600002444210 (-2700)


Snippet to CSV

Error with the following script: [22:28:03.838] ERROR: Snippet Transformer[Run Script] /Users/thomasperso/Library/Caches/com.runningwithcrayons.Alfred/Workflow Scripts/D6DFAF6B-099A-46F4-860B-91A5C3CCDE5E: execution error: Error: Error: exception raised by object: -[__NSCFString isFileReferenceURL]: unrecognized selector sent to instance 0x600000f48150 (-2700)

 

I have fixed the two JavaScript issues using ChatGPT.
I confirm that this version is working well!

CSV to snip.
 

// Helpers
function envVar(varName) {
  return $.NSProcessInfo
    .processInfo
    .environment
    .objectForKey(varName).js;
}

function runCommand(...arguments) {
  const task = $.NSTask.alloc.init;

  task.executableURL = $.NSURL.fileURLWithPath("/usr/bin/env");
  task.arguments = arguments;
  task.launchAndReturnError(false);

  task.waitUntilExit;
}

function writeFile(path, text) {
  $(text).writeToFileAtomicallyEncodingError(path, true, $.NSUTF8StringEncoding, undefined);
}

// Main
function run(argv) {
  const origData = JSON.parse(argv[0]);
  const fileManager = $.NSFileManager.defaultManager;
  const inputFile = envVar("input_file");

  // Create an NSURL object from input_file
  const inputFileURL = $.NSURL.fileURLWithPath($(inputFile).stringByExpandingTildeInPath);
  const collectionName = inputFileURL.URLByDeletingPathExtension.lastPathComponent.js;
  const outDir = $("~/Desktop").stringByExpandingTildeInPath.js;
  const outFile = `${outDir}/${collectionName}.alfredsnippets`;
  const tmpDir = fileManager.URLForDirectoryInDomainAppropriateForURLCreateError(
    $.NSItemReplacementDirectory,
    $.NSUserDomainMask,
    $.NSURL.fileURLWithPath(outDir),
    true,
    undefined
  ).path.js;

  // Abort if output file exists
  if (fileManager.fileExistsAtPath(outFile)) {
    console.log(`File exists: ${outFile}`);
    return JSON.stringify({
      alfredworkflow: {
        arg: outFile,
        variables: { file_exists: true }
      }
    });
  }

  // Generate snippet JSON files
  origData.forEach(item => {
    const itemUID = $.NSUUID.UUID.UUIDString.js;

    const snippetObj = {
      alfredsnippet: {
        snippet: item["snippet"],
        uid: itemUID,
        name: item["name"],
        keyword: item["keyword"]
      }
    };

    writeFile(`${tmpDir}/${itemUID}.json`, JSON.stringify(snippetObj));
  });

  // Archive the snippets into the output file
  runCommand("/usr/bin/ditto", "-ck", tmpDir, outFile);

  return JSON.stringify({
    alfredworkflow: {
      arg: outFile,
      variables: { file_exists: false }
    }
  });
}

 

Snip to CSV

 

// Helpers
function envVar(varName) {
  return $.NSProcessInfo
    .processInfo
    .environment
    .objectForKey(varName).js;
}

function runCommand(...arguments) {
  const task = $.NSTask.alloc.init;

  task.executableURL = $.NSURL.fileURLWithPath("/usr/bin/env");
  task.arguments = arguments;
  task.launchAndReturnError(false);

  task.waitUntilExit;
}

function readFile(path) {
  return $.NSString.stringWithContentsOfFileEncodingError(path, $.NSUTF8StringEncoding, undefined).js;
}

// Main
function run(argv) {
  const snippetsFile = argv[0];
  const fileManager = $.NSFileManager.defaultManager;
  const inputFile = envVar("input_file");

  // Create NSURL object from input_file
  const inputFileURL = $.NSURL.fileURLWithPath($(inputFile).stringByExpandingTildeInPath);
  const collectionName = inputFileURL.URLByDeletingPathExtension.lastPathComponent.js;
  const outDir = $("~/Desktop").stringByExpandingTildeInPath.js;
  const outFile = `${outDir}/${collectionName}.csv`;

  // Create NSURL for outDir and pass it correctly to URLForDirectoryInDomainAppropriateForURLCreateError
  const outDirURL = $.NSURL.fileURLWithPath(outDir);
  const tmpDir = fileManager.URLForDirectoryInDomainAppropriateForURLCreateError(
    $.NSItemReplacementDirectory,
    $.NSUserDomainMask,
    outDirURL,
    true,
    undefined
  ).path.js;

  const tmpDatabase = `${tmpDir}/${$.NSUUID.UUID.UUIDString.js}.sqlite3`;

  // Abort if output file exists
  if (fileManager.fileExistsAtPath(outFile)) {
    console.log(`File exists: ${outFile}`);

    return JSON.stringify({
      alfredworkflow: {
        arg: outFile,
        variables: { file_exists: true }
      }
    });
  }

  // Extract zip
  runCommand("/usr/bin/ditto", "-xk", snippetsFile, tmpDir);

  // Merge JSONs
  const jsonFiles = fileManager.contentsOfDirectoryAtURLIncludingPropertiesForKeysOptionsError(
    $.NSURL.fileURLWithPath(tmpDir),
    undefined,
    $.NSDirectoryEnumerationSkipsHiddenFiles,
    undefined
  ).js.map(p => p.path.js).filter(p => p.endsWith(".json"));

  const mergedData = jsonFiles.map(file => {
    const fullSnippet = JSON.parse(readFile(file));
    const snippetContents = fullSnippet["alfredsnippet"];
    delete snippetContents["uid"];
    return snippetContents;
  });

  // Create database
  runCommand("/usr/bin/sqlite3", tmpDatabase,
    "CREATE TABLE snippets ('name' TEXT, 'keyword' TEXT, 'snippet' TEXT)");

  mergedData.forEach(snippetData => {
    const nameString = snippetData["name"].replaceAll("'", "''");
    const keywordString = snippetData["keyword"].replaceAll("'", "''");
    const snippetString = snippetData["snippet"].replaceAll("'", "''");

    runCommand('/usr/bin/sqlite3', tmpDatabase,
      `INSERT INTO snippets (name, keyword, snippet) VALUES ('${nameString}', '${keywordString}', '${snippetString}')`);
  });

  // Return path to database
  return JSON.stringify({
    alfredworkflow: {
      arg: tmpDatabase,
      variables: { file_exists: false, out_file: outFile }
    }
  });
}

Posted

Welcome @TechnoWanderer42,

 

The auto-generated code messed up the organisation and introduced a bunch of unnecessary semicolons everywhere to essentially do a one-line change, so as you might understand I can’t take it as is. But I tested the current version in Sequoia and was able to reproduce the error, so I investigated the cause and made the fix myself in 2024.3, which I already released.

 

For future reports, please see How to Ask for Help with a Workflow post for a primer on the information to include when asking for assistance as that would’ve been helpful to investigate this in the proper way. Understanding why a problem happens is more important than taking any resolution. Thank you.

 

I also moved your post to the correct thread.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...