generated from polymech/site-template
latest
This commit is contained in:
parent
a29bae4136
commit
4a08425c24
@ -23,7 +23,7 @@
|
||||
"format": "unix-time"
|
||||
}
|
||||
],
|
||||
"default": "2025-04-06T09:23:22.935Z"
|
||||
"default": "2025-04-06T13:49:04.457Z"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -3,7 +3,7 @@
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Return a list of useful references, as Markdown, grouped : Articles, Books, Papers, Youtube, Software, Opensource Designs, ... Dont comment!\n\nText to process:\nThis tutorial demonstrates the process of cutting HDPE sheets with an X-Carve CNC. You can watch the complete video in Spanish with subtitles [here](https://www.youtube.com/watch?v=4LrrFz802To).\n\n\nUser Location: Mexico City, Mexico\n\nFor this step, measure the plastic sheet's height, width, and thickness. Our X-Carve machine uses the CAM software Easel for CNC milling. Easel allows you to simulate your material, and it includes HDPE 2-Colors in the cutting material list.\n\nUsing CNC clamps from the X-Carve, secure the sheet to the table.\n\nProceed to a vector graphics editor, like Inkscape, to create a design, or download an open-source vector file from [The Noun Project](https://thenounproject.com).\n\nDownload the SVG file and import it into Easel.\n\n### Instructions for CNC Router Setup\n\n1. Select the desired cutting width in the file.\n2. Ensure the sheet is securely fixed.\n3. Choose the cutting bit: 1/8 inch (3.175 mm) flat flute.\n4. Set the machine's origin to the bottom left corner.\n5. Raise the bit and activate the CNC router.\n\nTake your glasses or object, complete the post-processing, and share the results with others.\n\nYou can attempt this project using various CNC machines, including manual routers or saws, as demonstrated in this video: [youtu.be](https://youtu.be/gxkcffQD3eQ). It is important to share your work to support community growth.\n\nContributions and feedback are welcome."
|
||||
"content": "Return a list of useful references, as Markdown, grouped : Articles, Books, Papers, Youtube, Software, Opensource Designs, ... Dont comment!\n\nText to process:\nThis tutorial explains the process of cutting HDPE sheets using an X-Carve CNC. \n\nWatch the full video in Spanish with subtitles [here](https://www.youtube.com/watch?v=4LrrFz802To).\n\n\nUser Location: Mexico City, Mexico\n\n### Step Instructions:\n\nMeasure the plastic sheet's height, width, and thickness. The X-Carve machine uses the CAM software Easel, which facilitates CNC milling. Easel allows you to simulate your material, and it includes HDPE two-color options in its material list.\n\n- **Metric to Imperial Conversion:** \n Height, Width, Thickness: Convert as needed, e.g., 1 cm = 0.3937 in.\n\nI'm sorry, but I'm unable to process the task given the provided parameters. Could you please provide more information or clarify your request?\n\nProceed to a vector graphics editor like Inkscape to create a vector file or download one from [The Noun Project](https://thenounproject.com).\n\nDownload the SVG file, a vector format, and import it to Easel.\n\nWith the file ready, select the desired width for carving or cutting, and proceed to initiate the cut using the wizard:\n\n- Confirm that the sheet is secure.\n- Specify the cutting bit; a 1/8 inch (3.175 mm) flat flute bit is used.\n- Set the machine's 0-0 coordinate, always selecting the lower left corner.\n- Raise the bit and activate the CNC Router.\n\nTake your glasses or object, post-process them, and then share with others.\n\nThis project can also be attempted with various CNC machines or manual tools like routers and saws, as demonstrated [here](https://youtu.be/gxkcffQD3eQ). Sharing your work and contributing to the community is encouraged.\n\nFeel free to share your ideas and comments."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
|
||||
119
src/base/async-iterator.ts
Normal file
119
src/base/async-iterator.ts
Normal file
@ -0,0 +1,119 @@
|
||||
import { JSONPath } from 'jsonpath-plus'
|
||||
import pThrottle from 'p-throttle'
|
||||
import pMap from 'p-map'
|
||||
|
||||
export type AsyncTransformer = (input: string, path: string) => Promise<string>
|
||||
export type ErrorCallback = (path: string, value: string, error: any) => void
|
||||
export type FilterCallback = (input: string, path: string) => Promise<boolean>
|
||||
export type Filter = (input: string) => Promise<boolean>
|
||||
|
||||
export interface TransformOptions {
|
||||
transform: AsyncTransformer
|
||||
path: string
|
||||
throttleDelay: number
|
||||
concurrentTasks: number
|
||||
errorCallback: ErrorCallback
|
||||
filterCallback: FilterCallback
|
||||
}
|
||||
|
||||
export const isNumber: Filter = async (input: string) => (/^-?\d+(\.\d+)?$/.test(input))
|
||||
export const isBoolean: Filter = async (input: string) => /^(true|false)$/i.test(input)
|
||||
export const isValidString: Filter = async (input: string) => !(input.trim() !== '')
|
||||
|
||||
export const testFilters = (filters: Filter[]): FilterCallback => {
|
||||
return async (input: string) => {
|
||||
for (const filter of filters) {
|
||||
if (await filter(input)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
};
|
||||
|
||||
export const defaultFilters = (filters: Filter[] = []) =>
|
||||
[
|
||||
isNumber, isBoolean, isValidString, ...filters
|
||||
]
|
||||
|
||||
export async function transformObject(
|
||||
obj: any,
|
||||
transform: AsyncTransformer,
|
||||
path: string,
|
||||
throttleDelay: number,
|
||||
concurrentTasks: number,
|
||||
errorCallback: ErrorCallback,
|
||||
testCallback: FilterCallback
|
||||
): Promise<void> {
|
||||
const paths = JSONPath({ path, json: obj, resultType: 'pointer' });
|
||||
await pMap(
|
||||
paths,
|
||||
async (jsonPointer: any) => {
|
||||
const keys = jsonPointer.slice(1).split('/')
|
||||
await transformPath(obj, keys, transform, throttleDelay, concurrentTasks, jsonPointer, errorCallback, testCallback)
|
||||
},
|
||||
{ concurrency: concurrentTasks }
|
||||
)
|
||||
}
|
||||
export async function transformPath(
|
||||
obj: any,
|
||||
keys: string[],
|
||||
transform: AsyncTransformer,
|
||||
throttleDelay: number,
|
||||
concurrentTasks: number,
|
||||
currentPath: string,
|
||||
errorCallback: ErrorCallback,
|
||||
testCallback: FilterCallback
|
||||
): Promise<void> {
|
||||
|
||||
let current = obj
|
||||
|
||||
for (let i = 0; i < keys.length - 1; i++) {
|
||||
current = current[keys[i]]
|
||||
}
|
||||
const lastKey = keys[keys.length - 1]
|
||||
const throttle = pThrottle({
|
||||
limit: 1,
|
||||
interval: throttleDelay,
|
||||
})
|
||||
|
||||
if (typeof lastKey === 'string' && lastKey !== '') {
|
||||
try {
|
||||
const newKey = isValidString(lastKey) && !isNumber(lastKey) ? await throttle(transform)(lastKey, currentPath) : lastKey
|
||||
if (newKey !== lastKey) {
|
||||
current[newKey] = current[lastKey]
|
||||
delete current[lastKey]
|
||||
}
|
||||
if (typeof current[newKey] === 'string' && current[newKey] !== '') {
|
||||
if (await testCallback(current[newKey], `${currentPath}/${lastKey}`)) {
|
||||
current[newKey] = await throttle(transform)(current[newKey], `${currentPath}/${lastKey}`)
|
||||
}
|
||||
} else if (typeof current[newKey] === 'object' && current[newKey] !== null) {
|
||||
await transformObject(current[newKey], transform, '$.*', throttleDelay, concurrentTasks, errorCallback, testCallback)
|
||||
}
|
||||
} catch (error) {
|
||||
errorCallback(currentPath, lastKey, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const exampleTransformFunction: AsyncTransformer = async (input: string, path: string): Promise<string> => {
|
||||
if (input === 'random') throw new Error('API error')
|
||||
return input.toUpperCase()
|
||||
}
|
||||
|
||||
export const defaultError: ErrorCallback = (path: string, value: string, error: any): void => {
|
||||
// logger.error(`Error at path: ${path}, value: ${value}, error: ${error}`)
|
||||
}
|
||||
|
||||
export const defaultOptions = (options: TransformOptions = {} as TransformOptions): TransformOptions => {
|
||||
return {
|
||||
transform: exampleTransformFunction,
|
||||
path: options.path || '$[*][0,1,2]',
|
||||
throttleDelay: 10,
|
||||
concurrentTasks: 1,
|
||||
errorCallback: defaultError,
|
||||
filterCallback: testFilters(defaultFilters()),
|
||||
...options
|
||||
}
|
||||
}
|
||||
@ -6,37 +6,12 @@ import { meta } from '../base/url.js';
|
||||
|
||||
export interface FilterFunction { (text: string): string | Promise<string> }
|
||||
|
||||
export const BLACKLIST: readonly string[] = [
|
||||
'precious-plastic',
|
||||
'fair-enough',
|
||||
'mad-plastic-labs',
|
||||
'easymoulds',
|
||||
'plasticpreneur',
|
||||
'sustainable-design-studio',
|
||||
'johannplasto'
|
||||
] as const;
|
||||
import filterConfig from "config/filters.json" assert { type: "json" };
|
||||
|
||||
export const URL_BLACKLIST: readonly string[] = [
|
||||
"preciousplastic.com",
|
||||
"community.preciousplastic.com",
|
||||
"bazar.preciousplastic.com",
|
||||
"onearmy.earth",
|
||||
"davehakkens.nl",
|
||||
"sustainabledesign.studio"
|
||||
] as const;
|
||||
|
||||
export const WORD_BLACKLIST: readonly string[] = [
|
||||
"wizard",
|
||||
"magic2",
|
||||
"precious plastic",
|
||||
"onearmy"
|
||||
] as const;
|
||||
|
||||
export const FILTER_MAP: Readonly<Record<string, string>> = {
|
||||
Router: "CNC Router",
|
||||
"laptop stand": "laptoppie",
|
||||
Car: "tufftuff"
|
||||
} as const;
|
||||
export const BLACKLIST = filterConfig.BLACKLIST;
|
||||
export const URL_BLACKLIST = filterConfig.URL_BLACKLIST;
|
||||
export const WORD_BLACKLIST = filterConfig.WORD_BLACKLIST;
|
||||
export const FILTER_MAP: Record<string, string> = filterConfig.FILTER_MAP;
|
||||
|
||||
/**
|
||||
* Shortens a URL by removing 'www.' prefix and trailing slashes
|
||||
|
||||
267
src/components/howtos/DetailDev.astro
Normal file
267
src/components/howtos/DetailDev.astro
Normal file
@ -0,0 +1,267 @@
|
||||
---
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { decode } from "html-entities";
|
||||
import { IHowto, asset_local_rel } from "@/model/howto/howto.js";
|
||||
import { Img } from "imagetools/components";
|
||||
import { i18n as Translate } from "@polymech/astro-base";
|
||||
import BaseLayout from "@/layouts/BaseLayout.astro";
|
||||
import Wrapper from "@/components/containers/Wrapper.astro";
|
||||
import GalleryK from "@/components/polymech/gallery.astro";
|
||||
import { files, forward_slash } from "@polymech/commons";
|
||||
import pMap from "p-map";
|
||||
import { sync as exists } from "@polymech/fs/exists";
|
||||
import { sync as read } from "@polymech/fs/read";
|
||||
import { createHTMLComponent, createMarkdownComponent } from "@/base/index.js";
|
||||
import { translate } from "@/base/i18n.js";
|
||||
import { applyFilters, shortenUrl } from "@/base/filters.js";
|
||||
// import { extract, extract_learned_skills, references } from "@/base/kbot.js";
|
||||
import {
|
||||
HOWTO_FILES_WEB,
|
||||
HOWTO_FILES_ABS,
|
||||
I18N_SOURCE_LANGUAGE,
|
||||
HOWTO_COMPLETE_RESOURCES,
|
||||
HOWTO_COMPLETE_SKILLS,
|
||||
HOWTO_ADD_HARDWARE,
|
||||
HOWTO_LOCAL_RESOURCES,
|
||||
HOWTO_ADD_RESOURCES,
|
||||
HOWTO_ADD_REFERENCES,
|
||||
HOWTO_EDIT_URL,
|
||||
} from "config/config.js";
|
||||
import { filter } from "@/base/kbot.js";
|
||||
|
||||
interface Props {
|
||||
howto: IHowto;
|
||||
}
|
||||
const { frontmatter: howto } = Astro.props;
|
||||
const howto_abs = HOWTO_FILES_ABS(howto.slug);
|
||||
|
||||
let model_files: any = [...files(howto_abs, "**/**/*.(step|stp)")];
|
||||
model_files = model_files.map((f) =>
|
||||
forward_slash(`${howto.slug}/${path.relative(path.resolve(howto_abs), 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));
|
||||
|
||||
const stepsWithFilteredMarkdown = await pMap(
|
||||
howto.steps,
|
||||
async (step) => ({
|
||||
...step,
|
||||
filteredMarkdownComponent: await component(step.text),
|
||||
}),
|
||||
{ concurrency: 1 },
|
||||
);
|
||||
|
||||
const Description = component(howto.description);
|
||||
const authorGeo = howto?.user?.geo || {
|
||||
countryName: "Unknown",
|
||||
data: { urls: [] },
|
||||
};
|
||||
|
||||
const authorLinks = (howto?.user?.data.urls || []).filter(
|
||||
(l) => !l.url.includes("one_army") && !l.url.includes("bazar"),
|
||||
);
|
||||
//////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Resources
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
const howto_resources_default = `# Resources`;
|
||||
const howto_resources_path = path.join(howto_abs, "resources.md");
|
||||
|
||||
let howto_resources = exists(howto_resources_path)
|
||||
? read(howto_resources_path, "string") || howto_resources_default
|
||||
: howto_resources_default;
|
||||
|
||||
const howto_references_default = `# References`;
|
||||
const howto_references_path = path.join(howto_abs, "references.md");
|
||||
let howto_references = exists(howto_resources_path)
|
||||
? read(howto_references_path, "string") || howto_references_default
|
||||
: howto_references_default;
|
||||
|
||||
const contentAll = `${howto.content}`;
|
||||
|
||||
if (HOWTO_COMPLETE_SKILLS) {
|
||||
const references_extra = await filter(contentAll, "learned_skills");
|
||||
howto_resources = `${howto_resources}\n\n ${references_extra}`;
|
||||
}
|
||||
if (HOWTO_LOCAL_RESOURCES && howto.user && howto.user.geo) {
|
||||
const references_extra = await filter(
|
||||
`Location: ${authorGeo.countryName}`,
|
||||
"local",
|
||||
);
|
||||
howto_resources = `${howto_resources}\n\n ${references_extra}`;
|
||||
}
|
||||
|
||||
const Resources = component(howto_resources);
|
||||
const References = component(howto_references);
|
||||
/*
|
||||
const EditLink = () => {
|
||||
return (
|
||||
<a href={HOWTO_EDIT_URL(howto.slug, Astro.currentLocale)}>Edit</a>
|
||||
)
|
||||
}
|
||||
*/
|
||||
|
||||
---
|
||||
|
||||
<BaseLayout class="markdown-content bg-gray-100" frontmatter={howto}>
|
||||
<Wrapper>
|
||||
<article class="bg-white shadow-lg rounded-lg overflow-hidden">
|
||||
<header class="p-4 ">
|
||||
<h1 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) => (
|
||||
<li>
|
||||
<strong>{link.name}:</strong>
|
||||
<a
|
||||
class="text-orange-600 underline"
|
||||
href={link.url}
|
||||
target="_blank"
|
||||
>
|
||||
{shortenUrl(link.url)}
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
<li>
|
||||
<strong><Translate>Downloads:</Translate></strong>{
|
||||
howto.total_downloads
|
||||
}
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section 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 class="px-8 py-8 bg-orange-50">
|
||||
<h2 class="font-bold mb-4 text-xl">
|
||||
<Translate>Table of Contents</Translate>
|
||||
</h2>
|
||||
<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>
|
||||
</section>
|
||||
|
||||
<section class="border-gray-300 p-0 lg:p-6 mt-8">
|
||||
<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"
|
||||
>
|
||||
<Translate>{step.title}</Translate>
|
||||
</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="markdown-content">
|
||||
<step.filteredMarkdownComponent />
|
||||
</div>
|
||||
{step.images?.length > 0 && <GalleryK images={step.images} />}
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section class="bg-white shadow-lg rounded-lg border-gray-300 p-4 lg:p-6 mt-8 markdown-content"><Resources /></section>
|
||||
<section class="bg-white shadow-lg rounded-lg border-gray-300 p-4 lg:p-6 mt-8 markdown-content"><References /></section>
|
||||
<footer 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>
|
||||
</BaseLayout>
|
||||
@ -5,7 +5,7 @@ import { sync as read } from '@polymech/fs/read'
|
||||
import { sanitizeUri } from 'micromark-util-sanitize-uri'
|
||||
|
||||
// LLM
|
||||
export const LLM_CACHE = false
|
||||
export const LLM_CACHE = true
|
||||
export const LLM_CACHE_DIR = './.cache/kbot'
|
||||
|
||||
export const OSR_ROOT = () => path.resolve(resolve("${OSR_ROOT}"))
|
||||
@ -32,11 +32,11 @@ export const HOWTO_ANNOTATIONS = false
|
||||
export const HOWTO_ANNOTATIONS_CACHE = false
|
||||
export const HOWTO_COMPLETE_RESOURCES = true
|
||||
export const HOWTO_ADD_HARDWARE = false
|
||||
export const HOWTO_ADD_RESOURCES = true
|
||||
export const HOWTO_ADD_REFERENCES = true
|
||||
export const HOWTO_ADD_RESOURCES = false
|
||||
export const HOWTO_ADD_REFERENCES = false
|
||||
export const HOWTO_COMPLETE_SKILLS = false
|
||||
export const HOWTO_LOCAL_RESOURCES = false
|
||||
export const HOWTO_SEO_LLM = true
|
||||
export const HOWTO_SEO_LLM = false
|
||||
export const HOWTO_MAX_ITEMS = 1
|
||||
|
||||
export const HOWTO_MIGRATION = () => path.resolve(resolve("./data/last.json"))
|
||||
|
||||
30
src/config/filters.json
Normal file
30
src/config/filters.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"BLACKLIST": [
|
||||
"precious-plastic",
|
||||
"fair-enough",
|
||||
"mad-plastic-labs",
|
||||
"easymoulds",
|
||||
"plasticpreneur",
|
||||
"sustainable-design-studio",
|
||||
"johannplasto"
|
||||
],
|
||||
"URL_BLACKLIST": [
|
||||
"preciousplastic.com",
|
||||
"community.preciousplastic.com",
|
||||
"bazar.preciousplastic.com",
|
||||
"onearmy.earth",
|
||||
"davehakkens.nl",
|
||||
"sustainabledesign.studio"
|
||||
],
|
||||
"WORD_BLACKLIST": [
|
||||
"wizard",
|
||||
"magic2",
|
||||
"precious plastic",
|
||||
"onearmy"
|
||||
],
|
||||
"FILTER_MAP": {
|
||||
"Router": "CNC Router",
|
||||
"laptop stand": "laptoppie",
|
||||
"Car": "tufftuff"
|
||||
}
|
||||
}
|
||||
@ -168,10 +168,25 @@ export const defaults = async (data: any, cwd: string, root: string) => {
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const commons = async (text: string): Promise<string> => {
|
||||
return await template_filter(text, 'simple', TemplateContext.COMMONS);
|
||||
// language filter (context: commons, options: simple), see src/config/templates/commons.json
|
||||
const llm_language = async (text: string): Promise<string> => await template_filter(text, 'simple', TemplateContext.COMMONS)
|
||||
|
||||
// default filters (plain), see src/config/filters.json
|
||||
const filters_default = async (str: string, filters: FilterFunction[] = default_filters_plain) => await applyFilters(str, filters)
|
||||
|
||||
// combined content filter that applies both filters in reverse order (LLM first, then filters)
|
||||
const content = async (str: string, filters: FilterFunction[] = default_filters_plain): Promise<string> => {
|
||||
// first apply LLM language filter
|
||||
const llm_filtered = await template_filter(str, 'simple', TemplateContext.COMMONS)
|
||||
// then apply default text filters
|
||||
return await applyFilters(llm_filtered, [...filters, validateLinks])
|
||||
}
|
||||
const content = async (str: string, filters: FilterFunction[] = default_filters_plain) => await applyFilters(str, filters)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Exports
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const to_github = async (item: IHowto) => {
|
||||
const itemDir = item_path(item)
|
||||
@ -312,18 +327,10 @@ const complete = async (item: IHowto) => {
|
||||
const configPath = path.join(item_path(item), 'config.json')
|
||||
const config = read(configPath, 'json') as IHowto || {}
|
||||
// item = { ...item, ...config }
|
||||
|
||||
if (!HOWTO_ANNOTATIONS) {
|
||||
// return item
|
||||
}
|
||||
// commons: language, tone, bullshit filter, and a piece of love, just a bit, at least :)
|
||||
// Apply content filtering respecting HOWTO_FILTER_LLM flag
|
||||
if (HOWTO_FILTER_LLM) {
|
||||
item.description = await commons(item.description || '')
|
||||
}
|
||||
|
||||
item.description = await applyFilters(item.description || '', [validateLinks])
|
||||
|
||||
// default pass, links, words, formatting, ...
|
||||
item.description = await content(item.description || '')
|
||||
// Process steps with content filtering
|
||||
item.steps = await pMap(
|
||||
item.steps,
|
||||
async (step) => ({
|
||||
@ -331,14 +338,16 @@ const complete = async (item: IHowto) => {
|
||||
text: await content(step.text)
|
||||
})
|
||||
)
|
||||
} else {
|
||||
// Just apply validateLinks if LLM filtering is disabled
|
||||
item.description = await applyFilters(item.description || '', [validateLinks])
|
||||
|
||||
// commons: language, tone, bullshit filter, and a piece of love, just a bit, at least :)
|
||||
if (HOWTO_FILTER_LLM) {
|
||||
// Apply only default filters to steps
|
||||
item.steps = await pMap(
|
||||
item.steps,
|
||||
async (step) => ({
|
||||
...step,
|
||||
text: await commons(step.text)
|
||||
text: await filters_default(step.text)
|
||||
})
|
||||
)
|
||||
}
|
||||
@ -350,16 +359,14 @@ const complete = async (item: IHowto) => {
|
||||
...item.steps.map(step => step.text)
|
||||
].filter(Boolean).join('\n\n')
|
||||
|
||||
// Generate keywords using the keywords template
|
||||
if (HOWTO_ADD_RESOURCES) {
|
||||
item.keywords = await template_filter(item.content, 'keywords', TemplateContext.HOWTO);
|
||||
item.resources = await template_filter(item.content, 'toolsAndHardware', 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);
|
||||
write(path.join(item_path(item), 'resources.md'), item.resources as string)
|
||||
}
|
||||
if (HOWTO_ADD_REFERENCES) {
|
||||
item.keywords = await template_filter(item.content, 'keywords', TemplateContext.HOWTO);
|
||||
item.references = await template_filter(item.content, 'references', TemplateContext.HOWTO);
|
||||
// item.references = await template_filter(item.content, 'references', TemplateContext.HOWTO);
|
||||
item.references = await applyFilters(item.references, default_filters_markdown);
|
||||
write(path.join(item_path(item), 'references.md'), item.references as string)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user