Skip to content

Commit 62045f6

Browse files
committed
massive rerfactor to content handling
1 parent 37fca3c commit 62045f6

File tree

582 files changed

+11351
-17513
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

582 files changed

+11351
-17513
lines changed

README.md

Lines changed: 80 additions & 234 deletions
Original file line numberDiff line numberDiff line change
@@ -1,264 +1,110 @@
1-
# Website2
1+
# EJ Fox's Website
22

3-
**Website2** is a custom markdown processing and publishing system built with Nuxt 3. It enables seamless integration of rich content, interactive Gists, scrollytelling, and optimized image handling, while also supporting powerful features like syntax highlighting, Mermaid diagrams, and advanced caching.
4-
5-
Here’s a high-level, concise guide to the most important files and directories in the **Website2** project:
6-
7-
---
8-
9-
## Project Structure Overview
10-
11-
This guide will help you navigate the most essential parts of the **Website2** markdown processing and publishing system. Each key directory and file is briefly explained to give you an understanding of what it does and where to find critical functionality.
12-
13-
---
14-
15-
### Root Directory
16-
17-
- **`nuxt.config.ts`**: The configuration file for Nuxt 3, where global settings for the app are defined, including plugins, runtime configurations, and build options.
18-
19-
- **`app.vue`**: The root component of the application, which serves as the wrapper for all other pages. If you need to apply a global layout or logic, this is where it goes.
20-
21-
- **`package.json`**: The project manifest file. It lists dependencies, scripts, and metadata about the project. This is also where you define npm/yarn scripts for running and building the project.
22-
23-
- **`helpers.js`**: A utility file that contains helper functions used across different parts of the application. This is a great place to look for shared logic.
24-
25-
---
26-
27-
### Important Directories
28-
29-
- **`pages/`**: This directory contains the main pages for the app, defining the routes. For example:
30-
- **`index.vue`**: The homepage of the site.
31-
- **`blog/`**: Contains individual blog pages and content for the blog section.
32-
- **`projects.vue`**: A dedicated page for showcasing projects.
33-
34-
- **`composables/`**: Holds reusable logic that can be shared across components. Key files include:
35-
- **`useProcessedMarkdown.ts`**: The core composable responsible for fetching and processing markdown content.
36-
- **`useScraps.ts`**: Handles logic related to scrap content (a unique content type in this system).
37-
38-
- **`scripts/`**: Where custom Node.js scripts are stored. Examples:
39-
- **`processMarkdown.mjs`**: The main script that processes markdown files and converts them into HTML for display on the site.
40-
- **`generateShareImages.mjs`**: Generates shareable images for social media based on content.
41-
42-
- **`server/api/`**: Contains API endpoints for interacting with the backend. For instance:
43-
- **`manifest-lite.ts`**: Fetches the metadata for all blog posts.
44-
- **`posts/`**: Handles requests for individual blog posts based on the slug.
45-
- **`scraps.post.ts`**: Manages scrap content in the system.
46-
47-
- **`content/`**: Houses the markdown content files for the blog, scrapbook, and other sections. This is the source for the markdown processing pipeline.
48-
49-
- **`dist/`**: The output directory for the processed markdown files and other generated content.
50-
51-
52-
---
3+
A personal website and blog built with Nuxt 3, Vue 3, and D3.js. Content is managed through Obsidian and processed through a custom pipeline for seamless publishing.
534

545
## Features
556

56-
- **Markdown Processing**: Converts markdown files into HTML with automatic enhancements.
57-
- **Gist Embedding**: Execute and display Gists (HTML, JS, CSS, JSON) within markdown.
58-
- **VueUse & Pinia**: Vue.js utilities and state management.
59-
- **Scrollytelling Integration**: Trigger animations and events based on user scroll.
60-
- **OpenAI Plugin**: Integrated plugin for AI-powered features.
61-
- **Cloudinary Image Optimization**: Responsive images served from Cloudinary.
62-
- **Syntax Highlighting**: Uses Shiki for beautiful code block rendering.
63-
- **Mermaid Diagrams**: Embed diagrams in markdown files.
64-
- **Automatic TOC Generation**: Adds table of contents based on markdown headings.
65-
- **Custom Link and Image Processing**: Handles internal links, external links, and image transformations.
66-
- **Caching**: Uses NodeCache to optimize image dimensions and other recurring data.
67-
7+
- **Content Management**: Write in Obsidian with markdown, publish with git
8+
- **Modern Stack**: Built with Nuxt 3, Vue 3 Composition API, and D3.js
9+
- **Rich Content**: Support for code highlighting, Mermaid diagrams, and interactive elements
10+
- **Image Optimization**: Automatic Cloudinary integration for responsive images
11+
- **Smart Filtering**: Multi-layer content visibility protection
12+
- **Developer Experience**: Hot reload, TypeScript support, and detailed debugging
6813

