This commit is contained in:
afik.cohen 2023-12-04 17:54:33 -08:00
parent ea882652c0
commit d99a45d922
54 changed files with 1199 additions and 1329 deletions

View file

@ -5,18 +5,20 @@
<option name="HTML_ENFORCE_QUOTES" value="true" />
</HTMLCodeStyleSettings>
<JSCodeStyleSettings version="0">
<option name="USE_SEMICOLON_AFTER_STATEMENT" value="false" />
<option name="FORCE_SEMICOLON_STYLE" value="true" />
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="Remove" />
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
<option name="SPACES_WITHIN_IMPORTS" value="true" />
</JSCodeStyleSettings>
<TypeScriptCodeStyleSettings version="0">
<option name="USE_SEMICOLON_AFTER_STATEMENT" value="false" />
<option name="FORCE_SEMICOLON_STYLE" value="true" />
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="Remove" />
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
<option name="SPACES_WITHIN_IMPORTS" value="true" />
</TypeScriptCodeStyleSettings>

View file

@ -1 +1,4 @@
{}
printWidth: 100
semi: false
trailingComma: all
bracketSameLine: false

View file

@ -1,21 +1,23 @@
import fetch, {RequestInit, Response} from "node-fetch";
import fetch, { RequestInit, Response } from "node-fetch"
export async function userNickname(userId: string): Promise<string | null> {
const url = new URL(`${process.env.AUTH0_ISSUER_BASE_URL}/api/v2/users/${userId}`);
const params = new URLSearchParams({
fields: 'nickname',
include_fields: 'true'
});
url.search = params.toString();
const url = new URL(`${process.env.AUTH0_ISSUER_BASE_URL}/api/v2/users/${userId}`)
const params = new URLSearchParams({
fields: "nickname",
include_fields: "true",
})
url.search = params.toString()
const options: RequestInit = {
method: 'GET',
headers: {authorization: `Bearer ${process.env.AUTH0_MANAGEMENT_API_TOKEN}`}
};
const options: RequestInit = {
method: "GET",
headers: {
authorization: `Bearer ${process.env.AUTH0_MANAGEMENT_API_TOKEN}`,
},
}
const response: Response = await fetch(url, options);
if (response.ok) {
const user: { 'nickname' } = await response.json();
return user.nickname;
} else return null;
const response: Response = await fetch(url, options)
if (response.ok) {
const user: { nickname } = await response.json()
return user.nickname
} else return null
}

View file

