site template cleanup 1/3

This commit is contained in:
lovebird 2025-03-18 18:53:54 +01:00
parent 85f5f65a21
commit 9e72aca04c
20 changed files with 84 additions and 848 deletions

View File

@ -1,7 +1,7 @@
export default new Map([
["src/content/infopages/community.mdx", () => import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Finfopages%2Fcommunity.mdx&astroContentModuleFlag=true")],
["src/content/infopages/contact.mdx", () => import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Finfopages%2Fcontact.mdx&astroContentModuleFlag=true")],
["src/content/infopages/community.mdx", () => import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Finfopages%2Fcommunity.mdx&astroContentModuleFlag=true")],
["src/content/infopages/resources.mdx", () => import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Finfopages%2Fresources.mdx&astroContentModuleFlag=true")],
["src/content/infopages/software.mdx", () => import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Finfopages%2Fsoftware.mdx&astroContentModuleFlag=true")]]);

View File

@ -3,15 +3,15 @@ import pMap from 'p-map'
import { GlobOptions } from 'glob'
import { sanitizeUri } from 'micromark-util-sanitize-uri'
import ExifReader from 'exifreader'
import { loadImage } from "imagetools/api"
import { sanitizeFilename } from '@polymech/fs/utils'
import { resolve } from '@polymech/commons'
import { files } from '@polymech/commons'
import { sync as exists } from '@polymech/fs/exists'
import { sync as read } from '@polymech/fs/read'
import { sync as mv } from '@polymech/fs/move'
import { logger } from '@/base/index.js'
import { env } from './index.js'
import { GalleryImage, MetaJSON } from './images.js'
import {
removeArrayValues,
@ -25,12 +25,8 @@ import {
DEFAULT_IMAGE_URL, ASSETS_LOCAL,
ASSETS_GLOB
} from 'config/config.js'
import { GalleryImage, MetaJSON } from './images.js'
import { validateFilename, sanitizeFilename } from "@polymech/fs/utils"
import { env } from './index.js'
export const default_sanitizer = (files:string[]) => files.map((f) => sanitizeFilename(f))
export const default_sort = (files: string[]): string[] => {
const getSortableParts = (filename: string) => {
@ -51,25 +47,6 @@ export const default_sort = (files: string[]): string[] => {
})
}
export const default_sanitize = (paths: string[]): string[] => {
return paths.map((filePath) => {
const dir = path.dirname(filePath);
const originalFilename = path.basename(filePath);
const sanitizedFilename = sanitizeFilename(originalFilename)
if (originalFilename === sanitizedFilename) {
return filePath;
}
const newPath = path.join(dir, sanitizedFilename);
try {
mv(filePath, newPath);
return newPath;
} catch (error) {
return filePath; // Return the original path in case of failure
}
});
}
export const default_filter = async (url: string) => {
try {
const response = await fetch(url, { method: 'HEAD' })
@ -103,25 +80,23 @@ export const image_url = async (src, fallback = DEFAULT_IMAGE_URL) => {
return safeSrc
}
export const gallery = async (
assetPath,
product): Promise<GalleryImage[] | undefined> => {
product = '' + product
export const gallery = async ( assetPath, item): Promise<GalleryImage[]> => {
const root = resolve(PRODUCT_ROOT())
const profile = env()
const assetSlug = path.parse(assetPath).name
const productConfig: any = read(PRODUCT_CONFIG(product), "json")
if (!productConfig) {
logger.warn(`item gallery : item ${product} config not found !`)
const itemConfig: any = read(PRODUCT_CONFIG(item), "json")
if (!itemConfig) {
logger.warn(`item gallery : item ${item} config not found !`)
return []
}
const mediaPath = `${root}/${product}/${assetPath}/`
const mediaPath = `${root}/${item}/${assetPath}/`
if (!exists(mediaPath)) {
logger.warn(`item gallery : item ${product} media path not found ${mediaPath}!`)
logger.warn(`item gallery : item ${item} media path not found ${mediaPath}!`)
return []
}
const galleryGlob = (productConfig.gallery || {})[assetSlug]?.glob || ASSETS_GLOB
const galleryGlob = (itemConfig.gallery || {})[assetSlug]?.glob || ASSETS_GLOB
let galleryFiles: any[] = files(mediaPath, galleryGlob, {
cwd: mediaPath,
absolute: false,
@ -133,7 +108,7 @@ export const gallery = async (
}
if (!galleryFiles) {
logger.warn(`ProductGallery : Product ${product} media files not found ! ${mediaPath}`)
logger.warn(`gallery : ${item} media files not found ! ${mediaPath}`)
return []
}
const assetUrl = (filePath) => {
@ -141,20 +116,19 @@ export const gallery = async (
{
assetPath,
filePath,
ITEM_REL: product,
ITEM_REL: item,
...profile.variables
}
))
}
if (!ASSETS_LOCAL) {
galleryFiles = await pMap(galleryFiles, async (f) => (await default_filter(assetUrl(f))) ? f : null, { concurrency: 5 })
galleryFiles = galleryFiles.filter((f) => f !== null)
galleryFiles = default_sort(galleryFiles)
} else {
galleryFiles = galleryFiles.filter(default_filter_locale)
galleryFiles = default_sort(galleryFiles)
}
}
galleryFiles = default_sort(galleryFiles)
return await pMap(galleryFiles, async (file: string) => {
const parts = path.parse(file)

View File

@ -1,13 +1,13 @@
---
import "../styles/flowbite.css"
import "../styles/global.css"
import "../styles/custom.scss"
import { LANGUAGES_PROD } from "config/config.js"
import config from "@/config/config.json"
import { plainify } from "../base/strings.js"
import { plainify } from "@/base/strings.js"
import "../styles/global.css"
import "../styles/custom.scss"
import { AstroSeo } from "@astrolib/seo"
import { item_keywords } from '@/base/seo.js'
@ -55,7 +55,7 @@ const description = item?.description as string || config.metadata.description
const keywords = await item_keywords(item, Astro.currentLocale)
const tracking = config?.tracking?.googleAnalytics
const tracking = config?.tracking?.googleAnalytics || 'G-RW6Q6EG3J0'
---
<AstroSeo
@ -146,7 +146,7 @@ const tracking = config?.tracking?.googleAnalytics
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date())
gtag('config', {tracking})
gtag('config', "G-RW6Q6EG3J0")
</script>
)}

View File

@ -1,131 +0,0 @@
---
interface Props extends Omit<astroHTML.JSX.IframeHTMLAttributes, 'src' | 'srcdoc'> {
/**
* Pass `true` to embed using `www.youtube.com` instead of `www.youtube-nocookie.com`
*/
cookie?: boolean
/**
* YouTube IFrame Player API parameters
*
* Defaults to `{autoplay: 1}`, additional parameters will be merged into the defaults
* @see https://developers.google.com/youtube/player_parameters#Parameters
*/
embedParams?: EmbedParams
/**
* `loading` attribute for the thumbnail `<img>`
*
* Defaults to `"lazy"`
*/
loading?: 'eager' | 'lazy'
/**
* Pass `true` to omit the "Watch on YouTube" link - saves ~3.86 KB
*/
noLink?: boolean
/**
* Thumbnail image to use in the static embed
*
* Defaults to `"default"`, pass 1, 2 or 3 to use a screenshot from the video instead.
*/
thumbnail?: Thumbnail
/**
* Thumbnail resolution
*
* Defaults to `"sd"` (640x480)
*/
thumbnailRes?: ThumbnailRes
/**
* Title for the static embed
*/
title: string
/**
* 11-digit YouTube video id
*/
videoId: string
}
interface EmbedParams {
autoplay?: ToggleParam
cc_lang_pref?: string
cc_load_policy?: ToggleParam
color?: 'red' | 'white'
controls?: ToggleParam
disablekb?: ToggleParam
enablejsapi?: ToggleParam
end?: number
fs?: ToggleParam
hl?: string
iv_load_policy?: 1 | '1' | 3 | '3'
list?: string
listType?: 'playlist' | 'user_uploads'
loop?: ToggleParam
/** @deprecated has no effect, deprecated by YouTube on August 15 2023 */
modestbranding?: ToggleParam
origin?: string
playlist?: string
playslinline?: ToggleParam
rel?: ToggleParam
start?: number
widget_referrer?: string
}
type ToggleParam = 0 | '0' | 1 | '1'
type Thumbnail = 'default' | 1 | '1' | 2 | '2' | 3 | '3' | string
type ThumbnailRes = 120 | '120' | 'default' | 320 | '320' | 'medium' | 'mq' | 480 | '480' | 'high' | 'hq' | 640 | '640' | 'standard' | 'sd' | 1280 | '1280' | 'maxres'
const QUALITY_PREFIXES = {
120: '',
default: '',
320: 'mq',
medium: 'mq',
mq: 'mq',
480: 'hq',
high: 'hq',
hq: 'hq',
640: 'sd',
standard: 'sd',
sd: 'sd',
1280: 'maxres',
maxres: 'maxres',
}
let {
cookie = false,
embedParams,
loading = 'lazy',
noLink = false,
thumbnail = 'default',
thumbnailRes = 'sd',
title,
videoId,
...iframeAttributes
} = Astro.props as Props
let params: EmbedParams = {autoplay: 1, ...embedParams}
let embedQuery = Object.keys(params).map(key => `${key}=${params[key]}`).join('&')
let embedUrl = `https://www.youtube${cookie ? '' : '-nocookie'}.com/embed${videoId ? `/${videoId}` : ''}${embedQuery ? `?${embedQuery}` : ''}`
let thumbnailUrl = !/^(default|1|2|3)$/.test(String(thumbnail)) ? thumbnail : `https://i.ytimg.com/vi/${videoId}/${QUALITY_PREFIXES[thumbnailRes]}${thumbnail}.jpg`
let linkSvg = `<svg viewBox="0 0 110 26"><use href="#a" style="stroke-width:2px;stroke:#000;stroke-opacity:.15;stroke-linejoin:round"/><path id="a" fill="#fff" d="M16.68.99c-3.13.04-9.66.17-11.69.69-1.49.4-2.59 1.6-2.99 3-.69 2.7-.68 8.31-.68 8.31S1.31 18.6 2 21.3c.39 1.5 1.59 2.6 2.99 3 2.69.7 13.4.68 13.4.68s10.7.01 13.4-.68c1.5-.4 2.59-1.6 2.99-3 .69-2.7.68-8.31.68-8.31s.11-5.61-.68-8.31c-.4-1.5-1.59-2.6-2.99-3C29.11.98 18.4.99 18.4.99s-.67-.01-1.71 0zm72.21.9v21.28h2.78l.31-1.37h.09c.3.5.71.88 1.21 1.18.5.3 1.08.4 1.68.4 1.1 0 1.99-.49 2.49-1.59.5-1.1.81-2.7.81-4.9v-2.4c0-1.6-.11-2.9-.31-3.9-.2-.89-.5-1.59-1-2.09-.5-.4-1.1-.59-1.9-.59-.59 0-1.18.19-1.68.49-.49.3-1.01.8-1.21 1.4V1.9h-3.28zm-49.99.78 3.9 13.9.18 6.71h3.31v-6.71l3.87-13.9h-3.37l-1.4 6.31c-.4 1.89-.71 3.19-.81 3.99h-.09c-.2-1.1-.51-2.4-.81-3.99l-1.37-6.31h-3.4zm29.59 0v2.71h3.4v17.9h3.28V5.38h3.4s0-2.71-.09-2.71h-9.99zM15 7.79l8.9 5.18-8.9 5.09V7.78zm89.4.09c-1.7 0-2.89.59-3.59 1.59-.69.99-.99 2.6-.99 4.9v2.59c0 2.2.3 3.9.99 4.9.7 1.1 1.8 1.59 3.5 1.59 1.4 0 2.38-.3 3.18-1 .7-.7 1.09-1.69 1.09-3.09v-.5l-2.9-.21c0 1-.08 1.6-.28 2-.1.4-.5.62-1 .62-.3 0-.61-.11-.81-.31-.2-.3-.3-.59-.4-1.09-.1-.5-.09-1.21-.09-2.21v-.78l5.71-.09v-2.62c0-1.6-.1-2.78-.4-3.68-.2-.89-.71-1.59-1.31-1.99-.7-.4-1.48-.59-2.68-.59zm-50.49.09c-1.09 0-2.01.18-2.71.68-.7.4-1.2 1.12-1.49 2.12-.3 1-.5 2.27-.5 3.87v2.21c0 1.5.1 2.78.4 3.78.2.9.7 1.62 1.4 2.12.69.5 1.71.68 2.81.78 1.19 0 2.08-.28 2.78-.68.69-.4 1.09-1.09 1.49-2.09.39-1 .49-2.3.49-3.9v-2.21c0-1.6-.2-2.87-.49-3.87-.3-.89-.8-1.62-1.49-2.12-.7-.5-1.58-.68-2.68-.68zm12.18.09v11.9c-.1.3-.29.48-.59.68-.2.2-.51.31-.81.31-.3 0-.58-.1-.68-.4-.1-.3-.18-.7-.18-1.4V8.16h-3.4v11.21c0 1.4.18 2.39.68 3.09.49.7 1.21 1 2.21 1 1.4 0 2.48-.69 3.18-2.09h.09l.31 1.78h2.59V8.16s-3.4 0-3.4-.09zm17.31 0v11.9c-.1.3-.29.48-.59.68-.2.2-.51.31-.81.31-.3 0-.58-.1-.68-.4-.1-.3-.21-.7-.21-1.4V8.16h-3.4v11.21c0 1.4.21 2.39.71 3.09.5.7 1.18 1 2.18 1 1.39 0 2.51-.69 3.21-2.09h.09l.28 1.78h2.62V8.16s-3.4 0-3.4-.09zm20.9 2.09c.4 0 .58.11.78.31.2.3.3.59.4 1.09.1.5.09 1.21.09 2.21v1.09h-2.5v-1.09c0-1 0-1.71.09-2.21 0-.4.11-.8.31-1 .2-.3.51-.4.81-.4zm-50.49.12c.5 0 .8.18 1 .68.19.5.28 1.3.28 2.4v4.68c0 1.1-.08 1.9-.28 2.4s-.5.68-1 .68-.79-.18-.99-.68c-.2-.5-.31-1.3-.31-2.4v-4.68c0-1.1.11-1.9.31-2.4s.49-.68.99-.68zm39.68.09c.3 0 .61.1.81.4.2.3.27.67.37 1.37.1.6.12 1.51.12 2.71l.09 1.9c0 1.1 0 1.99-.09 2.59-.1.6-.19 1.08-.49 1.28-.2.3-.5.4-.9.4-.3 0-.51-.08-.81-.18-.2-.1-.39-.29-.59-.59v-8.5c.1-.4.29-.7.59-1 .3-.3.6-.4.9-.4z"/></svg>`
let playButtonSvg = `<svg viewBox="0 0 68 48"><path d="M66.52,7.74c-0.78-2.93-2.49-5.41-5.42-6.19C55.79,.13,34,0,34,0S12.21,.13,6.9,1.55 C3.97,2.33,2.27,4.81,1.48,7.74C0.06,13.05,0,24,0,24s0.06,10.95,1.48,16.26c0.78,2.93,2.49,5.41,5.42,6.19 C12.21,47.87,34,48,34,48s21.79-0.13,27.1-1.55c2.93-0.78,4.64-3.26,5.42-6.19C67.94,34.95,68,24,68,24S67.94,13.05,66.52,7.74z" fill="#f00"></path><path d="M 45,24 27,14 27,34" fill="#fff"></path></svg>`
let gradientStyle = `.gradient{width:100%;height:49px;padding-bottom:50px;position:absolute;top:0;background-repeat:repeat-x;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==);pointer-events:none}`
let linkStyle = noLink ? '' : '.woyt{z-index:2;background:rgba(23,23,23,.8);border-bottom-right-radius:2px;border-top-right-radius:2px;bottom:5px;height:47px;position:absolute}.woyt-text{color:#fff;float:left;font:500 16px/16px "YouTube Noto",Roboto,Arial,Helvetica,sans-serif;margin-left:12px;margin-top:16px}.woyt-logo{float:right;height:16px;margin-left:9px;margin-right:12px;margin-top:16px;width:72px}'
let style = `<style>*{padding:0;margin:0;overflow:hidden}html,body{height:100%;background:#000}img{position:absolute;width:100%;top:0;bottom:0;margin:auto}.button{position:absolute;left:50%;top:50%;width:68px;height:48px;margin-left:-34px;margin-top:-24px}.top{position:absolute;top:18px;left:18px;right:18px;display:flex;flex-wrap:nowrap}.title{color:#fff;font-size:18px;white-space:nowrap;word-wrap:normal;text-shadow:0 0 2px rgba(0,0,0,.5);font-family:"YouTube Noto",Roboto,Arial,Helvetica,sans-serif;line-height:1.3;text-overflow:ellipsis;overflow:hidden}${gradientStyle}${linkStyle}</style>`
let link = noLink ? '' : `<a href="https://www.youtube.com/watch?v=${videoId}${params.start ? `&t=${params.start}s` : ''}" target="_blank" aria-label="Watch on YouTube" class="woyt"><div aria-hidden="true"><div class="woyt-text">Watch on</div><div class="woyt-logo">${linkSvg}</div></div></a>`
let srcdoc = `${style}${link}<a href="${embedUrl}"><img src="${thumbnailUrl}" alt="${title}" loading="${loading}"><div class="gradient"></div><div class="top"><div class="title">${title}</div></div><div class="button">${playButtonSvg}</div></a>`
---
<iframe
loading={loading}
src={embedUrl}
srcdoc={srcdoc}
title={title}
{...iframeAttributes}
allow="accelerometer;autoplay;encrypted-media;gyroscope;picture-in-picture"
allowfullscreen
frameborder="0"
style="width:100%;aspect-ratio:16/9"
/>

View File

@ -1,140 +0,0 @@
<section>
<div class="grid sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 gap-2">
<div class="flex flex-col h-full">
<h1
class="text-lg text-neutral-600 font-mono tracking-tight text-balance">
Contact us
</h1>
<p class="text-sm text-balance text-neutral-500">
Reach out to us for any inquiries or assistance with your product or
service, or to get in touch with us for any other inquiries.
</p>
</div>
<form class="col-span-2 bg-white p-4 rounded-xl">
<div class="grid gap-2 grid-cols-1 sm:grid-cols-2">
<div>
<label
for="first-name"
class="sr-only"
>First name</label
>
<input
type="text"
id="first-name"
name="first-name"
autocomplete="given-name"
placeholder="Your name"
aria-label="First name"
class="flex-auto rounded-xl font-mono border-0 h-14 text-xs uppercase duration-300 px-3.5 py-2 text-neutral-500 ring-1 ring-inset ring-white placeholder:text-neutral-400 focus:ring-2 focus:ring-inset focus:ring-orange-600 bg-neutral-100 w-full"
/>
</div>
<div>
<label
for="last-name"
class="sr-only"
>Last name</label
>
<input
type="text"
id="last-name"
name="last-name"
autocomplete="family-name"
placeholder="Your last name"
aria-label="Last name"
class="flex-auto rounded-xl font-mono border-0 h-14 text-xs uppercase duration-300 px-3.5 py-2 text-neutral-500 ring-1 ring-inset ring-white placeholder:text-neutral-400 focus:ring-2 focus:ring-inset focus:ring-orange-600 bg-neutral-100 w-full"
/>
</div>
<div class="sm:col-span-2">
<label
for="company"
class="sr-only"
>Company</label
>
<input
type="text"
id="company"
name="company"
autocomplete="organization"
placeholder="Company name"
aria-label="Company"
class="flex-auto rounded-xl font-mono border-0 h-14 text-xs uppercase duration-300 px-3.5 py-2 text-neutral-500 ring-1 ring-inset ring-white placeholder:text-neutral-400 focus:ring-2 focus:ring-inset focus:ring-orange-600 bg-neutral-100 w-full"
/>
</div>
<div class="sm:col-span-2">
<label
for="email"
class="sr-only"
>Email</label
>
<input
type="email"
id="email"
name="email"
autocomplete="email"
placeholder="Your best email!"
aria-label="Email"
class="flex-auto rounded-xl font-mono border-0 h-14 text-xs uppercase duration-300 px-3.5 py-2 text-neutral-500 ring-1 ring-inset ring-white placeholder:text-neutral-400 focus:ring-2 focus:ring-inset focus:ring-orange-600 bg-neutral-100 w-full"
/>
</div>
<div class="sm:col-span-2">
<label
for="message"
class="sr-only"
>Message</label
>
<textarea
rows="12"
id="message"
name="message"
placeholder="Your message goes here..."
aria-label="Message"
class="flex-auto rounded-xl font-mono border-0 text-xs uppercase duration-300 px-3.5 py-2 text-neutral-500 ring-1 ring-inset ring-white placeholder:text-neutral-400 focus:ring-2 focus:ring-inset focus:ring-orange-600 bg-neutral-100 w-full"
></textarea>
</div>
</div>
<div class="mt-2 text-right">
<button
type="submit"
title="link to your page"
aria-label="your label"
class="relative group overflow-hidden pl-4 font-mono h-14 flex space-x-6 items-center bg-orange-500 hover:bg-black duration-300 rounded-xl w-full justify-between">
<span class="relative uppercase text-xs text-white">Submit</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"
></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"
></path>
</svg>
</div>
</div>
</button>
</div>
</form>
</div>
</section>

View File

@ -39,5 +39,4 @@ let data = itemData || {
}
}
---
<script type="application/ld+json" set:html={JSON.stringify(data, null, 2)} />

View File

@ -1,11 +0,0 @@
<section>
<div class="flex flex-col h-full ">
<h1
class="text-lg text-neutral-600 font-mono tracking-tight uppercase text-balance">
Williamsburg, one - your next ecommerce theme for all your products.
</h1>
<p class="text-sm text-pretty text-neutral-500 mt-2">
Your next minimalist ecommerce theme for all your digital products.
</p>
</div>
</section>

View File

@ -1,13 +0,0 @@
<section>
<div class="flex flex-col 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 uppercase">
Williamsburg, your next ecommerce theme for all your products.
</h1>
<p class="text-sm text-balance text-neutral-500">
Your next minimalist ecommerce theme for all your digital products.
</p>
</div>
</div>
</section>

View File

@ -1,13 +0,0 @@
<section>
<div class="flex flex-col 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 uppercase">
Williamsburg, your next ecommerce theme for all your products.
</h1>
<p class="text-sm text-balance text-neutral-500">
Your next minimalist ecommerce theme for all your digital products.
</p>
</div>
</div>
</section>

View File

@ -1,65 +0,0 @@
import { translate } from "@/base/i18n.js"
import config from "@/config/config.json"
}
/**
* Process and combine keywords from multiple sources, translate them, and trigger an optional callback
*
* @param options - Configuration for processing keywords
* @param options.itemConfig - The frontmatter or item configuration object
* @param options.sourceLanguage - The source language for translation
* @param options.targetLocale - The target locale for translation
* @param options.additionalKeywords - Extra keywords to add to the processed list
* @param options.onKeywordsProcessed - Optional callback that receives the processed keywords array
* @returns Object containing the processed keywords as both an array and comma-separated string
*/
export async function processKeywords({
itemConfig = {},
sourceLanguage,
targetLocale,
additionalKeywords = [],
onKeywordsProcessed
}: {
itemConfig?: Record<string, any>;
sourceLanguage: string;
targetLocale: string;
additionalKeywords?: string[];
onKeywordsProcessed?: (keywords: string[]) => void;
}) {
let systemKeywords = "";
if (itemConfig.PRODUCT_ROOT) {
const defaultsJson = await item_defaults(itemConfig.PRODUCT_ROOT);
const defaults: Record<string, string> = await import('@polymech/fs/read').then(m => m.sync(defaultsJson, 'json')) || {};
// Extract keywords from different sources
const defaultsKeywords = (defaults.keywords || "").split(',').map(k => k.trim()).filter(Boolean);
const configKeywords = (config.metadata?.keywords || "").split(',').map(k => k.trim()).filter(Boolean);
const itemKeywords = (itemConfig.keywords || "").split(',').map(k => k.trim()).filter(Boolean);
// Combine and deduplicate all keywords
const allKeywords = Array.from(new Set([
...defaultsKeywords,
...configKeywords,
...itemKeywords,
...additionalKeywords
])).join(',');
// Translate the keywords
systemKeywords = await translate(allKeywords, sourceLanguage, targetLocale);
}
// Final processing and deduplication
const keywordsArray = [...new Set([itemConfig.name, ...systemKeywords.split(','), ...additionalKeywords])].filter(Boolean);
const keywords = keywordsArray.join(',');
// Call the callback with the processed keywords if it exists
if (typeof onKeywordsProcessed === 'function') {
onKeywordsProcessed(keywordsArray);
}
return {
keywordsArray,
keywords
};
}

View File

@ -1,21 +0,0 @@
import { processKeywords } from '@/base/index.js'
const item_config = frontmatter as any || {}
// Process keywords using the extracted function
import { item_defaults, processKeywords } from '@/base/index.js'
itemConfig: item_config,
sourceLanguage: I18N_SOURCE_LANGUAGE,
targetLocale: locale,
additionalKeywords,
onKeywordsProcessed
})
// Process keywords using the extracted function
const item_config = frontmatter as any || {}
const { keywords, keywordsArray } = await processKeywords({
itemConfig: item_config,
sourceLanguage: I18N_SOURCE_LANGUAGE,
targetLocale: locale,
additionalKeywords,
onKeywordsProcessed
})

View File

@ -1,260 +0,0 @@
<section>
<div class="grid sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 gap-2">
<div class="flex flex-col">
<h1
class="text-lg text-neutral-600 font-mono tracking-tight text-balance">
Style guide
</h1>
<p class="text-sm text-balance text-neutral-500 mt-2">
Follow the style guide to create a consistent and professional look for
your website
</p>
</div>
<div class="lg:col-span-2">
<p class="text-lg text-neutral-600 font-mono tracking-tight text-balance">
Typography / Inter · IBM PLex Mono
</p>
<div class="mt-2">
<div>
<div class="space-y-2 py-3">
<div class="grid grid-cols-1 items-start lg:grid-cols-2">
<div><span class="text-black">Headers</span></div>
<div>
<span class="text-lg text-neutral-600 font-mono tracking-tight"
>Headers</span
>
</div>
</div>
<div class="grid grid-cols-1 items-start lg:grid-cols-2">
<div><span class="text-black">Link</span></div>
<div>
<a
href="#_"
class="text-sm hover:text-orange-600 text-neutral-500"
>Link text</a
>
</div>
</div>
<div class="grid grid-cols-1 items-start lg:grid-cols-2">
<div>
<span class="text-sm text-neutral-500">Paragraph</span>
</div>
<div>
<span class="text-sm text-neutral-600"
>I am so happy, my dear friend, so absorbed in the exquisite
sense of mere tranquil existence, that I neglect my talents.</span
>
</div>
</div>
<div class="grid grid-cols-1 items-start lg:grid-cols-2">
<div><span class="text-black">Caption</span></div>
<div>
<span class="text-netral-500 text-xs"
>Picture about somethign</span
>
</div>
</div>
<div class="grid grid-cols-1 items-start lg:grid-cols-2">
<div><span class="prose-styles">Code</span></div>
<div>
<code class="font-mono">tailwind.config.js</code>
</div>
</div>
<div class="grid grid-cols-1 items-start lg:grid-cols-2">
<div><span class="text-black">List</span></div>
<div>
<ul
class="text-neutral-600 list-inside space-y-3 list-disc"
role="list">
<ul
class="text-xs space-y-1 font-mono uppercase text-neutral-500"
role="list">
<li>Access to premium posts</li>
<li>Weekly newsletters</li>
<li>Simple, secure card payment</li>
<li>No Advertising</li>
<li>Special discounts</li>
</ul>
</ul>
</div>
</div>
</div>
</div>
</div>
<p
class="text-lg mt-12 text-neutral-600 font-mono tracking-tight text-balance">
Color palette
</p>
<div class="grid gap-2 grid-cols-2 lg:grid-cols-3 mt-2">
<div class="space-y-2">
<div class="lg:justify-center p-6 h-14 bg-white rounded-lg"></div>
</div>
<div>
<div class="lg:justify-center p-6 h-14 rounded-t-lg bg-orange-50">
</div>
<div class="lg:justify-center p-6 h-14 bg-orange-100"></div>
<div class="lg:justify-center p-6 h-14 bg-orange-200"></div>
<div class="lg:justify-center p-6 h-14 bg-orange-300"></div>
<div class="lg:justify-center p-6 h-14 bg-orange-400"></div>
<div class="lg:justify-center p-6 h-14 bg-orange-500"></div>
<div class="lg:justify-center p-6 h-14 bg-orange-600"></div>
<div class="lg:justify-center p-6 h-14 bg-orange-700"></div>
<div class="lg:justify-center p-6 h-14 bg-orange-800"></div>
<div class="lg:justify-center p-6 h-14 bg-orange-900"></div>
<div class="lg:justify-center p-6 h-14 rounded-b-lg bg-orange-950">
</div>
</div>
<div>
<div class="lg:justify-center p-6 h-14 rounded-t-lg bg-neutral-50">
</div>
<div class="lg:justify-center p-6 h-14 bg-neutral-100"></div>
<div class="lg:justify-center p-6 h-14 bg-neutral-200"></div>
<div class="lg:justify-center p-6 h-14 bg-neutral-300"></div>
<div class="lg:justify-center p-6 h-14 bg-neutral-400"></div>
<div class="lg:justify-center p-6 h-14 bg-neutral-500"></div>
<div class="lg:justify-center p-6 h-14 bg-neutral-600"></div>
<div class="lg:justify-center p-6 h-14 bg-neutral-700"></div>
<div class="lg:justify-center p-6 h-14 bg-neutral-800"></div>
<div class="lg:justify-center p-6 h-14 bg-neutral-900"></div>
<div class="lg:justify-center p-6 h-14 rounded-b-lg bg-neutral-950">
</div>
</div>
</div>
<p
class="text-lg mt-12 text-neutral-600 font-mono tracking-tight text-balance">
Buttons
</p>
<div class="flex flex-wrap gap-2 mt-6">
<button
type="submit"
title="link to your page"
aria-label="your label"
class="relative group overflow-hidden pl-4 font-mono h-14 flex space-x-6 items-center bg-orange-500 hover:bg-black duration-300 rounded-xl justify-between">
<span class="relative uppercase text-xs text-white">Primary</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"
></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"
></path>
</svg>
</div>
</div>
</button>
<button
type="submit"
title="link to your page"
aria-label="your label"
class="relative group overflow-hidden pl-4 justify-between text-xs text-white font-mono h-14 flex space-x-6 items-center bg-black hover:bg-neutral-200 hover:text-orange-600 duration-300 rounded-xl">
<span class="relative uppercase text-xs">Secondary</span>
<div
aria-hidden="true"
class="w-12 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"
></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"
></path>
</svg>
</div>
</div>
</button>
<button
type="submit"
title="link to your page"
aria-label="your label"
class="relative group overflow-hidden pl-4 justify-between text-xs text-orange-600 font-mono h-14 flex space-x-6 items-center bg-white hover:bg-neutral-200 hover:text-orange-600 duration-300 rounded-xl">
<span class="relative uppercase text-xs">Secondary</span>
<div
aria-hidden="true"
class="w-12 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"
></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"
></path>
</svg>
</div>
</div>
</button>
</div>
</div>
</div>
</section>

View File

@ -10,7 +10,7 @@
"logo_darkmode": "/images/logo-darkmode.png",
"logo_width": "150",
"logo_height": "33",
"logo_text": "Astrofront",
"logo_text": "Polymech Logo",
"image": {
"default": "/images/default-image.png",
"error": "/images/error-image.png",

View File

@ -1,59 +0,0 @@
{
"main": [
{
"name": "Home",
"url": "/"
},
{
"name": "Products",
"url": "/products"
},
{
"name": "Pages",
"url": "",
"hasChildren": true,
"children": [
{
"name": "About",
"url": "/about"
},
{
"name": "Contact",
"url": "/contact"
},
{
"name": "404 Page",
"url": "/404"
}
]
},
{
"name": "Contact",
"url": "/contact"
}
],
"footer": [
{
"name": "About",
"url": "/about"
},
{
"name": "Products",
"url": "/products"
},
{
"name": "Contact",
"url": "/contact"
}
],
"footerCopyright": [
{
"name": "Privacy & Policy",
"url": "/privacy-policy"
},
{
"name": "Terms of Service",
"url": "/terms-services"
}
]
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -4,14 +4,11 @@ import { } from 'astro:content'
import { sync as read } from '@polymech/fs/read'
import { sync as exists } from '@polymech/fs/exists'
import { validateFilename, validatePath, sanitizeFilename } from '@polymech/fs/utils'
import { filesEx, forward_slash, resolveConfig } from '@polymech/commons'
import { ICADNodeSchema, IComponentConfig } from '@polymech/commons/component'
import { RenderedContent, DataEntry } from "astro:content"
import type { Loader, LoaderContext } from 'astro/loaders'
import {
CAD_MAIN_MATCH, PRODUCT_BRANCHES,
CAD_EXTENSIONS, CAD_MODEL_EXT, PRODUCT_DIR, PRODUCT_GLOB,
@ -35,6 +32,8 @@ import { translate } from "@/base/i18n.js"
import { I18N_SOURCE_LANGUAGE } from "config/config.js"
import { slugify } from "@/base/strings.js"
export const ITEM_TYPE = 'product'
export interface IComponentConfigEx extends IComponentConfig {
content: string
extra_resources?: string
@ -86,6 +85,23 @@ const onComponent = async (item: IStoreItem, ctx: LoaderContext) => {
}
*/
}
export const defaults = async (data: IComponentConfigEx, cwd: string, root:string): Promise<IComponentConfigEx> => {
let defaultsJSON = await findUp('defaults.json', {
stopAt: root,
cwd: cwd
});
try {
if (defaultsJSON) {
data = {
...read(defaultsJSON, 'json') as any,
...data,
};
}
} catch (error) {
}
return data;
};
const cad = async (item: IStoreItem): Promise<ICADNodeSchema[]> => {
const root = PRODUCT_ROOT()
@ -168,21 +184,7 @@ const onItem = async (item: IStoreItem, ctx: LoaderContext) => {
//
// Variables
//
let defaultsJSON = await findUp('defaults.json', {
stopAt: PRODUCT_ROOT(),
cwd: itemDir
})
try {
if (defaultsJSON) {
data = {
...read(defaultsJSON, 'json') as any,
...data,
}
}
} catch (error) {
logger.error(`Error reading defaults.json: ${error.message}`);
}
data = await defaults(data, itemDir, PRODUCT_ROOT());
data = {
...data,
...default_profile.variables,
@ -223,7 +225,7 @@ export function loader(branch: string): Loader {
slug: id,
id,
title: product.name,
type: 'product',
type: ITEM_TYPE,
highlights: [],
components: [],
...product,
@ -249,7 +251,7 @@ export function loader(branch: string): Loader {
}
}
return {
name: "store-loader",
name: `astro:store:${ITEM_TYPE}`,
load
};
}

View File

@ -1,11 +0,0 @@
---
import BaseLayout from "@/layouts/BaseLayout.astro";
import PricingTiers from "@/components/pricing/PricingTiers.astro";
import Faq from "@/components/infopages/Faq.astro";
---
<BaseLayout>
<PricingTiers />
<Faq />
</BaseLayout>

View File

@ -1,7 +0,0 @@
---
import BaseLayout from "@/layouts/BaseLayout.astro";
import StyleGuide from "@/components/system/StyleGuide.astro";
---
<BaseLayout>
<StyleGuide />
</BaseLayout>

View File

@ -1,38 +1,38 @@
---
import BaseLayout from "@/layouts/BaseLayout.astro";
import Wrapper from "@/components/containers/Wrapper.astro";
import { getCollection } from "astro:content";
import KBot from "@/components/polymech/kbot.astro";
const allProducts = await getCollection("store");
const locale = Astro.currentLocale
const store = `${locale}/store/`
---
<BaseLayout>
<Wrapper variant="standard" class="py-4">
<section>
<div class="py-2 space-y-2">
<div class="grid md:grid-cols-1 lg:grid-cols-1 gap-4" >
<KBot template="research" filters="code" cache dst="./temp/software.md">
Summarize the following packages, as markdown, add a table
https://git.polymech.io/osr-plastic/osr-cad
https://git.polymech.io/osr-plastic/osr-code-bot
</KBot>
<KBot template="research" filters="code" cache dst="./temp/forum.md">
Top posts, as markdown, add a table, with links and image preview
https://forum.osr-plastic.org/
</KBot>
<KBot template="research" filters="code" cache dst="./temp/kbot.md">
Short summary, as markdown
https://www.npmjs.com/package/@plastichub/kbot
</KBot>
</div>
</div>
</section>
</Wrapper
></BaseLayout
>
---
import BaseLayout from "@/layouts/BaseLayout.astro";
import Wrapper from "@/components/containers/Wrapper.astro";
import { getCollection } from "astro:content";
import KBot from "@/components/polymech/kbot.astro";
const allProducts = await getCollection("store");
const locale = Astro.currentLocale
const store = `${locale}/store/`
---
<BaseLayout>
<Wrapper variant="standard" class="py-4">
<section>
<div class="py-2 space-y-2">
<div class="grid md:grid-cols-1 lg:grid-cols-1 gap-4" >
<KBot template="research" filters="code" cache dst="./temp/software.md">
Summarize the following packages, as markdown, add a table
https://git.polymech.io/osr-plastic/osr-cad
https://git.polymech.io/osr-plastic/osr-code-bot
</KBot>
<KBot template="research" filters="code" cache dst="./temp/forum.md">
Top posts, as markdown, add a table, with links and image preview
https://forum.osr-plastic.org/
</KBot>
<KBot template="research" filters="code" cache dst="./temp/kbot.md">
Short summary, as markdown
https://www.npmjs.com/package/@plastichub/kbot
</KBot>
</div>
</div>
</section>
</Wrapper
></BaseLayout
>