diff --git a/.gitignore b/.gitignore index 3249896..3a0ba6a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ node_modules !.env.example vite.config.js.timestamp-* vite.config.ts.timestamp-* -**/_test.* \ No newline at end of file +**/_test.* +/scratch \ No newline at end of file diff --git a/src/lib/Sidenote.svelte b/src/lib/Sidenote.svelte index 2048d32..1021eab 100644 --- a/src/lib/Sidenote.svelte +++ b/src/lib/Sidenote.svelte @@ -8,31 +8,36 @@ counter-reset: sidenote; } - .counter { - counter-increment: sidenote; + .counter.anchor { color: #444; margin-left: 0.065rem; - - &::after { - font-size: 0.75em; - position: relative; - bottom: 0.375rem; - color: var(--accent-color); - content: counter(sidenote); - } + font-size: 0.75em; + position: relative; + bottom: 0.375rem; + color: var(--accent-color); @media(max-width: $sidenote-breakpoint) { - &::after { - content: "[" counter(sidenote) "]"; - } - - &:hover::after { + &:hover { color: var(--content-color); cursor: pointer; } + + // only top-level anchors get brackets + &:not(.nested)::before { + content: '['; + } + &:not(.nested)::after { + content: ']'; + } } } + .counter.floating { + position: absolute; + transform: translateX(calc(-100% - 0.4em)); + color: var(--accent-color); + } + // hidden checkbox that tracks the state of the mobile sidenote .sidenote-toggle { display: none; @@ -50,7 +55,7 @@ @media(min-width: $sidenote-breakpoint) { // max sidenote width is 20rem, if the window is too small then it's // the width of the gutter, minus the gap between sidenote and gutter, - // minus an extra 1.5rem to account for the scrollbar on the right + // minus an extra 1.5rem to account for the scrollbar on the right --gap: 2.5rem; --gutter-width: calc(50vw - var(--content-width) / 2); --sidenote-width: min( @@ -64,7 +69,7 @@ margin-bottom: 0.75rem; } - @media(max-width: $sidenote-breakpoint) { + @media(max-width: $sidenote-breakpoint) { position: fixed; left: 0; right: 0; @@ -88,6 +93,9 @@ transform: translateY(0); // when moving hidden -> shown, ease-out transition-timing-function: ease-out; + // the active sidenote should be on top of any other sidenotes as well + // (this isn't critical unless you have JS disabled, but it's still annoying) + z-index: 20; } } } @@ -96,11 +104,11 @@ max-width: var(--content-width); margin: 0 auto; - &::before { - position: absolute; - transform: translateX(calc(-100% - 0.4em)); - content: counter(sidenote); - color: var(--accent-color); + + &.nested { + margin-right: 0; + margin-top: 0.75rem; + margin-bottom: 0; } } @@ -137,65 +145,75 @@ } // nesting still needs work - @media(min-width: $sidenote-breakpoint) { + /* @media(min-width: $sidenote-breakpoint) { .nested.sidenote { margin-right: 0; margin-top: 0.7rem; margin-bottom: 0; } - } + } */ - - + + -
+
-
+
+ {count} -
-
\ No newline at end of file + +
diff --git a/src/plugins/rehype.js b/src/plugins/rehype.js index 2c9f6d3..acb1329 100644 --- a/src/plugins/rehype.js +++ b/src/plugins/rehype.js @@ -12,6 +12,7 @@ export function localRehype() { const needsDropcap = vfile.data.fm.dropcap !== false let dropcapAdded = false; + let sidenotesCount = 0; let moduleScript; let imports = new Set(); if (needsDropcap) { @@ -35,7 +36,13 @@ export function localRehype() { if (needsDropcap && !dropcapAdded && isParagraph(node)) { addDropcap(node); dropcapAdded = true; - return SKIP; + } + + // add `count` prop to each component + if (isSidenote(node)) { + // increment the counter first so that the count starts at 1 + sidenotesCount += 1; + addSidenoteCount(node, sidenotesCount); } }); @@ -52,6 +59,9 @@ export function localRehype() { moduleScript.value = `${openingTag}\n\t${importScript}${remainder}`; } + + // const name = vfile.filename.split('/').findLast(() => true); + // writeFileSync(`scratch/${name}.json`, JSON.stringify(tree, undefined, 4)); } } @@ -78,6 +88,17 @@ function addDropcap(par) { } +function addSidenoteCount(node, count) { + // get the index of the closing > + const i = node.value.search(/>\s*$/); + if (i < 0) { + throw new Error('Failed to add counter to element, closing angle bracket not found.'); + } + // splice in the count prop + node.value = `${node.value.slice(0, i)} count={${count}}>`; +} + + function isHeading(node) { return node.type === 'element' && node.tagName.match(/h[1-6]/); } @@ -89,3 +110,7 @@ function isModuleScript(node) { function isParagraph(node) { return node.type === 'element' && node.tagName === 'p'; } + +function isSidenote(node) { + return node.type === 'raw' && node.value.match(/<\s*Sidenote/); +} diff --git a/src/routes/_posts/ssh-key-formats.svx b/src/routes/_posts/ssh-key-formats.svx index 50c9645..a4becec 100644 --- a/src/routes/_posts/ssh-key-formats.svx +++ b/src/routes/_posts/ssh-key-formats.svx @@ -7,13 +7,13 @@ date: 2024-07-06 import Sidenote from '$lib/Sidenote.svelte'; -Like a lot of people, my main experience with private keys has come from using them for SSH. I'm familiar with the theory, of course - I know generally what asymmetric encryption does,Although exactly _how_ it does so is still a complete mystery to me. I've looked up descriptions of RSA several times, and even tried to work my way through a toy example, but it's never helped. And I couldn't even _begin_ to explain elliptic curve cryptography beyond "black math magic". and I know that it means a compromised server can't reveal your private key, which is nice although if you only ever use a given private key to SSH into your server and the server is already compromised, is that really so helpful?Yes, yes, I know that it means you can use the same private key for _multiple_ things without having to worry, but in practice a lot of people seem to use separate private keys for separate things, and even though I'm not entirely sure why I feel uncomfortable doing otherwise. +Like a lot of people, my main experience with private keys has come from using them for SSH. I'm familiar with the theory, of course - I know generally what asymmetric encryption does,Although exactly _how_ it does so is still a complete mystery to me. I've looked up descriptions of RSA several times,Testing nested notes again. and even tried to work my way through a toy example, but it's never helped. And I couldn't even _begin_ to explain elliptic curve cryptography beyond "black math magic". and I know that it means a compromised server can't reveal your private key, which is nice although if you only ever use a given private key to SSH into your server and the server is already compromised, is that really so helpful?Yes, yes, I know that it means you can use the same private key for _multiple_ things without having to worry, but in practice a lot of people seem to use separate private keys for separate things, and even though I'm not entirely sure why I feel uncomfortable doing otherwise. What I was less aware of, however, was the various ways in which private keys can be _stored_, which rather suddenly became a more-than-purely-academic concern to me this past week. I had an old private key lying around which had originally been generated by AWS, and used a rather old format,The oldest, I believe, that's in widespread use still. and I needed it to be comprehensible by newer software which loftily refused to have anything to do with such outdated ways of expressing itself.Who would write such obdurately high-handed software, you ask? Well, uh. Me, as it turns out. In my defense, though, I doubt it would have taken _less_ time to switch to a different SSH-key library than to figure out the particular magic incantation needed to get `ssh-keygen` to do it. No problem, thought I, I'll just use `ssh-keygen` to convert the old format to a newer format! Unfortunately this was frustratinglyAnd needlessly, it seems to me? difficult to figure out, so I'm writing it up here for posterity and so that I never have to look it up again.You know how it works. Once you've taken the time to really describe process in detail, you have it locked in and never have to refer back to your notes. ## Preamble: Fantastic Formats and Where to Find Them -If you're like me, you're probably aware that private keys are usually delivered as big blobs of Base64-encoded text prefaced by headers like `-----BEGIN OPENSSH PRIVATE KEY----`, and for some reason never use file extensions.Well, for the ones generated by `ssh-keygen`, at least. OpenSSL-generated ones often use `.key` or `.pem`, but those aren't typically used for SSH, so are less relevant here. There are three common formats you're likely to encounter in the wild: +I was aware, of course, that private keys are usually delivered as files containing big blobs of Base64-encoded text, prefaced by headers like `-----BEGIN OPENSSH PRIVATE KEY----`, and for whatever reason lacking file extensions.Well, for the ones generated by `ssh-keygen`, at least. OpenSSL-generated ones often use `.key` or `.pem`, but those aren't typically used for SSH, so are less relevant here. What I wasn't aware of is that there are actually several _different_ such formats, which although they look quite similar from the outside are internally pretty different. There are three you're likely to encounter in the wild: 1. OpenSSH-formatted keys, which start with `BEGIN OPENSSH KEY`Plus the leading and trailing five dashes, but I'm tired of typing those out. and are the preferred way of formatting private keys for use with SSH, 2. PCKS#8-formatted keys, which start with `BEGIN PRIVATE KEY` or `BEGIN ENCRYPTED PRIVATE KEY`, and @@ -37,7 +37,7 @@ Both PKCS#1 and PKCS#8 use the same method for encoding the actual key data (whi ## The `ssh-keygen` Manpage is a Tapestry of Lies -So, I thought, I can use `ssh-keygen` to convert between these various and sundry formats, right? It can do that, it _has_ to be able to do that, right? +So, I thought, I can use `ssh-keygen` to convert between these various and sundry formats, right? It can do that, it _has_ to be able to do that, right? Well, yes. It _can_, but good luck figuring out _how_. For starters, like many older CLI tools, `ssh-keygen` has an awful lot of flags and options, and it's hard to distinguish between which are _modifiers_ - "do the same thing, but differently" - and _modes of operation_ - "do a different thing entirely". The modern way to handle this distinction is with subcommands which take entirely different sets of arguments, but `ssh-keygen` dates back to a time before that was common. @@ -75,7 +75,7 @@ Most probably this is just a case of a tool evolving organically over time rathe ## Imagining a Brighter Tomorrow -But it doesn't have to be this way! Nothing (that I can see) prevents the `-i` option from being updated to accept private keys as well as public keys: it's clearly perfectly capable of telling when a file _isn't_ a valid _public_ key in the specified format, so it it seems like it could just parse it as a private key instead, and keep going if successful. Or an entirely new option could be added for converting private keys. `-c` is already taken for changing comments, but there are a few letters remaining. I don't see a `-j` on the manpage, for instance, or an `-x`. +But it doesn't have to be this way! Nothing (that I can see) prevents the `-i` option from being updated to accept private keys as well as public keys: it's clearly perfectly capable of telling when a file _isn't_ a valid _public_ key in the specified format, so it it seems like it could just parse it as a private key instead, and keep going if successful. Or an entirely new option could be added for converting private keys. `-c` is already taken for changing comments, but there are a few letters remaining. I don't see a `-j` on the manpage, for instance, or an `-x`. I realize that an unforgiving reading of my travails in this endeavour might yield the conclusion that I'm an idiot with no reading comprehension, and that the manpage _clearly_ stated the solution to my problem _all along_, and if I had just RTFMNoob. then I could have avoided all this frustration, but that seems [a little unfair](https://xkcd.com/293/) to me.Besides, I'm annoyed, and it's more satisfying to blame others than admit any fault of my own. When you're writing the help message or manpage for your tool, you should _expect_ that people will be skimming it, looking to pick out the tidbits that are important to them right now, since for any tool of reasonable complexity 95% of the documentation is going to be irrelevant to any single user in any single situation.