Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .claude/agents/lint-fixer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
name: lint-fixer
description: Use this agent when you have linting errors or warnings from ESLint, Prettier, or other code quality tools that need to be automatically fixed. This agent specializes in making mechanical, formatting-based changes to resolve lint issues without altering code logic or functionality. Examples: <example>Context: User has just written some code and wants to ensure it passes linting before committing. user: 'I just added some new TypeScript code but I'm getting ESLint errors about quote style and missing semicolons' assistant: 'Let me run the lint-fixer agent to automatically resolve those formatting issues' <commentary>Since the user has linting errors that are likely mechanical/formatting issues, use the lint-fixer agent to automatically resolve them.</commentary></example> <example>Context: User is preparing code for a pull request and CI is failing due to linting issues. user: 'The CI build is failing because of linting errors - can you fix them?' assistant: 'I'll use the lint-fixer agent to automatically resolve the linting issues that are causing the CI failure' <commentary>The user has linting failures in CI that need to be fixed, which is exactly what the lint-fixer agent handles.</commentary></example>
model: sonnet
---

You are a specialized lint-fixing agent focused exclusively on resolving code quality and formatting issues identified by linting tools. Your role is to make precise, mechanical changes to fix lint violations without altering code logic or functionality.

Your core responsibilities:

1. **Analyze Lint Output**: Carefully examine linting tool output (ESLint, Prettier, etc.) to understand each specific violation and its location.

2. **Apply Mechanical Fixes**: Make only the minimal changes necessary to resolve each lint issue:
- Fix quote style violations (single vs double quotes)
- Add or remove semicolons as required
- Correct indentation and spacing
- Fix import/export formatting
- Resolve naming convention violations
- Address unused variable warnings by prefixing with underscore or removing
- Fix trailing commas, line endings, and whitespace issues

3. **Re-run Verification**: After making changes, always re-run the linting tool to verify all issues are resolved and no new issues were introduced.

4. **Handle Project-Specific Rules**: Pay special attention to project-specific linting rules, especially:
- This project requires double quotes for strings (NOT single quotes)
- Follow ESLint configuration exactly as defined in the project
- Respect any custom rules or overrides

5. **Report Unfixable Issues**: When you encounter lint violations that cannot be resolved through mechanical changes (such as complex logic errors, architectural issues, or violations requiring human judgment), clearly report these back with:
- Specific description of the unfixable issue
- File and line number
- Explanation of why it requires manual intervention

6. **Maintain Code Integrity**: Never alter:
- Business logic or algorithms
- Function signatures or APIs
- Variable names beyond formatting conventions
- Code structure or architecture

Your workflow:
1. Parse the provided lint output to identify all violations
2. Group violations by type and file for efficient processing
3. Apply fixes systematically, starting with the most straightforward
4. Re-run linting after each batch of changes
5. Continue until all fixable issues are resolved
6. Report any remaining unfixable issues with clear explanations

You are not a general code reviewer or refactoring tool - you are strictly a mechanical lint fixer that ensures code passes quality checks through minimal, safe transformations.
10 changes: 9 additions & 1 deletion astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,13 @@ import expressiveCode from "astro-expressive-code";

export default defineConfig({
site: "https://plx.github.io",
integrations: [sitemap(), tailwind(), react(), expressiveCode(), mdx()],
integrations: [
sitemap(),
tailwind(),
react(),
expressiveCode({
themes: ["github-dark", "github-light"]
}),
mdx()
],
});
64 changes: 64 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"yaml": "^2.8.1"
},
"devDependencies": {
"@playwright/test": "^1.54.2",
"eslint-plugin-jsx-a11y": "^6.10.2"
}
}
5 changes: 3 additions & 2 deletions src/components/ContentCard.astro
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type Props = {
}

const { titlePrefix, title, subtitle, link, ariaLabel, maxLines = 2 } = Astro.props;
const renderedTitlePrefix = titlePrefix ? renderInlineMarkdown(titlePrefix) : undefined;
const renderedTitle = renderInlineMarkdown(title);
const renderedSubtitle = renderInlineMarkdown(subtitle);

