From e02599bc5e81aa3bfcf419a3b97bd09fe518b1dd Mon Sep 17 00:00:00 2001 From: Anthony Campolo <12433465+ajcwebdev@users.noreply.github.com> Date: Thu, 3 Oct 2024 14:11:52 -0500 Subject: [PATCH 1/3] astro autosite generator --- .gitignore | 2 +- astro/.gitignore | 25 + astro/.npmrc | 1 + astro/README.md | 3 + astro/astro.config.ts | 19 + astro/package.json | 27 + astro/public/robots.txt | 4 + astro/src/components/BaseHead.astro | 70 + astro/src/components/FormattedDate.astro | 17 + astro/src/components/Paginator.astro | 29 + astro/src/components/blog/Hero.astro | 47 + astro/src/components/blog/PostPreview.astro | 25 + astro/src/components/blog/TOC.astro | 20 + astro/src/components/blog/TOCHeading.astro | 27 + astro/src/content/config.ts | 28 + ...-03-the-navbar-live-pod-planning-prompt.md | 2505 +++++++++++++++++ .../post/2024-07-18-the-navbar-live-prompt.md | 1713 +++++++++++ astro/src/data/post.ts | 39 + astro/src/env.d.ts | 7 + astro/src/layouts/Base.astro | 53 + astro/src/layouts/BlogPost.astro | 66 + astro/src/pages/404.astro | 13 + astro/src/pages/index.astro | 48 + astro/src/pages/posts/[...page].astro | 61 + astro/src/pages/posts/[...slug].astro | 24 + astro/src/pages/rss.xml.ts | 19 + astro/src/site.config.ts | 59 + astro/src/styles/global.css | 11 + astro/src/types.ts | 48 + astro/src/utils/index.ts | 66 + astro/tsconfig.json | 18 + 31 files changed, 5093 insertions(+), 1 deletion(-) create mode 100644 astro/.gitignore create mode 100644 astro/.npmrc create mode 100644 astro/README.md create mode 100644 astro/astro.config.ts create mode 100644 astro/package.json create mode 100644 astro/public/robots.txt create mode 100644 astro/src/components/BaseHead.astro create mode 100644 astro/src/components/FormattedDate.astro create mode 100644 astro/src/components/Paginator.astro create mode 100644 astro/src/components/blog/Hero.astro create mode 100644 astro/src/components/blog/PostPreview.astro create mode 100644 astro/src/components/blog/TOC.astro create mode 100644 astro/src/components/blog/TOCHeading.astro create mode 100644 astro/src/content/config.ts create mode 100644 astro/src/content/post/2024-07-03-the-navbar-live-pod-planning-prompt.md create mode 100644 astro/src/content/post/2024-07-18-the-navbar-live-prompt.md create mode 100644 astro/src/data/post.ts create mode 100644 astro/src/env.d.ts create mode 100644 astro/src/layouts/Base.astro create mode 100644 astro/src/layouts/BlogPost.astro create mode 100644 astro/src/pages/404.astro create mode 100644 astro/src/pages/index.astro create mode 100644 astro/src/pages/posts/[...page].astro create mode 100644 astro/src/pages/posts/[...slug].astro create mode 100644 astro/src/pages/rss.xml.ts create mode 100644 astro/src/site.config.ts create mode 100644 astro/src/styles/global.css create mode 100644 astro/src/types.ts create mode 100644 astro/src/utils/index.ts create mode 100644 astro/tsconfig.json diff --git a/.gitignore b/.gitignore index 6609ca4..e3f42ae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ node_modules .DS_Store -content +/content whisper.cpp package-lock.json .env diff --git a/astro/.gitignore b/astro/.gitignore new file mode 100644 index 0000000..1ce09ef --- /dev/null +++ b/astro/.gitignore @@ -0,0 +1,25 @@ +# build output +dist/ +.output/ + +# dependencies +node_modules/ + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +package-lock.json + +# environment variables +.env +.env.production + +# macOS-specific files +.DS_Store + +# misc +*.pem +.cache +.astro \ No newline at end of file diff --git a/astro/.npmrc b/astro/.npmrc new file mode 100644 index 0000000..b7425b9 --- /dev/null +++ b/astro/.npmrc @@ -0,0 +1 @@ +enable-pre-post-scripts=true \ No newline at end of file diff --git a/astro/README.md b/astro/README.md new file mode 100644 index 0000000..bb7f694 --- /dev/null +++ b/astro/README.md @@ -0,0 +1,3 @@ +# Astro Autoshow + +TODO \ No newline at end of file diff --git a/astro/astro.config.ts b/astro/astro.config.ts new file mode 100644 index 0000000..915fc72 --- /dev/null +++ b/astro/astro.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from "astro/config" +import mdx from "@astrojs/mdx" +import sitemap from "@astrojs/sitemap" +import icon from "astro-icon" +import expressiveCode from "astro-expressive-code" +import { expressiveCodeOptions } from "./src/site.config" + +// https://astro.build/config +export default defineConfig({ + site: "https://autoshow.sh/", + integrations: [ + expressiveCode(expressiveCodeOptions), + icon(), + sitemap(), + mdx(), + ], + // https://docs.astro.build/en/guides/prefetch/ + prefetch: true, +}) \ No newline at end of file diff --git a/astro/package.json b/astro/package.json new file mode 100644 index 0000000..31e58c1 --- /dev/null +++ b/astro/package.json @@ -0,0 +1,27 @@ +{ + "name": "astro-autogen", + "scripts": { + "dev": "astro dev", + "start": "astro dev", + "build": "astro build", + "preview": "astro preview", + "check": "astro check" + }, + "dependencies": { + "@astrojs/mdx": "^3.1.5", + "@astrojs/rss": "^4.0.7", + "@astrojs/sitemap": "^3.1.6", + "astro": "^4.15.4", + "astro-expressive-code": "^0.36.1", + "astro-icon": "^1.1.1", + "satori": "^0.10.14", + "satori-html": "^0.3.2", + "sharp": "^0.33.5" + }, + "devDependencies": { + "@iconify-json/mdi": "^1.2.0", + "@resvg/resvg-js": "^2.6.2", + "mdast-util-to-string": "^4.0.0", + "typescript": "^5.6.2" + } +} diff --git a/astro/public/robots.txt b/astro/public/robots.txt new file mode 100644 index 0000000..56774e1 --- /dev/null +++ b/astro/public/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Allow: / + +Sitemap: https://autoshow.sh/sitemap-index.xml \ No newline at end of file diff --git a/astro/src/components/BaseHead.astro b/astro/src/components/BaseHead.astro new file mode 100644 index 0000000..afbcdd2 --- /dev/null +++ b/astro/src/components/BaseHead.astro @@ -0,0 +1,70 @@ +--- +import type { SiteMeta } from "@/types" +import { siteConfig } from "@/site-config" +import "../styles/global.css" + +type Props = SiteMeta + +const { articleDate, description, ogImage, title } = Astro.props + +const titleSeparator = "•" +const siteTitle = `${title} ${titleSeparator} ${siteConfig.title}` +const canonicalURL = new URL(Astro.url.pathname, Astro.site) +const socialImageURL = new URL(ogImage ? ogImage : "/social-card.png", Astro.url).href +--- + + + + + + +{siteTitle} + +{/* Icons / Favicon */} + + + + + + +{/* Primary Meta Tags */} + + + + +{/* Theme Colour */} + + +{/* Open Graph / Facebook */} + + + + + + + + + +{ + articleDate && ( + <> + + + + ) +} + +{/* Twitter */} + + + + + + +{/* Sitemap */} + + +{/* RSS auto-discovery */} + + + diff --git a/astro/src/components/FormattedDate.astro b/astro/src/components/FormattedDate.astro new file mode 100644 index 0000000..e22b0b9 --- /dev/null +++ b/astro/src/components/FormattedDate.astro @@ -0,0 +1,17 @@ +--- +import type { HTMLAttributes } from "astro/types" +import { getFormattedDate } from "@/utils" + +type Props = HTMLAttributes<"time"> & { + date: Date + dateTimeOptions?: Intl.DateTimeFormatOptions +} + +const { date, dateTimeOptions, ...attrs } = Astro.props + +const postDate = getFormattedDate(date, dateTimeOptions) +--- + + diff --git a/astro/src/components/Paginator.astro b/astro/src/components/Paginator.astro new file mode 100644 index 0000000..b5a1845 --- /dev/null +++ b/astro/src/components/Paginator.astro @@ -0,0 +1,29 @@ +--- +import type { PaginationLink } from "@/types" + +interface Props { + nextUrl?: PaginationLink + prevUrl?: PaginationLink +} + +const { nextUrl, prevUrl } = Astro.props +--- + +{ + (prevUrl || nextUrl) && ( + + ) +} diff --git a/astro/src/components/blog/Hero.astro b/astro/src/components/blog/Hero.astro new file mode 100644 index 0000000..514d11e --- /dev/null +++ b/astro/src/components/blog/Hero.astro @@ -0,0 +1,47 @@ +--- +import type { CollectionEntry } from "astro:content" +import { Image } from "astro:assets" +import FormattedDate from "../FormattedDate.astro" + +interface Props { + content: CollectionEntry<"post"> +} + +const { + content: { data }, +} = Astro.props + +const dateTimeOptions: Intl.DateTimeFormatOptions = { + month: "long", +} +--- + +{ + data.coverImage && ( +
+ {data.coverImage.alt} +
+ ) +} +{data.draft ? (Draft) : null} +

+ {data.title} +

+
+

+ +

+ { + data.updatedDate && ( + + Last Updated: + + + ) + } +
diff --git a/astro/src/components/blog/PostPreview.astro b/astro/src/components/blog/PostPreview.astro new file mode 100644 index 0000000..2aae796 --- /dev/null +++ b/astro/src/components/blog/PostPreview.astro @@ -0,0 +1,25 @@ +--- +import type { HTMLTag, Polymorphic } from "astro/types" +import type { CollectionEntry } from "astro:content" +import { getPostSortDate } from "@/data/post" +import FormattedDate from "../FormattedDate.astro" + +type Props = Polymorphic<{ as: Tag }> & { + post: CollectionEntry<"post"> + withDesc?: boolean +} + +const { as: Tag = "div", post, withDesc = true } = Astro.props +const postDate = getPostSortDate(post) +--- + + + {post.data.draft && (Draft) } + + {post.data.title} + - + +

