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/**
.specstory/ .specstory/
.cursorindexingignore .cursorindexingignore
# AGENTS.md symlink
AGENTS.md

142
CLAUDE.md
View File

@ -1,93 +1,113 @@
```
# CLAUDE.md # CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Development Commands ## Development Commands
### Build the project ### Core Development
```bash ```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 ```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 pnpm notepad # Quick note-taking utility
```bash
npm run lint
```
### Run tests # Image conversion to AVIF format
```bash pnpm run convert:avif:all # Convert all images
npm run test pnpm run convert:avif:jpeg # Convert JPEG only
``` pnpm run convert:avif:png # Convert PNG only
### Build and preview a specific page
```bash
npm run build:page <page-name>
```
### Preview the blog section
```bash
npm run preview:blog
``` ```
## High-Level Architecture ## 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/**` ### 1. Content Layer (`src/content/**`)
2. **Component Layer** - Reusable UI components built with Astro, organized by purpose and functionality Content is managed via Astro's Content Collections API with schema validation defined in `src/content.config.ts`:
3. **Layout & Structure Layer** - Page templates that orchestrate component composition across different sections
### Content Structure - **`blog/`** - Blog posts as MDX files
- All content is stored in Markdown/MDX format within the `src/content/**` directory - Schema: title, description, pubDate, heroImage (optional), featured (boolean), category, tags
- Organized into logical groups: - Posts are sorted by pubDate (newest first)
- `sections/*` - About, Experience, Skills, Featured Project
- `pages/contact.mdx` - Contact form data
- `blog/*.mdx` - Blog posts with structured metadata and frontmatter
### Component Structure - **`sections/`** - Homepage section content (hero, experience, skills, featured-project)
The component architecture follows a consistent pattern with different types of components: - 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: ## Data Flow Patterns
- Featured post (first with `featured: true`)
- Editor's picks (next 3 posts after featured)
- Latest posts (all posts for filtering)
**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 ## Key Technical Patterns
- **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
### Design System Elements ### Image Handling
- **Styling System**: Consistent use of classes like `.animate-on-scroll`, `.stagger-*`, and `.border-white/[0.1]` - Assets in `src/assets/` are processed by Astro (use relative paths in frontmatter)
- **Navigation**: Responsive mobile menu with smooth transitions - Static files in `public/media/` are served as-is (use absolute paths like `/media/file.mp4`)
- **Accessibility**: Proper ARIA attributes, keyboard navigation support - AVIF conversion utility available for optimization
- **Performance**: Optimized image loading and lazy rendering (using AVIF/WebP formats)
### Technical Features ### Styling
- **AI Integration**: Blog post highlights AI/ML usage in technical workflow - Tailwind CSS v4 via Vite plugin
- **Interactive Elements**: Form dropdowns, modal responses for contact form - Custom animation classes: `.animate-on-scroll`, `.slide-up`, `.stagger-*`, `.fade-in`
- **Animation System**: Scroll-triggered animations with staggered effects - Monospace font used for technical labels and metadata
- **Responsive Design**: Mobile-first approach with viewport-specific classes and media queries
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 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 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} ${colors.cyan}git add <files>${colors.reset}
2. Run this script: 2. Run this script:
${colors.cyan}pnpm commit${colors.reset} ${colors.cyan}pnpm commit [options]${colors.reset}
3. Review the AI-generated commit message 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} - Get your key from: ${colors.dim}https://openrouter.ai/keys${colors.reset}
${colors.bright}Options:${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); 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 // Load environment variables
loadEnv(); loadEnv();
@ -428,27 +449,49 @@ async function main() {
// Generate commit message using OpenRouter // Generate commit message using OpenRouter
const generatedMessage = await generateCommitMessage(context); const generatedMessage = await generateCommitMessage(context);
// Get user approval let approved = autoAccept;
const rl = createReadlineInterface(); let message = generatedMessage;
const { approved, message } = await getUserApproval(generatedMessage, rl);
if (!approved) { // Get user approval if not auto-accepting
console.log(`\n${colors.yellow}⏭️ Commit cancelled${colors.reset}`); if (!autoAccept) {
const rl = createReadlineInterface();
const result = await getUserApproval(generatedMessage, rl);
approved = result.approved;
message = result.message;
rl.close(); 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 // Create the commit
const commitSuccess = createCommit(message); const commitSuccess = createCommit(message);
if (!commitSuccess) { if (!commitSuccess) {
rl.close();
process.exit(1); process.exit(1);
} }
// Ask to push // Handle push logic
const shouldPush = await askToPush(rl); let shouldPush = false;
rl.close();
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) { if (shouldPush) {
pushToRemote(); pushToRemote();