Standard dependency updates (#1)
Reviewed-on: #1 Co-authored-by: Zeke Abshire <zekeabshire@gmail.com> Co-committed-by: Zeke Abshire <zekeabshire@gmail.com>
This commit was merged in pull request #1.
This commit is contained in:
@@ -1,15 +1,20 @@
|
||||
import tseslint from "typescript-eslint";
|
||||
import nextVitals from "eslint-config-next/core-web-vitals";
|
||||
|
||||
/** @type {import("eslint").Linter.Config} */
|
||||
const config = {
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
project: true,
|
||||
...nextVitals,
|
||||
...tseslint.configs.recommended,
|
||||
...tseslint.configs.stylistic,
|
||||
languageOptions: {
|
||||
parser: tseslint.parser,
|
||||
parserOptions: {
|
||||
projectService: true,
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
"@typescript-eslint": tseslint.plugin,
|
||||
},
|
||||
plugins: ["@typescript-eslint"],
|
||||
extends: [
|
||||
"next/core-web-vitals",
|
||||
"plugin:@typescript-eslint/recommended-type-checked",
|
||||
"plugin:@typescript-eslint/stylistic-type-checked",
|
||||
],
|
||||
rules: {
|
||||
// These opinionated rules are enabled in stylistic-type-checked above.
|
||||
// Feel free to reconfigure them to your own preference.
|
||||
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
bash .gitea/scripts/pangolin-upsert.sh \
|
||||
--subdomain "${{ vars.PROD_SUBDOMAIN }}" \
|
||||
--port "${{ vars.PROD_PORT }}" \
|
||||
--resource-name "${{ vars.APP_NAME }}-production"
|
||||
--resource-name "${{ vars.APP_NAME }}-production" \
|
||||
--target-ip "${{ secrets.PANGOLIN_TARGET_IP }}"
|
||||
env:
|
||||
PANGOLIN_API_URL: ${{ secrets.PANGOLIN_API_URL }}
|
||||
@@ -63,11 +63,11 @@ jobs:
|
||||
docker build \
|
||||
--build-arg NEXT_PUBLIC_APP_ENV=preview \
|
||||
-t ${{ vars.APP_NAME }}:${{ steps.slug.outputs.slug }} \
|
||||
-f Dockerfile .
|
||||
-f dockerfile .
|
||||
|
||||
- name: Deploy preview container
|
||||
run: |
|
||||
bash scripts/deploy.sh \
|
||||
bash .gitea/scripts/deploy.sh \
|
||||
--name "${{ vars.APP_NAME }}" \
|
||||
--tag "${{ steps.slug.outputs.slug }}" \
|
||||
--port "${{ steps.slug.outputs.port }}" \
|
||||
@@ -75,10 +75,11 @@ jobs:
|
||||
|
||||
- name: Register Pangolin preview resource
|
||||
run: |
|
||||
bash scripts/pangolin-upsert.sh \
|
||||
bash .gitea/scripts/pangolin-upsert.sh \
|
||||
--subdomain "${{ steps.slug.outputs.slug }}.${{ vars.APP_NAME }}" \
|
||||
--port "${{ steps.slug.outputs.port }}" \
|
||||
--resource-name "${{ vars.APP_NAME }}-${{ steps.slug.outputs.slug }}"
|
||||
--resource-name "${{ vars.APP_NAME }}-${{ steps.slug.outputs.slug }}" \
|
||||
--target-ip "${{ secrets.PANGOLIN_TARGET_IP }}"
|
||||
env:
|
||||
PANGOLIN_API_URL: ${{ secrets.PANGOLIN_API_URL }}
|
||||
PANGOLIN_API_KEY: ${{ secrets.PANGOLIN_API_KEY }}
|
||||
@@ -103,7 +104,7 @@ jobs:
|
||||
|
||||
- name: Remove Pangolin resource
|
||||
run: |
|
||||
bash scripts/pangolin-delete.sh \
|
||||
bash .gitea/scripts/pangolin-delete.sh \
|
||||
--resource-name "${{ vars.APP_NAME }}-${{ steps.slug.outputs.slug }}"
|
||||
env:
|
||||
PANGOLIN_API_URL: ${{ secrets.PANGOLIN_API_URL }}
|
||||
|
||||
@@ -5,14 +5,10 @@
|
||||
await import("./src/env.mjs");
|
||||
|
||||
import createMDX from "@next/mdx";
|
||||
import rehypePrettyCode from "rehype-pretty-code";
|
||||
import remarkFrontmatter from "remark-frontmatter";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkMdxFrontmatter from "remark-mdx-frontmatter";
|
||||
|
||||
/** @type {import("next").NextConfig} */
|
||||
const config = {
|
||||
output: 'standalone',
|
||||
output: "standalone",
|
||||
pageExtensions: ["js", "jsx", "mdx", "ts", "tsx"],
|
||||
images: {
|
||||
remotePatterns: [
|
||||
@@ -39,9 +35,13 @@ const rehypePrettyCodeOptions = {
|
||||
|
||||
const withMDX = createMDX({
|
||||
options: {
|
||||
remarkPlugins: [remarkGfm, remarkFrontmatter, remarkMdxFrontmatter],
|
||||
remarkPlugins: [
|
||||
"remark-gfm",
|
||||
"remark-frontmatter",
|
||||
"remark-mdx-frontmatter",
|
||||
],
|
||||
// @ts-ignore
|
||||
rehypePlugins: [[rehypePrettyCode, rehypePrettyCodeOptions]],
|
||||
rehypePlugins: [["rehype-pretty-code", rehypePrettyCodeOptions]],
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
11356
package-lock.json
generated
11356
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
128
package.json
128
package.json
@@ -2,6 +2,7 @@
|
||||
"name": "zyrrus-website",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "next build",
|
||||
"db:push": "dotenv drizzle-kit push:mysql",
|
||||
@@ -11,80 +12,77 @@
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.1.0",
|
||||
"@dnd-kit/sortable": "^8.0.0",
|
||||
"@formkit/auto-animate": "^0.8.1",
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@formkit/auto-animate": "^0.9.0",
|
||||
"@mapbox/rehype-prism": "^0.9.0",
|
||||
"@mdx-js/loader": "^3.0.0",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"@next/mdx": "^13.5.6",
|
||||
"@planetscale/database": "^1.11.0",
|
||||
"@radix-ui/react-dialog": "^1.0.5",
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||
"@radix-ui/react-hover-card": "^1.0.7",
|
||||
"@radix-ui/react-label": "^2.0.2",
|
||||
"@radix-ui/react-popover": "^1.0.7",
|
||||
"@radix-ui/react-scroll-area": "^1.0.5",
|
||||
"@radix-ui/react-separator": "^1.0.3",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@radix-ui/react-switch": "^1.0.3",
|
||||
"@radix-ui/react-toast": "^1.1.5",
|
||||
"@radix-ui/react-tooltip": "^1.0.7",
|
||||
"@t3-oss/env-nextjs": "^0.7.0",
|
||||
"@tanstack/react-query": "^4.32.6",
|
||||
"@trpc/client": "^10.37.1",
|
||||
"@trpc/next": "^10.37.1",
|
||||
"@trpc/react-query": "^10.37.1",
|
||||
"@trpc/server": "^10.37.1",
|
||||
"@types/mdx": "^2.0.9",
|
||||
"@vercel/analytics": "^1.1.1",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"@mdx-js/loader": "^3.1.1",
|
||||
"@mdx-js/react": "^3.1.1",
|
||||
"@next/mdx": "^16.2.3",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||
"@radix-ui/react-hover-card": "^1.1.15",
|
||||
"@radix-ui/react-label": "^2.1.8",
|
||||
"@radix-ui/react-popover": "^1.1.15",
|
||||
"@radix-ui/react-scroll-area": "^1.2.10",
|
||||
"@radix-ui/react-separator": "^1.1.8",
|
||||
"@radix-ui/react-slot": "^1.2.4",
|
||||
"@radix-ui/react-switch": "^1.2.6",
|
||||
"@radix-ui/react-toast": "^1.2.15",
|
||||
"@radix-ui/react-tooltip": "^1.2.8",
|
||||
"@t3-oss/env-nextjs": "^0.13.11",
|
||||
"@tanstack/react-query": "^5.99.0",
|
||||
"@trpc/client": "^11.16.0",
|
||||
"@trpc/next": "^11.16.0",
|
||||
"@trpc/react-query": "^11.16.0",
|
||||
"@trpc/server": "^11.16.0",
|
||||
"@types/mdx": "^2.0.13",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.0.0",
|
||||
"cmdk": "^1.0.0",
|
||||
"drizzle-orm": "^0.28.5",
|
||||
"cmdk": "^1.1.1",
|
||||
"drizzle-orm": "^0.45.2",
|
||||
"exifr": "^7.1.3",
|
||||
"framer-motion": "^10.16.4",
|
||||
"lucide-react": "^0.288.0",
|
||||
"framer-motion": "^12.38.0",
|
||||
"lucide-react": "^1.8.0",
|
||||
"mini-svg-data-uri": "^1.4.4",
|
||||
"next": "^14.2.3",
|
||||
"next-mdx-remote": "^4.4.1",
|
||||
"next-themes": "^0.2.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-icons": "^4.11.0",
|
||||
"rehype-pretty-code": "^0.10.2",
|
||||
"next": "^16.2.3",
|
||||
"next-mdx-remote": "^6.0.0",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "^19.2.5",
|
||||
"react-dom": "^19.2.5",
|
||||
"react-icons": "^5.6.0",
|
||||
"rehype-pretty-code": "^0.14.3",
|
||||
"remark-frontmatter": "^5.0.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-mdx-frontmatter": "^4.0.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"remark-mdx-frontmatter": "^5.2.0",
|
||||
"seedrandom": "^3.0.5",
|
||||
"sharp": "^0.32.6",
|
||||
"shiki": "^0.14.5",
|
||||
"superjson": "^1.13.1",
|
||||
"swiper": "^11.0.7",
|
||||
"tailwind-merge": "^1.14.0",
|
||||
"sharp": "^0.34.5",
|
||||
"shiki": "^4.0.2",
|
||||
"superjson": "^2.2.6",
|
||||
"swiper": "^12.1.3",
|
||||
"tailwind-merge": "^3.5.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"zod": "^3.22.4"
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@types/eslint": "^8.44.2",
|
||||
"@types/node": "^18.16.0",
|
||||
"@types/react": "^18.2.20",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"@types/seedrandom": "^3.0.7",
|
||||
"@typescript-eslint/eslint-plugin": "^6.3.0",
|
||||
"@typescript-eslint/parser": "^6.3.0",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"dotenv-cli": "^7.3.0",
|
||||
"drizzle-kit": "^0.19.13",
|
||||
"eslint": "^8.47.0",
|
||||
"eslint-config-next": "^13.5.4",
|
||||
"mysql2": "^3.6.1",
|
||||
"postcss": "^8.4.27",
|
||||
"prettier": "^3.0.0",
|
||||
"prettier-plugin-tailwindcss": "^0.5.1",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"typescript": "^5.1.6"
|
||||
"@tailwindcss/postcss": "^4.2.2",
|
||||
"@tailwindcss/typography": "^0.5.19",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"@types/node": "^25.6.0",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@types/seedrandom": "^3.0.8",
|
||||
"@typescript-eslint/eslint-plugin": "^8.58.1",
|
||||
"@typescript-eslint/parser": "^8.58.1",
|
||||
"autoprefixer": "^10.4.27",
|
||||
"dotenv-cli": "^11.0.0",
|
||||
"eslint": "^10.2.0",
|
||||
"eslint-config-next": "^16.2.3",
|
||||
"postcss": "^8.5.9",
|
||||
"prettier": "^3.8.2",
|
||||
"prettier-plugin-tailwindcss": "^0.7.2",
|
||||
"tailwindcss": "^4.2.2",
|
||||
"typescript": "^6.0.2"
|
||||
},
|
||||
"ct3aMetadata": {
|
||||
"initVersion": "7.22.0"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const config = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
"@tailwindcss/postcss": {},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
1
src/additional.d.ts
vendored
Normal file
1
src/additional.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
declare module "swiper/css";
|
||||
@@ -118,19 +118,17 @@ const MotionCell = ({
|
||||
width={end.width}
|
||||
height={end.height}
|
||||
rx={radius}
|
||||
// Need to set initial position to start well
|
||||
initial
|
||||
x={start.x}
|
||||
y={start.y}
|
||||
// Setting origin to (0, 0) fixes rotation pivot point
|
||||
style={{ originX: 0, originY: 0 }}
|
||||
// Translation is relative to the initial (x, y)
|
||||
// Start in position immediately
|
||||
initial={{ x: start.x, y: start.y, rotate: rotation }}
|
||||
// Use absolute positions so clipPath rects and shadow rects stay aligned
|
||||
animate={
|
||||
shouldReduceMotion
|
||||
? { rotate: [rotation] }
|
||||
? { x: start.x, y: start.y, rotate: rotation }
|
||||
: {
|
||||
x: [0, end.x - start.x],
|
||||
y: [0, end.y - start.y],
|
||||
x: [start.x, end.x],
|
||||
y: [start.y, end.y],
|
||||
rotate: [rotation],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
TooltipTrigger,
|
||||
} from "~/app/components/ui/tooltip";
|
||||
import { type IconType } from "react-icons/lib";
|
||||
import { SidebarCommand } from "~/app/components/navigation/sidebar-command";
|
||||
import { ThemeToggle } from "~/app/components/theme/theme-toggle";
|
||||
import { Button } from "~/app/components/ui/button";
|
||||
import { type PropsWithChildren } from "react";
|
||||
@@ -62,22 +61,22 @@ const links: {
|
||||
{
|
||||
tooltip: "Home",
|
||||
href: "/",
|
||||
icon: GoHome,
|
||||
icon: GoHome as IconType,
|
||||
},
|
||||
{
|
||||
tooltip: "About",
|
||||
href: "/about",
|
||||
icon: GoPerson,
|
||||
icon: GoPerson as IconType,
|
||||
},
|
||||
{
|
||||
tooltip: "Resources",
|
||||
href: "/resources",
|
||||
icon: GoTerminal,
|
||||
icon: GoTerminal as IconType,
|
||||
},
|
||||
{
|
||||
tooltip: "Writing",
|
||||
href: "/writing",
|
||||
icon: GoPencil,
|
||||
icon: GoPencil as IconType,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import { ThemeProvider as NextThemesProvider } from "next-themes";
|
||||
import { type ThemeProviderProps } from "next-themes/dist/types";
|
||||
|
||||
type ThemeProviderProps = React.ComponentProps<typeof NextThemesProvider>;
|
||||
|
||||
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
|
||||
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import "~/styles/globals.css";
|
||||
|
||||
import { Analytics } from "@vercel/analytics/react";
|
||||
import { Inter } from "next/font/google";
|
||||
import { headers } from "next/headers";
|
||||
import { TRPCReactProvider } from "~/trpc/react";
|
||||
@@ -19,13 +18,14 @@ export const metadata = {
|
||||
icons: [{ rel: "icon", url: "/favicon.ico" }],
|
||||
};
|
||||
|
||||
export default function RootLayout({ children }: PropsWithChildren) {
|
||||
export default async function RootLayout({ children }: PropsWithChildren) {
|
||||
const hdrs = await headers();
|
||||
return (
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`font-sans ${inter.variable} relative bg-neutral-50 text-primary bg-dot-neutral-400/[0.2] selection:bg-accent selection:text-neutral-900 dark:bg-neutral-800 dark:bg-dot-neutral-600/[0.2]`}
|
||||
>
|
||||
<TRPCReactProvider headers={headers()}>
|
||||
<TRPCReactProvider headers={hdrs}>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
@@ -35,7 +35,6 @@ export default function RootLayout({ children }: PropsWithChildren) {
|
||||
{children}
|
||||
<Toaster />
|
||||
</ThemeProvider>
|
||||
<Analytics />
|
||||
</TRPCReactProvider>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import React, { RefObject } from "react";
|
||||
import { X, Trash2 } from "lucide-react";
|
||||
import { MultiSelect } from "~/app/components/multiselect";
|
||||
import { Badge } from "~/app/components/ui/badge";
|
||||
@@ -184,7 +184,7 @@ const SelectedTimeZones = ({
|
||||
|
||||
interface TimeZoneViewerProps {
|
||||
timeZones: Option[];
|
||||
scrollRef: React.RefObject<HTMLDivElement>;
|
||||
scrollRef: RefObject<HTMLDivElement | null>;
|
||||
startTimeString: string;
|
||||
endTimeString?: string;
|
||||
times: string[];
|
||||
|
||||
79
src/plugins/custom-utilities.ts
Normal file
79
src/plugins/custom-utilities.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import plugin from "tailwindcss/plugin";
|
||||
import svgToDataUri from "mini-svg-data-uri";
|
||||
|
||||
interface ColorPalette {
|
||||
[key: string]: string | ColorPalette;
|
||||
}
|
||||
|
||||
const flattenColorPalette = (
|
||||
colors: ColorPalette | null | undefined,
|
||||
): Record<string, string> => {
|
||||
if (!colors) return {};
|
||||
|
||||
const flattened: Record<string, string> = {};
|
||||
|
||||
Object.entries(colors).forEach(([color, values]) => {
|
||||
if (typeof values === "object") {
|
||||
const nestedFlatten = flattenColorPalette(values);
|
||||
Object.entries(nestedFlatten).forEach(([number, hex]) => {
|
||||
flattened[`${color}${number === "DEFAULT" ? "" : `-${number}`}`] = hex;
|
||||
});
|
||||
} else {
|
||||
flattened[color] = values;
|
||||
}
|
||||
});
|
||||
|
||||
return flattened;
|
||||
};
|
||||
|
||||
export default plugin(function ({ matchComponents, matchUtilities, theme }) {
|
||||
// Display text background headings
|
||||
matchComponents({
|
||||
"display-text": (value) => ({
|
||||
position: "relative",
|
||||
"&::before": {
|
||||
content: value,
|
||||
position: "absolute",
|
||||
fontWeight: "800",
|
||||
fontSize: "min(12.5rem, calc(1.5rem + 10vw))",
|
||||
lineHeight: "1",
|
||||
letterSpacing: "0.09em",
|
||||
left: "clamp(-17.5rem, calc(-43vw + 20rem), 0px)",
|
||||
top: "0",
|
||||
transform: "translateY(-50%)",
|
||||
zIndex: "-30",
|
||||
color: "var(--color-neutral-150)",
|
||||
},
|
||||
":is(.dark, .dark *) &::before": {
|
||||
color: "var(--color-neutral-750)",
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
// Background pattern utilities (grids + dots)
|
||||
matchUtilities(
|
||||
{
|
||||
"bg-grid": (value: string) => ({
|
||||
backgroundImage: `url("${svgToDataUri(
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" fill="none" stroke="${value}"><path d="M0 .5H31.5V32"/></svg>`,
|
||||
)}")`,
|
||||
}),
|
||||
"bg-grid-small": (value: string) => ({
|
||||
backgroundImage: `url("${svgToDataUri(
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="8" height="8" fill="none" stroke="${value}"><path d="M0 .5H31.5V32"/></svg>`,
|
||||
)}")`,
|
||||
}),
|
||||
"bg-dot": (value: string) => ({
|
||||
backgroundImage: `url("${svgToDataUri(
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="16" height="16" fill="none"><circle fill="${value}" id="pattern-circle" cx="10" cy="10" r="1.6257413380501518"></circle></svg>`,
|
||||
)}")`,
|
||||
}),
|
||||
},
|
||||
{
|
||||
values: flattenColorPalette(
|
||||
theme("backgroundColor") as ColorPalette | null | undefined,
|
||||
),
|
||||
type: "color",
|
||||
},
|
||||
);
|
||||
});
|
||||
@@ -1,3 +1,149 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@import "tailwindcss";
|
||||
|
||||
@plugin "@tailwindcss/typography";
|
||||
@plugin "tailwindcss-animate";
|
||||
@plugin "../plugins/custom-utilities.ts";
|
||||
|
||||
@source "../**/*.{ts,tsx,mdx}";
|
||||
|
||||
/* Class-based dark mode (for next-themes) */
|
||||
@variant dark (&:where(.dark, .dark *));
|
||||
|
||||
/* ── Theme ────────────────────────────────────────────────────────── */
|
||||
|
||||
@theme {
|
||||
--font-sans: "InterVariable", "Inter", ui-sans-serif, system-ui,
|
||||
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
|
||||
Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
|
||||
"Segoe UI Symbol", "Noto Color Emoji";
|
||||
|
||||
--breakpoint-sm: 588px;
|
||||
|
||||
--color-accent: #77aca9;
|
||||
|
||||
--color-neutral-*: initial;
|
||||
--color-neutral-50: #fefefe;
|
||||
--color-neutral-100: #f8f8f8;
|
||||
--color-neutral-150: #f4f4f4;
|
||||
--color-neutral-200: #e5e5e5;
|
||||
--color-neutral-300: #d4d4d4;
|
||||
--color-neutral-400: #a3a3a3;
|
||||
--color-neutral-500: #737373;
|
||||
--color-neutral-600: #525252;
|
||||
--color-neutral-700: #404040;
|
||||
--color-neutral-750: #333333;
|
||||
--color-neutral-800: #262626;
|
||||
--color-neutral-850: #222222;
|
||||
--color-neutral-900: #202020;
|
||||
--color-neutral-950: #131313;
|
||||
|
||||
--color-gruv-red-fg: #88403c;
|
||||
--color-gruv-red-bg: #ea6962;
|
||||
--color-gruv-orange-fg: #855231;
|
||||
--color-gruv-orange-bg: #e78a4e;
|
||||
--color-gruv-yellow-fg: #795f34;
|
||||
--color-gruv-yellow-bg: #d8a657;
|
||||
--color-gruv-green-fg: #585e36;
|
||||
--color-gruv-green-bg: #a9b665;
|
||||
--color-gruv-blue-fg: #405852;
|
||||
--color-gruv-blue-bg: #7daea3;
|
||||
|
||||
--animate-accordion-down: accordion-down 0.2s ease-out;
|
||||
--animate-accordion-up: accordion-up 0.2s ease-out;
|
||||
|
||||
--duration-2000: 2000ms;
|
||||
|
||||
--shadow-cutout-depth-0: 0px 25px 15px 0px rgba(0, 0, 0, 0.15) inset;
|
||||
--shadow-cutout-depth-1: 0px 25px 15px 0px rgba(0, 0, 0, 0.15) inset,
|
||||
0px 48px 15px 0px rgba(0, 0, 0, 0.05) inset;
|
||||
|
||||
--ease-bounce-up: cubic-bezier(0.5, 2.5, 0.7, 0.7);
|
||||
}
|
||||
|
||||
@keyframes accordion-down {
|
||||
from {
|
||||
height: 0;
|
||||
}
|
||||
to {
|
||||
height: var(--radix-accordion-content-height);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes accordion-up {
|
||||
from {
|
||||
height: var(--radix-accordion-content-height);
|
||||
}
|
||||
to {
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Container override ───────────────────────────────────────────── */
|
||||
|
||||
@utility container {
|
||||
width: 100%;
|
||||
margin-inline: auto;
|
||||
padding-inline: 1.5rem;
|
||||
@media (width >= 588px) {
|
||||
max-width: 588px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Semantic color utilities ─────────────────────────────────────── */
|
||||
|
||||
@utility text-primary {
|
||||
color: var(--color-neutral-900);
|
||||
.dark & {
|
||||
color: var(--color-neutral-50);
|
||||
}
|
||||
}
|
||||
|
||||
@utility text-secondary {
|
||||
color: var(--color-neutral-500);
|
||||
.dark & {
|
||||
color: var(--color-neutral-400);
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-0 {
|
||||
background-color: var(--color-neutral-50);
|
||||
.dark & {
|
||||
background-color: var(--color-neutral-800);
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-1 {
|
||||
background-color: var(--color-neutral-100);
|
||||
.dark & {
|
||||
background-color: var(--color-neutral-850);
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-2 {
|
||||
background-color: var(--color-neutral-150);
|
||||
.dark & {
|
||||
background-color: var(--color-neutral-900);
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Typography / prose overrides ─────────────────────────────────── */
|
||||
|
||||
.prose code::before,
|
||||
.prose code::after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
.prose code:not(:is(pre code)) {
|
||||
padding: 0.2em 0.4em;
|
||||
white-space: break-spaces;
|
||||
font-size: 85%;
|
||||
font-weight: 400;
|
||||
border-radius: 6px;
|
||||
color: var(--color-neutral-700);
|
||||
background-color: var(--color-neutral-200);
|
||||
}
|
||||
|
||||
.dark .prose code:not(:is(pre code)) {
|
||||
color: var(--color-neutral-300);
|
||||
background-color: var(--color-neutral-950);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { loggerLink, unstable_httpBatchStreamLink } from "@trpc/client";
|
||||
import { loggerLink, httpBatchStreamLink } from "@trpc/client";
|
||||
import { createTRPCReact } from "@trpc/react-query";
|
||||
import { useState } from "react";
|
||||
|
||||
@@ -18,14 +18,14 @@ export function TRPCReactProvider(props: {
|
||||
|
||||
const [trpcClient] = useState(() =>
|
||||
api.createClient({
|
||||
transformer,
|
||||
links: [
|
||||
loggerLink({
|
||||
enabled: (op) =>
|
||||
process.env.NODE_ENV === "development" ||
|
||||
(op.direction === "down" && op.result instanceof Error),
|
||||
}),
|
||||
unstable_httpBatchStreamLink({
|
||||
httpBatchStreamLink({
|
||||
transformer,
|
||||
url: getUrl(),
|
||||
headers() {
|
||||
const heads = new Map(props.headers);
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
import {
|
||||
createTRPCProxyClient,
|
||||
createTRPCClient,
|
||||
loggerLink,
|
||||
unstable_httpBatchStreamLink,
|
||||
httpBatchStreamLink,
|
||||
} from "@trpc/client";
|
||||
import { headers } from "next/headers";
|
||||
|
||||
import { type AppRouter } from "~/server/api/root";
|
||||
import { getUrl, transformer } from "./shared";
|
||||
|
||||
export const api = createTRPCProxyClient<AppRouter>({
|
||||
transformer,
|
||||
export const api = createTRPCClient<AppRouter>({
|
||||
links: [
|
||||
loggerLink({
|
||||
enabled: (op) =>
|
||||
process.env.NODE_ENV === "development" ||
|
||||
(op.direction === "down" && op.result instanceof Error),
|
||||
}),
|
||||
unstable_httpBatchStreamLink({
|
||||
httpBatchStreamLink({
|
||||
transformer,
|
||||
url: getUrl(),
|
||||
headers() {
|
||||
const heads = new Map(headers());
|
||||
async headers() {
|
||||
const hdrs = await headers();
|
||||
const heads = new Map(hdrs);
|
||||
heads.set("x-trpc-source", "rsc");
|
||||
return Object.fromEntries(heads);
|
||||
},
|
||||
|
||||
@@ -3,10 +3,18 @@ import path from "path";
|
||||
import { compileMDX } from "next-mdx-remote/rsc";
|
||||
import { type PostFrontmatter } from "~/utils/server/mdx/types";
|
||||
|
||||
const rootDir = path.join(process.cwd(), "src", "app", "(home)", "writing", "(posts)");
|
||||
const rootDir = path.join(
|
||||
process.cwd(),
|
||||
"src",
|
||||
"app",
|
||||
"(home)",
|
||||
"writing",
|
||||
"(posts)",
|
||||
);
|
||||
|
||||
const isValidMdxPage = (dirent: Dirent) => {
|
||||
const { name: fileName, path: filePath } = dirent;
|
||||
const fileName = dirent.name;
|
||||
const filePath = dirent.parentPath;
|
||||
|
||||
const subFiles = fs.readdirSync(path.join(filePath, fileName), {
|
||||
withFileTypes: true,
|
||||
|
||||
@@ -1,217 +1,3 @@
|
||||
import defaultTheme from "tailwindcss/defaultTheme";
|
||||
import plugin from "tailwindcss/plugin";
|
||||
import { type PluginAPI } from "tailwindcss/types/config";
|
||||
import type { Config } from "tailwindcss";
|
||||
import svgToDataUri from "mini-svg-data-uri";
|
||||
|
||||
export default {
|
||||
darkMode: "class",
|
||||
content: ["./src/**/*.{ts,tsx,mdx}"],
|
||||
theme: {
|
||||
fontFamily: {
|
||||
sans: ["InterVariable", "Inter", ...defaultTheme.fontFamily.sans],
|
||||
},
|
||||
container: {
|
||||
center: true,
|
||||
padding: "1.5rem",
|
||||
screens: {
|
||||
sm: "588px",
|
||||
},
|
||||
},
|
||||
extend: {
|
||||
screens: {
|
||||
sm: "588px",
|
||||
},
|
||||
colors: {
|
||||
accent: "#77ACA9", // Selection
|
||||
neutral: {
|
||||
50: "#fefefe", // (Dark theme) primary text | (Light theme) bg
|
||||
100: "#f8f8f8", // (Light theme) card depth 0, display text
|
||||
150: "#f4f4f4", // (Light theme) display text, card depth 1,
|
||||
200: "#e5e5e5", // (Light theme) borders, code bg
|
||||
300: "#d4d4d4", // (Dark theme) code text
|
||||
400: "#a3a3a3", // (Dark theme) secondary text, icons
|
||||
500: "#737373",
|
||||
600: "#525252", // (Light theme) secondary text, icons
|
||||
700: "#404040", // (Dark theme) borders, display text (25%) | (Light theme) code text
|
||||
750: "#333333", // (Dark theme) card highlight
|
||||
800: "#262626", // (Light theme) primary button fill | (Dark theme) bg
|
||||
850: "#222222", // (Dark theme) card depth 0
|
||||
900: "#202020", // (Dark theme) card depth 1 | (Light theme) primary text
|
||||
950: "#131313", // (Dark theme) code bg
|
||||
},
|
||||
"gruv-red": {
|
||||
fg: "#88403c",
|
||||
bg: "#EA6962",
|
||||
},
|
||||
"gruv-orange": {
|
||||
fg: "#855231",
|
||||
bg: "#E78A4E",
|
||||
},
|
||||
"gruv-yellow": {
|
||||
fg: "#795F34",
|
||||
bg: "#D8A657",
|
||||
},
|
||||
"gruv-green": {
|
||||
fg: "#585E36",
|
||||
bg: "#A9B665",
|
||||
},
|
||||
"gruv-blue": {
|
||||
fg: "#405852",
|
||||
bg: "#7DAEA3",
|
||||
},
|
||||
},
|
||||
keyframes: {
|
||||
"accordion-down": {
|
||||
from: { height: "0" },
|
||||
to: { height: "var(--radix-accordion-content-height)" },
|
||||
},
|
||||
"accordion-up": {
|
||||
from: { height: "var(--radix-accordion-content-height)" },
|
||||
to: { height: "0" },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
"accordion-down": "accordion-down 0.2s ease-out",
|
||||
"accordion-up": "accordion-up 0.2s ease-out",
|
||||
},
|
||||
transitionDuration: {
|
||||
"2000": "2000ms",
|
||||
},
|
||||
boxShadow: {
|
||||
"cutout-depth-0": "0px 25px 15px 0px rgba(0, 0, 0, 0.15) inset",
|
||||
"cutout-depth-1":
|
||||
"0px 25px 15px 0px rgba(0, 0, 0, 0.15) inset, 0px 48px 15px 0px rgba(0, 0, 0, 0.05) inset",
|
||||
},
|
||||
transitionTimingFunction: {
|
||||
"bounce-up": "cubic-bezier(0.5, 2.5, 0.7, 0.7)",
|
||||
},
|
||||
typography(theme: PluginAPI["theme"]) {
|
||||
return {
|
||||
DEFAULT: {
|
||||
css: {
|
||||
"code::before": { content: "none" },
|
||||
"code::after": { content: "none" },
|
||||
// Inline code blocks
|
||||
"code:not(:is(pre code))": {
|
||||
padding: "0.2em 0.4em",
|
||||
whiteSpace: "break-spaces",
|
||||
fontSize: "85%",
|
||||
fontWeight: "400",
|
||||
borderRadius: "6px",
|
||||
// Light theme
|
||||
color: theme("colors.neutral.700"),
|
||||
backgroundColor: theme("colors.neutral.200"),
|
||||
},
|
||||
".dark code:not(:is(pre code))": {
|
||||
color: theme("colors.neutral.300"),
|
||||
backgroundColor: theme("colors.neutral.950"),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
require("tailwindcss-animate"),
|
||||
require("@tailwindcss/typography"),
|
||||
plugin(function ({ matchComponents, addUtilities, theme }) {
|
||||
matchComponents({
|
||||
"display-text": (value) => ({
|
||||
position: "relative",
|
||||
"&:before": {
|
||||
content: value,
|
||||
position: "absolute",
|
||||
fontWeight: "800",
|
||||
fontSize: "min(12.5rem, calc(1.5rem + 10vw))",
|
||||
lineHeight: "1",
|
||||
letterSpacing: "0.09em",
|
||||
left: "clamp(-17.5rem, calc(-43vw + 20rem), 0px)",
|
||||
top: "0",
|
||||
transform: "translateY(-50%)",
|
||||
zIndex: "-30",
|
||||
"@apply text-neutral-150": {},
|
||||
"@apply dark:text-neutral-750": {},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
addUtilities({
|
||||
".text-primary": {
|
||||
color: theme("colors.neutral.900"),
|
||||
"@apply dark:text-neutral-50": {},
|
||||
},
|
||||
".text-secondary": {
|
||||
color: theme("colors.neutral.500"),
|
||||
"@apply dark:text-neutral-400": {},
|
||||
},
|
||||
// Background elevation surface (0 is base, 1 is card, ...)
|
||||
".bg-0": {
|
||||
backgroundColor: theme("colors.neutral.50"),
|
||||
"@apply dark:bg-neutral-800": {},
|
||||
},
|
||||
".bg-1": {
|
||||
backgroundColor: theme("colors.neutral.100"),
|
||||
"@apply dark:bg-neutral-850": {},
|
||||
},
|
||||
".bg-2": {
|
||||
backgroundColor: theme("colors.neutral.150"),
|
||||
"@apply dark:bg-neutral-900": {},
|
||||
},
|
||||
});
|
||||
}),
|
||||
plugin(function ({ matchUtilities, theme }) {
|
||||
matchUtilities(
|
||||
{
|
||||
"bg-grid": (value: string) => ({
|
||||
backgroundImage: `url("${svgToDataUri(
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" fill="none" stroke="${value}"><path d="M0 .5H31.5V32"/></svg>`,
|
||||
)}")`,
|
||||
}),
|
||||
"bg-grid-small": (value: string) => ({
|
||||
backgroundImage: `url("${svgToDataUri(
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="8" height="8" fill="none" stroke="${value}"><path d="M0 .5H31.5V32"/></svg>`,
|
||||
)}")`,
|
||||
}),
|
||||
"bg-dot": (value: string) => ({
|
||||
backgroundImage: `url("${svgToDataUri(
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="16" height="16" fill="none"><circle fill="${value}" id="pattern-circle" cx="10" cy="10" r="1.6257413380501518"></circle></svg>`,
|
||||
)}")`,
|
||||
}),
|
||||
},
|
||||
{
|
||||
values: flattenColorPalette(theme("backgroundColor")),
|
||||
type: "color",
|
||||
},
|
||||
);
|
||||
}),
|
||||
],
|
||||
} satisfies Config;
|
||||
|
||||
// Background Grids + Dots
|
||||
|
||||
interface ColorPalette {
|
||||
[key: string]: string | ColorPalette;
|
||||
}
|
||||
|
||||
const flattenColorPalette = (
|
||||
colors: ColorPalette | null | undefined,
|
||||
): Record<string, string> => {
|
||||
if (!colors) return {};
|
||||
|
||||
const flattened: Record<string, string> = {};
|
||||
|
||||
Object.entries(colors).forEach(([color, values]) => {
|
||||
if (typeof values === "object") {
|
||||
const nestedFlatten = flattenColorPalette(values);
|
||||
Object.entries(nestedFlatten).forEach(([number, hex]) => {
|
||||
flattened[`${color}${number === "DEFAULT" ? "" : `-${number}`}`] = hex;
|
||||
});
|
||||
} else {
|
||||
flattened[color] = values;
|
||||
}
|
||||
});
|
||||
|
||||
return flattened;
|
||||
};
|
||||
export default {} satisfies Config;
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2017",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"target": "es2022",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"skipLibCheck": true,
|
||||
@@ -10,17 +14,22 @@
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"jsx": "react-jsx",
|
||||
"incremental": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": ["./src/*"]
|
||||
"~/*": [
|
||||
"./src/*"
|
||||
]
|
||||
},
|
||||
"plugins": [{ "name": "next" }]
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
".eslintrc.cjs",
|
||||
@@ -30,7 +39,10 @@
|
||||
"**/*.cjs",
|
||||
"**/*.mjs",
|
||||
"**/*.mdx",
|
||||
".next/types/**/*.ts"
|
||||
".next/types/**/*.ts",
|
||||
".next/dev/types/**/*.ts"
|
||||
],
|
||||
"exclude": ["node_modules"]
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user