diff --git a/.gitignore b/.gitignore index 380cac5b..5d549670 100644 --- a/.gitignore +++ b/.gitignore @@ -253,3 +253,4 @@ $RECYCLE.BIN/ # End of https://www.toptal.com/developers/gitignore/api/osx,windows,linux,nextjs,react,node cdk.out +.claude/ diff --git a/package-lock.json b/package-lock.json index 55f2438d..32cf6cea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,7 @@ "lucide-react": "^0.483.0", "md5": "^2.3.0", "mystjs": "^0.0.15", - "next": "15.5.7", + "next": "15.5.9", "next-themes": "^0.4.4", "nextjs-toploader": "^3.8.16", "node-fetch": "^3.3.2", @@ -7209,9 +7209,9 @@ } }, "node_modules/@next/env": { - "version": "15.5.7", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.7.tgz", - "integrity": "sha512-4h6Y2NyEkIEN7Z8YxkA27pq6zTkS09bUSYC0xjd0NpwFxjnIKeZEeH591o5WECSmjpUhLn3H2QLJcDye3Uzcvg==", + "version": "15.5.9", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.9.tgz", + "integrity": "sha512-4GlTZ+EJM7WaW2HEZcyU317tIQDjkQIyENDLxYJfSWlfqguN+dHkZgyQTV/7ykvobU7yEH5gKvreNrH4B6QgIg==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -20383,12 +20383,12 @@ "license": "MIT" }, "node_modules/next": { - "version": "15.5.7", - "resolved": "https://registry.npmjs.org/next/-/next-15.5.7.tgz", - "integrity": "sha512-+t2/0jIJ48kUpGKkdlhgkv+zPTEOoXyr60qXe68eB/pl3CMJaLeIGjzp5D6Oqt25hCBiBTt8wEeeAzfJvUKnPQ==", + "version": "15.5.9", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.9.tgz", + "integrity": "sha512-agNLK89seZEtC5zUHwtut0+tNrc0Xw4FT/Dg+B/VLEo9pAcS9rtTKpek3V6kVcVwsB2YlqMaHdfZL4eLEVYuCg==", "license": "MIT", "dependencies": { - "@next/env": "15.5.7", + "@next/env": "15.5.9", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", diff --git a/package.json b/package.json index cdf01eaf..0217882e 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "lucide-react": "^0.483.0", "md5": "^2.3.0", "mystjs": "^0.0.15", - "next": "15.5.7", + "next": "15.5.9", "next-themes": "^0.4.4", "nextjs-toploader": "^3.8.16", "node-fetch": "^3.3.2", diff --git a/scripts/dev-clean-start.sh b/scripts/dev-clean-start.sh new file mode 100755 index 00000000..8af7c75d --- /dev/null +++ b/scripts/dev-clean-start.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +# Colors for output +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color +CHECK="✓" +ARROW="→" + +echo "${YELLOW}=== Clean Start for Source.coop Development ===${NC}" + +# Step 1: Clean up +echo -e "\n${YELLOW}${ARROW} Cleaning up...${NC}" +pkill -f "next dev" > /dev/null 2>&1 +docker-compose down > /dev/null 2>&1 +rm -rf .next +echo -e "${GREEN}${CHECK} Cleaned up old processes and build cache${NC}" + +# Step 2: Start Docker services +echo -e "\n${YELLOW}${ARROW} Starting Docker services (DynamoDB on :8000)...${NC}" +docker-compose up -d +echo -e "${GREEN}${CHECK} Docker services started${NC}" + +# Step 3: Wait for services +echo -e "\n${YELLOW}${ARROW} Waiting for services to initialize...${NC}" +sleep 10 +echo -e "${GREEN}${CHECK} Services ready${NC}" + +# Step 4: Start dev server without AWS profile +echo -e "\n${YELLOW}${ARROW} Starting development server...${NC}" +echo -e "${GREEN}Application will be available at: http://localhost:3000${NC}" +echo -e "${GREEN}DynamoDB Admin UI at: http://localhost:8001${NC}" +echo -e "\n${YELLOW}Press Ctrl+C to stop the development server${NC}\n" + +# Unset AWS profile to use local DynamoDB +unset AWS_PROFILE +unset AWS_DEFAULT_PROFILE +npm run dev + diff --git a/src/components/features/markdown/HeadingWithPermalink.tsx b/src/components/features/markdown/HeadingWithPermalink.tsx new file mode 100644 index 00000000..7a46382f --- /dev/null +++ b/src/components/features/markdown/HeadingWithPermalink.tsx @@ -0,0 +1,26 @@ +import { ReactNode } from "react"; +import { Heading } from "@radix-ui/themes"; +import { Link2Icon } from "@radix-ui/react-icons"; + +interface HeadingWithPermalinkProps { + id: string; + level: "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"; + children: ReactNode; + mb?: "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"; +} + +export function HeadingWithPermalink({ + id, + level, + children, + mb, +}: HeadingWithPermalinkProps) { + return ( + + {children} + + + + + ); +} diff --git a/src/components/features/markdown/MarkdownViewer.tsx b/src/components/features/markdown/MarkdownViewer.tsx index 1a69b3eb..08a36e8d 100644 --- a/src/components/features/markdown/MarkdownViewer.tsx +++ b/src/components/features/markdown/MarkdownViewer.tsx @@ -4,6 +4,7 @@ import remarkGfm from "remark-gfm"; import rehypeRaw from "rehype-raw"; import rehypeSanitize, { defaultSchema } from "rehype-sanitize"; import { Code } from "./codeConfig"; +import { HeadingWithPermalink } from "./HeadingWithPermalink"; import "@/styles/MarkdownViewer.css"; interface MarkdownViewerProps { @@ -38,39 +39,64 @@ export function MarkdownViewer({ content }: MarkdownViewerProps) { } return ( - + ( - + {children} - + ), h2: ({ children }) => ( - + {children} - + ), h3: ({ children }) => ( - + {children} - + ), h4: ({ children }) => ( - + {children} - + ), h5: ({ children }) => ( - + {children} - + ), h6: ({ children }) => ( - + {children} - + ), p: ({ children }) => ( @@ -78,7 +104,9 @@ export function MarkdownViewer({ content }: MarkdownViewerProps) { ), a: ({ href, children }) => ( - {children} + + {children} + ), table: ({ children }) => ( @@ -106,8 +134,9 @@ export function MarkdownViewer({ content }: MarkdownViewerProps) { return {codeContent}; }, }} - > - {content} - + > + {content} + + ); } diff --git a/src/styles/MarkdownViewer.css b/src/styles/MarkdownViewer.css index b02e1984..e2d5437f 100644 --- a/src/styles/MarkdownViewer.css +++ b/src/styles/MarkdownViewer.css @@ -74,15 +74,6 @@ code.inline-code { margin: 1em 0; } -/* Links */ -.markdown-viewer a { - color: var(--blue-9); - text-decoration: none; -} - -.markdown-viewer a:hover { - text-decoration: underline; -} /* Styles for MyST admonitions */ .admonition { @@ -135,3 +126,29 @@ code.inline-code { :root[class~="dark"] .admonition.important { background-color: var(--purple-12); } + +/* Permalink styles */ +.heading-with-permalink { + display: inline-flex; + align-items: center; + gap: 0.5rem; +} + +.permalink-link { + display: none; + color: var(--gray-9); + text-decoration: none; +} + +.heading-with-permalink:hover .permalink-link { + display: inline-flex; +} + +.permalink-link:hover { + color: var(--blue-9); +} + +.permalink-link svg { + width: 0.85em; + height: 0.85em; +}