feat: add side projects gallery with filtering - #14441
Conversation
Create a new /side-projects gallery page for PostHog team members to share interesting side projects. Features include: - Gallery page with dynamic tag filtering at /side-projects - Individual project detail pages with author, GitHub, and live URL links - Contribution guide for adding new projects - Link from LLM analytics docs at /docs/llm-analytics/demos Initial projects added: - LLM Analytics Apps (multi-provider CLI demo) - Andy's Daily Factoids (AI factoid generator) - Repo Stalker (GitHub PR/Issue AI assistant) - GitHub Standup Agent (AI standup summarizer) - Anomstack (anomaly detection with LLM alerts) - HogFlix / PostHog Demo 3000 (full PostHog demo) - Plot Agent (AI data visualization) Architecture follows existing Templates/Apps pattern with MDX content, frontmatter-based filtering, and Gatsby page generation.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 55ac38556c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| useEffect(() => { | ||
| const params = new URLSearchParams(location?.search) | ||
| const filter = params.get('filter') | ||
| const value = params.get('value') | ||
|
|
||
| if (filter === 'tags' && value) { | ||
| filterProjects(value) | ||
| } |
There was a problem hiding this comment.
Reset filters when URL query is cleared
The URL-driven filter state only updates when filter=tags and value are present, so if a user navigates from a filtered URL back to /side-projects without query params (e.g., browser back/forward or a link to the base page), the component keeps the old filteredProjects/currentFilter and shows filtered results even though the URL no longer requests a filter. Consider resetting the filter state when filter/value are missing to keep UI and URL in sync.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in the latest push – filter state re-syncs from location.search on every navigation and clears when the params are absent.
- Replace list view with responsive card grid (3/2/1 columns) - Add projectThumbnail field to schema for string image URLs - Rename author to projectAuthor to avoid schema conflict with blog posts - Add side projects link to community nav menu - Remove tag filter chips for cleaner UI - Add screenshots for Repo Stalker and Anomstack
- Add HouseWatch (ClickHouse monitoring toolkit, 607 stars) - Add DeskHog (ESP32 developer toy, 459 stars) - Add PostHog Wizard (AI-powered integration CLI, 80 stars) - Add MaxAI (AI support bot from Aruba hackathon) - Add side projects link to "Things that spark joy" menu - Replace code icon placeholder with random hedgehog characters
- Add thumbnail images for side projects gallery - Add anomaly-agent project entry - Remove guide and max-ai projects - Add README with contribution instructions
- Add collapsible filter panel with tag and author filters - URL-encoded filters for shareable links - Use GitHub OpenGraph images as thumbnail fallback - Add 18 new side projects: Pigeon Maps, Kea, FrameOS, Backname, G-Desktop-Suite, and more from top PostHog contributors - Clean up existing projects by removing "Try it" sections - Add custom thumbnails for backname, kea, pigeon-maps
- Update demos.mdx to use simpler ?tag= URL format - Add "See it in action" section to basics.mdx with link to demos
Link to community profile /community/profiles/37401 for all 6 of Andy Maguire's side projects using the teamLink frontmatter field.
Prioritize teamLink frontmatter field over profile lookup for author links in the gallery cards.
- Change tagline to "Side projects are awesome, help us learn and make us better at building stuff" - Remove thumbnails from SF Mono Powerline and PagerBot to use GitHub OpenGraph fallback
New side projects from PostHog/posthog.com contributors: - whatsapp-mcp (lharries) - WhatsApp MCP server for Claude integration - seegit (haacked) - Real-time Git repository visualizer - django-subdomains (tkaemming) - Django subdomain URL routing - aspnet-client-validation (haacked) - jQuery-free ASP.NET validation - routemagic (haacked) - ASP.NET routing utility - gutenberg-migration-guide (danielbachhuber) - WordPress migration docs - one-time-login (danielbachhuber) - WordPress one-time login URLs - pypi-assistant (Twixes) - VS Code PyPI package info extension - comp-scraping (lucasheriques) - Brazil salary data scraper - modulus-checker (pauldambra) - UK bank account validation - somsiad (Twixes) - Polish Discord bot - auth0-web-extension (pjhul) - Auth0 for browser extensions - medium-markdown (neilkakkar) - Medium to Markdown converter - metnopy (andehen) - Norwegian weather data API - poabus (rafaeelaudibert) - Porto Alegre bus route visualization - terraformation (benjackwhite) - Terraform state UI - writeconcise (ivanagas) - Concise writing tool - sheetstodashboard (alexkim205) - Spreadsheet to dashboard converter Added thumbnails for 6 projects with visual UIs.
Deploy previews use GATSBY_MINIMAL, which skipped side project detail pages entirely, so reviewers could not click through to any project from the gallery in a PR preview. Generated-By: PostHog Code Task-Id: 7750396d-c811-4d3a-a521-dfc389676a3a
|
happy to help test etc - all should be fairly safe i think as a new path and part of site so might be easier to ship and make incremental prs on top or we can wait and do big one via previews etc - im easy either way @joethreepwood - feel free to do whatever approach and i can help if/where needed. |
…upload Restyle the gallery to match /roadmap (Editor layout with hero header), widen the grid to four columns with more spacing, replace GitHub OpenGraph thumbnail fallbacks with generated card art adapted from EventGraphic (project title, creator avatar, role, logo bar), and let the add-project form upload a featured image to the media library. Generated-By: PostHog Code Task-Id: 7750396d-c811-4d3a-a521-dfc389676a3a
Split projects by former team members into a collapsed "PostHog Alums" section (auto-detected from community-profile team membership, overridable via frontmatter), fold near-duplicate tags into a canonical set, order projects newest first via an added date, restrict the creator filter to current team members, highlight projects not made by engineers with a badge and filter, swap the hero for the brand coding-group illustration, drop the Live badge, add card-level edit shortcuts, show the team add/edit UI without sign-in on dev builds so previews can exercise it, update the header copy, add Atuin, remove HogFlix and the LLM analytics demo entry, and fix the FrameOS image by generating its card art. Generated-By: PostHog Code Task-Id: 7750396d-c811-4d3a-a521-dfc389676a3a
… remove HouseWatch Generated-By: PostHog Code Task-Id: 7750396d-c811-4d3a-a521-dfc389676a3a
Detail pages added little over the card itself, so cards now open the project directly (live URL first, GitHub repo as fallback) in a new tab. Removes the SideProject template, its page creation in full and minimal builds, and the form's body field. Generated-By: PostHog Code Task-Id: 7750396d-c811-4d3a-a521-dfc389676a3a
|
I think good for reviews now! |
Three check runs from the earlier Actions outage are wedged in queued state and reject cancellation; this empty commit mints fresh check runs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QSyozUtvjmz3AreJbkaa47
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4b151066df
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| @@ -0,0 +1,586 @@ | |||
| import { Logo } from '@posthog/brand/logo' | |||
There was a problem hiding this comment.
Add the required component README
This commit creates a substantial new SideProjects component directory containing the gallery utilities and add/edit flow, but the directory has no README.md. Add comprehensive documentation covering the component's usage, data contract, and contribution flow as required for every new component.
AGENTS.md reference: AGENTS.md:L134-L135
Useful? React with 👍 / 👎.
| const canSubmit = | ||
| Boolean(values.title.trim() && values.description.trim() && values.projectAuthor.trim()) && !submitting |
There was a problem hiding this comment.
Require at least one project target URL
When a moderator leaves both URL inputs blank, this condition still enables submission. buildSideProjectMdx then omits both URLs, while ProjectCard derives its link solely from liveUrl || githubUrl, so merging the generated file creates an unclickable gallery card despite the contribution README requiring at least one target. Include the presence of either URL in this validation.
Useful? React with 👍 / 👎.
| const lines = ['---', `title: ${values.title.trim()}`] | ||
| if (values.description.trim()) { | ||
| lines.push(`description: ${values.description.trim()}`) | ||
| } |
There was a problem hiding this comment.
Escape form values when generating frontmatter
Free-text form values are interpolated into YAML without quoting or serialization. A routine description such as Dashboard: visualizes usage makes the generated frontmatter invalid, while values such as true, [work], or a title beginning with # can be coerced or discarded, causing the generated PR to fail content parsing or publish incorrect metadata. Serialize these values as valid YAML scalars rather than concatenating them directly.
Useful? React with 👍 / 👎.
| if (search) { | ||
| const haystack = [title, description, projectAuthor, ...tags].filter(Boolean).join(' ').toLowerCase() | ||
| return haystack.includes(search) |
There was a problem hiding this comment.
Keep aliased source tags in the search index
For every tag present in TAG_ALIASES, normalizeTags discards the original value before the search haystack is built. As a result, searching for a technology such as dagster does not find Anomstack even though its frontmatter contains that tag, because the tag has become data and the original term occurs nowhere else; this contradicts the intended behavior that one-off tags remain searchable. Include the raw tags alongside their canonical forms in the search haystack.
Useful? React with 👍 / 👎.
| if (featuredImage?.file) { | ||
| const uploaded = await uploadImage(featuredImage.file, await getJwt()) | ||
| thumbnailUrl = uploaded?.url || thumbnailUrl | ||
| } | ||
| window.open(getNewProjectUrl({ ...values, projectThumbnail: thumbnailUrl }), '_blank', 'noopener') |
There was a problem hiding this comment.
Open the GitHub tab before awaiting the image upload
When a featured image is selected, this window.open runs only after the JWT lookup and network upload have been awaited, so it is no longer reliably associated with the submit gesture and popup blockers can return null without opening GitHub, especially in Safari or after a slow upload. The code still sets submitted to true in that case, falsely telling the contributor that the tab opened. Open a blank tab synchronously during submission and navigate it after the upload, or use a same-tab redirect.
Useful? React with 👍 / 👎.
| projectAuthor: Danilo Campos | ||
| authorGitHub: danilocampos | ||
| githubUrl: https://github.com/PostHog/DeskHog | ||
| liveUrl: https://posthog.com/deskhog |
There was a problem hiding this comment.
Use a relative URL for the DeskHog project
Because cards prefer liveUrl and pass it through Link, this absolute PostHog URL is treated as external and opens DeskHog in a new browser tab instead of using the site's internal OS-style navigation. Change it to /deskhog, as required for internal links.
AGENTS.md reference: AGENTS.md:L115-L115
Useful? React with 👍 / 👎.
| const hasActiveFilters = Boolean(searchQuery.trim() || tagFilter || creatorFilter) | ||
| const visibleTags = showAllTags ? rankedTags : rankedTags.slice(0, VISIBLE_TAG_COUNT) | ||
| const showAlumni = alumniExpanded || hasActiveFilters |
There was a problem hiding this comment.
Let the alumni toggle work while filters are active
Whenever search, tag, or creator filtering is active, hasActiveFilters forces showAlumni to remain true regardless of alumniExpanded. The visible alumni header still presents itself as an expandable button and updates alumniExpanded when clicked, but its content and aria-expanded state cannot collapse, leaving users with a nonfunctional control during the exact state in which the section is automatically opened. Either allow the explicit toggle to override automatic expansion or stop presenting it as collapsible while filters are active.
Useful? React with 👍 / 👎.
- Add the required README for the SideProjects component directory - Serialize generated frontmatter values as single-quoted YAML scalars so colons, hashes, and keyword-like values can't break or coerce the file - Require at least one of repo/live URL in the add-project form (cards link to liveUrl || githubUrl, so neither means an unclickable card) - Open the GitHub tab synchronously on submit before awaiting the image upload so popup blockers don't eat it; fall back to same-tab navigation - Let the alumni toggle collapse while filters are active (auto-expand on filter activation instead of forcing the section open) - Keep raw tags in the search haystack so aliased one-offs (e.g. dagster) stay findable - Use a relative liveUrl for DeskHog so it opens with internal navigation Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QSyozUtvjmz3AreJbkaa47
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ce97ef6a17
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| label="Live URL" | ||
| name="liveUrl" | ||
| direction="column" | ||
| type="url" |
There was a problem hiding this comment.
For projects hosted on posthog.com, contributors should enter an internal path such as /deskhog, but the browser's native validation for type="url" rejects relative URLs and prevents the form's submit handler from running. This makes the documented add-project flow unusable for internal projects unless contributors violate the repository's internal-link convention; allow relative paths here or validate both relative and absolute URLs explicitly.
AGENTS.md reference: AGENTS.md:L109-L116
Useful? React with 👍 / 👎.
type="url" native validation rejected site-relative paths like /deskhog, which the internal-link convention requires. Validate explicitly instead: absolute http(s) URLs or paths starting with /. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QSyozUtvjmz3AreJbkaa47
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e91098c3fc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| setTagFilter(tag) | ||
| setCreatorFilter(creator) | ||
| } | ||
| }, [location?.search]) |
There was a problem hiding this comment.
Subscribe to the app window's query location
When a /side-projects window already exists and another window navigates to /side-projects?tag=...—including the new docs links—the app shell only refreshes the stored AppWindow.location (src/context/App.tsx:2158-2168) while continuing to render the original element (src/components/AppWindow/index.tsx:857). Consequently, this dependency remains unchanged and the effect neither applies nor clears the incoming filter. Fresh evidence after the prior fix is this existing-window path; read useWindow().appWindow.location.search and update the URL through the router instead.
AGENTS.md reference: AGENTS.md:L3-L5
Useful? React with 👍 / 👎.
| <IconPencil className="size-4" /> | ||
| </Link> | ||
| )} | ||
| <Link to={projectUrl} externalNoIcon className="block"> |
There was a problem hiding this comment.
Open internal project targets in a new app window
When projectUrl is site-relative—currently DeskHog uses /deskhog—this link performs an in-place Gatsby navigation, so the app shell replaces the focused gallery window instead of opening the project separately. External project targets open browser tabs, and internal creator links already request newWindow, so pass state={{ newWindow: true }} for relative project targets to preserve the gallery window.
AGENTS.md reference: AGENTS.md:L3-L5
Useful? React with 👍 / 👎.
| .toLowerCase() | ||
| .replace(/[^a-z0-9]+/g, '-') | ||
| .replace(/^-+|-+$/g, '') |
There was a problem hiding this comment.
Reject titles that produce an empty project slug
For a project name containing no ASCII letters or digits, such as a Chinese-only or emoji-only title, these replacements remove every character and return an empty slug. getNewProjectUrl consequently asks GitHub to create /index.mdx instead of <slug>/index.mdx, while canSubmit only checks that the original title is nonempty, so the handoff proceeds without a valid project directory. Validate that the generated slug is nonempty or use a Unicode-aware slugger.
Useful? React with 👍 / 👎.
| <Link | ||
| to={getEditProjectUrl(relativePath)} | ||
| externalNoIcon | ||
| className="absolute right-2 top-2 z-10 rounded-md border border-primary bg-primary p-1.5 text-secondary opacity-0 transition-opacity hover:text-primary group-hover:opacity-100" |
There was a problem hiding this comment.
Reveal the edit link on keyboard focus
For a signed-in moderator navigating by keyboard, this edit anchor remains in the tab order but stays at opacity-0 because visibility is only enabled by group-hover. Focusing it therefore produces an invisible active control with no discoverable edit affordance; add a focus-visible:opacity-100 state (or reveal it when the card contains focus).
Useful? React with 👍 / 👎.
- Read filters from the app window's location so an existing /side-projects window picks up ?tag= links from other windows (matches the fm and community profile pages' pattern) - Open site-relative project links in a new app window, preserving the gallery window like external links and creator links already do - Reveal the moderator edit link on keyboard focus, not just hover - Reject titles whose slug is empty (no ASCII letters/digits) so the GitHub handoff can't target /index.mdx Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QSyozUtvjmz3AreJbkaa47
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1fbe21326d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| className={`relative aspect-video w-full overflow-hidden bg-${background} ${ | ||
| darkText ? 'text-black' : 'text-white' | ||
| }`} |
There was a problem hiding this comment.
Replace stock colors with project tokens
When generated artwork is shown for a project without a thumbnail, this component applies stock text-black/text-white classes, along with nearby border-white and bg-white, instead of the project's themed color tokens. Replace these with project tokens so the artwork follows the repository's theming conventions.
AGENTS.md reference: AGENTS.md:L153-L155
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Keeping the literal colors here deliberately: SideProjectGraphic mirrors EventGraphic (src/components/EventGraphic/index.tsx), which uses the same text-black/text-white/border-white/bg-white values. The artwork renders over a fixed profile-color background, so it must not shift with the site theme – theme tokens would break contrast in dark mode. The lazy-loading comment from this review is fixed in the latest push.
Generated by Claude Code
| <img | ||
| src={avatarUrl || DEFAULT_HEDGEHOG} | ||
| alt="" | ||
| className="absolute bottom-0 left-1/2 w-[108%] max-w-none -translate-x-1/2" |
There was a problem hiding this comment.
Lazy-load generated-card avatars
For every project without projectThumbnail—currently a large portion of the gallery—the newer SideProjectGraphic path eagerly loads its creator avatar or fallback because this image has no loading="lazy". The unfiltered gallery therefore initiates dozens of below-the-fold avatar requests even though the explicit-thumbnail branch is lazy-loaded; apply the same loading behavior here.
Useful? React with 👍 / 👎.
The SideProjectGraphic avatar had no loading attribute, so every below-the-fold generated card fetched its avatar on first paint. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QSyozUtvjmz3AreJbkaa47
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QSyozUtvjmz3AreJbkaa47
|
this will need an @ianmatson or @smallbrownbike review, as style wise it's very different to the rest of the website so can't really go out as is. couple of intermediate suggestions for you though @joethreepwood:
|
|
Think you may have been looking at an old deploy, as some of those things are already fixed (e.g. no DeskHog, no intermediate pages) on my end - but will update on the rest! |
…projects Address review feedback: pitch the page at prospective hires with a link to the side gigs policy plus a disclaimer that these are personal projects, move the nav entry from Community/Sparks joy to Company, and remove DeskHog and PostHog Wizard since they are PostHog work rather than personal side projects. Generated-By: PostHog Code Task-Id: 7750396d-c811-4d3a-a521-dfc389676a3a
Generated-By: PostHog Code Task-Id: 7750396d-c811-4d3a-a521-dfc389676a3a
|
All of the suggestions from the review above are now in:
Still outstanding: the design review flagged at the top of the thread. |
Replace the MDX-plus-pull-request flow with the events pattern: projects are fetched client-side from the Squeak Strapi side-projects collection (PostHog/squeak-strapi#205) and team members add, edit, and delete them in a closable side modal. The former MDX entries become a bundled seed that renders immediately, survives API outages, and migrates via scripts/seed-side-projects.mjs. Also highlight the H1 with the /slack-style rough annotation, shrink and stylize the small print with updated copy, and add Railcode by Yakko Majuri to the alumni set. Generated-By: PostHog Code Task-Id: 7750396d-c811-4d3a-a521-dfc389676a3a
|
Decided that, sod it, let's just fully refactor this around Squeak instead of clumsy .mdx files too. Those changes should be landing soon, though there's a dependency in the PR above. @andrewm4894 want to take a look? |
Context
i was wanting to add a demos page for llm analytics as we often have some apps useful for users to see different llma integrations or examples etc - eg currently working on agent using open ai agents sdk as example agent that can use llma for tracing (github-standup-agent) - over time we will have more and more of these and im sure others do too for different products
more context in longer slack thread here
Changes
Add a side projects gallery at
/side-projects. Cards link straight to each project (live URL first, GitHub repo as fallback).side-projectscollection (companion PR: squeak-strapi#205) and are fetched client-side, matching how/eventsworks. Team members add, edit, and delete projects in a side modal, gated by the sameisModeratorcheck; dev builds show the UI without sign-in so previews can exercise itsrc/data/sideProjects.jsonis the bundled seed: it renders immediately, stays up if the API is unreachable, andscripts/seed-side-projects.mjsmigrates it into Strapi after the backend deploys/slack-style rough highlight on the H1, a hiring pitch linking to the side gigs policy, and a small-print disclaimerAfter merge
publicfind/findOne andmoderatorcreate/update/delete on Side project in the Strapi admin.SQUEAK_JWT=<moderator jwt> node scripts/seed-side-projects.mjsonce to migrate the seed entries.URLs to test
Checklist
vercel.json(n/a)