website

#astro#js#html#css

git clone https://git.pyrossh.dev/website

木 Personal website of pyrossh. Built with astrojs, shiki, vite.


136bbf7 — pyrossh 2025-11-15T14:30:48+05:30
update stuff

Generated files not shown: bun.lock

alchemy.run.ts ADDED
@@ -0,0 +1,15 @@
1
+ import alchemy from "alchemy";
2
+ import AWS from "alchemy/aws/control";
3
+
4
+ const app = await alchemy("my-first-app");
5
+
6
+ const reposBucket = await AWS.S3.Bucket("pyrossh-repos2", {
7
+ BucketName: "pyrossh-repos2",
8
+ Tags: [
9
+ { Key: "Environment", Value: "Production" },
10
+ { Key: "ProvisionedBy", Value: "Alchemy" }
11
+ ]
12
+ });
13
+
14
+
15
+ await app.finalize();
package.json CHANGED
@@ -18,6 +18,7 @@
18
18
  "@astrojs/rss": "^4.0.12",
19
19
  "@astrojs/sitemap": "^3.4.1",
20
20
  "@tauri-apps/plugin-log": "^2.7.1",
21
+ "alchemy": "^0.77.5",
21
22
  "astro": "^5.9.0",
22
23
  "astro-color-scheme": "^1.1.5",
23
24
  "astro-expressive-code": "^0.41.3",
src/components/RecursiveList.astro CHANGED
@@ -33,7 +33,7 @@ const { items, repoTitle, depth = 0 } = Astro.props;
33
33
  size="24px"
34
34
  color="hsl(45deg 75.37% 60%)"
35
35
  />
36
- <span class="folder-name"> {item.name}</span>
36
+ <span>{item.name}</span>
37
37
  </summary>
38
38
  <ul>
39
39
  <Astro.self
@@ -88,10 +88,6 @@ const { items, repoTitle, depth = 0 } = Astro.props;
88
88
  padding: 0;
89
89
  margin: 0.25rem 0 0.5rem 1rem;
90
90
  }
91
-
92
- .folder-name {
93
- color: var(--color-link);
94
- }
95
91
  }
96
92
 
97
93
  .file {
src/content.config.ts CHANGED
@@ -3,7 +3,7 @@ import { simpleGit } from "simple-git";
3
3
  import { glob } from 'astro/loaders';
4
4
  import { defineCollection, z } from 'astro:content';
5
5
  import { REPOS } from './consts';
6
- import { buildFileTree, sortChildren, fileNodeSchema } from "./utils/files";
6
+ import { buildFileTree, sortChildren, CommitSchema, FileNodeSchema, collectCommits } from "./utils/files";
7
7
 
8
8
  async function checkFileExists(filePath: string) {
9
9
  try {
@@ -24,17 +24,27 @@ export const collections = {
24
24
  const readmePath = repoPath + "/README.md";
25
25
  console.log("loading repo " + repoPath)
26
26
  const git = simpleGit(repoPath)
27
+ // "-p",
27
28
  const commits = await git.log(["--branches", "--tags"]);
28
29
  const paths = await git.raw(["ls-files"]);
29
- // await git.log(["-p", "--", "file"]); // to get diffs
30
- // await git.log(["file"]);
31
- const files = await Promise.all(paths.split("\n")
30
+ const files = [];
32
- .filter((p) => p.length > 0)
31
+ for (const p of paths.split("\n").filter((p) => p.length > 0)) {
32
+ try {
33
- .map((p) => fs.stat(`${repoPath}/${p}`)
33
+ const stat = await fs.stat(`${repoPath}/${p}`);
34
+ const isLarge = stat.size > 1024 * 512;
35
+ const history = isLarge ? { all: [] } : await git.log(["-p", p]);
36
+ files.push({
37
+ name: p,
34
- .then((stat) => ({ name: p, absolutePath: `${repoPath}/${p}`, ...stat }))
38
+ absolutePath: `${repoPath}/${p}`,
39
+ ...stat,
35
- .catch(() => null)
40
+ history: history.all.map(collectCommits),
36
- ));
41
+ })
42
+ } catch (e) {
43
+ // Ignore files not found
44
+ console.log(e);
45
+ }
46
+ }
37
- const fileTree = sortChildren(buildFileTree(files.filter((f) => f !== null)));
47
+ const fileTree = sortChildren(buildFileTree(files));
38
48
  const readmeContent = await checkFileExists(readmePath) ? await renderMarkdown(await fs.readFile(readmePath, 'utf-8')) : {
39
49
  html: '',
40
50
  metadata: {},
@@ -46,15 +56,7 @@ export const collections = {
46
56
  description: r.description,
47
57
  tags: r.tags,
48
58
  badges: r.badges || [],
49
- commits: commits.all.map((item) => ({
59
+ commits: commits.all.map(collectCommits),
50
- hash: item.hash,
51
- message: item.message,
52
- body: `${item.message}\n\n${item.body}`,
53
- author_name: item.author_name,
54
- date: item.date,
55
- branches: item.refs.split(",").filter((ref: string) => ref.includes("origin/")).map((ref) => ref.replace("origin/", "")),
56
- tags: item.refs.split(",").filter((ref: string) => ref.includes("tag: ")).map((ref) => ref.replace("tag: ", "")),
57
- })),
58
60
  files: fileTree,
59
61
  },
60
62
  rendered: readmeContent,
@@ -71,17 +73,8 @@ export const collections = {
71
73
  img: z.string(),
72
74
  alt: z.string(),
73
75
  })).optional(),
74
- commits: z.array(z.object({
76
+ commits: z.array(CommitSchema),
75
- hash: z.string(),
76
- message: z.string(),
77
- body: z.string(),
78
- author_name: z.string(),
79
- date: z.string(),
80
- branches: z.array(z.string()),
81
- tags: z.array(z.string()),
82
- // diff: z.string(),
83
- })),
84
- files: z.array(fileNodeSchema),
77
+ files: z.array(FileNodeSchema),
85
78
  }),
86
79
  }),
