diff --git a/bin/heimdall b/bin/heimdall
new file mode 100755
index 00000000..82c0cc18
--- /dev/null
+++ b/bin/heimdall
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+
+docker run -d \
+ --name=heimdall \
+ -e PUID=1000 \
+ -e PGID=1000 \
+ -e TZ=Etc/UTC \
+ -p 80:80 \
+ -p 443:443 \
+ -v heimdall:/config \
+ -it --rm \
+ lscr.io/linuxserver/heimdall:latest
+
+
+# --restart unless-stopped \
\ No newline at end of file
diff --git a/bin/links b/bin/links
new file mode 100755
index 00000000..f074e7cd
--- /dev/null
+++ b/bin/links
@@ -0,0 +1,53 @@
+#!/usr/bin/env ruby
+
+require 'date'
+require 'fileutils'
+
+dir = "/Users/wschenk/Library/Mobile Documents/iCloud~md~obsidian/Documents/my awesome vault/links"
+
+# Create a hash to store files by week
+files_by_week = {}
+
+# Loop through files in the directory
+Dir.glob(File.join(dir, "*.md")).each do |file|
+ filename = File.basename(file, ".md")
+ puts filename
+ date = Date.parse(filename) rescue next # Skip if filename is not a valid date
+
+ # Determine the week number
+ week = date.strftime("%Y-W%W")
+
+ # Initialize array for this week if it doesn't exist
+ files_by_week[week] ||= []
+
+ # Add file to the appropriate week
+ files_by_week[week] << file
+end
+
+# Process each week
+files_by_week.each do |week, files|
+ output = "# Week #{week}\n\n"
+
+ files.sort.each do |file|
+ filename = File.basename(file)
+ content = File.read(file)
+
+ # Remove date and ".md" from filename
+ header = File.basename(filename, ".md").sub(/^\d{4}-\d{2}-\d{2}-/, '')
+ output += "## #{header}\n\n"
+
+ # Add file contents
+ output += "#{content}\n\n"
+ end
+
+ # Write output to file
+ output_dir = File.join(dir, "../weekly_summaries")
+ FileUtils.mkdir_p(output_dir)
+ output_file = File.join(output_dir, "#{week}.md")
+ File.write(output_file, output)
+
+ # Print directory and filename
+ puts "Writing file: #{output_file}"
+end
+
+puts "Output directory: #{File.join(dir, 'weekly_summaries')}"
\ No newline at end of file
diff --git a/content/articles/2024/coding_with_cursor/cover.png b/content/articles/2024/coding_with_cursor/cover.png
new file mode 100644
index 00000000..ba35008c
Binary files /dev/null and b/content/articles/2024/coding_with_cursor/cover.png differ
diff --git a/content/articles/2024/coding_with_cursor/index.org b/content/articles/2024/coding_with_cursor/index.org
new file mode 100644
index 00000000..d32f227a
--- /dev/null
+++ b/content/articles/2024/coding_with_cursor/index.org
@@ -0,0 +1,45 @@
+#+title: Coding with cursor
+#+date: 2024-09-20T16:02:46
+#+draft: true
+
+I want to build a static app using modern javascript and html that
+that uses webcomponents. each component will live it its own file.
+The task of the application is to manage assets, and it will use
+supabase to do so. When a file is dragged onto the window, that gets
+added to the upload queue. The user can also press space to record
+audio, and space again to stop the recording and it will get added to
+the upload queue. The user can also press a button which lets them do
+a video recording, which when stopped will get added to the upload
+queue. When something is added to the upload queue, or if the
+device's network setting gets changes like they lost connectivity,
+everything is first stored locally in the webbrowser as a PWA so that
+the upload can resume. There's a process bar for the uploading so the
+user can see which files have been sucessfully uploaded or not
+
+
+
+please contiue the implmentation
+
+
+progress bar is referenced but not implemented
+
+
+
+can you import subapase from
+
+
+wait until the window is ready event to initialize supabase
+
+
+
+{statusCode: '403', error: 'Unauthorized', message: 'new row violates row-level security policy'}
+
+
+actually can you use supabase to get the anon user when it first starts up and print it out
+
+
+
+* References
+# Local Variables:
+# eval: (add-hook 'after-save-hook (lambda ()(org-babel-tangle)) nil t)
+# End:
diff --git a/content/howto/2024/astro_and_obsidian/cover.png b/content/howto/2024/astro_and_obsidian/cover.png
new file mode 100644
index 00000000..5e515b23
Binary files /dev/null and b/content/howto/2024/astro_and_obsidian/cover.png differ
diff --git a/content/howto/2024/astro_and_obsidian/index.org b/content/howto/2024/astro_and_obsidian/index.org
new file mode 100644
index 00000000..07784e82
--- /dev/null
+++ b/content/howto/2024/astro_and_obsidian/index.org
@@ -0,0 +1,412 @@
+#+title: Astro and Obsidian
+#+subtitle: easy editing
+#+tags[]: obsidian astro
+#+date: 2024-11-19T19:01:26
+
+I like obsidian a lot as an editor, and I wanted to see if I could
+use it to manage an astro site. Also, I've never built an astro site.
+
+Obsidian has a built in publishing feature, but I'm not sure that I
+fully understand how it works. Specifically I don't like the links
+between, the graph thing, and I'm not really sure how it thinks of how
+the content is managed. It could be amazing -- I haven't really
+looked at it.
+
+Anyway, lets figure it out. First you need node, then you need
+obsidian.
+
+* Create the site and add tailwind
+
+#+begin_src bash
+ npm create astro@latest my-project
+#+end_src
+
+Say blank, whatever you want to typescript, and install dependancies
+and a git repo.
+
+Then =cd my-project= and
+
+#+begin_src bash
+ npx astro add tailwind
+ npm install -D @tailwindcss/typography
+#+end_src
+
+And then make sure that you have the typography plugin installed in
+=tailwind.config.mjs=:
+
+#+begin_src javascript
+ /** @type {import('tailwindcss').Config} */
+ export default {
+ content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
+ theme: {
+ extend: {},
+ },
+ plugins: [require("@tailwindcss/typography")],
+ };
+
+#+end_src
+
+* Create the BaseLayout
+
+And start up the dev server
+
+#+begin_src bash
+ npm run dev
+
+#+end_src
+
+Make a layout in =src/layouts/BaseLayout.astro=:
+
+#+begin_src html
+ ---
+ const pageTitle = Astro.props.title || "My Astro Blog";
+ ---
+
+
+
+
+
+
+ {pageTitle}
+
+
+
+
+
+
+
+#+end_src
+
+You can test out if tailwind is working right by changing the content
+of =src/pages/index.astro= and adding some tailwind classes, like
+
+#+begin_src html
+---
+import BaseLayout from "../layout/BaseLayout.astro";
+---
+
+
Astro
+
Here is a bunch of my text
+
+#+end_src
+
+
+* Add =astro-rehype-relative-markdown-links=
+
+One of the fun things is to link between notes. Lets set that up so
+astro understands obsidian links. First the astro side:
+
+#+begin_src bash
+ npm install astro-rehype-relative-markdown-links
+#+end_src
+
+And so your =astro.config.mjs= looks like:
+
+#+begin_src js
+ import { defineConfig } from "astro/config";
+
+ import tailwind from "@astrojs/tailwind";
+ import rehypeAstroRelativeMarkdownLinks from "astro-rehype-relative-markdown-links";
+
+ // https://astro.build/config
+ export default defineConfig({
+ integrations: [tailwind()],
+ markdown: {
+ rehypePlugins: [rehypeAstroRelativeMarkdownLinks],
+ },
+ });
+#+end_src
+
+Now on the Obsidian side, go to =Manage Vaults= and then create a new vault.
+
+Call the vault =posts= and put it the =my-project/src/content= directory.
+
+In preferences, under =Files and Links=, turn off "wikilinks".
+
+* Make the post page templates
+
+Then create =src/pages/posts/[...slug].astro=:
+
+First we define =getStaticPaths=, which returns a list of pages that we
+want to render. The =slug= is the name of the url (basically) and =entry=
+is the post itself. This is what tell astro that these urls exist and
+need to be rendered.
+
+Then in the html part, we put the title, date, and a list of tags that
+may or may not be defined.
+
+#+begin_src html
+ ---
+ import BaseLayout from "../../layout/BaseLayout.astro";
+ import { getCollection } from 'astro:content';
+ import type { CollectionEntry } from 'astro:content';
+
+ // 1. Generate a new path for every collection entry
+ export async function getStaticPaths() {
+ const blogEntries = await getCollection('posts');
+ return blogEntries.map(entry => ({
+ params: { slug: entry.slug }, props: { entry },
+ }));
+ }
+ // 2. For your template, you can get the entry directly from the prop
+
+ const { entry } = Astro.props;
+ type Props = {
+ entry: CollectionEntry<'posts'>;
+ };
+ const { Content } = await entry.render();
+ ---
+
+
+
+#+end_src
+
+If you run the dev server, and don't have the BaseLayout specify the right
+charset you might see smart quotes all funky.
+
+#+begin_src html
+
+#+end_src
+
+So make that that is in the header (which probably should be there
+anyway.)
+
+
+* Create a few pages
+
+Back in obsidian, lets create some pages. In the Welcome page, remove
+everything and then create a new link to a new page.
+
+You can drag images into Obsidian and they will get optimized and
+deployed as needed.
+
+* Add support for callouts
+
+#+begin_src bash
+ npm install remark-obsidian-callout --save-dev
+
+#+end_src
+
+And inside of =astro.config.mjs=:
+
+#+begin_src javascript
+import { defineConfig } from "astro/config";
+
+import tailwind from "@astrojs/tailwind";
+import rehypeAstroRelativeMarkdownLinks from "astro-rehype-relative-markdown-links";
+import remarkObsidianCallout from "remark-obsidian-callout";
+
+// https://astro.build/config
+export default defineConfig({
+ integrations: [tailwind()],
+ markdown: {
+ rehypePlugins: [rehypeAstroRelativeMarkdownLinks],
+ remarkPlugins: [remarkObsidianCallout],
+ },
+});
+#+end_src
+
+/Adjust your blockquote styles as needed/.
+
+
+* Create a template for a post
+
+Create a =templates= folder and create =post.md= inside.
+
+#+begin_src markdown
+---
+title:
+date: {{date}}
+tags:
+---
+
+#+end_src
+
+Inside of your obsidian settings select =templates= as the template
+folder.
+
+* Create a blog index
+
+Get all the posts from the post collection, and sort them by date.
+
+=src/pages/blog.astro=:
+
+#+begin_src html
+ ---
+ import BaseLayout from "../layout/BaseLayout.astro";
+ import { getCollection } from 'astro:content';
+
+ const allPosts = await getCollection('posts');
+ // Sort posts by date
+ allPosts.sort((a, b) => new Date(b.data.date).getTime() - new Date(a.data.date).getTime());
+
+ ---
+
+
+
+
+
+#+end_src
+
+This links to the =posts/= pages that we defined above.
+
+* Tags
+
+How about tags?
+
+First lets create =src/pages/tags.astro= to display the list of tags.
+
+We get all of the posts, then all of the tags in each post and add
+them to a map of arrays. We could list out each post here, or just show
+the count of posts with that specific date.
+
+#+begin_src html
+ ---
+ import { getCollection } from 'astro:content';
+ import BaseLayout from "../layout/BaseLayout.astro";
+ const allPosts = await getCollection('posts');
+
+ const tags = {};
+
+ allPosts.forEach((post) => {
+ post.data.tags?.forEach((tag) => {
+ tags[tag] = tags[tag] || [];
+ tags[tag].push(post);
+ });
+ });
+
+ ---
+
+
+
+
+#+end_src
+
+And then we can create =src/pages/tags/[...slug].astro= to render each of the tag pages:
+
+#+begin_src html
+ ---
+ import { getCollection } from 'astro:content';
+ import BaseLayout from "../../layout/BaseLayout.astro";
+
+ // 1. Generate a new path for every collection entry
+ export async function getStaticPaths() {
+ const allPosts = await getCollection('posts');
+ const uniqueTags = [...new Set(allPosts.flatMap(post => post.data.tags ?? []))];
+
+ return uniqueTags.map(tag => ({
+ params: { slug: tag },
+ props: {
+ tag,
+ posts: allPosts.filter(post => post.data.tags?.includes(tag))
+ }
+ }));
+ }
+
+ // 2. For your template, you can get the entry directly from the prop
+ const { tag,posts } = Astro.props;
+ ---
+
+
+
+#+end_src
+
+
+* RSS
+
+First lets add the package:
+
+#+begin_src bash
+npm install @astrojs/rss
+#+end_src
+
+
+=src/utils/posts.js=:
+
+#+begin_src javascript
+import { getCollection } from "astro:content";
+
+export async function getPosts() {
+ let posts = await getCollection("posts");
+ // Sort posts by date
+ posts.sort(
+ (a, b) => new Date(b.data.date).getTime() - new Date(a.data.date).getTime()
+ );
+
+ // Filter out posts without a date or where date is not a real date
+ posts = posts.filter(
+ (post) => post.data.date && !isNaN(new Date(post.data.date).getTime())
+ );
+
+ return posts;
+}
+#+end_src
+
+And then create =src/pages/rss.xml.js=:
+
+#+begin_src javascript
+ import rss from "@astrojs/rss";
+ import { getPosts } from "../utils/posts";
+
+ export async function GET(context) {
+ const posts = await getPosts();
+
+ posts.forEach((post) => {
+ console.log("title", post.data.title || post.slug);
+ console.log("date", post.data.date);
+ console.log(
+ "description",
+ post.data.description || post.data.title || post.slug
+ );
+ console.log("link", `/posts/${post.slug}`);
+ console.log("---");
+ });
+
+ // console.log(blog);
+ return rss({
+ title: "Obsidian Blog",
+ description: "Rocks are cool",
+ site: context.site || "https://obsidian.blog",
+ items: posts.map((post) => ({
+ title: post.data.title || post.slug,
+ pubDate: post.data.date,
+ description: post.data.description || post.data.title || post.slug,
+ // Compute RSS link from post `slug`
+ link: `/posts/${post.slug}`,
+ })),
+ });
+ }
+#+end_src
+
+
+
+* References
+
+1. https://stackoverflow.com/questions/76163067/using-markdown-wiki-links-in-astro-framework
+1. https://github.com/vernak2539/astro-rehype-relative-markdown-links
+1. https://www.npmjs.com/package/remark-obsidian-callout
+2. https://help.obsidian.md/Editing+and+formatting/Callouts
+3. https://docs.astro.build/en/guides/rss/
+
+
+# Local Variables:
+# eval: (add-hook 'after-save-hook (lambda ()(org-babel-tangle)) nil t)
+# End:
diff --git a/content/howto/2024/coding_with_ai/cover.png b/content/howto/2024/coding_with_ai/cover.png
new file mode 100644
index 00000000..ceb4ef44
Binary files /dev/null and b/content/howto/2024/coding_with_ai/cover.png differ
diff --git a/content/howto/2024/coding_with_ai/index.org b/content/howto/2024/coding_with_ai/index.org
new file mode 100644
index 00000000..f40991a3
--- /dev/null
+++ b/content/howto/2024/coding_with_ai/index.org
@@ -0,0 +1,216 @@
+#+title: Coding with AI
+#+subtitle: v0 and cursor
+#+tags[]: nextjs v0 cursor
+#+date: 2024-09-13T10:21:38
+#+draft: true
+
+* v0
+
+Head over to v0.chat dev, and lets give it a prompt:
+
+#+begin_quote
+Make a UI that is to help people plan road trips. It will search and
+show you where electric vehicle chargers are and when you look at a
+charger it will show you the things that are nearby it
+#+end_quote
+
+Click add to codebase on the top right, and copy it to the clipboard.
+
+In my case
+
+#+begin_src bash
+ npx shadcn@latest add "https://v0.dev/chat/b/2hbXmv8?token=eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..I0FAEsakOaAJzb39.gDmpasvmglij1M-9qv8S9W22x2ySMS2F1G5mZS-TObk9_p-6GqE.8abpyK0OchsJ3jMu_53r-A"
+#+end_src
+
+* Setup the nextjs app
+
+#+begin_src bash
+ cd $(mktemp -d)
+ npx create-next-app@latest
+
+#+end_src
+
+I named it =roadtrip= and accepted all of the defaults.
+
+#+begin_src bash
+ cd roadtrip
+ cursor roadtrip
+ #+end_src
+
+* Add our page
+
+Run the =shadcn= command above
+
+#+begin_src bash
+✔ You need to create a component.json file to add components. Proceed? … yes
+✔ Which style would you like to use? › New York
+✔ Which color would you like to use as the base color? › Zinc
+✔ Would you like to use CSS variables for theming? … no / yes
+✔ Writing components.json.
+✔ Checking registry.
+✔ Installing dependencies.
+✔ Created 6 files:
+ - components/road-trip-planner.tsx
+ - components/ui/button.tsx
+ - components/ui/input.tsx
+ - components/ui/card.tsx
+ - components/ui/tabs.tsx
+ - components/ui/scroll-area.tsx
+#+end_src
+
+Then replace =app/page.tsx= with
+
+#+begin_src typescript
+ import { RoadTripPlannerComponent } from "@/components/road-trip-planner";
+
+ export default function Home() {
+ return ;
+ }
+#+end_src
+
+Run =npm run dev= to start up the server and check out
+[[http://localhost:3000]]
+
+* Comsposer
+
+command i to start the composer
+
+add the mapbox docs by typeing @docs and then putting in [[https://docs.mapbox.com/]]
+
+add openrouting serving by typing @docs and then adding https://openrouteservice.org/dev/
+
+add road-trip-planner to the composer
+
+#+begin_quote
+with @Mapboxfactor our the place holder of the map to use mapbox.
+when its first loaded the map will be centered around where the user
+is, and when map is dragged around it will do a search to the backend
+to see which chargers are available. If the user enters in location,
+it will first look to see where the @OpenRouteService drive is and
+plot that on the map, giving a list of chargers that are nearby
+#+end_quote
+
+then i say
+
+#+begin_quote
+put that component in a seperate file
+#+end_quote
+
+Accept all the changes.
+
+Add the library it asks
+
+#+begin_src bash
+ npm install mapbox-gl @types/mapbox-gl
+#+end_src
+
+Sign up for a mapbox account
+
+Get an access token, and then =.env.local= file with the token in it.
+Add to =.gitignore= and make sure that the =MapComponent= is using it.
+
+(Select it, command-k, and say "pull the token from the environment file")
+
+Looks like the refactoring didn't work totally, just select everything
+in =road-trip-planner= and say that the map stuff should be in the
+subcomponent.
+
+* Add route look up
+
+Clear out the composer, and open up the road-trip-planner pane
+
+#+begin_quote
+for the location planner, use @OpenRouteService to lookup a list of
+matching city names. Once the user selects them, put a pin on the map
+to the destination and look up the route to drive from one place to
+the other
+#+end_quote
+
+Go to openrouteservice.org
+
+Create an account and create a key
+
+Update =OPENROUTE_API_KEY= in =.env.local=
+
+Test it out, and it doesn't work. Looking at the web console I see an error
+
+#+begin_quote
+Instead of having to press the button to search, can you have it start
+searching while I'm typing. Also, it says GET
+http://localhost:3000/api/geocode?query=burtlington 404 (Not Found)
+#+end_quote
+
+Apply the changes, add the new library
+
+#+begin_src bash
+ npm install use-debounce
+#+end_src
+
+and restart
+
+* Tweaks
+
+#+begin_quote
+show the label part in the city selector
+#+end_quote
+
+#+begin_quote
+do to the trip planning itself, a curl call to get the results looks
+like this
+
+curl -X POST \
+ 'https://api.openrouteservice.org/v2/directions/driving-car' \
+ -H 'Content-Type: application/json; charset=utf-8' \
+ -H 'Accept: application/json, application/geo+json, application/gpx+xml, img/png; charset=utf-8' \
+ -H 'Authorization: 5b3ce3597851110001cf624896eaf204218948f19f0b26277e6b19ba' \
+ -d '{"coordinates":[[8.681495,49.41461],[8.686507,49.41943],[8.687872,49.420318]]}'. the results look like this curl -X POST \
+ 'https://api.openrouteservice.org/v2/directions/driving-car' \
+ -H 'Content-Type: application/json; charset=utf-8' \
+ -H 'Accept: application/json, application/geo+json, application/gpx+xml, img/png; charset=utf-8' \
+ -H 'Authorization: 5b3ce3597851110001cf624896eaf204218948f19f0b26277e6b19ba' \
+ -d '{"coordinates":[[8.681495,49.41461],[8.686507,49.41943],[8.687872,49.420318]]}'
+#+end_quote
+
+* Add chargers to the map
+
+
+#+begin_quote
+ok, now i want to search for chargers that are around the polyline,
+and pin them on the map. Update the list on the left to show which
+chargers are available. Use the end point
+@https://chargermap.fly.dev/in_map?n=44.600735057768574&e=-72.87918090820314&s=44.36804189293885&w=-73.72238159179689&connectors=null&dc=true&level1=true&level2=true
+which is the box that it will return the chargers in, and the three
+different types of chargers. level1, level2 and dc fast chargers.
+show a different pin for each of the chargers. an example of the json
+it returns is {
+
+
+ "id": 32973,
+ "latitude": 44.469281,
+ "longitude": -73.154972,
+ "name": "Burlington International Airport",
+ "address": "1200 Airport Dr",
+ "city": "South Burlington",
+ "state": "VT",
+ "zip": "05403",
+ "country": "US",
+ "facility": "AIRPORT",
+ "level1": null,
+ "level2": 6,
+ "dcfast": null,
+ "network": "Non-Networked",
+ "date_last_confirmed": "2023-09-14",
+ "workplace": "true",
+ "chademo": null,
+ "j1772": 1,
+ "j1772combo": null,
+ "nema1450": null,
+ "nema515": null,
+ "tesla": null
+ },
+#+end_quote
+* References
+# Local Variables:
+# eval: (add-hook 'after-save-hook (lambda ()(org-babel-tangle)) nil t)
+# End:
+
diff --git a/content/howto/2024/manual_prerender_with_webcomponents/cover.png b/content/howto/2024/manual_prerender_with_webcomponents/cover.png
new file mode 100644
index 00000000..5160e81d
Binary files /dev/null and b/content/howto/2024/manual_prerender_with_webcomponents/cover.png differ
diff --git a/content/howto/2024/manual_prerender_with_webcomponents/index.org b/content/howto/2024/manual_prerender_with_webcomponents/index.org
new file mode 100644
index 00000000..f606b471
--- /dev/null
+++ b/content/howto/2024/manual_prerender_with_webcomponents/index.org
@@ -0,0 +1,24 @@
+#+title: Manual prerender with webcomponents
+#+date: 2024-09-09T17:23:18
+#+draft: true
+
+This works better when you use the regular not shadow dom!
+
+#+begin_src typescript
+ import puppeteer from "https://deno.land/x/puppeteer@16.2.0/mod.ts";
+
+ const browser = await puppeteer.launch();
+ const page = await browser.newPage();
+ await page.goto("http://127.0.0.1:8000", {
+ waitUntil: "networkidle2",
+ });
+
+ const html = await page.content(); // serialized HTML of page DOM.
+ console.log(html);
+ await page.screenshot({ path: "example.png" });
+ await browser.close();
+#+end_src
+* References
+# Local Variables:
+# eval: (add-hook 'after-save-hook (lambda ()(org-babel-tangle)) nil t)
+# End:
diff --git a/content/howto/2024/using_shadcn_and_magicui/cover.png b/content/howto/2024/using_shadcn_and_magicui/cover.png
new file mode 100644
index 00000000..62d2e3ab
Binary files /dev/null and b/content/howto/2024/using_shadcn_and_magicui/cover.png differ
diff --git a/content/howto/2024/using_shadcn_and_magicui/index.org b/content/howto/2024/using_shadcn_and_magicui/index.org
new file mode 100644
index 00000000..51043a6f
--- /dev/null
+++ b/content/howto/2024/using_shadcn_and_magicui/index.org
@@ -0,0 +1,93 @@
+#+title: Using shadcn and magicui
+#+subtitle: all the cool kids are doing it
+#+tags[]: shadcd magicui
+#+date: 2024-09-10T14:17:20
+#+draft: true
+
+I've never used [[https://ui.shadcn.com/][shadcn]] or [[https://magicui.design/][magicui]] so lets play!
+at once!
+
+* Create a new directory and a nextapp
+
+#+begin_src bash
+ cd $(mktemp -d)
+
+
+ ✔ What is your project named? … monkeythumb
+✔ Would you like to use TypeScript? … No / Yes
+✔ Would you like to use ESLint? … No / Yes
+✔ Would you like to use Tailwind CSS? … No / Yes
+✔ Would you like to use `src/` directory? … No / Yes
+✔ Would you like to use App Router? (recommended) … No / Yes
+✔ Would you like to customize the default import alias (@/*)? … No / Yes
+#+end_src
+
+The open it up
+* shadcn install
+
+#+begin_src bash
+ pnpm dlx shadcn@latest init -d
+#+end_src
+
+* Add that button
+
+#+begin_src bash
+ pnpm dlx shadcn@latest add button
+#+end_src
+
+
+#+begin_src typescript
+ // app/page.tsx
+ import { Button } from "@/components/ui/button";
+
+ export default function Home() {
+ return ;
+ }
+
+
+#+end_src
+
+* Install magicui
+
+#+begin_src bash
+ pnpm dlx magicui-cli init
+
+
+#+end_src
+
+* Try the globe
+
+#+begin_src bash
+pnpm dlx magicui-cli add globe
+
+#+end_src
+
+And copy intoe =app.page.tsx=
+
+#+begin_src typescript
+import Globe from "@/components/magicui/globe";
+
+export default function Home() {
+ return (
+
+
+ Globe
+
+
+
+
+ );
+}
+
+#+end_src
+
+* Build a landing page
+
+#+begin_src bash
+pnpm dlx magicui-cli add typing-animation
+
+#+end_src
+* References
+# Local Variables:
+# eval: (add-hook 'after-save-hook (lambda ()(org-babel-tangle)) nil t)
+# End:
diff --git a/content/howto/2024/website_to_markdown/cover.png b/content/howto/2024/website_to_markdown/cover.png
new file mode 100644
index 00000000..76309ada
Binary files /dev/null and b/content/howto/2024/website_to_markdown/cover.png differ
diff --git a/content/howto/2024/website_to_markdown/index.org b/content/howto/2024/website_to_markdown/index.org
new file mode 100644
index 00000000..29499173
--- /dev/null
+++ b/content/howto/2024/website_to_markdown/index.org
@@ -0,0 +1,47 @@
+#+title: Website to markdown
+#+date: 2024-11-12T10:48:47
+#+draft: true
+
+
+* Installation
+
+#+begin_src bash
+ brew install JohannesKaufmann/tap/html2markdown
+#+end_src
+
+or
+
+#+begin_src bash
+ go install github.com/JohannesKaufmann/html-to-markdown/v2/cli@latest
+
+ # rename
+ (cd $(go env GOBIN); mv cli html2markdown)
+#+end_src
+
+* Running
+
+Examples:
+
+#+begin_src bash :results output
+ curl --no-progress-meter https://willschenk.com/fragments/2024/four_freedoms/ | html2markdown
+#+end_src
+
+#+begin_src bash :results output
+ curl --no-progress-meter https://www.nytimes.com/2024/11/12/world/europe/amanda-knox-perugia-italy.html | html2markdown
+#+end_src
+
+
+#+begin_src bash :results output
+ curl --no-progress-meter https://html-to-markdown.com/ | html2markdown
+#+end_src
+
+
+
+
+* References
+
+1. https://github.com/JohannesKaufmann/html-to-markdown
+
+# Local Variables:
+# eval: (add-hook 'after-save-hook (lambda ()(org-babel-tangle)) nil t)
+# End:
diff --git a/content/labnotes/2024/adding_obsidian_clipper/cover.png b/content/labnotes/2024/adding_obsidian_clipper/cover.png
new file mode 100644
index 00000000..72f8bb87
Binary files /dev/null and b/content/labnotes/2024/adding_obsidian_clipper/cover.png differ
diff --git a/content/labnotes/2024/adding_obsidian_clipper/index.org b/content/labnotes/2024/adding_obsidian_clipper/index.org
new file mode 100644
index 00000000..2137fa18
--- /dev/null
+++ b/content/labnotes/2024/adding_obsidian_clipper/index.org
@@ -0,0 +1,34 @@
+#+title: Adding obsidian clipper
+#+subtitle: organizing and collecting
+#+tags[]: obsidian
+#+date: 2024-09-26T15:57:10
+#+draft: true
+
+Install the [[https://chromewebstore.google.com/detail/obsidian-clipper/mphkdfmipddgfobjhphabphmpdckgfhb][extention from the chrome web store]].
+
+Select you vault name, like:
+
+=my awesome vault=
+
+I like to put things into a link directory, so we create a new note
+with the template of =links/{date}-{title}=
+
+#+begin_src markdown
+ > {clip}
+
+- [{title}]({url}) on [[Daily/{date}]]. [[{year}/{month}]].
+#+end_src
+
+Then pin the extention.
+
+* If it flashes
+
+Open up the extension settings and test the configuration -- then
+check the "always" checkbox.
+
+
+
+* References
+# Local Variables:
+# eval: (add-hook 'after-save-hook (lambda ()(org-babel-tangle)) nil t)
+# End:
diff --git a/content/labnotes/2024/compressing_images_for_web/cover.png b/content/labnotes/2024/compressing_images_for_web/cover.png
new file mode 100644
index 00000000..3f2f0c80
Binary files /dev/null and b/content/labnotes/2024/compressing_images_for_web/cover.png differ
diff --git a/content/labnotes/2024/compressing_images_for_web/index.org b/content/labnotes/2024/compressing_images_for_web/index.org
new file mode 100644
index 00000000..f7da824a
--- /dev/null
+++ b/content/labnotes/2024/compressing_images_for_web/index.org
@@ -0,0 +1,99 @@
+#+title: Compressing images for web
+#+subtitle: just smoosh them
+#+tags[]: node javascript sharp
+#+date: 2024-11-04T13:25:04
+#+draft: true
+
+#+begin_src bash
+ npm i -D sharp
+#+end_src
+
+Then, assuming your source images in the =src/images= directory:
+
+#+begin_src javascript
+ const sharp = require("sharp");
+ const fs = require("fs");
+ const path = require("path");
+
+ const imagesDir = path.join(__dirname, "src", "images");
+
+ // Read all files in the images directory
+ fs.readdir(imagesDir, (err, files) => {
+ if (err) {
+ console.error("Error reading images directory:", err);
+ return;
+ }
+
+ // Process each image file
+ files.forEach((file) => {
+ // Skip files that already end with _small.jpg
+ if (file.endsWith("_small.jpg")) {
+ return;
+ }
+
+ console.log(`Processing ${file}`);
+
+ const inputPath = path.join(imagesDir, file);
+
+ // Generate output filename by inserting _small before extension
+ const parsedFile = path.parse(file);
+ const outputFile = `${parsedFile.name}_small.jpg`;
+ const outputPath = path.join(imagesDir, outputFile);
+
+ // Compress and convert to jpg
+ sharp(inputPath)
+ .jpeg({
+ quality: 80, // Adjust quality (0-100)
+ chromaSubsampling: "4:4:4",
+ })
+ .resize(1920, 1080, {
+ fit: "inside", // Maintain aspect ratio
+ withoutEnlargement: true, // Don't enlarge smaller images
+ })
+ .toFile(outputPath)
+ .then(() => {
+ console.log(`Compressed ${file} -> ${outputFile}`);
+ })
+ .catch((err) => {
+ console.error(`Error processing ${file}:`, err);
+ });
+ });
+ });
+
+
+#+end_src
+
+
+Which really gets things down:
+
+#+begin_src bash
+ node compress.js
+Processing image1.png
+Processing image2.png
+Processing image3.png
+Processing image4.png
+Compressed image3.png -> image3_small.jpg
+Compressed image1.png -> image1_small.jpg
+Compressed image2.png -> image2_small.jpg
+Compressed image4.png -> image4_small.jpg
+wschenk@Wills-MacBook-Pro thefocus-landing % ls -l src/images
+total 27936
+-rw-r--r--@ 1 wschenk staff 2900871 Oct 23 16:27 image1.png
+-rw-r--r--@ 1 wschenk staff 355771 Nov 4 13:34 image1_small.jpg
+-rw-r--r--@ 1 wschenk staff 3215392 Oct 23 16:27 image2.png
+-rw-r--r--@ 1 wschenk staff 387309 Nov 4 13:34 image2_small.jpg
+-rw-r--r--@ 1 wschenk staff 1864875 Oct 23 16:28 image3.png
+-rw-r--r--@ 1 wschenk staff 252299 Nov 4 13:34 image3_small.jpg
+-rw-r--r--@ 1 wschenk staff 5263239 Oct 23 16:28 image4.png
+-rw-r--r--@ 1 wschenk staff 45470 Nov 4 13:34 image4_small.jpg
+
+#+end_src
+
+
+* References
+
+1. https://sharp.pixelplumbing.com/install#prebuilt-binaries
+
+# Local Variables:
+# eval: (add-hook 'after-save-hook (lambda ()(org-babel-tangle)) nil t)
+# End:
diff --git a/content/labnotes/2024/reading_feeds_with_fresh_rss/cover.png b/content/labnotes/2024/reading_feeds_with_fresh_rss/cover.png
new file mode 100644
index 00000000..4977bc00
Binary files /dev/null and b/content/labnotes/2024/reading_feeds_with_fresh_rss/cover.png differ
diff --git a/content/labnotes/2024/reading_feeds_with_fresh_rss/index.org b/content/labnotes/2024/reading_feeds_with_fresh_rss/index.org
new file mode 100644
index 00000000..e2dbcbe6
--- /dev/null
+++ b/content/labnotes/2024/reading_feeds_with_fresh_rss/index.org
@@ -0,0 +1,23 @@
+#+title: Reading feeds with FreshRSS
+#+date: 2024-09-22T20:04:33
+#+draft: true
+
+#+begin_src bash
+ docker run -it --rm \
+ --log-opt max-size=10m \
+ -p 8080:80 \
+ -e TZ=Europe/Paris \
+ -e 'CRON_MIN=1,31' \
+ -v freshrss_data:/var/www/FreshRSS/data \
+ -v freshrss_extensions:/var/www/FreshRSS/extensions \
+ --name freshrss \
+ freshrss/freshrss
+
+#+end_src
+
+
+
+* References
+# Local Variables:
+# eval: (add-hook 'after-save-hook (lambda ()(org-babel-tangle)) nil t)
+# End: