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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,5 @@ CLAUDE.md

site/SPECIFICATION.md
site/DESIGN.md
site/ARCHITECTURE.md
site/ARCHITECTURE.md .claude/settings.json
tmp
223 changes: 174 additions & 49 deletions bun.lock

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion bunfig.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ logsdx = "https://registry.npmjs.org/"
[build]
sourcemap = "external"
minify = true
target = "node"
target = "node"

[test]
root = "tests/"
36 changes: 32 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,37 @@
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
"require": "./dist/index.cjs",
"default": "./dist/index.mjs"
},
"./themes": {
"types": "./dist/themes/index.d.ts",
"import": "./dist/themes/index.mjs",
"require": "./dist/themes/index.cjs",
"default": "./dist/themes/index.mjs"
},
"./themes/*": {
"types": "./dist/themes/presets/*.d.ts",
"import": "./dist/themes/presets/*.mjs",
"require": "./dist/themes/presets/*.cjs",
"default": "./dist/themes/presets/*.mjs"
},
"./renderer": {
"types": "./dist/renderer/index.d.ts",
"import": "./dist/renderer/index.mjs",
"require": "./dist/renderer/index.cjs",
"default": "./dist/renderer/index.mjs"
},
"./fast": {
"types": "./dist/renderer/fast-mode.d.ts",
"import": "./dist/renderer/fast-mode.mjs",
"require": "./dist/renderer/fast-mode.cjs",
"default": "./dist/renderer/fast-mode.mjs"
}
},
"sideEffects": false,
"files": [
"dist/"
],
Expand All @@ -29,9 +56,10 @@
"build:esm": "bun build src/index.ts --outfile dist/index.mjs --format esm --minify",
"build:cli": "bun build src/cli/bin.ts --outfile dist/cli.js --format cjs --minify --target node",
"build:types": "tsc -p tsconfig.build.json",
"test": "bun test",
"test:coverage": "bun test --coverage --coverage-reporter=lcov",
"test:watch": "bun test --watch",
"test": "bun test tests/",
"test:coverage": "bun test tests/ --coverage --coverage-reporter=lcov",
"test:watch": "bun test tests/ --watch",
"site:test": "cd site && bun test",
"lint": "oxlint .",
"lint:fix": "oxlint . --fix",
"format": "bun run prettier --write .",
Expand Down
44 changes: 44 additions & 0 deletions scripts/build-themes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env bun
import { build } from "bun";
import { readdirSync } from "fs";
import { join } from "path";

const themesDir = join(import.meta.dir, "../src/themes/presets");
const outputDir = join(import.meta.dir, "../dist/themes/presets");

const themeFiles = readdirSync(themesDir).filter((file) =>
file.endsWith(".ts"),
);

console.log(`Building ${themeFiles.length} theme presets...`);

const buildPromises = themeFiles.map(async (file) => {
const themeName = file.replace(".ts", "");
const inputPath = join(themesDir, file);

await Promise.all([
build({
entrypoints: [inputPath],
outdir: outputDir,
format: "esm",
minify: true,
naming: `${themeName}.mjs`,
target: "browser",
}),

build({
entrypoints: [inputPath],
outdir: outputDir,
format: "cjs",
minify: true,
naming: `${themeName}.cjs`,
target: "node",
}),
]);

console.log(`✓ Built theme: ${themeName}`);
});

await Promise.all(buildPromises);

console.log("✓ All themes built successfully!");
61 changes: 61 additions & 0 deletions site/.github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: Tests

on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]

jobs:
unit-tests:
runs-on: ubuntu-latest
timeout-minutes: 10

steps:
- uses: actions/checkout@v4

- uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Run unit tests
run: bun test tests/

- name: Upload coverage
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results
path: coverage/
retention-days: 7

e2e-tests:
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- uses: actions/checkout@v4

- uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Install Playwright
run: bun run playwright:install --with-deps

- name: Run E2E tests
run: bun run test:e2e --project=chromium

- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 7
11 changes: 2 additions & 9 deletions site/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import { ThemeProvider } from "@/components/theme-provider";
import { Providers } from "@/components/providers";

const inter = Inter({ subsets: ["latin"] });

