chore(fire-tools): upgrade stamp to v1.13.2

- pnpm catalog: centralize version governance for biome, ts, vitest,
  zod, semantic-release, react, vite, and testing deps
- biome 1.9.4 → 2.4.13: migrate config (organizeImports → assist,
  files.ignore → files.includes, enable tailwindDirectives for @theme)
- zod 3.24.3 → 4.3.6: update error message API to { message: "..." }
  and .errors → .issues in all 4 Zod-using files
- vitest 3.2.3 → 4.1.5
- semantic-release 24.x → 25.0.3
- typescript 6.0.3 (ahead of 5.9.x standard; intentional)
- add commitlint 20.x with conventional config
- add husky 9.x with commit-msg and pre-commit hooks
- add .github/actions/setup composite action; update all 3 workflows
- fix: remove pnpm version: 9 from all workflows (reads packageManager)
- fix: node-version 20 → 24 in all 3 workflows
- add Makefile with dev/build/test/lint/typecheck/install targets
This commit is contained in:
Offending Commit
2026-04-27 11:20:47 -05:00
parent 8052a7d27a
commit 988ab36c32
61 changed files with 1056 additions and 822 deletions

View File

@@ -1,12 +1,36 @@
{ {
"fire_tools_version": "0.1.0", "fire_tools_version": "1.13.2",
"initialized_at": "2026-04-24T00:00:00Z", "initialized_at": "2026-04-24T00:00:00Z",
"profile": "other", "profile": "other",
"features": ["pnpm", "biome", "vitest", "zod"], "features": [
"pnpm",
"turborepo",
"typescript",
"biome",
"vitest",
"zod",
"commitlint",
"semantic-release",
"husky",
"actionlint",
"github-actions",
"makefile"
],
"deviations": [ "deviations": [
"No SQL database (Honcho API handles persistence)", {
"No turborepo (single package)", "tool": "drizzle",
"No commitlint/husky (small solo project)", "reason": "No SQL database — Honcho API handles all persistence; Drizzle is not applicable",
"No semantic-release (not publishing to npm)" "suppressed_at": "2026-04-27T00:00:00Z"
},
{
"tool": "playwright",
"reason": "Tauri desktop app — E2E testing approach uses Tauri's native test harness, not browser Playwright",
"suppressed_at": "2026-04-27T00:00:00Z"
},
{
"tool": "typescript-version",
"reason": "Using TypeScript 6.0.3 (ahead of standard 5.9.x) — intentional, team is on leading edge for this solo project",
"suppressed_at": "2026-04-27T00:00:00Z"
}
] ]
} }

15
.github/actions/setup/action.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
name: Setup pnpm and Node.js
description: Configure pnpm and Node.js 24 with caching, then install dependencies
runs:
using: composite
steps:
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: "24"
cache: pnpm
- run: pnpm install --frozen-lockfile
shell: bash

View File

@@ -13,15 +13,6 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v4 - uses: ./.github/actions/setup
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm turbo lint typecheck test build --filter=@openconcho/web - run: pnpm turbo lint typecheck test build --filter=@openconcho/web

View File

@@ -11,20 +11,10 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v4 - uses: ./.github/actions/setup
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- name: Install frontend dependencies
run: pnpm install --frozen-lockfile
- uses: tauri-apps/tauri-action@v0 - uses: tauri-apps/tauri-action@v0
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -18,16 +18,7 @@ jobs:
fetch-depth: 0 fetch-depth: 0
token: ${{ secrets.GH_TOKEN }} token: ${{ secrets.GH_TOKEN }}
- uses: pnpm/action-setup@v4 - uses: ./.github/actions/setup
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm exec semantic-release - run: pnpm exec semantic-release
env: env:

1
.husky/commit-msg Executable file
View File

@@ -0,0 +1 @@
pnpm exec commitlint --edit ${1}

4
.husky/pre-commit Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
STAGED=$(git diff --cached --name-only --diff-filter=ACMR | grep -E "\.(ts|tsx|js|jsx|css|json)$" || true)
[ -z "$STAGED" ] && exit 0
pnpm exec biome check --write --staged

22
Makefile Normal file
View File

@@ -0,0 +1,22 @@
.PHONY: dev build test lint lint-fix typecheck install
dev:
pnpm --filter @openconcho/desktop dev
build:
pnpm turbo run build
test:
pnpm turbo run test
lint:
pnpm turbo run lint
lint-fix:
pnpm exec biome check --write packages/web/src/
typecheck:
pnpm turbo run typecheck
install:
pnpm install

View File

@@ -1,34 +1,43 @@
{ {
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", "$schema": "https://biomejs.dev/schemas/2.4.13/schema.json",
"files": { "files": {
"ignore": ["src/routeTree.gen.ts", "src/api/schema.d.ts"] "includes": ["**", "!**/src/routeTree.gen.ts", "!**/src/api/schema.d.ts"]
}, },
"vcs": { "vcs": {
"enabled": true, "enabled": true,
"clientKind": "git", "clientKind": "git",
"useIgnoreFile": true "useIgnoreFile": true
}, },
"organizeImports": { "assist": {
"enabled": true "actions": {
}, "source": {
"linter": { "organizeImports": "on"
"enabled": true, }
"rules": { }
"recommended": true, },
"style": { "linter": {
"noNonNullAssertion": "warn" "enabled": true,
} "rules": {
} "recommended": true,
}, "style": {
"formatter": { "noNonNullAssertion": "warn"
"enabled": true, }
"indentStyle": "tab", }
"lineWidth": 100 },
}, "formatter": {
"javascript": { "enabled": true,
"formatter": { "indentStyle": "tab",
"quoteStyle": "double", "lineWidth": 100
"semicolons": "always" },
} "css": {
} "parser": {
"tailwindDirectives": true
}
},
"javascript": {
"formatter": {
"quoteStyle": "double",
"semicolons": "always"
}
}
} }

