gallery site keywords 1/2
This commit is contained in:
parent
5555c04387
commit
7a456b508d
File diff suppressed because one or more lines are too long
@ -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]"
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
>
|
||||
❮
|
||||
</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"
|
||||
>
|
||||
❯
|
||||
|
||||
@ -15,4 +15,5 @@ export interface Options {
|
||||
api_key?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
fitToBounds?: boolean;
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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({
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
Reference in New Issue
Block a user