tweakcn Documentation
repository·main·Indexed 27 days ago
https://github.com/jnsahaj/tweakcnA visual theme editor for Tailwind CSS and shadcn/ui components that allows developers to customize UI components using presets or advanced settings. Features include an OAuth 2.0 API for theme and profile management, community theme publishing, and a set of React hooks for theme mutations and data retrieval.
What's inside tweakcn
- To run the tweakcn development environment on your local machine, ensure you have the necessary prerequisites installed and follow the installation steps below.
Implement the OAuth 2.0 Authorization Code flow
maintweakcn uses the standard OAuth 2.0 Authorization Code flow. PKCE is supported for public clients (SPAs, mobile apps) by adding
code_challengeandcode_challenge_method=S256to the authorization request, and providing thecode_verifierduring the token exchange.1. Redirect the user to authorize
Send the user to:
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_STRINGUpon successful sign-in, the user is redirected to your
redirect_uriwith acodeparameter.2. Exchange the code for tokens
Exchange the authorization code for an access token and refresh token using a POST request:
3. Call the API
Use the
access_tokenas a Bearer token in theAuthorizationheader.4. Refresh tokens
When the access token expires (typically after 1 hour), use the
refresh_tokento obtain a new pair.5. Revoke tokens
Revoke an access or refresh token when it is no longer needed.
# 1. Exchange 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/callback # 2. Call the API curl https://tweakcn.com/api/v1/themes \ -H "Authorization: Bearer ACCESS_TOKEN" # 3. Refresh tokens 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_TOKEN # 4. Revoke tokens curl -X POST https://tweakcn.com/api/oauth/revoke \ -d token=ACCESS_OR_REFRESH_TOKENPrerequisites for running tweakcn
mainBefore installing tweakcn, ensure your environment meets the following requirements:
- Node.js: version 18 or higher
- Package Manager: npm, yarn, or pnpm
Register an OAuth app via CLI
mainTo allow external applications to authenticate users and access data, register an OAuth app using the provided CLI script. This will generate a
client_idand aclient_secret. Note that theclient_secretis only displayed once during creation.npx tsx scripts/create-oauth-app.ts \ --name "My App" \ --redirect-uris "https://myapp.com/callback" \ --scopes "themes:read,profile:read" \ --description "Optional description"Run the application locally using Docker Compose
mainTo run the full stack (application and database) locally, use the provided
docker-compose.ymlconfiguration. The setup builds the application from the local directory, maps port3000for the web service, and uses a PostgreSQL 15 database.Service Details:
- app: Builds from the current directory, uses
.env.localfor environment variables, and mounts the current directory to/appfor live development. It automatically runsnpx drizzle-kit pushto sync the database schema before starting the development server vianpm run dev. - db: A PostgreSQL 15 service running on port
5432with default credentials (postgres/postgres) and a database namedtweakcn.
- app: Builds from the current directory, uses
Integrate tweakcn with Better Auth genericOAuth
maintweakcn can be used as a provider within the Better Auth
genericOAuthplugin. You must configure theauthorizationUrl,tokenUrl, anduserInfoUrlcorrectly on the server, and usegenericOAuthClienton the client side.// 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", });Configure Drizzle ORM with drizzle-kit
mainThe project uses
drizzle-kitfor database schema management and migrations. The configuration is defined usingdefineConfigand requires a schema file, an output directory for migrations, and database credentials. Environment variables are loaded from.env.localusingdotenv.import "dotenv/config"; import { defineConfig } from "drizzle-kit"; import { config } from "dotenv"; config({ path: ".env.local" }); export default defineConfig({ out: "./drizzle", schema: "./db/schema.ts", dialect: "postgresql", dbCredentials: { url: process.env.DATABASE_URL!, }, });Reference: tweakcn OAuth API Endpoints
mainAll API endpoints require an
Authorization: Bearer <access_token>header.GET /api/oauth/userinfo: OIDC-compatible endpoint returning flat user fields. Requiresprofile:readscope.GET /api/v1/me: Returns the authenticated user's profile. Requiresprofile:readscope.GET /api/v1/themes: Returns all themes owned by the authenticated user. Requiresthemes:readscope.GET /api/v1/themes/:themeId: Returns a single theme by ID (must be owned by the user). Requiresthemes:readscope.
Reference: OAuth Scopes
mainUse these scopes to request specific permissions during the authorization flow:
Scope Description themes:readRead the user's saved themes profile:readRead the user's profile (name, email, avatar) Reference: OAuth Error Responses
mainError responses follow the OAuth 2.0 specification and return a JSON object containing the error type and description.
{ "error": "invalid_token", "error_description": "Invalid or expired access token" }Wait for a font to load with `waitForFont`
mainAsynchronously waits for a specific font family and weight to load using the nativedocument.fonts.loadAPI. It includes a configurable timeout to prevent infinite waiting. Returns aPromise<boolean>which resolves totrueif the font is loaded, orfalseif it fails or times out.Extract a font family name from a CSS string with `extractFontFamily`
mainParse a CSSfont-familystring to retrieve only the primary font name. The function strips quotes, handles whitespace, and returnsnullif the extracted name is a known system font or if the input is invalid.