Newbie question: I need help to get started

I find myself wasting a lot of time manually editing SVG icons so I can store them in a json file. Specifically, I need to:

  1. replace " with ’
  2. remove all line endings
  3. replace # with %23

I figured I could create an extension that does just that, but I have no idea where to start. The extension reference is just that - a reference, but I could use some hand holding on how to go about this.

Thank you very much for your help,


The plugin is a good idea, however, it might be an overkill for your task. Plus your specs and needs might change later, but you’ll be bound to support the plugin forever. For tasks like that, I tend to make tiny self-contained helper apps — single, self-contained HTML file — driven by Vue.js and CodeMirror — which does whatever is needed, quickly. For example, — that’s two fields, input and output, plus dumping into localstorage so that when you refresh, fields are not wiped. Each time it loads, it reads localstorage and restores the dumped values. Each time you edit CodeMirror, it dumps into localstorage. That example is consuming JS package’s UMD build from jsdelivr. Rollup can build UMD’s. But you could remove references to the tokenizer and just put three chained String.prototype.replace(). If you really struggle, I can help you wire all these three tasks for MVP, it’s like 5 minute job, a cheeky tiny Vue app with two CodeMirror fields. Once you have the MVP, you can a) post it online, get the feedback; and/or b) separate the functionality into a standalone npm package, write proper unit tests, gather other maintainers (it’s a challenge to maintain packages) then write Nova’s plugin and tap that package.

hi Roy,
thank you so much for your thoughtful reply. Problem is, I don’t use Vue.js and CodeMirror. My approach to software development is minimalistic - I avoid using/learning new frameworks as much as possible (by the time I have learned them chances are another, even better framework is available. think Next, for example). The suggestion of creating a small web app is compelling, though. I’ll give it a serious thought.
As for the extension: I thought that all it would take is for me to figure out how to read on open document in Nova, do the necessary replacements and save it. But I couldn’t find any information at all on how to even start designing an extension. Is it so much more complicated than the web app idea?

Hi Franco,
I really believe an extension would be overkill for this task. Have you considered using AppleScript?
I made a small script which should do what you want. It opens a file you choose, replaces the characters and saves it (overwriting).

tell application "Finder"
	set theFile to choose file
	set theFileRef to open for access theFile with write permission
	set theContents to read theFileRef
	set theContents to my replaceText("\"", "'", theContents)
	set theContents to my replaceText(ASCII character 10, "", theContents)
	set theContents to my replaceText("#", "%23", theContents)
	write theContents to theFileRef starting at 0
	close access theFileRef
end tell

on replaceText(needle, replacement, haystack)
	set prevTIDs to AppleScript's text item delimiters
	set AppleScript's text item delimiters to needle
	set textItems to every text item of haystack
	set AppleScript's text item delimiters to replacement
	set haystack to textItems as string
	set AppleScript's text item delimiters to prevTIDs
	return haystack
end replaceText

Although I must admit there may be cases where this fails. I don’t know all specs of the SVG file format.

1 Like

Now Applescript is probably the best approach for my problem. So thank you so much for helping, I really appreciate it.

An SVG document is just a text file, a markup language for vector design.
Replacing the double quotes won’t fail, that’s required by JSON and SVG doesn’t care. Neither removing the EOLs.
Replacing # with %23 is now an ANSI standard requirement for when you want to include an SVG directly as a src attribute i.e. in an img. In theory the entire SVG should be URL-encoded, but in my experience the # is the only character a browser could choke on.

I am not disagreeing with you on the extension question, I simply am curious to learn more about how to create an extension, as the documentation really is of no help. I thought my problem would be a gentle way to get into this area. I hope Panic at some point will provide some kind of introduction to designing extensions.

Here’s the tiny UI, 99 lines incl. comments, two versions (return result1 for regex’es only instead of result2) — it’s two input boxes only, local storage wired for persistence upon refresh, uses proper for JSON escaping, CSS theme is Oceanic Plus(-ish):

<!DOCTYPE html>
<html lang="en" dir="ltr">
    <meta charset="utf-8" />
    <title>SVG Processor</title>
    <style type="text/css">
      * {
        padding: 0;
        margin: 0;
        font-size: 14px;
        color: #f9c76b; /* yellow-ish */
        background-color: #1c2c34;
        font-family: sans-serif;
      body {
        margin: 0;
        overflow-x: hidden;
        overflow-y: hidden;
      textarea {
        color: #bec5cf; /* white-ish */
        width: 49%;
        height: 100vh;
        font-family: monospace;
    <div id="app">
      <textarea ref="in" v-model="text1" placeholder="put html here or drag a file"></textarea>
      <textarea ref="out" v-model="text2"></textarea>

    <script src=""></script>
    <script src=""></script>
      Vue.config.productionTip = false;
      const myStorage = window.localStorage;
      window.Event = new Vue();
      new Vue({
        el: "#app",
        data: {
          text1: "",
        mounted: function () {
          console.log(`068 called mounted()`);
          const retrieved = myStorage.getItem("svgprocessor");
          if (typeof retrieved === "string" && retrieved.length) {
            this.text1 = retrieved;
          } else {
            this.text1 = "";
        computed: {
          text2: function () {
            // update the "out" text area:
            if (typeof this.text1 === "string") {
              // version 1 - DIY escaping with replaces:
              const result1 = this.text1
                .replace(/"/g, "'")
                // CR/LF/CRLF line ending regex:
                .replace(/(\r?\n)/g, "")
                // replace one or more tab with nothing:
                .replace(/\t+/g, "")
                // replace more than one space with single space:
                .replace(/\s+/g, " ")
                // DIY-encode hashes:
                .replace(/#/g, "%23");

              // version 2 -
              const result2 = jsesc(
                  .replace(/(\r?\n)/g, "")
                  .replace(/\t+/g, ""), 
                  json: true,
              return result2; // return result1 alternatively
            return this.text1 || "";
        watch: {
          text1: function () {
            console.log(`113 dump to local storage`);
            // dump the input into the local storage so you can refresh
            if (typeof this.text1 === "string" && this.text1.length) {
              myStorage.setItem("svgprocessor", this.text1);
            } else {
              myStorage.setItem("svgprocessor", "");

take it and do whatever you want with it, no attribution needed, I submit it to Public Domain :stuck_out_tongue:

That would be nice. Especially in screencast format! :crossed_fingers:

1 Like

Yes indeed, and thank you so much for your help!


There has been a discussion thread on that topic.

1 Like

We’d love to do this! More in-depth, guided tutorials would definitely be a great resource. At the moment, we’ve been constrained by our staff size in terms of expanding our help content (Nova is built, QA’d, and documented by, effectively, a team of five.), but we really hope to be able to improve this going forward as our development milestones relax a bit.