@ -1,115 +1,116 @@
import {FileDiffContent} from "code-suggester/build/src/types"
import { FileDiffContent } from "code-suggester/build/src/types"
import Octokit from "@octokit/rest"
export async function createPullRequestFromDiffContents(
installationOctokit: Octokit,
owner: string,
repo: string,
pullNumber: number,
diffContentsMap: Map<string, FileDiffContent>
installationOctokit: Octokit,
owner: string,
repo: string,
pullNumber: number,
diffContentsMap: Map<string, FileDiffContent>,
): Promise<Octokit.Response<Octokit.PullsCreateResponse>> {
// Create a new branch
const newBranchName = `codeflash-suggestions-for-pr${pullNumber}-${Date.now()}`;
await installationOctokit.rest.git.createRef({
// Create a new branch
const newBranchName = `codeflash-suggestions-for-pr${pullNumber}-${Date.now()}`
await installationOctokit.rest.git.createRef({
owner,
repo,
ref: `refs/heads/${newBranchName}`,
sha: (
await installationOctokit.rest.git.getRef({
owner,
repo,
ref: `refs/heads/${newBranchName}`,
sha: (await installationOctokit.rest.git.getRef({
owner,
repo,
ref: `heads/main`,
})).data.object.sha,
});
ref: `heads/main`,
})
).data.object.sha,
})
// Apply changes from diffContentsMap to the new branch and commit
const treeItems: Octokit.GitCreateTreeParamsTree[] = [];
for (const [filePath, fileDiffContent] of diffContentsMap) {
// Create blobs for the changed files with newContent
const blobData = await installationOctokit.rest.git.createBlob({
owner,
repo,
content: fileDiffContent.newContent,
encoding: 'utf-8',
});
treeItems.push({
path: filePath,
mode: '100644', // blob
type: 'blob',
sha: blobData.data.sha,
});
}
// Get the current commit SHA of the new branch
const refData = await installationOctokit.rest.git.getRef({
owner,
repo,
ref: `heads/${newBranchName}`,
});
const currentCommitSha = refData.data.object.sha;
// Get the tree SHA of the current commit
const commitData = await installationOctokit.rest.git.getCommit({
owner,
repo,
commit_sha: currentCommitSha,
});
const currentTreeSha = commitData.data.tree.sha;
// Create a new tree with the changes
const newTreeData = await installationOctokit.rest.git.createTree({
owner,
repo,
base_tree: currentTreeSha,
tree: treeItems,
});
// Create a new commit with the new tree
const newCommitData = await installationOctokit.rest.git.createCommit({
owner,
repo,
message: `Code Suggestions for PR #${pullNumber}`,
tree: newTreeData.data.sha,
parents: [currentCommitSha],
});
// Update the new branch reference to point to the new commit
await installationOctokit.rest.git.updateRef({
owner,
repo,
ref: `heads/${newBranchName}`,
sha: newCommitData.data.sha,
});
// Get the head branch of the original pull request
const originalPrData = await installationOctokit.rest.pulls.get({
owner,
repo,
pull_number: pullNumber,
});
const targetBranch = originalPrData.data.head.ref;
// Open a new PR from the new branch onto the original PR's head branch
const newPrData = await installationOctokit.rest.pulls.create({
owner,
repo,
title: `CodeFlash Suggestions for PR #${pullNumber} ⚡️`,
head: newBranchName,
base: targetBranch,
body: `This pull request contains code suggestions for PR #${pullNumber}.
If you approve this dependent PR, these changes will be merged into that PR (branch \`${targetBranch}\`).`
});
// Make a comment on the original PR with a link to the new PR
const result = await installationOctokit.rest.issues.createComment({
owner: owner,
repo: repo,
issue_number: pullNumber,
body: `## ⚡️ CodeFlash found optimizations for this PR
I created a new dependent PR with the suggested changes: #${newPrData.data.number}
If you approve, it will be merged into this PR (branch \`${targetBranch}\`).`,
// Apply changes from diffContentsMap to the new branch and commit
const treeItems: Octokit.GitCreateTreeParamsTree[] = []
for (const [filePath, fileDiffContent] of diffContentsMap) {
// Create blobs for the changed files with newContent
const blobData = await installationOctokit.rest.git.createBlob({
owner,
repo,
content: fileDiffContent.newContent,
encoding: "utf-8",
})
return newPrData
treeItems.push({
path: filePath,
mode: "100644", // blob
type: "blob",
sha: blobData.data.sha,
})
}
// Get the current commit SHA of the new branch
const refData = await installationOctokit.rest.git.getRef({
owner,
repo,
ref: `heads/${newBranchName}`,
})
const currentCommitSha = refData.data.object.sha
// Get the tree SHA of the current commit
const commitData = await installationOctokit.rest.git.getCommit({
owner,
repo,
commit_sha: currentCommitSha,
})
const currentTreeSha = commitData.data.tree.sha
// Create a new tree with the changes
const newTreeData = await installationOctokit.rest.git.createTree({
owner,
repo,
base_tree: currentTreeSha,
tree: treeItems,
})
// Create a new commit with the new tree
const newCommitData = await installationOctokit.rest.git.createCommit({
owner,
repo,
message: `Code Suggestions for PR #${pullNumber}`,
tree: newTreeData.data.sha,
parents: [currentCommitSha],
})
// Update the new branch reference to point to the new commit
await installationOctokit.rest.git.updateRef({
owner,
repo,
ref: `heads/${newBranchName}`,
sha: newCommitData.data.sha,
})
// Get the head branch of the original pull request
const originalPrData = await installationOctokit.rest.pulls.get({
owner,
repo,
pull_number: pullNumber,
})
const targetBranch = originalPrData.data.head.ref
// Open a new PR from the new branch onto the original PR's head branch
const newPrData = await installationOctokit.rest.pulls.create({
owner,
repo,
title: `CodeFlash Suggestions for PR #${pullNumber} ⚡️`,
head: newBranchName,
base: targetBranch,
body: `This pull request contains code suggestions for PR #${pullNumber}.
If you approve this dependent PR, these changes will be merged into that PR (branch \`${targetBranch}\`).`,
})
// Make a comment on the original PR with a link to the new PR
const result = await installationOctokit.rest.issues.createComment({
owner: owner,
repo: repo,
issue_number: pullNumber,
body: `## ⚡️ CodeFlash found optimizations for this PR
I created a new dependent PR with the suggested changes: #${newPrData.data.number}
If you approve, it will be merged into this PR (branch \`${targetBranch}\`).`,
})
return newPrData
}

View file

@ -1,11 +1,11 @@
import {App} from "octokit";
import { App } from "octokit"
// import {getGithubAppPrivateKey, getGithubAppWebhookSecret} from "../common/azure-keyvault";
//TODO the above import doesn't work, we have to do this vvv
import azureKeyvault from "../common/azure-keyvault";
import azureKeyvault from "../common/azure-keyvault"
const {getGithubAppPrivateKey, getGithubAppWebhookSecret} = azureKeyvault;
const { getGithubAppPrivateKey, getGithubAppWebhookSecret } = azureKeyvault
const APP_ID: string = process.env.APP_ID || ''; // Replace with your GitHub App ID
const APP_ID: string = process.env.APP_ID || "" // Replace with your GitHub App ID
const PRIVATE_KEY: string = await getGithubAppPrivateKey()
const WEBHOOK_SECRET: string = await getGithubAppWebhookSecret()
@ -15,90 +15,94 @@ export const webhookApiPath: string = "/api/github/webhooks"
// See https://github.com/octokit/webhooks.js/#createnodemiddleware for all options
export const githubApp = new App({
appId: APP_ID,
privateKey: PRIVATE_KEY,
webhooks: {
secret: WEBHOOK_SECRET
},
});
appId: APP_ID,
privateKey: PRIVATE_KEY,
webhooks: {
secret: WEBHOOK_SECRET,
},
})
// Optional: Get & log the authenticated app's name
const {data} = await githubApp.octokit.request('/app')
const { data } = await githubApp.octokit.request("/app")
// Read more about custom logging: https://github.com/octokit/core.js#logging
githubApp.octokit.log.debug(`Authenticated as '${data.name}'`)
githubApp.webhooks.on('installation', async ({octokit, payload}) => {
console.log(`Received a new installation event: ${JSON.stringify(payload)}`)
// Create an installation access token
const installationAccessToken = await octokit.apps.createInstallationAccessToken({
installation_id: payload.installation.id,
});
console.log(`Installation access token: ${installationAccessToken.token}`);
githubApp.webhooks.on("installation", async ({ octokit, payload }) => {
console.log(`Received a new installation event: ${JSON.stringify(payload)}`)
// Create an installation access token
const installationAccessToken = await octokit.apps.createInstallationAccessToken({
installation_id: payload.installation.id,
})
console.log(`Installation access token: ${installationAccessToken.token}`)
})
githubApp.webhooks.on('pull_request.opened', async ({octokit, payload}) => {
console.log(`Received a pull request event: ${JSON.stringify(payload)}`)
try {
await octokit.rest.issues.createComment({
owner: payload.repository.owner.login,
repo: payload.repository.name,
issue_number: payload.pull_request.number,
body: "Thanks for opening this pull request!"
})
githubApp.webhooks.on("pull_request.opened", async ({ octokit, payload }) => {
console.log(`Received a pull request event: ${JSON.stringify(payload)}`)
try {
await octokit.rest.issues.createComment({
owner: payload.repository.owner.login,
repo: payload.repository.name,
issue_number: payload.pull_request.number,
body: "Thanks for opening this pull request!",
})
// await octokit.rest.pulls.createReviewComment({
// owner: payload.repository.owner.login,
// repo: payload.repository.name,
// pull_number: payload.pull_request.number,
// body: "Thanks for opening this pull request!",
// commit_id: payload.pull_request.head.sha,
// path:
// })
console.log(`Posted pull request comment!`)
} catch (error: any) {
if (error.response) {
console.error(`Error! Status: ${error.response.status}. Message: ${error.response.data.message}`)
} else {
console.error(error)
}
// await octokit.rest.pulls.createReviewComment({
// owner: payload.repository.owner.login,
// repo: payload.repository.name,
// pull_number: payload.pull_request.number,
// body: "Thanks for opening this pull request!",
// commit_id: payload.pull_request.head.sha,
// path:
// })
console.log(`Posted pull request comment!`)
} catch (error: any) {
if (error.response) {
console.error(
`Error! Status: ${error.response.status}. Message: ${error.response.data.message}`,
)
} else {
console.error(error)
}
}
})
githubApp.webhooks.on('installation.created', async ({octokit, payload}) => {
console.log(`Received a installation.created event: ${JSON.stringify(payload)}`);
try {
console.log(`Successfully processed this!!!`);
} catch (error: any) {
if (error.response) {
console.error(`Error! Status: ${error.response.status}. Message: ${error.response.data.message}`)
} else {
console.error(error)
}
githubApp.webhooks.on("installation.created", async ({ octokit, payload }) => {
console.log(`Received a installation.created event: ${JSON.stringify(payload)}`)
try {
console.log(`Successfully processed this!!!`)
} catch (error: any) {
if (error.response) {
console.error(
`Error! Status: ${error.response.status}. Message: ${error.response.data.message}`,
)
} else {
console.error(error)
}
});
}
})
githubApp.webhooks.on('installation_repositories.added', async ({octokit, payload}) => {
console.log(`Received a installation_repositories.added event: ${JSON.stringify(payload)}`);
try {
console.log(`Successfully processed this!!!`);
} catch (error: any) {
if (error.response) {
console.error(`Error! Status: ${error.response.status}. Message: ${error.response.data.message}`)
} else {
console.error(error)
}
githubApp.webhooks.on("installation_repositories.added", async ({ octokit, payload }) => {
console.log(`Received a installation_repositories.added event: ${JSON.stringify(payload)}`)
try {
console.log(`Successfully processed this!!!`)
} catch (error: any) {
if (error.response) {
console.error(
`Error! Status: ${error.response.status}. Message: ${error.response.data.message}`,
)
} else {
console.error(error)
}
});
}
})
// Optional: Handle errors
githubApp.webhooks.onError((error) => {
if (error.name === 'AggregateError') {
// Log Secret verification errors
console.log(`Error processing request: ${error.event}`)
} else {
console.log(error)
}
})
if (error.name === "AggregateError") {
// Log Secret verification errors
console.log(`Error processing request: ${error.event}`)
} else {
console.log(error)
}
})

View file

@ -1,71 +1,67 @@
import { createNodeMiddleware } from "@octokit/webhooks";
import express from "express";
import suggester from "code-suggester";
import { FileDiffContent } from "code-suggester/build/src/types";
import { createNodeMiddleware } from "@octokit/webhooks"
import express from "express"
import suggester from "code-suggester"
import { FileDiffContent } from "code-suggester/build/src/types"
import { determineValidHunks, objectToMap } from "./utils";
import { createPullRequestFromDiffContents } from "./create-pr-from-diffcontents";
import { githubApp, webhookApiPath } from "./github-app";
import functions from "../common/token-functions";
import dotenv from "dotenv";
import { userNickname } from "./auth0";
import { determineValidHunks, objectToMap } from "./utils"
import { createPullRequestFromDiffContents } from "./create-pr-from-diffcontents"
import { githubApp, webhookApiPath } from "./github-app"
import functions from "../common/token-functions"
import dotenv from "dotenv"
import { userNickname } from "./auth0"
const userForAPIKey = functions.userForAPIKey;
const userForAPIKey = functions.userForAPIKey
const port = process.env.PORT || 3001;
const port = process.env.PORT || 3001
if (process.env.NODE_ENV !== "production") {
dotenv.config({ path: ".env.local" });
dotenv.config({ path: ".env.local" })
}
const appExpress = express();
const appExpress = express()
// Mount the github webhook middleware onto the express application
appExpress.use(
createNodeMiddleware(githubApp.webhooks, {path: webhookApiPath}),
);
appExpress.use(createNodeMiddleware(githubApp.webhooks, { path: webhookApiPath }))
appExpress.use(express.json());
appExpress.use(express.json())
// Middleware to check for valid API key
appExpress.use(async (req, res, next) => {
const authHeader = req.headers.authorization;
const authHeader = req.headers.authorization
if (!authHeader) {
return res.status(401).send("Authorization header is missing");
return res.status(401).send("Authorization header is missing")
}
const apiKey = authHeader.replace(/^Bearer\s+/, "");
const userId = await userForAPIKey(apiKey);
const apiKey = authHeader.replace(/^Bearer\s+/, "")
const userId = await userForAPIKey(apiKey)
if (userId == null) {
return res.status(403).send("Invalid API key");
return res.status(403).send("Invalid API key")
}
req.userId = userId;
req.userId = userId
next();
});
next()
})
appExpress.post("/api/suggest-pr-changes", async (req, res) => {
try {
const { owner, repo, pullNumber, diffContents, prComment } = req.body;
const userId = req.userId;
const { owner, repo, pullNumber, diffContents, prComment } = req.body
const userId = req.userId
if (!repo || !owner || !pullNumber) {
return res.status(400).send("Missing required fields");
return res.status(400).send("Missing required fields")
}
const nickname = await userNickname(userId);
const nickname = await userNickname(userId)
if (!nickname) {
return res.status(401).send("Unauthorized"); //Error getting user nickname
return res.status(401).send("Unauthorized") //Error getting user nickname
}
const repoInstallation =
await githubApp.octokit.rest.apps.getRepoInstallation({
owner: owner,
repo: repo,
});
const installationId = repoInstallation.data.id;
const installationOctokit =
await githubApp.getInstallationOctokit(installationId);
const repoInstallation = await githubApp.octokit.rest.apps.getRepoInstallation({
owner: owner,
repo: repo,
})
const installationId = repoInstallation.data.id
const installationOctokit = await githubApp.getInstallationOctokit(installationId)
try {
// Check if the user is a collaborator on the repository
@ -73,23 +69,22 @@ appExpress.post("/api/suggest-pr-changes", async (req, res) => {
owner: owner,
repo: repo,
username: nickname,
});
})
// If the request is successful, the user is a collaborator and we can continue
if (response.status == 204) {
console.log(`${nickname} is a collaborator on ${owner}/${repo}`);
console.log(`${nickname} is a collaborator on ${owner}/${repo}`)
}
} catch (error) {
if (error.status === 404) {
return res.status(401).send("Unauthorized"); // User is not a collaborator
return res.status(401).send("Unauthorized") // User is not a collaborator
}
// Handle other potential errors
return res.status(500).send(`Error`); // Error checking collaborator status
return res.status(500).send(`Error`) // Error checking collaborator status
}
// Suggest changes
const diffContentsMap: Map<string, FileDiffContent> =
objectToMap(diffContents);
const diffContentsMap: Map<string, FileDiffContent> = objectToMap(diffContents)
const { validHunks, invalidHunks } = await determineValidHunks(
installationOctokit.rest,
@ -97,7 +92,7 @@ appExpress.post("/api/suggest-pr-changes", async (req, res) => {
pullNumber,
100,
diffContentsMap,
);
)
if (invalidHunks.size > 0) {
// we can't suggest all of the hunks for this PR, because some of them are invalid (out of scope for this PR).
@ -110,7 +105,7 @@ appExpress.post("/api/suggest-pr-changes", async (req, res) => {
repo,
pullNumber,
diffContentsMap,
);
)
// Add explanation comment
const result = await installationOctokit.rest.issues.createComment({
@ -118,10 +113,10 @@ appExpress.post("/api/suggest-pr-changes", async (req, res) => {
repo: repo,
issue_number: newPrData.data.number,
body: prComment,
});
})
// Respond with the new PR details
res.json(newPrData.data.number);
res.json(newPrData.data.number)
} else {
// all suggestions are valid and can be made on this PR.
// let's make the suggestions
@ -135,7 +130,7 @@ appExpress.post("/api/suggest-pr-changes", async (req, res) => {
pullNumber,
pageSize: 100,
},
);
)
// Add explanation comment
const result = await installationOctokit.rest.issues.createComment({
@ -143,13 +138,13 @@ appExpress.post("/api/suggest-pr-changes", async (req, res) => {
repo: repo,
issue_number: pullNumber,
body: prComment,
});
res.json(reviewNumber);
})
res.json(reviewNumber)
}
} catch (error) {
res.status(500).send(`Error creating pull request: ${error.message}`);
res.status(500).send(`Error creating pull request: ${error.message}`)
}
});
})
// WORKS without express.json() vvvvv
// const router = express.Router();
@ -172,6 +167,6 @@ appExpress.post("/api/suggest-pr-changes", async (req, res) => {
appExpress.listen(port, () => {
console.log(
`Server is listening on ${port} for CF API requests. Github App webhook events mounted at ${webhookApiPath}`,
);
console.log("Press Ctrl + C to quit.");
});
)
console.log("Press Ctrl + C to quit.")
})

View file

