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
23 changes: 1 addition & 22 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,9 @@ jobs:

- run: make ci-web

cargo-check:
name: Rust compile check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install Linux dependencies
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf

- uses: dtolnay/rust-toolchain@stable

- uses: Swatinem/rust-cache@v2
with:
workspaces: packages/desktop/src-tauri -> target

- uses: ./.github/actions/setup

- run: make ci-desktop

release:
name: Release
needs: [check, cargo-check]
needs: [check]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
Expand Down
16 changes: 16 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# openconcho Agent Notes

## CI policy

- PR CI only runs the web checks.
- Rust/Tauri compile-check is local-only for now because the Linux dependency setup on GitHub Actions is too slow for routine PR validation.

## Required local preflight

- Before pushing any change under `packages/desktop/**` or `packages/desktop/src-tauri/**`, run:
- `pnpm --filter @openconcho/desktop cargo-check`

## Useful commands

- `make ci-web` — matches current PR CI
- `pnpm --filter @openconcho/desktop cargo-check` — local desktop compile check
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Frontend UI for self-hosted Honcho instances — browse memories, peers, session
| `make test` | Vitest (unit + integration), excludes `e2e/` |
| `make test-e2e` | Playwright e2e (uncached) |
| `make check` | lint + typecheck + test |
| `pnpm --filter @openconcho/desktop cargo-check` | Local Rust/Tauri compile check before pushing desktop changes |
| `pnpm --filter @openconcho/web generate:api` | Regen `src/api/schema.d.ts` from `openapi.json` |

