lots of changes so remake of static site generator can handle the blog markdown
This commit is contained in:
parent
a09a07e3e0
commit
1b359aa812
14 changed files with 96 additions and 75 deletions
|
|
@ -97,6 +97,7 @@ services:
|
|||
- backend
|
||||
```
|
||||
and here's an nginx config
|
||||
|
||||
```nginx
|
||||
worker_processes 1;
|
||||
|
||||
|
|
@ -172,6 +173,7 @@ Okay I didn't say this outright before so I'll say it now. Google's documentatio
|
|||
>`~oia@
|
||||
|
||||
here's what I wrote trying to implement that
|
||||
|
||||
```rs
|
||||
fn enc_float(num:f64)->String{
|
||||
let mut working:i32 = (num*1e5).round() as i32;
|
||||
|
|
|
|||
|
|
@ -31,18 +31,12 @@ Then for a couple of reasons wanted to write a blog article about progress on [P
|
|||
So with inspiration in my heart to go and do stuff with Github actions I began. First off I needed to set up the condition for my workflow running which was a pretty simple as I wasn't really doing anything interesting here.
|
||||
|
||||
```yaml
|
||||
|
||||
on:
|
||||
|
||||
push:
|
||||
|
||||
branches:
|
||||
|
||||
- master
|
||||
|
||||
```
|
||||
|
||||
|
||||
## The jobs
|
||||
|
||||
I knew due to reading some pages on Github's action market place and previous context that I would need to have at least 3 if not more steps
|
||||
|
|
@ -82,35 +76,22 @@ Conveniently while `$user_home/.ssh/authorized_keys` is the default location for
|
|||
anyways yeah this is what I initially(spoiler I change it) wrote for Github actions to go and deploy the app.
|
||||
|
||||
```yaml
|
||||
|
||||
uses: up9cloud/action-rsync@master
|
||||
|
||||
env:
|
||||
|
||||
HOST: pagwin.xyz
|
||||
|
||||
KEY: ${{secrets.SSH_KEY}}
|
||||
|
||||
TARGET: /var/www/pagwin.xyz/
|
||||
|
||||
```
|
||||
|
||||
With that I saved the file to `.github/website-publish.yml` and felt a mild sense of accomplishment. In hindsight that sense and first file are hilarious and while I would love to immediately explain why first I want to take a second to show a step I added after I finished dealing with my stupidity. That step is a cleanup step that deletes the old site before copying over the new one so people can't snoop around in redundant files. I implemented that with this tidbit.
|
||||
|
||||
```yaml
|
||||
|
||||
uses: appleboy/ssh-action@master
|
||||
|
||||
with:
|
||||
|
||||
host: pagwin.xyz
|
||||
|
||||
username: website
|
||||
|
||||
key: ${{secrets.SSH_KEY}}
|
||||
|
||||
script: rm -rf /var/www/pagwin.xyz/*
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
|
@ -126,79 +107,39 @@ The obvious act of stupidity if you paid attention to what I wrote is that I sav
|
|||
Overall I'm very happy I did this because it gave me a nice bit of practical understanding of how to set up Github actions for future projects. I hope reading about my technical spaghetti VPS and idiocy wasn't too boring. Oh yeah for those who care this is what the yaml file looked like in the end
|
||||
|
||||
```yaml
|
||||
|
||||
name: Website publish
|
||||
|
||||
|
||||
|
||||
on:
|
||||
|
||||
push:
|
||||
|
||||
branches:
|
||||
|
||||
- master
|
||||
|
||||
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
||||
- name: Code Checkout
|
||||
|
||||
uses: actions/checkout@v2
|
||||
|
||||
with:
|
||||
|
||||
submodules: true
|
||||
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Hugo Setup
|
||||
|
||||
uses: peaceiris/actions-hugo@v2
|
||||
|
||||
with:
|
||||
|
||||
hugo-version: '0.91.2'
|
||||
|
||||
- name: Build
|
||||
|
||||
run: hugo --minify
|
||||
|
||||
- name: Clean
|
||||
|
||||
uses: appleboy/ssh-action@master
|
||||
|
||||
with:
|
||||
|
||||
host: pagwin.xyz
|
||||
|
||||
username: website
|
||||
|
||||
key: ${{secrets.SSH_KEY}}
|
||||
|
||||
script: rm -rf /var/www/pagwin.xyz/*
|
||||
|
||||
- name: Deploy
|
||||
|
||||
uses: up9cloud/action-rsync@master
|
||||
|
||||
env:
|
||||
|
||||
HOST: pagwin.xyz
|
||||
|
||||
USER: website
|
||||
|
||||
KEY: ${{secrets.SSH_KEY}}
|
||||
|
||||
SOURCE: ./public/*
|
||||
|
||||
TARGET: /var/www/pagwin.xyz/
|
||||
|
||||
```
|
||||
|
|
|
|||
19
posts/how.md
19
posts/how.md
|
|
@ -5,18 +5,25 @@ date: 2020-09-30
|
|||
draft: false
|
||||
tags: []
|
||||
---
|
||||
|
||||
## Prelude
|
||||
|
||||
Before we get to how I actually made this site let's discussed how I failed to make this site(repeatedly). I was inspired to make a simple website/blog from [this blog post](https://k1ss.org/blog/20191004a), I rapidly regretted having that as my main inspiration. I tried setting up scripts for generating pages using the output from pandoc, making the pages look nice and what not as well as make a script for generating an rss feed but rapidly realized that all of this was going to be a pain and gave up. Rinse and repeat a couple of times over several months to a year or so.
|
||||
|
||||
## Actually making this website
|
||||
|
||||
One day(2 days before this post was written actually) I was browsing reddit when I came across [this comment](https://www.reddit.com/r/linuxquestions/comments/j0wcfj/i_hand_you_a_computer_with_a_minimalistic_install/g6vxxxj/) and realized that I'm an idiot because static site generators exist and what I had previously been doing was basically writing my own, I may still write my own but more so as a project on it's own than as something that's contributing to something else. After that work went relatively smoothly with me spending the first day learning what the fuck hugo(no I didn't do my research into static site generators don't judge me okay) is then on the second day I actually started to get into writing all the stuff for the site. For my theme as you may already know if you've looked at the footer for this website I'm using liquorice as my theme. I chose it for being simple, very nice for reading text(what I expect will be the main thing that's done on this website) and because I just liked the overall feel. There were some aspects that I felt the need to improve though such as the homepage being a bit more than just a list of every page on the site, something about lists(I don't remember what), making the subsections of these blogs and other pages in the future jump points in case I write something that would actually benefit from those jump points and not just a short one page piece and finally making the links actually look visually distinct from the text beyond simply being bold. There are probably other changes I'm forgetting and in the future I expect I'll tweak this further but that's all for now. Some of those tweaks will be me making the website smaller and more compressed following the original spirit of that kiss blog and I can already see some points where I can shave some size off but that's a story for another time.
|
||||
|
||||
## Making the jump points
|
||||
|
||||
most of those points are pretty easy if you read hugo's documentation and are willing to try random things but the jump points are a slight challenge and something worth writing about in more detail. First things first ignoring all this being generated from markdown causing some oddities how do we make a jump point on a webpage? Well with anchor tags of course!
|
||||
|
||||
```html
|
||||
<a name="some_name_or_something_idk" href="./#some_name_or_something_idk">some content doesn't matter</a>
|
||||
```
|
||||
|
||||
this is nice now if somebody goes to `example.com/#some_name_or_something_idk` their browser will jump them straight down to wherever that anchor tag is. But it doesn't jump to the anchorblock when we click on it it simply sets our url and if we reload it jumps to it. *Editor's note: as I write this I'm unsure if I'm an idiot who didn't need to do the work with this javascript I'm about to talk about so it may well be possible that it's unnecessary and the above code already does that*. So in order to fix that we'll be adding an event to our anchor element like this.
|
||||
|
||||
```html
|
||||
<a name="name" href="./#name" id="name">some text</a>
|
||||
<script>
|
||||
|
|
@ -26,10 +33,13 @@ this is nice now if somebody goes to `example.com/#some_name_or_something_idk` t
|
|||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
Technically the event could be added by adding an onclick parameter to the anchor element in the dom but we we start dealing with another problem which I'll get to after explaining this it'll be way cleaner to just use `addEventListener` anyway the code is relatively self explanatory but I'll explain it anyways. Our element has an id that we attached to it by adding the parameter `id="name"` we can get our element in our code by asking the browser to give our element to us using the id as a reference to find the element with the method `document.getElementById`. We could totally just use `document.getElementsByName` and take the first element from that but I personally chose to add and use the id. With `addEventListener` we can attach a function that'll be called when an event fires in this case the click event for when the user clicks on the anchor element. The function in question take the event object given to it and takes out the dom element that was actually clicked on with the target property. We then scroll to that dom element with scrollIntoView. Now all we need to do is have it so that when we write out our header elements we just surround them with anchor elements and... wait.
|
||||
|
||||
## We didn't write those header elements though
|
||||
|
||||
Oh yeah we didn't write the header elements in the first place they're written in from whatever markdown generator that hugo uses. Well how do we handle this? There may be some way of changing how hugo generates html from the markdown but that sounds hard let's just write some javascript.
|
||||
|
||||
```js
|
||||
let elems = document.getElementsByTagName("h2");
|
||||
for(let elem of elems){
|
||||
|
|
@ -39,23 +49,32 @@ for(let elem of elems){
|
|||
});
|
||||
}
|
||||
```
|
||||
|
||||
Ok so you already understand that last bit with the event listener and what not so allow me to explain the rest. `document.getElementsByTagName` is the same as `document.getElementById` except it gets more than one element and does it by their tag name. The for loop iterates through all the elements we just got and through each iteration we can refer to the element we're on by the variable `elem`. The parameter of `outerHTML` isn't used very often `innerHTML` and `innerText` are used more often because most people only want to control the text inside of a dom element but want to leave the outer tags untouched but in this case that's useful because we actually want to add anchor tags around our header tags which is what we do. Hooray the problem with the markdown generation not allowing fine enough control was solved. Now about adding that script in to do that work.
|
||||
|
||||
## Adding the script in
|
||||
|
||||
You'd think this was simple and it kinda was but keep in mind that I've only been using hugo for less than 3 days at this point. Besides that I also only wanted this script in the single pages or the pages that blog/articles/whatever were on and not on list pages which list out all the pages as the list pages also used h2 elements but I didn't want the h2 elements there to be modified by this script. Thankfully this was easy because I had shortly before hand wanted to do something similar with a stylesheet but man adding in that stylesheet had some nuisances. The first thing I found of use for this purpose was [cond](https://gohugo.io/functions/cond/) but I still needed to figure out how to test for whether the page was a list or not so I started looking through hugo's page variables and I found 3 candidates for this `.IsNode`, `.IsPage` and `.IsSection` with the last one just being the negate. I got somewhat frustrated when I found none of these useful for what I was trying to do. Eventually I stumbled upon `.Kind` and bumbled about a bit trying to figure out how to test for a `.Kind` of page until I found [eq](https://gohugo.io/functions/eq/). So great I now can test for whether a page is a page I want the stylesheet applied to so
|
||||
|
||||
```html
|
||||
{{ cond (eq .Page.Kind "page") "<link rel='stylesheet' href='{{.Site.BaseURL}}/css/single.css'>" "" }}
|
||||
```
|
||||
|
||||
should work right? Nope nope nope for multiple reasons nope. For one thing trying to put the base url with curly brackets didn't work because apparently hugo doesn't do curly brackets in curly brackets also when I opened the page in a browser the tag and all the tags beneath it(which were placed in the head in the partial template btw) are now in the body??? Also I made it seem like I had solved the cond thing before this came up but that was happening at this point as well. So first things first how do we put a variable midway through a tag that we're inserting? Well apparently the answer to that is [printf](https://gohugo.io/functions/printf/)(I personally would've named it something like format rather than printf even if it uses something called printf internally but maybe that's just me) so now we have.
|
||||
|
||||
```html
|
||||
{{ cond (eq .Page.Kind "page") (printf "<link rel='stylesheet' href='%s/css/single.css'>" .Site.BaseURL) "" }}
|
||||
```
|
||||
|
||||
which is closer but it still jumps into the body for some reason. That reason as it turns out is because Hugo ~~being somewhat annoying because it decides not to warn you for failing to be explicit about whether you want a tag as a tag~~ being very cool and safe escaping all the tags to prevent cross site scripting/injection or whatever else problems in code that you're explicitly writing out in a folder for templates. Ugh anyways after running the output of the printf through [SafeHTML](https://gohugo.io/functions/safehtml/) we get this final iteration that works how I want it to of.
|
||||
|
||||
```html
|
||||
|
||||
{{ cond (eq .Page.Kind "page") ( safeHTML (printf "<link rel='stylesheet' href='%s/css/single.css'>" .Site.BaseURL)) "" }}
|
||||
```
|
||||
|
||||
Nothing about this changes for the script that we want on our blog pages only other than that we replace link with a script tag.
|
||||
|
||||
## Conclusion
|
||||
|
||||
This was fun and I'm glad I found out about the existance of [hugo](https://gohugo.io/). I'll probably update this site in the future and this blog will probably get outdated but unless I decide the site looks almost completely different run into a very annoying or interesting problem or completely remake the site for osme reason or another I probably won't update this blog or release any other blogs with updates on changes I make to this site(and knowing me even when those things come up I probably won't write about them) one of the things I want to change is the links to different platforms/feeds/whatever but based on already made efforts I think I'll save that for another time.
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@ tags: []
|
|||
So recently I’ve engaged in a renewed push to be productive somewhat consistently and this time it just may work(unlike the 3-5 other times). With this push, I’ve decided to begin moving off of youtube by going down to my subscriptions. In order to do that I implemented a few small projects.
|
||||
|
||||
## Getting the feeds(but not really)
|
||||
|
||||
So to enforce that my initial plan was to only watch the content I saw through an RSS feed, preferably via mpv. In order to do that I needed a list of channel ids corresponding to the youtube channels I was subscribed to. In order to get that I could’ve gone through and manually gotten each channel id through youtube’s web interface… But that would take forever and ain’t nobody got time for that manual labor when you can spend twice as long automating(although doing that automation gave me experience that may save me time now). So to do that I looked into google’s [Youtube Api](https://developers.google.com/youtube/v3) and found a way to get a list of [subscriptions](https://developers.google.com/youtube/v3/docs/subscriptions/list). But to make use of that I’d need to go and learn how to do stuff with OAuth. Thankfully after faffing about a bit I realized that there’s an [npm package](https://www.npmjs.com/package/googleapis) that does a lot of that work for me. Anyways with that, it was time to ~~steal example code~~ write software, oh hey where did all that preexisting code come from?
|
||||
|
||||
## Oh the callbacks
|
||||
|
||||
Well, that code came from [here](https://developers.google.com/youtube/v3/quickstart/nodejs) and oh my god do they use callbacks. Personally, I think callbacks suck and are the worst way of having some sort of asynchronous task. So I did a decent amount of refactoring to convert things to use promises. However much to my chagrin I found that I couldn’t use async await because apparently the npm package didn’t return normal promises, or maybe something else was happening I’m not entirely sure looking over the code now with intellisense but trust me when I tried back when I was figuring this out it didn’t work and it was annoying. Though that said I also don’t know why I couldn’t/wouldn’t convert from the weird promises to normal promises due to that being relatively easy with js’s promise api but I digress.
|
||||
|
||||
|
||||
## Getting the subs
|
||||
|
||||
I don’t remember if I implemented the code that got my subscriptions concurrently with the callback refactor or if I did it after. In any case, all of the code to get the subscriptions is 2 relatively small functions.
|
||||
|
|
@ -48,12 +48,15 @@ function handlePage(authority,response){
|
|||
}
|
||||
```
|
||||
Yeah, pretty simple but allow me to explain what bits of these 2 functions are doing and why.
|
||||
|
||||
```js
|
||||
var service = google.youtube("v3");
|
||||
//...
|
||||
let items = response.data.items;
|
||||
```
|
||||
|
||||
Both of these are done primarily for convenience so I'm not writing the same thing over and over again. If you notice the service one uses the inferior var instead of let it's because I was lazy and didn't change that bit from the example code. And now that I'm done with the bit you can find that code [here](https://developers.google.com/youtube/v3/quickstart/nodejs).
|
||||
|
||||
```js
|
||||
return service.subscriptions.list({
|
||||
mine:true,
|
||||
|
|
@ -63,24 +66,30 @@ Both of these are done primarily for convenience so I'm not writing the same thi
|
|||
pageToken: page ? page:""
|
||||
});
|
||||
```
|
||||
|
||||
The only other bit of code in the getSubscriptions function just calls the method in the npm package to make a request for the subscriptions of the user who provided Oauth authorization, 50 results at a time specifically giving things under the "snippet" category of data. For the pageToken bit what it's doing is if it's null/undefined it specifies it as an empty string so we get the first page and if it's not then it's just itself so we can get the next page.
|
||||
|
||||
```js
|
||||
for(let item of items){
|
||||
console.log(item.snippet.title+" ".repeat(60-item.snippet.title.length)+item.snippet.resourceId.channelId);
|
||||
}
|
||||
```
|
||||
|
||||
This bit of code just outputs each of the fetched channels' names and their id such that all the ids visually align for the part of my brain that wants everything to look neat. The reason I wanted things in this format was that I wanted to manually filter out the channels I didn't watch so having the channel name with the id would make it faster to get through for the obvious ones. The reason I was console.logging instead of writing to a file via the fs module was because I was lazy and decided to just have the information output via stdout and redirected to a file via a > operator in bash.
|
||||
|
||||
```js
|
||||
if(response.data.nextPageToken){
|
||||
getSubscriptions(authority, response.data.nextPageToken)
|
||||
.then(handlePage.bind(null,authority))
|
||||
}
|
||||
```
|
||||
|
||||
This last bit of code checks to see if there's a token for the next page of subscriptions and if there is it gets them, providing the nextPageToken to do just that in the getSubscriptions function and once the new response pops up it sends it to handlePage. More specifically what happens is I use the bind method of js functions as a way to have a partial function which can otherwise be said as a function that already has one of its arguments passed in. Until somewhat recently I wasn't aware you could use bind like that but one time when I was commenting an amount of annoyance at js not having a built-in function that allows for the easy construction of partial functions like Python's functools.partial or Haskell's function currying built into the language in a discord server a friend pointed out that the bind method can be used for that so the more you know I guess.
|
||||
|
||||
## Why I specified but not really for getting the feeds
|
||||
|
||||
As it turns out what I wanted could be better accomplished by self-hosting an [Invidious](https://github.com/iv-org/invidious) instance. However, my weird format that I had of my subs wouldn't work and I didn't want to redo filtering out channels I don't want so I decided to make a script that would make an opml file which is one of the file types that invidious could import. To do that I wrote a rust script.
|
||||
|
||||
```rs
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
|
@ -108,6 +117,7 @@ fn gen_middle(line:&str)->String{
|
|||
format!("<outline text=\"{}\" title=\"{}\" type=\"rss\" xmlUrl=\"/feed/channel/{}\"/>",name,name,id)
|
||||
}
|
||||
```
|
||||
|
||||
TLDR on that whole bunch of code is I have a constant string as the start of the file which gets output. Then there's a middle that's generated from the list of channels in that weird format the previous script generated such that every entry gets put into the template `<outline text=$channel_name title=$channel_name type="rss" xmlUrl="/feed/channel/$channel_id"/>"` and then all the channel entries are joined together. After that, I put a constant value at the end to close everything up. Developing that script there was a bit of a hiccup where Invidious wouldn't take it because the channel name only had the first word due to me making an initial mistake which I eventually fixed.
|
||||
|
||||
### Wait what about hosting an Invidious instance?
|
||||
|
|
|
|||
|
|
@ -16,14 +16,13 @@ Same deal as the [speedrun blog](https://pagwin.xyz/blog/speedrun/) putting down
|
|||
|
||||
## The entire set plus one more in the set
|
||||
|
||||
Let me start off where I started off<sup>[[1]](#1)</sup> lets say that someone who we shall name Steve anonymously puts a bounty on themselves which is described as "$1 in addition to whatever money Steve has on their person" how much money should be payed out to whoever collects the bounty on Steve and where would it come from? Well in this case the way that reality works and the set of actors involved constrains us to the answer of "whatever money Steve has on their person" and no more. This answer would correspond to addition being equivalent to the set union operator. That does work but with slightly different context it seems like the answer would be different, for example pretend that a god came down and said "I am going to transfer ten humans in addition to the entire human population to a habitable planet in a different galaxy". In this case specifying ten humans in addition implies that we're transferring a number of humans greater than just the current human population but also ten more because otherwise why specify those ten humans. The problem that I have is how many humans come out on the other side. "Why not just the current population plus ten?" well because transfer implies they already exist and aren't being created in that moment so the number should be the same and also my brain thinks there's an interpretation or slightly different wording where you could argue there'll be infinitely many humans. I'm pretty sure this is a [type 5 paradox](https://youtu.be/ppX7Qjbe6BM?t=2035)
|
||||
Let me start off where I started off<sup><a href="#1">[1]</a></sup> lets say that someone who we shall name Steve anonymously puts a bounty on themselves which is described as "$1 in addition to whatever money Steve has on their person" how much money should be payed out to whoever collects the bounty on Steve and where would it come from? Well in this case the way that reality works and the set of actors involved constrains us to the answer of "whatever money Steve has on their person" and no more. This answer would correspond to addition being equivalent to the set union operator. That does work but with slightly different context it seems like the answer would be different, for example pretend that a god came down and said "I am going to transfer ten humans in addition to the entire human population to a habitable planet in a different galaxy". In this case specifying ten humans in addition implies that we're transferring a number of humans greater than just the current human population but also ten more because otherwise why specify those ten humans. The problem that I have is how many humans come out on the other side. "Why not just the current population plus ten?" well because transfer implies they already exist and aren't being created in that moment so the number should be the same and also my brain thinks there's an interpretation or slightly different wording where you could argue there'll be infinitely many humans. I'm pretty sure this is a [type 5 paradox](https://youtu.be/ppX7Qjbe6BM?t=2035)
|
||||
|
||||
## Excel with types/static analysis?
|
||||
|
||||
So I think I started thinking about this when I rewatched [this Matt Parker video](https://www.youtube.com/watch?v=yb2zkxHDfUE). I'm wondering if there's a niche for some spreadsheet software is intended to require the user to specify types for cells or a full sql-esque table or something in addition to doing some nice lints/static analysis like you would see in software to minimize errors. My mind has also feature creeped this idea out a bit to have this program capable of exporting some file package/sql database and an executable so you can have something maintaining the structure of the data while other programs do automated stuff in the hopes that Ludicity doesn't come in for a [drop kick](https://ludic.mataroa.blog/blog/i-will-fucking-dropkick-you-if-you-use-that-spreadsheet/).
|
||||
|
||||
|
||||
## PSA please check to make sure browser zoom works okay on your site <sup>[[2]](#2)</sup>
|
||||
## PSA please check to make sure browser zoom works okay on your site <sup><a href="#2">[2]</a></sup>
|
||||
|
||||
And also phones and screen readers but the browser zoom one is the one that affected me when I'm writing this (over 2 months after the prior 2 sections). Particularly if you have a blog with some text content that's centered with whitespace as the margins and I zoom in I don't just want to see the text get bigger I also want the margins to shrink so the text has more room. Reason being so zooming isn't proportional to additional scrolling. Thanks.
|
||||
|
||||
|
|
@ -31,7 +30,6 @@ And also phones and screen readers but the browser zoom one is the one that affe
|
|||
|
||||
I was exploring the CSS to understand how the margins shrank for my site but not the offending site and I noticed on both sites that the css styling jumped a bit as I shrank and grew the page width only to realize the reason was because of some [CSS if statement(s)](https://css-tricks.com/a-complete-guide-to-css-media-queries/). I don't know why I previously thought you could deal with this in some other way but TIL.
|
||||
|
||||
|
||||
## How long should I make these?
|
||||
|
||||
I've never thought about this before but how long should I make blogs? How long should I make microblog dumps? I don't know I should probably think about that... later I will think about that later.
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Example:
|
|||
|
||||
Is it pseudo elements/pseudo classes?
|
||||
If so why not use this pattern where the `style` tag is directly next to what the CSS is touching?
|
||||
|
||||
```html
|
||||
<style>
|
||||
#unique_name {
|
||||
|
|
@ -53,8 +54,10 @@ If so why not use this pattern where the `style` tag is directly next to what th
|
|||
</style>
|
||||
<button id="unique_name">My Button</button>
|
||||
```
|
||||
|
||||
Replace `unique_name` with gibberish as needed.
|
||||
Once [scope](https://developer.mozilla.org/en-US/docs/Web/CSS/@scope) is ubiquitous you could even do this.
|
||||
|
||||
```html
|
||||
<!-- css-scope elem is an arbitrary elem, this could be a div or whatever else instead-->
|
||||
<css-scope>
|
||||
|
|
@ -74,6 +77,7 @@ Once [scope](https://developer.mozilla.org/en-US/docs/Web/CSS/@scope) is ubiquit
|
|||
<button>My Button</button>
|
||||
</css-scope>
|
||||
```
|
||||
|
||||
Maybe the gibberish solution doesn't scale without additional tooling and the scope solution will become standard practice in a few years, idk man.
|
||||
|
||||
### pre-publishing update
|
||||
|
|
@ -160,6 +164,7 @@ I made an [Excalidraw](https://excalidraw.com/) drawing for that post.
|
|||
I modified the svg export to add a style tag which makes the colors follow Excalidraw's light vs dark theming depending on a css media query.
|
||||
Thankfully whoever did light/dark theming was super lazy and just did a filter which I could replicate in CSS rather than needing to painstakingly pick every color from their site.
|
||||
After removing their filter from the XML I just added the following to the SVG.
|
||||
|
||||
```xml
|
||||
<style>
|
||||
@media (prefers-color-scheme: dark){
|
||||
|
|
|
|||
|
|
@ -10,11 +10,17 @@ tags: []
|
|||
Either I should've made pull request(s) to improve mineflayer or shut the fuck up.**
|
||||
|
||||
## Preface
|
||||
|
||||
Given I'm gonna be complaining about [mineflayer](https://github.com/PrismarineJS/mineflayer) you may be wondering why I don't roll with something else given my complaints. The problem with that is that there is nothing else to my knowledge or at least nothing else high level, not even in other languages. There probably is and I just didn't look hard enough but oh well. Also I would've built up my own thing from scratch but reverse engineering/reimplementing a network protocol without official docs and without even unofficial docs if you're trying to do stuff with older versions is kinda hard and if you wanna see how far I got then you can look at the [repo with my work](https://github.com/Pagwin2/McProtocolLearning) and I refused to work with the slightly lower level(relative to mineflayer) [node minecraft protocol](https://github.com/PrismarineJS/node-minecraft-protocol)(made by the same person) because if I'm using someone else's work I may as well go all the way to the highest level
|
||||
|
||||
## Why?
|
||||
|
||||
Oh yeah I decided to make a minecraft bot because it seemed fun and it seems like there's all sorts of room to implement cool stuff though the specifics of what I'm making will probably be covered in another block post. Anyways onto the problems with mineflayer
|
||||
|
||||
## constructor isn't used to create an object
|
||||
|
||||
Specifically the thing that's a problem is that when you want to create a new [Bot](https://github.com/PrismarineJS/mineflayer/blob/master/docs/api.md#bot) instance you need to call the method mineflayer.createBot. Why have a method completely detached from the object that makes a new instance of that object when you can just make it a constructor I have no idea. You may be wondering "what's the big deal it's a slightly different way to instantiate the object?" the problem with that being that if you try to make a subclass of `Bot` to add methods for your own purposes you have to do janky stuff to work around the constructor not being used. In my case I did
|
||||
|
||||
```typescript
|
||||
class taskBot extends mineflayer.Bot{
|
||||
...
|
||||
|
|
@ -27,18 +33,31 @@ class taskBot extends mineflayer.Bot{
|
|||
...
|
||||
}
|
||||
```
|
||||
|
||||
by the way I'm using typescript in case you can't tell which will lead to another couple of headaches I'll get into soon but yeah using `Object.assign` is absolutely not ideal at all(an as of writing I have no idea if using this hack leads to problems I haven't encountered yet). As an aside there's no reason this problem couldn't be fixed at least from the glances I've given to mineflayer's source code.
|
||||
|
||||
## different ways of having a point in 3d space passed to different functions
|
||||
|
||||
I hate this with a firey passion and am very happy that it's super easy to deal with this. Okay so you may be wondering "what's the big fuss?" and I'll tell you that different functions will take either a Vec3(I have a small gripe with Vec3 as well but that's not worth making a fuss about) or 3 separate arguments specifying the x, y and z coordinates. Having these 2 approaches means that there isn't a correct form to have positions stored in your program because you'll have to deal with at least 1 form that isn't the form you have them stored in anyways. This problem is an easy fix you can just have an array that stores 3 numbers in it and when you need a Vec3 or need to pass it into a function that takes 3 args use the spread operator as args to the function or the Vec3 constructor.
|
||||
|
||||
## typescript hell
|
||||
|
||||
As I've already mentioned I'm using typescript for my own purposes. However with typescript there are a couple of problems that come up that are annoying to deal with. Plugins and minecraft-data.
|
||||
|
||||
## Plugins
|
||||
|
||||
My gripe with plugins can be further subdivided into how plugins add attributes to the `Bot` instance and how they have the `Bot` instance emit new events. The first problem is that the `Bot` type has a set of attributes that typescript knows about but when you load a plugin for the Bot instance to do something like add pathfinding abilities a new attribute is added to the bot that has all the new abilities within it but typescript doesn't know that the `Bot` instance has a new attribute. The solution I found for this which also removed a bit of complexity from the code was to make a subclass of Bot and add in the plugin attributes as needed which led to the problem already described above involving the constructor. My second problem with the events was that typescript also keeps track of what events an event emitter will emit so if you try to listen for an event it won't emit it'll give you an error. But again when you run a `Bot` instance through a plugin it's type doesn't change so it doesn't get any of it's new events. Sadly the solution for this required me to commit what's effectively a typescript sin.
|
||||
|
||||
```typescript
|
||||
(this as any).once(...)
|
||||
```
|
||||
|
||||
I think I heard an angel die. Of course I personally don't blame these typescript problems on the developer because 1)they wrote this in javascript and mistakes can happen and 2) I don't know how I'd solve them so yeah.
|
||||
|
||||
## minecraft-data
|
||||
|
||||
this one's short, basically there isn't an easy way to get the type for the object you get when you provide your minecraft version to the `minecraft-data` module. There's probably a way(in fact I'm pretty confident that I'm being an idiot here) but I can't be bothered finding it
|
||||
|
||||
## conclusion
|
||||
|
||||
mineflayer dev if you're reading this for whatever reason please fix the problems relating to the constructor and consistent arguments to functions that take points. Although for the latter I understand if you can't make it all consistent because it would be a breaking API change in all likelyhood. Overall I think the api is alright and I don't have enough will power or brain cells to remake it for myself but I would certainly appreciate these pain points being addressed if they can.
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ So yeah... More on that when I discuss what I'm probably going to change in futu
|
|||
#### Making a github action out of it
|
||||
|
||||
I refuse to accept any downgrade in my own long term convenience as such it was imperative that I be able to have pushing/merging in a repo be equivalent to rebuilding and publishing the site. First things first I needed to containerize my static site generator. As such I wrote this `Dockerfile`
|
||||
|
||||
```dockerfile
|
||||
FROM haskell
|
||||
|
||||
|
|
@ -120,7 +121,7 @@ Picking colors was annoying because I wanted colors with good contrast so this s
|
|||
|
||||
### Icons
|
||||
|
||||
The navigation icons for this site were picked off the internet looking for a house icon and an RSS icon. Additionally in the footer I'm using [github's official svg logo](https://brand.github.com/foundations/logo)*, asterisk due to the css I added.
|
||||
The navigation icons for this site were picked off the internet looking for a house icon and an RSS icon. Additionally in the footer I'm using [github's official svg logo](https://brand.github.com/foundations/logo)\*, asterisk due to the css I added.
|
||||
|
||||
#### The CSS I added
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ tags: []
|
|||
---
|
||||
|
||||
As the description of this article says I wrote this to try and probably fail to win an argument. Namely, a friend ~~argued~~ asked "Why use rust enums just use an interface and manual type checks?" specifically the context was me advocating for the use of rust enums instead of C's union types. Example:
|
||||
|
||||
```dart
|
||||
String anything(Object something) {
|
||||
if(something is bool){
|
||||
|
|
@ -24,11 +25,13 @@ String anything(Object something) {
|
|||
return "$something";
|
||||
}
|
||||
```
|
||||
|
||||
Personally, I think this is a bad solution to the problem of having multiple valid types for something generally speaking. Specifically speaking taking an argument which we only know implements some interface can be good.
|
||||
|
||||
## Rust enum example
|
||||
|
||||
But I haven't made any arguments for my position yet I've just specified how you can go without C's unions which are terrible and you should never use them outside of the implementation of something that solves the problem in a better way. What makes rust enums great for this use case is that we are explicitly limiting what can be passed in at compile time and are being explicit about what we want and why. The rust equivalent to that example is
|
||||
|
||||
```rs
|
||||
//why a 128 bit integer and not just a 64 bit? Well because I wanted to show off that rust has that that's why
|
||||
enum OurEnum{InterviewQuestion(bool), FastFoodOrder(u128), Other(Box<dyn Display>)}
|
||||
|
|
@ -57,13 +60,13 @@ fn anything(something:OurEnum)->String{
|
|||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now you might be saying "oh my god that's way more verbose why would I want that?!?!". The reason you want that is because all that verbosity provides more information to people who have to deal with this code in the future ~~and also partially due to rust having &str and String as different things as well as me showing off a bit~~. So what new information do we get with this code that we don't get with the Dart code?
|
||||
|
||||
## Each case has a name
|
||||
|
||||
As the section title says each case has a name, now someone reading this code knows that our boolean case corresponds to InterviewQuestion whatever that may mean in a larger context. This also means that if we want different things for the same type under certain conditions we just need to add something to the enum.
|
||||
|
||||
|
||||
## Happy little compiler errors
|
||||
|
||||
In addition to the whole deal of enums helping self-document they also make it so if we fuck up we get a compiler error unless we use if let or _ but if you use those you are your own worst enemy. This also makes it so if we add a case we can't compile the code unless we handle that case everywhere we're doing stuff with our Enum. What this also means is that if we only wanted to handle booleans and unsigned integers we could do that without runtime errors.
|
||||
|
|
@ -93,10 +96,13 @@ Accept it person I'm arguing with Rust enums are superior, if anyone complains a
|
|||
But arguments/discussions aren't one sided like that and I chatted with the person shortly after writing this article and they made an interesting point.
|
||||
|
||||
## If the type fits you should accept it
|
||||
|
||||
This is a rebuke to my "Oh hey I implement your interface now, fuck you" and is simply asking how that's a problem. After all the interface should specify what you need from some data type to be able to use it in some application. This also moves the control flow of which code to run outside of the method taking in data. Combined with the idea that the interface setup can also give compiler errors where it makes sense the question has to be now be asked why are rust enums preferable?
|
||||
|
||||
## welll uuuuuuuuuuuuh
|
||||
|
||||
When you have a finite number of states which may contain state within themselves and aren't just representing different input types they're pretty nice.
|
||||
|
||||
## Conclusion 2(Electric boogaloo)
|
||||
|
||||
Use interfaces when you want certain garantees about a type, use rust enums when you want a finite set of states.
|
||||
|
|
|
|||
|
|
@ -14,13 +14,14 @@ tags: []
|
|||
|
||||
This article is the result of running into annoyances in rust's type system. These annoyances aren't bugs as much as they are limitations caused by how rust's type system is currently implemented. Interestingly one of these seems relatively easy to fix at first glance while the other is a problem whose root cause(s) have been around for a good few years and probably won't be fixed for at least some ways into the future.
|
||||
|
||||
## Case 1: From\<Result\<T,E\>\> for Result\<From\<T\>,E\> and vice versa for E
|
||||
## Case 1: `From<Result<T,E>>` for `Result<From<T>,E>` and vice versa for E
|
||||
|
||||
Did you know that rust didn't have this implemented for you? I certainly didn't until about a week ago when I wanted to convert from a `Result<String, Error>` to a `Result<(), Error>` for the sake of convenience when returning from a function. Now you may think that this is the one which I think is the easy fix but if you thought that you'd be wrong.
|
||||
|
||||
### Why this seems easy
|
||||
|
||||
If you don't know rust or didn't think about this that much yet you may wonder how this is easy. Well to answer that I think I'll just show you the implementation.
|
||||
|
||||
```rs
|
||||
impl <T,U:From<T>,E> From<Result<T,E>> for Result<U,E> {
|
||||
fn from(val:Result<T,E>){
|
||||
|
|
@ -28,9 +29,10 @@ impl <T,U:From<T>,E> From<Result<T,E>> for Result<U,E> {
|
|||
}
|
||||
}
|
||||
```
|
||||
|
||||
That's it the entire definition and implementation of what I want in just 5 lines of code where 2 of them are just curly brackets. So why doesn't that work?
|
||||
|
||||
### The motherf\*cking identity implementation
|
||||
### The motherf*cking identity implementation
|
||||
|
||||
Yeah, the issue is that rust implements `From<T> for T` so all types can be gotten from themselves. The issue with this is twofold
|
||||
1. it means that U can be T which means that
|
||||
|
|
@ -51,6 +53,7 @@ Nope
|
|||
Future Pagwin note: it turns out that solving this is [really hard](https://hackmd.io/@BoxyUwU/BJ6_bfmD0#Why-does-generic_const_exprs-not-work)
|
||||
|
||||
Oh yeah, there was a separate hiccup. Yeah that hiccup came from me trying to do SI-derived units via const generics, my minimum viable product version looked something like this
|
||||
|
||||
```rs
|
||||
struct Measure<const km:i16, const sec:i16, const kg:i16>{
|
||||
amount:f64
|
||||
|
|
@ -65,6 +68,7 @@ impl<const km1:i16, const sec1:i16,const kg1:i16,const km2:i16, const sec2:i16,c
|
|||
}
|
||||
}
|
||||
```
|
||||
|
||||
and had I not run into my type hiccup Mul would've been implemented similarly and Add and Sub would've been trivial. Alas, rust doesn't allow this to compile because `cannot perform const operation using kg1` and so on for all the other const generics... what? What do you mean I can't do a const operation with a const wtf ~~I also got an error that said something about how an associated type wasn't allowed when I set Rhs but shhhhh~~. Yeah in the compiler I imagine that allowing for calculating const generics with values that are const/const generics is relatively easy so I hope that does get improved at some point.
|
||||
|
||||
## Conclusion
|
||||
|
|
|
|||
|
|
@ -35,8 +35,6 @@ That said, for work done in this class, so far I've.
|
|||
- setup some docker files and a docker compose for the client (being worked on by my project partner), the server and a reverse proxy
|
||||
- Converted some json blobs of OCCTs api into differently shaped json blobs so I can avoid writing code that does more than serve static files to the extent possible
|
||||
|
||||
|
||||
|
||||
## 2025/03/01-2025/03/07
|
||||
|
||||
So a few things happened. First we had our first meeting going over a prior sprint. Learned that those meetings are reasonably loose in what needs to be done but also are expected on Monday and need the relevant folder made ahead of time.
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ Lol I redid it again and wrote a blog post on it but didn't publish it so that's
|
|||
## Music notation
|
||||
|
||||
So about a week ago I saw [this tantacrul video](https://www.youtube.com/watch?v=Eq3bUFgEcb4) and towards the start of it I decided to come up with a new music notation for fun. I yeeted the notes because I thought arbitrary symbols seemed dumb made use of color to communicate things like sharps and other symbols while keeping the current number of bars. Here's what I wrote down with little to no influence from the video.
|
||||
|
||||
```
|
||||
rather than arbitrary symbols lets use lines
|
||||
length is encoded via the length of a line
|
||||
|
|
@ -50,6 +51,7 @@ all notes need to end in a black endpoint to visually clarify the end of a note
|
|||
|
||||
background staff I think it's called is kept the same so pitch is clear
|
||||
```
|
||||
|
||||
After watching the video I realized that I had reinvented piano bars and that I left a lot unspecified and had 2 problem, 1) short notes are hard to diferentiate from each other 2) this setup screws over color blind people. There are hacky solutions to those problems and I could work on making this system more complete but I did it for fun and I'm bored of it now so I won't.
|
||||
|
||||
## I'm dipping my toes in haskell and I made a monstrosity
|
||||
|
|
|
|||
|
|
@ -5,21 +5,27 @@ date: 2021-07-03
|
|||
draft: false
|
||||
tags: []
|
||||
---
|
||||
|
||||
## Prelude
|
||||
|
||||
No this isn't comprehensive, so no you won't be able to immediately go start making something after having read this and no this doesn't cover everything you might possibly run into, see [#The Asterisk](#The%20Asterisk). Also the examples will be in Pseudocode so this isn't a generic python/javascript tutorial :P.
|
||||
|
||||
## The Asterisk
|
||||
|
||||
If you didn't notice the asterisk in the title next to every, now you know there is one. The reason for that asterisk is that this blog only covers stuff that is common(as built in language features) in procedural programming. If you don't know what "procedural" means don't worry about it and pretend that this covers every programming language.
|
||||
|
||||
## The Data
|
||||
|
||||
Fundamentally programming is about the manipulation of data for varying purposes. On occasion a programmer will want data to be displayed which can be accomplished in many ways. For the purposes of this blog `DISPLAY(some_data)` indicates that whatever `some_data` is should be displayed. Now that data can be displayed, what data can we have? There are a few different types of data we can have<sup>[1](#1)</sup>. For now I'll only specify 3. Those 3 are strings, numbers and booleans. First there are strings, more commonly known as text. In many programming languages, including our pseudocode, we can create a string in our program by surrounding some text with quotes `"like this"`. Our second type is numbers which hold numbers. We can of course add, subtract, multiply, divide and raise to a power all of these numbers with the notation of `number1 + number2`, `number1 - number2`, `number1 * number2`, `number1 / number2` and `number1 ^ number2` respectively. Last but not least we have booleans which can hold the values of `true` and `false`. Just like how we can apply different operations onto different number we also have some operations we can apply to booleans. First we have `NOT` which will take a boolean and give back the opposite boolean e.g. true → false and false → true. Second we have `OR` which takes 2 booleans and gives back true if either of them are true and false otherwise. Third we have `AND` which takes 2 booleans and gives back true only if both of them are true and false otherwise. Finally we have `XOR` which will only give back true if only 1 of it's inputs is true eg `true XOR true` → false and `false XOR false` → false but `true XOR false` → true.
|
||||
|
||||
## Variables
|
||||
|
||||
Variables allow for the storage of data in named<sup>[2](#2)</sup> buckets which is very useful especially when taking input that can change at runtime. We can put a value into a variable like so `someVar ← "a string"` which would set the variable someVar to the string value of "a string".
|
||||
|
||||
|
||||
## Conditionals
|
||||
|
||||
Conditionals(specified with `IF`) are statements that can allow us to gate code behind some condition being true. Besides the obvious of just specifying a boolean value ourself we can also perform certain tests on other values to get booleans. First we can see if 2 values are the same i.e. the same type and the same value of that type which we can do with a simple `value1 = value2` for ease of syntax we can also see if they are not equal to each other with `value1 ≠ value2` furthermore if both values are numbers we can see if one if greater than or less than the other with `value1 > value2` and `value1 < value2` respectively. I should probably provide an example snippet of pseudocode
|
||||
|
||||
```
|
||||
someNum ← 5
|
||||
"Sidenote: when I have a statement that can take multiple lines of code"
|
||||
|
|
@ -35,7 +41,9 @@ ELSE {
|
|||
```
|
||||
|
||||
## Procedures
|
||||
|
||||
Procedures are blocks of code which we can run when we want without having to copy and paste, furthermore we can change the values stored in variables in the procedure that the procedure lets us commonly referred to as arguments, an example of a procedure(in most procedural languages) is one which lets the programmer display some text which in our case is called `DISPLAY` the exact implementation of which is mostly unimportant for us to declare our own procedure we'll just put the word `PROC` before the name of our procedure and the arguments that our procedure takes separated by commas in parentheseses after the procedure's name for ane example of one of our user defined procedures I'll just write a procedure that prints out "Hello!" and then whatever was provided as an argument to it
|
||||
|
||||
```
|
||||
PROC helloProcedure (name) {
|
||||
DISPLAY("Hello!")
|
||||
|
|
@ -44,16 +52,22 @@ PROC helloProcedure (name) {
|
|||
"the code above hasn't been run yet we need to call the procedure in order to run it like below"
|
||||
helloProcedure("World")
|
||||
```
|
||||
|
||||
## Lists
|
||||
|
||||
lists are a methodology of storing multiple data values within a single variable<sup>[4](#4)</sup>. When you want to store or retrieve a value from a list you need to specify at what position in the list you want to retrieve the value from via a number with the indexing of that list commonly starting from 0 with certain very special languages which choose to start or allow the programmer to start lists from indices other than 0. When declaring a list in our pseudocode I'll use square brackets surrounded comma separated values of the list, the size of the list will not be set in stone for convenience but it should be noted that in most programming languages you need to be explicit when making a list larger. An example of creating and using a list is shown below
|
||||
|
||||
```
|
||||
someList <- ["First item", "Second item", "meh item"]
|
||||
someList[2] <- "Third item"
|
||||
"First item should be displayed when the line of code below is run"
|
||||
DISPLAY(someList[0])
|
||||
```
|
||||
|
||||
## Loops
|
||||
|
||||
loops are kinda self explanatory as they loop running code repeatedly. Commonly there are 2 types of loop in programming languages and when there are more they can be easily described in relation to these 2. These 2 types of loop are called a `WHILE` loop and a `FOREACH`<sup>[3](#3)</sup> loop. A `WHILE` loop will repeatedly run whatever code is inside of them as long as a condition is met, a foreach loop will go through each item in a collection of items such as a list and run some code using that item. Both loops make working with lists much easier because we don't need to write out the code for every single list entry and if the list we may be dealing with will have a size that cannot be known at the time of writing the program might, with the combination of some methodology of retrieving the length of the list, be the only option. the syntax for `FOREACH` loops will borrow from the syntax for assignment with the variable which will go through each value in our list being assigned to and the loop values are being pulled from being used as the value to assign from
|
||||
|
||||
```
|
||||
i <- 0
|
||||
"initializing an empty list which will be filled with the while loop below"
|
||||
|
|
@ -75,10 +89,13 @@ FOREACH n <- dataSet {
|
|||
DISPLAY(n)
|
||||
}
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
That should cover the basic syntax features someone trying to learn a procedural programming langauge should be trying to learn. Some programming languages have macros, terenary statements and extensive standard libraries which will have a bunch of useful utilities those are best learned once these basic syntax features are understood and different langauges tend to differ regarding what and whether these features are include
|
||||
|
||||
## Footnotes
|
||||
|
||||
<a href="./#1" name="1">1</a> - I see you in the back there person who wants to be technically correct(if I wasn't writing this it'd be me) with your statements about how in C a lot of what differentiates types is just dereferencing the integer pointers down to their values and pretending chars aren't integers but I don't care so shut up.
|
||||
|
||||
<a href="./#2" name="2">2</a> - most programming languages have certain specifications on what you can name your variables and there's also reccomended ways you should name your variables and both of these can vary per language so you should probably read their documentation for specifics but for my purposes I'm gonna stick to [camelCase](https://en.wikipedia.org/wiki/Camel_case) using only english letters as I'm unaware of any non-esoteric programing languages which disallow such naming
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ Example:
|
|||
|
||||
To provide an example I shall quote this post.
|
||||
|
||||
"Today I learned that **emphasis mine** refers to when a quote has emphasis added to it by the person quoting" \[emphasis mine\]
|
||||
"Today I learned that **emphasis mine** refers to when a quote has emphasis added to it by the person quoting" [emphasis mine]
|
||||
|
||||
## What sic means
|
||||
|
||||
|
|
@ -28,17 +28,16 @@ Example:
|
|||
|
||||
I will not quote this post in this example because doing the same thing twice without variation seems iffy.
|
||||
|
||||
"I will not quote this post in this example \[sic\] because doing the same thing twice without variation seems iffy"
|
||||
"I will not quote this post in this example [sic] because doing the same thing twice without variation seems iffy"
|
||||
|
||||
After posting and sharing with a friend they got confused so quick clarification. The error is the statement that I won't quote this post. The reason that's an error is that that quote is a quote from this post.
|
||||
|
||||
### Above may be wrong/incomplete
|
||||
|
||||
Upon having this proofread by a different friend (before friend mentioned in clarification) they pointed out that my understanding of \[sic\] is incomplete. Linking me to a [merriam webster post](https://www.merriam-webster.com/wordplay/sic-meaning-usage-editorial-citation) which says.
|
||||
Upon having this proofread by a different friend (before friend mentioned in clarification) they pointed out that my understanding of [sic] is incomplete. Linking me to a [merriam webster post](https://www.merriam-webster.com/wordplay/sic-meaning-usage-editorial-citation) which says.
|
||||
|
||||
> [*Sic*](https://www.merriam-webster.com/dictionary/sic) usually appears in parentheses or brackets, sometimes with the letters in italics. In this context it means “intentionally so written.” On its own, *sic* means “so” or “thus” and can be found in phrases such as *sic transit gloria mundi* ("so passes away the glory of the world") and *sic semper tyrannis* ("thus ever to tyrants," the motto of the state of Virginia).
|
||||
|
||||
|
||||
> What is denoted by *sic* is that the word or phrase that precedes it occurs in the original passage being quoted or name being used and was not introduced by the writer doing the quoting.
|
||||
|
||||
If this correction is wrong the fault is on me for failing to do further research and double check before posting this.
|
||||
|
|
|
|||
Loading…
Reference in a new issue