Technical Documentation: jnsahaj/tweakcn
โน๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference
Overview (`README.md`)
tweakcn is a powerful Visual Theme Editor for tailwind CSS & shadcn/ui components. It comes with Beautiful theme presets to get started, while aiming to offer advanced customisation for each aspect of your UI
Motivation
Websites made with shadcn/ui famously look the same. tweakcn is a tool that helps you customize shadcn/ui components visually, to make your components stand-out. The goal is to build a platform where a user can discover endless customization options and then have the ability to put their own twist on it.
Current Features
You can find the full feature list here: https://tweakcn.com/#features
Run Locally
IMPORTANT: For contributions, please see CONTRIBUTING.md.
Prerequisites
- Node.js 18+
- npm / yarn / pnpm
Installation
- Clone the repository:
git clone https://github.com/jnsahaj/tweakcn.git
cd tweakcn- Install dependencies:
npm install- Start the development server:
npm run dev- Open http://localhost:3000 in your browser.
Contributors
Made with contrib.rocks.
Interested in Contributing?
Contributions are welcome! Please feel free to submit a Pull Request.
Star History
Oauth Api (`docs/oauth-api.md`)
OAuth 2.0 API
tweakcn exposes an OAuth 2.0 API so external apps can authenticate users and access their data.
Registering an app
Register an OAuth app via the CLI script:
npx tsx scripts/create-oauth-app.ts \
--name "My App" \
--redirect-uris "https://myapp.com/callback" \
--scopes "themes:read,profile:read" \
--description "Optional description"This outputs a client_id and client_secret. The secret is shown once and cannot be retrieved later.
Authorization flow
Standard OAuth 2.0 Authorization Code flow. PKCE is supported but optional.
1. Redirect the user to authorize
GET https://tweakcn.com/api/oauth/authorize
?client_id=CLIENT_ID
&redirect_uri=https://myapp.com/callback
&response_type=code
&scope=themes:read profile:read
&state=RANDOM_STRINGIf the user is not signed in, they'll be shown a sign-in page. After signing in, they're redirected to your redirect_uri with an authorization code:
https://myapp.com/callback?code=AUTH_CODE&state=RANDOM_STRING2. Exchange the code for tokens
curl -X POST https://tweakcn.com/api/oauth/token \
-d grant_type=authorization_code \
-d client_id=CLIENT_ID \
-d client_secret=CLIENT_SECRET \
-d code=AUTH_CODE \
-d redirect_uri=https://myapp.com/callbackResponse:
{
"access_token": "...",
"refresh_token": "...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "themes:read profile:read"
}3. Call the API
Pass the access token as a Bearer token:
curl https://tweakcn.com/api/v1/themes \
-H "Authorization: Bearer ACCESS_TOKEN"4. Refresh tokens
Access tokens expire after 1 hour. Use the refresh token to get a new pair:
curl -X POST https://tweakcn.com/api/oauth/token \
-d grant_type=refresh_token \
-d client_id=CLIENT_ID \
-d client_secret=CLIENT_SECRET \
-d refresh_token=REFRESH_TOKEN5. Revoke tokens
curl -X POST https://tweakcn.com/api/oauth/revoke \
-d token=ACCESS_OR_REFRESH_TOKENUsing with Better Auth's genericOAuth
tweakcn works as a provider with Better Auth's genericOAuth plugin:
// server
import { genericOAuth } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
genericOAuth({
config: [
{
providerId: "tweakcn",
clientId: process.env.TWEAKCN_CLIENT_ID,
clientSecret: process.env.TWEAKCN_CLIENT_SECRET,
authorizationUrl: "https://tweakcn.com/api/oauth/authorize",
tokenUrl: "https://tweakcn.com/api/oauth/token",
userInfoUrl: "https://tweakcn.com/api/oauth/userinfo",
scopes: ["themes:read", "profile:read"],
},
],
}),
],
});// client
import { genericOAuthClient } from "better-auth/client/plugins";
const authClient = createAuthClient({
plugins: [genericOAuthClient()],
});
await authClient.signIn.oauth2({
providerId: "tweakcn",
callbackURL: "/dashboard",
});API endpoints
All endpoints require Authorization: Bearer <access_token>.
`GET /api/oauth/userinfo`
OIDC-compatible userinfo endpoint. Returns flat user fields. Requires profile:read scope.
{
"sub": "user_123",
"name": "Jane Doe",
"email": "[email protected]",
"picture": "https://..."
}`GET /api/v1/me`
Returns the authenticated user's profile. Requires profile:read scope.
{
"data": {
"id": "...",
"name": "Jane Doe",
"email": "[email protected]",
"image": "https://..."
}
}`GET /api/v1/themes`
Returns all themes owned by the authenticated user. Requires themes:read scope.
{
"data": [
{
"id": "...",
"name": "My Theme",
"styles": { ... },
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-01-01T00:00:00.000Z"
}
]
}`GET /api/v1/themes/:themeId`
Returns a single theme by ID. Only returns themes owned by the authenticated user. Requires themes:read scope.
Scopes
| Scope | Description |
|---|---|
themes:read |
Read the user's saved themes |
profile:read |
Read the user's profile (name, email, avatar) |
PKCE support
For public clients (e.g. SPAs, mobile apps), use PKCE by adding code_challenge and code_challenge_method=S256 to the authorize request, then code_verifier when exchanging the code.
Error responses
All error responses follow the OAuth 2.0 spec:
{
"error": "invalid_token",
"error_description": "Invalid or expired access token"
}--- METRICS ---
- Files Extracted: 2
- Estimated Token Budget: ~1824 tokens
- Canonical Reference: https://codewiki.google/github.com/jnsahaj/tweakcn