hello worlds
29
.gitignore
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# build output
|
||||||
|
dist/
|
||||||
|
# generated types
|
||||||
|
.astro/
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# logs
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
|
||||||
|
# environment variables
|
||||||
|
.env
|
||||||
|
.env.production
|
||||||
|
|
||||||
|
# Cloudflare Workers
|
||||||
|
.wrangler/
|
||||||
|
*.vars
|
||||||
|
tools/
|
||||||
|
|
||||||
|
# macOS-specific files
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# jetbrains setting folder
|
||||||
|
.idea/
|
||||||
4
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["astro-build.astro-vscode"],
|
||||||
|
"unwantedRecommendations": []
|
||||||
|
}
|
||||||
11
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"command": "./node_modules/.bin/astro dev",
|
||||||
|
"name": "Development server",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "node-terminal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
22
AGENTS.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
## Development
|
||||||
|
|
||||||
|
When starting the dev server, use background mode:
|
||||||
|
|
||||||
|
```
|
||||||
|
astro dev --background
|
||||||
|
```
|
||||||
|
|
||||||
|
Manage the background server with `astro dev stop`, `astro dev status`, and `astro dev logs`.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Full documentation: https://docs.astro.build
|
||||||
|
|
||||||
|
Consult these guides before working on related tasks:
|
||||||
|
|
||||||
|
- [Adding pages, dynamic routes, or middleware](https://docs.astro.build/en/guides/routing/)
|
||||||
|
- [Working with Astro components](https://docs.astro.build/en/basics/astro-components/)
|
||||||
|
- [Using React, Vue, Svelte, or other framework components](https://docs.astro.build/en/guides/framework-components/)
|
||||||
|
- [Adding or managing content](https://docs.astro.build/en/guides/content-collections/)
|
||||||
|
- [Adding styles or using Tailwind](https://docs.astro.build/en/guides/styling/)
|
||||||
|
- [Supporting multiple languages](https://docs.astro.build/en/guides/internationalization/)
|
||||||
43
README.md
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Astro Starter Kit: Minimal
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm create astro@latest -- --template minimal
|
||||||
|
```
|
||||||
|
|
||||||
|
> 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
|
||||||
|
|
||||||
|
## 🚀 Project Structure
|
||||||
|
|
||||||
|
Inside of your Astro project, you'll see the following folders and files:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/
|
||||||
|
├── public/
|
||||||
|
├── src/
|
||||||
|
│ └── pages/
|
||||||
|
│ └── index.astro
|
||||||
|
└── package.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
|
||||||
|
|
||||||
|
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
|
||||||
|
|
||||||
|
Any static assets, like images, can be placed in the `public/` directory.
|
||||||
|
|
||||||
|
## 🧞 Commands
|
||||||
|
|
||||||
|
All commands are run from the root of the project, from a terminal:
|
||||||
|
|
||||||
|
| Command | Action |
|
||||||
|
| :------------------------ | :----------------------------------------------- |
|
||||||
|
| `npm install` | Installs dependencies |
|
||||||
|
| `npm run dev` | Starts local dev server at `localhost:4321` |
|
||||||
|
| `npm run build` | Build your production site to `./dist/` |
|
||||||
|
| `npm run preview` | Preview your build locally, before deploying |
|
||||||
|
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
|
||||||
|
| `npm run astro -- --help` | Get help using the Astro CLI |
|
||||||
|
|
||||||
|
## 👀 Want to learn more?
|
||||||
|
|
||||||
|
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
|
||||||
15
astro.config.mjs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// @ts-check
|
||||||
|
import { defineConfig, envField } from 'astro/config';
|
||||||
|
|
||||||
|
import cloudflare from '@astrojs/cloudflare';
|
||||||
|
|
||||||
|
// https://astro.build/config
|
||||||
|
export default defineConfig({
|
||||||
|
adapter: cloudflare(),
|
||||||
|
env: {
|
||||||
|
schema: {
|
||||||
|
TWY_API_URL: envField.string({ context: "server", access: "public", default: "https://api.recroom.baby" }),
|
||||||
|
TWY_CDN_URL: envField.string({ context: "server", access: "public", default: "https://cdn.recroom.baby" })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
4751
package-lock.json
generated
Normal file
21
package.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "tenwholeyears-web",
|
||||||
|
"type": "module",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22.12.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "astro dev",
|
||||||
|
"build": "astro build",
|
||||||
|
"preview": "astro preview",
|
||||||
|
"astro": "astro",
|
||||||
|
"resize:image": "powershell -ExecutionPolicy Bypass -File ./tools/resize-images.ps1",
|
||||||
|
"generate-types": "wrangler types"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@astrojs/cloudflare": "^14.0.1",
|
||||||
|
"astro": "^7.0.3",
|
||||||
|
"wrangler": "^4.105.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
1109
public/css/bootstrap-responsive.css
vendored
Normal file
9
public/css/bootstrap-responsive.min.css
vendored
Normal file
6167
public/css/bootstrap.css
vendored
Normal file
9
public/css/bootstrap.min.css
vendored
Normal file
340
public/css/styles.css
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
/* Jumbotrons
|
||||||
|
-------------------------------------------------- */
|
||||||
|
|
||||||
|
/* Base class
|
||||||
|
------------------------- */
|
||||||
|
.jumbotron {
|
||||||
|
position: relative;
|
||||||
|
padding: 40px 0;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
text-shadow: 0 1px 3px rgba(0, 0, 0, .4), 0 0 30px rgba(0, 0, 0, .075);
|
||||||
|
background: #000;
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#020031', endColorstr='#6d3353', GradientType=1);
|
||||||
|
/* IE6-9 fallback on horizontal gradient */
|
||||||
|
-webkit-box-shadow: inset 0 3px 7px rgba(0, 0, 0, .2), inset 0 -3px 7px rgba(0, 0, 0, .2);
|
||||||
|
-moz-box-shadow: inset 0 3px 7px rgba(0, 0, 0, .2), inset 0 -3px 7px rgba(0, 0, 0, .2);
|
||||||
|
box-shadow: inset 0 3px 7px rgba(0, 0, 0, .2), inset 0 -3px 7px rgba(0, 0, 0, .2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.jumbotron h1 {
|
||||||
|
font-size: 80px;
|
||||||
|
font-weight: bold;
|
||||||
|
letter-spacing: -1px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jumbotron p {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 300;
|
||||||
|
line-height: 1.25;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Link styles (used on .masthead-links as well) */
|
||||||
|
.jumbotron a {
|
||||||
|
color: #fff;
|
||||||
|
color: rgba(255, 255, 255, .5);
|
||||||
|
-webkit-transition: all .2s ease-in-out;
|
||||||
|
-moz-transition: all .2s ease-in-out;
|
||||||
|
transition: all .2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jumbotron a:hover {
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 0 0 10px rgba(255, 255, 255, .25);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Download button */
|
||||||
|
.masthead .btn {
|
||||||
|
padding: 19px 24px;
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 200;
|
||||||
|
color: #fff;
|
||||||
|
/* redeclare to override the `.jumbotron a` */
|
||||||
|
border: 0;
|
||||||
|
-webkit-border-radius: 6px;
|
||||||
|
-moz-border-radius: 6px;
|
||||||
|
border-radius: 6px;
|
||||||
|
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 5px rgba(0, 0, 0, .25);
|
||||||
|
-moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 5px rgba(0, 0, 0, .25);
|
||||||
|
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 5px rgba(0, 0, 0, .25);
|
||||||
|
-webkit-transition: none;
|
||||||
|
-moz-transition: none;
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.masthead .btn:hover {
|
||||||
|
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 5px rgba(0, 0, 0, .25);
|
||||||
|
-moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 5px rgba(0, 0, 0, .25);
|
||||||
|
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 5px rgba(0, 0, 0, .25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.masthead .btn:active {
|
||||||
|
-webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, .1), 0 1px 0 rgba(255, 255, 255, .1);
|
||||||
|
-moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, .1), 0 1px 0 rgba(255, 255, 255, .1);
|
||||||
|
box-shadow: inset 0 2px 4px rgba(0, 0, 0, .1), 0 1px 0 rgba(255, 255, 255, .1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Pattern overlay
|
||||||
|
------------------------- */
|
||||||
|
.jumbotron .container {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jumbotron:after {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
background-image: url(/img/banner.gif);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
background-size: cover;
|
||||||
|
opacity: .6;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (-webkit-min-device-pixel-ratio: 2),
|
||||||
|
only screen and (min--moz-device-pixel-ratio: 2),
|
||||||
|
only screen and (-o-min-device-pixel-ratio: 2/1) {
|
||||||
|
|
||||||
|
.jumbotron:after {
|
||||||
|
background-size: 150px 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Masthead (docs home)
|
||||||
|
------------------------- */
|
||||||
|
.masthead {
|
||||||
|
padding: 70px 0 80px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.masthead h1 {
|
||||||
|
font-size: 120px;
|
||||||
|
line-height: 1;
|
||||||
|
letter-spacing: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.masthead-logo {
|
||||||
|
display: block;
|
||||||
|
width: 430px;
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
margin: 0 auto 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.masthead-title {
|
||||||
|
margin: 0 0 18px;
|
||||||
|
font-size: 42px;
|
||||||
|
line-height: 1;
|
||||||
|
letter-spacing: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.masthead p {
|
||||||
|
font-weight: 200;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
.masthead-download {
|
||||||
|
margin-top: 30px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.masthead-download .btn {
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.masthead-download .subtext {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.masthead-download .subtext span {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.masthead-platforms {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Home page
|
||||||
|
------------------------- */
|
||||||
|
.home-section {
|
||||||
|
padding: 30px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.intro-section {
|
||||||
|
padding-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-photo {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.archive-feature {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.archive-feature h3 {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.archive-feature .thumbnail {
|
||||||
|
display: block;
|
||||||
|
max-height: 200px;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-section .thumbnail img {
|
||||||
|
max-height: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-section .caption {
|
||||||
|
margin-top: 8px;
|
||||||
|
color: #777;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.credit-entry {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.credit-avatar {
|
||||||
|
width: 72px;
|
||||||
|
height: 72px;
|
||||||
|
margin-right: 16px;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.credit-entry h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.credit-entry p {
|
||||||
|
margin-bottom: 0;
|
||||||
|
color: #777;
|
||||||
|
}
|
||||||
|
|
||||||
|
.closing-section {
|
||||||
|
padding-bottom: 50px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.closing-section p {
|
||||||
|
max-width: 720px;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tablet to desktop
|
||||||
|
------------------------- */
|
||||||
|
@media (min-width: 768px) and (max-width: 979px) {
|
||||||
|
.jumbotron {
|
||||||
|
margin-top: -20px; /* Offset bottom margin on .navbar */
|
||||||
|
}
|
||||||
|
|
||||||
|
.jumbotron .container {
|
||||||
|
width: initial !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tablet
|
||||||
|
------------------------- */
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.jumbotron {
|
||||||
|
padding: 40px 20px;
|
||||||
|
margin-top: -20px; /* Offset bottom margin on .navbar */
|
||||||
|
margin-right: -20px;
|
||||||
|
margin-left: -20px;
|
||||||
|
}
|
||||||
|
.masthead h1 {
|
||||||
|
font-size: 90px;
|
||||||
|
}
|
||||||
|
.masthead-logo {
|
||||||
|
width: 360px;
|
||||||
|
}
|
||||||
|
.masthead-title {
|
||||||
|
font-size: 36px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
.masthead p,
|
||||||
|
.masthead .btn {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
.archive-feature .thumbnail,
|
||||||
|
.gallery-section .thumbnail img {
|
||||||
|
max-height: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Landscape phones
|
||||||
|
------------------------- */
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
/* Downsize the jumbotrons */
|
||||||
|
.jumbotron h1 {
|
||||||
|
font-size: 45px;
|
||||||
|
}
|
||||||
|
.masthead-logo {
|
||||||
|
width: 260px;
|
||||||
|
}
|
||||||
|
.masthead-title {
|
||||||
|
font-size: 28px;
|
||||||
|
}
|
||||||
|
.jumbotron p,
|
||||||
|
.jumbotron .btn {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
.jumbotron .btn {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.archive-feature .thumbnail,
|
||||||
|
.gallery-section .thumbnail img {
|
||||||
|
max-height: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Footer */
|
||||||
|
footer {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer .links * {
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer .links {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer .about * {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.download-card h3 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-patch-notes {
|
||||||
|
padding: 20px 0 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.patch-note h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
public/img/banner.gif
Normal file
|
After Width: | Height: | Size: 7.0 MiB |
BIN
public/img/banner.png
Normal file
|
After Width: | Height: | Size: 801 KiB |
BIN
public/img/credits/9021007.gif
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
public/img/credits/cheezit.png
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
public/img/credits/fexlar.png
Normal file
|
After Width: | Height: | Size: 305 KiB |
BIN
public/img/credits/funko.png
Normal file
|
After Width: | Height: | Size: 513 KiB |
BIN
public/img/credits/gabethefirst.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
public/img/credits/happyvr.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
public/img/credits/historyeraser.png
Normal file
|
After Width: | Height: | Size: 542 KiB |
BIN
public/img/credits/magix.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
public/img/credits/magolor.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
public/img/credits/mia.png
Normal file
|
After Width: | Height: | Size: 224 KiB |
BIN
public/img/credits/milky.png
Normal file
|
After Width: | Height: | Size: 360 KiB |
BIN
public/img/credits/nex.png
Normal file
|
After Width: | Height: | Size: 188 KiB |
BIN
public/img/credits/ryancr.png
Normal file
|
After Width: | Height: | Size: 209 KiB |
BIN
public/img/credits/socoolman.png
Normal file
|
After Width: | Height: | Size: 129 KiB |
BIN
public/img/credits/splooty.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
public/img/credits/starcenturion.gif
Normal file
|
After Width: | Height: | Size: 3.6 MiB |
BIN
public/img/credits/tagcomputing.png
Normal file
|
After Width: | Height: | Size: 112 KiB |
BIN
public/img/credits/wayne.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
public/img/glyphicons-halflings-white.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
public/img/glyphicons-halflings.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
public/img/home/card-horseshoes.png
Normal file
|
After Width: | Height: | Size: 151 KiB |
BIN
public/img/home/card-recroom.png
Normal file
|
After Width: | Height: | Size: 194 KiB |
BIN
public/img/home/feature.png
Normal file
|
After Width: | Height: | Size: 744 KiB |
BIN
public/img/logo-tenwholeyears.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
public/img/platforms/platform-linux.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
public/img/platforms/platform-oculus.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
public/img/platforms/platform-windows.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
public/img/videos/boombox.gif
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
public/img/videos/hide.gif
Normal file
|
After Width: | Height: | Size: 3.8 MiB |
BIN
public/img/videos/trickshot.gif
Normal file
|
After Width: | Height: | Size: 3.4 MiB |
BIN
public/img/videos/wave.gif
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
2280
public/js/bootstrap.js
vendored
Normal file
6
public/js/bootstrap.min.js
vendored
Normal file
2
public/js/jquery-4.0.0.min.js
vendored
Normal file
15
src/components/Footer.astro
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<footer class="container">
|
||||||
|
<hr />
|
||||||
|
<div class="links">
|
||||||
|
<a href="https://ko-fi.com/recroomarchive">Donate</a>
|
||||||
|
<span class="mx-1.5">|</span>
|
||||||
|
<a href="https://discord.gg/Mh725mfC4j" class="underline">Discord</a>
|
||||||
|
</div>
|
||||||
|
<div class="about">
|
||||||
|
<span>© {new Date().getFullYear()} RecRoomArchive</span>
|
||||||
|
<span>
|
||||||
|
TenWholeYears is not affiliated with Rec Room Inc. All rights
|
||||||
|
belong to their respective owners.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
25
src/layouts/Layout.astro
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
import Footer from "../components/Footer.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>TenWholeYears</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link href="/css/bootstrap.min.css" rel="stylesheet" media="screen" />
|
||||||
|
<link
|
||||||
|
href="/css/bootstrap-responsive.min.css"
|
||||||
|
rel="stylesheet"
|
||||||
|
media="screen"
|
||||||
|
/>
|
||||||
|
<link href="/css/styles.css" rel="stylesheet" />
|
||||||
|
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<slot />
|
||||||
|
<Footer />
|
||||||
|
<script src="/js/jquery-4.0.0.min.js" is:inline></script>
|
||||||
|
<script src="/js/bootstrap.min.js" is:inline></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
53
src/lib/api.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { TWY_API_URL } from 'astro:env/server';
|
||||||
|
|
||||||
|
import type { LatestBuildsResponse, PatchNote } from './types/api/builds';
|
||||||
|
import { ApiResult } from './types/api';
|
||||||
|
|
||||||
|
const BUILDS_V1 = "api/builds/v1";
|
||||||
|
|
||||||
|
export const api = {
|
||||||
|
builds: {
|
||||||
|
v1: {
|
||||||
|
async getLatestVersions(): Promise<ApiResult<LatestBuildsResponse>> {
|
||||||
|
const response = await fetch(`${TWY_API_URL}/${BUILDS_V1}/latest`, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
cache: "no-store",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error("Failed to load latest versions.", {
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
});
|
||||||
|
return ApiResult.failure<LatestBuildsResponse>("Failed to load latest versions.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var obj = (await response.json()) as LatestBuildsResponse;
|
||||||
|
|
||||||
|
return ApiResult.success(obj);
|
||||||
|
},
|
||||||
|
async getPatchNotes(): Promise<ApiResult<PatchNote[]>> {
|
||||||
|
const response = await fetch(`${TWY_API_URL}/${BUILDS_V1}/patchNotes`, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
cache: "no-store",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error("Failed to load account.", {
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
});
|
||||||
|
return ApiResult.failure<PatchNote[]>("Failed to load latest versions.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var obj = (await response.json()) as PatchNote[];
|
||||||
|
|
||||||
|
return ApiResult.success(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/lib/types/api/builds.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export interface LatestBuildsResponse {
|
||||||
|
latestWindowsVersion: string;
|
||||||
|
latestLinuxVersion: string;
|
||||||
|
latestMetaVersion: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PatchNote {
|
||||||
|
version: string;
|
||||||
|
date: string;
|
||||||
|
changes: string[];
|
||||||
|
}
|
||||||
13
src/lib/types/api/index.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
export type ApiResult<T> =
|
||||||
|
| { success: true; data: T }
|
||||||
|
| { success: false; error: string };
|
||||||
|
|
||||||
|
export const ApiResult = {
|
||||||
|
success<T>(data: T): ApiResult<T> {
|
||||||
|
return { success: true, data };
|
||||||
|
},
|
||||||
|
|
||||||
|
failure<T>(error: string): ApiResult<T> {
|
||||||
|
return { success: false, error };
|
||||||
|
}
|
||||||
|
};
|
||||||
98
src/pages/downloads.astro
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
---
|
||||||
|
import Layout from "../layouts/Layout.astro";
|
||||||
|
|
||||||
|
const downloads = [
|
||||||
|
{
|
||||||
|
platform: "Windows",
|
||||||
|
version: "v1.0.3",
|
||||||
|
href: "#",
|
||||||
|
icon: "/img/platforms/platform-windows.png",
|
||||||
|
checksum: "checksum: REPLACE_ME",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
platform: "Linux",
|
||||||
|
version: "v1.0.3",
|
||||||
|
href: "#",
|
||||||
|
icon: "/img/platforms/platform-linux.png",
|
||||||
|
checksum: "checksum: REPLACE_ME",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
platform: "Oculus Quest",
|
||||||
|
version: "v1.0.3",
|
||||||
|
href: "#",
|
||||||
|
icon: "/img/platforms/platform-oculus.png",
|
||||||
|
checksum: "checksum: REPLACE_ME",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const patchNotes = [
|
||||||
|
{
|
||||||
|
version: "v1.0.3",
|
||||||
|
date: "6/26/2026",
|
||||||
|
changes: [
|
||||||
|
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
|
||||||
|
"Nunc at augue eu nulla aliquet consectetur et in magna.",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
];
|
||||||
|
---
|
||||||
|
|
||||||
|
<Layout>
|
||||||
|
<div class="container">
|
||||||
|
<div class="page-header">
|
||||||
|
<p><a href="/">← Back to home</a></p>
|
||||||
|
<h1>Downloads</h1>
|
||||||
|
</div>
|
||||||
|
<p class="lead">
|
||||||
|
Choose the build for your platform.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
{downloads.map((download) => (
|
||||||
|
<div class="span4">
|
||||||
|
<div class="well text-center download-card">
|
||||||
|
<p>
|
||||||
|
<img
|
||||||
|
src={download.icon}
|
||||||
|
alt=""
|
||||||
|
width="72"
|
||||||
|
height="72"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<h3>{download.platform}</h3>
|
||||||
|
<p>{download.version}</p>
|
||||||
|
{/* <p class="muted">
|
||||||
|
<code>{download.checksum}</code>
|
||||||
|
</p> */}
|
||||||
|
<p>
|
||||||
|
<a
|
||||||
|
href={download.href}
|
||||||
|
class="btn btn-primary"
|
||||||
|
aria-label={`Download for ${download.platform}`}
|
||||||
|
>
|
||||||
|
Download
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="download-patch-notes">
|
||||||
|
<div class="page-header">
|
||||||
|
<h2>Patch Notes</h2>
|
||||||
|
</div>
|
||||||
|
{patchNotes.map((patchNote) => (
|
||||||
|
<div class="well patch-note">
|
||||||
|
<h3>{patchNote.version}</h3>
|
||||||
|
<p class="muted">{patchNote.date}</p>
|
||||||
|
<ul>
|
||||||
|
{patchNote.changes.map((change) => (
|
||||||
|
<li>{change}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
278
src/pages/index.astro
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
---
|
||||||
|
import Layout from "../layouts/Layout.astro";
|
||||||
|
|
||||||
|
const credits = [
|
||||||
|
{
|
||||||
|
name: "splootybean",
|
||||||
|
role: "Project Lead",
|
||||||
|
image: "/img/credits/splooty.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "fexlar",
|
||||||
|
role: "Server Developer",
|
||||||
|
image: "/img/credits/fexlar.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nex",
|
||||||
|
role: "Developer",
|
||||||
|
image: "/img/credits/nex.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Magix",
|
||||||
|
role: "Developer",
|
||||||
|
image: "/img/credits/magix.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Milky",
|
||||||
|
role: "Developer",
|
||||||
|
image: "/img/credits/milky.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SoCoolMan",
|
||||||
|
role: "Developer",
|
||||||
|
image: "/img/credits/socoolman.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "historyeraser",
|
||||||
|
role: "Logo Designer",
|
||||||
|
image: "/img/credits/historyeraser.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Wayne",
|
||||||
|
role: "Artist / Logo Designer",
|
||||||
|
image: "/img/credits/wayne.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "RyanCR",
|
||||||
|
role: "Artist / Composer",
|
||||||
|
image: "/img/credits/ryancr.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Funko",
|
||||||
|
role: "Artist",
|
||||||
|
image: "/img/credits/funko.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Cheezit",
|
||||||
|
role: "Artist",
|
||||||
|
image: "/img/credits/cheezit.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "9021007",
|
||||||
|
role: "Artist",
|
||||||
|
image: "/img/credits/9021007.gif",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Star Centurion",
|
||||||
|
role: "Artist",
|
||||||
|
image: "/img/credits/starcenturion.gif",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "GabeTheFirst",
|
||||||
|
role: "Artist",
|
||||||
|
image: "/img/credits/gabethefirst.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "HappyVR",
|
||||||
|
role: "Leaf Shader",
|
||||||
|
image: "/img/credits/happyvr.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Magolor",
|
||||||
|
role: "Test Squad",
|
||||||
|
image: "/img/credits/magolor.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TagComputing",
|
||||||
|
role: "Test Squad",
|
||||||
|
image: "/img/credits/tagcomputing.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mia",
|
||||||
|
role: "Test Squad",
|
||||||
|
image: "/img/credits/mia.png",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
---
|
||||||
|
|
||||||
|
<Layout>
|
||||||
|
<div class="jumbotron masthead">
|
||||||
|
<div class="container">
|
||||||
|
<img
|
||||||
|
src="/img/logo-tenwholeyears.png"
|
||||||
|
alt="asdasd"
|
||||||
|
class="masthead-logo"
|
||||||
|
/>
|
||||||
|
<h1 class="masthead-title">TenWholeYears</h1>
|
||||||
|
<p>A love letter to Rec Room on its true 10th anniversary</p>
|
||||||
|
<div class="masthead-download">
|
||||||
|
<a href="/downloads/" class="btn btn-primary btn-large">
|
||||||
|
Download for Windows x64
|
||||||
|
</a>
|
||||||
|
<div class="subtext">
|
||||||
|
<span>or</span>
|
||||||
|
<a href="/downloads/" class="masthead-platforms"
|
||||||
|
>Other Platforms</a
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container">
|
||||||
|
<section class="home-section intro-section">
|
||||||
|
<div class="row">
|
||||||
|
<div class="span6">
|
||||||
|
<h2>What is TenWholeYears?</h2>
|
||||||
|
<p class="lead">
|
||||||
|
TenWholeYears is a love letter to Rec Room and the
|
||||||
|
community that made it special. Created to celebrate Rec
|
||||||
|
Room's true tenth anniversary, we invite everyone to
|
||||||
|
visit the first release of Rec Room, explore activities
|
||||||
|
that never made it into the game, and reflect on how far
|
||||||
|
Rec Room and it's community has progressed over the last
|
||||||
|
ten years.
|
||||||
|
</p>
|
||||||
|
<p class="lead">
|
||||||
|
Read about the inspiration behind TenWholeYears <a
|
||||||
|
href="#">here</a
|
||||||
|
>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="span6">
|
||||||
|
<img
|
||||||
|
src="/img/home/feature.png"
|
||||||
|
alt="Beta Frisbee Activity"
|
||||||
|
class="thumbnail feature-photo"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="home-section">
|
||||||
|
<div class="page-header">
|
||||||
|
<h2>Explore the Game</h2>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="span4">
|
||||||
|
<div class="archive-feature">
|
||||||
|
<img
|
||||||
|
src="/img/home/card-horseshoes.png"
|
||||||
|
alt="Scrapped Horseshoes Activity"
|
||||||
|
class="thumbnail"
|
||||||
|
/>
|
||||||
|
<h3>Scrapped Activities</h3>
|
||||||
|
<p>
|
||||||
|
Explore prototype activities and unfinished ideas
|
||||||
|
that never made it into Rec Room. These activities
|
||||||
|
have been restored to show a glimpse into
|
||||||
|
what could of existed.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="span4">
|
||||||
|
<div class="archive-feature">
|
||||||
|
<img
|
||||||
|
src="/img/home/card-recroom.png"
|
||||||
|
alt="Beta LockerRoom (RecRoom)"
|
||||||
|
class="thumbnail"
|
||||||
|
/>
|
||||||
|
<h3>Lorem Ipsum</h3>
|
||||||
|
<p>
|
||||||
|
Nam fermentum ut lectus et placerat. Nulla nibh
|
||||||
|
nulla, rutrum at neque at, vulputate consequat.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="span4">
|
||||||
|
<div class="archive-feature">
|
||||||
|
<img
|
||||||
|
src="/img/home/card-horseshoes.png"
|
||||||
|
alt="Placeholder screenshots and media image"
|
||||||
|
class="thumbnail"
|
||||||
|
/>
|
||||||
|
<h3>Lorem Ipsum</h3>
|
||||||
|
<p>
|
||||||
|
Duis efficitur ligula non sem hendrerit interdum.
|
||||||
|
Donec facilisis efficitur malesuada. Nullam
|
||||||
|
venenatis.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="home-section gallery-section">
|
||||||
|
<div class="page-header">
|
||||||
|
<h2>Community Handheld Videos</h2>
|
||||||
|
</div>
|
||||||
|
<ul class="thumbnails">
|
||||||
|
<li class="span3">
|
||||||
|
<a href="/img/videos/boombox.gif" class="thumbnail">
|
||||||
|
<img
|
||||||
|
src="/img/videos/boombox.gif"
|
||||||
|
alt="splootybean grooving to the boombox"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<p class="caption">Filmed by <b>@fexlar</b></p>
|
||||||
|
</li>
|
||||||
|
<li class="span3">
|
||||||
|
<a href="/img/videos/hide.gif" class="thumbnail">
|
||||||
|
<img
|
||||||
|
src="/img/videos/hide.gif"
|
||||||
|
alt="guy hiding"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<p class="caption">Filmed by <b>@splootybean</b></p>
|
||||||
|
</li>
|
||||||
|
<li class="span3">
|
||||||
|
<a href="#" class="thumbnail">
|
||||||
|
<img
|
||||||
|
src="/img/videos/trickshot.gif"
|
||||||
|
alt="dodgeball trickshot"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<p class="caption">Filmed by <b>@_.golden_.</b></p>
|
||||||
|
</li>
|
||||||
|
<li class="span3">
|
||||||
|
<a href="#" class="thumbnail">
|
||||||
|
<img
|
||||||
|
src="/img/videos/wave.gif"
|
||||||
|
alt="hello there!"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<p class="caption">Filmed by <b>@splootybean</b></p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="home-section credits-section">
|
||||||
|
<div class="page-header">
|
||||||
|
<h2>Credits</h2>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
{
|
||||||
|
credits.map((credit) => (
|
||||||
|
<div class="span4">
|
||||||
|
<div class="credit-entry">
|
||||||
|
<img
|
||||||
|
src={credit.image}
|
||||||
|
alt={`${credit.name} pfp`}
|
||||||
|
class="credit-avatar"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<h3>{credit.name}</h3>
|
||||||
|
<p>{credit.role}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
56
tools/README.md
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Image Tools
|
||||||
|
|
||||||
|
Use `resize-images.ps1` to make web-sized PNGs for the index page.
|
||||||
|
|
||||||
|
Run commands from the project root.
|
||||||
|
|
||||||
|
## Presets
|
||||||
|
|
||||||
|
| Preset | Size | Use |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `logo` | 1200px wide | Source logo used in the masthead |
|
||||||
|
| `masthead` | 860px wide | Large feature image or decorative logo |
|
||||||
|
| `feature` | 900x506 | Main "What is TenWholeYears?" image |
|
||||||
|
| `card` | 600x338 | Three-column Explore section images |
|
||||||
|
| `gallery` | 480x270 | Gallery thumbnails |
|
||||||
|
| `banner` | 1920x1080 | Large background or hero screenshots |
|
||||||
|
| `platform-icon` | 256x256 | Square padded platform logos for the downloads page |
|
||||||
|
| `profile` | 512x512 | Square cropped profile pictures for credits |
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Resize one image for the intro feature section:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\tools\resize-images.ps1 -InputPath "C:\Users\Holden\Desktop\screenshot.png" -Preset feature
|
||||||
|
```
|
||||||
|
|
||||||
|
Resize one image and choose the output filename:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\tools\resize-images.ps1 -InputPath "C:\Users\Holden\Desktop\screenshot.png" -Preset gallery -OutputName "gallery-museum.png"
|
||||||
|
```
|
||||||
|
|
||||||
|
Resize every PNG/JPG/BMP in a folder:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\tools\resize-images.ps1 -InputPath "C:\Users\Holden\Desktop\recroom-screenshots" -Preset gallery
|
||||||
|
```
|
||||||
|
|
||||||
|
Resize and crop a profile picture for the credits section:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\tools\resize-images.ps1 -InputPath "C:\Users\Holden\Desktop\person.png" -Preset profile -OutputDir "public/img/profiles" -OutputName "person.png"
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, output files go here:
|
||||||
|
|
||||||
|
```text
|
||||||
|
public/img/generated/
|
||||||
|
```
|
||||||
|
|
||||||
|
Then use them in Astro like this:
|
||||||
|
|
||||||
|
```astro
|
||||||
|
<img src="/img/generated/gallery-museum.png" alt="Describe the screenshot" />
|
||||||
|
```
|
||||||
174
tools/resize-images.ps1
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$InputPath,
|
||||||
|
|
||||||
|
[ValidateSet("logo", "masthead", "feature", "card", "gallery", "banner", "platform-icon", "profile")]
|
||||||
|
[string]$Preset = "gallery",
|
||||||
|
|
||||||
|
[string]$OutputDir = "public/img/generated",
|
||||||
|
|
||||||
|
[string]$OutputName = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
$repoRoot = Resolve-Path (Join-Path $PSScriptRoot "..")
|
||||||
|
$resolvedInput = Resolve-Path $InputPath
|
||||||
|
|
||||||
|
if ([System.IO.Path]::IsPathRooted($OutputDir)) {
|
||||||
|
$resolvedOutputDir = $OutputDir
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$resolvedOutputDir = Join-Path $repoRoot $OutputDir
|
||||||
|
}
|
||||||
|
|
||||||
|
New-Item -ItemType Directory -Path $resolvedOutputDir -Force | Out-Null
|
||||||
|
|
||||||
|
$presets = @{
|
||||||
|
logo = @{
|
||||||
|
Width = 1200
|
||||||
|
Height = 0
|
||||||
|
Mode = "fit"
|
||||||
|
Suffix = "logo"
|
||||||
|
}
|
||||||
|
masthead = @{
|
||||||
|
Width = 860
|
||||||
|
Height = 0
|
||||||
|
Mode = "fit"
|
||||||
|
Suffix = "masthead"
|
||||||
|
}
|
||||||
|
feature = @{
|
||||||
|
Width = 900
|
||||||
|
Height = 506
|
||||||
|
Mode = "crop"
|
||||||
|
Suffix = "feature"
|
||||||
|
}
|
||||||
|
card = @{
|
||||||
|
Width = 600
|
||||||
|
Height = 338
|
||||||
|
Mode = "crop"
|
||||||
|
Suffix = "card"
|
||||||
|
}
|
||||||
|
gallery = @{
|
||||||
|
Width = 480
|
||||||
|
Height = 270
|
||||||
|
Mode = "crop"
|
||||||
|
Suffix = "gallery"
|
||||||
|
}
|
||||||
|
banner = @{
|
||||||
|
Width = 1920
|
||||||
|
Height = 1080
|
||||||
|
Mode = "crop"
|
||||||
|
Suffix = "banner"
|
||||||
|
}
|
||||||
|
"platform-icon" = @{
|
||||||
|
Width = 256
|
||||||
|
Height = 256
|
||||||
|
Mode = "pad"
|
||||||
|
Suffix = "platform-icon"
|
||||||
|
}
|
||||||
|
profile = @{
|
||||||
|
Width = 512
|
||||||
|
Height = 512
|
||||||
|
Mode = "crop"
|
||||||
|
Suffix = "profile"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-ImageFiles {
|
||||||
|
param([string]$Path)
|
||||||
|
|
||||||
|
$item = Get-Item -LiteralPath $Path
|
||||||
|
|
||||||
|
if ($item.PSIsContainer) {
|
||||||
|
return Get-ChildItem -LiteralPath $Path -File |
|
||||||
|
Where-Object { $_.Extension -match "^\.(png|jpg|jpeg|bmp)$" }
|
||||||
|
}
|
||||||
|
|
||||||
|
return @($item)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Resize-Image {
|
||||||
|
param(
|
||||||
|
[string]$SourcePath,
|
||||||
|
[string]$DestinationPath,
|
||||||
|
[int]$TargetWidth,
|
||||||
|
[int]$TargetHeight,
|
||||||
|
[string]$Mode
|
||||||
|
)
|
||||||
|
|
||||||
|
Add-Type -AssemblyName System.Drawing
|
||||||
|
|
||||||
|
$image = [System.Drawing.Image]::FromFile($SourcePath)
|
||||||
|
|
||||||
|
if ($TargetHeight -eq 0) {
|
||||||
|
$TargetHeight = [int][Math]::Round($image.Height * $TargetWidth / $image.Width)
|
||||||
|
}
|
||||||
|
|
||||||
|
$bitmap = New-Object System.Drawing.Bitmap $TargetWidth, $TargetHeight
|
||||||
|
$graphics = [System.Drawing.Graphics]::FromImage($bitmap)
|
||||||
|
|
||||||
|
$graphics.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
|
||||||
|
$graphics.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality
|
||||||
|
$graphics.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality
|
||||||
|
$graphics.CompositingQuality = [System.Drawing.Drawing2D.CompositingQuality]::HighQuality
|
||||||
|
$graphics.Clear([System.Drawing.Color]::Transparent)
|
||||||
|
|
||||||
|
if ($Mode -eq "crop") {
|
||||||
|
$scale = [Math]::Max($TargetWidth / $image.Width, $TargetHeight / $image.Height)
|
||||||
|
$sourceWidth = [int][Math]::Round($TargetWidth / $scale)
|
||||||
|
$sourceHeight = [int][Math]::Round($TargetHeight / $scale)
|
||||||
|
$sourceX = [int][Math]::Max(0, [Math]::Round(($image.Width - $sourceWidth) / 2))
|
||||||
|
$sourceY = [int][Math]::Max(0, [Math]::Round(($image.Height - $sourceHeight) / 2))
|
||||||
|
|
||||||
|
$sourceRect = New-Object System.Drawing.Rectangle $sourceX, $sourceY, $sourceWidth, $sourceHeight
|
||||||
|
$destRect = New-Object System.Drawing.Rectangle 0, 0, $TargetWidth, $TargetHeight
|
||||||
|
$graphics.DrawImage($image, $destRect, $sourceRect, [System.Drawing.GraphicsUnit]::Pixel)
|
||||||
|
}
|
||||||
|
elseif ($Mode -eq "pad") {
|
||||||
|
$scale = [Math]::Min($TargetWidth / $image.Width, $TargetHeight / $image.Height)
|
||||||
|
$drawWidth = [int][Math]::Round($image.Width * $scale)
|
||||||
|
$drawHeight = [int][Math]::Round($image.Height * $scale)
|
||||||
|
$drawX = [int][Math]::Round(($TargetWidth - $drawWidth) / 2)
|
||||||
|
$drawY = [int][Math]::Round(($TargetHeight - $drawHeight) / 2)
|
||||||
|
|
||||||
|
$destRect = New-Object System.Drawing.Rectangle $drawX, $drawY, $drawWidth, $drawHeight
|
||||||
|
$graphics.DrawImage($image, $destRect)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$graphics.DrawImage($image, 0, 0, $TargetWidth, $TargetHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
$bitmap.Save($DestinationPath, [System.Drawing.Imaging.ImageFormat]::Png)
|
||||||
|
|
||||||
|
$graphics.Dispose()
|
||||||
|
$bitmap.Dispose()
|
||||||
|
$image.Dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
$presetConfig = $presets[$Preset]
|
||||||
|
$files = Get-ImageFiles -Path $resolvedInput
|
||||||
|
|
||||||
|
foreach ($file in $files) {
|
||||||
|
$name = if ($OutputName -and $files.Count -eq 1) {
|
||||||
|
$OutputName
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"{0}-{1}.png" -f $file.BaseName, $presetConfig.Suffix
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([System.IO.Path]::GetExtension($name) -eq "") {
|
||||||
|
$name = "$name.png"
|
||||||
|
}
|
||||||
|
|
||||||
|
$destination = Join-Path $resolvedOutputDir $name
|
||||||
|
|
||||||
|
Resize-Image `
|
||||||
|
-SourcePath $file.FullName `
|
||||||
|
-DestinationPath $destination `
|
||||||
|
-TargetWidth $presetConfig.Width `
|
||||||
|
-TargetHeight $presetConfig.Height `
|
||||||
|
-Mode $presetConfig.Mode
|
||||||
|
|
||||||
|
Write-Output ("{0} -> {1}" -f $file.Name, $destination)
|
||||||
|
}
|
||||||
11
tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"extends": "astro/tsconfigs/strict",
|
||||||
|
"include": [
|
||||||
|
".astro/types.d.ts",
|
||||||
|
"**/*",
|
||||||
|
"./worker-configuration.d.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"dist"
|
||||||
|
]
|
||||||
|
}
|
||||||
14
wrangler.jsonc
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/wrangler/config-schema.json",
|
||||||
|
"compatibility_date": "2026-06-27",
|
||||||
|
"compatibility_flags": ["global_fetch_strictly_public"],
|
||||||
|
"name": "tenwholeyears-web",
|
||||||
|
"main": "@astrojs/cloudflare/entrypoints/server",
|
||||||
|
"assets": {
|
||||||
|
"directory": "./dist",
|
||||||
|
"binding": "ASSETS"
|
||||||
|
},
|
||||||
|
"observability": {
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
}
|
||||||