generated from polymech/site-template
stuff like that :)
This commit is contained in:
parent
d9acb49f47
commit
70e6c1d37b
@ -23,7 +23,7 @@
|
||||
"format": "unix-time"
|
||||
}
|
||||
],
|
||||
"default": "2025-08-19T18:25:54.314Z"
|
||||
"default": "2025-12-26T17:53:03.149Z"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
|
||||
export default new Map([
|
||||
["src/content/resources/workflow.mdx", () => import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Fresources%2Fworkflow.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/resources/workflow.mdx", () => import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Fresources%2Fworkflow.mdx&astroContentModuleFlag=true")]]);
|
||||
|
||||
81
.astro/content.d.ts
vendored
81
.astro/content.d.ts
vendored
@ -56,6 +56,10 @@ declare module 'astro:content' {
|
||||
collection: C;
|
||||
slug: E;
|
||||
};
|
||||
export type ReferenceLiveEntry<C extends keyof LiveContentConfig['collections']> = {
|
||||
collection: C;
|
||||
id: string;
|
||||
};
|
||||
|
||||
/** @deprecated Use `getEntry` instead. */
|
||||
export function getEntryBySlug<
|
||||
@ -84,6 +88,13 @@ declare module 'astro:content' {
|
||||
filter?: (entry: CollectionEntry<C>) => unknown,
|
||||
): Promise<CollectionEntry<C>[]>;
|
||||
|
||||
export function getLiveCollection<C extends keyof LiveContentConfig['collections']>(
|
||||
collection: C,
|
||||
filter?: LiveLoaderCollectionFilterType<C>,
|
||||
): Promise<
|
||||
import('astro').LiveDataCollectionResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>
|
||||
>;
|
||||
|
||||
export function getEntry<
|
||||
C extends keyof ContentEntryMap,
|
||||
E extends ValidContentEntrySlug<C> | (string & {}),
|
||||
@ -120,6 +131,10 @@ declare module 'astro:content' {
|
||||
? Promise<DataEntryMap[C][E]> | undefined
|
||||
: Promise<DataEntryMap[C][E]>
|
||||
: Promise<CollectionEntry<C> | undefined>;
|
||||
export function getLiveEntry<C extends keyof LiveContentConfig['collections']>(
|
||||
collection: C,
|
||||
filter: string | LiveLoaderEntryFilterType<C>,
|
||||
): Promise<import('astro').LiveDataEntryResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>>;
|
||||
|
||||
/** Resolve an array of entry references from the same collection */
|
||||
export function getEntries<C extends keyof ContentEntryMap>(
|
||||
@ -158,25 +173,7 @@ declare module 'astro:content' {
|
||||
};
|
||||
|
||||
type DataEntryMap = {
|
||||
"directory": Record<string, {
|
||||
id: string;
|
||||
body?: string;
|
||||
collection: "directory";
|
||||
data: InferEntrySchema<"directory">;
|
||||
rendered?: RenderedContent;
|
||||
filePath?: string;
|
||||
}>;
|
||||
"helpcenter": Record<string, {
|
||||
id: string;
|
||||
render(): Render[".md"];
|
||||
slug: string;
|
||||
body: string;
|
||||
collection: "helpcenter";
|
||||
data: InferEntrySchema<"helpcenter">;
|
||||
rendered?: RenderedContent;
|
||||
filePath?: string;
|
||||
}>;
|
||||
"howtos": Record<string, {
|
||||
"howtos": Record<string, {
|
||||
id: string;
|
||||
body?: string;
|
||||
collection: "howtos";
|
||||
@ -184,24 +181,6 @@ declare module 'astro:content' {
|
||||
rendered?: RenderedContent;
|
||||
filePath?: string;
|
||||
}>;
|
||||
"infopages": Record<string, {
|
||||
id: string;
|
||||
render(): Render[".md"];
|
||||
slug: string;
|
||||
body: string;
|
||||
collection: "infopages";
|
||||
data: InferEntrySchema<"infopages">;
|
||||
rendered?: RenderedContent;
|
||||
filePath?: string;
|
||||
}>;
|
||||
"projects": Record<string, {
|
||||
id: string;
|
||||
body?: string;
|
||||
collection: "projects";
|
||||
data: InferEntrySchema<"projects">;
|
||||
rendered?: RenderedContent;
|
||||
filePath?: string;
|
||||
}>;
|
||||
"resources": Record<string, {
|
||||
id: string;
|
||||
body?: string;
|
||||
@ -223,5 +202,33 @@ declare module 'astro:content' {
|
||||
|
||||
type AnyEntryMap = ContentEntryMap & DataEntryMap;
|
||||
|
||||
type ExtractLoaderTypes<T> = T extends import('astro/loaders').LiveLoader<
|
||||
infer TData,
|
||||
infer TEntryFilter,
|
||||
infer TCollectionFilter,
|
||||
infer TError
|
||||
>
|
||||
? { data: TData; entryFilter: TEntryFilter; collectionFilter: TCollectionFilter; error: TError }
|
||||
: { data: never; entryFilter: never; collectionFilter: never; error: never };
|
||||
type ExtractDataType<T> = ExtractLoaderTypes<T>['data'];
|
||||
type ExtractEntryFilterType<T> = ExtractLoaderTypes<T>['entryFilter'];
|
||||
type ExtractCollectionFilterType<T> = ExtractLoaderTypes<T>['collectionFilter'];
|
||||
type ExtractErrorType<T> = ExtractLoaderTypes<T>['error'];
|
||||
|
||||
type LiveLoaderDataType<C extends keyof LiveContentConfig['collections']> =
|
||||
LiveContentConfig['collections'][C]['schema'] extends undefined
|
||||
? ExtractDataType<LiveContentConfig['collections'][C]['loader']>
|
||||
: import('astro/zod').infer<
|
||||
Exclude<LiveContentConfig['collections'][C]['schema'], undefined>
|
||||
>;
|
||||
type LiveLoaderEntryFilterType<C extends keyof LiveContentConfig['collections']> =
|
||||
ExtractEntryFilterType<LiveContentConfig['collections'][C]['loader']>;
|
||||
type LiveLoaderCollectionFilterType<C extends keyof LiveContentConfig['collections']> =
|
||||
ExtractCollectionFilterType<LiveContentConfig['collections'][C]['loader']>;
|
||||
type LiveLoaderErrorType<C extends keyof LiveContentConfig['collections']> = ExtractErrorType<
|
||||
LiveContentConfig['collections'][C]['loader']
|
||||
>;
|
||||
|
||||
export type ContentConfig = typeof import("./../src/content.config.js");
|
||||
export type LiveContentConfig = never;
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -1,5 +1,5 @@
|
||||
{
|
||||
"_variables": {
|
||||
"lastUpdateCheck": 1755620245882
|
||||
"lastUpdateCheck": 1766761372436
|
||||
}
|
||||
}
|
||||
@ -10512,7 +10512,7 @@
|
||||
},
|
||||
"%3Ca%20class=%22text-orange-600%20underline%22%20href=%22https://linktr.ee/plastmakers": {
|
||||
"isValid": false,
|
||||
"timestamp": 1755627954986
|
||||
"timestamp": 1755628616096
|
||||
},
|
||||
"%0A%0ALaser-Cut%20Parts%20for%20Pressing%20Plates:%0A%5BPressing%20Plate%20Parts%5D(%0A%0AFor%20updates%20and%20tips%20on%20compression%20molding,%20visit:%0A%0A-%20~~%5BYouTube%20and%20Instagram%20Resources%5D(%3Ca%20class=%22text-orange-600%20underline%22%20href=%22https://linktr.ee/plastmakers": {
|
||||
"isValid": false,
|
||||
@ -10552,7 +10552,7 @@
|
||||
},
|
||||
"%3Ca%20class=%22text-orange-600%20underline%22%20href=%22https://www.qitech.de/en/industries": {
|
||||
"isValid": false,
|
||||
"timestamp": 1755628185764
|
||||
"timestamp": 1755628632676
|
||||
},
|
||||
"%3Ca%20class=%22text-orange-600%20underline%22%20href=%22https://www.qitech.de/en/ind/academy-area/filament-extruder-comparision": {
|
||||
"isValid": false,
|
||||
@ -10612,19 +10612,19 @@
|
||||
},
|
||||
"%3Ca%20class=%22text-orange-600%20underline%22%20href=%22https://www.darigovresearch.com/donate": {
|
||||
"isValid": false,
|
||||
"timestamp": 1755628451499
|
||||
"timestamp": 1755628664978
|
||||
},
|
||||
"%3Ca%20class=%22text-orange-600%20underline%22%20href=%22https://www.patreon.com/darigovresearch": {
|
||||
"isValid": false,
|
||||
"timestamp": 1755628451507
|
||||
"timestamp": 1755628664988
|
||||
},
|
||||
"%3Ca%20class=%22text-orange-600%20underline%22%20href=%22https://www.darigovresearch.com/": {
|
||||
"isValid": false,
|
||||
"timestamp": 1755628451514
|
||||
"timestamp": 1755628664995
|
||||
},
|
||||
"%3Ca%20class=%22text-orange-600%20underline%22%20href=%22https://www.youtube.com/channel/UCb34hWA6u2Lif92aljhV4HA": {
|
||||
"isValid": false,
|
||||
"timestamp": 1755628451521
|
||||
"timestamp": 1755628665004
|
||||
},
|
||||
"https://www.youtube.com/watch?v=IoSn84Axao8": {
|
||||
"isValid": true,
|
||||
@ -10663,5 +10663,41 @@
|
||||
"%3Ca%20class=%22text-orange-600%20underline%22%20href=%22https://www.dropbox.com/sh/xlts122wcb905q6/AABRgMZTki8gH1NqQ5SvOS-Ia?dl=0": {
|
||||
"isValid": false,
|
||||
"timestamp": 1755628607796
|
||||
},
|
||||
"%3Ca%20class=%22text-orange-600%20underline%22%20href=%22https://photos.app.goo.gl/7ALBkSbWRh6VzzaK9": {
|
||||
"isValid": false,
|
||||
"timestamp": 1755628624522
|
||||
},
|
||||
"%3Ca%20class=%22text-orange-600%20underline%22%20href=%22https://youtu.be/6Ae6oDKhqiE": {
|
||||
"isValid": false,
|
||||
"timestamp": 1755628632667
|
||||
},
|
||||
"%3Ca%20class=%22text-orange-600%20underline%22%20href=%22https://www.youtube.com/watch?v=6u6y6gD17rk&feature=youtu.be": {
|
||||
"isValid": false,
|
||||
"timestamp": 1755628640416
|
||||
},
|
||||
"%3Ca%20class=%22text-orange-600%20underline%22%20href=%22https://www.freecadweb.org/downloads.php": {
|
||||
"isValid": false,
|
||||
"timestamp": 1755628664967
|
||||
},
|
||||
"%3Ca%20class=%22text-orange-600%20underline%22%20href=%22https://www.youtube.com/watch?v=BTiQqPFE9vs": {
|
||||
"isValid": false,
|
||||
"timestamp": 1755628691506
|
||||
},
|
||||
"%3Ca%20class=%22text-orange-600%20underline%22%20href=%22https://www.youtube.com/watch?v=YzjTm3FRLVY&t=5s": {
|
||||
"isValid": false,
|
||||
"timestamp": 1755628745050
|
||||
},
|
||||
"%3Ca%20class=%22text-orange-600%20underline%22%20href=%22https://www.fosbarcelona.com/": {
|
||||
"isValid": false,
|
||||
"timestamp": 1755628754193
|
||||
},
|
||||
"%3Ca%20class=%22text-orange-600%20underline%22%20href=%22https://www.instagram.com/": {
|
||||
"isValid": false,
|
||||
"timestamp": 1755628754202
|
||||
},
|
||||
"%0A%0A%0AUser%20Location:%20Gandia,%20Spain%0A%0A##%20Guide%20Overview%0A%0AThis%20guide%20covers%20the%20creation%20of%20two%20products%20using%20panels%20or%20boards%20made%20from%20polypropylene%20plastic%20and%20fishing%20nets:%0A%0A-%20Urban%20Bench%0A-%20Dumbbells%0A%0AFor%20detailed%20instructions%20on%20creating%20panels%20with%20a%20sheet%20press,%20refer%20to%20the%20expanded%20guide%20titled%20%22Boards%20Made%20from%20Marine%20Litter.%22%0A%0AThis%20guide%20will%20focus%20on%20the%20product%20creation%20and%20manufacturing%20process.%0A%0A##%20How-To%20Guide%20for%20CNC%20Machining%20with%20CAD%20and%203D%20Modeling%20Software%0A%0AWe%20utilize%20CAD%20and%203D%20modeling%20software%20to%20design%20products%20and%20define%20cutting%20lines%20for%20CNC%20machining.%20For%203D%20modeling,%20Solidworks%20and%20Rhinoceros%20are%20used,%20while%20AutoCAD%20and%20Illustrator%20handle%202D%20cutting%20lines.%0A%0AOnce%20the%20design%20is%20finalized,%20we%20prepare%20the%20material%20for%20CNC%20machining.%0A%0A###%20Tips%20for%20CNC%20Machining%0A%0A-%20Efficiently%20utilize%20material%20to%20minimize%20waste%20by%20optimizing%20the%20cutting%20layout.%0A-%20Plastic%20boards,%20being%20generally%20softer%20than%20wood,%20can%20often%20be%20cut%20in%20one%20pass%20with%20the%20same%20drill%20bit,%20improving%20production%20time%20and%20finish%20quality.%20Conduct%20initial%20test%20cuts%20to%20ensure%20optimal%20settings.%0A-%20Collect%20shavings%20after%20cutting;%20these%20can%20be%20reused%20as%20raw%20material%20in%20other%20processes%20like%20sheet%20pressing%20or%20extrusion.%0A%0AAfter%20cutting%20the%20parts,%20proceed%20with%20assembling%20the%20bench%20and%20its%20framework.%20%0A%0AFor%20urban%20furniture,%20we%20have%20created%20a%20metal%20framework%20to%20enhance%20stability%20and%20durability.%0A%0A**Tips**%0A%0AUse%20galvanized%20or%20stainless%20steel%20screws%20and%20nuts%20to%20connect%20plastic%20elements%20to%20the%20metal%20structure.%20Stainless%20steel%20is%20more%20costly%20but%20more%20resistant%20to%20oxidation.%0A%0AWhen%20attaching%20plastic%20parts,%20drill%20a%20hole%20matching%20the%20screw%20size%20to%20prevent%20the%20plastic%20from%20breaking%20or%20deforming%20under%20pressure.%0A%0ATo%20create%20the%20round%20weight%20plates%20for%20dumbbells,%20we%20utilize%20marine%20litter%20boards,%20similar%20to%20those%20used%20for%20the%20bench,%20and%20cut%20them%20using%20a%20CNC%20machine.%0A%0AFor%20the%20handles,%20we%20employ%20a%20mix%20of%20recycled%20plastic%20from%20bottle%20caps.%0A%0AAn%20extrusion%20machine%20is%20used%20to%20form%20plastic%20rods%20measuring%203%20meters%20(9.8%20feet": {
|
||||
"isValid": false,
|
||||
"timestamp": 1755628762741
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "use a formal tone\nspell & grammar fix the text,\nremove emojis\nremove personal preferences or biases\nshorten text if possible but preserve personality\nremove references to preciousplastic, bazar and Discord\nremove any brain/green washing, eg: sustainable, circular, recycling ... inflated prospects\nRewrite the following text to remove any inflated or empty language, sugar-coated filler, and needless repetition. The result should be concise, direct, and preserve only the essential ideas.\nContext: howto tutorials, for makers\nConvert units, from metric to imperial and vice versa (in braces)\ndont comment just return as Markdown\n\nText to process:\nMaking clock is not difficult and you are able to recycle about 300g per clock in less than 2 hours. With creative design can be clock nice present for your friends or family. These hand made products can be done with electric oven, mini press and a simple compression mould. Clock diameter is 30 cm with thickness of 5mm. Material cost is about 6 EUR/ clock."
|
||||
"content": "Return a list of max. 10 keywords that can be used for SEO purposes, separated by commas (dont comment, just the list) : \n\nText to process:\nDeck rails have been used by skateboarders since the 80’s to (1) help boards slide better on handrails, coping, curbs, etc., and (2) to protect board graphics. These recycled rails succeed at both of those things, but offer something that skateboarding has never seen before: a set of deck rails that is made from 100% post-consumer waste.\n\n\nUser Location: Los Angeles, United States of America (the)\n\nYou can buy my mold, or my mold design from the [filtered] bazar or my website (links below), or design a version yourself.\n(with anything [filtered] related that sells on my website, I donate 5% of the sales to \n\n\n<a class=\"text-orange-600 underline\" href=\"https://skatehyena.com/\" target=\"_blank\" rel=\"noopener noreferrer\">skatehyena.com: skatehyena.com</a>\n\nIf you buy my mold, then you’ll receive it in about 4 weeks. \n\nIf you buy my mold design (or design it yourself), then you’ll have the digital file, but you’ll still need to have the mold made, which leaves two options:\n - Make it yourself\n - Send the file to someone to make the mold:\n - Your local CNC machinist \n - Whoever is the most local mold maker to you on the [filtered] Bazar \n\n\nCollect used plastic to shred and shred it, or buy pre-shredded plastic:\n\n \n \n\n(I’ve found that type #2 HDPE has worked best for me for durability and boardsliding, but I’d love to hear what other people find if another plastic type works better/differently for them)\n\n\nBuy or build an injection machine\n\n\n<a class=\"text-orange-600 underline\" href=\"https://youtu.be/qtZv96ciFIU\" target=\"_blank\" rel=\"noopener noreferrer\">youtu.be: youtu.be/qtZv96ciFIU</a>\n\n(also, I realize that an extruder might be a better [filtered] machine for this product. That said, I can’t afford an extruder, so I’ve been using the V3 injection machine. I’d love to hear any feedback if someone out there makes these rails with an extruder.)\n\n\nLearn how to use your new injection machine and mold and get a crash course on plastics (link below). When going through all of this educational info, if you have any questions feel free to email me at preciousplasticpasadena@gmail.com\n\n\n\nI've been using the [filtered] V3 injection machine with a carjack (because the mold is too wide to screw onto the injection machine all the way). See link below for [filtered]'s How-To for using the V3 injection machine.\n\nThe rail mold takes about 80 grams of molten plastic (this varies depending on the plastic type), so you'll end up using about 80% of the plastic from an injection machine that's been filled to the brim.\n\nI also pre-heat the mold for 15 minutes at 250°F / 121°C, so that when the molten plastic hits the mold, it's not hitting a lukewarm surface and allows for better melt-flow. \n\n\n\nAfter you've made the rails, screws are needed to attach the rails to the bottom of a skateboard. Order screws that fit the rails and work with skateboard decks (this took a lot of trial and error to figure out which screws work best).\n\nHere's the options I found that work best:\n - Order these: <a class=\"text-orange-600 underline\" href=\"https://www.mcmaster.com/91555A101/\" target=\"_blank\" rel=\"noopener noreferrer\">mcmaster.com: mcmaster.com/91555A101</a> \n - If you’re not able to order through McMaster, find screws that match the image attached to this step.\n\nI recommend using a plain non-powered phillips head screwdriver to screw the rails onto a board and not strip out the wood. But an electric drill can work if you’re delicate.\n\n\nMake your own recycled rails, and anything else that you can think of to have injection molds made of! And happy recycling!"
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
|
||||
118
app-config.json
Normal file
118
app-config.json
Normal file
@ -0,0 +1,118 @@
|
||||
{
|
||||
"site": {
|
||||
"title": "Polymech Library",
|
||||
"base_url": "https://library.polymech.info/",
|
||||
"description": "",
|
||||
"base_path": "/",
|
||||
"trailing_slash": false,
|
||||
"favicon": "/images/favicon.png",
|
||||
"logo": "/images/logo.png",
|
||||
"logo_darkmode": "/images/logo-darkmode.png",
|
||||
"logo_width": "150",
|
||||
"logo_height": "33",
|
||||
"logo_text": "Polymech Library",
|
||||
"image": {
|
||||
"default": "/images/default-image.png",
|
||||
"error": "/images/error-image.png",
|
||||
"alt": "Polymech Library"
|
||||
}
|
||||
},
|
||||
"footer_left": [
|
||||
{
|
||||
"href": "/${LANG}/resources/info/contact",
|
||||
"text": "Contact"
|
||||
},
|
||||
{
|
||||
"href": "https://forum.polymech.info/",
|
||||
"text": "Forum"
|
||||
},
|
||||
{
|
||||
"href": "https://files.polymech.info/",
|
||||
"text": "Files"
|
||||
},
|
||||
{
|
||||
"href": "https://git.polymech.info/explore/repos",
|
||||
"text": "Github"
|
||||
}
|
||||
],
|
||||
"footer_right": [],
|
||||
"settings": {
|
||||
"search": true,
|
||||
"account": true,
|
||||
"sticky_header": true,
|
||||
"theme_switcher": true,
|
||||
"default_theme": "system"
|
||||
},
|
||||
"params": {
|
||||
"contact_form_action": "#",
|
||||
"copyright": "Designed And Developed by [Themefisher](https://themefisher.com/)"
|
||||
},
|
||||
"navigation": {
|
||||
"top": [
|
||||
{
|
||||
"href": "/${LANG}",
|
||||
"text": "Home"
|
||||
},
|
||||
{
|
||||
"href": "/${LANG}/resources",
|
||||
"text": "Resources"
|
||||
},
|
||||
{
|
||||
"href": "/${LANG}/library",
|
||||
"text": "Library"
|
||||
},
|
||||
{
|
||||
"href": "/${LANG}/tutorials",
|
||||
"text": "Tutorials"
|
||||
},
|
||||
{
|
||||
"href": "https://service.polymech.info/",
|
||||
"text": "Media"
|
||||
},
|
||||
{
|
||||
"href": "https://forum.polymech.info/",
|
||||
"text": "Forum"
|
||||
},
|
||||
{
|
||||
"href": "/${LANG}/resources/info/contact",
|
||||
"text": "Contact"
|
||||
}
|
||||
]
|
||||
},
|
||||
"navigation_button": {
|
||||
"enable": true,
|
||||
"label": "Get Started",
|
||||
"link": "https://github.com/themefisher/astrofront"
|
||||
},
|
||||
"ecommerce": {
|
||||
"brand": "Polymech",
|
||||
"currencySymbol": "",
|
||||
"currencyCode": "EU"
|
||||
},
|
||||
"metadata": {
|
||||
"country": "Spain",
|
||||
"city": "Barcelona",
|
||||
"author": "Polymech",
|
||||
"author_bio": "I am in, if its true",
|
||||
"author_url": "https://polymech.info/",
|
||||
"image": "/images/og-image.png",
|
||||
"description": "Polymech is a plastic prototyping company that offers product design services.",
|
||||
"keywords": "Plastic, Prototyping, Product Design, Opensource"
|
||||
},
|
||||
"shopify": {
|
||||
"currencySymbol": "",
|
||||
"currencyCode": "EU",
|
||||
"collections": {
|
||||
"hero_slider": "hidden-homepage-carousel",
|
||||
"featured_products": "featured-products"
|
||||
}
|
||||
},
|
||||
"pages": {
|
||||
"home": {
|
||||
"hero": "https://assets.osr-plastic.org/machines//assets/newsletter/common/products/extruders/overview-3.jpg",
|
||||
"_blog": {
|
||||
"store": "resources"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
import { defineConfig } from 'astro/config'
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
import { imagetools } from "imagetools"
|
||||
// import domainExpansion from '@domain-expansion/astro';
|
||||
import react from "@astrojs/react"
|
||||
import mdx from "@astrojs/mdx";
|
||||
import { defineConfig } from 'astro/config';
|
||||
@ -9,6 +8,46 @@ import sitemap from "@astrojs/sitemap";
|
||||
import { rehypeAccessibleEmojis } from 'rehype-accessible-emojis';
|
||||
import getReadingTime from 'reading-time';
|
||||
import { toString } from 'mdast-util-to-string';
|
||||
import { PolymechInstance } from "@polymech/astro-base/registry";
|
||||
|
||||
// import domainExpansion from '@domain-expansion/astro';
|
||||
import yargs from 'yargs'
|
||||
import { hideBin } from 'yargs/helpers'
|
||||
|
||||
const argv = yargs(hideBin(process.argv)).argv;
|
||||
|
||||
PolymechInstance.setConfig({
|
||||
//...config,
|
||||
productCategory: 'Consumer Electronics2',
|
||||
languages: ['en', 'de', 'fr', 'es', 'it'],
|
||||
products: ['laptop', 'phone', 'tablet', 'headphones', 'smartwatch', 'camera'],
|
||||
features: ['multi-language', 'dynamic-content', 'seo-optimized', 'analytics'],
|
||||
apiEndpoints: {
|
||||
products: '/api/products',
|
||||
translations: '/api/translations',
|
||||
categories: '/api/categories',
|
||||
search: '/api/search'
|
||||
},
|
||||
callbacks: {
|
||||
onConfigUpdate: (config) => {
|
||||
//console.log('MyPages config updated:', config);
|
||||
},
|
||||
onLanguageChange: (lang) => {
|
||||
console.log('Language changed to:', lang);
|
||||
},
|
||||
onRouteRender: (path, { lang, slug }) => {
|
||||
console.log('Route rendered:', path, { lang, slug });
|
||||
},
|
||||
get: (path, { lang, slug }) => {
|
||||
//console.log('Getting data for:2', path, { lang, slug });
|
||||
return {
|
||||
title: 'Product Page',
|
||||
description: 'This is a product page',
|
||||
sharedPageText: 'This is shared text'
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export function remarkReadingTime() {
|
||||
return function (tree, { data }) {
|
||||
@ -17,7 +56,7 @@ export function remarkReadingTime() {
|
||||
data.astro.frontmatter.minutesRead = readingTime.text;
|
||||
};
|
||||
}
|
||||
//locales: ['en','es','fr','it','de'],
|
||||
|
||||
export default defineConfig({
|
||||
site: 'https://creava.org',
|
||||
devToolbar: {
|
||||
|
||||
@ -3,14 +3,8 @@
|
||||
{
|
||||
"path": "."
|
||||
},
|
||||
{
|
||||
"path": "../polymech-mono/packages"
|
||||
},
|
||||
{
|
||||
"path": "../polymech-astro"
|
||||
},
|
||||
{
|
||||
"path": "../site2"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
|
||||
3897
package-lock.json
generated
3897
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
20
package.json
20
package.json
@ -1,21 +1,24 @@
|
||||
{
|
||||
"name": "@plastichub/astro-site-template",
|
||||
"name": "@polymech/library",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "astro dev --mode dev --host=0.0.0.0",
|
||||
"generate:config": "npx vite-node scripts/generate-app-config.ts",
|
||||
"dev": "npm run generate:config && astro dev --mode dev --host=0.0.0.0",
|
||||
"dev:all": "concurrently \"npm run dev\" \"npm run serve:products\"",
|
||||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"build": "npm run generate:config && astro build -- --logLevel=info --branch=test",
|
||||
"test:build": "astro build ; cd dist ; serve",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro",
|
||||
"serve:products": "npx vite-node scripts/serve-products.ts",
|
||||
"generate-pwa-assets": "pwa-assets-generator --preset minimal-2023 public/logo.svg",
|
||||
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
||||
"lint": "eslint . --ext .ts,.tsx --fix",
|
||||
"lint:check": "eslint . --ext .ts,.tsx",
|
||||
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
|
||||
"test:lighthouse": "lighthouse https://polymech.io/en --output json --output html --output-path ./dist/reports/report.html --save-assets --chrome-flags=\"--window-size=1440,700 --headless\"",
|
||||
"test:lighthouse": "lighthouse https://polymech.info/en --output json --output html --output-path ./dist/reports/report.html --save-assets --chrome-flags=\"--window-size=1440,700 --headless\"",
|
||||
"test:debug": "playwright test",
|
||||
"test:ui": "playwright test --ui",
|
||||
"clean": "rm -rf dist",
|
||||
@ -54,6 +57,7 @@
|
||||
"autoprefixer": "^10.4.20",
|
||||
"axios": "^1.7.9",
|
||||
"cacache": "^19.0.1",
|
||||
"concurrently": "^9.2.1",
|
||||
"env-var": "^7.5.0",
|
||||
"exifreader": "^4.26.1",
|
||||
"file-type": "^20.0.0",
|
||||
@ -64,7 +68,7 @@
|
||||
"glob": "^11.0.1",
|
||||
"got": "^14.4.6",
|
||||
"html-entities": "^2.5.2",
|
||||
"imagetools": "file:../polymech-astro/packages/imagetools",
|
||||
"imagetools": "file:../polymech-astro/packages/imagetools_3",
|
||||
"jsonpath-plus": "^10.3.0",
|
||||
"lighthouse": "^12.3.0",
|
||||
"link-preview-js": "^3.0.14",
|
||||
@ -82,6 +86,7 @@
|
||||
"picomatch": "^4.0.2",
|
||||
"potrace": "^2.1.8",
|
||||
"puppeteer": "^22.3.0",
|
||||
"quicktype-core": "^23.2.6",
|
||||
"react-jsx-parser": "^2.3.0",
|
||||
"reading-time": "^1.5.0",
|
||||
"rehype-accessible-emojis": "^0.3.2",
|
||||
@ -92,11 +97,14 @@
|
||||
"remark-toc": "^9.0.0",
|
||||
"sanitize-html": "^2.14.0",
|
||||
"schema-dts": "^1.1.2",
|
||||
"sharp": "^0.29.3",
|
||||
"serve": "^14.2.5",
|
||||
"serve-handler": "^6.1.6",
|
||||
"sharp": "^0.34.5",
|
||||
"showdown": "^2.1.0",
|
||||
"tailwindcss": "^4.0.7",
|
||||
"type-fest": "^4.34.1",
|
||||
"vite": "^6.1.1",
|
||||
"vite-node": "^5.2.0",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"write-file-atomic": "^6.0.0",
|
||||
"xlsx": "^0.18.5",
|
||||
|
||||
35
scripts/commit.sh
Normal file
35
scripts/commit.sh
Normal file
@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Define repositories to commit to
|
||||
# . refers to the current directory (polymech/site2)
|
||||
# ../polymech-astro refers to the sibling directory
|
||||
REPOS=("." "../polymech-astro")
|
||||
|
||||
# Store the optional commit message
|
||||
MSG="$1"
|
||||
|
||||
# Iterate over each repository
|
||||
for repo in "${REPOS[@]}"; do
|
||||
echo "--------------------------------------------------"
|
||||
echo "Processing repository: $repo"
|
||||
echo "--------------------------------------------------"
|
||||
|
||||
# Execute in a subshell to preserve current directory
|
||||
(
|
||||
cd "$repo" || { echo "Failed to enter $repo"; exit 1; }
|
||||
|
||||
# Add all changes
|
||||
git add -A .
|
||||
|
||||
# Commit
|
||||
if [ -n "$MSG" ]; then
|
||||
git commit -m "$MSG"
|
||||
else
|
||||
# If no message provided, let git open the editor
|
||||
git commit
|
||||
fi
|
||||
|
||||
# Pushing changes
|
||||
git push
|
||||
)
|
||||
done
|
||||
64
scripts/generate-app-config.ts
Normal file
64
scripts/generate-app-config.ts
Normal file
@ -0,0 +1,64 @@
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { quicktype, InputData, jsonInputForTargetLanguage } from 'quicktype-core';
|
||||
|
||||
const CONFIG_PATH = path.resolve('./app-config.json');
|
||||
const OUTPUT_SCHEMA_PATH = path.resolve('./src/app/config.schema.ts');
|
||||
const OUTPUT_DTS_PATH = path.resolve('./src/app/config.d.ts');
|
||||
|
||||
async function main() {
|
||||
console.log(`Reading config from ${CONFIG_PATH}...`);
|
||||
const configContent = fs.readFileSync(CONFIG_PATH, 'utf8');
|
||||
|
||||
// 1. Generate TypeScript Definitions (d.ts) FIRST
|
||||
console.log('Generating TypeScript definitions...');
|
||||
|
||||
const tsInput = jsonInputForTargetLanguage("ts");
|
||||
await tsInput.addSource({
|
||||
name: "AppConfig",
|
||||
samples: [configContent]
|
||||
});
|
||||
|
||||
const tsInputData = new InputData();
|
||||
tsInputData.addInput(tsInput);
|
||||
|
||||
const tsResult = await quicktype({
|
||||
inputData: tsInputData,
|
||||
lang: "ts",
|
||||
rendererOptions: {
|
||||
"just-types": "true",
|
||||
"acronym-style": "original"
|
||||
}
|
||||
});
|
||||
|
||||
const tsCode = tsResult.lines.join('\n');
|
||||
fs.writeFileSync(OUTPUT_DTS_PATH, tsCode);
|
||||
console.log(`Wrote TypeScript definitions to ${OUTPUT_DTS_PATH}`);
|
||||
|
||||
// 2. Generate Zod Schema from Types using ts-to-zod
|
||||
console.log('Generating Zod schema from types...');
|
||||
|
||||
try {
|
||||
const { execSync } = await import('child_process');
|
||||
// ts-to-zod <input> <output>
|
||||
// Use relative paths to avoid Windows path concatenation issues with ts-to-zod
|
||||
const relDts = path.relative(process.cwd(), OUTPUT_DTS_PATH);
|
||||
const relSchema = path.relative(process.cwd(), OUTPUT_SCHEMA_PATH);
|
||||
|
||||
execSync(`npx ts-to-zod "${relDts}" "${relSchema}"`, { stdio: 'inherit', cwd: process.cwd() });
|
||||
|
||||
// Append export type AppConfig
|
||||
fs.appendFileSync(OUTPUT_SCHEMA_PATH, `\nexport type AppConfig = z.infer<typeof appConfigSchema>;\n`);
|
||||
|
||||
console.log(`Wrote Zod schema to ${OUTPUT_SCHEMA_PATH}`);
|
||||
} catch (error) {
|
||||
console.error('Failed to generate Zod schema:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error('Error fetching/generating config:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
25
scripts/serve-products.ts
Normal file
25
scripts/serve-products.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import handler from 'serve-handler';
|
||||
import http from 'http';
|
||||
import { PRODUCT_ROOT, FILE_SERVER_DEV } from '../src/app/config';
|
||||
|
||||
// Parse host and port from FILE_SERVER_DEV
|
||||
const [host, portStr] = FILE_SERVER_DEV.split(':');
|
||||
const port = parseInt(portStr, 10);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const server = http.createServer((request: any, response: any) => {
|
||||
return handler(request, response, {
|
||||
public: PRODUCT_ROOT(),
|
||||
headers: [
|
||||
{
|
||||
source: '**/*',
|
||||
headers: [{ key: 'Access-Control-Allow-Origin', value: '*' }]
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(port, host, () => {
|
||||
console.log(`Running at http://${host}:${port}`);
|
||||
console.log(`Serving files from: ${PRODUCT_ROOT()}`);
|
||||
});
|
||||
103
src/app/config.d.ts
vendored
Normal file
103
src/app/config.d.ts
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
export interface AppConfig {
|
||||
site: Site;
|
||||
footer_left: FooterLeft[];
|
||||
footer_right: any[];
|
||||
settings: Settings;
|
||||
params: Params;
|
||||
navigation: Navigation;
|
||||
navigation_button: NavigationButton;
|
||||
ecommerce: Ecommerce;
|
||||
metadata: Metadata;
|
||||
shopify: Shopify;
|
||||
pages: Pages;
|
||||
}
|
||||
|
||||
export interface Ecommerce {
|
||||
brand: string;
|
||||
currencySymbol: string;
|
||||
currencyCode: string;
|
||||
}
|
||||
|
||||
export interface FooterLeft {
|
||||
href: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface Metadata {
|
||||
country: string;
|
||||
city: string;
|
||||
author: string;
|
||||
author_bio: string;
|
||||
author_url: string;
|
||||
image: string;
|
||||
description: string;
|
||||
keywords: string;
|
||||
}
|
||||
|
||||
export interface Navigation {
|
||||
top: FooterLeft[];
|
||||
}
|
||||
|
||||
export interface NavigationButton {
|
||||
enable: boolean;
|
||||
label: string;
|
||||
link: string;
|
||||
}
|
||||
|
||||
export interface Pages {
|
||||
home: Home;
|
||||
}
|
||||
|
||||
export interface Home {
|
||||
hero: string;
|
||||
_blog: Blog;
|
||||
}
|
||||
|
||||
export interface Blog {
|
||||
store: string;
|
||||
}
|
||||
|
||||
export interface Params {
|
||||
contact_form_action: string;
|
||||
copyright: string;
|
||||
}
|
||||
|
||||
export interface Settings {
|
||||
search: boolean;
|
||||
account: boolean;
|
||||
sticky_header: boolean;
|
||||
theme_switcher: boolean;
|
||||
default_theme: string;
|
||||
}
|
||||
|
||||
export interface Shopify {
|
||||
currencySymbol: string;
|
||||
currencyCode: string;
|
||||
collections: Collections;
|
||||
}
|
||||
|
||||
export interface Collections {
|
||||
hero_slider: string;
|
||||
featured_products: string;
|
||||
}
|
||||
|
||||
export interface Site {
|
||||
title: string;
|
||||
base_url: string;
|
||||
description: string;
|
||||
base_path: string;
|
||||
trailing_slash: boolean;
|
||||
favicon: string;
|
||||
logo: string;
|
||||
logo_darkmode: string;
|
||||
logo_width: string;
|
||||
logo_height: string;
|
||||
logo_text: string;
|
||||
image: Image;
|
||||
}
|
||||
|
||||
export interface Image {
|
||||
default: string;
|
||||
error: string;
|
||||
alt: string;
|
||||
}
|
||||
108
src/app/config.schema.ts
Normal file
108
src/app/config.schema.ts
Normal file
@ -0,0 +1,108 @@
|
||||
// Generated by ts-to-zod
|
||||
import { z } from "zod";
|
||||
|
||||
export const footerLeftSchema = z.object({
|
||||
href: z.string(),
|
||||
text: z.string()
|
||||
});
|
||||
|
||||
export const settingsSchema = z.object({
|
||||
search: z.boolean(),
|
||||
account: z.boolean(),
|
||||
sticky_header: z.boolean(),
|
||||
theme_switcher: z.boolean(),
|
||||
default_theme: z.string()
|
||||
});
|
||||
|
||||
export const paramsSchema = z.object({
|
||||
contact_form_action: z.string(),
|
||||
copyright: z.string()
|
||||
});
|
||||
|
||||
export const navigationSchema = z.object({
|
||||
top: z.array(footerLeftSchema)
|
||||
});
|
||||
|
||||
export const navigationButtonSchema = z.object({
|
||||
enable: z.boolean(),
|
||||
label: z.string(),
|
||||
link: z.string()
|
||||
});
|
||||
|
||||
export const ecommerceSchema = z.object({
|
||||
brand: z.string(),
|
||||
currencySymbol: z.string(),
|
||||
currencyCode: z.string()
|
||||
});
|
||||
|
||||
export const metadataSchema = z.object({
|
||||
country: z.string(),
|
||||
city: z.string(),
|
||||
author: z.string(),
|
||||
author_bio: z.string(),
|
||||
author_url: z.string(),
|
||||
image: z.string(),
|
||||
description: z.string(),
|
||||
keywords: z.string()
|
||||
});
|
||||
|
||||
export const blogSchema = z.object({
|
||||
store: z.string()
|
||||
});
|
||||
|
||||
export const collectionsSchema = z.object({
|
||||
hero_slider: z.string(),
|
||||
featured_products: z.string()
|
||||
});
|
||||
|
||||
export const imageSchema = z.object({
|
||||
default: z.string(),
|
||||
error: z.string(),
|
||||
alt: z.string()
|
||||
});
|
||||
|
||||
export const siteSchema = z.object({
|
||||
title: z.string(),
|
||||
base_url: z.string(),
|
||||
description: z.string(),
|
||||
base_path: z.string(),
|
||||
trailing_slash: z.boolean(),
|
||||
favicon: z.string(),
|
||||
logo: z.string(),
|
||||
logo_darkmode: z.string(),
|
||||
logo_width: z.string(),
|
||||
logo_height: z.string(),
|
||||
logo_text: z.string(),
|
||||
image: imageSchema
|
||||
});
|
||||
|
||||
export const shopifySchema = z.object({
|
||||
currencySymbol: z.string(),
|
||||
currencyCode: z.string(),
|
||||
collections: collectionsSchema
|
||||
});
|
||||
|
||||
export const homeSchema = z.object({
|
||||
hero: z.string(),
|
||||
_blog: blogSchema
|
||||
});
|
||||
|
||||
export const pagesSchema = z.object({
|
||||
home: homeSchema
|
||||
});
|
||||
|
||||
export const appConfigSchema = z.object({
|
||||
site: siteSchema,
|
||||
footer_left: z.array(footerLeftSchema),
|
||||
footer_right: z.array(z.any()),
|
||||
settings: settingsSchema,
|
||||
params: paramsSchema,
|
||||
navigation: navigationSchema,
|
||||
navigation_button: navigationButtonSchema,
|
||||
ecommerce: ecommerceSchema,
|
||||
metadata: metadataSchema,
|
||||
shopify: shopifySchema,
|
||||
pages: pagesSchema
|
||||
});
|
||||
|
||||
export type AppConfig = z.infer<typeof appConfigSchema>;
|
||||
@ -5,14 +5,14 @@ import { sync as read } from '@polymech/fs/read'
|
||||
import { sanitizeUri } from 'micromark-util-sanitize-uri'
|
||||
|
||||
export const OSR_ROOT = () => path.resolve(resolve("${OSR_ROOT}"))
|
||||
export const FILE_SERVER_DEV = 'localhost:5000'
|
||||
|
||||
export const LOGGING_NAMESPACE = 'polymech-site'
|
||||
export const TRANSLATE_CONTENT = true
|
||||
export const LANGUAGES = ['en', 'ar', 'de', 'ja', 'es', 'zh', 'fr']
|
||||
//export const LANGUAGES_PROD = ['en', 'es', 'ar', 'de', 'ja', 'zh', 'fr', 'nl', 'it', 'pt']
|
||||
export const LANGUAGES_PROD = ['en', 'es']
|
||||
export const LANGUAGES_PROD = ['en', 'es', 'fr']
|
||||
export const isRTL = (lang) => lang === 'ar'
|
||||
|
||||
// i18n constants
|
||||
export const I18N_STORE = (root, lang) => `${root}/i18n-store/store-${lang}.json`
|
||||
export const I18N_SOURCE_LANGUAGE = 'en'
|
||||
@ -63,7 +63,7 @@ export const TASK_LOG_DIRECTORY = './logs/'
|
||||
|
||||
// Task - Retail Config
|
||||
export const REGISTER_PRODUCT_TASKS = true
|
||||
export const RETAIL_PRODUCT_BRANCH = 'site'
|
||||
export const LIBARY_BRANCH = 'site-prod'
|
||||
export const PROJECTS_BRANCH = 'projects'
|
||||
export const RETAIL_COMPILE_CACHE = false
|
||||
export const RETAIL_MEDIA_CACHE = true
|
||||
@ -95,9 +95,11 @@ export const CAD_URL = (file: string, variables: Record<string, string>) =>
|
||||
export const ASSET_URL = (file: string, variables: Record<string, string>) =>
|
||||
sanitizeUri(template("${OSR_MACHINES_ASSETS_URL}/products/${product_rel_min}/${file}", { file, ...variables }))
|
||||
|
||||
export const ITEM_ASSET_URL = (variables: Record<string, string>) =>
|
||||
export const ITEM_ASSET_URL_R = (variables: Record<string, string>) =>
|
||||
template("${OSR_MACHINES_ASSETS_URL}/${ITEM_REL}/${assetPath}/${filePath}", variables)
|
||||
|
||||
export const ITEM_ASSET_URL = (variables: Record<string, string>) =>
|
||||
template("http://${FILE_SERVER_DEV}/${ITEM_REL}/${assetPath}/${filePath}", variables)
|
||||
|
||||
//back compat - osr-cad
|
||||
export const parseBoolean = (value: string): boolean => {
|
||||
@ -122,7 +124,8 @@ export const SHOW_DEBUG = false
|
||||
export const SHOW_SAMPLES = true
|
||||
export const SHOW_README = false
|
||||
export const SHOW_RELATED = true
|
||||
|
||||
export const SHOW_SHOWCASE = true
|
||||
export const SHOW_SCREENSHOTS = true
|
||||
|
||||
/////////////////////////////////////////////
|
||||
//
|
||||
|
||||
@ -6,27 +6,27 @@
|
||||
"CACHE": "${root}/cache/",
|
||||
"CACHE_URL": "${abs_url}/cache/",
|
||||
"GIT_REPO": "https://git.polymech.io/",
|
||||
"OSR_MACHINES_ASSETS_URL":"https://assets.osr-plastic.org",
|
||||
"PRODUCTS_ASSETS_URL":"https://assets.osr-plastic.org/${product_rel}",
|
||||
"OSR_FILES_WEB":"https://files.polymech.io/files/machines",
|
||||
"PRODUCTS_FILES_URL":"${OSR_FILES_WEB}/${product_rel}",
|
||||
"DISCORD":"https://discord.gg/s8K7yKwBRc"
|
||||
"OSR_MACHINES_ASSETS_URL": "https://assets.osr-plastic.org",
|
||||
"PRODUCTS_ASSETS_URL": "https://assets.osr-plastic.org/${product_rel}",
|
||||
"OSR_FILES_WEB": "https://files.polymech.info/files/machines",
|
||||
"PRODUCTS_FILES_URL": "${OSR_FILES_WEB}/${product_rel}",
|
||||
"DISCORD": "https://discord.gg/s8K7yKwBRc"
|
||||
},
|
||||
"env": {
|
||||
"astro-release":{
|
||||
"astro-release": {
|
||||
"includes": [
|
||||
"${PRODUCT_ROOT}"
|
||||
],
|
||||
"variables": {
|
||||
"OSR_MACHINES_ASSETS_URL":"https://assets.osr-plastic.org/"
|
||||
"OSR_MACHINES_ASSETS_URL": "https://assets.osr-plastic.org/"
|
||||
}
|
||||
},
|
||||
"astro-debug":{
|
||||
"astro-debug": {
|
||||
"includes": [
|
||||
"${PRODUCT_ROOT}"
|
||||
],
|
||||
"variables": {
|
||||
"OSR_MACHINES_ASSETS_URL":"https://assets.osr-plastic.org",
|
||||
"OSR_MACHINES_ASSETS_URL": "https://assets.osr-plastic.org",
|
||||
"showCart": false,
|
||||
"showPrice": false,
|
||||
"showResources": false,
|
||||
@ -37,7 +37,5 @@
|
||||
"debug": true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -122,6 +122,7 @@ export const createTemplates = (context: TemplateContext = TemplateContext.COMMO
|
||||
}), {});
|
||||
};
|
||||
export type { TemplateConfig, LLMConfig, Prompt, PromptRegistry }
|
||||
|
||||
export {
|
||||
InstructionSchema,
|
||||
InstructionSetSchema,
|
||||
|
||||
@ -70,11 +70,13 @@ export const filter = async (content: string, tpl: string = 'howto', opts: Props
|
||||
logger.info(`kbot-result: template:${tpl} : context:${context} @ ${options.model} : ${result[0]}`)
|
||||
return result[0] as string;
|
||||
};
|
||||
|
||||
export const template_filter = async (text: string, template: string, context: TemplateContext = TemplateContext.COMMONS) => {
|
||||
if (!text || text.length < 20) {
|
||||
return text;
|
||||
}
|
||||
const templates = createTemplates(context);
|
||||
debugger
|
||||
if (!templates[template]) {
|
||||
logger.warn(`No template found for ${template}`);
|
||||
return text;
|
||||
|
||||
@ -26,7 +26,7 @@ import {
|
||||
ASSETS_GLOB
|
||||
} from 'config/config.js'
|
||||
|
||||
export const default_sanitizer = (files:string[]) => files.map((f) => sanitizeFilename(f))
|
||||
export const default_sanitizer = (files: string[]) => files.map((f) => sanitizeFilename(f))
|
||||
|
||||
export const default_sort = (files: string[]): string[] => {
|
||||
const getSortableParts = (filename: string) => {
|
||||
@ -80,7 +80,7 @@ export const image_url = async (src, fallback = DEFAULT_IMAGE_URL) => {
|
||||
return safeSrc
|
||||
}
|
||||
|
||||
export const gallery = async ( assetPath, item): Promise<GalleryImage[]> => {
|
||||
export const gallery = async (assetPath, item): Promise<GalleryImage[]> => {
|
||||
|
||||
const root = resolve(PRODUCT_ROOT())
|
||||
const profile = env()
|
||||
@ -93,7 +93,6 @@ export const gallery = async ( assetPath, item): Promise<GalleryImage[]> => {
|
||||
}
|
||||
const mediaPath = `${root}/${item}/${assetPath}/`
|
||||
if (!exists(mediaPath)) {
|
||||
logger.warn(`item gallery : item ${item} media path not found ${mediaPath}!`)
|
||||
return []
|
||||
}
|
||||
const galleryGlob = (itemConfig.gallery || {})[assetSlug]?.glob || ASSETS_GLOB
|
||||
|
||||
@ -1,16 +1,15 @@
|
||||
---
|
||||
|
||||
import path from "path";
|
||||
import { decode } from "html-entities";
|
||||
import { IHowto, asset_local_rel } from "@/model/howto/howto.js";
|
||||
import { IHowto, IStep, asset_local_rel } from "@/model/howto/howto.js";
|
||||
import { translate } from "@polymech/astro-base/base/i18n.js";
|
||||
import Translate from "@polymech/astro-base/components/i18n.astro";
|
||||
import BaseLayout from "@/layouts/BaseLayout.astro";
|
||||
import Sidebar from "@polymech/astro-base/components/sidebar/Sidebar.astro"
|
||||
import MobileToggle from "@polymech/astro-base/components/sidebar/MobileToggle.astro"
|
||||
import Sidebar from "@polymech/astro-base/components/sidebar/Sidebar.astro";
|
||||
import MobileToggle from "@polymech/astro-base/components/sidebar/MobileToggle.astro";
|
||||
import Breadcrumb from "@polymech/astro-base/components/Breadcrumb.astro";
|
||||
import { getSidebarConfig } from '@polymech/astro-base/config/sidebar';
|
||||
import type { MarkdownHeading } from 'astro';
|
||||
import { getSidebarConfig } from "@polymech/astro-base/config/sidebar";
|
||||
import type { MarkdownHeading } from "astro";
|
||||
import Wrapper from "@/components/containers/Wrapper.astro";
|
||||
import GalleryK from "@polymech/astro-base/components/GalleryK.astro";
|
||||
import { files, forward_slash } from "@polymech/commons";
|
||||
@ -33,6 +32,7 @@ import {
|
||||
HOWTO_ADD_REFERENCES,
|
||||
HOWTO_EDIT_URL,
|
||||
} from "config/config.js";
|
||||
|
||||
import { filter } from "@/base/kbot.js";
|
||||
import { getCollection } from "astro:content";
|
||||
import { group_by_cat } from "@/model/howto/howto.js";
|
||||
@ -51,29 +51,27 @@ model_files = model_files.map((f) =>
|
||||
|
||||
const content = async (str: string) =>
|
||||
await translate(str, I18N_SOURCE_LANGUAGE, Astro.currentLocale);
|
||||
|
||||
const component = async (str: string) =>
|
||||
await createMarkdownComponent(await content(str));
|
||||
|
||||
const componentHTML = async (str: string) =>
|
||||
await createHTMLComponent(await content(str));
|
||||
|
||||
// Debug: Check if howto.steps exists
|
||||
console.log('howto.steps:', howto.steps);
|
||||
console.log('howto.steps length:', howto.steps?.length);
|
||||
|
||||
const stepsWithFilteredMarkdown = await pMap(
|
||||
howto.steps || [],
|
||||
async (step) => ({
|
||||
async (step: IStep) => ({
|
||||
...step,
|
||||
filteredMarkdownComponent: await component(step.text),
|
||||
}),
|
||||
{ concurrency: 1 },
|
||||
);
|
||||
|
||||
console.log('stepsWithFilteredMarkdown length:', stepsWithFilteredMarkdown.length);
|
||||
|
||||
// Fetch all howtos for category navigation
|
||||
const allHowtos = await getCollection("howtos");
|
||||
const allHowtoItems = allHowtos.map((storeItem) => storeItem.data.item) as IHowto[];
|
||||
const allHowtoItems = allHowtos.map(
|
||||
(storeItem) => storeItem.data.item,
|
||||
) as IHowto[];
|
||||
const howtosByCategory = group_by_cat(allHowtoItems);
|
||||
const categories = Object.keys(howtosByCategory).sort();
|
||||
|
||||
@ -81,32 +79,42 @@ const categories = Object.keys(howtosByCategory).sort();
|
||||
const organizedCategories: any[] = [];
|
||||
|
||||
// Separate and organize categories
|
||||
const uncategorizedItems = howtosByCategory['Uncategorized'] || howtosByCategory['uncategorized'] || [];
|
||||
const categorizedItems = categories.filter(cat =>
|
||||
cat.toLowerCase() !== 'uncategorized' && cat !== 'Uncategorized'
|
||||
).sort();
|
||||
const uncategorizedItems =
|
||||
howtosByCategory["Uncategorized"] || howtosByCategory["uncategorized"] || [];
|
||||
const categorizedItems = categories
|
||||
.filter(
|
||||
(cat) => cat.toLowerCase() !== "uncategorized" && cat !== "Uncategorized",
|
||||
)
|
||||
.sort();
|
||||
|
||||
// Create dynamic category structure from actual data
|
||||
if (categorizedItems.length > 0) {
|
||||
organizedCategories.push({
|
||||
label: 'Browse by Category',
|
||||
label: "Browse by Category",
|
||||
collapsed: false,
|
||||
items: categorizedItems.map(category => ({
|
||||
items: categorizedItems.map((category) => ({
|
||||
label: `${category} (${howtosByCategory[category].length})`,
|
||||
collapsed: !(category === howto.category?.label), // Expand current category
|
||||
isSubGroup: true, // This makes it a collapsible subgroup
|
||||
items: howtosByCategory[category].slice(0, 8).map((categoryHowto: IHowto) => ({
|
||||
label: categoryHowto.title,
|
||||
href: `/${Astro.currentLocale}/howtos/${categoryHowto.slug}`,
|
||||
isCurrent: categoryHowto.slug === howto.slug
|
||||
})).concat(
|
||||
howtosByCategory[category].length > 8 ? [{
|
||||
label: `View all ${howtosByCategory[category].length}...`,
|
||||
href: `/${Astro.currentLocale}/howtos/category/${category.toLowerCase().replace(/\s+/g, '-')}`,
|
||||
isCurrent: false
|
||||
}] : []
|
||||
)
|
||||
}))
|
||||
items: howtosByCategory[category]
|
||||
.slice(0, 8)
|
||||
.map((categoryHowto: IHowto) => ({
|
||||
label: categoryHowto.title,
|
||||
href: `/${Astro.currentLocale}/howtos/${categoryHowto.slug}`,
|
||||
isCurrent: categoryHowto.slug === howto.slug,
|
||||
}))
|
||||
.concat(
|
||||
howtosByCategory[category].length > 8
|
||||
? [
|
||||
{
|
||||
label: `View all ${howtosByCategory[category].length}...`,
|
||||
href: `/${Astro.currentLocale}/howtos/category/${category.toLowerCase().replace(/\s+/g, "-")}`,
|
||||
isCurrent: false,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
),
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
@ -115,41 +123,47 @@ if (uncategorizedItems.length > 0) {
|
||||
organizedCategories.push({
|
||||
label: `Uncategorized (${uncategorizedItems.length})`,
|
||||
collapsed: true,
|
||||
items: uncategorizedItems.slice(0, 10).map((uncatHowto: IHowto) => ({
|
||||
label: uncatHowto.title,
|
||||
href: `/${Astro.currentLocale}/howtos/${uncatHowto.slug}`,
|
||||
isCurrent: uncatHowto.slug === howto.slug
|
||||
})).concat(
|
||||
uncategorizedItems.length > 10 ? [{
|
||||
label: `View all ${uncategorizedItems.length} guides...`,
|
||||
href: `/${Astro.currentLocale}/howtos/uncategorized`,
|
||||
isCurrent: false
|
||||
}] : []
|
||||
)
|
||||
items: uncategorizedItems
|
||||
.slice(0, 10)
|
||||
.map((uncatHowto: IHowto) => ({
|
||||
label: uncatHowto.title,
|
||||
href: `/${Astro.currentLocale}/howtos/${uncatHowto.slug}`,
|
||||
isCurrent: uncatHowto.slug === howto.slug,
|
||||
}))
|
||||
.concat(
|
||||
uncategorizedItems.length > 10
|
||||
? [
|
||||
{
|
||||
label: `View all ${uncategorizedItems.length} guides...`,
|
||||
href: `/${Astro.currentLocale}/howtos/uncategorized`,
|
||||
isCurrent: false,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
// Add quick navigation
|
||||
organizedCategories.unshift({
|
||||
label: 'Quick Navigation',
|
||||
label: "Quick Navigation",
|
||||
collapsed: false,
|
||||
items: [
|
||||
{
|
||||
label: 'All Guides',
|
||||
label: "All Guides",
|
||||
href: `/${Astro.currentLocale}/howtos`,
|
||||
isCurrent: false
|
||||
isCurrent: false,
|
||||
},
|
||||
{
|
||||
label: 'Recently Added',
|
||||
label: "Recently Added",
|
||||
href: `/${Astro.currentLocale}/howtos/recent`,
|
||||
isCurrent: false
|
||||
}
|
||||
]
|
||||
isCurrent: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const pageNavigation = organizedCategories;
|
||||
|
||||
|
||||
// Function to extract headings from markdown content
|
||||
const extractHeadingsFromMarkdown = (markdown: string): MarkdownHeading[] => {
|
||||
if (!markdown) return [];
|
||||
@ -161,9 +175,10 @@ const extractHeadingsFromMarkdown = (markdown: string): MarkdownHeading[] => {
|
||||
while ((match = headingRegex.exec(markdown)) !== null) {
|
||||
const depth = match[1].length as 1 | 2 | 3 | 4 | 5 | 6;
|
||||
const text = match[2].trim();
|
||||
const slug = text.toLowerCase()
|
||||
.replace(/[^\w\s-]/g, '') // Remove special characters
|
||||
.replace(/\s+/g, '-') // Replace spaces with hyphens
|
||||
const slug = text
|
||||
.toLowerCase()
|
||||
.replace(/[^\w\s-]/g, "") // Remove special characters
|
||||
.replace(/\s+/g, "-") // Replace spaces with hyphens
|
||||
.trim();
|
||||
|
||||
headings.push({ depth, slug, text });
|
||||
@ -222,8 +237,8 @@ const dynamicHeadings: MarkdownHeading[] = [];
|
||||
// Main title
|
||||
dynamicHeadings.push({
|
||||
depth: 1,
|
||||
slug: 'title',
|
||||
text: howto.title
|
||||
slug: "title",
|
||||
text: howto.title,
|
||||
});
|
||||
|
||||
// Extract headings from description if it contains markdown headings
|
||||
@ -233,12 +248,17 @@ if (howto.description) {
|
||||
// No headings in description, add it as a section
|
||||
dynamicHeadings.push({
|
||||
depth: 2,
|
||||
slug: 'description',
|
||||
text: 'Description'
|
||||
slug: "description",
|
||||
text: "Description",
|
||||
});
|
||||
} else {
|
||||
// Add description headings
|
||||
dynamicHeadings.push(...descriptionHeadings.map(h => ({ ...h, depth: Math.max(2, h.depth) as any })));
|
||||
dynamicHeadings.push(
|
||||
...descriptionHeadings.map((h) => ({
|
||||
...h,
|
||||
depth: Math.max(2, h.depth) as any,
|
||||
})),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,8 +267,8 @@ if (stepsWithFilteredMarkdown.length > 0) {
|
||||
// Steps section header
|
||||
dynamicHeadings.push({
|
||||
depth: 2,
|
||||
slug: 'steps',
|
||||
text: 'Steps'
|
||||
slug: "steps",
|
||||
text: "Steps",
|
||||
});
|
||||
|
||||
// Individual steps with extracted headings from their content
|
||||
@ -257,16 +277,16 @@ if (stepsWithFilteredMarkdown.length > 0) {
|
||||
dynamicHeadings.push({
|
||||
depth: 3,
|
||||
slug: `step-${idx + 1}`,
|
||||
text: `Step ${idx + 1}: ${step.title}`
|
||||
text: `Step ${idx + 1}: ${step.title}`,
|
||||
});
|
||||
|
||||
// Extract any headings from the step content
|
||||
const stepHeadings = extractHeadingsFromMarkdown(step.text);
|
||||
stepHeadings.forEach(heading => {
|
||||
stepHeadings.forEach((heading) => {
|
||||
dynamicHeadings.push({
|
||||
...heading,
|
||||
depth: Math.max(4, heading.depth) as any, // Make sure step content headings are at least depth 4
|
||||
slug: `step-${idx + 1}-${heading.slug}`
|
||||
slug: `step-${idx + 1}-${heading.slug}`,
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -277,11 +297,16 @@ const resourcesHeadings = extractHeadingsFromMarkdown(howto_resources);
|
||||
if (resourcesHeadings.length === 0) {
|
||||
dynamicHeadings.push({
|
||||
depth: 2,
|
||||
slug: 'resources',
|
||||
text: 'Resources'
|
||||
slug: "resources",
|
||||
text: "Resources",
|
||||
});
|
||||
} else {
|
||||
dynamicHeadings.push(...resourcesHeadings.map(h => ({ ...h, depth: Math.max(2, h.depth) as any })));
|
||||
dynamicHeadings.push(
|
||||
...resourcesHeadings.map((h) => ({
|
||||
...h,
|
||||
depth: Math.max(2, h.depth) as any,
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
// Add references section (check if references content has headings)
|
||||
@ -289,18 +314,23 @@ const referencesHeadings = extractHeadingsFromMarkdown(howto_references);
|
||||
if (referencesHeadings.length === 0) {
|
||||
dynamicHeadings.push({
|
||||
depth: 2,
|
||||
slug: 'references',
|
||||
text: 'References'
|
||||
slug: "references",
|
||||
text: "References",
|
||||
});
|
||||
} else {
|
||||
dynamicHeadings.push(...referencesHeadings.map(h => ({ ...h, depth: Math.max(2, h.depth) as any })));
|
||||
dynamicHeadings.push(
|
||||
...referencesHeadings.map((h) => ({
|
||||
...h,
|
||||
depth: Math.max(2, h.depth) as any,
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
// Add metadata section
|
||||
dynamicHeadings.push({
|
||||
depth: 2,
|
||||
slug: 'metadata',
|
||||
text: 'Metadata'
|
||||
slug: "metadata",
|
||||
text: "Metadata",
|
||||
});
|
||||
|
||||
const headings = dynamicHeadings;
|
||||
@ -312,7 +342,6 @@ const EditLink = () => {
|
||||
)
|
||||
}
|
||||
*/
|
||||
|
||||
---
|
||||
|
||||
<BaseLayout class="markdown-content bg-gray-100" frontmatter={howto}>
|
||||
@ -341,170 +370,196 @@ const EditLink = () => {
|
||||
/>
|
||||
|
||||
<Wrapper>
|
||||
<article class="bg-white shadow-lg rounded-lg overflow-hidden">
|
||||
<header class="p-4 ">
|
||||
<h1 id="title" class="text-4xl font-bold text-gray-800 mb-4">
|
||||
<Translate>{howto.title}</Translate>
|
||||
</h1>
|
||||
<GalleryK images={[{ src: howto.cover_image.src, alt: "" }]} />
|
||||
<div class="flex flex-wrap gap-2 mb-4">
|
||||
{
|
||||
howto.tags.map((tag) => (
|
||||
<span class="bg-orange-400 text-white text-xs px-3 py-1 rounded-full">
|
||||
<Translate>{tag.toUpperCase()}</Translate>
|
||||
</span>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</header>
|
||||
</article>
|
||||
<article class="bg-white shadow-lg rounded-lg overflow-hidden">
|
||||
<header class="p-4">
|
||||
<h1 id="title" class="text-4xl font-bold text-gray-800 mb-4">
|
||||
<Translate>{howto.title}</Translate>
|
||||
</h1>
|
||||
<GalleryK images={[{ src: howto.cover_image.src, alt: "" }]} />
|
||||
<div class="flex flex-wrap gap-2 mb-4">
|
||||
{
|
||||
howto.tags.map((tag) => (
|
||||
<span class="bg-orange-400 text-white text-xs px-3 py-1 rounded-full">
|
||||
<Translate>{tag.toUpperCase()}</Translate>
|
||||
</span>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</header>
|
||||
</article>
|
||||
|
||||
<section class="meta-view bg-white rounded-lg p-4 mt-4 truncate">
|
||||
<ul class="grid md:grid-cols-1 lg:grid-cols-2 gap-4 mt-8 mb-8">
|
||||
<li>
|
||||
<strong><Translate>Difficulty:</Translate></strong>
|
||||
<Translate>{howto.difficulty_level}</Translate>
|
||||
</li>
|
||||
<li>
|
||||
<strong><Translate>Time Required:</Translate></strong>
|
||||
<Translate>{decode(howto.time)}</Translate>
|
||||
</li>
|
||||
<li>
|
||||
<strong><Translate>Views:</Translate></strong>{howto.total_views}
|
||||
</li>
|
||||
<li>
|
||||
<strong><Translate>Creator:</Translate></strong>{howto._createdBy}
|
||||
</li>
|
||||
<li>
|
||||
<strong><Translate>Country:</Translate></strong>{authorGeo.countryName }
|
||||
</li>
|
||||
<li>
|
||||
<strong><Translate>Email:</Translate></strong>
|
||||
<a
|
||||
class="text-orange-600 underline"
|
||||
href={`mailto:${authorLinks.find((link) => link.name.toLowerCase() === "email")?.url.replace("mailto:", "")}`}
|
||||
>
|
||||
{
|
||||
authorLinks
|
||||
.find((link) => link.name.toLowerCase() === "email")
|
||||
?.url.replace("mailto:", "")
|
||||
}
|
||||
</a>
|
||||
</li>
|
||||
{
|
||||
authorLinks
|
||||
.filter((l) => l.name.toLowerCase() !== "email")
|
||||
.map((link) => (
|
||||
<section class="meta-view bg-white rounded-lg p-4 mt-4 truncate">
|
||||
<ul class="grid md:grid-cols-1 lg:grid-cols-2 gap-4 mt-8 mb-8">
|
||||
<li>
|
||||
<strong>{link.name}:</strong>
|
||||
<strong><Translate>Difficulty:</Translate></strong>
|
||||
<Translate>{howto.difficulty_level}</Translate>
|
||||
</li>
|
||||
<li>
|
||||
<strong><Translate>Time Required:</Translate></strong>
|
||||
<Translate>{decode(howto.time)}</Translate>
|
||||
</li>
|
||||
<li>
|
||||
<strong><Translate>Views:</Translate></strong>{
|
||||
howto.total_views
|
||||
}
|
||||
</li>
|
||||
<li>
|
||||
<strong><Translate>Creator:</Translate></strong>{
|
||||
howto._createdBy
|
||||
}
|
||||
</li>
|
||||
<li>
|
||||
<strong><Translate>Country:</Translate></strong>{
|
||||
authorGeo.countryName
|
||||
}
|
||||
</li>
|
||||
<li>
|
||||
<strong><Translate>Email:</Translate></strong>
|
||||
<a
|
||||
class="text-orange-600 underline"
|
||||
href={link.url}
|
||||
target="_blank"
|
||||
href={`mailto:${authorLinks.find((link) => link.name.toLowerCase() === "email")?.url.replace("mailto:", "")}`}
|
||||
>
|
||||
{shortenUrl(link.url)}
|
||||
{
|
||||
authorLinks
|
||||
.find((link) => link.name.toLowerCase() === "email")
|
||||
?.url.replace("mailto:", "")
|
||||
}
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
<li>
|
||||
<strong><Translate>Downloads:</Translate></strong>{
|
||||
howto.total_downloads
|
||||
}
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section id="description" class="bg-white p-8">
|
||||
<div class="mb-8 markdown-content">
|
||||
<Description />
|
||||
</div>
|
||||
<a
|
||||
href={HOWTO_FILES_WEB(howto.slug)}
|
||||
class="inline-block py-2 px-4 bg-orange-500 hover:bg-orange-700 text-white rounded-full mb-8"
|
||||
><Translate>Browse Files</Translate></a
|
||||
>
|
||||
</section>
|
||||
|
||||
<section id="table-of-contents" class="px-8 py-8 bg-orange-50">
|
||||
<h2 class="font-bold mb-4 text-xl">
|
||||
<Translate>Table of Contents</Translate>
|
||||
</h2>
|
||||
{stepsWithFilteredMarkdown && stepsWithFilteredMarkdown.length > 0 ? (
|
||||
<ul class="grid grid-cols-1 md:grid-cols-2 gap-2 list-decimal p-4">
|
||||
{
|
||||
stepsWithFilteredMarkdown.map((step, idx) => (
|
||||
{
|
||||
authorLinks
|
||||
.filter((l) => l.name.toLowerCase() !== "email")
|
||||
.map((link) => (
|
||||
<li>
|
||||
<strong>{link.name}:</strong>
|
||||
<a
|
||||
class="text-orange-600 underline"
|
||||
href={link.url}
|
||||
target="_blank"
|
||||
>
|
||||
{shortenUrl(link.url)}
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
<li>
|
||||
<a
|
||||
href={`#step-${idx + 1}`}
|
||||
class="text-orange-600 hover:underline"
|
||||
>
|
||||
<Translate>{step.title}</Translate>
|
||||
</a>
|
||||
<strong><Translate>Downloads:</Translate></strong>{
|
||||
howto.total_downloads
|
||||
}
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
) : (
|
||||
<div class="p-4 text-gray-600">
|
||||
<p>No steps available for this how-to guide.</p>
|
||||
<p>Debug: Steps length = {stepsWithFilteredMarkdown?.length || 0}</p>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section id="steps" class="border-gray-300 p-0 lg:p-6 mt-8">
|
||||
{stepsWithFilteredMarkdown && stepsWithFilteredMarkdown.length > 0 ? (
|
||||
<ol class="space-y-10">
|
||||
{
|
||||
stepsWithFilteredMarkdown.map((step, idx) => (
|
||||
<li
|
||||
id={`step-${idx + 1}`}
|
||||
class="bg-white shadow-sm rounded-lg p-2 lg:p-6"
|
||||
>
|
||||
<div class="mb-4 flex items-center">
|
||||
<span class="bg-orange-500 text-xl font-bold text-white rounded-full h-10 w-10 flex items-center justify-center mr-3">
|
||||
{idx + 1}
|
||||
</span>
|
||||
<h3 class="text-xl font-bold">
|
||||
<a
|
||||
href={`#step-${idx + 1}`}
|
||||
class="text-orange-600 hover:underline"
|
||||
<section id="description" class="bg-white p-8">
|
||||
<div class="mb-8 markdown-content">
|
||||
<Description />
|
||||
</div>
|
||||
<a
|
||||
href={HOWTO_FILES_WEB(howto.slug)}
|
||||
class="inline-block py-2 px-4 bg-orange-500 hover:bg-orange-700 text-white rounded-full mb-8"
|
||||
><Translate>Browse Files</Translate></a
|
||||
>
|
||||
</section>
|
||||
|
||||
<section id="table-of-contents" class="px-8 py-8 bg-orange-50">
|
||||
<h2 class="font-bold mb-4 text-xl">
|
||||
<Translate>Table of Contents</Translate>
|
||||
</h2>
|
||||
{
|
||||
stepsWithFilteredMarkdown &&
|
||||
stepsWithFilteredMarkdown.length > 0 ? (
|
||||
<ul class="grid grid-cols-1 md:grid-cols-2 gap-2 list-decimal p-4">
|
||||
{stepsWithFilteredMarkdown.map((step, idx) => (
|
||||
<li>
|
||||
<a
|
||||
href={`#step-${idx + 1}`}
|
||||
class="text-orange-600 hover:underline"
|
||||
>
|
||||
<Translate>{step.title}</Translate>
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<div class="p-4 text-gray-600">
|
||||
<p>No steps available for this how-to guide.</p>
|
||||
<p>
|
||||
Debug: Steps length ={" "}
|
||||
{stepsWithFilteredMarkdown?.length || 0}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</section>
|
||||
|
||||
<section id="steps" class="border-gray-300 p-0 lg:p-6 mt-8">
|
||||
{
|
||||
stepsWithFilteredMarkdown &&
|
||||
stepsWithFilteredMarkdown.length > 0 ? (
|
||||
<ol class="space-y-10">
|
||||
{stepsWithFilteredMarkdown.map((step, idx) => (
|
||||
<li
|
||||
id={`step-${idx + 1}`}
|
||||
class="bg-white shadow-sm rounded-lg p-2 lg:p-6"
|
||||
>
|
||||
<Translate>{step.title}</Translate>
|
||||
</a>
|
||||
</h3>
|
||||
<div class="mb-4 flex items-center">
|
||||
<span class="bg-orange-500 text-xl font-bold text-white rounded-full h-10 w-10 flex items-center justify-center mr-3">
|
||||
{idx + 1}
|
||||
</span>
|
||||
<h3 class="text-xl font-bold">
|
||||
<a
|
||||
href={`#step-${idx + 1}`}
|
||||
class="text-orange-600 hover:underline"
|
||||
>
|
||||
<Translate>{step.title}</Translate>
|
||||
</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="markdown-content">
|
||||
<step.filteredMarkdownComponent />
|
||||
</div>
|
||||
{step.images?.length > 0 && (
|
||||
<GalleryK images={step.images} />
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
) : (
|
||||
<div class="p-8 text-center text-gray-600">
|
||||
<p>No detailed steps are available for this how-to guide.</p>
|
||||
</div>
|
||||
<div class="markdown-content">
|
||||
<step.filteredMarkdownComponent />
|
||||
</div>
|
||||
{step.images?.length > 0 && <GalleryK images={step.images} />}
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ol>
|
||||
) : (
|
||||
<div class="p-8 text-center text-gray-600">
|
||||
<p>No detailed steps are available for this how-to guide.</p>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
</section>
|
||||
|
||||
<section id="resources" class="bg-white shadow-lg rounded-lg border-gray-300 p-4 lg:p-6 mt-8 markdown-content"><Resources /></section>
|
||||
<section id="references" class="bg-white shadow-lg rounded-lg border-gray-300 p-4 lg:p-6 mt-8 markdown-content"><References /></section>
|
||||
<footer id="metadata" class="p-8 text-sm border-t bg-white text-gray-600">
|
||||
<div class="flex justify-between">
|
||||
<span
|
||||
><Translate>Created on</Translate>: {
|
||||
new Date(howto._created).toLocaleDateString()
|
||||
}</span
|
||||
>
|
||||
<span
|
||||
>{howto.votedUsefulBy.length}
|
||||
<Translate>people found this useful</Translate></span
|
||||
>
|
||||
</div>
|
||||
</footer>
|
||||
<section
|
||||
id="resources"
|
||||
class="bg-white shadow-lg rounded-lg border-gray-300 p-4 lg:p-6 mt-8 markdown-content"
|
||||
>
|
||||
<Resources />
|
||||
</section>
|
||||
<section
|
||||
id="references"
|
||||
class="bg-white shadow-lg rounded-lg border-gray-300 p-4 lg:p-6 mt-8 markdown-content"
|
||||
>
|
||||
<References />
|
||||
</section>
|
||||
<footer
|
||||
id="metadata"
|
||||
class="p-8 text-sm border-t bg-white text-gray-600"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<span
|
||||
><Translate>Created on</Translate>: {
|
||||
new Date(howto._created).toLocaleDateString()
|
||||
}</span
|
||||
>
|
||||
<span
|
||||
>{howto.votedUsefulBy.length}
|
||||
<Translate>people found this useful</Translate></span
|
||||
>
|
||||
</div>
|
||||
</footer>
|
||||
</Wrapper>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@ -64,6 +64,7 @@ const cleanTime = decodedTime.replace(/</g, '<').replace(/>/g, '>').replac
|
||||
|
||||
// The URL prop already points to the correct howto page
|
||||
const cardUrl = url;
|
||||
|
||||
---
|
||||
|
||||
<article class="group relative bg-white rounded-2xl overflow-hidden shadow-sm hover:shadow-md transition-all duration-300 border border-gray-100 flex flex-col h-full">
|
||||
|
||||
@ -1,38 +1,47 @@
|
||||
---
|
||||
import { default_image } from "config/config.js";
|
||||
import Translate from "@/components/polymech/i18n.astro";
|
||||
import { Img } from "imagetools/components"
|
||||
const { title, url, price, model, selected = false } = Astro.props;
|
||||
const thumbnail = model?.assets?.renderings[0]?.src || default_image();
|
||||
const classes = `group relative bg-white overflow-hidden group rounded-xl ${selected ? "ring-2 ring-orange-500" : ""}`;
|
||||
import { default_image } from "@/app/config.js";
|
||||
const { title, url, price, model } = Astro.props;
|
||||
const thumbnail =
|
||||
model?.assets?.main_image?.url ||
|
||||
model?.assets?.gallery[0]?.url ||
|
||||
default_image();
|
||||
|
||||
const hash = model?.assets?.main_image?.hash || model?.assets?.gallery[0]?.hash;
|
||||
|
||||
import Img from "@polymech/astro-base/components/polymech/image.astro";
|
||||
---
|
||||
|
||||
<div class={classes}>
|
||||
<div
|
||||
class="p-4 overflow-hidden group-hover:opacity-75 duration-300 transition-all"
|
||||
>
|
||||
<a href={url} title={title} aria-label={title}>
|
||||
<Img
|
||||
src={thumbnail}
|
||||
alt={title}
|
||||
format="avif"
|
||||
objectFit="cover"
|
||||
placeholder="blurred"
|
||||
sizes="(min-width: 220px) 220px"
|
||||
breakpoints={{ count: 2, minWidth: 120, maxWidth: 430 }}
|
||||
/>
|
||||
<div
|
||||
class="group relative overflow-hidden rounded-xl shadow-sm hover:shadow-md transition-shadow duration-300"
|
||||
>
|
||||
<div class="aspect-square overflow-hidden">
|
||||
<a href={url} title={title} aria-label={title} class="block w-full h-full">
|
||||
<div class="w-full h-full flex items-center justify-center p-0 md:p-1">
|
||||
<Img
|
||||
src={thumbnail}
|
||||
s={hash}
|
||||
alt={title}
|
||||
format="avif"
|
||||
objectFit="contain"
|
||||
placeholder="blurred"
|
||||
sizes="(min-width: 220px) 220px"
|
||||
breakpoints={{ count: 2, minWidth: 120, maxWidth: 430 }}
|
||||
class="max-w-full max-h-full object-contain group-hover:scale-105 transition-transform duration-300"
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="p-4 text-xs text-neutral-500">
|
||||
<div class="flex items-center justify-between space-x-8">
|
||||
<h3>
|
||||
<a href={url} title={title} aria-label={title}>
|
||||
<span aria-hidden="true" class="absolute inset-0"></span>
|
||||
<Translate>{title}</Translate>
|
||||
</a>
|
||||
</h3>
|
||||
<p class="top-4 right-4"></p>
|
||||
<p class="mt-1"></p>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<h3 class="text-sm font-medium line-clamp-2">
|
||||
<a
|
||||
href={url}
|
||||
title={title}
|
||||
aria-label={title}
|
||||
class="hover:text-neutral-900 transition-colors"
|
||||
>
|
||||
<span aria-hidden="true" class="absolute inset-0"></span>
|
||||
{title}
|
||||
</a>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -14,7 +14,7 @@ export const LOGGING_NAMESPACE = 'polymech-site'
|
||||
export const TRANSLATE_CONTENT = true
|
||||
export const LANGUAGES = ['en', 'es']
|
||||
export const LANGUAGES_SITE = ['en', 'ar', 'de', 'ja', 'es', 'zh', 'fr']
|
||||
export const LANGUAGES_PROD = ['en','es','fr','it','de']
|
||||
export const LANGUAGES_PROD = ['en', 'es', 'fr', 'it', 'de']
|
||||
export const isRTL = (lang) => lang === 'ar'
|
||||
|
||||
// i18n constants
|
||||
@ -25,9 +25,10 @@ export const I18N_ASSET_PATH = "${SRC_DIR}/${SRC_NAME}-${DST_LANG}${SRC_EXT}"
|
||||
|
||||
// Library - Howtos
|
||||
export const HOWTO_GLOB = '**/config.json'
|
||||
export const FILES_WEB = 'https://files.polymech.io/files/machines/howtos/'
|
||||
export const FILES_WEB = 'https://files.polymech.info/files/machines/howtos/'
|
||||
export const HOWTO_EDIT_ROOT = 'https://git.polymech.io/osr-plastic/osr-machines/src/branch/master/howtos'
|
||||
export const HOWTO_FILTER_LLM = false
|
||||
export const HOWTO_LLM_KEYWORDS = false
|
||||
export const HOWTO_ANNOTATIONS = false
|
||||
export const HOWTO_ANNOTATIONS_CACHE = false
|
||||
export const HOWTO_COMPLETE_RESOURCES = false
|
||||
@ -37,7 +38,7 @@ export const HOWTO_ADD_REFERENCES = false
|
||||
export const HOWTO_COMPLETE_SKILLS = false
|
||||
export const HOWTO_LOCAL_RESOURCES = false
|
||||
export const HOWTO_SEO_LLM = false
|
||||
export const HOWTO_MAX_ITEMS = 100
|
||||
export const HOWTO_MAX_ITEMS = 1000
|
||||
|
||||
export const HOWTO_MIGRATION = () => path.resolve(resolve("./data/last.json"))
|
||||
export const HOWTO_ROOT_INTERN = () => path.resolve(resolve("./public/resources/howtos"))
|
||||
@ -48,8 +49,8 @@ export const HOWTO_EDIT_URL = (id: string, lang: string) => `${HOWTO_EDIT_ROOT}/
|
||||
|
||||
// Library - Directory
|
||||
export const DIRECTORY_GLOB = '**/config.json'
|
||||
export const DIRECTORY_FILES_BASE = 'https://files.polymech.io/files/directory/'
|
||||
export const DIRECTORY_EDIT_ROOT = 'https://git.polymech.io/osr-plastic/osr-machines/src/branch/master/directory'
|
||||
export const DIRECTORY_FILES_BASE = 'https://files.polymech.info/files/directory/'
|
||||
export const DIRECTORY_EDIT_ROOT = 'https://git.polymech.info/polymech/machines/src/branch/master/directory'
|
||||
export const DIRECTORY_FILTER_LLM = true
|
||||
export const DIRECTORY_ANNOTATIONS = false
|
||||
export const DIRECTORY_ANNOTATIONS_CACHE = false
|
||||
@ -118,8 +119,8 @@ export const TASK_LOG_DIRECTORY = './logs/'
|
||||
|
||||
// Task - Retail Config
|
||||
export const REGISTER_PRODUCT_TASKS = true
|
||||
export const RETAIL_PRODUCT_BRANCH = 'site'
|
||||
export const PROJECTS_BRANCH = 'projects'
|
||||
export const LIBARY_BRANCH = 'site-prod'
|
||||
export const PROJECTS_BRANCH = 'projects2'
|
||||
export const RETAIL_COMPILE_CACHE = false
|
||||
export const RETAIL_MEDIA_CACHE = true
|
||||
export const RETAIL_LOG_LEVEL_I18N_PRODUCT_ASSETS = 'info'
|
||||
@ -135,8 +136,7 @@ export const CAD_CAM_MAIN_MATCH = (product) => `${product}/cad*/*-CNC*.+(SLDASM)
|
||||
export const CAD_CACHE = true
|
||||
export const CAD_EXPORT_CONFIGURATIONS = false
|
||||
export const CAD_EXPORT_SUB_COMPONENTS = true
|
||||
export const CAD_MODEL_FILE_PATH = (SOURCE, CONFIGURATION = '') =>
|
||||
SOURCE.replace('.json', `${CONFIGURATION ? '-' + CONFIGURATION : ''}.tree.json`)
|
||||
export const CAD_MODEL_FILE_PATH = (SOURCE, CONFIGURATION = '') => SOURCE.replace('.json', `${CONFIGURATION ? '-' + CONFIGURATION : ''}.tree.json`)
|
||||
export const CAD_DEFAULT_CONFIGURATION = 'Default'
|
||||
export const CAD_RENDERER = 'solidworks'
|
||||
export const CAD_RENDERER_VIEW = 'Render'
|
||||
@ -155,9 +155,7 @@ export const ITEM_ASSET_URL = (variables: Record<string, string>) =>
|
||||
|
||||
|
||||
//back compat - osr-cad
|
||||
export const parseBoolean = (value: string): boolean => {
|
||||
return value === '1' || value.toLowerCase() === 'true';
|
||||
}
|
||||
export const parseBoolean = (value: string): boolean => { return value === '1' || value.toLowerCase() === 'true'; }
|
||||
/////////////////////////////////////////////
|
||||
//
|
||||
// Rendering - Store
|
||||
|
||||
@ -6,27 +6,27 @@
|
||||
"CACHE": "${root}/cache/",
|
||||
"CACHE_URL": "${abs_url}/cache/",
|
||||
"GIT_REPO": "https://git.polymech.io/",
|
||||
"OSR_MACHINES_ASSETS_URL":"https://assets.osr-plastic.org",
|
||||
"PRODUCTS_ASSETS_URL":"https://assets.osr-plastic.org/${product_rel}",
|
||||
"OSR_FILES_WEB":"https://files.polymech.io/files/machines",
|
||||
"PRODUCTS_FILES_URL":"${OSR_FILES_WEB}/${product_rel}",
|
||||
"DISCORD":"https://discord.gg/s8K7yKwBRc"
|
||||
"OSR_MACHINES_ASSETS_URL": "https://assets.osr-plastic.org",
|
||||
"PRODUCTS_ASSETS_URL": "https://assets.osr-plastic.org/${product_rel}",
|
||||
"OSR_FILES_WEB": "https://files.polymech.info/files/machines",
|
||||
"PRODUCTS_FILES_URL": "${OSR_FILES_WEB}/${product_rel}",
|
||||
"DISCORD": "https://discord.gg/s8K7yKwBRc"
|
||||
},
|
||||
"env": {
|
||||
"astro-release":{
|
||||
"astro-release": {
|
||||
"includes": [
|
||||
"${PRODUCT_ROOT}"
|
||||
],
|
||||
"variables": {
|
||||
"OSR_MACHINES_ASSETS_URL":"https://assets.osr-plastic.org/"
|
||||
"OSR_MACHINES_ASSETS_URL": "https://assets.osr-plastic.org/"
|
||||
}
|
||||
},
|
||||
"astro-debug":{
|
||||
"astro-debug": {
|
||||
"includes": [
|
||||
"${PRODUCT_ROOT}"
|
||||
],
|
||||
"variables": {
|
||||
"OSR_MACHINES_ASSETS_URL":"https://assets.osr-plastic.org",
|
||||
"OSR_MACHINES_ASSETS_URL": "https://assets.osr-plastic.org",
|
||||
"showCart": false,
|
||||
"showPrice": false,
|
||||
"showResources": false,
|
||||
@ -37,7 +37,5 @@
|
||||
"debug": true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,44 +1,53 @@
|
||||
import { glob } from 'astro/loaders'
|
||||
import { defineCollection, z } from "astro:content"
|
||||
import { ComponentConfigSchema } from '@polymech/commons/component'
|
||||
import { loader } from './model/component/component.js'
|
||||
import { loader as howtoLoader } from './model/howto/howto.js'
|
||||
import { loader as directorLoader } from './model/directory/item.js'
|
||||
import { RETAIL_PRODUCT_BRANCH, PROJECTS_BRANCH } from 'config/config.js'
|
||||
import { loader } from '@polymech/astro-base/model/component.js'
|
||||
import { loader as howtoLoader } from './model/howto/howto.js'
|
||||
import { loader as directorLoader } from './model/directory/item.js'
|
||||
import { LIBARY_BRANCH } from '@/app/config.js'
|
||||
|
||||
import { glob } from 'astro/loaders'
|
||||
|
||||
const store = defineCollection({
|
||||
loader: loader(RETAIL_PRODUCT_BRANCH) as any,
|
||||
loader: loader('site-dev') as any,
|
||||
schema: ComponentConfigSchema.passthrough(),
|
||||
})
|
||||
|
||||
/*
|
||||
const projects = defineCollection({
|
||||
loader: loader(PROJECTS_BRANCH) as any,
|
||||
schema: ComponentConfigSchema.passthrough(),
|
||||
})
|
||||
*/
|
||||
/*
|
||||
const helpcenter = defineCollection({
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
intro: z.string(),
|
||||
})
|
||||
})
|
||||
|
||||
const infopages = defineCollection({
|
||||
schema: z.object({
|
||||
title: z.string().optional(),
|
||||
intro: z.string().optional(),
|
||||
}).passthrough(),
|
||||
})
|
||||
*/
|
||||
const howtos = defineCollection({
|
||||
loader: howtoLoader(),
|
||||
schema: z.object({
|
||||
title: z.string().optional()
|
||||
}).passthrough()
|
||||
})
|
||||
|
||||
/*
|
||||
const directory = defineCollection({
|
||||
loader: directorLoader(),
|
||||
schema: z.object({
|
||||
title: z.string().optional()
|
||||
}).passthrough()
|
||||
})
|
||||
*/
|
||||
const resources = defineCollection({
|
||||
loader: glob({ base: './src/content/resources', pattern: '*.{md,mdx}' }),
|
||||
schema: z.object({
|
||||
@ -56,10 +65,9 @@ const resources = defineCollection({
|
||||
|
||||
export const collections = {
|
||||
store,
|
||||
projects,
|
||||
resources,
|
||||
helpcenter,
|
||||
infopages,
|
||||
//helpcenter,
|
||||
//infopages,
|
||||
howtos,
|
||||
directory
|
||||
//directory
|
||||
};
|
||||
@ -107,7 +107,7 @@ tags: ["community", "blogging", "c++"]
|
||||
|
||||
- [Machine & Components Library](https://forum.osr-plastic.org/c/machines/49)
|
||||
|
||||
- [Moulds - Library](https://files.polymech.io/files/machines/moulds/)
|
||||
- [Moulds - Library](https://files.polymech.info/files/machines/moulds/)
|
||||
|
||||
- [Git Repository Machines](https://git.polymech.io/osr-plastic/osr-machines)
|
||||
|
||||
|
||||
@ -1,27 +1,24 @@
|
||||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import BaseLayout from "./BaseLayout.astro";
|
||||
import { createMarkdownComponent } from "@/base/index.js";
|
||||
import { translate } from "@/base/i18n.js";
|
||||
import Wrapper from "@/components/containers/Wrapper.astro";
|
||||
|
||||
import Translate from "@/components/polymech/i18n.astro";
|
||||
import Readme from "@/components/polymech/readme.astro";
|
||||
import Gallery from "@/components/polymech/gallery.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 StoreEntries from "@/components/store/StoreEntries.astro";
|
||||
import {
|
||||
IComponentConfigEx,
|
||||
group_by_path,
|
||||
group_path,
|
||||
} from "@/model/component/component.js";
|
||||
|
||||
import "flowbite";
|
||||
|
||||
import Navigation from "@polymech/astro-base/components/global/Navigation.astro";
|
||||
import { createMarkdownComponent } from "@/base/index.js";
|
||||
import { translate } from "@polymech/astro-base/base/i18n.js";
|
||||
import Translate from "@polymech/astro-base/components/i18n.astro";
|
||||
import LGallery from "@polymech/astro-base/components/GalleryK.astro";
|
||||
|
||||
import BaseLayout from "./BaseLayout.astro";
|
||||
import Wrapper from "@polymech/astro-base/components/containers/Wrapper.astro";
|
||||
|
||||
import Readme from "@polymech/astro-base/components/polymech/readme.astro";
|
||||
import Breadcrumb from "@polymech/astro-base/components/Breadcrumb.astro";
|
||||
|
||||
import Resources from "@polymech/astro-base/components/polymech/resources.astro";
|
||||
import Specs from "@polymech/astro-base/components/specs.astro";
|
||||
|
||||
import TabButton from "@polymech/astro-base/components/tab-button.astro";
|
||||
import TabContent from "@polymech/astro-base/components/tab-content.astro";
|
||||
|
||||
import {
|
||||
I18N_SOURCE_LANGUAGE,
|
||||
SHOW_3D_PREVIEW,
|
||||
@ -37,25 +34,40 @@ import {
|
||||
SHOW_RESOURCES,
|
||||
SHOW_CHECKOUT,
|
||||
SHOW_README,
|
||||
SHOW_RELATED,
|
||||
DEFAULT_LICENSE,
|
||||
isRTL,
|
||||
} from "config/config.js";
|
||||
SHOW_SHOWCASE,
|
||||
SHOW_SCREENSHOTS,
|
||||
} from "@/app/config.js";
|
||||
|
||||
const { frontmatter: item, ...rest } = Astro.props;
|
||||
import { substitute } from "@polymech/commons/variables";
|
||||
import * as path from "path";
|
||||
import { sync as read } from "@polymech/fs/read";
|
||||
import { sync as exists } from "@polymech/fs/exists";
|
||||
import { PRODUCT_DIR } from "@/app/config.js";
|
||||
|
||||
const content = await translate(
|
||||
item.body || "",
|
||||
const { frontmatter: data, ...rest } = Astro.props;
|
||||
const itemDir = PRODUCT_DIR(data.rel);
|
||||
const contentPath = path.join(itemDir, "templates/shared", "body.md");
|
||||
if (exists(contentPath)) {
|
||||
data.content = read(contentPath) as string;
|
||||
}
|
||||
const content = substitute(false, data.content || "", {
|
||||
...data,
|
||||
LANG: Astro.currentLocale,
|
||||
});
|
||||
const translated_content = await translate(
|
||||
content,
|
||||
I18N_SOURCE_LANGUAGE,
|
||||
Astro.currentLocale,
|
||||
);
|
||||
const Body = createMarkdownComponent(content) as any;
|
||||
|
||||
const Body = await createMarkdownComponent(translated_content);
|
||||
|
||||
const str_debug =
|
||||
"```json\n" +
|
||||
JSON.stringify(
|
||||
{
|
||||
...item,
|
||||
...data,
|
||||
config: null,
|
||||
},
|
||||
null,
|
||||
@ -64,50 +76,66 @@ const str_debug =
|
||||
"\n```";
|
||||
|
||||
const Content_Debug = await createMarkdownComponent(str_debug);
|
||||
const view = "store";
|
||||
const items = await getCollection(view); //.filter((i) => item.rel !== i.id);
|
||||
//const group_id = group_path(item);
|
||||
const others = await group_by_path(items, Astro.currentLocale);
|
||||
---
|
||||
|
||||
<BaseLayout frontmatter={item} description={item.description} {...rest} class="">
|
||||
<BaseLayout
|
||||
frontmatter={data}
|
||||
description={data.description}
|
||||
hideNavigation={true}
|
||||
{...rest}
|
||||
>
|
||||
<Navigation />
|
||||
<Wrapper>
|
||||
<!-- Header with Breadcrumb on its own line -->
|
||||
<div
|
||||
class="flex flex-col gap-4 mb-4 py-4 border-b border-gray-200 dark:border-gray-700"
|
||||
>
|
||||
<!-- Breadcrumb -->
|
||||
<div class="flex-1 min-w-0">
|
||||
<Breadcrumb
|
||||
currentPath={Astro.url.pathname}
|
||||
collection="store"
|
||||
title={data.title}
|
||||
showHome={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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 class="grid grid-cols-1 lg:grid-cols-2 gap-4 lg:gap-6">
|
||||
<!-- Left Column: Description -->
|
||||
<div class="flex flex-col gap-4">
|
||||
<div>
|
||||
<article class="markdown-content bg-white rounded-xl p-4">
|
||||
<h1
|
||||
class="text-neutral-500 font-semibold mb-2 text-2xl"
|
||||
>
|
||||
<span><Translate>{`${item.title}`}</Translate></span>
|
||||
{
|
||||
isRTL(Astro.currentLocale) && (
|
||||
<div class="text-neutral-500 font-semibold mb-2">
|
||||
"{item.title}"
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</h1>
|
||||
<Body />
|
||||
</article>
|
||||
<h1 class="font-semibold mb-2 text-2xl">
|
||||
<Translate>{`${data.title}`}</Translate>
|
||||
</h1>
|
||||
{
|
||||
isRTL(Astro.currentLocale) && (
|
||||
<div class=" font-semibold mb-2">"{data.title}"</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="gap-2 flex flex-col h-full justify-end">
|
||||
<article
|
||||
class="markdown-content bg-white dark:bg-gray-800 rounded-xl p-4"
|
||||
>
|
||||
<Body />
|
||||
</article>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
{
|
||||
SHOW_3D_PREVIEW &&
|
||||
item.Preview3d &&
|
||||
item.cad &&
|
||||
item.cad[0] &&
|
||||
item.cad[0][".html"] && (
|
||||
data.Preview3d &&
|
||||
data.cad &&
|
||||
data.cad[0] &&
|
||||
data.cad[0][".html"] && (
|
||||
<a
|
||||
href={item.cad[0][".html"]}
|
||||
href={data.cad[0][".html"]}
|
||||
title="link to your page"
|
||||
aria-label="your label"
|
||||
class="relative group overflow-hidden pl-4 h-14 flex space-x-6 items-center bg-white hover:bg-neutral-200 duration-300 rounded-xl w-full justify-between"
|
||||
class="relative group overflow-hidden pl-4 h-14 flex space-x-6 items-center bg-white dark:bg-gray-800 hover:bg-neutral-200 dark:hover:bg-gray-700 duration-300 rounded-xl w-full justify-between rounded-xl"
|
||||
>
|
||||
<span class="relative uppercase text-xs text-neutral-600">
|
||||
<span class="relative uppercase text-xs">
|
||||
<Translate>3D Preview</Translate>
|
||||
</span>
|
||||
<div
|
||||
@ -150,20 +178,21 @@ const others = await group_by_path(items, Astro.currentLocale);
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
SHOW_CHECKOUT && item.checkout && (
|
||||
SHOW_CHECKOUT && data.checkout && (
|
||||
<a
|
||||
href={item.checkout}
|
||||
href={data.checkout}
|
||||
title="link to your page"
|
||||
aria-label="your label"
|
||||
class="relative group overflow-hidden pl-4 h-14 flex space-x-6 items-center bg-orange-500 hover:bg-black duration-300 rounded-xl w-full justify-between"
|
||||
class="relative group overflow-hidden pl-4 h-14 flex space-x-6 items-center bg-white dark:bg-gray-800 hover:bg-black dark:hover:bg-gray-700 duration-300 rounded-xl w-full justify-between"
|
||||
>
|
||||
<span class="relative uppercase text-xs text-white">
|
||||
<span class="relative uppercase text-xs ">
|
||||
<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"
|
||||
class="w-12 transition duration-300 -translate-y-7 group-hover:translate-y-7"
|
||||
>
|
||||
<div class="h-14 flex">
|
||||
<svg
|
||||
@ -204,58 +233,34 @@ const others = await group_by_path(items, Astro.currentLocale);
|
||||
</div>
|
||||
{
|
||||
SHOW_LICENSE && (
|
||||
<div class="space-y-2">
|
||||
<div class="bg-white rounded-xl p-4">
|
||||
<h3 class="text-lg text-neutral-600 uppercase tracking-tight">
|
||||
<Translate>License</Translate>
|
||||
</h3>
|
||||
<p class="text-neutral-500 mt-4 text-sm">
|
||||
{item.license || DEFAULT_LICENSE}
|
||||
</p>
|
||||
</div>
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl p-4">
|
||||
<h3 class="text-lg text-neutral-600 dark:text-gray-300 uppercase tracking-tight">
|
||||
License
|
||||
</h3>
|
||||
<p class=" mt-4 text-sm text-gray-700 dark:text-gray-300">
|
||||
{data.license}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Right Column: Gallery and Actions -->
|
||||
<div class="flex flex-col gap-4">
|
||||
{
|
||||
SHOW_RENDERINGS && data.assets?.renderings && (
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl p-4">
|
||||
<LGallery
|
||||
images={data.assets.gallery}
|
||||
gallerySettings={{ SHOW_TITLE: false }}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{
|
||||
SHOW_RENDERINGS && (
|
||||
<div
|
||||
class="flex-1 h-full bg-white"
|
||||
style="height: 100%; width: 100%;"
|
||||
>
|
||||
<Gallery
|
||||
images={item.assets.renderings}
|
||||
gallerySettings={{ SHOW_TITLE: false }}
|
||||
item={item}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{
|
||||
item.assets.showcase && item.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 bg-white rounded-xl">
|
||||
<Gallery
|
||||
images={item.assets.showcase}
|
||||
lightboxSettings={{
|
||||
SHOW_TITLE: false,
|
||||
SHOW_DESCRIPTION: false,
|
||||
SIZES_THUMB: "w-32 h-32",
|
||||
}}
|
||||
gallerySettings={{
|
||||
SHOW_TITLE: false,
|
||||
SHOW_DESCRIPTION: false,
|
||||
}}
|
||||
item={item}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
<section id="tabs-view">
|
||||
<div class="mb-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<ul
|
||||
@ -266,72 +271,125 @@ const others = await group_by_path(items, Astro.currentLocale);
|
||||
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 && item.readme && <TabButton title="Overview" />}
|
||||
{SHOW_SPECS && <TabButton title="Specs" />}
|
||||
{SHOW_GALLERY && <TabButton title="Gallery" />}
|
||||
{SHOW_RESOURCES && <TabButton title="Resources" />}
|
||||
{SHOW_README && <TabButton title="Overview" />}
|
||||
<TabButton title="Specs" />
|
||||
|
||||
<TabButton title="Resources" />
|
||||
{SHOW_SAMPLES && <TabButton title="Samples" />}
|
||||
{data.assets.showcase.length > 0 && <TabButton title="Showcase" />}
|
||||
{
|
||||
SHOW_SAMPLES && item.assets.samples.length > 0 && (
|
||||
<TabButton title="Samples" />
|
||||
)
|
||||
SHOW_SCREENSHOTS &&
|
||||
data.assets.screenshots &&
|
||||
data.assets.screenshots.length > 0 && (
|
||||
<TabButton title="Screenshots" />
|
||||
)
|
||||
}
|
||||
{SHOW_DEBUG && <TabButton title="Debug" />}
|
||||
</ul>
|
||||
</div>
|
||||
<div id="default-styled-tab-content">
|
||||
{SHOW_README && item.readme && <TabContent title="Overview" class="content">
|
||||
<TabContent title="Overview">
|
||||
{
|
||||
(
|
||||
<Readme markdown={item.readme} data={item} />
|
||||
SHOW_README && data.readme && (
|
||||
<Readme markdown={data.readme} data={data} />
|
||||
)
|
||||
}
|
||||
</TabContent>
|
||||
}
|
||||
{
|
||||
SHOW_SPECS && (
|
||||
<TabContent
|
||||
title="Specs"
|
||||
class="bg-white rounded-xl dark:bg-gray-800"
|
||||
>
|
||||
<Specs frontmatter={item} />
|
||||
</TabContent>
|
||||
)
|
||||
}
|
||||
{
|
||||
SHOW_GALLERY && (
|
||||
<TabContent title="Gallery" class="p-4 md:p-4 rounded-lg bg-white">
|
||||
<Gallery images={item.assets.gallery} item={item} />{" "}
|
||||
</TabContent>
|
||||
)
|
||||
}
|
||||
<div
|
||||
class="hidden bg-white rounded-xl dark:bg-gray-800"
|
||||
id="specs-view"
|
||||
role="tabpanel"
|
||||
aria-labelledby="dashboard-tab"
|
||||
>
|
||||
<Specs frontmatter={data} />
|
||||
</div>
|
||||
|
||||
{
|
||||
SHOW_SAMPLES && (
|
||||
<TabContent
|
||||
title="Samples"
|
||||
class="p-4 bg-white rounded-xl dark:bg-gray-800"
|
||||
<div
|
||||
class="hidden p-4 bg-white rounded-xl dark:bg-gray-800"
|
||||
id="samples-view"
|
||||
role="tabpanel"
|
||||
aria-labelledby="dashboard-tab"
|
||||
>
|
||||
<Gallery images={item.assets.samples} item={item} />
|
||||
</TabContent>
|
||||
<LGallery images={data.assets.samples} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
SHOW_RESOURCES && (
|
||||
<TabContent
|
||||
title="Resources"
|
||||
class="p-4 bg-white rounded-xl dark:bg-gray-800"
|
||||
<div
|
||||
class="hidden p-4 bg-white rounded-xl dark:bg-gray-800"
|
||||
id="resources-view"
|
||||
role="tabpanel"
|
||||
aria-labelledby="dashboard-tab"
|
||||
>
|
||||
<Resources frontmatter={item} />
|
||||
</TabContent>
|
||||
<Resources frontmatter={data} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
SHOW_SHOWCASE &&
|
||||
data.assets.showcase &&
|
||||
data.assets.showcase.length > 0 && (
|
||||
<div
|
||||
class="hidden p-4 bg-white rounded-xl dark:bg-gray-800"
|
||||
id="showcase-view"
|
||||
role="tabpanel"
|
||||
aria-labelledby="dashboard-tab"
|
||||
>
|
||||
<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,
|
||||
//SIZES_THUMB: "w-32 h-32",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
SHOW_SCREENSHOTS &&
|
||||
data.assets.screenshots &&
|
||||
data.assets.screenshots.length > 0 && (
|
||||
<div
|
||||
class="hidden p-4 bg-white rounded-xl dark:bg-gray-800"
|
||||
id="screenshots-view"
|
||||
role="tabpanel"
|
||||
aria-labelledby="dashboard-tab"
|
||||
>
|
||||
<LGallery
|
||||
images={data.assets.screenshots}
|
||||
lightboxSettings={{
|
||||
SHOW_TITLE: true,
|
||||
SHOW_DESCRIPTION: true,
|
||||
SIZES_THUMB: "w-32 h-32",
|
||||
}}
|
||||
gallerySettings={{
|
||||
SHOW_TITLE: true,
|
||||
SHOW_DESCRIPTION: true,
|
||||
SIZES_THUMB: "w-32 h-32",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
SHOW_DEBUG && (
|
||||
<TabContent
|
||||
title="Debug"
|
||||
class="rounded-lg bg-white p-4 dark:bg-gray-800"
|
||||
<div
|
||||
class="hidden rounded-lg bg-white p-4 dark:bg-gray-800"
|
||||
id="debug-view"
|
||||
role="tabpanel"
|
||||
aria-labelledby="dashboard-tab"
|
||||
>
|
||||
<Content_Debug />
|
||||
</TabContent>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
@ -372,38 +430,5 @@ const others = await group_by_path(items, Astro.currentLocale);
|
||||
});
|
||||
</script>
|
||||
</section>
|
||||
<hr />
|
||||
<h1 class="p-4 text-xs"> <Translate>Related</Translate>
|
||||
</h1>
|
||||
{
|
||||
SHOW_RELATED && (
|
||||
<section id="item_related" class="bg-blue-50 p-4 rounded-2xl">
|
||||
{Object.keys(others).map((relKey) => (
|
||||
<section>
|
||||
<h4
|
||||
aria-hidden="true"
|
||||
class="text-xs p-4 text-neutral-500"
|
||||
>
|
||||
{relKey}
|
||||
</h4>
|
||||
<div class="grid sm:grid-cols-4 lg:grid-cols-4 xl:grid-cols-4 gap-8">
|
||||
{others[relKey].map((post) => (
|
||||
<StoreEntries
|
||||
key={post.id}
|
||||
url={`/${Astro.currentLocale}/${view}/${post.id}`}
|
||||
title={post.data.title}
|
||||
price={post.data.price}
|
||||
type={post.data.type}
|
||||
alt={post.data.title}
|
||||
model={post.data}
|
||||
selected={post.id === item.rel}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
))}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
</Wrapper>
|
||||
</BaseLayout>
|
||||
|
||||
@ -1,188 +0,0 @@
|
||||
{
|
||||
"_createdBy": "gus-merckel",
|
||||
"mentions": [],
|
||||
"_deleted": false,
|
||||
"fileLink": "",
|
||||
"slug": "cut-out-shapes-out-of-plastic-sheets-with-a-cnc-",
|
||||
"_modified": "2023-10-27T18:09:36.519Z",
|
||||
"previousSlugs": [
|
||||
"cut-out-shapes-out-of-plastic-sheets-with-a-cnc-"
|
||||
],
|
||||
"_created": "2023-08-23T18:20:09.098Z",
|
||||
"description": "In this how to, I will show you our process to cut HDPE Sheets using a X-Carve CNC.\n\nHere is the full video in spanish with subtitles https://www.youtube.com/watch?v=4LrrFz802To ",
|
||||
"votedUsefulBy": [
|
||||
"sigolene",
|
||||
"mattia",
|
||||
"uillinoispreciousplastics"
|
||||
],
|
||||
"creatorCountry": "mx",
|
||||
"total_downloads": 0,
|
||||
"title": "Cut out shapes out of plastic sheets with a CNC ",
|
||||
"time": "< 5 hours",
|
||||
"files": [],
|
||||
"difficulty_level": "Medium",
|
||||
"_id": "038gjWgLjiyYknbEjDeI",
|
||||
"tags": {
|
||||
"RTCBJAFa05YBVVBy0KeO": true
|
||||
},
|
||||
"category":"machines",
|
||||
"total_views": 232,
|
||||
"_contentModifiedTimestamp": "2023-08-23T18:20:09.098Z",
|
||||
"cover_image": {
|
||||
"name": "IMG_20200605_142311.jpg",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2F038gjWgLjiyYknbEjDeI%2FIMG_20200605_142311.jpg?alt=media&token=c272c174-1adc-45af-967b-771adce7295d",
|
||||
"type": "image/jpeg",
|
||||
"fullPath": "uploads/howtos/038gjWgLjiyYknbEjDeI/IMG_20200605_142311.jpg",
|
||||
"updated": "2021-04-05T15:09:00.605Z",
|
||||
"size": 124661,
|
||||
"timeCreated": "2021-04-05T15:09:00.605Z",
|
||||
"contentType": "image/jpeg"
|
||||
},
|
||||
"comments": [],
|
||||
"moderatorFeedback": "",
|
||||
"steps": [
|
||||
{
|
||||
"title": "Measure the plastic sheet",
|
||||
"text": "For this step we need to measure our plastic sheet: Height, Width and Thickness. Our X-Carve machine works with the CAM Software EASEL, for me, the easiest software for CNC milling out there. \n\nThe cool thing about Easel (https://easel.inventables.com/) is that you can \"simulate\" your actual material and THEY EVEN HAVE HDPE 2-Colors in their cutting material lists!!\n\n\n",
|
||||
"images": [
|
||||
{
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/1.jpg",
|
||||
"name": "1.jpg",
|
||||
"size": 74095,
|
||||
"type": "image/jpeg",
|
||||
"timeCreated": "2021-03-26T19:42:05.766Z",
|
||||
"contentType": "image/jpeg",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F1.jpg?alt=media&token=293d733d-05a5-494a-9340-47f4564f1939",
|
||||
"updated": "2021-03-26T19:42:05.766Z"
|
||||
},
|
||||
{
|
||||
"contentType": "image/jpeg",
|
||||
"timeCreated": "2021-03-26T19:42:05.669Z",
|
||||
"updated": "2021-03-26T19:42:05.669Z",
|
||||
"size": 69665,
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F2.jpg?alt=media&token=004f50f1-97ac-4df4-9ba9-f463aa4cbca3",
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/2.jpg",
|
||||
"name": "2.jpg",
|
||||
"type": "image/jpeg"
|
||||
}
|
||||
],
|
||||
"_animationKey": "unique1"
|
||||
},
|
||||
{
|
||||
"text": "Using the CNC clamps from the X-Carve, secure the sheet to the table, ",
|
||||
"_animationKey": "unique2",
|
||||
"images": [
|
||||
{
|
||||
"updated": "2021-03-26T19:42:06.249Z",
|
||||
"size": 55544,
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/3.jpg",
|
||||
"timeCreated": "2021-03-26T19:42:06.249Z",
|
||||
"name": "3.jpg",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F3.jpg?alt=media&token=0b9c1914-1c75-429e-b34a-1e2b3706edef",
|
||||
"contentType": "image/jpeg",
|
||||
"type": "image/jpeg"
|
||||
}
|
||||
],
|
||||
"title": "Secure sheet "
|
||||
},
|
||||
{
|
||||
"title": "Choosing a file to cut ",
|
||||
"text": "Now we go to our illustrator, such as Inkscape to design a vector file or download and open source one frome https://thenounproject.com/.\n\nWe download the SVG file, which is an open source vector format and import it to Easel. \n",
|
||||
"images": [
|
||||
{
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F4.jpg?alt=media&token=1cd2d49d-9335-4bb1-ac2a-e625322ca604",
|
||||
"contentType": "image/jpeg",
|
||||
"timeCreated": "2021-03-26T19:42:06.727Z",
|
||||
"updated": "2021-03-26T19:42:06.727Z",
|
||||
"name": "4.jpg",
|
||||
"size": 42952,
|
||||
"type": "image/jpeg",
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/4.jpg"
|
||||
},
|
||||
{
|
||||
"size": 69255,
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/5.jpg",
|
||||
"updated": "2021-03-26T19:42:06.833Z",
|
||||
"timeCreated": "2021-03-26T19:42:06.833Z",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F5.jpg?alt=media&token=7cca786a-7d47-43bb-900b-b8d101c276b4",
|
||||
"name": "5.jpg",
|
||||
"contentType": "image/jpeg",
|
||||
"type": "image/jpeg"
|
||||
}
|
||||
],
|
||||
"_animationKey": "unique3"
|
||||
},
|
||||
{
|
||||
"text": "Now with the file we can choose the width we want to carve/cut and then we go to cut and start the wizzard:\n- We check that the sheet is fixed.\n- We also specify the cutting bit, we are using a 1/8 flat flute bit. \n- We tell the machine where the coordinate 0-0 is, which we always choose as the down left corner.\n- We raise the bit, turn on the Router!!!\n\nAND PUM THE MAGIC BEGINS!!",
|
||||
"title": "Follow the cutting Wizzard",
|
||||
"images": [
|
||||
{
|
||||
"timeCreated": "2021-03-26T19:42:07.493Z",
|
||||
"size": 72226,
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/6.jpg",
|
||||
"updated": "2021-03-26T19:42:07.493Z",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F6.jpg?alt=media&token=ba7195dd-7771-435f-a188-057457697332",
|
||||
"contentType": "image/jpeg",
|
||||
"type": "image/jpeg",
|
||||
"name": "6.jpg"
|
||||
},
|
||||
{
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/7.jpg",
|
||||
"size": 52424,
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F7.jpg?alt=media&token=a3d5820c-cfe2-484e-8f76-f861ab8b756d",
|
||||
"contentType": "image/jpeg",
|
||||
"type": "image/jpeg",
|
||||
"timeCreated": "2021-03-26T19:42:07.308Z",
|
||||
"updated": "2021-03-26T19:42:07.308Z",
|
||||
"name": "7.jpg"
|
||||
},
|
||||
{
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/8.jpg",
|
||||
"name": "8.jpg",
|
||||
"type": "image/jpeg",
|
||||
"timeCreated": "2021-03-26T19:42:07.346Z",
|
||||
"size": 55264,
|
||||
"contentType": "image/jpeg",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F8.jpg?alt=media&token=1c9816d7-3a99-4f41-8d3c-acc2670240f6",
|
||||
"updated": "2021-03-26T19:42:07.346Z"
|
||||
}
|
||||
],
|
||||
"_animationKey": "uniquenisc2v"
|
||||
},
|
||||
{
|
||||
"text": "You take now your glasses or object and postprocess them and of course show it to your friends, family and so on.\n\n\n",
|
||||
"images": [
|
||||
{
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/9.jpg",
|
||||
"contentType": "image/jpeg",
|
||||
"timeCreated": "2021-03-26T19:42:08.147Z",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F9.jpg?alt=media&token=4dcfe37d-e1ad-41e5-a590-40b4c37c5e1a",
|
||||
"name": "9.jpg",
|
||||
"updated": "2021-03-26T19:42:08.147Z",
|
||||
"type": "image/jpeg",
|
||||
"size": 82214
|
||||
}
|
||||
],
|
||||
"_animationKey": "uniquesgl34",
|
||||
"title": "Post-production and show case"
|
||||
},
|
||||
{
|
||||
"_animationKey": "uniquem4y0yi",
|
||||
"title": "Hack it and try it yourself",
|
||||
"text": "You can try this project with other types of CNC machines, even manual Routers or manual saw, as I did on this video: https://youtu.be/gxkcffQD3eQ, but the important thing is that you share what you do and help this community to grow!!!\n\nShare your ideas and comments!",
|
||||
"images": [
|
||||
{
|
||||
"contentType": "image/jpeg",
|
||||
"timeCreated": "2021-04-05T15:09:01.445Z",
|
||||
"fullPath": "uploads/howtos/038gjWgLjiyYknbEjDeI/IMG_20200605_142311.jpg",
|
||||
"type": "image/jpeg",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2F038gjWgLjiyYknbEjDeI%2FIMG_20200605_142311.jpg?alt=media&token=f94152ff-f923-4054-a3ad-d8ec588856fa",
|
||||
"size": 124661,
|
||||
"updated": "2021-04-05T15:09:01.445Z",
|
||||
"name": "IMG_20200605_142311.jpg"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"moderation": "accepted"
|
||||
}
|
||||
@ -15,33 +15,23 @@ import { IHowto, IImage, ITag, ITEM_TYPE } from './howto-model.js'
|
||||
import { BLACKLIST, default_filters_markdown, validateLinks } from '../../base/filters.js'
|
||||
import { download } from '../download.js'
|
||||
|
||||
import { filter } from "@/base/kbot.js"
|
||||
import { slugify } from "@/base/strings.js"
|
||||
import { urlCache } from '../../base/url-cache.js';
|
||||
|
||||
const expandUrls = true
|
||||
import {
|
||||
HOWTO_FILES_WEB,
|
||||
HOWTO_FILES_ABS,
|
||||
HOWTO_FILTER_LLM,
|
||||
default_image,
|
||||
HOWTO_ROOT,
|
||||
HOWTO_GLOB,
|
||||
HOWTO_MIGRATION,
|
||||
HOWTO_ANNOTATIONS,
|
||||
HOWTO_COMPLETE_RESOURCES,
|
||||
HOWTO_ADD_HARDWARE,
|
||||
HOWTO_COMPLETE_SKILLS,
|
||||
HOWTO_LOCAL_RESOURCES,
|
||||
HOWTO_ADD_RESOURCES,
|
||||
HOWTO_ADD_REFERENCES,
|
||||
HOWTO_SEO_LLM,
|
||||
HOWTO_MAX_ITEMS
|
||||
HOWTO_MAX_ITEMS,
|
||||
HOWTO_LLM_KEYWORDS
|
||||
} from "config/config.js"
|
||||
|
||||
import { logger } from '@/base/index.js'
|
||||
import { logger } from '@/base/index.js'
|
||||
import { applyFilters, default_filters_plain, FilterFunction } from '../../base/filters.js'
|
||||
import { TemplateContext, buildPrompt, LLMConfig, createTemplates } from '@/base/kbot-templates.js';
|
||||
import { TemplateContext } from '@/base/kbot-templates.js';
|
||||
import { template_filter } from '@/base/kbot.js'
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
@ -73,7 +63,6 @@ export const downloadFiles = async (dst: string, howto: IHowto) => {
|
||||
const parts = path.parse(asset_path);
|
||||
const zipout = path.join(asset_root, 'files')
|
||||
if (parts.ext === '.rar' || parts.ext === '.zip') {
|
||||
logger.info(`Extracting RAR file ${i.name} to ${zipout}`);
|
||||
try {
|
||||
if (!exists(asset_path)) {
|
||||
logger.error(`File does not exist: ${asset_path}`);
|
||||
@ -359,7 +348,9 @@ const complete = async (item: IHowto) => {
|
||||
...item.steps.map(step => step.text)
|
||||
].filter(Boolean).join('\n\n')
|
||||
|
||||
item.keywords = await template_filter(item.content, 'keywords', TemplateContext.HOWTO);
|
||||
if (HOWTO_LLM_KEYWORDS) {
|
||||
item.keywords = await template_filter(item.content, 'keywords', TemplateContext.HOWTO);
|
||||
}
|
||||
if (HOWTO_ADD_RESOURCES) {
|
||||
// item.resources = await template_filter(item.content, 'toolsAndHardware', TemplateContext.HOWTO);
|
||||
item.resources = await applyFilters(item.resources, default_filters_markdown);
|
||||
@ -408,7 +399,6 @@ const onStoreItem = async (store: any) => {
|
||||
item = await complete(item)
|
||||
const configPath = path.join(item_path(item), 'config.json')
|
||||
write(configPath, JSON.stringify(item, null, 2))
|
||||
logger.info(`Stored item ${item.slug} at ${configPath}`)
|
||||
store.data.item = item
|
||||
return store
|
||||
}
|
||||
|
||||
@ -1,339 +0,0 @@
|
||||
{
|
||||
"_createdBy": "gus-merckel",
|
||||
"mentions": [],
|
||||
"_deleted": false,
|
||||
"fileLink": "",
|
||||
"slug": "cut-out-shapes-out-of-plastic-sheets-with-a-cnc-",
|
||||
"_modified": "2023-10-27T18:09:36.519Z",
|
||||
"previousSlugs": [
|
||||
"cut-out-shapes-out-of-plastic-sheets-with-a-cnc-"
|
||||
],
|
||||
"_created": "2023-08-23T18:20:09.098Z",
|
||||
"description": "In this how to, I will show you our process to cut HDPE Sheets using a X-Carve CNC.\n\nHere is the full video in spanish with subtitles https://www.youtube.com/watch?v=4LrrFz802To ",
|
||||
"votedUsefulBy": [
|
||||
"sigolene",
|
||||
"mattia",
|
||||
"uillinoispreciousplastics"
|
||||
],
|
||||
"creatorCountry": "mx",
|
||||
"total_downloads": 0,
|
||||
"title": "Cut out shapes out of plastic sheets with a CNC ",
|
||||
"time": "< 5 hours",
|
||||
"files": [],
|
||||
"difficulty_level": "Medium",
|
||||
"_id": "038gjWgLjiyYknbEjDeI",
|
||||
"tags": [
|
||||
"HDPE"
|
||||
],
|
||||
"total_views": 232,
|
||||
"_contentModifiedTimestamp": "2023-08-23T18:20:09.098Z",
|
||||
"cover_image": {
|
||||
"name": "IMG_20200605_142311.jpg",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2F038gjWgLjiyYknbEjDeI%2FIMG_20200605_142311.jpg?alt=media&token=c272c174-1adc-45af-967b-771adce7295d",
|
||||
"type": "image/jpeg",
|
||||
"fullPath": "uploads/howtos/038gjWgLjiyYknbEjDeI/IMG_20200605_142311.jpg",
|
||||
"updated": "2021-04-05T15:09:00.605Z",
|
||||
"size": 124661,
|
||||
"timeCreated": "2021-04-05T15:09:00.605Z",
|
||||
"contentType": "image/jpeg"
|
||||
},
|
||||
"comments": [],
|
||||
"moderatorFeedback": "",
|
||||
"steps": [
|
||||
{
|
||||
"title": "Measure the plastic sheet",
|
||||
"text": "For this step we need to measure our plastic sheet: Height, Width and Thickness. Our X-Carve machine works with the CAM Software EASEL, for me, the easiest software for CNC milling out there. \n\nThe cool thing about Easel (https://easel.inventables.com/) is that you can \"simulate\" your actual material and THEY EVEN HAVE HDPE 2-Colors in their cutting material lists!!\n\n\n",
|
||||
"images": [
|
||||
{
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/1.jpg",
|
||||
"name": "1.jpg",
|
||||
"size": 74095,
|
||||
"type": "image/jpeg",
|
||||
"timeCreated": "2021-03-26T19:42:05.766Z",
|
||||
"contentType": "image/jpeg",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F1.jpg?alt=media&token=293d733d-05a5-494a-9340-47f4564f1939",
|
||||
"updated": "2021-03-26T19:42:05.766Z",
|
||||
"src": "/resources/howtos/cut-out-shapes-out-of-plastic-sheets-with-a-cnc-/1.jpg",
|
||||
"alt": "1.jpg"
|
||||
},
|
||||
{
|
||||
"contentType": "image/jpeg",
|
||||
"timeCreated": "2021-03-26T19:42:05.669Z",
|
||||
"updated": "2021-03-26T19:42:05.669Z",
|
||||
"size": 69665,
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F2.jpg?alt=media&token=004f50f1-97ac-4df4-9ba9-f463aa4cbca3",
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/2.jpg",
|
||||
"name": "2.jpg",
|
||||
"type": "image/jpeg",
|
||||
"src": "/resources/howtos/cut-out-shapes-out-of-plastic-sheets-with-a-cnc-/2.jpg",
|
||||
"alt": "2.jpg"
|
||||
}
|
||||
],
|
||||
"_animationKey": "unique1"
|
||||
},
|
||||
{
|
||||
"text": "Using the CNC clamps from the X-Carve, secure the sheet to the table, ",
|
||||
"_animationKey": "unique2",
|
||||
"images": [
|
||||
{
|
||||
"updated": "2021-03-26T19:42:06.249Z",
|
||||
"size": 55544,
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/3.jpg",
|
||||
"timeCreated": "2021-03-26T19:42:06.249Z",
|
||||
"name": "3.jpg",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F3.jpg?alt=media&token=0b9c1914-1c75-429e-b34a-1e2b3706edef",
|
||||
"contentType": "image/jpeg",
|
||||
"type": "image/jpeg",
|
||||
"src": "/resources/howtos/cut-out-shapes-out-of-plastic-sheets-with-a-cnc-/3.jpg",
|
||||
"alt": "3.jpg"
|
||||
}
|
||||
],
|
||||
"title": "Secure sheet "
|
||||
},
|
||||
{
|
||||
"title": "Choosing a file to cut ",
|
||||
"text": "Now we go to our illustrator, such as Inkscape to design a vector file or download and open source one frome https://thenounproject.com/.\n\nWe download the SVG file, which is an open source vector format and import it to Easel. \n",
|
||||
"images": [
|
||||
{
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F4.jpg?alt=media&token=1cd2d49d-9335-4bb1-ac2a-e625322ca604",
|
||||
"contentType": "image/jpeg",
|
||||
"timeCreated": "2021-03-26T19:42:06.727Z",
|
||||
"updated": "2021-03-26T19:42:06.727Z",
|
||||
"name": "4.jpg",
|
||||
"size": 42952,
|
||||
"type": "image/jpeg",
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/4.jpg",
|
||||
"src": "/resources/howtos/cut-out-shapes-out-of-plastic-sheets-with-a-cnc-/4.jpg",
|
||||
"alt": "4.jpg"
|
||||
},
|
||||
{
|
||||
"size": 69255,
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/5.jpg",
|
||||
"updated": "2021-03-26T19:42:06.833Z",
|
||||
"timeCreated": "2021-03-26T19:42:06.833Z",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F5.jpg?alt=media&token=7cca786a-7d47-43bb-900b-b8d101c276b4",
|
||||
"name": "5.jpg",
|
||||
"contentType": "image/jpeg",
|
||||
"type": "image/jpeg",
|
||||
"src": "/resources/howtos/cut-out-shapes-out-of-plastic-sheets-with-a-cnc-/5.jpg",
|
||||
"alt": "5.jpg"
|
||||
}
|
||||
],
|
||||
"_animationKey": "unique3"
|
||||
},
|
||||
{
|
||||
"text": "Now with the file we can choose the width we want to carve/cut and then we go to cut and start the wizzard:\n- We check that the sheet is fixed.\n- We also specify the cutting bit, we are using a 1/8 flat flute bit. \n- We tell the machine where the coordinate 0-0 is, which we always choose as the down left corner.\n- We raise the bit, turn on the Router!!!\n\nAND PUM THE MAGIC BEGINS!!",
|
||||
"title": "Follow the cutting Wizzard",
|
||||
"images": [
|
||||
{
|
||||
"timeCreated": "2021-03-26T19:42:07.493Z",
|
||||
"size": 72226,
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/6.jpg",
|
||||
"updated": "2021-03-26T19:42:07.493Z",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F6.jpg?alt=media&token=ba7195dd-7771-435f-a188-057457697332",
|
||||
"contentType": "image/jpeg",
|
||||
"type": "image/jpeg",
|
||||
"name": "6.jpg",
|
||||
"src": "/resources/howtos/cut-out-shapes-out-of-plastic-sheets-with-a-cnc-/6.jpg",
|
||||
"alt": "6.jpg"
|
||||
},
|
||||
{
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/7.jpg",
|
||||
"size": 52424,
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F7.jpg?alt=media&token=a3d5820c-cfe2-484e-8f76-f861ab8b756d",
|
||||
"contentType": "image/jpeg",
|
||||
"type": "image/jpeg",
|
||||
"timeCreated": "2021-03-26T19:42:07.308Z",
|
||||
"updated": "2021-03-26T19:42:07.308Z",
|
||||
"name": "7.jpg",
|
||||
"src": "/resources/howtos/cut-out-shapes-out-of-plastic-sheets-with-a-cnc-/7.jpg",
|
||||
"alt": "7.jpg"
|
||||
},
|
||||
{
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/8.jpg",
|
||||
"name": "8.jpg",
|
||||
"type": "image/jpeg",
|
||||
"timeCreated": "2021-03-26T19:42:07.346Z",
|
||||
"size": 55264,
|
||||
"contentType": "image/jpeg",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F8.jpg?alt=media&token=1c9816d7-3a99-4f41-8d3c-acc2670240f6",
|
||||
"updated": "2021-03-26T19:42:07.346Z",
|
||||
"src": "/resources/howtos/cut-out-shapes-out-of-plastic-sheets-with-a-cnc-/8.jpg",
|
||||
"alt": "8.jpg"
|
||||
}
|
||||
],
|
||||
"_animationKey": "uniquenisc2v"
|
||||
},
|
||||
{
|
||||
"text": "You take now your glasses or object and postprocess them and of course show it to your friends, family and so on.\n\n\n",
|
||||
"images": [
|
||||
{
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/9.jpg",
|
||||
"contentType": "image/jpeg",
|
||||
"timeCreated": "2021-03-26T19:42:08.147Z",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F9.jpg?alt=media&token=4dcfe37d-e1ad-41e5-a590-40b4c37c5e1a",
|
||||
"name": "9.jpg",
|
||||
"updated": "2021-03-26T19:42:08.147Z",
|
||||
"type": "image/jpeg",
|
||||
"size": 82214,
|
||||
"src": "/resources/howtos/cut-out-shapes-out-of-plastic-sheets-with-a-cnc-/9.jpg",
|
||||
"alt": "9.jpg"
|
||||
}
|
||||
],
|
||||
"_animationKey": "uniquesgl34",
|
||||
"title": "Post-production and show case"
|
||||
},
|
||||
{
|
||||
"_animationKey": "uniquem4y0yi",
|
||||
"title": "Hack it and try it yourself",
|
||||
"text": "You can try this project with other types of CNC machines, even manual Routers or manual saw, as I did on this video: https://youtu.be/gxkcffQD3eQ, but the important thing is that you share what you do and help this community to grow!!!\n\nShare your ideas and comments!",
|
||||
"images": [
|
||||
{
|
||||
"contentType": "image/jpeg",
|
||||
"timeCreated": "2021-04-05T15:09:01.445Z",
|
||||
"fullPath": "uploads/howtos/038gjWgLjiyYknbEjDeI/IMG_20200605_142311.jpg",
|
||||
"type": "image/jpeg",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2F038gjWgLjiyYknbEjDeI%2FIMG_20200605_142311.jpg?alt=media&token=f94152ff-f923-4054-a3ad-d8ec588856fa",
|
||||
"size": 124661,
|
||||
"updated": "2021-04-05T15:09:01.445Z",
|
||||
"name": "IMG_20200605_142311.jpg",
|
||||
"src": "/resources/howtos/cut-out-shapes-out-of-plastic-sheets-with-a-cnc-/IMG_20200605_142311.jpg",
|
||||
"alt": "IMG_20200605_142311.jpg"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"moderation": "accepted",
|
||||
"user": {
|
||||
"_modified": "2024-01-08T13:28:33.484Z",
|
||||
"_id": "gus-merckel",
|
||||
"subType": "mix",
|
||||
"moderation": "accepted",
|
||||
"_deleted": false,
|
||||
"verified": false,
|
||||
"type": "workspace",
|
||||
"location": {
|
||||
"lat": 19.3935,
|
||||
"lng": -99.1656
|
||||
},
|
||||
"_created": "2024-01-08T13:28:33.484Z",
|
||||
"geo": {
|
||||
"latitude": 19.3935,
|
||||
"lookupSource": "coordinates",
|
||||
"longitude": -99.1656,
|
||||
"localityLanguageRequested": "en",
|
||||
"continent": "North America",
|
||||
"continentCode": "NA",
|
||||
"countryName": "Mexico",
|
||||
"countryCode": "MX",
|
||||
"principalSubdivision": "Ciudad de Mexico",
|
||||
"principalSubdivisionCode": "MX-CMX",
|
||||
"city": "Mexico City",
|
||||
"locality": "Benito Juarez",
|
||||
"postcode": "03103",
|
||||
"plusCode": "76F29RVM+CQ",
|
||||
"localityInfo": {
|
||||
"administrative": [
|
||||
{
|
||||
"name": "Mexico",
|
||||
"description": "country in North America",
|
||||
"isoName": "Mexico",
|
||||
"order": 2,
|
||||
"adminLevel": 2,
|
||||
"isoCode": "MX",
|
||||
"wikidataId": "Q96",
|
||||
"geonameId": 3996063
|
||||
},
|
||||
{
|
||||
"name": "Mexico City",
|
||||
"description": "capital and largest city of Mexico",
|
||||
"order": 5,
|
||||
"adminLevel": 4,
|
||||
"wikidataId": "Q1489",
|
||||
"geonameId": 3530597
|
||||
},
|
||||
{
|
||||
"name": "Ciudad de Mexico",
|
||||
"description": "capital and largest city of Mexico",
|
||||
"isoName": "Ciudad de Mexico",
|
||||
"order": 6,
|
||||
"adminLevel": 4,
|
||||
"isoCode": "MX-CMX",
|
||||
"wikidataId": "Q1489",
|
||||
"geonameId": 3527646
|
||||
},
|
||||
{
|
||||
"name": "Benito Juarez",
|
||||
"description": "territorial demarcation of the Mexico City in Mexico",
|
||||
"order": 7,
|
||||
"adminLevel": 6,
|
||||
"wikidataId": "Q2356998",
|
||||
"geonameId": 3827406
|
||||
}
|
||||
],
|
||||
"informative": [
|
||||
{
|
||||
"name": "North America",
|
||||
"description": "continent and northern subcontinent of the Americas",
|
||||
"isoName": "North America",
|
||||
"order": 1,
|
||||
"isoCode": "NA",
|
||||
"wikidataId": "Q49",
|
||||
"geonameId": 6255149
|
||||
},
|
||||
{
|
||||
"name": "America/Mexico_City",
|
||||
"description": "time zone",
|
||||
"order": 3
|
||||
},
|
||||
{
|
||||
"name": "Greater Mexico City",
|
||||
"description": "geographical object",
|
||||
"order": 4,
|
||||
"wikidataId": "Q665894"
|
||||
},
|
||||
{
|
||||
"name": "03103",
|
||||
"description": "postal code",
|
||||
"order": 8
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"urls": [
|
||||
{
|
||||
"name": "Email",
|
||||
"url": "mailto:gustavomerckel@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Facebook",
|
||||
"url": "https://www.facebook.com/pl%c3%a1stico-chido-110888520718193"
|
||||
},
|
||||
{
|
||||
"name": "sponsor the work",
|
||||
"url": "https://www.patreon.com/one_army"
|
||||
}
|
||||
],
|
||||
"description": "Plástico Chido builds and modifies the PP machines, and also experiments with CNC and Laser Cut",
|
||||
"services": [
|
||||
{
|
||||
"welding": false,
|
||||
"assembling": false,
|
||||
"machining": false,
|
||||
"electronics": false,
|
||||
"molds": false
|
||||
}
|
||||
],
|
||||
"title": "Plástico Chido",
|
||||
"images": []
|
||||
},
|
||||
"detail": {
|
||||
"services": [],
|
||||
"urls": []
|
||||
}
|
||||
},
|
||||
"category": {
|
||||
"label": "uncategorized"
|
||||
}
|
||||
}
|
||||
@ -133,7 +133,7 @@ export async function getStaticPaths() {
|
||||
<!-- All Howtos Grid -->
|
||||
<div class="mb-12">
|
||||
<h2 class="text-2xl font-semibold text-gray-900 mb-6"><Translate>All Guides</Translate></h2>
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 gap-6">
|
||||
{items.map((item) => {
|
||||
const correspondingItem = all.find(storeItem => storeItem.data.item.slug === item.slug);
|
||||
return correspondingItem ? (
|
||||
|
||||
@ -50,7 +50,7 @@ const categories = Object.keys(howtosByCategory).sort()
|
||||
<!-- All Howtos Grid -->
|
||||
<div class="mb-12">
|
||||
<h2 class="text-2xl font-semibold text-gray-900 mb-6">All Guides</h2>
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 gap-2">
|
||||
{all.map((item) => (
|
||||
<HowtoCard
|
||||
url={`/${locale}/howtos/${item.id}`}
|
||||
@ -84,7 +84,7 @@ const categories = Object.keys(howtosByCategory).sort()
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 gap-2">
|
||||
{howtosByCategory[category].slice(0, 8).map((howto: IHowto) => {
|
||||
const correspondingItem = all.find(item => item.data.item.slug === howto.slug);
|
||||
return correspondingItem ? (
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
---
|
||||
import { foo, i18n as Translate } from "@polymech/astro-base"
|
||||
import test from "@/model/howto/howto.json"
|
||||
import Translate from "@polymech/astro-base/components/i18n.astro";
|
||||
---
|
||||
<Translate language="es">Hellau</Translate>
|
||||
|
||||
<Translate language="es">Hellau</Translate>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user