## Structure
Expand Down Expand Up @@ -58,3 +59,4 @@ Read `docs/architecture.md` for component overview, data flow, and design decisi
- **Conventional commits enforced** — commitlint runs in husky `commit-msg`; body lines must be ≤100 chars
- **Releases via semantic-release** — `.releaserc.json`; commits land on `main`, no manual version bumps
- **GitHub account** — push under `offendingcommit` (`gh auth switch` if needed)
- **Desktop preflight is local** — Rust/Tauri compile-check no longer runs in PR CI; run `pnpm --filter @openconcho/desktop cargo-check` before pushing any `packages/desktop/**` or `packages/desktop/src-tauri/**` change
30 changes: 28 additions & 2 deletions packages/web/src/components/conclusions/ConclusionBrowser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import { ConfirmDialog } from "@/components/shared/ConfirmDialog";
import { EmptyState } from "@/components/shared/EmptyState";
import { ErrorAlert } from "@/components/shared/ErrorAlert";
import { FormModal } from "@/components/shared/FormModal";
import { PageLoader } from "@/components/shared/LoadingSpinner";
import { Pagination } from "@/components/shared/Pagination";
import { Skeleton } from "@/components/shared/Skeleton";
import { SortControl, type SortDir } from "@/components/shared/SortControl";
import { TimestampChip } from "@/components/shared/TimestampChip";
import { Button } from "@/components/ui/button";
Expand Down Expand Up @@ -193,7 +193,7 @@ export function ConclusionBrowser() {
</form>

<ErrorAlert error={error instanceof Error ? error : null} />
{(isLoading || (activeSearch && searchLoading)) && <PageLoader />}
{(isLoading || (activeSearch && searchLoading)) && <ConclusionsSkeleton />}

{!isLoading && !searchLoading && displayedConclusions.length === 0 && (
<EmptyState
Expand Down Expand Up @@ -315,6 +315,32 @@ export function ConclusionBrowser() {
);
}

function ConclusionsSkeleton() {
return (
<div className="space-y-3" aria-hidden="true">
{Array.from({ length: 4 }).map((_, index) => (
<div
key={index}
className="rounded-xl p-5"
style={{ background: "var(--surface)", border: "1px solid var(--border)" }}
>
<Skeleton className="h-3 w-full rounded" />
<Skeleton className="mt-2 h-3 w-[94%] rounded" />
<Skeleton className="mt-2 h-3 w-[76%] rounded" />
<div
className="flex items-center gap-3 mt-4 pt-3"
style={{ borderTop: "1px solid var(--border)" }}
>
<Skeleton className="h-3 w-20 rounded" />
<Skeleton className="h-3 w-16 rounded" />
<Skeleton className="ml-auto h-6 w-28 rounded-full" />
</div>
</div>
))}
</div>
);
}

function CreateConclusionModal({
open,
onClose,
Expand Down
65 changes: 63 additions & 2 deletions packages/web/src/components/dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useState } from "react";
import { useQueueStatus, useWorkspaces } from "@/api/queries";
import type { components } from "@/api/schema.d.ts";
import { ErrorAlert } from "@/components/shared/ErrorAlert";
import { PageLoader } from "@/components/shared/LoadingSpinner";
import { Skeleton } from "@/components/shared/Skeleton";
import { Body, Muted, PageTitle, SectionHeading } from "@/components/ui/typography";
import { useDemo } from "@/hooks/useDemo";
import { COLOR } from "@/lib/constants";
Expand Down Expand Up @@ -182,7 +182,7 @@ export function Dashboard() {
</motion.div>

<ErrorAlert error={error instanceof Error ? error : null} />
{isLoading && <PageLoader />}
{isLoading && <DashboardSkeleton />}

{!isLoading && workspaces.length > 0 && (
<div className="space-y-4">
Expand Down Expand Up @@ -265,3 +265,64 @@ export function Dashboard() {
</div>
);
}

function DashboardSkeleton() {
return (
<div className="space-y-4" aria-hidden="true">
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
{Array.from({ length: 4 }).map((_, index) => (
<div key={index} className="rounded-xl p-4 theme-card">
<Skeleton accent={index === 0} className="h-8 w-16 rounded-lg" />
<Skeleton className="mt-3 h-3 w-20 rounded" />
</div>
))}
</div>

<div className="rounded-xl theme-card overflow-hidden">
<div
className="flex items-center gap-2 px-4 py-3"
style={{ borderBottom: "1px solid var(--border)" }}
>
<Skeleton accent className="h-4 w-4 rounded" />
<Skeleton className="h-4 w-28 rounded" />
<Skeleton className="ml-1 h-3 w-32 rounded" />
</div>

<div className="overflow-x-auto">
<table className="w-full text-xs">
<thead>
<tr style={{ background: "var(--bg-3)" }}>
{Array.from({ length: 6 }).map((_, index) => (
<th key={index} className="px-4 py-2 text-left">
<Skeleton className="h-3 w-14 rounded" />
</th>
))}
</tr>
</thead>
<tbody>
{Array.from({ length: 5 }).map((_, rowIndex) => (
<tr key={rowIndex} style={{ borderTop: "1px solid var(--border)" }}>
<td className="px-4 py-3">
<Skeleton accent className="h-3 w-28 rounded" />
</td>
<td className="px-4 py-3">
<div className="flex justify-end">
<Skeleton className="h-3 w-20 rounded" />
</div>
</td>
{Array.from({ length: 4 }).map((__, cellIndex) => (
<td key={cellIndex} className="px-4 py-3">
<div className="flex justify-end">
<Skeleton className="h-3 w-8 rounded" />
</div>
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
);
}
45 changes: 44 additions & 1 deletion packages/web/src/components/peers/PeerDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { JsonViewer } from "@/components/shared/JsonViewer";
import { PageLoader } from "@/components/shared/LoadingSpinner";
import { MarkdownRenderer } from "@/components/shared/MarkdownRenderer";
import { PeerCardViewer } from "@/components/shared/PeerCardViewer";
import { Skeleton } from "@/components/shared/Skeleton";
import { Button } from "@/components/ui/button";
import { Input, Textarea } from "@/components/ui/input";
import {
Expand Down Expand Up @@ -143,7 +144,7 @@ export function PeerDetail() {

<div className="mt-6 space-y-4">
<ErrorAlert error={error instanceof Error ? error : null} />
{isLoading && <PageLoader />}
{isLoading && <PeerDetailSkeleton />}

{!isLoading && peer && (
<>
Expand Down Expand Up @@ -423,3 +424,45 @@ export function PeerDetail() {
</div>
);
}

function PeerDetailSkeleton() {
return (
<div className="space-y-4" aria-hidden="true">
<div className="rounded-xl p-5 theme-card">
<Skeleton className="h-4 w-36 rounded" />
<div className="mt-4 flex gap-2">
<Skeleton className="h-10 flex-1 rounded-lg" />
<Skeleton accent className="h-10 w-24 rounded-lg" />
</div>
</div>

<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
{Array.from({ length: 2 }).map((_, index) => (
<div key={index} className="rounded-xl p-5 theme-card">
<div className="flex items-center justify-between mb-4">
<Skeleton className="h-4 w-28 rounded" />
<Skeleton className="h-8 w-16 rounded-lg" />
</div>
<Skeleton className="h-3 w-full rounded" />
<Skeleton className="mt-2 h-3 w-[92%] rounded" />
<Skeleton className="mt-2 h-3 w-[68%] rounded" />
<Skeleton className="mt-4 h-24 w-full rounded-lg" />
</div>
))}
</div>

<div className="rounded-xl p-5 theme-card">
<Skeleton className="h-4 w-24 rounded" />
<Skeleton className="mt-4 h-3 w-full rounded" />
<Skeleton className="mt-2 h-3 w-[95%] rounded" />
<Skeleton className="mt-2 h-3 w-[76%] rounded" />
</div>

<div className="rounded-xl theme-card overflow-hidden">
<div className="px-5 py-4">
<Skeleton className="h-4 w-20 rounded" />
</div>
</div>
</div>
);
}
41 changes: 39 additions & 2 deletions packages/web/src/components/peers/PeerList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import type { components } from "@/api/schema.d.ts";
import { EmptyState } from "@/components/shared/EmptyState";
import { ErrorAlert } from "@/components/shared/ErrorAlert";
import { JsonViewer } from "@/components/shared/JsonViewer";
import { PageLoader } from "@/components/shared/LoadingSpinner";
import { Pagination } from "@/components/shared/Pagination";
import { Skeleton } from "@/components/shared/Skeleton";
import { SortControl, type SortDir } from "@/components/shared/SortControl";
import { MonoCaption, PageTitle } from "@/components/ui/typography";
import { useDemo } from "@/hooks/useDemo";
Expand Down Expand Up @@ -178,7 +178,7 @@ export function PeerList() {
)}

<ErrorAlert error={error instanceof Error ? error : null} />
{isLoading && <PageLoader />}
{isLoading && <PeerListSkeleton />}

{!isLoading && peers.length === 0 && (
<EmptyState
Expand Down Expand Up @@ -329,3 +329,40 @@ export function PeerList() {
</div>
);
}

function PeerListSkeleton() {
return (
<div aria-hidden="true">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
{Array.from({ length: 6 }).map((_, index) => (
<div
key={index}
className="rounded-xl px-5 py-4"
style={{
background: COLOR.cardBaseBg,
border: `1px solid ${COLOR.cardBaseBorder}`,
}}
>
<div className="flex items-center justify-between">
<Skeleton accent className="h-4 w-40 rounded" />
<Skeleton className="h-4 w-4 rounded" />
</div>
<div className="mt-3 flex items-center gap-2 flex-wrap">
<Skeleton className="h-5 w-14 rounded-full" />
<Skeleton className="h-5 w-12 rounded-full" />
</div>
<div className="mt-3 flex items-center gap-2">
<Skeleton className="h-3 w-3 rounded-full" />
<Skeleton className="h-3 w-28 rounded" />
</div>
</div>
))}
</div>
<div className="mt-4 flex items-center justify-between">
<Skeleton className="h-8 w-20 rounded-lg" />
<Skeleton className="h-4 w-16 rounded" />
<Skeleton className="h-8 w-20 rounded-lg" />
</div>
</div>
);
}
41 changes: 39 additions & 2 deletions packages/web/src/components/sessions/SessionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { useSessions } from "@/api/queries";
import type { components } from "@/api/schema.d.ts";
import { EmptyState } from "@/components/shared/EmptyState";
import { ErrorAlert } from "@/components/shared/ErrorAlert";
import { PageLoader } from "@/components/shared/LoadingSpinner";
import { Pagination } from "@/components/shared/Pagination";
import { Skeleton } from "@/components/shared/Skeleton";
import { SortControl, type SortDir } from "@/components/shared/SortControl";
import { MonoCaption, PageTitle } from "@/components/ui/typography";
import { useDemo } from "@/hooks/useDemo";
Expand Down Expand Up @@ -105,7 +105,7 @@ export function SessionList() {
</motion.div>

<ErrorAlert error={error instanceof Error ? error : null} />
{isLoading && <PageLoader />}
{isLoading && <SessionListSkeleton />}

{!isLoading && sessions.length === 0 && (
<EmptyState
Expand Down Expand Up @@ -204,3 +204,40 @@ export function SessionList() {
</div>
);
}

function SessionListSkeleton() {
return (
<div aria-hidden="true">
<div className="space-y-2">
{Array.from({ length: 5 }).map((_, index) => (
<div
key={index}
className="rounded-xl px-5 py-4"
style={{
background: COLOR.cardBaseBg,
border: `1px solid ${COLOR.cardBaseBorder}`,
}}
>
<div className="flex items-center justify-between">
<Skeleton accent className="h-4 w-44 rounded" />
<div className="flex items-center gap-2">
{index % 2 === 0 && <Skeleton className="h-4 w-12 rounded-full" />}
<Skeleton className="h-4 w-4 rounded" />
</div>
</div>
<div className="mt-3 flex items-center gap-2">
<Skeleton className="h-3 w-3 rounded-full" />
<Skeleton className="h-3 w-28 rounded" />
<Skeleton className="h-5 w-16 rounded-md" />
</div>
</div>
))}
</div>
<div className="mt-4 flex items-center justify-between">
<Skeleton className="h-8 w-20 rounded-lg" />
<Skeleton className="h-4 w-16 rounded" />
<Skeleton className="h-8 w-20 rounded-lg" />
</div>
</div>
);
}
15 changes: 15 additions & 0 deletions packages/web/src/components/shared/Skeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { cn } from "@/lib/utils";

interface SkeletonProps extends React.HTMLAttributes<HTMLDivElement> {
accent?: boolean;
}

export function Skeleton({ accent = false, className, ...props }: SkeletonProps) {
return (
<div
aria-hidden="true"
className={cn("theme-skeleton rounded-md", accent && "theme-skeleton--accent", className)}
{...props}
/>
);
}
Loading
Loading