3
commitlint.config.mjs Normal file
View File

@@ -0,0 +1,3 @@
export default {
extends: ['@commitlint/config-conventional'],
};

View File

@@ -8,17 +8,21 @@
"build": "turbo run build", "build": "turbo run build",
"lint": "turbo run lint", "lint": "turbo run lint",
"test": "turbo run test", "test": "turbo run test",
"typecheck": "turbo run typecheck" "typecheck": "turbo run typecheck",
"prepare": "husky"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^1.9.4", "@biomejs/biome": "catalog:",
"@commitlint/cli": "~20.5.2",
"@commitlint/config-conventional": "~20.5.0",
"@semantic-release/changelog": "^6.0.0", "@semantic-release/changelog": "^6.0.0",
"@semantic-release/commit-analyzer": "^13.0.0", "@semantic-release/commit-analyzer": "^13.0.0",
"@semantic-release/exec": "^7.1.0", "@semantic-release/exec": "^7.1.0",
"@semantic-release/git": "^10.0.0", "@semantic-release/git": "^10.0.0",
"@semantic-release/github": "^10.0.0", "@semantic-release/github": "^10.0.0",
"@semantic-release/release-notes-generator": "^14.0.0", "@semantic-release/release-notes-generator": "^14.0.0",
"semantic-release": "^24.0.0", "husky": "~9.1.7",
"semantic-release": "catalog:",
"turbo": "^2" "turbo": "^2"
} }
} }

View File

@@ -33,28 +33,28 @@
"lucide-react": "^1.11.0", "lucide-react": "^1.11.0",
"luxon": "^3.7.2", "luxon": "^3.7.2",
"openapi-fetch": "^0.13.5", "openapi-fetch": "^0.13.5",
"react": "^19.2.5", "react": "catalog:",
"react-dom": "^19.2.5", "react-dom": "catalog:",
"react-markdown": "^10.1.0", "react-markdown": "^10.1.0",
"remark-gfm": "^4.0.1", "remark-gfm": "^4.0.1",
"tailwind-merge": "^3.5.0", "tailwind-merge": "^3.5.0",
"tailwindcss": "^4.2.4", "tailwindcss": "^4.2.4",
"zod": "^3.24.3" "zod": "catalog:"
}, },
"devDependencies": { "devDependencies": {
"@tanstack/router-plugin": "^1.120.3", "@tanstack/router-plugin": "^1.120.3",
"@testing-library/jest-dom": "^6.6.3", "@testing-library/jest-dom": "catalog:",
"@testing-library/react": "^16.3.0", "@testing-library/react": "catalog:",
"@testing-library/user-event": "^14.6.1", "@testing-library/user-event": "catalog:",
"@types/luxon": "^3.7.1", "@types/luxon": "^3.7.1",
"@types/node": "^25.6.0", "@types/node": "^25.6.0",
"@types/react": "^19.2.14", "@types/react": "catalog:",
"@types/react-dom": "^19.2.3", "@types/react-dom": "catalog:",
"@vitejs/plugin-react": "^6.0.1", "@vitejs/plugin-react": "catalog:",
"jsdom": "^26.1.0", "jsdom": "catalog:",
"openapi-typescript": "^7.8.0", "openapi-typescript": "^7.8.0",
"typescript": "~6.0.2", "typescript": "catalog:",
"vite": "^8.0.10", "vite": "catalog:",
"vitest": "^3.2.3" "vitest": "catalog:"
} }
} }

View File

