From 7e15c36e2f4197eec078773f6649763dae770df0 Mon Sep 17 00:00:00 2001 From: teernisse Date: Thu, 29 Jan 2026 22:55:31 -0500 Subject: [PATCH] Add project scaffolding and build configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initialize the session-viewer project — a tool for browsing, filtering, redacting, and exporting Claude Code session logs as self-contained HTML. Scaffolding includes: - package.json with Express server + React client dual-stack setup, dev/build/test/lint/typecheck scripts, and a CLI bin entry point - TypeScript configs: base tsconfig.json (ESNext, bundler resolution, strict, react-jsx) and tsconfig.server.json extending it for the Express server compilation target - Vite config: React plugin, Tailscale-aware dev server on :3847 with API proxy to :3848, client build output to dist/client - Vitest config: node environment, test discovery from tests/unit and src/client/components - ESLint flat config: typescript-eslint recommended, unused-vars with underscore exception - Tailwind CSS config scoped to src/client, PostCSS with autoprefixer - Playwright config: Chromium-only E2E against dev server - bin/session-viewer.js: CLI entry point that re-execs via tsx with browser-open flag Co-Authored-By: Claude Opus 4.5 --- bin/session-viewer.js | 24 +++++++++++++++++++ eslint.config.js | 18 +++++++++++++++ package.json | 54 +++++++++++++++++++++++++++++++++++++++++++ playwright.config.ts | 26 +++++++++++++++++++++ postcss.config.js | 6 +++++ tailwind.config.js | 8 +++++++ tsconfig.json | 23 ++++++++++++++++++ tsconfig.server.json | 10 ++++++++ vite.config.ts | 30 ++++++++++++++++++++++++ vitest.config.ts | 18 +++++++++++++++ 10 files changed, 217 insertions(+) create mode 100644 bin/session-viewer.js create mode 100644 eslint.config.js create mode 100644 package.json create mode 100644 playwright.config.ts create mode 100644 postcss.config.js create mode 100644 tailwind.config.js create mode 100644 tsconfig.json create mode 100644 tsconfig.server.json create mode 100644 vite.config.ts create mode 100644 vitest.config.ts diff --git a/bin/session-viewer.js b/bin/session-viewer.js new file mode 100644 index 0000000..6d6e3a6 --- /dev/null +++ b/bin/session-viewer.js @@ -0,0 +1,24 @@ +#!/usr/bin/env node + +// This bin script needs tsx to run TypeScript source directly. +// Use --import to register the tsx loader before importing our TS entry. +import { execFileSync } from "child_process"; +import { fileURLToPath } from "url"; +import path from "path"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const entryPoint = path.resolve(__dirname, "../src/server/index.ts"); + +// Re-exec with tsx if not already running under it +try { + execFileSync( + "npx", + ["tsx", entryPoint], + { + stdio: "inherit", + env: { ...process.env, SESSION_VIEWER_OPEN_BROWSER: "1" }, + } + ); +} catch { + process.exit(1); +} diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..9188236 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,18 @@ +import js from "@eslint/js"; +import tseslint from "typescript-eslint"; + +export default tseslint.config( + js.configs.recommended, + ...tseslint.configs.recommended, + { + ignores: ["dist/**", "node_modules/**", "bin/**"], + }, + { + rules: { + "@typescript-eslint/no-unused-vars": [ + "error", + { argsIgnorePattern: "^_" }, + ], + }, + } +); diff --git a/package.json b/package.json new file mode 100644 index 0000000..6fe7342 --- /dev/null +++ b/package.json @@ -0,0 +1,54 @@ +{ + "name": "session-viewer", + "version": "1.0.0", + "description": "Browse, filter, redact, and export Claude Code sessions as self-contained HTML", + "type": "module", + "bin": { + "session-viewer": "./bin/session-viewer.js" + }, + "scripts": { + "dev": "concurrently \"npm run dev:server\" \"npm run dev:client\"", + "dev:server": "tsx watch src/server/index.ts", + "dev:client": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "test": "vitest run", + "test:watch": "vitest", + "test:e2e": "playwright test", + "typecheck": "tsc --noEmit", + "lint": "eslint . --ext .ts,.tsx" + }, + "dependencies": { + "express": "^4.21.0", + "highlight.js": "^11.10.0", + "marked": "^14.0.0", + "marked-highlight": "^2.2.3", + "open": "^10.1.0", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@eslint/js": "^9.0.0", + "@playwright/test": "^1.48.0", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", + "@types/express": "^4.17.21", + "@types/node": "^22.0.0", + "@types/react": "^18.3.0", + "@types/react-dom": "^18.3.0", + "@types/supertest": "^6.0.0", + "@vitejs/plugin-react": "^4.3.0", + "autoprefixer": "^10.4.20", + "concurrently": "^9.0.0", + "eslint": "^9.0.0", + "jsdom": "^27.4.0", + "postcss": "^8.4.47", + "supertest": "^7.0.0", + "tailwindcss": "^3.4.13", + "tsx": "^4.19.0", + "typescript": "^5.6.0", + "typescript-eslint": "^8.0.0", + "vite": "^5.4.0", + "vitest": "^2.1.0" + } +} diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..5fa77aa --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,26 @@ +import { defineConfig, devices } from "@playwright/test"; + +export default defineConfig({ + testDir: "./tests/e2e", + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: "html", + use: { + baseURL: "http://localhost:3847", + trace: "on-first-retry", + }, + projects: [ + { + name: "chromium", + use: { ...devices["Desktop Chrome"] }, + }, + ], + webServer: { + command: "npm run dev", + url: "http://localhost:3847", + reuseExistingServer: !process.env.CI, + timeout: 30000, + }, +}); diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..2aa7205 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..de50715 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,8 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ["./src/client/**/*.{html,tsx,ts}"], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..a901544 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "outDir": "dist", + "rootDir": ".", + "baseUrl": ".", + "paths": { + "@shared/*": ["src/shared/*"] + } + }, + "include": ["src/**/*", "tests/**/*", "vite.config.ts", "vitest.config.ts", "playwright.config.ts"], + "exclude": ["node_modules", "dist"] +} diff --git a/tsconfig.server.json b/tsconfig.server.json new file mode 100644 index 0000000..65be72c --- /dev/null +++ b/tsconfig.server.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "ESNext", + "moduleResolution": "bundler", + "outDir": "dist/server", + "rootDir": "src" + }, + "include": ["src/server/**/*", "src/shared/**/*"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..701546a --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,30 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import path from "path"; + +export default defineConfig({ + plugins: [react()], + root: "src/client", + resolve: { + alias: { + "@shared": path.resolve(__dirname, "src/shared"), + }, + }, + server: { + port: 3847, + // Vite only supports one host. Use Tailscale IP so it's reachable + // from both the local machine (via the TS IP) and the tailnet. + // localhost:3847 won't work for the Vite dev server — use the TS IP. + host: "100.84.4.113", + proxy: { + "/api": { + target: "http://127.0.0.1:3848", + changeOrigin: true, + }, + }, + }, + build: { + outDir: "../../dist/client", + emptyOutDir: true, + }, +}); diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..3fa3f85 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,18 @@ +import { defineConfig } from "vitest/config"; +import path from "path"; + +export default defineConfig({ + resolve: { + alias: { + "@shared": path.resolve(__dirname, "src/shared"), + }, + }, + test: { + globals: true, + environment: "node", + include: [ + "tests/unit/**/*.test.ts", + "src/client/components/**/*.test.tsx", + ], + }, +});