@ -1,18 +1,18 @@
import fs from 'fs';
import fs from "fs"
const APP_ID: string = process.env.APP_ID || ''; // Replace with your GitHub App ID
const APP_ID: string = process.env.APP_ID || "" // Replace with your GitHub App ID
const PRIVATE_KEY: string = fs.readFileSync('codeflash-app.2023-11-04.private-key.pem', 'utf8');
const CLIENT_ID: string = process.env.CLIENT_ID || ''; // Replace with your GitHub App client ID
const CLIENT_SECRET: string = process.env.CLIENT_SECRET || ''; // Replace with your GitHub App client secret
const PRIVATE_KEY: string = fs.readFileSync("codeflash-app.2023-11-04.private-key.pem", "utf8")
const CLIENT_ID: string = process.env.CLIENT_ID || "" // Replace with your GitHub App client ID
const CLIENT_SECRET: string = process.env.CLIENT_SECRET || "" // Replace with your GitHub App client secret
if (!APP_ID || !PRIVATE_KEY || !CLIENT_ID || !CLIENT_SECRET) {
throw new Error('Environment variables are not set');
throw new Error("Environment variables are not set")
}
export async function createWorkflow(repoOwner: string, repoName: string) {
const content = Buffer.from(`
const content = Buffer.from(
`
name: Code Optimization
on:
@ -28,16 +28,17 @@ jobs:
- uses: CodeFlash/optimize-action@main
with:
token:
`).toString('base64');
`,
).toString("base64")
await installationOctokit.repos.createOrUpdateFileContents({
owner: repoOwner,
repo: repoName,
path: '.github/workflows/optimize.yml',
message: 'Setup Code Optimization action',
content: content,
});
await installationOctokit.repos.createOrUpdateFileContents({
owner: repoOwner,
repo: repoName,
path: ".github/workflows/optimize.yml",
message: "Setup Code Optimization action",
content: content,
})
}
// Example usage
createWorkflow('user-or-org', 'repo-name', 123456); // Replace with actual owner, repo name, and installation ID
createWorkflow("user-or-org", "repo-name", 123456) // Replace with actual owner, repo name, and installation ID

View file

@ -30,4 +30,4 @@
"exclude": [
"node_modules"
]
}
}

View file

@ -1,31 +1,38 @@
import {FileDiffContent, Hunk} from "code-suggester/build/src/types";
import { FileDiffContent, Hunk } from "code-suggester/build/src/types"
import {parseAllHunks} from "code-suggester/build/src/utils/diff-utils";
import {getRawSuggestionHunks, partitionSuggestedHunksByScope} from "code-suggester/build/src/utils/hunk-utils";
import {getPullRequestHunks} from "code-suggester/build/src/github/review-pull-request";
import { parseAllHunks } from "code-suggester/build/src/utils/diff-utils"
import {
getRawSuggestionHunks,
partitionSuggestedHunksByScope,
} from "code-suggester/build/src/utils/hunk-utils"
import { getPullRequestHunks } from "code-suggester/build/src/github/review-pull-request"
export function objectToMap(obj): Map<string, FileDiffContent> {
const map = new Map();
Object.keys(obj).forEach(key => {
if (obj[key] && typeof obj[key] === 'object') {
map.set(key, <FileDiffContent>{
oldContent: obj[key].oldContent,
newContent: obj[key].newContent
});
}
});
return map;
const map = new Map()
Object.keys(obj).forEach((key) => {
if (obj[key] && typeof obj[key] === "object") {
map.set(key, <FileDiffContent>{
oldContent: obj[key].oldContent,
newContent: obj[key].newContent,
})
}
})
return map
}
export async function determineValidHunks(octokit, remote, pullNumber, pageSize, diffContents) {
// get the hunks from the pull request
const pullRequestHunks = await getPullRequestHunks(octokit, remote, pullNumber, pageSize);
// get the hunks from the suggested change
const allSuggestedHunks: Map<string, Hunk[]> = typeof diffContents === 'string'
? parseAllHunks(diffContents)
: getRawSuggestionHunks(diffContents);
// split hunks by commentable and uncommentable
const {validHunks, invalidHunks} = partitionSuggestedHunksByScope(pullRequestHunks, allSuggestedHunks);
// get the hunks from the pull request
const pullRequestHunks = await getPullRequestHunks(octokit, remote, pullNumber, pageSize)
// get the hunks from the suggested change
const allSuggestedHunks: Map<string, Hunk[]> =
typeof diffContents === "string"
? parseAllHunks(diffContents)
: getRawSuggestionHunks(diffContents)
// split hunks by commentable and uncommentable
const { validHunks, invalidHunks } = partitionSuggestedHunksByScope(
pullRequestHunks,
allSuggestedHunks,
)
return {validHunks, invalidHunks}
return { validHunks, invalidHunks }
}

View file

@ -24,4 +24,4 @@ module.exports = {
rules: {
"react/react-in-jsx-scope": "off",
},
};
}

View file

@ -1 +1,4 @@
{}
printWidth: 100
semi: false
trailingComma: all
bracketSameLine: false

View file

@ -13,4 +13,4 @@
"components": "@/components",
"utils": "@/lib/utils"
}
}
}

View file

@ -1,6 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View file

@ -1,28 +1,25 @@
// app/api/auth/[auth0]/route.js
import { handleAuth, handleLogin, handleLogout } from '@auth0/nextjs-auth0'
import { handleAuth, handleLogin, handleLogout } from "@auth0/nextjs-auth0"
export const GET = handleAuth({
login: async (request: any, response: any) => {
console.log('Logging in')
console.log("Logging in")
try {
return await handleLogin(request, response, {
returnTo: '/app'
returnTo: "/app",
})
} catch (error) {
console.log('Error logging in:', error)
console.log("Error logging in:", error)
}
},
logout: async (request: any, response: any) => {
console.log('Logging out')
console.log("Logging out")
try {
return await handleLogout(request, response, {
returnTo: '/login'
returnTo: "/login",
})
} catch (error) {
console.log('Error logging out:', error)
console.log("Error logging out:", error)
}
}
}
)
},
})

View file

@ -1,7 +1,7 @@
export default function InstallationPage() {
return (
<>
<div>Installation</div>
</>
)
return (
<>
<div>Installation</div>
</>
)
}

View file

