Synced from site

This commit is contained in:
lovebird 2025-03-12 18:53:16 +01:00
parent 3e13363c6c
commit 6541d83fba
301 changed files with 31858 additions and 30212 deletions

View File

@ -0,0 +1,446 @@
{
"$ref": "#/definitions/projects",
"definitions": {
"projects": {
"type": "object",
"properties": {
"cart_id": {
"type": "string"
},
"code": {
"type": "string"
},
"price": {
"type": "number"
},
"cscartCats": {
"type": "array",
"items": {
"type": "number"
}
},
"cscartId": {
"type": "number"
},
"vendorId": {
"type": "number"
},
"shipping": {
"type": "object",
"properties": {
"price": {
"type": "number"
},
"transit": {
"type": "number"
},
"handling": {
"type": "number"
}
},
"additionalProperties": false,
"default": {
"price": 0,
"transit": 12,
"handling": 2
}
},
"manufacturing": {
"type": "object",
"properties": {
"lead_time": {
"type": "number"
}
},
"additionalProperties": false
},
"replaced_by": {
"type": "string"
},
"alternatives": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"url": {
"type": "string"
},
"type": {
"type": "string"
}
},
"required": [
"name",
"url",
"type"
],
"additionalProperties": false
}
},
"used_by": {
"type": "array",
"items": {
"$ref": "#/definitions/projects/properties/alternatives/items"
}
},
"image": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"url": {
"type": "string"
},
"thumb": {
"type": "string"
},
"responsive": {
"type": "string"
},
"meta": {},
"keywords": {
"type": "array",
"items": {
"type": "string"
}
},
"description": {
"type": "string"
},
"alt": {
"type": "string"
},
"title": {
"type": "string"
},
"height": {
"type": "number"
},
"width": {
"type": "number"
},
"order": {
"type": "number"
},
"exif": {
"type": "object",
"properties": {
"file": {},
"jfif": {},
"exif": {},
"gps": {}
},
"additionalProperties": false
}
},
"required": [
"url"
],
"additionalProperties": false
},
"name": {
"type": "string"
},
"flags": {
"type": "number"
},
"edrawings": {
"type": "string"
},
"cad": {
"type": "array",
"items": {
"type": "object",
"properties": {
"file": {
"type": "string"
},
"name": {
"type": "string"
},
"configuration": {
"type": "string"
},
"step": {
"type": "string"
},
"model": {
"type": "string"
},
"html": {
"type": "string"
}
},
"required": [
"file",
"name",
"configuration"
],
"additionalProperties": false
},
"default": []
},
"showDimensions": {
"type": "boolean"
},
"showParts": {
"type": "boolean"
},
"slug": {
"type": "string"
},
"keywords": {
"type": "string"
},
"meta_keywords": {
"type": "string"
},
"version": {
"type": "string"
},
"versions": {
"type": "object",
"properties": {
"version": {
"type": "string"
},
"up": {
"type": "string"
},
"down": {
"type": "string"
},
"family": {
"type": "string"
},
"sheet": {
"type": "string"
}
},
"additionalProperties": false
},
"status": {
"type": "string"
},
"authors": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"url": {
"type": "string"
}
},
"required": [
"name",
"url"
],
"additionalProperties": false
}
},
"assets": {
"type": "object",
"properties": {
"gallery": {
"type": "array",
"items": {
"$ref": "#/definitions/projects/properties/image"
}
},
"renderings": {
"type": "array",
"items": {
"$ref": "#/definitions/projects/properties/image"
}
},
"components": {
"type": "array",
"items": {
"$ref": "#/definitions/projects/properties/image"
}
},
"configurations": {
"type": "array",
"items": {
"$ref": "#/definitions/projects/properties/image"
}
},
"showcase": {
"type": "array",
"items": {
"$ref": "#/definitions/projects/properties/image"
}
},
"samples": {
"type": "array",
"items": {
"$ref": "#/definitions/projects/properties/image"
}
}
},
"additionalProperties": false,
"default": {
"gallery": [],
"renderings": [],
"components": [],
"configurations": [],
"showcase": [],
"samples": []
}
},
"resources": {
"type": "string"
},
"tests": {
"type": "array",
"items": {
"$ref": "#/definitions/projects/properties/alternatives/items"
},
"default": []
},
"download": {
"type": "boolean"
},
"gallery": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"glob": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"glob"
],
"additionalProperties": false
},
"default": {
"renderings": {
"glob": [
"*.+(JPG|jpg|png|PNG|gif)"
]
},
"gallery": {
"glob": [
"*.+(JPG|jpg|png|PNG|gif)"
]
},
"components": {
"glob": [
"*.+(JPG|jpg|png|PNG|gif)"
]
},
"configurations": {
"glob": [
"*.+(JPG|jpg|png|PNG|gif)"
]
},
"showcase": {
"glob": [
"*.+(JPG|jpg|png|PNG|gif)"
]
},
"samples": {
"glob": [
"*.+(JPG|jpg|png|PNG|gif)"
]
}
}
},
"Preview3d": {
"type": "boolean"
},
"howto_categories": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"steps": {},
"sourceLanguage": {
"type": "string"
},
"category": {
"type": "string"
},
"product_dimensions": {
"type": "string"
},
"production": {
"type": "object",
"properties": {
"fusion-folder": {
"type": "string"
},
"nc-folder": {
"type": "string"
},
"cam": {
"type": "array",
"items": {
"$ref": "#/definitions/projects/properties/authors/items"
}
}
},
"required": [
"fusion-folder",
"nc-folder",
"cam"
],
"additionalProperties": false
},
"score": {
"type": "number"
},
"body": {
"type": "string"
},
"features": {
"type": "string"
},
"highlights": {
"type": "string"
},
"specs": {
"type": "string"
},
"license": {
"type": "string"
},
"readme": {
"type": "string"
},
"shared": {
"type": "string"
}
},
"required": [
"code",
"manufacturing",
"name",
"slug",
"category"
],
"additionalProperties": true
}
},
"$schema": "http://json-schema.org/draft-07/schema#"
}

View File

