If you're not familiar with it Glitch is “the friendly community where everyone can discover and create the best stuff on the web.” I found it a few months ago after a co-worker pointed it out to me and I fell in love with it right away.
I could go on and explain all the things that make me love Glitch (50% of it are the cute graphics though 😍) but it suffices to say that it brought a level of support to the iPad that I hadn't seen before in other development environments. Since nowadays I only use the iPad this was a “game changer”™ to me.
However, as great as it is, it still had a few issues that slowed me down so I decided to improve it by extending it. In this post I'll detail the two main areas I focused on — Text Editor improvements and Prettier support — but before I go on I'll first explain the basics of the extension.
Glitch is not open source so I'm not able to just fork, fix and create a pull request for it (like I did for CodeSandbox), I had to find other ways to run my code in it. The most common way is probably through browser extensions, they are little apps you can install on your browser that have great power over what's running on the browser. Unfortunately Mobile Safari does not support this mechanism so it was a no go for me.
The next best thing are bookmarklets, these are (like the name implies) bookmarks, but instead of loading a specific address in the browser they run code! This is possible because the browser understands multiple different types of URLs, the type (aka protocol) of the URL is the first thing you see before the colon. For example, in
https is the type, which is the most common one, but there are many others, like
mailto but also..
If you want to try it out you can install the extension here.
As of now any key binding that uses
Cmd) does not work inside the text editor. Yes, that means, no undo (
Cmd-Z), search (
Cmd-F) or even block comment (
Cmd-/). [NB: this only reveals the amount of value Glitch brings to me even though I wasn't able to undo.]
All of this is not Glitch's fault at all, Mobile Safari has really bad support for certain keys on
input type="text" and
textarea elements. As of now it's not possible to know when the person pressed the following keys:
Command as no key event is emitted.
You might have noticed that I didn't mention
Alt, that's because this one emits events! It needs to, because on iOS (and MacOS)
Alt-<key> will print an actual character (e.g.: in the US keyboard
Alt-P will print
After learning this I made compromise: I replaced all
Cmd key bindings with
Alt ones, at the cost of not being able to print certain “uncommon” characters like
Alt-Z in the US keyboard, but now I've gained back the ability to undo!
There was still another issue that was driving me crazy, whenever you selected some text in the editor it would just scroll down to some random part of the code, making it almost impossible to select text. After some debugging I realized this was a bug in CodeMirror's
highlightSelectionMatches feature (CodeMirror is the component Glitch uses for text editing). For some reason when CodeMirror highlights text that matches the selection the editor jumps there as well. I haven't had the time yet to understand why this is happening, so I just disabled this feature.
How does all of this translate into code you ask? Good question! CodeMirror provides a very comprehensive API so I used it to change the keybindings and to disable the
highlightSelectionMatches feature. However, I needed to get a hold of the CodeMirror instance first, this was actually easier than I thought as CodeMirror sets the instance on the corresponding DOM element, hacky, but handy :-). As there's only one in the entire document I just
document.querySelector('.CodeMirror').CodeMirror to get it. Here's the end result and final code:
let cm = document.querySelector('.CodeMirror').CodeMirror; let extraKeys = cm.getOption('extraKeys'); cm.setOption('highlightSelectionMatches', false); extraKeys['Alt-Z'] = 'undo'; extraKeys['Shift-Alt-Z'] = 'redo'; extraKeys['Alt-/'] = 'toggleCommentIndented'; extraKeys['Alt-F'] = 'findPersistent'; extraKeys['Shift-Alt-F'] = 'findPrev';
I use prettier on save everyday at work and can't really imagine my life without it now, so I had to have it on Glitch too.
Prettier usually runs on node but it also offers a standalone version that runs on the browser. This is what I used to implement this feature.
Once I started I quickly realized this was not going to be as simple as initial thought. Adding a new script tag through the bookmarklet was not working at all, and this was because
https://glitch.com/edit implements a very tight Content Security Policy (CSP).
A CSP is a set of rules a webpage can define to restrict the type of resources that can be loaded. Glitch has a few ones that severly restricts the different ways to run external code:
script-src: this directive specifies not only which sources are permitted in a script tag but also limits the ability to create code from strings like
Function(). Glitch only allows script sources from its own domain —
https://glitch.com/— and a few CDN domains to load static resources from, it also strictly forbiddens the creation of any code through eval. That was a bummer :(.
frame-src: this directive is similar to the previous one and it restricts the sources that a frame or an iframe can load. Glitch only allows for
https://*.glitch.me https://*.glitch.staging.me https://*.glitch.development. The last two ones clearly included for development purposes of Glitch itself.
Fetch. In Glitch's case only its own api domain —
https://api.glitch.com— and a few others like github are allowed.
default-src: this directive specifcies the default to use for any other directive that is not explicitly written out. In the case of Glitch this is set to
'self', which means its own domain.
All of this disallowed me to include any of the prettier code through a script tag or loading it through XHR and then evaling it.
I included the
frame-src directive above because I noticed that it actually allows the loading of any Glitch project that anyone has created. The reason it does this is so you can see Code and App side by side. Communication between the iframe and the main window is possible so I leveraged this to load and run the prettier code.
I created a Glitch project with the standalone version of prettier and a function to call it. I loaded this project on an iframe created by the bookmarklet and now I only needed to call that function.
My first instinct was to access the iframe's window using
iframe.contentWindow, however, you can only access its properties if both pages are in the same domain. The problem here is that the main window is on
*.glitch.com and the iframe on
*.glitch.me (where all Glitch apps run). If this hadn't been the case then I could have set
window.document.domain = "glitch.com" on both pages to put them in the same domain and be done with it. However, this is only possible when they share the same second-level domain.
The alternative way to communicate between pages of different domains is the postMessage API. It's a simple API composed by a single method —
window.postMessage(message, targetURI) — that when called sends a message event to that window. The
targetURI parameter is the URL of the page you want to send the message to. This is a way to ensure you never send important data to another page by mistake, in our case we don't care so we use
In my case I wanted the main window to send code to the iframe and receive the formatted code back. This is roughly how I coded it, first the bookmarklet and then the iframe:
As you can see this is an async API meaning that it may take some time between the person saving and getting the code formatted. However, since both pages are running on the browser this operation is so fast you won't notice any delay.
This is a feature I'm trying out to help me write code on the phone. Formatting code on the phone is a serious pain and to make things worse there's no easy way to trigger prettier on save. When turned on (it's off by default) it will auto format the code with prettier after 2s of inactivity.
Finally, I wanted these extensions to feel like they were part of the Glitch ecosystem. For this effect I added notifications and preferences using the Glitch look and feel:
One of the risks of having this integration is the tight coupling it creates with Glitch. As a 3rd party this means Glitch can unilaterally break some of assumptions I’m making. But given the end result it’s a risk I’m willing to take for now.
I'd like to thank the Glitch team for an amazing product, and also for making source maps available for all the code. This allowed me to gain a better understanding of the underlying logic and to hook in and reuse parts of the application.