lukeonline - commit 4084ccf
fix: allow traversal of all paths4084ccf8ffa6934c2b22afe812acd1d4217e88c7LukeJ<luke.jones.2567@gmail.com>2026-09-29T22:06:34+01:00diff --git a/src/pages/projects/[name]/files/[...path].astro b/src/pages/projects/[name]/files/[...path].astro
index 239005c..b4cf90f 100644
--- a/src/pages/projects/[name]/files/[...path].astro
+++ b/src/pages/projects/[name]/files/[...path].astro
@@ -2,74 +2,98 @@
import { getCollection } from 'astro:content'
import { getTree } from '../../../../lib/repos'
import Base from '../../../../layouts/Base.astro'
-import { BUTTON_STYLING_SHARED, DISABLED_BUTTON_STYLING_SHARED } from '../../../../consts.ts'
export async function getStaticPaths() {
const repos = await getCollection('repos')
- return repos.map((repo) => ({
- params: {
- name: repo.data.name,
- },
- props: {
- repo,
- },
- }))
+ return repos.flatMap((repo) => {
+ const tree = getTree(repo.data.name)
+
+ const directories = new Set<string>()
+
+ for (const file of tree) {
+ const parts = file.path.split('/')
+
+ // Add every directory that exists in the tree.
+ for (let i = 1; i < parts.length; i++) {
+ directories.add(parts.slice(0, i).join('/'))
+ }
+ }
+
+ return [...directories].map((path) => ({
+ params: {
+ name: repo.data.name,
+ path,
+ },
+ props: {
+ repo,
+ tree,
+ },
+ }))
+ })
}
-const { repo } = Astro.props
+const { repo, tree } = Astro.props
+const { path } = Astro.params
-const tree = getTree(repo.data.name)
+const directory = path ?? ''
+const prefix = `${directory}/`
+
+const files = tree
+ .filter((file) => {
+ if (!file.path.startsWith(prefix)) {
+ return false
+ }
+
+ const remaining = file.path.slice(prefix.length)
+
+ return remaining.length > 0 && !remaining.includes('/')
+ })
+ .sort((a, b) => a.path.localeCompare(b.path))
-const files = tree.filter((file) => !file.path.includes('/'))
const directories = [
...new Set(
tree
- .filter((file) => file.path.includes('/'))
- .map((file) => file.path.split('/')[0])
+ .filter((file) => file.path.startsWith(prefix))
+ .map((file) => {
+ const remaining = file.path.slice(prefix.length)
+ return remaining.split('/')[0]
+ })
+ .filter(Boolean)
),
-]
+].sort((a, b) => a.localeCompare(b))
---
-<Base title={`${repo.data.name}`}>
- <div class="flex flex-row items-baseline gap-2">
- <code class="text-sm">{repo.data.defaultBranch}</code>
- <a
- class={BUTTON_STYLING_SHARED + ' h-8 p-1 pl-2 items-center gap-3'}
- href={`/projects/${repo.data.name}/commits`}
- >{repo.data.commitCount} commits</a
- >
+<Base title={`${repo.data.name} - ${directory}`}>
+ <div
+ class="flex flex-col rounded-sm p-2 font-mono text-sm whitespace-pre-line ring inset-shadow-sm ring-gray-600 inset-shadow-gray-400"
+ >
<a
- class={BUTTON_STYLING_SHARED + ' h-8 p-1 pl-2 items-center gap-3'}
+ class="p-0.5 hover:text-blue-800 focus:underline"
href={`/projects/${repo.data.name}/files`}
- >{tree.length} files</a
>
- <a
- class={BUTTON_STYLING_SHARED + ' h-8 p-1 pl-2 items-center gap-3'}
- href={`/projects/${repo.data.name}/file/README.md`}
- >README</a
- >
- <a
- class={BUTTON_STYLING_SHARED + ' h-8 p-1 pl-2 items-center gap-3'}
- href={`/projects/${repo.data.name}/file/LICENSE`}
- >LICENSE</a
- >
- </div>
- <div
- class="flex flex-col rounded-sm p-2 ring inset-shadow-sm ring-gray-600 inset-shadow-gray-400 font-mono text-sm whitespace-pre-line">
+ ../
+ </a>
+
{
- directories.map((directory) => (
- <a class="p-0.5 hover:text-blue-800 focus:underline" href={`/projects/${repo.data.name}/files/${directory}`}>
- {directory}/
- </a>
+ directories.map((child) => (
+ <a
+ class="p-0.5 hover:text-blue-800 focus:underline"
+ href={`/projects/${repo.data.name}/files/${directory}/${child}`}
+ >
+ {child}/
+ </a>
))
}
{
files.map((file) => (
- <a class="p-0.5 hover:text-blue-800 focus:underline" href={`/projects/${repo.data.name}/file/${file.path}`}>
- {file.path}
- </a>
+ <a
+ class="p-0.5 hover:text-blue-800 focus:underline"
+ href={`/projects/${repo.data.name}/file/${file.path}`}
+ >
+ {file.path.slice(prefix.length)}
+ </a>
))
}
</div>
diff --git a/src/pages/projects/[name]/files/index.astro b/src/pages/projects/[name]/files/index.astro
new file mode 100644
index 0000000..580d7a2
--- /dev/null
+++ b/src/pages/projects/[name]/files/index.astro
@@ -0,0 +1,62 @@
+---
+import { getCollection } from 'astro:content'
+import { getTree } from '../../../../lib/repos'
+import Base from '../../../../layouts/Base.astro'
+
+export async function getStaticPaths() {
+ const repos = await getCollection('repos')
+
+ return repos.map((repo) => ({
+ params: {
+ name: repo.data.name,
+ },
+ props: {
+ repo,
+ },
+ }))
+}
+
+const { repo } = Astro.props
+
+const tree = getTree(repo.data.name)
+
+const files = tree
+ .filter((file) => !file.path.includes('/'))
+ .sort((a, b) => a.path.localeCompare(b.path))
+
+const directories = [
+ ...new Set(
+ tree
+ .filter((file) => file.path.includes('/'))
+ .map((file) => file.path.split('/')[0])
+ ),
+].sort((a, b) => a.localeCompare(b))
+---
+
+<Base title={repo.data.name}>
+ <div
+ class="flex flex-col rounded-sm p-2 font-mono text-sm whitespace-pre-line ring inset-shadow-sm ring-gray-600 inset-shadow-gray-400"
+ >
+ {
+ directories.map((directory) => (
+ <a
+ class="p-0.5 hover:text-blue-800 focus:underline"
+ href={`/projects/${repo.data.name}/files/${directory}`}
+ >
+ {directory}/
+ </a>
+ ))
+ }
+
+ {
+ files.map((file) => (
+ <a
+ class="p-0.5 hover:text-blue-800 focus:underline"
+ href={`/projects/${repo.data.name}/file/${file.path}`}
+ >
+ {file.path}
+ </a>
+ ))
+ }
+ </div>
+</Base>