I have been asked to write an interview piece for @coder_story ( https://coderstory.io/ ), which I'm really happy about. 😄
I think that the best thing I could do for their readers would be to rebuild an old concept and live tweet about my building processes! 👨💻
Follow Along 👇🏻
The project I'm going to be revisiting was actually my first attempt at HTML, written just before I started University 🎓 in 2014.
I had just gotten into Linux 🐧, and wanted to play with HTML, so created an amateurish terminal-themed 'blog':
My old concept was essentially just a novel way of navigating pages and displaying text. 🤷🏻♀️
The markup was formatted very badly, and the site offers minimal interactivity. It's also waaay to inspired by The Matrix! That said, it still holds fondness in my heart.
I'd like the new version to be more interactive - there should be a handful of 'programs' ⚙️ (functions) the user can 'execute' and a 'filesystem' 💾 (just json) that they can navigate.
This is a small project so doesn't need much preparation, but I sketched my ideas on paper:
My @jekyllrb site uses a layout for all new pages that generates the meta tags and general boilerplate. I've done the necessary setup that will allow me to start coding.
The page is here: https://jetholt.com/terminal/
Essentially, I'll be starting with this:
When I'm building, I focus on core functionality first and visuals later, as a working product is better than a useless pretty one.
First step is to set things up so that we can print to the console when the user hits enter. It's a simple first step but a necessary one. I wanted to use the DOM's cloneNode(true) but it would have created duplicate id's. 🙅♀️
Next step: Parsing input and executing it. ✌
Now implemented rudimentary "parsing" and executing of user command.
I was going to define a bunch of global commands like `var echo = function(); `, and then have a switch statement to determine the string, but realised it would be easier to lookup properties from objects.
My terminal has a live clock so that I can see what time I ran a command. I was getting tired of seeing the clock be out of date 🕠so I added code to make this live 🕖. The timeout at the end runs twice a second to avoid accidental skips.
Next: The 'filesystem'. 💾✌️
I'm choosing to write the 'filesystem' portion next because it'll be required for many of the commands I want to write
I'm going to represent it as a set of nested JSON objects with properties like 'name', 'type' (directory, file,...), 'date','protections','contents','owner'...
By the way, I'm pushing my code live every time I tweet, follow along here:
I have some of my 'filesystem' 💽 in my HTML file and some in my JS file. The stuff in the HTML file lets me use Jekyll to 'pull in' the metadata about my blog posts when the page generates. I'll be adding more here later, this is just a proof of concept.
I'm back after getting food 🍲 and am working on implementing a basic form of `cd`.
When you're working on a project, you will often realise that a decision was not thought out; Have the `content` of a directory as an array is hard to work with. An object works much better. 🙈
There were definitely more improvements to be made with how I was creating the file system. 👎🏻
I've switched over to some object orientation to reduce code repetition and have made sure that directories have `.` and `..` set in their contents to make provenance much easier. 👍🏻
I wrote a helper function to parse a path like `../blog` or `~/blog` or `/home/jetroid/blog` and return it's FilesystemObject (from the tweet above ☝🏻).
This will make writing `cd` that much easier (and probably for other functions too!) 🤖
I'm having trouble with CORS right now blocking me from downloading files with my wget command, so I've set up https://jetroidcors.herokuapp.com/ using https://github.com/Rob--W/cors-anywhere … which should hopefully solve the problem. 👌
Hurray! `wget` is now working too! 🙌 (Or at least, the most basic functionality is working - I still need to update the text and check for bugs.)
I'm able to download files from both http://coderstory.io and http://jetholt.com (the two links I tested). 😊
It's late here now - 1:30AM, so I'm going to be going to sleep.
🕖 7 hours
⌨ 314 lines of code
🐦 20 tweets (including this one)
💾 7 git commits
🤖 5 semi-implemented commands
See you in the morning! 😅
I didn't make it for the morning, but I'm back now and working hard. 🙏
I realised I hadn't mentioned that I was going to be mimicking my current terminal rather than the more classic matrix-like http://hackertyper.com/ style.
A little bit of css and I've got 'the look'. 👌
Added some fluff to make it look more like you just logged into a neckbeard-admin's server. 😅
Obviously today I've broken protocol by focusing on front end visual rather than functionality, but the white background was giving me eye strain. 😳
I went down a rabbit-hole 🐇🕳 to get wget looking realistic. The command bloated up to 88 lines! 🤯
I also went down a 🐇🕳 to figure out how to record a gif on my laptop - but my laptop is too old and slow 🐢 so I had to ask my friend to help. 😅
Back again after eating 🍜 and I've implemented a `shutdown` command.
I wasn't able to find anywhere to copy some of the shutdown text so I had to write my own from memory. To bulk it up I added plenty of jokes. 😅
I was hoping to be able to reuse ♻ some of the code I had written for https://jetholt.com/hacking/ , but I found that the code I had grown more complex than I remembered. 😶
So I just used it for inspiration after reading through and understanding it. 😅
I need to write lots of text to the screen repeatedly, so I loop through the text backwards, creating a chain of nested functions. The first function created is the last executed. 🤔
I'm sure there are better ways (using the Promise API?) but I'm more confident doing this. 😅
Wasn't able to post a sign-off email last night because the WiFi in my apartment in Kuala Lumpur was being shaky.
Some stats from yesterday:
🕔 5 hours
⌨ 256 lines of code
🐦 6 tweets (not including this one)
💾 2 git commits
💓17 posted emojis
Today, I will be focussing on commands relating to users, permissions, and identity; such as 'su', 'sudo', 'who', and 'whoami'. 🔒👥
I'll probably have to learn a bit about how permissions and groups work, and I'll share that process here too. ✌
It's unlikely that the user accesses every blog post, so I decided to represent my blog posts in the filesystem as files with the content of "blogurl(<link-to-blogpost>)" rather than the actual content to reduce the downloaded bytes. ⬇
I request the HTML with AJAX when we cat.
UNIX PERMISSIONS SYSTEM
Each unix file has a set of permissions for the owner, the group, and for everyone else. These are whether a user in each category can Read, Write, or eXecute it.
This is represented in a couple of ways...:
rwxrwxrwx format and 777 format
In the rwxrwxrwx style format, each rwx shows the permissions of owner/group/others. If the letter (eg r = read) is there (rather than a dash), then they have the permission.
So rwxr---- means the owner can do everything 🌐, groups only read 📖, nothing for everyone else👎.
There's also the 777 style format, where again each of these three numbers represents permission of owner/group/others.
7 means the same as 'rwx' and 0 means '---'.
I'm not sure about the other numbers 🤔, but I know you can add them to make other combos, like binary.
For example, I don't know if 4 is read or execute. It's something like this
4 = read or execute?
2 = write
1 = execute or read?
But I'm not sure which is the correct order. 🤔
3 = 1+2 = write + (execute or read)
5 = 4+1 = read + execute
6 = 4+2 = (read or execute) + write
What I mean by this that 100 in binary is either equal to 4 or 1 depending on if you read it left to right or right to left (endianness). The most common reading is 100 = 4.
This makes me suspect that
4 = read
2 = write
1 = execute
But I'd need to look it up.
Bringing it all together, I suspect:
0 = no permission
1 = execute
2 = write
3 = write + execute
4 = read
5 = read + execute
6 = read + write
7 = read + write + execute
...and I just looked it up and that's correct. Hurray for figuring things out! 🥳
Thinking about it, I think the thing I need to learn more about the Unix Permission System is about groups.
I don't know:
🤔 How to find out what groups a user is in
🤔 What groups are responsible for other than permissions
🤔 What groups a typical user is in
The nice thing about writing out what you need to know like this is that these are basically questions I can throw into google...
16:57:44 [email protected]:0~/projects/jetroid.github.io$ groups
power wheel input storage users
I was working my way through implementing permissions functions, when I came upon a question:
What if the permissions is ---rwx--- ?
I can't do anything with it because I'm the owner, but can do everything because of my group? 🤔
Which takes precedence?👍
I find using the correct technical terms (like 'precedence') useful when talking about programming.
Not only will you be able to communicate clearly with others, but you can search for answers more easily.
I would have found it hard to find this particular answer otherwise! 😅
I'm a little behind with the tweets, but I've made a ton of progress..!
Newly implemented commands:
📒ls -al (honestly this took the most time)
Here's a little demo of sudo and su, as well as a few of the other commands.
Note that as `jetroid` chown was denied, but it was allowed as both a `sudo` command (using !!), and with `root`.
It looks so much like my terminal right now that I keep trying to use my shortcuts. 🤐
And this is a pretty good point to wrap up for today!
🕔 7 working hours
⌨ 423 lines of code
🐦 17 tweets (including this one)
💾 2 git commits
🤖 9 new commands
💪2 improved commands
I'm getting very close to the functionality I want this thing to have now! ✨
I've been pretty busy today, so I haven't managed to get much done here.
A command I've implemented (inspired by http://uni.xkcd.com , which seems to be broken (for me?) right now) is `display`🖥, which when ran against one of my blog posts, displays the text and images. 😀
I also implemented read/write permissions for use with cat, wget, and others (when I implement them!).
`jetroid` can't read /etc/shadow nor write to / 🙅♂️, but root can do both ✅.
You'll see the precedence described at https://unix.stackexchange.com/questions/134332/precedence-of-user-and-group-owner-in-file-permissions … quite clearly in lines 212-217. 😁
I just encountered a bug and it stumped me for a while - I think it'd be useful for @coder_story readers if I explain my debugging process too!
I was implementing `rm` and ran it on a file and I got
rm: cannot remove '': Is a directory
Uhh, what? But it's a file!?
I was confused, so added logs to the `rm` function (blue circle), then reran my command. I saw that `rm` was being called as if I had first specified the root `/` (green) by writing blank (orange).
I suspected multiple-spaces in the middle of a command, so I tested it (red).
rm was being called on `/` because:
1) If you're spliting on a character and you have a two of that character one after they other you get an empty string.
ie 'rm <filename>'.split(' ')` becomes:
2) the path '' (empty string) defaults to root (`/`)
We can solve that first point pretty easily by sanitising our input a little better, and replacing repeated spaces with a single space, then trimming off all whitespace to the left or right of what the user wrote.
It's a pretty simple fix, all things considered. 👍
But there was also a second 'bug' - did you notice it?
The natural whitespace collapsing is affecting the 'past terminal entry' in an undesired way! Multiple spaces got represented as a single space! 🤦♂️
To fix this, we can wrap it in a <pre> tag or use CSS white-space:pre;
`rm` is now implemented! 🎉
We're coming to the end of this little project now:
1.) One final `hacker` command
2.) Replace the command textbox with more realistic typing using event binding
3.) Add a 'Made By' + explanation to the lower corner, like with https://jetholt.com/hacking/
Wi-Fi has been out in my apartment all day again. 😒
The `hacker` command I mentioned turns the colorscheme from the default back into what I used with https://jetholt.com/old-terminal/ !
It's very simple - just adds the hacker class to the body and overrides css.
It makes me nostalgic!
Implementing more realistic typing took a while, and I ended up taking a different approach than I had expected.
I was planning on binding all kinds of different keypress, keyup, events and correlating keys to letters. It was a very convoluted method that didn't work well.
Instead, it was far easier to add a 1px by 1px <input> tag (with all kinds of styles to make it nearly invisible) in the upper left corner.
Size increased to 10px by 10px for demonstration.
(Extension used is http://getcssscan.com by @gvrizzo)
Then all it takes is a little function to take the data from that input tag and display it where I want, which I call onkeyup and oninput. A final onclick to the whole body to autofocus on the 1px by 1px text input completes the illusion. 🧙♂️
And it works really well! 😄
There are so many nice thing about using the native <input> element rather than hacking together something with event handlers - for one, it automatically brings up the keyboard on mobiles, which you can't do otherwise. For another, it's a lot simpler!
Think outside the box! 🤪
...and I just finished adding the 'Made By' widget!
This was really easy as I could copy it from https://jetholt.com/hacking/ - not only does it create a consistent style, but it saves a lot of time!
Some final statistics:
🕖 25 hours (how many composing tweets? 😝)
⌨ 1223 lines of code
🐦 60 tweets (including this one)
💾 15 git commits
🤖 18 commands
🤪 Too many emojis...
Crazy ride! Thanks for following along!
You can follow @JetroidMakes.