Expand All @@ -19,14 +19,7 @@ export default function RootLayout({
return (
<html lang="en" suppressHydrationWarning>
<body className={inter.className}>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
{children}
</ThemeProvider>
<Providers>{children}</Providers>
</body>
</html>
);
Expand Down
4 changes: 4 additions & 0 deletions site/bunfig.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[test]
preload = ["./test-setup.ts"]
root = "./tests"
isolate = true
104 changes: 104 additions & 0 deletions site/components/interactive/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { describe, test, expect, afterEach } from "bun:test";
import { render, screen, cleanup } from "@/tests/utils/test-utils";
import { userEvent } from "@testing-library/user-event";
import { InteractiveExamplesSection } from "../index";

describe("InteractiveExamplesSection", () => {
afterEach(cleanup);

test("renders section heading", () => {
render(<InteractiveExamplesSection />);

expect(screen.getByText(/Interactive/)).toBeTruthy();
expect(screen.getByText(/Theme Preview/)).toBeTruthy();
});

test("renders all theme selector buttons", () => {
render(<InteractiveExamplesSection />);

const themeNames = [
"GitHub",
"Solarized",
"Dracula",
"Nord",
"Monokai",
"Oh My Zsh",
];

themeNames.forEach((theme) => {
const button = screen.getByRole("button", { name: theme });
expect(button).toBeTruthy();
});
});

test("renders terminal and browser preview panes", () => {
render(<InteractiveExamplesSection />);

expect(screen.getByText("Terminal")).toBeTruthy();
expect(screen.getByText("Browser Console")).toBeTruthy();
});

test("changes theme on button click", async () => {
const user = userEvent.setup({ delay: null });
const { container } = render(<InteractiveExamplesSection />);

const draculaBtn = screen.getByRole("button", { name: "Dracula" });
await user.click(draculaBtn);

const content = container.textContent || "";
expect(content).toContain("Dracula");
});

test("renders color mode toggle buttons", () => {
render(<InteractiveExamplesSection />);

const allButtons = screen.getAllByRole("button");
const hasColorModeButtons = allButtons.some((btn) =>
btn.querySelector('[class*="lucide"]'),
);

expect(hasColorModeButtons).toBe(true);
});

test("displays sample logs in preview panes", () => {
const { container } = render(<InteractiveExamplesSection />);

const content = container.textContent || "";
expect(content).toContain("INFO");
expect(content).toContain("ERROR");
expect(content).toContain("WARN");
});

test("renders code examples section", () => {
render(<InteractiveExamplesSection />);

expect(screen.getByText("Quick Integration")).toBeTruthy();
expect(screen.getByText("Logger Integration Examples")).toBeTruthy();
});

test("shows code blocks with getLogsDX usage", () => {
const { container } = render(<InteractiveExamplesSection />);

const codeText = container.textContent || "";
expect(codeText).toContain("getLogsDX");
expect(codeText).toContain("processLine");
});

test("renders winston integration example", () => {
render(<InteractiveExamplesSection />);

expect(screen.getByText("Winston")).toBeTruthy();
});

test("renders pino integration example", () => {
render(<InteractiveExamplesSection />);

expect(screen.getByText("Pino")).toBeTruthy();
});

test("renders console override example", () => {
render(<InteractiveExamplesSection />);

expect(screen.getByText("Console Override")).toBeTruthy();
});
});
18 changes: 12 additions & 6 deletions site/components/interactive/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useRef } from "react";
import { Button } from "@/components/ui/button";
import { Moon, Sun, Monitor } from "lucide-react";
import type { ThemeConfig, ThemePair, ColorMode } from "./types";
Expand Down Expand Up @@ -295,14 +295,17 @@ export function InteractiveExamplesSection() {
const [selectedTheme, setSelectedTheme] = useState("GitHub");
const [colorMode, setColorMode] = useState<ColorMode>("system");
const [effectiveMode, setEffectiveMode] = useState<"light" | "dark">("dark");
const autoRotateRef = useRef(true);

useEffect(() => {
const themes = Object.keys(THEME_PAIRS);
const interval = setInterval(() => {
setSelectedTheme((current) => {
const currentIndex = themes.indexOf(current);
return themes[(currentIndex + 1) % themes.length];
});
if (autoRotateRef.current) {
setSelectedTheme((current) => {
const currentIndex = themes.indexOf(current);
return themes[(currentIndex + 1) % themes.length];
});
}
}, 3000);

return () => clearInterval(interval);
Expand Down Expand Up @@ -363,7 +366,10 @@ export function InteractiveExamplesSection() {
key={theme}
variant={selectedTheme === theme ? "default" : "outline"}
size="sm"
onClick={() => setSelectedTheme(theme)}
onClick={() => {
setSelectedTheme(theme);
autoRotateRef.current = false;
}}
>
{theme}
</Button>
Expand Down
7 changes: 5 additions & 2 deletions site/components/mdx/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,11 @@ function extractCodeContent(children: ReactNode): string | null {
return null;
}

if ("props" in children && children.props?.children) {
return extractCodeContent(children.props.children);
if ("props" in children) {
const element = children as React.ReactElement<{ children?: ReactNode }>;
if (element.props?.children) {
return extractCodeContent(element.props.children);
}
}

if (Array.isArray(children)) {
Expand Down
Loading