@ -1,23 +1,15 @@
import { Sidebar } from '@/components/dashboard/sidebar'
import { Sidebar } from "@/components/dashboard/sidebar"
export default function AppRootLayout ({
children
}: {
children: React.ReactNode
}) {
export default function AppRootLayout({ children }: { children: React.ReactNode }) {
return (
<>
<div className="flex ">
<div className=" max-w-xs">
<Sidebar className="h-screen border-r"/>
{/* <text>CODEFLASH!</text> */}
</div>
<div className="px-4 py-4">
{children}
</div>
</div>
</>
<>
<div className="flex ">
<div className=" max-w-xs">
<Sidebar className="h-screen border-r" />
{/* <text>CODEFLASH!</text> */}
</div>
<div className="px-4 py-4">{children}</div>
</div>
</>
)
}

View file

@ -1,5 +1,5 @@
import { redirect } from "next/navigation";
import { redirect } from "next/navigation"
export default async function AppHomePage() {
redirect("/app/install");
redirect("/app/install")
}

View file

@ -1,16 +1,18 @@
'use client'
import {Button} from '@/components/ui/button'
import {Trash2} from 'lucide-react'
import {deleteAPIKey} from '@/app/app/tokens/tokenfuncs'
"use client"
import { Button } from "@/components/ui/button"
import { Trash2 } from "lucide-react"
import { deleteAPIKey } from "@/app/app/tokens/tokenfuncs"
export function DeleteTokenButton ({ tokenId }: { tokenId: number }): JSX.Element {
export function DeleteTokenButton({ tokenId }: { tokenId: number }): JSX.Element {
return (
<form onSubmit={(event) => {
event.preventDefault()
deleteAPIKey(tokenId)
}}>
<form
onSubmit={(event) => {
event.preventDefault()
deleteAPIKey(tokenId)
}}
>
<Button className="icon" variant="ghost" type="submit">
<Trash2 size={12}/>
<Trash2 size={12} />
</Button>
</form>
)

View file

@ -1,6 +1,6 @@
"use client";
import { Button } from "@/components/ui/button";
import { useForm } from "react-hook-form";
"use client"
import { Button } from "@/components/ui/button"
import { useForm } from "react-hook-form"
import {
Dialog,
DialogClose,
@ -10,49 +10,49 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Form, FormControl, FormField } from "@/components/ui/form";
import { Icons } from "@/components/icons";
import * as z from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { useToast } from "@/components/ui/use-toast";
import React from "react";
import { generateToken } from "@/app/app/tokens/tokenfuncs";
import { Plus } from "lucide-react";
} from "@/components/ui/dialog"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Form, FormControl, FormField } from "@/components/ui/form"
import { Icons } from "@/components/icons"
import * as z from "zod"
import { zodResolver } from "@hookform/resolvers/zod"
import { useToast } from "@/components/ui/use-toast"
import React from "react"
import { generateToken } from "@/app/app/tokens/tokenfuncs"
import { Plus } from "lucide-react"
const formSchema = z.object({
keyname: z.string().max(255, { message: "Key name must be a shorter name" }),
});
})
export function CreateTokenDialog(): React.JSX.Element {
const [isResultDialogOpen, setIsResultDialogOpen] = React.useState(false);
const [token, setToken] = React.useState("");
const [keyname, setKeyname] = React.useState(""); // Only for dialog display purposes
const { toast } = useToast();
const [isResultDialogOpen, setIsResultDialogOpen] = React.useState(false)
const [token, setToken] = React.useState("")
const [keyname, setKeyname] = React.useState("") // Only for dialog display purposes
const { toast } = useToast()
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
keyname: "Secret Key",
},
});
})
async function onSubmit(values: z.infer<typeof formSchema>): void {
const token: string = await generateToken(values.keyname);
setToken(token);
setKeyname(values.keyname);
setIsResultDialogOpen(true);
const token: string = await generateToken(values.keyname)
setToken(token)
setKeyname(values.keyname)
setIsResultDialogOpen(true)
}
async function copyToClipboard(data: string): Promise<void> {
try {
await navigator.clipboard.writeText(data);
await navigator.clipboard.writeText(data)
toast({
description: "API Key Copied to Clipboard!",
});
})
} catch (err) {
console.error("Failed to copy: ", err);
console.error("Failed to copy: ", err)
}
}
@ -112,9 +112,9 @@ export function CreateTokenDialog(): React.JSX.Element {
<DialogTitle>New API key created</DialogTitle>
</DialogHeader>
<DialogDescription>
Please save this secret key somewhere safe and accessible. For
security reasons, you won't be able to view it again. If you lose
this secret key, you'll need to generate a new one.
Please save this secret key somewhere safe and accessible. For security reasons, you
won't be able to view it again. If you lose this secret key, you'll need to generate a
new one.
</DialogDescription>
<div className="gap-4 py-4">
<div className="grid grid-rows-2 items-center">
@ -133,7 +133,7 @@ export function CreateTokenDialog(): React.JSX.Element {
size="icon"
className="float-right ml-5 "
onClick={async (event) => {
await copyToClipboard(token);
await copyToClipboard(token)
}}
>
<Icons.copy />
@ -145,8 +145,8 @@ export function CreateTokenDialog(): React.JSX.Element {
<DialogClose asChild>
<form
onSubmit={(event) => {
setIsResultDialogOpen(false);
event.preventDefault();
setIsResultDialogOpen(false)
event.preventDefault()
}}
>
<Button type="submit">Done</Button>
@ -156,5 +156,5 @@ export function CreateTokenDialog(): React.JSX.Element {
</DialogContent>
</Dialog>
</>
);
)
}

View file

@ -1,4 +1,4 @@
import { type cf_api_keys, PrismaClient } from "@prisma/client";
import { type cf_api_keys, PrismaClient } from "@prisma/client"
import {
Table,
TableBody,
@ -6,27 +6,27 @@ import {
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import ReactMarkdown from "react-markdown";
import { getSession } from "@auth0/nextjs-auth0";
import { redirect } from "next/navigation";
import { CreateTokenDialog } from "@/app/app/tokens/dialog-create-token";
import { DeleteTokenButton } from "@/app/app/tokens/delete-token-button";
import { Separator } from "@/components/ui/separator";
} from "@/components/ui/table"
import ReactMarkdown from "react-markdown"
import { getSession } from "@auth0/nextjs-auth0"
import { redirect } from "next/navigation"
import { CreateTokenDialog } from "@/app/app/tokens/dialog-create-token"
import { DeleteTokenButton } from "@/app/app/tokens/delete-token-button"
import { Separator } from "@/components/ui/separator"
const prisma = new PrismaClient();
const prisma = new PrismaClient()
export default async function APITokenGenerator(): Promise<JSX.Element> {
const user = await getSession();
const user = await getSession()
if (user == null) {
redirect("/login");
redirect("/login")
}
const userId = user.user.sub;
const userId = user.user.sub
const apiKeys: cf_api_keys[] = await prisma.cf_api_keys.findMany({
where: { user_id: userId },
});
})
// {/* TODO: Query Auth0 for user's organizations */}
const organizations = ["CodeFlash AI", "Another Organization"];
const organizations = ["CodeFlash AI", "Another Organization"]
return (
<div>
@ -35,8 +35,8 @@ export default async function APITokenGenerator(): Promise<JSX.Element> {
</h3>
<Separator />
<p className="leading-7 [&:not(:first-child)]:mt-6">
Your secret API keys are listed below. Please note that we do not
display your secret API keys again after you generate them.
Your secret API keys are listed below. Please note that we do not display your secret API
keys again after you generate them.
</p>
<Table className="mt-6">
<TableHeader>
@ -54,17 +54,11 @@ export default async function APITokenGenerator(): Promise<JSX.Element> {
<TableCell className="py-0">{key.name}</TableCell>
{/* Display the key using a Markdown component */}
<TableCell className="py-0">
<ReactMarkdown>{`\`\`\`cf-${"*".repeat(3)}${
key.suffix
}\`\`\``}</ReactMarkdown>
<ReactMarkdown>{`\`\`\`cf-${"*".repeat(3)}${key.suffix}\`\`\``}</ReactMarkdown>
</TableCell>
<TableCell className="py-0">{key.created_at.toLocaleString()}</TableCell>
<TableCell className="py-0">
{key.created_at.toLocaleString()}
</TableCell>
<TableCell className="py-0">
{key.last_used != null
? key.last_used.toLocaleString()
: "Never"}
{key.last_used != null ? key.last_used.toLocaleString() : "Never"}
</TableCell>
<TableCell className="py-0">
<DeleteTokenButton tokenId={key.id} />
@ -89,5 +83,5 @@ export default async function APITokenGenerator(): Promise<JSX.Element> {
{/* </Select> */}
{/* </div> */}
</div>
);
)
}

View file

@ -1,12 +1,12 @@
'use server'
import { getSession } from '@auth0/nextjs-auth0'
import { redirect } from 'next/navigation'
import { deleteAPIKeyById, genAndStoreAPITokenHash } from 'common/token-functions'
"use server"
import { getSession } from "@auth0/nextjs-auth0"
import { redirect } from "next/navigation"
import { deleteAPIKeyById, genAndStoreAPITokenHash } from "common/token-functions"
export async function generateToken (keyName: string): Promise<string> {
export async function generateToken(keyName: string): Promise<string> {
const user = await getSession()
if (user == null) {
redirect('/login')
redirect("/login")
}
const userId = user.user.sub
@ -14,10 +14,10 @@ export async function generateToken (keyName: string): Promise<string> {
return token
}
export async function deleteAPIKey (id: number): Promise<void> {
export async function deleteAPIKey(id: number): Promise<void> {
const user = await getSession()
if (user == null) {
redirect('/login')
redirect("/login")
return
}
const userId = user.user.sub

View file

@ -3,75 +3,75 @@
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
--radius: 0.5rem;
}
--radius: 0.5rem;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%;
}
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%;
}
}
@layer base {
* {
@apply border-border;
}
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
body {
@apply bg-background text-foreground;
}
}

View file

@ -1,49 +1,39 @@
import type { Metadata } from 'next'
import { Inter as FontSans } from 'next/font/google'
import './globals.css'
import { cn } from '@/lib/utils'
import { ThemeProvider } from '@/components/theme-provider'
import { UserProvider } from '@auth0/nextjs-auth0/client'
import { Toaster } from '@/components/ui/toaster'
import type { Metadata } from "next"
import { Inter as FontSans } from "next/font/google"
import "./globals.css"
import { cn } from "@/lib/utils"
import { ThemeProvider } from "@/components/theme-provider"
import { UserProvider } from "@auth0/nextjs-auth0/client"
import { Toaster } from "@/components/ui/toaster"
const fontSans = FontSans({
subsets: ['latin'],
variable: '--font-sans'
subsets: ["latin"],
variable: "--font-sans",
})
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app'
title: "Create Next App",
description: "Generated by create next app",
}
export default function RootLayout ({
children
}: {
children: React.ReactNode
}) {
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body className={cn(
'min-h-screen bg-background font-sans antialiased',
fontSans.variable
)}>
<html lang="en">
<body className={cn("min-h-screen bg-background font-sans antialiased", fontSans.variable)}>
<UserProvider>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
<div className="flex flex-col justify-between w-full h-full min-h-screen">
<main className="flex-auto w-full max-w-none mx-auto">
{children}
</main>
<Toaster />
</div>
</ThemeProvider>
</UserProvider>
</body>
</html>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
<div className="flex flex-col justify-between w-full h-full min-h-screen">
<main className="flex-auto w-full max-w-none mx-auto">{children}</main>
<Toaster />
</div>
</ThemeProvider>
</UserProvider>
</body>
</html>
)
}

View file

