---
title: "Making Webtrotion"
slug: making-webtrotion
canonical_url: https://nerdymomocat.github.io/posts/making-webtrotion/
collection: Logbook
published_at: 2023-12-01T00:00:00.000Z
updated_at: 2026-01-08T00:00:00.000Z
tags: 
  - Notion
  - Astro
  - Websites
  - Webtrotion
excerpt: "By breaking things 10x a day"
author: "Nerdy Momo Cat"
---

## Navigation Context

- Canonical URL: https://nerdymomocat.github.io/posts/making-webtrotion/
- You are here: Home > Posts > Logbook > Making Webtrotion

### Useful Next Links
- [Home](https://nerdymomocat.github.io/)
- [Bundles](https://nerdymomocat.github.io/collections/bundles/)
- [Logbook](https://nerdymomocat.github.io/collections/logbook/)
- [Notes](https://nerdymomocat.github.io/collections/notes/)
- [Stream](https://nerdymomocat.github.io/collections/stream/)

### Related Content

#### Pages That Mention This Page
- [Home](https://nerdymomocat.github.io/)
- [Webtrotion vs Notion Sites v2](https://nerdymomocat.github.io/posts/webtrotion-vs-notion-sites-v2/)
- [TIL about template tags in HTML](https://nerdymomocat.github.io/posts/til-about-template-tags-in-html/)
- [I launched Webtrotion on Product Hunt](https://nerdymomocat.github.io/posts/i-launched-webtrotion-on-product-hunt/)
- [Bluesky comments now work on Webtrotion](https://nerdymomocat.github.io/posts/bluesky-comments-now-work-on-webtrotion/)
- [Webtrotion 2.0: Footnotes, Citations, and JSON5 Config](https://nerdymomocat.github.io/posts/webtrotion-20-footnotes-citations-and-json5-config/)
- [Webtrotion 2.3: Multiple Authors, External Content, Gallery Layout, and Copy as Markdown](https://nerdymomocat.github.io/posts/webtrotion-23-multiple-authors-external-content-gallery-layout-and-copy-as-markdown/)

![](https://nerdymomocat.github.io/_astro/info-alternate_gray.xE4Ugu2z_Z1EYdWV.svg)

Normally, if you want to add a page with Astro, you need to place an `.astro`\- or `.md`\- or `.html`\- file in your `src/pages`\-folder.

![](https://nerdymomocat.github.io/_astro/new-badge_red.vM6pi0Nb_Z1EYdWV.svg)

**Main future goal** **Dec 11, 2023, 01:38 AM**

Currently, the repo reads all blocks off notion, and writes them directly to renderable HTML. For saving re-computation of API calls, it saves API response to a json for posts. Instead of doing that, it might be ideal to write it as a content file that is rendered using MDX. Why? Because astro just released this [Incremental Content Caching (Experimental)](https://astro.build/blog/astro-4/#:~:text=the%20new%20APIs.-,Incremental%20Content%20Caching%20\(Experimental\),-Astro%204.0%20introduces). Caching the json, only caches the API request, but the build still often takes around 500ms at the very least. That would be the main incremental future goal from here on.

## On the Roadmap

### Bugs

**API issues**

⬜

Fix issue with [notion.site](http://notion.site/) being messed up because it thinks everything is part of its own site

**There is no difference in API response, which is why 🤷‍♀️**

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.IMoL01TM_1QpC6s.webp)](https://nerdymomocat.github.io/_astro/Untitled.IMoL01TM_1QpC6s.webp)

⬜

Image links

**Doesn’t show up in API**

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/image.BmnI8w3Q_lrU8a.webp)](https://nerdymomocat.github.io/_astro/image.BmnI8w3Q_lrU8a.webp)

⬜

API issue: Inline code background is overriden to default

⬜

Synced blocks currently do not update due to cache

⬜

Cannot correctly figure out if there is interlinked content or not which leads to empty table of contents sometimes, fix that

⬜

Datatables do not follow `table-layout:auto`

⬜

Fix this

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/image.D-RbNE0l_2sm9PV.webp)](https://nerdymomocat.github.io/_astro/image.D-RbNE0l_2sm9PV.webp)

⬜

Add lightbox expansion to youtube videos

⬜

Shift to to make the bluesky embeds smaller

* * *

### Features

⬜

Download a single page as HTML, print a single page as PDF

⬜

Theming (I’ll only do this if AI can implement this end to end)

⬜

Theming using Headings (font, weight, and casing), Links (decoration and hover effects), Shadows (style and presence), Borders (width and style), Border Radius (for all elements), and monotone colors (instead of Notion colors?), Background image,

⬜

Theme change animation

⬜

Denser option - table of contents on left or recent posts on left and footnotes, bibliography, interlinked content on right permanently similar to [Justin Duke’s site](https://jmduke.com/)?

⬜

Support subpages inside pages (without needing to add tags and such, so they would be indexable but part of particular page)

⬜

And someday figure out [lite embed for tiktok](https://github.com/justinribeiro/lite-tiktok)

⬜

Actually get expressive code to work because I do want annotations and collapsing unlike shiki. The tradeoff is that it doesn’t process the line, it needs metadata 😞 [BUT](https://github.com/expressive-code/expressive-code/issues/248#issuecomment-2368482823)

Some other interesting information: [https://twoslash.matthiesen.dev/getting-started/basic/](https://twoslash.matthiesen.dev/getting-started/basic/)

⬜

Should I switch to this? [![](https://github.com/fluidicon.png) GitHub GitHub - shishkin/astro-pagefind: Astro integration for Pagefind static site search.](https://github.com/shishkin/astro-pagefind?tab=readme-ov-file)

⬜

Subtags

⬜

You might like/related section based on embeddings

⬜

Iframe scroller to text fragment linked content. Why? So, for example you are commenting on something, you can link to that part and people can click to show on an iframe in the side or a bottom panel (similar to popup but complete frame) and ideally this doesn’t close but is just replaced by different targets if needed.

⬜

needs a marker in text to say what should open in this iframe rather than just link out

⬜

To do this without needing to create a new page for self content.

⬜

Show markers on iframe content where links are in the existing page and

⬜

Maybe ability to link them back (moonshot)

⬜

Make it work with pdfs

* * *

**Completed**

**In Jan 2026**

✅

Because pagespeed js now loads later, when I first click search or press cmd+K, text box loses focus, fix that

**In Dec 2025**

✅

cover image usage

✅

Add gallery view rather than of list view

✅

Multiple authors

**In Nov 2025**

✅

Very much later, possibly never : include random .md or .html files on page with the nice layout, tags, rss, sitemap, post listings etc.

✅

Maybe this can be done by converting notion page into mdx instead of directly to html (keeping eventual output exactly the same)

✅

or it can be done by allowing a mixture of content — how?

✅

Maybe start with basic stuff, aka, what about content published elsewhere? Can we find a way to list it such that it redirects on clicking on listing?

**In Oct 2025(Claude code, Gemini-cli and I)**

✅

Bib files

✅

Support footnotes — i can try to rely on comments (though unfortunately they do not how block text, only block id) — but they won’t be replicated if the page is downloaded as markdown (comments are exported in html though); so I want to use in-page features.

✅

Fix symbols in interconnected stuff to also include combos and not just repetitions so that the length remains smaller

✅

Use tag descriptions for hover and tag page and collection description for collection page

✅

Tag descriptions

✅

Collection descriptions

✅

Descriptions in OG images

✅

1 level Nested table of contents for headings (callouts, toggles, columns) \[Was done before idk when\]

✅

Links inside simple tables are not read right now

✅

Popups don’t show on margin notes

✅

If you get block links/page links using copy link when the page is open in peek mode, it adds v= in the href that is then pasted leading to the href not being processed correctly.

✅

Footnotes not on pages only on posts. We might want them on pages though?

✅

Uploaded icons to pages do not show up in page mentions (linked ones do)

✅

Try adding this for images instead of optimizing myself?

**Image opt**

[

GitHub - LyonSyonII/astro-preload: Download images at build time! Supports Iconify icons and arbitrary images.

Download images at build time! Supports Iconify icons and arbitrary images. - LyonSyonII/astro-preload

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/lyonsyonii/astro-preload#readme

![title](https://opengraph.githubassets.com/6e9ce92b5be96f25e46ab41bf848eb4bb974034f21af11c17979d5b7099e8adb/LyonSyonII/astro-preload)

](https://github.com/lyonsyonii/astro-preload#readme)

Bookmark for [https://github.com/lyonsyonii/astro-preload#readme](https://github.com/lyonsyonii/astro-preload#readme)

[

Astrojs assetsを使ってスクリーンサイズに最適化した画像をWebP形式で配信する - アルパカログ

Astroのassets機能を使えば画像幅を複数指定するだけでスクリーンサイズに応じて配信画像を切り替えることができます。Astroのassets機能を使ってユーザーのスクリーンサイズに最適化した画像を配信する方法を説明します。

![title](https://www.google.com/s2/favicons?domain=alpacat.com)

https://alpacat.com/posts/astrojs-assets

![title](https://alpacat.com/og-images/user-default.png)

](https://alpacat.com/posts/astrojs-assets)

Bookmark for [https://alpacat.com/posts/astrojs-assets](https://alpacat.com/posts/astrojs-assets)

Or

[

Dynamically import images

Learn how to dynamically import images using Vite’s import.meta.glob function.

![title](https://www.google.com/s2/favicons?domain=docs.astro.build)

https://docs.astro.build/en/recipes/dynamically-importing-images/

![title](https://docs.astro.build/open-graph/en/recipes/dynamically-importing-images.webp)

](https://docs.astro.build/en/recipes/dynamically-importing-images/)

Bookmark for [https://docs.astro.build/en/recipes/dynamically-importing-images/](https://docs.astro.build/en/recipes/dynamically-importing-images/)

[

GitHub - incluud/accessible-astro-components: A collection of accessible components for Astro projects with built-in ARIA attributes, keyboard navigation and interactive elements. Easy to implement and customize to your needs.

A collection of accessible components for Astro projects with built-in ARIA attributes, keyboard navigation and interactive elements. Easy to implement and customize to your needs. - incluud/access…

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/markteekman/accessible-astro-components

![title](https://repository-images.githubusercontent.com/396258029/69034a93-f063-4b66-818c-a319c7e944e2)

](https://github.com/markteekman/accessible-astro-components)

Bookmark for [https://github.com/markteekman/accessible-astro-components](https://github.com/markteekman/accessible-astro-components)

**In Sep 2025**

✅

Add option to download cached content using workflow

✅

Pre-filled search results based off query parameters

✅

Notion wraps default shortcode for inject and iframe

✅

Fix:

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/image.B2z-RS1g_1DJ4PL.webp)](https://nerdymomocat.github.io/_astro/image.B2z-RS1g_1DJ4PL.webp)

✅

Fix too many tags all in new line issue:

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/image.VueBMUx6_Zvu9N0.webp)](https://nerdymomocat.github.io/_astro/image.VueBMUx6_Zvu9N0.webp)

**In Dec 2024**

✅

Need to fix this: \[WARN\] `Astro.request.headers` is unavailable in "static" output mode, and in prerendered pages within "hybrid" and "server" output modes. If you need access to request headers, make sure that `output` is configured as either `"server"` or `output: "hybrid"` in your config file, and that the page accessing the headers is rendered on-demand. — started happening when I upgraded to astro 4.16 😠

No longer happens in v5, but I will check it out if it reappears

✅

Add option to remove caches from repo using github token

**So automatic authentication doesn’t work with gh actions-cache extension AND it is deprecated**

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/image.iFE9uRtt_Z12wMyY.webp)](https://nerdymomocat.github.io/_astro/image.iFE9uRtt_Z12wMyY.webp)

**Neither does the new gh actions**

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/image.BxXLqIQH_1j0fMV.webp)](https://nerdymomocat.github.io/_astro/image.BxXLqIQH_1j0fMV.webp)

**Attempt 1**

```
name: Deploy to GitHub Pages

on:
  push:
    branches: [main]
  schedule:
    - cron: "0 */8 * * *"
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: "pages"
  cancel-in-progress: true

env:
  BUILD_PATH: "."
  NOTION_API_SECRET: ${{ secrets.NOTION_API_SECRET }}

jobs:
  build:
    runs-on: ubuntu-latest
    environment: github-pages
    timeout-minutes: 45
    steps:
      - name: Checkout your repository using git
        uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: 21

      # Idea from https://www.voorhoede.nl/en/blog/super-fast-npm-install-on-github-actions/

      - name: Cache node manually
        id: npm-cache
        uses: actions/cache@v4
        with:
          path: ./node_modules
          key: modules-${{ hashFiles(format('{0}/package-lock.json', env.BUILD_PATH)) }}

      - name: Install dependencies
        if: steps.npm-cache.outputs.cache-hit != 'true'
        run: npm ci --ignore-scripts
        working-directory: ${{ env.BUILD_PATH }}

      - name: Setup Pages
        id: pages
        uses: actions/configure-pages@v5

      - name: Restore cached tmp
        if: steps.npm-cache.outputs.cache-hit == 'true'
        id: cache-tmp-restore
        uses: actions/cache/restore@v4
        with:
          path: ./tmp
          key: ${{ runner.os }}-${{steps.pages.outputs.origin}}-${{steps.pages.outputs.base_path}}-tmp-blogtrotion-${{ github.run_id }}-${{ github.run_attempt }}
          restore-keys: ${{ runner.os }}-${{steps.pages.outputs.origin}}-${{steps.pages.outputs.base_path}}-tmp-blogtrotion-

      - name: Restore cached public
        if: steps.npm-cache.outputs.cache-hit == 'true'
        id: cache-public-restore
        uses: actions/cache/restore@v4
        with:
          path: ./public
          key: ${{ runner.os }}-${{steps.pages.outputs.origin}}-${{steps.pages.outputs.base_path}}-public-blogtrotion-${{ github.run_id }}-${{ github.run_attempt }}
          restore-keys: ${{ runner.os }}-${{steps.pages.outputs.origin}}-${{steps.pages.outputs.base_path}}-public-blogtrotion-

      - name: Set and log environment secrets and variables and then Build with Astro and postbuild for pagefind
        env:
          NOTION_API_SECRET: ${{ secrets.NOTION_API_SECRET }}
          GITHUB_PAGES: true
          SITE: ${{ steps.pages.outputs.origin }}
          BASE: ${{ steps.pages.outputs.base_path }}
        run: |
          echo "NOTION_API_SECRET = $NOTION_API_SECRET"
          echo "GITHUB_PAGES = $GITHUB_PAGES"
          echo "SITE = $SITE"
          echo "BASE = $BASE"
          npx --no-install astro build
          # or if you prefer yarn, use: yarn build
          # --site and --base flags can be included if needed for Astro
          # --site steps.pages.outputs.origin" \
          # --base steps.pages.outputs.base_path"
          npx --no-install pagefind --site dist
        working-directory: ${{ env.BUILD_PATH }}

      - name: Save new tmp cache
        id: cache-tmp-save
        uses: actions/cache/save@v4
        with:
          path: ./tmp
          key: ${{ runner.os }}-${{steps.pages.outputs.origin}}-${{steps.pages.outputs.base_path}}-tmp-blogtrotion-${{ github.run_id }}-${{ github.run_attempt }}

      - name: Save new public cache
        id: cache-public-save
        uses: actions/cache/save@v4
        with:
          path: ./public
          key: ${{ runner.os }}-${{steps.pages.outputs.origin}}-${{steps.pages.outputs.base_path}}-public-blogtrotion-${{ github.run_id }}-${{ github.run_attempt }}

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: ${{ env.BUILD_PATH }}/dist

  deploy:
    needs: build
    runs-on: ubuntu-latest
    name: Deploy
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4

  workflow-keepalive:
    if: github.event_name == 'schedule'
    runs-on: ubuntu-latest
    permissions:
      actions: write
    steps:
      - uses: liskin/gh-workflow-keepalive@v1
```

**Info**

[

GitHub CLI

Take GitHub to the command line

![title](https://www.google.com/s2/favicons?domain=cli.github.com)

https://cli.github.com/manual/gh\_cache\_delete

![title](https://cli.github.com/assets/images/social-card.png)

](https://cli.github.com/manual/gh_cache_delete)

Bookmark for [https://cli.github.com/manual/gh\_cache\_delete](https://cli.github.com/manual/gh_cache_delete)

[

Contexts reference - GitHub Docs

Find information about contexts available in GitHub Actions workflows, including available properties, access methods, and usage examples.

![title](https://www.google.com/s2/favicons?domain=docs.github.com)

https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#github-context

![title](https://docs.github.com/assets/cb-345/images/social-cards/actions.png)

](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#github-context)

Bookmark for [https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#github-context](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#github-context)

[

GitHub - azu/github-actions-overwrite-cache-example: GitHub Actions overwrite cache example repo: restore + delete + save

GitHub Actions overwrite cache example repo: restore + delete + save - azu/github-actions-overwrite-cache-example

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/azu/github-actions-overwrite-cache-example

![title](https://opengraph.githubassets.com/421de264f81f8abb087a57af89e57efe0c7de1c24d06e2072259104605d4fd71/azu/github-actions-overwrite-cache-example)

](https://github.com/azu/github-actions-overwrite-cache-example)

Bookmark for [https://github.com/azu/github-actions-overwrite-cache-example](https://github.com/azu/github-actions-overwrite-cache-example)

✅

Fix RSS, currently it only has the title and not the complete content and I DO NOT WANT THAT

**Some internet stuff for this**

[

Astro RSS MDX

Showing more love for Astro, now that we can finally render MDX in RSS content.

![title](https://www.google.com/s2/favicons?domain=blog.damato.design)

https://blog.damato.design/posts/astro-rss-mdx/

![title](https://blog.damato.design/og-images/astro-rss-mdx.png)

](https://blog.damato.design/posts/astro-rss-mdx/)

Bookmark for [https://blog.damato.design/posts/astro-rss-mdx/](https://blog.damato.design/posts/astro-rss-mdx/)

[

RSS, Astro, and Me – Part 2

The details of my custom RSS feed.

![title](https://www.google.com/s2/favicons?domain=scottwillsey.com)

https://scottwillsey.com/rss-pt2/





](https://scottwillsey.com/rss-pt2/)

Bookmark for [https://scottwillsey.com/rss-pt2/](https://scottwillsey.com/rss-pt2/)

[

Adding to Allowed Tags in Sanitize-Html

sanitize-html is a great utility for cleaning up HTML to include in RSS, among other things, but it has a limited set of allowed tags. Here’s how to add to that list.

![title](https://www.google.com/s2/favicons?domain=scottwillsey.com)

https://scottwillsey.com/sanitize-html/

![title](https://scottwillsey.com/_astro/saw111.Cprn7ULf_ZB6aHy.webp)

](https://scottwillsey.com/sanitize-html/)

Bookmark for [https://scottwillsey.com/sanitize-html/](https://scottwillsey.com/sanitize-html/)

[

Atom Feed Generation Script - equk’s blog

I’m equilibriumuk, a software engineer. I write about programming & computer related topics

![title](https://www.google.com/s2/favicons?domain=equk.co.uk)

https://equk.co.uk/2023/02/22/atom-feed-generation-script/

![title](https://equk.co.uk/media/social.png)

](https://equk.co.uk/2023/02/22/atom-feed-generation-script/)

Bookmark for [https://equk.co.uk/2023/02/22/atom-feed-generation-script/](https://equk.co.uk/2023/02/22/atom-feed-generation-script/)

[

JSON Feed

![title](https://www.google.com/s2/favicons?domain=www.jsonfeed.org)

https://www.jsonfeed.org/





](https://www.jsonfeed.org/)

Bookmark for [https://www.jsonfeed.org/](https://www.jsonfeed.org/)

[

RSS the hard way: Adventures with Astro Assets

There’s something depressing about a blog about blogging. I never wanted this to be one of those blogs. But I’ve struggled so much with Astro’s image feature these past few weeks that I really hope it will benefit others and amount to a bit more than one over-engineered RSS feed.

![title](https://www.google.com/s2/favicons?domain=birtles.blog)

https://birtles.blog/2023/10/02/rss-the-hard-way/

![title](https://birtles.blog/2023/10/02/rss-the-hard-way/social.png)

](https://birtles.blog/2023/10/02/rss-the-hard-way/)

Bookmark for [https://birtles.blog/2023/10/02/rss-the-hard-way/](https://birtles.blog/2023/10/02/rss-the-hard-way/)

[

GitHub - wezm/rsspls: Generate RSS feeds from websites

Generate RSS feeds from websites. Contribute to wezm/rsspls development by creating an account on GitHub.

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/wezm/rsspls

![title](https://opengraph.githubassets.com/cc27393c8a20d1071cb6b082686fb2b32ff7915610f707cde049d5429475571e/wezm/rsspls)

](https://github.com/wezm/rsspls)

Bookmark for [https://github.com/wezm/rsspls](https://github.com/wezm/rsspls)

[

Chris Morgan’s blog

![title](https://www.google.com/s2/favicons?domain=chrismorgan.info)

https://chrismorgan.info/feed.xml





](https://chrismorgan.info/feed.xml)

Bookmark for [https://chrismorgan.info/feed.xml](https://chrismorgan.info/feed.xml)

✅

Look at copy method here: [https://github.com/otoyo/astro-notion-blog/issues/205](https://github.com/otoyo/astro-notion-blog/issues/205)

**In Nov 2024**

✅

Embed bluesky

**In Notion**

[![Stef Walter](https://cdn.bsky.app/img/avatar/plain/did:plc:bjpczagdj45x4rrjshvjxcnc/bafkreiczgxnhlnazvfeykubufnxblc42yq7vi62ttzw5qfipb7niykyujm)](https://bsky.app/profile/stephaniewalter.bsky.social)

[Stef Walter](https://bsky.app/profile/stephaniewalter.bsky.social) [@stephaniewalter.bsky.social](https://bsky.app/profile/stephaniewalter.bsky.social)

[![Bluesky](data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20fill='none'%20viewBox='0%200%20320%20286'%3e%3cpath%20fill='rgb(10,122,255)'%20d='M69.364%2019.146c36.687%2027.806%2076.147%2084.186%2090.636%20114.439%2014.489-30.253%2053.948-86.633%2090.636-114.439C277.107-.917%20320-16.44%20320%2032.957c0%209.865-5.603%2082.875-8.889%2094.729-11.423%2041.208-53.045%2051.719-90.071%2045.357%2064.719%2011.12%2081.182%2047.953%2045.627%2084.785-80%2082.874-106.667-44.333-106.667-44.333s-26.667%20127.207-106.667%2044.333c-35.555-36.832-19.092-73.665%2045.627-84.785-37.026%206.362-78.648-4.149-90.071-45.357C5.603%20115.832%200%2042.822%200%2032.957%200-16.44%2042.893-.917%2069.364%2019.147Z'/%3e%3c/svg%3e)](https://bsky.app/profile/did:plc:bjpczagdj45x4rrjshvjxcnc/post/3lbr3ah5ujs2u)

My weekly Newsletter is out: - User Testing Templates Starter Kit - Free Collaborative Whiteboard Tool - Framing Effect - A PacCam Face-Controlled Game - Fun Emoji Rain Experiment - Image Accessibility on Bluesky - Using Auto Layout in Figma [stephaniewalter.design/blog/pixels-...](https://stephaniewalter.design/blog/pixels-of-the-week-november-24-2024/)

[![Pixels of the Week – November 24, 2024 by Stéphanie Walter - UX Researcher & Designer.](https://cdn.bsky.app/img/feed_thumbnail/plain/did:plc:bjpczagdj45x4rrjshvjxcnc/bafkreic2jh33xdlfbnajjxo4njqqqfsv2jcleac2l35pqdcnqw4zocjibi)

stephaniewalter.design

Pixels of the Week – November 24, 2024 by Stéphanie Walter - UX Researcher & Designer.

User testing templates starter kit, free wireframe tool, framing effect, a PacCam face-controlled game, image accessibility on Bluesky, using auto layout in Figma, CSS new logo, Penpot plugins contest...



](https://stephaniewalter.design/blog/pixels-of-the-week-november-24-2024/)[November 25, 2024 at 8:12 AM UTC](https://bsky.app/profile/did:plc:bjpczagdj45x4rrjshvjxcnc/post/3lbr3ah5ujs2u)

Mentions [bluesky post](https://bsky.app/profile/stephaniewalter.bsky.social/post/3lbr3ah5ujs2u)

✅

Comments using bluesky (see [https://graysky.app/blog/2024-02-05-adding-blog-comments](https://graysky.app/blog/2024-02-05-adding-blog-comments) , [A comment system on top of ATProto and Bluesky](https://snorre.io/blog/2023-08-19-atproto-bluesky-comment-system/) , [Adding Bluesky Comments to Your Astro Blog](https://blog.jade0x.com/post/adding-bluesky-comments-to-your-astro-blog/) , [https://www.coryzue.com/writing/bluesky-comments/](https://www.coryzue.com/writing/bluesky-comments/) , [https://bsky.app/profile/louee.bsky.social/post/3lbsizqjik22o](https://bsky.app/profile/louee.bsky.social/post/3lbsizqjik22o) , [https://nicoritschel.com/writing/bluesky](https://nicoritschel.com/writing/bluesky) , [https://github.com/ascorbic/bluesky-comments-tag](https://github.com/ascorbic/bluesky-comments-tag) , [https://jack.is/posts/bluesky-comments](https://jack.is/posts/bluesky-comments) and [https://emilyliu.me/blog/comments](https://emilyliu.me/blog/comments) )

✅

Fix the display option of bluesky comments, aka, if no post is found or there is an error, remove that tab (and remove the complete comments div if there is no giscus involved)

✅

Add umami as an option: [https://github.com/yeskunall/astro-umami](https://github.com/yeskunall/astro-umami)

[https://github.com/umami-software/umami/discussions/2826#discussioncomment-10815810](https://github.com/umami-software/umami/discussions/2826#discussioncomment-10815810)

✅

Support for custom emojis

**Liamm\_top asked to see if there is a way to fix this for Chinese deployment which needs:**

✅

Fix the slug regex to support chinese characters Mar 8, 2024

✅

Deploy to vercel

✅

Caching in vercel? (somehow it works — see [https://github.com/nerdymomocat-templates/webtrotion-astro-notion-cms-website-blog/issues/3](https://github.com/nerdymomocat-templates/webtrotion-astro-notion-cms-website-blog/issues/3)

✅

Collections are being cleared in slug for chinese characters and tags Mar 8, 2024

✅

Icon has issues in satori when deploying on custom domains or has base URL? Mar 7, 2024

✅

Remove pnpm Feb 25, 2024

✅

OG image font for a different language Mar 7, 2024

**In Sept 2024**

✅

Paste as mentions in notion doesn’t work?

**The fudge, does not show up in API too?**

[![Links pasted as mention don’t show up?](https://nerdymomocat.github.io/_astro/Untitled.BhFgG7_D_ZmuroI.webp)](https://nerdymomocat.github.io/_astro/Untitled.BhFgG7_D_ZmuroI.webp)

Links pasted as mention don’t show up?

**In Aug 2024**

✅

#IMP

✅

There is overscroll on chrome on iOS (not on safari, not even on arc). Aug 18, 2024 : apparently this is only on iPhone SE and I do not have the patience to figure it out

✅

And the back to top button overlaps the toc button on pages,

✅

and the toc size is too tall for iOS (because address bar thing and SE is tiny)

✅

Currently the font size is fixed for any screen/viewport size but this shouldn’t be the case. so i need to find a way to fix that.

✅

Search on mobile also has open tags which is irritating

✅

Headings on same page should not show a popup, they should directly link to that position on click. The popup is useless in that case.

_Problem? Currently there is no way to know if the linked block is on the same page or is on a different page; and the idea is we want the headings popup to show up if it is on a different page._

**In June/July 2024**

✅

Issue with meta scraper sometimes (some URLs are fine in the same run)

**Details**

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.CZvd2ViF_Z1jG9qY.webp)](https://nerdymomocat.github.io/_astro/Untitled.CZvd2ViF_Z1jG9qY.webp)

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.Bx-v4HJs_2pBR68.webp)](https://nerdymomocat.github.io/_astro/Untitled.Bx-v4HJs_2pBR68.webp)

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.D4HgskdI_ZHw7Q5.webp)](https://nerdymomocat.github.io/_astro/Untitled.D4HgskdI_ZHw7Q5.webp)

Seems like a kofi blocking thing? Yeah and found Jul 25, 2024 that even using [dub.sh](http://dub.sh/) or something like that also does not work

I tried doing something like this but I do not like how it looks:

```
<!--Commenting here so that it does not inject -->
<!DOCTYPE html> <!-- inject -->
<script src='https://storage.ko-fi.com/cdn/scripts/overlay-widget.js'></script>
<script>
  kofiWidgetOverlay.draw('nerdymomocat', {
    'type': 'floating-chat',
    'floating-chat.donateButton.text': 'Buy me a coffee',
    'floating-chat.donateButton.background-color': '#F3E1C6',
    'floating-chat.donateButton.text-color': '#4F1116'
  });
</script>
```

**i tried this too but did not like how long af it was\\**

```
<!--Commenting here so that it does not inject -->
<!DOCTYPE html> <!-- inject -->
<a
  href="https://ko-fi.com/nerdymomocat"
  target="_blank"
  rel="noopener noreferrer"
  class="flex w-full min-w-0 max-w-full grow select-none items-stretch overflow-hidden rounded border border-gray-200 no-underline dark:border-gray-800"
>
  <div
    class="flex-bookmark-text overflow-hidden p-3 text-left text-textColor/90"
  >
    <div
      class="mb-0.5 h-6 overflow-hidden truncate whitespace-nowrap leading-5"
    >
      Support Nerdy Momo Cat on Ko-fi! Buy her a hot chocolate ❤️
    </div>
    <div
      class="h-8 overflow-hidden text-xs leading-4 opacity-80"
    >
      Support Nerdy Momo Cat On Ko-fi. Ko-fi lets you support the people and causes you love with small donations
    </div>
    <div class="mt-1.5 flex max-w-full">
      <div class="mr-1.5 h-4 w-4 min-w-4">
        <img
          class="inline-block max-w-full"
          loading="lazy"
          src="https://www.google.com/s2/favicons?domain=ko-fi.com"
          alt="title"
          decoding="async"
        />
      </div>
      <div
        class="overflow-hidden truncate whitespace-nowrap text-xs leading-4"
      >
        https://ko-fi.com/
      </div>
    </div>
  </div>
  <div
    class="flex-bookmark-image relative hidden sm:block"
  >
    <img
      class="!absolute h-full w-full object-contain object-right"
      loading="lazy"
      src="https://storage.ko-fi.com/cdn/useruploads/138404e3-801f-4922-beb3-0db70efc0578_ed507fa6-95c6-4a2f-9dc9-e97d8d19cab4.png"
      alt="title"
      decoding="async"
    />
  </div>
</a>
```

thought about using this but i do not like how it adds extra useless javascript

```
<!--Commenting here so that it does not inject -->
<!DOCTYPE html> <!-- inject -->
<script type='text/javascript' src='https://storage.ko-fi.com/cdn/widget/Widget_2.js'></script><script type='text/javascript'>kofiwidget2.init('Support my bread addiction', '#4A1510', 'I2I6X8MM3');kofiwidget2.draw();</script> 
```

this looks ugly

```
<!--Commenting here so that it does not inject -->
<!DOCTYPE html> <!-- inject -->
<div>
    <a href="https://www.buymeacoffee.com/yourusername" target="_blank" rel="noopener noreferrer"
        style="
            background-color: #f59e0b;
            color: white;
            font-weight: 500;
            border-radius: 6px;
            padding: 8px 16px;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            text-decoration: none;
            transition: background-color 0.3s ease-in-out;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        "
        onmouseover="this.style.backgroundColor='#d97706'"
        onmouseout="this.style.backgroundColor='#f59e0b'"
    >
        <span style="font-size: 1.125rem; margin-right: 8px;">☕</span>
        Buy me a coffee
    </a>
</div>
```

✅

Fix plain text does not match, using plaintext

✅

scrolling expanding table of contents (this can be done using intersection observer — I am not sure it is worth doing though? Or it might be)

✅

Fix the distance between toc and toc button on mobile

✅

Fix:

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.BOSG0My6_ZXuIMB.webp)](https://nerdymomocat.github.io/_astro/Untitled.BOSG0My6_ZXuIMB.webp)

\[Shiki\] 10 instances have been created. Shiki is supposed to be used as a singleton, consider refactoring your code to cache your highlighter instance; Or call `highlighter.dispose()` to release unused instances.

Solved — use this `_transformers_``= {TRANSFORMER_FUNCTIONS_ARR}` instead of changing array based on shortcode because it creates a new highlighter then.

✅

⚠️ keep an eye on build times though!

✅

And maybe move metascraper out and just use noembed instead?

[

Noembed - oEmbed everything.

![title](https://www.google.com/s2/favicons?domain=noembed.com)

https://noembed.com/





](https://noembed.com/)

Bookmark for [https://noembed.com/](https://noembed.com/)

✅

Use [expressive code?](https://github.com/expressive-code/expressive-code/tree/main/packages/astro-expressive-code) though I am not sure how we will get that information from Notion? Maybe from a caption? Or from a shortcode at start of the code block like code inject?

_Also update here:_

[

Feature: Astro Expressive Code should handle Astro’s \`<Code />\` component · Issue #28 · expressive-code/expressive-code

Not sure if in the right repo for my request – but here we go 😄 I have astro-expressive-code installed on my Astro site. I’m also making use of astro’s built-in <Code /> component for some code tha…

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/expressive-code/expressive-code/issues/28#issuecomment-1904949907

![title](https://opengraph.githubassets.com/b5cc5c49c4ecbb79f79746f09176fe5f3687f007da3e69c4e6467f18c27453ef/expressive-code/expressive-code/issues/28)

](https://github.com/expressive-code/expressive-code/issues/28#issuecomment-1904949907)

Bookmark for [https://github.com/expressive-code/expressive-code/issues/28#issuecomment-1904949907](https://github.com/expressive-code/expressive-code/issues/28#issuecomment-1904949907)

More update here:

 [![](https://astro.build/favicon.svg) Astro Astro 4.11 | Astro](https://astro.build/blog/astro-4110/#shiki-transfomers-in-the-code-component) with [![](https://shiki.style/logo.svg) Pine Wu, Anthony Fu Shiki](https://shiki.style/packages/transformers#shikijs-transformers)

-   Attempted here: 1

✅

Starlight also has an [interesting mobile menu implementation](https://github.com/withastro/starlight/blob/7a3ca6494918ccd6a370985d971c3547e1caeafb/packages/starlight/components/MobileTableOfContents.astro) btw

✅

Add sort, filter to tables?

[Simple Data Tables](https://github.com/fiduswriter/Simple-DataTables), [Sortable](https://github.com/tofsjonas/sortable), [List.js](https://github.com/javve/list.js), [Filterlist](https://github.com/smohadjer/filterlist)

**In March 2024**

✅

Clicking read more (on mobile at least) does not close the popup?

✅

dynamically load og image fonts

✅

Fix OG image generation with the logo

✅

add a print stylesheet

✅

Set the bookmarks to take full width?

✅

Add error fixing to astro notion tweet, because if someone deletes the tweet that has been embedded, the build stops

✅

Fix web-bookmark sizing being all over the place

✅

Tags on the page when they wrap do not have vertical spacing

✅

Do not height restrict mermaid previews

✅

bold headings by default

✅

extend bookmark full width if no image

* * *

✅

Static Regeneration

**Docs**

[

How to Implement Incremental Static Regeneration (ISR) in Astro | LogSnag

Learn to implement Incremental Static Regeneration (ISR) in Astro for faster page updates and improved site performance, without full rebuilds.

![title](https://www.google.com/s2/favicons?domain=logsnag.com)

https://logsnag.com/blog/implementing-isr-in-astro

![title](https://logsnag.com/media/blog/implementing-isr-in-astro/thumbnail.png)

](https://logsnag.com/blog/implementing-isr-in-astro)

Bookmark for [https://logsnag.com/blog/implementing-isr-in-astro](https://logsnag.com/blog/implementing-isr-in-astro)

[

Astro on Vercel

Learn how to use Vercel’s features with Astro

![title](https://www.google.com/s2/favicons?domain=vercel.com)

https://vercel.com/docs/frameworks/astro

![title](https://vercel.com/api/docs-og?title=Astro%20on%20Vercel&format=docs&sig=a1abf7285646800684c776bc1f5bb1feaac459b2c1d2cbed1445261dfbe3cb40&category=Supported%20Frameworks&subcategory=Frontends)

](https://vercel.com/docs/frameworks/astro)

Bookmark for [https://vercel.com/docs/frameworks/astro](https://vercel.com/docs/frameworks/astro)

[

How to do ISR and advanced caching with Astro | Netlify Developers

Incremental Static Regeneration (ISR) is a powerful pattern for rendering pages on the web. Astro has useful tools to do fine-grained ISR and other advanced caching patterns when deployed to Netlify. This guide will show you how to do it.

![title](https://www.google.com/s2/favicons?domain=developers.netlify.com)

https://developers.netlify.com/guides/how-to-do-advanced-caching-and-isr-with-astro/

![title](https://developers.netlify.com/preview-image/guides/how-to-do-advanced-caching-and-isr-with-astro)

](https://developers.netlify.com/guides/how-to-do-advanced-caching-and-isr-with-astro/)

Bookmark for [https://developers.netlify.com/guides/how-to-do-advanced-caching-and-isr-with-astro/](https://developers.netlify.com/guides/how-to-do-advanced-caching-and-isr-with-astro/)

[

Middleware

Learn how to use middleware in Astro.

![title](https://www.google.com/s2/favicons?domain=docs.astro.build)

https://docs.astro.build/en/guides/middleware/

![title](https://docs.astro.build/open-graph/en/guides/middleware.webp)

](https://docs.astro.build/en/guides/middleware/)

Bookmark for [https://docs.astro.build/en/guides/middleware/](https://docs.astro.build/en/guides/middleware/)

✅

Menu bar on mobile only clicks on words not the whole line

✅

sticky header (CANCELLED - I DO NOT LIKE THIS MUCH)

✅

referenced pages, referenced in pages etc

✅

Remember for later, we did not move collections out like tags because we would kinda need to have all collections list page and that makes no sense

**Oh, it already gives an error anyway**

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.MrWDNVsX_2a99YI.webp)](https://nerdymomocat.github.io/_astro/Untitled.MrWDNVsX_2a99YI.webp)

✅

[html embedded into iframe rather than inserted into div](https://github.com/withastro/astro/issues/1227#issuecomment-1223608634) \- some 5 min exploration did not work

✅

Fix lightbox

✅

google maps embed

✅

pdf embed

✅

OG only for recent entries in single page preview collections

Lol! Later? I did it Nov 28, 2023, 05:44 AM just because it was absolutely NOT needed

✅

Only run cron if anything in notion has been updated (might need to look into build adapters)

✅

incremental builds (On-Demand Incremental Static Regeneration) when astro implements them

kinda doing them right now with aggressive caching of API requests at elast

✅

Issues with sitemap:

**Screenshot**

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.DVcTjdzF_ZpfeBd.webp)](https://nerdymomocat.github.io/_astro/Untitled.DVcTjdzF_ZpfeBd.webp)

[

Astro Sitemaps: Add Post and Page XML Sitemaps

Astro sitemaps: how to take your Astro site SEO to the next level adding rich XML sitemaps to keep Google up-to-date with changes.

![title](https://www.google.com/s2/favicons?domain=rodneylab.com)

https://rodneylab.com/astro-sitemaps/

![title](https://nebula-a.rodneylab.com/v1/lab/post/astro-sitemaps/astro-sitemaps-facebook.9afc7c911013.png)

](https://rodneylab.com/astro-sitemaps/)

Bookmark for [https://rodneylab.com/astro-sitemaps/](https://rodneylab.com/astro-sitemaps/)

✅

Sometimes when the page height is tiny, like this one because information is all hidden in toggles or stream posts, giscus loads immediately causing a 500ms block time at least. Look into this for that

[

Making giscus less gabby | BryceWray.com

The goal was to offer a comments section without forcing a massive JavaScript load on every visitor. Mission accomplished. Here’s how.

![title](https://www.google.com/s2/favicons?domain=www.brycewray.com)

https://www.brycewray.com/posts/2023/08/making-giscus-less-gabby/#fnref:3

![title](https://www.brycewray.com/images/social-OG-bkgd_1280x669.png)

](https://www.brycewray.com/posts/2023/08/making-giscus-less-gabby/#fnref:3)

Bookmark for [https://www.brycewray.com/posts/2023/08/making-giscus-less-gabby/#fnref:3](https://www.brycewray.com/posts/2023/08/making-giscus-less-gabby/#fnref:3)

**Jun 14, 2024**

✅

Pinned posts for a collection?

✅

Email clicks should copy email and show a toast, not open a `mailto:` link.

✅

When linking to a block that is a toggle, and it is shown in popup, it feels weird, that I can click the arrow, but nothing opens up

* * *

## Astro Resources

These are some links that are important to consider for astro:

[

GitHub - chrismwilliams/astro-theme-cactus: A simple Astro theme. Use it to create your blog or website.

A simple Astro theme. Use it to create your blog or website. - chrismwilliams/astro-theme-cactus

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/chrismwilliams/astro-theme-cactus

![title](https://repository-images.githubusercontent.com/491806564/a422e228-9413-4098-b143-3845a308232e)

](https://github.com/chrismwilliams/astro-theme-cactus)

Bookmark for [https://github.com/chrismwilliams/astro-theme-cactus](https://github.com/chrismwilliams/astro-theme-cactus)

**Some other websites with same theme**

[

File not found · gotofritz/gotofritz.github.io

My personal website. Contribute to gotofritz/gotofritz.github.io development by creating an account on GitHub.

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/gotofritz/gotofritz.github.io/tree/main/src/components

![title](https://opengraph.githubassets.com/b0353e58766a61b8df43ff2462dd486d236edb06d9e8ddf8f68e34e17d85be26/gotofritz/gotofritz.github.io)

](https://github.com/gotofritz/gotofritz.github.io/tree/main/src/components)

Bookmark for [https://github.com/gotofritz/gotofritz.github.io/tree/main/src/components](https://github.com/gotofritz/gotofritz.github.io/tree/main/src/components)

[

Migrating My Blog to Astro

I built this site with SvelteKit before it was stable, hoping it wouldn’t change too much. How naive of me. Of course it did. Faced with the prospect of rebuilding it from scratch, I decided I’d rather try something new. Enter Astro

![title](https://www.google.com/s2/favicons?domain=gotofritz.net)

https://gotofritz.net/blog/migrating-blog-to-astro/

![title](https://gotofritz.net/blog/migrating-blog-to-astro/feature.png)

](https://gotofritz.net/blog/migrating-blog-to-astro/)

Bookmark for [https://gotofritz.net/blog/migrating-blog-to-astro/](https://gotofritz.net/blog/migrating-blog-to-astro/)

[

Blog Posts - Jafar Aziz

Welcome to my website! I am a web developer. My stack includes Typescript (React/Next.js & Express), Laravel, and Java (Kotlin). I have a deep interest in Linux and Docker. Sometimes, I do scripting with Python, Node.js, or Bash to automate my tasks.

![title](https://www.google.com/s2/favicons?domain=www.jafaraziz.com)

https://www.jafaraziz.com/blog/

![title](https://jafaraziz.com/open-graph/blog.png)

](https://www.jafaraziz.com/blog/)

Bookmark for [https://www.jafaraziz.com/blog/](https://www.jafaraziz.com/blog/)

[

GitHub - otoyo/astro-notion-blog: 🚀 Begin building your very own Notion Blog with Astro.

🚀 Begin building your very own Notion Blog with Astro. - otoyo/astro-notion-blog

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/otoyo/astro-notion-blog

![title](https://repository-images.githubusercontent.com/586710695/8567de91-d9a8-4d77-b2d6-7ee7374116e8)

](https://github.com/otoyo/astro-notion-blog)

Bookmark for [https://github.com/otoyo/astro-notion-blog](https://github.com/otoyo/astro-notion-blog)

[

GitHub - ekafyi/astronotion-starter-ssg: Opinionated starter site to make Astro sites with content from Notion, Markdown, and more

Opinionated starter site to make Astro sites with content from Notion, Markdown, and more - ekafyi/astronotion-starter-ssg

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/ekafyi/astronotion-starter-ssg

![title](https://opengraph.githubassets.com/b85091028d1ac846de06b94077185ad6941df2357b53ce639331c355dcd3998a/ekafyi/astronotion-starter-ssg)

](https://github.com/ekafyi/astronotion-starter-ssg)

Bookmark for [https://github.com/ekafyi/astronotion-starter-ssg](https://github.com/ekafyi/astronotion-starter-ssg)

[

GitHub - jcha0713/astro-notion

Contribute to jcha0713/astro-notion development by creating an account on GitHub.

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/jcha0713/astro-notion

![title](https://opengraph.githubassets.com/e620274bbe0db352d7a7860250eacad3268176bb5f152fd603edcced0d92f951/jcha0713/astro-notion)

](https://github.com/jcha0713/astro-notion)

Bookmark for [https://github.com/jcha0713/astro-notion](https://github.com/jcha0713/astro-notion)

## Checking out other stuff

**I could have used Nuxt or Next or maybe even hugo but I don’t know why I do not want to use them Oct 27, 2023, 06:58 AM**

[

GitHub - jdan/notes: Static site generator from a Notion DB

Static site generator from a Notion DB. Contribute to jdan/notes development by creating an account on GitHub.

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/jdan/notes

![title](https://opengraph.githubassets.com/967ba800fa8b18b08b1e26b2c6d297cd333c43c97b4542f4e38b3a2323800ec6/jdan/notes)

](https://github.com/jdan/notes)

Bookmark for [https://github.com/jdan/notes](https://github.com/jdan/notes)

[

GitHub - HEIGE-PCloud/Notion-Hugo: Use Notion as the CMS for your Hugo site

Use Notion as the CMS for your Hugo site. Contribute to HEIGE-PCloud/Notion-Hugo development by creating an account on GitHub.

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/HEIGE-PCloud/Notion-Hugo

![title](https://opengraph.githubassets.com/95a3148994f988f09f58be052567c0a8b6b073a27ed99ef18b6d8e9708d0a77a/HEIGE-PCloud/Notion-Hugo)

](https://github.com/HEIGE-PCloud/Notion-Hugo)

Bookmark for [https://github.com/HEIGE-PCloud/Notion-Hugo](https://github.com/HEIGE-PCloud/Notion-Hugo)

[

GitHub - morethanmin/morethan-log: 😎 A static blog using notion database

😎 A static blog using notion database. Contribute to morethanmin/morethan-log development by creating an account on GitHub.

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/morethanmin/morethan-log

![title](https://repository-images.githubusercontent.com/502087689/68165e8c-b9af-449e-aa04-6b648032ceba)

](https://github.com/morethanmin/morethan-log)

Bookmark for [https://github.com/morethanmin/morethan-log](https://github.com/morethanmin/morethan-log)

[

GitHub - tangly1024/NotionNext: 使用 NextJS + Notion API 实现的，支持多种部署方案的静态博客，无需服务器、零门槛搭建网站，为Notion和所有创作者设计。 (A static blog built with NextJS and Notion API, supporting multiple deployment options. No server required, zero threshold to set up a website. Designed for Notion and all creators.)

使用 NextJS + Notion API 实现的，支持多种部署方案的静态博客，无需服务器、零门槛搭建网站，为Notion和所有创作者设计。 (A static blog built with NextJS and Notion API, supporting multiple deployment options. No server required, zero threshold t…

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/tangly1024/NotionNext

![title](https://opengraph.githubassets.com/d9afef0f3771eb94547b3eceac4be3aa204e7ed1bfcffd7dbe8c5ab71a10e442/tangly1024/NotionNext)

](https://github.com/tangly1024/NotionNext)

Bookmark for [https://github.com/tangly1024/NotionNext](https://github.com/tangly1024/NotionNext)

[

GitHub - samuelkraft/notion-blog-nextjs: Next.js example blog powered by Notion Public API

Next.js example blog powered by Notion Public API. Contribute to samuelkraft/notion-blog-nextjs development by creating an account on GitHub.

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/samuelkraft/notion-blog-nextjs

![title](https://opengraph.githubassets.com/0c2946a3c069dd4ba7b838d0645024ede55fc46873be69078a0cd296942f152d/samuelkraft/notion-blog-nextjs)

](https://github.com/samuelkraft/notion-blog-nextjs)

Bookmark for [https://github.com/samuelkraft/notion-blog-nextjs](https://github.com/samuelkraft/notion-blog-nextjs)

[

GitHub - samuelkraft/notion-blog-nextjs: Next.js example blog powered by Notion Public API

Next.js example blog powered by Notion Public API. Contribute to samuelkraft/notion-blog-nextjs development by creating an account on GitHub.

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/samuelkraft/notion-blog-nextjs

![title](https://opengraph.githubassets.com/0c2946a3c069dd4ba7b838d0645024ede55fc46873be69078a0cd296942f152d/samuelkraft/notion-blog-nextjs)

](https://github.com/samuelkraft/notion-blog-nextjs)

Bookmark for [https://github.com/samuelkraft/notion-blog-nextjs](https://github.com/samuelkraft/notion-blog-nextjs)

**Let me start with basic questions**

Do I need to use npm or can I do styling with CSS/Tailwind and generate mds using n2y?

And what happens if I do that?

What is not supported in mdx basically?

I will need to fix this stuff

```
class CalloutBlock(Block):
    def __init__(self, client, notion_data, page, get_children=True):
        super().__init__(client, notion_data, page, get_children)
        self.rich_text = client.wrap_notion_rich_text_array(
            self.notion_type_data["rich_text"], self
        )
        # the color and icon are not currently used

    def to_pandoc(self):
        content = self.rich_text.to_pandoc()
        if self.has_children:
            children = self.children_to_pandoc()
            result = [Para(content)]
            result.extend(children)
        else:
            result = Para(content)
        return result

class AudioBlock(ContentBlock):
    pass


class VideoBlock(ContentBlock):
    pass
class ToggleBlock(Block):
    """
    Generates a bulleted list item with indented children. A plugin may be used
    to add html classes and replicate the interactive behavior found in Notion.
    """

    def __init__(self, client, notion_data, page, get_children=True):
        super().__init__(client, notion_data, page, get_children)
        self.rich_text = client.wrap_notion_rich_text_array(
            self.notion_type_data["rich_text"], self
        )

    def to_pandoc(self):
        header = self.rich_text.to_pandoc()
        children = self.children_to_pandoc()
        content = [Para(header)]
        content.extend(children)
        return BulletList([content])
```

## Components and libraries for astro

**Comments with giscus**

[

Oops! You found a missing page!

Oops! It looks like this page is lost in space!

![title](https://www.google.com/s2/favicons?domain=thezal.dev)

https://thezal.dev/article\_giscus\_astro/

![title](https://thezal.dev/social-card.png)

](https://thezal.dev/article_giscus_astro/)

Bookmark for [https://thezal.dev/article\_giscus\_astro/](https://thezal.dev/article_giscus_astro/)

[

Free Comment Section on Your Website with Giscus

With the introduction of GitHub discussions, Giscus emerged. An easy way to add comments to our site. Let’s create a comment section with Giscus.

![title](https://www.google.com/s2/favicons?domain=danidiaztech.com)

https://danidiaztech.com/integrate-astro-giscus/

![title](https://danidiaztech.com/header/free-comment-section.png)

](https://danidiaztech.com/integrate-astro-giscus/)

Bookmark for [https://danidiaztech.com/integrate-astro-giscus/](https://danidiaztech.com/integrate-astro-giscus/)

[

Add comments Section to your Astro blog

A quick guide on how to add a comments section to your Astro blog

![title](https://www.google.com/s2/favicons?domain=elazizi.com)

https://elazizi.com/posts/add-comments-section-to-your-astro-blog

![title](https://elazizi.com/images/giscuss-example.png)

](https://elazizi.com/posts/add-comments-section-to-your-astro-blog)

Bookmark for [https://elazizi.com/posts/add-comments-section-to-your-astro-blog](https://elazizi.com/posts/add-comments-section-to-your-astro-blog)

**Other components**

[

GitHub - cijiugechu/astro-satori: This Astro integration brings satori to your Astro project.

This Astro integration brings satori to your Astro project. - cijiugechu/astro-satori

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/cijiugechu/astro-satori

![title](https://opengraph.githubassets.com/613851a32052ba9843bc010b876285fd7f2e96bffa13b44a2b5be682302c9e90/cijiugechu/astro-satori)

](https://github.com/cijiugechu/astro-satori)

Bookmark for [https://github.com/cijiugechu/astro-satori](https://github.com/cijiugechu/astro-satori)

[

GitHub - sebholstein/astro-google-fonts-optimizer: An Astro integration to optimize the Google Fonts loading performance

An Astro integration to optimize the Google Fonts loading performance - sebholstein/astro-google-fonts-optimizer

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/sebholstein/astro-google-fonts-optimizer

![title](https://opengraph.githubassets.com/8a38a05b881e93587532ba163c270750518aa6a9beeaff8120d2ed8b90016768/sebholstein/astro-google-fonts-optimizer)

](https://github.com/sebholstein/astro-google-fonts-optimizer)

Bookmark for [https://github.com/sebholstein/astro-google-fonts-optimizer](https://github.com/sebholstein/astro-google-fonts-optimizer)

[

![title](https://www.google.com/s2/favicons?domain=www.npmjs.com)

https://www.npmjs.com/package/astro-tweet





](https://www.npmjs.com/package/astro-tweet)

Bookmark for [Just a moment...](https://www.npmjs.com/package/astro-tweet)

[

GitHub - RafidMuhymin/astro-imagetools: Image Optimization tools for the Astro JS framework

Image Optimization tools for the Astro JS framework - RafidMuhymin/astro-imagetools

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/RafidMuhymin/astro-imagetools#readme

![title](https://opengraph.githubassets.com/23fdf4192babfac4d4adaf06c401e1d1b0cc82a31f20cde6c393944773eca507/RafidMuhymin/astro-imagetools)

](https://github.com/RafidMuhymin/astro-imagetools#readme)

Bookmark for [https://github.com/RafidMuhymin/astro-imagetools#readme](https://github.com/RafidMuhymin/astro-imagetools#readme)

[

astro-components/packages/astro-pagination at main · philnash/astro-components

A collection of Astro components. Contribute to philnash/astro-components development by creating an account on GitHub.

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/philnash/astro-components/tree/main/packages/astro-pagination

![title](https://opengraph.githubassets.com/afbe2c5103d8150916eb47015f9e744c729388f712a0f665fd11f1831ed91bfa/philnash/astro-components)

](https://github.com/philnash/astro-components/tree/main/packages/astro-pagination)

Bookmark for [https://github.com/philnash/astro-components/tree/main/packages/astro-pagination](https://github.com/philnash/astro-components/tree/main/packages/astro-pagination)

[

GitHub - natemoo-re/astro-remote: Render remote HTML or Markdown content in Astro with full control over the output

Render remote HTML or Markdown content in Astro with full control over the output - natemoo-re/astro-remote

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/natemoo-re/astro-remote

![title](https://opengraph.githubassets.com/a9e4c220c64ace3c92141907137849c955d6a4f6ffd819de43391d3d0b31e8ee/natemoo-re/astro-remote)

](https://github.com/natemoo-re/astro-remote)

Bookmark for [https://github.com/natemoo-re/astro-remote](https://github.com/natemoo-re/astro-remote)

## Log

**Nov 22, 2023, 01:12 PM**

Files that still have original style

✅

bookmark.astro

✅

callout.astro

**Nov 22, 2023, 08:48 PM**

**Link previews**

[https://github.com/cijiugechu/astro-link-preview/tree/main/packages/playground](https://github.com/cijiugechu/astro-link-preview/tree/main/packages/playground)

[

GitHub - saket/unfurl: Generate rich previews of links, inspired by Slack.

Generate rich previews of links, inspired by Slack. - saket/unfurl

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/saket/unfurl

![title](https://repository-images.githubusercontent.com/470656959/7d9b7b6b-d674-4e37-9bac-48f8a7427e82)

](https://github.com/saket/unfurl)

Bookmark for [https://github.com/saket/unfurl](https://github.com/saket/unfurl)

[https://www.npmjs.com/package/link-preview-js](https://www.npmjs.com/package/link-preview-js)

[https://github.com/beeman/micro-unfurl](https://github.com/beeman/micro-unfurl)

[

Google Search

![title](https://www.google.com/s2/favicons?domain=webcache.googleusercontent.com)

https://webcache.googleusercontent.com/search?q=cache:https://medium.com/slack-developer-blog/everything-you-ever-wanted-to-know-about-unfurling-but-were-afraid-to-ask-or-how-to-make-your-e64b4bb9254&strip=0&vwsrc=1&referer=medium-parser





](https://webcache.googleusercontent.com/search?q=cache:https://medium.com/slack-developer-blog/everything-you-ever-wanted-to-know-about-unfurling-but-were-afraid-to-ask-or-how-to-make-your-e64b4bb9254&strip=0&vwsrc=1&referer=medium-parser)

Bookmark for [https://webcache.googleusercontent.com/search?q=cache:https://medium.com/slack-developer-blog/everything-you-ever-wanted-to-know-about-unfurling-but-were-afraid-to-ask-or-how-to-make-your-e64b4bb9254&strip=0&vwsrc=1&referer=medium-parser](https://webcache.googleusercontent.com/search?q=cache:https://medium.com/slack-developer-blog/everything-you-ever-wanted-to-know-about-unfurling-but-were-afraid-to-ask-or-how-to-make-your-e64b4bb9254&strip=0&vwsrc=1&referer=medium-parser)

**Nov 23, 2023, 09:02 PM**

✅

Initial stuff about filter done but remember it sometimes gives error, so figure that out!

✅

Default slugs

**Nov 24, 2023, 02:32 AM**

✅

same page href

✅

code copy

**i added svg to make it look nice AND GRR**

![](https://nerdymomocat.github.io/_astro/report_red.Bv_P4rAp_Z1EYdWV.svg)

The issue you're encountering is due to event delegation. When you click on the svg or path, they become the target of the event, not the button. Since the data-code attribute is on the button, clicking on the svg results in null because ev.target is the svg which does not have the data-code attribute. To consistently get the data-code attribute from the button regardless of where the click occurs (on the button or its child elements), you should use ev.currentTarget instead of ev.target. The currentTarget is always the element to which the event handler has been attached, in this case, the button.

✅

fix code highlight! move to shiki because that is default with astro!

including `_import_` `"../styles/syntax-coloring.css";` worked! but i am still gonna try a tiny bit to move to shiki!

nah prism is good enough!

✅

WTF is the `z-index` changing to be on top of the header on mobile man?

Well, I found a nice svg and thought, hey, i can start with -90 degree angle, then rotate it on click, BIG MISTAKE!

```
<svg class="-rotate-90  transform transition-all duration-300 group-open:rotate-180" fill="none" height="20" width="20" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24">
 <polyline points="6 9 12 15 18 9"></polyline>
</svg>
```

**Nov 24, 2023, 10:22 AM**

✅

tags color in notion to tags color on page

✅

on post

✅

on blog tags

**Nov 24, 2023, 07:16 PM**

![](https://nerdymomocat.github.io/_astro/pencil_gray.ii2C82LM_Z1EYdWV.svg)

If I want notes on my homepage which I very well might, change here `src/pages/[...page].astro`.

✅

how do i embed any html in page?

✅

Image optimization maybe Nov 24, 2023 (I used astro image tools, I am not sure it works though?)

✅

Stream/JSON/preview — i really do not know the solution

![](https://nerdymomocat.github.io/_astro/confetti-ball_orange.DjAtj8bq_Z1EYdWV.svg)

YAY! I DID IT!

So, I think initially I was thinking of using notion relations and items and sub-items to load them into a single page. Instead I have an idea. What if the collection page preview shows the complete content/full post instead of just the preview? That basically solves our problem, right?

✅

Remove notion extras

✅

Figure out how to create post-preview with all blocks

I think this is where the information is: `src/pages/posts/collection/ [collection]/ [...page].astro`

**This seems to be the post preview code**

```
<ul class="space-y-8 text-start">
  { page.data.map((post) => (
  <li class="flex flex-col flex-wrap gap-2 sm:flex-row [&_q]:basis-full">
    <PostPreview post="{post}" as="h2" withDesc />
  </li>
  )) }
</ul>
```

**This is post preview. astro**

```
---
// import type { CollectionEntry } from "astro:content";
// import type { HTMLTag, Polymorphic } from "astro/types";
import type { HTMLTag } from "astro/types";
import FormattedDate from "../FormattedDate.astro";
import type { Post } from "src/lib/interfaces";
import { getPostBySlug } from "@/lib/notion/client";

// type Props<Tag extends HTMLTag> = Polymorphic<{ as: Tag }> & {
// 	post: CollectionEntry<"post">;
// 	withDesc?: boolean;
// };
//FIXME: why is this a new method?

interface Props {
	post: Post;
	as?: HTMLTag;
	withDesc?: boolean;
}

const { post, as: Tag = "div", withDesc = false } = Astro.props;

//TODO: what is this?
// const postDate = post.data.updatedDate ?? post.data.publishDate;
---
<FormattedDate date={new Date(post.Date)} class="min-w-[120px] text-gray-500" />
<Tag>
	<a
		href={`/posts/${post.Slug}/`}
		class="cactus-link"
		rel="prefetch"
	>
		{post.Title}
	</a>
</Tag>
{withDesc && <q class="line-clamp-3 block italic">{post.Excerpt}</q>}
```

**This is how it is done for direct root pages**

```
<PageLayout meta={{ title: page.Title || siteConfig.title }}>
	<section>
		<NotionBlocks blocks={blocks} isRoot={true} />
	</section>
	<!-- {
    //NOTE: This added a recent posts thing to my page. I can add it back later if needed
		!slug && (
			<section aria-label="Blog post list" class="mt-16">
				<h2 class="title mb-4 text-xl">Posts</h2>
				<ul class="space-y-4 sm:space-y-2">
					{rankedPosts.map((p) => (
						<li class="flex flex-col gap-x-2 sm:flex-row">
							<PostPreview post={p} />
						</li>
					))}
				</ul>
			</section>
		)
	} -->
</PageLayout>
```

✅

Figure out how to create post preview with all blocks for only particular collections

✅

toggle issue, if i toggle parent, the arrow for all child blocks toggles too (the content does not show, but it does toggle)

✅

HTML Injection?

⬜

Figure out later how to scope these scripts or load these into iframe instead maybe?

**Should these scripts be at a component level or the whole blogpost level?**

**Me:**

```
<script>
	import mermaid from "mermaid";
	mermaid.initialize({ startOnLoad: true, theme: "neutral" });
</script>

<script>
  document.querySelectorAll("button.copy").forEach((button) => {
  button.addEventListener("click", (ev) => {
    const svg = button.querySelector('.svg-icon path');
    const code = ev.currentTarget.getAttribute("data-code");
    navigator.clipboard.writeText(code);
    if (svg) {
      svg.setAttribute("fill", "limegreen");
      setTimeout(() => {
        svg.setAttribute("fill", "white");
      }, 1000);
    }
  });
});
</script>
```

**ChatGPT:**

Deciding whether to place scripts at the component level or at the whole blog post level in an Astro project depends on the specific use case and context of the scripts. Here are some guidelines to help you make this decision for the two scripts you've mentioned:

1.  **Mermaid Initialization Script:**
    -   If the Mermaid charts are used only within a specific component and not throughout the entire blog post, it makes sense to keep the Mermaid initialization script at the component level. This way, the script will only run where it's needed, which can improve performance and maintainability.
    -   If Mermaid charts are a common feature across various parts of your blog post or site, you might consider placing this script at a higher level (like the layout or page level) so that it's available throughout the blog post or across multiple pages.
2.  **Button Copy Script:**
    -   If the copy functionality for code blocks is specific to a component (like a code snippet component), then it's appropriate to have this script at the component level. This approach encapsulates the functionality within the component, making it reusable and easy to manage.
    -   However, if you have a similar copy functionality required in multiple components across the blog post, you might want to centralize this script at the blog post level to avoid redundancy and keep the code DRY (Don't Repeat Yourself).

Here are some additional considerations:

-   **Performance and Load Times:** Keeping scripts at the component level ensures they're only loaded and run when the component is used, which can be beneficial for performance.
-   **Reusability:** If you foresee the need to use these features (like Mermaid charts or copy buttons) in multiple components across different parts of your site, extracting the scripts to a higher level and making them globally available might be more efficient.
-   **Maintainability:** Keeping scripts close to where they are used enhances maintainability, as it's easier to understand and modify the code when everything related to a particular feature is in one place.

Ultimately, the decision depends on the specific requirements of your project and how you plan to scale or maintain it in the future.

![](https://nerdymomocat.github.io/_astro/drafts_blue.GbNPm2Ep_Z1EYdWV.svg)

If you have multiple `<AlertButton />` components on a page, Astro will not run the script multiple times. Scripts are bundled and only included once per page. Using `querySelectorAll` ensures that this script attaches the event listener to every button with the `alert` class found on the page. [GRRRR (link here)](https://docs.astro.build/en/guides/client-side-scripts/#handle-onclick-and-other-events)

✅

Make sure all onclick are either inside the element or have selector all.

✅

Move tweets to static tweet-astro

**Nov 25, 2023, 12:13 AM**

✅

fix site properties !MAJOR

In the original otoyo’s repo, those things are in `src/layouts/Layout.astro`

Can’t use astro global in typescript 😠

`getSite` is repeated in `blog-helpers.ts` and `astro.config.ts` at the moment.

Somehow could not run getDatabase in `site.config.ts`; so I created a file of my own: `siteInfoSetup`

THE WHOLE FUDGING ISSUE WAS IN `DATE.TS` NOT IN CONFIG FILE

Now it is not running at all?

So, there is a circular dependency, `getNavLink` was calling `pathJoin` in `index`, which was trying to import `siteInfo.ts`. Awesome

**temp code holder**

```
import { getDatabase } from "@/lib/notion/client";
import { getNavLink, getSite } from "@/lib/blog-helpers";
import { SiteInfo } from "@/types";

export async function siteInfoFunc(): Promise<SiteInfo> {
  const tl = "", ds = "", path = "/", oim = "";
  const database = await getDatabase();

  const rawTitle = database.Title.includes("||") ? database.Title.split(" || ")[0] : database.Title;
  const siteAuthor = database.Title.includes("||") ? database.Title.split(" || ")[1] : "";
  const siteTitle = tl ? `${tl} - ${rawTitle}` : rawTitle;
  const siteDescription = ds ? ds : database.Description;
  const siteURL = new URL(getNavLink(path), getSite()).toString();

  // console.log(siteTitle, siteDescription, siteURL);


  // export const siteInfo = () => {
  //   return [siteTitle, siteDescription, siteURL];
  // }

  const siteInfoF: SiteInfo = {
    title: siteTitle,
    description: siteDescription,
    author: siteAuthor,
    lang: "en",
    homePageSlug: "index",
    // Include view-transitions: https://docs.astro.build/en/guides/view-transitions/
    includeViewTransitions: false,
    // Meta property, found in src/components/BaseHead.astro L:42
    ogLocale: "en",
    // Date.prototype.toLocaleDateString() parameters, found in src/utils/date.ts.
    date: {
      locale: "en",
      options: {
        day: "numeric",
        month: "short",
        year: "numeric",
      },
    },
    webmentions: {
      link: "https://webmention.io/astro-cactus.chriswilliams.dev/webmention",
      //  // site: "https://astro-cactus.chriswilliams.dev/",
    },
  };
  return siteInfoF;
}
```

**dump site.config.ts original**

```
import type { SiteConfig } from "@/types";


export const siteConfig: SiteConfig = {
  // Used as both a meta property (src/components/BaseHead.astro L:31 + L:49) & the generated satori png (src/pages/og-image/[slug].png.ts)
  //todo: get this from notion maybe? and we do not need locale.
  author: "Chris Williams",
  // Meta property used to construct the meta title property, found in src/components/BaseHead.astro L:11
  title: "Astro Theme Cactus",
  // Meta property used as the default description meta property
  description: "An opinionated starter theme for Astro",
  // HTML lang property, found in src/layouts/Base.astro L:18
  lang: "en-GB",
  // Meta property, found in src/components/BaseHead.astro L:42
  ogLocale: "en_GB",
  // Date.prototype.toLocaleDateString() parameters, found in src/utils/date.ts.
  date: {
    locale: "en-GB",
    options: {
      day: "numeric",
      month: "short",
      year: "numeric",
    },
  },
  homePageSlug: "index",
  // Include view-transitions: https://docs.astro.build/en/guides/view-transitions/
  includeViewTransitions: false,
  webmentions: {
    link: "https://webmention.io/astro-cactus.chriswilliams.dev/webmention",
  },
};
```

So, now after 6 hours, we are at a point, where build works but dev does not and I do not know why.

Cannot read properties of undefined (reading 'date') It has something to do with date. I am not sure what.

**Error dump**

```
npm run dev
astro-notion-cms-cactus@3.5.0 dev
astro dev
🚀  astro  v3.4.0 started in 491ms

┃ Local    http://localhost:4321/
┃ Network  use --host to expose

(node:44831) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
8:57:33 PM [vite] Error when evaluating SSR module /src/utils/date.ts:
|- TypeError: Cannot read properties of undefined (reading 'date')
at eval (/Users/nerdymomocat/Documents/Websites/astro-notion-cms-copy-to-cactus-theme/src/utils/date.ts:5:75)
at async instantiateModule (file:///Users/nerdymomocat/Documents/Websites/astro-notion-cms-copy-to-cactus-theme/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:56052:9)

8:57:33 PM [vite] Error when evaluating SSR module /src/utils/index.ts: failed to import "/src/utils/date.ts"
|- TypeError: Cannot read properties of undefined (reading 'date')
at eval (/Users/nerdymomocat/Documents/Websites/astro-notion-cms-copy-to-cactus-theme/src/utils/date.ts:5:75)
at async instantiateModule (file:///Users/nerdymomocat/Documents/Websites/astro-notion-cms-copy-to-cactus-theme/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:56052:9)

8:57:33 PM [vite] Error when evaluating SSR module /src/lib/notion/client.ts: failed to import "/src/utils/index.ts"
|- TypeError: Cannot read properties of undefined (reading 'date')
at eval (/Users/nerdymomocat/Documents/Websites/astro-notion-cms-copy-to-cactus-theme/src/utils/date.ts:5:75)
at async instantiateModule (file:///Users/nerdymomocat/Documents/Websites/astro-notion-cms-copy-to-cactus-theme/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:56052:9)

8:57:33 PM [vite] Error when evaluating SSR module /src/site.config.ts: failed to import "/src/lib/notion/client.ts"
|- TypeError: Cannot read properties of undefined (reading 'date')
at eval (/Users/nerdymomocat/Documents/Websites/astro-notion-cms-copy-to-cactus-theme/src/utils/date.ts:5:75)
at async instantiateModule (file:///Users/nerdymomocat/Documents/Websites/astro-notion-cms-copy-to-cactus-theme/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:56052:9)

8:57:33 PM [vite] Error when evaluating SSR module /src/components/BaseHead.astro: failed to import "/src/site.config.ts"
|- TypeError: Cannot read properties of undefined (reading 'date')
at eval (/Users/nerdymomocat/Documents/Websites/astro-notion-cms-copy-to-cactus-theme/src/utils/date.ts:5:75)
at async instantiateModule (file:///Users/nerdymomocat/Documents/Websites/astro-notion-cms-copy-to-cactus-theme/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:56052:9)

8:57:33 PM [vite] Error when evaluating SSR module /src/layouts/Base.astro: failed to import "/src/components/BaseHead.astro"
|- TypeError: Cannot read properties of undefined (reading 'date')
at eval (/Users/nerdymomocat/Documents/Websites/astro-notion-cms-copy-to-cactus-theme/src/utils/date.ts:5:75)
at async instantiateModule (file:///Users/nerdymomocat/Documents/Websites/astro-notion-cms-copy-to-cactus-theme/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:56052:9)

8:57:33 PM [vite] Error when evaluating SSR module /Users/nerdymomocat/Documents/Websites/astro-notion-cms-copy-to-cactus-theme/src/pages/[...page].astro: failed to import "/src/layouts/Base.astro"
|- TypeError: Cannot read properties of undefined (reading 'date')
at eval (/Users/nerdymomocat/Documents/Websites/astro-notion-cms-copy-to-cactus-theme/src/utils/date.ts:5:75)
at async instantiateModule (file:///Users/nerdymomocat/Documents/Websites/astro-notion-cms-copy-to-cactus-theme/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:56052:9)

error   Cannot read properties of undefined (reading 'date')
File:
/Users/nerdymomocat/Documents/Websites/astro-notion-cms-copy-to-cactus-theme/src/utils/date.ts:9:53
Code:
> 9 | const dateFormat = new Intl.DateTimeFormat(siteInfo.date.locale, siteInfo.date.options);
|                                                     ^
11 | export function getFormattedDate(
12 |   date: string | number | Date,
Stacktrace:
TypeError: Cannot read properties of undefined (reading 'date')
at /Users/nerdymomocat/Documents/Websites/astro-notion-cms-copy-to-cactus-theme/src/utils/date.ts:9:53
at async instantiateModule (file:///Users/nerdymomocat/Documents/Websites/astro-notion-cms-copy-to-cactus-theme/node_modules/vite/dist/node/chunks/dep-bb8a8339.js:56052:9)
```

  

[https://docs.astro.build/en/guides/data-fetching/#:~:text=💡 Take advantage of top-level await inside of your Astro component script](https://docs.astro.build/en/guides/data-fetching/#:~:text=%F0%9F%92%A1%20Take%20advantage%20of%20top%2Dlevel%20await%20inside%20of%20your%20Astro%20component%20script).

[![https://docs.astro.build/en/guides/configuring-astro/#environment-variables](https://nerdymomocat.github.io/_astro/Untitled.DiN1LiWk_2k8dVs.webp)](https://nerdymomocat.github.io/_astro/Untitled.DiN1LiWk_2k8dVs.webp)

[https://docs.astro.build/en/guides/configuring-astro/#environment-variables](https://docs.astro.build/en/guides/configuring-astro/#environment-variables)

So basically, astro config can only be static in dev mode at least, in build mode I can use github process variables. But I need database information? So, I do not really know what to do about it?

Like the getSite we wrote technically doesn’t really work?

```
export const CUSTOM_DOMAIN =
  import.meta.env.CUSTOM_DOMAIN || process.env.CUSTOM_DOMAIN || 'alpacat.com' // <- Set your costom domain if you have. e.g. alpacat.com
export const BASE_PATH =
  import.meta.env.BASE_PATH || process.env.BASE_PATH || '' // <- Set sub directory path if you want. e.g. /docs/
```

```
import { CUSTOM_DOMAIN, BASE_PATH } from "./src/constants";

const getSite = function () {
  if (CUSTOM_DOMAIN) {
    return new URL(BASE_PATH, `https://${CUSTOM_DOMAIN}`).toString();
  }

  if (process.env.VERCEL && process.env.VERCEL_URL) {
    return new URL(BASE_PATH, `https://${process.env.VERCEL_URL}`).toString();
  }

  if (process.env.CF_PAGES) {
    if (process.env.CF_PAGES_BRANCH !== 'main') {
      return new URL(BASE_PATH, process.env.CF_PAGES_URL).toString();
    }

    return new URL(
      BASE_PATH,
      `https://${new URL(process.env.CF_PAGES_URL).host
        .split('.')
        .slice(1)
        .join('.')}`
    ).toString();
  }

  return new URL(BASE_PATH, 'http://localhost:4321').toString();
};
```

So, right now it works with me using `export key=value` on CLI

**Nov 25, 2023, 08:26 AM**

⬜

Do not show pages with slug starting with underscore in

✅

collection index page,

✅

posts page,

✅

tag page,

✅

rss,

⬜

sitemap

⬜

fix robots.txt somehow?

✅

check audio embeds

✅

So, I've got this mobile button that opens and closes a navigation bar when I click on it. But here's the thing - when the menu is open, I want it to close if I click anywhere outside of it.

⬜

giscus

⬜

how do i use webmentions?

⬜

og image creation using satori

[I don’t get how astro satori works 🤷‍♀️](https://github.com/cijiugechu/astro-satori/blob/main/packages/core/package.json)

[

Generate OpenGraph Images with Asto and Satori - Jafar Aziz

OpenGraph images are used to enhance the appearance and shareability of website content, and this tutorial we will generate static OpenGraph png Images with Asto.js and Satori

![title](https://www.google.com/s2/favicons?domain=www.jafaraziz.com)

https://www.jafaraziz.com/blog/generate-open-graph-images-with-astro-and-satori/

![title](https://jafaraziz.com/open-graph/blog/generate-open-graph-images-with-astro-and-satori.png)

](https://www.jafaraziz.com/blog/generate-open-graph-images-with-astro-and-satori/)

Bookmark for [https://www.jafaraziz.com/blog/generate-open-graph-images-with-astro-and-satori/](https://www.jafaraziz.com/blog/generate-open-graph-images-with-astro-and-satori/)

Stuff like [this](https://github.com/tomaskebrle/astro-og-image) is more complicated than the existing one. So, I am gonna try to figure out the existing one!

[

GitHub - CyberKoalaStudios/og-image-generator: Integration to generate static Open Graph images, at build time

Integration to generate static Open Graph images, at build time - CyberKoalaStudios/og-image-generator

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/CyberKoalaStudios/og-image-generator

![title](https://repository-images.githubusercontent.com/648207063/f277ace9-3e24-46a5-b59a-21ed69266d58)

](https://github.com/CyberKoalaStudios/og-image-generator)

Bookmark for [https://github.com/CyberKoalaStudios/og-image-generator](https://github.com/CyberKoalaStudios/og-image-generator)

✅

JSON options rather than everything in env? Only Notion values in env!

⬜

Look at the cache stuff in otoyo’s post. Maybe it is just for cloudfare? Idk!

✅

Change getMenu to be: instead of excluding pages with no rank or rank 0, just set ranks of those to 99. remember that homePageSlug if available will always have the top rank.

✅

Fix post preview for empty desc

⬜

THEME AND FONTS!

✅

[Notion API does not say](https://developers.notion.com/reference/block#keys) that block type can be `audio` , add audio support man

**This is duplicated across 5 files, maybe fix it later?**

```
const fileAtacchedBlocks = extractTargetBlocks("image", blocks)
	.concat(extractTargetBlocks("video", blocks))
	.concat(extractTargetBlocks("file", blocks))
	.filter((block) => {
		if (!block) {
			return false;
		}
		const imageOrVideoOrAudioOrFile = block.NImage || block.File || block.Video || block.Audio;
		return (
			imageOrVideoOrAudioOrFile &&
			imageOrVideoOrAudioOrFile.File &&
			imageOrVideoOrAudioOrFile.File.Url
		);
	});

// Download files
await Promise.all(
	fileAtacchedBlocks
		.map(async (block) => {
			const expiryTime = (block.NImage || block.File || block.Video || block.Audio).File.ExpiryTime;
			if (Date.parse(expiryTime) > Date.now()) {
				return Promise.resolve(block);
			}
			return getBlock(block.Id);
		})
		.map((promise) =>
			promise.then((block) => {
				let url!: URL;
				try {
					url = new URL((block.NImage || block.File || block.Video || block.Audio).File.Url);
				} catch (err) {
					console.log("Invalid file URL");
					return Promise.reject();
				}
				return Promise.resolve(url);
			}),
		)
		.map((promise) => promise.then(downloadFile)),
);
```

⬜

Side note, apparently if it is a video embed but not youtube, it is not rendered? Maybe call the generic embed in that case?

**Code here**

```
videoUrl &&
			(url ? (
				isYouTubeURL(url) ? (
					<iframe
          class="w-full h-[340px] max-w-sm:h-[220px]"
						src={`https://www.youtube.com/embed/${parseYouTubeVideoId(url)}`}
						title="YouTube video player"
						allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
						allowfullscreen
					/>
				) : null
```

**Nov 25, 2023, 07:39 PM**

✅

Issue with toggle 2 headings? (FIXED! I had not changed the polyline for some reason!)

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.Db3si40B_27P3kL.webp)](https://nerdymomocat.github.io/_astro/Untitled.Db3si40B_27P3kL.webp)

⬜

Do not show pages with slug starting with underscore in

✅

collection index page,

✅

posts page,

✅

tag page,

✅

rss,

⬜

sitemap

⬜

fix robots.txt somehow?

⬜

giscus

⬜

how do i use webmentions?

⬜

links to main pages and pages as URL seems to be broken?

✅

OG image with satori

**og image creation using satori**

[I don’t get how astro satori works 🤷‍♀️](https://github.com/cijiugechu/astro-satori/blob/main/packages/core/package.json)

[

Generate OpenGraph Images with Asto and Satori - Jafar Aziz

OpenGraph images are used to enhance the appearance and shareability of website content, and this tutorial we will generate static OpenGraph png Images with Asto.js and Satori

![title](https://www.google.com/s2/favicons?domain=www.jafaraziz.com)

https://www.jafaraziz.com/blog/generate-open-graph-images-with-astro-and-satori/

![title](https://jafaraziz.com/open-graph/blog/generate-open-graph-images-with-astro-and-satori.png)

](https://www.jafaraziz.com/blog/generate-open-graph-images-with-astro-and-satori/)

Bookmark for [https://www.jafaraziz.com/blog/generate-open-graph-images-with-astro-and-satori/](https://www.jafaraziz.com/blog/generate-open-graph-images-with-astro-and-satori/)

Stuff like [this](https://github.com/tomaskebrle/astro-og-image) is more complicated than the existing one. So, I am gonna try to figure out the existing one!

[

GitHub - CyberKoalaStudios/og-image-generator: Integration to generate static Open Graph images, at build time

Integration to generate static Open Graph images, at build time - CyberKoalaStudios/og-image-generator

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/CyberKoalaStudios/og-image-generator

![title](https://repository-images.githubusercontent.com/648207063/f277ace9-3e24-46a5-b59a-21ed69266d58)

](https://github.com/CyberKoalaStudios/og-image-generator)

Bookmark for [https://github.com/CyberKoalaStudios/og-image-generator](https://github.com/CyberKoalaStudios/og-image-generator)

Need to change this function to have all of them:

```
export const getStaticPaths: GetStaticPaths = async () => {
  const posts = await getAllPosts();
  return posts.filter((post) => !post.FeaturedImage).map(({ Slug }) => ({ params: { slug: Slug } }));
};
```

**Made huge progress on how to include image on side**

```
<div
    style={{
      height: "100%",
      width: "100%",
      display: "flex",
      backgroundColor: "white",

      backgroundSize: "100px 100px",
      fontFamily: "JetBrainsMono-Bold",
    }}
  >
    <div
      style={{
        padding: "20px",
        display: "flex",
        width: "100%",
        height: "100%",
        justifyContent: "center",
        alignItem: "stretch",
      }}
    >

      <div
        style={{
          display: "flex",
          flexDirection: "row",
          justifyContent: "space-between",
          border: "1px solid #374151",
          boxShadow: "5px 5px 0px #374151",
          width: "100%",
          height: "100%",
          padding: "10px",
        }}
      >
      <div style={{display:"flex", flex: 1}}>
              <img src="https://nerdymomocat.github.io/images/me.png" width={400} height={400} style={{
      width: '100%', 
      height: '100%', 
      objectFit: 'contain', 
      objectPosition: 'center'
    }} />

      </div>
      <div style={{display:"flex", flexDirection:"column", flex: 1,}}>
        <div
          style={{
            fontSize: "32px",
            fontWeight: "900",
            lineHeight: "3rem",
            padding: "10px 0 50px 0",
            color: "#374151",
            flex: 1,
            display: "flex",
            fontFamily: "PlusJakartaSans",
          }}
        >
          Demo New Page: Trying to get Astro+Notion working how I want to
        </div>
        <div
          style={{
            fontSize: "16px",
            fontWeight: "900",
            color: "#374151",
            display: "flex",
            flexDirection: "row",
            justifyContent: "space-between",
            alignItems: "center",
          }}
        >
          <div>
            DATE HERE
          </div>
          <div style={{ display: "flex", alignItems: "center" }}>
            <span style={{ marginRight: "16px" }}>Jafar Aziz</span>
          </div>
        </div>
        </div>
      </div>
    </div>
  </div>
```

**Next up, image as background blur or maybe a div on bottom?**

✅

contain does not work, i need to set specific size for it to fit by ratio? →no assume it is a square, if not a square, it goes into background mode!

**This works with HTML native, but not with svg or svg→png**

```
<div
  style={{
    position: 'relative',
    height: "100%",
    width: "100%",
    display: "flex",
    fontFamily: "JetBrainsMono-Bold",
  }}
>
  <div
    style={{
      position: 'absolute',
      top: 0,
      left: 0,
      height: '100%',
      width: '100%',
      backgroundImage: `url('https://nerdymomocat.github.io/images/me.png')`,
      backgroundRepeat: 'no-repeat',
      backgroundSize: 'contain',
      backgroundPosition: 'center',
      filter: 'blur(8px)',
      zIndex: -1, // Ensures this div is behind the content
      backgroundColor: "white"
    }}
  ></div>
    <div
      style={{
        padding: "20px",
        display: "flex",
        width: "100%",
        height: "100%",
        justifyContent: "center",
        alignItem: "stretch",
      }}
    >

      <div
        style={{
          display: "flex",
          flexDirection: "row",
          justifyContent: "space-between",
          border: "1px solid #374151",
          boxShadow: "5px 5px 0px #374151",
          width: "100%",
          height: "100%",
          padding: "10px",
        }}
      >

      <div style={{display:"flex", flexDirection:"column", flex: 1,}}>
        <div
          style={{
            fontSize: "32px",
            fontWeight: "900",
            lineHeight: "3rem",
            padding: "10px 0 50px 0",
            color: "#374151",
            flex: 1,
            display: "flex",
            fontFamily: "PlusJakartaSans",
          }}
        >
          Demo New Page: Trying to get Astro+Notion working how I want to
        </div>
        <div
          style={{
            fontSize: "16px",
            fontWeight: "900",
            color: "#374151",
            display: "flex",
            flexDirection: "row",
            justifyContent: "space-between",
            alignItems: "center",
          }}
        >
          <div>
            DATE HERE
          </div>
          <div style={{ display: "flex", alignItems: "center" }}>
            <span style={{ marginRight: "16px" }}>Jafar Aziz</span>
          </div>
        </div>
        </div>
      </div>
    </div>
  </div>
```

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.BW346ri4_160Uer.webp)](https://nerdymomocat.github.io/_astro/Untitled.BW346ri4_160Uer.webp)

Also, [There is no](https://github.com/vercel/satori#css) [`z-index`](https://github.com/vercel/satori#css) [support in SVG. Elements that come later in the document will be painted on top.](https://github.com/vercel/satori#css)

```
await satori(
  <img src="data:image/png;base64,..." width={200} height={300} />,
  // Or src={arrayBuffer}, src={buffer}
  options
)
```

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.CvMZ4_Gi_1UCM7K.webp)](https://nerdymomocat.github.io/_astro/Untitled.CvMZ4_Gi_1UCM7K.webp)

**Masking with white image or white linear translucent layer somehow does not work?**

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.C0rYzPNt_7ndDb.webp)](https://nerdymomocat.github.io/_astro/Untitled.C0rYzPNt_7ndDb.webp)

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.Cq-Yz2Hx_14UScC.webp)](https://nerdymomocat.github.io/_astro/Untitled.Cq-Yz2Hx_14UScC.webp)

**Somehow this started working, we do not question it, we roll with it!**

```
<div
  style={{
    height: '100%',
    width: '100%',
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center',
    fontSize: 32,
    fontWeight: 600,
    backgroundColor: 'white'
  }}
>
<div
    style={{
      position: 'absolute', // Absolutely positioned
      top: 0,
      left: 0,
      height: '100%',
      width: '100%',
      background: 'url(https://maggieappleton.com/images/posts/squish-structure/sms2.62.jpeg)',
      backgroundRepeat: "no-repeat",
      maskImage: 'linear-gradient(to bottom, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0.3))',
      backgroundColor: 'white'
    }}
  ></div>

<div
      style={{
        padding: "20px",
        display: "flex",
        width: "100%",
        height: "100%",
        justifyContent: "center",
        alignItem: "stretch",
      }}
    >

      <div
        style={{
          display: "flex",
          flexDirection: "row",
          justifyContent: "space-between",
          border: "1px solid #374151",
          boxShadow: "5px 5px 0px #374151",
          width: "100%",
          height: "100%",
          padding: "10px",
        }}
      >

      <div style={{display:"flex", flexDirection:"column", flex: 1,}}>
        <div
          style={{
            fontSize: "px",
            fontWeight: "900",
            lineHeight: "3rem",
            padding: "10px 0 50px 0",
            color: "#374151",
            flex: 1,
            display: "flex",
            fontFamily: "PlusJakartaSans",
          }}
        >
          Demo New Page: Trying to get Astro+Notion working how I want to
        </div>
        <div
          style={{
            fontSize: "16px",
            fontWeight: "900",
            color: "#374151",
            display: "flex",
            flexDirection: "row",
            justifyContent: "space-between",
            alignItems: "center",
          }}
        >
          <div>
            DATE HERE
          </div>
          <div style={{ display: "flex", alignItems: "center" }}>
            <span style={{ marginRight: "16px" }}>Nerdy Momo Cat</span>
          </div>
        </div>
        </div>
      </div>
    </div>
</div>
```

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.BpFeX_ts_28hYWd.webp)](https://nerdymomocat.github.io/_astro/Untitled.BpFeX_ts_28hYWd.webp)

**And this too! (but it looks weird with colored background images)**

```
<div
  style={{
    height: '100%',
    width: '100%',
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center',
    fontSize: 32,
    fontWeight: 600,
    backgroundColor: 'white'
  }}
>
   <img src="https://maggieappleton.com/images/posts/squish-structure/sms2.66.jpeg" style={{position: 'absolute', // Absolutely positioned
      top: 0,
      left: 0,
      height: '100%',
      width: '100%',
      objectFit: 'contain', 
      objectPosition: 'center',
      maskImage: 'linear-gradient(to bottom, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.2))'
    }} />

<div
      style={{
        padding: "20px",
        display: "flex",
        width: "100%",
        height: "100%",
        justifyContent: "center",
        alignItem: "stretch",
      }}
    >

      <div
        style={{
          display: "flex",
          flexDirection: "row",
          justifyContent: "space-between",
          border: "1px solid #374151",
          boxShadow: "5px 5px 0px #374151",
          width: "100%",
          height: "100%",
          padding: "10px",
        }}
      >

      <div style={{display:"flex", flexDirection:"column", flex: 1,}}>
        <div
          style={{
            fontSize: "px",
            fontWeight: "900",
            lineHeight: "3rem",
            padding: "10px 0 50px 0",
            color: "#374151",
            flex: 1,
            display: "flex",
            fontFamily: "PlusJakartaSans",
          }}
        >
          Demo New Page: Trying to get Astro+Notion working how I want to
        </div>
        <div
          style={{
            fontSize: "16px",
            fontWeight: "900",
            color: "#374151",
            display: "flex",
            flexDirection: "row",
            justifyContent: "space-between",
            alignItems: "center",
          }}
        >
          <div>
            DATE HERE
          </div>
          <div style={{ display: "flex", alignItems: "center" }}>
            <span style={{ marginRight: "16px" }}>Nerdy Momo Cat</span>
          </div>
        </div>
        </div>
      </div>
    </div>
</div>
```

**I like this, 3 part linear gradient**

```
<div
  style={{
    height: '100%',
    width: '100%',
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center',
    fontSize: 32,
    fontWeight: 600,
    backgroundColor: 'white'
  }}
>
   <img src="https://maggieappleton.com/images/posts/squish-structure/sms2.66.jpeg" style={{position: 'absolute', // Absolutely positioned
      top: 0,
      left: 0,
      height: '100%',
      width: '100%',
maskImage: 'linear-gradient(to bottom, rgba(255, 255, 255, 0.15) 25%, rgba(255, 255, 255, 0.15) 25%, rgba(255, 255, 255, 0.3) 90%, rgba(255, 255, 255, 0.1) 90%)',
objectFit: 'cover'
    }} />

<div
      style={{
        padding: "20px",
        display: "flex",
        width: "100%",
        height: "100%",
        justifyContent: "center",
        alignItem: "stretch",
      }}
    >

      <div
        style={{
          display: "flex",
          flexDirection: "row",
          justifyContent: "space-between",
          border: "1px solid #374151",
          boxShadow: "5px 5px 0px #374151",
          width: "100%",
          height: "100%",
          padding: "10px",
        }}
      >

      <div style={{display:"flex", flexDirection:"column", flex: 1,}}>
        <div
          style={{
            fontSize: "px",
            fontWeight: "900",
            lineHeight: "3rem",
            padding: "10px 0 50px 0",
            color: "#374151",
            flex: 1,
            display: "flex",
            fontFamily: "PlusJakartaSans",
          }}
        >
          Demo New Page: Trying to get Astro+Notion working how I want to
        </div>
        <div
          style={{
            fontSize: "16px",
            fontWeight: "900",
            color: "#374151",
            display: "flex",
            flexDirection: "row",
            justifyContent: "space-between",
            alignItems: "center",
          }}
        >
          <div>
            DATE HERE
          </div>
          <div style={{ display: "flex", alignItems: "center" }}>
            <span style={{ marginRight: "16px" }}>Nerdy Momo Cat</span>
          </div>
        </div>
        </div>
      </div>
    </div>
</div>
```

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.CJMNUqNa_Z1X2c6m.webp)](https://nerdymomocat.github.io/_astro/Untitled.CJMNUqNa_Z1X2c6m.webp)

📝 Using sharp instead of resvg because that is what the blogpost above uses and I ain’t figuring it out myself

Converting to tailwind satori format because uh, that is what cactus uses and I do not have enough brain or brainpower to figure it out.

[Lol! satori does not support obejctFit in tailwind 🤣](https://og-playground.vercel.app/?share=zZXBbqMwEIZfxfKhvYRCUkoBhd3T7hPsMYfaZgCnYCM8hkZR3n0NSUkb9ZRKVS62x575Pfr8S95ToXOgKV3nsic4ZBvaQc1Q9kCKGt6mwRO6JhKhMZ7BDlBUZPAKW9ekOk4Ib-gNlUshvPR4zcSrnwSk9eIN_bVRhJzVz6LLC8n53ha9ZXQqJOQ4fi0wN7e1BmWx8zjgAKDmYlfYHssYN7q2Y4MaUTdT3y7NGqlKl_Gi-RYEOrEeupeU5BoMURrJoLvXtd-e9WQz5jvFzwg-1m8oMZ1wKRVia1LfZz1D1pmHUmJluTXQCa0QFD4I3fjWf4yeo_gp-W2yOLizGYujKF8mIiwERCEvomcWxCHwx1WxSjiPIA6DVcCe7vosdHf5p-bWvoM0ra9hNvP_DjyFTKpv4ZsUbgLgObpx9xrc1XBp4BG8IYVUcI17R8Vsvz_t_5WYkvvp8J4cDjfwNj9m7gu27_6-ku5s7q_5Tsc3Qvg9Os50QXWLUitD0z0dZI4VTZNVsKAVyLJCmoaRC3LgtqRpwWoDCwqN3sp_u3b8XtwrjJHTKVzrfxoOOU2xs3A4_Ac)

Apparently, no conversion NEEDED. Why are these frameworks so complicated smh?

**Alright, here is every template**

```
const markup_img_bg = (title: string, pubDate: string, img_url: string) =>
  html`<div
  style="height: 100%; width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: 32px; font-weight: 700; background-color: white; font-family: JetBrainsMono;"
>
   <img src="${img_url}"
        style="position: absolute; top: 0; left: 0; height: 100%; width: 100%; mask-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.15) 25%, rgba(255, 255, 255, 0.15) 25%, rgba(255, 255, 255, 0.3) 90%, rgba(255, 255, 255, 0.1) 90%); object-fit: cover;"
   />

  <div
    style="padding: 20px; display: flex; width: 100%; height: 100%; justify-content: center; align-items: stretch;"
  >
    <div
      style="display: flex; flex-direction: row; justify-content: space-between; border: 1px solid #374151; box-shadow: 5px 5px 0px #374151; width: 100%; height: 100%; padding: 10px;"
    >
      <div style="display: flex; flex-direction: column; flex: 1;">
        <div
          style="font-size: px; font-weight: 700; line-height: 3rem; padding: 10px 0 50px 0; color: #374151; flex: 1; display: flex;"
        >
          ${title}
        </div>
        <div
          style="font-size: 16px; font-weight: 700; color: #374151; display: flex; flex-direction: row; justify-content: space-between; align-items: center;"
        >
          <div>
            ${pubDate}
          </div>
          <div style="display: flex; align-items: center;">
            <span style="margin-right: 16px;">${siteInfo.author}</span>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>`;

const markup_img_sq_with_desc = (title: string, pubDate: string, desc: string, img_url: string) =>
  html`<div
  style="height: 100%; width: 100%; display: flex; background-color: white; font-family: JetBrainsMono-Bold;"
>
  <div
      style="padding: 20px; display: flex; width: 100%; height: 100%; justify-content: center; align-items: stretch;"
  >
      <div
          style="display: flex; flex-direction: row; justify-content: space-between; border: 1px solid #374151; box-shadow: 5px 5px 0px #374151; width: 100%; height: 100%; padding: 10px;"
      >
          <div style="display: flex; flex: 1;">
              <img src="${img_url}"
                   style="width: 100%; height: 100%; object-fit: contain; object-position: center;" />
          </div>
          <div style="display: flex; flex-direction: column; flex: 1;">
              <div
                  style="font-size: 32px; font-weight: 700; line-height: 3rem; padding: 10px 0 50px 0; color: #374151; flex: 0.3; display: flex; font-family: monospace;"
              >
              ${title}
              </div>
              <div
                  style="font-size: 24px; font-weight: 700; line-height: 3rem; padding: 10px 0 50px 0; color: #374151; flex: 1; display: flex; font-family: monospace;"
              >
              ${desc}
              </div>
              <div
                  style="font-size: 16px; font-weight: 700; color: #374151; display: flex; flex-direction: row; justify-content: space-between; align-items: center;"
              >
                  <div>
                      ${pubDate}
                  </div>
                  <div style="display: flex; align-items: center;">
                      <span style="margin-right: 16px;">${siteInfo.author}</span>
                  </div>
              </div>
          </div>
      </div>
  </div>
</div>`;

const markup_img_sq_without_desc = (title: string, pubDate: string, img_url: string) =>
  html`<div
  style="height: 100%; width: 100%; display: flex; background-color: white; font-family: JetBrainsMono-Bold;"
>
  <div
      style="padding: 20px; display: flex; width: 100%; height: 100%; justify-content: center; align-items: stretch;"
  >
      <div
          style="display: flex; flex-direction: row; justify-content: space-between; border: 1px solid #374151; box-shadow: 5px 5px 0px #374151; width: 100%; height: 100%; padding: 10px;"
      >
          <div style="display: flex; flex: 1;">
              <img src="${img_url}"
                   style="width: 100%; height: 100%; object-fit: contain; object-position: center;" />
          </div>
          <div style="display: flex; flex-direction: column; flex: 1;">
              <div
                  style="font-size: 32px; font-weight: 700; line-height: 3rem; padding: 10px 0 50px 0; color: #374151; flex: 1; display: flex; font-family: monospace;"
              >
              ${title}
              </div>
              <div
                  style="font-size: 16px; font-weight: 700; color: #374151; display: flex; flex-direction: row; justify-content: space-between; align-items: center;"
              >
                  <div>
                      ${pubDate}
                  </div>
                  <div style="display: flex; align-items: center;">
                      <span style="margin-right: 16px;">${siteInfo.author}</span>
                  </div>
              </div>
          </div>
      </div>
  </div>
</div>`;

const markup_img_none_with_desc = (title: string, pubDate: string, desc: string) =>
  html`<div style="height: 100%; width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: 32px; font-weight: 700; background-color: white; background-image: radial-gradient(circle at 25px 25px, lightgray 2%, transparent 0%), radial-gradient(circle at 75px 75px, lightgray 2%, transparent 0%); background-size: 100px 100px; font-family: JetBrainsMono-Bold;">

  <div style="padding: 20px; display: flex; width: 100%; height: 100%; justify-content: center; align-items: stretch;">

      <div style="display: flex; flex-direction: row; justify-content: space-between; border: 1px solid #374151; box-shadow: 5px 5px 0px #374151; width: 100%; height: 100%; padding: 10px;">

          <div style="display: flex; flex-direction: column; flex: 1;">
              <div style="font-size: 52px; font-weight: 700; line-height: 4rem; padding: 10px 50px; color: #374151; flex: 0.4; display: flex;">
                  ${title}
              </div>
              <div style="font-size: 30px; font-weight: 700; line-height: 4rem; padding: 10px 50px; color: #374151; flex: 1; display: flex;">
                  ${desc}
              </div>
              <div style="font-size: 24px; font-weight: 700; color: #374151; display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding: 10px;">
                  <div>
                      ${pubDate}
                  </div>
                  <div style="display: flex; align-items: center;">
                      <span style="margin-right: 16px;">Nerdy Momo Cat</span>
                  </div>
              </div>
          </div>
      </div>
  </div>
</div>
`;

const markup_img_none_without_desc = (title: string, pubDate: string) =>
  html`<div style="height: 100%; width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: 32px; font-weight: 700; background-color: white; background-image: radial-gradient(circle at 25px 25px, lightgray 2%, transparent 0%), radial-gradient(circle at 75px 75px, lightgray 2%, transparent 0%); background-size: 100px 100px; font-family: JetBrainsMono-Bold;">

  <div style="padding: 20px; display: flex; width: 100%; height: 100%; justify-content: center; align-items: stretch;">

      <div style="display: flex; flex-direction: row; justify-content: space-between; border: 1px solid #374151; box-shadow: 5px 5px 0px #374151; width: 100%; height: 100%; padding: 10px;">

          <div style="display: flex; flex-direction: column; flex: 1;">
              <div style="font-size: 52px; font-weight: 700; line-height: 4rem; padding: 10px 50px; color: #374151; flex: 1; display: flex;">
                  ${title}
              </div>
              <div style="font-size: 24px; font-weight: 700; color: #374151; display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding: 10px;">
                  <div>
                      ${pubDate}
                  </div>
                  <div style="display: flex; align-items: center;">
                      <span style="margin-right: 16px;">Nerdy Momo Cat</span>
                  </div>
              </div>
          </div>
      </div>
  </div>
</div>
`;
```

I need to fix:

✅

create different kinds of og images based on featured image

✅

og images for non-post pages

**Processing dump**

1.  The only place meta property is set is in `BaseHead.astro` `<meta` `_property_``="og:image"` `_content_``={socialImageURL} />`
    
    There is an indication in `Layout.astro`, but remember that is just copied from otoyo’s blog for reference, and not actually used
    
    Now we have 2 things:
    
    `_const_` `{ title, description, ogImage, articleDate } = Astro.props;`
    
    and
    
    `_const_` `socialImageURL =` `_new_` `URL(ogImage ? ogImage : "/social-card.png", Astro.url).href;`
    
2.  The only place `BaseHead` is imported is `Base.Astro` by `_import_` `BaseHead` `_from_` `"@/components/BaseHead";`
    
    ```
    const {
    	meta: { title = siteInfo.title, description = siteInfo.description, ogImage, articleDate },
    } = Astro.props;
    ```
    
    `<BaseHead` `_title_``={title}` `_description_``={description}` `_ogImage_``={ogImage}` `_articleDate_``={articleDate} />`
    
3.  Finally `Base` is imported at multiple places
    
    ✅
    
    `BlogPost.Astro`
    
    ```
    const { post, ogImage, headings } = Astro.props;
    const socialImage = ogImage ?? `/og-image/${post.Slug}.png`;
    <BaseLayout
    	meta={{ title: post.Title, description: post.Excerpt, articleDate, ogImage: socialImage }}
    >
    ```
    
    ✅
    
    Now, in `src/pages/posts/[slug].astro`
    
    ```
    import PostLayout from "@/layouts/BlogPost.astro";
    
    export async function getStaticPaths() {
    	const posts = await getAllPosts();
    
    	return posts.map((post) => ({
    		params: { slug: post.Slug },
    		props: { post },
    	}));
    }
    interface Props {
    	post: Post;
    }
    const { post } = Astro.props;
    
    if (!post) {
    	throw new Error("Post not found. slug: ${slug}");
    }
    
    let ogImage;
    if (post.FeaturedImage) {
    	ogImage = new URL(filePath(new URL(post.FeaturedImage.Url)), Astro.site).toString();
    }
    
    <PostLayout post={post} ogImage={ogImage} headings={headings}>
    ```
    
    It is not possible to know size of the image at build time, so I cannot do the square, non-square thing. I am just gonna put it as an option!
    
    ✅
    
    `[..page].astro` in `src/pages`
    
    ```
    export async function getStaticPaths() {
    	const pages = await getPages();
    
    	return pages.map((page) => ({
    		params: { page: page.Slug === siteInfo.homePageSlug ? undefined : `/${page.Slug}` },
    		props: { page },
    	}));
    }
    
    interface Props {
    	page: Post;
    }
    
    const { page: slug } = Astro.params;
    const { page } = Astro.props;
    <PageLayout meta={{ title: page.Title || siteInfo.title }}>
    ```
    
    ⬜
    
    `404.astro` in `src/pages`
    
    ```
    const meta = {
    	title: "Oops! You found a missing page!",
    	description: "Oops! It looks like this page is lost in space!",
    };
    ---
    
    <PageLayout meta={meta}>
    ```
    
    ✅
    
    `[..page].astro` in `src/pages/posts`
    
    ```
    const meta = {
    	title: "Posts",
    	description: "Ready my collection of posts and the things that interest me",
    };
    
    <PageLayout meta={meta}>
    ```
    
    ✅
    
    `[..page].astro` in `src/pages/posts/collection/[collection]`
    
    ```
    const meta = {
    	title: `${collection}`,
    	description: "Read my collection of posts.",
    };
    
    <PageLayout meta={meta}>
    ```
    
    ✅
    
    `index.astro` in `src/pages/tags`
    
    ```
    const meta = {
    	title: "All Tags",
    	description: "A list of all the topics I've written about in my posts",
    };
    ---
    
    <PageLayout meta={meta}>
    ```
    
    ✅
    
    `[..page].astro` in `src/pages/tags/[tag]/[...page].astro`
    
    ```
    const meta = {
    	title: `Tag: ${tag}`,
    	description: `View all posts with the tag - ${tag}`,
    };
    
    <PageLayout meta={meta}>
    ```
    

**Where are these download files?**

```
export async function downloadFile(url: URL) {
  let res!: AxiosResponse;
  try {
    res = await axios({
      method: "get",
      url: url.toString(),
      timeout: REQUEST_TIMEOUT_MS,
      responseType: "stream",
    });
  } catch (err) {
    console.log(err);
    return Promise.resolve();
  }

  if (!res || res.status != 200) {
    console.log(res);
    return Promise.resolve();
  }

  const BASE_DIR = "./public/notion/";
  if (!fs.existsSync(BASE_DIR)) {
    fs.mkdirSync(BASE_DIR);
  }

  const dir = BASE_DIR + url.pathname.split("/").slice(-2)[0];
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir);
  }

  const filename = decodeURIComponent(url.pathname.split("/").slice(-1)[0]);
  const filepath = `${dir}/${filename}`;

  const writeStream = fs.createWriteStream(filepath);
  const rotate = sharp().rotate();

  let stream = res.data;

  if (res.headers["content-type"] === "image/jpeg") {
    stream = stream.pipe(rotate);
  }
  stream.pipe(new ExifTransformer()).pipe(writeStream);
}
```

Lol! We are back to the issue of image not being generated, and satori needs absolute path, and base64 just refuses to work with larger images?

Thought — we are eventually generating a png anyway. If the data has not expired — we can maybe just use that. **Expiry duration seems to be an hour**

`.webp` does not work for this, but that is okay. just use `png` or `jpeg`.

Example of `_new_` `URL(filePath(``_new_` `URL(post.FeaturedImage.Url)), Astro.site).toString();` and `post.FeaturedImage.Url`

**Dumped code of using sharp and buffer etc in case I need it later**

```
var dimensions = sizeOf('public/notion/90518bf5-b2a3-49ce-af8d-5fa59d9e11f4/ai-forest2x.webp');
  console.log(new URL(post.FeaturedImage.Url)).toString();
  // console.log(dimensions.width, dimensions.height);

  const buffimg = await sharp("public/notion/90518bf5-b2a3-49ce-af8d-5fa59d9e11f4/ai-forest2x.webp").toBuffer();
  // console.log(`data:image/png;base64,${buffimg.toString('base64')}`);
  // console.log(metadata);
  // let chosen_markup;
  // let chosen_markup = markup_img_none_without_desc(title, postDate);
  let chosen_markup = markup_img_sq_without_desc(title, postDate, "data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==");
  console.log(chosen_markup);
```

```
http://localhost:4321/notion/d0d55339-5e7e-4aae-8fea-af6b97142bf9/1548870028.png
https://prod-files-secure.s3.us-west-2.amazonaws.com/6083354a-c9d6-441d-886e-3fa82c5b48de/d0d55339-5e7e-4aae-8fea-af6b97142bf9/1548870028.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45HZZMZUHI%2F20231126%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20231126T024537Z&X-Amz-Expires=3600&X-Amz-Signature=60184f099b79dfa496ebc2d27c6cf58ffea13e8f98e0c28352e60c06a98346eb&X-Amz-SignedHeaders=host&x-id=GetObject
```

So image URLs here have & which satori very nicely (NOT!) escapes leading to invalid url for image. I just…cannot stop laughing. It has been 10 hours.

Nov 26, 2023, 06:31 AM : got it to work for all posts. It might fail if it does not understand width/height, but I think that is okay?

Lol, I added this for my sanity. GPT is awesome for such things

```
let svg;
  try {
    svg = await satori(chosen_markup, ogOptions);
  } catch (error) {
    console.error("Error in satori:", error);
    // Fallback to a basic markup if satori fails
    const fallback_markup = post?.Excerpt ? obj_img_none_with_desc(title, postDate, post?.Excerpt) : obj_img_none_without_desc(title, postDate);
    svg = await satori(fallback_markup, ogOptions);
  }
```

  

⬜

Look at the cache stuff in otoyo’s post. Maybe it is just for cloudfare? Idk!

⬜

THEME AND FONTS!

⬜

Logo to database icon!

⬜

Set favicon!

⬜

Side note, apparently if it is a video embed but not youtube, it is not rendered? Maybe call the generic embed in that case?

**Code here**

```
videoUrl &&
			(url ? (
				isYouTubeURL(url) ? (
					<iframe
          class="w-full h-[340px] max-w-sm:h-[220px]"
						src={`https://www.youtube.com/embed/${parseYouTubeVideoId(url)}`}
						title="YouTube video player"
						allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
						allowfullscreen
					/>
				) : null
```

✅

Caption is not rendering links because of this, need to shift it to a rich text output. Caption cannot have children, I think! Yes, they cannot!

```
{
	richTexts.length > 0 && richTexts[0].Text.Content && (
		<div class="caption pt-1 text-sm">{richTexts[0].Text.Content}</div>
	)
}
```

✅

Heading toggle clashes with the # sign, what is a good idea here? maybe especially change margin for those??

**Nov 26, 2023**

✅

Do not show pages with slug starting with underscore in

✅

collection index page,

✅

posts page,

✅

tag page,

✅

rss,

✅

sitemap

✅

links to main pages and pages as URL seems to be broken?

✅

Logo to database icon!

✅

Side note, apparently if it is a video embed but not youtube, it is not rendered? Maybe call the generic embed in that case?

**Code here**

```
videoUrl &&
			(url ? (
				isYouTubeURL(url) ? (
					<iframe
          class="w-full h-[340px] max-w-sm:h-[220px]"
						src={`https://www.youtube.com/embed/${parseYouTubeVideoId(url)}`}
						title="YouTube video player"
						allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
						allowfullscreen
					/>
				) : null
```

✅

Why the fudge does it keep scrolling to tiktok man?

[

GitHub - justinribeiro/lite-tiktok: A web component that lazy loads TikTok embeds. Currently experimental and a work in progress.

A web component that lazy loads TikTok embeds. Currently experimental and a work in progress. - justinribeiro/lite-tiktok

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/justinribeiro/lite-tiktok

![title](https://opengraph.githubassets.com/9bfc4a5fa79f79a9f3af6545547a8a7304d54f315fbc0958719c94e4eedcacb4/justinribeiro/lite-tiktok)

](https://github.com/justinribeiro/lite-tiktok)

Bookmark for [https://github.com/justinribeiro/lite-tiktok](https://github.com/justinribeiro/lite-tiktok)

**I am having scroll issues with it and can’t set muted and sometimes a scrollbar shows up in iframe. I just used muted on blockquote**

```
<lite-tiktok videoid={videoId} autoload></lite-tiktok>;

<script>
	//Copied from https://github.com/justinribeiro/lite-tiktok because I could not get import to work for some stupid reason
	/**
	 *
	 * A web component that lazy loads TikTok embeds. Currently experimental and a work in progress.
	 *
	 * A less interesting variation of lite-youtube (https://github.com/justinribeiro/lite-youtube)
	 *
	 */
	export class LiteTTEmbed extends HTMLElement {
		shadowRoot!: ShadowRoot;
		private domRefFrame!: HTMLDivElement;
		private domRefImg!: {
			fallback: HTMLImageElement;
			webp: HTMLSourceElement;
			jpeg: HTMLSourceElement;
		};
		private domRefPlayButton!: HTMLButtonElement;
		private static isPreconnected = false;
		private isIframeLoaded = false;

		constructor() {
			super();
			this.setupDom();
		}

		static get observedAttributes(): string[] {
			return ["videoid", "playlistid"];
		}

		connectedCallback(): void {
			this.addEventListener("pointerover", LiteTTEmbed.warmConnections, {
				once: true,
			});

			this.addEventListener("click", () => this.addIframe());
		}

		get videoId(): string {
			return encodeURIComponent(this.getAttribute("videoid") || "");
		}

		set videoId(id: string) {
			this.setAttribute("videoid", id);
		}

		get autoLoad(): boolean {
			return this.hasAttribute("autoload");
		}

		set __data(obj: any) {
			this.__data = obj;
		}

		get __data(): any {
			return this.__data;
		}

		/**
		 * Define our shadowDOM for the component
		 */
		private setupDom(): void {
			const shadowDom = this.attachShadow({ mode: "open" });
			shadowDom.innerHTML = `
      <style>
        :host {
          contain: content;
          display: block;
          position: relative;
          width: 100%;
          height: 735px;
        }

        #frame, iframe {
          position: absolute;
          width: 100%;
          height: 100%;
          left: 0;
        }

        #frame {
          cursor: pointer;
        }

        #fake {
          display: flex;
          position: relative;
          height: 100%;
          justify-content: center;
          background: #000;
          width: 80%;
          margin: auto;
        }

        #logo {
          position: absolute;
          width: 48px;
          height: 48px;
          margin: 1rem;
          right: 0;
        }

        #fallbackPlaceholder {
          height: 100%;
          position: fixed;
          top: 50%;
          left: 50%;
          transform: translate(-50%, -50%);
        }

        #playButton {
          width: 150px;
          height: 150px;
          background-color: transparent;
          z-index: 1;
          border: 0;
        }

        #playButton:before {
          content: '';
          border-style: solid;
          border-width: 22px 0 22px 40px;
          border-color: transparent transparent transparent #fff;
        }

        #playButton,
        #playButton:before {
          position: absolute;
          top: 50%;
          left: 50%;
          transform: translate3d(-50%, -50%, 0);
          cursor: inherit;
        }

        /* Post-click styles */
        .activated {
          cursor: unset;
        }

        #frame.activated::before,
        #frame.activated > #fake {
          display: none;
        }
      </style>
      <div id="frame">
        <div id="fake">
          <picture>
            <source id="jpegPlaceholder" type="image/jpeg">
            <img id="fallbackPlaceholder" referrerpolicy="origin" loading="lazy">
          </picture>
          <button id="playButton"></button>
          <svg id="logo" xmlns="http://www.w3.org/2000/svg"><path d="M19.8 19.8v-1.6L18 18a12.1 12.1 0 0 0-7 22 12 12 0 0 1 8.6-20.3Z" fill="#25F4EE"/><path d="M20 37.4c3 0 5.5-2.4 5.6-5.3V5.7h4.8L30.3 4h-6.6v26.4a5.5 5.5 0 0 1-8.1 4.7c1 1.4 2.6 2.3 4.4 2.3Zm19.4-22.7v-1.5c-1.8 0-3.5-.6-5-1.5a9 9 0 0 0 5 3Z" fill="#25F4EE"/><path d="M34.4 11.7a9.1 9.1 0 0 1-2.2-6h-1.8c.4 2.5 2 4.6 4 6ZM18.1 24.6c-3 0-5.6 2.5-5.6 5.6 0 2.1 1.3 4 3 4.9a5.5 5.5 0 0 1 6.2-8.5v-6.7l-1.7-.2h-.2v5.1l-1.7-.2Z" fill="#FE2C55"/><path d="M39.4 14.6v5.1c-3.4 0-6.6-1.1-9.2-3v13.5a12 12 0 0 1-19 10 12.1 12.1 0 0 0 21-8.3V18.5a16 16 0 0 0 9.1 3v-6.7c-.6 0-1.3 0-1.9-.2Z" fill="#FE2C55"/><path d="M30.3 30.2V16.8a15 15 0 0 0 9.1 3v-5.2c-2-.4-3.7-1.4-5-3a9.2 9.2 0 0 1-4-6h-4.8v26.5a5.5 5.5 0 0 1-10 3 5.5 5.5 0 0 1 4.2-10.2v-5.2a12.1 12.1 0 0 0-8.6 20.4 12 12 0 0 0 19-9.9Z" fill="#fff"/></svg>
        </div>
      </div>
    `;
			this.domRefFrame = shadowDom.querySelector<HTMLDivElement>("#frame")!;
			this.domRefImg = {
				fallback: shadowDom.querySelector("#fallbackPlaceholder")!,
				webp: shadowDom.querySelector("#webpPlaceholder")!,
				jpeg: shadowDom.querySelector("#jpegPlaceholder")!,
			};
			this.domRefPlayButton = shadowDom.querySelector("#playButton")!;
		}

		/**
		 * Parse our attributes and fire up some placeholders
		 */
		private async setupComponent(): Promise<void> {
			// Don't take the hit if the autoload is set because this is all just too
			// expensive given the base embeds weight
			if (!this.autoLoad) {
				const request = await fetch(
					`https://www.tiktok.com/oembed?url=https://www.tiktok.com/video/${this.videoId}`,
				);
				const data = await request.json();

				this.initImagePlaceholder(data);

				this.domRefPlayButton.setAttribute("aria-label", `Play: ${data.title}`);
				this.setAttribute("title", `Play: ${data.title}`);
			} else {
				this.initIntersectionObserver();
			}
		}

		/**
		 * Lifecycle method that we use to listen for attribute changes to period
		 * @param {*} name
		 * @param {*} oldVal
		 * @param {*} newVal
		 */
		attributeChangedCallback(name: string, oldVal: unknown, newVal: unknown): void {
			switch (name) {
				case "videoid": {
					if (oldVal !== newVal) {
						this.setupComponent();

						// if we have a previous iframe, remove it and the activated class
						if (this.domRefFrame.classList.contains("activated")) {
							this.domRefFrame.classList.remove("activated");
							this.shadowRoot.querySelector("iframe")!.remove();
							this.isIframeLoaded = false;
						}
					}
					break;
				}
				default:
					break;
			}
		}

		/**
		 * Inject the iframe into the component body
		 * @param {boolean} isIntersectionObserver
		 */
		private addIframe(isIntersectionObserver = false): void {
			if (!this.isIframeLoaded) {
				const iframeHTML = `
<iframe frameborder="0"
  sandbox="allow-popups allow-popups-to-escape-sandbox allow-scripts allow-top-navigation allow-same-origin"
  src="https://www.tiktok.com/embed/v2/${this.videoId}">
</iframe>
`;
				this.domRefFrame.insertAdjacentHTML("beforeend", iframeHTML);
				this.domRefFrame.classList.add("activated");
				this.isIframeLoaded = true;
			}
		}

		/**
		 * Setup the placeholder image for the component
		 */
		private initImagePlaceholder(data: any): void {
			this.domRefImg.jpeg.srcset = data.thumbnail_url;
			this.domRefImg.fallback.src = data.thumbnail_url;
			this.domRefImg.fallback.setAttribute("aria-label", `Play: ${data.title}`);
			this.domRefImg?.fallback?.setAttribute("alt", `Play: ${data.title}`);
		}

		/**
		 * Setup the Intersection Observer to load the iframe when scrolled into view
		 */
		private initIntersectionObserver(): void {
			const options = {
				root: null,
				rootMargin: "0px",
				threshold: 0,
			};

			const observer = new IntersectionObserver((entries, observer) => {
				entries.forEach((entry) => {
					if (entry.isIntersecting && !this.isIframeLoaded) {
						LiteTTEmbed.warmConnections();
						this.addIframe(true);
						observer.unobserve(this);
					}
				});
			}, options);

			observer.observe(this);
		}

		/**
		 * Add a <link rel={preload | preconnect} ...> to the head
		 * @param {string} kind
		 * @param {string} url
		 * @param {string} as
		 */
		private static addPrefetch(kind: string, url: string): void {
			const linkElem = document.createElement("link");
			linkElem.rel = kind;
			linkElem.href = url;
			linkElem.crossOrigin = "true";
			document.head.append(linkElem);
		}

		/**
		 * Begin preconnecting to warm up the iframe load Since the embed's network
		 * requests load within its iframe, preload/prefetch'ing them outside the
		 * iframe will only cause double-downloads. So, the best we can do is warm up
		 * a few connections to origins that are in the critical path.
		 *
		 * Maybe `<link rel=preload as=document>` would work, but it's unsupported:
		 * http://crbug.com/593267 But TBH, I don't think it'll happen soon with Site
		 * Isolation and split caches adding serious complexity.
		 */
		private static warmConnections(): void {
			if (LiteTTEmbed.isPreconnected) return;

			LiteTTEmbed.addPrefetch("preconnect", "https://www.tiktok.com");
			LiteTTEmbed.addPrefetch("preconnect", "https://mcs.us.tiktok.com");
			LiteTTEmbed.addPrefetch("preconnect", "https://mon.us.tiktokv.com");

			LiteTTEmbed.isPreconnected = true;
		}
	}
	// Register custom element
	customElements.define("lite-tiktok", LiteTTEmbed);
</script>
```

**Nov 26, 2023, 10:42 PM**

✅

fix robots.txt somehow?

Using this

[

![title](https://www.google.com/s2/favicons?domain=www.npmjs.com)

https://www.npmjs.com/package/astro-robots-txt





](https://www.npmjs.com/package/astro-robots-txt)

Bookmark for [Just a moment...](https://www.npmjs.com/package/astro-robots-txt)

**Can change policy too if ever needed**

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.BvktNwAa_1paA1K.webp)](https://nerdymomocat.github.io/_astro/Untitled.BvktNwAa_1paA1K.webp)

⬜

how do i use webmentions? #LATER

✅

Look at the cache stuff in otoyo’s post. Maybe it is just for cloudfare? Idk! #LATER

✅

Set favicon!

✅

Why does otoyo add everything to public and not to src? Maybe adding to src will help with optimization?

![](https://nerdymomocat.github.io/_astro/info-alternate_gray.xE4Ugu2z_Z1EYdWV.svg)

This is gonna be difficult because while Image has bundling things, everything else does not? and because everything else does not, it needs to be referenced using public anyway. So, I can technically do this where I route downloaded images, videos, audio and files to different places and import using that, but that is too much work and I ain’t doing that!

**Trying to optimize just images**

**Original**

```
export async function downloadFile(url: URL) {
  let res!: AxiosResponse;
  try {
    res = await axios({
      method: "get",
      url: url.toString(),
      timeout: REQUEST_TIMEOUT_MS,
      responseType: "stream",
    });
  } catch (err) {
    console.log(err);
    return Promise.resolve();
  }

  if (!res || res.status != 200) {
    console.log(res);
    return Promise.resolve();
  }

  const BASE_DIR = "./public/notion/";
  // const BASE_DIR = "./src/notion-assets/";
  if (!fs.existsSync(BASE_DIR)) {
    fs.mkdirSync(BASE_DIR);
  }

  const dir = BASE_DIR + url.pathname.split("/").slice(-2)[0];
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir);
  }

  const filename = decodeURIComponent(url.pathname.split("/").slice(-1)[0]);
  const filepath = `${dir}/${filename}`;

  const writeStream = fs.createWriteStream(filepath);
  const rotate = sharp().rotate();

  let stream = res.data;

  if (res.headers["content-type"] === "image/jpeg") {
    stream = stream.pipe(rotate);
  }
  stream.pipe(new ExifTransformer()).pipe(writeStream);
}
```

**New**

```
export async function downloadFile(url: URL, optimize_img = true) {
  let res!: AxiosResponse;
  try {
    res = await axios({
      method: "get",
      url: url.toString(),
      timeout: REQUEST_TIMEOUT_MS,
      responseType: "stream",
    });
  } catch (err) {
    console.log(err);
    return Promise.resolve();
  }

  if (!res || res.status != 200) {
    console.log(res);
    return Promise.resolve();
  }

  const BASE_DIR = "./public/notion/";
  if (!fs.existsSync(BASE_DIR)) {
    fs.mkdirSync(BASE_DIR);
  }

  const dir = BASE_DIR + url.pathname.split("/").slice(-2)[0];
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir);
  }

  const filename = decodeURIComponent(url.pathname.split("/").slice(-1)[0]);
  const filepath = `${dir}/${filename}`;

  let stream = res.data;
  if (res.headers["content-type"] === "image/jpeg") {
    stream = stream.pipe(sharp().rotate());
  }

  const isImage = res.headers["content-type"]?.startsWith("image/");
  if (isImage && optimize_img) {
    // Process and write only the optimized WebP image
    // const webpPath = `${dir}/${filename.split('.')[0]}.webp`;
    const webpPath = `${dir}/${filename.substring(0, filename.lastIndexOf('.'))}.webp`
    stream.pipe(sharp()
      // .resize({ width: 1024 }) // Adjust the size as needed for "medium"
      .webp({ quality: 80 })) // Adjust quality as needed
      .toFile(webpPath)
      .catch(err => {
        console.error('Error processing image:', err);
      });
  } else {
    // Original behavior for non-image files or when not optimizing
    const writeStream = fs.createWriteStream(filepath);
    stream.pipe(new ExifTransformer()).pipe(writeStream);
  }
}
```

✅

Headings as nested children do not show up in ToC

**Process**

So, blocks show up as

```
{
    Id: 'f042d61d-6289-49b0-9276-945047b8561f',
    Type: 'column_list',
    HasChildren: true,
    ColumnList: { Columns: [Array] }
  },
{
    Id: '44a84a9a-60e6-443d-8434-e5a9d43ff06a',
    Type: 'numbered_list_item',
    HasChildren: true,
    NumberedListItem: { RichTexts: [Array], Color: 'default', Children: [Array] }
  },
```

so, I will need to flatten these I guess?

**I can do something like this but I am not sure I want it. Plan cancelled (even Notion does not do it!)**

```
export function buildHeadings(blocks: Block[]): Heading[] {
  // Helper function to flatten blocks recursively
  function flattenBlocks(blocks: Block[]): Block[] {
    return blocks.reduce((acc, block) => {
      acc.push(block);

      // Handle column_list blocks
      if (block.Type === 'column_list' && block.ColumnList) {
        block.ColumnList.Columns.forEach(column => {
          if (column.HasChildren && column.Children) {
            acc.push(...flattenBlocks(column.Children));
          }
        });
      }
      // Recursively flatten other blocks except table_row
      else if (block.Type !== 'table_row' && block.HasChildren && (block.Paragraph || block.Heading1 || block.Heading2 || block.Heading3 || block.BulletedListItem || block.NumberedListItem || block.ToDo || block.Quote || block.Callout || block.SyncedBlock || block.Toggle).Children) {
        acc.push(...flattenBlocks((block.Paragraph || block.Heading1 || block.Heading2 || block.Heading3 || block.BulletedListItem || block.NumberedListItem || block.ToDo || block.Quote || block.Callout || block.SyncedBlock || block.Toggle).Children));
      }

      return acc;
    }, [] as Block[]);
  }

  // Flatten the block structure
  const allBlocks = flattenBlocks(blocks);

  // Filter for heading blocks
  return allBlocks.filter(block => HEADING_BLOCKS.includes(block.Type)).map(cleanHeading);
}
```

⬜

Links on this page #Medium Term LATER Might need some flattening like this . And see this is a good use of not having all headings show up in ToC because I can use them for linking internally while still not cluttering stuff! Win Win!

⬜

Add any html/md content to posts. I am not sure how this works and I’ll need to figure out a way but that is too much work man.

✅

Only show last updated if it is not same

**THEME AND FONTS AND CSS!!**

✅

Replaced 404 page from [here](https://storyset.com/illustration/404-error-with-a-cute-animal/amico#utm_source=freepik&utm_medium=referall&utm_campaign=storiesdetail&utm_content=edit-button&utm_term=edit)

✅

Colors

**Original tailwind.config.cs for safety purposes**

```
import type { Config } from "tailwindcss";
import { fontFamily } from "tailwindcss/defaultTheme";
import plugin from "tailwindcss/plugin";
export default {
  content: ["./src/**/*.{astro,html,js,jsx,md,svelte,ts,tsx,vue}"],
  darkMode: "class",
  corePlugins: {
    // disable aspect ratio as per docs -> @tailwindcss/aspect-ratio
    aspectRatio: false,
    // disable some core plugins as they are included in the css, even when unused
    touchAction: false,
    ringOffsetWidth: false,
    ringOffsetColor: false,
    scrollSnapType: false,
    borderOpacity: false,
    textOpacity: false,
    fontVariantNumeric: false,
  },
  theme: {
    extend: {
      colors: {
        bgColor: "hsl(var(--theme-bg) / <alpha-value>)",
        textColor: "hsl(var(--theme-text) / <alpha-value>)",
        link: "hsl(var(--theme-link) / <alpha-value>)",
        accent: "hsl(var(--theme-accent) / <alpha-value>)",
        "accent-2": "hsl(var(--theme-accent-2) / <alpha-value>)",
        quote: "hsl(var(--theme-quote) / <alpha-value>)",
      },
      fontFamily: {
        // Add any custom fonts here
        sans: [...fontFamily.sans],
        serif: [...fontFamily.serif],
        mono: [...fontFamily.mono]
      },
      transitionProperty: {
        height: "height",
      },
 eslint-disable-next-line @typescript-eslint/ban-ts-comment
      @ts-ignore
      Remove above once tailwindcss exposes theme type
      typography: (theme) => ({
        cactus: {
          css: {
            "--tw-prose-body": theme("colors.textColor / 1"),
            "--tw-prose-headings": theme("colors.accent-2 / 1"),
            "--tw-prose-links": theme("colors.textColor / 1"),
            "--tw-prose-bold": theme("colors.textColor / 1"),
            "--tw-prose-bullets": theme("colors.textColor / 1"),
            "--tw-prose-quotes": theme("colors.quote / 1"),
            "--tw-prose-code": theme("colors.textColor / 1"),
            "--tw-prose-hr": "0.5px dashed #666",
            "--tw-prose-th-borders": "#666",
          },
        },
        DEFAULT: {
          css: {
            a: {
              "@apply cactus-link no-underline": "",
            },
            strong: {
              fontWeight: "700",
            },
            code: {
              border: "1px dotted #666",
              borderRadius: "2px",
            },
            blockquote: {
              borderLeftWidth: "0",
            },
            hr: {
              borderTopStyle: "dashed",
            },
            thead: {
              borderBottomWidth: "none",
            },
            "thead th": {
              fontWeight: "700",
              borderBottom: "1px dashed #666",
            },
            "tbody tr": {
              borderBottomWidth: "none",
            },
            tfoot: {
              borderTop: "1px dashed #666",
            },
            sup: {
              "@apply ms-0.5": "",
              a: {
                "@apply bg-none": "",
                "&:hover": {
                  "@apply text-link no-underline bg-none": "",
                },
                "&:before": {
                  content: "'['",
                },
                "&:after": {
                  content: "']'",
                },
              },
            },
          },
        },
        sm: {
          css: {
            code: {
              fontSize: theme("fontSize.sm")[0],
              fontWeight: "400",
            },
          },
        },
      }),
    },
  },
  plugins: [
    // require("@tailwindcss/typography"),
    require("@tailwindcss/aspect-ratio"),
    plugin(function ({ addComponents }) {
      addComponents({
        ".cactus-link": {
          "@apply bg-[size:100%_6px] bg-bottom bg-repeat-x": {},
          backgroundImage:
            "linear-gradient(transparent,transparent 5px,hsl(var(--theme-text)) 5px,hsl(var(--theme-text)))",
          "&:hover": {
            backgroundImage:
              "linear-gradient(transparent,transparent 4px,hsl(var(--theme-link)) 4px,hsl(var(--theme-link)))",
          },
        },
        ".title": {
          "@apply text-2xl font-semibold text-accent-2": {},
        },
      });
    }),
  ],
} satisfies Config;
```

**Original global.css for safety purposes**

```
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    color-scheme: light;
    /* https://tailwindcss.com/docs/customizing-colors#using-css-variables */
    --theme-bg: 0deg 0% 98%;
    --theme-link: 172deg 18% 41%;
    --theme-text: 203deg 11% 15%;
    --theme-accent: 351deg 66% 48%;
    --theme-accent-2: 0deg 0% 7%;
    --theme-quote: 351deg 66% 48%;
  }

  :root.dark {
    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%;
  }

  html {
    @apply scroll-smooth;
  }

  html body {
    @apply mx-auto flex min-h-screen max-w-3xl flex-col bg-bgColor px-8 pt-8 text-textColor antialiased overflow-x-hidden;
  }
}
```

**Process**

Focussing here

```
extend: {
      colors: {
        bgColor: "hsl(var(--theme-bg) / <alpha-value>)",
        textColor: "hsl(var(--theme-text) / <alpha-value>)",
        link: "hsl(var(--theme-link) / <alpha-value>)",
        accent: "hsl(var(--theme-accent) / <alpha-value>)",
        "accent-2": "hsl(var(--theme-accent-2) / <alpha-value>)",
        quote: "hsl(var(--theme-quote) / <alpha-value>)",
      },
      fontFamily: {
        // Add any custom fonts here
        sans: [...fontFamily.sans],
        serif: [...fontFamily.serif],
        mono: [...fontFamily.mono]
      },
```

**Why are theses themed this way? I am losing hope for my method**

```
<style is:global>
	:root {
		--pagefind-ui-font: inherit;
	}

	#cactus__search .pagefind-ui__search-clear {
		width: calc(60px * var(--pagefind-ui-scale));
		padding: 0;
		background-color: transparent;
		overflow: hidden;
	}
	#cactus__search .pagefind-ui__search-clear:focus {
		outline: 1px solid theme("colors.accent-2.DEFAULT");
	}
	#cactus__search .pagefind-ui__search-clear::before {
		content: "";
		-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke-width='1.5' stroke='currentColor' %3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M6 18L18 6M6 6l12 12'%3E%3C/path%3E%3C/svg%3E")
			center / 60% no-repeat;
		mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke-width='1.5' stroke='currentColor' %3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M6 18L18 6M6 6l12 12'%3E%3C/path%3E%3C/svg%3E")
			center / 60% no-repeat;
		background-color: theme("colors.accent.DEFAULT");
		display: block;
		width: 100%;
		height: 100%;
	}

	#cactus__search .pagefind-ui__result {
		border: 0;
	}

	#cactus__search .pagefind-ui__result-link {
		background-size: 100% 6px;
		background-position: bottom;
		background-repeat: repeat-x;
		background-image: linear-gradient(
			transparent,
			transparent 5px,
			theme("colors.textColor.DEFAULT") 5px,
			theme("colors.textColor.DEFAULT")
		);
	}

	#cactus__search .pagefind-ui__result-link:hover {
		text-decoration: none;
		background-image: linear-gradient(
			transparent,
			transparent 4px,
			theme("colors.link.DEFAULT") 4px,
			theme("colors.link.DEFAULT")
		);
	}

	#cactus__search mark {
		color: theme("colors.quote.DEFAULT");
		background-color: transparent;
		font-weight: 600;
	}
</style>

<style>
	#cactus__search {
		--pagefind-ui-primary: theme("colors.accent.DEFAULT");
		--pagefind-ui-text: theme("colors.textColor.DEFAULT");
		--pagefind-ui-background: theme("colors.bgColor.DEFAULT");
		--pagefind-ui-border: theme("colors.zinc.400");
		--pagefind-ui-border-width: 1px;
	}
</style>
```

**Especially because, there is this too!**

```
const colorThemeMetaTag = document.querySelector("meta[name='theme-color']");
		document.documentElement.classList.toggle("dark", newTheme === "dark");
		const bgColour = getComputedStyle(document.body).getPropertyValue("--theme-bg");

		colorThemeMetaTag.setAttribute("content", `hsl(${bgColour})`);
		if (typeof localStorage !== "undefined") {
			localStorage.setItem("theme", newTheme);
		}
	}
```

**Saving current mod if I change my mind**

```
colors: {
        bgColor:
        {
          DEFAULT: "hsl(0deg 0% 98% / <alpha-value>)",
          "dark": "hsl(210deg 6% 12% / <alpha-value>)"
        },
        textColor:
        {
          DEFAULT: "hsl(var(--theme-text) / <alpha-value>)",
          "dark": "hsl(var(--theme-text) / <alpha-value>)"
        },
        link:
        {
          DEFAULT: "hsl(var(--theme-link) / <alpha-value>)",
          "dark": "hsl(var(--theme-link) / <alpha-value>)"
        },
        accent:
        {
          DEFAULT: "hsl(var(--theme-accent) / <alpha-value>)",
          "dark": "hsl(var(--theme-accent) / <alpha-value>)"
        },
        "accent-2":
        {
          DEFAULT: "hsl(var(--theme-accent-2) / <alpha-value>)",
          "dark": "hsl(var(--theme-accent-2) / <alpha-value>)"
        },
        "quote":
        {
          DEFAULT: "hsl(var(--theme-quote) / <alpha-value>)",
          "dark": "hsl(var(--theme-quote) / <alpha-value>)"
        }
      },
```

✅

Fonts

**From lillian weng’s blog**

font-family: -apple-system,BlinkMacSystemFont,segoe ui,Roboto,Oxygen,Ubuntu,Cantarell,open sans,helvetica neue,sans-serif;

✅

Header expands completely?

**Process**

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.D6sNhXkY_Z15fMcp.webp)](https://nerdymomocat.github.io/_astro/Untitled.D6sNhXkY_Z15fMcp.webp)

[

GitHub - surjithctly/astro-navbar: Astro-Navbar is a fully responsive headless navigation bar for Astro. It supports mobile responsive toggle and dropdowns.

Astro-Navbar is a fully responsive headless navigation bar for Astro. It supports mobile responsive toggle and dropdowns. - surjithctly/astro-navbar

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/surjithctly/astro-navbar

![title](https://opengraph.githubassets.com/d330a927ba8ac86e6fd50a52319934756a6e5c4d3ad009ec01e4b14b07e8f944/surjithctly/astro-navbar)

](https://github.com/surjithctly/astro-navbar)

Bookmark for [https://github.com/surjithctly/astro-navbar](https://github.com/surjithctly/astro-navbar)

Somehow, [the sticky header part](https://github.com/surjithctly/astro-navbar#sticky-header) is not working, will look later

Is it even adding much benefit then? I do not like the part where it shifts layout rather than popup 😕

**Code if I need again (Search terms, astro, navbar, astro-navbar)**

```
---
import { Dropdown as DropdownContainer, DropdownItems } from "astro-navbar";
const { title, lastItem, children } = Astro.props;
---

<li class="relative">
  <DropdownContainer class="group">
    <button
      class="flex items-center gap-1 w-full lg:w-auto lg:px-3 py-2 text-gray-600 hover:text-gray-900">
      <span>{title}</span>
      <svg
        xmlns="http://www.w3.org/2000/svg"
        fill="none"
        viewBox="0 0 24 24"
        stroke-width="3"
        stroke="currentColor"
        class="w-3 h-3 mt-0.5 group-open:rotate-180">
        <path
          stroke-linecap="round"
          stroke-linejoin="round"
          d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path>
      </svg>
    </button>
    <DropdownItems>
      <div
        class:list={[
          "lg:absolute w-full lg:w-48 z-10",
          lastItem
            ? "lg:right-0 origin-top-right"
            : "lg:left-0 origin-top-left",
        ]}>
        <div
          class="px-3 lg:py-2 lg:bg-white lg:rounded-md lg:shadow lg:border flex flex-col">
          {
            children.map((item) => (
              <a
                href={item.path}
                class="py-1 text-gray-600 hover:text-gray-900">
                {item.title}
              </a>
            ))
          }
        </div>
      </div>
    </DropdownItems>
  </DropdownContainer>
</li>
```

```
---
interface Props {
  href: string;
  size?: "md" | "lg";
  block?: boolean;
  style?: "outline" | "primary" | "inverted" | "muted";
  class?: string;
  [x: string]: any;
}

const {
  href,
  block,
  size = "lg",
  style = "primary",
  class: className,
  ...rest
} = Astro.props;

const sizes = {
  lg: "px-5 py-2.5",
  md: "px-4 py-2",
};

const styles = {
  outline: "bg-white border-2 border-black hover:bg-gray-100 text-black ",
  primary: "bg-black text-white hover:bg-gray-800  border-2 border-transparent",
  inverted: "bg-white text-black   border-2 border-transparent",
  muted: "bg-gray-100 hover:bg-gray-200   border-2 border-transparent",
};
---

<a
  href={href}
  {...rest}
  class:list={[
    "rounded text-center transition focus-visible:ring-2 ring-offset-2 ring-gray-200",
    block && "w-full",
    sizes[size],
    styles[style],
    className,
  ]}
  ><slot />
</a>
```

```
---
import Link from "./link.astro";
import Dropdown from "./dropdown.astro";
import { Astronav, MenuItems, MenuIcon, StickyHeader } from "astro-navbar";

import ThemeToggle from "../ThemeToggle.astro";
import Search from "../Search.astro";
import { getMenu } from "@/utils";
import { siteInfo } from "@/site.config";
import { filePath } from "@/lib/blog-helpers";

const menuitems = await getMenu();

const url = new URL(Astro.request.url);

let customIconURL: string = "";
if (siteInfo.logo && siteInfo.logo.Type === "file") {
	try {
		customIconURL = filePath(new URL(siteInfo.logo.Url));
	} catch (err) {
		console.log("Invalid DB custom icon URL");
	}
}

const menuitemsold = [
	{
		title: "Features",
		path: "#",
		children: [
			{ title: "Action", path: "/" },
			{ title: "Another action", path: "#" },
			{ title: "Dropdown Submenu", path: "#" },
			{ title: "404 Page", path: "/404" },
		],
	},
	{
		title: "Pricing",
		path: "/pricing",
	},
	{
		title: "About",
		path: "/about",
	},
	{
		title: "Blog",
		path: "/blog",
	},
	{
		title: "Contact",
		path: "/contact",
	},
	{
		title: "Pro Version",
		badge: true,
		path: "https://astroship-pro.web3templates.com/",
	},
];
---

<div class="max-w-screen-xl px-5">
	<StickyHeader
		class="sticky top-0 z-20 transition-all"
		scrollY={50}
		defaultClass="py-5 border-transparent"
		activeClass="py-2 backdrop-blur-lg"
	>
		<Astronav closeOnClick>
			<div class="flex w-full items-center justify-between lg:w-auto">
				<a
					href="/"
					class="inline-flex items-center grayscale hover:filter-none sm:relative sm:inline-block"
					aria-current={url.pathname === "/" ? "page" : false}
				>
					{
						siteInfo.logo && siteInfo.logo.Type === "emoji" ? (
							<>
								<span class="sm:h-15 sm:w-15 me-3 h-10 w-10 sm:absolute sm:start-[-4.5rem] sm:me-0">
									{siteInfo.logo.Emoji}
								</span>
								<span class="text-xl font-bold sm:text-2xl">{siteInfo.title}</span>
							</>
						) : siteInfo.logo && siteInfo.logo.Type === "external" ? (
							<>
								<img
									class="sm:h-15 sm:w-15 me-3 h-10 w-10 object-contain sm:absolute sm:start-[-4.5rem] sm:me-0"
									src={siteInfo.logo.Url}
								/>
								<span class="text-xl font-bold sm:text-2xl">{siteInfo.title}</span>
							</>
						) : siteInfo.logo && siteInfo.logo.Type === "file" && customIconURL ? (
							<>
								<img
									src={customIconURL}
									class="sm:h-15 sm:w-15 me-3 h-10 w-10 object-contain sm:start-[-4.5rem] sm:me-0"
								/>
								<span class="text-xl font-bold sm:text-2xl">{siteInfo.title}</span>
							</>
						) : (
							<span class="text-xl font-bold sm:text-2xl">{siteInfo.title}</span>
						)
					}
				</a>
				<div class="block lg:hidden">
					<MenuIcon class="h-4 w-4 text-gray-800" />
				</div>
			</div>
			<MenuItems class="mt-2 hidden w-full lg:mt-0 lg:flex lg:w-auto">
				<ul class="flex flex-col lg:flex-row lg:gap-3">
					{
						menuitems.map((item, index) => (
							<>
								{item.children && (
									<Dropdown
										title={item.title}
										children={item.children}
										lastItem={index === menuitems.length - 1}
									/>
								)}

								{!item.children && (
									<li>
										<a
											href={item.path}
											class="flex items-center py-2 text-gray-600 hover:text-gray-900 lg:px-3"
										>
											<span> {item.title}</span>
											{item.badge && (
												<span class="ml-1 animate-pulse rounded-full bg-indigo-600 px-2 py-0.5 text-[10px] font-semibold uppercase text-white">
													New
												</span>
											)}
										</a>
									</li>
								)}
							</>
						))
					}
				</ul>
				<div class="mt-3 flex items-center gap-4 lg:hidden">
					<Link href="#" style="muted" block size="md">Log in</Link>
					<Link href="#" size="md" block>Sign up</Link>
				</div>
			</MenuItems>
		</Astronav>
		<div>
			<div class="hidden items-center gap-4 lg:flex">
				<a href="#">Log in</a>
				<Link href="#" size="md">Sign up</Link>
			</div>
		</div>
	</StickyHeader>
</div>
```

**I don’t know what I messed up, restart time for CSS, dumping modified header code here (Changed <main id="main" class="mx-auto max-w-prose px-8"> to have no class**

```
---
import ThemeToggle from "../ThemeToggle.astro";
import Search from "../Search.astro";
import { getMenu } from "@/utils";
import { siteInfo } from "@/site.config";
import { filePath } from "@/lib/blog-helpers";

const menuLinks = await getMenu();

const url = new URL(Astro.request.url);

let customIconURL: string = "";
if (siteInfo.logo && siteInfo.logo.Type === "file") {
	try {
		customIconURL = filePath(new URL(siteInfo.logo.Url));
	} catch (err) {
		console.log("Invalid DB custom icon URL");
	}
}
---

<script>
	class MobileNavBtn extends HTMLElement {
		constructor() {
			super();

			const mobileButtonEl = document.getElementById("toggle-navigation-menu") as HTMLButtonElement;
			let menuOpen = false;

			function toggleMobileMenu() {
				const headerEl = document.getElementById("main-header")!;
				headerEl.classList.toggle("menu-open");
				menuOpen = !menuOpen;
				mobileButtonEl.setAttribute("aria-expanded", menuOpen.toString());
			}

			mobileButtonEl.addEventListener("click", () => toggleMobileMenu());

			// document.addEventListener("astro:after-swap", () => {
			// 	if (menuOpen) toggleMobileMenu();
			// });
		}
	}

	customElements.define("mobile-button", MobileNavBtn);

	document.addEventListener("DOMContentLoaded", function () {
		var toggleButton = document.getElementById("toggle-navigation-menu");

		document.addEventListener("click", function (event) {
			var navMenu = document.getElementById("navigation-menu");
			var mainHeader = document.getElementById("main-header");

			// Check if the click is on the toggle button or a descendant of it
			var isClickInsideToggleButton = toggleButton.contains(event.target);

			if (!isClickInsideToggleButton && event.target instanceof Node) {
				var isClickInsideNav = navMenu.contains(event.target);

				if (!isClickInsideNav && mainHeader) {
					// If the click is outside the navMenu and mainHeader exists, remove 'menu-open' attribute
					mainHeader.classList.remove("menu-open");
				}
			}
		});
	});
</script>
<!-- <div class="mx-auto max-w-screen-xl px-5"> -->
<header
	id="main-header"
	class="group relative my-5 mb-16 flex items-center justify-between sm:ps-[4.5rem]"
	transition:animate="none"
>
	<div class="flex w-full items-center justify-between lg:w-auto">
		<a
			href="/"
			class="inline-flex items-center grayscale hover:filter-none sm:relative sm:inline-block"
			aria-current={url.pathname === "/" ? "page" : false}
		>
			{
				siteInfo.logo && siteInfo.logo.Type === "emoji" ? (
					<>
						<span class="sm:h-15 sm:w-15 me-3 h-10 w-10 sm:absolute sm:start-[-4.5rem] sm:me-0">
							{siteInfo.logo.Emoji}
						</span>
						<span class="text-xl font-bold sm:text-2xl">{siteInfo.title}</span>
					</>
				) : siteInfo.logo && siteInfo.logo.Type === "external" ? (
					<>
						<img
							class="sm:h-15 sm:w-15 me-3 h-10 w-10 object-contain sm:absolute sm:start-[-4.5rem] sm:me-0"
							src={siteInfo.logo.Url}
						/>
						<span class="text-xl font-bold sm:text-2xl">{siteInfo.title}</span>
					</>
				) : siteInfo.logo && siteInfo.logo.Type === "file" && customIconURL ? (
					<>
						<img
							src={customIconURL}
							class="sm:h-15 sm:w-15 me-3 h-10 w-10 object-contain sm:absolute sm:start-[-4.5rem] sm:me-0"
						/>
						<span class="text-xl font-bold sm:text-2xl">{siteInfo.title}</span>
					</>
				) : (
					<span class="text-xl font-bold sm:text-2xl">{siteInfo.title}</span>
				)
			}
		</a>
	</div>

	<nav
		id="navigation-menu"
		class="absolute -inset-x-4 top-14 mt-2 flex hidden w-full flex-col items-end rounded-md bg-bgColor/[.97] py-2 text-accent shadow backdrop-blur group-[.menu-open]:z-50 group-[.menu-open]:flex sm:static sm:z-auto sm:-ms-4 sm:mt-1 sm:flex sm:flex-row sm:items-center sm:rounded-none sm:bg-transparent sm:py-0 sm:shadow-none sm:backdrop-blur-none lg:mt-0 lg:flex lg:w-auto lg:flex-row lg:gap-3"
		aria-label="Main menu"
	>
		{
			menuLinks.map((link) => (
				<a
					href={link.path}
					class="flex flex-col px-4 py-4 sm:py-0 sm:hover:underline lg:flex-row lg:gap-3"
					aria-current={url.pathname === link.path ? "page" : false}
					rel="prefetch"
				>
					{link.title}
				</a>
			))
		}
	</nav>
	<div class="items-center gap-4 lg:flex">
		<Search />
		<ThemeToggle />
	</div>
	<mobile-button>
		<button
			id="toggle-navigation-menu"
			class="group relative ms-4 h-7 w-7 sm:invisible sm:hidden"
			type="button"
			aria-label="Open main menu"
			aria-expanded="false"
			aria-haspopup="menu"
		>
			<svg
				id="line-svg"
				class="absolute start-1/2 top-1/2 h-full w-full -translate-x-1/2 -translate-y-1/2 transition-all group-aria-expanded:scale-0 group-aria-expanded:opacity-0"
				aria-hidden="true"
				focusable="false"
				xmlns="http://www.w3.org/2000/svg"
				fill="none"
				viewBox="0 0 24 24"
				stroke-width="1.5"
				stroke="currentColor"
			>
				<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 9h16.5m-16.5 6.75h16.5"
				></path>
			</svg>
			<svg
				id="cross-svg"
				class="absolute start-1/2 top-1/2 h-full w-full -translate-x-1/2 -translate-y-1/2 scale-0 text-accent opacity-0 transition-all group-aria-expanded:scale-100 group-aria-expanded:opacity-100"
				class="text-accent"
				aria-hidden="true"
				focusable="false"
				xmlns="http://www.w3.org/2000/svg"
				fill="none"
				viewBox="0 0 24 24"
				stroke-width="1.5"
				stroke="currentColor"
			>
				<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"></path>
			</svg>
		</button>
	</mobile-button>
</header>
<!-- </div> -->
```

I THINK IT IS THE CODEPEN EMBED CAUSING ISSUES ON LARGE SCREENS WITH ToC! NOT THE CODE 😠

**I do not know what it is**

Discarding some commits

```
import { filePath } from "../../../lib/blog-helpers";
import EmbedGeneric from "./EmbedGeneric.astro";
export interface Props {
	block: interfaces.Block;
@@ -32,7 +33,9 @@ if (block.Video.External) {
						allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
						allowfullscreen
					/>
				) : null
				) : (
					<EmbedGeneric url={url} />
				)
			) : (
```

```
style="max-width: 605px;"
	class="tiktok-embed"
	cite={url.toString()}
	data-video-id={videoId}
	style="max-width: 605px;"
	muted
>
```

```
<iframe
	allowtransparency="true"
	class="max-w-sm:h-[220px] h-[340px] w-full border-none"
	src={url.toString()}
	loading="lazy"></iframe>
```

in index.ts

```
export async function getMenu(): Promise<
  { title: string; path: string; children?: { title: string; path: string }[] }[]
> {
```

The fudge man, this is the issue? I had removed `max-w-prose` from here.

```
<PostLayout post={post} headings={headings}>
	<div class="post-body max-w-prose">
		<NotionBlocks blocks={blocks} isRoot={true} />
	</div>
</PostLayout>
```

Okay, so moving max-w-prose from body to main basically does not work. Great! Wrapping the main into a different div and applying max-w-prose to it also does not work.

✅

Social icons in footer

Astro Icon also supports Iconify out-of-the-box with minimal configuration. To use an icon set from Iconify follow the instructions below:

1.  Find an Icon Set to use on the [Iconify Icon Sets website](https://docs.astro.build/en/guides/integrations-guide/)
2.  Install the package (eg. `npm i -D @iconify-json/mdi`)
3.  Add an entry to the `astro.config.mjs` file `astro.config.mjs`

This does not work. Apparently all icons shown on icones are not in astro-icon 🤷‍♀️

✅

Make footer look nicer

✅

giscus

**Process**

```
giscus: 
    repo: nerdymomocat/nerdymomocat.github.io
    repo-id: R_kgDOJoxbeA
    category: Announcements
    category-id: DIC_kwDOJoxbeM4CWzrO
    mapping: pathname
    loading: lazy
    input-position: top
    reactions-enabled: true
```

✅

table of contents issue (again) — this time the article class is not affected - [i just copied a fix off here. Made the main area width as 125% on larger screens. And then divided it into 4 cols, flex article to 3, toc to last.](https://gotofritz.net/blog/migrating-blog-to-astro/#preventing-the-main-content-area-from-expanding-when-the-content-is-too-wide)

✅

toggle headings click issue

**Done by adding a javascript that adds details to the closest parent**

```
<details class="toggle mb-0 mt-8">
			<summary class="flex cursor-pointer list-none gap-2">
				<div>
					<svg
						class="rotate-svg mt-1 flex-shrink-0 transition-all duration-300"
						fill="none"
						height="20"
						width="20"
						stroke="currentColor"
						stroke-linecap="round"
						stroke-linejoin="round"
						stroke-width="2"
						viewBox="0 0 24 24"
					>
						<polyline points="12 8 18 14 12 20" />
					</svg>
				</div>
				<a
					class="inline"
					href={`#${id}`}
					id={id}
					onclick="var fullUrl = `${window.location.origin}${window.location.pathname}#${this.id}`; navigator.clipboard.writeText(fullUrl); this.closest('details').open = !this.closest('details').open;"
				>
					<h4 class="toggle-h4 mb-4 mt-8 inline text-lg">
						{block.Heading3.RichTexts.map((richText: interfaces.RichText) => (
							<RichText richText={richText} blockID={block.Id} />
						))}
					</h4>
				</a>
			</summary>
			<div class="pl-10">
				{block.Heading3.Children && (
					<NotionBlocks blocks={block.Heading3.Children} headings={headings} />
				)}
			</div>
		</details>
```

✅

And the mobile menu is transparent again?

**Nov 27, 2023, 09:35 PM**

✅

Github workflow

**Suggested github pages astro file**

```
# Sample workflow for building and deploying an Astro site to GitHub Pages
#
# To get started with Astro see: https://docs.astro.build/en/getting-started/
#
name: Deploy Astro site to Pages

on:
  # Runs on pushes targeting the default branch
  push:
    branches: ["main"]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
  contents: read
  pages: write
  id-token: write

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
  group: "pages"
  cancel-in-progress: false

env:
  BUILD_PATH: "." # default value when not using subfolders
  # BUILD_PATH: subfolder

jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Detect package manager
        id: detect-package-manager
        run: |
          if [ -f "${{ github.workspace }}/yarn.lock" ]; then
            echo "manager=yarn" >> $GITHUB_OUTPUT
            echo "command=install" >> $GITHUB_OUTPUT
            echo "runner=yarn" >> $GITHUB_OUTPUT
            exit 0
          elif [ -f "${{ github.workspace }}/package.json" ]; then
            echo "manager=npm" >> $GITHUB_OUTPUT
            echo "command=ci" >> $GITHUB_OUTPUT
            echo "runner=npx --no-install" >> $GITHUB_OUTPUT
            exit 0
          else
            echo "Unable to determine package manager"
            exit 1
          fi
      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: "16"
          cache: ${{ steps.detect-package-manager.outputs.manager }}
          cache-dependency-path: ${{ env.BUILD_PATH }}/package-lock.json
      - name: Setup Pages
        id: pages
        uses: actions/configure-pages@v3
      - name: Install dependencies
        run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }}
        working-directory: ${{ env.BUILD_PATH }}
      - name: Build with Astro
        run: |
          ${{ steps.detect-package-manager.outputs.runner }} astro build \
            --site "${{ steps.pages.outputs.origin }}" \
            --base "${{ steps.pages.outputs.base_path }}"
        working-directory: ${{ env.BUILD_PATH }}
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v2
        with:
          path: ${{ env.BUILD_PATH }}/dist

  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    needs: build
    runs-on: ubuntu-latest
    name: Deploy
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v2
```

**Current file copied from my previous attempts**

```
name: Deploy to GitHub Pages

on:
  # push:
  #   branches: [ main ]
  # schedule:
  #   - cron: '0 */8 * * *'
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

env:
  NOTION_API_SECRET: ${{ secrets.NOTION_API_SECRET }}

jobs:
  build:
    runs-on: ubuntu-latest
    environment: github-pages
    steps:
      - name: Checkout your repository using git
        uses: actions/checkout@v3

      - name: Set and log environment secrets and variables
        run: |
          # echo "DATABASE_ID = $DATABASE_ID"
          echo "NOTION_API_SECRET = $NOTION_API_SECRET"

      - name: Install, build, and upload your site
        uses: withastro/action@v1
        with:
          path: .
          node-version: 20

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: github-pages
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v1
```

**Modification attempt**

```
name: Deploy to GitHub Pages

on:
  # push:
  #   branches: [ main ]
  # schedule:
  #   - cron: '0 */8 * * *'
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: "pages"
  cancel-in-progress: true

env:
  BUILD_PATH: "."
  NOTION_API_SECRET: ${{ secrets.NOTION_API_SECRET }}

jobs:
  build:
    runs-on: ubuntu-latest
    environment: github-pages
    steps:
      - name: Checkout your repository using git
        uses: actions/checkout@v3

      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: 21
          cache: 'npm'
          cache-dependency-path: ${{ env.BUILD_PATH }}/package-lock.json

      - name: Setup Pages
        id: pages
        uses: actions/configure-pages@v3

      - name: Install dependencies
        run: npm ci
        working-directory: ${{ env.BUILD_PATH }}

      - name: Set and log environment secrets and variables
        run: |
          # echo "DATABASE_ID = $DATABASE_ID"
          echo "NOTION_API_SECRET = $NOTION_API_SECRET"
          echo "GITHUB_PAGES = true"
          echo "SITE = ${{ steps.pages.outputs.origin }}"
          echo "BASE = ${{ steps.pages.outputs.base_path }}"

      - name: Build with Astro
        env:
          NOTION_API_SECRET: ${{ secrets.NOTION_API_SECRET }}
          GITHUB_PAGES: true
          SITE: ${{ steps.pages.outputs.origin }}
          BASE: ${{ steps.pages.outputs.base_path }}
        run: |
          echo "NOTION_API_SECRET = $NOTION_API_SECRET"
          echo "GITHUB_PAGES = $GITHUB_PAGES"
          echo "SITE = $SITE"
          echo "BASE = $BASE"
          npx --no-install astro build
          # or if you prefer yarn, use: yarn build
          # --site and --base flags can be included if needed for Astro
          # --site steps.pages.outputs.origin" \
          # --base steps.pages.outputs.base_path"
        working-directory: ${{ env.BUILD_PATH }}

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v2
        with:
          path: ${{ env.BUILD_PATH }}/dist



  deploy:
    needs: build
    runs-on: ubuntu-latest
    name: Deploy
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v2
```

  

✅

Alright, as expected site/base is messed up because it is hardcoded but I should have that fixed by tonight and we are done!

```
//Might need to modify this to also include collection paths
export const getPageLink = (page: number, tag: string) => {
  if (page === 1) {
    return tag ? getTagLink(tag) : path.join(BASE_PATH, "/");
  }
  return tag
    ? path.join(BASE_PATH, `/posts/tag/${encodeURIComponent(tag)}/page/${page.toString()}`)
    : path.join(BASE_PATH, `/posts/page/${page.toString()}`);
};
```

✅

Pagefind does not work?

**Some final bug squashes Nov 27, 2023, 07:00 PM**

💡

Well, you cannot fork your own repo and I want to use that repo as a template, so irritating! Now, searching for a solution.

✅

Now transferring giscs is an issue and someone has had that problem before of course

[

can’t transfer discussion · community · Discussion #3381

When choosing to transfer a discussion, none of our repos is offered, and none match search. We have multiple open source repos, as well as multiple private ones. Can anybody transfer a discussion…

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/orgs/community/discussions/3381

![title](https://opengraph.githubassets.com/8125a1d891890b108a28c6a6c6a6590579b0db027dfbe315a7e84f906179f279/orgs/community/discussions/3381)

](https://github.com/orgs/community/discussions/3381)

Bookmark for [https://github.com/orgs/community/discussions/3381](https://github.com/orgs/community/discussions/3381)

Specifically [here](https://github.com/orgs/community/discussions/3381#discussioncomment-2655724)

✅

Weird two line menu items on half screen, maybe set the font size to text-sm on mid sized screens?

✅

I thought I wanted rank of pages to change but I don’t

**Nov 28, 2023, 11:15 PM**

✅

TF is up with performance?

**Screenshots**

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.CKx8Sf5s_aYgOw.webp)](https://nerdymomocat.github.io/_astro/Untitled.CKx8Sf5s_aYgOw.webp)

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.BzV-TMQi_Z1QEEaC.webp)](https://nerdymomocat.github.io/_astro/Untitled.BzV-TMQi_Z1QEEaC.webp)

ALSO GOOGLE

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.BkJ8nInC_ZWnqB6.webp)](https://nerdymomocat.github.io/_astro/Untitled.BkJ8nInC_ZWnqB6.webp)

✅

Performance

Keywords: #On-Demand Incremental Static Regeneration

Basic steps:

✅

use two files buildtimestart.txt and buildtimeend.txt in the folder .tmp/

✅

know that the last updated time does not pass to parent blocks other than pages; so if a post is updated, you cannot use some blocks and not use some blocks; do not make it complicated

✅

remember notion does not tell you when pages have been deleted, so they will still exist in the cache? OMG? maybe I do retrieve every post in a call but use cached blocks — wtf.

**Points**

Steps:

✅

Add partytown for GA script

✅

Check how many times getAllPosts is being called

✅

Cache getAllPosts results and move filters out

✅

Cache block results but invalidate them if post is new

✅

Cache og-images generated or just generate them in public folder so that they are easier to figure out if they have been cached?

✅

Cache things downloaded from Notion

**Process**

**Nov 29, 2023, 05:48 AM**

✅

change how posts are filtered

✅

change getAllPosts

✅

change getAllPosts references

✅

change getAllPostsAndPages references

Complicated cache:

[

GitHub - actions/cache: Cache dependencies and build outputs in GitHub Actions

Cache dependencies and build outputs in GitHub Actions - actions/cache

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/actions/cache

![title](https://opengraph.githubassets.com/e5377362419f3aaa331f8bf11ae5866960ebdbac7115dc2e5a6bec1fa53874ed/actions/cache)

](https://github.com/actions/cache)

Bookmark for [https://github.com/actions/cache](https://github.com/actions/cache)

**From otoyo**

```
"scripts": {
    "dev": "astro dev",
    "start": "astro dev",
    "build": "astro build",
    "build:cached": "npm run cache:fetch && astro build",
    "preview": "astro preview",
    "astro": "astro",
    "lint": "eslint --ext .js,.ts,.astro src",
    "format": "npx prettier --write .",
    "cache:fetch": "node scripts/blog-contents-cache.cjs",
    "cache:purge": "nx reset && rm -f tmp/*",
    "_fetch-notion-blocks": "node scripts/retrieve-block-children.cjs"
  },
```

**Articles**

[

![title](https://www.google.com/s2/favicons?domain=duffn.dev)

https://duffn.dev/caching-astro-fetch-requests/





](https://duffn.dev/caching-astro-fetch-requests/)

Bookmark for [https://duffn.dev/caching-astro-fetch-requests/](https://duffn.dev/caching-astro-fetch-requests/)

[

Local data caching with Astro - Sanderg.nl

In my last blog post I talked about the new way of routing in Astro v0.19. In it I mentioned that at the moment there is still an issue with the getStaticPaths method, where it calls the function for every page that is created. This means that if you make an API call that then tells Astro to create 100 pages, this API call will be made 100x. If you have a rate-limit of, for example, 1 request per second, you fly right through it. At the time of writing, the Astro team has stated in their Discord that they are testing a fix for this, which will be released very soon, but suppose you want to start with Astro v0.19.0 now, how can you solve this?

![title](https://www.google.com/s2/favicons?domain=sanderg.nl)

https://sanderg.nl/en/posts/astro-local-data-caching/

![title](https://res.cloudinary.com/sandergnl/image/upload/og-images/sanderg.nl_en_posts_astro-local-data-caching.png)

](https://sanderg.nl/en/posts/astro-local-data-caching/)

Bookmark for [https://sanderg.nl/en/posts/astro-local-data-caching/](https://sanderg.nl/en/posts/astro-local-data-caching/)

**Dec 2, 2023, 08:49 AM**

I tried using `<Code>` component from Astro, but it is hard to add styles to it

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.D6Qq-hYr_Z20oBtB.webp)](https://nerdymomocat.github.io/_astro/Untitled.D6Qq-hYr_Z20oBtB.webp)

**For lightbox, doing import "../styles/glightbox.css" adds some error to captions**

I need to use import

I need to use window loaded or something to connect it

Can’t get dynamic jsx based import to work

Landed on CDN for both

And need npm install glightbox

**Dec 3, 2023, 07:51 AM**

**Mainly adding stuff for on this page and linked to this page**

✅

get all on this page

✅

get all to this page

✅

decide on a tooltip library

✅

trying flowbite

**Dec 3, 2023, 11:18 PM**

✅

I got this error once while building but cannot reproduce it: error Cannot read properties of undefined (reading 'title')

```
Orphan heading found: Heading 2.
@notionhq/client warn: request fail {
  code: 'object_not_found',
  message: 'Could not find block with ID: 3cb777e3-4815-41c8-8f30-d790b6ce7685. Make sure the relevant pages and databases are shared with your integration.'
}
Request was aborted
```

✅

look at tailwind aspect ratio plugin that is imported later (might help with google maps and everything there!!

✅

popover

✅

fix current page block popover for links in post body

⬜

add bookmark as popover for external links?

✅

add references to table of contents

✅

FLOWBITE IS SO DARN HEAVY

The general performance is not perfect anyway

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.ClaPrqx6_Z1FR6oj.webp)](https://nerdymomocat.github.io/_astro/Untitled.ClaPrqx6_Z1FR6oj.webp)

**Trying preline**

[![Nah, pretty much the same: first load of preline || Mobile](https://nerdymomocat.github.io/_astro/Untitled.BdYLShBt_URO9P.webp)](https://nerdymomocat.github.io/_astro/Untitled.BdYLShBt_URO9P.webp)

Nah, pretty much the same: first load of preline || Mobile

[![Second load on preline || mobile](https://nerdymomocat.github.io/_astro/Untitled.BALbHxQj_Z2eeNqE.webp)](https://nerdymomocat.github.io/_astro/Untitled.BALbHxQj_Z2eeNqE.webp)

Second load on preline || mobile

[![Second load on flowbite || Mobile](https://nerdymomocat.github.io/_astro/Untitled.CebSNgzN_Z1NxbxI.webp)](https://nerdymomocat.github.io/_astro/Untitled.CebSNgzN_Z1NxbxI.webp)

Second load on flowbite || Mobile

[![Nah, pretty much the same: first load of preline || Desktop](https://nerdymomocat.github.io/_astro/Untitled.Cgs1Ynkj_W0Jj5.webp)](https://nerdymomocat.github.io/_astro/Untitled.Cgs1Ynkj_W0Jj5.webp)

Nah, pretty much the same: first load of preline || Desktop

[![Second load on flowbite || Desktop](https://nerdymomocat.github.io/_astro/Untitled.DXC9_YYx_Z1Tcfbg.webp)](https://nerdymomocat.github.io/_astro/Untitled.DXC9_YYx_Z1Tcfbg.webp)

Second load on flowbite || Desktop

[![Second load on flowbite || Desktop](https://nerdymomocat.github.io/_astro/Untitled.Cf2HFmNB_Z1jXaks.webp)](https://nerdymomocat.github.io/_astro/Untitled.Cf2HFmNB_Z1jXaks.webp)

Second load on flowbite || Desktop

  

**Npage**

```
<span class="text-link no-underline hover:underline hover:decoration-accent-2 hover:underline-offset-4"><button data-popover-target=`popover-description-${id}` data-popover-placement="bottom-end" type="button" id=`popover-click-${id}`>{popoverSpanText?popoverSpanText:<slot/>}<span class="sr-only">Show information for the linked content</span></button></span>
  <div data-popover id=`popover-description-${id}` role="tooltip" class="absolute z-10 invisible inline-block text-sm text-gray-500 transition-opacity duration-300 bg-white border border-gray-200 rounded-lg shadow-sm opacity-0 w-72 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-400">
    <a href={linkedTo}>
      <div class="p-3 space-y-2">
          <h3 class="font-semibold underline decoration-wavy decoration-accent-2 text-accent">{popoverTitle}</h3>
          {excerpt && <p>{excerpt}</p>}
          <a href={linkedTo} class="flex items-center font-medium text-blue-600 dark:text-blue-500 dark:hover:text-blue-600 hover:text-blue-700 hover:underline">{linkText}<svg class="w-2 h-2 ms-1.5 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 6 10">
      <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 9 4-4-4-4"/>
    </svg></a>
      </div>
    </a>
      <div data-popper-arrow></div>
  </div>
```

**NBlock**

```
<span class="text-link no-underline hover:underline hover:decoration-accent-2 hover:underline-offset-4"><button data-popover-target=`popover-description-${id}` data-popover-placement="bottom-end" type="button">{popoverSpanText?popoverSpanText:<slot/>}<span class="sr-only">Show information for the linked content</span></button></span>
  <div data-popover id=`popover-description-${id}` role="tooltip" class="absolute z-10 invisible inline-block text-sm text-gray-500 transition-opacity duration-300 bg-white border border-gray-200 rounded-lg shadow-sm opacity-0 w-72 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-400">
    <a href={linkedTo}>
      <div class="p-3 space-y-2">
          {popoverTitle && <h3 class="font-semibold font-semibold underline decoration-wavy decoration-accent-2 text-accent">{popoverTitle}</h3>}
          <NotionBlocks blocks={[block]} renderChildren={false}/>
          <a href={linkedTo} class="flex items-center font-medium text-blue-600 dark:text-blue-500 dark:hover:text-blue-600 hover:text-blue-700 hover:underline">{linkText}<svg class="w-2 h-2 ms-1.5 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 6 10">
      <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 9 4-4-4-4"/>
    </svg></a>
      </div>
    </a>
      <div data-popper-arrow></div>
  </div>
```

[

404: Not Found

![title](https://www.google.com/s2/favicons?domain=chiubaca.com)

https://chiubaca.com/how-use-npm-modules-client-side-in-astrojs-3m37/





](https://chiubaca.com/how-use-npm-modules-client-side-in-astrojs-3m37/)

Bookmark for [https://chiubaca.com/how-use-npm-modules-client-side-in-astrojs-3m37/](https://chiubaca.com/how-use-npm-modules-client-side-in-astrojs-3m37/)

**This does not work**

```
REFERENCES && (
<script>
  import {initPopovers} from 'flowbite';
  window.addEventListener('load', () => {
    initPopovers();
  });
</script>
  )
}
```

Neither does this

```
{
REFERENCES && (
<script is:inline defer>
  import {initPopovers} from 'flowbite';
  window.addEventListener('load', () => {
    initPopovers();
  });
</script>
  )
}
```

**Maybe the new popover API? (too complicated to position and auto close!!)**

[

Introducing the popover API | Blog | Chrome for Developers

Learn how to build tooltips, menus, and more with the new popover API.

![title](https://www.google.com/s2/favicons?domain=developer.chrome.com)

https://developer.chrome.com/blog/introducing-popover-api/#anchor-positioning

![title](https://www.gstatic.com/devrel-devsite/prod/v4d48f48533ab79e337c1ef540cdee78fc2ebfef5357fb91b7a6b4a7aa8d0c6c8/chrome/images/lockup.svg)

](https://developer.chrome.com/blog/introducing-popover-api/#anchor-positioning)

Bookmark for [https://developer.chrome.com/blog/introducing-popover-api/#anchor-positioning](https://developer.chrome.com/blog/introducing-popover-api/#anchor-positioning)

[

Cómo unir elementos entre sí con el posicionamiento de anclaje de CSS | Blog | Chrome for Developers

Se lanzará una nueva API en la plataforma web para ayudarte a posicionar elementos de forma adaptativa y sin trucos.

![title](https://www.google.com/s2/favicons?domain=developer.chrome.com)

https://developer.chrome.com/blog/tether-elements-to-each-other-with-css-anchor-positioning/

![title](https://www.gstatic.com/devrel-devsite/prod/v4d48f48533ab79e337c1ef540cdee78fc2ebfef5357fb91b7a6b4a7aa8d0c6c8/chrome/images/lockup.svg)

](https://developer.chrome.com/blog/tether-elements-to-each-other-with-css-anchor-positioning/)

Bookmark for [https://developer.chrome.com/blog/tether-elements-to-each-other-with-css-anchor-positioning/](https://developer.chrome.com/blog/tether-elements-to-each-other-with-css-anchor-positioning/)

```
<span class="text-link no-underline hover:underline hover:decoration-accent-2 hover:underline-offset-4"><button popovertarget=`popover-description-${id}` type="button" id=`popover-click-${id}`>{popoverSpanText?popoverSpanText:<slot/>}<span class="sr-only">Show information for the linked content</span></button></span>
  <div popover id=`popover-description-${id}` role="tooltip">
    <a href={linkedTo}>
      <div class="p-3 space-y-2">
          <h3 class="font-semibold underline decoration-wavy decoration-accent-2 text-accent">{popoverTitle}</h3>
          {excerpt && <p>{excerpt}</p>}
          <a href={linkedTo} class="flex items-center font-medium text-blue-600 dark:text-blue-500 dark:hover:text-blue-600 hover:text-blue-700 hover:underline">{linkText}<svg class="w-2 h-2 ms-1.5 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 6 10">
      <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 9 4-4-4-4"/>
    </svg></a>
      </div>
    </a>
      <div data-popper-arrow></div>
  </div>
```

**Dec 8, 2023, 02:20 AM**

✅

The async tag on mermaid was making it not render at all. Oh, and the `import mermaid from mermaid` works there 🤷‍♀️

✅

Fixing scroll on bottom nav and wth is this code with double `!` ?

```
function callback(entries: IntersectionObserverEntry[]) {
      entries.forEach((entry) => {
        if (entry.target === targetHeader) {
          scrollBtn.dataset.show = (!entry.isIntersecting).toString();
        }

        const isBottomNavVisible = !entries.some((entry) => entry.isIntersecting);
        bottomTocNav.classList.toggle("hidden", !isBottomNavVisible);
      });
    }
```

✅

btw, the audio element in popup is causing horizontal scroll:

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.B9iXwUrI_Z2pgY10.webp)](https://nerdymomocat.github.io/_astro/Untitled.B9iXwUrI_Z2pgY10.webp)

✅

lol at z-index and gpt absolutely making sure to be incorrect (patchy fix - does not work for gray/light-gray)

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.oyhr0rwB_Z122Mf6.webp)](https://nerdymomocat.github.io/_astro/Untitled.oyhr0rwB_Z122Mf6.webp)

**GPT**

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.Cmz8PUHU_1R27lT.webp)](https://nerdymomocat.github.io/_astro/Untitled.Cmz8PUHU_1R27lT.webp)

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.BZ8xDM0G_ZKF1IN.webp)](https://nerdymomocat.github.io/_astro/Untitled.BZ8xDM0G_ZKF1IN.webp)

**Dec 8, 2023**

Updating to astro 4

**Dec 9, 2023, 11:53 PM**

✅

Astro-tweet has not updated and I do not like looks of `@astro-community/astro-embed-twitter` . Maybe I just copy paste the component from astro-tweet

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.DnxpU1S-_1Qy2MG.webp)](https://nerdymomocat.github.io/_astro/Untitled.DnxpU1S-_1Qy2MG.webp)

✅

Remove URL normalization for same page popups on anchor links (the interlinked content part works fine)

✅

Add URL normalization for different page links in popups

**No need. Somehow on build, there is a trailing slash added, but not in dev? So, basically if I do navlink#block-id, that causes issues in build but not in dev — if I do navlink/#block-id that causes issues in dev but not in build.**

[![How it looks in dev (no trailing slash)](https://nerdymomocat.github.io/_astro/Untitled.CuJxmF-u_ZSkFet.webp)](https://nerdymomocat.github.io/_astro/Untitled.CuJxmF-u_ZSkFet.webp)

How it looks in dev (no trailing slash)

[![Has trailing slash in build?](https://nerdymomocat.github.io/_astro/Untitled.qsHP_jzj_ZW7HXf.webp)](https://nerdymomocat.github.io/_astro/Untitled.qsHP_jzj_ZW7HXf.webp)

Has trailing slash in build?

**Dec 10, 2023, 09:20 PM**

⬜

Refractor based off this later? [https://github.com/chrismwilliams/astro-theme-cactus/commit/bd1a17c7895ec8c86cf477267b71fbb8e4635d04](https://github.com/chrismwilliams/astro-theme-cactus/commit/bd1a17c7895ec8c86cf477267b71fbb8e4635d04)

✅

Popover to floating ui

So, I have the basic code and I have tried a million methods but they don’t really work to have a clean outcome.

**This is the very basic code that I understand right now**

```
<script type="module">
  import { computePosition, offset, shift, flip, autoUpdate } from 'https://cdn.jsdelivr.net/npm/@floating-ui/dom@1.5.3/+esm';

  document.addEventListener('DOMContentLoaded', () => {
  const popoverTriggers = document.querySelectorAll('[data-popover-target]');
  let openPopovers = [];
  let cleanupAutoUpdate = new Map();
  let hoverTimeout;

  const hideAllPopovers = () => {
    openPopovers.forEach(popoverEl => hidePopover(popoverEl));
    };

  const hidePopover = (popoverEl) => {
    if (popoverEl) {
      popoverEl.style.visibility = 'hidden';
      popoverEl.style.opacity = '0';
      popoverEl.style.top = '0px';
      popoverEl.style.left = '0px';

      const cleanup = cleanupAutoUpdate.get(popoverEl);
      if (cleanup) {
        cleanup();
        cleanupAutoUpdate.delete(popoverEl);
      }
      const openPopoverIndex = openPopovers.indexOf(popoverEl);
      if (openPopoverIndex !== -1) {
        openPopovers.splice(openPopoverIndex, 1);
      }
    }
  };


  const showPopover = (triggerEl, popoverEl) => {

    const update = () => {
      computePosition(triggerEl, popoverEl, {
        middleware: [shift({ padding: 5 }), flip({ padding: 5 }), offset(10)]
      }).then(({ x, y }) => {
        Object.assign(popoverEl.style, {
          left: `${x}px`,
          top: `${y}px`,
          position: 'absolute'
        });
      });
    };

    update();
    popoverEl.style.visibility = 'visible';
    popoverEl.style.opacity = '1';
    openPopovers.push(popoverEl);
    cleanupAutoUpdate.set(popoverEl, autoUpdate(triggerEl, popoverEl, update));
  };

  popoverTriggers.forEach(triggerEl => {
    const popoverID = triggerEl.dataset.popoverTarget;
    const popoverEl = document.getElementById(popoverID);

    triggerEl.addEventListener('mouseenter', () => {
      clearTimeout(hoverTimeout);
      showPopover(triggerEl, popoverEl);
    });

    triggerEl.addEventListener('mouseleave', () => {
      hoverTimeout = setTimeout(() => {
          hidePopover(popoverEl);
      }, 100);
    });

    triggerEl.addEventListener('focus', () => {
      showPopover(triggerEl, popoverEl);
    });

    triggerEl.addEventListener('blur', () => {
      hidePopover(popoverEl);
    });

    popoverEl.addEventListener('mouseenter', () => {
      clearTimeout(hoverTimeout);
    });

    popoverEl.addEventListener('mouseleave', () => {
      hidePopover(popoverEl);
    });
  });

  document.addEventListener('click', (event) => {
    if (!event.target.closest('[data-popover-target]')) {
      hideAllPopovers();
    }
  });

  document.addEventListener('keydown', (event) => {
    if (event.key === 'Escape') {
      hideAllPopovers();
    }
  });
});

</script>
```

The problem is, if I move from one one triggerEl to another triggerEl quickly within 100ms, then nothing closes that has already been opened. Why? Because it goes to mouseleave here which has a timeout of 100ms to be able to move cursor to popover offset.

```
    triggerEl.addEventListener('mouseenter', () => {
      clearTimeout(hoverTimeout);
      showPopover(triggerEl, popoverEl);
    });

    triggerEl.addEventListener('mouseleave', () => {
      hoverTimeout = setTimeout(() => {
          hidePopover(popoverEl);
      }, 100);
    });
```

It should call this and at least close after 100ms BUT the hovertimeout is universal:

```
  const hidePopover = (popoverEl) => {
    if (popoverEl) {
      popoverEl.style.visibility = 'hidden';
      popoverEl.style.opacity = '0';
      popoverEl.style.top = '0px';
      popoverEl.style.left = '0px';

      const cleanup = cleanupAutoUpdate.get(popoverEl);
      if (cleanup) {
        cleanup();
        cleanupAutoUpdate.delete(popoverEl);
      }
      const openPopoverIndex = openPopovers.indexOf(popoverEl);
      if (openPopoverIndex !== -1) {
        openPopovers.splice(openPopoverIndex, 1);
      }
    }
  };
```

So, entering into a new triggerEl just clears the universal hovertimeout. And the hidePopover never gets called.

This is a good thing for nested triggers, because we want their parents to not be closed. But we want only 1 popup of each LEVEL. Let me explain level.

For example: Page has Trigger A and Trigger B. Trigger A has a popover which has Trigger A1 and Trigger A2. Trigger A2 has a popup that has Trigger A21 and Trigger A22.

Say, I hover over Trigger A and then suddenly move under 100ms to Trigger B, so popover from Trigger A will never close because moving to Trigger B has cleared timeout.

But if I hover over Trigger A, and it opens a popup, we hover over Trigger A1 inside the popover from trigger A, and that makes it so that the parent popup does not close which is a good thing. Because we want one popup each level. But if we also suddenly move from Trigger A1 to Trigger A2, the popup from Trigger A1 will not close too because moving to Trigger A2 clears timeout.

The complicated part here is maintaining and tracking levels. Especially consider that this code adds event handlers to all triggers no matter how nested, so this tracking needs to be taken care of in showPopover (maybe add trigger element and the containment level somewhere, check if there is a document contain or popover contain match for trigger element, and then immediately hidepopover if there is).

```
  popoverTriggers.forEach(triggerEl => {
    const popoverID = triggerEl.dataset.popoverTarget;
    const popoverEl = document.getElementById(popoverID);

    triggerEl.addEventListener('mouseenter', () => {
      clearTimeout(hoverTimeout);
      showPopover(triggerEl, popoverEl);
    });

    triggerEl.addEventListener('mouseleave', () => {
      hoverTimeout = setTimeout(() => {
          hidePopover(popoverEl);
      }, 100);
    });

    triggerEl.addEventListener('focus', () => {
      showPopover(triggerEl, popoverEl);
    });

    triggerEl.addEventListener('blur', () => {
      hidePopover(popoverEl);
    });

    popoverEl.addEventListener('mouseenter', () => {
      clearTimeout(hoverTimeout);
    });

    popoverEl.addEventListener('mouseleave', () => {
      hidePopover(popoverEl);
    });
  });
```

**Dec 12, 2023, 06:30 AM**

Finished a bunch of optimizations; I am not even sure if most of them worked, but at least now I know what is going into it!

Lol, should have known you can run lighthouse locally and not push stuff 100 times 🤣

^ Oh doesn’t really do what I want

Partytown in define vars thing

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.jAKPtoOL_Z1GzahK.webp)](https://nerdymomocat.github.io/_astro/Untitled.jAKPtoOL_Z1GzahK.webp)

and without it

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.BgANwp-s_ZdmSVO.webp)](https://nerdymomocat.github.io/_astro/Untitled.BgANwp-s_ZdmSVO.webp)

I will later try to figure out what happens with no partytown

Btw,

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.CEQrbGnd_Z2i1mt5.webp)](https://nerdymomocat.github.io/_astro/Untitled.CEQrbGnd_Z2i1mt5.webp)

The main benefit of using lazysizes for me is just that it does not add lazy to the first image on the page, so it reduces that blocking time. If there is another way to do that, we should do it.

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.3d3sH4Mq_ZtuxGq.webp)](https://nerdymomocat.github.io/_astro/Untitled.3d3sH4Mq_ZtuxGq.webp)

**Dec 12, 2023, 07:45 PM**

Hmm, even after changing popovers to hidden, it still takes really long

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.rv0SfDCJ_iLSkI.webp)](https://nerdymomocat.github.io/_astro/Untitled.rv0SfDCJ_iLSkI.webp)

Lol

> **Note:** In Firefox, the `loading` attribute must be defined before the `src` attribute, otherwise it has no effect ([Firefox bug 1647077](https://bugzil.la/1647077)).

No, script based change to eager does not really work. As expected to be fair.  
OMG WHAT THE HELL

i am gonna laugh so much if this works. i keep forgetting i can use ts files as normal js files for key store values and keep trying to pass build time variables through components or whatever that thing is. 🤣

[![Update 2023-12-12T12:53:00.000-08:00 I am laughing 😅 ](https://nerdymomocat.github.io/_astro/Untitled.C6UDprL1_2eOnYB.webp)](https://nerdymomocat.github.io/_astro/Untitled.C6UDprL1_2eOnYB.webp)

Update Dec 12, 2023, 08:53 PM I am laughing 😅 

Dec 13, 2023, 06:05 AM Moved code highlighting to inbuilt shiki

**Dec 13, 2023, 08:44 AM**

Astro code highlighting

  

**Jan 31, 2024, 09:54 PM**

**Apr 10, 2024, 08:49 AM**

Trying to add collections by streaming a public page component into an iframe similar to [embednotion](https://embednotion.com/).

[![This is where direct copy pasting gets me.](https://nerdymomocat.github.io/_astro/Untitled.DTDj_PsB_Z2fJqc7.webp)](https://nerdymomocat.github.io/_astro/Untitled.DTDj_PsB_Z2fJqc7.webp)

This is where direct copy pasting gets me.

**Jun 25, 2024, 05:55 AM**

[

Shiki

A beautiful yet powerful syntax highlighter

![title](https://www.google.com/s2/favicons?domain=shiki.style)

https://shiki.style/packages/transformers

![title](https://shiki.style/og.png)

](https://shiki.style/packages/transformers)

Bookmark for [https://shiki.style/packages/transformers](https://shiki.style/packages/transformers)

[

Astro render context

![title](https://www.google.com/s2/favicons?domain=docs.astro.build)

https://docs.astro.build/en/reference/api-reference/#code-

![title](https://docs.astro.build/open-graph/en/reference/api-reference.webp)

](https://docs.astro.build/en/reference/api-reference/#code-)

Bookmark for [https://docs.astro.build/en/reference/api-reference/#code-](https://docs.astro.build/en/reference/api-reference/#code-)

[

astro/packages/astro/components/Code.astro at main · withastro/astro

The web framework for content-driven websites. ⭐️ Star to support our work! - withastro/astro

![title](https://www.google.com/s2/favicons?domain=github.com)

https://github.com/withastro/astro/blob/main/packages/astro/components/Code.astro

![title](https://opengraph.githubassets.com/8d637e1cf921033499776dcda254038148a5358e05fa54b4fbd52cd6a70d4805/withastro/astro)

](https://github.com/withastro/astro/blob/main/packages/astro/components/Code.astro)

Bookmark for [https://github.com/withastro/astro/blob/main/packages/astro/components/Code.astro](https://github.com/withastro/astro/blob/main/packages/astro/components/Code.astro)

It doesn’t have this:

[

![title](https://www.google.com/s2/favicons?domain=expressive-code.com)

https://expressive-code.com/reference/style-overrides/#collapsiblesections





](https://expressive-code.com/reference/style-overrides/#collapsiblesections)

Bookmark for [https://expressive-code.com/reference/style-overrides/#collapsiblesections](https://expressive-code.com/reference/style-overrides/#collapsiblesections)

The issue with transformers

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.4PNy7wTO_Z1MKoya.webp)](https://nerdymomocat.github.io/_astro/Untitled.4PNy7wTO_Z1MKoya.webp)

I tried using both NACode and direct code component. It is like the second time this is called — transformerNotationFocus, it is not rendering?

```
      <NACode code={`const foo = 'hello'
const bar = ' world'
console.log(foo + bar) // [!code focus]
`} lang={language}   transformers={[transformerNotationFocus()]}/>
```

I even tried changing styles to outside

```

  pre.has-focused .line:not(.focused) {
    @apply blur-[1px];
  }
```

The problem is repeated component calls

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.pnGKM9TE_ZJIHEq.webp)](https://nerdymomocat.github.io/_astro/Untitled.pnGKM9TE_ZJIHEq.webp)

```
      <Code code={`const foo = 'hello'
const bar = ' world'
console.log(foo + bar) // [!code focus]
`} lang={language}   transformers={[transformerNotationFocus()]}/>
<Code code={`const foo = 'hello'
const bar = ' world'
console.log(foo + bar) // [!code focus]
`} lang={language}   transformers={[transformerNotationFocus()]}/>
```

**Jun 25, 2024**

Trying to make clipboard code better with these rules:

```
/*
Rules:
1. Two things to do: remove lines and clean up comments in remaining lines
2. For remove lines, only consider these commands: [!code --] or [!code warning] or [!code error]
3. Remove the line when the command is after the comment mark (any number of spaces between comment mark and coommand are fine). there can be text after command.
4. do not consider command or comment mark if they are part of a string literal enclosed with ` ` or " "
5. if a line is not removed, then consider these commands: [!code --] [!code ++] [!code warning] [!code error] [!code highlight] [!code highlight:number_here] [!code word:word_here:number_here] [!code word:word_here] [!code focus] [!code focus:number_here].
6. if these occur right after the comment mark (any number of spaces between comment mark and coommand are fine)
and are not part of string literal, just remove the command from if it occurs after the comment mark (and is not in string literal).
do not remove the line. there can be stuff after the command.
7. if the part after comment mark is empty or only white spaces, remeove the comment mark. again remember comment marks that are part of string literals are not considered for this.
*/
```

Nothing works and i have been trying for 3 hours now.

**code at Jun 25, 2024, 06:37 PM**

```

type CommentStyle = string | string[];

const commentStyles: Record<string, CommentStyle> = {
	// Double slash comments
	"//": [
		"default",
		"c",
		"cpp",
		"java",
		"javascript",
		"typescript",
		"csharp",
		"go",
		"swift",
		"kotlin",
		"scala",
		"rust",
		"dart",
		"css",
		"php",
		"pascal",
		"groovy",
	],

	// Hash comments
	"#": [
		"python",
		"ruby",
		"perl",
		"bash",
		"powershell",
		"php",
		"yaml",
		"toml",
		"r",
		"julia",
		"elixir",
	],

	// Double dash comments
	"--": ["haskell", "lua", "sql", "plsql", "ada"],

	// Percent comments
	"%": ["matlab", "octave", "latex", "katex", "erlang"],

	// Semicolon comments
	";": ["lisp", "clojure", "scheme", "ini"],

	// Other specific comment styles
	"⍝": ["apl"],
	"!": ["fortran"],
	"'": ["vb"],
	"*>": ["cobol"],
	"<!--": ["html"],

	// Languages without standard comment syntax
	"": ["brainfuck", "whitespace"],
};

// Invert the commentStyles object for easier lookup
const languageCommentStyles: Record<string, CommentStyle> = Object.entries(commentStyles).reduce(
	(acc, [style, langs]) => {
		(Array.isArray(langs) ? langs : [langs]).forEach((lang) => {
			acc[lang] = style;
		});
		return acc;
	},
	{} as Record<string, CommentStyle>,
);

const getCommentStyle = (language: string): CommentStyle => {
	return languageCommentStyles[language.toLowerCase()] || languageCommentStyles.default;
};

const escapeRegExp = (string: string): string => string.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");

const createCommentRegex = (style: string | string[]): RegExp => {
  const patterns = Array.isArray(style) ? style : [style];
  const escapedPatterns = patterns.map(escapeRegExp).join("|");
  return new RegExp(`^(.*?)(${escapedPatterns})(.*)$`);
};

const notationRegex = /\[!code (focus|--|\+\+|highlight|word:[^\]]+|error|warning)\]/g;

const createRemoveLineRegex = (style: string | string[]): RegExp => {
  const patterns = Array.isArray(style) ? style : [style];
  const escapedPatterns = patterns.map(escapeRegExp).join("|");
  return new RegExp(`(${escapedPatterns})\\s*\\[!code (--|error|warning)\\](?!\\s{2,})`);
};

const processCode = (code: string, language: string): string => {
  if (!languageCommentStyles[language] || language === "plaintext") {
    return code;
  }

  const style = getCommentStyle(language);
  const commentRegex = createCommentRegex(style);
  const removeLineRegex = /^\s*(\/\/|#|--|%|;|⍝|!|'|\*>|<!--)\s*\[!code\s+(--|error|warning)\]/;
  const cleanupRegex = /\[!code\s+(--|error|warning|highlight|word:[^\]]+|\+\+|focus)(:[\d]+)?\]/g;

  return code
    .split("\n")
    .filter((line) => {
      const commentMatch = line.match(commentRegex);
      if (commentMatch) {
        const [, before, commentStart, commentContent] = commentMatch;
        // Check if the comment start is within a string literal
        const stringLiteralRegex = /(['"`])(?:(?!\1)[^\\]|\\.)*\1/g;
        let isInStringLiteral = false;
        let match;
        while ((match = stringLiteralRegex.exec(before)) !== null) {
          if (match[0].includes(commentStart)) {
            isInStringLiteral = true;
            break;
          }
        }
        if (isInStringLiteral) {
          return true;
        }
        // Check if the line should be removed
        return !removeLineRegex.test(line);
      }
      return true;
    })
    .map((line) => {
      return line.replace(commentRegex, (_, before, commentStart, commentContent) => {
        // Clean up the comment content
        const cleaned = commentContent.replace(cleanupRegex, (match) => {
          // Only remove the command if it's at the start of the comment
          return commentContent.trim().startsWith(match) ? "" : match;
        }).trim();
        if (cleaned) {
          return `${before}${commentStart} ${cleaned}`;
        } else {
          return before.trimEnd();
        }
      });
    })
    .join("\n");
};



// Usage remains the same
const code = block.Code.RichTexts.map(
	(richText: interfaces.RichText) => richText.Text.Content,
).join("");
const language =
	block.Code.Language.toLowerCase() === "plain text"
		? "plaintext"
		: block.Code.Language.toLowerCase();
const modifiedCode = processCode(code, language);

const code_inject = SHORTCODES["html"] ? code.startsWith(SHORTCODES["html"]) : false;
const plainTextCaption = block.Code?.Caption.map((richText) => richText.PlainText).join(" ");
```

While these rules make sense, shiki’s own regex isn’t this complicated:

Errors: in second line, comment mark is removed? and in last line it does remove comment mark from both places.

also btw, a line with this `console.log('hewwo') console.log('hewwo') console.log('hewwo') console.log('hewwo') console.log('hewwo') console.log('hewwo') // [!code --] [!code focus]` becaxztomes `console.log('hewwo') console.log('hewwo') console.log('hewwo') console.log('hewwo') console.log('hewwo') console.log('hewwo') [!code focus]`

and this is rendered as ++ `console.log('use // [!code ++]') // [!code --]` with text as: `console.log('use')`

So, it seems like they are just

```
console.log('hewwo') console.log('hewwo') console.log('hewwo') console.log('hewwo') console.log('hewwo') console.log('hewwo') 
console.log('hello') // blah blah come!
console.log('goodbye') // hellllaaaaa blah blah come!
console.log('goodbye') // [!code  --] blah blah come!
console.log('use // [!code --]') 
```

```
console.log('hewwo') console.log('hewwo') console.log('hewwo') console.log('hewwo') console.log('hewwo') console.log('hewwo') 
console.log('hello') # blah blah come!
console.log('goodbye') # hellllaaaaa blah blah come!
console.log('goodbye') # [!code  --] blah blah come!
console.log('# [!code --]') 
```

```
console.log('hewwo') console.log('hewwo') console.log('hewwo') console.log('hewwo') console.log('hewwo') console.log('hewwo') 
console.log('hello') % blah blah come!
console.log('goodbye') % hellllaaaaa blah blah come!
console.log('goodbye') % [!code  --] blah blah come!
console.log('\% [!code --]') 
```

```
<h1>Welcome to My Website</h1> 
<h1>Welcome to My Website</h1> <!-- blah blah come! -->
<h1>Welcome to My Website</h1> <!--  helllaaa -->
<h1>Welcome to My Website</h1> <!-- [!code  --] blah blah come! -->
<h1>Welcome  [!code ++] to My Website</h1> 
```

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.NW8EwLvp_Zu4lGN.webp)](https://nerdymomocat.github.io/_astro/Untitled.NW8EwLvp_Zu4lGN.webp)

Okay, I think I'm done. Using expressive code is way more complicated because the markdown option does parse normally but the code option doesn't have just like internal parser. You can't just be like code equal to code. You have to parse every single thing out of it like deletions, insertions, etc. It feels like I'm writing a parser of my own and that would be a hassle. I went the easy route and like keyboard clipboard cleanup and did what Shaky was doing. It made more sense because if you wanted Shaky transformer highlighting then you would be using it properly anyway. So yeah.

**Jun 26, 2024, 02:16 AM**

Started a new block because this now about footnotes.

**Footnote testing**

\[^abr3a\]: footnote much before the mention itself

this is a normal footnote \[^abra1\]

\[^abra1\]: footnote content on a new block

this is a normal footnote marker with no space before\[^abra2\]or after  
\[^abra2\]: footnote content on new line, same block

this is a common invalid footnote \[^ abr3a\]

this is a common valid footnote with no attached footnote content \[^abra3\]

double mention of same footnote marker \[^abra2\]

footnote marker for multiline footnote \[^abra4\]

\[^abra4\]: hey how are you?

multiline comment using indented blocks

what if it has a callout?

![](https://nerdymomocat.github.io/_astro/info-alternate_gray.xE4Ugu2z_Z1EYdWV.svg)

random info in footnote

footnote marker for multiline again? \[^abra5\]  
\[^abra5\]: hey? how are you doing? multiline comment but in same block  
what is up?

\[^abra6\]: what if i have footnote content that is not a footnote marked anywhere? markdown just removes this

This is what I have.

Here is what I am thinking I guess

-   Identify footnote content when it starts with \[^anywordwithoutspace\]:
    -   The footnote content is consider till the end of that block including children. This will consider when footnote content is a separate block, when it is in same block at end, when it has multiline footnote with child blocks, when it has multiline footnotes using enter key.
    -   The only place this does not work is when in a single block someone has multiline footnotes and then the footnote ends when they press double enter in same block. But I am ignoring that case, why are you using notion, end that block?
-   Identify footnote marker if any content anywhere in plaintext has \[^anywordwithoutspace\]. It is not needed that this marker have spaces around it.

**Jun 26, 2024**

Error in run here:

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.CPeTYS1X_Z2rNUJV.webp)](https://nerdymomocat.github.io/_astro/Untitled.CPeTYS1X_Z2rNUJV.webp)

I tried making it so that avif is default and oh my god, it doubled the time and increased econnreset errors so much!

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.DvA2cKin_2bbTIi.webp)](https://nerdymomocat.github.io/_astro/Untitled.DvA2cKin_2bbTIi.webp)

**Jun 30, 2024**

Tried making it so that tags aren’t always open on mobile but this is not currently working for pagefind filter

```
<script>
document.addEventListener('DOMContentLoaded', () => {
  if (window.innerWidth <= 640) {
    document.querySelectorAll('.pagefind-ui__filter-block').forEach(block => {
      block.removeAttribute('open');
      console.log("removed!");
    });
  }
});
</script>
```

**Jul 2, 2024**

If I dynamically generate popovers — it helps!

[![Before](https://nerdymomocat.github.io/_astro/Untitled.DcKYR8Ds_ZguvSm.webp)](https://nerdymomocat.github.io/_astro/Untitled.DcKYR8Ds_ZguvSm.webp)

Before

[![After](https://nerdymomocat.github.io/_astro/Untitled.CaXKr-X6_2gtIx1.webp)](https://nerdymomocat.github.io/_astro/Untitled.CaXKr-X6_2gtIx1.webp)

After

**Jul 5, 2024**

Figure out basic improvement on tables

Using simple datatables because i really do not want to import jquery too with datatables Jul 5, 2024, 05:47 PM

Options for simple datatables [here](https://github.com/fiduswriter/simple-datatables/blob/fd8986fbae0dd5b69a22df9dfab1e28779fc3e31/src/types.ts#L470).

[https://fiduswriter.github.io/simple-datatables/demos/10-filters/](https://fiduswriter.github.io/simple-datatables/demos/10-filters/)

[https://www.npmjs.com/package/simple-datatables/v/9.0.4?activeTab=code](https://www.npmjs.com/package/simple-datatables/v/9.0.4?activeTab=code)

[https://fiduswriter.github.io/simple-datatables/documentation/](https://fiduswriter.github.io/simple-datatables/documentation/)

-   fix row spacing
-   fix that on small screens or larger tables overflow-x and table-auto stop working with datatables
-   fix filter issues
-   Relevant demos
    
    ✅
    
    [both AND and OR search](https://fiduswriter.github.io/simple-datatables/demos/22-and-search/)
    
    ✅
    
    [column filters](https://fiduswriter.github.io/simple-datatables/demos/20-column-filters/)
    
    ⬜
    
    [column filter button](https://fiduswriter.github.io/simple-datatables/demos/23-column-filter-button/)
    

**Jul 6, 2024**

Removing the css does not fix these issues:

-   The top <thead> <tr> is white???
-   On search, the auto table layout does not work and overflow X is always none

It fixes this:

-   borders between cells are back

I just pushed it out 🤷‍♀️

**Jul 7, 2024**

Reducing div size. Both openai and claude suddenly recommended `<template>` tag. But know that in astro, that tag should be the end of the file or it is not processed correctly 🤷‍♀️.

SIGNIFICANT REDUCTION IN DOM SIZE

[![After (1200)](https://nerdymomocat.github.io/_astro/Untitled.D8FT-wlK_Z16HvN.webp)](https://nerdymomocat.github.io/_astro/Untitled.D8FT-wlK_Z16HvN.webp)

After (1200)

[![Before (2063)](https://nerdymomocat.github.io/_astro/Untitled.-ft7RDHk_aQDMH.webp)](https://nerdymomocat.github.io/_astro/Untitled.-ft7RDHk_aQDMH.webp)

Before (2063)

Merging Jul 7, 2024, 09:24 PM:

[![Before](https://nerdymomocat.github.io/_astro/Untitled.Crz-ene2_1XKdv3.webp)](https://nerdymomocat.github.io/_astro/Untitled.Crz-ene2_1XKdv3.webp)

Before

[![after](https://nerdymomocat.github.io/_astro/Untitled.BzIbPzB2_Z2nnMnj.webp)](https://nerdymomocat.github.io/_astro/Untitled.BzIbPzB2_Z2nnMnj.webp)

after

Figcaption issue

```
---
import { ENABLE_LIGHTBOX } from "../../../constants.ts";
import * as interfaces from "../../../lib/interfaces.ts";
import { filePath, getFirstImage } from "../../../lib/blog-helpers";
import Caption from "../Caption.astro";
// import GLightbox from 'glightbox';


export interface Props {
	block: interfaces.Block;
  setId?:boolean;
}

const { block, setId=true } = Astro.props;

let image = "";
if (block.NImage?.External) {
	image = block.NImage.External.Url;
} else if (block.NImage?.File) {
	image = filePath(new URL(block.NImage.File.OptimizedUrl));
}

const plainTextCaption = block.NImage?.Caption.map((richText) => richText.PlainText).join(' ');

---

<figure class="flex max-w-full mx-auto mt-1" id={setId?block.Id:undefined}>
  {
      image && (
          <div class="mx-auto min-w-0">
              <div>
                  {ENABLE_LIGHTBOX ? (
                      <a data-type="image" href={image} class="mediaglightbox" data-description={plainTextCaption} aria-label={`Open image with alt ${plainTextCaption} in lightbox`}>
                          <img class="imagemedia block max-w-full rounded-md"  loading={getFirstImage()?"eager":"lazy"} src={image} alt={plainTextCaption}/>
                      </a>
                  ) : (
                      <img class="imagemedia block max-w-full rounded-md" src={image} loading={getFirstImage()?"eager":"lazy"} alt={plainTextCaption}/>
                  )}
              </div>
              <Caption richTexts={block.NImage.Caption} block={block} as="figcaption" />
          </div>
      )
  }
</figure>
```

Before

```
---
import { ENABLE_LIGHTBOX } from "../../../constants.ts";
import * as interfaces from "../../../lib/interfaces.ts";
import { filePath, getFirstImage } from "../../../lib/blog-helpers";
import Caption from "../Caption.astro";
// import GLightbox from 'glightbox';


export interface Props {
	block: interfaces.Block;
  setId?:boolean;
}

const { block, setId=true } = Astro.props;

let image = "";
if (block.NImage?.External) {
	image = block.NImage.External.Url;
} else if (block.NImage?.File) {
	image = filePath(new URL(block.NImage.File.OptimizedUrl));
}

const plainTextCaption = block.NImage?.Caption.map((richText) => richText.PlainText).join(' ');

---

{image && (<figure class="max-w-full mx-auto mt-1" id={setId?block.Id:undefined}>
  {
              <div class="mx-auto min-w-0">
                  {ENABLE_LIGHTBOX ? (
                      <a data-type="image" href={image} class="mediaglightbox" data-description={plainTextCaption} aria-label={`Open image with alt ${plainTextCaption} in lightbox`}>
                          <img class="imagemedia block max-w-full rounded-md"  loading={getFirstImage()?"eager":"lazy"} src={image} alt={plainTextCaption}/>
                      </a>
                  ) : (
                      <img class="imagemedia block max-w-full rounded-md" src={image} loading={getFirstImage()?"eager":"lazy"} alt={plainTextCaption}/>
                  )}
              </div>
          <Caption richTexts={block.NImage.Caption} block={block} as="figcaption" />

  }
</figure>)}
```

Removing script=module thing. Cannot remove from glightbox btw 🤷‍♀️ or from floating-ui. But it works with removing from simple-datatables.

I tried adding lazyload to videos and it messed up the rating more???

[![Image uploaded to Notion](https://nerdymomocat.github.io/_astro/Untitled.BOzaLgoq_2v98iS.webp)](https://nerdymomocat.github.io/_astro/Untitled.BOzaLgoq_2v98iS.webp)

**Sep 13, 2024**

Page link mentions now show up in API

**I got this for example with a normal website**

```
{
    "object": "block",
    "id": "4869b8e5-edb5-45b3-a3cf-c30674961562",
    "parent": {
        "type": "page_id",
        "page_id": "85cb68b6-b12f-4a62-af33-3348ea751f77"
    },
    "created_time": "2024-06-23T20:49:00.000Z",
    "last_edited_time": "2024-08-02T12:08:00.000Z",
    "created_by": {
        "object": "user",
        "id": "5aa7609b-54f8-42b7-b4a2-6f38a03279bd"
    },
    "last_edited_by": {
        "object": "user",
        "id": "5aa7609b-54f8-42b7-b4a2-6f38a03279bd"
    },
    "has_children": true,
    "archived": false,
    "in_trash": false,
    "type": "paragraph",
    "paragraph": {
        "rich_text": [
            {
                "type": "text",
                "text": {
                    "content": "Paste link as mention: ",
                    "link": null
                },
                "annotations": {
                    "bold": false,
                    "italic": false,
                    "strikethrough": false,
                    "underline": false,
                    "code": false,
                    "color": "default"
                },
                "plain_text": "Paste link as mention: ",
                "href": null
            },
            {
                "type": "mention",
                "mention": {
                    "type": "link_mention",
                    "link_mention": {
                        "href": "https://shiki.style/packages/transformers#shikijs-transformers",
                        "title": "Shiki",
                        "icon_url": "https://shiki.style/logo.svg",
                        "description": "A beautiful yet powerful syntax highlighter",
                        "link_author": "Pine Wu, Anthony Fu",
                        "thumbnail_url": "https://shiki.style/og.png"
                    }
                },
                "annotations": {
                    "bold": false,
                    "italic": false,
                    "strikethrough": false,
                    "underline": false,
                    "code": false,
                    "color": "default"
                },
                "plain_text": "https://shiki.style/packages/transformers#shikijs-transformers",
                "href": "https://shiki.style/packages/transformers#shikijs-transformers"
            }
        ],
        "color": "default"
    }
},
```

**when it is like an iframe embed, it is**

```
{
    "object": "block",
    "id": "101817d0-5c92-80b2-9e24-d20eb882b801",
    "parent": {
        "type": "page_id",
        "page_id": "85cb68b6-b12f-4a62-af33-3348ea751f77"
    },
    "created_time": "2024-09-14T06:22:00.000Z",
    "last_edited_time": "2024-09-14T06:22:00.000Z",
    "created_by": {
        "object": "user",
        "id": "5aa7609b-54f8-42b7-b4a2-6f38a03279bd"
    },
    "last_edited_by": {
        "object": "user",
        "id": "5aa7609b-54f8-42b7-b4a2-6f38a03279bd"
    },
    "has_children": true,
    "archived": false,
    "in_trash": false,
    "type": "paragraph",
    "paragraph": {
        "rich_text": [
            {
                "type": "text",
                "text": {
                    "content": "Embed paste link as mention: ",
                    "link": null
                },
                "annotations": {
                    "bold": false,
                    "italic": false,
                    "strikethrough": false,
                    "underline": false,
                    "code": false,
                    "color": "default"
                },
                "plain_text": "Embed paste link as mention: ",
                "href": null
            },
            {
                "type": "mention",
                "mention": {
                    "type": "link_mention",
                    "link_mention": {
                        "href": "https://open.spotify.com/playlist/37i9dQZF1DX5KpP2LN299J",
                        "title": "This Is Taylor Swift",
                        "height": 352,
                        "icon_url": "https://open.spotifycdn.com/cdn/images/favicon32.b64ecc03.png",
                        "iframe_url": "https://open.spotify.com/embed/playlist/37i9dQZF1DX5KpP2LN299J?utm_source=oembed",
                        "description": "Playlist · Spotify · 180 items · 6.1M likes",
                        "link_provider": "Spotify",
                        "thumbnail_url": "https://i.scdn.co/image/ab67706f0000000252feef11af8c9d412769ec5a"
                    }
                },
                "annotations": {
                    "bold": false,
                    "italic": false,
                    "strikethrough": false,
                    "underline": false,
                    "code": false,
                    "color": "default"
                },
                "plain_text": "https://open.spotify.com/playlist/37i9dQZF1DX5KpP2LN299J",
                "href": "https://open.spotify.com/playlist/37i9dQZF1DX5KpP2LN299J"
            }
        ],
        "color": "default"
    }
},
```

**popup div for spotify**

```
<div style="display: flex; align-items: center; position: relative; flex-direction: column-reverse; transform-origin: 0% top; left: 0px; top: 6px;">
  <div
    role="dialog"
    style="border-radius: 10px; background: white; backdrop-filter: none; position: relative; max-width: calc(-24px + 100vw); box-shadow: rgba(15, 15, 15, 0.1) 0px 0px 0px 1px, rgba(15, 15, 15, 0.1) 0px 2px 4px; overflow: hidden;"
  >
    <div>
      <div style="width: 340px; max-height: 420px; flex-direction: column; align-items: flex-start;">
        <div style="position: absolute; top: 8px; right: 8px; z-index: 2;">
          <div
            role="button"
            tabindex="0"
            style="user-select: none; transition: background 20ms ease-in; cursor: pointer; display: flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 4px; fill: rgba(55, 53, 47, 0.35); background: rgba(0, 0, 0, 0.6);"
          >
            <svg
              role="graphics-symbol"
              viewBox="0 0 13 3"
              class="dots"
              style="width: 14px; height: 100%; display: block; fill: white; flex-shrink: 0;"
            >
              <g>
                <path d="M3,1.5A1.5,1.5,0,1,1,1.5,0,1.5,1.5,0,0,1,3,1.5Z"></path>
                <path d="M8,1.5A1.5,1.5,0,1,1,6.5,0,1.5,1.5,0,0,1,8,1.5Z"></path>
                <path d="M13,1.5A1.5,1.5,0,1,1,11.5,0,1.5,1.5,0,0,1,13,1.5Z"></path>
              </g>
            </svg>
          </div>
        </div>
        <div style="width: 340px; height: 100%; aspect-ratio: auto; position: relative;">
          <div style="position: relative; display: flex; justify-content: center; width: 100%; min-height: 100px; height: 352px;">
            <div style="position: absolute; left: 0px; top: 0px; width: 100%; height: 100%; border-radius: 1px;">
              <div style="width: 100%; height: 100%;">
                <div style="position: absolute; left: 0px; top: 0px; width: 100%; height: 100%; border-radius: 1px; pointer-events: auto;">
                  <iframe
                    src="https://open.spotify.com/embed/playlist/37i9dQZF1DX5KpP2LN299J?utm_source=oembed"
                    frameborder="0"
                    sandbox="allow-scripts allow-popups allow-top-navigation-by-user-activation allow-forms allow-same-origin allow-storage-access-by-user-activation allow-popups-to-escape-sandbox"
                    allowfullscreen=""
                    style="position: absolute; left: 0px; top: 0px; width: 100%; height: 100%; border-radius: 1px; pointer-events: auto;"
                  ></iframe>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>;
```

Somehow this doesn’t work:

```
const heightClass = Height && Height < 380 ? `h-[${Height}px]` : 'h-[380px]';
```

but this does

```
          <div class="w-full min-h-[100px]" style={Height && Height>380?"height:380px":("height:"+Height?.toString()+"px")}>
```

it is not reading the tailwind `h-[${Height}px]`. I even tried with toString conversion 🤷‍♀️.

The image stuff

```
<img src="https://aersf.com/cdn/shop/files/AERL1101_carryon_black_34hero_1200x1200.jpg?v=1715313300" referrerpolicy="same-origin" style="display: block; object-fit: cover; border-radius: 2px; width: 100%; height: 100%; left: 0px; top: calc(50% - 140px);">
```

## Log of I Tried It and It Didn’t Work

**I thought maybe I can try changing color of all uploaded icons in dark mode but this doesn’t work**

```
let iconURL = post.Icon && post.Icon.Type === "external" && isNotionIconURL(post.Icon.Url)?post.Icon.Url:null;
//does not work because that needs to be in client side script
if (rootHasDarkClass() && iconURL){iconURL = post.Icon.Url+"?mode=dark";}
```

So, I am removing this function

```
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 rootHasDarkClass() {
	return elementHasClass(document.documentElement, "dark");
}
```

```
export const isNotionIconURL = (url: string): boolean => {
	return !url.startsWith("https://www.notion.so/icons/");
};
```

```
export const getIconCssFilter = (iconUrl: string): string | null | undefined => {
	// Regular expression to match the pattern:
	// (1) Any characters after the last underscore
	// (2) Ending with ".svg"
	const regex = /_([^.]+)\.svg$/;
	const match = iconUrl.match(regex);

	// If a match is found, return the captured group, which is the color part.
	// Otherwise, return null if no match is found.
	return match ? match[1] : null;
};
```

**Not supporting reading time/count, so I removed the code related to that**

```
const BLOCKS_FOR_WORD_COUNT: BlockTypes[] = [
	"heading_1",
	"heading_2",
	"heading_3",
	"paragraph",
	"bulleted_list_item",
	"numbered_list_item",
	"to_do",
	"callout",
	"quote",
];

type ContentBlock = {
	content: string;
	children: ContentBlock[];
};
function getSupportedBlocks(blocks: Block[]): ContentBlock[] {
	return blocks
		.filter((block) => BLOCKS_FOR_WORD_COUNT.includes(block.Type))
		.map((block) => {
			if (block.Type === "heading_1" && block.Heading1) {
				return {
					content: block.Heading1.RichTexts.map((text) => text.PlainText).join(" "),
					children: getSupportedBlocks(block.Heading1?.Children || []),
				};
			}
			if (block.Type === "heading_2" && block.Heading2) {
				return {
					content: block.Heading2.RichTexts.map((text) => text.PlainText).join(" "),
					children: getSupportedBlocks(block.Heading2?.Children || []),
				};
			}
			if (block.Type === "heading_3" && block.Heading3) {
				return {
					content: block.Heading3.RichTexts.map((text) => text.PlainText).join(" "),
					children: getSupportedBlocks(block.Heading3?.Children || []),
				};
			}
			if (block.Type === "paragraph" && block.Paragraph) {
				return {
					content: block.Paragraph.RichTexts.map((text) => text.PlainText).join(" "),
					children: getSupportedBlocks(block.Paragraph?.Children || []),
				};
			}
			if (block.Type === "bulleted_list_item" && block.BulletedListItem) {
				return {
					content: block.BulletedListItem.RichTexts.map((text) => text.PlainText).join(" "),
					children: getSupportedBlocks(block.BulletedListItem?.Children || []),
				};
			}
			if (block.Type === "numbered_list_item" && block.NumberedListItem) {
				return {
					content: block.NumberedListItem.RichTexts.map((text) => text.PlainText).join(" "),
					children: getSupportedBlocks(block.NumberedListItem?.Children || []),
				};
			}
			if (block.Type === "to_do" && block.ToDo) {
				return {
					content: block.ToDo.RichTexts.map((text) => text.PlainText).join(" "),
					children: getSupportedBlocks(block.ToDo?.Children || []),
				};
			}
			if (block.Type === "callout" && block.Callout) {
				return {
					content: block.Callout.RichTexts.map((text) => text.PlainText).join(" "),
					children: getSupportedBlocks(block.Callout?.Children || []),
				};
			}
			if (block.Type === "quote" && block.Quote) {
				return {
					content: block.Quote.RichTexts.map((text) => text.PlainText).join(" "),
					children: getSupportedBlocks(block.Quote?.Children || []),
				};
			}

			return { content: "", children: [] };
		});
}

function generateTexts(contentBlocks: ContentBlock[]): string {
	return contentBlocks
		.map((block) => {
			return block.content + "\n" + generateTexts(block.children);
		})
		.join("\n");
}

export function getPageStats(blocks: Block[]) {
	const supportedBlocks = getSupportedBlocks(blocks);
	const pageText = generateTexts(supportedBlocks);

	const wordCount = pageText.split(" ").length;

	return {
		wordCount,
		readTime: `${Math.round((wordCount / 250) * 100) / 100} min`,
	};
}
```

**I am using paginate by astro, so I do not need functions to define content by page number**

```
export async function getPageBySlug(slug: string): Promise<Post | null> {
	const allPosts = await getAllPages();
	return allPosts.find((post) => post.Slug === slug) || null;
}

export async function getPosts(pageSize = 10): Promise<Post[]> {
	const allPosts = await getAllPosts();
	return allPosts.slice(0, pageSize);
}

export async function getRankedPosts(pageSize = 10): Promise<Post[]> {
	const allPosts = await getAllPosts();
	return allPosts
		.filter((post) => !!post.Rank)
		.sort((a, b) => {
			if (a.Rank > b.Rank) {
				return -1;
			} else if (a.Rank === b.Rank) {
				return 0;
			}
			return 1;
		})
		.slice(0, pageSize);
}

export async function getPostsByTag(tagName: string, pageSize = 10): Promise<Post[]> {
	if (!tagName) return [];

	const allPosts = await getAllPosts();
	return allPosts
		.filter((post) => post.Tags.find((tag) => tag.name === tagName))
		.slice(0, pageSize);
}

// page starts from 1 not 0
export async function getPostsByPage(page: number): Promise<Post[]> {
	if (page < 1) {
		return [];
	}

	const allPosts = await getAllPosts();

	const startIndex = (page - 1) * NUMBER_OF_POSTS_PER_PAGE;
	const endIndex = startIndex + NUMBER_OF_POSTS_PER_PAGE;

	return allPosts.slice(startIndex, endIndex);
}

// page starts from 1 not 0
export async function getPostsByTagAndPage(tagName: string, page: number): Promise<Post[]> {
	if (page < 1) {
		return [];
	}

	const allPosts = await getAllPosts();
	const posts = allPosts.filter((post) => post.Tags.find((tag) => tag.name === tagName));

	const startIndex = (page - 1) * NUMBER_OF_POSTS_PER_PAGE;
	const endIndex = startIndex + NUMBER_OF_POSTS_PER_PAGE;

	return posts.slice(startIndex, endIndex);
}

export async function getNumberOfPages(): Promise<number> {
	const allPosts = await getAllPosts();
	return (
		Math.floor(allPosts.length / NUMBER_OF_POSTS_PER_PAGE) +
		(allPosts.length % NUMBER_OF_POSTS_PER_PAGE > 0 ? 1 : 0)
	);
}

export async function getNumberOfPagesByTag(tagName: string): Promise<number> {
	const allPosts = await getAllPosts();
	const posts = allPosts.filter((post) => post.Tags.find((tag) => tag.name === tagName));
	return (
		Math.floor(posts.length / NUMBER_OF_POSTS_PER_PAGE) +
		(posts.length % NUMBER_OF_POSTS_PER_PAGE > 0 ? 1 : 0)
	);
}
```

**Removing stuff from blog-helpers**

date formatting is a different file

```
export const getDateStr = (date: string) => {
	const dt = new Date(date);

	if (date.indexOf("T") !== -1) {
		// Consider timezone
		const elements = date.split("T")[1].split(/([+-])/);
		if (elements.length > 1) {
			const diff = parseInt(`${elements[1]}${elements[2]}`, 10);
			dt.setHours(dt.getHours() + diff);
		}
	}

	const y = dt.getFullYear();
	const m = ("00" + (dt.getMonth() + 1)).slice(-2);
	const d = ("00" + dt.getDate()).slice(-2);
	return y + "-" + m + "-" + d;
};
```

didn’t use page or tag links

```
export const getTagLink = (tag: string) => {
	return path.join(BASE_PATH, `/posts/tag/${encodeURIComponent(tag)}`);
};

export const getPageLink = (page: number, tag: string) => {
	if (page === 1) {
		return tag ? getTagLink(tag) : path.join(BASE_PATH, "/");
	}
	return tag
		? path.join(BASE_PATH, `/posts/tag/${encodeURIComponent(tag)}/page/${page.toString()}`)
		: path.join(BASE_PATH, `/posts/page/${page.toString()}`);
};
```

have 100 functions for file path

```
export const getStaticFilePath = (pathsup: string): string => {
	return path.join(BASE_PATH, pathsup);
};
```

  

* * *

## Tips

[

Syncing a fork - GitHub Docs

Sync a fork of a repository to keep it up-to-date with the upstream repository.

![title](https://www.google.com/s2/favicons?domain=docs.github.com)

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

![title](https://docs.github.com/assets/cb-345/images/social-cards/pull-requests.png)

](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork)

Update fork

Bookmark for [https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork)

[

Material Design

Build beautiful, usable products faster. Material Design is an adaptable system—backed by open-source code—that helps teams build high quality digital experiences.

![title](https://www.google.com/s2/favicons?domain=m2.material.io)

https://m2.material.io/design/color/the-color-system.html#tools-for-picking-colors

![title](https://material.io/static/assets/result.png)

](https://m2.material.io/design/color/the-color-system.html#tools-for-picking-colors)

Colr scheme choices

Bookmark for [https://m2.material.io/design/color/the-color-system.html#tools-for-picking-colors](https://m2.material.io/design/color/the-color-system.html#tools-for-picking-colors)