69-
This will set up the project, including the repo and its dependencies for rapid prototyping.
14+
## Content Management
7015

71-
---
16+
### Writing Content
7217

73-
## Dependencies
18+
Content is written in Markdown within Obsidian and organized into sections:
19+
- **Blog Posts**: Main articles and thoughts
20+
- **Week Notes**: Weekly updates in YYYY-WW format
21+
- **Projects**: Portfolio and project showcases
22+
- **Reading Notes**: Book summaries and reflections
23+
- **Robots**: AI and automation experiments
24+
- **Prompts**: Writing prompts and exercises
7425

75-
- **Nuxt 3**
76-
- **Vue 3**
77-
- **Node.js**
78-
- **Cloudinary**: For image hosting and optimization.
79-
- **Shiki**: For syntax highlighting.
26+
### Frontmatter Options
8027

28+
Control post visibility and metadata with frontmatter:
29+
```yaml
8130
---
82-
83-
## Development
84-
85-
To set up the project for development, follow these steps:
86-
87-
1. Install dependencies:
88-
89-
```bash
90-
yarn install
91-
```
92-
93-
2. Start the development server:
94-
95-
```bash
96-
yarn dev
97-
```
98-
99-
3. Open the project at `http://localhost:3000`.
100-
31+
title: Post Title
32+
date: 2024-01-01
33+
hidden: true/false # Completely excludes content from processing
34+
share: true/false # Required for drafts/robots
35+
dek: Description # Required for week notes
10136
---
102-
103-
## Production
104-
105-
To build and preview the project in production mode:
106-
107-
1. Build the project for production:
108-
109-
```bash
110-
yarn build
11137
```
11238

113-
2. Preview the production build locally:
39+
## Content Processing Pipeline
11440

115-
```bash
116-
yarn preview
117-
```
41+
### 1. Import Stage (import.mjs)
11842

119-
---
43+
First line of defense - reads from Obsidian vault and performs initial filtering:
44+
- Immediately skips any content with hidden: true
45+
- Filters sensitive content (drafts, robots) based on share status
46+
- Auto-corrects week note dates from filenames
47+
- Generates metadata (word count, reading time)
12048

121-
## Deployment
49+
### 2. Processing Stage (processMarkdown.mjs)
12250

123-
Website2 can be deployed on Netlify or any other hosting provider that supports static site generation.
51+
Second line of defense - transforms content for web display:
52+
- Double-checks hidden status before processing
53+
- Converts markdown to HTML
54+
- Handles syntax highlighting
55+
- Processes Mermaid diagrams
56+
- Optimizes images via Cloudinary
57+
- Generates manifest-lite.json for quick access
12458

125-
To deploy to Netlify, use the following badge to monitor the deployment status:
59+
### 3. Runtime Stage (useProcessedMarkdown.ts)
12660