@ -296,11 +296,7 @@
}
},
"resources": {
"type": "array",
"items": {
"$ref": "#/definitions/store/properties/alternatives/items"
},
"default": []
"type": "string"
},
"tests": {
"type": "array",
@ -413,6 +409,27 @@
},
"score": {
"type": "number"
},
"body": {
"type": "string"
},
"features": {
"type": "string"
},
"highlights": {
"type": "string"
},
"specs": {
"type": "string"
},
"license": {
"type": "string"
},
"readme": {
"type": "string"
},
"shared": {
"type": "string"
}
},
"required": [

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
{
"_variables": {
"lastUpdateCheck": 1740681917455
"lastUpdateCheck": 1741718719942
}
}

189
README.md
View File

@ -1,111 +1,144 @@
# Williamsburg
# Astro Site Documentation
## Template Integrations
- Tailwind CSS v4
- Astro SEO - Powered by [@astrolib/seo](https://github.com/onwidget/astrolib/tree/main/packages/seo)
- Astro Sitemap - https://docs.astro.build/en/guides/integrations-guide/sitemap/
This README provides comprehensive documentation for this Astro-based website project.
## Template Structure
## 🚀 Project Overview
The template follows a typical Astro project structure. You'll find the following key directories and files:
This is a multilingual website built with [Astro](https://astro.build), a modern static site generator. The site features a blog, content collections, and internationalization support. It uses Tailwind CSS for styling and includes various components and layouts for consistent design.
## 📁 Project Structure
```
/
├── public/
├── public/ # Static assets
├── src/
│ └── pages/
│ └── index.astro
└── package.json
│ ├── components/ # UI components
│ ├── content/ # Content collections
│ ├── layouts/ # Page layouts
│ ├── pages/ # Page routes
│ │ ├── en/ # English pages
│ │ └── [lang]/ # Other language pages
│ ├── i18n/ # Internationalization files
│ └── content.config.ts # Content collection configuration
├── astro.config.mjs # Astro configuration
├── tailwind.config.cjs # Tailwind CSS configuration
└── package.json # Project dependencies
```
- `src/pages/`: Contains `.astro` and `.md` files. Each file becomes a route in your project based on its name.
- `src/components/`: Ideal for placing your Astro/React/Vue/Svelte/Preact components.
- `public/`: For static assets such as images that you want to serve directly.
## 📚 Content Collections
## Commands
The site utilizes Astro's content collections to manage structured content. Collections are defined in `src/content.config.ts` and include:
All commands are run from the root of the project, from a terminal:
- **Blog Posts**: Articles with frontmatter for metadata like title, date, author, etc.
- **Authors**: Information about content authors.
| Command | Action |
| :--------------------- | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:3000` |
| `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 |
Collections are stored in the `src/content/` directory, organized by language:
- `src/content/blog/en/` - English blog posts
- `src/content/blog/[lang]/` - Blog posts in other languages
Learn more - Explore more through Astro's official [documentation](https://docs.astro.build).
## 🧩 Components
------
Updated on 30th December 2024
The site includes reusable UI components located in `src/components/`:
## This update includes:
- Add Tailwind CSS v4 Beta
On this version, Tailwind CSS is now beta version from Tailwind CSS V4, this means that there's no `tailwind.config.mjs` file anymore. From now on, all style will be added on the `css` file. You can find the styles on the `src/styles/global.css` file.
- Astro V5
This update includes Astro V5, which is a major update that includes several new features and improvements.
- **Core Components**: Basic UI elements like buttons, cards, and navigation
- **Blog Components**: Specialized components for the blog section
- **SEO Components**: Components for SEO optimization
- **UI Components**: Interface elements like headers, footers, and menus
These components help maintain consistent design across the site while making development more efficient.
- Astro SEO by @astrolib/seo
This update includes the integration of the Astro SEO package by @astrolib/seo, is an integration that makes managing your SEO easier in Astro projects. It is fully based on the excellent Next SEO library
## 📏 Layouts
## On the next update
Layouts in `src/layouts/` provide consistent page structures:
- Add Image component from Astro
The Astro Image component is coming back to the themes
- **BaseLayout.astro**: The main layout with common elements (header, footer)
- **BlogLayout.astro**: Specialized layout for blog posts
- **PostLayout.astro**: Layout for individual blog posts
- **AboutLayout.astro**: Layout for about pages
- Reusable components
This template now includes reusable components, such as the `Text` component:
Layouts wrap content with consistent HTML structure, styling, and components.
- Text Component
A versatile and reusable component for handling text across your project, ensuring consistency and easy customization.
## 🌐 Internationalization (i18n)
- **HTML Tags:** Easily change the HTML element (like `p`, `h1`, `span`, `a`) using the `tag` prop, with `p` being the default.
- **Variants:** Pick from preset text styles (such as `displayXL` or `textBase`) for a consistent look.
- **Custom Classes:** Add or adjust styles with the `class` prop.
- **Accessibility:** Customize with additional props like `id`, `href`, `title`, and `style`.
- **Content Slot:** Add any content inside the component, including optional left and right icons.
Example usage:
```astro
<Text tag="h1" variant="displayXL" class="text-center">
Welcome to the new version!
</Text>
The site supports multiple languages through a built-in internationalization system:
- Default language is English (`en`)
- Content is organized by language in directory structures
- URL format follows `/[lang]/[route]` pattern
- Translation files are stored in the `src/i18n/` directory
Language switching is available to visitors, with localized content and UI elements.
## 📦 Dependencies
The project relies on the following main dependencies:
- **Astro**: Core framework for building the site
- **Tailwind CSS**: Utility-first CSS framework for styling
- **@astrojs/tailwind**: Tailwind CSS integration for Astro
- **@astrojs/mdx**: MDX integration for enhanced markdown
- **@astrojs/sitemap**: Automatic sitemap generation
- **@astrojs/image**: Image optimization tools
Additional dependencies may include utility libraries and development tools.
## 🛠️ Development Instructions
### Prerequisites
- Node.js (v16 or later)
- npm or yarn
### Getting Started
1. Clone the repository:
```bash
git clone <repository-url>
cd <project-directory>
```
2. Install dependencies:
```bash
npm install
# or
yarn install
```
3. Start the development server:
```bash
npm run dev
# or
yarn dev
```
4. Open your browser and visit `http://localhost:3000`
### Building for Production
```bash
npm run build
# or
yarn build
```
- Button Component
A customizable button component with options to fit your design needs:
The build output will be in the `dist/` directory.
- **Variants:** Choose from predefined styles like `primary` (dark background) and `secondary` (lighter background), with support for dark mode.
- **Sizes:** Select `small` or `medium` for different button heights and padding.
- **Gaps:** Control the spacing between content with the `gapSize` prop (either `small` or `medium`).
- **Custom Classes:** Apply additional styles using the `class` prop.
- **Slots:** Include icons or extra content with optional `left-icon` and `right-icon` slots.
Example usage:
```astro
<Button size="small" variant="primary">Primary small</Button>
```
### Additional Commands
- Wrapper Component
A flexible layout component that helps with consistent spacing and alignment.
- **Preview build**: `npm run preview`
- **Check code**: `npm run astro check`
- **Lint code**: `npm run lint` (if configured)
- **Variants:** The default `standard` variant includes responsive widths, centered content, and padding.
- **Custom Classes:** Add or change styles with the `class` prop.
- **Content Slot:** Easily add any child components or content inside.
## 📝 Contributing
```astro
<Wrapper variant="standard">
Your content goes here
</Wrapper>
```
-----
When contributing to this project, please follow these guidelines:
### [Support](https://lexingtonthemes.com/legal/support/)
### [Documentation](https://lexingtonthemes.com/documentation/)
### [Get your bundle](https://lexingtonthemes.com)
1. Create components in the appropriate directories
2. Follow the existing code style and naming conventions
3. Add proper documentation for new features
4. Test your changes before submitting
## 📄 License
### References
[Add your license information here]
-[PWA](https://vite-pwa-org.netlify.app/)

View File

@ -3,24 +3,37 @@ import tailwindcss from '@tailwindcss/vite'
import { imagetools } from "imagetools"
import react from "@astrojs/react"
import mdx from "@astrojs/mdx";
import { defineConfig } from 'astro/config';
import compress from 'vite-plugin-compression';
import sitemap from "@astrojs/sitemap";
export default defineConfig({
site: 'https://polymech.io',
devToolbar: {
enabled: true,
enabled: false,
},
i18n: {
locales: ["es", "en", "de", "fr", "it", "ar", "ja", "zh"],
locales: ["es", "en", "de", "fr", "it", "ar", "ja", "zh", "nl", "it", "pt"],
defaultLocale: "en",
},
vite: {
plugins: [
tailwindcss()
tailwindcss({
config: './tailwind.config.cjs',
jit: true
}),
compress({
algorithm: 'gzip', // You can also use 'brotliCompress' for Brotli
threshold: 1024, // Compress files larger than 1KB
deleteOriginFile: false, // Keep original files (optional)
})
],
build: {
target: 'esnext',
assetsDir: './assets',
modulePreload: { polyfill: false },
commonjsOptions: { esmExternals: true }
commonjsOptions: { esmExternals: true },
assetsInlineLimit: 1024,
sourcemap: false,
},
ssr: {
external: ['cacache', 'glob', 'xlsx', 'sharp', '@polymech/kbot-d']
@ -46,10 +59,11 @@ export default defineConfig({
skipInline: false,
drafts: true
},
site: 'https://polymech.io',
integrations: [
//starlight(),
//sitemap(),
sitemap({
}),
mdx(),
//AstroPWA({}),
react(),

142
package-lock.json generated
View File

@ -30,6 +30,7 @@
"autoprefixer": "^10.4.20",
"axios": "^1.7.9",
"cacache": "^19.0.1",
"env-var": "^7.5.0",
"exifreader": "^4.26.1",
"file-type": "^20.0.0",
"find-cache-dir": "^5.0.0",
@ -64,6 +65,8 @@
"tailwindcss": "^4.0.7",
"type-fest": "^4.34.1",
"vite": "^6.1.1",
"vite-plugin-compression": "^0.5.1",
"write-file-atomic": "^6.0.0",
"xlsx": "^0.18.5",
"yargs": "^17.7.2",
"zod": "^3.24.2"
@ -5132,6 +5135,24 @@
"node": ">=8"
}
},
"node_modules/configstore/node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"license": "ISC"
},
"node_modules/configstore/node_modules/write-file-atomic": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
"integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
"license": "ISC",
"dependencies": {
"imurmurhash": "^0.1.4",
"is-typedarray": "^1.0.0",
"signal-exit": "^3.0.2",
"typedarray-to-buffer": "^3.1.5"
}
},
"node_modules/convert-source-map": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
@ -5701,6 +5722,15 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/env-var": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/env-var/-/env-var-7.5.0.tgz",
"integrity": "sha512-mKZOzLRN0ETzau2W2QXefbFjo5EF4yWq28OyKb9ICdeNhHJlOE/pHHnz4hdYJ9cNZXcJHo5xN4OT4pzuSHSNvA==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
@ -6959,6 +6989,20 @@
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
"license": "MIT"
},
"node_modules/fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/fs-minipass": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz",
@ -7382,7 +7426,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=8"
@ -8445,6 +8488,18 @@
"node": ">=6"
}
},
"node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@ -15325,6 +15380,15 @@
"url": "https://opencollective.com/unified"
}
},
"node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"license": "MIT",
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/unset-value": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
@ -15814,6 +15878,63 @@
}
}
},
"node_modules/vite-plugin-compression": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/vite-plugin-compression/-/vite-plugin-compression-0.5.1.tgz",
"integrity": "sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==",
"license": "MIT",
"dependencies": {
"chalk": "^4.1.2",
"debug": "^4.3.3",
"fs-extra": "^10.0.0"
},
"peerDependencies": {
"vite": ">=2.0.0"
}
},
"node_modules/vite-plugin-compression/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/vite-plugin-compression/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/vite-plugin-compression/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/vite/node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@ -16195,22 +16316,17 @@
"license": "ISC"
},
"node_modules/write-file-atomic": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
"integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-6.0.0.tgz",
"integrity": "sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==",
"license": "ISC",
"dependencies": {
"imurmurhash": "^0.1.4",
"is-typedarray": "^1.0.0",
"signal-exit": "^3.0.2",
"typedarray-to-buffer": "^3.1.5"
}
"signal-exit": "^4.0.1"
},
"node_modules/write-file-atomic/node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"license": "ISC"
"engines": {
"node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/ws": {
"version": "7.5.10",

View File

@ -40,6 +40,7 @@
"autoprefixer": "^10.4.20",
"axios": "^1.7.9",
"cacache": "^19.0.1",
"env-var": "^7.5.0",
"exifreader": "^4.26.1",
"file-type": "^20.0.0",
"find-cache-dir": "^5.0.0",
@ -74,6 +75,8 @@
"tailwindcss": "^4.0.7",
"type-fest": "^4.34.1",
"vite": "^6.1.1",
"vite-plugin-compression": "^0.5.1",
"write-file-atomic": "^6.0.0",
"xlsx": "^0.18.5",
"yargs": "^17.7.2",
"zod": "^3.24.2"

View File

@ -65,7 +65,7 @@ export default async function renderImg(props) {
fadeInTransition,
{ isImg: true }
);
const style = getStyleElement({ styleAttributes, backgroundStyles })
const style = getStyleElement({ styleAttributes })
const link = getLinkElement({ images, preload, imagesizes, linkAttributes })
const layoutStyles = getLayoutStyles({ layout })

View File

@ -35,7 +35,7 @@ export default async function ({
const { path, base, rest, image, imageWidth, imageHeight, imageFormat } =
await getProcessedImage(src, transformConfigs);
await delay(250);
await delay(100);
src = path;
rest.aspect = `${imageWidth / imageHeight}`;
@ -63,7 +63,7 @@ export default async function ({
rest
),
async () => {
await delay(250);
await delay(100);
return await getArtDirectedImages(
artDirectives,
placeholder,

View File

@ -43,7 +43,7 @@ export default async function getImageSources(
formats,
async (format) => {
try {
await delay(250);
await delay(100);
const srcset = await getSrcset(src, base, requiredBreakpoints, format, {
...rest,
...formatOptions[format],

View File

@ -5,8 +5,8 @@ export default {
"publicDir": "C:\\Users\\zx\\Desktop\\polymech\\polymech-site\\public\\",
"rootDir": "C:\\Users\\zx\\Desktop\\polymech\\polymech-site\\",
"mode": "production",
"outDir": "dist",
"assetsDir": "/_astro",
"outDir": "C:\\Users\\zx\\Desktop\\polymech\\polymech-site\\dist\\",
"assetsDir": "_astro",
"sourcemap": false,
"assetFileNames": "/_astro/[name]@[width].[hash][extname]"
}

View File

@ -53,5 +53,5 @@ export default function printWarning({
: `can't be defined inside attributes.${element}`)) +
colours.reset;
console.log(flag + keyLog, messageLog);
//console.log(flag + keyLog, messageLog);
}

View File

@ -14,33 +14,22 @@
"image": {
"default": "/images/default-image.png",
"error": "/images/error-image.png",
"alt": "Astrofront"
"alt": "Astrofront",
"src": "https://assets.osr-plastic.org/machines//assets/newsletter/common/products/extruders/overview-3.jpg"
}
},
"footer_left": [
{
"href": "/rss.xml",
"text": "RSS"
},
{
"href": "/helpcenter/home",
"text": "Help"
},
{
"href": "/infopages/terms",
"text": "Terms"
},
{
"href": "/infopages/privacy",
"text": "Privacy"
},
{
"href": "/forms/contact",
"href": "/infopages/contact",
"text": "Contact"
},
} ,
{
"href": "/infopages/about",
"text": "About us"
"href": "/newsletter/newsletter_2024_09_en-hugo-release.html",
"text": "Newsletter"
}
],
"footer_right": [
@ -92,5 +81,8 @@
"store": "resources"
}
}
},
"tracking": {
"googleAnalytics": "G-RW6Q6EG3J0"
}
}

View File

@ -9,8 +9,7 @@ export const OSR_ROOT = () => path.resolve(resolve("${OSR_ROOT}"))
export const LOGGING_NAMESPACE = 'polymech-site'
export const TRANSLATE_CONTENT = true
export const LANGUAGES = ['en', 'ar', 'de', 'ja', 'es', 'zh', 'fr']
//export const LANGUAGES_PROD = ['en']
export const LANGUAGES_PROD = ['en', 'es', 'ar', 'de', 'ja', 'zh', 'fr']
export const LANGUAGES_PROD = ['en', 'es', 'ar', 'de', 'ja', 'zh', 'fr', 'nl', 'it', 'pt']
export const isRTL = (lang) => lang === 'ar'
// i18n constants
@ -61,6 +60,7 @@ export const TASK_LOG_DIRECTORY = './logs/'
// Task - Retail Config
export const REGISTER_PRODUCT_TASKS = true
export const RETAIL_PRODUCT_BRANCH = 'site'
export const PROJECTS_BRANCH = 'projects'
export const RETAIL_COMPILE_CACHE = false
export const RETAIL_MEDIA_CACHE = true
export const RETAIL_LOG_LEVEL_I18N_PRODUCT_ASSETS = 'info'
@ -107,7 +107,7 @@ export const SHOW_LICENSE = true
export const SHOW_RENDERINGS = true
export const SHOW_TABS = false
export const SHOW_GALLERY = false
export const SHOW_GALLERY = true
export const SHOW_FILES = true
export const SHOW_SPECS = true
export const SHOW_CHECKOUT = true
@ -117,6 +117,8 @@ export const SHOW_RESOURCES = true
export const SHOW_DEBUG = false
export const SHOW_SAMPLES = true
export const SHOW_README = false
export const SHOW_RELATED = true
/////////////////////////////////////////////
//
@ -159,13 +161,13 @@ export const IMAGE_SETTINGS =
SHOW_DESCRIPTION: false,
SIZES_THUMB: O_IMAGE.sizes_thumbs,
SIZES_LARGE: O_IMAGE.sizes_large,
SIZES_REGULAR: O_IMAGE.sizes
SIZES_REGULAR: O_IMAGE.sizes_medium
},
LIGHTBOX: {
SHOW_TITLE: true,
SHOW_DESCRIPTION: true,
SIZES_THUMB: O_IMAGE.sizes_thumbs,
SIZES_LARGE: O_IMAGE.sizes_large,
SIZES_REGULAR: O_IMAGE.sizes
SIZES_REGULAR: O_IMAGE.sizes_medium
}
}

View File

@ -20,25 +20,22 @@ export const items = async (opts: { locale: string }) => {
}
]
}
export const footer_left = async ( locale: string ) => {
const _T = async (text: string) => await translate(text, I18N_SOURCE_LANGUAGE, locale)
return await pMap(config.footer_left, async (item) => {
const isAbsoluteUrl = (url: string): boolean => /^[a-zA-Z]+:/.test(url);
const createFooterLinks = async (items: any[], locale: string) => {
const _T = async (text: string) => await translate(text, I18N_SOURCE_LANGUAGE, locale);
return await pMap(items, async (item) => {
const translatedText = await _T(item.text); // Single translation call
return {
"href": `${item.href}`,
"title": await _T(item.text),
"ariaLabel": item.text,
"href": isAbsoluteUrl(item.href) ? item.href : `/${locale}${item.href}`,
"title": translatedText, // Use cached translation
"ariaLabel": translatedText, // Use cached translation
"class": "hover:text-orange-600"
}
};
});
}
export const footer_right = async ( locale: string ) => {
const _T = async (text: string) => await translate(text, I18N_SOURCE_LANGUAGE, locale)
return await pMap(config.footer_right, async (item) => {
return {
"href": `/${item.href}`,
"title": await _T(item.text),
"ariaLabel": item.text,
"class": "hover:text-orange-600"
}
});
}
};
export const footer_left = (locale: string) => createFooterLinks(config.footer_left, locale);
export const footer_right = (locale: string) => createFooterLinks(config.footer_right, locale);

View File

@ -12,7 +12,7 @@ export enum E_BROADBAND_SPEED {
}
const imageConfigSchema = z.object({
sizes: z.string(),
sizes_medium: z.string(),
sizes_thumbs: z.string(),
sizes_large: z.string(),
})
@ -28,21 +28,21 @@ export const IMAGE_PRESET: Images =
{
[E_BROADBAND_SPEED.SLOW]: {
// For 2g connections: smaller image widths help performance. (Middle East & Africa)
sizes: "(min-width: 100px) 100px, 100vw",
sizes_medium: "(min-width: 100px) 100px, 100vw",
sizes_thumbs: "(min-width: 80px) 80px, 80vw",
sizes_large: "(min-width: 320px) 320px, 320vw",
},
[E_BROADBAND_SPEED.MEDIUM]:
{
// For 3g connections: a moderate size image for a balance of quality and speed.
sizes: "(min-width: 800px) 800px, 800vw",
sizes_medium: "(min-width: 400px) 400px, 400vw",
sizes_thumbs: "(min-width: 120px) 120px, 120vw",
sizes_large: "(min-width: 1024px) 1024px, 1024vw",
},
[E_BROADBAND_SPEED.FAST]:
{
// For 4g connections: larger images for high-resolution displays.
sizes: "(min-width: 1024px) 1024px, 1024vw",
sizes_medium: "(min-width: 1024px) 1024px, 1024vw",
sizes_thumbs: "(min-width: 180px) 180px, 180vw",
sizes_large: "(min-width: 1200px) 1200px, 1200vw"
}

View File

@ -4,9 +4,13 @@ import { sync as exists } from '@polymech/fs/exists'
import type { IOptions } from '@polymech/i18n'
import { CONFIG_DEFAULT } from '@polymech/commons'
import { I18N_ASSET_PATH, I18N_CACHE, I18N_SOURCE_LANGUAGE,
PRODUCT_DIR, PRODUCT_SPECS,
RETAIL_LOG_LEVEL_I18N_PRODUCT_ASSETS } from '@/app/config.js'
import {
I18N_ASSET_PATH,
I18N_CACHE,
I18N_SOURCE_LANGUAGE,
PRODUCT_SPECS,
RETAIL_LOG_LEVEL_I18N_PRODUCT_ASSETS
} from '@/app/config.js'
import { translateXLS } from '@polymech/i18n/translate_xls'
import { I18N_STORE, OSR_ROOT } from 'config/config.js'

View File

@ -9,6 +9,7 @@ import { findUp } from 'find-up'
import { createLogger } from '@polymech/log'
import { parse, IProfile } from '@polymech/commons/profile'
import { renderMarkup } from "@/model/component.js";
import {
LOGGING_NAMESPACE,
OSRL_ENV,
@ -63,7 +64,10 @@ export async function markdownToHtml(markdown: string): Promise<string> {
return result.toString();
}
export async function createMarkdownComponent(markdown: string) {
export const createMarkdownComponent = async (markdown: string) => {
const html = unescapeHTML(await markdownToHtml(markdown));
return createComponent(() => renderTemplate(html as any, []));
}
export const createHTMLComponent = async (html: string) =>
createComponent(() => renderTemplate(unescapeHTML(html) as any, []))

View File

@ -47,13 +47,15 @@ const hreflangs = LANGUAGES_PROD.filter((lang)=>lang!==Astro.currentLocale).map(
}))
const image = item?.image || config.site.image
const image_url = image.url
const image_url = image.src
const image_alt = image.alt
const title = item?.title as string || config.site.title || ""
const description = item?.description as string || config.metadata.description
const keywords = await item_keywords(item, Astro.currentLocale)
const tracking = config?.tracking?.googleAnalytics
---
<AstroSeo
@ -129,18 +131,25 @@ const keywords = await item_keywords(item, Astro.currentLocale)
<!--- IBM Plex Mono font from fonts.google.com -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap"
rel="stylesheet"
/>
<link rel="preload" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap" as="style" />
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap" rel="stylesheet" media="print" onload="this.onload=null;this.removeAttribute('media');" />
<script is:inline src="https://cdn.jsdelivr.net/npm/flowbite@3.0.0/dist/flowbite.min.js"></script>
<script src="/js/flowbite.min.js" defer></script>
<!-- alpine JS -->
<script src="//unpkg.com/alpinejs" defer></script>
<script src="/js/cdn.min.js" defer></script>
<StructuredData frontmatter={item} />
{tracking && (
<script async src={`https://www.googletagmanager.com/gtag/js?id=${tracking}`}></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date())
gtag('config', {tracking})
</script>
)}
{ REDIRECT && <script>
const currentPath = window.location.pathname;
if (!/^\/[a-z]{2}(\/|$)/i.test(currentPath)) {
@ -150,5 +159,8 @@ const keywords = await item_keywords(item, Astro.currentLocale)
window.location.href = `/${language}`;
}
</script>
<link rel="sitemap" href="/sitemap-index.xml" />
}

View File

@ -46,12 +46,12 @@ const footerRight = await footer_right(locale)
<Wrapper variant="standard" class="py-12">
<footer class="py-2">
<div class="p-4 xl:pb-0 bg-[white] overflow-hidden rounded-xl">
<div class="grid md:grid-cols-3 gap-6">
<div class="p-4 bg-[white] overflow-hidden rounded-xl">
<div class="grid grid-cols-2 gap-6">
<div
class="flex flex-col h-full justify-between xl:pb-2 order-last md:order-none"
>
class="flex flex-col justify-between">
<nav role="navigation">
<ul class="text-xs space-y-1 font-mono uppercase text-neutral-500">
{
footerRight.map((link) => (
@ -75,19 +75,8 @@ const footerRight = await footer_right(locale)
}
</div>
</nav>
<div class="flex gap-4 mt-12 items-center">
<img src="/logos/transparent.svg" alt="logo" class="size-4" />
<p
class="text-xs leading-5 text-neutral-400 text-pretty font-mono uppercase"
>
</p>
</div>
</div>
<img
src="/logos/transparent.svg"
alt="logo"
class="size-12 md:mx-auto fill-orange-600"
/>
<div class="flex flex-col justify-between md:text-right xl:pb-2 mr-2">
<nav role="navigation">
<ul class="text-xs space-y-1 font-mono uppercase text-neutral-500">

View File

@ -23,9 +23,7 @@ const navItems = await items({ locale });
title={item.title}
aria-label={item.ariaLabel}
class={item.class}
>
{item.title}
</a>
>{item.title}</a>
))
}
</nav>

View File

@ -43,9 +43,7 @@ const mergedLightboxSettings = {
SHOW_DESCRIPTION: lightboxSettings.SHOW_DESCRIPTION ?? IMAGE_SETTINGS.LIGHTBOX.SHOW_DESCRIPTION,
};
const locale = Astro.currentLocale || "en";
---
<div
x-data={`
{
@ -76,8 +74,8 @@ const locale = Astro.currentLocale || "en";
lightboxLoaded = false;
preloadAndOpen();
}
}"
class="product-gallery"><div class="flex flex-col h-full">
}"class="product-gallery">
<div class="flex flex-col h-full">
<!-- Main Image (click to preload and open lightbox) -->
<div class="flex-1 flex items-center justify-center" @click="preloadAndOpen()">
{images.map((image, index) => (
@ -88,8 +86,12 @@ const locale = Astro.currentLocale || "en";
objectFit="contain"
format="avif"
placeholder="blurred"
sizes={mergedGallerySettings.SIZES_REGULAR}
class="rounded max-h-full object-contain main-image"
breakpoints={[200,300,500]}
fadeInTransition={{
delay: "0.5s",
duration: "2.5s",
timingFunction: "linear",
}}
attributes={{
img: { class: "main-image rounded-lg max-h-[60vh] aspect-square" }
}}

View File

@ -113,10 +113,9 @@ const ld = await toJsonLd(images, Astro.currentLocale || I18N_SOURCE_LANGUAGE, c
this.lightboxLoaded = false;
let img = new Image();
img.src = this.images[this.currentIndex].src;
img.onload = () => {
this.lightboxLoaded = true;
this.open = true;
};
img.onload = () => { };
}
}
`}
@ -152,8 +151,8 @@ const ld = await toJsonLd(images, Astro.currentLocale || I18N_SOURCE_LANGUAGE, c
alt={alt_translated[index]}
objectFit="contain"
format="avif"
placeholder="blurred"
sizes={mergedGallerySettings.SIZES_REGULAR}
loading="lazy"
attributes={{
img: { class: "main-image p-1 rounded-lg max-h-[60vh] aspect-square" }
}}
@ -172,7 +171,8 @@ const ld = await toJsonLd(images, Astro.currentLocale || I18N_SOURCE_LANGUAGE, c
))}
</div>
)}
<!-- Thumbnails -->
<!-- Thumbnails - only shown when multiple images exist -->
{images.length > 1 && (
<div class="p-1 overflow-x-auto scrollbar-thin scrollbar-thumb-gray-400 scrollbar-track-gray-200">
<div class="flex p-2 mt-2 ml-2 mr-2 gap-2 items-center justify-center">
{images.map((image, index) => (
@ -189,6 +189,7 @@ const ld = await toJsonLd(images, Astro.currentLocale || I18N_SOURCE_LANGUAGE, c
placeholder="blurred"
sizes={mergedGallerySettings.SIZES_THUMB}
alt={alt_translated[index]}
breakpoints={[100]}
attributes={{
img: {
class: "w-32 h-32 rounded-lg hover:ring-2 hover:ring-blue-500 thumbnail-img aspect-square"
@ -200,6 +201,7 @@ const ld = await toJsonLd(images, Astro.currentLocale || I18N_SOURCE_LANGUAGE, c
))}
</div>
</div>
)}
</div>
<!-- Lightbox Modal (with swipe functionality) -->
<div
@ -268,6 +270,5 @@ const ld = await toJsonLd(images, Astro.currentLocale || I18N_SOURCE_LANGUAGE, c
&#10095;
</button>
</div>
<script type="application/ld+json" set:html={ JSON.stringify(ld) }></script>
</div>

View File

@ -15,6 +15,7 @@ const {
const content = await Astro.slots.render('default')
const translatedText = await translate(content, I18N_SOURCE_LANGUAGE, language, rest)
---
<div data-widget="polymech.i18n" class={clazz}>
{translatedText}

View File

@ -1,19 +1,19 @@
---
import { component, createMarkdownComponent } from "@/base/index.js";
import { createMarkdownComponent } from "@/base/index.js";
import Translation from "@/components/polymech/i18n.astro";
import Link from "./link.astro";
const { frontmatter: data, ...rest } = Astro.props;
const { frontmatter: data } = Astro.props;
const LINK_CLASSES = "text-blue-400 hover:text-blue-800 hover:underline";
const getHref = (key, data) => {
switch (key) {
case "cad":
return data.cad?.[0]?.[".html"];
return data.cad?.[0]?.[".html"]
default:
return data[key];
return data[key]
}
};
}
const checkCondition = (key, data) => {
switch (key) {
@ -74,11 +74,11 @@ const filteredGroups: Record<
return acc;
}, {});
const extraContent = [data.extra_resources, data.tests]
const extraContent = [data.resources, data.tests]
.filter((s) => s && s.length)
.map(createMarkdownComponent)
const sharedContent = [data.shared_resources]
const sharedContent = [data.shared]
.filter((s) => s && s.length)
.map(createMarkdownComponent)
---
@ -89,10 +89,10 @@ const sharedContent = [data.shared_resources]
{
Object.entries(filteredGroups).map(([name, links]) => (
<div class="p-2">
<h3 class="text-xl font-bold mb-2">
<h3 class="text-x font-bold mb-2">
<Translation>{name}</Translation>
</h3>
<ul class="list-disc pl-6 space-y-1">
<ul class="list-disc pl-6">
{links.map((link) => (
<Link href={link.getHref()} className={LINK_CLASSES}>
<Translation>{link.label}</Translation>
@ -105,7 +105,7 @@ const sharedContent = [data.shared_resources]
</div>
{
extraContent.length > 0 && (
<div class="p-2">
<div class="p-4">
<h3 class="text-xl font-bold mb-2">
<Translation>Extras</Translation>
</h3>
@ -119,7 +119,7 @@ const sharedContent = [data.shared_resources]
}
{
sharedContent.length > 0 && (
<div class="p-2">
<div class="p-4">
<div class="grid grid-cols-1 gap-4 extra-resources">
{sharedContent.map(async (Content) => (
<Content class="extra-resource" />

241
src/components/report.md Normal file
View File

@ -0,0 +1,241 @@
# Astro Project Analysis: polymech-site
## 1. Project Structure Overview
Based on the explored files, the polymech-site project follows a typical Astro project structure with some custom organization patterns:
### Directory Structure
```
polymech-site/
├── src/
│ ├── components/
│ │ ├── global/
│ │ │ └── Navigation.astro
│ │ ├── polymech/
│ │ │ └── i18n.astro
│ │ └── BaseHead.astro
│ ├── layouts/
│ │ └── BaseLayout.astro
│ ├── pages/
│ │ └── index.astro
│ └── base/ (utilities)
├── public/
├── astro.config.mjs
└── package.json
```
### Key Components
- **BaseHead.astro**: Handles metadata, SEO elements, and global head content
- **BaseLayout.astro**: Provides the main page structure and wraps content
- **Navigation.astro**: Implements site navigation with potential interactive elements
- **i18n.astro**: Appears to handle internationalization at the component level
### Configuration
The project uses:
- Astro's built-in features for static site generation
- Various integrations defined in astro.config.mjs
- A component-based architecture with clear separation of concerns
## 2. Patterns and Potential Issues
### Observed Patterns
1. **Component Organization**
- Components are organized by functionality and scope (global vs. specific)
- Layout components are separated from regular components
- Reusable UI elements are componentized
2. **Internationalization Approach**
- Custom i18n implementation at the component level
- The project appears to handle multiple languages
3. **Rendering Strategy**
- Primarily static rendering with some potential client-side hydration
- Heavy use of Astro's component model
### Potential Issues
1. **Internationalization Complexity**
- The i18n implementation appears custom and potentially complex
- No clear centralized i18n management system was identified
2. **Component Granularity**
- With 163 Astro files, there might be issues with component granularity
- Potential for component duplication or insufficient reuse
3. **Build Optimization**
- No clear strategy for code splitting or lazy loading of components
- Potential performance issues with large number of components
4. **SEO Implementation**
- SEO handling appears to be primarily in BaseHead, which may limit page-specific optimizations
- No clear structured data implementation was observed
## 3. Best Practices for Astro Development
### Component Architecture
1. **Island Architecture**
- Leverage Astro's island architecture more explicitly
- Only hydrate interactive components, keeping most of the site static
2. **Component Composition**
- Use slots and props for flexible component composition
- Create truly reusable components with clear interfaces
3. **Content Management**
- Use Astro's content collections for structured content
- Separate content from presentation where possible
### Performance
1. **Image Optimization**
- Use Astro's built-in image optimization features
- Implement responsive images with appropriate formats
2. **CSS Strategy**
- Scope CSS to components where possible
- Consider using a utility-first approach for consistency
3. **JavaScript Usage**
- Minimize client-side JavaScript
- Use directives like `client:load` and `client:visible` strategically
### Development Workflow
1. **Type Safety**
- Leverage TypeScript for component props
- Define clear interfaces for data structures
2. **Testing**
- Implement component testing with appropriate tools
- Consider visual regression testing for UI components
## 4. Specific Improvement Suggestions
### Performance Optimization
1. **Asset Optimization**
- Implement proper image optimization with `<Image />` component
- Use responsive images with srcset for different viewport sizes
2. **Code Splitting**
- Review client-side JavaScript usage and split appropriately
- Use dynamic imports for larger client components
3. **Resource Hints**
- Add preload/prefetch directives for critical resources
- Implement resource prioritization
```astro
<!-- Example improvement for BaseHead.astro -->
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
```
### Internationalization (i18n) Implementation
1. **Centralized Solution**
- Consider adopting a more standardized i18n solution like Astro i18n or astro-i18next
- Implement a centralized translation management system
2. **Content Structure**
- Organize content by language in content collections
- Create a clear separation between content and presentation
3. **URL Strategy**
- Implement a consistent URL strategy for language variants (e.g., /en/page, /fr/page)
- Add language metadata to improve SEO for multilingual content
```astro
<!-- Example improvement for i18n handling -->
---
import { getLanguageFromURL } from '../utils/i18n';
const lang = getLanguageFromURL(Astro.url.pathname);
const translations = await import(`../i18n/${lang}.js`);
---
```
### Component Organization
1. **Atomic Design Principles**
- Consider reorganizing components following atomic design principles
- Create atoms, molecules, organisms, templates, and pages
2. **Documentation**
- Add component documentation with examples
- Create a component playground or storybook
3. **Naming Conventions**
- Implement consistent naming conventions
- Group related components in subdirectories
### Code Maintainability
1. **TypeScript Integration**
- Add TypeScript for all component props
- Define interfaces for data structures
```astro
<!-- Example improvement with TypeScript -->
---
interface Props {
title: string;
description?: string;
image?: { src: string; alt: string };
canonicalURL?: URL;
}
const { title, description, image, canonicalURL = Astro.url } = Astro.props;
---
```
2. **Code Comments**
- Add meaningful comments for complex logic
- Document component usage and requirements
3. **Consistent Formatting**
- Implement consistent code formatting with tools like Prettier
- Add ESLint for code quality enforcement
### SEO Optimization
1. **Structured Data**
- Implement JSON-LD structured data for key pages
- Add appropriate schema.org markup
```astro
<!-- Example JSON-LD implementation -->
<script type="application/ld+json" set:html={JSON.stringify({
"@context": "https://schema.org",
"@type": "WebSite",
"name": "Polymech",
"url": Astro.site.toString()
})} />
```
2. **Meta Tag Management**
- Create a dedicated SEO component with flexible options
- Ensure all pages have appropriate meta tags
3. **Performance Metrics**
- Optimize for Core Web Vitals
- Add monitoring for key performance metrics
## 5. Conclusion
The polymech-site project demonstrates a solid foundation using Astro's component model and static site generation capabilities. With 163 Astro files, it's a substantial project that would benefit from more structured organization principles and standardized approaches to common concerns like internationalization and SEO.
By implementing the suggested improvements, the project could achieve better performance, maintainability, and extensibility while leveraging Astro's strengths in creating fast, content-focused websites.
Key priorities should be:
1. Optimizing asset delivery and performance
2. Standardizing the i18n approach
3. Enhancing component documentation and organization
4. Improving SEO implementation with structured data
5. Adding type safety with TypeScript
These changes would significantly improve the developer experience and end-user performance while making the codebase more maintainable for future development.

View File

@ -1,11 +1,14 @@
---
import { default_image } from "@/app/config.js"
const { title, url, price, model } = Astro.props
const thumbnail = model?.assets?.renderings[0]?.url || default_image()
import Img from "@/components/polymech/image.astro"
---
import { default_image } from "@/app/config.js";
import Img from "@/components/polymech/image.astro";
import Translate from "@/components/polymech/i18n.astro";
<div class="group relative bg-white overflow-hidden group rounded-xl font-mono">
const { title, url, price, model, selected = false } = Astro.props;
const thumbnail = model?.assets?.renderings[0]?.url || default_image();
const classes = `group relative bg-white overflow-hidden group rounded-xl font-mono ${selected ? "ring-2 ring-orange-500" : ""}`
---
<div class={classes}>
<div class="p-4 overflow-hidden group-hover:opacity-75 duration-300 transition-all">
<a href={url} title={title} aria-label={title}>
<Img
@ -24,7 +27,7 @@ import Img from "@/components/polymech/image.astro"
<h3>
<a href={url} title={title} aria-label={title}>
<span aria-hidden="true" class="absolute inset-0"></span>
{title}
<Translate>{title}</Translate>
</a>
</h3>
<p class="absolute top-4 right-4">{price}</p>

View File

@ -19,7 +19,7 @@ const pages = [
},
{
name: "Forms",
links: [{ href: "/forms/contact", text: "Contact" }],
links: [{ href: "/forms/contact2", text: "Contact" }],
},
{
name: "System",

View File

@ -1,10 +1,16 @@
import { defineCollection, z } from "astro:content"
import { loader } from './model/component.js'
import { ComponentConfigSchema } from '@polymech/commons/component'
import { RETAIL_PRODUCT_BRANCH, PROJECTS_BRANCH } from 'config/config.js'
import { glob } from 'astro/loaders'
const store = defineCollection({
loader: loader() as any,
loader: loader(RETAIL_PRODUCT_BRANCH) as any,
schema: ComponentConfigSchema.passthrough(),
})
const projects = defineCollection({
loader: loader(PROJECTS_BRANCH) as any,
schema: ComponentConfigSchema.passthrough(),
})
const helpcenter = defineCollection({
@ -35,8 +41,9 @@ const resources = defineCollection({
});
export const collections = {
store: store,
helpcenter: helpcenter,
infopages: infopages,
resources
store,
projects,
resources,
helpcenter,
infopages
};

View File

@ -0,0 +1,17 @@
## Contact
## Administration & Sales
[sales@plastic-hub.com](mailto:/sales@plastic-hub.com)
Anne Barbier - +34 691 952 287
Address - Factory
Polígono Can Clapers
Carrer Can peric 11, 1B
08181 Sentmenat ~ Spain
VAT - ID : ESY0100830N
[Get Directions](https://www.google.com/maps/place/Plastic+Hub/@41.6093789,2.1399009,17z/data=!3m1!4b1!4m5!3m4!1s0x12a4eb80cfabbbfd:0x2cd16f2aff436ed!8m2!3d41.6094019!4d2.1421267)

View File

@ -2,7 +2,7 @@
import BaseHead from "../components/BaseHead.astro";
import Navigation from "../components/global/Navigation.astro";
import Footer from "../components/global/Footer.astro";
import { I18N_SOURCE_LANGUAGE, isRTL } from "config/config.js"
import { isRTL } from "config/config.js"
const { frontmatter: frontmatter, additionalKeywords, ...rest } = Astro.props;
@ -14,7 +14,7 @@ const { frontmatter: frontmatter, additionalKeywords, ...rest } = Astro.props;
{...rest}
/>
</head>
<body class="bg-neutral-100 mx-auto 2xl:max-w-7xl flex flex-col min-h-svh p-4">
<body class="bg-neutral-100 mx-auto 2xl:max-w-7xl flex flex-col min-h-svh p-4 text-neutral-500 font-mono">
<Navigation />
<main class="grow">
<slot />

View File

@ -1,14 +1,19 @@
---
import BaseLayout from "./BaseLayout.astro";
const { frontmatter } = Astro.props;
import Wrapper from "@/components/containers/Wrapper.astro";
import { I18N_SOURCE_LANGUAGE } from "config/config.js"
import { translate } from '@/base/i18n.js'
import { createHTMLComponent } from '@/base/index.js'
const content = await Astro.slots.render('default')
const translatedText = await translate(content, I18N_SOURCE_LANGUAGE, Astro.currentLocale)
const Markup = await createHTMLComponent(translatedText)
---
<BaseLayout>
<Wrapper>
<section>
<div class="grid sm:grid-cols-1 lg:grid-cols-1 xl:grid-cols-1 gap-2">
<div class="simple-prose-styles py-2 lg:col-start-3 justify-end p-4">
<slot />
<div class="grid sm:grid-cols-1 lg:grid-cols-1 xl:grid-cols-1 gap-2 text-neutral-500 markdown-content">
<div class="py-2 p-4 bg-white rounded-xl">
<Markup/>
</div>
</div>
</section>

View File

@ -6,7 +6,7 @@ import Wrapper from "@/components/containers/Wrapper.astro"
<BaseLayout>
<Wrapper>
<section>
<div class="grid grid-cols-1 md:grid-cols-1">
<div class="grid grid-cols-1 md:grid-cols-1 lg:grid-cols-2 gap-4">
<div class="flex flex-col">
<div class="prose-styles lg:col-span-2">
<img

View File

@ -1,4 +1,5 @@
---
import { getCollection } from "astro:content";
import BaseLayout from "./BaseLayout.astro";
import { createMarkdownComponent } from "@/base/index.js";
import { translate } from "@/base/i18n.js";
@ -12,6 +13,13 @@ import Specs from "@/components/polymech/specs.astro";
import TabButton from "@/components/polymech/tab-button.astro";
import TabContent from "@/components/polymech/tab-content.astro";
import StoreEntries from "@/components/store/StoreEntries.astro";
import {
IComponentConfigEx,
group_by_path,
group_path,
} from "@/model/component.js";
import "flowbite";
import {
@ -29,13 +37,15 @@ import {
SHOW_RESOURCES,
SHOW_CHECKOUT,
SHOW_README,
SHOW_RELATED,
DEFAULT_LICENSE,
isRTL,
} from "config/config.js";
const { frontmatter: item, ...rest } = Astro.props;
const content = await translate(
item.content || "",
item.body || "",
I18N_SOURCE_LANGUAGE,
Astro.currentLocale,
);
@ -54,22 +64,26 @@ const str_debug =
"\n```";
const Content_Debug = await createMarkdownComponent(str_debug);
const view = "store";
const items = await getCollection(view); //.filter((i) => item.rel !== i.id);
//const group_id = group_path(item);
const others = await group_by_path(items, Astro.currentLocale);
---
<BaseLayout frontmatter={item} description={item.description} {...rest}>
<BaseLayout frontmatter={item} description={item.description} {...rest} class="">
<Wrapper>
<section>
<div class="grid sm:grid-cols-2 lg:grid-cols-2 just xl:grid-cols-2 gap-2">
<div class="grid sm:grid-cols-2 lg:grid-cols-2 just xl:grid-cols-2 gap-2 ">
<div class="flex flex-col gap-2 h-full justify-between">
<div>
<article class="markdown-content bg-white rounded-xl p-4">
<h1
class="text-neutral-500 font-mono font-semibold mb-2 text-2xl"
class="text-neutral-500 font-semibold mb-2 text-2xl"
>
<span><Translate>{`${item.title}`}</Translate></span>
{
isRTL(Astro.currentLocale) && (
<div class="text-neutral-500 font-mono font-semibold mb-2">
<div class="text-neutral-500 font-semibold mb-2">
"{item.title}"
</div>
)
@ -90,7 +104,7 @@ const Content_Debug = await createMarkdownComponent(str_debug);
href={item.cad[0][".html"]}
title="link to your page"
aria-label="your label"
class="relative group overflow-hidden pl-4 font-mono h-14 flex space-x-6 items-center bg-white hover:bg-neutral-200 duration-300 rounded-xl w-full justify-between"
class="relative group overflow-hidden pl-4 h-14 flex space-x-6 items-center bg-white hover:bg-neutral-200 duration-300 rounded-xl w-full justify-between"
>
<span class="relative uppercase text-xs text-neutral-600">
<Translate>3D Preview</Translate>
@ -141,7 +155,7 @@ const Content_Debug = await createMarkdownComponent(str_debug);
href={item.checkout}
title="link to your page"
aria-label="your label"
class="relative group overflow-hidden pl-4 font-mono h-14 flex space-x-6 items-center bg-orange-500 hover:bg-black duration-300 rounded-xl w-full justify-between"
class="relative group overflow-hidden pl-4 h-14 flex space-x-6 items-center bg-orange-500 hover:bg-black duration-300 rounded-xl w-full justify-between"
>
<span class="relative uppercase text-xs text-white">
<Translate>Add to cart</Translate>
@ -191,7 +205,7 @@ const Content_Debug = await createMarkdownComponent(str_debug);
SHOW_LICENSE && (
<div class="space-y-2">
<div class="bg-white rounded-xl p-4">
<h3 class="text-lg text-neutral-600 uppercase font-mono tracking-tight">
<h3 class="text-lg text-neutral-600 uppercase tracking-tight">
<Translate>License</Translate>
</h3>
<p class="text-neutral-500 mt-4 text-sm">
@ -251,45 +265,70 @@ const Content_Debug = await createMarkdownComponent(str_debug);
data-tabs-inactive-classes="dark:border-transparent text-gray-500 hover:text-gray-600 dark:text-gray-400 border-gray-100 hover:border-gray-300 dark:border-gray-700 dark:hover:text-gray-300"
role="tablist"
>
{SHOW_README && <TabButton title="Overview" />}
<TabButton title="Specs" />
<TabButton title="Gallery" />
<TabButton title="Resources" />
{SHOW_SAMPLES && <TabButton title="Samples" />}
{SHOW_README && item.readme && <TabButton title="Overview" />}
{SHOW_SPECS && <TabButton title="Specs" />}
{SHOW_GALLERY && <TabButton title="Gallery" />}
{SHOW_RESOURCES && <TabButton title="Resources" />}
{
SHOW_SAMPLES && item.assets.samples.length > 0 && (
<TabButton title="Samples" />
)
}
{SHOW_DEBUG && <TabButton title="Debug" />}
</ul>
</div>
<div id="default-styled-tab-content">
<TabContent title="Overview" class="content">
{SHOW_README && item.readme && <TabContent title="Overview" class="content">
{
SHOW_README && item.readme && (
(
<Readme markdown={item.readme} data={item} />
)
}
</TabContent>
<TabContent title="Specs" class="bg-white rounded-xl dark:bg-gray-800 font-mono">
}
{
SHOW_SPECS && (
<TabContent
title="Specs"
class="bg-white rounded-xl dark:bg-gray-800"
>
<Specs frontmatter={item} />
</TabContent>
)
}
{
SHOW_GALLERY && (
<TabContent title="Gallery" class="p-0 md:p-4 rounded-lg bg-white">
<GalleryK images={item.assets.gallery} item={item} />
<GalleryK images={item.assets.gallery} item={item} />{" "}
</TabContent>
)
}
{
SHOW_SAMPLES && (
<TabContent title="Samples" class="p-4 bg-white rounded-xl dark:bg-gray-800">
<TabContent
title="Samples"
class="p-4 bg-white rounded-xl dark:bg-gray-800"
>
<GalleryK images={item.assets.samples} item={item} />
</TabContent>
)
}
{
SHOW_RESOURCES && (
<TabContent title="Resources" class="p-4 bg-white rounded-xl dark:bg-gray-800">
<TabContent
title="Resources"
class="p-4 bg-white rounded-xl dark:bg-gray-800"
>
<Resources frontmatter={item} />
</TabContent>
)
}
{
SHOW_DEBUG && (
<TabContent title="Debug" class="rounded-lg bg-white p-4 dark:bg-gray-800">
<TabContent
title="Debug"
class="rounded-lg bg-white p-4 dark:bg-gray-800"
>
<Content_Debug />
</TabContent>
)
@ -332,5 +371,38 @@ const Content_Debug = await createMarkdownComponent(str_debug);
});
</script>
</section>
<hr />
<h1 aria-hidden="true" class="p-4 text-xs space-x-8"> <Translate>Related</Translate>
</h1>
{
SHOW_RELATED && (
<section id="item_related" class="bg-blue-50 p-4 rounded-2xl">
{Object.keys(others).map((relKey) => (
<section>
<h4
aria-hidden="true"
class="p-4 text-xl text-neutral-500 space-x-8"
>
{relKey}
</h4>
<div class="grid sm:grid-cols-4 lg:grid-cols-4 xl:grid-cols-4 gap-2">
{others[relKey].map((post) => (
<StoreEntries
key={post.id}
url={`/${Astro.currentLocale}/${view}/${post.id}`}
title={post.data.title}
price={post.data.price}
type={post.data.type}
alt={post.data.title}
model={post.data}
selected={post.id === item.rel}
/>
))}
</div>
</section>
))}
</section>
)
}
</Wrapper>
</BaseLayout>

8
src/middleware.ts Normal file
View File

@ -0,0 +1,8 @@
import { defineMiddleware } from 'astro/middleware';
export const onRequest = defineMiddleware((context, next) => {
if (context.request.url.endsWith('/sitemap.xml')) {
return context.redirect('/sitemap-root.xml'); // Ensure direct access
}
return next();
});

View File

@ -9,6 +9,7 @@ import { ICADNodeSchema, IComponentConfig } from '@polymech/commons/component'
import { ContentEntryRenderFunction, ContentEntryType } from 'astro'
import { RenderedContent, DataEntry } from "astro:content"
import type { Loader, LoaderContext } from 'astro/loaders'
import {
@ -26,13 +27,16 @@ import { gallery } from '../base/media.js';
import { get } from '@polymech/commons/component'
import { PFilterValid } from '@polymech/commons/filter'
import { IAssemblyData} from '@polymech/cad'
import { IAssemblyData } from '@polymech/cad'
import { logger as log } from '@/base/index.js'
import { translate } from "@/base/i18n.js"
import { I18N_SOURCE_LANGUAGE } from "config/config.js"
import { slugify } from "@/base/strings.js"
interface ILoaderContextEx extends LoaderContext {
export interface ILoaderContextEx extends LoaderContext {
entryTypes: Map<string, ContentEntryType>
}
interface IComponentConfigEx extends IComponentConfig {
export interface IComponentConfigEx extends IComponentConfig {
content: string
extra_resources?: string
shared_resources?: string
@ -48,8 +52,7 @@ let loaderCtx: ILoaderContextEx
const renderFunctionByContentType = new WeakMap<ContentEntryType, ContentEntryRenderFunction>();
const filterBranch = (items: { rel: string, config, path }[],
branch: string = RETAIL_PRODUCT_BRANCH) => {
const filterBranch = (items: { rel: string, config, path }[], branch: string) => {
if (!PRODUCT_BRANCHES) {
return items
}
@ -60,7 +63,9 @@ const filterBranch = (items: { rel: string, config, path }[],
return items.filter((item) => branchItems.includes(item.rel))
}
export const items = (opts: {}) => filterBranch(get(`${PRODUCT_ROOT()}/${PRODUCT_GLOB}`, PRODUCT_ROOT(), PFilterValid.marketplace_component))
export const items = (branch: string) =>
filterBranch(get(`${PRODUCT_ROOT()}/${PRODUCT_GLOB}`,
PRODUCT_ROOT(), PFilterValid.marketplace_component), branch)
const onComponent = async (item: IStoreItem, ctx: ILoaderContextEx) => {
@ -163,7 +168,7 @@ const onItem = async (item: IStoreItem, ctx: ILoaderContextEx) => {
ctx.logger.error(`Error completing ${''}: no data`);
return
}
if(!loaderCtx){
if (!loaderCtx) {
loaderCtx = ctx
}
const { logger } = ctx
@ -179,22 +184,10 @@ const onItem = async (item: IStoreItem, ctx: ILoaderContextEx) => {
renderings: [],
gallery: []
}
//////////////////////////////////////////
//
// Body
//
let contentPath = path.join(itemDir, 'templates/shared', 'body.md')
await getRenderFunction(contentPath)
if (exists(contentPath)) {
data.content = read(contentPath) as string
item.filePath = contentPath
}
//////////////////////////////////////////
//
// Item Extra Resources
//
let resourcesPath = path.join(itemDir, 'templates/shared', 'resources.md')
exists(resourcesPath) && (data.extra_resources = read(resourcesPath) as string || "")
data.body = (read(path.join(itemDir, 'templates/shared', 'body.md')) as string) || ""
data.resources = (read(path.join(itemDir, 'templates/shared', 'resources.md')) as string) || ""
//////////////////////////////////////////
//
// Item Shared Resources
@ -203,15 +196,13 @@ const onItem = async (item: IStoreItem, ctx: ILoaderContextEx) => {
stopAt: PRODUCT_ROOT(),
cwd: itemDir
}) || ""
exists(resourcesDefaultPath) && (data.shared_resources = read(resourcesDefaultPath) as string || "")
data.shared = (read(resourcesDefaultPath) as string) || ""
//////////////////////////////////////////
//
// Readme
//
let readmePath = path.join(itemDir, 'Readme.md')
if (exists(readmePath)) {
data.readme = read(readmePath) as string
}
data.readme = (read(readmePath) as string) || ""
//////////////////////////////////////////
//
// Variables
@ -247,18 +238,17 @@ const onItem = async (item: IStoreItem, ctx: ILoaderContextEx) => {
data.assets.renderings = await gallery('renderings', data.rel) as []
data.assets.renderings.length && (data.thumbnail =
{
alt: '',
url: data.assets.renderings[0].thumb,
src: data.assets.renderings[0].thumb
alt: data.assets.renderings[0].alt,
url: data.assets.renderings[0].url,
src: data.assets.renderings[0].url
})
data.assets.gallery = await gallery('media/gallery', data.rel) as []
data.image = data.assets.renderings[0] || {}
data.assets.showcase = await gallery('media/showcase', data.rel) as []
data.assets.samples = await gallery('media/samples', data.rel) as []
}
export function loader(): Loader {
export function loader(branch: string): Loader {
const load = async ({
config,
logger,
@ -269,18 +259,19 @@ export function loader(): Loader {
entryTypes }: ILoaderContextEx) => {
store.clear();
let products = items({})
let products = items(branch)
for (const item of products) {
const product: any = item.config
const id = product.slug;
const data = {
rel: item.rel,
title: product.name,
slug: id,
id,
title: product.name,
type: 'product',
highlights: [],
components: [],
...product
...product,
}
//const parsedData = await parseData({ id, data: data });
const storeItem = {
@ -298,9 +289,11 @@ export function loader(): Loader {
generateDigest,
entryTypes
} as any)
storeItem.data['config'] = JSON.stringify({
...storeItem.data
}, null, 2)
store.set(storeItem)
}
}
@ -309,3 +302,21 @@ export function loader(): Loader {
load
};
}
export const group_path = (item) => item.id.split("/")[1]
const group_label = async (text: string, locale) => await translate(slugify(text), I18N_SOURCE_LANGUAGE, locale)
const group = async (items, locale) => {
return items.reduce(async (accPromise, item) => {
const acc = await accPromise
const id = group_path(item)
let key: string = await group_label(id, locale)
key = key.charAt(0).toUpperCase() + key.slice(1)
if (!acc[key]) {
acc[key] = []
}
acc[key].push(item)
return acc
}, {})
}
export const group_by_path = async (items, locale): Promise<IComponentConfigEx[]> => await group(items, locale)

View File

@ -59,11 +59,15 @@ export const get = async (node: IComponentNode, component: IComponentConfig, opt
url?: string
}): Promise<ProductJsonLD> => {
if(!component.price || !component.body){
return {} as any
}
const jsonLD: ProductJsonLD = {
'@context': 'https://schema.org',
'@type': 'Product',
name: component.name,
description: component.keywords,
description: component.body || component.keywords,
sku: component.code,
brand: {
'@type': 'Brand',

View File

@ -3,16 +3,16 @@ import BaseLayout from "@/layouts/BaseLayout.astro";
import Wrapper from "@/components/containers/Wrapper.astro";
import { LANGUAGES_PROD } from "config/config.js";
import config from "config/config.json";
import { getCollection } from "astro:content";
import StoreEntries from "@/components/store/StoreEntries.astro";
import CtaOne from "@/components/cta/CtaOne.astro";
import { translate } from "@/base/i18n.js"
import { I18N_SOURCE_LANGUAGE } from "config/config.js"
import { slugify } from "@/base/strings.js"
import { group_by_path } from "@/model/component.js";
const allProducts = await getCollection("store")
const locale = Astro.currentLocale
const store = `/${locale}/store/`;
const view = "store";
const items = await getCollection(view);
const locale = Astro.currentLocale;
const store = `/${locale}/${view}/`;
export function getStaticPaths() {
const all: unknown[] = [];
@ -26,38 +26,38 @@ export function getStaticPaths() {
});
return all;
}
const group_label = async (text: string) => await translate(slugify(text), I18N_SOURCE_LANGUAGE, locale)
const group = async (items) => {
return items.reduce(async (accPromise, item: any) => {
const acc = await accPromise
const id = item.id.split("/")[1]
let key:string = (await group_label(id))
key = key.charAt(0).toUpperCase() + key.slice(1)
if (!acc[key]) {
acc[key] = []
const groups = await group_by_path(items, locale);
const frontmatter = {
title: "Store",
description: "Store",
keywords: "Store",
image:{
src: config.pages.home.hero,
url: config.pages.home.hero,
alt: config.metadata.keywords
}
acc[key].push(item)
return acc
}, {})
}
const items = await group(allProducts)
};
---
<BaseLayout>
<Wrapper variant="standard" class="py-4">
<BaseLayout frontmatter={frontmatter}>
<Wrapper variant="standard" class="py-4 font-mono">
<CtaOne />
<section>
<div class="py-2 space-y-2">
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
</div>
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-4"></div>
</div>
</section>
{
Object.keys(items).map((relKey) => (
Object.keys(groups).map((relKey) => (
<section>
<h1 aria-hidden="true" class="p-4 text-2xl text-neutral-500 space-x-8"> {relKey} </h1>
<h3
aria-hidden="true"
class="p-4 text-xs space-x-8 text-neutral-600"
>
{relKey}
</h3>
<div class="grid sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-2 gap-2">
{items[relKey].map((post) => (
{groups[relKey].map((post) => (
<StoreEntries
key={post.id}
url={store + post.id}

View File

@ -0,0 +1,34 @@
---
import { getCollection } from 'astro:content'
import { LANGUAGES_PROD as LANGUAGES } from "config/config.js"
import InfoPagesLayout from '@/layouts/InfoPagesLayout.astro';
export async function getStaticPaths()
{
const view = 'infopages'
const items = await getCollection(view)
const all: unknown[] = []
LANGUAGES.forEach((lang) => {
items.forEach((item) => {
all.push({
params: {
locale: lang,
path: item.slug,
},
props: {
page: item,
locale: lang,
path: item.id,
view
},
trailingSlash: false
})
})
})
return all
}
const { page } = Astro.props;
const { Content } = await page.render();
---
<InfoPagesLayout frontmatter={page.data}>
<Content />
</InfoPagesLayout>

View File

@ -31,5 +31,13 @@ const locale = Astro.currentLocale || "en"
</div>
</section>
{ <script>
const currentPath = window.location.pathname;
if (!/^\/[a-z]{2}(\/|$)/i.test(currentPath)) {
let language = navigator.language
language = language.split('-')[0]
language = "en"
window.location.href = `/${language}`;
}
</script>
</BaseLayout>

13
src/pages/robots.txt.ts Normal file
View File

@ -0,0 +1,13 @@
import type { APIRoute } from 'astro';
const getRobotsTxt = (sitemapURL: URL) => `
User-agent: *
Allow: /
Sitemap: ${sitemapURL.href}
`;
export const GET: APIRoute = ({ site }) => {
const sitemapURL = new URL('sitemap-index.xml', site);
return new Response(getRobotsTxt(sitemapURL));
};

View File

@ -0,0 +1,24 @@
---
export async function GET() {
console.log('sitemap-index.xml.astro')
const locales = ['en', 'es', 'fr'];
const baseUrl = 'https://yourdomain.com';
let sitemaps = locales
.map(locale => `<sitemap><loc>${baseUrl}/sitemap-${locale}.xml</loc></sitemap>`)
.join('');
return new Response(
`<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${sitemaps}
</sitemapindex>`,
{
headers: {
'Content-Type': 'application/xml',
},
}
);
}
---

View File

@ -1,5 +1,5 @@
@import "tailwindcss";
@plugin "@tailwindcss/forms";
@plugin "@tailwindcss/typography";
@layer components {
@ -54,6 +54,7 @@
.astro-imagetools-img {
@apply min-w-full border-collapse rounded-lg overflow-hidden;
background-image: none !important;
object-fit: contain;
}
.lightbox-main{
background-image: none !important;
@ -172,9 +173,6 @@
.markdown-content {
@apply prose dark:prose-invert max-w-none font-mono;
}

17
sync-polycraft.json Normal file
View File

@ -0,0 +1,17 @@
{
"debug": true,
"matching": [
"src/**",
"*.md",
"*.json",
"*.cjs",
"docs",
"packages/imagetools/**",
"scripts",
"meta/**",
"tests/**",
"*.mjs",
"!node_modules/**",
"!ref/**"
]
}

View File

@ -0,0 +1,410 @@
{
"name": "url-redirector",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "url-redirector",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"@types/node": "^22.13.10",
"@types/yargs": "^17.0.33",
"ts-node": "^10.9.2",
"typescript": "^5.8.2",
"yargs": "^17.7.2"
}
},
"node_modules/@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
"license": "MIT",
"dependencies": {
"@jridgewell/trace-mapping": "0.3.9"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@tsconfig/node10": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
"license": "MIT"
},
"node_modules/@tsconfig/node12": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
"license": "MIT"
},
"node_modules/@tsconfig/node14": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
"license": "MIT"
},
"node_modules/@tsconfig/node16": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
"license": "MIT"
},
"node_modules/@types/node": {
"version": "22.13.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz",
"integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.20.0"
}
},
"node_modules/@types/yargs": {
"version": "17.0.33",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz",
"integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==",
"license": "MIT",
"dependencies": {
"@types/yargs-parser": "*"
}
},
"node_modules/@types/yargs-parser": {
"version": "21.0.3",
"resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz",
"integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==",
"license": "MIT"
},
"node_modules/acorn": {
"version": "8.14.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/acorn-walk": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
"license": "MIT",
"dependencies": {
"acorn": "^8.11.0"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"license": "MIT"
},
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^7.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT"
},
"node_modules/create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"license": "MIT"
},
"node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.3.1"
}
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"license": "MIT"
},
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"license": "ISC",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"license": "ISC"
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/ts-node": {
"version": "10.9.2",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
"license": "MIT",
"dependencies": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
"@tsconfig/node12": "^1.0.7",
"@tsconfig/node14": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"v8-compile-cache-lib": "^3.0.1",
"yn": "3.1.1"
},
"bin": {
"ts-node": "dist/bin.js",
"ts-node-cwd": "dist/bin-cwd.js",
"ts-node-esm": "dist/bin-esm.js",
"ts-node-script": "dist/bin-script.js",
"ts-node-transpile-only": "dist/bin-transpile.js",
"ts-script": "dist/bin-script-deprecated.js"
},
"peerDependencies": {
"@swc/core": ">=1.2.50",
"@swc/wasm": ">=1.2.50",
"@types/node": "*",
"typescript": ">=2.7"
},
"peerDependenciesMeta": {
"@swc/core": {
"optional": true
},
"@swc/wasm": {
"optional": true
}
}
},
"node_modules/typescript": {
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
"integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
"license": "MIT"
},
"node_modules/v8-compile-cache-lib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
"license": "MIT"
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"license": "ISC",
"engines": {
"node": ">=10"
}
},
"node_modules/yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
"license": "MIT",
"dependencies": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.1.1"
},
"engines": {
"node": ">=12"
}
},
"node_modules/yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"license": "MIT",
"engines": {
"node": ">=6"
}
}
}
}

View File

@ -0,0 +1,27 @@
{
"name": "url-redirector",
"version": "1.0.0",
"description": "",
"main": "dist/index.js",
"bin": {
"url-redirector": "dist/index.js"
},
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"run": "node dist/index.js --file=./test.json",
"dev": "ts-node --esm src/index.ts",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"type": "module",
"dependencies": {
"@types/node": "^22.13.10",
"@types/yargs": "^17.0.33",
"ts-node": "^10.9.2",
"typescript": "^5.8.2",
"yargs": "^17.7.2"
}
}

View File

@ -0,0 +1,4 @@
{
"/old-page": "/new-page",
"https://shop.osr-plastic.org/injection/katbot/": "https://polymech.io/en/store/products/injection/katbot-pro-mega-beta/"
}

View File

@ -0,0 +1,113 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "ES2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "libReplacement": true, /* Enable lib replacement. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "NodeNext", /* Specify what module code is generated. */
"rootDir": "src", /* Specify the root folder within your source files. */
"moduleResolution": "NodeNext", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "noUncheckedSideEffectImports": true, /* Check side effect imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
"outDir": "dist", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
// "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}

View File

@ -4,9 +4,9 @@ export default {
theme: {
extend: {},
},
daisyui: {
// Add your daisy ui themes here
themes: ["night"],
},
plugins: [require("daisyui"), require("@tailwindcss/typography")],
};
plugins: [require("@tailwindcss/typography")],
presets: [
]
}

View File

@ -1 +0,0 @@
TEST,TEST
1 TEST TEST

View File

@ -1,10 +0,0 @@
{
"glossaryId": "12816437-7e4f-4807-9137-eff9ce324a19",
"name": "OSR-en-fr",
"ready": true,
"sourceLang": "en",
"targetLang": "fr",
"creationTime": "2025-03-07T12:38:05.401Z",
"entryCount": 1,
"hash": "zDrlayXO43sB98wNoqof0g=="
}

View File

@ -0,0 +1,10 @@
{
"glossaryId": "94f02b66-2ee8-4119-a8d7-e0a11dd8992d",
"name": "OSR-en-pt",
"ready": true,
"sourceLang": "en",
"targetLang": "pt",
"creationTime": "2025-03-11T22:17:00.006Z",
"entryCount": 1,
"hash": "zDrlayXO43sB98wNoqof0g=="
}