gallery site keywords 1/2

This commit is contained in:
lovebird 2025-03-08 00:05:48 +01:00
parent 5555c04387
commit 7a456b508d
10 changed files with 406 additions and 270 deletions

File diff suppressed because one or more lines are too long

View File

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

View File

@ -28,6 +28,8 @@ export interface Props {
canonical?: string;
view: string;
path: string;
additionalKeywords?: string[];
onKeywordsProcessed?: (keywords: string[]) => void;
frontmatter?: {
title?: string;
description?: string;
@ -36,7 +38,7 @@ export interface Props {
};
}
const env = import.meta.env
const { frontmatter, view, path } = Astro.props
const { frontmatter, view, path, additionalKeywords = [], onKeywordsProcessed } = Astro.props
const { url } = Astro.request
const REDIRECT = false //import.meta.env.I18N_REDIRECT
const _url = Astro.url
@ -69,11 +71,18 @@ if(item_config.PRODUCT_ROOT){
const allKeywords = Array.from(new Set([
...defaultsKeywords,
...configKeywords,
...itemKeywords
...itemKeywords,
...additionalKeywords
])).join(',');
system_keywords = await translate(allKeywords, I18N_SOURCE_LANGUAGE, locale)
}
const keywords = [ ...new Set([item_config.name, ...system_keywords.split(',')])].join(',')
const keywordsArray = [...new Set([item_config.name, ...system_keywords.split(','), ...additionalKeywords])]
const keywords = keywordsArray.join(',')
// Call the callback with the processed keywords if it exists
if (typeof onKeywordsProcessed === 'function') {
onKeywordsProcessed(keywordsArray);
}
---
<AstroSeo

View File

@ -15,6 +15,8 @@ interface Image {
export interface Props {
images: Image[];
id?: string;
onAltTextsProcessed?: (altTexts: string[]) => void;
siteKeywords?: string[];
gallerySettings?: {
SIZES_REGULAR?: string;
SIZES_THUMB?: string;
@ -31,7 +33,7 @@ export interface Props {
};
}
const { images, gallerySettings = {}, lightboxSettings = {} } = Astro.props;
const { images, gallerySettings = {}, lightboxSettings = {}, onAltTextsProcessed, siteKeywords = [] } = Astro.props;
const mergedGallerySettings = {
SIZES_REGULAR: gallerySettings.SIZES_REGULAR || IMAGE_SETTINGS.GALLERY.SIZES_REGULAR,
@ -47,6 +49,36 @@ const mergedLightboxSettings = {
};
const locale = Astro.currentLocale || "en";
// Function to augment alt text with keywords
const augmentAltText = (altText, keywords) => {
if (!keywords || keywords.length === 0) return altText;
// Filter out keywords that are already in the alt text
const filteredKeywords = keywords.filter(keyword =>
keyword && keyword.trim() !== '' && !altText.toLowerCase().includes(keyword.toLowerCase())
);
// If no keywords to add, return original alt text
if (filteredKeywords.length === 0) return altText;
// Add keywords to alt text
return `${altText} (${filteredKeywords.join(', ')})`;
};
// Pre-calculate translated alt text for all images with augmented keywords
const translatedAltTexts = await Promise.all(
images.map(async (image) => {
// Augment alt text with site keywords before translation
const augmentedAltText = augmentAltText(image.alt, siteKeywords);
return await translate(augmentedAltText, I18N_SOURCE_LANGUAGE, locale);
})
);
// Pass the translated alt texts to the parent component if callback exists
if (onAltTextsProcessed && typeof onAltTextsProcessed === 'function') {
onAltTextsProcessed(translatedAltTexts.filter(Boolean));
}
---
<div
@ -75,8 +107,15 @@ const locale = Astro.currentLocale || "en";
}
this.isSwiping = false;
},
preloadAndOpen() {
preloadAndOpen(index = null) {
// If we're in the middle of a swipe gesture, don't open the lightbox
if (this.isSwiping) return;
// If an index is provided, update the current index
if (index !== null) {
this.currentIndex = index;
}
this.lightboxLoaded = false;
let img = new Image();
img.src = this.images[this.currentIndex].src;
@ -112,7 +151,7 @@ const locale = Astro.currentLocale || "en";
<div x-show={`currentIndex === ${index}`} key={index} class="w-full h-full flex items-center justify-center">
<Img
src={image.src}
alt={image.alt,I18N_SOURCE_LANGUAGE,locale}
alt={translatedAltTexts[index]}
objectFit="contain"
format="avif"
placeholder="blurred"
@ -152,7 +191,7 @@ const locale = Astro.currentLocale || "en";
placeholder="blurred"
sizes={mergedGallerySettings.SIZES_THUMB}
class="w-32 h-32 p-1 object-contain rounded hover:ring-2 hover:ring-blue-500"
alt={translate(image.alt,I18N_SOURCE_LANGUAGE,locale)}
alt={translatedAltTexts[index]}
attributes={{
img: {
class: "w-32 h-32 rounded-lg hover:ring-2 hover:ring-blue-500 thumbnail-img aspect-square",
@ -182,7 +221,7 @@ const locale = Astro.currentLocale || "en";
<div x-show={`currentIndex === ${index}`} key={index}>
<Img
src={image.src}
alt={image.alt}
alt={translatedAltTexts[index]}
placeholder="blurred"
format="avif"
objectFit="contain"
@ -209,16 +248,20 @@ const locale = Astro.currentLocale || "en";
<!-- Navigation Buttons -->
<button
x-show="currentIndex > 0"
x-on:click="currentIndex--;"
class="absolute left-0 top-1/2 transform -translate-y-1/2 p-4 m-[8px] text-white text-3xl bg-gray-800/75 bg-opacity-75 rounded-lg lightbox-nav"
x-on:click="currentIndex--; lightboxLoaded = false; preloadAndOpen();"
:disabled="!lightboxLoaded"
class="absolute left-0 top-1/2 transform -translate-y-1/2 p-4 m-[8px] text-white text-3xl bg-gray-800/75 bg-opacity-75 rounded-lg lightbox-nav"
:class="{'opacity-50 cursor-not-allowed': !lightboxLoaded, 'hover:bg-gray-700/75': lightboxLoaded}"
aria-label="Previous"
>
&#10094;
</button>
<button
x-show="currentIndex < total - 1"
x-on:click="currentIndex++; "
x-on:click="currentIndex++; lightboxLoaded = false; preloadAndOpen();"
:disabled="!lightboxLoaded"
class="absolute right-0 top-1/2 transform -translate-y-1/2 p-4 m-[8px] text-white text-3xl bg-gray-800/75 bg-opacity-75 rounded-lg lightbox-nav"
:class="{'opacity-50 cursor-not-allowed': !lightboxLoaded, 'hover:bg-gray-700/75': lightboxLoaded}"
aria-label="Next"
>
&#10095;

View File

@ -15,4 +15,5 @@ export interface Options {
api_key?: string;
width?: number;
height?: number;
fitToBounds?: boolean;
}

View File

@ -80,64 +80,9 @@ const { locations = [
geo: { lat: -2.269872, lon: 40.904167 },
title: 'Jannat House',
group: 'hotels'
},
// Restaurants
{
geo: { lat: -2.269925, lon: 40.901678 },
title: 'Whispers Café',
group: 'restaurants',
website: 'https://www.tripadvisor.com/Restaurant_Review-Whispers-Cafe-Lamu'
},
{
geo: { lat: -2.271564, lon: 40.902122 },
title: 'Petley\'s Inn Restaurant',
group: 'restaurants'
},
{
geo: { lat: -2.273275, lon: 40.889950 },
title: 'Peponi Restaurant',
group: 'restaurants'
},
{
geo: { lat: -2.270115, lon: 40.901988 },
title: 'Seafront Café',
group: 'restaurants'
},
// Local Attractions
{
geo: { lat: -2.228056, lon: 40.839722 },
title: 'Takwa Ruins',
group: 'attractions',
website: 'https://www.nationalmuseums.or.ke/takwa-ruins'
},
{
geo: { lat: -2.303333, lon: 40.810000 },
title: 'Dodori National Reserve',
group: 'attractions'
},
{
geo: { lat: -2.267778, lon: 40.902222 },
title: 'Lamu Donkey Sanctuary',
group: 'attractions',
website: 'https://www.lamuconservationtrust.org/donkey-sanctuary'
},
// Markets
{
geo: { lat: -2.269205, lon: 40.902015 },
title: 'Lamu Market',
group: 'markets',
website: 'https://www.visitlamu.org/market'
},
{
geo: { lat: -2.270250, lon: 40.901750 },
title: 'Lamu Fish Market',
group: 'markets'
}
], options = {} } = Astro.props;
const { zoom = 12, api_key = '', width = 1200, height = 600 } = options;
const { zoom: manualZoom = 12, api_key = '', width = 800, height = 400, fitToBounds = true } = options;
// Group locations
const groupedLocations = locations.reduce((acc, loc) => {
@ -151,10 +96,22 @@ const groupedLocations = locations.reduce((acc, loc) => {
// Define colors for different groups
const groupColors: Record<string, string> = {
default: 'red',
default: 'red-600',
historic_sites: 'amber-600',
beaches: 'sky-500',
hotels: 'emerald-600',
religious_sites: 'indigo-600',
// Add more predefined colors for groups
};
// Define a mapping between Tailwind colors and Google Maps colors
const tailwindToGoogleColors: Record<string, string> = {
'gray': 'gray',
'sky': 'blue',
'blue': 'blue',
'pink': 'pink'
};
// Get a color for a group
const getColorForGroup = (group: string): string => {
if (groupColors[group]) {
@ -162,7 +119,7 @@ const getColorForGroup = (group: string): string => {
}
// Generate colors for groups that don't have predefined colors
const colors = ['blue', 'green', 'purple', 'orange', 'yellow', 'pink', 'brown'];
const colors = ['blue-600', 'emerald-500', 'violet-600', 'amber-500', 'yellow-500', 'pink-500', 'stone-600', 'indigo-500', 'teal-600', 'orange-500', 'rose-500'];
const index = Object.keys(groupColors).length % colors.length;
groupColors[group] = colors[index];
return groupColors[group];
@ -183,20 +140,130 @@ Object.entries(groupedLocations).forEach(([group, locs]) => {
// Generate markers query param with different colors for each group
const markersParam = Object.entries(groupedLocations).map(([group, locs]) => {
const color = getColorForGroup(group);
const tailwindColor = getColorForGroup(group);
// Extract the base color name for Google Maps (before the dash)
const colorBase = tailwindColor.split('-')[0];
// Map Tailwind color to Google Maps color
const googleMapColor = tailwindToGoogleColors[colorBase] || 'red';
return locs.map((loc, groupIndex) => {
const index = allLocationsWithIndices.findIndex(item => item.location === loc);
return `markers=color:${color}%7Clabel:${String.fromCharCode(65 + index)}%7C${loc.geo.lat},${loc.geo.lon}`;
return `markers=color:${googleMapColor}%7Clabel:${String.fromCharCode(65 + index)}%7C${loc.geo.lat},${loc.geo.lon}`;
}).join('&');
}).join('&');
const centerLat = locations.reduce((sum, loc) => sum + loc.geo.lat, 0) / locations.length;
const centerLon = locations.reduce((sum, loc) => sum + loc.geo.lon, 0) / locations.length;
// Calculate bounds of all locations
const calculateBounds = (locs: Location[]) => {
if (locs.length === 0) return {
minLat: 0, maxLat: 0,
minLon: 0, maxLon: 0,
centerLat: 0, centerLon: 0
};
let minLat = locs[0].geo.lat;
let maxLat = locs[0].geo.lat;
let minLon = locs[0].geo.lon;
let maxLon = locs[0].geo.lon;
locs.forEach(loc => {
minLat = Math.min(minLat, loc.geo.lat);
maxLat = Math.max(maxLat, loc.geo.lat);
minLon = Math.min(minLon, loc.geo.lon);
maxLon = Math.max(maxLon, loc.geo.lon);
});
return { minLat, maxLat, minLon, maxLon };
};
// Calculate appropriate zoom level based on bounds
const calculateZoomLevel = (bounds: { minLat: number, maxLat: number, minLon: number, maxLon: number }) => {
// Handle edge cases
if (!bounds ||
bounds.maxLat === bounds.minLat ||
bounds.maxLon === bounds.minLon) {
return manualZoom;
}
// Add more padding (30%) to ensure all pins are visible with breathing room
const padding = 0.3;
// Apply padding to the bounds
const latDiff = bounds.maxLat - bounds.minLat;
const lonDiff = bounds.maxLon - bounds.minLon;
const paddedMinLat = bounds.minLat - (latDiff * padding);
const paddedMaxLat = bounds.maxLat + (latDiff * padding);
const paddedMinLon = bounds.minLon - (lonDiff * padding);
const paddedMaxLon = bounds.maxLon + (lonDiff * padding);
// Calculate using the Mercator projection formula
// This is a more reliable algorithm for Google Maps Static API
// Helper function to convert latitude to radians and apply Mercator projection
const latRad = (lat: number) => {
const sin = Math.sin(lat * Math.PI / 180);
const radX2 = Math.log((1 + sin) / (1 - sin)) / 2;
return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2;
};
// Calculate world coordinate spans
const latFraction = (latRad(paddedMaxLat) - latRad(paddedMinLat)) / Math.PI;
const lngFraction = ((paddedMaxLon - paddedMinLon) % 360) / 360;
// Calculate zoom levels based on the map container dimensions
const latZoom = Math.log(height / 256 / latFraction) / Math.LN2;
const lngZoom = Math.log(width / 256 / lngFraction) / Math.LN2;
// Use the smaller of the two zoom levels
let zoom = Math.min(latZoom, lngZoom);
// Floor the zoom level to get a nice integer
zoom = Math.floor(zoom);
// Limit zoom for better usability
const ZOOM_MAX = 18;
zoom = Math.min(zoom, ZOOM_MAX);
// Ensure a minimum zoom level
zoom = Math.max(zoom, 1);
// If calculated zoom is very low (showing too much of the world),
// but pins are actually close together, increase zoom
if (zoom < 3 && latDiff < 5 && lonDiff < 5) {
zoom = Math.min(12, Math.max(zoom + 3, 8));
}
// Sanity check - use manual zoom if calculation failed
if (isNaN(zoom)) {
zoom = manualZoom;
}
return zoom;
};
// Calculate center point based on chosen method
let centerLat, centerLon, zoom;
if (fitToBounds && locations.length > 1) {
const bounds = calculateBounds(locations);
centerLat = (bounds.minLat + bounds.maxLat) / 2;
centerLon = (bounds.minLon + bounds.maxLon) / 2;
zoom = calculateZoomLevel(bounds);
} else {
// Use simple average for center if fitToBounds is false or only one location
centerLat = locations.reduce((sum, loc) => sum + loc.geo.lat, 0) / locations.length;
centerLon = locations.reduce((sum, loc) => sum + loc.geo.lon, 0) / locations.length;
zoom = manualZoom;
}
const googleMapsUrl = api_key ?
`https://maps.googleapis.com/maps/api/staticmap?center=${centerLat},${centerLon}&zoom=${zoom}&size=${width}x${height}&${markersParam}&key=${api_key}` : null;
const hasError = !api_key;
// Debug information (will appear in server logs)
console.log(`Map Debug - Zoom: ${zoom}, Center: (${centerLat}, ${centerLon})`);
console.log(`Map Debug - Bounds: Min(${calculateBounds(locations).minLat}, ${calculateBounds(locations).minLon}), Max(${calculateBounds(locations).maxLat}, ${calculateBounds(locations).maxLon})`);
---
<div class="map-container w-full max-w-4xl mx-auto border border-gray-200 rounded-lg overflow-hidden shadow-md m-4">
@ -218,12 +285,11 @@ const hasError = !api_key;
<ul class="space-y-1">
{locs.map((loc) => {
const index = allLocationsWithIndices.findIndex(item => item.location === loc);
const color = getColorForGroup(group);
const tailwindColor = getColorForGroup(group) || 'red-600';
return (
<li key={index} class="flex items-center gap-2">
<span
class={`flex w-6 h-6 rounded-full bg-${color}-500 text-white items-center justify-center text-sm font-bold`}
style={`background-color: ${color}`}
class={`flex w-6 h-6 rounded-full bg-${tailwindColor} text-white items-center justify-center text-sm font-bold`}
>
{String.fromCharCode(65 + index)}
</span>

View File

@ -1,6 +1,6 @@
import { defineCollection, z } from "astro:content"
import { loader } from './model/component.js'
import { IComponentConfig, ComponentConfigSchema } from '@polymech/commons/component'
import { ComponentConfigSchema } from '@polymech/commons/component'
import { glob } from 'astro/loaders'
const store = defineCollection({

View File

@ -4,16 +4,21 @@ import Navigation from "../components/global/Navigation.astro";
import Footer from "../components/global/Footer.astro";
import { I18N_SOURCE_LANGUAGE, isRTL } from "config/config.js"
const { frontmatter: frontmatter, ...rest } = Astro.props;
const { frontmatter: frontmatter, additionalKeywords, ...rest } = Astro.props;
---
<html lang={Astro.currentLocale} class="scroll-smooth" dir={isRTL(Astro.currentLocale) ? "rtl" : "ltr"}>
<head>
<BaseHead frontmatter={frontmatter} {...rest} />
<BaseHead
frontmatter={frontmatter}
{...rest}
/>
</head>
<body class="bg-neutral-100 mx-auto 2xl:max-w-7xl flex flex-col min-h-svh p-4">
<Navigation />
<main class="grow"><slot /></main>
<main class="grow">
<slot />
</main>
<Footer />
</body>
</html>

View File

@ -6,13 +6,13 @@ import Wrapper from "@/components/containers/Wrapper.astro";
import Translate from "@/components/polymech/i18n.astro";
import Readme from "@/components/polymech/readme.astro";
import LGallery from "@/components/polymech/GalleryK.astro"
import Resources from "@/components/polymech/resources.astro"
import Specs from "@/components/polymech/specs.astro"
import TabButton from "@/components/polymech/tab-button.astro"
import TabContent from "@/components/polymech/tab-content.astro"
import GalleryK from "@/components/polymech/GalleryK.astro";
import Resources from "@/components/polymech/resources.astro";
import Specs from "@/components/polymech/specs.astro";
import TabButton from "@/components/polymech/tab-button.astro";
import TabContent from "@/components/polymech/tab-content.astro";
import "flowbite"
import "flowbite";
import {
I18N_SOURCE_LANGUAGE,
@ -25,7 +25,7 @@ import {
SHOW_TABS,
SHOW_SPECS,
SHOW_DEBUG,
SHOW_SAMPLES,
SHOW_SAMPLES,
SHOW_RESOURCES,
SHOW_CHECKOUT,
SHOW_README,
@ -38,8 +38,8 @@ const content = await translate(
data.content || "",
I18N_SOURCE_LANGUAGE,
Astro.currentLocale,
)
const Body = createMarkdownComponent(content)
);
const Body = createMarkdownComponent(content) as any;
const str_debug =
"```json\n" +
@ -53,159 +53,190 @@ const str_debug =
) +
"\n```";
const Content_Debug = await createMarkdownComponent(str_debug)
const Content_Debug = await createMarkdownComponent(str_debug);
---
<BaseLayout frontmatter={data} description={data.description} {...rest}>
<Wrapper>
<section>
<div class="grid sm:grid-cols-2 lg:grid-cols-2 just xl:grid-cols-2 gap-2">
<div class="flex flex-col gap-2 h-full justify-between">
<div>
<article class="markdown-content bg-white rounded-xl p-4 " >
<h1 class="text-neutral-500 font-mono font-semibold mb-2 text-2xl">
<Translate>{`${data.title}`}</Translate></span>
{ isRTL(Astro.currentLocale) && (
<div class="text-neutral-500 font-mono font-semibold mb-2">
"{data.title}"
</div>
)}
</h1>
<Body/>
</article>
</div>
<div class="gap-2 flex flex-col h-full justify-end">
{ SHOW_3D_PREVIEW && data.Preview3d && data.cad && data.cad[0] && data.cad[0]['.html'] && (
<a
href={ data.cad[0]['.html']}
title="link to your page"
aria-label="your label"
class="relative group overflow-hidden pl-4 font-mono h-14 flex space-x-6 items-center bg-white hover:bg-neutral-200 duration-300 rounded-xl w-full justify-between"
>
<span class="relative uppercase text-xs text-neutral-600"
><Translate>3D Preview</Translate></span>
<div
aria-hidden="true"
class="w-12 text-orange-600 transition duration-300 -translate-y-7 group-hover:translate-y-7"
<article class="markdown-content bg-white rounded-xl p-4">
<h1
class="text-neutral-500 font-mono font-semibold mb-2 text-2xl"
>
<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"
<span><Translate>{`${data.title}`}</Translate></span>
{
isRTL(Astro.currentLocale) && (
<div class="text-neutral-500 font-mono font-semibold mb-2">
"{data.title}"
</div>
)
}
</h1>
<Body />
</article>
</div>
<div class="gap-2 flex flex-col h-full justify-end">
{
SHOW_3D_PREVIEW &&
data.Preview3d &&
data.cad &&
data.cad[0] &&
data.cad[0][".html"] && (
<a
href={data.cad[0][".html"]}
title="link to your page"
aria-label="your label"
class="relative group overflow-hidden pl-4 font-mono h-14 flex space-x-6 items-center bg-white hover:bg-neutral-200 duration-300 rounded-xl w-full justify-between"
>
<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>
</a>
)
}
{ SHOW_CHECKOUT && data.checkout &&
<a
href={data.checkout}
title="link to your page"
aria-label="your label"
class="relative group overflow-hidden pl-4 font-mono h-14 flex space-x-6 items-center bg-orange-500 hover:bg-black duration-300 rounded-xl w-full justify-between"
>
<span class="relative uppercase text-xs text-white"><Translate>Add to cart</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"></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>
</a>
<span class="relative uppercase text-xs text-neutral-600">
<Translate>3D Preview</Translate>
</span>
<div
aria-hidden="true"
class="w-12 text-orange-600 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>
</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>
</div>
</div>
</a>
)
}
</div>
{
SHOW_LICENSE && (
<div class="space-y-2">
<div class="bg-white rounded-xl p-4">
<>
{
SHOW_CHECKOUT && data.checkout && (
<a
href={data.checkout}
title="link to your page"
aria-label="your label"
class="relative group overflow-hidden pl-4 font-mono h-14 flex space-x-6 items-center bg-orange-500 hover:bg-black duration-300 rounded-xl w-full justify-between"
>
<span class="relative uppercase text-xs text-white">
<Translate>Add to cart</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>
</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>
</div>
</div>
</a>
)
}
</div>
{
SHOW_LICENSE && (
<div class="space-y-2">
<div class="bg-white rounded-xl p-4">
<h3 class="text-lg text-neutral-600 uppercase font-mono tracking-tight">
License
<Translate>License</Translate>
</h3>
<p class="text-neutral-500 mt-4 text-sm">{data.license || DEFAULT_LICENSE}</p>
</>
<p class="text-neutral-500 mt-4 text-sm">
{data.license || DEFAULT_LICENSE}
</p>
</div>
</div>
</div>
)
}
</div>
)
}
</div>
{
SHOW_RENDERINGS && (
<div
class="flex-1 h-full bg-white" style="height: 100%; width: 100%;"
> <LGallery images={data.assets.renderings} gallerySettings={{SHOW_TITLE:false}} /></div>
class="flex-1 h-full bg-white"
style="height: 100%; width: 100%;"
>
<GalleryK
images={data.assets.renderings}
gallerySettings={{ SHOW_TITLE: false }}
/>
</div>
)
}
</div>
</section>
{data.assets.showcase && data.assets.showcase.length > 0 && (
<section>
<div class="mb-2 md:mb-16 mt-0 md:mt-16 p-2 md:p-4 border-b border-gray-200 dark:border-gray-700">
<LGallery images={data.assets.showcase}
lightboxSettings={{
SHOW_TITLE: false,
SHOW_DESCRIPTION: false,
SIZES_THUMB: "w-32 h-32",
}}
gallerySettings={{
SHOW_TITLE: false,
SHOW_DESCRIPTION: false
}} />
</div>
</section>)
{
data.assets.showcase && data.assets.showcase.length > 0 && (
<section>
<div class="mb-2 md:mb-16 mt-0 md:mt-16 p-2 md:p-4 border-b border-gray-200 dark:border-gray-700">
<GalleryK
images={data.assets.showcase}
lightboxSettings={{
SHOW_TITLE: false,
SHOW_DESCRIPTION: false,
SIZES_THUMB: "w-32 h-32",
}}
gallerySettings={{
SHOW_TITLE: false,
SHOW_DESCRIPTION: false,
}}
/>
</div>
</section>
)
}
<section id="tabs-view">
@ -218,7 +249,7 @@ const Content_Debug = await createMarkdownComponent(str_debug)
data-tabs-inactive-classes="dark:border-transparent text-gray-500 hover:text-gray-600 dark:text-gray-400 border-gray-100 hover:border-gray-300 dark:border-gray-700 dark:hover:text-gray-300"
role="tablist"
>
{ SHOW_README && <TabButton title="Overview" /> }
{SHOW_README && <TabButton title="Overview" />}
<TabButton title="Specs" />
<TabButton title="Gallery" />
<TabButton title="Resources" />
@ -228,60 +259,41 @@ const Content_Debug = await createMarkdownComponent(str_debug)
</div>
<div id="default-styled-tab-content">
<TabContent title="Overview" class="content">
{SHOW_README && data.readme && <Readme markdown={data.readme} data={data}/>}
{
SHOW_README && data.readme && (
<Readme markdown={data.readme} data={data} />
)
}
</TabContent>
<TabContent title="Specs" class="bg-white rounded-xl dark:bg-gray-800 font-mono">
<Specs frontmatter={data} />
</TabContent>
<TabContent title="Gallery" class="p-0 md:p-4 rounded-lg bg-white">
<GalleryK images={data.assets.gallery} />
</TabContent>
<div
class="hidden bg-white rounded-xl dark:bg-gray-800 font-mono"
id="specs-view"
role="tabpanel"
aria-labelledby="dashboard-tab"
><Specs frontmatter={data} />
</div>
<div
class="hidden p-0 md:p-4 rounded-lg bg-white"
id="gallery-view"
role="tabpanel"
aria-labelledby="dashboard-tab"
>
<LGallery images={data.assets.gallery} />
</div>
{
SHOW_SAMPLES && (
<div
class="hidden p-4 bg-white rounded-xl dark:bg-gray-800"
id="samples-view"
role="tabpanel"
aria-labelledby="dashboard-tab"
>
<LGallery images={data.assets.samples} />
</div>
<TabContent title="Samples" class="p-4 bg-white rounded-xl dark:bg-gray-800">
<GalleryK images={data.assets.samples} />
</TabContent>
)
}
{
SHOW_RESOURCES && (
<div
class="hidden p-4 bg-white rounded-xl dark:bg-gray-800"
id="resources-view"
role="tabpanel"
aria-labelledby="dashboard-tab"
>
<TabContent title="Resources" class="p-4 bg-white rounded-xl dark:bg-gray-800">
<Resources frontmatter={data} />
</div>
</TabContent>
)
}
{
SHOW_DEBUG && (
<div
class="hidden rounded-lg bg-white p-4 dark:bg-gray-800"
id="debug-view"
role="tabpanel"
aria-labelledby="dashboard-tab"
><Content_Debug />
</div>
<TabContent title="Debug" class="rounded-lg bg-white p-4 dark:bg-gray-800">
<Content_Debug />
</TabContent>
)
}
</div>
<script>
<script>
window.addEventListener("hashchange", () => {
const hash = window.location.hash.substring(1);
if (hash) {
@ -290,7 +302,7 @@ const Content_Debug = await createMarkdownComponent(str_debug)
);
if (tabTrigger) {
setTimeout(() => {
tabTrigger.click();
(tabTrigger as HTMLElement).click();
}, 100);
}
}
@ -317,6 +329,6 @@ const Content_Debug = await createMarkdownComponent(str_debug)
});
});
</script>
</section>
</section>
</Wrapper>
</BaseLayout>

View File

@ -23,10 +23,10 @@ const mapOptions = {
<div class="grid md:grid-cols-1 lg:grid-cols-1 gap-4" >
<KBot router="openai" model="gpt-4.5-preview" mode="completion" template="code_simple" disabled={true}>
</KBot>
<KBot template="research" filters="code" renderer="md" cache dst="./temp/research-plastic.md">
<KBot template="research" filters="code" renderer="md" cache dst="./temp/research-plastic.md" disabled={true}>
Plastic types, as jsx, for Astro, nicely styled table, for nice people :)
</KBot>
<KBot filters="code" template="code_simple" cache dst="./temp/jsx2.md" renderer="jsx">
<KBot filters="code" template="code_simple" cache dst="./temp/jsx2.md" renderer="jsx" disabled={true}>
simple table, 3 rows, for astro, as jsx, ensure table styling with borders
</KBot>
</div>