+ {withDesc && {post.data.description}} +
+
\ No newline at end of file diff --git a/astro/src/components/blog/TOC.astro b/astro/src/components/blog/TOC.astro new file mode 100644 index 0000000..5c52556 --- /dev/null +++ b/astro/src/components/blog/TOC.astro @@ -0,0 +1,20 @@ +--- +import type { MarkdownHeading } from "astro" +import { generateToc } from "@/utils" +import TOCHeading from "./TOCHeading.astro" + +interface Props { + headings: MarkdownHeading[] +} + +const { headings } = Astro.props + +const toc = generateToc(headings) +--- + + diff --git a/astro/src/components/blog/TOCHeading.astro b/astro/src/components/blog/TOCHeading.astro new file mode 100644 index 0000000..57bdcc3 --- /dev/null +++ b/astro/src/components/blog/TOCHeading.astro @@ -0,0 +1,27 @@ +--- +import type { TocItem } from "@/utils" + +interface Props { + heading: TocItem +} + +const { + heading: { slug, children, text }, +} = Astro.props +--- + +
  • + {text} + { + !!children.length && ( + + ) + } +
  • diff --git a/astro/src/content/config.ts b/astro/src/content/config.ts new file mode 100644 index 0000000..21fd4da --- /dev/null +++ b/astro/src/content/config.ts @@ -0,0 +1,28 @@ +import { defineCollection, z } from "astro:content"; + +const post = defineCollection({ + schema: ({ image }) => + z.object({ + coverImage: z + .object({ + alt: z.string(), + src: image(), + }) + .optional(), + description: z.string().min(10).max(200), + draft: z.boolean().default(false), + ogImage: z.string().optional(), + publishDate: z + .string() + .or(z.date()) + .transform((val) => new Date(val)), + title: z.string().max(60), + updatedDate: z + .string() + .optional() + .transform((str) => (str ? new Date(str) : undefined)), + }), + type: "content", +}); + +export const collections = { post }; diff --git a/astro/src/content/post/2024-07-03-the-navbar-live-pod-planning-prompt.md b/astro/src/content/post/2024-07-03-the-navbar-live-pod-planning-prompt.md new file mode 100644 index 0000000..4fc3634 --- /dev/null +++ b/astro/src/content/post/2024-07-03-the-navbar-live-pod-planning-prompt.md @@ -0,0 +1,2505 @@ +--- +showLink: "https://www.youtube.com/watch?v=c0A75c9e9ks" +channel: "Navbar" +channelURL: "https://www.youtube.com/@the_navbar" +title: "The NavBar LIVE — Pod Planning 📆" +description: "A candid planning session for 'The NavBar' podcast, discussing future format changes, guest appearances, and content ideas to improve the show's structure and appeal." +publishDate: "2024-07-03" +ogImage: "https://i.ytimg.com/vi/c0A75c9e9ks/maxresdefault.jpg" +--- + +## Episode Summary + +In this episode, the hosts of "The NavBar" podcast, Simon and John, engage in an informal yet productive planning session for their show's future. They discuss several key areas for improvement, including maintaining a more consistent 30-minute format, incorporating guest appearances, and introducing new segment ideas such as game-style episodes and listener Q&As. The conversation touches on the challenges of balancing work commitments with podcast production, the importance of timely releases, and strategies for creating more engaging content. Throughout the discussion, the hosts share personal anecdotes and tangential thoughts, showcasing their chemistry and the show's casual style. The episode concludes with their traditional "sick picks" segment, where they enthusiastically recommend DaVinci Resolve video editing software and the art of making sourdough bread. + +## Chapters + +### 00:00 - Introduction and Current Challenges + +The hosts begin by discussing the current state of their podcast and some of the challenges they face. They touch on the difficulties of consistently releasing episodes and maintaining a regular schedule amidst their busy lives. + +This opening segment sets the tone for the planning session, highlighting the hosts' desire to improve their podcast's structure and consistency. They discuss the impact of their busy schedules on podcast production and express their commitment to making positive changes. The conversation provides insight into the behind-the-scenes aspects of podcasting and the challenges faced by content creators. + +### 03:00 - Planning for Future Episodes + +Simon and John delve into their ideas for improving the podcast's format and content. They discuss aiming for a more consistent 30-minute episode length, incorporating guest appearances, and introducing new segment ideas. + +In this section, the hosts brainstorm various ways to enhance their podcast. They consider the benefits of shorter, more focused episodes and discuss strategies for incorporating guest speakers. The conversation reveals their commitment to evolving the show and providing more value to their listeners. They also touch on the importance of maintaining their casual, conversational style while implementing these changes. + +### 15:00 - New Content Ideas and Audience Engagement + +The hosts explore ideas for new types of content, including game-style episodes and listener Q&As. They also discuss ways to increase audience engagement and gather feedback from their listeners. + +This chapter showcases the hosts' creativity and desire to diversify their content. They consider implementing trivia segments, discussing programming concepts in a more interactive format, and incorporating listener questions. The conversation highlights their aim to make the podcast more engaging and interactive for their audience, while also potentially expanding their listener base. + +### 29:56 - Sick Picks and Wrap-up + +In their traditional "sick picks" segment, Simon enthusiastically recommends DaVinci Resolve video editing software, while John shares his passion for making sourdough bread. They conclude the episode by summarizing their plans for the podcast's future. + +The final segment of the episode provides insight into the hosts' personal interests and recommendations. Simon's detailed explanation of DaVinci Resolve showcases his enthusiasm for video editing and its relevance to content creation. John's recommendation of sourdough baking offers a contrasting, non-tech related pick, highlighting the diverse interests of the hosts. The conversation wraps up with a brief summary of their plans for improving the podcast, ending on a positive and forward-looking note. + +## Transcript + +[00:00] - Hello, everybody. + +[00:03] - Hello, welcome to the podcast. + +[00:06] - Yeah. + +[00:07] - We're just gonna jump straight in. + +[00:08] - Woo. + +[00:10] - Welcome to the podcast, another week. + +[00:12] Simon, how are you? + +[00:14] - Another week, I am very cold. + +[00:16] You might hear in this episode + +[00:17] that we both have a little crookiness in our voice. + +[00:20] It's the middle of winter in Australia. + +[00:22] It's a miserable rainy day outside. + +[00:24] You can't see with the curtains, + +[00:26] but it's cold and humid and kids, + +[00:29] we were lamenting about having kids in winter + +[00:32] and they always bring some sickness from school + +[00:35] and then you just go in circle + +[00:38] of like constantly being sick at home. + +[00:40] - You just can't avoid it. + +[00:41] Like kids just do not understand + +[00:44] not spreading snot and saliva all over each other's faces. + +[00:49] They just haven't learned that yet. + +[00:51] And so it's just impossible + +[00:53] to not catch some kind of sickness throughout winter. + +[00:56] We actually had a not great one this weekend. + +[01:00] We were away with a bunch of our kids' friends + +[01:05] and their parents. + +[01:06] It was a really lovely weekend away. + +[01:07] We went to the beach, + +[01:08] even though it was freezing cold and raining, + +[01:10] but it was very nice. + +[01:11] - I was gonna say. + +[01:11] (laughing) + +[01:13] - And while we were there, + +[01:16] the first night we were there, + +[01:17] our youngest, he's about two years old, + +[01:19] vomited during the night. + +[01:21] And that's always like a, + +[01:24] is it one vomit or is it like, + +[01:26] is it an isolated thing? + +[01:28] Is it like a cold or like, + +[01:31] is it due to him being just regularly sick + +[01:33] or is it this thing called gastro + +[01:35] or I'm sure there's a non-Australian name for this, + +[01:39] but basically like a stomach flu + +[01:41] that goes through the whole house. + +[01:43] And that is unstoppable. + +[01:44] Like everyone in the house is just vomiting + +[01:47] and everything else that comes along with that. + +[01:50] You don't have much control over your stomach. + +[01:53] And so we were like, + +[01:54] oh no, we're in this house, + +[01:56] like in close quarters + +[01:57] with like all of these other families + +[01:59] and we're gonna spread it around. + +[02:00] We're all just gonna be like locked down to this house + +[02:03] or, you know, taking turns with the toilet. + +[02:06] But then it didn't happen again. + +[02:09] And I think maybe he, + +[02:10] he maybe has like an insensitivity to strawberries. + +[02:15] Sometimes when he has a bunch of strawberries, + +[02:16] he just like, you know, vomits + +[02:18] or it really upsets his stomach. + +[02:20] And then other times he's fine. + +[02:21] So it seemed fine. + +[02:22] Like awesome, okay. + +[02:24] No one else got sick the whole time, + +[02:25] which was really good. + +[02:26] And then of course on the drive home, + +[02:28] while trying to get back to, you know, + +[02:31] from an exhausted weekend + +[02:33] where everyone just needs to rest, + +[02:35] he threw up in the car + +[02:36] and then we had to take everything out of the car + +[02:38] and like clean everything + +[02:40] and like change him + +[02:41] and try and get- + +[02:42] - It was down the creases of the baby seat + +[02:45] and in the- + +[02:45] - Yeah, yeah. + +[02:46] No, it's deep in the baby seat. + +[02:47] (laughs) + +[02:49] - I've been there so many times. + +[02:51] Drive the rest of the way home + +[02:52] with that like stench of vomit. + +[02:54] But yes, he seems fine now, + +[02:57] but it was a bit of a stressful time. + +[03:00] - Oh man. + +[03:01] My kids are a bit older, + +[03:02] but I've been through so many experiences just like this. + +[03:05] Maybe that's the vomit episode, + +[03:08] but I remember going to the snowy mountains + +[03:11] from New South Wales, Sydney, + +[03:12] where we used to live. + +[03:14] There's at some point you, + +[03:15] as mountains tend to do, + +[03:16] you go to windy up and down roads. + +[03:19] And we were like literally, + +[03:21] I don't know exactly how far from the ski, + +[03:23] but we were going on a ski trip + +[03:25] and that was the last stretch. + +[03:26] We had refueled petrol and we were like, + +[03:28] okay, let's put the gear on the kids + +[03:30] so that like we get out of the car + +[03:31] and we go straight skiing. + +[03:32] (laughs) + +[03:34] Of course, like with like seven layers of jackets + +[03:36] and gloves and stuff. + +[03:37] (laughs) + +[03:38] - It's not a good time for it. + +[03:39] - This is when my daughter decided to be road sick + +[03:42] and do the classic like vomit over every single possible, + +[03:47] like part. + +[03:49] - It's amazing how far it can spray. + +[03:51] Again, like when you don't, + +[03:52] when you're a kid and you just don't understand, + +[03:54] like vomit into a bucket, + +[03:56] like put it into a container, + +[03:58] keep it contained. + +[03:59] No, just spray it. + +[04:01] (laughs) + +[04:03] - When you said finish the drive + +[04:06] with the stench in the back, + +[04:07] that's exactly, + +[04:08] I remember like finishing this. + +[04:10] It's about seven hours from Sydney + +[04:11] and the last 20, 30 minutes were like, + +[04:15] and of course we didn't go skiing. + +[04:16] We went, we parked on the parking + +[04:17] and we watched everyone go skiing + +[04:19] while we change all the layers. + +[04:21] And we're like, okay, + +[04:21] now we don't have ski gear. + +[04:22] How can we ski? + +[04:23] (groans) + +[04:26] At the price of the ski trip in Australia + +[04:28] and you're having a wonderful time. + +[04:30] (laughs) + +[04:32] - Yeah, that sounds like a stressful trip. + +[04:36] At least it wasn't after you purchased everything + +[04:38] and then realized you couldn't go. + +[04:39] So like you'd purchased a Lyft ticket + +[04:42] for the ludicrous amount of hundreds of dollars + +[04:44] that it is for a Lyft pass for the day + +[04:46] and hiring gear. + +[04:48] - Yeah, we had to still rent stuff. + +[04:49] We ended up putting her, + +[04:51] shout out to Perisher in the snowy mountains, + +[04:55] which happens to have a daycare on the mountain, + +[04:57] that the tourism industry is very well polished down there + +[05:02] so that you can arrive with nothing, + +[05:04] like in jeans and normal shoes. + +[05:06] And you literally like, you know, + +[05:09] when you do the airport check + +[05:10] where they make you spread your arms and legs + +[05:11] for the x-ray. + +[05:12] You go like this and they measure you, + +[05:14] give you jacket, pants, stuff. + +[05:15] Do you need childcare? + +[05:16] Do you need lesson for the, + +[05:17] like you, if you have money, + +[05:19] you tick all the boxes and it's all taken care. + +[05:22] - Yeah. + +[05:23] - That's pretty impressive. + +[05:24] - Yeah, it is very convenient. + +[05:25] Is that not a thing in the rest of the world? + +[05:27] Do they, like, I guess smaller mountains + +[05:28] just wouldn't have the facilities to like, + +[05:32] to have things like childcare and kids' lessons + +[05:35] and stuff, right? + +[05:35] - Yeah, so Perisher is the equivalent + +[05:38] if you were in Switzerland, + +[05:39] that would be Zermatt or Camp Montana, + +[05:41] like the big, big, fancy resorts. + +[05:44] I grew up in a proximity of three or four + +[05:47] really small, low-key resorts. + +[05:49] So there would be one ski shop somewhere in the village + +[05:53] and everyone rocks up with stuff + +[05:56] because they know, like, you gotta organize it beforehand + +[05:59] 'cause it's a small, low-key resort, + +[06:00] but tourists would show up + +[06:01] and then line up in this one sports shop, + +[06:05] which has like, not particularly enthusiastic + +[06:09] and hospitality-trained staff, + +[06:13] which are pretty grumpy and it takes forever to rent + +[06:16] and they don't have much choice. + +[06:17] And yeah, discovering how it works in Australia + +[06:21] and also in Park City, like a couple of months ago in Utah, + +[06:25] just you go and you can choose six models + +[06:28] and they have these demo boards and it's all super, + +[06:30] it takes three minutes to get all your gear. + +[06:33] Yeah, my experience renting stuff in Switzerland + +[06:35] is way more, it's also 10 years in the past, + +[06:38] so you might have caught up, + +[06:40] but I've always thought like, + +[06:42] when I discovered the proper way of doing it, + +[06:44] I was like, oh, we are so behind in terms of like, + +[06:48] providing an experience and a like, happy, quick service. + +[06:51] It's just like, yeah, stand there + +[06:53] and they ignore you for like 45 minutes + +[06:54] and you're like, + +[06:55] you can see people who drove from Geneva airport + +[06:58] and they're like, I wanna go ski, + +[06:59] just give me something and I'll pay, whatever. + +[07:01] - But that's the thing, they're not turning around + +[07:04] and being like, I'm flying back out of here, + +[07:05] so they don't really have a choice. + +[07:08] There's no reason to increase the customer service. + +[07:12] - Yeah, so my friend, + +[07:14] we have promised our very large audience of seven, + +[07:18] which is my mom, your partner. + +[07:22] - Yeah, Kara, when she gets around to editing our podcast, + +[07:27] she has to watch it. + +[07:29] What about our active listeners? + +[07:30] - The view count goes, + +[07:32] but we have promised for like three or four weeks now + +[07:35] to kind of talk about what we wanna do, + +[07:38] have like some sort of a little adult like plan + +[07:41] with the podcast in terms of like, + +[07:43] what sort of, if we wanna have different topics + +[07:45] or maybe bring some guests along + +[07:47] and maybe think about the format + +[07:49] a little bit more consistent. + +[07:51] Although we both agree, + +[07:52] it's kind of cool to be like YOLO free handing it + +[07:56] and kind of like, if it's a bit longer or a bit shorter + +[07:59] or a bit unstructured, this is good as well. + +[08:02] So do you have any specific topics + +[08:06] you would like to bring up? + +[08:07] We're going to do this finally this week. + +[08:10] - Yeah, goal number one. + +[08:12] Actually release the podcast. + +[08:14] This is something that I have not been doing well. + +[08:17] I think we have like- + +[08:18] - The what? + +[08:19] We have a podcast? + +[08:20] - We have an audio version of this, seriously? + +[08:23] You wouldn't know it if you were subscribing to us + +[08:25] on Spotify or Apple podcast + +[08:30] or any of the other podcast platforms, + +[08:32] because I have been doing a terrible job + +[08:34] of actually uploading them after we finished recording. + +[08:37] When we finish recording, I'm always like, + +[08:39] all right, what do I need to do this week? + +[08:41] And just work takes over and my whole week disappears. + +[08:45] And so yes, we are going to get better + +[08:48] at actually releasing these right after we record the video, + +[08:52] quickly get it out in audio form + +[08:57] and also do a nicer version for YouTube, I reckon, + +[09:02] chopping off the start and end and maybe stacking us + +[09:07] or yeah, maybe putting it out on Twitter + +[09:09] or something like that. + +[09:10] Anyway, being more professional + +[09:11] about how we actually package it up and put it out + +[09:14] rather than just doing this live thing + +[09:16] that gets like, you know, 30 views after we're finished + +[09:20] and then being like, all right, cool, + +[09:21] we recorded another podcast. + +[09:23] Yeah, need to get better at it. + +[09:25] - That ties in perfectly in one thing I wanted to touch on + +[09:29] and you feel free to push back if you don't think it's good, + +[09:31] but I kind of want to try to have the episodes + +[09:35] a bit shorter intentionally. + +[09:37] And right now we kind of talk + +[09:39] and when it reaches one hour, + +[09:40] it's like, ah, we should just maybe cut it off + +[09:43] and then we go into the sick peaks for another half an hour. + +[09:47] And so I was like, I'd love the idea of aiming + +[09:49] for like have 30 minutes as like a sort of like guideline. + +[09:54] And when you reach 30 minutes, + +[09:56] you consider wrapping up with like a little sick peak. + +[09:58] So it ends up anywhere between 30 minutes and three hours. + +[10:02] But what you just said is something + +[10:07] that was going through my mind. + +[10:08] If we have little shorter recordings + +[10:10] and we try to start as closely + +[10:12] as the whatever time we choose, + +[10:14] we can use that like extra gain free time + +[10:19] after the recording to quickly, + +[10:22] like I always want to do this like short, + +[10:26] like, ah, this moment was really cool. + +[10:27] I want to do a little clip. + +[10:29] And then if you don't do it after, + +[10:30] you kind of forget about the episode. + +[10:31] But if we record and right after it's taken 45 minutes, + +[10:35] I'm like, oh, I've blocked an hour + +[10:36] and a bit for this recording. + +[10:38] I'm like, I can very quickly now in Notion + +[10:41] or anywhere just make notes. + +[10:43] These things we said we should do a clip + +[10:45] and kind of just do some like pre-planning + +[10:48] of how to make like extra content + +[10:51] or like write the description of the episode + +[10:55] and put it on the website or as a draft or whatever. + +[10:58] Like do that sort of prep work. + +[11:00] You can even maybe edit the audio. + +[11:02] Like, I don't, yeah, just do some of this admin stuff + +[11:06] we have to do because it's just us. + +[11:08] I mean, your wife is helping us, + +[11:11] but it's not like we have a team + +[11:13] of dedicated assistants doing lots of stuff, so. + +[11:16] - She's not helping enough. + +[11:17] Otherwise we would be, you know, + +[11:18] actually releasing the episodes. + +[11:20] - But I thought, yeah, shorter episode means more + +[11:23] easy to consume for the audience. + +[11:25] And then we can reclaim some of the time + +[11:28] that we can invest in doing this sort of like + +[11:31] adult professional management. + +[11:32] And usually I'm cutting you off from talking + +[11:35] and talking and talking at about 60 minutes. + +[11:37] So I'm happy to cut you off from talking and talking, + +[11:39] talking at about 30 minutes. + +[11:40] If you'd like. + +[11:41] - Speaking of. + +[11:41] (laughing) + +[11:44] No, yeah. + +[11:45] - But yeah, this sounds like a very good idea. + +[11:46] And I feel like just generally + +[11:47] like a 30 minute podcast is probably much more consumable + +[11:51] than a hour or, you know, most of our podcasts, + +[11:54] like you're saying, we start thinking + +[11:56] about wrapping it up at an hour. + +[11:57] Maybe we should start thinking about wrapping it up + +[11:59] at like 20 minutes and jump into seg pics + +[12:02] so that the whole thing is like 30 minutes. + +[12:04] 'Cause I think at the moment it's about. + +[12:06] - It's probably an hour plus. + +[12:07] - Close to an hour and a half for each of the episodes. + +[12:10] - Yeah, I don't want it to be too rigid and stuff. + +[12:14] Like I, one podcast I've started listening to, + +[12:16] I really, really enjoy is Matt Stauffer + +[12:19] from the Laravel ecosystem. + +[12:20] It's called The Business of Laravel. + +[12:22] And he talks about people who built real businesses + +[12:25] using Laravel and how they went about it. + +[12:27] So Aaron Francis was on there + +[12:29] and also his podcast partner, Ian Lansman. + +[12:33] And the whole discussion, + +[12:35] you can see Matt like being cognizant of the time. + +[12:38] He's like, oh, I want to ask these two questions. + +[12:40] I realized we have 10 minutes left. + +[12:41] And it is like, you can tell there's a timer + +[12:44] going on the side and they're trying to always stick + +[12:46] to half an hour to 40 minutes. + +[12:49] And I do like the fact that, you know, + +[12:52] it's going to be a condensed episode + +[12:53] and you can like around your lifestyle, + +[12:56] if you have a drive that is 20 minutes + +[12:59] and then you listen at 1X, 1.5X or two, + +[13:02] like you can, you know, it's going to be exactly this time + +[13:04] and you can consume that thing. + +[13:06] It's kind of nice to have a predictable + +[13:07] without being completely Swiss precision about it. + +[13:11] - Yeah, that's a good point. + +[13:14] Why aren't you the one keeping track of the time? + +[13:16] Come on. + +[13:17] - Why do you think I am an expat? + +[13:21] I had to flee this like overly organized and serious + +[13:26] and you need to do the right thing + +[13:29] and stay in your lane stuff to spread my wings + +[13:32] and be able to be free. + +[13:34] I needed to go to Australia, which is much more laid back. + +[13:37] - The laziest country where time doesn't really matter. + +[13:41] - No, the laziest country is Greece. + +[13:42] My dad's Greek and in Greece, they have a common saying, + +[13:46] which means let's do it tomorrow. + +[13:47] Like whatever you ask is Avrio, Avrio means tomorrow. + +[13:52] If you go to Greece, like within one or two days, + +[13:54] you'll ask a question. + +[13:54] Someone will say Avrio and it means tomorrow. + +[13:57] And basically it's like, ah. + +[14:00] - Later, another time. + +[14:01] - It doesn't mean tomorrow, it means not now. + +[14:04] - Yeah, it's the Greek version of, + +[14:06] we'll circle back to this next. + +[14:08] - Yes, let's take it offline. + +[14:10] - Yeah, let's take it offline. + +[14:12] All of those, yeah. + +[14:14] - Let's fix it later. + +[14:14] - But yes, like you were saying, you know, + +[14:17] one time when you do need to be very cautious of time + +[14:20] or aware of time is when you have guests, + +[14:22] which is another thing that we wanna get into. + +[14:24] So we've been talking about this since the start. + +[14:26] We have had one guest on the Next.js episode. + +[14:30] - Yeah, Luke Bennett, friend of sneak meal + +[14:32] and friend period, a good friend of mine. + +[14:35] Yeah, so shout out to Luke because the idea + +[14:38] of having these guests happened 15 minutes + +[14:41] before the actual live recording. + +[14:44] - Yeah. + +[14:45] - There was Next.js conf that happened. + +[14:47] And I, just before we went live, + +[14:49] you and I, I could see in our team Slack, + +[14:52] we have a very vibrant Slack channel at SyncMeal + +[14:55] and Luke was doing this recap of the Next.js conf + +[14:59] 'cause he obviously got up early or stayed up late + +[15:01] to catch up with it. + +[15:03] And I was like, I just poked, hey, how did, + +[15:06] he's never been on a live podcast + +[15:07] or anything at that point. + +[15:09] How do you feel about going live with your camera + +[15:12] on full screen on YouTube to give us a recap of stuff? + +[15:16] And shout out, he was like, okay, I'll do that. + +[15:20] Yeah, yeah, I can, I think I can do it. + +[15:22] And he jumped in and gave us a really cool recap. + +[15:24] And yeah, that was awesome. + +[15:25] And he's the only guest at this point for the whole-- + +[15:28] - Actually, we had one more special surprise short guest. + +[15:33] - That wasn't a guest, it was a replacement for you. + +[15:36] We didn't show up. + +[15:37] - And who was it? + +[15:40] - Ken C Dodds blessed us with eight minutes in his Tesla + +[15:44] while waiting for his kids to finish a music lesson. + +[15:48] So that was the week, literally, not a week, + +[15:50] like two or three days after Epic Web Conf. + +[15:53] I planned to tell John about the whole conference + +[15:57] and you couldn't be present. + +[15:59] And I was like, oh, let's do next week. + +[16:00] And then I was like, no, it just happened. + +[16:02] So there's like this breaking news, whatever, + +[16:06] slash I am super enthusiastic + +[16:08] and I have my sunburn from the skiing and all the stuff. + +[16:12] And so I thought I'll go YOLO. + +[16:14] And I went YOLO. + +[16:15] And in the background, I just said to Kent, + +[16:17] hey, I'm doing this. + +[16:19] I'm doing this. + +[16:20] Like if you know any of the speakers, whatever. + +[16:22] No, I didn't say to Kent. + +[16:23] I sent to the whole speaker lineup + +[16:24] and even the conference discord, all the attendees, + +[16:28] if anyone wants to jump in and share the recap with me. + +[16:32] And then while I was presenting, + +[16:34] after 45 minutes of talking by myself, + +[16:37] I hear that little interview ring + +[16:40] because I had shared the link and I pick up + +[16:42] and it's Kent C. Dodds sitting in his car + +[16:44] and he was like, I've got eight minutes. + +[16:45] And so we went straight into like, + +[16:47] here, the floor is yours, whatever you want to say. + +[16:49] And yeah, that was cool. + +[16:51] - That's awesome. + +[16:51] And just to, just in case you're wondering + +[16:55] how much Simon actually saw this as a replacement for me + +[16:58] in the audio file of that episode, + +[17:01] like we upload the audio and video. + +[17:04] - Does it say John? + +[17:05] - So I can edit it. + +[17:06] Yeah, usually it's John and Simon. + +[17:09] And this one was Simon and Kent C. Myers. + +[17:12] So he really worked hard to replace me. + +[17:17] But yeah, there's a whole bunch of people we want to get on. + +[17:22] Selma White Panther would be awesome to get on, + +[17:25] Anthony Campolo, Aaron Francis would be an awesome person + +[17:30] to get on, there's heaps. + +[17:32] - Yeah, there's, we have a sneaky list + +[17:34] that keeps growing of people that we on Twitter + +[17:38] or in person kind of planted the seed + +[17:40] and like, oh, I'd love to come. + +[17:41] So we kind of want to start doing this. + +[17:44] And yes, like you said, this kind of is when you, + +[17:48] when you bring other people in + +[17:50] and you kind of have to be a bit more intentional + +[17:53] about the start time and how long they should block. + +[17:57] So I just, we start at 9.30, + +[17:58] but sometimes 10, quarter past 10, + +[18:00] because we just hang out and then, + +[18:03] so block three hours and you should be good. + +[18:05] Like we don't want to do that. + +[18:07] So yeah, start at my, I think aiming for 30 minutes + +[18:11] and if it goes to 40, it's fine. + +[18:13] If we have 43 already, maybe we'd say, + +[18:18] hey, we're not going to do sick picks this week + +[18:19] because that's a can of worms for another 15 minutes easily + +[18:22] 'cause we get so excited. + +[18:24] - Simon's just really hoping for that + +[18:26] because he hasn't, I'm looking at the board now + +[18:28] and he hasn't added any sick picks. + +[18:30] I catch him off guard every week. + +[18:33] - I have one, good one. + +[18:35] - Okay, all right, he's got one to go. + +[18:36] - Ready to go. + +[18:37] - Yeah, the other thing with guests I wanted to touch on + +[18:40] is we, I don't think we have the clout just yet to say, + +[18:45] this is our rules if you want to show up, whatever, + +[18:49] but we want to be intentional in trying to keep + +[18:51] that Australian morning time slot, + +[18:54] which kind of works with the US and most of the world, + +[18:56] except maybe Europe, instead of doing, + +[19:00] maybe like if there's one specific case, + +[19:02] we might do like record in the evening + +[19:04] and not do it live or even do live, + +[19:06] but the idea is to try to, with this podcast, + +[19:09] like America has dozens of amazing live podcasts + +[19:13] and I don't know many other Australian duos or singles + +[19:18] that do like some tech podcasts. + +[19:21] So we want to try to, even if we usually have zero to one + +[19:24] or two people watching live, + +[19:25] but we want to try to create that intentionally + +[19:29] APAC friendly time zone production. + +[19:32] So if you want to come, I guess the idea was like, + +[19:36] it would probably be your evening, + +[19:37] most places you live in the world, + +[19:39] unless you live in Australia or, + +[19:42] actually we have so much overlap with like Philippines, + +[19:45] India, Indonesia, like there's a lot of countries + +[19:48] where there's really interesting people. + +[19:49] And I kind of am brainwashed from trying to, + +[19:53] when I do my workshops with Epic Web or Pro Tailwind, + +[19:56] I cater for San Francisco and LA + +[19:59] and West Coast of the United States, + +[20:02] but that's not the only two options. + +[20:04] It's not our time zone or Pacific time, + +[20:07] there's plenty of other places to go. + +[20:10] - And yeah, as you said, like right now with us, + +[20:14] I guess we're not in daylight savings + +[20:17] and I guess maybe the US is in daylight savings. + +[20:19] Anyway, there's like a nice overlap at the moment + +[20:22] of yeah, our not too ridiculously early in the morning, + +[20:27] it lines up nicely with the not too ridiculous late + +[20:31] in the US. + +[20:32] And so, yeah, give it like two months + +[20:35] and that won't be the case. + +[20:37] It'll be another like ridiculous situation + +[20:39] where it's 4 a.m. or something in the States, but. + +[20:42] - Yeah, it's two hour spread + +[20:45] because everyone moves up one hour the other way. + +[20:46] It makes, even with my family, + +[20:49] like the kids try to speak with the grandparents + +[20:51] and sometimes I try to speak with them too, + +[20:54] but we try to organize the grandkids and grandchildren + +[20:57] for the relationship and because they know me. + +[21:01] I'm not changing anymore. + +[21:03] Well, I'm probably, but we find a time + +[21:06] that works perfectly right after dinner. + +[21:08] And then when it becomes the routine, + +[21:09] it's just like gets all jacked up because we changed him. + +[21:12] And now it's like, oh, let's maybe before breakfast + +[21:15] in the morning, it's gonna work. + +[21:17] The Europe and Australia's East Coast + +[21:20] is probably the most ridiculous 12 hour, not 12, + +[21:24] but it's like almost 12. + +[21:26] It goes from 10 to eight. + +[21:27] And that just, kids go to bed quite early + +[21:30] and my parents sleep quite late in the morning + +[21:33] because they're retired and they earned it + +[21:35] to not have to get up early. + +[21:37] So there's, the window gets surprisingly difficult + +[21:41] very easily. + +[21:42] - Yeah. + +[21:44] So something else we wanna do other than guests is, + +[21:47] well, something I wanna do is have like some more + +[21:53] kind of fun game style episodes. + +[21:56] I used to love, what was that podcast + +[21:59] that I used to listen to a lot about JavaScript? + +[22:02] What's the JavaScript one? + +[22:04] - I don't know. + +[22:05] - Something. + +[22:07] - Is it two people more, there's so many. + +[22:10] - Yeah, no, frontend happy hour was one of them. + +[22:14] And then there was another one. + +[22:15] Anyway, it was always fun when they had guests on + +[22:18] and they like just basically did like a fun little + +[22:21] trivia session or with syntax there's like stumped + +[22:26] is one of the categories of episodes I used to do. + +[22:30] So I think it'd be really fun to have. + +[22:33] - To expose ourselves. + +[22:34] - Yeah, expose ourselves and how little we actually know + +[22:37] and do some like interview style questions + +[22:41] about HTML, CSS, JavaScript, and maybe SQL as well. + +[22:45] We'll see, we'll see how either of us will do on SQL. + +[22:48] - I'll ask the questions. + +[22:50] - But yeah, I think that'd be a fun way to start it + +[22:54] and kind of design it with some points + +[22:58] and things that don't really matter + +[23:00] but just to have some fun. + +[23:01] And then we could have other styles of it + +[23:05] like Jeopardy style ones or just some fun little game style + +[23:10] episodes that we could kind of sprinkle + +[23:14] in there occasionally. + +[23:15] - This is great. + +[23:16] This is a great idea. + +[23:17] When you brought that up, my instant like imposter syndrome + +[23:21] you think was like, oh, I don't want Java. + +[23:22] What if I can't answer the question? + +[23:24] And I was like, so what? + +[23:25] Like, whatever. + +[23:26] I've got skills that other people don't have. + +[23:29] I definitely have not skills that people have, but so what? + +[23:33] And so I thought like it may as well + +[23:36] because typically these stumped question is like JavaScript, + +[23:38] TypeScript, CSS, whatever. + +[23:41] And I thought because we both create a lot of videos + +[23:44] and stuff, we could expand to do some funny questions + +[23:47] about like different types of microphone + +[23:49] or lighting techniques or editing, + +[23:51] or what's a ripple delete or like whatever, + +[23:54] like expand this quiz to more areas than just programming. + +[23:59] - Yeah. + +[24:01] - And then because as everyone knows, + +[24:03] John always picks Ryobi tools for his six bits. + +[24:06] We were joking that maybe we can even expand + +[24:10] like some quiz of like the lawnmower line from Ryobi + +[24:15] and like some technicalities between the 36 volt + +[24:18] versus 18 volt battery mower. + +[24:21] - You have no idea how much I'm going to win in this. + +[24:24] Like you don't want to challenge me on Ryobi tool trivia. + +[24:29] - But yeah, I think it's great. + +[24:32] And the whole genre, it's a French word, by the way, + +[24:37] you say genre in French. + +[24:41] - John, genre. + +[24:42] - Genre, the whole genre of a game/quiz/question/answer + +[24:47] is really cool and appealing to me. + +[24:51] I think it'd be good fun. + +[24:52] And you can also combine it with the previous topic, + +[24:55] which is having guests and like bring guests + +[24:56] and do fun stuff. + +[24:58] - I like that a lot. + +[24:59] - And I would love to just like have a chat with guests + +[25:03] about something they're working on + +[25:06] or their history or whatever. + +[25:07] That would be fun. + +[25:08] And it would give us an excuse to bring them back as well + +[25:10] if we had like a quiz happening + +[25:13] or we could like pick two competing technologies + +[25:16] and run a quiz between the-- + +[25:19] - Pit them against each other. + +[25:20] - Yeah, work out which one is the best dev tool + +[25:23] by seeing how well they do with interview questions. + +[25:28] - That's great. + +[25:29] I really like this. + +[25:30] Another sort of topic idea ahead. + +[25:33] And of course, now I forgot. + +[25:36] The whole last minute, I was in my head thinking of this. + +[25:39] All right, if you get something, it'll pop back in my head, + +[25:44] but I was thinking of-- + +[25:45] - Okay, and so, yes. + +[25:49] Another, see, I really, back on the like game stuff, + +[25:53] like I love board games and I always find it's like, + +[25:56] you know, a fun way to, like over the weekend, + +[25:59] we played so much Uno, like an insane amount of Uno + +[26:03] and not with the kids. + +[26:04] Like we waited until the kids went to bed. + +[26:06] And as soon as the kids were out of the picture, + +[26:09] it was glasses of wine and Uno for just hours. + +[26:14] Like we stayed up until like 12 o'clock + +[26:16] just playing Uno one after another. + +[26:17] And it was really nice having those kinds of like + +[26:19] simple games where like, + +[26:22] you're still able to kind of have a conversation, + +[26:25] but it gives you something to kind of, + +[26:27] to concentrate on if there are some lulls + +[26:30] in the conversation or if-- + +[26:32] - A prompt. + +[26:33] It's great. + +[26:34] Uno is amazing. + +[26:35] We camp a lot and we definitely play Uno a lot + +[26:37] with the kids most of the time, + +[26:39] but I like the idea of like, go to bed + +[26:41] and then we can have a-- + +[26:43] - Adults only Uno. + +[26:46] - Yeah. + +[26:47] It's funny, a random side quest, + +[26:49] which is how we end up in our long episode, + +[26:51] but I saw a tweet from a Taylor Otwell, + +[26:55] creator of Laravel. + +[26:57] And he was like, what is in your list of like, + +[27:00] you call it S tier, like the top tier software. + +[27:04] And he was like, the constraints are like, + +[27:07] it's super simple, incredibly powerful and easy + +[27:12] and stuff like that. + +[27:12] In his list, there was Trello and there was spreadsheets + +[27:17] and it's undeniably both, you can do crazy stuff with it + +[27:20] and it's pretty intuitive and you can learn the basics + +[27:24] really quickly and then go deep. + +[27:26] And I think in a non-software way, + +[27:28] the Uno game is part of this, like it's super simple + +[27:32] and it creates so many experiences from two people to like, + +[27:36] I don't know, I've played Uno at New Year's Eve + +[27:38] with like families and cousins and like 15 plus people. + +[27:42] And we played from 6 p.m. till midnight, nonstop + +[27:45] and no one wanted to stop. + +[27:46] Like, ah, who cares about the fireworks, we're playing Uno. + +[27:49] - Yeah, yeah, that was our weekend. + +[27:52] It was like, anytime there was just like a break, + +[27:55] like, oh, like a couple of people need to shower + +[27:58] and then we're gonna go to the playground or whatever. + +[28:00] It was like, quick game Uno, let's break out the deck. + +[28:04] It's like, it's so simple, there's no setup, it's great. + +[28:06] - I gotta say, I really respect and appreciate the fact + +[28:10] that you say Uno, because it's, again, it's Italian. + +[28:13] I'm Swiss and we speak Italian and German and French. + +[28:16] So Uno means one in Italian. + +[28:18] And that's why when there's one card, you say Uno. + +[28:21] And my kids say, you know, + +[28:23] and I'm expecting most English-speaking people say, + +[28:28] you wanna play Uno? + +[28:29] And I'm like, Uno, you gotta say Uno. + +[28:33] And like, they know and they say it sometimes + +[28:37] but they always revert back to the Aussie, you know. + +[28:40] And it just, it does my head in. + +[28:42] I'm sorry, if you play Uno, call it Uno. + +[28:45] - Yeah, Uno, Uno, for sure. + +[28:48] Did you remember what you were gonna say? + +[28:51] - Yes, now I'm gonna forget again. + +[28:53] No, I remember. + +[28:54] All right. + +[28:57] Yes, now I remember again that I forgot that I remember. + +[29:03] - We're not editing the podcast anymore, Simon. + +[29:05] - No, it's live. + +[29:07] Ship it live, man. + +[29:09] We are live, actually. + +[29:10] The other topic I would like to do, + +[29:12] but this requests us to find a better way + +[29:14] to collect more questions is this like mailbag, + +[29:17] sort of like, send a question. + +[29:19] We've kind of done that on the website with the suggestion, + +[29:22] but it's because our audience is so big, + +[29:25] it hasn't really caught up. + +[29:28] But maybe a more intentional on Twitter, + +[29:30] like say, hey, we're going live tomorrow, + +[29:32] like send us a question. + +[29:33] We wanna do mailbag stuff or AMA or whatever. + +[29:36] And it just, I have a list of questions. + +[29:38] So not that we ever stuck for topics, + +[29:40] but it's kind of nice to mix it up with like, + +[29:42] hey, let's do listeners question episode. + +[29:44] And we go through anything from one to six, 10, 20, + +[29:49] whatever gets us to the 30 minutes, + +[29:52] which by the way, + +[29:53] we are 29 minutes and 56 seconds right now. + +[29:57] - Gonna wrap it up, Simon. + +[29:58] I'm afraid I gotta cut you off there + +[30:00] and move on to sick pics. + +[30:01] - Yeah, but we wanted to talk about the future. + +[30:04] We wanted to be a little bit more short and on time, + +[30:08] have guests and have episodes about games and stuff + +[30:12] and AMA, mailbag. + +[30:13] So we've covered all of this and I think + +[30:16] it's pretty good format. + +[30:18] - So if you head to navbar.tech/suggestions, + +[30:23] oh, no, the deployment's disabled. + +[30:29] (laughing) + +[30:31] I think our site's down. + +[30:33] I'll fix that up before you try and go there. + +[30:37] But if you go to navbar.tech/suggestions, + +[30:42] you can request an episode that you would like us to cover + +[30:45] or a topic you would like us to cover. + +[30:48] 'Cause yeah, we would love to cover some things + +[30:50] that you folks are interested in hearing about. + +[30:54] Otherwise we're just gonna keep talking about Ryobi tools. + +[30:56] - Yeah. + +[30:58] Yes, so this is basically the very crux + +[31:03] of making the NavBar sustainable for us, + +[31:07] fun and more mature and capable of bringing people along. + +[31:11] And who knows, maybe one day bring sponsors or whatever, + +[31:15] like someone wants to acquire us for $2 billion. + +[31:17] I guess to do that, + +[31:20] you need to have some sort of structure to it. + +[31:24] This is the goal, isn't it? + +[31:25] Retire wealthy from a podcast. + +[31:28] - That's right. + +[31:29] - Or continue doing it, but sustainably. + +[31:31] Yes, so with all that. + +[31:35] - Do you have a sick pick for me, Simon? + +[31:36] - I do. + +[31:37] - Oh, yes. + +[31:38] - I haven't put it. + +[31:39] We have this little back channel thing notes, + +[31:41] but I'm very off the cup. + +[31:45] So I didn't put it, + +[31:46] but my sick pick this week is DaVinci Resolve + +[31:50] and specifically the Fusion page in DaVinci Resolve. + +[31:54] So if you don't know, + +[31:56] DaVinci Resolve is a video editing software, + +[32:01] but instead of doing just video editing, + +[32:03] it does the whole umbrella of editing videos, + +[32:08] but also doing motion graphics like chapter tiles + +[32:12] and the special effects and explosions and wobbly things. + +[32:16] And then audio engineering and color correction, + +[32:19] color grading, all that stuff. + +[32:21] Typically, if you're in the Adobe world, + +[32:23] you have like Adobe Premiere for the editing. + +[32:27] And then you export everything to After Effects + +[32:30] for the special effects + +[32:31] and export everything to Adobe Audition, + +[32:33] or I don't know how it's called, the audio one. + +[32:36] Or if you're on Apple, + +[32:37] you have Final Cut Pro for the editing. + +[32:39] And then I think you still, + +[32:40] you can do a bit of motion graphics, + +[32:42] but if you want to go full on Hollywood movies, + +[32:45] you have to go to After Effects, which is Adobe. + +[32:47] So you have to pay for the subscription. + +[32:49] Anyway, Resolve has that amazing thing, + +[32:52] which is the shared state timeline between all these apps. + +[32:56] So you edit your video, + +[32:58] and then if you want to do some motion graphics, + +[33:00] you go to that Fusion page and they have the same timeline, + +[33:04] and you can start making motion graphics, + +[33:06] which get embedded in your timeline. + +[33:08] And if you want to change the audio, + +[33:09] you go to the audio page and it's still the same timeline. + +[33:12] So it's basically having multiple apps + +[33:15] that share the same editing cut timeline you work on. + +[33:18] - That workflow is just so good. + +[33:20] Like, yeah, having to move it + +[33:21] into a completely different app is just such, + +[33:24] like, so time-consuming, + +[33:25] and then exporting it back out of there + +[33:26] and putting it back in. + +[33:27] And especially if you're then crossing over, + +[33:29] like you were saying, those different brands, + +[33:30] like if you're editing in Apple stuff, + +[33:33] like moving it across to an Adobe application + +[33:37] to do After Effects stuff, + +[33:38] and then moving it back again to Apple, + +[33:40] like it would just, yeah, it's a nightmare. + +[33:42] And so just being able to click on a tab + +[33:44] and you're already there, + +[33:44] like your whole project, whatever you're working on, + +[33:47] right there, and you can start working on motion graphics. + +[33:49] - Yeah, it's incredible. + +[33:50] Like, Resolve is a big, big, big, + +[33:53] like, learning curve, especially Fusion. + +[33:57] But it's so rewarding that everything you do, + +[33:59] you will probably never reach the ceiling + +[34:04] of what the software can do. + +[34:05] Just for context, people in Hollywood studio, + +[34:08] like the Marvel movies are edited with Resolve. + +[34:11] Like, if you go on the Resolve website, + +[34:13] you can see all these crazy Hollywood, + +[34:16] like top class, top tier movies, editing with Resolve. + +[34:20] And so I don't think us little YouTube + +[34:22] dev ed content creators will hit that limit. + +[34:27] But on your point, I've shared a little clip + +[34:30] from a video this morning from Casey Ferris, + +[34:32] who's a really good YouTube teacher for Resolve stuff, + +[34:35] and I learned all my Fusion stuff from. + +[34:37] And he's got this example where there's a lady + +[34:40] who takes a chicken nugget and opens it. + +[34:42] And he's like, "Oh, imagine you wanted to reinforce + +[34:45] that it's steamy hot and you want a bit of steam coming out." + +[34:48] And he's like, "Okay, in After Effects, + +[34:50] like in whatever editing Final Cut or Premiere, + +[34:53] you would export the whole project + +[34:55] and then import in After Effects and add your little smoke, + +[34:58] and then re-export that, + +[35:00] and then re-import in your editing stuff." + +[35:02] And then you're like, "Oh, it's a bit too much. + +[35:03] Let's re-export and just do that dance so many times." + +[35:07] And he's like, "Check this out. + +[35:08] Click on Fusion tab, create a, it's called a little, + +[35:11] like a fractal noise, whatever, that makes some clouds." + +[35:15] And then he makes a mask so it just scoped to right + +[35:18] at the opening of the chicken nugget. + +[35:20] And then he goes back to the edit page, + +[35:21] and there's smoke coming out of the nugget. + +[35:23] And it's like he never left the app or the workflow, + +[35:27] and it took about 10, 15 seconds. + +[35:29] And that really drives the point home + +[35:31] of why it's a good investment to learn this, + +[35:34] even if you already know After Effects. + +[35:36] But this is my sick pick because this past week, + +[35:40] I've really played, I've started going on a deep dive on Fusion, + +[35:44] and I've discovered it is very similar to programming, + +[35:47] and funny enough, to Tailwind CSS philosophy. + +[35:50] The way Fusion works, there's a little thing called a node + +[35:54] for each single purpose function, + +[35:56] exactly like Tailwind utility class, + +[35:58] where you combine padding and background colors + +[36:00] and border and all that stuff. + +[36:02] In Fusion, you have a background and a text node + +[36:04] and then a merge to merge them together, + +[36:06] and then a transform to zoom in or rotate them. + +[36:09] And instead of just clicking on your text and rotate it, + +[36:11] you're like, "Okay, I need to transform the text. + +[36:13] So I will pipe it into a transform node." + +[36:16] And the transform node is where you do the rotation. + +[36:18] And so you compose all these little nodes together, + +[36:21] and it gives this incredible composability + +[36:24] that you can do anything you think of with just simple, + +[36:28] single-purpose nodes that you can assemble + +[36:31] in like Lego blocks, basically. + +[36:33] And it's really cool. + +[36:34] Then you can export it as a reusable component with props. + +[36:38] So I've made that title thing. + +[36:40] I know John knows because I select him the result, + +[36:42] and I tweeted it yesterday. + +[36:44] I've created this cool title, and I was like, + +[36:47] I imagine people would change the color, the text, + +[36:49] whatever it says, and then that's it. + +[36:51] And so you expose these as options in, it's called a macro, + +[36:55] but it's kind of like a React component + +[36:56] with props and props API. + +[36:59] And then people drag your new fun title component + +[37:01] you've created, and all they can do is put the text, + +[37:04] choose the color, + +[37:05] and then they have the thing completely made. + +[37:08] And you can right-click and explode it + +[37:10] to kind of eject and control all the parts, + +[37:12] but it's so cool, and it's very program-y-like, + +[37:15] and I really, really enjoy that. + +[37:17] - Yeah, and you can actually do + +[37:20] some actual programming in it too. + +[37:22] So I've also been playing with this recently. + +[37:26] I did a, for our Superbases now, GA video, + +[37:31] which we put out a couple of months ago. + +[37:33] I, yeah, I got right into the motion graphics stuff + +[37:37] and doing like animated text things. + +[37:42] And one of them was like a counter + +[37:45] that counted up to 1 million databases + +[37:48] that we're now hosting at Superbase. + +[37:50] And so, yeah, you can write these like expressions + +[37:54] that can basically, yeah, + +[37:57] like loop through a collection of numbers + +[37:59] over a specific amount of time. + +[38:01] And that's just like the beginning. + +[38:05] Like there's this whole, + +[38:06] the whole Lua programming language + +[38:07] is exposed under the hood. + +[38:10] And so you can, yeah, + +[38:12] you can write any kind of programming stuff you want, + +[38:15] which is just so helpful when you're, + +[38:18] when you are a software developer or whatever, + +[38:21] and your mind thinks like that. + +[38:23] How, like, how can I write a little logical expression + +[38:28] that's gonna do this thing for me, + +[38:30] rather than understanding how to do + +[38:33] like design and motion graphics things. + +[38:35] It's nice being able to access all of it. + +[38:40] Yeah, and it's exposed to every single field. + +[38:42] It's something I hadn't realized, + +[38:43] but any text field inputs, + +[38:46] if you type equal, + +[38:48] it's exactly like in Excel spreadsheets. + +[38:51] If you type equals, you enter, + +[38:53] oh, you're gonna give me a formula or some function. + +[38:56] And a quick example I did in my title, + +[38:59] I did this stop motion feel like, + +[39:02] instead of it being too fluid + +[39:04] when the letters come up and bounce + +[39:05] and then land in position, + +[39:07] I want it to be, to have that kind of like stop motion + +[39:09] every six frames or so. + +[39:11] So it's kind of jittery on purpose. + +[39:13] And then I also added some panning rotation. + +[39:16] And then I kind of mapped all of this + +[39:18] to a time stretcher node. + +[39:20] So again, this node is to make like speed warps and stuff. + +[39:24] And in the tutorial, again, Casey Ferris, + +[39:26] which I mentioned before, + +[39:27] showed how you can press equal + +[39:30] and then JavaScript developers will understand that. + +[39:33] Each frame has a reference called time. + +[39:37] So if you just go time, + +[39:38] it's gonna be the normal frame. + +[39:40] Each frame is its own frame. + +[39:41] But you could go floor, like math.floor in JavaScript. + +[39:45] So it's gonna bring it back down + +[39:46] to the nearest round integer. + +[39:49] And so the function was like flow + +[39:52] and then time divided by 3.5. + +[39:55] And so every 3.5 frames, it would freeze, + +[39:58] bring it back down always + +[40:00] because it hasn't reached the next one. + +[40:02] And then you multiply it by 3.5 again, + +[40:04] so it doesn't slow motion. + +[40:06] So it goes at the normal speed, + +[40:08] but it kind of holds back for 3.5 frames. + +[40:11] And then it jumps to the next one. + +[40:13] And so you have this effect. + +[40:16] And then typically you would expose that as a prop, + +[40:18] as like the jitter speed. + +[40:21] And you could have a slider from zero to whatever. + +[40:24] And if you go zero, it's like completely fluid. + +[40:26] And if you go to 24, it's just one frame per second. + +[40:29] And then you can, + +[40:30] I find amazing that you can just write a little function + +[40:33] that you have the time as a variable, + +[40:35] and then you can do whatever math function + +[40:38] just with the flow of, + +[40:39] like you can have these expressions, + +[40:41] which is the Lua. + +[40:42] I haven't even looked into it, + +[40:44] but if you know Java, + +[40:44] like it's a programming language. + +[40:46] You can do math and stuff really intuitively. + +[40:50] And the fact that you can right click on any field + +[40:52] and pass an expression + +[40:54] instead of just the values that are offered to you. + +[40:57] So you can tie it to another field and say, + +[40:59] take this field, follow this SVD path, + +[41:01] but then just add like variability. + +[41:03] Like it's crazy. + +[41:04] You can do anything you want. + +[41:05] - They actually have like an API as well + +[41:09] for DaVinci Resolve. + +[41:10] I started writing like a script + +[41:12] that could do a bunch of the kind of, + +[41:15] the preparing of a project + +[41:18] that I do when I create a new project. + +[41:20] And so I automated the process of like, + +[41:23] like I click a button + +[41:25] and then it like creates a new project + +[41:28] with a particular resolution and frame rate. + +[41:30] It drags in the media, + +[41:32] it creates proxy media so I can edit it really quickly. + +[41:37] And yeah, and I think did the audio transcription as well. + +[41:42] It like triggered off a whole bunch of tasks + +[41:44] so I could just like drop a whole bunch of media in a folder + +[41:46] and click a button and then just walk away. + +[41:47] And when I come back, + +[41:48] I'm actually ready to start editing this video. + +[41:52] So yeah, it's really cool that they, + +[41:54] yeah, DaVinci Resolve kind of exposing these ways + +[41:57] to write like code in your video editing software. + +[42:01] That's nuts. + +[42:03] - From the beginning, I think, correct me if I'm wrong, + +[42:05] but I think it uses Postgres database to store all your, + +[42:09] it's definitely a database, + +[42:10] but I think it's Postgres, yeah. + +[42:12] So it's a bit intimidating when you start to like, + +[42:14] oh, create your database and like, + +[42:15] oh, I just want to edit a video, + +[42:17] but you just have to give it a folder + +[42:19] and then it's going to store all these databases. + +[42:22] And then it stores XML and it's all programmery. + +[42:25] It's basically like a really elegant UI control stuff, + +[42:28] like Webflow, but for video editing, I guess. + +[42:31] - It's insane. + +[42:32] And I think, let us know in the comments and whatever, + +[42:36] if we, I think we should do a whole episode on Resolve + +[42:39] 'cause we both use it + +[42:40] and it's really, really amazing piece of software. + +[42:43] And we haven't mentioned it's completely free. + +[42:46] - It's insane. + +[42:47] - It's ridiculous. + +[42:48] - It's free. + +[42:49] Like it's nuts. + +[42:50] Like there is a paid version. + +[42:51] I think both of us have the paid version now, + +[42:53] which gives you access to like a couple of like AI features + +[42:56] and stuff, but like everything we were just talking about, + +[42:58] like the whole thing is free. + +[43:03] Like, it's not like they give you access + +[43:04] to like this, you know, very beginner. + +[43:07] - With a watermark or yeah. + +[43:09] - Yeah, and it's not like a timed trial + +[43:11] or anything like that. + +[43:12] The whole thing is free, + +[43:13] but then there are a couple of like really, you know, + +[43:16] niche tools that they add on top + +[43:19] that are just the things that like cost them money to run. + +[43:22] So like, you know, AI transcription of scripts + +[43:25] and stuff like that. + +[43:27] And yeah, there are just like very few small, + +[43:30] very, very pro features that you can purchase + +[43:35] by buying the software, + +[43:37] but you can edit Hollywood level movies + +[43:40] with completely the free version. + +[43:42] Like nothing is holding you back, it's nuts. + +[43:44] - A friend of mine kept recommending it. + +[43:46] And I was like, in my head, I was always thinking, + +[43:49] yeah, it's going to be like 15,000. + +[43:51] Like the whole Adobe Premiere Suite, + +[43:53] if you buy it off the right, like not the subscription. + +[43:56] I was like, I don't have money. + +[43:57] I want a free tool. + +[43:58] And one day he said, dude, I keep telling you it is free. + +[44:02] And in my head, I was like, yeah, + +[44:03] but watermark three export in 480. + +[44:06] - Or like, it's going to be like the iMovie version + +[44:09] and really you need like Final Cut to do anything real. + +[44:12] - And I tried the free and I was blown away. + +[44:15] If you've seen any of my YouTube videos + +[44:18] on the Tailwind Labs channel, + +[44:19] and it's all the free resolve, + +[44:20] and I barely scratched the surface. + +[44:22] Like it's, this is why we're going to make an episode. + +[44:25] And in the episode, we will reveal to you + +[44:27] how they can make it free, + +[44:29] because it's like a really strange business model, + +[44:31] but it's a good one. + +[44:33] - And this is also why we can't do 30 minute episodes. + +[44:36] (laughs) + +[44:37] Simon is too excited about the things + +[44:38] that he wants to sick pick. + +[44:40] (laughs) + +[44:41] - And now for John Mayer's sick pick. + +[44:43] (laughs) + +[44:44] - My sick pick this week is to learn to make sourdough + +[44:47] or like any kind of bread. + +[44:48] I just like bread. + +[44:49] There's something special about it. + +[44:51] It like connects you to, you know, + +[44:54] it connects you to the ingredients that you're making. + +[44:57] And there's just something about sourdough. + +[45:00] Like the process is quite, + +[45:02] like it's not a super complicated bread, + +[45:05] but there are so many steps where you can go wrong + +[45:09] just by changing something a tiny, tiny little amount. + +[45:12] And so like, it is a super simple bread. + +[45:16] It's got super simple ingredients. + +[45:17] And like the process is like that each step is quite simple, + +[45:23] but yeah, it just like everything needs to be done + +[45:26] in the correct way. + +[45:27] And I feel like you learn so many techniques + +[45:30] about baking and bread stuff, + +[45:33] and like the strength of dough and different flours + +[45:36] that you should use and stuff like that. + +[45:38] It's just, it's a really satisfying thing + +[45:40] when you get it right. + +[45:42] And so I got really into making sourdough + +[45:44] during the COVID lockdown. + +[45:47] And so I couldn't go anywhere, + +[45:49] couldn't go to any of the nice bakeries + +[45:51] that were just outside of our area + +[45:54] that we were allowed to go. + +[45:56] And so, yeah, I started like learning how to make sourdough + +[46:00] and I got to like a certain level and I didn't, + +[46:03] like it kind of just the interest fell off eventually. + +[46:06] And then I tried to make some + +[46:07] after not making some for years, + +[46:09] and it just went terribly wrong + +[46:12] because I didn't have all of those techniques + +[46:14] fresh in my mind and all those little bits. + +[46:16] - Yeah. + +[46:18] That compounding care that you give sourdough over time, + +[46:22] 'cause it takes like three days to make a loaf of sourdough + +[46:24] or really it takes like three months + +[46:27] to make a loaf of sourdough + +[46:29] because you've got to like develop a sourdough culture. + +[46:33] You've got to develop like the bacteria + +[46:35] that's like the yeast that, you know, + +[46:39] that eats the bread and creates that like sour flavor. + +[46:44] So it's this like really long process. + +[46:47] And so when you get to the end + +[46:48] and you actually have like bread that worked out + +[46:51] and you did it correctly and like you understand + +[46:54] all the little bits that need to go into it, + +[46:57] it's just so satisfying + +[46:59] and it's such a delicious type of bread. + +[47:01] And it's so nice to like share with friends, + +[47:03] take it to a dinner party or whatever it's. + +[47:06] Yeah, it's amazing. + +[47:07] And so I recommend you go and learn how to make sourdough + +[47:12] and go and watch the YouTube videos on it. + +[47:15] And even if it seems ridiculous, + +[47:18] each of these like little things that they're doing, + +[47:20] like, come on, that's not gonna make a difference. + +[47:21] The sum of all of those little things + +[47:23] makes a huge difference + +[47:25] and it's really satisfying and really- + +[47:26] - If you have the dials right, this is- + +[47:28] - Yeah, it's really therapeutic to go through this like, + +[47:31] this whole process and get to the end + +[47:33] where you have this very delicious bread. + +[47:35] - Yeah, it turns out- + +[47:36] - But actually don't eat all of it yourself + +[47:38] over and over again, + +[47:39] because that's not a good idea to eat that much bread. + +[47:42] - Do it for sharing, yes. + +[47:43] - That's right, yeah. + +[47:44] So take it to the dinner party or like make it for the kids. + +[47:48] Like, yeah, share it. + +[47:49] It's a sharing bread. + +[47:51] It's not an eat an entire loaf yourself bread, yeah. + +[47:54] - I really like what you say about the longer + +[47:57] and more care and craft and learning and failure + +[48:00] and then getting good at it it takes, + +[48:02] the more joyful the experience is at the end. + +[48:05] Like it's, there's something to be said about like, + +[48:08] people wanna take shortcuts and do stuff + +[48:10] they nail on the first try. + +[48:12] But if you speak to someone like Jalen Brown + +[48:14] or Jason Tatum from the Boston Celtics for eight years, + +[48:18] they've been- + +[48:19] - The champion of the NBA final. + +[48:21] We still need to do an episode on basketball + +[48:24] to lose all of our listeners. + +[48:25] - But before they won, + +[48:27] and the same happened with the Golden State, + +[48:29] where before they won and Jordan's Bulls, + +[48:31] like that seven or eight years of failing, + +[48:34] getting so close and then, + +[48:35] oh, we need a little bit more three-point shooting, + +[48:37] tightening up the defense and then the adjustments. + +[48:40] Concessions, there's people that brought them almost there + +[48:43] and got traded to bring the last piece + +[48:45] that allowed them to win the championship. + +[48:47] On that note, I don't wanna make it longer, + +[48:49] but I love how the coach, the Boston Celtics coach said, + +[48:53] "We won with this team today." + +[48:55] There's players like Marcus Smart and other people + +[48:57] that have left the team literally last year. + +[49:00] And they also won the championship in a way + +[49:03] because there's no way we get there + +[49:05] without their contributions and helping us figure out + +[49:08] what's the last week. + +[49:09] Do I add a bit more yeast to the temperature for the bread? + +[49:12] At what time do I add the beer + +[49:13] or whatever you put in there? + +[49:15] It's really all these things that together bring you + +[49:18] to the final result that you can really enjoy + +[49:21] and share with everyone. + +[49:23] - Yeah, yeah, love it. + +[49:24] And I will definitely make you some sourdough + +[49:27] if you ever come to Melbourne + +[49:28] or if I come up and visit in Sydney. + +[49:30] - Not Sydney, six hours further. + +[49:33] - Sydney-ish, New South Wales, Northern New South Wales. + +[49:36] - Yeah, small state. + +[49:39] - Yeah. + +[49:39] - All right, look at us, 15 minutes. + +[49:41] We have done all the things. + +[49:42] We can wrap up. + +[49:43] - It's the shortest episode we've done. + +[49:44] We were aiming for 30. + +[49:45] We got to 50, but we've definitely improved + +[49:49] or reduced the amount of time we take for an episode. + +[49:51] So thank you so much for listening + +[49:53] and we will see you again next week. + +[49:54] - See you next week. + +[49:55] Thanks for tuning in. + +[49:56] Let us know what you feel about the comments + +[49:58] and see you soon. + +[50:00] Bye. + +[50:01] you + +[50:03] you diff --git a/astro/src/content/post/2024-07-18-the-navbar-live-prompt.md b/astro/src/content/post/2024-07-18-the-navbar-live-prompt.md new file mode 100644 index 0000000..2c59f7d --- /dev/null +++ b/astro/src/content/post/2024-07-18-the-navbar-live-prompt.md @@ -0,0 +1,1713 @@ +--- +showLink: "https://www.youtube.com/watch?v=R_ewWEVUpJs" +channel: "Navbar" +channelURL: "https://www.youtube.com/@the_navbar" +title: "The NavBar LIVE" +description: "In this episode of The NavBar podcast, hosts John and Simon discuss recent projects, play a web development trivia game, and share their 'sick picks' including a pizza oven and online course." +publishDate: "2024-07-18" +ogImage: "https://i.ytimg.com/vi/R_ewWEVUpJs/maxresdefault.jpg" +--- + +## Episode Summary + +This episode of The NavBar podcast features hosts John and Simon engaging in casual conversation and a web development trivia game. They begin by discussing Simon's recently launched Tailwind CSS course and his new "Tailwind Toots" short-form content series. The hosts then play a trivia game asking each other questions about JavaScript, HTML, and CSS concepts, demonstrating their knowledge while also acknowledging areas where they are less certain. Throughout the episode, they share personal anecdotes and insights from their experiences in web development. The conversation touches on topics like functional programming, React patterns, and the evolution of state management libraries. They conclude by sharing their "sick picks" - Simon promotes his new course while John enthusiastically recommends a pizza oven, describing his journey to make the perfect pizza. + +## Chapters + +### 00:00 - Introduction and Course Launch Discussion + +Simon discusses the launch of his new Tailwind CSS course "Pixel Perfect Figma to Tailwind". He explains his approach to marketing the course, including creating additional free content to promote it. John and Simon discuss the challenges of producing and launching online courses, emphasizing the importance of continued marketing efforts after the initial release. + +The hosts delve into the intricacies of course creation and marketing, highlighting the significance of consistent promotion even after launch. They discuss various strategies Simon employs, such as creating complementary free content and leveraging social media. This segment provides valuable insights for content creators and educators in the tech space, showcasing the behind-the-scenes work that goes into successful online course launches. + +### 09:43 - Tailwind Toots and Content Creation Strategies + +Simon introduces his new project "Tailwind Toots", a series of short-form content pieces focusing on Tailwind CSS tips. He explains the concept, format, and his motivations for creating this type of content. The hosts discuss the benefits of creating bite-sized, easily shareable content in the web development education space. + +This chapter explores the emerging trend of short-form educational content in the tech industry. Simon's "Tailwind Toots" project serves as a case study for creating engaging, easily digestible content that can attract and retain audience attention. The discussion touches on the balance between in-depth tutorials and quick tips, highlighting the value of both approaches in tech education. It also provides insights into content creation strategies and the importance of adapting to changing audience preferences in the digital age. + +### 18:51 - Web Development Trivia Game + +John introduces a web development trivia game he created, where they take turns asking each other questions about JavaScript, HTML, and CSS. They discuss topics such as higher-order functions, strict mode in JavaScript, empty elements in HTML, and the differences between CSS Grid and Flexbox. + +This segment offers an entertaining and educational dive into various web development concepts. The hosts' responses to the questions provide insights into their expertise while also highlighting areas where even experienced developers may have knowledge gaps. The discussion around each question often leads to deeper explorations of related topics, offering listeners a mix of trivia and practical knowledge. This format demonstrates an engaging way to review and discuss technical concepts, potentially inspiring listeners to reflect on their own understanding of these topics. + +### 1:04:33 - Sick Picks: Pizza Oven and Course Promotion + +For their "sick picks" segment, Simon promotes his Tailwind CSS course again, while John enthusiastically recommends an Ooni pizza oven. John shares his journey of trying to make the perfect pizza and describes the features and benefits of the Ooni oven. They discuss the potential for using such an oven for gatherings and even as a business idea. + +The "sick picks" segment provides a lighthearted conclusion to the episode, blending personal interests with professional recommendations. Simon's repeated promotion of his course underscores the ongoing nature of course marketing, while John's detailed description of the pizza oven offers an engaging narrative that goes beyond mere product recommendation. Their discussion about the potential uses of the oven for social gatherings and business ventures demonstrates how personal interests can intersect with entrepreneurial thinking, providing listeners with both entertainment and potential inspiration. + +## Transcript + +[00:00] think. Welcome to The Nav Bar. We are live on Twitch, YouTube and Twitter. And I almost thought + +[00:07] that John had frozen, but he's very concentrated. Oh yeah, no, sorry. I'm just sharing the link out + +[00:12] on Twitter. That's what's got my concentrated face on. We can start the podcast bit separate + +[00:20] to the live bit. Welcome to the live viewers. Hello. Hello. We were just talking about all the + +[00:26] things that we want to do, all of the ways we want to tweak The Nav Bar as we always do. Look + +[00:31] at this beautiful layout that Simon's got. Definitely just for the live people. Actually, + +[00:35] no, because one of the things we were talking about is we're going to start putting these out + +[00:38] as videos, both on embedded tweets and also YouTube and stuff. And so people will actually + +[00:44] be able to see this beautiful layout that Simon has prepared. Exactly. My turn to pause because + +[00:52] I heard children in my backyard and my family is not supposed to be here. So if my neighbors just + +[00:58] come and start playing basketball because we have a basketball hoop and it's very community vibe + +[01:02] feel around here. Nice. I can hear stuff. I can hear stuff. If you're joining us in the chat, + +[01:12] let us know if you can hear. Simon reckons he has lawnmowers going, he's got dogs barking, + +[01:17] and now he reckons he has kids in the background. So let us know if you can hear any of those + +[01:21] things because I can't. I think it's all in his head. We'll see. The dogs will understand. + +[01:32] Stop barking. I'm recording a podcast. Simon, Simon, Simon. Well, thanks for joining us on + +[01:41] the live stream to watch me, uh, you know, try to work out what to do by myself. So this is Simon's + +[01:49] beautiful layout that he's put together. Uh, he's got the welcome to the navbar page up. And so this + +[01:54] is our website, which you can go and check out at navbar.tech, I think. No, navbar dot, uh, + +[02:02] I can't see it at the top. I'm pretty sure it's navbar.tech. Let's see. Oh, not tej.tech. It is + +[02:11] in fact navbar.tech. And you can see a list of all of the episodes that we've done so far. Well, + +[02:18] the funny thing is that episode 23. Hello. Hey puppy. So flex is joining us for the, + +[02:28] for the podcast. I'm out of breath now. The best way to make the dog stop barking is to take him + +[02:32] in here. So neighbor's mowing and they have their grandchildren and they're running around + +[02:37] and screaming. And so my dogs and their dogs and every dog. So Simon's just taking me off + +[02:43] the podcast entirely. That's fine. First he leaves me alone in the podcast and then he takes over. + +[02:49] There we go. Hello. This is T-Rex. Oh, T-Rex. Oh, look at the T-Rex arms. Oh, + +[02:55] look at those little arms. You know, Caro and I, my wife and I have been watching, + +[02:59] uh, like for no reason at all. We've been watching all of the Jurassic Park movies. Yep. And so we, + +[03:05] we started with like the original trilogy, which gets way worse as you, as you go. The first movie + +[03:11] is awesome. Second movie is okay. Third movie, not good at all. Uh, but then we just started + +[03:16] like watching all the new ones as well. And yeah, they're, they're okay. Yeah. Not great. + +[03:22] That happens a lot with movies. The sequels kind of nosedive at some point. + +[03:26] Yeah. Yeah. So I think there's six of them all up. So we're, we're determined to get through + +[03:32] all of them. Yeah. So the reason I've built this super fancy layout here, if I go back to the + +[03:39] screen share is that, uh, first of all, you should check the knife bar website. If you haven't, uh, + +[03:46] there's a few episodes we have to add here, but there's so many, that's what I was just about to + +[03:50] say. I think this one is like episode 30 and the last one there is episode 23. So I'm sorry, + +[03:56] everyone. I will get these uploaded. I've actually like they're edited now. I just need to go through + +[04:01] and do a last like export and upload. And we're good to go. So I will, like I say, every week, + +[04:07] I'll totally do it this week. I will find the time to add those episodes. + +[04:12] Yes. It's like the, the comments to do refactor later. + +[04:16] It's just like the show notes every week. I make big promises, things that will be in + +[04:20] the show notes, things that will be on the website. Yeah. But the other reason I did this + +[04:25] is because, um, John built an incredible website recently. I don't know if you know about it. + +[04:34] It's almost as well styled as, as Simon's. Oh no. The number up the top is yeah. Okay. There + +[04:39] we go. There we go. So we need to start the episode. What's the number at the top? + +[04:44] Oh, the, the number of viewers in mine, I can see the number of people in that. + +[04:49] 34. + +[04:50] Yeah. 6,850,000. Uh, and that's being blocked by the, uh, oh, that's blocking. + +[04:56] Oh yeah. It's not, it's not, it's blocking your UI, but not the viewers. + +[05:00] And I'm sure it's, I'm sure everywhere else. It's fine. + +[05:02] Go back to the other one and let's start the podcast on that one. + +[05:07] I think I'm, um, uh, we've reached the, the, the part of the relationship where I'm starting to + +[05:12] annoy John because I, I'm just want to muck around and be funny. And he likes the structure, + +[05:17] like he wants the official start, take a deeper, welcome to the nav bar. + +[05:23] You can see it's starting to eat at me. When does the actual podcast start? How do we do this? + +[05:29] We're just hanging. This is a podcast, man. We like, yes, there's the audio version, but like, + +[05:34] I like the, the idea of like making the live stuff more of a equal parts with the, I know you don't. + +[05:41] And anyone who was listening to last week where Simon said, we need to start shortening the + +[05:46] episodes so that we can like get this done quickly. This is, this is what he does in the + +[05:51] next episode. It's just, let's have a, let's have a whole podcast before the podcast. + +[05:55] Man, human stuff. I like it. I like it. I like it too. You should leave that in the audio version. + +[06:03] I don't. Yeah. Whatever. If you think people listening to the podcast want to just like the + +[06:08] factual official serious stuff, maybe the, I don't know. I like, I, I use, I compare a lot with Aaron + +[06:14] and Ian Lansman's podcast, and I love how they just talk about HVAC and random stuff. And it's. + +[06:20] Oh yeah, no, we can talk about random stuff. We just have to do this first. + +[06:23] Okay. Welcome to the NavBar podcast. Thank you for joining us for another week. My name's John. + +[06:28] This is Simon. Let's go. What are we doing this week? + +[06:30] What are we doing this week? Well, first of all, I have to make an announcement. I have launched + +[06:35] my epic web dev pixel perfect Figma to Tailwind. + +[06:39] Awesome. I have been so excited for this for a very, very long time. + +[06:45] How has the launch been going so far? It's been going great. I have learned + +[06:50] my mistake from last time where I launched Pro Tailwind and I did all this work. And then I + +[06:54] launched and I was like, job done. I've launched. I can just forget about it and do some other stuff. + +[07:00] And now it's going just to magically sell by itself. Actually, when you launch is the, just + +[07:04] the start of the, like, everyone's like, congrats, what are you going to start doing that you've + +[07:08] launched? I was like, well, work on the launch. And basically, yeah, it's like the launch is like + +[07:13] the tip of the iceberg and then everything else underneath it is like, oh, why are we, + +[07:19] for anyone watching the, oh, nevermind. That's completely in my head. That was on the local + +[07:25] recording. When I did that heart, Apple did its little heart thing, but I haven't frozen. + +[07:32] So that's good for anyone that didn't join last week or hasn't listened to that episode. + +[07:36] Uh, we had, or every episode we've had problems with, uh, the built-in MacOS, uh, like gesture + +[07:43] detection. And so when we do a gesture, it just like freezes the camera. Um, and you need to + +[07:49] turn it off for every application. So great. To name a few, uh, Scott Tolinski, Wes Boss, + +[07:55] and Ken C Dodds also got burned by this in the recent weeks. So it's, it's definitely a thing, + +[08:00] even season, uh, people used to record stuff and produce stuff. I get caught with that + +[08:05] even after turning it off and it just somehow comes back. This is the closest we got from + +[08:10] John dropping a swear word on the pod. I get them all out before we hit, before we hit record. + +[08:20] Simon tells me just before we're going to record, all right, clean it up. No more swearing. We're + +[08:24] going to put this out as a clean podcast. And so, uh, I'm very, I'm very well behaved once, + +[08:29] once we go live. Yes. So let me just finish my thought and then we can move to the actual topic + +[08:34] of today. I promise. Uh, yes, last time I launched and then I let it go in a launch is kind of like, + +[08:39] you've got this wave and then it goes away inevitably. And the idea is to try to keep + +[08:44] riding that wave and just do little pushes to, to maintain the, kind of like the retention on + +[08:49] YouTube video. Like once it drops, you've, you've lost people. And so basically I've sent, uh, + +[08:54] emails with like new, I've produced basically one YouTube video per day since it launched + +[09:01] instead of just retiring on an Island. And I just kind of emailed this with the, an article + +[09:06] and the video and then it'll call to action. Hey, by the way, if you like this, you can check out + +[09:11] this course and I'm just going to do the plug. Now there's six hours left before the 20% launch + +[09:16] discount of goes to normal pricing. So if you haven't checked it out, six hours, hurry right + +[09:23] now, go to epicweb.dev and then you will find the little banner at the top. Uh, the, the workshop's + +[09:29] called pixel perfect tailwinds. And we design, we take a Figma design and make it pixel perfect. + +[09:36] So it's responsive. It's a pretty cool stuff. Yeah. Right. It is. It is an amazing workshop. + +[09:43] And yeah, it's like the, the preparation that goes into putting a course together. It's like, + +[09:48] like the, the release is like the tip of the iceberg, but underneath that, that big mound + +[09:52] underneath is like all the preparation beforehand, like half of it's probably the preparation before, + +[09:58] maybe more than that. But then there's like this huge chunk of actually like marketing it + +[10:02] afterwards. Cause you can, you can put out the most amazing thing in the world, but if you don't + +[10:06] get it out to people, if you don't reach people that are interested in, in, in learning about + +[10:11] tailwinds, then like, you know, that's, that's a lot of time that you put into something that, + +[10:16] that just kind of flops. And so there's so much work that needs to go into, + +[10:20] you know, getting that message out there. So people know that the course actually exists. + +[10:25] And don't feel bad for sharing things a couple of times. I've spammed my course like for a whole + +[10:29] week. Kent C. Doggs with like a massive audience have spammed on Twitter as well and emailed his + +[10:35] whole entire mailing list. And then the other day he made a little poll, like, Hey, did you know + +[10:40] Simon has a workshop? Yes, no. And there's about 35% people said no. And I was like, like, I feel + +[10:46] like I'm going to get canceled for like just abusing, sharing the same thing over and over. + +[10:51] And there's still people on Twitter that follow Kent and me, and they didn't know. And it's like. + +[10:57] Yeah. And just, just for some perspective, like I've probably only seen, like I've seen one tweet + +[11:02] from Kent and maybe two tweets from you that have gone through my feed. And so like, I'm pretty + +[11:06] active in liking and sharing all of your stuff. And so the fact that I'm seeing so few things means + +[11:12] you could be repeating yourself a lot more. That's something that I learned when I, when I started at + +[11:17] Superbase, I used to like, you know, once I had put out the tweet, I was like, well, I've done + +[11:21] the tweet. And so that's done. I can't, I can't do that again. And yeah, the person that I was + +[11:26] working with at that point, thankfully challenged that idea and was like, no, you can like put out + +[11:31] the, like, not, not exactly the same tweet, like restructure it. Like, don't, don't just say the + +[11:35] thing again, but you can say the same thing in a different way, like five or 10 times. And yeah, + +[11:43] it's not going to annoy people and it's just going to keep reaching new people that didn't see the + +[11:47] original stuff anyway. And it's a nice, because making new videos or new little tips or contents + +[11:52] is a nice way to bring people back to the same thing with a fresh, new idea. And the first, + +[11:57] my first idea was to do, oh, I'll do a quick tip on design tokens in Tailwind and how you can map + +[12:02] them to background colors, text colors. And the next thing, you know, I did a 32 video workshop, + +[12:07] like to promote my paid workshop. I made the exact same similar structured workshop, but for free. + +[12:14] And it's like, it's not as long, but it's like a whole day workshop. Lots of exercise, 32 videos, + +[12:19] I've recorded everything I have to edit now, but it went from a, hey, let's do a quick five + +[12:24] minute video to, oh, I could structure it in six, seven lessons to, oh, problem solution sequence. + +[12:29] And the next thing, you know, I've built a whole app, a workshop app with like problem blog posts, + +[12:36] links to, anyway, it's going to be all free and it's taking me hundreds of hours to produce. + +[12:42] And when you talk about the tip of the iceberg, this is, that's the thing under that's not going + +[12:46] to make me any money directly, but hopefully people look at it and then there's a link to + +[12:51] the paid one. And they're like, oh, Simon Karma points. Let's support the guy. Very, very cool. + +[12:57] And with that, everyone that follows the rules, Simon says, purchase the course. + +[13:01] Yes. And when I say, when Simon says, it's not me, don't listen to me, but listen to Simon. + +[13:06] When Simon says you got to do it, you've been, you've been raised that way. You know, + +[13:13] you just have to, you showed me something very cool that you're working on to get some of these + +[13:18] tips out there. Is that something we're allowed to talk about? What did I show you? I showed + +[13:23] you a lot of things. You showed me like a tailwind tips thing that you had been working on. + +[13:28] Oh yeah. Yeah. That's public. Yeah. Sick. Tell us about it. The little toots thing. + +[13:34] Yeah. Yeah. Tell us about tailwind toots. So I've always, exactly what I've just told you, like I, + +[13:43] I started with a little idea and it turned into a full day workshop and it's taking hundreds of + +[13:50] hours to produce. And every time I make a video, I think it's going to be two, three minutes and + +[13:54] it turned into 27 minutes. And I'm like, I would love a really simple format, like a mini, mini + +[13:59] tutorial. Uh, like it's almost like Tik Tok or YouTube shorts, like less than one minute. + +[14:06] And there's vertical only. And it's like, just for the Gen Z sort of like super high energy. + +[14:12] And I'm not teaching you deep topics. I just pick one tiny thing, like eggheads, + +[14:16] shortened down egghead, if you think. And I was like, Oh, mini toots, tailwind shorts, + +[14:21] tailwind clips. Uh, and just as a joke, because I remember there was, uh, well, like, + +[14:29] there was a lot of controversy in the tailwind world at some point because someone compared, + +[14:34] uh, tailwind to a fart in a blog post that got very, very, very popular because it was + +[14:38] shared by... It is a tailwind, like... Yes. So I thought I'd take that twist of the people + +[14:45] comparing to the fart and make it kind of fun. And I, I was like, tailwind toots kind of sounds + +[14:49] funny. But then I realized that tutorial, uh, it's a mini toots. And, um, I, I, I just did for fun, + +[14:57] a little motion graphics intro and it looked so cool. I was like, Oh crap, this is cool. This + +[15:02] is actually cool. And I bought it the main name and I've thought I'm going to make it a little + +[15:06] series. Um, yeah. It is awesome. So go and check out tailwind-toots.com. It is a lot of fun. And + +[15:17] are you planning to put a whole bunch of tailwind toots on there? I have already failed, but the + +[15:23] idea is to do one per day because they literally take me... The intro took me about one or two + +[15:28] hours to do. Uh, but the, the execution on the, on the actual videos, uh, because they're like + +[15:38] really short, it actually takes me only about five minutes to record and 20 minutes to edit, + +[15:43] I guess. Uh, I'll just, I don't know if the sound's going to come through, but I'll play one. + +[15:47] Uh, if you, if you're not, if you're listening, you're missing out. And if you're here, you can + +[15:50] see. Oh, I can't quite hear. See, there's a fantastic little fart noise there, uh, that you + +[15:59] just missed. Yeah. So if you, if you can't see that basically like there's a little stop motion + +[16:05] intro and there's a tailwind logo that comes and then it makes a playful confetti fart and moves + +[16:11] out of the way and then the short intro starts. Uh, and yeah, I mean, I think this is going to + +[16:19] be fun because it's something that I can consistently do. Uh, I've done it for two + +[16:23] days back to back and then I failed already because I'm producing the workshop launch + +[16:28] because it's the last day, but I'll go back to it every time I have a super simple idea. + +[16:31] The first one is literally, um, uh, I forgot what it is, but it's like a super tiny little + +[16:37] thing that you, that you can explain in five seconds. I think the second one is talking about + +[16:41] gap. And so how you can use grid and the gap property that, uh, will then make things in + +[16:48] a list respect things like margin and padding and things. Yeah. So I think the smallest possible + +[16:52] scope, like if I'm, if I'm building something and I realize, oh, maybe people don't know, + +[16:57] but you can bake in the opacity of a color by doing slash 30 for 30% opacity instead of going + +[17:03] BG green 500 and then BG opacity 30. And I just do a tip on that and it takes five seconds and + +[17:09] it's like a 15 second clip with the intro and the, and it's, it's really cool. And the idea is that + +[17:14] this is the sort of stuff people like and share because it's very tweetable and like friendly + +[17:20] and fun and still informative. And I can, I can do one a day without freaking out. Yes. + +[17:25] You asked me to talk about this. No. And in terms of like knowledge, + +[17:30] like you are like the person who probably like you probably like, okay, Adam might know more about + +[17:37] like the, the actual like design of tailwind, but you've probably used tailwind more than Adam has. + +[17:44] Like you, you are the, like the pinnacle of education in terms of, of, of tailwind. And so + +[17:51] all of those little bits of nuggets of things, like your brain would just be filled with them. + +[17:56] There'd be so many things that you would take for granted that you think that people know about + +[18:00] tailwind just because you've been doing it for years. That's exactly right. So the, yeah, that's + +[18:06] the sort of stuff that people pick up on a video that I do. Oh, I had no, like in the workshops, + +[18:10] they say, Oh, I didn't know you could do this. I'm like, Oh, that's like a one-on-one thing for me, + +[18:16] but it makes sense. And then like, this is the sort of tips I want to do. Just go through the + +[18:21] docs and like anything that I'm like, Oh yeah, this thing, not everyone knows, little toots. + +[18:25] Yeah. I'm excited. It needs to be a way you can subscribe to them. So I get notified every time + +[18:32] there's a new toot. Yeah. They're just like notifications and on, on your computer, it just + +[18:39] makes a little fart noise. There's a new tailwind toot out. There you go. Instead of the stripe + +[18:45] sales, like, yeah, yeah, yeah. That's right. Yeah. Would you like to talk about the thing + +[18:51] that you've built as well? I would love to talk about the extreme amount of effort that I've gone + +[18:58] in to build the thing that I've built, uh, last night at about 10 30 PM, I was like, Oh, that's + +[19:04] right. On the next episode of the nav bar, we're going to do a, uh, a stumped episode, essentially + +[19:11] a stumped inspired episode. So syntax and a whole bunch of other podcasts have this, uh, this format + +[19:18] where, um, you like, I will ask Simon a like web development interview question, uh, and then we'll + +[19:26] see how he does in answering it. And then he's going to ask me a question and we'll go back and + +[19:30] forth. Uh, it's just a fun way to, to break up the podcast a little bit and, uh, do a little bit + +[19:35] of a, a game. So I was like, well, we need some questions. And so I found a GitHub repo, which + +[19:41] will be in the show notes that had a whole list. It was, it was the one with the most stars, um, + +[19:49] in front end interview questions. And so it had just a big Markdown file of a whole bunch of + +[19:55] HTML, CSS, and JavaScript questions. And so I, uh, copied those Markdown files and then built a new + +[20:03] Astro app just because it was going to be super fast to do it this way. Um, and so I added, uh, + +[20:10] the, these Markdown files and then just created a separate page for JavaScript, HTML, and CSS that + +[20:14] pulls from that particular Markdown file. And it just converts the big Markdown list into a Jason + +[20:20] list and then picks a random question from that list. So when you click either JavaScript, HTML, + +[20:25] or CSS, uh, it will navigate to that page and give you a new question. So you can just keep + +[20:31] clicking CSS and it will just keep giving you a fresh interview question every time. + +[20:36] - Is that using the new Astro actions and servers? + +[20:40] - It doesn't need to use any actions. This is all just like server side stuff. And so, yeah, + +[20:44] it is a, um, a dynamic application. So the output in the Astro config needs to be set to server, + +[20:52] which means that every page is loaded on the server. It's doing SSR stuff rather than statically + +[20:58] generated entirely, uh, static content, um, which is obviously necessary because we want to keep + +[21:05] loading a new question every time we navigate to that page. So if we, if we didn't set that + +[21:09] configuration, then it would load it once and then that would be it. It would just stick with that, + +[21:14] which is exactly what happened when I pushed it to production, because I forgot that that's + +[21:18] something that I need to do to make things dynamic. And so I set that config, uh, and then + +[21:23] I forgot that I don't, that I need to include a Vercel adapter to, to actually run that server + +[21:30] stuff. And so I did that and now it's a beautiful site with absolutely zero CSS. It doesn't even + +[21:38] like, this is probably the first project in like three years that hasn't included Tailwind. And + +[21:43] so it even has the terrible default styling of the browser that like serif font. That's just. + +[21:49] - Yeah. It's beautiful. You can see there's a body tag and then just hrefs anchor tags inside. + +[21:55] - That's all you need. + +[21:56] - Yep. Like it. + +[21:58] - All right, Simon, are you ready? + +[22:00] - I've added some things that we should add as categories, but so far that's just like, + +[22:06] if I refresh the page, they're going to go. Yeah. So we thought that'd be fun to kind of + +[22:11] expose ourselves and make sure we never get another job ever anywhere else by, + +[22:15] by being fraudulent on live streams on three platforms. Good idea. + +[22:20] - As you can hear, Simon's feeling a little, a little anxious about this, + +[22:23] a little anxious about exposing his, his lack of knowledge over these categories. So + +[22:28] I think he's going to be fine. And also I think it's, you know, it helps humanize the whole thing + +[22:34] and make other people feel like they don't need to know everything in the entire world, + +[22:37] because I'm sure there's stuff that we're not going to know as well. + +[22:39] - Yeah. And the reason it's on screen. So like, if you're listening, we have the website and + +[22:45] there's three buttons, JavaScript, HTML, CSS. And when I click on one, according to John, + +[22:49] it's going to pull up a question so we can take turns. Do we... + +[22:53] - Simon's pretending he didn't just stay up all night reading every single one to make sure that + +[22:57] he had a prepared answer. I've never seen this website in my life. What is this? What's going + +[23:02] to happen when I click these buttons? - I got all my notes here. + +[23:05] - He's ready to go. So yeah, I think we need to add a Tailwind one in there that has some like + +[23:13] proper stumping questions for Tailwind. And we need to add SQL or some kind of like + +[23:18] database-y stuff, which you can try and stump me on. I'm sure you will easily stump me on. + +[23:22] So would you like to go first, Simon? - Yeah. We got about seven, eight minutes + +[23:27] in a half an hour window. I think it's cool to do like a test run of this and then we can do + +[23:31] a whole episode. So how do we go? Do we get to choose who, like if I choose a category and then + +[23:41] you ask, how do we go? - I guess I get to choose + +[23:43] the category and then you ask me the question. So how about we go with trusty old JavaScript? + +[23:47] - Oh, start with JavaScript. Okay. - No, you're asking me a question, + +[23:51] so it's going to be okay. - Yeah. I zoomed in a lot, + +[23:54] so I don't know how big the text is going to go. Hey, perfect. What is the definition of a higher + +[24:00] order function? - Okay. I haven't written a + +[24:04] higher order function in a long time or the React pattern that was inspired by it, which is a higher + +[24:09] order component. I haven't written one of those in years. I used to write a lot of them. And so I + +[24:16] probably can't actually explain this. So a higher order function is simply just a function that + +[24:21] returns another function. So it's a function that you can call, you pass it parameters or whatever, + +[24:27] and it will return you a function that you can then call later. And so this is a way that you + +[24:32] can kind of have like some like secret config-y stuff, or it doesn't necessarily have to be + +[24:39] secret, but some kind of like configuration stuff that you need to do to the function that you don't + +[24:43] want the user to have to write a whole bunch of stuff to do. So an example from coming from the + +[24:48] Superbase side, we do a lot of like handling auth stuff for you. And so there's a lot of stuff in + +[24:56] Superbase where you call a function and you get back another function, and that's something that + +[25:00] you can call later to see whether the user is signed in or not. That would be a good example + +[25:04] of a higher order function. - Wow. + +[25:06] - What do you think, Simon? Because there's one part that's missing from this, because I just + +[25:12] cobbled it together very quickly last night, is that it doesn't actually have the answers. So I + +[25:17] guess we just need to... Well, I guess now there's another level to the game. + +[25:20] - I was about to say that. - You need to decide, + +[25:23] was that answer correct enough? And so part of the game is that I need to sound confident enough + +[25:29] that even if I'm completely making it up, it sounds accurate enough that you give me the point. + +[25:33] - I just realized, as you started answering, I was like, "Oh, I'm asking myself the question + +[25:38] because I'm going to have to confirm or deny." I think you did a good job. And you mentioned that + +[25:44] in React, it used to be a pattern. So before, now we have hooks that kind of let you add, + +[25:50] augment a component with functionality. And before that, there was the render prop pattern, + +[25:55] which lets you pass some things to a component. And even before that, there was this higher order + +[26:00] component thing where you would have a component and then you're like, "Oh, I wish we had access + +[26:04] to more stuff." And it would do a few things here and there that we don't need to replicate every + +[26:08] time. So you would have a component that adds this thing and then returns the other components. + +[26:13] So like you said, React components are really function calls. So it's a function that wraps + +[26:18] some config and functionality and then passes it to the next thing that the user consumes. And so + +[26:23] when they consume that, it comes with the banana in the jungle around it sort of stuff. + +[26:27] >> That's right. There was function as a child component. And there were all these crazy patterns + +[26:35] to do this kind of handling some of that stuff somewhere else and giving you a simple interface + +[26:42] to just use it. >> And they needed to be another + +[26:45] component because to be able to set props and do anything, you needed a component unit. + +[26:50] And that's where hooks kind of came. And they were like, "Oh, you don't need to return anything. + +[26:54] You just do the logic and then you use that hook in your function." And so you're like, + +[26:59] "Oh, are you telling me I don't need to have a whole UI components that returns + +[27:03] nothing just to have some logic that I pass to something else?" Then hooks became like, + +[27:07] "You just take the thing you want to augment with and write it as basically a JavaScript function + +[27:12] that starts with use. And then you can use that inside your component instead of passing it from + +[27:18] another component as an arrow function where the props get accessed." And yeah. + +[27:21] >> It just shows you how clever people will be. You give them a couple of tools and they will + +[27:29] create these insane patterns to do really complex things. And some of those catch on and some of + +[27:36] them don't. And then obviously all of these patterns inspired things that obviously were + +[27:41] lacking like hooks, which are now some form of that is in every component library or every web + +[27:48] development framework. >> You mentioned Kent C. Dodds. + +[27:51] I want to say, I think the render prop pattern was... It existed probably. I don't think Kent + +[27:57] invented it, but it got popularized very, very bigly with his downshift library, which was + +[28:04] basically a way to do combo box, drop down, whatever, without any headless style. And + +[28:10] basically it gave you the primitive and all the logic. And then you would be able to just do the + +[28:15] front end by consuming this. And it was, I think it was all, not the higher order function, but the + +[28:20] render prop pattern. So you have this crazy syntax where the child of the component is actually a + +[28:27] function that returns and you can restructure the props there and then use them inside there. + +[28:33] That's how I discovered the render prop pattern. >> That's a great example of like, yeah, all that + +[28:38] configuration is done for you. There's so much stuff that a combo box needs to do. And for you + +[28:44] to copy and paste that from the docs, there's a huge amount of code for something to go wrong. + +[28:49] Whereas if you just consume this library, it does all of that for you and then just gives + +[28:56] you the opportunity to tell it how you want to style it. And so it's taking one of those concerns + +[29:00] away and doing all of that config or all of that logic, and then just giving you the bit that you + +[29:05] want to be unique for your app, which is where do we get the results from and what does it look like + +[29:10] on the page? >> Exactly. That's headless UI in a nutshell. If you use headless UI speaking, + +[29:15] literally headless UI library, they use the render prop pattern. And if you use something like React + +[29:20] area, it came a bit later. It's very hooks driven. You have use accessibility keyboard navigation. + +[29:28] There's all these use hooks. And then you can compose anything. You literally just write CSS + +[29:35] and apply styles to this thing that has all the accessibility concerns, which is great. + +[29:39] >> All right. I give you a pass. That was a really good answer. Should I have a crack? + +[29:43] I'm scared of clicking on JavaScript, but we'll do this. >> You don't have to. You can choose + +[29:48] CSS or HTML. It's up to you. >> I'll go JS. You went in the deep end. I'll try that too. Although + +[29:52] we come from different backgrounds. So I don't know if I'm going to click and you can read the + +[29:56] question. Okay? >> All right. + +[29:57] The question is what is strict mode? What are some of the advantages/disadvantages of using it? I'm + +[30:06] glad you got this question. I know I can kind of like describe it. But let's see how you do. + +[30:11] >> So strict mode is a mode in JavaScript that's a bit more strict where you add use strict at the + +[30:18] very top of the file. And it's like basically before TypeScript and flow and all this type + +[30:24] safety stuff, it kind of enforced a little bit more rigor in this wild west of JavaScript. + +[30:31] Unfortunately, I cannot give you one single example of what strict mode enforces. + +[30:37] >> I can't remember either. >> But I remember, yeah, it's just it's + +[30:45] almost like a more serious even now in TypeScript you have TypeScript loose, relaxed, and strict. + +[30:51] And that was the strict version of just JavaScript. And I cannot for the life of me tell you one + +[30:57] thing. But it was like the equivalent of like adding some ESLint stuff that you shouldn't do. + +[31:01] >> Yeah. That's a good example. Yeah. So, like, it was like a subset of rules that would, like, + +[31:08] fail if you used certain things. Like, I don't think it was as strict as this. But let's just + +[31:14] say that one of those rules was like using double equals instead of triple equals. That's the kind + +[31:20] of thing. It would say you can't use double equals. You have to use triple equals. Which, + +[31:25] yeah, is like an ESLint rule that you might set up for your project where you have a list of + +[31:29] things that you want to be able to that you want it to throw errors about or tell you not to do + +[31:36] in your code. And so, I guess, yeah, strict mode was like a subset of JavaScript of things you're + +[31:42] allowed to do in JavaScript. >> React also has a strict + +[31:45] mode and TypeScript has a strict mode. Like, everything has, like, a step up. Like, it's + +[31:49] basically the buttoned up, like, to the top version. >> We're taking this seriously now. We're going + +[31:55] to a dinner party kind of JavaScript. >> I used to know, like, I probably used to + +[32:00] know. But I haven't written use strict at the top of a file in, like, probably a decade. So, + +[32:06] that makes it kind of hard to... >> Yeah. It definitely takes me back + +[32:09] to, like, yeah, like, pre-job. Like, I feel like even when I was, like, at university, + +[32:15] I maybe used use strict. Like, it was kind of a common thing that you put at the top of a file + +[32:20] before things like React and Angular and everything else kicked off. + +[32:24] >> Yeah. So, when I used to do ThemeForest templates, this is kind of how I got my start + +[32:29] into client work. I made templates on ThemeForest and then it got me some street cred and landed + +[32:34] some clients. And I remember uploading my first theme. Like, I got rejected many times. But when + +[32:39] it was design-wise ready for acceptance, they were, like, they're, like, three or four little + +[32:45] things to fine tune and we'll be ready to approve this. One of them is you need to add use strict + +[32:50] on the top of the file. And I literally just add use strict and they accepted it. That was my first, + +[32:56] like, oh, that's a thing. And then I looked into it and then proceeded to forget about it. That + +[33:00] was 2013. So, 11 years ago. >> Nice. All right. So, we're trying to + +[33:06] keep these episodes short. So, should we just do one more question each and then wrap it up + +[33:10] with some sick picks? >> Yes. + +[33:11] >> All right. >> HTML or CSS? + +[33:14] >> Let's go HTML. >> All right. + +[33:18] >> All right. >> I'm reading. It's for you. + +[33:21] Sorry. >> John, question for you. What are + +[33:25] empty elements in HTML? >> All right. I don't actually know what + +[33:31] empty -- well, okay. All right. I'm going to assume that this means what I used to refer to as + +[33:40] void -- no, self-closing tags is what I'm going to go with. And so, I'm going to say empty elements + +[33:47] in HTML are a HTML element that doesn't have any children or any content inside of it. And so, + +[33:56] something like a horizontal line, for example, that is a HTML element that does not actually + +[34:03] have any content inside of it. And so, it just draws a line on the screen and you -- the way + +[34:10] that you do that is with a self-closing tag. And so, you have the open angled bracket and then HR + +[34:18] for horizontal rule and then space if you want to make it look nice. And then a closing slash and + +[34:26] then the angle bracket. You don't have any content in there. It's just the thing itself. It can still + +[34:30] have attributes. And so, it could still have an ID and a class and be styled and things like that. + +[34:35] But it doesn't have any children elements within it and it doesn't have any content within it. So, + +[34:41] it doesn't have any text or anything like that. What do you reckon, Simon? + +[34:44] >> I genuinely do not know if that's -- the first thing I thought is exactly that. I was like, + +[34:50] yeah, self-closing like an image tag or what you said, the horizontal rule. But then I thought, + +[34:56] I don't know if you know, but the template tag in HTML, and it's like a React fragment. A template + +[35:01] is a disappearing thing. So, you can put it in your markup and then it disappears. And so, + +[35:08] maybe that's what -- because empty elements, like I'm sure some people will see that question and + +[35:14] be born and raised with React as the first thing they learn. They're like, oh, it's a fragment. + +[35:18] It's an empty tag with no semantic stuff. So, HTML is that template tag, which is -- and if + +[35:26] you've done some Vue.js, they use the template heavily, or Alpine.js, because the thing will + +[35:30] disappear. So, if you want to do an if/else, you can have a template with the condition and + +[35:35] it's going to not render an empty element in the -- so, maybe that's an -- I just said empty + +[35:41] element. So, maybe that's how I think. So, what I think is you need to add a button here that says + +[35:48] you click and it asks Claude or chatGPT and then it gives us the answer. That would be kind of fun. + +[35:52] >> That is a fantastic idea. And PRs are open for anyone that wants to implement this. + +[35:59] Oh, Simon's going for it. All right. He's asking chatGPT. Oh, no, he's asking Claude, + +[36:04] what is an empty element in HTML? This is a fantastic idea. This is how we get our answers. + +[36:09] We don't think for ourselves. We use robots. >> An empty element is an element that does not + +[36:14] have any content between opening and closing tags. >> Boom. Boom. + +[36:17] >> Self-closing. Line break, horizontal rule, image, inputs, meta, link. + +[36:22] >> There you go. But I feel like your answer was also fantastic. I would have given it to + +[36:27] you. So, I reckon we both get a point. >> You get a car. Everyone gets a car. + +[36:31] >> Everyone gets a totally meaningless point. >> My turn. HTML, you ask the question. + +[36:39] >> You want to go CSS. You know you want to go CSS. Oh, no, we're going HTML. All right. + +[36:44] Have you used different -- all right. Skip that one. Next one. Have you -- it doesn't really make + +[36:50] sense. That's an interview question, but it doesn't make sense in this trivia style of stuff. + +[36:55] Consider HTML5 as an open web platform. What are the -- let's go a different one. These suck. + +[37:01] What -- this is a fantastic question. What are data attributes good for? This is where you do + +[37:10] data dash something. >> Don't start answering. + +[37:13] Look at John starting answering for me. First of all, you say data attributes. I say database, + +[37:22] but data attributes. >> People always -- like, I work for a + +[37:26] database company. And so, people always call me out for my pronunciation of data. And the funniest + +[37:33] thing is in tweets, they're always like, dude, it's pronounced data, not data. Hilarious. + +[37:38] >> Duh. Duh. So, data attributes -- >> What's data? + +[37:44] >> Yeah. So, basically, it allows you to pass any arbitrary attributes to an HTML element. Almost + +[37:52] like a React prop. Except it can't be, like, objects and complex stuff. But so, I'll give you + +[37:59] a concrete example. If you want to enable dark mode or a special theme on the website, you could + +[38:05] have a class that says, like, theme -- that's what I actually do in my free workshop that I'm + +[38:09] building. Theme citrus. It's like an orangey, lemony color theme. And you could have a class, + +[38:15] and if you add the class to the body tag, then the theme applies. But you could have a data dash + +[38:21] theme equals citrus. And then it's much more explicit. It's like, oh, the person looking at + +[38:26] the HTML sees data theme. Oh, that's probably a theme explicit way of showing intent. And so, + +[38:34] you can do a lot of things like this. And you can access them in JavaScript and you + +[38:41] can access them in CSS as well. So, it's super hookable. For example, I've seen people building + +[38:47] entire UIs and they use data attributes instead of classes for everything. And then that means + +[38:53] that you can completely style them independently of any CSS framework. It gives people just, like, + +[39:00] use data dash whatever. In CSS, if you want to target it, you need square brackets around it. + +[39:05] So, if it's data theme, you need opening square, data theme equals citrus closing square brackets, + +[39:10] that's the selector. And so, you can do really, really cool stuff. And so, you can augment your + +[39:16] HTML with, like, pluggable logic stuff. Like, you can do JavaScript or CSS responses to whatever the + +[39:24] data attribute is. >> Perfect. I love it. Yes. So, yeah, + +[39:31] that is definitely the thing that I found data attributes most useful for is, yeah, being able + +[39:36] to select them in both JavaScript and CSS. Like, beyond class and ID, is there a way to just, like, + +[39:47] select something based on some other arbitrary prop in CSS? + +[39:51] >> Probably. But, like, what you just said, class is meant for styling and ID used to be, like, a + +[39:58] unique thing for JavaScript. But you can only have one in the that has to be unique because it's an + +[40:04] ID. And then when you use it as a CSS selector, it's got high specificity. So, like, it wasn't + +[40:10] designed for the sort of, like, oh, I want to almost annotate my element with, like, a little + +[40:16] label or tag or something. Data attributes, exactly that. Yep. + +[40:20] >> Yeah. And I guess in JavaScript, like, there's I don't think there's a way to select something. + +[40:26] Like, the old way of doing it was get element by ID or get element by class name. And then + +[40:33] that got replaced with a query selector, which allows you to basically do, like, a CSS selector. + +[40:38] But, yeah, I don't think there's a way to select something other than ID or class name or a data + +[40:45] attribute. And so, it gives you, like, that extra escape hatch to have any other piece of data that + +[40:52] you want to attach to a HTML element that you then want to be selectable in either JavaScript or CSS. + +[40:59] >> So, I'll give you a really concrete example. If you're in React and you want to do, like, + +[41:04] prop-based style. You have a button and it has a variant, primary, and secondary. You can do a + +[41:10] React prop and it's, like, primary or secondary and you apply different classes. And then if you + +[41:15] take this outside of React in HTML, you're toast. Because, oh, there's no props and no components + +[41:20] in there. But if you had the exact same components and instead of doing a prop called variant, + +[41:26] you did an attribute data-variant equals primary. Data variant secondary. It's still just HTML and + +[41:34] CSS. So, you can move that to HTML without React and you can still kind of style the button + +[41:40] differently based on the data attributes. Where if it was a variant prop, that's a React JavaScript + +[41:46] thing. It doesn't exist in HTML. So, you can be clever about how you if you think you're + +[41:52] going to have a part of the website or your application in your company that doesn't use + +[41:56] JavaScript frameworks, you're like, oh, use the platform. That was the HTML5 building blocks, + +[42:02] the question before. Data attributes is one of the big building blocks. Do I pass? + +[42:07] - Absolutely. Flying colors. + +[42:10] - John, we have a rule, but I really want to do one CSS question each. So, + +[42:13] we've covered the whole loop. My sick pick is CSS and I'm going to ask John a question. + +[42:19] - Our first episode trying to go with the shorter format, we have failed. But I love it. Let's do + +[42:26] it. - John, question for you. I'm + +[42:28] clicking on CSS and asking, can you give an example of a pseudo class and can you provide + +[42:34] an example use case for a pseudo class? - Oh, Simon, Simon, Simon. I always forget + +[42:40] which one is the pseudo class and which one is the... + +[42:43] - Pseudo element. - Pseudo element. Okay. So, + +[42:49] I'm definitely not going to be able to distinguish between those two, but maybe you can give us a + +[42:53] follow-up answer that can. But a pseudo class, I'm going to say, is something that you can select + +[42:59] based on a particular state. And so, a good example of a pseudo class is like hover. So, + +[43:06] you want to select, basically, you want to change the styling of like a button when you hover over + +[43:12] it. And so, your selector would either select the button element or it might select it by its ID or + +[43:18] its class. And then you do colon hover. - Sometimes one, sometimes two. + +[43:27] - Sometimes one, sometimes two. - I think one is fine. I always do + +[43:30] one. You used to, like, I think there was a browser thing. + +[43:33] - Right. I remember when there was a distinction. But yes, I don't think there's a distinction now. + +[43:37] And we all use Tailwind anyway. So, there's always a single. So, yes, you do, like, you know, + +[43:43] whatever the button selector is. And then colon hover. And then you specify your styles that are + +[43:48] specific to when you're hovering. And so, that might be like either underlining the link or + +[43:52] something that you're hovering over. Or it might be changing the color of the button. Just to give + +[43:58] the user some feedback that they're actually doing something. And that's a clickable element. + +[44:02] So, how did I do? - Yeah, that's great. That's + +[44:06] exactly right. Pseudo class is a thing that represents a different state. Like hover, + +[44:11] active. There's lots, lots, lots, lots of them to get really complex stuff. And I think the + +[44:16] difference with pseudo element is when instead of having a div or a span, you have a before or + +[44:21] after, which is kind of like a it's attached to the element. You can have before and after. So, + +[44:27] if you want, like, a floating quote on a testimonial, you can go before. And then + +[44:32] you go position absolute top left, font size, blah, blah, blah. And you position your little + +[44:36] quotes without adding an element. - So, that's the distinction + +[44:39] between pseudo class and pseudo element. Is a pseudo element is actually, like, a fake element? + +[44:45] - Yeah. It's more like a shadow element. - You can treat it like an element. + +[44:49] - Yeah. - Yeah. + +[44:50] - Right. - That's it. + +[44:51] - That makes sense. - Good. Question for me. + +[44:53] You gonna read it? - Oh, I'm gonna read it. What is the + +[44:56] difference between CSS grid and Flexbox? Come on. How does he get this one? When would you use one + +[45:02] over the other? - Listen, man. I've done live + +[45:06] workshops. And this is the most asked questions during the live workshops. Because every time + +[45:11] you build a UI, people are, like, oh, how do you know when to use? So, the first part of the + +[45:16] question is very simple. CSS grid and Flexbox are two completely different layout engines. + +[45:21] But the second one, when to use one or another, is kind of, like, more where the money shot is. + +[45:29] CSS grid is basically a way to lay out a grid with rows and columns. And then you can assign + +[45:35] things to specific columns or, like, cells. Think of a table, Excel sheet. I want to put this image + +[45:42] in the row C4 or something like this. And you can skip cells, which means you can have the title on + +[45:49] the top left, like we see. And you could have maybe two thirds of the bottom, two thirds of + +[45:54] the right in that cell, you could have an image. You can do absolute position, but the grid really + +[46:00] gives a grid. And then you can decide if you show the line or not. So, it's like a grid-locked + +[46:08] design layout primitive. And then Flexbox is more of a free flowing in one direction thing. So, + +[46:16] you can flex horizontal and whatever you put is going to go side by side or flex col, which is a + +[46:23] flex direction column, which is, like, vertical. And so, it doesn't have it's a uni-direction + +[46:31] concern, where if grid is two directions, X and Y axis, Flexbox is only one. So, you can never say + +[46:38] go, like, you can make it wrap. So, it's going to make rows and effectively use multiple dimensions, + +[46:45] but it goes horizontal or vertical. And so, when to use which or what, first of all, if you ever + +[46:54] -- actually, let me go on my Twitter. There was a really interesting point discussion today. + +[47:00] If you don't see, again, you're missing out. Where is Dev Neal who took a screenshot? If I don't find + +[47:07] it immediately, I will not show it. But he shared this thing. So, it's a screenshot of my workshop. + +[47:14] And basically, it shows a grid of things. It looks like a grid because it's all aligned, + +[47:18] but the last row is kind of offset. It's centered and it's breaking the grid rows. + +[47:23] And the grid, like, in the DevTools, you can turn on the grid lines, like, + +[47:29] the visual representation. So, it really looks like a grid because these lines are turned on. + +[47:33] And someone said, hey, how do you actually do the last line kind of offset? And the secret is here, + +[47:39] it's using Flexbox. Because it wraps from left to right, and then it has these justify items + +[47:47] center. Justify item is on the main axis. And so, the last row is center aligned and it breaks out. + +[47:53] It's hard to explain if you're listening, but it breaks out of what looks like a grid for all the + +[47:57] other rows. And basically, if you want to do something like this, CSS grid is going to be + +[48:03] really not the right decision. Especially in that design, you've got to imagine as the + +[48:08] viewport gets bigger or smaller, things wrap and just use whatever space. So, my short answer to + +[48:14] the first question, this one, when we use I always say that if you want to let things free, flowy, + +[48:24] and use whatever space available, use Flexbox. Flexbox is that elastic, fluid, give me whatever + +[48:30] space I have and I'll find a way to fit in. Use Flexbox when you want to do stuff and + +[48:36] the available space dictates how they should go. And then use grid if you want it more controlled. + +[48:42] I have my grid organization, my sidebar here, the holy grail layout. I want these things like this. + +[48:48] And the great thing is you can use Flexbox inside of grids. You can also use grid inside of Flexbox + +[48:55] if you want. But think of them as different things. Really, grid is two axis. Flex is one. + +[49:01] And grid is very strict control. And Flexbox is free, sometimes magic. A good friend of mine + +[49:09] at a conference talk called Flexbox the I'm feeling lucky button they had on Google. + +[49:13] Just like, let's put Flex and see what happens. That looks kind of cool. + +[49:19] >> Nice. I'm afraid you're completely wrong. You couldn't have been more wrong with your + +[49:24] description. I'm joking. That is 100% correct. Well done. Great explanation. Yes, I have an + +[49:33] interesting history with grid and Flexbox in that when I started learning to code, + +[49:40] it was at university and the courses were outdated and things. So, I learned lots about float layouts + +[49:47] and clear fixes and all of that stuff, which I'm sure you would have learned. You would have learned + +[49:53] at the right time back when you were actually implementing that when there was no Flexbox + +[49:57] and grid. The difference was there was Flexbox and grid. But, yeah, I think grid had just come + +[50:07] out. Like, it was supported across all browsers. But it was very early days. And I remember, like, + +[50:14] I learned that. And I was like, oh, my god, we don't need floats. This is amazing. This is the + +[50:18] tool for everything. And I started trying to, like, wedge everything into this grid layout + +[50:23] and try and use that for everything I was designing. And my boss at the time kept being like, + +[50:28] I think you should learn Flexbox as well. Grid is appropriate for some stuff. But I think you're + +[50:34] maybe making this more complex than it needs to be. I kept being like, no, no, no. Grid's the + +[50:39] answer. It's the future. It's everything. It's everything we need to do. And then I learned + +[50:42] how to use Flexbox. And I was like, ah, okay. I probably don't need grid for a lot of the things + +[50:48] that I'm using it for. And then I think since then, like, maybe used grid, like, a couple of + +[50:55] times for a very specific layout where I'm going for that, like, yeah, that table, like, fixed grid + +[51:01] thing. But most of the time when you design, well, I find most of the time the things that I'm + +[51:05] designing want, like, with that responsive layout need to kind of shift around where they are and + +[51:12] where they take up space. And so, having that, like, wrapping Flexbox always, well, pretty much + +[51:18] always seems to be the thing that I reach for. What about yourself? Like, how often do you + +[51:22] actually use grid? So, listening to you, I feel like I use grid a lot more than you. + +[51:26] I'm bad at CS. I'm terrible at it. I do things the wrong way because I don't understand the + +[51:33] other way enough. Like, I struggle to explain my reasoning. Like, whenever I see something, + +[51:39] I always kind of, like, sometimes you could use both. And when I teach my workshop where we do, + +[51:44] like, a grid of logos, I'm like, you could do Flex or grid. And I'm going to show grid. And + +[51:49] then in the solution, it shows Flex. And, like, I'm, like, purposely saying, oh, like, depending + +[51:53] on what morning I wake up, I would go with this thing. It's not always very clear which one I + +[51:58] would choose from. But I tend to use grid for also very small things. And, like, a media object or + +[52:07] you think, like, yes, the media object is when you have an image or media on the left. And then + +[52:12] title and thing on the right. Like, the typical UI card. Like, a contact card or something. + +[52:18] And basically, you can go Flex on the parent. And so, you have the image on the left. And the next + +[52:24] one goes on the right. And you add a little gap. And then you can decide which one shrinks or + +[52:29] grows based on the elasticity of Flexbox. But you can also have a grid with two columns. And then + +[52:35] you go auto and then 1FR. So, I said grid is very strict, but it also has that elastic mindset. So, + +[52:42] 1FR is one fraction unit of whatever's available. So, if you go 1FR, it's going to elastically + +[52:48] adapt to whatever. So, I know most people for this UI card that you see a contact name, + +[52:55] they would go for Flex. But I tend to probably use grid. And then you're, like, what if it goes + +[53:01] stacked on mobile? And you're, like, well, it goes from a two column grid to a one column with + +[53:05] two rows grid. So, it also has that restructure. It really depends. I really use Flex when I don't + +[53:15] know how much space things should use. And I'm, like, I want Flex to decide. Like, the wrapping + +[53:20] logos I was showing, like, basically, it's going to spread from left to right. And when there's no + +[53:27] more space, it wraps to the next line. And as you grow the viewport, it just reshuffles every time. + +[53:31] And that with grid is impossible, because you have to define how many you would have to go. + +[53:36] That's 224 pixels. That's three columns. Like, you have to, like, aggressively hard code the + +[53:43] grid columns, which goes against the whole idea of flowing, fluid HTML. So, if I don't know what + +[53:50] space things should, I'll say Flex, you decide. And if I know what I want to achieve, I tend to + +[53:55] use grid. Because it's capable of doing almost the same sort of things with the right wrangling + +[54:01] of grid template rows and columns. >> Nice. Well, yeah. And I like what + +[54:06] you said before about, like, the top level layout. Like, usually, that's pretty predictable. Like, + +[54:10] you want it to be laid out in a particular way on, you know, on desktop and a particular way on + +[54:16] mobile. And so, that makes sense to code that out of shell as, like, in grid. And then, yeah, lay + +[54:22] things out with Flexbox or whatever. Throughout. This has been fun. This was fun. So, you weren't + +[54:30] exposed. If anything, you were, like, look at that explanation of Flexbox first grid. It's amazing. + +[54:36] >> We could have gone I'm gonna be very honest now. John shared the link yesterday, and I clicked + +[54:44] once on JavaScript just to get a vibe for it, yeah? And the question was, what is currying + +[54:50] in JavaScript? And I was, like, oh, come on, man. I've heard of it. I've never used it. + +[54:54] >> Can I try and answer it? >> Yes. + +[54:56] >> Okay. So, like, yeah, just to give some context as well. Even when I was, like, when I suggested + +[55:04] we were gonna do this, that's the thing well, that enclosures the thing that Simon freaked out about, + +[55:10] like, having to actually explain. All right. So, currying is a way actually it is a higher order + +[55:17] function. It is a way to return a function from another function that sets some of that stuff up + +[55:23] for you. And it uses closures to close over oh, look, you've got it up right there. Don't give me + +[55:29] the answer. >> You're talking about it, and I'm + +[55:34] showing you what I've learned about it. >> It allows you to, like, call one function + +[55:39] passing particular arguments, and then it will return you another function that uses those + +[55:45] arguments. So, it's a way of creating a closure around those arguments that you fed into it, + +[55:50] and it gives you it returns you a function that you can call somewhere else. A way that I've used + +[55:55] this a lot in the past is with, like, React when we all moved to React stuff, and you need to set + +[56:04] up, like, a back when React was all just frontend stuff. No servers. If I needed to, like, let's say + +[56:12] I was building a table that had a collection of items, and I wanted a delete button for each item. + +[56:17] Let's say that's a big form or a collection of forms that have a the button submits the form. + +[56:29] No, we don't need to do that. All right. We're clicking a button, and the button we want to + +[56:34] write, like, a click handler for deleting that item. If we're just iterating over all of the + +[56:40] different items in that list to render out a button, we need to know specifically which + +[56:47] button we're clicking. Like, which ID does that relate to? And so, currying is a really good way + +[56:52] that you can that you can pass an ID into a function to get back the click handler + +[57:01] that will delete that specific item. So, it has a closure around the ID that you have passed in + +[57:07] to render that button. >> Yeah. + +[57:11] >> And it can trigger that as an event handler and delete the correct item from the list. + +[57:15] >> So, like, what you explain is how we try to explain closures. Like, it's literally a + +[57:21] function that then wraps the little scope that's accessible inside. I don't know if it's true, + +[57:27] but when I read the answer, it made an example that makes sense. What it said, + +[57:33] it's exactly what you said, but it said, like, if you have a function with multiple arguments, + +[57:37] instead of passing them all, the first function calls another function with the next argument, + +[57:42] and then it calls another function with the third as a chain. And one of the benefits of that, + +[57:49] let me show you the little example if you're watching. Basically, you have a function add + +[57:54] number, and you have 1, 2, 3, and this is the normal way, and then the currying would be to, + +[57:59] like, return the second number in the function, and then return the function with the number 3. + +[58:04] And then basically, it lets you kind of do it's almost like currying is almost like tailwind + +[58:09] classes composition, in a way, because instead of having all the concerns handled in one function, + +[58:14] you can kind of chain them and think of an array that map, then that filter, then that reduce, + +[58:20] and you can have, like, a chain of assembly, and it lets you also make, if you don't want to use + +[58:26] the whole assembly line, building the whole car, you're like, no, no, I just want to attach the + +[58:29] door. Maybe you can call the whatever, like, find a way to just use that intermediary step. + +[58:35] So it's kind of like closure, but in a chained way. + +[58:40] >> Yeah. Like, it's a functional programming pattern. So currying is, like, a thing that + +[58:46] comes from functional programming paradigm stuff. And so, yeah, that is how you do any kind of + +[58:55] programming, and functional programming is that each function is, like, is one individual piece + +[59:00] of what you're doing. And so, yeah, you chain them all together, and each function passes the + +[59:06] result of that function onto the next function, which passes the result of that function onto the + +[59:09] next one. Yeah. >> That's a nice way to break it. Like, + +[59:14] things pass the results to the next function. Yeah. + +[59:16] >> When I worked at realestate.com.au, Australia's leading real estate company. + +[59:23] >> They should sponsor you. Every single time you mention that, you go, + +[59:26] "Australia's leading, number one." >> Yeah. Better than Domain, + +[59:30] Australia's leading real estate listing site. Yeah, they have a very strong functional + +[59:38] programming culture there, and a lot of the back ends are all written in Scala, + +[59:41] which is a functional programming language, and that kind of carries through to the front end, + +[59:47] where they, at the time that I was there, were encouraging a lot of, like, functional + +[59:52] programming style in React, which, yeah, which was kind of nice, because React itself + +[59:58] kind of encourages thinking about things in a functional programming way. + +[60:03] Rather than mutating state, you are often calling a function that returns you a new thing. So, + +[60:09] like you gave the example before of .map on an array, you're not mutating the actual array, + +[60:14] you're getting back a new array. So, that's, like, yeah. + +[60:17] >> So, you were 100% using Redux, and you were watching the Dan Abramov course on-- + +[60:22] >> Yeah. Definitely, 100%. >> Because Redux is all about everything + +[60:29] you do, you never mutate, you just spread the existing object into your reducer, and you make + +[60:34] sure that you just spread things, and use all these ES6 functionalities that let you not mutate, + +[60:40] but create a new object, and then you return that, and it's all about basically immutability, + +[60:46] in a way, like, not really, but, like, just never mutating something, and always just + +[60:50] making a copy, and then returning that. >> Yeah. In fact, before-- the company that + +[60:56] I worked at before that used the thing that was the thing before Redux. What is it? + +[61:04] >> MobX? >> No, no, it was before that as well. So, + +[61:07] I think it may have been called Flow, which was also, like, a type script alternative. + +[61:12] But maybe it was called Flow, I don't remember. But there was another thing that was, like, + +[61:17] even more ridiculously complex, which then Redux was inspired by, and was fixing. Or Flask, or + +[61:27] Fluid, or something like that. Who knows? Something. I'm sure if you look at the Redux docs, + +[61:32] it claims what it was inspired by. But yeah, we used that at the company I was at before. + +[61:38] Australia's leading real estate listing website. And it was a nightmare. It was very frustrating, + +[61:46] as all of these patterns become when they're used incorrectly in a large organization with + +[61:50] a lot of devs who partially understand it. >> So, John Myers, the answer was Flux. + +[61:56] >> Flux. That's the one. That's it. >> I've asked Claude what was used before. + +[62:02] And it said before Redux, blah, blah, blah. >> I felt like there was a play on words with + +[62:09] the Redux. I was trying to think what is a name. And that's why Fluid was in my mind. So, yeah, + +[62:13] Flux. >> The context API was definitely not before + +[62:16] Redux. I don't think so. Yeah, maybe it was. But then there was the use context. So, Facebook + +[62:23] developed Flux with Relay and GraphQL and all these things. That's the thing. And then Redux + +[62:28] is the one I was thinking of. >> So, Redux has one store that's + +[62:32] the single source of truth. And Flux had, yeah, lots of multiple stores. Anyway, Simon, do you + +[62:38] have a sick pick for me? Because we've got to wrap this up. >> No, but we should do a JavaScript + +[62:41] question. Yes, I have a sick pick. It's called Pixel Perfect Figma to Tailwind. It's six hours + +[62:51] left in the discount. And actually, I'm going to pull up here on the thing because I keep showing + +[62:58] if I go to epicweb.dev, let me just share my screen one more time. It's like a shameless + +[63:03] plug/sick pick in one. I've completely butchered the thing, but that's there you go. So, you can + +[63:10] see at the top. Price goes up in six hours, 14 minutes, 7 seconds. So, that's epicweb.dev. And + +[63:16] then you can either go in the pro workshops and you will find it here. Or you can click on that + +[63:22] banner. And then this is the workshop. I'm already signed in. And, yeah, it's a hands-on workshop. We + +[63:30] do not only Flex and Grid, but we also look at subgrid, which is when you have a grid that is + +[63:39] offloading its grid cells and rows to the parent grid. So, you can have a parent grid + +[63:43] and then two different elements that kind of line up with the parent grid, which is super powerful. + +[63:48] It's hard to find a good example. And the UI we built in this workshop is the perfect example + +[63:52] for subgrid. So, I've done an exercise on this. And people really loved it. It's typically + +[63:58] something you haven't used on your day-to-day life because it's, one, new and, two, very niche. But + +[64:05] seeing this, I thought that example made really sense to why subgrid is useful. So, check it out. + +[64:11] If you missed the deadline, you can still get it. But it goes up by 20% more after the 6 hours and + +[64:17] 13 seconds and 12 minutes. And that's it. >> And remember, everyone, you are now + +[64:23] more than six months through your company's education budget. So, if you have any education + +[64:30] budget left over, it's time to funnel that towards Simon. And if you do it in the next few hours. + +[64:35] >> So, we just missed the end of financial year Australian. And first of June or end of June is + +[64:42] the end of, like, where everyone has budgets they need to use. Otherwise, they don't get renewed. + +[64:46] We launch right after that, which is kind of like, oh, well. But, yeah. Now you have a new + +[64:53] budget for the new year. Go burn it. Burn! >> Or if it's done by just years, calendar years, + +[64:59] then you've got less than six months to use it. So, if you're holding onto it for any reason, + +[65:03] give it to Simon. >> Yes. So, my sick pick is that. But + +[65:07] shout out to Kent C. Dodds who created Epic Web and who chose to reach out to me as to become an + +[65:15] instructor. And I'm ever grateful. And I think I did not disappoint so far. So, he's also happy. + +[65:20] So, that's pretty cool. But shout out, Kent. You're my sick pick. >> Done a sick pick and a + +[65:26] shameless plug all in one. That's excellent. My sick pick this week is going to be an Ooni pizza + +[65:33] oven. So, I have a whole history with pizza ovens. I've always been on this hunt to try and make + +[65:42] the perfect pizza. And I'm always one step away from the perfect pizza. And so, like >> I know + +[65:51] the feeling. >> And so, I built this outdoor pizza oven myself, which I've probably sick picked + +[65:58] previously and told you all to do. Don't go do that. I'm un-sick picking building your own pizza + +[66:04] oven. Unless you build the proper pizza oven dome with a chimney and follow a proper building + +[66:10] schematic or whatever and do it properly. What I did was stack a collection of bricks and a slab. + +[66:17] >> You sick picked that one. Yeah, that's cool. >> Two slabs of stone from Bunnings, which is our + +[66:23] hardware store. And the way that you offset it means that the heat kind of folds back down over + +[66:30] the top of your pizza. The heat part is awesome. It's hard to get it up to proper 500 degrees + +[66:38] Celsius is usually what you want to get it to, which is an insane, insane heat. >> Wow, yeah. + +[66:44] >> And so, but part of the problem is because it doesn't have a chimney or a way to get rid of the + +[66:49] smoke. All of the smoke just like falls over the pizza as well. And like, you know, smoke is a nice + +[66:55] flavor if you're like smoking things and whatever. And like, you know, smoky pizza is good. That's + +[66:59] why you want to cook it on wood fire. But when it's like just like when it's 100% of the smoke + +[67:07] and like the black, like really bad smoke that's just going over your pizza and all the ash and + +[67:12] everything from the fire, it's not as pleasant. It ends up being like too smoky and just, yeah, + +[67:18] too far. So I bought a Ooni pizza oven. I got the, I think it's the 12G, which is surprisingly + +[67:27] affordable for what it is. >> Is it by Ryobi? >> It's not by Ryobi, but it is like the Ryobi + +[67:35] of pizza ovens. In fact, it actually is. Like Ooni, I think is like the entry-level, but still + +[67:41] good brand, which I think perfectly is Ryobi. Ryobi is made for like people who want to, like + +[67:49] the home hardware person who wants to DIY projects at home. It's probably not used by that many like + +[67:55] professional carpenters or whatever. But it's the brand that is high enough quality to like, + +[68:03] like it's a step up in quality from the entry-level drill or whatever, but it's probably + +[68:08] not at that point that a professional will use it. I'd say there'd be like a little bit of overlap + +[68:12] into could be used as professional, but probably- >> Yeah, it's hit that sweet spot between the + +[68:17] everyday consumer and the pro, like just, yeah, perfect. Keep going. >> So this pizza oven is + +[68:23] exactly that. And so it is affordable, but it is fantastic. And so it's like, it's quite small. + +[68:30] It's like portable. You can move it around. It's got the dome shape over the top. It's a very like + +[68:35] shallow and it's got a chimney and you just, you put like charcoal and bits of wood in a little + +[68:42] basket, which is what you like, you burn what's in the basket, the fuel basket. And then that + +[68:49] heats up the oven, but it heats it up incredibly quickly. So just like a couple of small pieces + +[68:53] of wood and some charcoal, it like heats up the oven to 500 degrees Celsius, probably in like + +[69:00] 20 minutes. >> Oh, wow. >> And then it like keeps that heat. You just add like a couple- >> It's + +[69:05] uniform, yeah. >> Yeah, yeah. And so the charcoal keeps it like consistent heat. And then the wood + +[69:10] gives it some extra flame, which helps color the pizza, helps give it that leoparding on the crust + +[69:16] and gives it a little bit of smoke, but not too much smoke that it's like, + +[69:21] it tastes like you're eating poison. So it's amazing, like surprisingly good. And you can also + +[69:28] get a, I didn't get this, but you can get an optional gas attachment, which turns it into + +[69:34] like a gas pizza oven. So if you don't even wanna like mess with fire and charcoal and trying to + +[69:40] keep things hot, you can get the gas attachment, which just gives it like perfect uniform heat. + +[69:45] It heats up in like 10 minutes to full heat and just stays there and will be completely like + +[69:50] consistent the whole time. But I feel like it's a crop out. I feel like I wanna burn the wood. I + +[69:57] wanna get some of that like smoky flavor. And so yeah, this pizza oven is incredible and it takes + +[70:06] like less than 90 seconds to cook a pizza. That's how hot it is. >> Once it's hot, you just put it + +[70:13] for a minute and a half. >> Yeah. Yeah. And so yeah, you've got to like be very strategic with + +[70:20] the kinds of toppings that you like. Obviously you wouldn't put raw chicken on your pizza, + +[70:25] but if you put just like quite simple pizzas, like tomato based with like some cheese and basil and + +[70:32] a fairly minimal pizza cooks perfectly and is absolutely delicious. I give it 10 out of 10. + +[70:41] >> That's awesome. And if you want to add chicken or meat or whatever, you can pre-cook it and then + +[70:48] put it on top. >> Exactly. Yeah. So if you want to put like mushrooms or chicken or any kind of meat, + +[70:54] just cook it first. Obviously pizza goes very well with like cured meats, like salami and + +[71:00] ham and all of those things. So they're fine to chuck directly on it. >> So the awesome vibe about + +[71:06] this, because it's so quick is we, when we go camping, we have this little camp oven. It's like, + +[71:11] doesn't take 90 seconds. It takes more like 90 minutes, but we have like, we have this pizza + +[71:16] base from Baker's Delight, which is like a chain of bakeries in Australia. And they do a really + +[71:22] good pizza dough that's pre-made. So you literally don't make anything. And then we, we have all + +[71:26] these bowls with tomatoes, mushrooms, capsicums, whatever, cheese. And then the kids kind of + +[71:32] prepare their little mini pizzas and then put in there. And then you wait for like forever. But I + +[71:38] love the, if like, if you have this like party at home and you have all these different ingredients, + +[71:42] you're like, Hey, make your own pizza. Give it to me when you're ready. In the minute and a half, + +[71:46] you can eat that. So you get from idea. I always talk about this feedback loop. Like I love Tailwind + +[71:52] because you have an idea and really quickly you can see it in the browser. So a kid can have an + +[71:57] idea about a pizza or an adult. And then within two minutes, it's like ready to eat. And it's, + +[72:03] that's pretty cool. Like that's a really good party trick where you assemble the thing and you, + +[72:07] it's instantly ready to consume. Yeah. Yeah. That's a good point. I should, + +[72:11] should have a pizza party. I've already challenged, I have two other friends who have like + +[72:14] different pizza ovens. I've already challenged them to a recurring competition, a pizza cooking + +[72:20] competition for the rest of our lives. And so we're going to start in like, in, in a couple + +[72:26] of months, we're going to have a, have a pizza competition. We're going to buy a golden pizza + +[72:29] peel, which is like the, the thing that you use to put the pizza in. And it goes around to the + +[72:34] winner. Yeah. Yeah. Yeah. And we're going to engrave it every time with the winner. And we've + +[72:38] committed to doing this for the rest of our lives. So getting very serious about the pizza game. + +[72:43] Yeah. Yeah. My son and daughter both love like preparing the pizza with me. So usually I'll + +[72:50] like roll out the dough because they're pretty, like the soft hands and the care that goes into + +[72:58] preparing a, like rolling out a pizza dough. But once it's rolled out, they love putting + +[73:04] the tomato paste on and like herbs and cheese and stuff. And yeah, that, like that feedback loop + +[73:11] is, is so important for kids. Like previously they would make a pizza and then they'd be like, + +[73:17] all right, I'm ready to eat. Cause I'm sitting here looking at this delicious pizza that I've + +[73:20] made. Then it's like, yeah, cool. Just give me like half an hour to actually cook it. + +[73:23] They're sitting there going nuts and like expecting dinner. + +[73:28] Inevitably they're still hungry and they'll make one more, but half an hour later, they come to + +[73:32] that. The information has come to the brain that no dude, you've eaten, you're good. And that you + +[73:37] get that cooked pizza that absolutely nobody wants. Every time we have the, we have the + +[73:42] left at the end. And then I either make the mistake of like the dad mistake of, ah, fine. + +[73:47] I'll eat all the pizza because I can't wait. But yeah, like the, when we, when we did pizzas on + +[73:55] last Friday, uh, like, yeah, just the speed at which it cooked was perfect. So they would prepare + +[74:00] it. I'd take it outside. I'd put it in. I'd be out there for like 90 seconds. And then I would + +[74:05] come back in. You've got to be like really fast with turning it as well, because it actually does + +[74:09] cook like entirely in 90 seconds. Um, and so you got to keep rotating it. So you get, uh, even + +[74:14] cooking all the way around the pizza. Um, but then I would come in with this like beautifully + +[74:18] steaming pizza and yeah. That's great. Epic. Uh, you, you buy a trailer and then you go, + +[74:24] you know, like this little coffee vans that make coffee on the go. You can make these pizzas like + +[74:29] in, in one minute, like no one wants to go to a little, little trailer and buy a pizza and wait + +[74:35] half an hour. But if you can just get it in one minute, it's, it's awesome. If you took one of + +[74:39] these camping, like you could sell it to the whole campground. Like charge 15, 20 bucks a pizza, + +[74:45] have some really like basic ingredients that people can choose from, from, and they choose + +[74:50] a pizza. Their kids can even get involved in like making their own pizza. And then boom, + +[74:54] 90 seconds. You say this because that's a whole business in Australia. I've seen more and more + +[74:58] people. There was for long, longest time, uh, the coffee van that shows up or like the, + +[75:03] the really annoying ice cream thing with the music where all the kids just, and it's disgusting, + +[75:08] warm ice cream. But I've seen more and more of this pizza oven on, uh, like the, the dome one + +[75:13] on the trailer. And then they rock up to the camping and they don't even have to play the + +[75:18] music. The smell plays the music. Like it's not the kids who want the ice cream. It's the parents + +[75:23] who wants the pizza. And then kids, when they smell it, they also like you rock up and literally + +[75:28] every single one in the camp run is going to want a pizza instantly. And then if you make it in one + +[75:33] minute and it looks steamy, yeah, it's, um, it's, it's a business. Yeah. And pizza is so like easy + +[75:40] to eat at like a campground or whatever. Like you just have a slice. You don't need a plate or + +[75:44] whatever. Yeah. Not even just wipe on your camping stuff you wear. Cause it's full of dirt anyway. + +[75:50] Yeah. Yeah. I love it. Let's pivot. We're no longer, no longer super base. We're now + +[75:57] super pizza base and we are pro pizza wind. Yeah. Imagine if you rock up in the camping + +[76:03] with your golden shovel thing engraved, like I won in 2024, five, six, and seven, + +[76:10] four years in a row. And you get the gold display behind me when I win it in a couple of months, + +[76:17] there will be the golden pizza peel hanging behind me. Do you know what should be here? + +[76:20] Somewhere here, the YouTube tailwind labs plague, because they're going to reach a hundred K. + +[76:26] I'm not going to demand it, but I want, I want Adam to send it to me without me asking. + +[76:31] It's not, it's probably not going to happen, but he's got like two. I mean, + +[76:35] he created tailwind fair enough. My, my, my work wouldn't reach that amount of people without his + +[76:41] work. But as, as far as YouTube is concerned, I've got 98% of the land grab on that YouTube channel + +[76:47] to the point people think I am the creator of tailwind, which I do think doesn't sit very well + +[76:53] always with the creators of tailwind. But anyway, if you see this item, which you don't, I, the + +[76:59] plaque would go well here. See inside, inside of it. Perfect. Nice. I'd shine a lamp on it just + +[77:08] to have the little triangle play triangle glow. Yeah. All right. Coming soon. Simon with a silver + +[77:15] play button for a hundred K subscribers on YouTube. Thank you all so much for listening + +[77:21] to another week of the NavBar and we will see you again next week. See you later. Thanks for + +[77:26] joining and let us know if you want more of this stumped question. I think it was fun. Bye. + +[77:31] I definitely want more. So let's do one now. No, I'm joking. Bye. + +[77:37] [BLANK_AUDIO] diff --git a/astro/src/data/post.ts b/astro/src/data/post.ts new file mode 100644 index 0000000..d14dc54 --- /dev/null +++ b/astro/src/data/post.ts @@ -0,0 +1,39 @@ +import { type CollectionEntry, getCollection } from "astro:content"; +import { siteConfig } from "@/site-config"; + +/** filter out draft posts based on the environment */ +export async function getAllPosts() { + return await getCollection("post", ({ data }) => { + return import.meta.env.PROD ? !data.draft : true; + }); +} + +/** returns the date of the post based on option in siteConfig.sortPostsByUpdatedDate */ +export function getPostSortDate(post: CollectionEntry<"post">) { + return siteConfig.sortPostsByUpdatedDate && post.data.updatedDate !== undefined + ? new Date(post.data.updatedDate) + : new Date(post.data.publishDate); +} + +/** sort post by date (by siteConfig.sortPostsByUpdatedDate), desc.*/ +export function sortMDByDate(posts: CollectionEntry<"post">[]) { + return posts.sort((a, b) => { + const aDate = getPostSortDate(a).valueOf(); + const bDate = getPostSortDate(b).valueOf(); + return bDate - aDate; + }); +} + +/** groups posts by year (based on option siteConfig.sortPostsByUpdatedDate), using the year as the key + * Note: This function doesn't filter draft posts, pass it the result of getAllPosts above to do so. + */ +export function groupPostsByYear(posts: CollectionEntry<"post">[]) { + return posts.reduce[]>>((acc, post) => { + const year = getPostSortDate(post).getFullYear(); + if (!acc[year]) { + acc[year] = []; + } + acc[year]?.push(post); + return acc; + }, {}); +} \ No newline at end of file diff --git a/astro/src/env.d.ts b/astro/src/env.d.ts new file mode 100644 index 0000000..ae855c1 --- /dev/null +++ b/astro/src/env.d.ts @@ -0,0 +1,7 @@ +/* eslint-disable @typescript-eslint/triple-slash-reference */ +/// +/// + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/astro/src/layouts/Base.astro b/astro/src/layouts/Base.astro new file mode 100644 index 0000000..d43e006 --- /dev/null +++ b/astro/src/layouts/Base.astro @@ -0,0 +1,53 @@ +--- +import type { SiteMeta } from "@/types" +import BaseHead from "@/components/BaseHead.astro" +import { siteConfig } from "@/site-config" +import { menuLinks } from "@/site-config" + +interface Props { + meta: SiteMeta +} + +const { + meta: { articleDate, description = siteConfig.description, ogImage, title }, +} = Astro.props + +const year = new Date().getFullYear() +const url = new URL(Astro.request.url) +--- + + + + + + +
    +
    + + Autoshow.sh + + +
    +
    +
    + +
    +
    +
    + © {siteConfig.author} {year}
    Astro Autoshow +
    +
    + + diff --git a/astro/src/layouts/BlogPost.astro b/astro/src/layouts/BlogPost.astro new file mode 100644 index 0000000..ef7d7d1 --- /dev/null +++ b/astro/src/layouts/BlogPost.astro @@ -0,0 +1,66 @@ +--- +import type { CollectionEntry } from "astro:content" +import BlogHero from "@/components/blog/Hero.astro" +import TOC from "@/components/blog/TOC.astro" +import BaseLayout from "./Base.astro" + +interface Props { + post: CollectionEntry<"post"> +} + +const { post } = Astro.props +const { + data: { description, ogImage, publishDate, title, updatedDate }, + slug, +} = post +const socialImage = ogImage ?? `/og-image/${slug}.png` +const articleDate = updatedDate?.toISOString() ?? publishDate.toISOString() +const { headings } = await post.render() +--- + + +
    + {!!headings.length && } +
    +
    +
    + +
    +
    +
    + +
    + + diff --git a/astro/src/pages/404.astro b/astro/src/pages/404.astro new file mode 100644 index 0000000..efc0fe7 --- /dev/null +++ b/astro/src/pages/404.astro @@ -0,0 +1,13 @@ +--- +import PageLayout from "@/layouts/Base.astro"; + +const meta = { + description: "Oops! It looks like this page is lost in space!", + title: "Oops! You found a missing page!", +}; +--- + + +

    404 | Oops something went wrong

    +

    Please use the navigation to find your way back

    +
    diff --git a/astro/src/pages/index.astro b/astro/src/pages/index.astro new file mode 100644 index 0000000..74faf8f --- /dev/null +++ b/astro/src/pages/index.astro @@ -0,0 +1,48 @@ +--- +import PageLayout from "@/layouts/Base.astro"; + +import { Icon } from "astro-icon/components"; + +/** + Uses https://www.astroicon.dev/getting-started/ + Find icons via guide: https://www.astroicon.dev/guides/customization/#open-source-icon-sets + Only installed pack is: @iconify-json/mdi +*/ +const socialLinks: { + friendlyName: string; + link: string; + name: string; +}[] = [ + { + friendlyName: "Github", + link: "https://github.com/ajcwebdev/autoshow", + name: "mdi:github", + }, +]; +--- + + +
    +

    Autoshow Website Generator

    +

    This uses Autoshow and Astro to generate individual web pages with show notes based on video and audio content of any kind.

    +
    +

    Social Links:

    + +
    +
    +
    diff --git a/astro/src/pages/posts/[...page].astro b/astro/src/pages/posts/[...page].astro new file mode 100644 index 0000000..e3f7a52 --- /dev/null +++ b/astro/src/pages/posts/[...page].astro @@ -0,0 +1,61 @@ +--- +import type { GetStaticPaths, Page } from "astro"; +import type { CollectionEntry } from "astro:content"; + +import Pagination from "@/components/Paginator.astro"; +import PostPreview from "@/components/blog/PostPreview.astro"; +import PageLayout from "@/layouts/Base.astro"; +import { getAllPosts, groupPostsByYear, sortMDByDate } from "@/data/post"; + +export const getStaticPaths = (async ({ paginate }) => { + const allPosts = await getAllPosts(); + const allPostsByDate = sortMDByDate(allPosts); + return paginate(allPostsByDate, { pageSize: 20 }); +}) satisfies GetStaticPaths; + +interface Props { + page: Page>; +} + +const { page } = Astro.props; + +const paginationProps = { + ...(page.url.prev && { + prevUrl: { + text: `← Previous Posts`, + url: page.url.prev, + }, + }), + ...(page.url.next && { + nextUrl: { + text: `Next Posts →`, + url: page.url.next, + }, + }), +}; + +const groupedByYear = groupPostsByYear(page.data); +const descYearKeys = Object.keys(groupedByYear).sort((a, b) => +b - +a); +--- + + +

    Posts

    +
    +
    + { + descYearKeys.map((yearKey) => ( + <> +

    {yearKey}

    + {groupedByYear[yearKey]?.map((p) => ( + + ))} + + )) + } + +
    +
    +
    diff --git a/astro/src/pages/posts/[...slug].astro b/astro/src/pages/posts/[...slug].astro new file mode 100644 index 0000000..d9c29a5 --- /dev/null +++ b/astro/src/pages/posts/[...slug].astro @@ -0,0 +1,24 @@ +--- +import type { GetStaticPaths, InferGetStaticPropsType } from "astro"; + +import { getAllPosts } from "@/data/post"; +import PostLayout from "@/layouts/BlogPost.astro"; + +// if you're using an adaptor in SSR mode, getStaticPaths wont work -> https://docs.astro.build/en/guides/routing/#modifying-the-slug-example-for-ssr +export const getStaticPaths = (async () => { + const blogEntries = await getAllPosts(); + return blogEntries.map((entry) => ({ + params: { slug: entry.slug }, + props: { entry }, + })); +}) satisfies GetStaticPaths; + +type Props = InferGetStaticPropsType; + +const { entry } = Astro.props; +const { Content } = await entry.render(); +--- + + + + diff --git a/astro/src/pages/rss.xml.ts b/astro/src/pages/rss.xml.ts new file mode 100644 index 0000000..6236b64 --- /dev/null +++ b/astro/src/pages/rss.xml.ts @@ -0,0 +1,19 @@ +import rss from "@astrojs/rss"; +import { siteConfig } from "@/site-config"; +import { getAllPosts } from "@/data/post"; + +export const GET = async () => { + const posts = await getAllPosts(); + + return rss({ + title: siteConfig.title, + description: siteConfig.description, + site: import.meta.env.SITE, + items: posts.map((post) => ({ + title: post.data.title, + description: post.data.description, + pubDate: post.data.publishDate, + link: `posts/${post.slug}`, + })), + }); +}; diff --git a/astro/src/site.config.ts b/astro/src/site.config.ts new file mode 100644 index 0000000..c7038e4 --- /dev/null +++ b/astro/src/site.config.ts @@ -0,0 +1,59 @@ +import type { SiteConfig } from "@/types"; +import type { AstroExpressiveCodeOptions } from "astro-expressive-code"; + +export const siteConfig: SiteConfig = { + author: "Anthony Campolo", + title: "Astro Autoshow", + description: "Autoshow site generator with Astro.", + lang: "en-US", + ogLocale: "en_US", + sortPostsByUpdatedDate: false, + date: { + locale: "en-US", + options: { + day: "numeric", + month: "long", + year: "numeric", + }, + }, +}; + +export const menuLinks: { path: string; title: string }[] = [ + { + title: "Home", + path: "/", + }, + { + title: "Blog", + path: "/posts/", + }, +]; + +// https://expressive-code.com/reference/configuration/ +export const expressiveCodeOptions: AstroExpressiveCodeOptions = { + // One dark, one light theme => https://expressive-code.com/guides/themes/#available-themes + themes: ["dracula", "github-light"], + themeCssSelector(theme, { styleVariants }) { + // If one dark and one light theme are available + // generate theme CSS selectors compatible with cactus-theme dark mode switch + if (styleVariants.length >= 2) { + const baseTheme = styleVariants[0]?.theme; + const altTheme = styleVariants.find((v) => v.theme.type !== baseTheme?.type)?.theme; + if (theme === baseTheme || theme === altTheme) return `[data-theme='${theme.type}']`; + } + // return default selector + return `[data-theme="${theme.name}"]`; + }, + useThemedScrollbars: false, + styleOverrides: { + frames: { + frameBoxShadowCssValue: "none", + }, + uiLineHeight: "inherit", + codeFontSize: "0.875rem", + codeLineHeight: "1.7142857rem", + borderRadius: "4px", + codePaddingInline: "1rem", + codeFontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;', + }, +}; diff --git a/astro/src/styles/global.css b/astro/src/styles/global.css new file mode 100644 index 0000000..b838b72 --- /dev/null +++ b/astro/src/styles/global.css @@ -0,0 +1,11 @@ +@layer base { + :root { + color-scheme: dark; + --theme-bg: 210deg 6% 12%; + --theme-link: 330deg 49% 67%; + --theme-text: 220deg 3% 79%; + --theme-accent: 159deg 64% 45%; + --theme-accent-2: 0deg 0% 93%; + --theme-quote: 102deg 100% 86%; + } +} diff --git a/astro/src/types.ts b/astro/src/types.ts new file mode 100644 index 0000000..ae52b16 --- /dev/null +++ b/astro/src/types.ts @@ -0,0 +1,48 @@ +export interface SiteConfig { + author: string; + title: string; + description: string; + lang: string; + ogLocale: string; + date: { + locale: string | string[] | undefined; + options: Intl.DateTimeFormatOptions; + }; + sortPostsByUpdatedDate: boolean; +} + +export interface PaginationLink { + url: string; + text?: string; + srLabel?: string; +} + +export interface SiteMeta { + title: string; + description?: string; + ogImage?: string | undefined; + articleDate?: string | undefined; +} + +export interface Author { + type: string; + name: string; + photo: string; + url: string; +} + +export interface Content { + "content-type": string; + value: string; + html: string; + text: string; +} + +export interface Rels { + canonical: string; +} + +export interface Summary { + "content-type": string; + value: string; +} diff --git a/astro/src/utils/index.ts b/astro/src/utils/index.ts new file mode 100644 index 0000000..cc71636 --- /dev/null +++ b/astro/src/utils/index.ts @@ -0,0 +1,66 @@ +import type { MarkdownHeading } from "astro"; +import { siteConfig } from "@/site-config"; + +const dateFormat = new Intl.DateTimeFormat(siteConfig.date.locale, siteConfig.date.options); + +export function getFormattedDate( + date: string | number | Date, + options?: Intl.DateTimeFormatOptions, +) { + if (typeof options !== "undefined") { + return new Date(date).toLocaleDateString(siteConfig.date.locale, { + ...(siteConfig.date.options as Intl.DateTimeFormatOptions), + ...options, + }); + } + + return dateFormat.format(new Date(date)); +} + + +export interface TocItem extends MarkdownHeading { + children: TocItem[]; +} + +interface TocOpts { + maxHeadingLevel?: number | undefined; + minHeadingLevel?: number | undefined; +} + +/** Inject a ToC entry as deep in the tree as its `depth` property requires. */ +function injectChild(items: TocItem[], item: TocItem): void { + const lastItem = items.at(-1); + if (!lastItem || lastItem.depth >= item.depth) { + items.push(item); + } else { + injectChild(lastItem.children, item); + return; + } +} + +export function generateToc( + headings: ReadonlyArray, + { maxHeadingLevel = 4, minHeadingLevel = 2 }: TocOpts = {}, +) { + // by default this ignores/filters out h1 and h5 heading(s) + const bodyHeadings = headings.filter( + ({ depth }) => depth >= minHeadingLevel && depth <= maxHeadingLevel, + ); + const toc: Array = []; + + for (const heading of bodyHeadings) injectChild(toc, { ...heading, children: [] }); + + return toc; +} + +export function toggleClass(element: HTMLElement, className: string) { + element.classList.toggle(className); +} + +export function elementHasClass(element: HTMLElement, className: string) { + return element.classList.contains(className); +} + +export function rootInDarkMode() { + return document.documentElement.getAttribute("data-theme") === "dark"; +} diff --git a/astro/tsconfig.json b/astro/tsconfig.json new file mode 100644 index 0000000..ed1ea60 --- /dev/null +++ b/astro/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "astro/tsconfigs/strictest", + "compilerOptions": { + "baseUrl": ".", + "lib": ["es2022", "dom", "dom.iterable"], + "paths": { + "@/assets/*": ["src/assets/*"], + "@/components/*": ["src/components/*"], + "@/data/*": ["src/data/*"], + "@/layouts/*": ["src/layouts/*"], + "@/utils": ["src/utils/index.ts"], + "@/types": ["src/types.ts"], + "@/site-config": ["src/site.config.ts"] + } + }, + "include": ["./.eslintrc.cjs", "**/*"], + "exclude": ["node_modules", "**/node_modules/*", ".vscode", "dist"] +} From 98c241ac3e50a64285262bd8359aa97f090401d4 Mon Sep 17 00:00:00 2001 From: Anthony Campolo <12433465+ajcwebdev@users.noreply.github.com> Date: Thu, 3 Oct 2024 14:23:42 -0500 Subject: [PATCH 2/3] update astro content collection --- astro/src/content/config.ts | 38 +++++++++++++------------------------ 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/astro/src/content/config.ts b/astro/src/content/config.ts index 21fd4da..400c0c0 100644 --- a/astro/src/content/config.ts +++ b/astro/src/content/config.ts @@ -1,28 +1,16 @@ -import { defineCollection, z } from "astro:content"; +import { defineCollection, z } from "astro:content" const post = defineCollection({ - schema: ({ image }) => - z.object({ - coverImage: z - .object({ - alt: z.string(), - src: image(), - }) - .optional(), - description: z.string().min(10).max(200), - draft: z.boolean().default(false), - ogImage: z.string().optional(), - publishDate: z - .string() - .or(z.date()) - .transform((val) => new Date(val)), - title: z.string().max(60), - updatedDate: z - .string() - .optional() - .transform((str) => (str ? new Date(str) : undefined)), - }), - type: "content", -}); + schema: ({ image }) => + z.object({ + title: z.string().max(60), + description: z.string().min(10).max(200), + publishDate: z.string().or(z.date()).transform((val) => new Date(val)), + updatedDate: z.string().optional().transform((str) => (str ? new Date(str) : undefined)), + coverImage: z.object({alt: z.string(),src: image()}).optional(), + ogImage: z.string().optional(), + }), + type: "content", +}) -export const collections = { post }; +export const collections = { post } \ No newline at end of file From 67a76522dfdb4d07cff5caa598b9792ec9f5324a Mon Sep 17 00:00:00 2001 From: Anthony Campolo <12433465+ajcwebdev@users.noreply.github.com> Date: Thu, 3 Oct 2024 15:25:09 -0500 Subject: [PATCH 3/3] refactor to package structure --- package.json | 4 +- {astro => packages/astro}/.gitignore | 0 {astro => packages/astro}/.npmrc | 0 {astro => packages/astro}/README.md | 0 {astro => packages/astro}/astro.config.ts | 0 {astro => packages/astro}/package.json | 0 {astro => packages/astro}/public/robots.txt | 0 .../astro}/src/components/BaseHead.astro | 0 .../astro}/src/components/FormattedDate.astro | 0 .../astro}/src/components/Paginator.astro | 0 .../astro}/src/components/blog/Hero.astro | 0 .../src/components/blog/PostPreview.astro | 0 .../astro}/src/components/blog/TOC.astro | 0 .../src/components/blog/TOCHeading.astro | 0 .../astro}/src/content/config.ts | 0 ...-03-the-navbar-live-pod-planning-prompt.md | 0 .../post/2024-07-18-the-navbar-live-prompt.md | 0 {astro => packages/astro}/src/data/post.ts | 0 {astro => packages/astro}/src/env.d.ts | 0 .../astro}/src/layouts/Base.astro | 0 .../astro}/src/layouts/BlogPost.astro | 0 {astro => packages/astro}/src/pages/404.astro | 0 .../astro}/src/pages/index.astro | 0 .../astro}/src/pages/posts/[...page].astro | 0 .../astro}/src/pages/posts/[...slug].astro | 0 .../astro}/src/pages/rss.xml.ts | 0 {astro => packages/astro}/src/site.config.ts | 0 .../astro}/src/styles/global.css | 0 {astro => packages/astro}/src/types.ts | 0 {astro => packages/astro}/src/utils/index.ts | 0 {astro => packages/astro}/tsconfig.json | 0 packages/server/README.md | 259 ++++++++++++++++++ {server => packages/server}/fetch.js | 3 +- {server => packages/server}/index.js | 0 {server => packages/server}/routes/file.js | 2 +- .../server}/routes/playlist.js | 2 +- {server => packages/server}/routes/rss.js | 2 +- {server => packages/server}/routes/urls.js | 2 +- {server => packages/server}/routes/video.js | 2 +- .../server}/utils/reqToOpts.js | 0 {web => packages/web}/.gitignore | 0 {web => packages/web}/index.html | 0 {web => packages/web}/package.json | 0 {web => packages/web}/src/App.jsx | 0 {web => packages/web}/src/Form.jsx | 0 {web => packages/web}/src/index.css | 0 {web => packages/web}/src/main.jsx | 0 {web => packages/web}/vite.config.js | 0 48 files changed, 268 insertions(+), 8 deletions(-) rename {astro => packages/astro}/.gitignore (100%) rename {astro => packages/astro}/.npmrc (100%) rename {astro => packages/astro}/README.md (100%) rename {astro => packages/astro}/astro.config.ts (100%) rename {astro => packages/astro}/package.json (100%) rename {astro => packages/astro}/public/robots.txt (100%) rename {astro => packages/astro}/src/components/BaseHead.astro (100%) rename {astro => packages/astro}/src/components/FormattedDate.astro (100%) rename {astro => packages/astro}/src/components/Paginator.astro (100%) rename {astro => packages/astro}/src/components/blog/Hero.astro (100%) rename {astro => packages/astro}/src/components/blog/PostPreview.astro (100%) rename {astro => packages/astro}/src/components/blog/TOC.astro (100%) rename {astro => packages/astro}/src/components/blog/TOCHeading.astro (100%) rename {astro => packages/astro}/src/content/config.ts (100%) rename {astro => packages/astro}/src/content/post/2024-07-03-the-navbar-live-pod-planning-prompt.md (100%) rename {astro => packages/astro}/src/content/post/2024-07-18-the-navbar-live-prompt.md (100%) rename {astro => packages/astro}/src/data/post.ts (100%) rename {astro => packages/astro}/src/env.d.ts (100%) rename {astro => packages/astro}/src/layouts/Base.astro (100%) rename {astro => packages/astro}/src/layouts/BlogPost.astro (100%) rename {astro => packages/astro}/src/pages/404.astro (100%) rename {astro => packages/astro}/src/pages/index.astro (100%) rename {astro => packages/astro}/src/pages/posts/[...page].astro (100%) rename {astro => packages/astro}/src/pages/posts/[...slug].astro (100%) rename {astro => packages/astro}/src/pages/rss.xml.ts (100%) rename {astro => packages/astro}/src/site.config.ts (100%) rename {astro => packages/astro}/src/styles/global.css (100%) rename {astro => packages/astro}/src/types.ts (100%) rename {astro => packages/astro}/src/utils/index.ts (100%) rename {astro => packages/astro}/tsconfig.json (100%) create mode 100644 packages/server/README.md rename {server => packages/server}/fetch.js (87%) rename {server => packages/server}/index.js (100%) rename {server => packages/server}/routes/file.js (94%) rename {server => packages/server}/routes/playlist.js (94%) rename {server => packages/server}/routes/rss.js (94%) rename {server => packages/server}/routes/urls.js (94%) rename {server => packages/server}/routes/video.js (94%) rename {server => packages/server}/utils/reqToOpts.js (100%) rename {web => packages/web}/.gitignore (100%) rename {web => packages/web}/index.html (100%) rename {web => packages/web}/package.json (100%) rename {web => packages/web}/src/App.jsx (100%) rename {web => packages/web}/src/Form.jsx (100%) rename {web => packages/web}/src/index.css (100%) rename {web => packages/web}/src/main.jsx (100%) rename {web => packages/web}/vite.config.js (100%) diff --git a/package.json b/package.json index a7eeed8..9c8316e 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,8 @@ "u": "node --env-file=.env --no-warnings src/autoshow.js --whisper large --urls", "p": "node --env-file=.env --no-warnings src/autoshow.js --whisper large --playlist", "f": "node --env-file=.env --no-warnings src/autoshow.js --whisper large --file", - "serve": "node --env-file=.env --no-warnings --watch server/index.js", - "fetch": "node --env-file=.env --no-warnings server/fetch.js", + "serve": "node --env-file=.env --no-warnings --watch packages/server/index.js", + "fetch": "node --env-file=.env --no-warnings packages/server/fetch.js", "test-local": "node --test test/local.test.js", "test-all": "node --test test/all.test.js" }, diff --git a/astro/.gitignore b/packages/astro/.gitignore similarity index 100% rename from astro/.gitignore rename to packages/astro/.gitignore diff --git a/astro/.npmrc b/packages/astro/.npmrc similarity index 100% rename from astro/.npmrc rename to packages/astro/.npmrc diff --git a/astro/README.md b/packages/astro/README.md similarity index 100% rename from astro/README.md rename to packages/astro/README.md diff --git a/astro/astro.config.ts b/packages/astro/astro.config.ts similarity index 100% rename from astro/astro.config.ts rename to packages/astro/astro.config.ts diff --git a/astro/package.json b/packages/astro/package.json similarity index 100% rename from astro/package.json rename to packages/astro/package.json diff --git a/astro/public/robots.txt b/packages/astro/public/robots.txt similarity index 100% rename from astro/public/robots.txt rename to packages/astro/public/robots.txt diff --git a/astro/src/components/BaseHead.astro b/packages/astro/src/components/BaseHead.astro similarity index 100% rename from astro/src/components/BaseHead.astro rename to packages/astro/src/components/BaseHead.astro diff --git a/astro/src/components/FormattedDate.astro b/packages/astro/src/components/FormattedDate.astro similarity index 100% rename from astro/src/components/FormattedDate.astro rename to packages/astro/src/components/FormattedDate.astro diff --git a/astro/src/components/Paginator.astro b/packages/astro/src/components/Paginator.astro similarity index 100% rename from astro/src/components/Paginator.astro rename to packages/astro/src/components/Paginator.astro diff --git a/astro/src/components/blog/Hero.astro b/packages/astro/src/components/blog/Hero.astro similarity index 100% rename from astro/src/components/blog/Hero.astro rename to packages/astro/src/components/blog/Hero.astro diff --git a/astro/src/components/blog/PostPreview.astro b/packages/astro/src/components/blog/PostPreview.astro similarity index 100% rename from astro/src/components/blog/PostPreview.astro rename to packages/astro/src/components/blog/PostPreview.astro diff --git a/astro/src/components/blog/TOC.astro b/packages/astro/src/components/blog/TOC.astro similarity index 100% rename from astro/src/components/blog/TOC.astro rename to packages/astro/src/components/blog/TOC.astro diff --git a/astro/src/components/blog/TOCHeading.astro b/packages/astro/src/components/blog/TOCHeading.astro similarity index 100% rename from astro/src/components/blog/TOCHeading.astro rename to packages/astro/src/components/blog/TOCHeading.astro diff --git a/astro/src/content/config.ts b/packages/astro/src/content/config.ts similarity index 100% rename from astro/src/content/config.ts rename to packages/astro/src/content/config.ts diff --git a/astro/src/content/post/2024-07-03-the-navbar-live-pod-planning-prompt.md b/packages/astro/src/content/post/2024-07-03-the-navbar-live-pod-planning-prompt.md similarity index 100% rename from astro/src/content/post/2024-07-03-the-navbar-live-pod-planning-prompt.md rename to packages/astro/src/content/post/2024-07-03-the-navbar-live-pod-planning-prompt.md diff --git a/astro/src/content/post/2024-07-18-the-navbar-live-prompt.md b/packages/astro/src/content/post/2024-07-18-the-navbar-live-prompt.md similarity index 100% rename from astro/src/content/post/2024-07-18-the-navbar-live-prompt.md rename to packages/astro/src/content/post/2024-07-18-the-navbar-live-prompt.md diff --git a/astro/src/data/post.ts b/packages/astro/src/data/post.ts similarity index 100% rename from astro/src/data/post.ts rename to packages/astro/src/data/post.ts diff --git a/astro/src/env.d.ts b/packages/astro/src/env.d.ts similarity index 100% rename from astro/src/env.d.ts rename to packages/astro/src/env.d.ts diff --git a/astro/src/layouts/Base.astro b/packages/astro/src/layouts/Base.astro similarity index 100% rename from astro/src/layouts/Base.astro rename to packages/astro/src/layouts/Base.astro diff --git a/astro/src/layouts/BlogPost.astro b/packages/astro/src/layouts/BlogPost.astro similarity index 100% rename from astro/src/layouts/BlogPost.astro rename to packages/astro/src/layouts/BlogPost.astro diff --git a/astro/src/pages/404.astro b/packages/astro/src/pages/404.astro similarity index 100% rename from astro/src/pages/404.astro rename to packages/astro/src/pages/404.astro diff --git a/astro/src/pages/index.astro b/packages/astro/src/pages/index.astro similarity index 100% rename from astro/src/pages/index.astro rename to packages/astro/src/pages/index.astro diff --git a/astro/src/pages/posts/[...page].astro b/packages/astro/src/pages/posts/[...page].astro similarity index 100% rename from astro/src/pages/posts/[...page].astro rename to packages/astro/src/pages/posts/[...page].astro diff --git a/astro/src/pages/posts/[...slug].astro b/packages/astro/src/pages/posts/[...slug].astro similarity index 100% rename from astro/src/pages/posts/[...slug].astro rename to packages/astro/src/pages/posts/[...slug].astro diff --git a/astro/src/pages/rss.xml.ts b/packages/astro/src/pages/rss.xml.ts similarity index 100% rename from astro/src/pages/rss.xml.ts rename to packages/astro/src/pages/rss.xml.ts diff --git a/astro/src/site.config.ts b/packages/astro/src/site.config.ts similarity index 100% rename from astro/src/site.config.ts rename to packages/astro/src/site.config.ts diff --git a/astro/src/styles/global.css b/packages/astro/src/styles/global.css similarity index 100% rename from astro/src/styles/global.css rename to packages/astro/src/styles/global.css diff --git a/astro/src/types.ts b/packages/astro/src/types.ts similarity index 100% rename from astro/src/types.ts rename to packages/astro/src/types.ts diff --git a/astro/src/utils/index.ts b/packages/astro/src/utils/index.ts similarity index 100% rename from astro/src/utils/index.ts rename to packages/astro/src/utils/index.ts diff --git a/astro/tsconfig.json b/packages/astro/tsconfig.json similarity index 100% rename from astro/tsconfig.json rename to packages/astro/tsconfig.json diff --git a/packages/server/README.md b/packages/server/README.md new file mode 100644 index 0000000..47fb1d2 --- /dev/null +++ b/packages/server/README.md @@ -0,0 +1,259 @@ +## Process Endpoints + +### Playlist Endpoint + +http://localhost:3000/playlist + +```js +const TEST_REQ_01 = { + "playlistUrl": "https://www.youtube.com/playlist?list=PLCVnrVv4KhXPz0SoAVu8Rc1emAdGPbSbr" +} + +const TEST_REQ_02 = { + "playlistUrl": "https://www.youtube.com/playlist?list=PLCVnrVv4KhXPz0SoAVu8Rc1emAdGPbSbr", + "whisperModel": "tiny" +} + +const TEST_REQ_03 = { + "playlistUrl": "https://www.youtube.com/playlist?list=PLCVnrVv4KhXPz0SoAVu8Rc1emAdGPbSbr", + "whisperModel": "tiny", + "llm": "llama" +} + +const TEST_REQ_04 = { + "playlistUrl": "https://www.youtube.com/playlist?list=PLCVnrVv4KhXPz0SoAVu8Rc1emAdGPbSbr", + "prompts": ["titles", "mediumChapters"], + "whisperModel": "tiny", + "llm": "llama" +} +``` + +### URLs Endpoint + +http://localhost:3000/urls + +```js +const TEST_REQ_05 = { + "filePath": "content/example-urls.md" +} + +const TEST_REQ_06 = { + "filePath": "content/example-urls.md", + "whisperModel": "tiny" +} + +const TEST_REQ_07 = { + "filePath": "content/example-urls.md", + "whisperModel": "tiny", + "llm": "llama" +} + +const TEST_REQ_08 = { + "filePath": "content/example-urls.md", + "prompts": ["titles", "mediumChapters"], + "whisperModel": "tiny", + "llm": "llama" +} +``` + +### File Endpoint + +http://localhost:3000/file + +```js +const TEST_REQ_09 = { + "filePath": "content/audio.mp3" +} + +const TEST_REQ_10 = { + "filePath": "content/audio.mp3", + "whisperModel": "tiny" +} + +const TEST_REQ_11 = { + "filePath": "content/audio.mp3", + "whisperModel": "tiny", + "llm": "llama" +} + +const TEST_REQ_12 = { + "filePath": "content/audio.mp3", + "prompts": ["titles"], + "whisperModel": "tiny", + "llm": "llama" +} +``` + +### RSS Endpoint + +http://localhost:3000/rss + +```js +const TEST_REQ_13 = { + "rssUrl": "https://feeds.transistor.fm/fsjam-podcast/" +} + +const TEST_REQ_14 = { + "rssUrl": "https://feeds.transistor.fm/fsjam-podcast/", + "whisperModel": "tiny" +} + +const TEST_REQ_15 = { + "rssUrl": "https://feeds.transistor.fm/fsjam-podcast/", + "whisperModel": "tiny", + "llm": "llama" +} + +const TEST_REQ_16 = { + "rssUrl": "https://feeds.transistor.fm/fsjam-podcast/", + "whisperModel": "tiny", + "order": "newest", + "skip": 94 +} + +const TEST_REQ_17 = { + "rssUrl": "https://feeds.transistor.fm/fsjam-podcast/", + "whisperModel": "tiny", + "order": "oldest", + "skip": 94 +} +``` + +### Video Endpoint + +http://localhost:3000/video + +```js +const TEST_REQ_18 = { + "youtubeUrl": "https://www.youtube.com/watch?v=MORMZXEaONk" +} + +const TEST_REQ_19 = { + "youtubeUrl": "https://www.youtube.com/watch?v=MORMZXEaONk", + "whisperModel": "tiny", + "llm": "llama" +} + +const TEST_REQ_20 = { + "youtubeUrl": "https://www.youtube.com/watch?v=MORMZXEaONk", + "llm": "chatgpt" +} + +const TEST_REQ_21 = { + "youtubeUrl": "https://www.youtube.com/watch?v=MORMZXEaONk", + "llm": "chatgpt", + "llmModel": "GPT_4o_MINI" +} + +const TEST_REQ_22 = { + "youtubeUrl": "https://www.youtube.com/watch?v=MORMZXEaONk", + "llm": "claude" +} + +const TEST_REQ_23 = { + "youtubeUrl": "https://www.youtube.com/watch?v=MORMZXEaONk", + "llm": "claude", + "llmModel": "CLAUDE_3_SONNET" +} + +const TEST_REQ_24 = { + "youtubeUrl": "https://www.youtube.com/watch?v=MORMZXEaONk", + "llm": "gemini" +} + +const TEST_REQ_25 = { + "youtubeUrl": "https://www.youtube.com/watch?v=MORMZXEaONk", + "llm": "gemini", + "llmModel": "GEMINI_1_5_FLASH" +} + +const TEST_REQ_26 = { + "youtubeUrl": "https://www.youtube.com/watch?v=MORMZXEaONk", + "llm": "cohere" +} + +const TEST_REQ_27 = { + "youtubeUrl": "https://www.youtube.com/watch?v=MORMZXEaONk", + "llm": "cohere", + "llmModel": "COMMAND_R_PLUS" +} + +const TEST_REQ_28 = { + "youtubeUrl": "https://www.youtube.com/watch?v=MORMZXEaONk", + "llm": "mistral" +} + +const TEST_REQ_29 = { + "youtubeUrl": "https://www.youtube.com/watch?v=MORMZXEaONk", + "llm": "mistral", + "llmModel": "MIXTRAL_8x7b" +} + +const TEST_REQ_30 = { + "youtubeUrl": "https://www.youtube.com/watch?v=MORMZXEaONk", + "llm": "octo" +} + +const TEST_REQ_31 = { + "youtubeUrl": "https://www.youtube.com/watch?v=MORMZXEaONk", + "llm": "octo", + "llmModel": "LLAMA_3_1_8B" +} + +const TEST_REQ_32 = { + "youtubeUrl": "https://www.youtube.com/watch?v=MORMZXEaONk", + "whisperModel": "tiny" +} + +const TEST_REQ_33 = { + "youtubeUrl": "https://www.youtube.com/watch?v=MORMZXEaONk", + "transcriptService": "deepgram" +} + +const TEST_REQ_34 = { + "youtubeUrl": "https://www.youtube.com/watch?v=MORMZXEaONk", + "transcriptService": "deepgram", + "llm": "llama" +} + +const TEST_REQ_35 = { + "youtubeUrl": "https://www.youtube.com/watch?v=MORMZXEaONk", + "transcriptService": "assembly" +} + +const TEST_REQ_36 = { + "youtubeUrl": "https://www.youtube.com/watch?v=MORMZXEaONk", + "transcriptService": "assembly", + "llm": "llama" +} + +const TEST_REQ_37 = { + "youtubeUrl": "https://ajc.pics/audio/fsjam-short.mp3", + "transcriptService": "assembly", + "speakerLabels": true +} + +const TEST_REQ_38 = { + "youtubeUrl": "https://ajc.pics/audio/fsjam-short.mp3", + "transcriptService": "assembly", + "speakerLabels": true, + "llm": "llama" +} + +const TEST_REQ_39 = { + "youtubeUrl": "https://www.youtube.com/watch?v=MORMZXEaONk", + "prompts": ["titles", "mediumChapters"] +} + +const TEST_REQ_40 = { + "youtubeUrl": "https://www.youtube.com/watch?v=MORMZXEaONk", + "prompts": ["titles", "summary", "shortChapters", "takeaways", "questions"] +} + +const TEST_REQ_41 = { + "youtubeUrl": "https://www.youtube.com/watch?v=MORMZXEaONk", + "prompts": ["titles", "summary", "shortChapters", "takeaways", "questions"], + "whisperModel": "tiny", + "llm": "llama" +} +``` \ No newline at end of file diff --git a/server/fetch.js b/packages/server/fetch.js similarity index 87% rename from server/fetch.js rename to packages/server/fetch.js index d7e344b..9caef82 100644 --- a/server/fetch.js +++ b/packages/server/fetch.js @@ -1,6 +1,7 @@ // server/fetch.js const BASE_URL = 'http://localhost:3000' +const VIDEO_ROUTE = 'video' const data = { youtubeUrl: 'https://www.youtube.com/watch?v=jKB0EltG9Jo', @@ -10,7 +11,7 @@ const data = { const fetchVideo = async () => { try { - const response = await fetch(`${BASE_URL}/video`, { + const response = await fetch(`${BASE_URL}/${VIDEO_ROUTE}`, { method: 'POST', headers: { 'Content-Type': 'application/json' diff --git a/server/index.js b/packages/server/index.js similarity index 100% rename from server/index.js rename to packages/server/index.js diff --git a/server/routes/file.js b/packages/server/routes/file.js similarity index 94% rename from server/routes/file.js rename to packages/server/routes/file.js index 5ac7c11..b7eb045 100644 --- a/server/routes/file.js +++ b/packages/server/routes/file.js @@ -1,6 +1,6 @@ // server/routes/file.js -import { processFile } from '../../src/commands/processFile.js' +import { processFile } from '../../../src/commands/processFile.js' import { reqToOpts } from '../utils/reqToOpts.js' // Handler for /file route diff --git a/server/routes/playlist.js b/packages/server/routes/playlist.js similarity index 94% rename from server/routes/playlist.js rename to packages/server/routes/playlist.js index a276fbd..bfc6088 100644 --- a/server/routes/playlist.js +++ b/packages/server/routes/playlist.js @@ -1,6 +1,6 @@ // server/routes/playlist.js -import { processPlaylist } from '../../src/commands/processPlaylist.js' +import { processPlaylist } from '../../../src/commands/processPlaylist.js' import { reqToOpts } from '../utils/reqToOpts.js' // Handler for /playlist route diff --git a/server/routes/rss.js b/packages/server/routes/rss.js similarity index 94% rename from server/routes/rss.js rename to packages/server/routes/rss.js index 2ff5212..5792dcc 100644 --- a/server/routes/rss.js +++ b/packages/server/routes/rss.js @@ -1,6 +1,6 @@ // server/routes/rss.js -import { processRSS } from '../../src/commands/processRSS.js' +import { processRSS } from '../../../src/commands/processRSS.js' import { reqToOpts } from '../utils/reqToOpts.js' // Handler for /rss route diff --git a/server/routes/urls.js b/packages/server/routes/urls.js similarity index 94% rename from server/routes/urls.js rename to packages/server/routes/urls.js index 9fd1bc3..82ae3d0 100644 --- a/server/routes/urls.js +++ b/packages/server/routes/urls.js @@ -1,6 +1,6 @@ // server/routes/urls.js -import { processURLs } from '../../src/commands/processURLs.js' +import { processURLs } from '../../../src/commands/processURLs.js' import { reqToOpts } from '../utils/reqToOpts.js' // Handler for /urls route diff --git a/server/routes/video.js b/packages/server/routes/video.js similarity index 94% rename from server/routes/video.js rename to packages/server/routes/video.js index 08dd2e7..d3d0afc 100644 --- a/server/routes/video.js +++ b/packages/server/routes/video.js @@ -1,6 +1,6 @@ // server/routes/video.js -import { processVideo } from '../../src/commands/processVideo.js' +import { processVideo } from '../../../src/commands/processVideo.js' import { reqToOpts } from '../utils/reqToOpts.js' // Handler for /video route diff --git a/server/utils/reqToOpts.js b/packages/server/utils/reqToOpts.js similarity index 100% rename from server/utils/reqToOpts.js rename to packages/server/utils/reqToOpts.js diff --git a/web/.gitignore b/packages/web/.gitignore similarity index 100% rename from web/.gitignore rename to packages/web/.gitignore diff --git a/web/index.html b/packages/web/index.html similarity index 100% rename from web/index.html rename to packages/web/index.html diff --git a/web/package.json b/packages/web/package.json similarity index 100% rename from web/package.json rename to packages/web/package.json diff --git a/web/src/App.jsx b/packages/web/src/App.jsx similarity index 100% rename from web/src/App.jsx rename to packages/web/src/App.jsx diff --git a/web/src/Form.jsx b/packages/web/src/Form.jsx similarity index 100% rename from web/src/Form.jsx rename to packages/web/src/Form.jsx diff --git a/web/src/index.css b/packages/web/src/index.css similarity index 100% rename from web/src/index.css rename to packages/web/src/index.css diff --git a/web/src/main.jsx b/packages/web/src/main.jsx similarity index 100% rename from web/src/main.jsx rename to packages/web/src/main.jsx diff --git a/web/vite.config.js b/packages/web/vite.config.js similarity index 100% rename from web/vite.config.js rename to packages/web/vite.config.js