87
80
  content: defineCollection({
src/layouts/{Base.astro → BaseLayout.astro} RENAMED
@@ -1,5 +1,5 @@
1
1
  ---
2
- import "./Base.css"; // Note: This has to be first always
2
+ // import "./BaseLayout.css"; // Note: This has to be first always
3
3
  import { ClientRouter } from "astro:transitions";
4
4
  import ResetCSS from "@appzic/astro-reset-css";
5
5
  import Header from "@/components/Header.astro";
@@ -87,3 +87,61 @@ const image = "/favicon.png";
87
87
  <Footer />
88
88
  </body>
89
89
  </html>
90
+ <style>
91
+ :root {
92
+ --color-black: light-dark(hsl(0, 0%, 0%), hsl(0, 0%, 0%));
93
+ --color-white: light-dark(hsl(0, 0%, 100%), hsl(0, 0%, 100%));
94
+ --color-accent: light-dark(hsl(65, 92%, 76%), hsl(65, 92%, 76%));
95
+ --color-body-bg: light-dark(hsl(0, 0%, 100%), hsl(210, 13%, 15%));
96
+ --color-box-bg: light-dark(hsl(0, 0%, 95%), hsl(210, 13%, 9%));
97
+ --color-text: light-dark(hsl(0, 0%, 13%), hsl(210, 17%, 98%));
98
+ --color-link: light-dark(hsl(211, 100%, 45%), hsl(211, 100%, 60%));
99
+ --color-post-link: light-dark(hsl(0, 0%, 0%), hsl(211, 100%, 60%));
100
+ --color-highlight-bg: light-dark(hsl(183, 80%, 88%), hsl(183, 80%, 18%));
101
+ --color-code-fg: light-dark(hsl(348, 86%, 43%), hsl(348, 86%, 80%));
102
+ --color-code-bg: light-dark(hsl(0, 0%, 95%), hsl(0, 0%, 0%));
103
+ --color-pre-bg: light-dark(hsl(0, 0%, 95%), hsl(0, 0%, 0%));
104
+ --color-tag: light-dark(hsl(152, 96%, 35%), hsl(152, 96%, 44%));
105
+ --btn-light: none;
106
+ --btn-dark: block;
107
+
108
+ .astro-code,
109
+ .astro-code span {
110
+ background: var(--color-pre-bg) !important;
111
+ }
112
+ }
113
+
114
+ [data-theme="dark"] {
115
+ --btn-light: block;
116
+ --btn-dark: none;
117
+
118
+ .expressive-code .frame pre {
119
+ background: var(--color-pre-bg) !important;
120
+ }
121
+ }
122
+
123
+ body {
124
+ display: flex;
125
+ flex-direction: column;
126
+ flex: 1;
127
+ background-color: var(--color-body-bg);
128
+ color: var(--color-text);
129
+ font-family: system-ui;
130
+ text-rendering: optimizeLegibility;
131
+ font-variant-ligatures: common-ligatures;
132
+ font-kerning: normal;
133
+ font-size: 1.25rem;
134
+ line-height: 1.5;
135
+ min-height: 100vh;
136
+
137
+ @media (max-width: 720px) {
138
+ font-size: 1.1rem;
139
+ }
140
+ }
141
+
142
+ main {
143
+ margin-top: 1rem;
144
+ margin-bottom: 1rem;
145
+ padding-bottom: 1em;
146
+ }
147
+ </style>
src/layouts/{Base.css → BaseLayout.css} RENAMED
File without changes
src/layouts/FileLayout.astro ADDED
@@ -0,0 +1,121 @@
1
+ ---
2
+ import { Icon } from "astro-icon/components";
3
+ import { type CollectionEntry } from "astro:content";
4
+ import RepoLayout from "@/layouts/RepoLayout.astro";
5
+
6
+ type Props = {
7
+ repo: CollectionEntry<"repos">;
8
+ file: {
9
+ name: string;
10
+ path: string;
11
+ size: number;
12
+ isDirectory: boolean;
13
+ absolutePath: string;
14
+ };
15
+ };
16
+
17
+ const { repo, file } = Astro.props;
18
+ const { pathname } = Astro.url;
19
+ ---
20
+
21
+ <RepoLayout data={repo.data}>
22
+ {
23
+ (
24
+ <div>
25
+ <hr />
26
+ <div class="title">
27
+ <span>file:</span>
28
+ <Icon name="mdi:file" size="24px" color="hsl(0deg 75% 75%)" />
29
+ <h3>{file.path}</h3>
30
+ </div>
31
+ <hr />
32
+ <div class="nav">
33
+ <div>
34
+ <a
35
+ aria-current={
36
+ pathname == `/repos/${repo.id}/files/${file.path}`
37
+ ? "true"
38
+ : "false"
39
+ }
40
+ href={`/repos/${repo.id}/files/${file.path}`}
41
+ >
42
+ Contents
43
+ </a>
44
+ </div>
45
+ <div>|</div>
46
+ <div>
47
+ <a
48
+ aria-current={
49
+ pathname == `/repos/${repo.id}/files/${file.path}/history`
50
+ ? "true"
51
+ : "false"
52
+ }
53
+ href={`/repos/${repo.id}/files/${file.path}/history`}
54
+ >
55
+ History
56
+ </a>
57
+ </div>
58
+ <div>|</div>
59
+ <div>
60
+ <a
61
+ aria-current={
62
+ pathname == `/repos/${repo.id}/files/${file.path}/blame`
63
+ ? "true"
64
+ : "false"
65
+ }
66
+ href={`/repos/${repo.id}/files/${file.path}/blame`}
67
+ >
68
+ Blame
69
+ </a>
70
+ </div>
71
+ </div>
72
+ <hr />
73
+ <slot />
74
+ </div>
75
+ )
76
+ }
77
+ </RepoLayout>
78
+ <style>
79
+ .nav {
80
+ display: flex;
81
+ align-items: center;
82
+ margin-bottom: 0.5rem;
83
+
84
+ @media (max-width: 720px) {
85
+ margin-top: 0.5rem;
86
+ }
87
+
88
+ div {
89
+ padding-right: 0.5rem;
90
+ }
91
+
92
+ a {
93
+ font-size: 1rem;
94
+ font-weight: 500;
95
+ padding-right: 0.5rem;
96
+ cursor: pointer;
97
+ color: var(--color-link);
98
+
99
+ &[aria-current="true"] {
100
+ text-underline-offset: 4px;
101
+ text-decoration: underline;
102
+ }
103
+ }
104
+ }
105
+
106
+ .title {
107
+ display: flex;
108
+ align-items: center;
109
+ margin-top: 0.75rem;
110
+ margin-bottom: 0.5rem;
111
+ font-size: 1.1rem;
112
+ font-weight: 500;
113
+ }
114
+ span {
115
+ font-weight: 600;
116
+ margin-right: 4px;
117
+ }
118
+ h3 {
119
+ text-decoration: underline;
120
+ }
121
+ </style>
src/layouts/{Repo.astro → RepoLayout.astro} RENAMED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  import { type CollectionEntry } from "astro:content";
3
- import Layout from "@/layouts/Base.astro";
3
+ import Layout from "@/layouts/BaseLayout.astro";
4
4
 
5
5
  type Props = Pick<CollectionEntry<"repos">, "data">;
6
6
 
src/pages/404.astro CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- import Layout from "@/layouts/Base.astro";
2
+ import Layout from "@/layouts/BaseLayout.astro";
3
3
  ---
4
4
 
5
5
  <Layout title="pyrossh" description="Page Not found">
src/pages/500.astro CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- import Layout from "@/layouts/Base.astro";
2
+ import Layout from "@/layouts/BaseLayout.astro";
3
3
  ---
4
4
 
5
5
  <Layout title="pyrossh" description="Error Page">
src/pages/cv/index.astro CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- import Layout from "@/layouts/Base.astro";
2
+ import Layout from "@/layouts/BaseLayout.astro";
3
3
  ---
4
4
 
5
5
  <Layout title="cv" description="Peter John's CV">
src/pages/index.astro CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  import { Image } from "astro:assets";
3
- import Layout from "@/layouts/Base.astro";
3
+ import Layout from "@/layouts/BaseLayout.astro";
4
4
  import { REPOS, TOOLS } from "@/consts";
5
5
  import styles from "./index.module.css";
6
6
  ---
src/pages/only-bible-app/index.astro CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  import { Image } from "astro:assets";
3
- import Layout from "@/layouts/Base.astro";
3
+ import Layout from "@/layouts/BaseLayout.astro";
4
4
  import PlayStoreIcon from "@/assets/icons/playstore.svg";
5
5
  import AppStoreIcon from "@/assets/icons/appstore.svg";
6
6
  import AppIconPng from "@/assets/images/app_icon.png";
src/pages/only-bible-app/privacy-policy/index.astro CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- import Layout from "@/layouts/Base.astro";
2
+ import Layout from "@/layouts/BaseLayout.astro";
3
3
  ---
4
4
 
5
5
  <Layout
src/pages/only-bible-app/terms-and-conditions/index.astro CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- import Layout from "@/layouts/Base.astro";
2
+ import Layout from "@/layouts/BaseLayout.astro";
3
3
  ---
4
4
 
5
5
  <Layout
src/pages/posts/[...slug].astro CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  import { type CollectionEntry, getCollection } from "astro:content";
3
3
  import { render } from "astro:content";
4
- import Layout from "@/layouts/Base.astro";
4
+ import Layout from "@/layouts/BaseLayout.astro";
5
5
  import FormattedDate from "@/components/FormattedDate.astro";
6
6
 
7
7
  export async function getStaticPaths() {
src/pages/posts/index.astro CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  import { getCollection } from "astro:content";
3
3
  import FormattedDate from "../../components/FormattedDate.astro";
4
- import Layout from "@/layouts/Base.astro";
4
+ import Layout from "@/layouts/BaseLayout.astro";
5
5
 
6
6
  const posts = (await getCollection("content")).sort(
7
7
  (a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
src/pages/repos/[...slug]/commits/index.astro CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  import { type CollectionEntry, getCollection } from "astro:content";
3
- import RepoLayout from "@/layouts/Repo.astro";
3
+ import RepoLayout from "@/layouts/RepoLayout.astro";
4
4
  import Commit from "@/components/Commit.astro";
5
5
 
6
6
  export async function getStaticPaths() {
src/pages/repos/[...slug]/files/[...file]/blame/index.astro ADDED
@@ -0,0 +1,42 @@
1
+ ---
2
+ import { type CollectionEntry, getCollection } from "astro:content";
3
+ import FileLayout from "@/layouts/FileLayout.astro";
4
+ import { collectFiles } from "@/utils/files";
5
+
6
+ export async function getStaticPaths() {
7
+ const repos = await getCollection("repos");
8
+ return repos.flatMap((repo) => {
9
+ const allFiles = collectFiles(repo.data.files);
10
+ return allFiles.map((file) => ({
11
+ params: { slug: repo.id, file: file.path },
12
+ props: {
13
+ repo: repo,
14
+ file: file,
15
+ },
16
+ }));
17
+ });
18
+ }
19
+ type Props = {
20
+ repo: CollectionEntry<"repos">;
21
+ file: {
22
+ name: string;
23
+ path: string;
24
+ size: number;
25
+ isDirectory: boolean;
26
+ absolutePath: string;
27
+ history: {
28
+ hash: string;
29
+ message: string;
30
+ body: string;
31
+ branches: string[];
32
+ tags: string[];
33
+ }[];
34
+ };
35
+ };
36
+
37
+ const { repo, file } = Astro.props;
38
+ ---
39
+
40
+ <FileLayout repo={repo} file={file}>
41
+ WIP: Work in Progress...
42
+ </FileLayout>
src/pages/repos/[...slug]/files/[...file]/history/index.astro ADDED
@@ -0,0 +1,43 @@
1
+ ---
2
+ import { type CollectionEntry, getCollection } from "astro:content";
3
+ import FileLayout from "@/layouts/FileLayout.astro";
4
+ import Commit from "@/components/Commit.astro";
5
+ import { collectFiles } from "@/utils/files";
6
+
7
+ export async function getStaticPaths() {
8
+ const repos = await getCollection("repos");
9
+ return repos.flatMap((repo) => {
10
+ const allFiles = collectFiles(repo.data.files);
11
+ return allFiles.map((file) => ({
12
+ params: { slug: repo.id, file: file.path },
13
+ props: {
14
+ repo: repo,
15
+ file: file,
16
+ },
17
+ }));
18
+ });
19
+ }
20
+ type Props = {
21
+ repo: CollectionEntry<"repos">;
22
+ file: {
23
+ name: string;
24
+ path: string;
25
+ size: number;
26
+ isDirectory: boolean;
27
+ absolutePath: string;
28
+ history: {
29
+ hash: string;
30
+ message: string;
31
+ body: string;
32
+ branches: string[];
33
+ tags: string[];
34
+ }[];
35
+ };
36
+ };
37
+
38
+ const { repo, file } = Astro.props;
39
+ ---
40
+
41
+ <FileLayout repo={repo} file={file}>
42
+ {file.history.map((commit) => <Commit {...commit} message={commit.body} />)}
43
+ </FileLayout>
src/pages/repos/[...slug]/files/[...file]/index.astro CHANGED
@@ -3,7 +3,7 @@ import fs from "fs/promises";
3
3
  import path from "path";
4
4
  import { type CollectionEntry, getCollection } from "astro:content";
5
5
  import { Code } from "astro-expressive-code/components";
6
- import RepoLayout from "@/layouts/Repo.astro";
6
+ import FileLayout from "@/layouts/FileLayout.astro";
7
7
  import { collectFiles } from "@/utils/files";
8
8
 
9
9
  export async function getStaticPaths() {
@@ -32,7 +32,6 @@ type Props = {
32
32
 
33
33
  const { repo, file } = Astro.props;
34
34
  const contentBuffer = await fs.readFile(file.absolutePath);
35
- const name = path.basename(file.name);
36
35
  const ext = path.extname(file.name).replace(".", "");
37
36
  const isBinary = ["apk", "dex", "ap_", "jar", "fnt"].includes(ext);
38
37
  const isLarge = file.size > 1024 * 512; // 512KB
@@ -50,49 +49,26 @@ const isImage = [
50
49
  ].includes(ext);
51
50
  ---
52
51
 
53
- <RepoLayout data={repo.data}>
52
+ <FileLayout repo={repo} file={file}>
53
+ <div>
54
- {
54
+ {
55
+ isBinary || isLarge ? (
56
+ <p>The file is too large to be displayed ({file.size} bytes).</p>
55
- (
57
+ ) : isImage ? (
56
- <div>
58
+ <img
59
+ src={`data:image/${ext};base64,${Buffer.from(contentBuffer).toString("base64")}`}
60
+ alt={file.path}
61
+ />
62
+ ) : (
63
+ <Code
64
+ code={
65
+ Buffer.from(contentBuffer).toString("utf-8") ||
66
+ "No content to display."
67
+ }
57
- <div class="title">
68
+ lang={ext || "txt"}
69
+ wrap
58
- <span>file:</span>
70
+ />
71
+ )
59
- <h3>{file.path}</h3>
72
+ }
60
- </div>
73
+ </div>
61
- {isBinary || isLarge ? (
62
- <p>The file is too large to be displayed ({file.size} bytes).</p>
63
- ) : isImage ? (
64
- <img
65
- src={`data:image/${ext};base64,${Buffer.from(contentBuffer).toString("base64")}`}
66
- alt={file.path}
67
- />
68
- ) : (
69
- <Code
70
- code={
71
- Buffer.from(contentBuffer).toString("utf-8") ||
72
- "No content to display."
73
- }
74
- lang={ext || "txt"}
75
- wrap
76
- />
77
- )}
78
- </div>
79
- )
80
- }
81
- </RepoLayout>
74
+ </FileLayout>
82
- <style>
83
- .title {
84
- display: flex;
85
- align-items: center;
86
- margin-top: 0.75rem;
87
- margin-bottom: 0.5rem;
88
- font-size: 1.1rem;
89
- font-weight: 500;
90
- }
91
- span {
92
- font-weight: 600;
93
- margin-right: 4px;
94
- }
95
- h3 {
96
- text-decoration: underline;
97
- }
98
- </style>
src/pages/repos/[...slug]/files/index.astro CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  import { type CollectionEntry, getCollection } from "astro:content";
3
- import RepoLayout from "@/layouts/Repo.astro";
3
+ import RepoLayout from "@/layouts/RepoLayout.astro";
4
4
  import RecursiveList from "@/components/RecursiveList.astro";
5
5
 
6
6
  export async function getStaticPaths() {
src/pages/repos/[...slug]/index.astro CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  import { type CollectionEntry, getCollection } from "astro:content";
3
3
  import { render } from "astro:content";
4
- import RepoLayout from "@/layouts/Repo.astro";
4
+ import RepoLayout from "@/layouts/RepoLayout.astro";
5
5
  import Commit from "@/components/Commit.astro";
6
6
  import styles from "./index.module.css";
7
7
 
src/utils/files.ts CHANGED
@@ -1,5 +1,24 @@
1
1
  import { z } from 'astro:content';
2
2
 
3
+ export interface DiffSummary {
4
+ changed: number;
5
+ insertions: number;
6
+ deletions: number;
7
+ files: string[];
8
+ }
9
+
10
+ export interface Commit {
11
+ hash: string;
12
+ message: string;
13
+ body: string;
14
+ author_name: string;
15
+ author_email: string;
16
+ date: string;
17
+ branches: string[];
18
+ tags: string[];
19
+ diff: DiffSummary;
20
+ }
21
+
3
22
  export interface FileNode {
4
23
  name: string;
5
24
  path: string;
@@ -8,20 +27,45 @@ export interface FileNode {
8
27
  size?: number;
9
28
  ext?: string;
10
29
  absolutePath?: string;
30
+ history?: Commit[];
11
31
  }
12
32
 
33
+ export const CommitSchema = z.object({
34
+ hash: z.string(),
35
+ message: z.string(),
36
+ body: z.string(),
37
+ author_name: z.string(),
38
+ author_email: z.string(),
39
+ date: z.string(),
40
+ branches: z.array(z.string()),
41
+ tags: z.array(z.string()),
42
+ // diff: z.string(),
43
+ });
44
+
13
- export const fileNodeSchema: z.ZodType<FileNode> = z.lazy(() =>
45
+ export const FileNodeSchema: z.ZodType<FileNode> = z.lazy(() =>
14
46
  z.object({
15
47
  name: z.string(),
16
48
  path: z.string(),
17
49
  isDirectory: z.boolean(),
18
- children: z.array(fileNodeSchema),
50
+ children: z.array(FileNodeSchema),
19
51
  size: z.number(),
20
52
  ext: z.string(),
21
53
  absolutePath: z.string(),
54
+ history: z.array(CommitSchema),
22
55
  })
23
56
  );
24
57
 
58
+ export const collectCommits = (item: { hash: any; message: any; body: any; author_name: any; author_email: any; date: any; refs: string; }) => ({
59
+ hash: item.hash,
60
+ message: item.message,
61
+ body: `${item.message}\n\n${item.body}`,
62
+ author_name: item.author_name,
63
+ author_email: item.author_email,
64
+ date: item.date,
65
+ branches: item.refs.split(",").filter((ref: string) => ref.includes("origin/")).map((ref) => ref.replace("origin/", "")),
66
+ tags: item.refs.split(",").filter((ref: string) => ref.includes("tag: ")).map((ref) => ref.replace("tag: ", "")),
67
+ });
68
+
25
69
  export const collectFiles = (nodes: FileNode[]): FileNode[] => {
26
70
  const files: FileNode[] = [];
27
71
 
@@ -76,6 +120,7 @@ export const buildFileTree = (files: any[]): FileNode[] => {
76
120
  // It's a file, add file metadata
77
121
  newNode.size = file.size;
78
122
  newNode.ext = file.ext;
123
+ newNode.history = file.history;
79
124
  newNode.absolutePath = file.absolutePath;
80
125
  } else {
81
126
  // It's a directory