@ -1,9 +1,9 @@
"use client";
import Link from "next/link";
import { UserAuthForm } from "@/components/user-auth-form";
import { redirect } from "next/navigation";
import { Icons } from "@/components/icons";
import { useUser } from "@auth0/nextjs-auth0/client";
"use client"
import Link from "next/link"
import { UserAuthForm } from "@/components/user-auth-form"
import { redirect } from "next/navigation"
import { Icons } from "@/components/icons"
import { useUser } from "@auth0/nextjs-auth0/client"
// export const metadata: Metadata = {
// title: 'CodeFlash login',
@ -11,13 +11,13 @@ import { useUser } from "@auth0/nextjs-auth0/client";
// }
export default function AuthenticationPage(): JSX.Element {
const { user, error, isLoading } = useUser();
const { user, error, isLoading } = useUser()
if (isLoading) return <div>Loading...</div>;
if (error) return <div>{error.message}</div>;
if (isLoading) return <div>Loading...</div>
if (error) return <div>{error.message}</div>
if (user) {
console.log("user logged in!", user.name);
redirect("/app");
console.log("user logged in!", user.name)
redirect("/app")
} else {
return (
<>
@ -42,24 +42,16 @@ export default function AuthenticationPage(): JSX.Element {
<div className="lg:p-8">
<div className="mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px]">
<div className="flex flex-col space-y-2 text-center">
<h1 className="text-2xl font-semibold tracking-tight">
Start using CodeFlash
</h1>
<h1 className="text-2xl font-semibold tracking-tight">Start using CodeFlash</h1>
</div>
<UserAuthForm />
<p className="px-8 text-center text-sm text-muted-foreground">
By clicking continue, you agree to our{" "}
<Link
href="/terms"
className="underline underline-offset-4 hover:text-primary"
>
<Link href="/terms" className="underline underline-offset-4 hover:text-primary">
Terms of Service
</Link>{" "}
and{" "}
<Link
href="/privacy"
className="underline underline-offset-4 hover:text-primary"
>
<Link href="/privacy" className="underline underline-offset-4 hover:text-primary">
Privacy Policy
</Link>
.
@ -68,6 +60,6 @@ export default function AuthenticationPage(): JSX.Element {
</div>
</div>
</>
);
)
}
}

View file

@ -1,12 +1,12 @@
export default function LogoBox () {
export default function LogoBox() {
return (
<div className="block h-11">
<div className="grid grid-cols-2">
{/* <Icons.codeflash_logo/> */}
<h2 className="scroll-m-20 pb-2 text-3xl font-semibold tracking-tight first:mt-0 pl-8">
CodeFlash
</h2>
</div>
</div>
<div className="block h-11">
<div className="grid grid-cols-2">
{/* <Icons.codeflash_logo/> */}
<h2 className="scroll-m-20 pb-2 text-3xl font-semibold tracking-tight first:mt-0 pl-8">
CodeFlash
</h2>
</div>
</div>
)
}

View file

@ -1,15 +1,15 @@
"use client";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import LogoBox from "@/components/dashboard/logo-box";
import { SignOut } from "@/components/ui/SignOut";
import Link from "next/link";
import { usePathname } from "next/navigation";
import React from "react";
import { KeyRound } from "lucide-react";
"use client"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import LogoBox from "@/components/dashboard/logo-box"
import { SignOut } from "@/components/ui/SignOut"
import Link from "next/link"
import { usePathname } from "next/navigation"
import React from "react"
import { KeyRound } from "lucide-react"
export function Sidebar({ className }: any) {
const currentRoute = usePathname();
const currentRoute = usePathname()
return (
<div className={cn("flex flex-col h-screen pt-3 pb-6 max-w-xs", className)}>
<LogoBox />
@ -18,9 +18,7 @@ export function Sidebar({ className }: any) {
<div className="space-y-2 grid gap-y-1">
<Link href="/app/install">
<Button
variant={
currentRoute === "/app/install" ? "secondary" : "ghost"
}
variant={currentRoute === "/app/install" ? "secondary" : "ghost"}
className="w-full justify-start"
>
<svg
@ -55,5 +53,5 @@ export function Sidebar({ className }: any) {
<SignOut className="w-28 items-center mx-auto" />
</div>
</div>
);
)
}

View file

@ -2,44 +2,58 @@ type IconProps = React.HTMLAttributes<SVGElement>
export const Icons = {
github: (props: IconProps) => (
<svg viewBox="0 0 438.549 438.549" {...props}>
<path
fill="currentColor"
d="M409.132 114.573c-19.608-33.596-46.205-60.194-79.798-79.8-33.598-19.607-70.277-29.408-110.063-29.408-39.781 0-76.472 9.804-110.063 29.408-33.596 19.605-60.192 46.204-79.8 79.8C9.803 148.168 0 184.854 0 224.63c0 47.78 13.94 90.745 41.827 128.906 27.884 38.164 63.906 64.572 108.063 79.227 5.14.954 8.945.283 11.419-1.996 2.475-2.282 3.711-5.14 3.711-8.562 0-.571-.049-5.708-.144-15.417a2549.81 2549.81 0 01-.144-25.406l-6.567 1.136c-4.187.767-9.469 1.092-15.846 1-6.374-.089-12.991-.757-19.842-1.999-6.854-1.231-13.229-4.086-19.13-8.559-5.898-4.473-10.085-10.328-12.56-17.556l-2.855-6.57c-1.903-4.374-4.899-9.233-8.992-14.559-4.093-5.331-8.232-8.945-12.419-10.848l-1.999-1.431c-1.332-.951-2.568-2.098-3.711-3.429-1.142-1.331-1.997-2.663-2.568-3.997-.572-1.335-.098-2.43 1.427-3.289 1.525-.859 4.281-1.276 8.28-1.276l5.708.853c3.807.763 8.516 3.042 14.133 6.851 5.614 3.806 10.229 8.754 13.846 14.842 4.38 7.806 9.657 13.754 15.846 17.847 6.184 4.093 12.419 6.136 18.699 6.136 6.28 0 11.704-.476 16.274-1.423 4.565-.952 8.848-2.383 12.847-4.285 1.713-12.758 6.377-22.559 13.988-29.41-10.848-1.14-20.601-2.857-29.264-5.14-8.658-2.286-17.605-5.996-26.835-11.14-9.235-5.137-16.896-11.516-22.985-19.126-6.09-7.614-11.088-17.61-14.987-29.979-3.901-12.374-5.852-26.648-5.852-42.826 0-23.035 7.52-42.637 22.557-58.817-7.044-17.318-6.379-36.732 1.997-58.24 5.52-1.715 13.706-.428 24.554 3.853 10.85 4.283 18.794 7.952 23.84 10.994 5.046 3.041 9.089 5.618 12.135 7.708 17.705-4.947 35.976-7.421 54.818-7.421s37.117 2.474 54.823 7.421l10.849-6.849c7.419-4.57 16.18-8.758 26.262-12.565 10.088-3.805 17.802-4.853 23.134-3.138 8.562 21.509 9.325 40.922 2.279 58.24 15.036 16.18 22.559 35.787 22.559 58.817 0 16.178-1.958 30.497-5.853 42.966-3.9 12.471-8.941 22.457-15.125 29.979-6.191 7.521-13.901 13.85-23.131 18.986-9.232 5.14-18.182 8.85-26.84 11.136-8.662 2.286-18.415 4.004-29.263 5.146 9.894 8.562 14.842 22.077 14.842 40.539v60.237c0 3.422 1.19 6.279 3.572 8.562 2.379 2.279 6.136 2.95 11.276 1.995 44.163-14.653 80.185-41.062 108.068-79.226 27.88-38.161 41.825-81.126 41.825-128.906-.01-39.771-9.818-76.454-29.414-110.049z"
></path>
</svg>
<svg viewBox="0 0 438.549 438.549" {...props}>
<path
fill="currentColor"
d="M409.132 114.573c-19.608-33.596-46.205-60.194-79.798-79.8-33.598-19.607-70.277-29.408-110.063-29.408-39.781 0-76.472 9.804-110.063 29.408-33.596 19.605-60.192 46.204-79.8 79.8C9.803 148.168 0 184.854 0 224.63c0 47.78 13.94 90.745 41.827 128.906 27.884 38.164 63.906 64.572 108.063 79.227 5.14.954 8.945.283 11.419-1.996 2.475-2.282 3.711-5.14 3.711-8.562 0-.571-.049-5.708-.144-15.417a2549.81 2549.81 0 01-.144-25.406l-6.567 1.136c-4.187.767-9.469 1.092-15.846 1-6.374-.089-12.991-.757-19.842-1.999-6.854-1.231-13.229-4.086-19.13-8.559-5.898-4.473-10.085-10.328-12.56-17.556l-2.855-6.57c-1.903-4.374-4.899-9.233-8.992-14.559-4.093-5.331-8.232-8.945-12.419-10.848l-1.999-1.431c-1.332-.951-2.568-2.098-3.711-3.429-1.142-1.331-1.997-2.663-2.568-3.997-.572-1.335-.098-2.43 1.427-3.289 1.525-.859 4.281-1.276 8.28-1.276l5.708.853c3.807.763 8.516 3.042 14.133 6.851 5.614 3.806 10.229 8.754 13.846 14.842 4.38 7.806 9.657 13.754 15.846 17.847 6.184 4.093 12.419 6.136 18.699 6.136 6.28 0 11.704-.476 16.274-1.423 4.565-.952 8.848-2.383 12.847-4.285 1.713-12.758 6.377-22.559 13.988-29.41-10.848-1.14-20.601-2.857-29.264-5.14-8.658-2.286-17.605-5.996-26.835-11.14-9.235-5.137-16.896-11.516-22.985-19.126-6.09-7.614-11.088-17.61-14.987-29.979-3.901-12.374-5.852-26.648-5.852-42.826 0-23.035 7.52-42.637 22.557-58.817-7.044-17.318-6.379-36.732 1.997-58.24 5.52-1.715 13.706-.428 24.554 3.853 10.85 4.283 18.794 7.952 23.84 10.994 5.046 3.041 9.089 5.618 12.135 7.708 17.705-4.947 35.976-7.421 54.818-7.421s37.117 2.474 54.823 7.421l10.849-6.849c7.419-4.57 16.18-8.758 26.262-12.565 10.088-3.805 17.802-4.853 23.134-3.138 8.562 21.509 9.325 40.922 2.279 58.24 15.036 16.18 22.559 35.787 22.559 58.817 0 16.178-1.958 30.497-5.853 42.966-3.9 12.471-8.941 22.457-15.125 29.979-6.191 7.521-13.901 13.85-23.131 18.986-9.232 5.14-18.182 8.85-26.84 11.136-8.662 2.286-18.415 4.004-29.263 5.146 9.894 8.562 14.842 22.077 14.842 40.539v60.237c0 3.422 1.19 6.279 3.572 8.562 2.379 2.279 6.136 2.95 11.276 1.995 44.163-14.653 80.185-41.062 108.068-79.226 27.88-38.161 41.825-81.126 41.825-128.906-.01-39.771-9.818-76.454-29.414-110.049z"
></path>
</svg>
),
spinner: (props: IconProps) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<path d="M21 12a9 9 0 1 1-6.219-8.56"/>
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
</svg>
),
codeflash_logo: (props: IconProps) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="mr-2 h-6 w-6"
>
<path d="M15 6v12a3 3 0 1 0 3-3H6a3 3 0 1 0 3 3V6a3 3 0 1 0-3 3h12a3 3 0 1 0-3-3"/>
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="mr-2 h-6 w-6"
>
<path d="M15 6v12a3 3 0 1 0 3-3H6a3 3 0 1 0 3 3V6a3 3 0 1 0-3 3h12a3 3 0 1 0-3-3" />
</svg>
),
copy: (props: IconProps) => (
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="lucide lucide-copy"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>
)
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="lucide lucide-copy"
>
<rect width="14" height="14" x="8" y="8" rx="2" ry="2" />
<path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" />
</svg>
),
}

View file

@ -1,16 +1,14 @@
"use client"
import * as React from "react"
import {useEffect, useState} from "react"
import {ThemeProvider as NextThemesProvider} from "next-themes"
import {type ThemeProviderProps} from "next-themes/dist/types"
import { useEffect, useState } from "react"
import { ThemeProvider as NextThemesProvider } from "next-themes"
import { type ThemeProviderProps } from "next-themes/dist/types"
export function ThemeProvider({children, ...props}: ThemeProviderProps) {
const [isClient, setIsClient] = useState(false);
useEffect(() => {
setIsClient(true);
}, []);
return (isClient ? <NextThemesProvider {...props}>{children}</NextThemesProvider>
: <>{children}</>
)
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
const [isClient, setIsClient] = useState(false)
useEffect(() => {
setIsClient(true)
}, [])
return isClient ? <NextThemesProvider {...props}>{children}</NextThemesProvider> : <>{children}</>
}

View file

@ -1,18 +1,18 @@
'use client'
import { Button } from '@/components/ui/button'
import { Icons } from '@/components/icons'
import * as React from 'react'
"use client"
import { Button } from "@/components/ui/button"
import { Icons } from "@/components/icons"
import * as React from "react"
export function SignIn ({
export function SignIn({
provider,
...props
}: { provider?: string } & React.ComponentPropsWithRef<typeof Button>) {
return (
<a href="/api/auth/login">
<Button {...props}>
<Icons.github className="mr-2 h-4 w-4"/>
Sign In with GitHub
</Button>
</a>
<a href="/api/auth/login">
<Button {...props}>
<Icons.github className="mr-2 h-4 w-4" />
Sign In with GitHub
</Button>
</a>
)
}

View file

@ -1,13 +1,13 @@
'use client'
import { Button } from '@/components/ui/button'
import { cn } from '@/lib/utils'
"use client"
import { Button } from "@/components/ui/button"
import { cn } from "@/lib/utils"
export function SignOut ({ props, className }) {
export function SignOut({ props, className }) {
return (
<a href="/api/auth/logout">
<Button className={cn('', className)} {...props}>
Sign Out
</Button>
</a>
<a href="/api/auth/logout">
<Button className={cn("", className)} {...props}>
Sign Out
</Button>
</a>
)
}

View file

@ -1,56 +1,49 @@
import * as React from "react"
import {Slot} from "@radix-ui/react-slot"
import {cva, type VariantProps} from "class-variance-authority"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import {cn} from "@/lib/utils"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({className, variant, size, asChild = false, ...props}, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({variant, size, className}))}
ref={ref}
{...props}
/>
)
}
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />
)
},
)
Button.displayName = "Button"
export {Button, buttonVariants}
export { Button, buttonVariants }

