This commit is contained in:
babayaga 2025-12-27 09:25:40 +01:00
parent e7411d448c
commit e4f2d4c51b
30 changed files with 1479 additions and 234 deletions

View File

@ -0,0 +1,250 @@
{
"site": {
"title": "Polymech Library",
"base_url": "https://library.polymech.info/",
"description": "",
"base_path": "/",
"trailing_slash": false,
"favicon": "/images/favicon.png",
"logo": "/images/logo.png",
"logo_darkmode": "/images/logo-darkmode.png",
"logo_width": "150",
"logo_height": "33",
"logo_text": "Polymech Library",
"image": {
"default": "/images/default-image.png",
"error": "/images/error-image.png",
"alt": "Polymech Library"
}
},
"footer_left": [
{
"href": "/${LANG}/resources/info/contact",
"text": "Contact"
},
{
"href": "https://forum.polymech.info/",
"text": "Forum"
},
{
"href": "https://files.polymech.info/",
"text": "Files"
},
{
"href": "https://git.polymech.info/explore/repos",
"text": "Github"
}
],
"footer_right": [],
"settings": {
"search": true,
"account": true,
"sticky_header": true,
"theme_switcher": true,
"default_theme": "system"
},
"params": {
"contact_form_action": "#",
"copyright": "Designed And Developed by [Themefisher](https://themefisher.com/)"
},
"navigation": {
"top": [
{
"href": "/${LANG}",
"text": "Home"
},
{
"href": "/${LANG}/resources",
"text": "Resources"
},
{
"href": "/${LANG}/library",
"text": "Library"
},
{
"href": "/${LANG}/howtos",
"text": "Tutorials"
},
{
"href": "https://service.polymech.info/",
"text": "Media"
},
{
"href": "https://forum.polymech.info/",
"text": "Forum"
},
{
"href": "/${LANG}/resources/info/contact",
"text": "Contact"
}
]
},
"navigation_button": {
"enable": true,
"label": "Get Started",
"link": "https://github.com/themefisher/astrofront"
},
"ecommerce": {
"brand": "Polymech",
"currencySymbol": "",
"currencyCode": "EU"
},
"metadata": {
"country": "Spain",
"city": "Barcelona",
"author": "Polymech",
"author_bio": "I am in, if its true",
"author_url": "https://polymech.info/",
"image": "/images/og-image.png",
"description": "Polymech is a plastic prototyping company that offers product design services.",
"keywords": "Plastic, Prototyping, Product Design, Opensource"
},
"shopify": {
"currencySymbol": "",
"currencyCode": "EU",
"collections": {
"hero_slider": "hidden-homepage-carousel",
"featured_products": "featured-products"
}
},
"pages": {
"home": {
"hero": "https://assets.osr-plastic.org/machines//assets/newsletter/common/products/extruders/overview-3.jpg",
"_blog": {
"store": "resources"
}
}
},
"core": {
"logging_namespace": "polymech-site",
"translate_content": true,
"languages": [
"en",
"ar",
"de",
"ja",
"es",
"zh",
"fr"
],
"languages_prod": [
"en",
"es",
"fr"
],
"rtl_languages": [
"ar"
],
"osr_root": "${OSR_ROOT}"
},
"dev": {
"file_server": "localhost:5000"
},
"i18n": {
"store": "${OSR_ROOT}/i18n-store/store-${LANG}.json",
"cache": true,
"asset_path": "${SRC_DIR}/${SRC_NAME}-${DST_LANG}${SRC_EXT}"
},
"products": {
"root": "${OSR_ROOT}/products",
"howto_migration": "./data/last.json",
"glob": "**/config.json",
"enabled": "${OSR_ROOT}/products/config/machines.json"
},
"retail": {
"library_branch": "site-prod",
"projects_branch": "projects"
},
"rss": {
"title": "Polymech RSS Feed",
"description": ""
},
"osrl": {
"env": "astro-release",
"env_dev": "astro-debug",
"module_name": "polymech.io",
"lang_flavor": "osr",
"product_profile": "./src/app/profile.json"
},
"features": {
"show_description": false,
"show_license": true,
"show_renderings": true,
"show_tabs": false,
"show_gallery": true,
"show_files": true,
"show_specs": true,
"show_checkout": true,
"show_contact": true,
"show_3d_preview": true,
"show_resources": true,
"show_debug": false,
"show_samples": true,
"show_readme": false,
"show_related": true,
"show_showcase": true,
"show_screenshots": true
},
"defaults": {
"image_url": "https://picsum.photos/640/640",
"license": "CERN Open Source Hardware License",
"contact": "sales@plastic-hub.com"
},
"cad": {
"cache": true,
"export_configurations": true,
"export_sub_components": true,
"renderer": "solidworks",
"renderer_view": "Render",
"renderer_quality": 1,
"extensions": [
".STEP",
".html"
],
"model_ext": ".tree.json",
"default_configuration": "Default",
"main_match": "${product}/cad*/*Global*.+(SLDASM)",
"cam_main_match": "${product}/cad*/*-CNC*.+(SLDASM)"
},
"assets": {
"cad_url": "${OSR_MACHINES_ASSETS_URL}/${file}",
"url": "${OSR_MACHINES_ASSETS_URL}/products/${product_rel_min}/${file}",
"item_url_r": "${OSR_MACHINES_ASSETS_URL}/${ITEM_REL}/${assetPath}/${filePath}",
"item_url": "http://${FILE_SERVER_DEV}/${ITEM_REL}/${assetPath}/${filePath}"
},
"optimization": {
"image_settings": {
"gallery": {
"show_title": true,
"show_description": false,
"sizes_thumb": "(min-width: 120px) 120px, 120vw",
"sizes_large": "(min-width: 1024px) 1024px, 1024vw",
"sizes_regular": "(min-width: 400px) 400px, 400vw"
},
"lightbox": {
"show_title": true,
"show_description": true,
"sizes_thumb": "(min-width: 120px) 120px, 120vw",
"sizes_large": "(min-width: 1024px) 1024px, 1024vw",
"sizes_regular": "(min-width: 400px) 400px, 400vw"
}
},
"presets": {
"slow": {
"sizes_medium": "(min-width: 100px) 100px, 100vw",
"sizes_thumbs": "(min-width: 80px) 80px, 80vw",
"sizes_large": "(min-width: 320px) 320px, 320vw"
},
"medium": {
"sizes_medium": "(min-width: 400px) 400px, 400vw",
"sizes_thumbs": "(min-width: 120px) 120px, 120vw",
"sizes_large": "(min-width: 1024px) 1024px, 1024vw"
},
"fast": {
"sizes_medium": "(min-width: 1024px) 1024px, 1024vw",
"sizes_thumbs": "(min-width: 180px) 180px, 180vw",
"sizes_large": "(min-width: 1200px) 1200px, 1200vw"
}
}
}
}

View File

@ -0,0 +1,5 @@
{
"core": {
"logging_namespace": "LOCAL_CONFIG_OVERRIDE"
}
}

3
packages/polymech/scripts/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
node_modules/
config.json
.kbot

View File

@ -0,0 +1,43 @@
# File Deployment Script
A simple deployment script that uses Node-SSH to:
1. Upload a file from `./releases/dist.zip` to `/var/vhosts/polymech.io/httpdocs`
2. Unzip the file on the server
## Prerequisites
- Node.js
- NPM
## Installation
```bash
npm install
```
## Configuration
Edit the `config.json` file with your server credentials:
```json
{
"server": {
"host": "your-server-host",
"username": "your-username",
"password": "your-password",
"port": 22
}
}
```
## Usage
```bash
npm run deploy
```
## Note
For security, `config.json` is included in `.gitignore` to prevent sensitive credentials from being committed to version control.

View File

@ -0,0 +1,6 @@
npm run build
./scripts/deploy.sh
#Shutdown.exe -s -t 00
#sh scripts/sync.sh
#sh scripts/zip.sh

View File

@ -0,0 +1,35 @@
#!/bin/bash
# Define repositories to commit to
# . refers to the current directory (polymech/site2)
# ../polymech-astro refers to the sibling directory
REPOS=("." "../polymech-astro")
# Store the optional commit message
MSG="$1"
# Iterate over each repository
for repo in "${REPOS[@]}"; do
echo "--------------------------------------------------"
echo "Processing repository: $repo"
echo "--------------------------------------------------"
# Execute in a subshell to preserve current directory
(
cd "$repo" || { echo "Failed to enter $repo"; exit 1; }
# Add all changes
git add -A .
# Commit
if [ -n "$MSG" ]; then
git commit -m "$MSG"
else
# If no message provided, let git open the editor
git commit
fi
# Pushing changes
git push
)
done

View File

@ -0,0 +1,59 @@
/**
* Deployment Script
* Uploads dist.zip to server and unzips it
*/
const { NodeSSH } = require('node-ssh');
const fs = require('fs');
const path = require('path');
// Load credentials from config.json
const config = require('./config.json');
// Paths
const localFilePath = './releases/dist.zip';
const remoteDirectory = '/var/www/vhosts/polymech.io/httpdocs';
const remoteFilePath = `${remoteDirectory}/dist.zip`;
const ssh = new NodeSSH();
/**
* Main deployment function
*/
async function deploy() {
try {
// Check if the local file exists
if (!fs.existsSync(localFilePath)) {
throw new Error(`File ${localFilePath} not found`);
}
console.log('Connecting to server...');
await ssh.connect({
host: config.server.host,
username: config.server.username,
password: config.server.password,
port: config.server.port
});
// Upload the file
console.log(`Uploading ${localFilePath} to ${remoteFilePath}...`);
await ssh.putFile(localFilePath, remoteFilePath);
// Unzip the file
console.log(`Unzipping file on server...`);
await ssh.execCommand(`unzip -o ${remoteFilePath} -d ${remoteDirectory}`);
// Optional: Clean up the zip file after extraction
console.log(`Removing zip file...`);
await ssh.execCommand(`rm ${remoteFilePath}`);
console.log('Deployment completed successfully!');
} catch (error) {
console.error('Deployment failed:', error);
} finally {
// Close the connection
ssh.dispose();
}
}
deploy();

View File

@ -0,0 +1,9 @@
#!/bin/bash
# Example script for Google Drive operations using rclone
# Define local and remote paths
LOCAL_DIR="./dist"
GDRIVE_DIR="creava:/httpdocs"
rclone copy "${LOCAL_DIR}" "${GDRIVE_DIR}" --progress --transfers 4

View File

@ -0,0 +1,64 @@
import fs from 'fs';
import path from 'path';
import { quicktype, InputData, jsonInputForTargetLanguage } from 'quicktype-core';
const CONFIG_PATH = path.resolve('./app-config.json');
const OUTPUT_SCHEMA_PATH = path.resolve('./src/app/config.schema.ts');
const OUTPUT_DTS_PATH = path.resolve('./src/app/config.d.ts');
async function main() {
console.log(`Reading config from ${CONFIG_PATH}...`);
const configContent = fs.readFileSync(CONFIG_PATH, 'utf8');
// 1. Generate TypeScript Definitions (d.ts) FIRST
console.log('Generating TypeScript definitions...');
const tsInput = jsonInputForTargetLanguage("ts");
await tsInput.addSource({
name: "AppConfig",
samples: [configContent]
});
const tsInputData = new InputData();
tsInputData.addInput(tsInput);
const tsResult = await quicktype({
inputData: tsInputData,
lang: "ts",
rendererOptions: {
"just-types": "true",
"acronym-style": "original"
}
});
const tsCode = tsResult.lines.join('\n');
fs.writeFileSync(OUTPUT_DTS_PATH, tsCode);
console.log(`Wrote TypeScript definitions to ${OUTPUT_DTS_PATH}`);
// 2. Generate Zod Schema from Types using ts-to-zod
console.log('Generating Zod schema from types...');
try {
const { execSync } = await import('child_process');
// ts-to-zod <input> <output>
// Use relative paths to avoid Windows path concatenation issues with ts-to-zod
const relDts = path.relative(process.cwd(), OUTPUT_DTS_PATH);
const relSchema = path.relative(process.cwd(), OUTPUT_SCHEMA_PATH);
execSync(`npx ts-to-zod "${relDts}" "${relSchema}"`, { stdio: 'inherit', cwd: process.cwd() });
// Append export type AppConfig
fs.appendFileSync(OUTPUT_SCHEMA_PATH, `\nexport type AppConfig = z.infer<typeof appConfigSchema>;\n`);
console.log(`Wrote Zod schema to ${OUTPUT_SCHEMA_PATH}`);
} catch (error) {
console.error('Failed to generate Zod schema:', error);
throw error;
}
}
main().catch(err => {
console.error('Error fetching/generating config:', err);
process.exit(1);
});

View File

@ -0,0 +1,20 @@
{
"name": "file-deployment",
"version": "1.0.0",
"description": "Simple deployment script using Node-SSH",
"main": "deploy.js",
"type": "commonjs",
"scripts": {
"deploy": "node deploy.js"
},
"keywords": [
"deployment"
],
"author": "Guenter",
"license": "ISC",
"dependencies": {
"@plastichub/fs": "file:../../../osr/osr-mono/packages/fs",
"@plastichub/osr-commons": "file:../../../osr/osr-mono/packages/osr-commons",
"node-ssh": "^13.2.0"
}
}

View File

@ -0,0 +1,172 @@
const { spawnSync } = require('child_process');
const fs = require('fs');
const path = require('path');
// Create temp directory if it doesn't exist
const tempDir = path.join(process.cwd(), 'temp');
if (!fs.existsSync(tempDir)) {
fs.mkdirSync(tempDir, { recursive: true });
}
function profileBuild() {
console.log('Starting build profiling...\n');
const startTime = new Date();
const timestamp = startTime.getTime();
// Run the build command with profiling flags
const buildResult = spawnSync('npm', [
'run',
'build',
'--',
'--node-options=--cpu-prof --heap-prof --trace-gc --no-logfile-per-isolate --log-internal-timer-events --log-timer-events --prof'
], {
stdio: ['inherit', 'pipe', 'pipe'],
encoding: 'utf8',
shell: true,
env: { ...process.env, FORCE_COLOR: '1' }
});
const endTime = new Date();
const duration = (endTime - startTime) / 1000;
// Process profiling data
try {
// Find and process the CPU profile
const cpuProfiles = fs.readdirSync('.').filter(f => f.endsWith('.cpuprofile'));
if (cpuProfiles.length > 0) {
const profileFile = path.join(tempDir, 'cpu-last.cpuprofile');
fs.renameSync(cpuProfiles[0], profileFile);
console.log('CPU profile saved:', profileFile);
} else {
console.log('No CPU profile found');
}
// Find and process heap snapshots
const heapFiles = fs.readdirSync('.').filter(f => f.endsWith('.heapprofile'));
if (heapFiles.length > 0) {
const heapFile = path.join(tempDir, 'heap-last.heapprofile');
fs.renameSync(heapFiles[0], heapFile);
console.log('Heap snapshot saved:', heapFile);
} else {
console.log('No heap snapshot found');
}
// Find and process v8.log
const v8Logs = fs.readdirSync('.').filter(f => f.startsWith('v8.log'));
if (v8Logs.length > 0) {
const logFile = path.join(tempDir, 'v8-last.log');
fs.renameSync(v8Logs[0], logFile);
console.log('V8 log saved:', logFile);
} else {
console.log('No V8 log found');
}
// Generate markdown report
const reportContent = generateMarkdownReport({
startTime,
endTime,
duration,
output: buildResult.stdout || '',
error: buildResult.stderr || '',
status: buildResult.status === 0 ? 'Success' : 'Failed',
cpuProfiles: cpuProfiles.length > 0 ? ['cpu-last.cpuprofile'] : [],
heapFiles: heapFiles.length > 0 ? ['heap-last.heapprofile'] : [],
v8Logs: v8Logs.length > 0 ? ['v8-last.log'] : []
});
fs.writeFileSync(path.join(tempDir, 'report-build.md'), reportContent);
console.log('\nBuild profiling completed.');
console.log(`Profile data saved in: ${tempDir}`);
console.log(`Markdown report saved at: ${path.join(tempDir, 'report-build.md')}\n`);
if (buildResult.status !== 0) {
console.error('Build failed with error:');
console.error(buildResult.stderr);
process.exit(1);
}
} catch (err) {
console.error('Error processing profile data:', err);
process.exit(1);
}
}
function generateMarkdownReport({ startTime, endTime, duration, output, error, status, cpuProfiles, heapFiles, v8Logs }) {
const durationMinutes = Math.floor(duration / 60);
const durationSeconds = (duration % 60).toFixed(2);
return `# Astro Build Performance Report
## Build Information
- **Start Time:** ${startTime.toLocaleString()}
- **End Time:** ${endTime.toLocaleString()}
- **Duration:** ${durationMinutes}m ${durationSeconds}s
- **Build Status:** ${status === 'Success' ? '✅ Success' : '❌ Failed'}
## Profile Data
${cpuProfiles.length > 0 ? `### CPU Profiles
CPU profiles are available in:
${cpuProfiles.map(file => `- \`${file}\``).join('\n')}
To analyze the CPU profiles:
1. Open Chrome DevTools
2. Go to the Performance tab
3. Click "Load profile..."
4. Select a CPU profile file from the temp directory
` : ''}
${heapFiles.length > 0 ? `### Heap Snapshots
Heap snapshots are available in:
${heapFiles.map(file => `- \`${file}\``).join('\n')}
To analyze the heap snapshots:
1. Open Chrome DevTools
2. Go to the Memory tab
3. Click "Load..."
4. Select a heap snapshot file from the temp directory
` : ''}
${v8Logs.length > 0 ? `### V8 Logs
V8 execution logs are available in:
${v8Logs.map(file => `- \`${file}\``).join('\n')}
To analyze the V8 logs:
1. Use Node.js's built-in profiler tools
2. Run \`node --prof-process <log-file>\` to process the logs
3. Review the generated report for execution statistics
` : ''}
## Build Output
\`\`\`
${output}
\`\`\`
${error ? `## Build Errors\n\`\`\`\n${error}\n\`\`\`\n` : ''}
## Recommendations
1. **Build Process Optimization**
- Enable parallel processing where available
- Implement build caching
- Consider using bundling for frequently used modules
2. **Resource Management**
- Review and optimize memory-intensive operations
- Consider implementing lazy loading for large modules
- Monitor and optimize garbage collection patterns
3. **Performance Monitoring**
- Set up continuous performance monitoring
- Track build times over time
- Monitor resource usage during builds
`;
}
profileBuild();

View File

@ -0,0 +1,25 @@
import handler from 'serve-handler';
import http from 'http';
import { PRODUCT_ROOT, FILE_SERVER_DEV } from '../src/app/config';
// Parse host and port from FILE_SERVER_DEV
const [host, portStr] = FILE_SERVER_DEV.split(':');
const port = parseInt(portStr, 10);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const server = http.createServer((request: any, response: any) => {
return handler(request, response, {
public: PRODUCT_ROOT(),
headers: [
{
source: '**/*',
headers: [{ key: 'Access-Control-Allow-Origin', value: '*' }]
}
]
});
});
server.listen(port, host, () => {
console.log(`Running at http://${host}:${port}`);
console.log(`Serving files from: ${PRODUCT_ROOT()}`);
});

View File

@ -0,0 +1,8 @@
echo "Starting sync from ./dist to creava:/httpdocs..."
rclone sync ./dist creava:/httpdocs \
--progress \
--stats-one-line \
-v
echo "Sync completed!"

View File

@ -0,0 +1,11 @@
osr-sync sync --logLevel=debug --clear=false --source='.' --target='../astro-playground' --debug=false --profile="./sync-playground.json"
cd ../astro-playground
git add -A .
git commit -m "Synced from site"
git push
# osr-sync sync --logLevel=debug --clear=false --source='.' --target='../guides' --debug=false --profile="./sync-playground.json"
#cd ../guides
#git add .
#git commit -m "Synced from site"
#git push

View File

@ -0,0 +1,11 @@
import { loadConfig } from '../src/app/config-loader.js';
import { I18N_SOURCE_LANGUAGE } from "../src/app/constants.js";
try {
const config = loadConfig(I18N_SOURCE_LANGUAGE);
console.log('LOGGING_NAMESPACE:', config.core.logging_namespace);
console.log('SHOW_GALLERY:', config.features.show_gallery);
} catch (e) {
console.error(e);
}

View File

@ -0,0 +1,17 @@
## Todos
skip checked todos
- platform : windows
- environment : Astro (5.3+)
- language: Typescript, ESM
- dont use classes
- no lazy imports
- use pure functions
- each handler has the same function signature and naming
- [ ] create a registry of handlers, transforming json data to platform specific formats, in ../src/model/registry.ts, the input schema is provided (see ../polymech-mono/packages/commons/src/component.ts), create a new file per type, eg: ../src/model/rss.ts
- [ ] "rss" (@astrojs/rss)
- [ ] "merchant" (google)
- [ ] "json-ld" (products)
- [ ] evaluate the registry on demand

View File

@ -0,0 +1,8 @@
## Todos
skip checked todos
- [ ] create javascript script, using Node-SSH :
- [ ] upload a file from "./releases/dist.zip", to /var/vhosts/polymech.io/httpdocs
- [ ] unzip the file on the server
- [ ] assume credentials from a json file

View File

@ -0,0 +1,13 @@
#kbotd --prompt=./Gallery.md --mode=completion --dst=./GalleryK.astro --filters=code --router=openai --model=gpt-4o
#kbotd --prompt=./Gallery.md --mode=completion --dst=./GalleryK2.astro --filters=code --include=./GalleryS.astro --router=openai --model=gpt-4o
#kbotd --prompt=./Lightbox.md --mode=completion --dst=./GalleryL.astro --filters=code --include=./GalleryM.astro --include=./lightbox.html --router=openai --model=gpt-4o
#kbotd --prompt=./todos.md --mode=completion --dst=./GalleryL2.astro --filters=code --include=./GalleryL.astro --model=openai/o1
#kbotd --prompt=./todos.md --include=resources.astro --filters=code
#kbotd --prompt=./todos.md --model=anthropic/claude-3.7-sonnet
# kbotd --prompt=./todos-merchant.md --model=anthropic/claude-3.7-sonnet --include=../../polymech-mono/packages/commons/src/component.ts
#--model=anthropic/claude-3.7-sonnet \
kbotd --preferences ./todos-merchant.md \
--include=../../polymech-mono/packages/commons/src/component.ts \
--disable=terminal,git,npm,user,interact,search,email,web \
--disableTools=read_file,read_files,list_files,file_exists,web

View File

@ -0,0 +1,37 @@
// src/integrations/build-logger.ts
import type { AstroIntegration } from 'astro';
export default function buildLogger(): AstroIntegration {
return {
name: 'build-logger',
hooks: {
'astro:build:start': () => {
console.log('Build started');
},
'astro:build:setup': ({ vite, target }) => {
console.log(`Build setup for target: ${target}`);
},
'astro:build:generated': ({ dir }) => {
console.log(`Files generated in: ${dir}`);
},
'astro:build:ssr': ({ manifest }) => {
console.log('SSR build complete');
console.log(`Manifest entries: ${Object.keys(manifest).length}`);
},
'astro:build:done': ({ dir, routes }) => {
console.log('Build completed');
console.log(`Output directory: ${dir}`);
console.log(`Built routes: ${routes.length}`);
// Log details of each built route
routes.forEach((route, index) => {
console.log(`[${index + 1}] Route: ${route.route}`);
console.log(` Type: ${route.type}`);
if (route.distURL) {
console.log(` Output: ${route.distURL.pathname}`);
}
});
}
},
};
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
osr-sync zip --logLevel=debug --clear=false --source='./dist' --target='./dist.zip' --debug=false --profile="./sync-deploy.json"

View File

@ -1,42 +1,107 @@
import * as fs from "fs";
import * as path from "path";
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import { substitute } from "@polymech/commons/variables";
import { appConfigSchema } from "@/app/config.schema.js";
import type { AppConfig } from "@/app/config.schema.js";
import { appConfigSchema } from "./config.schema.js";
import type { AppConfig } from "./config.schema.js";
import { I18N_SOURCE_LANGUAGE } from "@/app/config.js"
import { I18N_SOURCE_LANGUAGE } from "./constants.js"
const DEFAULT_CONFIG_PATH = path.resolve("./app-config.json");
const LIBRARY_CONFIG_PATH = path.resolve("./app-config.json");
const USER_CONFIG_DEFAULT_PATH = path.resolve("./app-config.local.json");
function deepMerge(target: any, source: any): any {
if (typeof source !== 'object' || source === null) {
return source; // Primitives or null overwrite
}
if (Array.isArray(source)) {
return source; // Arrays overwrite
}
if (typeof target !== 'object' || target === null || Array.isArray(target)) {
return source; // Target is not mergeable object, overwrite
}
const result = { ...target };
for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
const val = source[key];
if (typeof val === 'object' && val !== null && !Array.isArray(val)) {
result[key] = deepMerge(result[key], val);
} else {
result[key] = val;
}
}
}
return result;
}
export function loadConfig(
locale: string = I18N_SOURCE_LANGUAGE,
configPath: string = DEFAULT_CONFIG_PATH
libraryPath: string = LIBRARY_CONFIG_PATH
): AppConfig {
let rawContent: string;
// 1. Load Library Config (Defaults)
let rawLibraryContent: string;
try {
rawContent = fs.readFileSync(configPath, 'utf-8');
rawLibraryContent = fs.readFileSync(libraryPath, 'utf-8');
} catch (error) {
throw new Error(`Failed to read config file at ${configPath}: ${error}`);
throw new Error(`Failed to read library config file at ${libraryPath}: ${error}`);
}
const variables = {
LANG: locale
LANG: locale,
...process.env
};
const substitutedContent = substitute(false, rawContent, variables);
let parsedConfig: unknown;
const substitutedLibraryContent = substitute(false, rawLibraryContent, variables);
let libraryConfig: any;
try {
parsedConfig = JSON.parse(substitutedContent);
libraryConfig = JSON.parse(substitutedLibraryContent);
} catch (error) {
throw new Error(`Failed to parse config JSON after substitution: ${error}`);
throw new Error(`Failed to parse library config JSON: ${error}`);
}
const result = appConfigSchema.safeParse(parsedConfig);
// 2. Parse CLI Arguments
// We assume the caller might want to pass args, or we just grab process.argv
// We cast to any because yargs returns a complex type
const argv: any = yargs(hideBin(process.argv)).parseSync ? yargs(hideBin(process.argv)).parseSync() : (yargs(hideBin(process.argv)) as any).argv;
// 3. Determine User Config Path
// Check for --config <path>
const userConfigPath = argv.config ? path.resolve(argv.config) : USER_CONFIG_DEFAULT_PATH;
// 4. Load User Config (if exists)
let userConfig: any = {};
if (fs.existsSync(userConfigPath)) {
try {
const rawUserContent = fs.readFileSync(userConfigPath, 'utf-8');
const substitutedUserContent = substitute(false, rawUserContent, variables);
userConfig = JSON.parse(substitutedUserContent);
} catch (error) {
console.warn(`Failed to load or parse user config at ${userConfigPath}: ${error}`);
}
}
// 5. Merge: Library <- User <- CLI
// Note: yargs parses --config as part of argv, but also other flags like --core.logging_namespace
// We filter out specific known CLI-only flags if needed, but config schema validation will drop unknown keys anyway?
// Actually zod 'strip' is default in safeParse? No, usually it passes through unless strict().
// We should probably rely on valid keys overwriting.
// CLI args often come with standard keys like '$0', '_' which we might want to exclude if we blindly merge.
// However, deepMerge will add them.
// Ideally we would only merge keys that exist in the schema, but dynamic is fine for now.
let mergedConfig = deepMerge(libraryConfig, userConfig);
mergedConfig = deepMerge(mergedConfig, argv);
// 6. Validate
const result = appConfigSchema.safeParse(mergedConfig);
if (!result.success) {
// Pretty print error if possible or just message
throw new Error(`Config validation failed: ${result.error.message}`);
}

216
packages/polymech/src/app/config.d.ts vendored Normal file
View File

@ -0,0 +1,216 @@
export interface AppConfig {
site: Site;
footer_left: FooterLeft[];
footer_right: any[];
settings: Settings;
params: Params;
navigation: Navigation;
navigation_button: NavigationButton;
ecommerce: Ecommerce;
metadata: Metadata;
shopify: Shopify;
pages: Pages;
core: Core;
dev: Dev;
i18n: I18N;
products: Products;
retail: Retail;
rss: Rss;
osrl: Osrl;
features: { [key: string]: boolean };
defaults: Defaults;
cad: Cad;
assets: Assets;
optimization: Optimization;
}
export interface Assets {
cad_url: string;
url: string;
item_url_r: string;
item_url: string;
}
export interface Cad {
cache: boolean;
export_configurations: boolean;
export_sub_components: boolean;
renderer: string;
renderer_view: string;
renderer_quality: number;
extensions: string[];
model_ext: string;
default_configuration: string;
main_match: string;
cam_main_match: string;
}
export interface Core {
logging_namespace: string;
translate_content: boolean;
languages: string[];
languages_prod: string[];
rtl_languages: string[];
osr_root: string;
}
export interface Defaults {
image_url: string;
license: string;
contact: string;
}
export interface Dev {
file_server: string;
}
export interface Ecommerce {
brand: string;
currencySymbol: string;
currencyCode: string;
}
export interface FooterLeft {
href: string;
text: string;
}
export interface I18N {
store: string;
cache: boolean;
asset_path: string;
}
export interface Metadata {
country: string;
city: string;
author: string;
author_bio: string;
author_url: string;
image: string;
description: string;
keywords: string;
}
export interface Navigation {
top: FooterLeft[];
}
export interface NavigationButton {
enable: boolean;
label: string;
link: string;
}
export interface Optimization {
image_settings: ImageSettings;
presets: Presets;
}
export interface ImageSettings {
gallery: Gallery;
lightbox: Gallery;
}
export interface Gallery {
show_title: boolean;
show_description: boolean;
sizes_thumb: string;
sizes_large: string;
sizes_regular: string;
}
export interface Presets {
slow: Fast;
medium: Fast;
fast: Fast;
}
export interface Fast {
sizes_medium: string;
sizes_thumbs: string;
sizes_large: string;
}
export interface Osrl {
env: string;
env_dev: string;
module_name: string;
lang_flavor: string;
product_profile: string;
}
export interface Pages {
home: Home;
}
export interface Home {
hero: string;
_blog: Blog;
}
export interface Blog {
store: string;
}
export interface Params {
contact_form_action: string;
copyright: string;
}
export interface Products {
root: string;
howto_migration: string;
glob: string;
enabled: string;
}
export interface Retail {
library_branch: string;
projects_branch: string;
}
export interface Rss {
title: string;
description: string;
}
export interface Settings {
search: boolean;
account: boolean;
sticky_header: boolean;
theme_switcher: boolean;
default_theme: string;
}
export interface Shopify {
currencySymbol: string;
currencyCode: string;
collections: Collections;
}
export interface Collections {
hero_slider: string;
featured_products: string;
}
export interface Site {
title: string;
base_url: string;
description: string;
base_path: string;
trailing_slash: boolean;
favicon: string;
logo: string;
logo_darkmode: string;
logo_width: string;
logo_height: string;
logo_text: string;
image: Image;
}
export interface Image {
default: string;
error: string;
alt: string;
}

View File

@ -1,8 +1,8 @@
{
"site": {
"title": "Polymech",
"base_url": "https://polymech.info/",
"description": "",
"base_url": "https://creava.org/",
"description" : "",
"base_path": "/",
"trailing_slash": false,
"favicon": "/images/favicon.png",
@ -14,9 +14,27 @@
"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": "/infopages/privacy",
"text": "Privacy"
},
{
"href": "/infopages/contact",
"text": "Contact"
} ,
{
"href": "/newsletter/newsletter_2024_09_en-hugo-release.html",
"text": "Newsletter"
}
],
"footer_right": [
],
"settings": {
"search": true,
"account": true,
@ -36,7 +54,7 @@
"ecommerce": {
"brand": "Polymech",
"currencySymbol": "",
"currencyCode": "EU"
"currencyCode": "EUR"
},
"metadata": {
"country": "Spain",
@ -50,18 +68,21 @@
},
"shopify": {
"currencySymbol": "",
"currencyCode": "EU",
"currencyCode": "EUR",
"collections": {
"hero_slider": "hidden-homepage-carousel",
"featured_products": "featured-products"
}
},
"pages": {
"home": {
"pages":{
"home":{
"hero": "https://assets.osr-plastic.org/machines//assets/newsletter/common/products/extruders/overview-3.jpg",
"_blog": {
"store": "posts"
"blog":{
"store": "resources"
}
}
},
"tracking": {
"googleAnalytics": "G-RW6Q6EG3J0"
}
}

View File

@ -0,0 +1,221 @@
// Generated by ts-to-zod
import { z } from "zod";
export const footerLeftSchema = z.object({
href: z.string(),
text: z.string()
});
export const settingsSchema = z.object({
search: z.boolean(),
account: z.boolean(),
sticky_header: z.boolean(),
theme_switcher: z.boolean(),
default_theme: z.string()
});
export const paramsSchema = z.object({
contact_form_action: z.string(),
copyright: z.string()
});
export const navigationSchema = z.object({
top: z.array(footerLeftSchema)
});
export const navigationButtonSchema = z.object({
enable: z.boolean(),
label: z.string(),
link: z.string()
});
export const ecommerceSchema = z.object({
brand: z.string(),
currencySymbol: z.string(),
currencyCode: z.string()
});
export const metadataSchema = z.object({
country: z.string(),
city: z.string(),
author: z.string(),
author_bio: z.string(),
author_url: z.string(),
image: z.string(),
description: z.string(),
keywords: z.string()
});
export const coreSchema = z.object({
logging_namespace: z.string(),
translate_content: z.boolean(),
languages: z.array(z.string()),
languages_prod: z.array(z.string()),
rtl_languages: z.array(z.string()),
osr_root: z.string()
});
export const devSchema = z.object({
file_server: z.string()
});
export const i18NSchema = z.object({
store: z.string(),
cache: z.boolean(),
asset_path: z.string()
});
export const productsSchema = z.object({
root: z.string(),
howto_migration: z.string(),
glob: z.string(),
enabled: z.string()
});
export const retailSchema = z.object({
library_branch: z.string(),
projects_branch: z.string()
});
export const rssSchema = z.object({
title: z.string(),
description: z.string()
});
export const osrlSchema = z.object({
env: z.string(),
env_dev: z.string(),
module_name: z.string(),
lang_flavor: z.string(),
product_profile: z.string()
});
export const defaultsSchema = z.object({
image_url: z.string(),
license: z.string(),
contact: z.string()
});
export const cadSchema = z.object({
cache: z.boolean(),
export_configurations: z.boolean(),
export_sub_components: z.boolean(),
renderer: z.string(),
renderer_view: z.string(),
renderer_quality: z.number(),
extensions: z.array(z.string()),
model_ext: z.string(),
default_configuration: z.string(),
main_match: z.string(),
cam_main_match: z.string()
});
export const assetsSchema = z.object({
cad_url: z.string(),
url: z.string(),
item_url_r: z.string(),
item_url: z.string()
});
export const gallerySchema = z.object({
show_title: z.boolean(),
show_description: z.boolean(),
sizes_thumb: z.string(),
sizes_large: z.string(),
sizes_regular: z.string()
});
export const fastSchema = z.object({
sizes_medium: z.string(),
sizes_thumbs: z.string(),
sizes_large: z.string()
});
export const blogSchema = z.object({
store: z.string()
});
export const collectionsSchema = z.object({
hero_slider: z.string(),
featured_products: z.string()
});
export const imageSchema = z.object({
default: z.string(),
error: z.string(),
alt: z.string()
});
export const siteSchema = z.object({
title: z.string(),
base_url: z.string(),
description: z.string(),
base_path: z.string(),
trailing_slash: z.boolean(),
favicon: z.string(),
logo: z.string(),
logo_darkmode: z.string(),
logo_width: z.string(),
logo_height: z.string(),
logo_text: z.string(),
image: imageSchema
});
export const shopifySchema = z.object({
currencySymbol: z.string(),
currencyCode: z.string(),
collections: collectionsSchema
});
export const imageSettingsSchema = z.object({
gallery: gallerySchema,
lightbox: gallerySchema
});
export const presetsSchema = z.object({
slow: fastSchema,
medium: fastSchema,
fast: fastSchema
});
export const homeSchema = z.object({
hero: z.string(),
_blog: blogSchema
});
export const pagesSchema = z.object({
home: homeSchema
});
export const optimizationSchema = z.object({
image_settings: imageSettingsSchema,
presets: presetsSchema
});
export const appConfigSchema = z.object({
site: siteSchema,
footer_left: z.array(footerLeftSchema),
footer_right: z.array(z.any()),
settings: settingsSchema,
params: paramsSchema,
navigation: navigationSchema,
navigation_button: navigationButtonSchema,
ecommerce: ecommerceSchema,
metadata: metadataSchema,
shopify: shopifySchema,
pages: pagesSchema,
core: coreSchema,
dev: devSchema,
i18n: i18NSchema,
products: productsSchema,
retail: retailSchema,
rss: rssSchema,
osrl: osrlSchema,
features: z.record(z.string(), z.boolean()),
defaults: defaultsSchema,
cad: cadSchema,
assets: assetsSchema,
optimization: optimizationSchema
});
export type AppConfig = z.infer<typeof appConfigSchema>;

View File

@ -3,29 +3,35 @@ import { IMAGE_PRESET, E_BROADBAND_SPEED } from "./network.js"
import { resolve, template } from '@polymech/commons'
import { sync as read } from '@polymech/fs/read'
import { sanitizeUri } from 'micromark-util-sanitize-uri'
import { loadConfig } from './config-loader.js'
import { I18N_SOURCE_LANGUAGE } from "./constants.js"
export const OSR_ROOT = () => path.resolve(resolve("${OSR_ROOT}"))
export const FILE_SERVER_DEV = 'localhost:5000'
export { I18N_SOURCE_LANGUAGE }
// Load config
const config = loadConfig(I18N_SOURCE_LANGUAGE)
export const OSR_ROOT = () => path.resolve(resolve(config.core.osr_root))
export const FILE_SERVER_DEV = config.dev.file_server
export const LOGGING_NAMESPACE = config.core.logging_namespace
export const TRANSLATE_CONTENT = config.core.translate_content
export const LANGUAGES = config.core.languages
export const LANGUAGES_PROD = config.core.languages_prod
export const isRTL = (lang) => config.core.rtl_languages.includes(lang)
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', 'es', 'ar', 'de', 'ja', 'zh', 'fr', 'nl', 'it', 'pt']
export const LANGUAGES_PROD = ['en', 'es', 'fr']
export const isRTL = (lang) => lang === 'ar'
// i18n constants
export const I18N_STORE = (root, lang) => `${root}/i18n-store/store-${lang}.json`
export const I18N_SOURCE_LANGUAGE = 'en'
export const I18N_CACHE = true
export const I18N_ASSET_PATH = "${SRC_DIR}/${SRC_NAME}-${DST_LANG}${SRC_EXT}"
export const I18N_STORE = (root, lang) => template(config.i18n.store, { root, lang, LANG: lang, OSR_ROOT: root })
export const I18N_CACHE = config.i18n.cache
export const I18N_ASSET_PATH = config.i18n.asset_path
// Products
export const HOWTO_MIGRATION = () => path.resolve(resolve("./data/last.json"))
export const HOWTO_MIGRATION = () => path.resolve(resolve(config.products.howto_migration))
// Products
export const PRODUCT_ROOT = () => path.resolve(resolve("${OSR_ROOT}/products"))
export const PRODUCT_ROOT = () => path.resolve(resolve(config.products.root))
export const PRODUCT_BRANCHES = read(path.join(PRODUCT_ROOT(), 'config/machines.json'), 'json')
export const PRODUCT_GLOB = '**/config.json'
export const PRODUCT_GLOB = config.products.glob
// Product compiler
export const PRODUCT_CONFIG = (product) =>
@ -34,72 +40,52 @@ export const PRODUCT_CONFIG = (product) =>
product
}))
export const PRODUCT_DIR = (product) => path.resolve(resolve(`${PRODUCT_ROOT()}/${product}`))
export const PRODUCT_HUGO_TEMPLATE = './osr/hugo/root.html'
export const PRODUCTS_TARGET_SRC = './src/content/en/retail'
export const PRODUCTS_TARGET = (lang) => `./content/${lang}/products`
// OSRL - Language
export const IS_DEV = true
export const OSRL_ENV = 'astro-release'
export const OSRL_ENV_DEV = 'astro-debug'
export const OSRL_ENV = config.osrl.env
export const OSRL_ENV_DEV = config.osrl.env_dev
export const OSRL_ENVIRONMENT = IS_DEV ? OSRL_ENV_DEV : OSRL_ENV
export const OSRL_MODULE_NAME = 'polymech.io'
export const OSRL_PRODUCT_PROFILE = './src/app/profile.json'
export const OSRL_LANG_FLAVOR = 'osr'
export const OSRL_MODULE_NAME = config.osrl.module_name
export const OSRL_PRODUCT_PROFILE = config.osrl.product_profile
export const OSRL_LANG_FLAVOR = config.osrl.lang_flavor
// Products
export const ENABLED_PRODUCTS = "${OSR_ROOT}/products/config/machines.json"
export const ENABLED_PRODUCTS = resolve(config.products.enabled)
export const PRODUCT_SPECS = (rel) => `${PRODUCT_ROOT()}/${rel}/specs.xlsx`
// Tasks
export const TASK_CONFIG_LOG_DIRECTORY = './config/'
// Task: compile:content
export const TASK_COMPILE_CONTENT = true
export const TASK_COMPILE_CONTENT_CACHE = false
// Task - Logging
export const TASK_LOG_DIRECTORY = './logs/'
// Task - Retail Config
export const REGISTER_PRODUCT_TASKS = true
export const LIBARY_BRANCH = 'site-prod'
export const PROJECTS_BRANCH = 'projects'
export const LIBARY_BRANCH = config.retail.library_branch
export const PROJECTS_BRANCH = config.retail.projects_branch
export const RETAIL_COMPILE_CACHE = false
export const RETAIL_MEDIA_CACHE = true
export const RETAIL_LOG_LEVEL_I18N_PRODUCT_ASSETS = 'info'
export const ConvertProductMedia = true
export const TranslateProductAssets = false
export const PopulateProductDefaults = true
// CAD
export const CAD_MAIN_MATCH = (product) => `${product}/cad*/*Global*.+(SLDASM)`
export const CAD_CAM_MAIN_MATCH = (product) => `${product}/cad*/*-CNC*.+(SLDASM)`
export const CAD_MAIN_MATCH = (product) => template(config.cad.main_match, { product })
export const CAD_CAM_MAIN_MATCH = (product) => template(config.cad.cam_main_match, { product })
export const CAD_CACHE = true
export const CAD_EXPORT_CONFIGURATIONS = true
export const CAD_EXPORT_SUB_COMPONENTS = true
export const CAD_MODEL_FILE_PATH = (SOURCE, CONFIGURATION = '') =>
SOURCE.replace('.json', `${CONFIGURATION ? '-' + CONFIGURATION : ''}.tree.json`)
export const CAD_DEFAULT_CONFIGURATION = 'Default'
export const CAD_RENDERER = 'solidworks'
export const CAD_RENDERER_VIEW = 'Render'
export const CAD_RENDERER_QUALITY = 1
export const CAD_EXTENSIONS = ['.STEP', '.html']
export const CAD_MODEL_EXT = '.tree.json'
SOURCE.replace('.json', `${CONFIGURATION ? '-' + CONFIGURATION : ''}${config.cad.model_ext}`)
export const CAD_URL = (file: string, variables: Record<string, string>) =>
sanitizeUri(template("${OSR_MACHINES_ASSETS_URL}/${file}", { file, ...variables }))
sanitizeUri(template(config.assets.cad_url, { file, ...variables }))
export const ASSET_URL = (file: string, variables: Record<string, string>) =>
sanitizeUri(template("${OSR_MACHINES_ASSETS_URL}/products/${product_rel_min}/${file}", { file, ...variables }))
sanitizeUri(template(config.assets.url, { file, ...variables }))
export const ITEM_ASSET_URL_R = (variables: Record<string, string>) =>
template("${OSR_MACHINES_ASSETS_URL}/${ITEM_REL}/${assetPath}/${filePath}", variables)
template(config.assets.item_url_r, variables)
export const ITEM_ASSET_URL = (variables: Record<string, string>) =>
template("http://${FILE_SERVER_DEV}/${ITEM_REL}/${assetPath}/${filePath}", variables)
template(config.assets.item_url, variables)
//back compat - osr-cad
export const parseBoolean = (value: string): boolean => {
@ -108,40 +94,38 @@ export const parseBoolean = (value: string): boolean => {
/////////////////////////////////////////////
//
// Rendering
export const SHOW_DESCRIPTION = false
export const SHOW_LICENSE = true
export const SHOW_RENDERINGS = true
export const SHOW_TABS = false
export const SHOW_GALLERY = true
export const SHOW_FILES = true
export const SHOW_SPECS = true
export const SHOW_CHECKOUT = true
export const SHOW_CONTACT = true
export const SHOW_3D_PREVIEW = true
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
export const SHOW_DESCRIPTION = config.features.show_description
export const SHOW_LICENSE = config.features.show_license
export const SHOW_RENDERINGS = config.features.show_renderings
export const SHOW_TABS = config.features.show_tabs
export const SHOW_GALLERY = config.features.show_gallery
export const SHOW_FILES = config.features.show_files
export const SHOW_SPECS = config.features.show_specs
export const SHOW_CHECKOUT = config.features.show_checkout
export const SHOW_CONTACT = config.features.show_contact
export const SHOW_3D_PREVIEW = config.features.show_3d_preview
export const SHOW_RESOURCES = config.features.show_resources
export const SHOW_DEBUG = config.features.show_debug
export const SHOW_SAMPLES = config.features.show_samples
export const SHOW_README = config.features.show_readme
export const SHOW_RELATED = config.features.show_related
export const SHOW_SHOWCASE = config.features.show_showcase
export const SHOW_SCREENSHOTS = config.features.show_screenshots
/////////////////////////////////////////////
//
// Plugins
//
// RSS
export const RSS_CONFIG =
{
title: 'Polymech RSS Feed',
description: '',
}
export const RSS_CONFIG = config.rss
/////////////////////////////////////////////
//
// Defaults
export const DEFAULT_IMAGE_URL = 'https://picsum.photos/640/640'
export const DEFAULT_IMAGE_URL = config.defaults.image_url
export const default_image = () => {
return {
@ -151,28 +135,12 @@ export const default_image = () => {
}
}
export const DEFAULT_LICENSE = `CERN Open Source Hardware License`
export const DEFAULT_CONTACT = `sales@plastic-hub.com`
export const DEFAULT_LICENSE = config.defaults.license
export const DEFAULT_CONTACT = config.defaults.contact
/////////////////////////////////////////////
//
// Optimization
export const O_IMAGE = IMAGE_PRESET[E_BROADBAND_SPEED.MEDIUM]
export const IMAGE_SETTINGS =
{
GALLERY: {
SHOW_TITLE: true,
SHOW_DESCRIPTION: false,
SIZES_THUMB: O_IMAGE.sizes_thumbs,
SIZES_LARGE: O_IMAGE.sizes_large,
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_medium
}
}
export const IMAGE_SETTINGS = config.optimization.image_settings

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(),
})
@ -24,26 +24,15 @@ const imagesSchema = z.object({
type Images = z.infer<typeof imagesSchema>;
import { loadConfig } from './config-loader.js'
import { I18N_SOURCE_LANGUAGE } from "./constants.js"
// Load config
const config = loadConfig(I18N_SOURCE_LANGUAGE)
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_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_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_thumbs: "(min-width: 180px) 180px, 180vw",
sizes_large: "(min-width: 1200px) 1200px, 1200vw"
}
[E_BROADBAND_SPEED.SLOW]: config.optimization.presets.slow,
[E_BROADBAND_SPEED.MEDIUM]: config.optimization.presets.medium,
[E_BROADBAND_SPEED.FAST]: config.optimization.presets.fast
}

View File

@ -1,43 +0,0 @@
{
"includes": [],
"variables": {
"PRODUCT_ROOT": "${root}/${product}/",
"abs_url": "https://assets.osr-plastic.org",
"CACHE": "${root}/cache/",
"CACHE_URL": "${abs_url}/cache/",
"GIT_REPO": "https://git.polymech.io/",
"OSR_MACHINES_ASSETS_URL":"https://assets.osr-plastic.org",
"PRODUCTS_ASSETS_URL":"https://assets.osr-plastic.org/${product_rel}",
"OSR_FILES_WEB":"https://files.polymech.io/files/machines",
"PRODUCTS_FILES_URL":"${OSR_FILES_WEB}/${product_rel}",
"DISCORD":"https://discord.gg/s8K7yKwBRc"
},
"env": {
"astro-release":{
"includes": [
"${PRODUCT_ROOT}"
],
"variables": {
"OSR_MACHINES_ASSETS_URL":"https://assets.osr-plastic.org/"
}
},
"astro-debug":{
"includes": [
"${PRODUCT_ROOT}"
],
"variables": {
"OSR_MACHINES_ASSETS_URL":"https://assets.osr-plastic.org",
"showCart": false,
"showPrice": false,
"showResources": false,
"showShipping": false,
"showPaymentTerms": false,
"showHowtos": false,
"showRenderings": true,
"debug": true
}
}
}
}

View File

@ -1,8 +0,0 @@
{
"shop":{
"title": "Shop",
"description": "",
"items":"${OSR_ROOT}/products/products/**/config.json",
"root":"${OSR_ROOT}/products"
}
}

View File

@ -1,44 +0,0 @@
{
"colors": {
"default": {
"theme_color": {
"primary": "#121212",
"body": "#fff",
"border": "#eaeaea",
"theme_light": "#f2f2f2",
"theme_dark": "#000"
},
"text_color": {
"default": "#444",
"dark": "#000",
"light": "#666"
}
},
"darkmode": {
"theme_color": {
"primary": "#fff",
"body": "#252525",
"border": "#3E3E3E",
"theme_light": "#222222",
"theme_dark": "#000"
},
"text_color": {
"default": "#DDD",
"dark": "#fff",
"light": "#DDD"
}
}
},
"fonts": {
"font_family": {
"primary": "Karla:wght@400;500;700",
"primary_type": "sans-serif",
"secondary": "",
"secondary_type": ""
},
"font_size": {
"base": "16",
"scale": "1.2"
}
}
}