generated from nhcarrigan/template
feat: add koikatsu (#46)
### Explanation _No response_ ### Issue _No response_ ### Attestations - [x] I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/) - [x] I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/). - [x] My contribution complies with the [Contributor Covenant](https://docs.nhcarrigan.com/dev/covenant/). ### Dependencies - [x] I have pinned the dependencies to a specific patch version. ### Style - [x] I have run the linter and resolved any errors. - [x] My pull request uses an appropriate title, matching the conventional commit standards. - [x] My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request. ### Tests - [x] My contribution adds new code, and I have added tests to cover it. - [x] My contribution modifies existing code, and I have updated the tests to reflect these changes. - [x] All new and existing tests pass locally with my changes. - [x] Code coverage remains at or above the configured threshold. ### Documentation _No response_ ### Versioning _No response_ Reviewed-on: https://codeberg.org/nhcarrigan/portfolio/pulls/46 Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com> Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
This commit is contained in:
parent
a919df8f4d
commit
408c4ae16a
2
do.env
Normal file
2
do.env
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
DO_ACCESS_KEY="op://Environment Variables - Development/DO Spaces/username"
|
||||||
|
DO_SECRET_KEY="op://Environment Variables - Development/DO Spaces/credential"
|
30
koikatsu.ts
Normal file
30
koikatsu.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { ListObjectsV2Command, S3Client } from "@aws-sdk/client-s3";
|
||||||
|
import { writeFile } from "fs/promises";
|
||||||
|
import { join } from "path";
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const s3 = new S3Client({
|
||||||
|
endpoint: "https://sfo3.digitaloceanspaces.com",
|
||||||
|
region: "sfo3",
|
||||||
|
credentials: {
|
||||||
|
accessKeyId: process.env.DO_ACCESS_KEY as string,
|
||||||
|
secretAccessKey: process.env.DO_SECRET_KEY as string
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const command = new ListObjectsV2Command({
|
||||||
|
Bucket: "nhcarrigan-cdn",
|
||||||
|
Prefix: "koikatsu"
|
||||||
|
})
|
||||||
|
|
||||||
|
const { Contents } = await s3.send(command);
|
||||||
|
|
||||||
|
const names = Contents?.map((content) => content.Key?.replace("koikatsu/", "")).filter((name) => name) || [];
|
||||||
|
|
||||||
|
await writeFile(join(process.cwd(), "src", "config", "Koikatsu.ts"), `/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
export const Koikatsu = ${JSON.stringify(names, null, 2)};`, "utf-8");
|
||||||
|
})()
|
@ -8,7 +8,8 @@
|
|||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "eslint src test --max-warnings 0",
|
"lint": "eslint src test --max-warnings 0",
|
||||||
"test": "vitest run --coverage"
|
"test": "vitest run --coverage",
|
||||||
|
"koikatsu": "op run --env-file=do.env --no-masking -- tsx koikatsu.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-svg-core": "6.6.0",
|
"@fortawesome/fontawesome-svg-core": "6.6.0",
|
||||||
@ -21,6 +22,7 @@
|
|||||||
"react-dom": "18.3.1"
|
"react-dom": "18.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@aws-sdk/client-s3": "3.693.0",
|
||||||
"@nhcarrigan/eslint-config": "5.0.0-rc2",
|
"@nhcarrigan/eslint-config": "5.0.0-rc2",
|
||||||
"@nhcarrigan/typescript-config": "4.0.0",
|
"@nhcarrigan/typescript-config": "4.0.0",
|
||||||
"@types/node": "22.8.4",
|
"@types/node": "22.8.4",
|
||||||
@ -31,6 +33,7 @@
|
|||||||
"jsdom": "25.0.1",
|
"jsdom": "25.0.1",
|
||||||
"postcss": "8.4.47",
|
"postcss": "8.4.47",
|
||||||
"tailwindcss": "3.4.14",
|
"tailwindcss": "3.4.14",
|
||||||
|
"tsx": "4.19.2",
|
||||||
"typescript": "5.6.3",
|
"typescript": "5.6.3",
|
||||||
"vitest": "2.1.4"
|
"vitest": "2.1.4"
|
||||||
}
|
}
|
||||||
|
1702
pnpm-lock.yaml
generated
1702
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
39
src/app/koikatsu/page.tsx
Normal file
39
src/app/koikatsu/page.tsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import { KoikatsuComponent } from "../../components/koikatsu";
|
||||||
|
import { Rule } from "../../components/rule";
|
||||||
|
import { Koikatsu } from "../../config/Koikatsu";
|
||||||
|
import type { JSX } from "react";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the /games page.
|
||||||
|
* @returns A React Component.
|
||||||
|
*/
|
||||||
|
const KoikatsuPage = (): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<main
|
||||||
|
className="w-[95%] text-center
|
||||||
|
max-w-4xl m-auto mt-16 mb-16 rounded-lg"
|
||||||
|
>
|
||||||
|
<h1 className="text-5xl">{`Koikatsu`}</h1>
|
||||||
|
<section>
|
||||||
|
<p className="mb-2">{`Images of Naomi generated in Koikatsu. Note that the list of images is dynamically generated, so we aren't able to provide alt text for these.`}</p>
|
||||||
|
<Rule />
|
||||||
|
<div className="grid sm:grid-cols-2 lg:grid-cols-3 grid-cols-1 gap-y-5">
|
||||||
|
{Koikatsu.toSorted((a, b) => {
|
||||||
|
return a.localeCompare(b);
|
||||||
|
}).map((img) => {
|
||||||
|
return (
|
||||||
|
<KoikatsuComponent img={img} key={img} />
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default KoikatsuPage;
|
36
src/components/koikatsu.tsx
Normal file
36
src/components/koikatsu.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import Image from "next/image";
|
||||||
|
import type { JSX } from "react";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the view for a koikatsu shot.
|
||||||
|
* @param props - An object containing the image file name.
|
||||||
|
* @param props.img - The file name of the image to render.
|
||||||
|
* @returns A JSX element.
|
||||||
|
*/
|
||||||
|
export const KoikatsuComponent = ({ img }: { readonly img: string }):
|
||||||
|
JSX.Element => {
|
||||||
|
return (
|
||||||
|
<div className="w-[300px] border-2
|
||||||
|
border-solid border-[--foreground] m-auto text-center items-center">
|
||||||
|
<a
|
||||||
|
href={`https://cdn.nhcarrigan.com/koikatsu/${img}`}
|
||||||
|
rel="noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
alt="This work is solely decorative. If you'd like alt text,
|
||||||
|
please reach out in our Discord and we'll be happy to provide it."
|
||||||
|
className="m-auto object-cover"
|
||||||
|
height={300}
|
||||||
|
src={`https://cdn.nhcarrigan.com/koikatsu/${img}`}
|
||||||
|
width={300}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
33
src/config/Koikatsu.ts
Normal file
33
src/config/Koikatsu.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
export const Koikatsu = [
|
||||||
|
"CharaStudio-2024-10-15-19-14-34-Render.png",
|
||||||
|
"CharaStudio-2024-10-15-19-15-43-Render.png",
|
||||||
|
"CharaStudio-2024-10-16-09-30-57-Render.png",
|
||||||
|
"CharaStudio-2024-10-18-11-54-39-Render.png",
|
||||||
|
"CharaStudio-2024-10-26-09-13-34-Render.png",
|
||||||
|
"CharaStudio-2024-10-27-09-44-24-Render.png",
|
||||||
|
"CharaStudio-2024-10-27-09-45-11-Render.png",
|
||||||
|
"CharaStudio-2024-10-31-13-20-16-Render.png",
|
||||||
|
"CharaStudio-2024-10-31-13-27-20-Render.png",
|
||||||
|
"CharaStudio-2024-11-01-22-20-26-Render.png",
|
||||||
|
"CharaStudio-2024-11-01-22-21-00-Render.png",
|
||||||
|
"CharaStudio-2024-11-01-22-22-04-Render.png",
|
||||||
|
"CharaStudio-2024-11-01-22-22-40-Render.png",
|
||||||
|
"CharaStudio-2024-11-01-22-23-32-Render.png",
|
||||||
|
"CharaStudio-2024-11-02-13-41-30-Render.png",
|
||||||
|
"CharaStudio-2024-11-02-13-45-05-Render.png",
|
||||||
|
"CharaStudio-2024-11-02-13-46-02-Render.png",
|
||||||
|
"CharaStudio-2024-11-14-18-12-22-Render.png",
|
||||||
|
"CharaStudio-2024-11-14-18-13-23-Render.png",
|
||||||
|
"CharaStudio-2024-11-18-10-22-40-Render.png",
|
||||||
|
"CharaStudio-2024-11-18-10-23-28-Render.png",
|
||||||
|
"CharaStudio-2024-11-18-10-24-39-Render.png",
|
||||||
|
"CharaStudio-2024-11-18-10-25-50-Render.png",
|
||||||
|
"CharaStudio-2024-11-18-10-27-48-Render.png",
|
||||||
|
"CharaStudio-2024-11-18-10-29-25-Render.png",
|
||||||
|
"CharaStudio-2024-11-18-10-30-05-Render.png",
|
||||||
|
];
|
@ -25,6 +25,7 @@ export const NavItems = [
|
|||||||
{ href: "/play", text: "Play with Naomi" },
|
{ href: "/play", text: "Play with Naomi" },
|
||||||
{ href: "/tech", text: "Technologies" },
|
{ href: "/tech", text: "Technologies" },
|
||||||
{ href: "/contribute", text: "Contribute to our Projects" },
|
{ href: "/contribute", text: "Contribute to our Projects" },
|
||||||
|
{ href: "/koikatsu", text: "Koikatsu Scenes" },
|
||||||
].sort((a, b) => {
|
].sort((a, b) => {
|
||||||
return a.text.localeCompare(b.text);
|
return a.text.localeCompare(b.text);
|
||||||
});
|
});
|
||||||
|
17
test/config/Koikatsu.spec.ts
Normal file
17
test/config/Koikatsu.spec.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { Koikatsu } from "../../src/config/Koikatsu";
|
||||||
|
|
||||||
|
describe("koikatsu strings", () => {
|
||||||
|
it("should be unique", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const set = new Set(
|
||||||
|
Koikatsu,
|
||||||
|
);
|
||||||
|
expect(set, "are not unique").toHaveLength(Koikatsu.length);
|
||||||
|
});
|
||||||
|
});
|
@ -28,6 +28,7 @@
|
|||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules",
|
"node_modules",
|
||||||
"vitest.config.ts"
|
"vitest.config.ts",
|
||||||
|
"koikatsu.ts"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ export default defineConfig({
|
|||||||
branches: 100
|
branches: 100
|
||||||
},
|
},
|
||||||
extension: [".ts"],
|
extension: [".ts"],
|
||||||
exclude: [...coverageConfigDefaults.exclude, "tailwind.config.ts"]
|
exclude: [...coverageConfigDefaults.exclude, "tailwind.config.ts", "koikatsu.ts"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
Loading…
x
Reference in New Issue
Block a user