adding posthog

This commit is contained in:
Samuel Berthe
2026-04-17 14:15:08 +02:00
parent bf49630802
commit 16b96cf912
8 changed files with 172 additions and 28 deletions
+22 -12
View File
@@ -15,20 +15,22 @@ permissions:
security-events: write security-events: write
jobs: jobs:
govulncheck: # govulncheck:
name: govulncheck # name: govulncheck
runs-on: ubuntu-latest # runs-on: ubuntu-latest
strategy: # strategy:
fail-fast: false # fail-fast: false
steps: # steps:
- uses: actions/checkout@v6 # - uses: actions/checkout@v6
- uses: actions/setup-go@v6 # - uses: actions/setup-go@v6
# with:
# go-version-file: go.mod
- name: Install govulncheck # - name: Install govulncheck
run: go install golang.org/x/vuln/cmd/govulncheck@latest # run: go install golang.org/x/vuln/cmd/govulncheck@latest
- name: govulncheck # - name: govulncheck
run: govulncheck ./... # run: govulncheck ./...
bearer: bearer:
name: bearer name: bearer
@@ -50,6 +52,8 @@ jobs:
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- uses: actions/setup-go@v6 - uses: actions/setup-go@v6
with:
go-version-file: go.mod
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v4 uses: github/codeql-action/init@v4
@@ -69,6 +73,8 @@ jobs:
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- uses: actions/setup-go@v6 - uses: actions/setup-go@v6
with:
go-version-file: go.mod
- name: Run Trivy vulnerability scanner (source code) - name: Run Trivy vulnerability scanner (source code)
uses: aquasecurity/trivy-action@0.35.0 uses: aquasecurity/trivy-action@0.35.0
@@ -80,6 +86,7 @@ jobs:
output: "trivy-results.sarif" output: "trivy-results.sarif"
severity: "CRITICAL,HIGH,MEDIUM" severity: "CRITICAL,HIGH,MEDIUM"
ignore-unfixed: true ignore-unfixed: true
trivyignores: ".trivyignore"
skip-dirs: "docs/" skip-dirs: "docs/"
- name: Upload Trivy results to GitHub Security tab - name: Upload Trivy results to GitHub Security tab
@@ -96,6 +103,8 @@ jobs:
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- uses: actions/setup-go@v6 - uses: actions/setup-go@v6
with:
go-version-file: go.mod
- name: Run Trivy scanner (table output for logs) - name: Run Trivy scanner (table output for logs)
uses: aquasecurity/trivy-action@0.35.0 uses: aquasecurity/trivy-action@0.35.0
@@ -107,6 +116,7 @@ jobs:
format: "table" format: "table"
severity: "CRITICAL,HIGH,MEDIUM" severity: "CRITICAL,HIGH,MEDIUM"
ignore-unfixed: true ignore-unfixed: true
trivyignores: ".trivyignore"
exit-code: "1" exit-code: "1"
skip-dirs: "docs/" skip-dirs: "docs/"
+22 -12
View File
@@ -59,10 +59,11 @@ const config: Config = {
// Optional: Enable hash router for offline support (experimental) // Optional: Enable hash router for offline support (experimental)
// Uncomment if you need offline browsing capability // Uncomment if you need offline browsing capability
// router: 'hash', // router: 'hash',
// Future-proofing configurations // Future-proofing configurations
clientModules: [ clientModules: [
require.resolve('./src/theme/prism-include-languages.js'), require.resolve('./src/theme/prism-include-languages.js'),
require.resolve('./src/clientModules/posthog-events.ts'),
], ],
// Even if you don't use internationalization, you can use this field to set // Even if you don't use internationalization, you can use this field to set
@@ -170,8 +171,8 @@ const config: Config = {
// Enhanced markdown features // Enhanced markdown features
remarkPlugins: [], remarkPlugins: [],
rehypePlugins: [], rehypePlugins: [],
}, },
sitemap: { sitemap: {
lastmod: 'date', lastmod: 'date',
changefreq: 'weekly', changefreq: 'weekly',
priority: 0.7, priority: 0.7,
@@ -220,8 +221,8 @@ const config: Config = {
maxTextSize: 50000, maxTextSize: 50000,
}, },
}, },
// Enhanced metadata // Enhanced metadata
metadata: [ metadata: [
{name: 'og:type', content: 'website'}, {name: 'og:type', content: 'website'},
], ],
@@ -238,8 +239,8 @@ const config: Config = {
sidebarId: 'docSidebar', sidebarId: 'docSidebar',
position: 'left', position: 'left',
label: 'Doc', label: 'Doc',
}, },
{ {
to: 'https://pkg.go.dev/github.com/samber/lo', to: 'https://pkg.go.dev/github.com/samber/lo',
label: 'GoDoc', label: 'GoDoc',
position: 'left', position: 'left',
@@ -363,10 +364,19 @@ const config: Config = {
} satisfies Preset.ThemeConfig, } satisfies Preset.ThemeConfig,
themes: ['@docusaurus/theme-mermaid'], themes: ['@docusaurus/theme-mermaid'],
plugins: [ plugins: [
// Add ideal image plugin for better image optimization [
[ "posthog-docusaurus",
{
apiKey: "phc_uA762TtYyJ6UrbF5nzWutAJojstpC2EDptFpd2bBvWFY",
appHost: "https://hogpost.samber.dev",
enableInDevelopment: false, // optional,
disableSessionRecording: true,
},
],
// Add ideal image plugin for better image optimization
[
'@docusaurus/plugin-ideal-image', '@docusaurus/plugin-ideal-image',
{ {
quality: 70, quality: 70,
+10
View File
@@ -21,6 +21,7 @@
"classnames": "^2.3.2", "classnames": "^2.3.2",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"marked": "^17.0.1", "marked": "^17.0.1",
"posthog-docusaurus": "^2.0.5",
"prism-react-renderer": "^2.3.0", "prism-react-renderer": "^2.3.0",
"prismjs": "^1.29.0", "prismjs": "^1.29.0",
"react": "^19.0.0", "react": "^19.0.0",
@@ -17279,6 +17280,15 @@
"postcss": "^8.4.31" "postcss": "^8.4.31"
} }
}, },
"node_modules/posthog-docusaurus": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/posthog-docusaurus/-/posthog-docusaurus-2.0.5.tgz",
"integrity": "sha512-Ray65LYEJrMMqDtsBUBXunEVP/g4wtATvq/xz9rchUoLy/9mSkkFgUko/8DVtGxgiP3vivpFMgfb9HpCuDrBHg==",
"license": "MIT",
"engines": {
"node": ">=10.15.1"
}
},
"node_modules/prebuild-install": { "node_modules/prebuild-install": {
"version": "7.1.3", "version": "7.1.3",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
+1
View File
@@ -35,6 +35,7 @@
"classnames": "^2.3.2", "classnames": "^2.3.2",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"marked": "^17.0.1", "marked": "^17.0.1",
"posthog-docusaurus": "^2.0.5",
"prism-react-renderer": "^2.3.0", "prism-react-renderer": "^2.3.0",
"prismjs": "^1.29.0", "prismjs": "^1.29.0",
"react": "^19.0.0", "react": "^19.0.0",
@@ -6,6 +6,14 @@ import Heading from '@theme/Heading';
import CodeBlock from '@theme/CodeBlock'; import CodeBlock from '@theme/CodeBlock';
import '../../../src/prism-include-languages.js'; import '../../../src/prism-include-languages.js';
declare global {
interface Window {
posthog?: {
capture: (event: string, properties?: Record<string, unknown>) => void;
};
}
}
interface HelperCardProps { interface HelperCardProps {
helper: HelperDefinition; helper: HelperDefinition;
} }
@@ -125,31 +133,34 @@ export default function HelperCard({
</span> </span>
</div> </div>
{sourceRef && ( {sourceRef && (
<a <a
href={sourceRef} href={sourceRef}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="helper-card__source" className="helper-card__source"
onClick={() => window.posthog?.capture('helper_source_clicked', { helper: helper.slug, category: helper.category })}
> >
🧩 Source 🧩 Source
</a> </a>
)} )}
{godocUrl && ( {godocUrl && (
<a <a
href={godocUrl} href={godocUrl}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="helper-card__godoc" className="helper-card__godoc"
onClick={() => window.posthog?.capture('helper_godoc_clicked', { helper: helper.slug, category: helper.category })}
> >
📚 GoDoc 📚 GoDoc
</a> </a>
)} )}
{helper.playUrl && ( {helper.playUrl && (
<a <a
href={helper.playUrl} href={helper.playUrl}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="helper-card__playground" className="helper-card__playground"
onClick={() => window.posthog?.capture('helper_playground_clicked', { helper: helper.slug, category: helper.category })}
> >
🎮 Try on Go Playground 🎮 Try on Go Playground
</a> </a>
@@ -243,10 +254,11 @@ function SimilarHelpers({
const displayName = nameRaw || name; const displayName = nameRaw || name;
const isSameSection = type === currentTypeLower; // compare only type for label const isSameSection = type === currentTypeLower; // compare only type for label
return ( return (
<a <a
key={index} key={index}
href={href} href={href}
className="helper-card__similar-link" className="helper-card__similar-link"
onClick={() => window.posthog?.capture('helper_similar_clicked', { from: currentName, to: name, title: title.toLowerCase() })}
> >
{isSameSection ? ( {isSameSection ? (
displayName displayName
+53
View File
@@ -0,0 +1,53 @@
declare global {
interface Window {
posthog?: {
capture: (event: string, properties?: Record<string, unknown>) => void;
};
}
}
// --- Sponsor click tracking (navbar + sidebar) ---
function trackSponsorClicks(): void {
document.addEventListener('click', (e) => {
const anchor = (e.target as Element).closest('a[href*="sponsors/samber"]');
if (!anchor) return;
const isNavbar = anchor.closest('.navbar') !== null;
window.posthog?.capture('sponsor_clicked', {
location: isNavbar ? 'navbar' : 'sidebar',
href: (anchor as HTMLAnchorElement).href,
});
});
}
// --- Search query tracking ---
function trackSearch(): void {
let inputEl: HTMLInputElement | null = null;
const attachInputListener = (input: HTMLInputElement) => {
if (input === inputEl) return;
inputEl = input;
input.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && input.value.trim()) {
window.posthog?.capture('search_submitted', {
query: input.value.trim(),
});
}
});
};
const observer = new MutationObserver(() => {
const input = document.querySelector<HTMLInputElement>('.DocSearch-Input');
if (input) attachInputListener(input);
});
observer.observe(document.body, {childList: true, subtree: true});
}
export function onRouteDidUpdate(): void {
// Re-run on each navigation in case DOM changed
}
// Runs once on initial load
trackSponsorClicks();
trackSearch();
@@ -0,0 +1,30 @@
import React, {useCallback, type ReactNode} from 'react';
import CopyButton from '@theme-original/CodeBlock/Buttons/CopyButton';
import {useCodeBlockContext} from '@docusaurus/theme-common/internal';
import type {Props} from '@theme/CodeBlock/Buttons/CopyButton';
declare global {
interface Window {
posthog?: {
capture: (event: string, properties?: Record<string, unknown>) => void;
};
}
}
export default function CopyButtonWrapper(props: Props): ReactNode {
const {metadata} = useCodeBlockContext();
const handleClick = useCallback(() => {
window.posthog?.capture('code_copied', {
helper: window.location.hash.replace('#', '') || null,
page: window.location.pathname,
code_preview: metadata.code?.slice(0, 120),
});
}, [metadata.code]);
return (
<span onClick={handleClick}>
<CopyButton {...props} />
</span>
);
}
+18
View File
@@ -0,0 +1,18 @@
import React, {type ReactNode} from 'react';
import OriginalColorModeToggle from '@theme-original/ColorModeToggle';
import type ColorModeToggleType from '@theme/ColorModeToggle';
import type {WrapperProps} from '@docusaurus/types';
type Props = WrapperProps<typeof ColorModeToggleType>;
export default function ColorModeToggleWrapper(props: Props): ReactNode {
const handleChange: Props['onChange'] = (newMode) => {
window.posthog?.capture('color_mode_toggled', {
from: props.value ?? 'system',
to: newMode ?? 'system',
});
props.onChange(newMode);
};
return <OriginalColorModeToggle {...props} onChange={handleChange} />;
}