Compare commits

...

3 Commits

Author SHA1 Message Date
1b64116fa3 notepad: ◈ INCIDENT REPORT #8291-Δ :: Personnel discovered 2025-12-18 15:13:02 -07:00
7aa073f241 Enhance git-commit utility with --accept, --push, and --no-push flags
- Add new CLI options for auto‑accept, auto‑push, and skip‑push behavior.
- Update usage examples and help text in `git-commit.js`.
- Revise `CLAUDE.md` to reflect the new commit workflow and options.
- Adjust script logic to handle flags without user prompts.
2025-12-18 15:10:04 -07:00
d336705c5c Update .gitignore, CLAUDE.md, and Experience component
- Add AGENTS.md symlink to .gitignore for tracking documentation.
- Expand CLAUDE.md with core development commands, utilities, image conversion scripts, and detailed architecture sections.
- Modify src/components/sections/Experience.astro to reflect updated schema and presentation.
2025-12-18 15:08:04 -07:00
4 changed files with 141 additions and 73 deletions

3
.gitignore vendored
View File

@ -33,3 +33,6 @@ src/utils/.env
.specstory/**
.specstory/
.cursorindexingignore
# AGENTS.md symlink
AGENTS.md

142
CLAUDE.md
View File

@ -1,93 +1,113 @@
```
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Development Commands
### Build the project
### Core Development
```bash
npm run build
pnpm dev # Run development server
pnpm build # Build the project
pnpm preview # Build and preview with Wrangler
pnpm deploy # Build and deploy to Cloudflare Pages
```
### Run development server
### Utilities
```bash
npm run dev
```
# Git commit message automation
pnpm commit # Interactive: review, accept/edit, optionally push
pnpm commit --accept # Auto-accept message, prompt for push
pnpm commit --accept --no-push # Auto-accept and commit without pushing
pnpm commit --accept --push # Fully automated: accept and push
### Lint the codebase
```bash
npm run lint
```
pnpm notepad # Quick note-taking utility
### Run tests
```bash
npm run test
```
### Build and preview a specific page
```bash
npm run build:page <page-name>
```
### Preview the blog section
```bash
npm run preview:blog
# Image conversion to AVIF format
pnpm run convert:avif:all # Convert all images
pnpm run convert:avif:jpeg # Convert JPEG only
pnpm run convert:avif:png # Convert PNG only
```
## High-Level Architecture
The website follows a clean separation of concerns with three distinct layers:
This is an Astro-based portfolio and blog site deployed on Cloudflare Pages. The architecture follows a content-driven approach with three distinct layers:
1. **Content Layer** - Markdown/MDX files containing structured content located in `src/content/**`
2. **Component Layer** - Reusable UI components built with Astro, organized by purpose and functionality
3. **Layout & Structure Layer** - Page templates that orchestrate component composition across different sections
### 1. Content Layer (`src/content/**`)
Content is managed via Astro's Content Collections API with schema validation defined in `src/content.config.ts`:
### Content Structure
- All content is stored in Markdown/MDX format within the `src/content/**` directory
- Organized into logical groups:
- `sections/*` - About, Experience, Skills, Featured Project
- `pages/contact.mdx` - Contact form data
- `blog/*.mdx` - Blog posts with structured metadata and frontmatter
- **`blog/`** - Blog posts as MDX files
- Schema: title, description, pubDate, heroImage (optional), featured (boolean), category, tags
- Posts are sorted by pubDate (newest first)
### Component Structure
The component architecture follows a consistent pattern with different types of components:
- **`sections/`** - Homepage section content (hero, experience, skills, featured-project)
- Each section has a custom schema for its specific data needs
- Experience entries include systemId, status, dates, company, role, achievements, links
- Skills entries include domain, tools, proficiency
**Core Components**: Reusable elements like `BlogCard`, `FormattedDate`, and `Navigation`
- **`pages/`** - Page-specific content (contact form configuration)
- Includes form labels, social links, subject options
**Section Components**: Page-specific sections like `Experience`, `Skills`, and `FeaturedProject`
### 2. Component Layer
Components are organized by purpose:
**Layout Components**: Base templates that provide shared styling and structure (e.g., `BaseLayout`, `BlogPost`)
- **Core UI**: `BlogCard`, `FormattedDate`, `Navigation`, `Footer`, `GridOverlay`
- **Blog-specific**: `BlogFilters`, `ReadingProgress`, `TableOfContents`, `PostNavigation`, `RelatedPosts`
- **Section components**: `Hero`, `Experience`, `Skills`, `FeaturedProject`
### Component Relationships
### 3. Page & Layout Layer
- **Layouts**: `BaseLayout` (shared structure), `BlogPost` (blog template)
- **Routes**: Static routes in `src/pages/` with dynamic blog routes via `[...slug].astro`
**Blog Section Flow**: The blog page (`src/pages/blog/index.astro`) fetches all blog posts via `getCollection()` and organizes content into three distinct sections:
- Featured post (first with `featured: true`)
- Editor's picks (next 3 posts after featured)
- Latest posts (all posts for filtering)
## Data Flow Patterns
**Content Rendering Pattern**: All components use a consistent data model where properties are passed through props. For example, `BlogCard` receives title, description, pubDate, and heroImage as parameters.
### Blog Index (`src/pages/blog/index.astro`)
1. Fetches all posts via `getCollection('blog')`
2. Sorts by pubDate (newest first)
3. Identifies featured post (first with `featured: true` or fallback to latest)
4. Renders featured hero + filterable grid of all posts
5. Extracts unique categories for filter UI
### Data Flow Architecture
### Individual Blog Posts (`src/pages/blog/[...slug].astro`)
1. Uses `getStaticPaths()` to generate all blog post routes
2. For each post, calculates:
- Previous/next posts (by date)
- Related posts (matching category or shared tags, limited to 3)
- Reading time (based on word count, 200 wpm)
3. Passes everything to `BlogPost` layout which handles headings, navigation, related posts
### Content Collections
All content follows the schema validation pattern:
```
Content Files → Astro Content API → Page Components → UI Components → Final Render
MDX file → src/content.config.ts schema → getCollection() → Component props
```
- **Content Collection**: Configured in `src/content.config.ts` with schema validation for frontmatter
- **Data Fetching**: Uses Astro's content API to load and transform data from Markdown/MDX files
- **Component Composition**: Pages assemble components based on fetched data, creating dynamic and responsive layouts
## Key Technical Patterns
### Design System Elements
- **Styling System**: Consistent use of classes like `.animate-on-scroll`, `.stagger-*`, and `.border-white/[0.1]`
- **Navigation**: Responsive mobile menu with smooth transitions
- **Accessibility**: Proper ARIA attributes, keyboard navigation support
- **Performance**: Optimized image loading and lazy rendering (using AVIF/WebP formats)
### Image Handling
- Assets in `src/assets/` are processed by Astro (use relative paths in frontmatter)
- Static files in `public/media/` are served as-is (use absolute paths like `/media/file.mp4`)
- AVIF conversion utility available for optimization
### Technical Features
- **AI Integration**: Blog post highlights AI/ML usage in technical workflow
- **Interactive Elements**: Form dropdowns, modal responses for contact form
- **Animation System**: Scroll-triggered animations with staggered effects
- **Responsive Design**: Mobile-first approach with viewport-specific classes and media queries
### Styling
- Tailwind CSS v4 via Vite plugin
- Custom animation classes: `.animate-on-scroll`, `.slide-up`, `.stagger-*`, `.fade-in`
- Monospace font used for technical labels and metadata
The architecture is highly maintainable with clear separation of content from presentation. The use of Astro's data API and component system enables dynamic content generation while maintaining a consistent visual language throughout the site.
```
### Deployment
- Cloudflare Pages adapter configured in `astro.config.mjs`
- Image service set to "compile" mode
- Platform proxy enabled for development
## Blog Post Creation Workflow
1. Create `.mdx` file in `src/content/blog/` (filename becomes URL slug)
2. Add required frontmatter: title, description, pubDate
3. Optionally add: heroImage, featured, category, tags
4. Write content using Markdown/MDX with embedded JSX/HTML
5. Images can reference `src/assets/` (relative) or `public/media/` (absolute)
## Utility Scripts
- **`src/utils/convert-to-avif.js`** - Converts images to AVIF format with quality options
- **`src/utils/git-commit.js`** - Auto-generates commit messages from staged changes
- **`src/utils/notepad.js`** - Quick note-taking utility

View File

@ -11,3 +11,5 @@ tags: ['opinions', 'satire']
**[2025-12-18 13:55:20]** This is my personal notepad. You will find nothing of interest here.
**[2025-12-18 14:32:02]** im trying to figure out what my favorite color is
**[2025-12-18 15:13:02]** ◈ INCIDENT REPORT #8291-Δ :: Personnel discovered singing in backwards Sumerian :: Object Class: K̵͉̈́E̴̹͝T̷͎̓E̸̳̿R̶̰̈́ :: The stars are RIGHT :: Recovered text fragment: 'ph'nglui mglw'nafh C̴t̷h̵u̷l̴h̵u̸ R'lyeh wgah'nagl fhtagn' :: Seventeen (17) observers report seeing ████ behind the moon :: Directive: OBSERVE. DO NOT ENGAGE. :: In his house at R'lyeh, dead ██████ waits dreaming ::

View File

@ -373,7 +373,7 @@ ${colors.bright}Usage:${colors.reset}
${colors.cyan}git add <files>${colors.reset}
2. Run this script:
${colors.cyan}pnpm commit${colors.reset}
${colors.cyan}pnpm commit [options]${colors.reset}
3. Review the AI-generated commit message
@ -388,7 +388,23 @@ ${colors.bright}Requirements:${colors.reset}
- Get your key from: ${colors.dim}https://openrouter.ai/keys${colors.reset}
${colors.bright}Options:${colors.reset}
--help, -h Show this help message
--help, -h Show this help message
--accept, -a Auto-accept the generated commit message without prompting
--push, -p Automatically push to remote after committing
--no-push, -n Skip the push prompt (commit only, don't push)
${colors.bright}Examples:${colors.reset}
${colors.cyan}pnpm commit${colors.reset}
Interactive mode - review, accept/edit, optionally push
${colors.cyan}pnpm commit --accept${colors.reset}
Auto-accept commit message, still prompt for push
${colors.cyan}pnpm commit --accept --no-push${colors.reset}
Auto-accept and commit without pushing
${colors.cyan}pnpm commit --accept --push${colors.reset}
Fully automated - accept and push without any prompts
`);
}
@ -403,6 +419,11 @@ async function main() {
process.exit(0);
}
// Check for flags
const autoAccept = args.includes('--accept') || args.includes('-a');
const autoPush = args.includes('--push') || args.includes('-p');
const noPush = args.includes('--no-push') || args.includes('-n');
// Load environment variables
loadEnv();
@ -428,27 +449,49 @@ async function main() {
// Generate commit message using OpenRouter
const generatedMessage = await generateCommitMessage(context);
// Get user approval
const rl = createReadlineInterface();
const { approved, message } = await getUserApproval(generatedMessage, rl);
let approved = autoAccept;
let message = generatedMessage;
if (!approved) {
console.log(`\n${colors.yellow}⏭️ Commit cancelled${colors.reset}`);
// Get user approval if not auto-accepting
if (!autoAccept) {
const rl = createReadlineInterface();
const result = await getUserApproval(generatedMessage, rl);
approved = result.approved;
message = result.message;
rl.close();
process.exit(0);
if (!approved) {
console.log(`\n${colors.yellow}⏭️ Commit cancelled${colors.reset}`);
process.exit(0);
}
} else {
console.log(`\n${colors.bright}${colors.green}📝 Generated commit message:${colors.reset}`);
console.log(`${colors.dim}${'─'.repeat(60)}${colors.reset}`);
console.log(message);
console.log(`${colors.dim}${'─'.repeat(60)}${colors.reset}\n`);
console.log(`${colors.cyan}Auto-accepting with --accept flag${colors.reset}`);
}
// Create the commit
const commitSuccess = createCommit(message);
if (!commitSuccess) {
rl.close();
process.exit(1);
}
// Ask to push
const shouldPush = await askToPush(rl);
rl.close();
// Handle push logic
let shouldPush = false;
if (noPush) {
console.log(`${colors.cyan}Skipping push with --no-push flag${colors.reset}`);
} else if (autoPush) {
console.log(`${colors.cyan}Auto-pushing with --push flag${colors.reset}`);
shouldPush = true;
} else {
const rl = createReadlineInterface();
shouldPush = await askToPush(rl);
rl.close();
}
if (shouldPush) {
pushToRemote();