@@ -1,6 +1,6 @@
import createClient from "openapi-fetch";
import { loadConfig } from "@/lib/config"; import { loadConfig } from "@/lib/config";
import { httpFetch } from "@/lib/http"; import { httpFetch } from "@/lib/http";
import createClient from "openapi-fetch";
import type { paths } from "./schema.d.ts"; import type { paths } from "./schema.d.ts";
export function createHonchoClient() { export function createHonchoClient() {

View File

@@ -1,12 +1,12 @@
import { Link, useParams } from "@tanstack/react-router";
import { AnimatePresence, motion } from "framer-motion";
import { Brain, Send } from "lucide-react";
import { useEffect, useRef, useState } from "react";
import { useChat } from "@/api/queries"; import { useChat } from "@/api/queries";
import { LoadingSpinner } from "@/components/shared/LoadingSpinner"; import { LoadingSpinner } from "@/components/shared/LoadingSpinner";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/input"; import { Textarea } from "@/components/ui/input";
import { SectionHeading } from "@/components/ui/typography"; import { SectionHeading } from "@/components/ui/typography";
import { Link, useParams } from "@tanstack/react-router";
import { AnimatePresence, motion } from "framer-motion";
import { Brain, Send } from "lucide-react";
import { useEffect, useRef, useState } from "react";
interface Message { interface Message {
id: string; id: string;

View File

@@ -1,3 +1,8 @@
import { Link, useParams } from "@tanstack/react-router";
import { AnimatePresence, motion } from "framer-motion";
import { ArrowLeft, Eye, Lightbulb, Plus, Search, Trash2, X } from "lucide-react";
import { useMemo, useState } from "react";
import { z } from "zod";
import { import {
useConclusions, useConclusions,
useCreateConclusion, useCreateConclusion,
@@ -18,18 +23,13 @@ import { Input, Textarea } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Body, Caption, MonoCaption, Muted, PageTitle } from "@/components/ui/typography"; import { Body, Caption, MonoCaption, Muted, PageTitle } from "@/components/ui/typography";
import { COLOR } from "@/lib/constants"; import { COLOR } from "@/lib/constants";
import { Link, useParams } from "@tanstack/react-router";
import { AnimatePresence, motion } from "framer-motion";
import { ArrowLeft, Eye, Lightbulb, Plus, Search, Trash2, X } from "lucide-react";
import { useMemo, useState } from "react";
import { z } from "zod";
type Conclusion = components["schemas"]["Conclusion"]; type Conclusion = components["schemas"]["Conclusion"];
const createSchema = z.object({ const createSchema = z.object({
observer_id: z.string().min(1, "Observer peer ID is required"), observer_id: z.string().min(1, { message: "Observer peer ID is required" }),
observed_id: z.string().min(1, "Observed peer ID is required"), observed_id: z.string().min(1, { message: "Observed peer ID is required" }),
content: z.string().min(1, "Content is required"), content: z.string().min(1, { message: "Content is required" }),
session_id: z.string().optional(), session_id: z.string().optional(),
}); });
@@ -348,7 +348,7 @@ function CreateConclusionModal({
const result = createSchema.safeParse(fields); const result = createSchema.safeParse(fields);
if (!result.success) { if (!result.success) {
const errs: Record<string, string> = {}; const errs: Record<string, string> = {};
for (const issue of result.error.errors) errs[issue.path[0] as string] = issue.message; for (const issue of result.error.issues) errs[issue.path[0] as string] = issue.message;
setValidationErrors(errs); setValidationErrors(errs);
return; return;
} }

View File

@@ -1,3 +1,7 @@
import { Link } from "@tanstack/react-router";
import { motion } from "framer-motion";
import { Activity, Boxes, ChevronRight, CircleDot, LayoutDashboard } from "lucide-react";
import { useState } from "react";
import { useQueueStatus, useWorkspaces } from "@/api/queries"; import { useQueueStatus, useWorkspaces } from "@/api/queries";
import type { components } from "@/api/schema.d.ts"; import type { components } from "@/api/schema.d.ts";
import { ErrorAlert } from "@/components/shared/ErrorAlert"; import { ErrorAlert } from "@/components/shared/ErrorAlert";
@@ -5,10 +9,6 @@ import { PageLoader } from "@/components/shared/LoadingSpinner";
import { Body, Muted, PageTitle, SectionHeading } from "@/components/ui/typography"; import { Body, Muted, PageTitle, SectionHeading } from "@/components/ui/typography";
import { COLOR } from "@/lib/constants"; import { COLOR } from "@/lib/constants";
import { formatCount } from "@/lib/utils"; import { formatCount } from "@/lib/utils";
import { Link } from "@tanstack/react-router";
import { motion } from "framer-motion";
import { Activity, Boxes, ChevronRight, CircleDot, LayoutDashboard } from "lucide-react";
import { useState } from "react";
type QueueStatus = components["schemas"]["QueueStatus"]; type QueueStatus = components["schemas"]["QueueStatus"];

View File

@@ -1,9 +1,9 @@
import { useTheme } from "@/hooks/useTheme";
import { loadConfig } from "@/lib/config";
import { COLOR } from "@/lib/constants";
import { Link, useMatchRoute } from "@tanstack/react-router"; import { Link, useMatchRoute } from "@tanstack/react-router";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { Boxes, Brain, ChevronRight, LayoutDashboard, Moon, Settings, Sun } from "lucide-react"; import { Boxes, Brain, ChevronRight, LayoutDashboard, Moon, Settings, Sun } from "lucide-react";
import { useTheme } from "@/hooks/useTheme";
import { loadConfig } from "@/lib/config";
import { COLOR } from "@/lib/constants";
const navItems = [ const navItems = [
{ to: "/" as const, label: "Dashboard", icon: LayoutDashboard, exact: true }, { to: "/" as const, label: "Dashboard", icon: LayoutDashboard, exact: true },

View File

@@ -1,3 +1,17 @@
import { Link, useNavigate, useParams } from "@tanstack/react-router";
import { AnimatePresence, motion } from "framer-motion";
import {
ChevronDown,
Eye,
EyeOff,
MessageCircle,
Save,
Search,
User,
Users,
X,
} from "lucide-react";
import { useState } from "react";
import { import {
usePeer, usePeer,
usePeerCard, usePeerCard,
@@ -23,20 +37,6 @@ import {
SectionHeading, SectionHeading,
} from "@/components/ui/typography"; } from "@/components/ui/typography";
import { COLOR } from "@/lib/constants"; import { COLOR } from "@/lib/constants";
import { Link, useNavigate, useParams } from "@tanstack/react-router";
import { AnimatePresence, motion } from "framer-motion";
import {
ChevronDown,
Eye,
EyeOff,
MessageCircle,
Save,
Search,
User,
Users,
X,
} from "lucide-react";
import { useState } from "react";
export function PeerDetail() { export function PeerDetail() {
const { workspaceId, peerId } = useParams({ strict: false }) as { const { workspaceId, peerId } = useParams({ strict: false }) as {

View File

@@ -1,3 +1,7 @@
import { Link, useNavigate, useParams } from "@tanstack/react-router";
import { motion, type Variants } from "framer-motion";
import { ArrowLeft, ChevronRight, Clock, Eye, Users } from "lucide-react";
import { useMemo, useState } from "react";
import { usePeers } from "@/api/queries"; import { usePeers } from "@/api/queries";
import type { components } from "@/api/schema.d.ts"; import type { components } from "@/api/schema.d.ts";
import { EmptyState } from "@/components/shared/EmptyState"; import { EmptyState } from "@/components/shared/EmptyState";
@@ -8,10 +12,6 @@ import { Pagination } from "@/components/shared/Pagination";
import { SortControl, type SortDir } from "@/components/shared/SortControl"; import { SortControl, type SortDir } from "@/components/shared/SortControl";
import { MonoCaption, PageTitle } from "@/components/ui/typography"; import { MonoCaption, PageTitle } from "@/components/ui/typography";
import { COLOR } from "@/lib/constants"; import { COLOR } from "@/lib/constants";
import { Link, useNavigate, useParams } from "@tanstack/react-router";
import { type Variants, motion } from "framer-motion";
import { ArrowLeft, ChevronRight, Clock, Eye, Users } from "lucide-react";
import { useMemo, useState } from "react";
type Peer = components["schemas"]["Peer"]; type Peer = components["schemas"]["Peer"];

View File

@@ -1,3 +1,7 @@
import { Link, useNavigate, useParams } from "@tanstack/react-router";
import { AnimatePresence, motion } from "framer-motion";
import { AlignLeft, Clock, Copy, MessageSquare, Search, Trash2, Users, X } from "lucide-react";
import { useState } from "react";
import { import {
useAddPeersToSession, useAddPeersToSession,
useCloneSession, useCloneSession,
@@ -26,10 +30,6 @@ import {
PageTitle, PageTitle,
SectionHeading, SectionHeading,
} from "@/components/ui/typography"; } from "@/components/ui/typography";
import { Link, useNavigate, useParams } from "@tanstack/react-router";
import { AnimatePresence, motion } from "framer-motion";
import { AlignLeft, Clock, Copy, MessageSquare, Search, Trash2, Users, X } from "lucide-react";
import { useState } from "react";
type Message = components["schemas"]["Message"]; type Message = components["schemas"]["Message"];
type SessionSummaries = components["schemas"]["SessionSummaries"]; type SessionSummaries = components["schemas"]["SessionSummaries"];

View File

@@ -1,3 +1,7 @@
import { Link, useNavigate, useParams } from "@tanstack/react-router";
import { motion, type Variants } from "framer-motion";
import { ArrowLeft, ChevronRight, CircleDot, Clock, MessageSquare } from "lucide-react";
import { useMemo, useState } from "react";
import { useSessions } from "@/api/queries"; import { useSessions } from "@/api/queries";
import type { components } from "@/api/schema.d.ts"; import type { components } from "@/api/schema.d.ts";
import { EmptyState } from "@/components/shared/EmptyState"; import { EmptyState } from "@/components/shared/EmptyState";
@@ -7,10 +11,6 @@ import { Pagination } from "@/components/shared/Pagination";
import { SortControl, type SortDir } from "@/components/shared/SortControl"; import { SortControl, type SortDir } from "@/components/shared/SortControl";
import { MonoCaption, PageTitle } from "@/components/ui/typography"; import { MonoCaption, PageTitle } from "@/components/ui/typography";
import { COLOR } from "@/lib/constants"; import { COLOR } from "@/lib/constants";
import { Link, useNavigate, useParams } from "@tanstack/react-router";
import { type Variants, motion } from "framer-motion";
import { ArrowLeft, ChevronRight, CircleDot, Clock, MessageSquare } from "lucide-react";
import { useMemo, useState } from "react";
type Session = components["schemas"]["Session"]; type Session = components["schemas"]["Session"];

View File

@@ -1,19 +1,19 @@
import { AnimatePresence, motion } from "framer-motion";
import { AlertCircle, CheckCircle, Loader, Lock, LockOpen, Wifi, WifiOff } from "lucide-react";
import { useState } from "react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input, Textarea } from "@/components/ui/input"; import { Input, Textarea } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Muted } from "@/components/ui/typography"; import { Muted } from "@/components/ui/typography";
import { import {
type Config, type Config,
type HealthStatus,
checkConnection, checkConnection,
configSchema, configSchema,
type HealthStatus,
loadConfig, loadConfig,
saveConfig, saveConfig,
} from "@/lib/config"; } from "@/lib/config";
import { COLOR } from "@/lib/constants"; import { COLOR } from "@/lib/constants";
import { AnimatePresence, motion } from "framer-motion";
import { AlertCircle, CheckCircle, Loader, Lock, LockOpen, Wifi, WifiOff } from "lucide-react";
import { useState } from "react";
interface SettingsFormProps { interface SettingsFormProps {
onSaved?: () => void; onSaved?: () => void;

View File

@@ -1,3 +1,4 @@
import { AlertTriangle } from "lucide-react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Dialog, Dialog,
@@ -7,7 +8,6 @@ import {
DialogTitle, DialogTitle,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { COLOR } from "@/lib/constants"; import { COLOR } from "@/lib/constants";
import { AlertTriangle } from "lucide-react";
interface ConfirmDialogProps { interface ConfirmDialogProps {
open: boolean; open: boolean;

View File

@@ -1,7 +1,7 @@
import { Body, Caption } from "@/components/ui/typography";
import { COLOR } from "@/lib/constants";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import type { LucideIcon } from "lucide-react"; import type { LucideIcon } from "lucide-react";
import { Body, Caption } from "@/components/ui/typography";
import { COLOR } from "@/lib/constants";
interface EmptyStateProps { interface EmptyStateProps {
icon?: LucideIcon; icon?: LucideIcon;

View File

@@ -1,5 +1,5 @@
import { COLOR } from "@/lib/constants";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { COLOR } from "@/lib/constants";
interface LoadingSpinnerProps { interface LoadingSpinnerProps {
size?: "sm" | "md" | "lg"; size?: "sm" | "md" | "lg";

View File

@@ -1,9 +1,9 @@
import { TimestampChip } from "@/components/shared/TimestampChip";
import { COLOR } from "@/lib/constants";
import { Link } from "@tanstack/react-router"; import { Link } from "@tanstack/react-router";
import { DateTime } from "luxon"; import { DateTime } from "luxon";
import ReactMarkdown, { type Components } from "react-markdown"; import ReactMarkdown, { type Components } from "react-markdown";
import remarkGfm from "remark-gfm"; import remarkGfm from "remark-gfm";
import { TimestampChip } from "@/components/shared/TimestampChip";
import { COLOR } from "@/lib/constants";
// ─── Types ──────────────────────────────────────────────────────────────────── // ─── Types ────────────────────────────────────────────────────────────────────

View File

@@ -1,8 +1,8 @@
import { ChevronDown } from "lucide-react";
import { useState } from "react";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import { COLOR } from "@/lib/constants"; import { COLOR } from "@/lib/constants";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { ChevronDown } from "lucide-react";
import { useState } from "react";
interface Props { interface Props {
lines: string[]; lines: string[];

View File

@@ -1,5 +1,5 @@
import { COLOR } from "@/lib/constants";
import { ArrowDown, ArrowUp } from "lucide-react"; import { ArrowDown, ArrowUp } from "lucide-react";
import { COLOR } from "@/lib/constants";
export type SortDir = "asc" | "desc"; export type SortDir = "asc" | "desc";

View File

@@ -1,6 +1,6 @@
import { DateTime } from "luxon";
import { COLOR } from "@/lib/constants"; import { COLOR } from "@/lib/constants";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { DateTime } from "luxon";
interface Props { interface Props {
/** ISO-like string: "2026-04-24 18:18:48" or any Luxon-parseable string */ /** ISO-like string: "2026-04-24 18:18:48" or any Luxon-parseable string */

View File

@@ -1,6 +1,6 @@
import { cn } from "@/lib/utils"; import { cva, type VariantProps } from "class-variance-authority";
import { type VariantProps, cva } from "class-variance-authority";
import type { HTMLAttributes } from "react"; import type { HTMLAttributes } from "react";
import { cn } from "@/lib/utils";
const badgeVariants = cva( const badgeVariants = cva(
"inline-flex items-center gap-1 rounded-md border px-2 py-0.5 text-xs font-medium transition-colors", "inline-flex items-center gap-1 rounded-md border px-2 py-0.5 text-xs font-medium transition-colors",

View File

@@ -1,7 +1,7 @@
import { cn } from "@/lib/utils";
import { Slot } from "@radix-ui/react-slot"; import { Slot } from "@radix-ui/react-slot";
import { type VariantProps, cva } from "class-variance-authority"; import { cva, type VariantProps } from "class-variance-authority";
import { forwardRef } from "react"; import { forwardRef } from "react";
import { cn } from "@/lib/utils";
const buttonVariants = cva( const buttonVariants = cva(
[ [

View File

@@ -1,5 +1,5 @@
import { cn } from "@/lib/utils";
import type { HTMLAttributes } from "react"; import type { HTMLAttributes } from "react";
import { cn } from "@/lib/utils";
export function Card({ className, ...props }: HTMLAttributes<HTMLDivElement>) { export function Card({ className, ...props }: HTMLAttributes<HTMLDivElement>) {
return ( return (

View File

@@ -6,4 +6,4 @@ const Collapsible = CollapsiblePrimitive.Root;
const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger; const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent; const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;
export { Collapsible, CollapsibleTrigger, CollapsibleContent }; export { Collapsible, CollapsibleContent, CollapsibleTrigger };

View File

@@ -1,7 +1,7 @@
import { cn } from "@/lib/utils";
import * as DialogPrimitive from "@radix-ui/react-dialog"; import * as DialogPrimitive from "@radix-ui/react-dialog";
import { X } from "lucide-react"; import { X } from "lucide-react";
import { forwardRef } from "react"; import { forwardRef } from "react";
import { cn } from "@/lib/utils";
export const Dialog = DialogPrimitive.Root; export const Dialog = DialogPrimitive.Root;
export const DialogTrigger = DialogPrimitive.Trigger; export const DialogTrigger = DialogPrimitive.Trigger;

View File

@@ -1,5 +1,5 @@
import { cn } from "@/lib/utils";
import { forwardRef } from "react"; import { forwardRef } from "react";
import { cn } from "@/lib/utils";
export type InputProps = React.InputHTMLAttributes<HTMLInputElement>; export type InputProps = React.InputHTMLAttributes<HTMLInputElement>;

View File

@@ -1,6 +1,6 @@
import { cn } from "@/lib/utils";
import * as LabelPrimitive from "@radix-ui/react-label"; import * as LabelPrimitive from "@radix-ui/react-label";
import { forwardRef } from "react"; import { forwardRef } from "react";
import { cn } from "@/lib/utils";
export const Label = forwardRef< export const Label = forwardRef<
React.ComponentRef<typeof LabelPrimitive.Root>, React.ComponentRef<typeof LabelPrimitive.Root>,

View File

@@ -1,6 +1,6 @@
import { cn } from "@/lib/utils";
import * as SeparatorPrimitive from "@radix-ui/react-separator"; import * as SeparatorPrimitive from "@radix-ui/react-separator";
import { forwardRef } from "react"; import { forwardRef } from "react";
import { cn } from "@/lib/utils";
export const Separator = forwardRef< export const Separator = forwardRef<
React.ComponentRef<typeof SeparatorPrimitive.Root>, React.ComponentRef<typeof SeparatorPrimitive.Root>,

View File

@@ -1,5 +1,5 @@
import { cn } from "@/lib/utils";
import { forwardRef } from "react"; import { forwardRef } from "react";
import { cn } from "@/lib/utils";
export const Table = forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>( export const Table = forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>(
({ className, ...props }, ref) => ( ({ className, ...props }, ref) => (

View File

@@ -1,5 +1,5 @@
import { cn } from "@/lib/utils";
import * as TooltipPrimitive from "@radix-ui/react-tooltip"; import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import { cn } from "@/lib/utils";
export const TooltipProvider = TooltipPrimitive.Provider; export const TooltipProvider = TooltipPrimitive.Provider;
export const Tooltip = TooltipPrimitive.Root; export const Tooltip = TooltipPrimitive.Root;

View File

@@ -1,15 +1,15 @@
import type { UseMutationResult } from "@tanstack/react-query";
import { useState } from "react";
import { z } from "zod";
import { FormModal } from "@/components/shared/FormModal"; import { FormModal } from "@/components/shared/FormModal";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Caption } from "@/components/ui/typography"; import { Caption } from "@/components/ui/typography";
import { COLOR } from "@/lib/constants"; import { COLOR } from "@/lib/constants";
import type { UseMutationResult } from "@tanstack/react-query";
import { useState } from "react";
import { z } from "zod";
const schema = z.object({ const schema = z.object({
observer: z.string().min(1, "Observer peer ID is required"), observer: z.string().min(1, { message: "Observer peer ID is required" }),
observed: z.string().optional(), observed: z.string().optional(),
session_id: z.string().optional(), session_id: z.string().optional(),
}); });
@@ -46,7 +46,7 @@ export function ScheduleDreamModal({ open, onClose, mutation }: Props) {
session_id: sessionId || undefined, session_id: sessionId || undefined,
}); });
if (!result.success) { if (!result.success) {
setValidationError(result.error.errors[0].message); setValidationError(result.error.issues[0].message);
return; return;
} }
await mutation.mutateAsync({ await mutation.mutateAsync({

View File

@@ -1,3 +1,9 @@
import { Link } from "@tanstack/react-router";
import { open } from "@tauri-apps/plugin-shell";
import { AnimatePresence, motion } from "framer-motion";
import { ArrowLeft, ExternalLink, Plus, Trash2, Webhook, Zap } from "lucide-react";
import { useState } from "react";
import { z } from "zod";
import { useCreateWebhook, useDeleteWebhook, useTestWebhook, useWebhooks } from "@/api/queries"; import { useCreateWebhook, useDeleteWebhook, useTestWebhook, useWebhooks } from "@/api/queries";
import { ConfirmDialog } from "@/components/shared/ConfirmDialog"; import { ConfirmDialog } from "@/components/shared/ConfirmDialog";
import { ErrorAlert } from "@/components/shared/ErrorAlert"; import { ErrorAlert } from "@/components/shared/ErrorAlert";
@@ -6,14 +12,8 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Body, Muted, PageTitle, SectionHeading } from "@/components/ui/typography"; import { Body, Muted, PageTitle, SectionHeading } from "@/components/ui/typography";
import { COLOR } from "@/lib/constants"; import { COLOR } from "@/lib/constants";
import { Link } from "@tanstack/react-router";
import { open } from "@tauri-apps/plugin-shell";
import { AnimatePresence, motion } from "framer-motion";
import { ArrowLeft, ExternalLink, Plus, Trash2, Webhook, Zap } from "lucide-react";
import { useState } from "react";
import { z } from "zod";
const urlSchema = z.string().url("Must be a valid URL"); const urlSchema = z.string().url({ message: "Must be a valid URL" });
interface Props { interface Props {
workspaceId: string; workspaceId: string;
@@ -34,7 +34,7 @@ export function WebhookManager({ workspaceId }: Props) {
e.preventDefault(); e.preventDefault();
const result = urlSchema.safeParse(url); const result = urlSchema.safeParse(url);
if (!result.success) { if (!result.success) {
setUrlError(result.error.errors[0].message); setUrlError(result.error.issues[0].message);
return; return;
} }
await createWebhook.mutateAsync(url); await createWebhook.mutateAsync(url);

View File

@@ -1,12 +1,3 @@
import { useDeleteWorkspace, useQueueStatus, useScheduleDream, useWorkspace } from "@/api/queries";
import { ConfirmDialog } from "@/components/shared/ConfirmDialog";
import { ErrorAlert } from "@/components/shared/ErrorAlert";
import { JsonViewer } from "@/components/shared/JsonViewer";
import { PageLoader } from "@/components/shared/LoadingSpinner";
import { Button } from "@/components/ui/button";
import { Body, Caption, PageTitle, SectionHeading } from "@/components/ui/typography";
import { ScheduleDreamModal } from "@/components/workspaces/ScheduleDreamModal";
import { COLOR } from "@/lib/constants";
import { Link, useNavigate, useParams } from "@tanstack/react-router"; import { Link, useNavigate, useParams } from "@tanstack/react-router";
import { AnimatePresence, motion } from "framer-motion"; import { AnimatePresence, motion } from "framer-motion";
import { import {
@@ -22,6 +13,15 @@ import {
Zap, Zap,
} from "lucide-react"; } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { useDeleteWorkspace, useQueueStatus, useScheduleDream, useWorkspace } from "@/api/queries";
import { ConfirmDialog } from "@/components/shared/ConfirmDialog";
import { ErrorAlert } from "@/components/shared/ErrorAlert";
import { JsonViewer } from "@/components/shared/JsonViewer";
import { PageLoader } from "@/components/shared/LoadingSpinner";
import { Button } from "@/components/ui/button";
import { Body, Caption, PageTitle, SectionHeading } from "@/components/ui/typography";
import { ScheduleDreamModal } from "@/components/workspaces/ScheduleDreamModal";
import { COLOR } from "@/lib/constants";
const NAV_SECTIONS = [ const NAV_SECTIONS = [
{ {

View File

@@ -1,3 +1,7 @@
import { useNavigate } from "@tanstack/react-router";
import { motion, type Variants } from "framer-motion";
import { Boxes, ChevronRight, Clock } from "lucide-react";
import { useMemo, useState } from "react";
import { useWorkspaces } from "@/api/queries"; import { useWorkspaces } from "@/api/queries";
import type { components } from "@/api/schema.d.ts"; import type { components } from "@/api/schema.d.ts";
import { EmptyState } from "@/components/shared/EmptyState"; import { EmptyState } from "@/components/shared/EmptyState";
@@ -7,10 +11,6 @@ import { Pagination } from "@/components/shared/Pagination";
import { SortControl, type SortDir } from "@/components/shared/SortControl"; import { SortControl, type SortDir } from "@/components/shared/SortControl";
import { MonoCaption, Muted, PageTitle } from "@/components/ui/typography"; import { MonoCaption, Muted, PageTitle } from "@/components/ui/typography";
import { COLOR } from "@/lib/constants"; import { COLOR } from "@/lib/constants";
import { useNavigate } from "@tanstack/react-router";
import { type Variants, motion } from "framer-motion";
import { Boxes, ChevronRight, Clock } from "lucide-react";
import { useMemo, useState } from "react";
type Workspace = components["schemas"]["Workspace"]; type Workspace = components["schemas"]["Workspace"];

View File

@@ -1,5 +1,5 @@
import { type Theme, applyTheme, getStoredTheme } from "@/lib/theme";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { applyTheme, getStoredTheme, type Theme } from "@/lib/theme";
export function useTheme() { export function useTheme() {
const [theme, setTheme] = useState<Theme>(() => getStoredTheme()); const [theme, setTheme] = useState<Theme>(() => getStoredTheme());

View File

@@ -87,7 +87,9 @@ body {
background: var(--bg); background: var(--bg);
color: var(--text-1); color: var(--text-1);
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
transition: background 0.2s ease, color 0.2s ease; transition:
background 0.2s ease,
color 0.2s ease;
} }
#root { #root {
@@ -101,7 +103,8 @@ body::before {
content: ""; content: "";
position: fixed; position: fixed;
inset: 0; inset: 0;
background-image: linear-gradient(var(--grid-line) 1px, transparent 1px), background-image:
linear-gradient(var(--grid-line) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-line) 1px, transparent 1px); linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
background-size: 32px 32px; background-size: 32px 32px;
pointer-events: none; pointer-events: none;

View File

@@ -1,10 +1,10 @@
import { httpFetch } from "@/lib/http";
import { z } from "zod"; import { z } from "zod";
import { httpFetch } from "@/lib/http";
const CONFIG_KEY = "openconcho:config"; const CONFIG_KEY = "openconcho:config";
export const configSchema = z.object({ export const configSchema = z.object({
baseUrl: z.string().url("Must be a valid URL"), baseUrl: z.string().url({ message: "Must be a valid URL" }),
token: z.string().optional().default(""), token: z.string().optional().default(""),
}); });

View File

@@ -1,5 +1,5 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { RouterProvider, createRouter } from "@tanstack/react-router"; import { createRouter, RouterProvider } from "@tanstack/react-router";
import { StrictMode } from "react"; import { StrictMode } from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import { routeTree } from "./routeTree.gen"; import { routeTree } from "./routeTree.gen";

View File

@@ -1,8 +1,8 @@
import { createRootRoute, Outlet, useRouter } from "@tanstack/react-router";
import { useEffect } from "react";
import { Sidebar } from "@/components/layout/Sidebar"; import { Sidebar } from "@/components/layout/Sidebar";
import { loadConfig } from "@/lib/config"; import { loadConfig } from "@/lib/config";
import { applyTheme, getStoredTheme } from "@/lib/theme"; import { applyTheme, getStoredTheme } from "@/lib/theme";
import { Outlet, createRootRoute, useRouter } from "@tanstack/react-router";
import { useEffect } from "react";
function RootLayout() { function RootLayout() {
const config = loadConfig(); const config = loadConfig();

View File

@@ -1,5 +1,5 @@
import { Dashboard } from "@/components/dashboard/Dashboard";
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { Dashboard } from "@/components/dashboard/Dashboard";
export const Route = createFileRoute("/")({ export const Route = createFileRoute("/")({
component: Dashboard, component: Dashboard,

View File

@@ -1,7 +1,7 @@
import { SettingsForm } from "@/components/settings/SettingsForm";
import { createFileRoute, useNavigate } from "@tanstack/react-router"; import { createFileRoute, useNavigate } from "@tanstack/react-router";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { Brain } from "lucide-react"; import { Brain } from "lucide-react";
import { SettingsForm } from "@/components/settings/SettingsForm";
export const Route = createFileRoute("/settings")({ export const Route = createFileRoute("/settings")({
component: SettingsPage, component: SettingsPage,

View File

@@ -1,5 +1,5 @@
import { WorkspaceList } from "@/components/workspaces/WorkspaceList";
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { WorkspaceList } from "@/components/workspaces/WorkspaceList";
export const Route = createFileRoute("/workspaces")({ export const Route = createFileRoute("/workspaces")({
component: WorkspaceList, component: WorkspaceList,

View File

@@ -1,5 +1,5 @@
import { WorkspaceDetail } from "@/components/workspaces/WorkspaceDetail";
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { WorkspaceDetail } from "@/components/workspaces/WorkspaceDetail";
export const Route = createFileRoute("/workspaces_/$workspaceId")({ export const Route = createFileRoute("/workspaces_/$workspaceId")({
component: WorkspaceDetail, component: WorkspaceDetail,

View File

@@ -1,5 +1,5 @@
import { ConclusionBrowser } from "@/components/conclusions/ConclusionBrowser";
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { ConclusionBrowser } from "@/components/conclusions/ConclusionBrowser";
export const Route = createFileRoute("/workspaces_/$workspaceId_/conclusions")({ export const Route = createFileRoute("/workspaces_/$workspaceId_/conclusions")({
component: ConclusionBrowser, component: ConclusionBrowser,

View File

@@ -1,5 +1,5 @@
import { PeerList } from "@/components/peers/PeerList";
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { PeerList } from "@/components/peers/PeerList";
export const Route = createFileRoute("/workspaces_/$workspaceId_/peers")({ export const Route = createFileRoute("/workspaces_/$workspaceId_/peers")({
component: PeerList, component: PeerList,

View File

@@ -1,5 +1,5 @@
import { PeerDetail } from "@/components/peers/PeerDetail";
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { PeerDetail } from "@/components/peers/PeerDetail";
export const Route = createFileRoute("/workspaces_/$workspaceId_/peers_/$peerId")({ export const Route = createFileRoute("/workspaces_/$workspaceId_/peers_/$peerId")({
component: PeerDetail, component: PeerDetail,

View File

@@ -1,5 +1,5 @@
import { ChatPage } from "@/components/chat/ChatPage";
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { ChatPage } from "@/components/chat/ChatPage";
export const Route = createFileRoute("/workspaces_/$workspaceId_/peers_/$peerId_/chat")({ export const Route = createFileRoute("/workspaces_/$workspaceId_/peers_/$peerId_/chat")({
component: ChatPage, component: ChatPage,

View File

@@ -1,5 +1,5 @@
import { SessionList } from "@/components/sessions/SessionList";
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { SessionList } from "@/components/sessions/SessionList";
export const Route = createFileRoute("/workspaces_/$workspaceId_/sessions")({ export const Route = createFileRoute("/workspaces_/$workspaceId_/sessions")({
component: SessionList, component: SessionList,

View File

@@ -1,5 +1,5 @@
import { SessionDetail } from "@/components/sessions/SessionDetail";
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { SessionDetail } from "@/components/sessions/SessionDetail";
export const Route = createFileRoute("/workspaces_/$workspaceId_/sessions_/$sessionId")({ export const Route = createFileRoute("/workspaces_/$workspaceId_/sessions_/$sessionId")({
component: SessionDetail, component: SessionDetail,

View File

@@ -1,5 +1,5 @@
import { WebhookManager } from "@/components/workspaces/WebhookManager";
import { createFileRoute, useParams } from "@tanstack/react-router"; import { createFileRoute, useParams } from "@tanstack/react-router";
import { WebhookManager } from "@/components/workspaces/WebhookManager";
export const Route = createFileRoute("/workspaces_/$workspaceId_/webhooks")({ export const Route = createFileRoute("/workspaces_/$workspaceId_/webhooks")({
component: WebhookManagerPage, component: WebhookManagerPage,

1389
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +1,28 @@
packages: packages:
- packages/* - packages/*
allowBuilds:
esbuild: true
catalog:
# Standard tooling
"@biomejs/biome": "^2.4.0"
# Testing
"@testing-library/jest-dom": "^6.6.3"
"@testing-library/react": "^16.3.0"
"@testing-library/user-event": "^14.6.1"
"@types/react": "^19.2.14"
"@types/react-dom": "^19.2.3"
"@vitejs/plugin-react": "^6.0.1"
jsdom: "^26.1.0"
# React
react: "^19.2.5"
react-dom: "^19.2.5"
semantic-release: "^25.0.0"
typescript: "~6.0.2"
# Vite
vite: "^8.0.10"
vitest: "^4.0.0"
zod: "^4.0.0"