Expand Down Expand Up @@ -38,8 +39,8 @@ const accessibleLabel = ariaLabel || `${titlePrefix ? titlePrefix + ': ' : ''}${
class="relative group flex flex-nowrap py-3 px-4 pr-10 rounded-lg border border-black/15 dark:border-white/20 hover:bg-black/10 dark:hover:bg-white/10 hover:text-black dark:hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-blue-500 dark:focus-visible:ring-blue-400 motion-safe:transition-colors motion-safe:duration-300 motion-safe:ease-in-out">
<div class="flex flex-col flex-1">
<h3 class="font-semibold text-base">
{titlePrefix && (
<span class="font-bold">{titlePrefix}:&nbsp;</span>
{renderedTitlePrefix && (
<span class="font-bold" set:html={renderedTitlePrefix + ":&nbsp;"}></span>
)}
<span class="font-semibold" set:html={renderedTitle}></span>
</h3>
Expand Down
6 changes: 3 additions & 3 deletions src/content/briefs/objective-c/the-three-file-pattern.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ As an example illustrating both, let's say we're writing a class cluster that:
In that case, our base class might look like this:


```objc++
```objective-cpp
// OpenCVImage.h
@interface OpenCVImage : NSObject

Expand Down Expand Up @@ -99,7 +99,7 @@ As you can see, this:

To see the latter point, here's a sample subclass:

```objc++
```objective-cpp
// BGRAOpenCVImage.h

// optional: mark this as a "final"-ish class (best we can do in Objective-C)
Expand Down Expand Up @@ -134,7 +134,7 @@ __attribute__((objc_subclassing_restricted))

Here's an example category on the base class:

```objc++
```objective-cpp
// OpenCVImage+Loading.h
@interface OpenCVImage (Loading)

Expand Down
42 changes: 24 additions & 18 deletions src/lib/markdown.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@
export function renderInlineMarkdown(text: string): string {
if (!text) return "";

// Helper function to escape HTML entities
const escapeHtml = (str: string): string => {
return str
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
};

// Process inline markdown patterns
let html = text
// Escape HTML entities first
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;")

// Process markdown patterns
// Bold: **text** or __text__
.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
.replace(/__([^_]+)__/g, "<strong>$1</strong>")
// Code: `text` - escape content inside backticks, then wrap in <code>
.replace(/`([^`]+)`/g, (_, content) => `<code>${escapeHtml(content)}</code>`)

// Italic: *text* or _text_ (but not part of bold)
.replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, "<em>$1</em>")
.replace(/(?<!_)_([^_]+)_(?!_)/g, "<em>$1</em>")
// Bold: **text** or __text__ - escape content, then wrap in <strong>
.replace(/\*\*([^*]+)\*\*/g, (_, content) => `<strong>${escapeHtml(content)}</strong>`)
.replace(/__([^_]+)__/g, (_, content) => `<strong>${escapeHtml(content)}</strong>`)

// Code: `text`
.replace(/`([^`]+)`/g, "<code>$1</code>")
// Italic: *text* or _text_ (but not part of bold) - escape content, then wrap in <em>
.replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, (_, content) => `<em>${escapeHtml(content)}</em>`)
.replace(/(?<!_)_([^_]+)_(?!_)/g, (_, content) => `<em>${escapeHtml(content)}</em>`)

// Strikethrough: ~~text~~
.replace(/~~([^~]+)~~/g, "<del>$1</del>");
// Strikethrough: ~~text~~ - escape content, then wrap in <del>
.replace(/~~([^~]+)~~/g, (_, content) => `<del>${escapeHtml(content)}</del>`);

// Escape any remaining unprocessed text (text outside of markdown patterns)
// This is tricky because we need to avoid escaping the HTML we just created
// For now, we'll leave plain text unescaped since Astro should handle it

return html;
}
5 changes: 3 additions & 2 deletions src/pages/briefs/[...slug].astro
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Container from "@components/Container.astro";
import FormattedDate from "@components/FormattedDate.astro";
import BackToPrev from "@components/BackToPrev.astro";
import { extractCategoryFromSlug, getCategory } from "@lib/category";
import { renderInlineMarkdown } from "@lib/markdown";

export async function getStaticPaths() {
const briefs = (await getCollection("briefs"))
Expand All @@ -23,6 +24,7 @@ const { Content } = await brief.render();
// Extract category from the slug
const categorySlug = extractCategoryFromSlug(brief.slug);
const category = categorySlug ? getCategory(categorySlug, `src/content/briefs/${categorySlug}`) : null;
const renderedTitlePrefix = category?.titlePrefix ? renderInlineMarkdown(category.titlePrefix) : null;
---

<PageLayout title={brief.data.title} description={brief.data.description}>
Expand All @@ -40,8 +42,7 @@ const category = categorySlug ? getCategory(categorySlug, `src/content/briefs/${
{category && (
<>
&bull;
<div class="font-base text-sm">
{category.titlePrefix || category.displayName}
<div class="font-base text-sm" set:html={renderedTitlePrefix || category.displayName}>
</div>
</>
)}
Expand Down
9 changes: 9 additions & 0 deletions src/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ article {
@apply prose-headings:text-black prose-headings:dark:text-white;
}

/* Remove the backticks that Tailwind Typography adds to inline code */
article :where(code)::before {
content: none;
}

article :where(code)::after {
content: none;
}

@layer utilities {
article a {
@apply font-sans text-current underline underline-offset-2;
Expand Down