diff --git a/pages/index.tsx b/pages/index.tsx index b1856dd..ea71056 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -14,6 +14,8 @@ export async function getStaticProps(): Promise<{ props: PageProps }> { const sortedPosts = posts + .filter((post) => post !== null) + .filter((post) => !post.unlisted) .sort((a: Post, b: Post) => b.date.valueOf() - a.date.valueOf()) .map((post: Post): SerializablePost => ({ ...post, date: post.date.toISOString() })) diff --git a/posts/2019-07-29_pixelbook-first-impressions.md b/posts/2019-07-29_pixelbook-first-impressions.md new file mode 100644 index 0000000..91604f9 --- /dev/null +++ b/posts/2019-07-29_pixelbook-first-impressions.md @@ -0,0 +1,50 @@ +--- +title: Pixelbook First Impressions +subtitle: Becasue web dev in a web browser is sure to go well (/s) +--- + +My wife and I have been looking to upgrade her laptop for about a month or so. Our options were to buy her a new one directly or to find a replacement for me so she could use my old one. Last week we came across an interesting option: a used but great condition Pixelbook for basically half price. + +I’ve been using this Pixelbook for about a week now, and I think I’m ready to give my first impressions. + +### First, some context + +I’m a computer science student with an emphasis towards security and web programming. I already have a serviceable desktop PC for occasional gaming and class projects. My previous laptop was a Dell Inspiron from 2015. It worked well enough --- a bit heavy, but it was powerful enough to serve as my primary machine for several years. Since building my desktop I no longer need quite as much power on the go, so it was time to find something new. + +Not everyone is in a position like mine, nor do we all have the same expectations with our devices. Because of this, you may disagree with my observations or conclusions. That's okay --- this isn't meant to be a review, but only a personal account of my experiences with the device. + +### The Hardware + + +I purchased the i3 / 128 Gb model Pixelbook. It’s surprisingly lightweight when compared to any laptop I’ve ever owned. I’ve not ever intentionally bought a lightweight device before though, so take that with a grain of salt. The keyboard is okay - it’s a bit soft and the lack of delete or end keys really bothers me. On the upside: the adjustable keyboard backlight is a nice touch. + +The touchscreen is responsive and seems to be accurate. Its pen is useful for note-taking or annotating documents. Overall the hardware is not disappointing, but there's nothing particularly remarkable about it. + +It's real strength in this category is the versatile tablet mode. It works well when propped up as a tent, and this has quickly become my favorite orientation to use for D&D sessions! + +### The Software + +This is where we get into the contentious topics! ChromeOS is ... okay. It’s really not consistent and still feels very unfinished. Installing Linux apps in a container helps, but there are definitely some issues. Linux apps are inconsistent in how they handle the high-dpi screen, and sometimes the whole container sems to crash. This wouldn't be so bad if the integration was a bit more seamless --- an easy way to expose development ports, or integrating the clipboard perhaps. + +Coming in with a slightly better overall experience is the Android app support. Clipboard works, the text is scaled properly, and these apps generally feel better to use. Not every app is available, and even those that are can sometimes seem stretched to fit the screen. On the whole though, Android apps are a much more delightful experience than Linux ones. + +Chrome apps are of course the star of the show on ChromeOS and franky I don’t really use them that much. Other than the core OS I don't use Chrome on my Pixelbook. For development I need a browser running _inside_ the container, so I've kept on using Firefox for now. + +### Conclusion + +Pros: + +- Very convenient form factor +- Tablet mode is fantastic for annotations, note taking, etc +- Linux support works fairly well +- Powerful enough for my development, although your workflow may vary + +Cons: + +- ChromeOS settings are quite limited +- Keyboard lacks some keys I really do miss (del, home, end) +- Can’t remap the Google Assistant key to function more like a traditional Super key +- Minor bugs here and there … nothing game-breaking, but they add up + +Overall, I’m happy enough with my new Pixelbook. ChromeOS is just _barely_ good enough that I put up with it, but who knows! I may still flash it with real Linux just for fun one of these days. If I do I'll be sure to update this post to include my experiences with Linux driver support, bootloader issues, etc. + diff --git a/posts/2019-08-14_pixelbook-dev-mode.md b/posts/2019-08-14_pixelbook-dev-mode.md new file mode 100644 index 0000000..c9aa631 --- /dev/null +++ b/posts/2019-08-14_pixelbook-dev-mode.md @@ -0,0 +1,56 @@ +--- +title: "Pixelbook Development Mode: A Cautionary Tale" +subtitle: It was all going far too well +--- + +So for about a month I've been using the Pixelbook as my portable development +and note-taking laptop, and it's been generally going well. Like I mentioned in +my [first impressions](/pixelbook-first-impressions) post, the +pen support is great, Android apps run surprisingly well, and while the Linux +container has some quirks it's pretty useful overall. + +My biggest complaint so far, is that enabling ChromeOS developer mode puts it +it in a state where it's far too easy to accidentally wipe your system. **(Yes, +I say this from experience - it just happened to me.)** + +Putting the Pixelbook in developer mode requires disabling its boot-time OS +verification, and every time the device starts it splashes up a warning screen +to inform you of this fact. The warning screen helpfully recommends you press +the spacebar to re-enable OS verification, but neglects to mention that doing +so will factory reset the device. + +> *Developer Mode Boot Screen* +> +> ![Image of Pixelbook stating "OS verification is off"](/images/chromebook-developer-mode-warning.png) +> +> Source [Android Central](https://www.androidcentral.com/how-enable-developer-mode-chrome-os) + +If developer mode was more stable, this wouldn't really be an issue - the +problem comes from when the device reboots unexpectedly. + +This morning I was using my Pixelbook in tablet mode with the keyboard folded +behind it. This configuration puts the keys on the exposed bottom face of the +device . . . where I was holding it. + +I was moving some windows around when the system froze and restarted, and before +I could realize what was happening my Pixelbook started resetting itself. The +way I was holding it, the spacebar was depressed (which normally isn't an issue +since keyboard input is disabled in tablet mode, but the bootloader isn't smart +enough to know that) and within seconds it had cleared the OS verification +screen and started the reset. + +Luckily most of my projects are in git and all the Android apps I was using +sync to cloud storage automatically, so I didn't end up losing anything. But it +definitely made my heart skip a beat when I saw that powerwash screen come up! + +So if you think you'd like to get the latest features, or pehaps you want to +sideload some Android apps onto your Pixelbook - whatever it is, think long and +hard about the risk of living one crash away from accidentally resetting your +device. + +I think Google should potentially make it a bit harder to activate the powerwash +function from the bootloader, but developer mode _is_ supposed to be unstable +and dangerous. Personally I'll be sticking to the stable branch from now on, +so there's not any risk of this happening again. If you decide developer mode +is right for you, be sure to back up your work regularly! + diff --git a/posts/2019-12-04_html-beginners-guide.md b/posts/2019-12-04_html-beginners-guide.md new file mode 100644 index 0000000..7f8604a --- /dev/null +++ b/posts/2019-12-04_html-beginners-guide.md @@ -0,0 +1,213 @@ +--- +title: "A Beginner's Guide to HTML: Layout and Display" +subtitle: Some early thoughts for a friend +--- + +Welcome back! It's been a while, but today we're going to talk about +web stuff - I've been meaning to write a beginner's guide for a friend, so +today's the day! + +We're not going to get too far into depth, we'll just be going over the basics of +browser layout, and how to use display `inline`, `block`, and `inline-block` elements. + +> *Note:* This post will not be addressing `flex` or `grid` elements - I plan to cover those in a later post. + +## Browser Layout and the Display Property + +Every element in an HTML page has a `display` property - you might see it defined in CSS like this: + +```css +img.photo { + display: block; +} + +img.icon { + display: inline; +} + +img.galleryCard { + display: inline-block; +} +``` + +This instructs the browser to render various elements as either `display: block`, `display: inline`, or `display: inline-block`. But what exactly do these display rules mean? + +Generally speaking: block elements are elements which the browser renders as a box, and inline elements are elements which the browser renders as flowing text. Let’s get into that a bit more! + +### Block elements: + +- Can have a defined width and height +- Can be given both vertical and horizontal margins +- Clear the whole width of their container + +Block elements are typically things like <div>, <ul>, and header (<h1>…<h6>) elements - although most HTML elements can be rendered as a block if you enforce it with the `display: block` rule. Here’s a quick example: + + +``` +
+

This box is display block. Notice it takes the full width of its container.

+
+``` + +
+
+

This box is display block. Notice it takes the full width of its container.

+
+
+ +See how this box automatically takes the full width? We can give it a fixed width and it will behave slightly differently: + +``` +
+

This box is display block.

+

It is constrained to be 200px wide.

+
+``` + +
+
+

This box is display block.

+

It is constrained to be 200px wide.

+
+
+ +Let’s look at what’s going on here: + +- The box is set to have a width of 200px +- We give it 10px of padding on each side (so the text doesn't run right up to the edge) +- We set the background color of the box and text color for elements in the box + +Remember how initially I mentioned that block elements clear the width of their container? Let's demonstrate that: + +
+ Text before the block element . . . +
+

Even when we remove the centering margin - block elements clear the whole width of their container.

+
+ Text after the block element . . . +
+ +Block elements clear all of the space to the side of them _by default_. This can be convenient sometimes, but other times it will be a bit annoying - try putting a border around a <h1> tag sometime, unless you’re clever the border will extend to the full width of the page. + +So if that’s block elements, how are inline elements different? + +### Inline elements: + +- Flow from line to line, filling the space defined by their closest `block` ancestor +- Cannot have vertical margins +- Do not clear space to either side of them + +Inline elements are things like <a>, <b>, <i>, and <span> tags. These are effectively treated as text, they wrap at the end of the line, and generally flow like you would expect text to in a word processor, although they don’t necessarily have to be text: + +``` + + + + +``` + +
+ + + + +
+ +Notice how when your screen isn't wide enough for all of them, these kitten images wrap at the end of the line? Also of interest is that there is a space between each of them (from the whitespace in our source code) but not between different rows of images. Images are by default `display: inline` so the browser tries to use its text-layout rules for how it should display these kittens. + +One downside to inline display elements like this is that we have very little control over how much space is around our kittens - we can set a left or right margin, but there’s no way to make sure we have vertical spacing between the rows. We’ll see in a bit how we can use `inline-block` to get around this, but let’s keep exploring how `inline` works for a second. + +Because these images are treated as very tall text, other text immediately before or after the kittens can also get weird - inline elements **don’t** clear space beside them like block elements: + +``` +Text immediately before an image + + +Text immediately after +``` + +
+ Text immediately before an image + + + Text immediately after +
+ +But remember we said before that any of these `display` properties can be overwritten?! If we instead set our kittens to be `display: block` then we see that our kittens suddenly behave much more like our space-clearing box from earlier: + +``` +Text immediately before an image + +Text immediately after +``` + +
+ Text immediately before an image + + Text immediately after +
+ +**Note:** This technique of setting images to be `display: block` is often used for illustrations on the web - just add an `auto` side margin and it’s even centered: + + +``` +Illustration of a kitten: + +(Text after the kitten) +``` + +
+ Illustration of a kitten: + + (Text after the kitten) +
+ +Now you might be thinking _“Couldn’t I just use <p> tags for my text and not have to deal with this `display: block` stuff?”_ . . . well yes. Sort of. + +It’s true that if you put your text in <p> tags then you get a nice result more or less for free: + +``` +

Text in a <p> before an image

+ +

Text in a <p> after

+``` + +
+

Text in a <p> before an image

+ +

Text in a <p> after

+
+ +But even here, you’re dealing with the pernicious influence of the `display: block` - <p> tags are block elements! This might seem counter-intuitive at first because we started by describing inline elements like flowing text, and <p> tags are _literally for text_, but I promise it makes sense: <p> tags represent a paragraph, and a paragraph should fill the width of its container. (Now, the _text inside_ the <p> tag is inline, but that’s another story . . . ) + +So anyways, now that we’ve exposed the dirty secret of <p> tags, what exactly are `inline-block` elements? + +### Inline-Block: A Surprisingly Useful Hybrid + +So it should come as no surprise to you that an element with the style rule `display: inline-block` should behave in some ways like an inline element, and in some ways like a block element. `inline-block` is useful because it allows us the control over width, height, padding, and margin that a `block` element does, but it can be used a situation where `block` doesn’t work - primarily where you want your elements to come one after the other within the same line like an `inline` element. + +This kind of element is commonly used for photo galleries or navigation menus like this: + +``` +Home +Posts +Contact +``` + +
+Home +Posts +Contact +
+ +There are a lot of different ways to use `inline-block` elements, and for a long time it was the preferred way to make several block elements that flowed from left to right rather than just top to bottom. Nowadays this use has largely been replaced by the new CSS flexbox and grid rules - but we’ll have to talk about that another time! + diff --git a/posts/2020-01-14_pixelbook-scaling-mode.md b/posts/2020-01-14_pixelbook-scaling-mode.md new file mode 100644 index 0000000..789d3ba --- /dev/null +++ b/posts/2020-01-14_pixelbook-scaling-mode.md @@ -0,0 +1,36 @@ +--- +title: "Adventures with my Pixelbook: Linux Scaling Mode" +subtitle: Be warned, it's kind of janky +unlisted: true +--- + +This is just a quick update on one of the largest issues I've had with the +ChromeOS Linux app support: display scaling. + +ChromeOS has a display scaling mode for Linux applications that don't handle the +high-DPI screen very well, and it generally works okay. It's a bit blurry, but +better than having a tiny UI in some applications. + +The downside is that to enable this mode for an app, you need to pin that app +to your taskbar. + +I'm not particularly fond of having a full taskbar, so I like to keep just the +essentials on there - and what about apps I need to launch with command line +parameters? Those can't be pinned to the taskbar without losing that ability. + +If you've run into this issue, then don't worry - there is a solution! + +ChromeOS's Linux container is allowed to draw to the screen through a special +X11 server that is run _inside_ the container, and then forwards that image +to the Wayland server running in ChromeOS. The magical screen scaling? It's +just a second X11 server! + +Therein lies the trick to start apps with window scaling, +simply set the `DISPLAY` environment variable to `:1`. + +```shell +DISPLAY=:1 firefox +``` + +Enjoy running your Linux apps without needing a magnifying glass! + diff --git a/posts/2022-04-05_debugging-write-up.md b/posts/2022-04-05_debugging-write-up.md new file mode 100644 index 0000000..0138444 --- /dev/null +++ b/posts/2022-04-05_debugging-write-up.md @@ -0,0 +1,120 @@ +--- +title: Sample Debugging Write-up +subtitle: '' +unlisted: true +--- + +> This is a sample write-up of how I investigated an issue for work. +> +> I'm putting this up with permisison from my former job, but it is ***unlisted*** +and I would appreciate if you do not share it around. +> +> Additionally, code screenshots originally included when sending this write-up +to my co-workers have been removed so as to not leak confidential information. +Where applicable the document has been lightly re-worded to still make sense. + +## Initial Issue + +To start off with, let's talk about the actual bug. We observed that after +deploying a small change (which added a few static files and a URL redirect), our A/B +test between versions of the site started to act weird. Several of the pages +that had been implemented in the new design would briefly display the old UI, +then flash abruptly to the new version. + +This issue was easily reproducible, and from our analytics it appeared +to be happening to **all** visitors assigned to the old UI. The weirdest part +though was that none of those affected pages were at all related to the changes +we had eployed! + +At first I didn't really know what was going on, so I started poking around +in the sources for the various pages that manifested this issue. I didn't +expect there to be anything there, particularly since none of them had been +changed in the bad deploy, but it was a good place to start. + +It's important to note here that our A/B testing for this application was +done at least in part at the HTTP routing level. Multiple different Next.js page +paths were being remapped by our reverse proxy to the same external URL, and +so from the beginning I knew something was rather weird here, as the old design +wasn't even importing any of the components of the new design. Just from that +we knew this couldn't be a component-level issue, and this had something to +do with Next's page routing. + +## Weird behavior in webpack + +After quickly double checking there was nothing obvious in the pages themselves, +I opened up the browser debugger and checked to see +which component files were being loaded. I thought perhaps there +could be an issue where the static cached version of the page was the old design +but then on rendering in the client it would reset to the new one. + +While I was there, I happened to see something interesting: both versions of the page +were having their JS bundles loaded, and both still had sourcemaps cached in the browser developer tools. This seemed +weird to me. If the browser were (for some reason) choosing to reload the tab, it would +have unloaded all of the old layout's resources. Seeing both loaded at once +indicated that Next was doing a *client side* navigation from one to the other. + +So after noting down that I needed to remind the team to disable sourcemaps in production, I started +digging into the debugger and dropping breakpoints. + +## Debugging + +I added a breakpoint to Next's `router.push()` and `router.replace()` functions, +just to see if they were being called at all. + +As luck would have it, I was right - the `replace()` function *was* being called +immediately after the page's first render. +Firefox's debugger even helpfully had a call stack for me, with where it was being called from! + +As I clicked through the call stack I found there was a check somewhere in one +of Next's internal components - as part of its `componentDidMount()` function +there were a few conditions where it would force-reload the page. + +Because of how it's built, Next's sourcemaps were incomplete in the view I had, +but after comparing some of the constants in the file I was able to track it down +to the file [next/client/index.js](https://github.com/vercel/next.js/blob/v11.1.0/packages/next/client/index.tsx#L181) +in the Next.js source (line 181). + +This section definitely looked like it could be causing the issue - the way Next +was figuring out what URL to pass to `router.replace()` was relying on the URL +in the browser's location history, which means that it would entirely bypass +our reverse proxy A/B routing. Unfortunately I didn't yet know why this was +being called, or if it was the only reason the page was being reinitialized. + +I spent a while walking through the conditions in this line one by one, using +my debugger to check the value of each variable in the faulty production pages, +and work out what exactly was happening. It skips the checks for 404 and error pages, +since that's not the kind of page we were on, `isFallback` is false so it moves +on to the other conditions combined with an `||` there, `data.nextExport` is +undefined (as we don't use static export for our site), but `hydrateProps` and +`hydrateProps.__N_SSG` were both set! + +So because `hydrateProps` and `hydrateProps.__N_SSG` both exist, it reloads the +page if either of the following conditions are true: + + - The page has a query string (`location.search`) + - The environment variable `__NEXT_HAS_REWRITES` is set + +Obviously the pages we first observed the bug on didn't have query strings, but +the rewrite condition had been optimized by webpack to just `true` so that seemed promising. With a bit more searching I tracked that +environment variable to Next's [webpack config](https://github.com/vercel/next.js/blob/v11.1.0/packages/next/build/webpack-config.ts), +which seems to set the variable if Next's config file has a rewrite property configured. + +## Confirming the hunch + +At this point it seemed I had found the issue, but I wanted to confirm it without +any doubt. So I reverted my local repo to the last commit we had deployed before +seeing the issue, and quickly navigated back to that same line in the generated +app bundle. This time the `process.env.__NEXT_HAS_REWRITES` condition had been +entirely removed, as webpack saw that environment variable was `false` for the build +and could never affect the condition. + +Lastly I spun up my full local environment, reverse proxy and all, just to confirm +that in as close to a production-like environment as I could. I removed the rewrite +rule from the Next.js application and made an equivalent redirect in our reverse +proxy - just to make sure I could preserve the intended application behavior without +the bug. + +And sure enough - with the redirect behavior moved out of Next.js and into the reverse +proxy, the bug entirely disappeared! So I reverted and restored my changes a few times +just to double check, and then alerted the other senior developers I had a solution. + diff --git a/public/images/chromebook-developer-mode-warning.png b/public/images/chromebook-developer-mode-warning.png new file mode 100644 index 0000000..c18cc59 Binary files /dev/null and b/public/images/chromebook-developer-mode-warning.png differ diff --git a/utils/post.ts b/utils/post.ts index 86c992f..14a7074 100644 --- a/utils/post.ts +++ b/utils/post.ts @@ -44,7 +44,7 @@ export async function loadSinglePage(slug: string): Promise { const fileContents = (await fs.readFile(path.join(process.cwd(), 'posts', fileName))).toString('utf8') const { attributes, body } = frontmatter(fileContents) - type Attributes = { [key: string]: string } + type Attributes = { [key: string]: any } const data: Attributes = attributes as Attributes if (!data.title) return null; @@ -54,7 +54,7 @@ export async function loadSinglePage(slug: string): Promise { date: new Date(postMatch.groups.date), title: data.title, subtitle: data.subtitle, - unlisted: data.unlisted === 'true', + unlisted: typeof data.unlisted === 'boolean' ? data.unlisted : false, body } }