View file

@ -1,79 +1,56 @@
import * as React from "react"
import {cn} from "@/lib/utils"
import { cn } from "@/lib/utils"
const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({className, ...props}, ref) => (
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
"rounded-lg border bg-card text-card-foreground shadow-sm",
className
)}
{...props}
ref={ref}
className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)}
{...props}
/>
))
),
)
Card.displayName = "Card"
const CardHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({className, ...props}, ref) => (
<div
ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
))
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
),
)
CardHeader.displayName = "CardHeader"
const CardTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({className, ...props}, ref) => (
const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
({ className, ...props }, ref) => (
<h3
ref={ref}
className={cn(
"text-2xl font-semibold leading-none tracking-tight",
className
)}
{...props}
ref={ref}
className={cn("text-2xl font-semibold leading-none tracking-tight", className)}
{...props}
/>
))
),
)
CardTitle.displayName = "CardTitle"
const CardDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({className, ...props}, ref) => (
<p
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<p ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
))
CardDescription.displayName = "CardDescription"
const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({className, ...props}, ref) => (
const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
))
),
)
CardContent.displayName = "CardContent"
const CardFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({className, ...props}, ref) => (
<div
ref={ref}
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
))
const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
),
)
CardFooter.displayName = "CardFooter"
export {Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent}
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }

View file

@ -22,7 +22,7 @@ const DialogOverlay = React.forwardRef<
ref={ref}
className={cn(
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
className,
)}
{...props}
/>
@ -39,7 +39,7 @@ const DialogContent = React.forwardRef<
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className
className,
)}
{...props}
>
@ -53,29 +53,14 @@ const DialogContent = React.forwardRef<
))
DialogContent.displayName = DialogPrimitive.Content.displayName
const DialogHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-1.5 text-center sm:text-left",
className
)}
{...props}
/>
const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div className={cn("flex flex-col space-y-1.5 text-center sm:text-left", className)} {...props} />
)
DialogHeader.displayName = "DialogHeader"
const DialogFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
)}
className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
{...props}
/>
)
@ -87,10 +72,7 @@ const DialogTitle = React.forwardRef<
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={cn(
"text-lg font-semibold leading-none tracking-tight",
className
)}
className={cn("text-lg font-semibold leading-none tracking-tight", className)}
{...props}
/>
))

View file

