Skip to content
Draft
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
1504708
feat: marketplace UI overhaul — HuggingFace/npm-style discovery hub
vincentkoc Apr 3, 2026
70af109
fix: address PR review feedback
vincentkoc Apr 3, 2026
7ff601b
fix: resolve all lint errors
vincentkoc Apr 3, 2026
b4e8a26
chore: remove accidentally committed skill/agent config files
vincentkoc Apr 3, 2026
7452cf6
feat: white background, onboarding explainer, filter reset, transitions
vincentkoc Apr 4, 2026
6df3a3b
feat: monochrome dark TUI aesthetic — complete design pivot
vincentkoc Apr 4, 2026
c62ab8b
fix: complete audit cleanup — monochrome purity, touch targets, a11y
vincentkoc Apr 4, 2026
e41d5d6
fix: restore banned accounts in management
ImLukeF Apr 4, 2026
522fa22
fix: polish pass — tighter spacing, no empty sections, subtle skeletons
vincentkoc Apr 4, 2026
b7923ed
Update .gitignore
vincentkoc Apr 6, 2026
383844c
chore: various component changes
vincentkoc Apr 6, 2026
051b1da
fix: audit fixes — warm colors, lint, ARIA labels
vincentkoc Apr 6, 2026
39c0fa2
merge: integrate origin/main into feat/marketplace-ui-overhaul
BunsDev Apr 7, 2026
a8a6242
fix: address PR review — reason guard, one-shot fetch, index scan
BunsDev Apr 7, 2026
5003c1b
fix: address remaining PR review comments
BunsDev Apr 7, 2026
b255b58
Merge pull request #1567 from openclaw/okcode/conflict-resolution-plan
BunsDev Apr 7, 2026
f636b31
feat: design foundation sweep (#1570) thanks @BunsDev
BunsDev Apr 7, 2026
298cbdd
fix: denormalize user hover stats
BunsDev Apr 8, 2026
9a45c37
fix(ui): align browse page widths across tabs
ngutman Apr 9, 2026
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
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@ jobs:

- name: Test
run: bun run test
env:
VITE_CONVEX_URL: https://example.invalid

- name: Coverage
run: bun run coverage
env:
VITE_CONVEX_URL: https://example.invalid

- name: ClawHub CLI Verify
run: bun run --cwd packages/clawhub verify
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@ coverage
playwright-report
test-results
.playwright
convex/_generated/
skills-lock.json
*/skills/*
skills/*
8 changes: 8 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,11 @@
- **32K document limit per query.** Split `.collect()` calls by a partition field (e.g., one day at a time instead of a 7-day range). See `rebuildTrendingLeaderboardAction` in `convex/leaderboards.ts` for an example.
- **Common mistakes**: `.filter().collect()` without an index; `ctx.db.get()` on large docs in a loop for list views; while loops that paginate the whole table to find filtered results.
- **Before writing or reviewing Convex queries, check deployment health.** Run `bunx convex insights` to check for OCC conflicts, `bytesReadLimit`, and `documentsReadLimit` errors. Run `bunx convex logs --failure` to see individual error messages and stack traces. This helps identify which functions are causing bandwidth issues so you can prioritize fixes.

<!-- convex-ai-start -->
This project uses [Convex](https://convex.dev) as its backend.

When working on Convex code, **always read `convex/_generated/ai/guidelines.md` first** for important guidelines on how to correctly use Convex APIs and patterns. The file contains rules that override what you may have learned about Convex from training data.

Convex agent skills for common tasks can be installed by running `npx convex ai-files install`.
<!-- convex-ai-end -->
8 changes: 8 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,11 @@

- Tests use `._handler` to call mutation handlers directly with mock `db` objects.
- Mock `db` objects MUST include `normalizeId: vi.fn()` for trigger wrapper compatibility.

<!-- convex-ai-start -->
This project uses [Convex](https://convex.dev) as its backend.

When working on Convex code, **always read `convex/_generated/ai/guidelines.md` first** for important guidelines on how to correctly use Convex APIs and patterns. The file contains rules that override what you may have learned about Convex from training data.

Convex agent skills for common tasks can be installed by running `npx convex ai-files install`.
<!-- convex-ai-end -->
6 changes: 6 additions & 0 deletions convex/_generated/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import type * as lib_contentTypes from "../lib/contentTypes.js";
import type * as lib_embeddingVisibility from "../lib/embeddingVisibility.js";
import type * as lib_embeddings from "../lib/embeddings.js";
import type * as lib_githubAccount from "../lib/githubAccount.js";
import type * as lib_githubActionsOidc from "../lib/githubActionsOidc.js";
import type * as lib_githubBackup from "../lib/githubBackup.js";
import type * as lib_githubIdentity from "../lib/githubIdentity.js";
import type * as lib_githubImport from "../lib/githubImport.js";
Expand Down Expand Up @@ -92,11 +93,13 @@ import type * as lib_userSearch from "../lib/userSearch.js";
import type * as lib_webhooks from "../lib/webhooks.js";
import type * as llmEval from "../llmEval.js";
import type * as maintenance from "../maintenance.js";
import type * as packagePublishTokens from "../packagePublishTokens.js";
import type * as packages from "../packages.js";
import type * as publishers from "../publishers.js";
import type * as rateLimits from "../rateLimits.js";
import type * as search from "../search.js";
import type * as seed from "../seed.js";
import type * as seedDemo from "../seedDemo.js";
import type * as seedSouls from "../seedSouls.js";
import type * as skillStatEvents from "../skillStatEvents.js";
import type * as skillTransfers from "../skillTransfers.js";
Expand Down Expand Up @@ -161,6 +164,7 @@ declare const fullApi: ApiFromModules<{
"lib/embeddingVisibility": typeof lib_embeddingVisibility;
"lib/embeddings": typeof lib_embeddings;
"lib/githubAccount": typeof lib_githubAccount;
"lib/githubActionsOidc": typeof lib_githubActionsOidc;
"lib/githubBackup": typeof lib_githubBackup;
"lib/githubIdentity": typeof lib_githubIdentity;
"lib/githubImport": typeof lib_githubImport;
Expand Down Expand Up @@ -205,11 +209,13 @@ declare const fullApi: ApiFromModules<{
"lib/webhooks": typeof lib_webhooks;
llmEval: typeof llmEval;
maintenance: typeof maintenance;
packagePublishTokens: typeof packagePublishTokens;
packages: typeof packages;
publishers: typeof publishers;
rateLimits: typeof rateLimits;
search: typeof search;
seed: typeof seed;
seedDemo: typeof seedDemo;
seedSouls: typeof seedSouls;
skillStatEvents: typeof skillStatEvents;
skillTransfers: typeof skillTransfers;
Expand Down
5 changes: 5 additions & 0 deletions convex/devSeed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,11 @@ export const seedSkillMutation = internalMutation({
createdAt: now,
updatedAt: now,
});
await ctx.db.patch(userId, {
publishedSkills: 1,
totalStars: 0,
totalDownloads: 0,
});

const versionId = await ctx.db.insert("skillVersions", {
skillId,
Expand Down
2 changes: 1 addition & 1 deletion convex/lib/githubActionsOidc.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* @vitest-environment node */

import { describe, expect, it } from "vitest";
import { describe, expect, it, vi } from "vitest";
import {
extractWorkflowFilenameFromWorkflowRef,
verifyGitHubActionsTrustedPublishJwt,
Expand Down
67 changes: 67 additions & 0 deletions convex/lib/userSkillStats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import type { Doc, Id } from "../_generated/dataModel";
import type { MutationCtx } from "../_generated/server";

function getSkillContribution(skill: Doc<"skills">) {
if (skill.softDeletedAt) {
return { publishedSkills: 0, totalStars: 0, totalDownloads: 0 };
}

return {
publishedSkills: 1,
totalStars: skill.stats?.stars ?? 0,
totalDownloads: skill.stats?.downloads ?? 0,
};
}

async function patchUserStats(
ctx: Pick<MutationCtx, "db">,
userId: Id<"users">,
delta: { publishedSkills: number; totalStars: number; totalDownloads: number },
) {
const user = await ctx.db.get(userId);
if (!user) return;

await ctx.db.patch(userId, {
publishedSkills: Math.max(0, (user.publishedSkills ?? 0) + delta.publishedSkills),
totalStars: Math.max(0, (user.totalStars ?? 0) + delta.totalStars),
totalDownloads: Math.max(0, (user.totalDownloads ?? 0) + delta.totalDownloads),
});
}

export async function adjustUserSkillStatsForSkillChange(
ctx: Pick<MutationCtx, "db">,
previousSkill: Doc<"skills"> | null | undefined,
nextSkill: Doc<"skills"> | null | undefined,
) {
if (!previousSkill && !nextSkill) return;

const prevOwnerId = previousSkill?.ownerUserId ?? null;
const nextOwnerId = nextSkill?.ownerUserId ?? null;
const prevContribution = previousSkill ? getSkillContribution(previousSkill) : null;
const nextContribution = nextSkill ? getSkillContribution(nextSkill) : null;

if (prevOwnerId && prevOwnerId === nextOwnerId) {
await patchUserStats(ctx, prevOwnerId, {
publishedSkills: (nextContribution?.publishedSkills ?? 0) - (prevContribution?.publishedSkills ?? 0),
totalStars: (nextContribution?.totalStars ?? 0) - (prevContribution?.totalStars ?? 0),
totalDownloads: (nextContribution?.totalDownloads ?? 0) - (prevContribution?.totalDownloads ?? 0),
});
return;
}

if (prevOwnerId) {
await patchUserStats(ctx, prevOwnerId, {
publishedSkills: -(prevContribution?.publishedSkills ?? 0),
totalStars: -(prevContribution?.totalStars ?? 0),
totalDownloads: -(prevContribution?.totalDownloads ?? 0),
});
}

if (nextOwnerId) {
await patchUserStats(ctx, nextOwnerId, {
publishedSkills: nextContribution?.publishedSkills ?? 0,
totalStars: nextContribution?.totalStars ?? 0,
totalDownloads: nextContribution?.totalDownloads ?? 0,
});
}
}
6 changes: 5 additions & 1 deletion convex/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ const users = defineTable({
githubFetchedAt: v.optional(v.number()),
githubProfileSyncedAt: v.optional(v.number()),
trustedPublisher: v.optional(v.boolean()),
publishedSkills: v.optional(v.number()),
totalStars: v.optional(v.number()),
totalDownloads: v.optional(v.number()),
personalPublisherId: v.optional(v.id("publishers")),
requiresModerationAt: v.optional(v.number()),
requiresModerationReason: v.optional(v.string()),
Expand All @@ -40,7 +43,8 @@ const users = defineTable({
})
.index("email", ["email"])
.index("phone", ["phone"])
.index("handle", ["handle"]);
.index("handle", ["handle"])
.index("by_active_handle", ["deletedAt", "deactivatedAt", "handle"]);

const publishers = defineTable({
kind: v.union(v.literal("user"), v.literal("org")),
Expand Down
Loading
Loading