navigation maps for navigating across pages
This commit is contained in:
		
							parent
							
								
									28a39fc883
								
							
						
					
					
						commit
						09ff8a742e
					
				
					 15 changed files with 382 additions and 71 deletions
				
			
		
							
								
								
									
										1
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -1231,6 +1231,7 @@ dependencies = [
 | 
			
		|||
 "pulldown-cmark",
 | 
			
		||||
 "rand",
 | 
			
		||||
 "serde",
 | 
			
		||||
 "serde_json",
 | 
			
		||||
 "tokio",
 | 
			
		||||
 "toml_edit",
 | 
			
		||||
 "tower-http",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										47
									
								
								README.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								README.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,47 @@
 | 
			
		|||
# liquidex's treehouse
 | 
			
		||||
 | 
			
		||||
Welcome to the Construct.
 | 
			
		||||
 | 
			
		||||
If you haven't seen the treehouse yet, you [may wanna](https://liquidex.house). It's pretty darn cool.
 | 
			
		||||
 | 
			
		||||
Please note that this repository contains spoilers. So if you like exploring by yourself, you may wanna do that first before diving into the source code.
 | 
			
		||||
 | 
			
		||||
*Spoilers for what?*, you might ask.
 | 
			
		||||
 | 
			
		||||
…
 | 
			
		||||
 | 
			
		||||
You have been warned.
 | 
			
		||||
 | 
			
		||||
## Building
 | 
			
		||||
 | 
			
		||||
To build the website:
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
cargo run -p treehouse regenerate
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This will spit out a directory `target/site` containing the static pages. You're free to use any HTTP server you wish, but for development purposes treehouse includes one in the CLI:
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
cargo run -p treehouse regenerate --serve
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This will fire up a server on port 8080. No way to change that, sorry. Edit the source code.
 | 
			
		||||
 | 
			
		||||
If you're developing, you may wanna use [`cargo-watch`](https://crates.io/crates/cargo-watch):
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
cargo watch -- cargo run -p treehouse regenerate --serve
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The website will reload itself automatically if you change any file in the repository.
 | 
			
		||||
 | 
			
		||||
## Contributing
 | 
			
		||||
 | 
			
		||||
If you found a typo, be my guest. Just note that some typos are intentional, please make sure you understand the full context of the sentence.
 | 
			
		||||
 | 
			
		||||
If you found a bug, by all means please submit a fix. (or at least an issue.)
 | 
			
		||||
 | 
			
		||||
Since this is my personal website, I don't accept outside contributions for new content. Because then it would no longer be *my* treehouse.
 | 
			
		||||
 | 
			
		||||
If you wish to create something similar to liquidex's treehouse, you probably want to use more mature software instead of my scrappy, opinionated piece of art. Check out [Logseq](https://logseq.com/) - it has static site generation built in, with a much more approachable UI, cross-device sync, and great customizability through community-made themes and plugins.
 | 
			
		||||
| 
						 | 
				
			
			@ -1 +1,2 @@
 | 
			
		|||
% id = "01H8V55MGHGYXWY8F287FMNXNY"
 | 
			
		||||
- section under construction. sorry! in the meantime, maybe you wanna read [my ramblings about the treehouse][branch:01H89RFHCQCD3E1XS5XAPW86J5]?
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -111,7 +111,7 @@
 | 
			
		|||
                - but you may not find the experience favorable
 | 
			
		||||
 | 
			
		||||
    % id = "01H89RFHCQ7HTZSP6P2RZR8JHE"
 | 
			
		||||
    + but most importantly of all, it is *weird*.
 | 
			
		||||
    + but most important of all, it is *weird*.
 | 
			
		||||
 | 
			
		||||
        % id = "01H89RFHCQTRVPZ0AJ0DGJHXKX"
 | 
			
		||||
        - weird as me
 | 
			
		||||
| 
						 | 
				
			
			@ -131,11 +131,13 @@
 | 
			
		|||
    % id = "01H89RFHCQ2GWJPTAKTRGS1QAC"
 | 
			
		||||
    - weird poems and philosophical talk are over, it's time to focus on the tech.
 | 
			
		||||
 | 
			
		||||
        % id = "01H8V55APDEF8WTQ3KFC7E9HWB"
 | 
			
		||||
        - call this an overview, Defense of Design, or what have you
 | 
			
		||||
 | 
			
		||||
    % id = "01H89RFHCQF4N9T05B9DVWX67K"
 | 
			
		||||
    - treehouse is built in the programming language that gives me the most pleasure coding.
 | 
			
		||||
 | 
			
		||||
        % id = "01H8V55APDXH5N1YC2WXDBCDN4"
 | 
			
		||||
        - no need for you to know more. :shhh:
 | 
			
		||||
 | 
			
		||||
        % id = "01H89RFHCQRA4BBBWDC8K68BB0"
 | 
			
		||||
| 
						 | 
				
			
			@ -145,60 +147,80 @@
 | 
			
		|||
        % id = "01H89RFHCQA32MCR4MDCANDNC7"
 | 
			
		||||
        - but being the altruist I am - don't worry, it _will_ be open source one day.
 | 
			
		||||
 | 
			
		||||
            % id = "01H8V55APDHC0DZNF67DS76P8H"
 | 
			
		||||
            - in case you're reading this in the far future, and this is still here…
 | 
			
		||||
              you wouldn't mind [dropping me a line][branch:01H89P3CH8CD28KGX9GVRFK60E] would you?
 | 
			
		||||
 | 
			
		||||
    % id = "01H89RFHCQAQVXP6B2H0T8NNDS"
 | 
			
		||||
    - personally… the language you build a personal project with almost never matters. it's rather how you execute your ideas.
 | 
			
		||||
 | 
			
		||||
        % id = "01H8V55APDRZHHG69A6BWQM8YE"
 | 
			
		||||
        + therefore I find boasting that my project is powered by a `$LANGUAGE` or a `$FRAMEWORK` unnecessary.
 | 
			
		||||
 | 
			
		||||
            % id = "01H8V55APDNJ4W3MY1A204PHJ3"
 | 
			
		||||
            + but if you really care that much, it's built with Rust, plain HTML5, plain CSS, and
 | 
			
		||||
              [Vanilla JS](http://vanilla-js.com/).
 | 
			
		||||
 | 
			
		||||
                % id = "01H8V55APDYHZG2QXQ78HZE97V"
 | 
			
		||||
                - (yes, I know that website is super old, but I still find it incredibly funny :hueh:)
 | 
			
		||||
 | 
			
		||||
    % id = "01H89RFHCQFWC2FWBAE9PVNC08"
 | 
			
		||||
    - as I alluded to [here][branch:01H89RFHCQ3EAP0F6PRSEK7S1T], treehouse is built to decay gracefully
 | 
			
		||||
      as you take away the fancy parts.
 | 
			
		||||
 | 
			
		||||
        % id = "01H8V55APDMPF3WFTNTFSYBQRF"
 | 
			
		||||
        - you will be able to read it just fine without JavaScript, just that it'll be a little
 | 
			
		||||
          less pleasant.
 | 
			
		||||
 | 
			
		||||
            % id = "01H8V55APD1RSSRMDZ3CEE9S9C"
 | 
			
		||||
            - that's because the JS handles stuff like lazy loading of linked trees.
 | 
			
		||||
 | 
			
		||||
                % id = "01H8V55APDAPSR7R9M569GW4S7"
 | 
			
		||||
                - you may have seen a brief _"Loading…"_ text flash before your eyes as you opened this
 | 
			
		||||
                _about this_ branch - that's it downloading another tree and pasting it into this document
 | 
			
		||||
 | 
			
		||||
                    % id = "01H8V55APDS9BNC2TQ5S5VYC0G"
 | 
			
		||||
                    - when JS is unavailable we fall back to a plain old `<a>` link through a
 | 
			
		||||
                    `<noscript>` tag. if you have uBlock Origin, or any other JS blocker, you can try
 | 
			
		||||
                    that out yourself!
 | 
			
		||||
 | 
			
		||||
            % id = "01H8V55APD503DMXJD9JA2WJGS"
 | 
			
		||||
            - the JS also lets you select text in these `<details>` elements without them collapsing
 | 
			
		||||
              as you release the mouse button.
 | 
			
		||||
 | 
			
		||||
                % id = "01H8V55APD34KE6ED6MRTW34H6"
 | 
			
		||||
                - I have no clue why it is this way by default, but frankly it's like a 5 line
 | 
			
		||||
                  usability fix, so why not.
 | 
			
		||||
 | 
			
		||||
                    % id = "01H8V55APDR3VCEANQ6XVJP3X8"
 | 
			
		||||
                    - if you have accessibility concerns about this decision, please let me know.
 | 
			
		||||
 | 
			
		||||
            % id = "01H8V55APDFW5SPAKSQ04K5HRE"
 | 
			
		||||
            - it also saves your progress as you read. if you refresh the page, you'll notice you end up exactly where you left off!
 | 
			
		||||
 | 
			
		||||
            % id = "01H8V55APDNSXZMZJ2K1KW11KN"
 | 
			
		||||
            - but, there is one very crucial piece of JavaScript that makes this website tick, and your experience **will be degraded** if you disable it. that feature is linking to branches.
 | 
			
		||||
 | 
			
		||||
                % id = "01H8V55APDBG6AH2W3RNMX724X"
 | 
			
		||||
                - by default, if you link to an element by its id and it's contained within a `<details>`, the `<details>` will not expand. :ralsei_dead:
 | 
			
		||||
 | 
			
		||||
                % id = "01H8V55APDE0KSKKAS8RBRPYRM"
 | 
			
		||||
                - therefore there's a bit of JS to make that work, _and_ to tie that together with lazy loading.
 | 
			
		||||
 | 
			
		||||
        % id = "01H8V55APDTZSSYTY17SYZ6DGQ"
 | 
			
		||||
        - treehouse will not work *as* fine without CSS though - the `<details>` will look extremely
 | 
			
		||||
          janky, but the content should still be fully readable.
 | 
			
		||||
 | 
			
		||||
    % id = "01H8V55APDWN8TV31K4SXBTTWB"
 | 
			
		||||
    - myself, I'm writing this content in a bespoke format called `.tree`.
 | 
			
		||||
 | 
			
		||||
        % id = "01H8V55APDGX4EYV8W7ECXZ6FP"
 | 
			
		||||
        - the structure of `.tree` files is extremely minimal. there are only a few syntactic features to speak of.
 | 
			
		||||
 | 
			
		||||
        % id = "01H8V55APDR95HSV0TX7TK63AF"
 | 
			
		||||
        - here's a taste of `.tree`:
 | 
			
		||||
 | 
			
		||||
            % id = "01H8V55APD5686J8GTXP118V8E"
 | 
			
		||||
            - ```
 | 
			
		||||
            \% id = "root"
 | 
			
		||||
            \- this is a branch
 | 
			
		||||
| 
						 | 
				
			
			@ -214,95 +236,141 @@
 | 
			
		|||
                \- and this branch links to another tree
 | 
			
		||||
            ```
 | 
			
		||||
 | 
			
		||||
        % id = "01H8V55APDB49SPHPPMV2BCMW3"
 | 
			
		||||
        - the `.tree` format is line-based. that means the `%`, `-`, and `+` tokens are only
 | 
			
		||||
          interpreted when at the beginning of a line.
 | 
			
		||||
 | 
			
		||||
        % id = "01H8V55APD67W8FHEZK6E2A21K"
 | 
			
		||||
        - the basic unit of `.tree` is a branch. branches…
 | 
			
		||||
 | 
			
		||||
            % id = "01H8V55APDQ1KS7S313GK590GM"
 | 
			
		||||
            - …are located at a specific _indent level_
 | 
			
		||||
 | 
			
		||||
            % id = "01H8V55APDHNRFCGH9STD46J0F"
 | 
			
		||||
            - …may or may not have _attributes_ - that's what the percentage sign `%` begins
 | 
			
		||||
 | 
			
		||||
            % id = "01H8V55APDF9C7Y0QDYF3EBMMS"
 | 
			
		||||
            - …may or may not be expanded by default (this is the branch's _kind_) - that's what the minus `-` and plus `+` tokens do
 | 
			
		||||
 | 
			
		||||
        % id = "01H8V55APDJ6EYE4PR370JAT4W"
 | 
			
		||||
        - each branch is constructed in this order: optional attributes, kind, content
 | 
			
		||||
 | 
			
		||||
        % id = "01H8V55APDGT47G5DKZKTQ1S80"
 | 
			
		||||
        - and ends when another line beginning with `%`, `-`, or `+` is found.
 | 
			
		||||
 | 
			
		||||
        % id = "01H8V55APDQ3VYBBTTMNFTVNV4"
 | 
			
		||||
        - other than that, `.tree` assumes nothing about what format the branch attributes or content are encoded in.
 | 
			
		||||
 | 
			
		||||
            % id = "01H8V55APDCXPW8JPRYZHY9NXV"
 | 
			
		||||
            - I chose TOML and Markdown for their ease of use and flexibility, but the parser couldn't care less.
 | 
			
		||||
 | 
			
		||||
                % id = "01H8V55APDWDPSHNZX6QJFM8ZA"
 | 
			
		||||
                - …actually, that's a lie. see that code example above? Markdown code fences \`\`\` are handled specially to let embed `.tree` source code blocks within `.tree` files. that's all.
 | 
			
		||||
 | 
			
		||||
    % id = "01H8V55APDVQB6AD23Y6PZPPB8"
 | 
			
		||||
    - you may have noticed in that code example above that almost every branch has an `id` attribute.
 | 
			
		||||
 | 
			
		||||
        % id = "01H8V55APD4H5JT8NTYCCTN77G"
 | 
			
		||||
        - this is because I want every branch to be individually linkable and quotable.
 | 
			
		||||
 | 
			
		||||
            % id = "01H8V55APDPSZNP9AK7QRK62BG"
 | 
			
		||||
            - if you ever want to link anything I said in an argument online: hover over a branch, right-click that little _permalink_ icon that's on the right side of the screen, and _Copy Link_.
 | 
			
		||||
 | 
			
		||||
        % id = "01H8V55APDCZ0HY20GGXYE8D1G"
 | 
			
		||||
        - but manually coming up with ids for branches would be quite the nuisance.
 | 
			
		||||
 | 
			
		||||
        % id = "01H8V55APDXV0KRVEF9PTA72J7"
 | 
			
		||||
        - that's why with the power of Tools Programming™, I built a tool that fills out all the ids for me.
 | 
			
		||||
 | 
			
		||||
            % id = "01H8V55APD8VG7WE6JX5JBDJEC"
 | 
			
		||||
            - I chose [ulids](https://github.com/ulid/spec) for generated ids, for a few reasons:
 | 
			
		||||
 | 
			
		||||
                % id = "01H8V55APD2J58ENT1T0JRJ7H6"
 | 
			
		||||
                - they're guaranteed to be unique, so I can be sure no two branches end up having the same id
 | 
			
		||||
 | 
			
		||||
                    % id = "01H8V55APDGV6PKJKZ16CJ1YXY"
 | 
			
		||||
                    - which is precisely what lets me pull the trick where I embed one tree within another, and you just dig into `index.html`.
 | 
			
		||||
 | 
			
		||||
                % id = "01H8V55APD1CYZD8GE3AK87QK2"
 | 
			
		||||
                - they embed a timestamp, which I can use to display the date/time when a branch was written, because I generally `fix` branches right after writing
 | 
			
		||||
 | 
			
		||||
                    % id = "01H8V55APDFXW5A5J85HJQP17C"
 | 
			
		||||
                    - as well as highlight branches which were added since your last visit
 | 
			
		||||
 | 
			
		||||
                    % id = "01H8V55APDPZK4C8H5TTHTHH7J"
 | 
			
		||||
                    - not that either of these features are implemented as of writing this (2023-08-20)
 | 
			
		||||
 | 
			
		||||
                % id = "01H8V55APDXB9AXTAGAE9TNQRB"
 | 
			
		||||
                - they're much more compact than [uuids](https://en.wikipedia.org/wiki/Uuid)
 | 
			
		||||
 | 
			
		||||
                    % id = "01H8V55APDZE5AE92GDM2TH2EF"
 | 
			
		||||
                    - which use 32 hexadecimal digits separated unevenly by 4 dashes, giving you 36 characters.
 | 
			
		||||
 | 
			
		||||
                        % id = "01H8V55APDS478E3ED5CRVT6RE"
 | 
			
		||||
                        - who even remembers how many digits there are between each dash? I know I don't.
 | 
			
		||||
 | 
			
		||||
                    % id = "01H8V55APDXP1D0MHGN2DXBT16"
 | 
			
		||||
                    - ulids on the other hand are 26 digits encoded with [Crockford's base32](https://www.crockford.com/base32.html)
 | 
			
		||||
 | 
			
		||||
                        % id = "01H8V55APD79QTC2NFC6X7S3W4"
 | 
			
		||||
                        - which is cool because it's much denser while avoiding ambiguous characters - `0`, `O`, and `o` are all interpreted as `0` (zero).
 | 
			
		||||
 | 
			
		||||
    % id = "01H8V55APDXT064G9PA78JW0CM"
 | 
			
		||||
    - noticed how fast the treehouse restores your state? there's basically no delay.
 | 
			
		||||
 | 
			
		||||
        % id = "01H8V55APDGS626M5Y6DDAS9ZE"
 | 
			
		||||
        - this is because it restores your state _as it's loading in_, by using [Web Components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements).
 | 
			
		||||
 | 
			
		||||
            % id = "01H8V55APD73AG65NP2VGX6169"
 | 
			
		||||
            - despite many people calling that API extremely low-level, I beg to differ. it's actually pretty easy and pleasant to use.
 | 
			
		||||
 | 
			
		||||
            % id = "01H8V55APDDHYNRZXQDEG0A242"
 | 
			
		||||
            - most importantly it lets me enhance vanilla `<li>` elements with custom behavior executed on load, which I use to restore your reading progress as the page is loading in.
 | 
			
		||||
 | 
			
		||||
        % id = "01H8V55APD4V54M2EKQKQ4GNP8"
 | 
			
		||||
        - linked branches also use Web Components by the way.
 | 
			
		||||
 | 
			
		||||
    % id = "01H8V55APDKY0HYEJM2C1CJHEG"
 | 
			
		||||
    - while not strictly a technical topic, I'd like to shout out [Recursive](https://recursive.design) for being an awesome font :ralsei_love:
 | 
			
		||||
 | 
			
		||||
        % id = "01H8V55APD88JCQBQ4SHVCYR97"
 | 
			
		||||
        - (not to be confused with Font Awesome, which I do not use here. icon designs are my own.)
 | 
			
		||||
 | 
			
		||||
        % id = "01H8V55APD0S00ACN0YY7Y9MSW"
 | 
			
		||||
        - being a variable font, I can tweak the text's look and feel on many different axes.
 | 
			
		||||
 | 
			
		||||
        % id = "01H8V55APDRG88JRV74MMDTV4J"
 | 
			
		||||
        - the one that I wanted to shout out in particular is `CASL`, which lets me make it look a lot more happy and playful.
 | 
			
		||||
 | 
			
		||||
            % id = "01H8V55APDMP5FE9ZCMRACH18W"
 | 
			
		||||
            + almost like Comic Sans, but without all the stigma.
 | 
			
		||||
 | 
			
		||||
                % id = "01H8V55APDPGMD33J4M3SJ2QKP"
 | 
			
		||||
                - I actually considered using [Comic Neue](https://comicneue.com/) as the font on this website, but ultimately chose Recursive.
 | 
			
		||||
 | 
			
		||||
                    % id = "01H8V55APDXBS7HMDSCEJ1JDPY"
 | 
			
		||||
                    - Comic Neue and not the Real™ Comic Sans MS because it's licensed under the SIL Open Font License, which means I could redistribute it on my website.
 | 
			
		||||
 | 
			
		||||
                        % id = "01H8V55APDFT0Y4ECEM92XFTTR"
 | 
			
		||||
                        - not everyone runs Windows or macOS, so just assuming the reader has Comic Sans installed isn't ideal.
 | 
			
		||||
 | 
			
		||||
            % id = "01H8V55APD2BPGQXEK7JARYJ9H"
 | 
			
		||||
            - in particular the variant on my website is 100% casual (`CASL`), and -2.0 slanted (`slnt`).
 | 
			
		||||
 | 
			
		||||
                % id = "01H8V55APDXZJE3HHH5AQ8ZQHF"
 | 
			
		||||
                + that little bit of slant makes it look just a little more like handwriting.
 | 
			
		||||
 | 
			
		||||
                    % id = "01H8V55APDGSB2Y742S07DHTAZ"
 | 
			
		||||
                    - I actually thought of handwriting all the text in the treehouse, but ultimately thought it would be too much work. and a technical challenge too.
 | 
			
		||||
 | 
			
		||||
                        % id = "01H8V55APDDXH02F91RM19RDHX"
 | 
			
		||||
                        - how to make it scalable (as in, how to keep it smooth when you zoom in?)
 | 
			
		||||
 | 
			
		||||
                        % id = "01H8V55APDQ1N44QASCHVDB4D5"
 | 
			
		||||
                        - how to make it scalable (as in, my hand really fucking hurts from writing this much, and I *really* do not wanna go back to the times of writing school essays please?)
 | 
			
		||||
 | 
			
		||||
                        % id = "01H8V55APDP5T3EYPVMVN9QVK7"
 | 
			
		||||
                        - how to make it scalable (as in, how to distribute this many images to the user without tanking their internet connection?)
 | 
			
		||||
 | 
			
		||||
                        % id = "01H8V55APDB8PC2BF5P9TVYJ35"
 | 
			
		||||
                        - not to mention I'd have to handwrite a lot of text, and *then* either use OCR or type it all out again for accessibility. yeah, no thanks methinks.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -82,7 +82,7 @@
 | 
			
		|||
    - apart from programming I also take interest in other various things
 | 
			
		||||
 | 
			
		||||
        % id = "01H89P3CH864VWM4JXQ0N5C1YV"
 | 
			
		||||
        - like [video games][page:/games]
 | 
			
		||||
        - like [video games][branch:games]
 | 
			
		||||
 | 
			
		||||
            % id = "01H89P3CH8WK8F2XKBXFVE8KNX"
 | 
			
		||||
            - yeah those are pretty great aren't they
 | 
			
		||||
| 
						 | 
				
			
			@ -97,10 +97,10 @@
 | 
			
		|||
                - [DELTARUNE](https://deltarune.com)
 | 
			
		||||
 | 
			
		||||
                % id = "01H89P3CH89TQ3SFG2Z40J29HX"
 | 
			
		||||
                - they're pretty great you should check them out (also check out [my games corner][page:games]? :pleading_face:)
 | 
			
		||||
                - they're pretty great you should check them out (also check out [my games corner][branch:games]? :pleading_face:)
 | 
			
		||||
 | 
			
		||||
        % id = "01H89P3CH8AJATQ5DJBBFXJ1NH"
 | 
			
		||||
        - or [music][page:music]
 | 
			
		||||
        - or [music][branch:music]
 | 
			
		||||
 | 
			
		||||
            % id = "01H89P3CH8XQ59YZD3RFRYQ2BM"
 | 
			
		||||
            - various genres from electronic through jazz and even rock
 | 
			
		||||
| 
						 | 
				
			
			@ -117,8 +117,10 @@
 | 
			
		|||
                    % id = "01H89P3CH8C719GX9CTK36FXXN"
 | 
			
		||||
                    + if you don't know what that means then good for you
 | 
			
		||||
 | 
			
		||||
                        % id = "01H8V55G7M7KEKBYX2D2RP9QQE"
 | 
			
		||||
                        + but if you so insist… [you have been warned](https://www.youtube.com/watch?v=BB7ExLcUuH0)
 | 
			
		||||
 | 
			
		||||
                            % id = "01H8V55G7MWC7MQ9YQVDDFZ9QV"
 | 
			
		||||
                            - (this is one of the more crazy ones anyways. he makes some [more normal stuff](https://www.youtube.com/watch?v=IuI6MMUtNdU) too if you care)
 | 
			
		||||
 | 
			
		||||
                % id = "01H89P3CH8XP6ZA1RWYVZWRXN3"
 | 
			
		||||
| 
						 | 
				
			
			@ -147,6 +149,7 @@
 | 
			
		|||
            % id = "01H89P3CH80XYPAHXH1QZJ9SMZ"
 | 
			
		||||
            + so I type here like I would type on some chat platform. but it's a tree, just like Reddit!
 | 
			
		||||
 | 
			
		||||
                % id = "01H8V55G7MS3NBPFFFKYE8YGFK"
 | 
			
		||||
                - except on chat I don't capitalize "I" so if that pisses you off you may wanna brace yourself (or just tell me, I'll do my best to adapt)
 | 
			
		||||
 | 
			
		||||
        % id = "01H89P3CH8CD28KGX9GVRFK60E"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
% id = "treehouse"
 | 
			
		||||
- # liquidex's treehouse
 | 
			
		||||
 | 
			
		||||
    % id = "01H8V556P1PND8DQ73XBTZZJH7"
 | 
			
		||||
    - welcome! make yourself at home
 | 
			
		||||
 | 
			
		||||
    ---
 | 
			
		||||
| 
						 | 
				
			
			@ -15,8 +16,10 @@
 | 
			
		|||
 | 
			
		||||
    ---
 | 
			
		||||
 | 
			
		||||
    % id = "01H8V556P1GRAA3717VH3QJFMV"
 | 
			
		||||
    - below you can find my Special Interest Corners™
 | 
			
		||||
 | 
			
		||||
        % id = "01H8V556P1KDBC9EYYSA1F44ZJ"
 | 
			
		||||
        - ie. thoughts on stuff that I wanna jot down somewhere
 | 
			
		||||
 | 
			
		||||
    % id = "programming"
 | 
			
		||||
| 
						 | 
				
			
			@ -30,3 +33,7 @@
 | 
			
		|||
    % id = "games"
 | 
			
		||||
      content.link = "2023-08-20-under-construction"
 | 
			
		||||
    + ## games
 | 
			
		||||
 | 
			
		||||
    % id = "3d-printing"
 | 
			
		||||
      content.link = "2023-08-20-under-construction"
 | 
			
		||||
    + ## 3D printing
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2
									
								
								content/secret.tree
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								content/secret.tree
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,2 @@
 | 
			
		|||
% id = "man"
 | 
			
		||||
- He is behind the tree.
 | 
			
		||||
| 
						 | 
				
			
			@ -24,5 +24,6 @@ tower-livereload = "0.8.0"
 | 
			
		|||
walkdir = "2.3.3"
 | 
			
		||||
ulid = "1.0.0"
 | 
			
		||||
rand = "0.8.5"
 | 
			
		||||
serde_json = "1.0.105"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
use std::{
 | 
			
		||||
    ffi::OsStr,
 | 
			
		||||
    path::{Path, PathBuf},
 | 
			
		||||
    time::Instant,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use anyhow::{bail, Context};
 | 
			
		||||
| 
						 | 
				
			
			@ -18,7 +19,9 @@ use tower_livereload::LiveReloadLayer;
 | 
			
		|||
use walkdir::WalkDir;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    cli::parse::parse_tree_with_diagnostics, html::tree::branches_to_html, tree::SemaRoots,
 | 
			
		||||
    cli::parse::parse_tree_with_diagnostics,
 | 
			
		||||
    html::{navmap::build_navigation_map, tree::branches_to_html},
 | 
			
		||||
    tree::SemaRoots,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::state::{FileId, Treehouse};
 | 
			
		||||
| 
						 | 
				
			
			@ -103,6 +106,13 @@ impl Generator {
 | 
			
		|||
            &dirs.template_dir.join("tree.hbs"),
 | 
			
		||||
        )?;
 | 
			
		||||
 | 
			
		||||
        struct ParsedTree {
 | 
			
		||||
            tree_path: String,
 | 
			
		||||
            file_id: FileId,
 | 
			
		||||
            target_path: PathBuf,
 | 
			
		||||
        }
 | 
			
		||||
        let mut parsed_trees = vec![];
 | 
			
		||||
 | 
			
		||||
        for path in &self.tree_files {
 | 
			
		||||
            let utf8_filename = path.to_string_lossy();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -127,42 +137,60 @@ impl Generator {
 | 
			
		|||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            let file_id = treehouse.add_file(
 | 
			
		||||
                utf8_filename.into_owned(),
 | 
			
		||||
                Some(tree_path.with_extension("").to_string_lossy().into_owned()),
 | 
			
		||||
                source,
 | 
			
		||||
            );
 | 
			
		||||
            let tree_path = tree_path.with_extension("").to_string_lossy().into_owned();
 | 
			
		||||
            let file_id =
 | 
			
		||||
                treehouse.add_file(utf8_filename.into_owned(), Some(tree_path.clone()), source);
 | 
			
		||||
 | 
			
		||||
            if let Ok(roots) = parse_tree_with_diagnostics(&mut treehouse, file_id) {
 | 
			
		||||
                let roots = SemaRoots::from_roots(&mut treehouse, file_id, roots);
 | 
			
		||||
 | 
			
		||||
                let mut tree = String::new();
 | 
			
		||||
                branches_to_html(&mut tree, &mut treehouse, file_id, &roots.branches);
 | 
			
		||||
 | 
			
		||||
                let template_data = TemplateData { tree };
 | 
			
		||||
                let templated_html = match handlebars.render("tree", &template_data) {
 | 
			
		||||
                    Ok(html) => html,
 | 
			
		||||
                    Err(error) => {
 | 
			
		||||
                        Self::wrangle_handlebars_error_into_diagnostic(
 | 
			
		||||
                            &mut treehouse,
 | 
			
		||||
                            tree_template,
 | 
			
		||||
                            error.line_no,
 | 
			
		||||
                            error.column_no,
 | 
			
		||||
                            error.desc,
 | 
			
		||||
                        )?;
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                std::fs::create_dir_all(
 | 
			
		||||
                    target_path
 | 
			
		||||
                        .parent()
 | 
			
		||||
                        .expect("there should be a parent directory to generate files into"),
 | 
			
		||||
                )?;
 | 
			
		||||
                std::fs::write(target_path, templated_html)?;
 | 
			
		||||
                treehouse.roots.insert(tree_path.clone(), roots);
 | 
			
		||||
                parsed_trees.push(ParsedTree {
 | 
			
		||||
                    tree_path,
 | 
			
		||||
                    file_id,
 | 
			
		||||
                    target_path,
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for parsed_tree in parsed_trees {
 | 
			
		||||
            let mut tree = String::new();
 | 
			
		||||
            // Temporarily steal the tree out of the treehouse.
 | 
			
		||||
            let roots = treehouse
 | 
			
		||||
                .roots
 | 
			
		||||
                .remove(&parsed_tree.tree_path)
 | 
			
		||||
                .expect("tree should have been added to the treehouse");
 | 
			
		||||
            branches_to_html(
 | 
			
		||||
                &mut tree,
 | 
			
		||||
                &mut treehouse,
 | 
			
		||||
                parsed_tree.file_id,
 | 
			
		||||
                &roots.branches,
 | 
			
		||||
            );
 | 
			
		||||
            treehouse.roots.insert(parsed_tree.tree_path, roots);
 | 
			
		||||
 | 
			
		||||
            let template_data = TemplateData { tree };
 | 
			
		||||
            let templated_html = match handlebars.render("tree", &template_data) {
 | 
			
		||||
                Ok(html) => html,
 | 
			
		||||
                Err(error) => {
 | 
			
		||||
                    Self::wrangle_handlebars_error_into_diagnostic(
 | 
			
		||||
                        &mut treehouse,
 | 
			
		||||
                        tree_template,
 | 
			
		||||
                        error.line_no,
 | 
			
		||||
                        error.column_no,
 | 
			
		||||
                        error.desc,
 | 
			
		||||
                    )?;
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            std::fs::create_dir_all(
 | 
			
		||||
                parsed_tree
 | 
			
		||||
                    .target_path
 | 
			
		||||
                    .parent()
 | 
			
		||||
                    .expect("there should be a parent directory to generate files into"),
 | 
			
		||||
            )?;
 | 
			
		||||
            std::fs::write(parsed_tree.target_path, templated_html)?;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(treehouse)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -181,6 +209,8 @@ pub struct TemplateData {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
pub fn regenerate(dirs: &Dirs<'_>) -> anyhow::Result<()> {
 | 
			
		||||
    let start = Instant::now();
 | 
			
		||||
 | 
			
		||||
    info!("cleaning target directory");
 | 
			
		||||
    let _ = std::fs::remove_dir_all(dirs.target_dir);
 | 
			
		||||
    std::fs::create_dir_all(dirs.target_dir)?;
 | 
			
		||||
| 
						 | 
				
			
			@ -193,8 +223,18 @@ pub fn regenerate(dirs: &Dirs<'_>) -> anyhow::Result<()> {
 | 
			
		|||
    generator.add_directory_rec(dirs.content_dir)?;
 | 
			
		||||
    let treehouse = generator.generate_all_files(dirs)?;
 | 
			
		||||
 | 
			
		||||
    info!("generating navigation map");
 | 
			
		||||
    let navigation_map = build_navigation_map(&treehouse, "index");
 | 
			
		||||
    std::fs::write(
 | 
			
		||||
        dirs.target_dir.join("navmap.js"),
 | 
			
		||||
        navigation_map.to_javascript(),
 | 
			
		||||
    )?;
 | 
			
		||||
 | 
			
		||||
    treehouse.report_diagnostics()?;
 | 
			
		||||
 | 
			
		||||
    let duration = start.elapsed();
 | 
			
		||||
    info!("generation done in {duration:?}");
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
use std::fmt::{self, Display, Write};
 | 
			
		||||
 | 
			
		||||
mod markdown;
 | 
			
		||||
pub mod navmap;
 | 
			
		||||
pub mod tree;
 | 
			
		||||
 | 
			
		||||
pub struct EscapeAttribute<'a>(&'a str);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										82
									
								
								crates/treehouse/src/html/navmap.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								crates/treehouse/src/html/navmap.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,82 @@
 | 
			
		|||
use std::collections::HashMap;
 | 
			
		||||
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    state::Treehouse,
 | 
			
		||||
    tree::{attributes::Content, SemaBranchId},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Default, Serialize)]
 | 
			
		||||
pub struct NavigationMap {
 | 
			
		||||
    /// Tells you which pages need to be opened to get to the key.
 | 
			
		||||
    pub paths: HashMap<String, Vec<String>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl NavigationMap {
 | 
			
		||||
    pub fn to_javascript(&self) -> String {
 | 
			
		||||
        format!(
 | 
			
		||||
            "export const navigationMap = {};",
 | 
			
		||||
            serde_json::to_string(&self.paths)
 | 
			
		||||
                .expect("serialization of the navigation map should not fail")
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Default)]
 | 
			
		||||
pub struct NavigationMapBuilder {
 | 
			
		||||
    stack: Vec<String>,
 | 
			
		||||
    navigation_map: NavigationMap,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl NavigationMapBuilder {
 | 
			
		||||
    pub fn enter_tree(&mut self, tree: String) {
 | 
			
		||||
        self.stack.push(tree.clone());
 | 
			
		||||
        self.navigation_map.paths.insert(tree, self.stack.clone());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn exit_tree(&mut self) {
 | 
			
		||||
        self.stack.pop();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn finish(self) -> NavigationMap {
 | 
			
		||||
        self.navigation_map
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn build_navigation_map(treehouse: &Treehouse, root_tree_path: &str) -> NavigationMap {
 | 
			
		||||
    let mut builder = NavigationMapBuilder::default();
 | 
			
		||||
 | 
			
		||||
    fn rec_branch(
 | 
			
		||||
        treehouse: &Treehouse,
 | 
			
		||||
        builder: &mut NavigationMapBuilder,
 | 
			
		||||
        branch_id: SemaBranchId,
 | 
			
		||||
    ) {
 | 
			
		||||
        let branch = treehouse.tree.branch(branch_id);
 | 
			
		||||
        if let Content::Link(linked) = &branch.attributes.content {
 | 
			
		||||
            rec_tree(treehouse, builder, linked);
 | 
			
		||||
        } else {
 | 
			
		||||
            for &child_id in &branch.children {
 | 
			
		||||
                rec_branch(treehouse, builder, child_id);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn rec_tree(treehouse: &Treehouse, builder: &mut NavigationMapBuilder, tree_path: &str) {
 | 
			
		||||
        if let Some(roots) = treehouse.roots.get(tree_path) {
 | 
			
		||||
            // Pages can link to each other causing infinite recursion, so we need to handle that
 | 
			
		||||
            // case by skipping pages that already have been analyzed.
 | 
			
		||||
            if !builder.navigation_map.paths.contains_key(tree_path) {
 | 
			
		||||
                builder.enter_tree(tree_path.to_owned());
 | 
			
		||||
                for &branch_id in &roots.branches {
 | 
			
		||||
                    rec_branch(treehouse, builder, branch_id);
 | 
			
		||||
                }
 | 
			
		||||
                builder.exit_tree();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rec_tree(treehouse, &mut builder, root_tree_path);
 | 
			
		||||
 | 
			
		||||
    builder.finish()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -17,7 +17,6 @@ pub fn branch_to_html(
 | 
			
		|||
    file_id: FileId,
 | 
			
		||||
    branch_id: SemaBranchId,
 | 
			
		||||
) {
 | 
			
		||||
    // Reborrow because the closure requires unique access (it adds a new diagnostic.)
 | 
			
		||||
    let source = treehouse.source(file_id);
 | 
			
		||||
    let branch = treehouse.tree.branch(branch_id);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,7 @@ use codespan_reporting::{
 | 
			
		|||
};
 | 
			
		||||
use ulid::Ulid;
 | 
			
		||||
 | 
			
		||||
use crate::tree::{SemaBranchId, SemaTree};
 | 
			
		||||
use crate::tree::{SemaBranchId, SemaRoots, SemaTree};
 | 
			
		||||
 | 
			
		||||
pub type Files = SimpleFiles<String, String>;
 | 
			
		||||
pub type FileId = <Files as codespan_reporting::files::Files<'static>>::FileId;
 | 
			
		||||
| 
						 | 
				
			
			@ -20,6 +20,7 @@ pub struct Treehouse {
 | 
			
		|||
 | 
			
		||||
    pub tree: SemaTree,
 | 
			
		||||
    pub branches_by_named_id: HashMap<String, SemaBranchId>,
 | 
			
		||||
    pub roots: HashMap<String, SemaRoots>,
 | 
			
		||||
 | 
			
		||||
    // Bit of a hack because I don't wanna write my own `Files`.
 | 
			
		||||
    tree_paths: Vec<Option<String>>,
 | 
			
		||||
| 
						 | 
				
			
			@ -42,6 +43,7 @@ impl Treehouse {
 | 
			
		|||
 | 
			
		||||
            tree: SemaTree::default(),
 | 
			
		||||
            branches_by_named_id: HashMap::new(),
 | 
			
		||||
            roots: HashMap::new(),
 | 
			
		||||
 | 
			
		||||
            tree_paths: vec![],
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,5 @@
 | 
			
		|||
import { navigationMap } from "/navmap.js";
 | 
			
		||||
 | 
			
		||||
const branchStateKey = "treehouse.openBranches";
 | 
			
		||||
let branchState = JSON.parse(localStorage.getItem(branchStateKey)) || {};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -31,10 +33,13 @@ class Branch extends HTMLLIElement {
 | 
			
		|||
customElements.define("th-b", Branch, { extends: "li" });
 | 
			
		||||
 | 
			
		||||
class LinkedBranch extends Branch {
 | 
			
		||||
    static byLink = new Map();
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super();
 | 
			
		||||
 | 
			
		||||
        this.linkedTree = this.getAttribute("data-th-link");
 | 
			
		||||
        LinkedBranch.byLink.set(this.linkedTree, this);
 | 
			
		||||
 | 
			
		||||
        this.loadingState = "notloaded";
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -51,46 +56,45 @@ class LinkedBranch extends Branch {
 | 
			
		|||
        // correctly, as Branch saves the state in localStorage. Having an expanded-by-default
 | 
			
		||||
        // linked block can be useful in development.
 | 
			
		||||
        if (this.details.open) {
 | 
			
		||||
            this.loadTree();
 | 
			
		||||
            this.loadTree("constructor");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.details.addEventListener("toggle", _ => {
 | 
			
		||||
            if (this.details.open) {
 | 
			
		||||
                this.loadTree();
 | 
			
		||||
                this.loadTree("toggle");
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    loadTree() {
 | 
			
		||||
        if (this.loadingState == "notloaded") {
 | 
			
		||||
            this.loadingState = "loading";
 | 
			
		||||
    async loadTreePromise(_initiator) {
 | 
			
		||||
        try {
 | 
			
		||||
            let response = await fetch(`/${this.linkedTree}.html`);
 | 
			
		||||
            if (response.status == 404) {
 | 
			
		||||
                throw `Hmm, seems like the tree "${this.linkedTree}" does not exist.`;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fetch(`/${this.linkedTree}.html`)
 | 
			
		||||
                .then(response => {
 | 
			
		||||
                    if (response.status == 404) {
 | 
			
		||||
                        throw `Hmm, seems like the tree "${this.linkedTree}" does not exist.`;
 | 
			
		||||
                    }
 | 
			
		||||
                    return response.text();
 | 
			
		||||
                })
 | 
			
		||||
                .then(text => {
 | 
			
		||||
                    let parser = new DOMParser();
 | 
			
		||||
                    let linkedDocument = parser.parseFromString(text, "text/html");
 | 
			
		||||
                    let main = linkedDocument.getElementsByTagName("main")[0];
 | 
			
		||||
                    let ul = main.getElementsByTagName("ul")[0];
 | 
			
		||||
            let text = await response.text();
 | 
			
		||||
            let parser = new DOMParser();
 | 
			
		||||
            let linkedDocument = parser.parseFromString(text, "text/html");
 | 
			
		||||
            let main = linkedDocument.getElementsByTagName("main")[0];
 | 
			
		||||
            let ul = main.getElementsByTagName("ul")[0];
 | 
			
		||||
 | 
			
		||||
                    this.loadingText.remove();
 | 
			
		||||
                    this.innerUL.innerHTML = ul.innerHTML;
 | 
			
		||||
 | 
			
		||||
                    this.loadingState = "loaded";
 | 
			
		||||
                })
 | 
			
		||||
                .catch(error => {
 | 
			
		||||
                    this.loadingText.innerText = error.toString();
 | 
			
		||||
                    this.loadingState = "error";
 | 
			
		||||
                });
 | 
			
		||||
            this.loadingText.remove();
 | 
			
		||||
            this.innerUL.innerHTML = ul.innerHTML;
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            this.loadingText.innerText = error.toString();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    loadTree() {
 | 
			
		||||
        if (!this.loading) {
 | 
			
		||||
            this.loading = this.loadTreePromise();
 | 
			
		||||
        }
 | 
			
		||||
        return this.loading;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
customElements.define("th-b-linked", LinkedBranch, { extends: "li" });
 | 
			
		||||
 | 
			
		||||
function expandDetailsRecursively(element) {
 | 
			
		||||
| 
						 | 
				
			
			@ -102,13 +106,61 @@ function expandDetailsRecursively(element) {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// When you click on a link, and the destination is within a <details> that is not expanded,
 | 
			
		||||
// expand the <details> recursively.
 | 
			
		||||
window.addEventListener("popstate", _ => {
 | 
			
		||||
    let element = document.getElementById(window.location.hash.substring(1));
 | 
			
		||||
    if (element !== undefined) {
 | 
			
		||||
function navigateToPage(page) {
 | 
			
		||||
    window.location.pathname = `/${page}.html`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function navigateToBranch(fragment) {
 | 
			
		||||
    if (fragment.length == 0) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let element = document.getElementById(fragment);
 | 
			
		||||
    if (element !== null) {
 | 
			
		||||
        // If the element is already loaded on the page, we're good.
 | 
			
		||||
        expandDetailsRecursively(element);
 | 
			
		||||
        window.location.hash = window.location.hash;
 | 
			
		||||
    } else {
 | 
			
		||||
        // The element is not loaded, we need to load the tree that has it.
 | 
			
		||||
        let parts = fragment.split(':');
 | 
			
		||||
        if (parts.length >= 2) {
 | 
			
		||||
            let [page, _id] = parts;
 | 
			
		||||
            let fullPath = navigationMap[page];
 | 
			
		||||
            if (Array.isArray(fullPath)) {
 | 
			
		||||
                // TODO: This logic will probably need to be upgraded at some point to support
 | 
			
		||||
                // navigation maps with roots other than index. Currently though only index is
 | 
			
		||||
                // generated so that doesn't matter.
 | 
			
		||||
                let [_root, ...path] = fullPath;
 | 
			
		||||
                if (path !== undefined) {
 | 
			
		||||
                    let isNotAtIndexHtml = window.location.pathname != "/index.html";
 | 
			
		||||
                    for (let linked of path) {
 | 
			
		||||
                        let branch = LinkedBranch.byLink.get(linked);
 | 
			
		||||
 | 
			
		||||
                        if (isNotAtIndexHtml && branch === undefined) {
 | 
			
		||||
                            navigateToPage("index");
 | 
			
		||||
                            return;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        await branch.loadTree("navigateToBranch");
 | 
			
		||||
                        branch.details.open = true;
 | 
			
		||||
                    }
 | 
			
		||||
                    window.location.hash = window.location.hash;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                // In case the navigation map does not contain the given page, we can try
 | 
			
		||||
                // redirecting the user to a concrete page on the site.
 | 
			
		||||
                navigateToPage(page);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function navigateToCurrentBranch() {
 | 
			
		||||
    let location = window.location.hash.substring(1);
 | 
			
		||||
    navigateToBranch(location);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// When you click on a link, and the destination is within a <details> that is not expanded,
 | 
			
		||||
// expand the <details> recursively.
 | 
			
		||||
window.addEventListener("popstate", navigateToCurrentBranch);
 | 
			
		||||
addEventListener("DOMContentLoaded", navigateToCurrentBranch);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,18 +1,19 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
 | 
			
		||||
<html>
 | 
			
		||||
<html lang="en-US">
 | 
			
		||||
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
 | 
			
		||||
    <title>{{ config.user.title }}</title>
 | 
			
		||||
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1">
 | 
			
		||||
 | 
			
		||||
    <link rel="stylesheet" href="{{ site }}/static/css/main.css">
 | 
			
		||||
    <link rel="stylesheet" href="{{ site }}/static/css/tree.css">
 | 
			
		||||
    <link rel="stylesheet" href="{{ site }}/static/font/font.css">
 | 
			
		||||
 | 
			
		||||
    <script type="module" src="{{ site }}/navmap.js"></script>
 | 
			
		||||
    <script type="module" src="{{ site }}/static/js/tree.js"></script>
 | 
			
		||||
    <script type="module" src="{{ site }}/static/js/usability.js"></script>
 | 
			
		||||
</head>
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +39,10 @@
 | 
			
		|||
        </div>
 | 
			
		||||
    </noscript>
 | 
			
		||||
 | 
			
		||||
    <nav>
 | 
			
		||||
        <a href="{{ site }}/" title="Back to index"></a>
 | 
			
		||||
    </nav>
 | 
			
		||||
 | 
			
		||||
    <main class="tree">
 | 
			
		||||
        {{{ tree }}}
 | 
			
		||||
    </main>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue