refactor pm base | config

This commit is contained in:
babayaga 2025-12-28 09:32:39 +01:00
parent 3da1040d72
commit a7ee97717b
36 changed files with 901 additions and 867 deletions

View File

@ -23,7 +23,7 @@
"format": "unix-time"
}
],
"default": "2025-12-27T18:35:25.856Z"
"default": "2025-12-28T08:31:54.457Z"
},
"description": {
"type": "string",

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,57 @@
{
"llm": {
"cache": true,
"cache_dir": "./.cache/kbot"
},
"howtos": {
"glob": "**/config.json",
"files_web": "https://files.polymech.info/files/machines/howtos/",
"edit_root": "https://git.polymech.io/osr-plastic/osr-machines/src/branch/master/howtos",
"filter_llm": false,
"llm_keywords": false,
"annotations": false,
"annotations_cache": false,
"complete_resources": false,
"add_hardware": false,
"add_resources": false,
"add_references": false,
"complete_skills": false,
"local_resources": false,
"seo_llm": false,
"max_items": 10,
"root_intern": "./public/resources/howtos",
"root": "${OSR_ROOT}/osr-machines/howtos"
},
"directory": {
"glob": "**/config.json",
"files_base": "https://files.polymech.info/files/directory/",
"edit_root": "https://git.polymech.info/polymech/machines/src/branch/master/directory",
"filter_llm": true,
"annotations": false,
"annotations_cache": false,
"complete_resources": true,
"add_hardware": false,
"add_resources": true,
"add_references": true,
"complete_skills": false,
"local_resources": false,
"seo_llm": true,
"max_items": 0,
"migration": "./data/last.json",
"root_intern": "./public/resources/directory",
"root": "${OSR_ROOT}/osr-machines/directory"
},
"components": {
"root": "${OSR_ROOT}/osr-machines",
"glob": "**/config.json",
"enabled": "${OSR_ROOT}/components/config/machines.json"
},
"tasks": {
"config_log_directory": "./config/",
"log_directory": "./logs/",
"compile_content": true,
"compile_content_cache": false
},
"site": {
"title": "Polymech Library",
"base_url": "https://library.polymech.info/",
@ -154,7 +207,13 @@
},
"retail": {
"library_branch": "site-dev",
"projects_branch": "projects"
"projects_branch": "projects",
"compile_cache": false,
"media_cache": true,
"log_level_i18n_product_assets": "info",
"convert_product_media": true,
"translate_product_assets": false,
"populate_product_defaults": true
},
"rss": {
"title": "Polymech RSS Feed",
@ -208,6 +267,8 @@
"cam_main_match": "${product}/cad*/*-CNC*.+(SLDASM)"
},
"assets": {
"local": false,
"glob": "*.+(JPG|jpg|jpeg|png|PNG|gif)",
"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}",

View File

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

6
package-lock.json generated
View File

@ -7265,9 +7265,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001700",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz",
"integrity": "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==",
"version": "1.0.30001761",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001761.tgz",
"integrity": "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==",
"funding": [
{
"type": "opencollective",

View File

@ -129,4 +129,4 @@
"ts-jest": "^29.3.0",
"vitest": "^1.3.1"
}
}
}

264
src/app/config-types.ts Normal file
View File

@ -0,0 +1,264 @@
export interface AppConfig {
llm: Llm;
howtos: Directory;
directory: Directory;
components: Components;
tasks: Tasks;
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: Components;
retail: Retail;
rss: Rss;
osrl: Osrl;
features: { [key: string]: boolean };
defaults: Defaults;
cad: Cad;
assets: Assets;
optimization: Optimization;
}
export interface Assets {
local: boolean;
glob: string;
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 Components {
root: string;
glob: string;
enabled: string;
howto_migration?: 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 Directory {
glob: string;
files_base?: string;
edit_root: string;
filter_llm: boolean;
annotations: boolean;
annotations_cache: boolean;
complete_resources: boolean;
add_hardware: boolean;
add_resources: boolean;
add_references: boolean;
complete_skills: boolean;
local_resources: boolean;
seo_llm: boolean;
max_items: number;
migration?: string;
root_intern: string;
root: string;
files_web?: string;
llm_keywords?: boolean;
}
export interface Ecommerce {
brand: string;
currencySymbol: string;
currencyCode: string;
}
export interface FooterLeft {
href: string;
text: string;
}
export interface I18N {
store: string;
cache: boolean;
source_language: string;
asset_path: string;
}
export interface Llm {
cache: boolean;
cache_dir: 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 Retail {
library_branch: string;
projects_branch: string;
compile_cache: boolean;
media_cache: boolean;
log_level_i18n_product_assets: string;
convert_product_media: boolean;
translate_product_assets: boolean;
populate_product_defaults: boolean;
}
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;
}
export interface Tasks {
config_log_directory: string;
log_directory: string;
compile_content: boolean;
compile_content_cache: boolean;
}

67
src/app/config.d.ts vendored
View File

@ -1,4 +1,9 @@
export interface AppConfig {
llm: Llm;
howtos: Directory;
directory: Directory;
components: Components;
tasks: Tasks;
site: Site;
footer_left: FooterLeft[];
footer_right: any[];
@ -13,7 +18,7 @@ export interface AppConfig {
core: Core;
dev: Dev;
i18n: I18N;
products: Products;
products: Components;
retail: Retail;
rss: Rss;
osrl: Osrl;
@ -25,6 +30,8 @@ export interface AppConfig {
}
export interface Assets {
local: boolean;
glob: string;
cad_url: string;
url: string;
item_url_r: string;
@ -45,6 +52,13 @@ export interface Cad {
cam_main_match: string;
}
export interface Components {
root: string;
glob: string;
enabled: string;
howto_migration?: string;
}
export interface Core {
logging_namespace: string;
translate_content: boolean;
@ -64,6 +78,28 @@ export interface Dev {
file_server: string;
}
export interface Directory {
glob: string;
files_base?: string;
edit_root: string;
filter_llm: boolean;
annotations: boolean;
annotations_cache: boolean;
complete_resources: boolean;
add_hardware: boolean;
add_resources: boolean;
add_references: boolean;
complete_skills: boolean;
local_resources: boolean;
seo_llm: boolean;
max_items: number;
migration?: string;
root_intern: string;
root: string;
files_web?: string;
llm_keywords?: boolean;
}
export interface Ecommerce {
brand: string;
currencySymbol: string;
@ -82,6 +118,11 @@ export interface I18N {
asset_path: string;
}
export interface Llm {
cache: boolean;
cache_dir: string;
}
export interface Metadata {
country: string;
city: string;
@ -159,16 +200,15 @@ export interface Params {
copyright: string;
}
export interface Products {
root: string;
howto_migration: string;
glob: string;
enabled: string;
}
export interface Retail {
library_branch: string;
projects_branch: string;
library_branch: string;
projects_branch: string;
compile_cache: boolean;
media_cache: boolean;
log_level_i18n_product_assets: string;
convert_product_media: boolean;
translate_product_assets: boolean;
populate_product_defaults: boolean;
}
export interface Rss {
@ -215,3 +255,10 @@ export interface Image {
error: string;
alt: string;
}
export interface Tasks {
config_log_directory: string;
log_directory: string;
compile_content: boolean;
compile_content_cache: boolean;
}

View File

@ -1,6 +1,47 @@
// Generated by ts-to-zod
import { z } from "zod";
export const llmSchema = z.object({
cache: z.boolean(),
cache_dir: z.string()
});
export const directorySchema = z.object({
glob: z.string(),
files_base: z.string().optional(),
edit_root: z.string(),
filter_llm: z.boolean(),
annotations: z.boolean(),
annotations_cache: z.boolean(),
complete_resources: z.boolean(),
add_hardware: z.boolean(),
add_resources: z.boolean(),
add_references: z.boolean(),
complete_skills: z.boolean(),
local_resources: z.boolean(),
seo_llm: z.boolean(),
max_items: z.number(),
migration: z.string().optional(),
root_intern: z.string(),
root: z.string(),
files_web: z.string().optional(),
llm_keywords: z.boolean().optional()
});
export const componentsSchema = z.object({
root: z.string(),
glob: z.string(),
enabled: z.string(),
howto_migration: z.string().optional()
});
export const tasksSchema = z.object({
config_log_directory: z.string(),
log_directory: z.string(),
compile_content: z.boolean(),
compile_content_cache: z.boolean()
});
export const footerLeftSchema = z.object({
href: z.string(),
text: z.string()
@ -66,16 +107,15 @@ export const i18NSchema = z.object({
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()
projects_branch: z.string(),
compile_cache: z.boolean(),
media_cache: z.boolean(),
log_level_i18n_product_assets: z.string(),
convert_product_media: z.boolean(),
translate_product_assets: z.boolean(),
populate_product_defaults: z.boolean()
});
export const rssSchema = z.object({
@ -112,6 +152,8 @@ export const cadSchema = z.object({
});
export const assetsSchema = z.object({
local: z.boolean(),
glob: z.string(),
cad_url: z.string(),
url: z.string(),
item_url_r: z.string(),
@ -194,6 +236,11 @@ export const optimizationSchema = z.object({
});
export const appConfigSchema = z.object({
llm: llmSchema,
howtos: directorySchema,
directory: directorySchema,
components: componentsSchema,
tasks: tasksSchema,
site: siteSchema,
footer_left: z.array(footerLeftSchema),
footer_right: z.array(z.any()),
@ -208,7 +255,7 @@ export const appConfigSchema = z.object({
core: coreSchema,
dev: devSchema,
i18n: i18NSchema,
products: productsSchema,
products: componentsSchema,
retail: retailSchema,
rss: rssSchema,
osrl: osrlSchema,

View File

@ -3,13 +3,22 @@ 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 { AppConfig } from './config-types.js'
import { loadConfig } from '@polymech/astro-base/app/config-loader.js'
import { appConfigSchema } from './config.schema.js'
import { loadConfig } from '@polymech/astro-base/app/config-loader'
const config = loadConfig()
const config = loadConfig(
'en',
'./app-config.json',
appConfigSchema
) as AppConfig
export const OSR_ROOT = () => path.resolve(resolve(config.core.osr_root))
export const FILE_SERVER_DEV = config.dev.file_server
export const I18N_SOURCE_LANGUAGE = config.i18n.source_language
// LLM
export const LLM_CACHE = config.llm.cache
export const LLM_CACHE_DIR = config.llm.cache_dir
export const LOGGING_NAMESPACE = config.core.logging_namespace
export const TRANSLATE_CONTENT = config.core.translate_content
@ -22,6 +31,58 @@ export const I18N_STORE = (root, lang) => template(config.i18n.store, { root, la
export const I18N_CACHE = config.i18n.cache
export const I18N_ASSET_PATH = config.i18n.asset_path
// Library - Howtos
export const HOWTO_GLOB = config.howtos.glob
export const FILES_WEB = config.howtos.files_web
export const HOWTO_EDIT_ROOT = config.howtos.edit_root
export const HOWTO_FILTER_LLM = config.howtos.filter_llm
export const HOWTO_LLM_KEYWORDS = config.howtos.llm_keywords
export const HOWTO_ANNOTATIONS = config.howtos.annotations
export const HOWTO_ANNOTATIONS_CACHE = config.howtos.annotations_cache
export const HOWTO_COMPLETE_RESOURCES = config.howtos.complete_resources
export const HOWTO_ADD_HARDWARE = config.howtos.add_hardware
export const HOWTO_ADD_RESOURCES = config.howtos.add_resources
export const HOWTO_ADD_REFERENCES = config.howtos.add_references
export const HOWTO_COMPLETE_SKILLS = config.howtos.complete_skills
export const HOWTO_LOCAL_RESOURCES = config.howtos.local_resources
export const HOWTO_SEO_LLM = config.howtos.seo_llm
export const HOWTO_MAX_ITEMS = config.howtos.max_items
export const HOWTO_ROOT_INTERN = () => path.resolve(resolve(config.howtos.root_intern))
export const HOWTO_ROOT = () => path.resolve(resolve(config.howtos.root))
export const HOWTO_FILES_ABS = (id) => `${HOWTO_ROOT()}/${id}`
export const HOWTO_FILES_WEB = (id: string) => `${FILES_WEB}/${id}`
export const HOWTO_EDIT_URL = (id: string, lang: string) => `${HOWTO_EDIT_ROOT}/${id}`
// Library - Directory
export const DIRECTORY_GLOB = config.directory.glob
export const DIRECTORY_FILES_BASE = config.directory.files_base
export const DIRECTORY_EDIT_ROOT = config.directory.edit_root
export const DIRECTORY_FILTER_LLM = config.directory.filter_llm
export const DIRECTORY_ANNOTATIONS = config.directory.annotations
export const DIRECTORY_ANNOTATIONS_CACHE = config.directory.annotations_cache
export const DIRECTORY_COMPLETE_RESOURCES = config.directory.complete_resources
export const DIRECTORY_ADD_HARDWARE = config.directory.add_hardware
export const DIRECTORY_ADD_RESOURCES = config.directory.add_resources
export const DIRECTORY_ADD_REFERENCES = config.directory.add_references
export const DIRECTORY_COMPLETE_SKILLS = config.directory.complete_skills
export const DIRECTORY_LOCAL_RESOURCES = config.directory.local_resources
export const DIRECTORY_SEO_LLM = config.directory.seo_llm
export const DIRECTORY_MAX_ITEMS = config.directory.max_items
export const DIRECTORY_MIGRATION = () => path.resolve(resolve(config.directory.migration))
export const DIRECTORY_ROOT_INTERN = () => path.resolve(resolve(config.directory.root_intern))
export const DIRECTORY_ROOT = () => path.resolve(resolve(config.directory.root))
export const DIRECTORY_FILES_ABS = (id) => `${DIRECTORY_ROOT()}/${id}`
export const DIRECTORY_FILES_WEB = (id: string) => `${DIRECTORY_FILES_BASE}/${id}`
export const DIRECTORY_EDIT_URL = (id: string, lang: string) => `${DIRECTORY_EDIT_ROOT}/${id}`
// Library Components
export const COMPONENT_ROOT = () => path.resolve(resolve(config.components.root))
export const COMPONENT_GLOB = config.components.glob
export const ENABLED_COMPONENTS = resolve(config.components.enabled)
export const COMPONENT_SPECS = (rel) => `${COMPONENT_ROOT()}/${rel}/specs.xlsx`
// Products
export const HOWTO_MIGRATION = () => path.resolve(resolve(config.products.howto_migration))
@ -37,8 +98,14 @@ 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`
// Product assets
export const ASSETS_LOCAL = config.assets.local
export const ASSETS_GLOB = config.assets.glob
// OSRL - Language
export const IS_DEV = true
export const OSRL_ENV = config.osrl.env
@ -53,24 +120,43 @@ 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/'
export const TASK_CONFIG_LOG_DIRECTORY = config.tasks.config_log_directory
// Task: compile:content
export const TASK_COMPILE_CONTENT = config.tasks.compile_content
export const TASK_COMPILE_CONTENT_CACHE = config.tasks.compile_content_cache
// Task - Logging
export const TASK_LOG_DIRECTORY = config.tasks.log_directory
// Task - Retail Config
export const REGISTER_PRODUCT_TASKS = true
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 RETAIL_COMPILE_CACHE = config.retail.compile_cache
export const RETAIL_MEDIA_CACHE = config.retail.media_cache
export const RETAIL_LOG_LEVEL_I18N_PRODUCT_ASSETS = config.retail.log_level_i18n_product_assets
export const ConvertProductMedia = config.retail.convert_product_media
export const TranslateProductAssets = config.retail.translate_product_assets
export const PopulateProductDefaults = config.retail.populate_product_defaults
// CAD
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 = config.cad.cache
export const CAD_EXPORT_CONFIGURATIONS = config.cad.export_configurations
export const CAD_EXPORT_SUB_COMPONENTS = config.cad.export_sub_components
export const CAD_MODEL_FILE_PATH = (SOURCE, CONFIGURATION = '') =>
SOURCE.replace('.json', `${CONFIGURATION ? '-' + CONFIGURATION : ''}${config.cad.model_ext}`)
export const CAD_DEFAULT_CONFIGURATION = config.cad.default_configuration
export const CAD_RENDERER = config.cad.renderer
export const CAD_RENDERER_VIEW = config.cad.renderer_view
export const CAD_RENDERER_QUALITY = config.cad.renderer_quality
export const CAD_EXTENSIONS = config.cad.extensions
export const CAD_MODEL_EXT = config.cad.model_ext
export const CAD_URL = (file: string, variables: Record<string, string>) =>
sanitizeUri(template(config.assets.cad_url, { file, ...variables }))

View File

@ -1,4 +1,9 @@
import { z } from "zod"
import { loadConfig } from '@polymech/astro-base/app/config-loader.js'
const config = loadConfig()
/////////////////////////////////////////////
//
// Optimizations
@ -24,9 +29,6 @@ const imagesSchema = z.object({
type Images = z.infer<typeof imagesSchema>;
import { loadConfig } from '@polymech/astro-base/app/config-loader.js'
const config = loadConfig()
export const IMAGE_PRESET: Images =
{
[E_BROADBAND_SPEED.SLOW]: config.optimization.presets.slow,

View File

@ -1,72 +0,0 @@
import * as path from 'path'
import { resolve } from '@polymech/commons'
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_SPECS,
RETAIL_LOG_LEVEL_I18N_PRODUCT_ASSETS
} from 'config/config.js'
import { translateXLS } from '@polymech/i18n/translate_xls'
import { I18N_STORE, OSR_ROOT } from 'config/config.js'
import { translateText } from '@polymech/i18n/translate_text'
import { logger } from './index.js'
export type { IOptions } from '@polymech/i18n'
export const translate = async (text: string, srcLanguage = 'en', targetLanguage, opts = {}) => {
if (!targetLanguage) {
return text
}
try {
const store = I18N_STORE(OSR_ROOT(), targetLanguage)
let translation = text
translation = await translateText(text, srcLanguage, targetLanguage, {
store,
...opts
})
return translation
} catch (e) {
logger.error(`Failed to translate text: ${text} from ${srcLanguage} to ${targetLanguage} : ${e.message}`)
}
return text
}
export const translateSheets = async (product, language) => {
const config: any = CONFIG_DEFAULT()
if (language === I18N_SOURCE_LANGUAGE) {
return
}
const i18nOptions: IOptions = {
srcLang: I18N_SOURCE_LANGUAGE,
dstLang: language,
src: PRODUCT_SPECS(product),
store: I18N_STORE(OSR_ROOT(), language),
dst: I18N_ASSET_PATH,
query: "$[*][0,1,2,3]",
cache: I18N_CACHE,
api_key: config.deepl.auth_key,
logLevel: RETAIL_LOG_LEVEL_I18N_PRODUCT_ASSETS
}
const src = `${PRODUCT_SPECS(product)}`
const srcParts = path.parse(src)
const dst = path.resolve(resolve(I18N_ASSET_PATH, false, {
SRC_DIR: srcParts.dir,
SRC_NAME: srcParts.name,
SRC_EXT: srcParts.ext,
DST_LANG: language
}))
if (I18N_CACHE && exists(dst)) {
return dst
}
logger.debug(`Translate assets ${src} to ${language}`)
try {
return await translateXLS(path.resolve(src), dst, i18nOptions)
} catch (e) {
logger.error(`Failed to translate assets ${src} to ${language}`, e.message)
}
}

View File

@ -46,7 +46,7 @@ export async function markdownToHtml(markdown: string): Promise<string> {
.use(remarkRehype, { allowDangerousHtml: true }) // ← preserve raw HTML
.use(rehypeStringify, { allowDangerousHtml: true }) // ← allow it through
.process(markdown);
return result.toString();
}
export const createMarkdownComponent = async (markdown: string) => {

View File

@ -1,12 +1,12 @@
import { IComponentConfig } from '@polymech/commons'
import { sync as read } from '@polymech/fs/read'
import { I18N_SOURCE_LANGUAGE } from 'config/config.js'
import { translate } from '@/base/i18n.js'
import { translate } from '@polymech/astro-base/base/i18n.js'
import { item_defaults } from '@/base/index.js'
import config from "../config/config.json" with { "type": "json" }
const I18N_SOURCE_LANGUAGE = 'en'
const keywords = (keywords: string) => keywords.split(',').map(k => k.trim()).filter(Boolean);

View File

@ -1,42 +0,0 @@
---
const { title, url, pubDate, description, image } = Astro.props;
import { DEFAULT_IMAGE_URL } from "@/config/config";
import { Img } from "imagetools/components";
---
<div
class="group relative flex flex-col h-full justify-between items-start p-4 bg-white text-sm text-neutral-500 rounded-2xl font-mono"
>
<dt>
<h2 class="uppercase group-hover:text-orange-500 text-balance">
<div class="z-0 scale-95 transition group-hover:scale-100"></div>
<a href={url} title={title}>
<span class="absolute -inset-x-4 -inset-y-6 z-20 sm:-inset-x-6"
></span><span class="relative z-10">{title}</span><br /><span
class="text-sm">{pubDate}</span
></a
>
<div class="">
<Img
src={image.src || DEFAULT_IMAGE_URL}
alt={image.alt}
sizes={`
(min-width: 1024px) 33vw,
(min-width: 640px) 50vw,
100vw
`}
attributes={{
img: {
class: "w-full p-2 rounded-2xl relative object-contain lg:mt-2",
},
}}
/>
</div>
</h2>
</dt>
<dd>
<p class="relative z-10 line-clamp-2">
{description}
</p>
</dd>
</div>

View File

@ -1,29 +0,0 @@
---
const { title, url, description, pubDate, author, image } = Astro.props;
import { translate } from "@/base/i18n.js";
import { I18N_SOURCE_LANGUAGE } from "config/config.js";
const locale = Astro.currentLocale;
const title_i18n = await translate(title, I18N_SOURCE_LANGUAGE, locale);
const description_i18n = await translate(description, I18N_SOURCE_LANGUAGE, locale);
---
<article
class="flex flex-col group items-start justify-between p-4 bg-white rounded-xl overflow-hidden relative">
<h3
class="text-sm text-neutral-600 uppercase font-mono tracking-tight group-hover:text-orange-600">
<a
href={url}
title={title}
aria-label={title}>
<span class="absolute inset-0"></span>
{title_i18n}
</a>
</h3>
<p class="text-neutral-500 font-mono text-xs uppercase mt-2">
{author} ·
<time datetime={pubDate}>{pubDate}</time>
</p>
<p class="text-sm mt-12 text-balance text-neutral-500 line-clamp-2">
{description_i18n}
</p>
</article>

View File

@ -1,6 +1,6 @@
---
import Translate from "@/components/polymech/i18n.astro"
import { Img } from "imagetools/components"
import Translate from "@polymech/astro-base/components/i18n.astro";
import { Img } from "imagetools/components";
interface Props {
title: string;
@ -12,18 +12,20 @@ interface Props {
}
const {
title = "Directory" ,
title = "Directory",
description = "",
buttonText = "Directory",
buttonLink = "/howtos",
imageSrc = "/images/home/world.png",
imageAlt = ""
imageAlt = "",
} = Astro.props;
---
<section>
<div class="py-2">
<div class="flex flex-col md:flex-row gap-6 p-4 bg-white rounded-xl overflow-hidden relative">
<div
class="flex flex-col md:flex-row gap-6 p-4 bg-white rounded-xl overflow-hidden relative"
>
<!-- Image takes 50% -->
<div class="w-full md:w-1/2">
<Img
@ -31,8 +33,9 @@ const {
alt={imageAlt}
attributes={{
img: {
class: "w-full max-w-100 rounded-lg hover:ring-2 hover:ring-blue-500 aspect-square object-contain"
}
class:
"w-full max-w-100 rounded-lg hover:ring-2 hover:ring-blue-500 aspect-square object-contain",
},
}}
/>
</div>
@ -52,30 +55,47 @@ const {
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 justify-between"
>
<span class="relative uppercase text-xs text-white"><Translate>{buttonText}</Translate></span>
<span class="relative uppercase text-xs text-white"
><Translate>{buttonText}</Translate></span
>
<div
aria-hidden="true"
class="w-12 text-white transition duration-300 -translate-y-7 group-hover:translate-y-7"
>
<div class="h-14 flex">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" class="size-6 m-auto fill-white">
<path stroke-linecap="round" stroke-linejoin="round"
d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3" />
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-6 m-auto fill-white"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3"></path>
</svg>
</div>
<div class="h-14 flex">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" class="size-6 m-auto fill-white">
<path stroke-linecap="round" stroke-linejoin="round"
d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3" />
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-6 m-auto fill-white"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3"></path>
</svg>
</div>
</div>
</a>
</div>
</div>
</div>
</div>
</section>

View File

@ -1,6 +1,6 @@
---
import Translate from "@/components/polymech/i18n.astro"
import { Img } from "imagetools/components"
import Translate from "@polymech/astro-base/components/i18n.astro";
import { Img } from "imagetools/components";
interface Props {
title: string;
description: string;
@ -16,13 +16,15 @@ const {
buttonText = "Knowledge Base",
buttonLink = "/howtos",
imageSrc = "/images/home/icon-knowledgebase.png",
imageAlt = ""
imageAlt = "",
} = Astro.props;
---
<section>
<div class="py-2">
<div class="flex flex-col md:flex-row gap-6 p-4 bg-white rounded-xl overflow-hidden relative">
<div
class="flex flex-col md:flex-row gap-6 p-4 bg-white rounded-xl overflow-hidden relative"
>
<!-- Image takes 50% -->
<div class="w-full md:w-1/2">
<Img
@ -30,8 +32,9 @@ const {
alt={imageAlt}
attributes={{
img: {
class: "w-full max-w-100 rounded-lg hover:ring-2 hover:ring-blue-500 aspect-square object-contain"
}
class:
"w-full max-w-100 rounded-lg hover:ring-2 hover:ring-blue-500 aspect-square object-contain",
},
}}
/>
</div>
@ -51,30 +54,47 @@ const {
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 justify-between"
>
<span class="relative uppercase text-xs text-white"><Translate>{buttonText}</Translate></span>
<span class="relative uppercase text-xs text-white"
><Translate>{buttonText}</Translate></span
>
<div
aria-hidden="true"
class="w-12 text-white transition duration-300 -translate-y-7 group-hover:translate-y-7"
>
<div class="h-14 flex">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" class="size-6 m-auto fill-white">
<path stroke-linecap="round" stroke-linejoin="round"
d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3" />
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-6 m-auto fill-white"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3"></path>
</svg>
</div>
<div class="h-14 flex">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" class="size-6 m-auto fill-white">
<path stroke-linecap="round" stroke-linejoin="round"
d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3" />
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-6 m-auto fill-white"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3"></path>
</svg>
</div>
</div>
</a>
</div>
</div>
</div>
</div>
</section>

View File

@ -1,6 +1,6 @@
---
import Translate from "@/components/polymech/i18n.astro"
import { Img } from "imagetools/components"
import Translate from "@polymech/astro-base/components/i18n.astro";
import { Img } from "imagetools/components";
interface Props {
title: string;
description: string;
@ -16,13 +16,15 @@ const {
buttonText = "Shop",
buttonLink = "/en/shop",
imageSrc = "/images/home/shop.png",
imageAlt = ""
imageAlt = "",
} = Astro.props;
---
<section>
<div class="py-2">
<div class="flex flex-col md:flex-row gap-6 p-4 bg-white rounded-xl overflow-hidden relative">
<div
class="flex flex-col md:flex-row gap-6 p-4 bg-white rounded-xl overflow-hidden relative"
>
<!-- Image takes 50% -->
<div class="w-full md:w-1/2">
<Img
@ -30,8 +32,9 @@ const {
alt={imageAlt}
attributes={{
img: {
class: "w-full max-w-100 rounded-lg hover:ring-2 hover:ring-blue-500 aspect-square object-contain"
}
class:
"w-full max-w-100 rounded-lg hover:ring-2 hover:ring-blue-500 aspect-square object-contain",
},
}}
/>
</div>
@ -51,30 +54,47 @@ const {
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 justify-between"
>
<span class="relative uppercase text-xs text-white"><Translate>{buttonText}</Translate></span>
<span class="relative uppercase text-xs text-white"
><Translate>{buttonText}</Translate></span
>
<div
aria-hidden="true"
class="w-12 text-white transition duration-300 -translate-y-7 group-hover:translate-y-7"
>
<div class="h-14 flex">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" class="size-6 m-auto fill-white">
<path stroke-linecap="round" stroke-linejoin="round"
d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3" />
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-6 m-auto fill-white"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3"></path>
</svg>
</div>
<div class="h-14 flex">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" class="size-6 m-auto fill-white">
<path stroke-linecap="round" stroke-linejoin="round"
d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3" />
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-6 m-auto fill-white"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3"></path>
</svg>
</div>
</div>
</a>
</div>
</div>
</div>
</div>
</section>

View File

@ -23,15 +23,10 @@ import { applyFilters, shortenUrl } from "@/base/filters.js";
import {
HOWTO_FILES_WEB,
HOWTO_FILES_ABS,
I18N_SOURCE_LANGUAGE,
HOWTO_COMPLETE_RESOURCES,
HOWTO_COMPLETE_SKILLS,
HOWTO_ADD_HARDWARE,
HOWTO_LOCAL_RESOURCES,
HOWTO_ADD_RESOURCES,
HOWTO_ADD_REFERENCES,
HOWTO_EDIT_URL,
} from "config/config.js";
I18N_SOURCE_LANGUAGE,
} from "../../app/config.js";
import { filter } from "@/base/kbot.js";
import { getCollection } from "astro:content";

View File

@ -12,7 +12,7 @@ import pMap from "p-map";
import { sync as exists } from "@polymech/fs/exists";
import { sync as read } from "@polymech/fs/read";
import { createHTMLComponent, createMarkdownComponent } from "@/base/index.js";
import { translate } from "@/base/i18n.js";
import { translate } from "@polymech/astro-base/base/i18n.js";
import { applyFilters, shortenUrl } from "@/base/filters.js";
// import { extract, extract_learned_skills, references } from "@/base/kbot.js";
import {
@ -27,6 +27,7 @@ import {
HOWTO_ADD_REFERENCES,
HOWTO_EDIT_URL,
} from "config/config.js";
import { filter } from "@/base/kbot.js";
interface Props {
@ -106,13 +107,12 @@ const EditLink = () => {
)
}
*/
---
<BaseLayout class="markdown-content bg-gray-100" frontmatter={howto}>
<Wrapper>
<article class="bg-white shadow-lg rounded-lg overflow-hidden">
<header class="p-4 ">
<header class="p-4">
<h1 class="text-4xl font-bold text-gray-800 mb-4">
<Translate>{howto.title}</Translate>
</h1>
@ -146,7 +146,9 @@ const EditLink = () => {
<strong><Translate>Creator:</Translate></strong>{howto._createdBy}
</li>
<li>
<strong><Translate>Country:</Translate></strong>{authorGeo.countryName }
<strong><Translate>Country:</Translate></strong>{
authorGeo.countryName
}
</li>
<li>
<strong><Translate>Email:</Translate></strong>
@ -216,7 +218,7 @@ const EditLink = () => {
</ul>
</section>
<section class="border-gray-300 p-0 lg:p-6 mt-8">
<section class="border-gray-300 p-0 lg:p-6 mt-8">
<ol class="space-y-10">
{
stepsWithFilteredMarkdown.map((step, idx) => (
@ -247,8 +249,16 @@ const EditLink = () => {
</ol>
</section>
<section class="bg-white shadow-lg rounded-lg border-gray-300 p-4 lg:p-6 mt-8 markdown-content"><Resources /></section>
<section class="bg-white shadow-lg rounded-lg border-gray-300 p-4 lg:p-6 mt-8 markdown-content"><References /></section>
<section
class="bg-white shadow-lg rounded-lg border-gray-300 p-4 lg:p-6 mt-8 markdown-content"
>
<Resources />
</section>
<section
class="bg-white shadow-lg rounded-lg border-gray-300 p-4 lg:p-6 mt-8 markdown-content"
>
<References />
</section>
<footer class="p-8 text-sm border-t bg-white text-gray-600">
<div class="flex justify-between">
<span

View File

@ -2,8 +2,8 @@
import { Img } from "imagetools/components";
import { translate } from "@polymech/astro-base/base/i18n.js";
import { decode } from "html-entities";
import { I18N_SOURCE_LANGUAGE } from "config/config.js";
import Translate from "@/components/polymech/i18n.astro";
const I18N_SOURCE_LANGUAGE = 'en'
import Translate from "@polymech/astro-base/components/i18n.astro";
import type { IHowto } from "@/model/howto/howto.js";
export interface Props {

View File

@ -1,6 +1,6 @@
---
import { IMAGE_SETTINGS } from "config/config.js";
import Translate from "@/components/polymech/i18n.astro";
import Translate from "@polymech/astro-base/components/i18n.astro";
import { Img } from "imagetools/components";
import { asset_local_rel, IHowto } from "@/model/howto/howto.js";
const { title, url, model, selected = false } = Astro.props;
@ -10,13 +10,17 @@ const coverLocaleRel = await asset_local_rel(item, item.cover_image);
const { images, gallerySettings = {}, lightboxSettings = {} } = Astro.props;
const mergedGallerySettings = {
SIZES_REGULAR: gallerySettings.SIZES_REGULAR || IMAGE_SETTINGS.GALLERY.SIZES_REGULAR,
SIZES_THUMB: gallerySettings.SIZES_THUMB || IMAGE_SETTINGS.GALLERY.SIZES_THUMB,
SIZES_REGULAR:
gallerySettings.SIZES_REGULAR || IMAGE_SETTINGS.GALLERY.SIZES_REGULAR,
SIZES_THUMB:
gallerySettings.SIZES_THUMB || IMAGE_SETTINGS.GALLERY.SIZES_THUMB,
SHOW_TITLE: gallerySettings.SHOW_TITLE ?? IMAGE_SETTINGS.GALLERY.SHOW_TITLE,
SHOW_DESCRIPTION: gallerySettings.SHOW_DESCRIPTION ?? IMAGE_SETTINGS.GALLERY.SHOW_DESCRIPTION,
}
SHOW_DESCRIPTION:
gallerySettings.SHOW_DESCRIPTION ?? IMAGE_SETTINGS.GALLERY.SHOW_DESCRIPTION,
};
---
<div class={classes}>
<div class={classes}>
<div
class="p-4 overflow-hidden group-hover:opacity-75 duration-300 transition-all"
>
@ -30,13 +34,11 @@ const mergedGallerySettings = {
placeholder="blurred"
sizes={mergedGallerySettings.SIZES_REGULAR}
breakpoints={{ count: 2, minWidth: 180, maxWidth: 450 }}
attributes={
{
img: {
class: "w-32 h-32 rounded hover:ring-2 aspect-square thumbnail-img"
}
}
}
attributes={{
img: {
class: "w-32 h-32 rounded hover:ring-2 aspect-square thumbnail-img",
},
}}
/>
</a>
</div>

View File

@ -1,22 +0,0 @@
---
import { I18N_SOURCE_LANGUAGE } from "config/config.js"
import { translate, IOptions } from '@/base/i18n.js'
export interface Props extends IOptions {
language?: string,
clazz?:string
}
const {
language = Astro.currentLocale,
clazz = '',
...rest
} = Astro.props
const content = await Astro.slots.render('default')
const translatedText = await translate(content, I18N_SOURCE_LANGUAGE, language, rest)
---
<p data-widget="polymech.i18n" class={clazz}>
{translatedText}
</p>

View File

@ -1,40 +0,0 @@
---
import * as path from "path"
import { sync as fileExists } from "@polymech/fs/exists"
import { specs } from "@/base/specs.js"
import { render, logger } from "@/base/index.js"
import { translateSheets } from "@/base/i18n.js"
import { I18N_SOURCE_LANGUAGE, LANGUAGES_PROD as LANGUAGES, PRODUCT_SPECS } from "config/config.js"
import DefaultComponent from "../Default.astro"
const { frontmatter: data } = Astro.props
const locale = Astro.currentLocale || I18N_SOURCE_LANGUAGE
const specs_table = async (relPath) =>
{
let specsPath = path.join(PRODUCT_SPECS(relPath));
if (!fileExists(specsPath)) {
logger.debug(`No specs found for ${specsPath}`);
return null;
}
if (locale !== I18N_SOURCE_LANGUAGE && LANGUAGES.includes(locale)) {
const i18n = await translateSheets(relPath, locale);
if (!i18n) {
logger.debug(`No i18n found for ${relPath} : ${locale}`);
} else {
specsPath = i18n
}
}
return specs(specsPath)
}
const tableHTML = await specs_table(data.rel)
let SpecsComponent
if (tableHTML) {
SpecsComponent = await render(tableHTML)
} else {
SpecsComponent = DefaultComponent
}
---
<div class="bg-white rounded-xl specs specs-table">
<SpecsComponent />
</div>

View File

@ -1,34 +0,0 @@
---
import { translate } from "@/base/i18n.js";
import { I18N_SOURCE_LANGUAGE } from "config/config.js";
import { slugify } from "@/base/strings.js"
export interface Props {
title: string;
}
const { title = "title" } = Astro.props;
const slug = `${slugify(title)}-tab-button`;
const id = `#${slug}-tab`;
const view = `#${slugify(title)}-view`;
const locale = Astro.currentLocale;
const title_i18n = await translate(title, I18N_SOURCE_LANGUAGE, locale);
---
<li>
<a
data-tabs-target={view}
href={id}
class="inline-block p-4 border-b-2
border-transparent rounded-t-lg
text-xs
hover:text-gray-600
hover:border-gray-300
dark:hover:text-gray-300"
id={slug}
type="button"
role="tab"
aria-controls="settings"
aria-selected="false">{title_i18n}</a
>
</li>

View File

@ -2,7 +2,7 @@
"site": {
"title": "Creava",
"base_url": "https://creava.org/",
"description" : "",
"description": "",
"base_path": "/",
"trailing_slash": false,
"favicon": "/images/favicon.png",
@ -26,15 +26,13 @@
{
"href": "/infopages/contact",
"text": "Contact"
} ,
},
{
"href": "/newsletter/newsletter_2024_09_en-hugo-release.html",
"text": "Newsletter"
}
],
"footer_right": [
],
"footer_right": [],
"settings": {
"search": true,
"account": true,
@ -74,10 +72,10 @@
"featured_products": "featured-products"
}
},
"pages":{
"home":{
"pages": {
"home": {
"hero": "https://assets.osr-plastic.org/machines//assets/newsletter/common/products/extruders/overview-3.jpg",
"blog":{
"blog": {
"store": "resources"
}
}
@ -85,4 +83,4 @@
"_tracking": {
"googleAnalytics": "G-RW6Q6EG3J0"
}
}
}

View File

@ -1,223 +1 @@
import * as path from 'path'
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'
// LLM
export const LLM_CACHE = true
export const LLM_CACHE_DIR = './.cache/kbot'
export const OSR_ROOT = () => path.resolve(resolve("${OSR_ROOT}"))
export const LOGGING_NAMESPACE = 'polymech-site'
export const TRANSLATE_CONTENT = true
export const LANGUAGES = ['en', 'es']
export const LANGUAGES_SITE = ['en', 'ar', 'de', 'ja', 'es', 'zh', 'fr']
export const LANGUAGES_PROD = ['en', 'es', 'fr', 'it', 'de']
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}"
// Library - Howtos
export const HOWTO_GLOB = '**/config.json'
export const FILES_WEB = 'https://files.polymech.info/files/machines/howtos/'
export const HOWTO_EDIT_ROOT = 'https://git.polymech.io/osr-plastic/osr-machines/src/branch/master/howtos'
export const HOWTO_FILTER_LLM = false
export const HOWTO_LLM_KEYWORDS = false
export const HOWTO_ANNOTATIONS = false
export const HOWTO_ANNOTATIONS_CACHE = false
export const HOWTO_COMPLETE_RESOURCES = false
export const HOWTO_ADD_HARDWARE = false
export const HOWTO_ADD_RESOURCES = false
export const HOWTO_ADD_REFERENCES = false
export const HOWTO_COMPLETE_SKILLS = false
export const HOWTO_LOCAL_RESOURCES = false
export const HOWTO_SEO_LLM = false
export const HOWTO_MAX_ITEMS = 1000
export const HOWTO_MIGRATION = () => path.resolve(resolve("./data/last.json"))
export const HOWTO_ROOT_INTERN = () => path.resolve(resolve("./public/resources/howtos"))
export const HOWTO_ROOT = () => path.resolve(resolve("${OSR_ROOT}/osr-machines/howtos"))
export const HOWTO_FILES_ABS = (id) => `${HOWTO_ROOT()}/${id}`
export const HOWTO_FILES_WEB = (id: string) => `${FILES_WEB}/${id}`
export const HOWTO_EDIT_URL = (id: string, lang: string) => `${HOWTO_EDIT_ROOT}/${id}`
// Library - Directory
export const DIRECTORY_GLOB = '**/config.json'
export const DIRECTORY_FILES_BASE = 'https://files.polymech.info/files/directory/'
export const DIRECTORY_EDIT_ROOT = 'https://git.polymech.info/polymech/machines/src/branch/master/directory'
export const DIRECTORY_FILTER_LLM = true
export const DIRECTORY_ANNOTATIONS = false
export const DIRECTORY_ANNOTATIONS_CACHE = false
export const DIRECTORY_COMPLETE_RESOURCES = true
export const DIRECTORY_ADD_HARDWARE = false
export const DIRECTORY_ADD_RESOURCES = true
export const DIRECTORY_ADD_REFERENCES = true
export const DIRECTORY_COMPLETE_SKILLS = false
export const DIRECTORY_LOCAL_RESOURCES = false
export const DIRECTORY_SEO_LLM = true
export const DIRECTORY_MAX_ITEMS = 0
export const DIRECTORY_MIGRATION = () => path.resolve(resolve("./data/last.json"))
export const DIRECTORY_ROOT_INTERN = () => path.resolve(resolve("./public/resources/directory"))
export const DIRECTORY_ROOT = () => path.resolve(resolve("${OSR_ROOT}/osr-machines/directory"))
export const DIRECTORY_FILES_ABS = (id) => `${DIRECTORY_ROOT()}/${id}`
export const DIRECTORY_FILES_WEB = (id: string) => `${DIRECTORY_FILES_BASE}/${id}`
export const DIRECTORY_EDIT_URL = (id: string, lang: string) => `${DIRECTORY_EDIT_ROOT}/${id}`
// Library Products
export const PRODUCT_ROOT = () => path.resolve(resolve("${OSR_ROOT}/products"))
export const PRODUCT_BRANCHES = read('./library.json', 'json')
export const PRODUCT_GLOB = '**/config.json'
export const ENABLED_PRODUCTS = "${OSR_ROOT}/products/config/machines.json"
export const PRODUCT_SPECS = (rel) => `${PRODUCT_ROOT()}/${rel}/specs.xlsx`
// Library Components
export const COMPONENT_ROOT = () => path.resolve(resolve("${OSR_ROOT}/osr-machines"))
export const COMPONENT_GLOB = '**/config.json'
export const ENABLED_COMPONENTS = "${OSR_ROOT}/components/config/machines.json"
export const COMPONENT_SPECS = (rel) => `${COMPONENT_ROOT()}/${rel}/specs.xlsx`
// Product compiler
export const PRODUCT_CONFIG = (product) =>
path.resolve(resolve(`${PRODUCT_ROOT()}/${product}/config.json`, false,
{
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`
// Product assets
export const ASSETS_LOCAL = false
export const ASSETS_GLOB = '*.+(JPG|jpg|jpeg|png|PNG|gif)'
// OSRL - Language
export const IS_DEV = true
export const OSRL_ENV = 'astro-release'
export const OSRL_ENV_DEV = 'astro-debug'
export const OSRL_ENVIRONMENT = IS_DEV ? OSRL_ENV_DEV : OSRL_ENV
export const OSRL_MODULE_NAME = 'polymech.io'
export const OSRL_PRODUCT_PROFILE = './src/config/profile.json'
export const OSRL_LANG_FLAVOR = 'osr'
// 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 = 'projects2'
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_CACHE = true
export const CAD_EXPORT_CONFIGURATIONS = false
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'
export const CAD_URL = (file: string, variables: Record<string, string>) =>
sanitizeUri(template("${OSR_MACHINES_ASSETS_URL}/${file}", { 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 }))
export const ITEM_ASSET_URL = (variables: Record<string, string>) =>
template("${OSR_MACHINES_ASSETS_URL}/${ITEM_REL}/${assetPath}/${filePath}", variables)
//back compat - osr-cad
export const parseBoolean = (value: string): boolean => { return value === '1' || value.toLowerCase() === 'true'; }
/////////////////////////////////////////////
//
// Rendering - Store
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
/////////////////////////////////////////////
//
// Plugins
// RSS
export const RSS_CONFIG =
{
title: 'Polymech RSS Feed',
description: '',
}
/////////////////////////////////////////////
//
// Defaults
export const DEFAULT_IMAGE_URL = 'https://picsum.photos/640/640'
export const default_image = () => {
return {
alt: 'none',
src: DEFAULT_IMAGE_URL
}
}
export const DEFAULT_LICENSE = `CERN Open Source Hardware License`
export const DEFAULT_CONTACT = `sales@plastic-hub.com`
/////////////////////////////////////////////
//
// 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 * from '../app/config.js'

View File

@ -1,21 +0,0 @@
---
import BaseLayout from "./BaseLayout.astro";
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 text-neutral-500 markdown-content">
<div class="py-2 p-4 bg-white rounded-xl">
<Markup/>
</div>
</div>
</section>
</Wrapper>
</BaseLayout>

View File

@ -17,8 +17,11 @@ import Specs from "@polymech/astro-base/components/specs.astro";
import TabButton from "@polymech/astro-base/components/tab-button.astro";
import TabContent from "@polymech/astro-base/components/tab-content.astro";
import { AppConfig } from "@/app/config.schema.js";
import { loadConfig } from "@polymech/astro-base/app/config-loader.js";
const config = loadConfig();
import {
I18N_SOURCE_LANGUAGE,
SHOW_3D_PREVIEW,
SHOW_FILES,
SHOW_GALLERY,
@ -53,9 +56,10 @@ const content = substitute(false, data.content || "", {
...data,
LANG: Astro.currentLocale,
});
const translated_content = await translate(
content,
I18N_SOURCE_LANGUAGE,
config.i18n.source_language,
Astro.currentLocale,
);

View File

@ -7,10 +7,10 @@ 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 { group_by_path } from "@/model/component/component.js";
import { group_by_path } from "@polymech/astro-base/model/component.js";
const view = "library";
const items = await getCollection("store");
const items = await getCollection("library");
const locale = Astro.currentLocale;
const store = `/${locale}/${view}/`;

View File

@ -2,11 +2,11 @@
import { group_by_cat } from "@/model/howto/howto.js";
import { getCollection } from "astro:content";
import BaseLayout from "@/layouts/BaseLayout.astro";
import Sidebar from "@polymech/astro-base/components/sidebar/Sidebar.astro"
import MobileToggle from "@polymech/astro-base/components/sidebar/MobileToggle.astro"
import { getSidebarConfig } from '@polymech/astro-base/config/sidebar';
import Sidebar from "@polymech/astro-base/components/sidebar/Sidebar.astro";
import MobileToggle from "@polymech/astro-base/components/sidebar/MobileToggle.astro";
import { getSidebarConfig } from "@polymech/astro-base/config/sidebar";
import HowtoCard from "@/components/howtos/HowtoCard.astro";
import Translate from "@/components/polymech/i18n.astro";
import Translate from "@polymech/astro-base/components/i18n.astro";
import { LANGUAGES_PROD as LANGUAGES } from "config/config.js";
import type { IHowto } from "@/model/howto/howto.js";
@ -23,32 +23,42 @@ const categories = Object.keys(groups).sort();
const organizedCategories: any[] = [];
// Separate and organize categories
const uncategorizedItems = groups['Uncategorized'] || groups['uncategorized'] || [];
const categorizedItems = categories.filter(cat =>
cat.toLowerCase() !== 'uncategorized' && cat !== 'Uncategorized'
).sort();
const uncategorizedItems =
groups["Uncategorized"] || groups["uncategorized"] || [];
const categorizedItems = categories
.filter(
(cat) => cat.toLowerCase() !== "uncategorized" && cat !== "Uncategorized",
)
.sort();
// Add categorized items with better organization
if (categorizedItems.length > 0) {
organizedCategories.push({
label: 'Browse by Category',
label: "Browse by Category",
collapsed: false,
items: categorizedItems.map(category => ({
items: categorizedItems.map((category) => ({
label: `${category} (${groups[category].length})`,
collapsed: true,
isSubGroup: true, // This makes it a collapsible subgroup
items: groups[category].slice(0, 8).map((howto: IHowto) => ({
label: howto.title,
href: `/${locale}/howtos/${howto.slug}`,
isCurrent: false
})).concat(
groups[category].length > 8 ? [{
label: `View all ${groups[category].length} guides...`,
href: `/${locale}/howtos/category/${category.toLowerCase().replace(/\s+/g, '-')}`,
isCurrent: false
}] : []
)
}))
items: groups[category]
.slice(0, 8)
.map((howto: IHowto) => ({
label: howto.title,
href: `/${locale}/howtos/${howto.slug}`,
isCurrent: false,
}))
.concat(
groups[category].length > 8
? [
{
label: `View all ${groups[category].length} guides...`,
href: `/${locale}/howtos/category/${category.toLowerCase().replace(/\s+/g, "-")}`,
isCurrent: false,
},
]
: [],
),
})),
});
}
@ -57,65 +67,73 @@ if (uncategorizedItems.length > 0) {
organizedCategories.push({
label: `Uncategorized (${uncategorizedItems.length})`,
collapsed: true,
items: uncategorizedItems.slice(0, 10).map((uncatHowto: IHowto) => ({
label: uncatHowto.title,
href: `/${locale}/howtos/${uncatHowto.slug}`,
isCurrent: false
})).concat(
uncategorizedItems.length > 10 ? [{
label: `View all ${uncategorizedItems.length} guides...`,
href: `/${locale}/howtos/uncategorized`,
isCurrent: false
}] : []
)
items: uncategorizedItems
.slice(0, 10)
.map((uncatHowto: IHowto) => ({
label: uncatHowto.title,
href: `/${locale}/howtos/${uncatHowto.slug}`,
isCurrent: false,
}))
.concat(
uncategorizedItems.length > 10
? [
{
label: `View all ${uncategorizedItems.length} guides...`,
href: `/${locale}/howtos/uncategorized`,
isCurrent: false,
},
]
: [],
),
});
}
// Add quick navigation
organizedCategories.unshift({
label: 'Quick Navigation',
label: "Quick Navigation",
collapsed: false,
items: [
{
label: 'All Guides',
label: "All Guides",
href: `/${locale}/howtos`,
isCurrent: true // This is the current page
isCurrent: true, // This is the current page
},
{
label: 'Recently Added',
label: "Recently Added",
href: `/${locale}/howtos/recent`,
isCurrent: false
}
]
isCurrent: false,
},
],
});
const pageNavigation = organizedCategories;
export async function getStaticPaths() {
return LANGUAGES.map((lang) => ({
params: { locale: lang },
}));
return LANGUAGES.map((lang) => ({
params: { locale: lang },
}));
}
---
<BaseLayout frontmatter={{
title: "How-To Guides",
description: "Browse our collection of step-by-step how-to guides"
}}>
<BaseLayout
frontmatter={{
title: "How-To Guides",
description: "Browse our collection of step-by-step how-to guides",
}}
>
<div class="layout-with-sidebar">
<!-- Mobile Toggle -->
<MobileToggle />
<!-- Sidebar -->
<div class="sidebar-wrapper">
<Sidebar
config={sidebarConfig}
currentUrl={Astro.url}
<Sidebar
config={sidebarConfig}
currentUrl={Astro.url}
pageNavigation={pageNavigation}
/>
</div>
<!-- Main Content -->
<main class="main-content-with-sidebar">
<div class="container mx-auto px-4 py-4 md:px-6 md:py-6">
@ -126,100 +144,143 @@ export async function getStaticPaths() {
<Translate>How-To Guides</Translate>
</h1>
<p class="text-lg text-gray-600 mb-6">
<Translate>{all.length} guide{all.length !== 1 ? 's' : ''} available across {categories.length} categories</Translate>
<Translate
>{all.length} guide{all.length !== 1 ? "s" : ""} available across
{categories.length} categories</Translate
>
</p>
</div>
<!-- All Howtos Grid -->
<div class="mb-12">
<h2 class="text-2xl font-semibold text-gray-900 mb-6"><Translate>All Guides</Translate></h2>
<div class="grid sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 gap-6">
{items.map((item) => {
const correspondingItem = all.find(storeItem => storeItem.data.item.slug === item.slug);
return correspondingItem ? (
<HowtoCard
url={`/${locale}/howtos/${item.slug}`}
title={item.title}
description={item.description}
alt={item.title}
category={item.category?.label}
difficulty_level={item.difficulty_level}
time={item.time}
total_views={item.total_views}
total_downloads={item.total_downloads}
author={item._createdBy}
tags={item.tags?.map(tag => typeof tag === 'string' ? tag : tag.label) || []}
locale={locale}
howto={item}
/>
) : null;
})}
<h2 class="text-2xl font-semibold text-gray-900 mb-6">
<Translate>All Guides</Translate>
</h2>
<div
class="grid sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 gap-6"
>
{
items.map((item) => {
const correspondingItem = all.find(
(storeItem) => storeItem.data.item.slug === item.slug,
);
return correspondingItem ? (
<HowtoCard
url={`/${locale}/howtos/${item.slug}`}
title={item.title}
description={item.description}
alt={item.title}
category={item.category?.label}
difficulty_level={item.difficulty_level}
time={item.time}
total_views={item.total_views}
total_downloads={item.total_downloads}
author={item._createdBy}
tags={
item.tags?.map((tag) =>
typeof tag === "string" ? tag : tag.label,
) || []
}
locale={locale}
howto={item}
/>
) : null;
})
}
</div>
</div>
<!-- Categories Section -->
<div class="space-y-12" id="categories">
{categories.map((category) => (
<section class="category-section">
<div class="flex items-center justify-between mb-6">
<h2
id={category.toLowerCase().replace(/\s+/g, '-')}
class="text-2xl font-semibold text-gray-900 cursor-pointer hover:text-orange-600 transition-colors"
onclick={`window.location.hash = '${category.toLowerCase().replace(/\s+/g, '-')}'`}
>
<Translate>{category}</Translate>
</h2>
</div>
<div class="grid sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{groups[category].slice(0, 8).map((howto: IHowto) => {
const correspondingItem = all.find(storeItem => storeItem.data.item.slug === howto.slug);
return correspondingItem ? (
<HowtoCard
url={`/${locale}/howtos/${howto.slug}`}
title={howto.title}
description={howto.description}
alt={howto.title}
category={howto.category?.label}
difficulty_level={howto.difficulty_level}
time={howto.time}
total_views={howto.total_views}
total_downloads={howto.total_downloads}
author={howto._createdBy}
tags={howto.tags?.map(tag => typeof tag === 'string' ? tag : tag.label) || []}
locale={locale}
howto={howto}
/>
) : null;
})}
</div>
{groups[category].length > 8 && (
<div class="mt-6 text-center">
<a
href={`/${locale}/howtos/category/${category.toLowerCase().replace(/\s+/g, '-')}`}
class="inline-flex items-center px-4 py-2 bg-orange-500 hover:bg-orange-600 text-white rounded-lg transition-colors"
{
categories.map((category) => (
<section class="category-section">
<div class="flex items-center justify-between mb-6">
<h2
id={category.toLowerCase().replace(/\s+/g, "-")}
class="text-2xl font-semibold text-gray-900 cursor-pointer hover:text-orange-600 transition-colors"
onclick={`window.location.hash = '${category.toLowerCase().replace(/\s+/g, "-")}'`}
>
<Translate>View all</Translate> {groups[category].length} <Translate>guides in</Translate> {category}
<svg class="w-4 h-4 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
</svg>
</a>
<Translate>{category}</Translate>
</h2>
</div>
)}
</section>
))}
<div class="grid sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{groups[category].slice(0, 8).map((howto: IHowto) => {
const correspondingItem = all.find(
(storeItem) => storeItem.data.item.slug === howto.slug,
);
return correspondingItem ? (
<HowtoCard
url={`/${locale}/howtos/${howto.slug}`}
title={howto.title}
description={howto.description}
alt={howto.title}
category={howto.category?.label}
difficulty_level={howto.difficulty_level}
time={howto.time}
total_views={howto.total_views}
total_downloads={howto.total_downloads}
author={howto._createdBy}
tags={
howto.tags?.map((tag) =>
typeof tag === "string" ? tag : tag.label,
) || []
}
locale={locale}
howto={howto}
/>
) : null;
})}
</div>
{groups[category].length > 8 && (
<div class="mt-6 text-center">
<a
href={`/${locale}/howtos/category/${category.toLowerCase().replace(/\s+/g, "-")}`}
class="inline-flex items-center px-4 py-2 bg-orange-500 hover:bg-orange-600 text-white rounded-lg transition-colors"
>
<Translate>View all</Translate>{" "}
{groups[category].length}{" "}
<Translate>guides in</Translate> {category}
<svg
class="w-4 h-4 ml-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5l7 7-7 7"
/>
</svg>
</a>
</div>
)}
</section>
))
}
</div>
<!-- Back to top -->
<div class="mt-12 text-center">
<a
href="#top"
<a
href="#top"
class="inline-flex items-center px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors"
>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 10l7-7m0 0l7 7m-7-7v18"/>
<svg
class="w-4 h-4 mr-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 10l7-7m0 0l7 7m-7-7v18"></path>
</svg>
<Translate>Back to top</Translate>
</a>

View File

@ -1,34 +0,0 @@
---
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

@ -1,21 +0,0 @@
---
import { getCollection } from "astro:content";
import HelpcenterLayout from "../../layouts/HelpcenterLayout.astro";
export async function getStaticPaths() {
const helpcenter = await getCollection("helpcenter");
const paths = helpcenter.map((page) => {
return {
params: { slug: page.slug },
props: { page },
trailingSlash: false,
};
});
return paths;
}
const { page } = Astro.props;
const { Content } = await page.render();
---
<HelpcenterLayout frontmatter={page.data}>
<Content />
</HelpcenterLayout>

View File

@ -1,40 +0,0 @@
---
import BaseLayout from "@/layouts/BaseLayout.astro";
import HelpEntries from "@/components/helpcenter/HelpEntries.astro";
import { getCollection } from "astro:content";
const allEntries = await getCollection("helpcenter");
import Wrapper from "@/components/containers/Wrapper.astro";
---
<BaseLayout>
<Wrapper>
<section>
<div
class="flex flex-col gap-12 h-full justify-between p-4 text-center py-20">
<div class="max-w-xl mx-auto">
<h1
class="text-lg text-neutral-600 font-mono tracking-tight text-balance">
Welcome to our help center, How can we help you?
</h1>
<p class="text-sm text-balance text-neutral-500">
Search around for answers to your questions and get the help you need.
</p>
</div>
</div>
</section>
<section>
<div class="flex flex-col gap-2">
{
allEntries.slice().map((post) => (
<HelpEntries
url={"/helpcenter/" + post.slug}
title={post.data.title}
/>
))
}
</div>
</section>
</Wrapper>
</BaseLayout>

View File

@ -1,21 +0,0 @@
---
import { getCollection } from 'astro:content';
import InfoPagesLayout from '@/layouts/InfoPagesLayout.astro';
export async function getStaticPaths() {
const infoPages = await getCollection('infopages');
const paths = infoPages.map(page => {
return {
params: { slug: page.slug },
props: { page },
trailingSlash: false,
};
});
return paths;
}
const { page } = Astro.props;
const { Content } = await page.render();
---
<InfoPagesLayout frontmatter={page.data}>
<Content />
</InfoPagesLayout>