diff --git a/LICENSE b/LICENSE
index 3a3b2d2..98845b0 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2025 Nuxt UI Templates
+Copyright (c) 2025 GitFlux
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/app/components/Issue/Card.vue b/app/components/Issue/Card.vue
new file mode 100644
index 0000000..4e98190
--- /dev/null
+++ b/app/components/Issue/Card.vue
@@ -0,0 +1,170 @@
+
+
+
+
+
+ No {{ currentState === 'all' ? '' : currentState }} issues found
+
+
+ {{ currentState === 'open'
+ ? 'There are no open issues for this repository.'
+ : currentState === 'closed'
+ ? 'There are no closed issues for this repository.'
+ : 'This repository has no issues yet.' }}
+
+
+
+
+
+
+
+
+
+
+
+ Loading more issues...
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/components/Readme.vue b/app/components/Readme.vue
index ae28e3b..1bb8562 100644
--- a/app/components/Readme.vue
+++ b/app/components/Readme.vue
@@ -14,21 +14,21 @@ function handleClick(event: MouseEvent) {
if (!target) return;
// Handle copy button clicks
- const copyTarget = target.closest('[data-copy]');
+ const copyTarget = target.closest("[data-copy]");
if (copyTarget) {
- const wrapper = copyTarget.closest('.readme-code-block');
+ const wrapper = copyTarget.closest(".readme-code-block");
if (!wrapper) return;
- const pre = wrapper.querySelector('pre');
+ const pre = wrapper.querySelector("pre");
if (!pre?.textContent) return;
copy(pre.textContent);
- const icon = copyTarget.querySelector('span');
+ const icon = copyTarget.querySelector("span");
if (!icon) return;
- const originalIcon = 'i-heroicons-document-duplicate';
- const successIcon = 'i-heroicons-check';
+ const originalIcon = "i-heroicons-document-duplicate";
+ const successIcon = "i-heroicons-check";
icon.classList.remove(originalIcon);
icon.classList.add(successIcon);
@@ -62,6 +62,26 @@ function handleClick(event: MouseEvent) {
min-width: 0;
}
+@media screen and (prefers-color-scheme: light) {
+ .readme {
+ color: #000;
+ }
+
+ .readme svg {
+ filter: invert(1);
+ }
+}
+
+@media screen and (prefers-color-scheme: dark) {
+ .readme {
+ color: #fff;
+ }
+
+ .readme svg {
+ filter: invert(1);
+ }
+}
+
/* README headings */
.readme :deep(h1),
.readme :deep(h2),
@@ -71,7 +91,6 @@ function handleClick(event: MouseEvent) {
.readme :deep(h6) {
font-family: "Monaspace Krypton", ui-monospace, SFMono-Regular, monospace;
font-weight: 500;
- color: rgb(17 24 39);
margin-top: 2rem;
margin-bottom: 1rem;
line-height: 1.3;
@@ -260,4 +279,4 @@ function handleClick(event: MouseEvent) {
border-top-color: rgb(75 85 99);
}
}
-
\ No newline at end of file
+
diff --git a/app/composables/useGithub.ts b/app/composables/useGithub.ts
index a7314be..fdc6340 100644
--- a/app/composables/useGithub.ts
+++ b/app/composables/useGithub.ts
@@ -3,11 +3,11 @@ import { Octokit } from "octokit";
const useOctokit = () => {
const { user } = useUserSession();
- if (!user.value?.githubToken) {
+ if (!(user.value as any)?.githubToken) {
throw new Error("GitHub access token not found. Please login first.");
}
- return new Octokit({ auth: user.value.githubToken });
+ return new Octokit({ auth: (user.value as any).githubToken });
};
export const useAuthenticatedUser = async (token: string) => {
@@ -99,3 +99,70 @@ export const useGithubRepoReleases = (owner: string, repo: string) => {
{},
);
};
+
+export const useGithubRepoIssues = (
+ owner: string,
+ repo: string,
+ options: MaybeRef<{
+ state?: "open" | "closed" | "all";
+ sort?: "created" | "updated" | "comments";
+ direction?: "asc" | "desc";
+ per_page?: number;
+ page?: number;
+ }> = {},
+) => {
+ return useAsyncData(
+ () => `github-issues-${owner}-${repo}-${JSON.stringify(unref(options))}`,
+ async () => {
+ const octokit = useOctokit();
+ const opts = unref(options);
+ return octokit.rest.issues.listForRepo({
+ owner,
+ repo,
+ state: opts.state || "open",
+ sort: opts.sort || "created",
+ direction: opts.direction || "desc",
+ per_page: opts.per_page || 25,
+ page: opts.page || 1,
+ });
+ },
+ {
+ watch: [() => unref(options)],
+ },
+ );
+};
+
+export const useGithubRepoIssuesCount = (owner: string, repo: string, state: "closed") => {
+ return useAsyncData(
+ `github-issues-count-${owner}-${repo}-${state}`,
+ async () => {
+ const octokit = useOctokit();
+ try {
+ // Use the search API to get an accurate count of closed issues
+ // This is more efficient than fetching all issues just for the count
+ const searchQuery = `repo:${owner}/${repo} type:issue state:${state}`;
+ const response = await octokit.rest.search.issuesAndPullRequests({
+ q: searchQuery,
+ per_page: 1, // We only need the total_count, not the actual results
+ });
+ return response.data.total_count;
+ } catch {
+ // Fallback: try to fetch first page of closed issues and estimate
+ try {
+ const response = await octokit.rest.issues.listForRepo({
+ owner,
+ repo,
+ state,
+ per_page: 1,
+ });
+ // This won't give us the exact count, but it's better than nothing
+ // GitHub doesn't provide total count in the response headers for issues API
+ return response.data.length > 0 ? 1 : 0;
+ } catch {
+ return 0;
+ }
+ }
+ },
+ {},
+ );
+};
diff --git a/app/pages/[org]/[repo].vue b/app/pages/[org]/[repo]/index.vue
similarity index 94%
rename from app/pages/[org]/[repo].vue
rename to app/pages/[org]/[repo]/index.vue
index 0a557f7..2aff136 100644
--- a/app/pages/[org]/[repo].vue
+++ b/app/pages/[org]/[repo]/index.vue
@@ -136,7 +136,7 @@ function getLanguageColor(language: string): string {
-
+
@@ -211,6 +211,23 @@ function getLanguageColor(language: string): string {
-
-
-
-
+
Back to Home
-
+
diff --git a/nuxt.config.ts b/nuxt.config.ts
index cdf4621..fe29f09 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -21,7 +21,7 @@ export default defineNuxtConfig({
},
session: {
name: "nuxt-session",
- password: process.env.NUXT_SESSION_PASSWORD || "",
+ password: "",
cookie: {
sameSite: "lax",
secure: false, // Allow cookies over HTTP in development