config | pm-astro cli
This commit is contained in:
parent
5c81694024
commit
940be340fa
@ -4,10 +4,15 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "tsc -p . --watch",
|
||||
"build": "tsc -p . "
|
||||
"build": "tsc -p . ",
|
||||
"build:cli": "tsc -p tsconfig.cli.json"
|
||||
},
|
||||
"bin": {
|
||||
"pm-astro": "./dist/bin.js"
|
||||
},
|
||||
"exports": {
|
||||
".": "./dist/index.js",
|
||||
"./app/*": "./src/app/*",
|
||||
"./plugins/*": "./plugins/*",
|
||||
"./base/*": "./dist/base/*",
|
||||
"./model/*": "./dist/model/*",
|
||||
@ -30,6 +35,7 @@
|
||||
"@polymech/i18n": "file:../../../polymech-mono/packages/i18n",
|
||||
"@polymech/kbot-d": "file:../../../polymech-mono/packages/kbot",
|
||||
"@polymech/log": "file:../../../polymech-mono/packages/log",
|
||||
"@types/yargs": "^17.0.35",
|
||||
"astro": "^5.13.2",
|
||||
"exifreader": "^4.31.1",
|
||||
"find-up": "^7.0.0",
|
||||
@ -43,6 +49,7 @@
|
||||
"mdast-util-to-string": "^4.0.0",
|
||||
"node-xlsx": "^0.24.0",
|
||||
"p-map": "^7.0.3",
|
||||
"quicktype-core": "^23.2.6",
|
||||
"react-jsx-parser": "^2.4.0",
|
||||
"reading-time": "^1.5.0",
|
||||
"rehype-stringify": "^10.0.1",
|
||||
@ -54,4 +61,4 @@
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"yargs": "^18.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
111
packages/polymech/ref/main.ts
Normal file
111
packages/polymech/ref/main.ts
Normal file
@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env node
|
||||
import yargs from 'yargs'
|
||||
import { hideBin } from 'yargs/helpers'
|
||||
import { toYargs } from '@polymech/commons'
|
||||
import { createLogger } from '@polymech/log'
|
||||
|
||||
import { OptionsSchema, schemas, types } from './zod_schema.js'
|
||||
import { IKBotTask } from '@polymech/ai-tools'
|
||||
|
||||
import helpCommand from './commands/help.js'
|
||||
import { examples } from './commands/examples.js'
|
||||
import { init } from './commands/init.js'
|
||||
import { build } from './commands/build.js'
|
||||
import { fetch } from './commands/fetch.js'
|
||||
import { run } from './commands/run.js'
|
||||
|
||||
import { transcribeCommand, TranscribeOptionsSchema } from './commands/transcribe.js'
|
||||
import { imageCommand, ImageOptionsSchema } from './commands/images.js'
|
||||
import { ttsCommand, TTSOptionsSchema } from './commands/tts.js'
|
||||
|
||||
export const logger: any = createLogger('llm-tools')
|
||||
|
||||
const modify = async (argv: IKBotTask) => await run(argv as IKBotTask)
|
||||
|
||||
const yargOptions: any = {
|
||||
onKey: ((_yargs, key, options) => {
|
||||
switch (key) {
|
||||
case 'prompt':
|
||||
{
|
||||
return _yargs.positional(key, options)
|
||||
}
|
||||
case 'include':
|
||||
{
|
||||
return _yargs.option(key, {...options, alias: key[0].toLowerCase()})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
yargs(hideBin(process.argv))
|
||||
.command(
|
||||
'init',
|
||||
'Initialize KBot configuration',
|
||||
(yargs) => toYargs(yargs, OptionsSchema(), yargOptions),
|
||||
init
|
||||
)
|
||||
.command(
|
||||
'modify [prompt]',
|
||||
'Modify an existing project',
|
||||
(yargs) => toYargs(yargs, OptionsSchema(), yargOptions),
|
||||
modify
|
||||
)
|
||||
.command(
|
||||
'image [prompt]',
|
||||
'Create or edit an image',
|
||||
(yargs) => toYargs(yargs, ImageOptionsSchema(), yargOptions),
|
||||
imageCommand
|
||||
)
|
||||
.command(
|
||||
'transcribe',
|
||||
'Transcribe audio files',
|
||||
(yargs) => toYargs(yargs, TranscribeOptionsSchema(), yargOptions),
|
||||
transcribeCommand
|
||||
)
|
||||
.command(
|
||||
'tts',
|
||||
'Convert text to speech using ElevenLabs',
|
||||
(yargs) => toYargs(yargs, TTSOptionsSchema(), yargOptions),
|
||||
ttsCommand
|
||||
)
|
||||
.command(
|
||||
'types',
|
||||
'Generate types',
|
||||
(yargs) => { },
|
||||
(argv) => types()
|
||||
)
|
||||
.command(
|
||||
'schemas',
|
||||
'Generate schemas',
|
||||
(yargs) => { },
|
||||
(argv) => schemas()
|
||||
)
|
||||
.command(
|
||||
'build',
|
||||
'Build kbot essentials',
|
||||
(yargs) => { },
|
||||
(argv) => build()
|
||||
)
|
||||
.command(
|
||||
'fetch',
|
||||
"Fetch models, to $HOME/.kbot/",
|
||||
(yargs) => { },
|
||||
(argv) => fetch()
|
||||
)
|
||||
.command(
|
||||
'help-md',
|
||||
'Generate markdown help',
|
||||
(yargs) => { },
|
||||
helpCommand
|
||||
)
|
||||
.command(
|
||||
'examples',
|
||||
'Show examples',
|
||||
(yargs) => { },
|
||||
examples
|
||||
)
|
||||
.command(['modify [prompt]', '$0'], 'Default command modify',
|
||||
(yargs) => toYargs(yargs, OptionsSchema(), yargOptions), modify)
|
||||
.help()
|
||||
//.wrap(yargs.terminalWidth() - 20)
|
||||
.parse()
|
||||
224
packages/polymech/ref/resize.ts
Normal file
224
packages/polymech/ref/resize.ts
Normal file
@ -0,0 +1,224 @@
|
||||
import * as CLI from 'yargs'
|
||||
import { logger } from '../index.js'
|
||||
import {
|
||||
resize,
|
||||
ResizeOptionsSchema
|
||||
} from '../lib/media/images/resize.js'
|
||||
import { cli } from '../cli.js'
|
||||
import {
|
||||
sanitize,
|
||||
defaults
|
||||
} from '../_cli.js'
|
||||
import { toYargs } from '@polymech/commons'
|
||||
|
||||
import {
|
||||
IOptions,
|
||||
IResizeOptions
|
||||
} from '../types.js'
|
||||
|
||||
export const defaultOptions = (yargs: CLI.Argv) => {
|
||||
return toYargs(yargs, ResizeOptionsSchema(), {
|
||||
onKey: ((yargs, key, options) => {
|
||||
switch (key) {
|
||||
case 'src':
|
||||
return yargs.positional(key, {...options, demandOption: true})
|
||||
default:
|
||||
return yargs.option(key, options)
|
||||
}
|
||||
})
|
||||
}).option('gui', {
|
||||
boolean: true,
|
||||
default: false,
|
||||
describe: 'Launch interactive graphical user interface'
|
||||
})
|
||||
}
|
||||
|
||||
export const command = 'resize';
|
||||
export const desc = 'Resizes files';
|
||||
export const builder = defaultOptions;
|
||||
|
||||
export async function handler(argv: CLI.Arguments) {
|
||||
defaults()
|
||||
const options = sanitize(argv) as IOptions & IResizeOptions
|
||||
logger.settings.minLevel = options.logLevel as any
|
||||
|
||||
// Check if GUI mode is requested
|
||||
if (options.gui) {
|
||||
const { renderSchemaUI } = await import('../lib/ui/electron.js')
|
||||
const { ResizeOptionsSchema } = await import('../lib/media/images/resize.js')
|
||||
|
||||
// Generate schema for UI
|
||||
const schema = ResizeOptionsSchema()
|
||||
|
||||
// Convert Zod schema to JSON Schema (enhanced)
|
||||
const jsonSchema = {
|
||||
type: 'object',
|
||||
title: 'Resize Image Options',
|
||||
properties: {
|
||||
src: {
|
||||
type: 'string',
|
||||
title: 'Source Files',
|
||||
description: 'Source file(s) or directory to resize (supports glob patterns)',
|
||||
examples: ['image.jpg', '*.png', './photos/', 'images/**/*.jpg']
|
||||
},
|
||||
dst: {
|
||||
type: 'string',
|
||||
title: 'Destination',
|
||||
description: 'Output directory or file pattern for resized images'
|
||||
},
|
||||
width: {
|
||||
type: 'integer',
|
||||
title: 'Width (pixels)',
|
||||
description: 'Target width in pixels',
|
||||
minimum: 1,
|
||||
maximum: 10000
|
||||
},
|
||||
height: {
|
||||
type: 'integer',
|
||||
title: 'Height (pixels)',
|
||||
description: 'Target height in pixels',
|
||||
minimum: 1,
|
||||
maximum: 10000
|
||||
},
|
||||
percent: {
|
||||
type: 'number',
|
||||
title: 'Resize Percentage',
|
||||
description: 'Resize by percentage (e.g., 50 for 50%)',
|
||||
minimum: 1,
|
||||
maximum: 1000
|
||||
},
|
||||
square: {
|
||||
type: 'boolean',
|
||||
title: 'Square Output',
|
||||
description: 'Fit image within specified width to create 1:1 aspect ratio',
|
||||
default: false
|
||||
},
|
||||
fillColor: {
|
||||
type: 'string',
|
||||
title: 'Background Fill Color',
|
||||
description: 'Color to use for background when creating square images',
|
||||
default: 'white',
|
||||
examples: ['white', 'black', 'transparent', '#FF0000']
|
||||
},
|
||||
fit: {
|
||||
type: 'string',
|
||||
title: 'Resize Fit Mode',
|
||||
description: 'How the image should be resized to fit the dimensions',
|
||||
enum: ['cover', 'contain', 'fill', 'inside', 'outside'],
|
||||
default: 'cover'
|
||||
},
|
||||
withoutEnlargement: {
|
||||
type: 'boolean',
|
||||
title: 'Prevent Enlargement',
|
||||
description: 'Do not enlarge images that are smaller than target size',
|
||||
default: false
|
||||
},
|
||||
quality: {
|
||||
type: 'integer',
|
||||
title: 'Quality',
|
||||
description: 'Output quality for JPEG images (1-100)',
|
||||
minimum: 1,
|
||||
maximum: 100,
|
||||
default: 90
|
||||
}
|
||||
},
|
||||
required: ['src'],
|
||||
anyOf: [
|
||||
{
|
||||
required: ['width']
|
||||
},
|
||||
{
|
||||
required: ['height']
|
||||
},
|
||||
{
|
||||
required: ['percent']
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const uiSchema = {
|
||||
'ui:order': ['src', 'dst', 'width', 'height', 'percent', 'fit', 'square', 'fillColor', 'withoutEnlargement', 'quality'],
|
||||
src: {
|
||||
'ui:placeholder': 'e.g., *.jpg or ./photos/',
|
||||
'ui:help': 'Supports files, directories, and glob patterns',
|
||||
'ui:widget': 'textarea',
|
||||
'ui:options': {
|
||||
rows: 2
|
||||
}
|
||||
},
|
||||
dst: {
|
||||
'ui:placeholder': 'e.g., ./resized/ or output.jpg',
|
||||
'ui:help': 'Output directory or file pattern'
|
||||
},
|
||||
width: {
|
||||
'ui:help': 'Leave empty to maintain aspect ratio'
|
||||
},
|
||||
height: {
|
||||
'ui:help': 'Leave empty to maintain aspect ratio'
|
||||
},
|
||||
percent: {
|
||||
'ui:help': 'Alternative to width/height - resize by percentage'
|
||||
},
|
||||
fit: {
|
||||
'ui:help': 'How to fit image when both width and height are specified'
|
||||
},
|
||||
fillColor: {
|
||||
'ui:help': 'Used when square mode creates padding',
|
||||
'ui:placeholder': 'white, black, transparent, #FF0000'
|
||||
},
|
||||
square: {
|
||||
'ui:help': 'Creates square images by padding with fillColor'
|
||||
},
|
||||
withoutEnlargement: {
|
||||
'ui:help': 'Prevents making small images larger'
|
||||
},
|
||||
quality: {
|
||||
'ui:help': 'Only applies to JPEG output files'
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Extract CLI options that were provided
|
||||
const cliOptions: any = {};
|
||||
if (options.src) cliOptions.src = options.src;
|
||||
if (options.dst) cliOptions.dst = options.dst;
|
||||
if (options.width) cliOptions.width = options.width;
|
||||
if (options.height) cliOptions.height = options.height;
|
||||
if (options.percent) cliOptions.percent = options.percent;
|
||||
if (options.square !== undefined) cliOptions.square = options.square;
|
||||
if (options.fillColor) cliOptions.fillColor = options.fillColor;
|
||||
if (options.fit) cliOptions.fit = options.fit;
|
||||
if (options.withoutEnlargement !== undefined) cliOptions.withoutEnlargement = options.withoutEnlargement;
|
||||
|
||||
const result = await renderSchemaUI({
|
||||
schema: jsonSchema,
|
||||
uiSchema,
|
||||
title: 'Resize Images',
|
||||
formData: {
|
||||
fillColor: 'white',
|
||||
square: false,
|
||||
fit: 'cover',
|
||||
withoutEnlargement: false,
|
||||
quality: 90
|
||||
},
|
||||
cliOptions
|
||||
})
|
||||
|
||||
if (result && result.data) {
|
||||
// Merge UI result with CLI options
|
||||
const uiOptions = { ...options, ...result.data }
|
||||
await resize(uiOptions)
|
||||
} else {
|
||||
logger.info('Operation cancelled by user')
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('GUI Error:', error)
|
||||
logger.info('Falling back to CLI mode...')
|
||||
await resize(options)
|
||||
}
|
||||
} else {
|
||||
await resize(options)
|
||||
}
|
||||
}
|
||||
|
||||
cli.command(command, desc, builder, handler)
|
||||
345
packages/polymech/ref/zod_schema.ts
Normal file
345
packages/polymech/ref/zod_schema.ts
Normal file
@ -0,0 +1,345 @@
|
||||
import { z } from 'zod'
|
||||
import * as path from 'node:path'
|
||||
import chalk from 'chalk'
|
||||
import env from 'env-var'
|
||||
import { generate_interfaces, ZodMetaMap, resolve, write } from '@polymech/commons'
|
||||
import { sync as exists } from '@polymech/fs/exists'
|
||||
import { sync as writeFS } from '@polymech/fs/write'
|
||||
import { sync as readFS } from '@polymech/fs/read'
|
||||
import { isArray, isFunction, isString } from '@polymech/core/primitives'
|
||||
import { zodResponseFormat } from "openai/helpers/zod"
|
||||
|
||||
import { API_PREFIX, LOGGING_DIRECTORY, PREFERENCES_FILE_NAME } from './constants.js'
|
||||
|
||||
export const get_var = (key: string = '') => env.get(key).asString() || env.get(key.replace(/-/g, '_')).asString() || env.get(key.replace(/_/g, '-')).asString()
|
||||
export const HOME = (sub = '') => path.join(process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'] || '', sub)
|
||||
export const PREFERENCES_DEFAULT = (key: string = 'KBOT_PREFERENCES') => get_var(key) || path.join(HOME(`.${API_PREFIX}`), PREFERENCES_FILE_NAME)
|
||||
import { jsonSchemaToZod } from "json-schema-to-zod"
|
||||
import { Filters } from './filters.js'
|
||||
import { models_dist } from './models/index.js'
|
||||
import { defaultTemplate } from './tools.js'
|
||||
|
||||
export const E_Filters = z.enum(Object.keys(Filters) as any)
|
||||
|
||||
export const E_RouterTypeSchema = z.enum(['openrouter', 'openai', 'deepseek', 'huggingface', 'ollama', 'fireworks', 'gemini', 'xai'])
|
||||
export type E_RouterType = z.infer<typeof E_RouterTypeSchema>
|
||||
|
||||
export const E_Mode = {
|
||||
COMPLETION: 'completion',
|
||||
TOOLS: 'tools',
|
||||
ASSISTANT: 'assistant',
|
||||
RESPONSES: 'responses',
|
||||
CUSTOM: 'custom'
|
||||
} as const
|
||||
|
||||
export const EType = z.enum([
|
||||
E_Mode.COMPLETION,
|
||||
E_Mode.TOOLS,
|
||||
E_Mode.ASSISTANT,
|
||||
E_Mode.RESPONSES,
|
||||
E_Mode.CUSTOM
|
||||
])
|
||||
|
||||
// Define the new enum for append modes
|
||||
export const E_AppendMode = z.enum(['concat', 'merge', 'replace'])
|
||||
export type E_AppendModeType = z.infer<typeof E_AppendMode>
|
||||
|
||||
|
||||
// Define the new enum for wrap modes
|
||||
export const E_WrapMode = z.enum(['meta', 'none'])
|
||||
export type E_WrapModeType = z.infer<typeof E_WrapMode>
|
||||
|
||||
// Define the new enum for glob extensions (presets)
|
||||
export const E_GlobExtension = z.enum(['match-cpp']) // Add more presets here later if needed
|
||||
export type E_GlobExtensionType = z.infer<typeof E_GlobExtension>
|
||||
|
||||
export type OptionsSchemaMeta = Record<string, unknown>
|
||||
|
||||
export { fetchOpenRouterModels, listModelsAsStrings as listOpenRouterModelsAsStrings } from './models/openrouter.js'
|
||||
export { fetchOpenAIModels, listModelsAsStrings as listOpenAIModelsAsStrings } from './models/openai.js'
|
||||
|
||||
let schemaMap
|
||||
|
||||
export const OptionsSchema = (opts?: any): any => {
|
||||
|
||||
schemaMap = ZodMetaMap.create<OptionsSchemaMeta>()
|
||||
schemaMap.add(
|
||||
'path',
|
||||
z.string()
|
||||
.min(1)
|
||||
.default('.')
|
||||
.describe('Target directory')
|
||||
, { 'ui:widget': 'file' })
|
||||
.add(
|
||||
'prompt',
|
||||
z.string()
|
||||
.describe('The prompt. Supports file paths and environment variables.')
|
||||
.optional()
|
||||
)
|
||||
.add(
|
||||
'output',
|
||||
z.string()
|
||||
.optional()
|
||||
.describe('Optional output path for modified files (Tool mode only)')
|
||||
)
|
||||
.add(
|
||||
'dst',
|
||||
z.string()
|
||||
.optional()
|
||||
.describe('Optional destination path for the result, will substitute ${MODEL_NAME} and ${ROUTER} in the path. Optional, used for "completion" mode')
|
||||
)
|
||||
.add(
|
||||
'append',
|
||||
E_AppendMode
|
||||
.optional()
|
||||
.describe('How to handle output if --dst file already exists: "concat" (append) or "merge" (try to merge structures if possible, otherwise append). Only used if --dst is specified.')
|
||||
)
|
||||
.add(
|
||||
'wrap',
|
||||
E_WrapMode
|
||||
.default('none')
|
||||
.describe('Specify how to wrap the output, "meta (file name, absolute path, cwd)" or "none".')
|
||||
)
|
||||
.add(
|
||||
'each',
|
||||
z.string()
|
||||
.optional()
|
||||
.describe('Iterate over items, supported: GLOB | Path to JSON File | array of strings (comma separated). To test different models, use --each="gpt-3.5-turbo,gpt-4o", the actual string will exposed as variable `ITEM`, eg: --dst="${ITEM}-output.md"')
|
||||
)
|
||||
.add(
|
||||
'disable',
|
||||
z.array(z.string())
|
||||
.default([])
|
||||
.describe(`Disable tools categories, eg: --disable=${defaultTemplate.tools.join(',')}`)
|
||||
)
|
||||
.add(
|
||||
'disableTools',
|
||||
z.array(z.string())
|
||||
.optional()
|
||||
.default([])
|
||||
.describe('List of specific tools to disable')
|
||||
)
|
||||
.add(
|
||||
'tools',
|
||||
z.union(
|
||||
[
|
||||
z.array(z.string()),
|
||||
z.string()
|
||||
]).optional()
|
||||
.default(defaultTemplate.tools)
|
||||
.describe(`List of tools to use. Can be built-in tool names or paths to custom tool files. Default: ${defaultTemplate.tools.join(',')}`)
|
||||
.transform((val) => Array.isArray(val) ? val : val.split(','))
|
||||
)
|
||||
.add(
|
||||
'include',
|
||||
z.array(z.string())
|
||||
.optional()
|
||||
.describe('Comma separated glob patterns or paths, eg --include=src/*.tsx,src/*.ts --include=package.json')
|
||||
)
|
||||
.add(
|
||||
'exclude',
|
||||
z.array(z.string())
|
||||
.optional()
|
||||
.describe('Comma separated glob patterns or paths, eg --exclude=src/*.tsx,src/*.ts --exclude=package.json')
|
||||
)
|
||||
.add(
|
||||
'globExtension',
|
||||
z.union([E_GlobExtension, z.string()]) // Allow preset enum or custom string
|
||||
.optional()
|
||||
.describe(
|
||||
'Specify a glob extension behavior. Available presets: ' + E_GlobExtension.options.join(', ') +
|
||||
'. Also accepts a custom glob pattern with variables like ${SRC_DIR}, ${SRC_NAME}, ${SRC_EXT}. ' +
|
||||
'E.g., \"match-cpp\" or \"${SRC_DIR}/${SRC_NAME}*.cpp\"'
|
||||
)
|
||||
)
|
||||
.add(
|
||||
'api_key',
|
||||
z.string()
|
||||
.optional()
|
||||
.describe('Explicit API key to use')
|
||||
)
|
||||
.add(
|
||||
'model',
|
||||
z.string()
|
||||
.optional()
|
||||
.describe(`AI model to use for processing. Available models:\n${models_dist().join('\n')}`)
|
||||
)
|
||||
.add(
|
||||
'router',
|
||||
z.string()
|
||||
.default('openrouter')
|
||||
.describe('Router to use: openai, openrouter or deepseek')
|
||||
)
|
||||
.add(
|
||||
'mode',
|
||||
EType
|
||||
.default(E_Mode.TOOLS)
|
||||
.describe(`Chat completion mode:\n\t completion, tools, assistant.
|
||||
${chalk.green.bold('completion')}: no support for tools, please use --dst parameter to save the output.
|
||||
${chalk.green.bold('tools')}: allows for tools to be used, eg 'save to ./output.md'. Not all models support this mode.
|
||||
${chalk.green.bold('responses')}: allows for responses to be used, eg 'save to ./output.md'. Not all models support this mode.
|
||||
${chalk.green.bold('assistant')}: : allows documents (PDF, DOCX, ...) to be added but dont support tools. Use --dst to save the output. Supported files :
|
||||
${chalk.green.bold('custom')}: custom mode
|
||||
`)
|
||||
)
|
||||
.add(
|
||||
'logLevel',
|
||||
z.number()
|
||||
.default(4)
|
||||
.describe('Logging level for the application')
|
||||
)
|
||||
.add(
|
||||
'profile',
|
||||
z.string()
|
||||
.optional()
|
||||
.describe('Path to profile for variables. Supports environment variables.')
|
||||
)
|
||||
.add(
|
||||
'baseURL',
|
||||
z.string()
|
||||
.optional()
|
||||
.describe('Base URL for the API, set via --router or directly')
|
||||
)
|
||||
.add(
|
||||
'config',
|
||||
z.string()
|
||||
.optional()
|
||||
.describe('Path to JSON configuration file (API keys). Supports environment variables.')
|
||||
)
|
||||
.add(
|
||||
'dump',
|
||||
z.string()
|
||||
.optional()
|
||||
.describe('Create a script')
|
||||
)
|
||||
.add(
|
||||
'preferences',
|
||||
z.string()
|
||||
.default(PREFERENCES_DEFAULT())
|
||||
.describe('Path to preferences file, eg: location, your email address, gender, etc. Supports environment variables.')
|
||||
)
|
||||
.add(
|
||||
'logs',
|
||||
z.string()
|
||||
.default(LOGGING_DIRECTORY)
|
||||
.describe('Logging directory')
|
||||
).add(
|
||||
'stream',
|
||||
z.boolean()
|
||||
.default(false)
|
||||
.describe('Enable streaming (verbose LLM output)')
|
||||
)
|
||||
.add(
|
||||
'alt',
|
||||
z.boolean()
|
||||
.default(false)
|
||||
.describe('Use alternate tokenizer & instead of $')
|
||||
)
|
||||
.add(
|
||||
'env',
|
||||
z.string()
|
||||
.default('default')
|
||||
.describe('Environment (in profile)')
|
||||
)
|
||||
.add(
|
||||
'variables',
|
||||
z.record(z.string(), z.string())
|
||||
.optional()
|
||||
.default({})
|
||||
)
|
||||
.add(
|
||||
'filters',
|
||||
z.union([
|
||||
z.string(),
|
||||
z.array(E_Filters),
|
||||
z.array(z.string()),
|
||||
z.array(z.function())
|
||||
])
|
||||
.optional()
|
||||
.default('')
|
||||
.describe(`List of filters to apply to the output.
|
||||
Used only in completion mode and a given output file specified with --dst.
|
||||
It unwraps by default any code or data in Markdown.
|
||||
Choices:\n\t${Object.keys(Filters)}\n`)
|
||||
.transform((val) => {
|
||||
if (isArray(val) && val.length && isFunction(val[0])) {
|
||||
return val
|
||||
}
|
||||
let filters = isString(val) ? val.split(',') : val
|
||||
filters = filters.map((f: any) => Filters[f]).filter(Boolean)
|
||||
return filters
|
||||
})
|
||||
)
|
||||
.add(
|
||||
'query',
|
||||
z.string()
|
||||
.nullable()
|
||||
.optional()
|
||||
.default(null)
|
||||
.describe('JSONPath query to be used to transform input objects')
|
||||
)
|
||||
.add(
|
||||
'dry',
|
||||
z.union([
|
||||
z.boolean(),
|
||||
z.string().transform((val) => val.toLowerCase() === 'true')
|
||||
])
|
||||
.optional()
|
||||
.default(false)
|
||||
.describe('Dry run - only write out parameters without making API calls')
|
||||
)
|
||||
.add(
|
||||
'format',
|
||||
z.union([
|
||||
z.string().transform((val) => {
|
||||
try {
|
||||
// Check if the string is a file path
|
||||
if (exists(val) && val.endsWith('.json')) {
|
||||
const content = readFS(val);
|
||||
const schema = JSON.parse(content.toString());
|
||||
const zodSchemaStr = jsonSchemaToZod(schema);
|
||||
// Evaluate the string to get the actual Zod schema
|
||||
const zodSchema = eval(`(${zodSchemaStr})`);
|
||||
return zodResponseFormat(zodSchema, "format");
|
||||
} else {
|
||||
// Try parsing as JSON schema first
|
||||
try {
|
||||
const schema = JSON.parse(val);
|
||||
const zodSchemaStr = jsonSchemaToZod(schema);
|
||||
const zodSchema = eval(`(${zodSchemaStr})`);
|
||||
return zodResponseFormat(zodSchema, "format");
|
||||
} catch {
|
||||
// If not JSON, try evaluating as Zod schema directly
|
||||
const zodSchema = eval(`(${val})`);
|
||||
return zodResponseFormat(zodSchema, "format");
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`Error parsing format: ${e}`)
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
z.any().transform((val) => {
|
||||
// If it's already a Zod schema, use it directly
|
||||
if (val && typeof val === 'object' && 'parse' in val) {
|
||||
return zodResponseFormat(val, "format");
|
||||
}
|
||||
return null;
|
||||
})
|
||||
])
|
||||
.optional()
|
||||
.describe('Format for structured outputs. Can be a Zod schema, a Zod schema string, a JSON schema string, or a path to a JSON file.')
|
||||
);
|
||||
return schemaMap.root()
|
||||
.passthrough()
|
||||
.describe('IKBotOptions')
|
||||
}
|
||||
export const types = () => {
|
||||
generate_interfaces([OptionsSchema()], 'src/zod_types.ts')
|
||||
generate_interfaces([OptionsSchema()], path.resolve(resolve('../ai-tools/src/types_kbot.ts')))
|
||||
schemas()
|
||||
}
|
||||
export const schemas = () => {
|
||||
write([OptionsSchema()], 'schema.json', 'kbot', {})
|
||||
writeFS('schema_ui.json', schemaMap.getUISchema())
|
||||
}
|
||||
3
packages/polymech/src/app/index.ts
Normal file
3
packages/polymech/src/app/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './config.js'
|
||||
export * from './config-loader.js'
|
||||
|
||||
6
packages/polymech/src/bin.ts
Normal file
6
packages/polymech/src/bin.ts
Normal file
@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env node
|
||||
import { cli } from './cli.js';
|
||||
import './commands/build-config.js';
|
||||
import './commands/build.js';
|
||||
|
||||
cli.parse();
|
||||
8
packages/polymech/src/cli.ts
Normal file
8
packages/polymech/src/cli.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import yargs from 'yargs';
|
||||
import { hideBin } from 'yargs/helpers';
|
||||
|
||||
export const cli = yargs(hideBin(process.argv))
|
||||
.scriptName('pm-astro')
|
||||
.usage('$0 <cmd> [args]')
|
||||
.demandCommand(1, 'You need at least one command before moving on')
|
||||
.help();
|
||||
@ -1,14 +1,43 @@
|
||||
|
||||
import * as CLI from 'yargs'
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { quicktype, InputData, jsonInputForTargetLanguage } from 'quicktype-core';
|
||||
import { cli } from '../cli.js';
|
||||
|
||||
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');
|
||||
export const command = 'build:config [src]';
|
||||
export const desc = 'Generate config types and schema from JSON';
|
||||
export const builder = (yargs: CLI.Argv) => {
|
||||
return yargs
|
||||
.positional('src', {
|
||||
describe: 'Input JSON config path',
|
||||
type: 'string',
|
||||
default: './app-config.json',
|
||||
alias: 'config'
|
||||
})
|
||||
.option('dest-types', {
|
||||
alias: 'd',
|
||||
type: 'string',
|
||||
describe: 'Output d.ts path',
|
||||
default: './src/app/config.d.ts'
|
||||
})
|
||||
.option('dest-schema', {
|
||||
alias: 'z',
|
||||
type: 'string',
|
||||
describe: 'Output Zod schema path',
|
||||
default: './src/app/config.schema.ts'
|
||||
});
|
||||
};
|
||||
|
||||
export async function handler(argv: CLI.Arguments) {
|
||||
const CONFIG_PATH = path.resolve(argv.src as string);
|
||||
const OUTPUT_DTS_PATH = path.resolve(argv.destTypes as string);
|
||||
const OUTPUT_SCHEMA_PATH = path.resolve(argv.destSchema as string);
|
||||
|
||||
async function main() {
|
||||
console.log(`Reading config from ${CONFIG_PATH}...`);
|
||||
if (!fs.existsSync(CONFIG_PATH)) {
|
||||
console.error(`Config file not found at ${CONFIG_PATH}`);
|
||||
process.exit(1);
|
||||
}
|
||||
const configContent = fs.readFileSync(CONFIG_PATH, 'utf8');
|
||||
|
||||
// 1. Generate TypeScript Definitions (d.ts) FIRST
|
||||
@ -28,11 +57,12 @@ async function main() {
|
||||
lang: "ts",
|
||||
rendererOptions: {
|
||||
"just-types": "true",
|
||||
"acronym-style": "original"
|
||||
"acronym-style": "original" // Keep naming consistent
|
||||
}
|
||||
});
|
||||
|
||||
const tsCode = tsResult.lines.join('\n');
|
||||
fs.mkdirSync(path.dirname(OUTPUT_DTS_PATH), { recursive: true });
|
||||
fs.writeFileSync(OUTPUT_DTS_PATH, tsCode);
|
||||
console.log(`Wrote TypeScript definitions to ${OUTPUT_DTS_PATH}`);
|
||||
|
||||
@ -46,6 +76,9 @@ async function main() {
|
||||
const relDts = path.relative(process.cwd(), OUTPUT_DTS_PATH);
|
||||
const relSchema = path.relative(process.cwd(), OUTPUT_SCHEMA_PATH);
|
||||
|
||||
// Ensure output dir exists
|
||||
fs.mkdirSync(path.dirname(OUTPUT_SCHEMA_PATH), { recursive: true });
|
||||
|
||||
execSync(`npx ts-to-zod "${relDts}" "${relSchema}"`, { stdio: 'inherit', cwd: process.cwd() });
|
||||
|
||||
// Append export type AppConfig
|
||||
@ -58,7 +91,4 @@ async function main() {
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error('Error fetching/generating config:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
cli.command(command, desc, builder, handler);
|
||||
38
packages/polymech/src/commands/build.ts
Normal file
38
packages/polymech/src/commands/build.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { spawnSync } from 'child_process';
|
||||
import * as CLI from 'yargs';
|
||||
import { cli } from '../cli.js';
|
||||
import { handler as configHandler, builder as configBuilder } from './build-config.js';
|
||||
|
||||
export const command = 'build [src]';
|
||||
export const desc = 'Generate config and build Astro app';
|
||||
|
||||
export const builder = (yargs: CLI.Argv) => {
|
||||
return configBuilder(yargs)
|
||||
.strict(false);
|
||||
};
|
||||
|
||||
export async function handler(argv: CLI.Arguments) {
|
||||
// 1. Config Generation
|
||||
// We execute the config generation logic in-process
|
||||
await configHandler(argv);
|
||||
|
||||
// 2. Astro Build
|
||||
// We forward arguments to Astro.
|
||||
// Finding where 'build' command is in the args list.
|
||||
// Note: process.argv = [node, bin, 'build', ...flags]
|
||||
const buildIndex = process.argv.indexOf('build');
|
||||
const forwardedArgs = buildIndex !== -1 ? process.argv.slice(buildIndex + 1) : [];
|
||||
|
||||
console.log('[pm-astro] Running astro build...');
|
||||
// Execute astro build using npx to resolve local binary
|
||||
const result = spawnSync('npx', ['astro', 'build', ...forwardedArgs], {
|
||||
stdio: 'inherit',
|
||||
shell: true
|
||||
});
|
||||
|
||||
if (result.status !== 0) {
|
||||
process.exit(result.status || 1);
|
||||
}
|
||||
}
|
||||
|
||||
cli.command(command, desc, builder, handler);
|
||||
@ -5,7 +5,10 @@ export const foo2 = 2
|
||||
// export { default as Test } from './components/test.astro'
|
||||
|
||||
// Export collection utilities
|
||||
export * from './base/collections'
|
||||
export * from './base/collections.js'
|
||||
export * from './app/index.js'
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,21 +1,18 @@
|
||||
---
|
||||
import StoreLayout from '@/layouts/StoreLayout.astro'
|
||||
import { getCollection } from 'astro:content'
|
||||
import { LANGUAGES_PROD } from "config/config.js"
|
||||
import StoreLayout from "@/layouts/StoreLayout.astro";
|
||||
import { getCollection } from "astro:content";
|
||||
import { LANGUAGES_PROD } from "config/config.js";
|
||||
import StoreEntries from "@/components/store/StoreEntries.astro";
|
||||
import BaseLayout from "@/layouts/BaseLayout.astro";
|
||||
import Wrapper from "@/components/containers/Wrapper.astro";
|
||||
import Translate from "@polymech/astro-base/components/i18n.astro";
|
||||
import { translate } from "@/base/i18n.js"
|
||||
import { I18N_SOURCE_LANGUAGE } from "config/config.js"
|
||||
import { slugify } from "@/base/strings.js"
|
||||
import { slugify } from "@/base/strings.js";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const view = "store";
|
||||
const allProducts = await getCollection(view);
|
||||
const all: unknown[] = [];
|
||||
|
||||
export async function getStaticPaths()
|
||||
{
|
||||
const view = 'store'
|
||||
const allProducts = await getCollection(view)
|
||||
const all: unknown[] = []
|
||||
|
||||
LANGUAGES_PROD.forEach((lang) => {
|
||||
// Add individual product routes
|
||||
allProducts.forEach((product) => {
|
||||
@ -29,26 +26,28 @@ export async function getStaticPaths()
|
||||
locale: lang,
|
||||
path: product.id,
|
||||
view,
|
||||
type: 'product'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
type: "product",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// Add folder routes for categories
|
||||
const folders = new Set<string>()
|
||||
allProducts.forEach(product => {
|
||||
const segments = product.id.split('/').filter(segment => segment !== '')
|
||||
const folders = new Set<string>();
|
||||
allProducts.forEach((product) => {
|
||||
const segments = product.id
|
||||
.split("/")
|
||||
.filter((segment) => segment !== "");
|
||||
if (segments.length > 1) {
|
||||
// Add all possible folder paths (e.g., products, products/sheetpress, etc.)
|
||||
for (let i = 1; i < segments.length; i++) {
|
||||
const folderPath = segments.slice(0, i).join('/')
|
||||
folders.add(folderPath)
|
||||
const folderPath = segments.slice(0, i).join("/");
|
||||
folders.add(folderPath);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
// Add folder paths
|
||||
Array.from(folders).forEach(folder => {
|
||||
Array.from(folders).forEach((folder) => {
|
||||
all.push({
|
||||
params: {
|
||||
locale: lang,
|
||||
@ -56,160 +55,221 @@ export async function getStaticPaths()
|
||||
},
|
||||
props: {
|
||||
folderPath: folder,
|
||||
products: allProducts.filter(product => {
|
||||
const productFolder = product.id.split('/').slice(0, -1).join('/')
|
||||
return productFolder === folder
|
||||
products: allProducts.filter((product) => {
|
||||
const productFolder = product.id
|
||||
.split("/")
|
||||
.slice(0, -1)
|
||||
.join("/");
|
||||
return productFolder === folder;
|
||||
}),
|
||||
locale: lang,
|
||||
view,
|
||||
type: 'folder'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
type: "folder",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// Add special case for store/ (root store) - shows all products
|
||||
all.push({
|
||||
params: {
|
||||
locale: lang,
|
||||
path: '',
|
||||
path: "",
|
||||
},
|
||||
props: {
|
||||
folderPath: '',
|
||||
folderPath: "",
|
||||
products: allProducts, // All products
|
||||
locale: lang,
|
||||
view,
|
||||
type: 'folder',
|
||||
isRootStore: true
|
||||
}
|
||||
})
|
||||
|
||||
type: "folder",
|
||||
isRootStore: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Add special case for store/products/ - shows all products
|
||||
all.push({
|
||||
params: {
|
||||
locale: lang,
|
||||
path: 'products',
|
||||
path: "products",
|
||||
},
|
||||
props: {
|
||||
folderPath: 'products',
|
||||
folderPath: "products",
|
||||
products: allProducts, // All products
|
||||
locale: lang,
|
||||
view,
|
||||
type: 'folder',
|
||||
isProductsFolder: true
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return all
|
||||
type: "folder",
|
||||
isProductsFolder: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return all;
|
||||
}
|
||||
|
||||
const { page, folderPath, products, locale, type, isRootStore, isProductsFolder, ...rest } = Astro.props as any
|
||||
const {
|
||||
page,
|
||||
folderPath,
|
||||
products,
|
||||
locale,
|
||||
type,
|
||||
isRootStore,
|
||||
isProductsFolder,
|
||||
...rest
|
||||
} = Astro.props as any;
|
||||
---
|
||||
|
||||
{type === 'folder' ? (
|
||||
(() => {
|
||||
// Handle folder view - show all products in category
|
||||
let categoryTitle: string
|
||||
const categoryDescription = 'Browse our complete collection of products'
|
||||
if (isRootStore) {
|
||||
// Root store - all products
|
||||
categoryTitle = 'All Products'
|
||||
} else if (isProductsFolder) {
|
||||
// Products folder - all products
|
||||
categoryTitle = 'Products'
|
||||
} else {
|
||||
// Regular category folder
|
||||
const categoryName = folderPath.split('/').pop() || folderPath
|
||||
categoryTitle = categoryName
|
||||
}
|
||||
categoryTitle = categoryTitle.charAt(0).toUpperCase() + categoryTitle.slice(1)
|
||||
|
||||
// Group products by category for all products views
|
||||
let groupedProducts: any = {}
|
||||
if (isRootStore || isProductsFolder) {
|
||||
// Use the same grouping logic as homepage - simplified for now
|
||||
groupedProducts = products.reduce((acc, item: any) => {
|
||||
const id = item.id.split("/")[1]
|
||||
const key = slugify(id)
|
||||
.split('-')
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
||||
.join(' ')
|
||||
if (!acc[key]) {
|
||||
acc[key] = []
|
||||
{
|
||||
type === "folder"
|
||||
? (() => {
|
||||
// Handle folder view - show all products in category
|
||||
let categoryTitle: string;
|
||||
const categoryDescription =
|
||||
"Browse our complete collection of products";
|
||||
if (isRootStore) {
|
||||
// Root store - all products
|
||||
categoryTitle = "All Products";
|
||||
} else if (isProductsFolder) {
|
||||
// Products folder - all products
|
||||
categoryTitle = "Products";
|
||||
} else {
|
||||
// Regular category folder
|
||||
const categoryName =
|
||||
folderPath.split("/").pop() || folderPath;
|
||||
categoryTitle = categoryName;
|
||||
}
|
||||
acc[key].push(item)
|
||||
return acc
|
||||
}, {})
|
||||
} else {
|
||||
// For category views, just sort products by title
|
||||
groupedProducts = {
|
||||
[categoryTitle]: products.sort((a: any, b: any) =>
|
||||
a.data.title.localeCompare(b.data.title)
|
||||
)
|
||||
}
|
||||
}
|
||||
const store = `/${locale}/store/`;
|
||||
return (
|
||||
<BaseLayout>
|
||||
<Wrapper variant="standard" class="py-4">
|
||||
<section class="mb-12">
|
||||
<h1 class="mb-6 text-2xl font-semibold">
|
||||
<Translate>{categoryTitle}</Translate>
|
||||
</h1>
|
||||
<p class="text-lg text-gray-600 mb-8">
|
||||
<Translate>{categoryDescription}</Translate>
|
||||
</p>
|
||||
{Object.keys(groupedProducts).length > 0 ? (
|
||||
Object.keys(groupedProducts).map((categoryKey) => (
|
||||
<section class="mb-12">
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-2 gap-6">
|
||||
{groupedProducts[categoryKey].map((product: any) => (
|
||||
<StoreEntries
|
||||
key={product.id}
|
||||
url={store + product.id}
|
||||
title={product.data.title}
|
||||
type={product.data.type}
|
||||
alt={product.data.title}
|
||||
model={product.data}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
))
|
||||
) : (
|
||||
<div class="text-center py-12">
|
||||
<div class="text-gray-400 mb-4">
|
||||
<svg class="w-16 h-16 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-2">
|
||||
<Translate>No products in this category</Translate>
|
||||
</h3>
|
||||
<p class="text-gray-600">
|
||||
<Translate>This category appears to be empty.</Translate>
|
||||
categoryTitle =
|
||||
categoryTitle.charAt(0).toUpperCase() +
|
||||
categoryTitle.slice(1);
|
||||
|
||||
// Group products by category for all products views
|
||||
let groupedProducts: any = {};
|
||||
if (isRootStore || isProductsFolder) {
|
||||
// Use the same grouping logic as homepage - simplified for now
|
||||
groupedProducts = products.reduce((acc, item: any) => {
|
||||
const id = item.id.split("/")[1];
|
||||
const key = slugify(id)
|
||||
.split("-")
|
||||
.map(
|
||||
(word) =>
|
||||
word.charAt(0).toUpperCase() +
|
||||
word.slice(1).toLowerCase(),
|
||||
)
|
||||
.join(" ");
|
||||
if (!acc[key]) {
|
||||
acc[key] = [];
|
||||
}
|
||||
acc[key].push(item);
|
||||
return acc;
|
||||
}, {});
|
||||
} else {
|
||||
// For category views, just sort products by title
|
||||
groupedProducts = {
|
||||
[categoryTitle]: products.sort((a: any, b: any) =>
|
||||
a.data.title.localeCompare(b.data.title),
|
||||
),
|
||||
};
|
||||
}
|
||||
const store = `/${locale}/store/`;
|
||||
return (
|
||||
<BaseLayout>
|
||||
<Wrapper variant="standard" class="py-4">
|
||||
<section class="mb-12">
|
||||
<h1 class="mb-6 text-2xl font-semibold">
|
||||
<Translate>{categoryTitle}</Translate>
|
||||
</h1>
|
||||
<p class="text-lg text-gray-600 mb-8">
|
||||
<Translate>{categoryDescription}</Translate>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<div class="mt-12 text-center">
|
||||
<a
|
||||
href={`/${locale}/store/`}
|
||||
class="inline-flex items-center px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors"
|
||||
>
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/>
|
||||
</svg>
|
||||
<Translate>Back to Store</Translate>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
</Wrapper>
|
||||
</BaseLayout>
|
||||
)
|
||||
})()
|
||||
) : (
|
||||
(() => {
|
||||
const { data } = page
|
||||
return <StoreLayout frontmatter={data} {...rest}/>
|
||||
})()
|
||||
)}
|
||||
{Object.keys(groupedProducts).length > 0 ? (
|
||||
Object.keys(groupedProducts).map(
|
||||
(categoryKey) => (
|
||||
<section class="mb-12">
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-2 gap-6">
|
||||
{groupedProducts[
|
||||
categoryKey
|
||||
].map((product: any) => (
|
||||
<StoreEntries
|
||||
key={product.id}
|
||||
url={
|
||||
store +
|
||||
product.id
|
||||
}
|
||||
title={
|
||||
product.data
|
||||
.title
|
||||
}
|
||||
type={
|
||||
product.data
|
||||
.type
|
||||
}
|
||||
alt={
|
||||
product.data
|
||||
.title
|
||||
}
|
||||
model={product.data}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
),
|
||||
)
|
||||
) : (
|
||||
<div class="text-center py-12">
|
||||
<div class="text-gray-400 mb-4">
|
||||
<svg
|
||||
class="w-16 h-16 mx-auto"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1"
|
||||
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-2">
|
||||
<Translate>
|
||||
No products in this category
|
||||
</Translate>
|
||||
</h3>
|
||||
<p class="text-gray-600">
|
||||
<Translate>
|
||||
This category appears to be
|
||||
empty.
|
||||
</Translate>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<div class="mt-12 text-center">
|
||||
<a
|
||||
href={`/${locale}/store/`}
|
||||
class="inline-flex items-center px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors"
|
||||
>
|
||||
<svg
|
||||
class="w-4 h-4 mr-2"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M10 19l-7-7m0 0l7-7m-7 7h18"
|
||||
/>
|
||||
</svg>
|
||||
<Translate>Back to Store</Translate>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
</Wrapper>
|
||||
</BaseLayout>
|
||||
);
|
||||
})()
|
||||
: (() => {
|
||||
const { data } = page;
|
||||
return <StoreLayout frontmatter={data} {...rest} />;
|
||||
})()
|
||||
}
|
||||
|
||||
216
packages/polymech/temp-config.d.ts
vendored
Normal file
216
packages/polymech/temp-config.d.ts
vendored
Normal file
@ -0,0 +1,216 @@
|
||||
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;
|
||||
core: Core;
|
||||
dev: Dev;
|
||||
i18n: I18N;
|
||||
products: Products;
|
||||
retail: Retail;
|
||||
rss: Rss;
|
||||
osrl: Osrl;
|
||||
features: { [key: string]: boolean };
|
||||
defaults: Defaults;
|
||||
cad: Cad;
|
||||
assets: Assets;
|
||||
optimization: Optimization;
|
||||
}
|
||||
|
||||
export interface Assets {
|
||||
cad_url: string;
|
||||
url: string;
|
||||
item_url_r: string;
|
||||
item_url: string;
|
||||
}
|
||||
|
||||
export interface Cad {
|
||||
cache: boolean;
|
||||
export_configurations: boolean;
|
||||
export_sub_components: boolean;
|
||||
renderer: string;
|
||||
renderer_view: string;
|
||||
renderer_quality: number;
|
||||
extensions: string[];
|
||||
model_ext: string;
|
||||
default_configuration: string;
|
||||
main_match: string;
|
||||
cam_main_match: string;
|
||||
}
|
||||
|
||||
export interface Core {
|
||||
logging_namespace: string;
|
||||
translate_content: boolean;
|
||||
languages: string[];
|
||||
languages_prod: string[];
|
||||
rtl_languages: string[];
|
||||
osr_root: string;
|
||||
}
|
||||
|
||||
export interface Defaults {
|
||||
image_url: string;
|
||||
license: string;
|
||||
contact: string;
|
||||
}
|
||||
|
||||
export interface Dev {
|
||||
file_server: string;
|
||||
}
|
||||
|
||||
export interface Ecommerce {
|
||||
brand: string;
|
||||
currencySymbol: string;
|
||||
currencyCode: string;
|
||||
}
|
||||
|
||||
export interface FooterLeft {
|
||||
href: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface I18N {
|
||||
store: string;
|
||||
cache: boolean;
|
||||
asset_path: 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 Optimization {
|
||||
image_settings: ImageSettings;
|
||||
presets: Presets;
|
||||
}
|
||||
|
||||
export interface ImageSettings {
|
||||
gallery: Gallery;
|
||||
lightbox: Gallery;
|
||||
}
|
||||
|
||||
export interface Gallery {
|
||||
show_title: boolean;
|
||||
show_description: boolean;
|
||||
sizes_thumb: string;
|
||||
sizes_large: string;
|
||||
sizes_regular: string;
|
||||
}
|
||||
|
||||
export interface Presets {
|
||||
slow: Fast;
|
||||
medium: Fast;
|
||||
fast: Fast;
|
||||
}
|
||||
|
||||
export interface Fast {
|
||||
sizes_medium: string;
|
||||
sizes_thumbs: string;
|
||||
sizes_large: string;
|
||||
}
|
||||
|
||||
export interface Osrl {
|
||||
env: string;
|
||||
env_dev: string;
|
||||
module_name: string;
|
||||
lang_flavor: string;
|
||||
product_profile: 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 Products {
|
||||
root: string;
|
||||
howto_migration: string;
|
||||
glob: string;
|
||||
enabled: string;
|
||||
}
|
||||
|
||||
export interface Retail {
|
||||
library_branch: string;
|
||||
projects_branch: string;
|
||||
}
|
||||
|
||||
export interface Rss {
|
||||
title: string;
|
||||
description: 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;
|
||||
}
|
||||
221
packages/polymech/temp-config.schema.ts
Normal file
221
packages/polymech/temp-config.schema.ts
Normal file
@ -0,0 +1,221 @@
|
||||
// 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 coreSchema = z.object({
|
||||
logging_namespace: z.string(),
|
||||
translate_content: z.boolean(),
|
||||
languages: z.array(z.string()),
|
||||
languages_prod: z.array(z.string()),
|
||||
rtl_languages: z.array(z.string()),
|
||||
osr_root: z.string()
|
||||
});
|
||||
|
||||
export const devSchema = z.object({
|
||||
file_server: z.string()
|
||||
});
|
||||
|
||||
export const i18NSchema = z.object({
|
||||
store: z.string(),
|
||||
cache: z.boolean(),
|
||||
asset_path: z.string()
|
||||
});
|
||||
|
||||
export const productsSchema = z.object({
|
||||
root: z.string(),
|
||||
howto_migration: z.string(),
|
||||
glob: z.string(),
|
||||
enabled: z.string()
|
||||
});
|
||||
|
||||
export const retailSchema = z.object({
|
||||
library_branch: z.string(),
|
||||
projects_branch: z.string()
|
||||
});
|
||||
|
||||
export const rssSchema = z.object({
|
||||
title: z.string(),
|
||||
description: z.string()
|
||||
});
|
||||
|
||||
export const osrlSchema = z.object({
|
||||
env: z.string(),
|
||||
env_dev: z.string(),
|
||||
module_name: z.string(),
|
||||
lang_flavor: z.string(),
|
||||
product_profile: z.string()
|
||||
});
|
||||
|
||||
export const defaultsSchema = z.object({
|
||||
image_url: z.string(),
|
||||
license: z.string(),
|
||||
contact: z.string()
|
||||
});
|
||||
|
||||
export const cadSchema = z.object({
|
||||
cache: z.boolean(),
|
||||
export_configurations: z.boolean(),
|
||||
export_sub_components: z.boolean(),
|
||||
renderer: z.string(),
|
||||
renderer_view: z.string(),
|
||||
renderer_quality: z.number(),
|
||||
extensions: z.array(z.string()),
|
||||
model_ext: z.string(),
|
||||
default_configuration: z.string(),
|
||||
main_match: z.string(),
|
||||
cam_main_match: z.string()
|
||||
});
|
||||
|
||||
export const assetsSchema = z.object({
|
||||
cad_url: z.string(),
|
||||
url: z.string(),
|
||||
item_url_r: z.string(),
|
||||
item_url: z.string()
|
||||
});
|
||||
|
||||
export const gallerySchema = z.object({
|
||||
show_title: z.boolean(),
|
||||
show_description: z.boolean(),
|
||||
sizes_thumb: z.string(),
|
||||
sizes_large: z.string(),
|
||||
sizes_regular: z.string()
|
||||
});
|
||||
|
||||
export const fastSchema = z.object({
|
||||
sizes_medium: z.string(),
|
||||
sizes_thumbs: z.string(),
|
||||
sizes_large: 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 imageSettingsSchema = z.object({
|
||||
gallery: gallerySchema,
|
||||
lightbox: gallerySchema
|
||||
});
|
||||
|
||||
export const presetsSchema = z.object({
|
||||
slow: fastSchema,
|
||||
medium: fastSchema,
|
||||
fast: fastSchema
|
||||
});
|
||||
|
||||
export const homeSchema = z.object({
|
||||
hero: z.string(),
|
||||
_blog: blogSchema
|
||||
});
|
||||
|
||||
export const pagesSchema = z.object({
|
||||
home: homeSchema
|
||||
});
|
||||
|
||||
export const optimizationSchema = z.object({
|
||||
image_settings: imageSettingsSchema,
|
||||
presets: presetsSchema
|
||||
});
|
||||
|
||||
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,
|
||||
core: coreSchema,
|
||||
dev: devSchema,
|
||||
i18n: i18NSchema,
|
||||
products: productsSchema,
|
||||
retail: retailSchema,
|
||||
rss: rssSchema,
|
||||
osrl: osrlSchema,
|
||||
features: z.record(z.string(), z.boolean()),
|
||||
defaults: defaultsSchema,
|
||||
cad: cadSchema,
|
||||
assets: assetsSchema,
|
||||
optimization: optimizationSchema
|
||||
});
|
||||
|
||||
export type AppConfig = z.infer<typeof appConfigSchema>;
|
||||
12
packages/polymech/tsconfig.cli.json
Normal file
12
packages/polymech/tsconfig.cli.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": false
|
||||
},
|
||||
"include": [
|
||||
"src/bin.ts",
|
||||
"src/cli.ts",
|
||||
"src/commands/**/*.ts"
|
||||
],
|
||||
"files": []
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user