@ -17,18 +17,16 @@ const Form = FormProvider
type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
name: TName
}
const FormFieldContext = React.createContext<FormFieldContextValue>(
{} as FormFieldContextValue
)
const FormFieldContext = React.createContext<FormFieldContextValue>({} as FormFieldContextValue)
const FormField = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({
...props
}: ControllerProps<TFieldValues, TName>) => {
@ -66,22 +64,19 @@ type FormItemContextValue = {
id: string
}
const FormItemContext = React.createContext<FormItemContextValue>(
{} as FormItemContextValue
const FormItemContext = React.createContext<FormItemContextValue>({} as FormItemContextValue)
const FormItem = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => {
const id = React.useId()
return (
<FormItemContext.Provider value={{ id }}>
<div ref={ref} className={cn("space-y-2", className)} {...props} />
</FormItemContext.Provider>
)
},
)
const FormItem = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
const id = React.useId()
return (
<FormItemContext.Provider value={{ id }}>
<div ref={ref} className={cn("space-y-2", className)} {...props} />
</FormItemContext.Provider>
)
})
FormItem.displayName = "FormItem"
const FormLabel = React.forwardRef<
@ -111,11 +106,7 @@ const FormControl = React.forwardRef<
<Slot
ref={ref}
id={formItemId}
aria-describedby={
!error
? `${formDescriptionId}`
: `${formDescriptionId} ${formMessageId}`
}
aria-describedby={!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`}
aria-invalid={!!error}
{...props}
/>

View file

@ -1,26 +1,24 @@
import * as React from "react"
import {cn} from "@/lib/utils"
import { cn } from "@/lib/utils"
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {
}
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({className, type, ...props}, ref) => {
return (
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
}
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className,
)}
ref={ref}
{...props}
/>
)
},
)
Input.displayName = "Input"
export {Input}
export { Input }

View file

@ -2,25 +2,20 @@
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import {cva, type VariantProps} from "class-variance-authority"
import { cva, type VariantProps } from "class-variance-authority"
import {cn} from "@/lib/utils"
import { cn } from "@/lib/utils"
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
)
const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants>
>(({className, ...props}, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />
))
Label.displayName = LabelPrimitive.Root.displayName
export {Label}
export { Label }

View file

@ -1,129 +1,121 @@
"use client"
import * as React from "react"
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
import {cva} from "class-variance-authority"
import {ChevronDown} from "lucide-react"
import { cva } from "class-variance-authority"
import { ChevronDown } from "lucide-react"
import {cn} from "@/lib/utils"
import { cn } from "@/lib/utils"
const NavigationMenu = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root>
>(({className, children, ...props}, ref) => (
<NavigationMenuPrimitive.Root
ref={ref}
className={cn(
"relative z-10 flex max-w-max flex-1 items-center justify-center",
className
)}
{...props}
>
{children}
<NavigationMenuViewport/>
</NavigationMenuPrimitive.Root>
React.ElementRef<typeof NavigationMenuPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root>
>(({ className, children, ...props }, ref) => (
<NavigationMenuPrimitive.Root
ref={ref}
className={cn("relative z-10 flex max-w-max flex-1 items-center justify-center", className)}
{...props}
>
{children}
<NavigationMenuViewport />
</NavigationMenuPrimitive.Root>
))
NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName
const NavigationMenuList = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.List>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List>
>(({className, ...props}, ref) => (
<NavigationMenuPrimitive.List
ref={ref}
className={cn(
"group flex flex-1 list-none items-center justify-center space-x-1",
className
)}
{...props}
/>
React.ElementRef<typeof NavigationMenuPrimitive.List>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List>
>(({ className, ...props }, ref) => (
<NavigationMenuPrimitive.List
ref={ref}
className={cn("group flex flex-1 list-none items-center justify-center space-x-1", className)}
{...props}
/>
))
NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName
const NavigationMenuItem = NavigationMenuPrimitive.Item
const navigationMenuTriggerStyle = cva(
"group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50"
"group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50",
)
const NavigationMenuTrigger = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger>
>(({className, children, ...props}, ref) => (
<NavigationMenuPrimitive.Trigger
ref={ref}
className={cn(navigationMenuTriggerStyle(), "group", className)}
{...props}
>
{children}{" "}
<ChevronDown
className="relative top-[1px] ml-1 h-3 w-3 transition duration-200 group-data-[state=open]:rotate-180"
aria-hidden="true"
/>
</NavigationMenuPrimitive.Trigger>
React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<NavigationMenuPrimitive.Trigger
ref={ref}
className={cn(navigationMenuTriggerStyle(), "group", className)}
{...props}
>
{children}{" "}
<ChevronDown
className="relative top-[1px] ml-1 h-3 w-3 transition duration-200 group-data-[state=open]:rotate-180"
aria-hidden="true"
/>
</NavigationMenuPrimitive.Trigger>
))
NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName
const NavigationMenuContent = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>
>(({className, ...props}, ref) => (
<NavigationMenuPrimitive.Content
ref={ref}
className={cn(
"left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto ",
className
)}
{...props}
/>
React.ElementRef<typeof NavigationMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>
>(({ className, ...props }, ref) => (
<NavigationMenuPrimitive.Content
ref={ref}
className={cn(
"left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto ",
className,
)}
{...props}
/>
))
NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName
const NavigationMenuLink = NavigationMenuPrimitive.Link
const NavigationMenuViewport = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>
>(({className, ...props}, ref) => (
<div className={cn("absolute left-0 top-full flex justify-center")}>
<NavigationMenuPrimitive.Viewport
className={cn(
"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",
className
)}
ref={ref}
{...props}
/>
</div>
React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>
>(({ className, ...props }, ref) => (
<div className={cn("absolute left-0 top-full flex justify-center")}>
<NavigationMenuPrimitive.Viewport
className={cn(
"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",
className,
)}
ref={ref}
{...props}
/>
</div>
))
NavigationMenuViewport.displayName =
NavigationMenuPrimitive.Viewport.displayName
NavigationMenuViewport.displayName = NavigationMenuPrimitive.Viewport.displayName
const NavigationMenuIndicator = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Indicator>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator>
>(({className, ...props}, ref) => (
<NavigationMenuPrimitive.Indicator
ref={ref}
className={cn(
"top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
className
)}
{...props}
>
<div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md"/>
</NavigationMenuPrimitive.Indicator>
React.ElementRef<typeof NavigationMenuPrimitive.Indicator>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator>
>(({ className, ...props }, ref) => (
<NavigationMenuPrimitive.Indicator
ref={ref}
className={cn(
"top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
className,
)}
{...props}
>
<div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
</NavigationMenuPrimitive.Indicator>
))
NavigationMenuIndicator.displayName =
NavigationMenuPrimitive.Indicator.displayName
NavigationMenuIndicator.displayName = NavigationMenuPrimitive.Indicator.displayName
export {
navigationMenuTriggerStyle,
NavigationMenu,
NavigationMenuList,
NavigationMenuItem,
NavigationMenuContent,
NavigationMenuTrigger,
NavigationMenuLink,
NavigationMenuIndicator,
NavigationMenuViewport,
navigationMenuTriggerStyle,
NavigationMenu,
NavigationMenuList,
NavigationMenuItem,
NavigationMenuContent,
NavigationMenuTrigger,
NavigationMenuLink,
NavigationMenuIndicator,
NavigationMenuViewport,
}

View file

@ -1,10 +1,10 @@
'use client'
"use client"
import * as React from 'react'
import * as SelectPrimitive from '@radix-ui/react-select'
import {Check, ChevronDown, ChevronUp} from 'lucide-react'
import * as React from "react"
import * as SelectPrimitive from "@radix-ui/react-select"
import { Check, ChevronDown, ChevronUp } from "lucide-react"
import {cn} from '@/lib/utils'
import { cn } from "@/lib/utils"
const Select = SelectPrimitive.Root
@ -13,136 +13,129 @@ const SelectGroup = SelectPrimitive.Group
const SelectValue = SelectPrimitive.Value
const SelectTrigger = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
>(({className, children, ...props}, ref) => (
<SelectPrimitive.Trigger
ref={ref}
className={cn(
'flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
className
)}
{...props}
>
{children}
<SelectPrimitive.Icon asChild>
<ChevronDown className="h-4 w-4 opacity-50"/>
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Trigger
ref={ref}
className={cn(
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
className,
)}
{...props}
>
{children}
<SelectPrimitive.Icon asChild>
<ChevronDown className="h-4 w-4 opacity-50" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
))
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
const SelectScrollUpButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
>(({className, ...props}, ref) => (
<SelectPrimitive.ScrollUpButton
ref={ref}
className={cn(
'flex cursor-default items-center justify-center py-1',
className
)}
{...props}
>
<ChevronUp className="h-4 w-4"/>
</SelectPrimitive.ScrollUpButton>
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollUpButton
ref={ref}
className={cn("flex cursor-default items-center justify-center py-1", className)}
{...props}
>
<ChevronUp className="h-4 w-4" />
</SelectPrimitive.ScrollUpButton>
))
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
const SelectScrollDownButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
>(({className, ...props}, ref) => (
<SelectPrimitive.ScrollDownButton
ref={ref}
className={cn(
'flex cursor-default items-center justify-center py-1',
className
)}
{...props}
>
<ChevronDown className="h-4 w-4"/>
</SelectPrimitive.ScrollDownButton>
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollDownButton
ref={ref}
className={cn("flex cursor-default items-center justify-center py-1", className)}
{...props}
>
<ChevronDown className="h-4 w-4" />
</SelectPrimitive.ScrollDownButton>
))
SelectScrollDownButton.displayName =
SelectPrimitive.ScrollDownButton.displayName
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName
const SelectContent = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({className, children, position = 'popper', ...props}, ref) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
ref={ref}
className={cn(
'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
position === 'popper' &&
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
className
)}
position={position}
{...props}
React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, position = "popper", ...props }, ref) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
ref={ref}
className={cn(
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className,
)}
position={position}
{...props}
>
<SelectScrollUpButton />
<SelectPrimitive.Viewport
className={cn(
"p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
)}
>
<SelectScrollUpButton/>
<SelectPrimitive.Viewport
className={cn(
'p-1',
position === 'popper' &&
'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]'
)}
>
{children}
</SelectPrimitive.Viewport>
<SelectScrollDownButton/>
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
{children}
</SelectPrimitive.Viewport>
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
))
SelectContent.displayName = SelectPrimitive.Content.displayName
const SelectLabel = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
>(({className, ...props}, ref) => (
<SelectPrimitive.Label
ref={ref}
className={cn('py-1.5 pl-8 pr-2 text-sm font-semibold', className)}
{...props}
/>
React.ElementRef<typeof SelectPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Label
ref={ref}
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
{...props}
/>
))
SelectLabel.displayName = SelectPrimitive.Label.displayName
const SelectItem = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
>(({className, children, ...props}, ref) => (
<SelectPrimitive.Item
ref={ref}
className={cn(
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
className
)}
{...props}
>
React.ElementRef<typeof SelectPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Item
ref={ref}
className={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className,
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check className="h-4 w-4"/>
<Check className="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
))
SelectItem.displayName = SelectPrimitive.Item.displayName
const SelectSeparator = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
>(({className, ...props}, ref) => (
<SelectPrimitive.Separator
ref={ref}
className={cn('-mx-1 my-1 h-px bg-muted', className)}
{...props}
/>
React.ElementRef<typeof SelectPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props}
/>
))
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
@ -156,5 +149,5 @@ export {
SelectItem,
SelectSeparator,
SelectScrollUpButton,
SelectScrollDownButton
SelectScrollDownButton,
}

View file

@ -8,24 +8,19 @@ import { cn } from "@/lib/utils"
const Separator = React.forwardRef<
React.ElementRef<typeof SeparatorPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
>(
(
{ className, orientation = "horizontal", decorative = true, ...props },
ref
) => (
<SeparatorPrimitive.Root
ref={ref}
decorative={decorative}
orientation={orientation}
className={cn(
"shrink-0 bg-border",
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
className
)}
{...props}
/>
)
)
>(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => (
<SeparatorPrimitive.Root
ref={ref}
decorative={decorative}
orientation={orientation}
className={cn(
"shrink-0 bg-border",
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
className,
)}
{...props}
/>
))
Separator.displayName = SeparatorPrimitive.Root.displayName
export { Separator }

View file

@ -1,117 +1,91 @@
import * as React from 'react'
import * as React from "react"
import {cn} from '@/lib/utils'
import { cn } from "@/lib/utils"
const Table = React.forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({className, ...props}, ref) => (
const Table = React.forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>(
({ className, ...props }, ref) => (
<div className="relative w-full overflow-auto">
<table
ref={ref}
className={cn('w-full caption-bottom text-sm', className)}
{...props}
/>
<table ref={ref} className={cn("w-full caption-bottom text-sm", className)} {...props} />
</div>
))
Table.displayName = 'Table'
),
)
Table.displayName = "Table"
const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({className, ...props}, ref) => (
<thead ref={ref} className={cn('[&_tr]:border-b', className)} {...props} />
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
))
TableHeader.displayName = 'TableHeader'
TableHeader.displayName = "TableHeader"
const TableBody = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({className, ...props}, ref) => (
<tbody
ref={ref}
className={cn('[&_tr:last-child]:border-0', className)}
{...props}
/>
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody ref={ref} className={cn("[&_tr:last-child]:border-0", className)} {...props} />
))
TableBody.displayName = 'TableBody'
TableBody.displayName = "TableBody"
const TableFooter = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({className, ...props}, ref) => (
<tfoot
ref={ref}
className={cn(
'border-t bg-muted/50 font-medium [&>tr]:last:border-b-0',
className
)}
{...props}
/>
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className)}
{...props}
/>
))
TableFooter.displayName = 'TableFooter'
TableFooter.displayName = "TableFooter"
const TableRow = React.forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement>
>(({className, ...props}, ref) => (
const TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>(
({ className, ...props }, ref) => (
<tr
ref={ref}
className={cn(
'border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted',
className
)}
{...props}
ref={ref}
className={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
className,
)}
{...props}
/>
))
TableRow.displayName = 'TableRow'
),
)
TableRow.displayName = "TableRow"
const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({className, ...props}, ref) => (
<th
ref={ref}
className={cn(
'h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0',
className
)}
{...props}
/>
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th
ref={ref}
className={cn(
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
className,
)}
{...props}
/>
))
TableHead.displayName = 'TableHead'
TableHead.displayName = "TableHead"
const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({className, ...props}, ref) => (
<td
ref={ref}
className={cn('p-4 align-middle [&:has([role=checkbox])]:pr-0', className)}
{...props}
/>
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td
ref={ref}
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
{...props}
/>
))
TableCell.displayName = 'TableCell'
TableCell.displayName = "TableCell"
const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({className, ...props}, ref) => (
<caption
ref={ref}
className={cn('mt-4 text-sm text-muted-foreground', className)}
{...props}
/>
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption ref={ref} className={cn("mt-4 text-sm text-muted-foreground", className)} {...props} />
))
TableCaption.displayName = 'TableCaption'
TableCaption.displayName = "TableCaption"
export {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption
}
export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption }

View file

@ -15,7 +15,7 @@ const ToastViewport = React.forwardRef<
ref={ref}
className={cn(
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
className
className,
)}
{...props}
/>
@ -35,13 +35,12 @@ const toastVariants = cva(
defaultVariants: {
variant: "default",
},
}
},
)
const Toast = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
VariantProps<typeof toastVariants>
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> & VariantProps<typeof toastVariants>
>(({ className, variant, ...props }, ref) => {
return (
<ToastPrimitives.Root
@ -61,7 +60,7 @@ const ToastAction = React.forwardRef<
ref={ref}
className={cn(
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
className
className,
)}
{...props}
/>
@ -76,7 +75,7 @@ const ToastClose = React.forwardRef<
ref={ref}
className={cn(
"absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
className
className,
)}
toast-close=""
{...props}
@ -90,11 +89,7 @@ const ToastTitle = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Title>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Title
ref={ref}
className={cn("text-sm font-semibold", className)}
{...props}
/>
<ToastPrimitives.Title ref={ref} className={cn("text-sm font-semibold", className)} {...props} />
))
ToastTitle.displayName = ToastPrimitives.Title.displayName

View file

@ -20,9 +20,7 @@ export function Toaster() {
<Toast key={id} {...props}>
<div className="grid gap-1">
{title && <ToastTitle>{title}</ToastTitle>}
{description && (
<ToastDescription>{description}</ToastDescription>
)}
{description && <ToastDescription>{description}</ToastDescription>}
</div>
{action}
<ToastClose />

View file

@ -1,10 +1,7 @@
// Inspired by react-hot-toast library
import * as React from "react"
import type {
ToastActionElement,
ToastProps,
} from "@/components/ui/toast"
import type { ToastActionElement, ToastProps } from "@/components/ui/toast"
const TOAST_LIMIT = 1
const TOAST_REMOVE_DELAY = 1000000
@ -83,9 +80,7 @@ export const reducer = (state: State, action: Action): State => {
case "UPDATE_TOAST":
return {
...state,
toasts: state.toasts.map((t) =>
t.id === action.toast.id ? { ...t, ...action.toast } : t
),
toasts: state.toasts.map((t) => (t.id === action.toast.id ? { ...t, ...action.toast } : t)),
}
case "DISMISS_TOAST": {
@ -109,7 +104,7 @@ export const reducer = (state: State, action: Action): State => {
...t,
open: false,
}
: t
: t,
),
}
}

View file

@ -1,7 +1,7 @@
import * as React from "react";
import * as React from "react"
import { cn } from "@/lib/utils";
import { SignIn } from "@/components/ui/SignIn";
import { cn } from "@/lib/utils"
import { SignIn } from "@/components/ui/SignIn"
interface UserAuthFormProps extends React.HTMLAttributes<HTMLDivElement> {}
@ -10,12 +10,10 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
<div className={cn("grid gap-6", className)} {...props}>
<div className="relative">
<div className="relative flex justify-center text-xs uppercase">
<span className="bg-background px-2 text-muted-foreground">
Sign In or sign up
</span>
<span className="bg-background px-2 text-muted-foreground">Sign In or sign up</span>
</div>
</div>
<SignIn className="w-full" />
</div>
);
)
}

View file

@ -1,11 +1,11 @@
import { PrismaClient } from '@prisma/client'
import { PrismaClient } from "@prisma/client"
// Docs about instantiating `PrismaClient` with Next.js:
// https://pris.ly/d/help/next-js-best-practices
let prisma: PrismaClient
if (process.env.NODE_ENV === 'production') {
if (process.env.NODE_ENV === "production") {
prisma = new PrismaClient()
} else {
if (!global.prisma) {

View file

@ -1,6 +1,6 @@
import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn (...inputs: ClassValue[]) {
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}

View file

@ -1,10 +1,10 @@
// TODO: Could not make this work, this is preferred way. See if you can make it work.
import { withMiddlewareAuthRequired } from '@auth0/nextjs-auth0/edge'
import { withMiddlewareAuthRequired } from "@auth0/nextjs-auth0/edge"
export default withMiddlewareAuthRequired({
returnTo: '/login'
returnTo: "/login",
})
export const config = {
matcher: '/app/:path*'
matcher: "/app/:path*",
}

View file

@ -1,76 +1,76 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ["class"],
content: [
'./pages/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',
'./app/**/*.{ts,tsx}',
'./src/**/*.{ts,tsx}',
],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
keyframes: {
"accordion-down": {
from: {height: 0},
to: {height: "var(--radix-accordion-content-height)"},
},
"accordion-up": {
from: {height: "var(--radix-accordion-content-height)"},
to: {height: 0},
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
},
darkMode: ["class"],
content: [
"./pages/**/*.{ts,tsx}",
"./components/**/*.{ts,tsx}",
"./app/**/*.{ts,tsx}",
"./src/**/*.{ts,tsx}",
],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
},
plugins: [require("tailwindcss-animate")],
}
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
keyframes: {
"accordion-down": {
from: { height: 0 },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: 0 },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
},
},
plugins: [require("tailwindcss-animate")],
}

View file

@ -1,24 +1,23 @@
import type {Config} from 'tailwindcss'
import {fontFamily} from "tailwindcss/defaultTheme"
import type { Config } from "tailwindcss"
import { fontFamily } from "tailwindcss/defaultTheme"
const config: Config = {
content: [
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
backgroundImage: {
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
'gradient-conic':
'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
},
fontFamily: {
sans: ["var(--font-sans)", ...fontFamily.sans],
}
},
content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic": "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
},
fontFamily: {
sans: ["var(--font-sans)", ...fontFamily.sans],
},
},
plugins: [],
},
plugins: [],
}
export default config

View file

@ -1 +1,4 @@
{}
printWidth: 100
semi: false
trailingComma: all
bracketSameLine: false

View file

@ -1,30 +1,29 @@
import {DefaultAzureCredential} from "@azure/identity";
import {KeyVaultSecret, SecretClient} from "@azure/keyvault-secrets";
import { DefaultAzureCredential } from "@azure/identity"
import { KeyVaultSecret, SecretClient } from "@azure/keyvault-secrets"
const keyVaultName: string = process.env["KEY_VAULT_NAME"];
if (!keyVaultName) throw new Error("KEY_VAULT_NAME is empty");
const kvUri: string = `https://${keyVaultName}.vault.azure.net`;
const credential: DefaultAzureCredential = new DefaultAzureCredential();
const client: SecretClient = new SecretClient(kvUri, credential);
const keyVaultName: string = process.env["KEY_VAULT_NAME"]
if (!keyVaultName) throw new Error("KEY_VAULT_NAME is empty")
const kvUri: string = `https://${keyVaultName}.vault.azure.net`
const credential: DefaultAzureCredential = new DefaultAzureCredential()
const client: SecretClient = new SecretClient(kvUri, credential)
async function getSecret(secretName: string): Promise<string> {
const secret: KeyVaultSecret = await client.getSecret(secretName);
return secret.value;
const secret: KeyVaultSecret = await client.getSecret(secretName)
return secret.value
}
export async function getGithubAppPrivateKey(): Promise<string> {
return await getSecret("github-app-private-key-0");
return await getSecret("github-app-private-key-0")
}
export async function getGithubAppWebhookSecret(): Promise<string> {
return await getSecret("github-app-webhook-secret-1");
return await getSecret("github-app-webhook-secret-1")
}
export async function getCFAPIJWTPrivateKey(): Promise<string> {
return await getSecret("cf-api-jwt-private-key-0");
return await getSecret("cf-api-jwt-private-key-0")
}
export async function getCFAPIJWTPublicKey(): Promise<string> {
return await getSecret("cf-api-jwt-public-key-0");
return await getSecret("cf-api-jwt-public-key-0")
}

View file

@ -5,14 +5,12 @@
// import { PrismaClient, type cf_api_keys } from 'common'
//
// TODO reenable this once
// https://github.com/vercel/next.js/issues/46070
// is resolved.
// export * from '@prisma/client';
import {PrismaClient} from '@prisma/client';
import { PrismaClient } from "@prisma/client"
export const prisma = new PrismaClient();
export const prisma = new PrismaClient()
prisma.cf_api_keys

View file

@ -1,33 +1,34 @@
import * as jose from 'jose';
import crypto from 'crypto';
import {getCFAPIJWTPrivateKey, getCFAPIJWTPublicKey} from './azure-keyvault';
import * as jose from "jose"
import crypto from "crypto"
import { getCFAPIJWTPrivateKey, getCFAPIJWTPublicKey } from "./azure-keyvault"
export async function getJWTToken (cf_user_id: string) {
export async function getJWTToken(cf_user_id: string) {
const private_secret_key = await getCFAPIJWTPrivateKey()
const secret = crypto.createPrivateKey(private_secret_key)
const alg = 'EdDSA'
const alg = "EdDSA"
const jwt :string = await new jose.SignJWT({ 'user_id': cf_user_id })
const jwt: string = await new jose.SignJWT({ user_id: cf_user_id })
.setProtectedHeader({ alg })
.setIssuedAt()
.setIssuer('https://codeflash.ai')
.setExpirationTime('30d')
.setIssuer("https://codeflash.ai")
.setExpirationTime("30d")
.sign(secret)
return jwt
}
export async function verifyJWTToken (jwt: string) {
export async function verifyJWTToken(jwt: string) {
const public_secret_key = await getCFAPIJWTPublicKey()
const alg = 'EdDSA'
const alg = "EdDSA"
const publicKey = await jose.importSPKI(public_secret_key, alg)
const { payload, protectedHeader } = await jose.jwtVerify(jwt, publicKey, {
issuer: 'https://codeflash.ai',
}).catch(async (err) => {
console.log(err)
throw new jose.errors.JWSSignatureVerificationFailed()
})
return {payload, protectedHeader}
const { payload, protectedHeader } = await jose
.jwtVerify(jwt, publicKey, {
issuer: "https://codeflash.ai",
})
.catch(async (err) => {
console.log(err)
throw new jose.errors.JWSSignatureVerificationFailed()
})
return { payload, protectedHeader }
}

View file

@ -1,74 +1,73 @@
'use server'
import util from 'util'
import {createHash, type Hash, randomBytes} from 'crypto'
import {prisma} from "./index";
"use server"
import util from "util"
import { createHash, type Hash, randomBytes } from "crypto"
import { prisma } from "./index"
// const prisma = new PrismaClient()
async function generateRandomAPIToken(): Promise<string> {
const randomBytesAsync = util.promisify(randomBytes)
const token = (await randomBytesAsync(48)).toString('base64url')
return 'cf-' + token
const randomBytesAsync = util.promisify(randomBytes)
const token = (await randomBytesAsync(48)).toString("base64url")
return "cf-" + token
}
export async function hashToken(token: string) {
const hash: Hash = createHash('sha384')
hash.update(token)
return hash.digest('base64url');
const hash: Hash = createHash("sha384")
hash.update(token)
return hash.digest("base64url")
}
export async function genAndStoreAPITokenHash(keyName: string, userId: number): Promise<string> {
// The idea here is to not store the API Keys in the database in plaintext. Because if we do that, then
// the keys are vulnerable to internal and external database breaches. Instead, we store the hash
// of the key in the database. When a user wants to use the key, they send the plaintext key to the server,
// which hashes it and compares it to the hash in the database. If they match, then the key is valid.
// The reason to not use bcrypt is that bcrypt is designed to be slow, which is good for passwords, but
// not good for API keys. Also with salting, one can't simply search for a key in the DB.
// Instead, we use SHA-384, which is designed to be fast. Rainbow table attacks are mitigated by using a long
// random key, for which rainbow tables become unfeasible and the attacker would have to brute force the key.
// The reason to use SHA-384 instead of SHA-256 is that SHA-384 has a longer output, which makes it much harder
// to brute force.
// The idea here is to not store the API Keys in the database in plaintext. Because if we do that, then
// the keys are vulnerable to internal and external database breaches. Instead, we store the hash
// of the key in the database. When a user wants to use the key, they send the plaintext key to the server,
// which hashes it and compares it to the hash in the database. If they match, then the key is valid.
// The reason to not use bcrypt is that bcrypt is designed to be slow, which is good for passwords, but
// not good for API keys. Also with salting, one can't simply search for a key in the DB.
// Instead, we use SHA-384, which is designed to be fast. Rainbow table attacks are mitigated by using a long
// random key, for which rainbow tables become unfeasible and the attacker would have to brute force the key.
// The reason to use SHA-384 instead of SHA-256 is that SHA-384 has a longer output, which makes it much harder
// to brute force.
const token: string = await generateRandomAPIToken()
const hashedToken = await hashToken(token);
await prisma.cf_api_keys.create({
data: {
key: hashedToken,
user_id: userId,
suffix: token.slice(-4),
name: keyName
}
})
return token
const token: string = await generateRandomAPIToken()
const hashedToken = await hashToken(token)
await prisma.cf_api_keys.create({
data: {
key: hashedToken,
user_id: userId,
suffix: token.slice(-4),
name: keyName,
},
})
return token
}
export async function userForAPIKey(key: string): Promise<null | string> {
// TODO: Add a rate limiter to prevent brute force attacks.
const hashedToken = await hashToken(key);
try {
const result = await prisma.cf_api_keys.update({
where: {
key: hashedToken
},
data: {
last_used: new Date()
}
})
if(result == null){
return null;
}
else{
return result.user_id
}
} catch (e) {
return null;
// TODO: Add a rate limiter to prevent brute force attacks.
const hashedToken = await hashToken(key)
try {
const result = await prisma.cf_api_keys.update({
where: {
key: hashedToken,
},
data: {
last_used: new Date(),
},
})
if (result == null) {
return null
} else {
return result.user_id
}
} catch (e) {
return null
}
}
export async function deleteAPIKeyById(id: number, userId: number): void {
await prisma.cf_api_keys.delete({
where: {
id,
user_id: userId
}
})
await prisma.cf_api_keys.delete({
where: {
id,
user_id: userId,
},
})
}