127-
[![Netlify Status](https://api.netlify.com/api/v1/badges/981b9e46-6878-4ddb-a716-2713c5f3e412/deploy-status)](https://app.netlify.com/sites/ejfox-nuxt-template/deploys)
61+
Final line of defense - handles dynamic content filtering:
62+
- Triple-checks hidden status before serving
63+
- Applies visibility rules
64+
- Sorts and groups content
65+
- Provides composables for content access
66+
- Manages special section requirements
12867

129-
---
130-
131-
## Embedding Executable Gists
132-
133-
### Overview
134-
135-
**Website2** allows you to embed executable Gists directly into markdown files. These Gists can contain a combination of HTML, JS, CSS, and other file types to create interactive visualizations or complex web components.
136-
137-
### Basic Usage
138-
139-
To embed a Gist, use the following syntax in your markdown:
140-
141-
```markdown
142-
[gist id="your-gist-id-here"]
143-
```
144-
145-
This will fetch and render all relevant files from the Gist automatically. Supported file types include:
146-
- `index.html`: The primary HTML file.
147-
- `script.js`: JavaScript code for interactivity.
148-
- `styles.css`: Optional styles for custom design.
149-
- `data.json`: Any accompanying data for dynamic content.
150-
151-
### Advanced Features
152-
153-
- **Scrollytelling**: Integrate scrollytelling events by embedding Gists and defining scroll-triggered behaviors in your Gist's JS.
154-
- **Custom Data Handling**: Pass custom JSON data from the Gist into your visualizations.
155-
156-
---
157-
158-
## API Endpoints
159-
160-
Website2 includes a few core API endpoints to interact with the content:
161-
162-
1. **Fetch Single Post**
163-
- **Endpoint**: `/api/posts/:slug`
164-
- **Description**: Fetches the content and metadata for a single post.
165-
- **Returns**: JSON object containing the post's HTML and metadata (title, date, tags).
166-
167-
2. **Fetch All Posts**
168-
- **Endpoint**: `/api/manifest-lite`
169-
- **Description**: Retrieves a list of all posts with basic metadata.
170-
- **Returns**: Array of post metadata (slug, title, date, etc.).
171-
172-
---
173-
174-
## Composables
175-
176-
The system provides several composables to fetch and process content:
68+
## Visibility Rules
17769

178-
1. **`getPostBySlug(slug)`**
179-
- Fetches the content and metadata of a post by its slug.
180-
- Example:
181-
```js
182-
const post = await getPostBySlug('my-post-slug')
183-
```
70+
### Regular Posts
71+
- Visible by default
72+
- Setting hidden: true prevents the post from being processed at all
18473

185-
2. **`getAllPosts()`**
186-
- Retrieves all posts, filtered and sorted.
187-
- Example:
188-
```js
189-
const posts = await getAllPosts()
190-
```
74+
### Drafts & Robots
75+
- Hidden by default
76+
- Require share: true to be processed
77+
- Setting hidden: true overrides share status
19178

192-
3. **`getPostsWithContent(limit, offset)`**
193-
- Fetches a subset of posts with their full content.
194-
- Example:
195-
```js
196-
const posts = await getPostsWithContent(10, 0)
197-
```
79+
### Week Notes
80+
- Visible by default if they have a description
81+
- Use YYYY-WW filename format
82+
- Setting hidden: true prevents processing
19883

199-
4. **`getNextPrevPosts(slug)`**
200-
- Fetches the next and previous posts relative to the current one.
201-
- Example:
202-
```js
203-
const { next, prev } = await getNextPrevPosts('current-slug')
204-
```
205-
206-
---
207-
208-
## Markdown Processing
209-
210-
**Website2** uses a highly customized markdown processing pipeline, featuring:
211-
212-
- **Unified & Remark**: The core libraries used for parsing and transforming markdown into HTML.
213-
- **Plugins**: Extends functionality with plugins such as:
214-
- `remarkParse`: Parses the markdown.
215-
- `remarkGfm`: Adds support for GitHub Flavored Markdown (tables, task lists).
216-
- `remarkObsidian`: Enables Obsidian-style internal linking.
217-
- `rehypeStringify`: Converts processed markdown into HTML.
218-
- `rehypePrettyCode`: Syntax highlighting using Shiki.
219-
220-
### Custom Features
221-
222-
- **Cloudinary Image Optimization**: Dynamically transforms and optimizes images via Cloudinary, ensuring responsive design and fast load times.
223-
- **Table of Contents (TOC)**: Automatically generates a table of contents from the headings in markdown files.
224-
- **Custom Link & Image Processing**: Special handling for internal links, external links (with icons), and Cloudinary-optimized images.
225-
226-
---
227-
228-
## Caching
229-
230-
Website2 uses **NodeCache** to cache data such as image dimensions and other frequently accessed resources, reducing the need for redundant API calls and improving performance.
231-
232-
- **TTL (Time-to-Live)**: Set to 1 day for most cached resources.
233-
- **Auto-Save**: The cache is saved to disk on exit, ensuring persistence between sessions.
234-
235-
---
236-
237-
## Output
238-
239-
Processed markdown files, including their HTML and metadata, are saved in the `dist/processed` directory. Metadata includes:
240-
- **Title**
241-
- **Slug**
242-
- **Date**
243-
- **Modified Date**
244-
- **Word Count**
245-
- **Reading Time**
246-
- **Image and Link Count**
84+
## Development
24785

248-
These files are available through the `/api` endpoints and used to power the website's content delivery.
86+
### Prerequisites
87+
- Node.js 18+
88+
- Yarn
89+
- Obsidian vault with content
24990

250-
---
91+
### Setup
92+
1. Clone the repository
93+
2. Install dependencies: `yarn install`
94+
3. Copy .env.example to .env and configure
95+
4. Run development server: `yarn dev`
25196

252-
## Advanced Features
97+
### Content Processing
98+
1. Write content in Obsidian
99+
2. Run `yarn process` to rebuild content
100+
3. Changes appear in development server
253101

254-
1. **Scrollytelling Integration**: Easily trigger animations or events when elements come into view, adding interactivity to your markdown content.
255-
256-
2. **Custom Data Handling in Gists**: Pass JSON or other custom data directly from a Gist into your JavaScript for dynamic visualizations.
257-
258-
---
102+
### Environment Variables
103+
- `CLOUDINARY_*`: Image optimization settings
104+
- `DEBUG_IMPORT`: Show import process details
105+
- `DEBUG_PROCESS`: Show markdown processing details
106+
- `DEBUG_POSTS`: Show post filtering details
259107

260-
## Development Notes
108+
## License
261109

262-
- The `processMarkdown.mjs` script is responsible for transforming markdown into HTML. It applies several transformations, including image optimization, code syntax highlighting, and GitHub Flavored Markdown support.
263-
- Gist embedding is handled by the markdown processor, fetching all Gist files and rendering them interactively on the page.
264-
- The system is highly customizable, with support for additional markdown plugins and transformations.
110+
MIT License - See LICENSE file for details

components/PostMetadata.vue

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
<template>
22
<div class="w-full text-sm text-zinc-600 dark:text-zinc-400 flex flex-wrap items-center gap-x-4 gap-y-2 monospace">
3+
<!-- we wanna include the folder name here -->
4+
<span class="flex items-center metadata-item text-xs tracking-widest" ref="folderRef" v-if="doc.slug">
5+
<!-- <UIcon name="bi:folder" class="mr-2 text-zinc-400 dark:text-zinc-600" /> -->
6+
7+
/{{ doc.slug.split('/')[0] }}/
8+
</span>
9+
10+
<!-- <pre>{{ doc }}</pre> -->
311

412
<!-- Draft status -->
513
<span v-if="doc.draft" class="flex items-center text-red-500 dark:text-red-400 font-sans metadata-item"
@@ -14,6 +22,9 @@
1422
<time>{{ formatBlogDate(new Date(doc.date)) }}</time>
1523
</span>
1624

25+
26+
27+
1728
<!-- Reading Time -->
1829
<span v-if="doc.readingTime > 1" class="flex items-center metadata-item" ref="readingTimeRef">
1930
<UIcon name="bi:clock-history" class="mr-2 text-zinc-400 dark:text-zinc-600" />
@@ -63,9 +74,10 @@ const readingTimeRef = ref(null)
6374
const wordCountRef = ref(null)
6475
const imageCountRef = ref(null)
6576
const linkCountRef = ref(null)
77+
const folderRef = ref(null)
6678
6779
const formatNumber = format(',d')
68-
const formatBlogDate = timeFormat('%b %Y')
80+
const formatBlogDate = timeFormat('%b %d %Y')
6981
7082
const formatRelativeTime = (date) => {
7183
try {
@@ -86,7 +98,8 @@ const animateItems = async () => {
8698
readingTimeRef.value,
8799
wordCountRef.value,
88100
imageCountRef.value,
89-
linkCountRef.value
101+
linkCountRef.value,
102+
folderRef.value
90103
].filter(Boolean)
91104
92105
if (!items.length) return

0 commit comments

Comments
 (0)