@elsapiens/cli 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -490,7 +490,7 @@ async function pageCommand(pageName, options) {
490
490
  cancel2();
491
491
  }
492
492
  let name = pageName;
493
- if (!name) {
493
+ if (!name && !options.yes) {
494
494
  name = await text2({
495
495
  message: "Page name:",
496
496
  placeholder: "Users",
@@ -502,39 +502,66 @@ async function pageCommand(pageName, options) {
502
502
  }
503
503
  });
504
504
  }
505
+ if (!name) {
506
+ logger.error("Page name is required");
507
+ process.exit(1);
508
+ }
505
509
  const pascalName = toPascalCase(name);
506
510
  const kebabName = toKebabCase(name);
507
511
  const routePath = toRoutePath(name);
508
- const template = await select2({
509
- message: "Select a template:",
510
- options: [
511
- {
512
- value: "dashboard",
513
- label: "Dashboard",
514
- hint: "Overview page with stats, charts, and activity table"
515
- },
516
- {
517
- value: "list",
518
- label: "List",
519
- hint: "Data listing with search, filters, and table"
520
- },
521
- {
522
- value: "settings",
523
- label: "Settings",
524
- hint: "Configuration page with form sections"
525
- }
526
- ],
527
- initialValue: options.template
528
- });
529
- const title = await text2({
530
- message: "Page title:",
531
- initialValue: pascalName
532
- });
533
- const description = await text2({
534
- message: "Page description:",
535
- placeholder: "Manage your data",
536
- initialValue: `Manage ${kebabName.replace(/-/g, " ")}`
537
- });
512
+ let template = options.template;
513
+ if (!options.yes) {
514
+ template = await select2({
515
+ message: "Select a template:",
516
+ options: [
517
+ {
518
+ value: "dashboard",
519
+ label: "Dashboard",
520
+ hint: "Overview page with stats, charts, and activity table"
521
+ },
522
+ {
523
+ value: "list",
524
+ label: "List",
525
+ hint: "Data listing with search, filters, and table"
526
+ },
527
+ {
528
+ value: "settings",
529
+ label: "Settings",
530
+ hint: "Configuration page with form sections"
531
+ }
532
+ ],
533
+ initialValue: options.template
534
+ });
535
+ }
536
+ let title = pascalName;
537
+ if (!options.yes) {
538
+ title = await text2({
539
+ message: "Page title:",
540
+ initialValue: pascalName
541
+ });
542
+ }
543
+ let description = `Manage ${kebabName.replace(/-/g, " ")}`;
544
+ if (!options.yes) {
545
+ description = await text2({
546
+ message: "Page description:",
547
+ placeholder: "Manage your data",
548
+ initialValue: description
549
+ });
550
+ }
551
+ let icon = "Package";
552
+ let singularTitle = pascalName.endsWith("s") ? pascalName.slice(0, -1) : pascalName;
553
+ if (template === "list" && !options.yes) {
554
+ icon = await text2({
555
+ message: "Icon name (from lucide-react):",
556
+ placeholder: "Package",
557
+ initialValue: icon
558
+ });
559
+ singularTitle = await text2({
560
+ message: 'Singular title (for "Add Item" button):',
561
+ placeholder: singularTitle,
562
+ initialValue: singularTitle
563
+ });
564
+ }
538
565
  let pagesDir = options.path ? resolvePath(options.path) : null;
539
566
  if (!pagesDir) {
540
567
  pagesDir = await findPagesDir();
@@ -554,7 +581,9 @@ async function pageCommand(pageName, options) {
554
581
  kebabName,
555
582
  title,
556
583
  description,
557
- routePath
584
+ routePath,
585
+ icon,
586
+ singularTitle
558
587
  });
559
588
  await writeFile(filePath, content);
560
589
  logger.success(`Created ${path4.relative(getCwd(), filePath)}`);
@@ -667,7 +696,7 @@ function createCli() {
667
696
  program.name("elsapiens").description("CLI scaffolding tool for elSapiens SDK projects").version(VERSION, "-v, --version", "Display version number");
668
697
  program.command("init [project-name]").alias("create").description("Create a new elSapiens project with full setup").option("--no-git", "Skip git initialization").option("-y, --yes", "Skip prompts and use defaults").action(initCommand);
669
698
  program.command("add").alias("generate").alias("g").description("Add a page or component to the project").addCommand(
670
- new Command("page").argument("[name]", "Page name").description("Add a new page").option("-t, --template <type>", "Page template (dashboard, list, settings)", "dashboard").option("-p, --path <dir>", "Custom pages directory").action(addCommand.page)
699
+ new Command("page").argument("[name]", "Page name").description("Add a new page").option("-t, --template <type>", "Page template (dashboard, list, settings)", "dashboard").option("-p, --path <dir>", "Custom pages directory").option("-y, --yes", "Skip prompts and use defaults").action(addCommand.page)
671
700
  ).addCommand(
672
701
  new Command("component").argument("[name]", "Component name").description("Add a new component").option("-t, --template <type>", "Component template (simple, stateful)", "simple").option("-p, --path <dir>", "Custom components directory").action(addCommand.component)
673
702
  );
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/commands/init.ts","../src/utils/file.ts","../src/utils/prompt.ts","../src/utils/template.ts","../src/utils/logger.ts","../src/commands/add/page.ts","../src/commands/add/component.ts","../src/commands/add/index.ts","../src/index.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { initCommand } from './commands/init.js';\nimport { addCommand } from './commands/add/index.js';\n\nconst VERSION = '0.1.0';\n\nexport function createCli(): Command {\n const program = new Command();\n\n program\n .name('elsapiens')\n .description('CLI scaffolding tool for elSapiens SDK projects')\n .version(VERSION, '-v, --version', 'Display version number');\n\n // Init command - create new project\n program\n .command('init [project-name]')\n .alias('create')\n .description('Create a new elSapiens project with full setup')\n .option('--no-git', 'Skip git initialization')\n .option('-y, --yes', 'Skip prompts and use defaults')\n .action(initCommand);\n\n // Add command - add pages, components, etc.\n program\n .command('add')\n .alias('generate')\n .alias('g')\n .description('Add a page or component to the project')\n .addCommand(\n new Command('page')\n .argument('[name]', 'Page name')\n .description('Add a new page')\n .option('-t, --template <type>', 'Page template (dashboard, list, settings)', 'dashboard')\n .option('-p, --path <dir>', 'Custom pages directory')\n .action(addCommand.page)\n )\n .addCommand(\n new Command('component')\n .argument('[name]', 'Component name')\n .description('Add a new component')\n .option('-t, --template <type>', 'Component template (simple, stateful)', 'simple')\n .option('-p, --path <dir>', 'Custom components directory')\n .action(addCommand.component)\n );\n\n return program;\n}\n","/**\n * Init Command Module\n *\n * This module provides the `init` command for creating new elSapiens projects.\n * It handles project scaffolding, template generation, and optional git initialization.\n *\n * @module commands/init\n */\n\nimport path from 'path';\nimport { execSync } from 'child_process';\nimport {\n directoryExists,\n ensureDir,\n writeFile,\n resolvePath,\n toPascalCase,\n} from '../utils/file.js';\nimport {\n intro,\n outro,\n text,\n confirm,\n spinner,\n note,\n cancel,\n} from '../utils/prompt.js';\nimport { renderApp } from '../utils/template.js';\nimport { logger } from '../utils/logger.js';\n\n/**\n * Options for the init command.\n *\n * @interface InitOptions\n * @property {boolean} git - Whether to initialize a git repository\n * @property {boolean} yes - Skip all prompts and use default values\n */\ninterface InitOptions {\n git: boolean;\n yes: boolean;\n}\n\n/**\n * Initializes a new elSapiens project with the complete setup.\n *\n * This command creates a complete project structure including:\n * - package.json with all dependencies\n * - Vite configuration\n * - Tailwind CSS configuration\n * - PostCSS configuration\n * - TypeScript configuration\n * - HTML entry point\n * - React application files (main.tsx, App.tsx with full layout)\n * - CSS styles with full design system\n * - Dashboard and Settings pages\n * - Translations setup\n * - Help topics setup\n * - Services setup (ShortcutManager, Logger, ApiClient)\n * - Environment example file\n * - .gitignore file\n *\n * @param projectName - The name of the project to create. If not provided,\n * the user will be prompted to enter one (unless --yes is used).\n * @param options - Configuration options for project initialization\n * @param options.git - Whether to initialize a git repository after creation\n * @param options.yes - Skip interactive prompts and use default values\n * @returns A promise that resolves when the project is created successfully\n *\n * @example\n * // Create a project with default settings\n * await initCommand('my-app', { git: true, yes: false });\n *\n * @example\n * // Create a project without prompts\n * await initCommand('my-dashboard', { git: true, yes: true });\n *\n * @example\n * // Interactive mode (no project name provided)\n * await initCommand(undefined, { git: false, yes: false });\n *\n * @throws Will exit with code 1 if project creation fails\n */\nexport async function initCommand(\n projectName: string | undefined,\n options: InitOptions\n): Promise<void> {\n intro('elSapiens CLI');\n\n // Get project name\n let name = projectName;\n if (!name && !options.yes) {\n name = await text({\n message: 'Project name:',\n placeholder: 'my-app',\n validate: (value) => {\n if (!value) return 'Project name is required';\n if (!/^[a-z0-9-_]+$/i.test(value)) {\n return 'Project name can only contain letters, numbers, hyphens, and underscores';\n }\n },\n });\n }\n\n if (!name) {\n name = 'my-elsapiens-app';\n }\n\n // Get API configuration\n let httpsPort = '443';\n let grpcPort = '9090';\n let restPort = '8080';\n\n if (!options.yes) {\n httpsPort = await text({\n message: 'HTTPS port:',\n placeholder: '443',\n initialValue: '443',\n validate: (value) => {\n const port = parseInt(value, 10);\n if (isNaN(port) || port < 1 || port > 65535) {\n return 'Please enter a valid port number (1-65535)';\n }\n },\n }) || '443';\n\n grpcPort = await text({\n message: 'gRPC port:',\n placeholder: '9090',\n initialValue: '9090',\n validate: (value) => {\n const port = parseInt(value, 10);\n if (isNaN(port) || port < 1 || port > 65535) {\n return 'Please enter a valid port number (1-65535)';\n }\n },\n }) || '9090';\n\n restPort = await text({\n message: 'REST API port:',\n placeholder: '8080',\n initialValue: '8080',\n validate: (value) => {\n const port = parseInt(value, 10);\n if (isNaN(port) || port < 1 || port > 65535) {\n return 'Please enter a valid port number (1-65535)';\n }\n },\n }) || '8080';\n }\n\n const projectDir = resolvePath(name);\n\n // Check if directory exists\n if (await directoryExists(projectDir)) {\n const shouldOverwrite = options.yes\n ? false\n : await confirm({\n message: `Directory \"${name}\" already exists. Continue anyway?`,\n initialValue: false,\n });\n\n if (!shouldOverwrite) {\n cancel('Project creation cancelled.');\n }\n }\n\n // Start creating project\n const s = spinner();\n s.start('Creating project...');\n\n try {\n await ensureDir(projectDir);\n\n // Generate files\n const pascalName = toPascalCase(name);\n\n // Create package.json\n const packageJson = await renderApp('package', {\n projectName: name,\n pascalName,\n });\n await writeFile(path.join(projectDir, 'package.json'), packageJson);\n\n // Create vite.config.ts\n const viteConfig = await renderApp('vite-config', {\n projectName: name,\n pascalName,\n });\n await writeFile(path.join(projectDir, 'vite.config.ts'), viteConfig);\n\n // Create tailwind.config.js\n const tailwindConfig = await renderApp('tailwind-config', {\n projectName: name,\n pascalName,\n });\n await writeFile(path.join(projectDir, 'tailwind.config.js'), tailwindConfig);\n\n // Create postcss.config.js\n const postcssConfig = await renderApp('postcss-config', {\n projectName: name,\n pascalName,\n });\n await writeFile(path.join(projectDir, 'postcss.config.js'), postcssConfig);\n\n // Create tsconfig.json\n const tsconfig = await renderApp('tsconfig', {\n projectName: name,\n });\n await writeFile(path.join(projectDir, 'tsconfig.json'), tsconfig);\n\n // Create tsconfig.node.json\n const tsconfigNode = await renderApp('tsconfig-node', {\n projectName: name,\n });\n await writeFile(path.join(projectDir, 'tsconfig.node.json'), tsconfigNode);\n\n // Create index.html\n const indexHtml = await renderApp('index-html', {\n projectName: name,\n pascalName,\n });\n await writeFile(path.join(projectDir, 'index.html'), indexHtml);\n\n // Create src/main.tsx\n const mainTsx = await renderApp('main', {\n projectName: name,\n pascalName,\n });\n await writeFile(path.join(projectDir, 'src', 'main.tsx'), mainTsx);\n\n // Create src/App.tsx\n const appTsx = await renderApp('app', {\n projectName: name,\n pascalName,\n });\n await writeFile(path.join(projectDir, 'src', 'App.tsx'), appTsx);\n\n // Create src/index.css\n const indexCss = await renderApp('index-css', {\n projectName: name,\n });\n await writeFile(path.join(projectDir, 'src', 'index.css'), indexCss);\n\n // Create src/vite-env.d.ts\n const viteEnv = await renderApp('vite-env', {\n projectName: name,\n });\n await writeFile(path.join(projectDir, 'src', 'vite-env.d.ts'), viteEnv);\n\n // Create src/pages/Dashboard.tsx\n const dashboardPage = await renderApp('page-dashboard', {\n projectName: name,\n pascalName,\n });\n await writeFile(\n path.join(projectDir, 'src', 'pages', 'Dashboard.tsx'),\n dashboardPage\n );\n\n // Create src/pages/Settings.tsx\n const settingsPage = await renderApp('page-settings', {\n projectName: name,\n pascalName,\n });\n await writeFile(\n path.join(projectDir, 'src', 'pages', 'Settings.tsx'),\n settingsPage\n );\n\n // Create src/translations/common/en.ts\n const translationsCommonEn = await renderApp('translations-common-en', {\n projectName: name,\n pascalName,\n });\n await writeFile(\n path.join(projectDir, 'src', 'translations', 'common', 'en.ts'),\n translationsCommonEn\n );\n\n // Create src/translations/common/index.ts\n const translationsCommonIndex = await renderApp('translations-common-index', {\n projectName: name,\n pascalName,\n });\n await writeFile(\n path.join(projectDir, 'src', 'translations', 'common', 'index.ts'),\n translationsCommonIndex\n );\n\n // Create src/translations/settings/en.ts\n const translationsSettingsEn = await renderApp('translations-settings-en', {\n projectName: name,\n pascalName,\n });\n await writeFile(\n path.join(projectDir, 'src', 'translations', 'settings', 'en.ts'),\n translationsSettingsEn\n );\n\n // Create src/translations/settings/index.ts\n const translationsSettingsIndex = await renderApp('translations-settings-index', {\n projectName: name,\n pascalName,\n });\n await writeFile(\n path.join(projectDir, 'src', 'translations', 'settings', 'index.ts'),\n translationsSettingsIndex\n );\n\n // Create src/help-topics.ts\n const helpTopics = await renderApp('help-topics', {\n projectName: name,\n pascalName,\n });\n await writeFile(\n path.join(projectDir, 'src', 'help-topics.ts'),\n helpTopics\n );\n\n // Create src/services/index.ts\n const servicesSetup = await renderApp('services-setup', {\n projectName: name,\n pascalName,\n });\n await writeFile(\n path.join(projectDir, 'src', 'services', 'index.ts'),\n servicesSetup\n );\n\n // Create .env.example\n const envExample = await renderApp('env-example', {\n projectName: name,\n pascalName,\n httpsPort,\n grpcPort,\n restPort,\n });\n await writeFile(path.join(projectDir, '.env.example'), envExample);\n\n // Create .env with same values\n await writeFile(path.join(projectDir, '.env'), envExample);\n\n // Create eslint.config.js\n const eslintConfig = await renderApp('eslint-config', {\n projectName: name,\n pascalName,\n });\n await writeFile(path.join(projectDir, 'eslint.config.js'), eslintConfig);\n\n // Create vitest.config.ts\n const vitestConfig = await renderApp('vitest-config', {\n projectName: name,\n pascalName,\n });\n await writeFile(path.join(projectDir, 'vitest.config.ts'), vitestConfig);\n\n // Create src/test/setup.ts\n const testSetup = await renderApp('test-setup', {\n projectName: name,\n pascalName,\n });\n await writeFile(path.join(projectDir, 'src', 'test', 'setup.ts'), testSetup);\n\n // Create .husky/pre-commit\n const huskyPreCommit = await renderApp('husky-pre-commit', {\n projectName: name,\n pascalName,\n });\n await writeFile(path.join(projectDir, '.husky', 'pre-commit'), huskyPreCommit);\n\n // Create .gitignore\n const gitignore = `# Dependencies\nnode_modules/\n\n# Build output\ndist/\n\n# IDE\n.idea/\n.vscode/\n*.swp\n*.swo\n\n# OS\n.DS_Store\nThumbs.db\n\n# Environment\n.env\n.env.local\n.env.*.local\n\n# Logs\n*.log\nnpm-debug.log*\n\n# Coverage\ncoverage/\n`;\n await writeFile(path.join(projectDir, '.gitignore'), gitignore);\n\n s.stop('Project created!');\n\n // Initialize git\n if (options.git) {\n const initGit = options.yes\n ? true\n : await confirm({\n message: 'Initialize git repository?',\n initialValue: true,\n });\n\n if (initGit) {\n try {\n execSync('git init -b main', { cwd: projectDir, stdio: 'ignore' });\n execSync('git add .', { cwd: projectDir, stdio: 'ignore' });\n execSync('git commit -m \"Initial commit\"', { cwd: projectDir, stdio: 'ignore' });\n logger.success('Git repository initialized with initial commit');\n } catch {\n logger.warn('Failed to initialize git repository');\n }\n }\n }\n\n // Show next steps\n note(\n `cd ${name}\npnpm install\npnpm dev`,\n 'Next steps'\n );\n\n outro('Happy coding!');\n } catch (error) {\n s.stop('Failed to create project');\n logger.error(\n error instanceof Error ? error.message : 'Unknown error occurred'\n );\n process.exit(1);\n }\n}\n","import fs from 'fs-extra';\nimport path from 'path';\n\n/**\n * Check if a directory exists\n */\nexport async function directoryExists(dirPath: string): Promise<boolean> {\n try {\n const stat = await fs.stat(dirPath);\n return stat.isDirectory();\n } catch {\n return false;\n }\n}\n\n/**\n * Check if a file exists\n */\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n const stat = await fs.stat(filePath);\n return stat.isFile();\n } catch {\n return false;\n }\n}\n\n/**\n * Create a directory (and parents) if it doesn't exist\n */\nexport async function ensureDir(dirPath: string): Promise<void> {\n await fs.ensureDir(dirPath);\n}\n\n/**\n * Write content to a file, creating directories as needed\n */\nexport async function writeFile(\n filePath: string,\n content: string\n): Promise<void> {\n await fs.ensureDir(path.dirname(filePath));\n await fs.writeFile(filePath, content, 'utf-8');\n}\n\n/**\n * Read file content\n */\nexport async function readFile(filePath: string): Promise<string> {\n return fs.readFile(filePath, 'utf-8');\n}\n\n/**\n * Copy a file\n */\nexport async function copyFile(src: string, dest: string): Promise<void> {\n await fs.ensureDir(path.dirname(dest));\n await fs.copyFile(src, dest);\n}\n\n/**\n * Copy a directory recursively\n */\nexport async function copyDir(src: string, dest: string): Promise<void> {\n await fs.copy(src, dest);\n}\n\n/**\n * Get the current working directory\n */\nexport function getCwd(): string {\n return process.cwd();\n}\n\n/**\n * Resolve a path relative to cwd\n */\nexport function resolvePath(...paths: string[]): string {\n return path.resolve(getCwd(), ...paths);\n}\n\n/**\n * Check if current directory is an elsapiens project\n */\nexport async function isElSapiensProject(dir: string = getCwd()): Promise<boolean> {\n const packageJsonPath = path.join(dir, 'package.json');\n\n if (!(await fileExists(packageJsonPath))) {\n return false;\n }\n\n try {\n const content = await readFile(packageJsonPath);\n const pkg = JSON.parse(content);\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n\n // Check for any @elsapiens package\n return Object.keys(deps).some((dep) => dep.startsWith('@elsapiens/'));\n } catch {\n return false;\n }\n}\n\n/**\n * Find the pages directory in a project\n */\nexport async function findPagesDir(dir: string = getCwd()): Promise<string | null> {\n const candidates = [\n path.join(dir, 'src', 'pages'),\n path.join(dir, 'src', 'app'),\n path.join(dir, 'pages'),\n ];\n\n for (const candidate of candidates) {\n if (await directoryExists(candidate)) {\n return candidate;\n }\n }\n\n return null;\n}\n\n/**\n * Find the components directory in a project\n */\nexport async function findComponentsDir(\n dir: string = getCwd()\n): Promise<string | null> {\n const candidates = [\n path.join(dir, 'src', 'components'),\n path.join(dir, 'components'),\n ];\n\n for (const candidate of candidates) {\n if (await directoryExists(candidate)) {\n return candidate;\n }\n }\n\n return null;\n}\n\n/**\n * Convert a string to PascalCase\n */\nexport function toPascalCase(str: string): string {\n return str\n .split(/[-_\\s]+/)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join('');\n}\n\n/**\n * Convert a string to kebab-case\n */\nexport function toKebabCase(str: string): string {\n return str\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .toLowerCase();\n}\n\n/**\n * Convert a string to a valid route path\n */\nexport function toRoutePath(str: string): string {\n return '/' + toKebabCase(str);\n}\n","import * as p from '@clack/prompts';\nimport pc from 'picocolors';\n\n/**\n * Show intro banner\n */\nexport function intro(message: string): void {\n p.intro(pc.bgCyan(pc.black(` ${message} `)));\n}\n\n/**\n * Show outro message\n */\nexport function outro(message: string): void {\n p.outro(pc.green(message));\n}\n\n/**\n * Show a cancel message and exit\n */\nexport function cancel(message: string = 'Operation cancelled.'): never {\n p.cancel(message);\n process.exit(0);\n}\n\n/**\n * Check if the user cancelled the prompt\n */\nexport function isCancel(value: unknown): value is symbol {\n return p.isCancel(value);\n}\n\n/**\n * Handle cancel - exit if cancelled, return value otherwise\n */\nexport function handleCancel<T>(value: T | symbol): T {\n if (isCancel(value)) {\n cancel();\n }\n return value as T;\n}\n\n/**\n * Ask for text input\n */\nexport async function text(options: {\n message: string;\n placeholder?: string;\n initialValue?: string;\n validate?: (value: string) => string | void;\n}): Promise<string> {\n const result = await p.text(options);\n return handleCancel(result);\n}\n\n/**\n * Ask for a selection from options\n */\nexport async function select<T extends string>(options: {\n message: string;\n options: { value: T; label: string; hint?: string }[];\n initialValue?: T;\n}): Promise<T> {\n const result = await p.select(options);\n return handleCancel(result) as T;\n}\n\n/**\n * Ask for multiple selections\n */\nexport async function multiselect<T extends string>(options: {\n message: string;\n options: { value: T; label: string; hint?: string }[];\n required?: boolean;\n}): Promise<T[]> {\n const result = await p.multiselect(options);\n return handleCancel(result) as T[];\n}\n\n/**\n * Ask for confirmation\n */\nexport async function confirm(options: {\n message: string;\n initialValue?: boolean;\n}): Promise<boolean> {\n const result = await p.confirm(options);\n return handleCancel(result);\n}\n\n/**\n * Show a spinner during async operation\n */\nexport function spinner(): {\n start: (message?: string) => void;\n stop: (message?: string, code?: number) => void;\n message: (message?: string) => void;\n} {\n return p.spinner();\n}\n\n/**\n * Show a group of tasks\n */\nexport async function tasks(\n items: { title: string; task: () => Promise<string> }[]\n): Promise<void> {\n await p.tasks(items);\n}\n\n/**\n * Log a note/info message\n */\nexport function note(message: string, title?: string): void {\n p.note(message, title);\n}\n\n/**\n * Log a message during prompts\n */\nexport function log(options: { message?: string; symbol?: string }): void {\n p.log.message(options.message ?? '', { symbol: options.symbol });\n}\n","import ejs from 'ejs';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\nimport { readFile } from './file.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Templates are in dist/templates when bundled (same level as index.js)\nconst TEMPLATES_DIR = path.resolve(__dirname, 'templates');\n\nexport interface TemplateData {\n [key: string]: unknown;\n}\n\n/**\n * Get the path to a template file\n */\nexport function getTemplatePath(category: string, name: string): string {\n return path.join(TEMPLATES_DIR, category, `${name}.ejs`);\n}\n\n/**\n * Render an EJS template with the given data\n */\nexport async function renderTemplate(\n templatePath: string,\n data: TemplateData\n): Promise<string> {\n const template = await readFile(templatePath);\n return ejs.render(template, data, {\n async: false,\n });\n}\n\n/**\n * Render a named template from the templates directory\n */\nexport async function render(\n category: string,\n name: string,\n data: TemplateData\n): Promise<string> {\n const templatePath = getTemplatePath(category, name);\n return renderTemplate(templatePath, data);\n}\n\n/**\n * Render a page template\n */\nexport async function renderPage(\n templateName: string,\n data: {\n name: string;\n pascalName: string;\n title: string;\n description: string;\n [key: string]: unknown;\n }\n): Promise<string> {\n return render('pages', templateName, data);\n}\n\n/**\n * Render a component template\n */\nexport async function renderComponent(\n templateName: string,\n data: {\n name: string;\n pascalName: string;\n [key: string]: unknown;\n }\n): Promise<string> {\n return render('components', templateName, data);\n}\n\n/**\n * Render an app template\n */\nexport async function renderApp(\n templateName: string,\n data: {\n projectName: string;\n [key: string]: unknown;\n }\n): Promise<string> {\n return render('app', templateName, data);\n}\n","import pc from 'picocolors';\n\nexport const logger = {\n info: (message: string) => {\n console.log(pc.blue('ℹ'), message);\n },\n\n success: (message: string) => {\n console.log(pc.green('✓'), message);\n },\n\n warn: (message: string) => {\n console.log(pc.yellow('⚠'), message);\n },\n\n error: (message: string) => {\n console.log(pc.red('✗'), message);\n },\n\n log: (message: string) => {\n console.log(message);\n },\n\n blank: () => {\n console.log();\n },\n\n title: (message: string) => {\n console.log();\n console.log(pc.bold(pc.cyan(message)));\n },\n\n step: (message: string) => {\n console.log(pc.dim(' -'), message);\n },\n\n code: (code: string) => {\n console.log(pc.dim(' ') + pc.cyan(code));\n },\n\n box: (message: string) => {\n const line = '─'.repeat(message.length + 2);\n console.log(pc.dim(`┌${line}┐`));\n console.log(pc.dim('│ ') + pc.bold(message) + pc.dim(' │'));\n console.log(pc.dim(`└${line}┘`));\n },\n};\n\nexport default logger;\n","/**\n * Add Page Command Module\n *\n * This module provides the `add page` command for creating new page components\n * in an existing elSapiens project. It supports multiple page templates and\n * generates the necessary routing code.\n *\n * @module commands/add/page\n */\n\nimport path from 'path';\nimport {\n fileExists,\n findPagesDir,\n isElSapiensProject,\n writeFile,\n resolvePath,\n toPascalCase,\n toKebabCase,\n toRoutePath,\n getCwd,\n} from '../../utils/file.js';\nimport {\n intro,\n outro,\n text,\n select,\n note,\n cancel,\n} from '../../utils/prompt.js';\nimport { renderPage } from '../../utils/template.js';\nimport { logger } from '../../utils/logger.js';\n\n/**\n * Options for the page command.\n *\n * @interface PageOptions\n * @property {('dashboard' | 'list' | 'settings')} template - The page template to use\n * @property {string} [path] - Optional custom path for the pages directory\n */\ninterface PageOptions {\n template: 'dashboard' | 'list' | 'settings';\n path?: string;\n}\n\n/**\n * Creates a new page component in an elSapiens project.\n *\n * This command generates a page file based on the selected template and provides\n * instructions for integrating it into the application's routing configuration.\n *\n * Available templates:\n * - `dashboard` - Overview page with stats, charts, and activity table\n * - `list` - Data listing page with search, filters, and table\n * - `settings` - Configuration page with form sections\n *\n * The command will:\n * 1. Verify the current directory is an elSapiens project\n * 2. Prompt for page name if not provided\n * 3. Allow template selection\n * 4. Prompt for page title and description\n * 5. Generate the page file in the pages directory\n * 6. Display routing instructions for App.tsx integration\n *\n * @param pageName - The name of the page to create. Should be in PascalCase or\n * will be converted. If not provided, user will be prompted.\n * @param options - Configuration options for page generation\n * @param options.template - The page template to use ('dashboard', 'list', or 'settings')\n * @param options.path - Custom path for the pages directory (defaults to auto-detected)\n * @returns A promise that resolves when the page is created successfully\n *\n * @example\n * // Create a dashboard page named 'Analytics'\n * await pageCommand('Analytics', { template: 'dashboard' });\n *\n * @example\n * // Create a list page with custom path\n * await pageCommand('Users', { template: 'list', path: './src/views' });\n *\n * @example\n * // Interactive mode (no page name provided)\n * await pageCommand(undefined, { template: 'settings' });\n *\n * @throws Will exit with code 1 if page creation fails\n * @throws Will cancel if not in an elSapiens project directory\n * @throws Will cancel if the page file already exists\n */\nexport async function pageCommand(\n pageName: string | undefined,\n options: PageOptions\n): Promise<void> {\n intro('Add Page');\n\n // Check if we're in an elsapiens project\n const isProject = await isElSapiensProject();\n if (!isProject) {\n logger.warn('This does not appear to be an elSapiens project.');\n logger.info('Run \"el init\" to create a new project first.');\n cancel();\n }\n\n // Get page name\n let name = pageName;\n if (!name) {\n name = await text({\n message: 'Page name:',\n placeholder: 'Users',\n validate: (value) => {\n if (!value) return 'Page name is required';\n if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(value)) {\n return 'Page name must start with a letter and contain only letters and numbers';\n }\n },\n });\n }\n\n const pascalName = toPascalCase(name);\n const kebabName = toKebabCase(name);\n const routePath = toRoutePath(name);\n\n // Get template\n const template = await select({\n message: 'Select a template:',\n options: [\n {\n value: 'dashboard',\n label: 'Dashboard',\n hint: 'Overview page with stats, charts, and activity table',\n },\n {\n value: 'list',\n label: 'List',\n hint: 'Data listing with search, filters, and table',\n },\n {\n value: 'settings',\n label: 'Settings',\n hint: 'Configuration page with form sections',\n },\n ],\n initialValue: options.template,\n });\n\n // Get page title\n const title = await text({\n message: 'Page title:',\n initialValue: pascalName,\n });\n\n // Get page description\n const description = await text({\n message: 'Page description:',\n placeholder: 'Manage your data',\n initialValue: `Manage ${kebabName.replace(/-/g, ' ')}`,\n });\n\n // Find pages directory\n let pagesDir = options.path ? resolvePath(options.path) : null;\n if (!pagesDir) {\n pagesDir = await findPagesDir();\n if (!pagesDir) {\n pagesDir = resolvePath('src', 'pages');\n }\n }\n\n const filePath = path.join(pagesDir, `${pascalName}.tsx`);\n\n // Check if file exists\n if (await fileExists(filePath)) {\n logger.error(`File already exists: ${filePath}`);\n cancel();\n }\n\n try {\n // Render template\n const content = await renderPage(template, {\n name,\n pascalName,\n kebabName,\n title,\n description,\n routePath,\n });\n\n // Write file\n await writeFile(filePath, content);\n\n logger.success(`Created ${path.relative(getCwd(), filePath)}`);\n\n // Show routing instructions\n note(\n `Add this route to your App.tsx:\n\nimport ${pascalName} from './pages/${pascalName}';\n\n<Route path=\"${routePath}\" element={<${pascalName} />} />\n\nAnd add to navigation:\n{ id: '${kebabName}', path: '${routePath}', label: '${title}', icon: <Icon /> }`,\n 'Next steps'\n );\n\n outro('Page created!');\n } catch (error) {\n logger.error(\n error instanceof Error ? error.message : 'Failed to create page'\n );\n process.exit(1);\n }\n}\n","/**\n * Add Component Command Module\n *\n * This module provides the `add component` command for creating new React components\n * in an existing elSapiens project. It supports simple and stateful component templates.\n *\n * @module commands/add/component\n */\n\nimport path from 'path';\nimport {\n fileExists,\n findComponentsDir,\n isElSapiensProject,\n writeFile,\n resolvePath,\n toPascalCase,\n getCwd,\n} from '../../utils/file.js';\nimport { intro, outro, text, select, note, cancel } from '../../utils/prompt.js';\nimport { renderComponent } from '../../utils/template.js';\nimport { logger } from '../../utils/logger.js';\n\n/**\n * Options for the component command.\n *\n * @interface ComponentOptions\n * @property {('simple' | 'stateful')} template - The component template to use\n * @property {string} [path] - Optional custom path for the components directory\n */\ninterface ComponentOptions {\n template: 'simple' | 'stateful';\n path?: string;\n}\n\n/**\n * Creates a new React component in an elSapiens project.\n *\n * This command generates a component file based on the selected template and\n * provides usage instructions for importing and using the component.\n *\n * Available templates:\n * - `simple` - Basic functional component with props and className support\n * - `stateful` - Component with useState hook and onChange callback pattern\n *\n * The command will:\n * 1. Verify the current directory is an elSapiens project\n * 2. Prompt for component name if not provided\n * 3. Allow template selection (simple or stateful)\n * 4. Generate the component file in the components directory\n * 5. Display usage instructions for importing the component\n *\n * @param componentName - The name of the component to create. Should be in PascalCase\n * or will be converted. If not provided, user will be prompted.\n * @param options - Configuration options for component generation\n * @param options.template - The component template to use ('simple' or 'stateful')\n * @param options.path - Custom path for the components directory (defaults to auto-detected)\n * @returns A promise that resolves when the component is created successfully\n *\n * @example\n * // Create a simple component named 'UserCard'\n * await componentCommand('UserCard', { template: 'simple' });\n *\n * @example\n * // Create a stateful component with custom path\n * await componentCommand('Counter', { template: 'stateful', path: './src/ui' });\n *\n * @example\n * // Interactive mode (no component name provided)\n * await componentCommand(undefined, { template: 'simple' });\n *\n * @throws Will exit with code 1 if component creation fails\n * @throws Will cancel if not in an elSapiens project directory\n * @throws Will cancel if the component file already exists\n */\nexport async function componentCommand(\n componentName: string | undefined,\n options: ComponentOptions\n): Promise<void> {\n intro('Add Component');\n\n // Check if we're in an elsapiens project\n const isProject = await isElSapiensProject();\n if (!isProject) {\n logger.warn('This does not appear to be an elSapiens project.');\n logger.info('Run \"el init\" to create a new project first.');\n cancel();\n }\n\n // Get component name\n let name = componentName;\n if (!name) {\n name = await text({\n message: 'Component name:',\n placeholder: 'UserCard',\n validate: (value) => {\n if (!value) return 'Component name is required';\n if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(value)) {\n return 'Component name must start with a letter and contain only letters and numbers';\n }\n },\n });\n }\n\n const pascalName = toPascalCase(name);\n\n // Get template\n const template = await select({\n message: 'Select a template:',\n options: [\n {\n value: 'simple',\n label: 'Simple',\n hint: 'Basic component with props and className',\n },\n {\n value: 'stateful',\n label: 'Stateful',\n hint: 'Component with useState and onChange callback',\n },\n ],\n initialValue: options.template,\n });\n\n // Find components directory\n let componentsDir = options.path ? resolvePath(options.path) : null;\n if (!componentsDir) {\n componentsDir = await findComponentsDir();\n if (!componentsDir) {\n componentsDir = resolvePath('src', 'components');\n }\n }\n\n const filePath = path.join(componentsDir, `${pascalName}.tsx`);\n\n // Check if file exists\n if (await fileExists(filePath)) {\n logger.error(`File already exists: ${filePath}`);\n cancel();\n }\n\n try {\n // Render template\n const content = await renderComponent(template, {\n name,\n pascalName,\n });\n\n // Write file\n await writeFile(filePath, content);\n\n logger.success(`Created ${path.relative(getCwd(), filePath)}`);\n\n // Show usage instructions\n note(\n `Import and use your component:\n\nimport { ${pascalName} } from './components/${pascalName}';\n\n<${pascalName} className=\"...\" />`,\n 'Usage'\n );\n\n outro('Component created!');\n } catch (error) {\n logger.error(\n error instanceof Error ? error.message : 'Failed to create component'\n );\n process.exit(1);\n }\n}\n","import { pageCommand } from './page.js';\nimport { componentCommand } from './component.js';\n\nexport const addCommand = {\n page: pageCommand,\n component: componentCommand,\n};\n","/**\n * elSapiens CLI Entry Point\n *\n * This module serves as the main entry point for the elSapiens CLI application.\n * It initializes and starts the command-line interface by creating and parsing\n * the CLI instance.\n *\n * @module cli\n *\n * @example\n * // Running the CLI from the command line:\n * npx el init my-project\n * npx el add page Users\n * npx el add component UserCard\n */\n\nimport { createCli } from './cli.js';\n\n/**\n * Creates the CLI instance and parses command-line arguments.\n *\n * The CLI supports the following commands:\n * - `init [name]` - Initialize a new elSapiens project\n * - `add page [name]` - Add a new page to an existing project\n * - `add component [name]` - Add a new component to an existing project\n *\n * @example\n * // The CLI is automatically invoked when running the package:\n * // npx el init my-app --template full --git\n */\nconst cli = createCli();\ncli.parse();\n"],"mappings":"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","names":["path","intro","outro","cancel","isCancel","text","select","confirm","spinner","note","path","path","pc","intro","text","confirm","cancel","spinner","path","note","outro","path","intro","cancel","text","select","path","note","outro","path","intro","cancel","text","select","path","note","outro"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/commands/init.ts","../src/utils/file.ts","../src/utils/prompt.ts","../src/utils/template.ts","../src/utils/logger.ts","../src/commands/add/page.ts","../src/commands/add/component.ts","../src/commands/add/index.ts","../src/index.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { initCommand } from './commands/init.js';\nimport { addCommand } from './commands/add/index.js';\n\nconst VERSION = '0.1.0';\n\nexport function createCli(): Command {\n const program = new Command();\n\n program\n .name('elsapiens')\n .description('CLI scaffolding tool for elSapiens SDK projects')\n .version(VERSION, '-v, --version', 'Display version number');\n\n // Init command - create new project\n program\n .command('init [project-name]')\n .alias('create')\n .description('Create a new elSapiens project with full setup')\n .option('--no-git', 'Skip git initialization')\n .option('-y, --yes', 'Skip prompts and use defaults')\n .action(initCommand);\n\n // Add command - add pages, components, etc.\n program\n .command('add')\n .alias('generate')\n .alias('g')\n .description('Add a page or component to the project')\n .addCommand(\n new Command('page')\n .argument('[name]', 'Page name')\n .description('Add a new page')\n .option('-t, --template <type>', 'Page template (dashboard, list, settings)', 'dashboard')\n .option('-p, --path <dir>', 'Custom pages directory')\n .option('-y, --yes', 'Skip prompts and use defaults')\n .action(addCommand.page)\n )\n .addCommand(\n new Command('component')\n .argument('[name]', 'Component name')\n .description('Add a new component')\n .option('-t, --template <type>', 'Component template (simple, stateful)', 'simple')\n .option('-p, --path <dir>', 'Custom components directory')\n .action(addCommand.component)\n );\n\n return program;\n}\n","/**\n * Init Command Module\n *\n * This module provides the `init` command for creating new elSapiens projects.\n * It handles project scaffolding, template generation, and optional git initialization.\n *\n * @module commands/init\n */\n\nimport path from 'path';\nimport { execSync } from 'child_process';\nimport {\n directoryExists,\n ensureDir,\n writeFile,\n resolvePath,\n toPascalCase,\n} from '../utils/file.js';\nimport {\n intro,\n outro,\n text,\n confirm,\n spinner,\n note,\n cancel,\n} from '../utils/prompt.js';\nimport { renderApp } from '../utils/template.js';\nimport { logger } from '../utils/logger.js';\n\n/**\n * Options for the init command.\n *\n * @interface InitOptions\n * @property {boolean} git - Whether to initialize a git repository\n * @property {boolean} yes - Skip all prompts and use default values\n */\ninterface InitOptions {\n git: boolean;\n yes: boolean;\n}\n\n/**\n * Initializes a new elSapiens project with the complete setup.\n *\n * This command creates a complete project structure including:\n * - package.json with all dependencies\n * - Vite configuration\n * - Tailwind CSS configuration\n * - PostCSS configuration\n * - TypeScript configuration\n * - HTML entry point\n * - React application files (main.tsx, App.tsx with full layout)\n * - CSS styles with full design system\n * - Dashboard and Settings pages\n * - Translations setup\n * - Help topics setup\n * - Services setup (ShortcutManager, Logger, ApiClient)\n * - Environment example file\n * - .gitignore file\n *\n * @param projectName - The name of the project to create. If not provided,\n * the user will be prompted to enter one (unless --yes is used).\n * @param options - Configuration options for project initialization\n * @param options.git - Whether to initialize a git repository after creation\n * @param options.yes - Skip interactive prompts and use default values\n * @returns A promise that resolves when the project is created successfully\n *\n * @example\n * // Create a project with default settings\n * await initCommand('my-app', { git: true, yes: false });\n *\n * @example\n * // Create a project without prompts\n * await initCommand('my-dashboard', { git: true, yes: true });\n *\n * @example\n * // Interactive mode (no project name provided)\n * await initCommand(undefined, { git: false, yes: false });\n *\n * @throws Will exit with code 1 if project creation fails\n */\nexport async function initCommand(\n projectName: string | undefined,\n options: InitOptions\n): Promise<void> {\n intro('elSapiens CLI');\n\n // Get project name\n let name = projectName;\n if (!name && !options.yes) {\n name = await text({\n message: 'Project name:',\n placeholder: 'my-app',\n validate: (value) => {\n if (!value) return 'Project name is required';\n if (!/^[a-z0-9-_]+$/i.test(value)) {\n return 'Project name can only contain letters, numbers, hyphens, and underscores';\n }\n },\n });\n }\n\n if (!name) {\n name = 'my-elsapiens-app';\n }\n\n // Get API configuration\n let httpsPort = '443';\n let grpcPort = '9090';\n let restPort = '8080';\n\n if (!options.yes) {\n httpsPort = await text({\n message: 'HTTPS port:',\n placeholder: '443',\n initialValue: '443',\n validate: (value) => {\n const port = parseInt(value, 10);\n if (isNaN(port) || port < 1 || port > 65535) {\n return 'Please enter a valid port number (1-65535)';\n }\n },\n }) || '443';\n\n grpcPort = await text({\n message: 'gRPC port:',\n placeholder: '9090',\n initialValue: '9090',\n validate: (value) => {\n const port = parseInt(value, 10);\n if (isNaN(port) || port < 1 || port > 65535) {\n return 'Please enter a valid port number (1-65535)';\n }\n },\n }) || '9090';\n\n restPort = await text({\n message: 'REST API port:',\n placeholder: '8080',\n initialValue: '8080',\n validate: (value) => {\n const port = parseInt(value, 10);\n if (isNaN(port) || port < 1 || port > 65535) {\n return 'Please enter a valid port number (1-65535)';\n }\n },\n }) || '8080';\n }\n\n const projectDir = resolvePath(name);\n\n // Check if directory exists\n if (await directoryExists(projectDir)) {\n const shouldOverwrite = options.yes\n ? false\n : await confirm({\n message: `Directory \"${name}\" already exists. Continue anyway?`,\n initialValue: false,\n });\n\n if (!shouldOverwrite) {\n cancel('Project creation cancelled.');\n }\n }\n\n // Start creating project\n const s = spinner();\n s.start('Creating project...');\n\n try {\n await ensureDir(projectDir);\n\n // Generate files\n const pascalName = toPascalCase(name);\n\n // Create package.json\n const packageJson = await renderApp('package', {\n projectName: name,\n pascalName,\n });\n await writeFile(path.join(projectDir, 'package.json'), packageJson);\n\n // Create vite.config.ts\n const viteConfig = await renderApp('vite-config', {\n projectName: name,\n pascalName,\n });\n await writeFile(path.join(projectDir, 'vite.config.ts'), viteConfig);\n\n // Create tailwind.config.js\n const tailwindConfig = await renderApp('tailwind-config', {\n projectName: name,\n pascalName,\n });\n await writeFile(path.join(projectDir, 'tailwind.config.js'), tailwindConfig);\n\n // Create postcss.config.js\n const postcssConfig = await renderApp('postcss-config', {\n projectName: name,\n pascalName,\n });\n await writeFile(path.join(projectDir, 'postcss.config.js'), postcssConfig);\n\n // Create tsconfig.json\n const tsconfig = await renderApp('tsconfig', {\n projectName: name,\n });\n await writeFile(path.join(projectDir, 'tsconfig.json'), tsconfig);\n\n // Create tsconfig.node.json\n const tsconfigNode = await renderApp('tsconfig-node', {\n projectName: name,\n });\n await writeFile(path.join(projectDir, 'tsconfig.node.json'), tsconfigNode);\n\n // Create index.html\n const indexHtml = await renderApp('index-html', {\n projectName: name,\n pascalName,\n });\n await writeFile(path.join(projectDir, 'index.html'), indexHtml);\n\n // Create src/main.tsx\n const mainTsx = await renderApp('main', {\n projectName: name,\n pascalName,\n });\n await writeFile(path.join(projectDir, 'src', 'main.tsx'), mainTsx);\n\n // Create src/App.tsx\n const appTsx = await renderApp('app', {\n projectName: name,\n pascalName,\n });\n await writeFile(path.join(projectDir, 'src', 'App.tsx'), appTsx);\n\n // Create src/index.css\n const indexCss = await renderApp('index-css', {\n projectName: name,\n });\n await writeFile(path.join(projectDir, 'src', 'index.css'), indexCss);\n\n // Create src/vite-env.d.ts\n const viteEnv = await renderApp('vite-env', {\n projectName: name,\n });\n await writeFile(path.join(projectDir, 'src', 'vite-env.d.ts'), viteEnv);\n\n // Create src/pages/Dashboard.tsx\n const dashboardPage = await renderApp('page-dashboard', {\n projectName: name,\n pascalName,\n });\n await writeFile(\n path.join(projectDir, 'src', 'pages', 'Dashboard.tsx'),\n dashboardPage\n );\n\n // Create src/pages/Settings.tsx\n const settingsPage = await renderApp('page-settings', {\n projectName: name,\n pascalName,\n });\n await writeFile(\n path.join(projectDir, 'src', 'pages', 'Settings.tsx'),\n settingsPage\n );\n\n // Create src/translations/common/en.ts\n const translationsCommonEn = await renderApp('translations-common-en', {\n projectName: name,\n pascalName,\n });\n await writeFile(\n path.join(projectDir, 'src', 'translations', 'common', 'en.ts'),\n translationsCommonEn\n );\n\n // Create src/translations/common/index.ts\n const translationsCommonIndex = await renderApp('translations-common-index', {\n projectName: name,\n pascalName,\n });\n await writeFile(\n path.join(projectDir, 'src', 'translations', 'common', 'index.ts'),\n translationsCommonIndex\n );\n\n // Create src/translations/settings/en.ts\n const translationsSettingsEn = await renderApp('translations-settings-en', {\n projectName: name,\n pascalName,\n });\n await writeFile(\n path.join(projectDir, 'src', 'translations', 'settings', 'en.ts'),\n translationsSettingsEn\n );\n\n // Create src/translations/settings/index.ts\n const translationsSettingsIndex = await renderApp('translations-settings-index', {\n projectName: name,\n pascalName,\n });\n await writeFile(\n path.join(projectDir, 'src', 'translations', 'settings', 'index.ts'),\n translationsSettingsIndex\n );\n\n // Create src/help-topics.ts\n const helpTopics = await renderApp('help-topics', {\n projectName: name,\n pascalName,\n });\n await writeFile(\n path.join(projectDir, 'src', 'help-topics.ts'),\n helpTopics\n );\n\n // Create src/services/index.ts\n const servicesSetup = await renderApp('services-setup', {\n projectName: name,\n pascalName,\n });\n await writeFile(\n path.join(projectDir, 'src', 'services', 'index.ts'),\n servicesSetup\n );\n\n // Create .env.example\n const envExample = await renderApp('env-example', {\n projectName: name,\n pascalName,\n httpsPort,\n grpcPort,\n restPort,\n });\n await writeFile(path.join(projectDir, '.env.example'), envExample);\n\n // Create .env with same values\n await writeFile(path.join(projectDir, '.env'), envExample);\n\n // Create eslint.config.js\n const eslintConfig = await renderApp('eslint-config', {\n projectName: name,\n pascalName,\n });\n await writeFile(path.join(projectDir, 'eslint.config.js'), eslintConfig);\n\n // Create vitest.config.ts\n const vitestConfig = await renderApp('vitest-config', {\n projectName: name,\n pascalName,\n });\n await writeFile(path.join(projectDir, 'vitest.config.ts'), vitestConfig);\n\n // Create src/test/setup.ts\n const testSetup = await renderApp('test-setup', {\n projectName: name,\n pascalName,\n });\n await writeFile(path.join(projectDir, 'src', 'test', 'setup.ts'), testSetup);\n\n // Create .husky/pre-commit\n const huskyPreCommit = await renderApp('husky-pre-commit', {\n projectName: name,\n pascalName,\n });\n await writeFile(path.join(projectDir, '.husky', 'pre-commit'), huskyPreCommit);\n\n // Create .gitignore\n const gitignore = `# Dependencies\nnode_modules/\n\n# Build output\ndist/\n\n# IDE\n.idea/\n.vscode/\n*.swp\n*.swo\n\n# OS\n.DS_Store\nThumbs.db\n\n# Environment\n.env\n.env.local\n.env.*.local\n\n# Logs\n*.log\nnpm-debug.log*\n\n# Coverage\ncoverage/\n`;\n await writeFile(path.join(projectDir, '.gitignore'), gitignore);\n\n s.stop('Project created!');\n\n // Initialize git\n if (options.git) {\n const initGit = options.yes\n ? true\n : await confirm({\n message: 'Initialize git repository?',\n initialValue: true,\n });\n\n if (initGit) {\n try {\n execSync('git init -b main', { cwd: projectDir, stdio: 'ignore' });\n execSync('git add .', { cwd: projectDir, stdio: 'ignore' });\n execSync('git commit -m \"Initial commit\"', { cwd: projectDir, stdio: 'ignore' });\n logger.success('Git repository initialized with initial commit');\n } catch {\n logger.warn('Failed to initialize git repository');\n }\n }\n }\n\n // Show next steps\n note(\n `cd ${name}\npnpm install\npnpm dev`,\n 'Next steps'\n );\n\n outro('Happy coding!');\n } catch (error) {\n s.stop('Failed to create project');\n logger.error(\n error instanceof Error ? error.message : 'Unknown error occurred'\n );\n process.exit(1);\n }\n}\n","import fs from 'fs-extra';\nimport path from 'path';\n\n/**\n * Check if a directory exists\n */\nexport async function directoryExists(dirPath: string): Promise<boolean> {\n try {\n const stat = await fs.stat(dirPath);\n return stat.isDirectory();\n } catch {\n return false;\n }\n}\n\n/**\n * Check if a file exists\n */\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n const stat = await fs.stat(filePath);\n return stat.isFile();\n } catch {\n return false;\n }\n}\n\n/**\n * Create a directory (and parents) if it doesn't exist\n */\nexport async function ensureDir(dirPath: string): Promise<void> {\n await fs.ensureDir(dirPath);\n}\n\n/**\n * Write content to a file, creating directories as needed\n */\nexport async function writeFile(\n filePath: string,\n content: string\n): Promise<void> {\n await fs.ensureDir(path.dirname(filePath));\n await fs.writeFile(filePath, content, 'utf-8');\n}\n\n/**\n * Read file content\n */\nexport async function readFile(filePath: string): Promise<string> {\n return fs.readFile(filePath, 'utf-8');\n}\n\n/**\n * Copy a file\n */\nexport async function copyFile(src: string, dest: string): Promise<void> {\n await fs.ensureDir(path.dirname(dest));\n await fs.copyFile(src, dest);\n}\n\n/**\n * Copy a directory recursively\n */\nexport async function copyDir(src: string, dest: string): Promise<void> {\n await fs.copy(src, dest);\n}\n\n/**\n * Get the current working directory\n */\nexport function getCwd(): string {\n return process.cwd();\n}\n\n/**\n * Resolve a path relative to cwd\n */\nexport function resolvePath(...paths: string[]): string {\n return path.resolve(getCwd(), ...paths);\n}\n\n/**\n * Check if current directory is an elsapiens project\n */\nexport async function isElSapiensProject(dir: string = getCwd()): Promise<boolean> {\n const packageJsonPath = path.join(dir, 'package.json');\n\n if (!(await fileExists(packageJsonPath))) {\n return false;\n }\n\n try {\n const content = await readFile(packageJsonPath);\n const pkg = JSON.parse(content);\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n\n // Check for any @elsapiens package\n return Object.keys(deps).some((dep) => dep.startsWith('@elsapiens/'));\n } catch {\n return false;\n }\n}\n\n/**\n * Find the pages directory in a project\n */\nexport async function findPagesDir(dir: string = getCwd()): Promise<string | null> {\n const candidates = [\n path.join(dir, 'src', 'pages'),\n path.join(dir, 'src', 'app'),\n path.join(dir, 'pages'),\n ];\n\n for (const candidate of candidates) {\n if (await directoryExists(candidate)) {\n return candidate;\n }\n }\n\n return null;\n}\n\n/**\n * Find the components directory in a project\n */\nexport async function findComponentsDir(\n dir: string = getCwd()\n): Promise<string | null> {\n const candidates = [\n path.join(dir, 'src', 'components'),\n path.join(dir, 'components'),\n ];\n\n for (const candidate of candidates) {\n if (await directoryExists(candidate)) {\n return candidate;\n }\n }\n\n return null;\n}\n\n/**\n * Convert a string to PascalCase\n */\nexport function toPascalCase(str: string): string {\n return str\n .split(/[-_\\s]+/)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join('');\n}\n\n/**\n * Convert a string to kebab-case\n */\nexport function toKebabCase(str: string): string {\n return str\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .toLowerCase();\n}\n\n/**\n * Convert a string to a valid route path\n */\nexport function toRoutePath(str: string): string {\n return '/' + toKebabCase(str);\n}\n","import * as p from '@clack/prompts';\nimport pc from 'picocolors';\n\n/**\n * Show intro banner\n */\nexport function intro(message: string): void {\n p.intro(pc.bgCyan(pc.black(` ${message} `)));\n}\n\n/**\n * Show outro message\n */\nexport function outro(message: string): void {\n p.outro(pc.green(message));\n}\n\n/**\n * Show a cancel message and exit\n */\nexport function cancel(message: string = 'Operation cancelled.'): never {\n p.cancel(message);\n process.exit(0);\n}\n\n/**\n * Check if the user cancelled the prompt\n */\nexport function isCancel(value: unknown): value is symbol {\n return p.isCancel(value);\n}\n\n/**\n * Handle cancel - exit if cancelled, return value otherwise\n */\nexport function handleCancel<T>(value: T | symbol): T {\n if (isCancel(value)) {\n cancel();\n }\n return value as T;\n}\n\n/**\n * Ask for text input\n */\nexport async function text(options: {\n message: string;\n placeholder?: string;\n initialValue?: string;\n validate?: (value: string) => string | void;\n}): Promise<string> {\n const result = await p.text(options);\n return handleCancel(result);\n}\n\n/**\n * Ask for a selection from options\n */\nexport async function select<T extends string>(options: {\n message: string;\n options: { value: T; label: string; hint?: string }[];\n initialValue?: T;\n}): Promise<T> {\n const result = await p.select(options);\n return handleCancel(result) as T;\n}\n\n/**\n * Ask for multiple selections\n */\nexport async function multiselect<T extends string>(options: {\n message: string;\n options: { value: T; label: string; hint?: string }[];\n required?: boolean;\n}): Promise<T[]> {\n const result = await p.multiselect(options);\n return handleCancel(result) as T[];\n}\n\n/**\n * Ask for confirmation\n */\nexport async function confirm(options: {\n message: string;\n initialValue?: boolean;\n}): Promise<boolean> {\n const result = await p.confirm(options);\n return handleCancel(result);\n}\n\n/**\n * Show a spinner during async operation\n */\nexport function spinner(): {\n start: (message?: string) => void;\n stop: (message?: string, code?: number) => void;\n message: (message?: string) => void;\n} {\n return p.spinner();\n}\n\n/**\n * Show a group of tasks\n */\nexport async function tasks(\n items: { title: string; task: () => Promise<string> }[]\n): Promise<void> {\n await p.tasks(items);\n}\n\n/**\n * Log a note/info message\n */\nexport function note(message: string, title?: string): void {\n p.note(message, title);\n}\n\n/**\n * Log a message during prompts\n */\nexport function log(options: { message?: string; symbol?: string }): void {\n p.log.message(options.message ?? '', { symbol: options.symbol });\n}\n","import ejs from 'ejs';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\nimport { readFile } from './file.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Templates are in dist/templates when bundled (same level as index.js)\nconst TEMPLATES_DIR = path.resolve(__dirname, 'templates');\n\nexport interface TemplateData {\n [key: string]: unknown;\n}\n\n/**\n * Get the path to a template file\n */\nexport function getTemplatePath(category: string, name: string): string {\n return path.join(TEMPLATES_DIR, category, `${name}.ejs`);\n}\n\n/**\n * Render an EJS template with the given data\n */\nexport async function renderTemplate(\n templatePath: string,\n data: TemplateData\n): Promise<string> {\n const template = await readFile(templatePath);\n return ejs.render(template, data, {\n async: false,\n });\n}\n\n/**\n * Render a named template from the templates directory\n */\nexport async function render(\n category: string,\n name: string,\n data: TemplateData\n): Promise<string> {\n const templatePath = getTemplatePath(category, name);\n return renderTemplate(templatePath, data);\n}\n\n/**\n * Render a page template\n */\nexport async function renderPage(\n templateName: string,\n data: {\n name: string;\n pascalName: string;\n title: string;\n description: string;\n [key: string]: unknown;\n }\n): Promise<string> {\n return render('pages', templateName, data);\n}\n\n/**\n * Render a component template\n */\nexport async function renderComponent(\n templateName: string,\n data: {\n name: string;\n pascalName: string;\n [key: string]: unknown;\n }\n): Promise<string> {\n return render('components', templateName, data);\n}\n\n/**\n * Render an app template\n */\nexport async function renderApp(\n templateName: string,\n data: {\n projectName: string;\n [key: string]: unknown;\n }\n): Promise<string> {\n return render('app', templateName, data);\n}\n","import pc from 'picocolors';\n\nexport const logger = {\n info: (message: string) => {\n console.log(pc.blue('ℹ'), message);\n },\n\n success: (message: string) => {\n console.log(pc.green('✓'), message);\n },\n\n warn: (message: string) => {\n console.log(pc.yellow('⚠'), message);\n },\n\n error: (message: string) => {\n console.log(pc.red('✗'), message);\n },\n\n log: (message: string) => {\n console.log(message);\n },\n\n blank: () => {\n console.log();\n },\n\n title: (message: string) => {\n console.log();\n console.log(pc.bold(pc.cyan(message)));\n },\n\n step: (message: string) => {\n console.log(pc.dim(' -'), message);\n },\n\n code: (code: string) => {\n console.log(pc.dim(' ') + pc.cyan(code));\n },\n\n box: (message: string) => {\n const line = '─'.repeat(message.length + 2);\n console.log(pc.dim(`┌${line}┐`));\n console.log(pc.dim('│ ') + pc.bold(message) + pc.dim(' │'));\n console.log(pc.dim(`└${line}┘`));\n },\n};\n\nexport default logger;\n","/**\n * Add Page Command Module\n *\n * This module provides the `add page` command for creating new page components\n * in an existing elSapiens project. It supports multiple page templates and\n * generates the necessary routing code.\n *\n * @module commands/add/page\n */\n\nimport path from 'path';\nimport {\n fileExists,\n findPagesDir,\n isElSapiensProject,\n writeFile,\n resolvePath,\n toPascalCase,\n toKebabCase,\n toRoutePath,\n getCwd,\n} from '../../utils/file.js';\nimport {\n intro,\n outro,\n text,\n select,\n note,\n cancel,\n} from '../../utils/prompt.js';\nimport { renderPage } from '../../utils/template.js';\nimport { logger } from '../../utils/logger.js';\n\n/**\n * Options for the page command.\n *\n * @interface PageOptions\n * @property {('dashboard' | 'list' | 'settings')} template - The page template to use\n * @property {string} [path] - Optional custom path for the pages directory\n * @property {boolean} [yes] - Skip prompts and use defaults\n */\ninterface PageOptions {\n template: 'dashboard' | 'list' | 'settings';\n path?: string;\n yes?: boolean;\n}\n\n/**\n * Creates a new page component in an elSapiens project.\n *\n * This command generates a page file based on the selected template and provides\n * instructions for integrating it into the application's routing configuration.\n *\n * Available templates:\n * - `dashboard` - Overview page with stats, charts, and activity table\n * - `list` - Data listing page with search, filters, and table\n * - `settings` - Configuration page with form sections\n *\n * The command will:\n * 1. Verify the current directory is an elSapiens project\n * 2. Prompt for page name if not provided\n * 3. Allow template selection\n * 4. Prompt for page title and description\n * 5. Generate the page file in the pages directory\n * 6. Display routing instructions for App.tsx integration\n *\n * @param pageName - The name of the page to create. Should be in PascalCase or\n * will be converted. If not provided, user will be prompted.\n * @param options - Configuration options for page generation\n * @param options.template - The page template to use ('dashboard', 'list', or 'settings')\n * @param options.path - Custom path for the pages directory (defaults to auto-detected)\n * @returns A promise that resolves when the page is created successfully\n *\n * @example\n * // Create a dashboard page named 'Analytics'\n * await pageCommand('Analytics', { template: 'dashboard' });\n *\n * @example\n * // Create a list page with custom path\n * await pageCommand('Users', { template: 'list', path: './src/views' });\n *\n * @example\n * // Interactive mode (no page name provided)\n * await pageCommand(undefined, { template: 'settings' });\n *\n * @throws Will exit with code 1 if page creation fails\n * @throws Will cancel if not in an elSapiens project directory\n * @throws Will cancel if the page file already exists\n */\nexport async function pageCommand(\n pageName: string | undefined,\n options: PageOptions\n): Promise<void> {\n intro('Add Page');\n\n // Check if we're in an elsapiens project\n const isProject = await isElSapiensProject();\n if (!isProject) {\n logger.warn('This does not appear to be an elSapiens project.');\n logger.info('Run \"el init\" to create a new project first.');\n cancel();\n }\n\n // Get page name\n let name = pageName;\n if (!name && !options.yes) {\n name = await text({\n message: 'Page name:',\n placeholder: 'Users',\n validate: (value) => {\n if (!value) return 'Page name is required';\n if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(value)) {\n return 'Page name must start with a letter and contain only letters and numbers';\n }\n },\n });\n }\n\n if (!name) {\n logger.error('Page name is required');\n process.exit(1);\n }\n\n const pascalName = toPascalCase(name);\n const kebabName = toKebabCase(name);\n const routePath = toRoutePath(name);\n\n // Get template\n let template = options.template;\n if (!options.yes) {\n template = await select({\n message: 'Select a template:',\n options: [\n {\n value: 'dashboard',\n label: 'Dashboard',\n hint: 'Overview page with stats, charts, and activity table',\n },\n {\n value: 'list',\n label: 'List',\n hint: 'Data listing with search, filters, and table',\n },\n {\n value: 'settings',\n label: 'Settings',\n hint: 'Configuration page with form sections',\n },\n ],\n initialValue: options.template,\n });\n }\n\n // Get page title\n let title = pascalName;\n if (!options.yes) {\n title = await text({\n message: 'Page title:',\n initialValue: pascalName,\n });\n }\n\n // Get page description\n let description = `Manage ${kebabName.replace(/-/g, ' ')}`;\n if (!options.yes) {\n description = await text({\n message: 'Page description:',\n placeholder: 'Manage your data',\n initialValue: description,\n });\n }\n\n // Get additional options for list template\n let icon = 'Package';\n let singularTitle = pascalName.endsWith('s') ? pascalName.slice(0, -1) : pascalName;\n\n if (template === 'list' && !options.yes) {\n icon = await text({\n message: 'Icon name (from lucide-react):',\n placeholder: 'Package',\n initialValue: icon,\n });\n\n singularTitle = await text({\n message: 'Singular title (for \"Add Item\" button):',\n placeholder: singularTitle,\n initialValue: singularTitle,\n });\n }\n\n // Find pages directory\n let pagesDir = options.path ? resolvePath(options.path) : null;\n if (!pagesDir) {\n pagesDir = await findPagesDir();\n if (!pagesDir) {\n pagesDir = resolvePath('src', 'pages');\n }\n }\n\n const filePath = path.join(pagesDir, `${pascalName}.tsx`);\n\n // Check if file exists\n if (await fileExists(filePath)) {\n logger.error(`File already exists: ${filePath}`);\n cancel();\n }\n\n try {\n // Render template\n const content = await renderPage(template, {\n name,\n pascalName,\n kebabName,\n title,\n description,\n routePath,\n icon,\n singularTitle,\n });\n\n // Write file\n await writeFile(filePath, content);\n\n logger.success(`Created ${path.relative(getCwd(), filePath)}`);\n\n // Show routing instructions\n note(\n `Add this route to your App.tsx:\n\nimport ${pascalName} from './pages/${pascalName}';\n\n<Route path=\"${routePath}\" element={<${pascalName} />} />\n\nAnd add to navigation:\n{ id: '${kebabName}', path: '${routePath}', label: '${title}', icon: <Icon /> }`,\n 'Next steps'\n );\n\n outro('Page created!');\n } catch (error) {\n logger.error(\n error instanceof Error ? error.message : 'Failed to create page'\n );\n process.exit(1);\n }\n}\n","/**\n * Add Component Command Module\n *\n * This module provides the `add component` command for creating new React components\n * in an existing elSapiens project. It supports simple and stateful component templates.\n *\n * @module commands/add/component\n */\n\nimport path from 'path';\nimport {\n fileExists,\n findComponentsDir,\n isElSapiensProject,\n writeFile,\n resolvePath,\n toPascalCase,\n getCwd,\n} from '../../utils/file.js';\nimport { intro, outro, text, select, note, cancel } from '../../utils/prompt.js';\nimport { renderComponent } from '../../utils/template.js';\nimport { logger } from '../../utils/logger.js';\n\n/**\n * Options for the component command.\n *\n * @interface ComponentOptions\n * @property {('simple' | 'stateful')} template - The component template to use\n * @property {string} [path] - Optional custom path for the components directory\n */\ninterface ComponentOptions {\n template: 'simple' | 'stateful';\n path?: string;\n}\n\n/**\n * Creates a new React component in an elSapiens project.\n *\n * This command generates a component file based on the selected template and\n * provides usage instructions for importing and using the component.\n *\n * Available templates:\n * - `simple` - Basic functional component with props and className support\n * - `stateful` - Component with useState hook and onChange callback pattern\n *\n * The command will:\n * 1. Verify the current directory is an elSapiens project\n * 2. Prompt for component name if not provided\n * 3. Allow template selection (simple or stateful)\n * 4. Generate the component file in the components directory\n * 5. Display usage instructions for importing the component\n *\n * @param componentName - The name of the component to create. Should be in PascalCase\n * or will be converted. If not provided, user will be prompted.\n * @param options - Configuration options for component generation\n * @param options.template - The component template to use ('simple' or 'stateful')\n * @param options.path - Custom path for the components directory (defaults to auto-detected)\n * @returns A promise that resolves when the component is created successfully\n *\n * @example\n * // Create a simple component named 'UserCard'\n * await componentCommand('UserCard', { template: 'simple' });\n *\n * @example\n * // Create a stateful component with custom path\n * await componentCommand('Counter', { template: 'stateful', path: './src/ui' });\n *\n * @example\n * // Interactive mode (no component name provided)\n * await componentCommand(undefined, { template: 'simple' });\n *\n * @throws Will exit with code 1 if component creation fails\n * @throws Will cancel if not in an elSapiens project directory\n * @throws Will cancel if the component file already exists\n */\nexport async function componentCommand(\n componentName: string | undefined,\n options: ComponentOptions\n): Promise<void> {\n intro('Add Component');\n\n // Check if we're in an elsapiens project\n const isProject = await isElSapiensProject();\n if (!isProject) {\n logger.warn('This does not appear to be an elSapiens project.');\n logger.info('Run \"el init\" to create a new project first.');\n cancel();\n }\n\n // Get component name\n let name = componentName;\n if (!name) {\n name = await text({\n message: 'Component name:',\n placeholder: 'UserCard',\n validate: (value) => {\n if (!value) return 'Component name is required';\n if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(value)) {\n return 'Component name must start with a letter and contain only letters and numbers';\n }\n },\n });\n }\n\n const pascalName = toPascalCase(name);\n\n // Get template\n const template = await select({\n message: 'Select a template:',\n options: [\n {\n value: 'simple',\n label: 'Simple',\n hint: 'Basic component with props and className',\n },\n {\n value: 'stateful',\n label: 'Stateful',\n hint: 'Component with useState and onChange callback',\n },\n ],\n initialValue: options.template,\n });\n\n // Find components directory\n let componentsDir = options.path ? resolvePath(options.path) : null;\n if (!componentsDir) {\n componentsDir = await findComponentsDir();\n if (!componentsDir) {\n componentsDir = resolvePath('src', 'components');\n }\n }\n\n const filePath = path.join(componentsDir, `${pascalName}.tsx`);\n\n // Check if file exists\n if (await fileExists(filePath)) {\n logger.error(`File already exists: ${filePath}`);\n cancel();\n }\n\n try {\n // Render template\n const content = await renderComponent(template, {\n name,\n pascalName,\n });\n\n // Write file\n await writeFile(filePath, content);\n\n logger.success(`Created ${path.relative(getCwd(), filePath)}`);\n\n // Show usage instructions\n note(\n `Import and use your component:\n\nimport { ${pascalName} } from './components/${pascalName}';\n\n<${pascalName} className=\"...\" />`,\n 'Usage'\n );\n\n outro('Component created!');\n } catch (error) {\n logger.error(\n error instanceof Error ? error.message : 'Failed to create component'\n );\n process.exit(1);\n }\n}\n","import { pageCommand } from './page.js';\nimport { componentCommand } from './component.js';\n\nexport const addCommand = {\n page: pageCommand,\n component: componentCommand,\n};\n","/**\n * elSapiens CLI Entry Point\n *\n * This module serves as the main entry point for the elSapiens CLI application.\n * It initializes and starts the command-line interface by creating and parsing\n * the CLI instance.\n *\n * @module cli\n *\n * @example\n * // Running the CLI from the command line:\n * npx el init my-project\n * npx el add page Users\n * npx el add component UserCard\n */\n\nimport { createCli } from './cli.js';\n\n/**\n * Creates the CLI instance and parses command-line arguments.\n *\n * The CLI supports the following commands:\n * - `init [name]` - Initialize a new elSapiens project\n * - `add page [name]` - Add a new page to an existing project\n * - `add component [name]` - Add a new component to an existing project\n *\n * @example\n * // The CLI is automatically invoked when running the package:\n * // npx el init my-app --template full --git\n */\nconst cli = createCli();\ncli.parse();\n"],"mappings":";;;AAAA,SAAS,eAAe;;;ACSxB,OAAOA,WAAU;AACjB,SAAS,gBAAgB;;;ACVzB,OAAO,QAAQ;AACf,OAAO,UAAU;AAKjB,eAAsB,gBAAgB,SAAmC;AACvE,MAAI;AACF,UAAM,OAAO,MAAM,GAAG,KAAK,OAAO;AAClC,WAAO,KAAK,YAAY;AAAA,EAC1B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKA,eAAsB,WAAW,UAAoC;AACnE,MAAI;AACF,UAAM,OAAO,MAAM,GAAG,KAAK,QAAQ;AACnC,WAAO,KAAK,OAAO;AAAA,EACrB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKA,eAAsB,UAAU,SAAgC;AAC9D,QAAM,GAAG,UAAU,OAAO;AAC5B;AAKA,eAAsB,UACpB,UACA,SACe;AACf,QAAM,GAAG,UAAU,KAAK,QAAQ,QAAQ,CAAC;AACzC,QAAM,GAAG,UAAU,UAAU,SAAS,OAAO;AAC/C;AAKA,eAAsB,SAAS,UAAmC;AAChE,SAAO,GAAG,SAAS,UAAU,OAAO;AACtC;AAoBO,SAAS,SAAiB;AAC/B,SAAO,QAAQ,IAAI;AACrB;AAKO,SAAS,eAAe,OAAyB;AACtD,SAAO,KAAK,QAAQ,OAAO,GAAG,GAAG,KAAK;AACxC;AAKA,eAAsB,mBAAmB,MAAc,OAAO,GAAqB;AACjF,QAAM,kBAAkB,KAAK,KAAK,KAAK,cAAc;AAErD,MAAI,CAAE,MAAM,WAAW,eAAe,GAAI;AACxC,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,UAAU,MAAM,SAAS,eAAe;AAC9C,UAAM,MAAM,KAAK,MAAM,OAAO;AAC9B,UAAM,OAAO,EAAE,GAAG,IAAI,cAAc,GAAG,IAAI,gBAAgB;AAG3D,WAAO,OAAO,KAAK,IAAI,EAAE,KAAK,CAAC,QAAQ,IAAI,WAAW,aAAa,CAAC;AAAA,EACtE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKA,eAAsB,aAAa,MAAc,OAAO,GAA2B;AACjF,QAAM,aAAa;AAAA,IACjB,KAAK,KAAK,KAAK,OAAO,OAAO;AAAA,IAC7B,KAAK,KAAK,KAAK,OAAO,KAAK;AAAA,IAC3B,KAAK,KAAK,KAAK,OAAO;AAAA,EACxB;AAEA,aAAW,aAAa,YAAY;AAClC,QAAI,MAAM,gBAAgB,SAAS,GAAG;AACpC,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAKA,eAAsB,kBACpB,MAAc,OAAO,GACG;AACxB,QAAM,aAAa;AAAA,IACjB,KAAK,KAAK,KAAK,OAAO,YAAY;AAAA,IAClC,KAAK,KAAK,KAAK,YAAY;AAAA,EAC7B;AAEA,aAAW,aAAa,YAAY;AAClC,QAAI,MAAM,gBAAgB,SAAS,GAAG;AACpC,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,aAAa,KAAqB;AAChD,SAAO,IACJ,MAAM,SAAS,EACf,IAAI,CAAC,SAAS,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC,EAAE,YAAY,CAAC,EACxE,KAAK,EAAE;AACZ;AAKO,SAAS,YAAY,KAAqB;AAC/C,SAAO,IACJ,QAAQ,mBAAmB,OAAO,EAClC,QAAQ,WAAW,GAAG,EACtB,YAAY;AACjB;AAKO,SAAS,YAAY,KAAqB;AAC/C,SAAO,MAAM,YAAY,GAAG;AAC9B;;;ACvKA,YAAY,OAAO;AACnB,OAAO,QAAQ;AAKR,SAASC,OAAM,SAAuB;AAC3C,EAAE,QAAM,GAAG,OAAO,GAAG,MAAM,IAAI,OAAO,GAAG,CAAC,CAAC;AAC7C;AAKO,SAASC,OAAM,SAAuB;AAC3C,EAAE,QAAM,GAAG,MAAM,OAAO,CAAC;AAC3B;AAKO,SAASC,QAAO,UAAkB,wBAA+B;AACtE,EAAE,SAAO,OAAO;AAChB,UAAQ,KAAK,CAAC;AAChB;AAKO,SAASC,UAAS,OAAiC;AACxD,SAAS,WAAS,KAAK;AACzB;AAKO,SAAS,aAAgB,OAAsB;AACpD,MAAIA,UAAS,KAAK,GAAG;AACnB,IAAAD,QAAO;AAAA,EACT;AACA,SAAO;AACT;AAKA,eAAsBE,MAAK,SAKP;AAClB,QAAM,SAAS,MAAQ,OAAK,OAAO;AACnC,SAAO,aAAa,MAAM;AAC5B;AAKA,eAAsBC,QAAyB,SAIhC;AACb,QAAM,SAAS,MAAQ,SAAO,OAAO;AACrC,SAAO,aAAa,MAAM;AAC5B;AAiBA,eAAsBC,SAAQ,SAGT;AACnB,QAAM,SAAS,MAAQ,UAAQ,OAAO;AACtC,SAAO,aAAa,MAAM;AAC5B;AAKO,SAASC,WAId;AACA,SAAS,UAAQ;AACnB;AAcO,SAASC,MAAK,SAAiB,OAAsB;AAC1D,EAAE,OAAK,SAAS,KAAK;AACvB;;;ACnHA,OAAO,SAAS;AAChB,OAAOC,WAAU;AACjB,SAAS,qBAAqB;AAG9B,IAAM,aAAa,cAAc,YAAY,GAAG;AAChD,IAAM,YAAYC,MAAK,QAAQ,UAAU;AAGzC,IAAM,gBAAgBA,MAAK,QAAQ,WAAW,WAAW;AASlD,SAAS,gBAAgB,UAAkB,MAAsB;AACtE,SAAOA,MAAK,KAAK,eAAe,UAAU,GAAG,IAAI,MAAM;AACzD;AAKA,eAAsB,eACpB,cACA,MACiB;AACjB,QAAM,WAAW,MAAM,SAAS,YAAY;AAC5C,SAAO,IAAI,OAAO,UAAU,MAAM;AAAA,IAChC,OAAO;AAAA,EACT,CAAC;AACH;AAKA,eAAsB,OACpB,UACA,MACA,MACiB;AACjB,QAAM,eAAe,gBAAgB,UAAU,IAAI;AACnD,SAAO,eAAe,cAAc,IAAI;AAC1C;AAKA,eAAsB,WACpB,cACA,MAOiB;AACjB,SAAO,OAAO,SAAS,cAAc,IAAI;AAC3C;AAKA,eAAsB,gBACpB,cACA,MAKiB;AACjB,SAAO,OAAO,cAAc,cAAc,IAAI;AAChD;AAKA,eAAsB,UACpB,cACA,MAIiB;AACjB,SAAO,OAAO,OAAO,cAAc,IAAI;AACzC;;;ACxFA,OAAOC,SAAQ;AAER,IAAM,SAAS;AAAA,EACpB,MAAM,CAAC,YAAoB;AACzB,YAAQ,IAAIA,IAAG,KAAK,QAAG,GAAG,OAAO;AAAA,EACnC;AAAA,EAEA,SAAS,CAAC,YAAoB;AAC5B,YAAQ,IAAIA,IAAG,MAAM,QAAG,GAAG,OAAO;AAAA,EACpC;AAAA,EAEA,MAAM,CAAC,YAAoB;AACzB,YAAQ,IAAIA,IAAG,OAAO,QAAG,GAAG,OAAO;AAAA,EACrC;AAAA,EAEA,OAAO,CAAC,YAAoB;AAC1B,YAAQ,IAAIA,IAAG,IAAI,QAAG,GAAG,OAAO;AAAA,EAClC;AAAA,EAEA,KAAK,CAAC,YAAoB;AACxB,YAAQ,IAAI,OAAO;AAAA,EACrB;AAAA,EAEA,OAAO,MAAM;AACX,YAAQ,IAAI;AAAA,EACd;AAAA,EAEA,OAAO,CAAC,YAAoB;AAC1B,YAAQ,IAAI;AACZ,YAAQ,IAAIA,IAAG,KAAKA,IAAG,KAAK,OAAO,CAAC,CAAC;AAAA,EACvC;AAAA,EAEA,MAAM,CAAC,YAAoB;AACzB,YAAQ,IAAIA,IAAG,IAAI,KAAK,GAAG,OAAO;AAAA,EACpC;AAAA,EAEA,MAAM,CAAC,SAAiB;AACtB,YAAQ,IAAIA,IAAG,IAAI,MAAM,IAAIA,IAAG,KAAK,IAAI,CAAC;AAAA,EAC5C;AAAA,EAEA,KAAK,CAAC,YAAoB;AACxB,UAAM,OAAO,SAAI,OAAO,QAAQ,SAAS,CAAC;AAC1C,YAAQ,IAAIA,IAAG,IAAI,SAAI,IAAI,QAAG,CAAC;AAC/B,YAAQ,IAAIA,IAAG,IAAI,SAAI,IAAIA,IAAG,KAAK,OAAO,IAAIA,IAAG,IAAI,SAAI,CAAC;AAC1D,YAAQ,IAAIA,IAAG,IAAI,SAAI,IAAI,QAAG,CAAC;AAAA,EACjC;AACF;;;AJoCA,eAAsB,YACpB,aACA,SACe;AACf,EAAAC,OAAM,eAAe;AAGrB,MAAI,OAAO;AACX,MAAI,CAAC,QAAQ,CAAC,QAAQ,KAAK;AACzB,WAAO,MAAMC,MAAK;AAAA,MAChB,SAAS;AAAA,MACT,aAAa;AAAA,MACb,UAAU,CAAC,UAAU;AACnB,YAAI,CAAC,MAAO,QAAO;AACnB,YAAI,CAAC,iBAAiB,KAAK,KAAK,GAAG;AACjC,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAEA,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AAGA,MAAI,YAAY;AAChB,MAAI,WAAW;AACf,MAAI,WAAW;AAEf,MAAI,CAAC,QAAQ,KAAK;AAChB,gBAAY,MAAMA,MAAK;AAAA,MACrB,SAAS;AAAA,MACT,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU,CAAC,UAAU;AACnB,cAAM,OAAO,SAAS,OAAO,EAAE;AAC/B,YAAI,MAAM,IAAI,KAAK,OAAO,KAAK,OAAO,OAAO;AAC3C,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF,CAAC,KAAK;AAEN,eAAW,MAAMA,MAAK;AAAA,MACpB,SAAS;AAAA,MACT,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU,CAAC,UAAU;AACnB,cAAM,OAAO,SAAS,OAAO,EAAE;AAC/B,YAAI,MAAM,IAAI,KAAK,OAAO,KAAK,OAAO,OAAO;AAC3C,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF,CAAC,KAAK;AAEN,eAAW,MAAMA,MAAK;AAAA,MACpB,SAAS;AAAA,MACT,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU,CAAC,UAAU;AACnB,cAAM,OAAO,SAAS,OAAO,EAAE;AAC/B,YAAI,MAAM,IAAI,KAAK,OAAO,KAAK,OAAO,OAAO;AAC3C,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF,CAAC,KAAK;AAAA,EACR;AAEA,QAAM,aAAa,YAAY,IAAI;AAGnC,MAAI,MAAM,gBAAgB,UAAU,GAAG;AACrC,UAAM,kBAAkB,QAAQ,MAC5B,QACA,MAAMC,SAAQ;AAAA,MACZ,SAAS,cAAc,IAAI;AAAA,MAC3B,cAAc;AAAA,IAChB,CAAC;AAEL,QAAI,CAAC,iBAAiB;AACpB,MAAAC,QAAO,6BAA6B;AAAA,IACtC;AAAA,EACF;AAGA,QAAM,IAAIC,SAAQ;AAClB,IAAE,MAAM,qBAAqB;AAE7B,MAAI;AACF,UAAM,UAAU,UAAU;AAG1B,UAAM,aAAa,aAAa,IAAI;AAGpC,UAAM,cAAc,MAAM,UAAU,WAAW;AAAA,MAC7C,aAAa;AAAA,MACb;AAAA,IACF,CAAC;AACD,UAAM,UAAUC,MAAK,KAAK,YAAY,cAAc,GAAG,WAAW;AAGlE,UAAM,aAAa,MAAM,UAAU,eAAe;AAAA,MAChD,aAAa;AAAA,MACb;AAAA,IACF,CAAC;AACD,UAAM,UAAUA,MAAK,KAAK,YAAY,gBAAgB,GAAG,UAAU;AAGnE,UAAM,iBAAiB,MAAM,UAAU,mBAAmB;AAAA,MACxD,aAAa;AAAA,MACb;AAAA,IACF,CAAC;AACD,UAAM,UAAUA,MAAK,KAAK,YAAY,oBAAoB,GAAG,cAAc;AAG3E,UAAM,gBAAgB,MAAM,UAAU,kBAAkB;AAAA,MACtD,aAAa;AAAA,MACb;AAAA,IACF,CAAC;AACD,UAAM,UAAUA,MAAK,KAAK,YAAY,mBAAmB,GAAG,aAAa;AAGzE,UAAM,WAAW,MAAM,UAAU,YAAY;AAAA,MAC3C,aAAa;AAAA,IACf,CAAC;AACD,UAAM,UAAUA,MAAK,KAAK,YAAY,eAAe,GAAG,QAAQ;AAGhE,UAAM,eAAe,MAAM,UAAU,iBAAiB;AAAA,MACpD,aAAa;AAAA,IACf,CAAC;AACD,UAAM,UAAUA,MAAK,KAAK,YAAY,oBAAoB,GAAG,YAAY;AAGzE,UAAM,YAAY,MAAM,UAAU,cAAc;AAAA,MAC9C,aAAa;AAAA,MACb;AAAA,IACF,CAAC;AACD,UAAM,UAAUA,MAAK,KAAK,YAAY,YAAY,GAAG,SAAS;AAG9D,UAAM,UAAU,MAAM,UAAU,QAAQ;AAAA,MACtC,aAAa;AAAA,MACb;AAAA,IACF,CAAC;AACD,UAAM,UAAUA,MAAK,KAAK,YAAY,OAAO,UAAU,GAAG,OAAO;AAGjE,UAAM,SAAS,MAAM,UAAU,OAAO;AAAA,MACpC,aAAa;AAAA,MACb;AAAA,IACF,CAAC;AACD,UAAM,UAAUA,MAAK,KAAK,YAAY,OAAO,SAAS,GAAG,MAAM;AAG/D,UAAM,WAAW,MAAM,UAAU,aAAa;AAAA,MAC5C,aAAa;AAAA,IACf,CAAC;AACD,UAAM,UAAUA,MAAK,KAAK,YAAY,OAAO,WAAW,GAAG,QAAQ;AAGnE,UAAM,UAAU,MAAM,UAAU,YAAY;AAAA,MAC1C,aAAa;AAAA,IACf,CAAC;AACD,UAAM,UAAUA,MAAK,KAAK,YAAY,OAAO,eAAe,GAAG,OAAO;AAGtE,UAAM,gBAAgB,MAAM,UAAU,kBAAkB;AAAA,MACtD,aAAa;AAAA,MACb;AAAA,IACF,CAAC;AACD,UAAM;AAAA,MACJA,MAAK,KAAK,YAAY,OAAO,SAAS,eAAe;AAAA,MACrD;AAAA,IACF;AAGA,UAAM,eAAe,MAAM,UAAU,iBAAiB;AAAA,MACpD,aAAa;AAAA,MACb;AAAA,IACF,CAAC;AACD,UAAM;AAAA,MACJA,MAAK,KAAK,YAAY,OAAO,SAAS,cAAc;AAAA,MACpD;AAAA,IACF;AAGA,UAAM,uBAAuB,MAAM,UAAU,0BAA0B;AAAA,MACrE,aAAa;AAAA,MACb;AAAA,IACF,CAAC;AACD,UAAM;AAAA,MACJA,MAAK,KAAK,YAAY,OAAO,gBAAgB,UAAU,OAAO;AAAA,MAC9D;AAAA,IACF;AAGA,UAAM,0BAA0B,MAAM,UAAU,6BAA6B;AAAA,MAC3E,aAAa;AAAA,MACb;AAAA,IACF,CAAC;AACD,UAAM;AAAA,MACJA,MAAK,KAAK,YAAY,OAAO,gBAAgB,UAAU,UAAU;AAAA,MACjE;AAAA,IACF;AAGA,UAAM,yBAAyB,MAAM,UAAU,4BAA4B;AAAA,MACzE,aAAa;AAAA,MACb;AAAA,IACF,CAAC;AACD,UAAM;AAAA,MACJA,MAAK,KAAK,YAAY,OAAO,gBAAgB,YAAY,OAAO;AAAA,MAChE;AAAA,IACF;AAGA,UAAM,4BAA4B,MAAM,UAAU,+BAA+B;AAAA,MAC/E,aAAa;AAAA,MACb;AAAA,IACF,CAAC;AACD,UAAM;AAAA,MACJA,MAAK,KAAK,YAAY,OAAO,gBAAgB,YAAY,UAAU;AAAA,MACnE;AAAA,IACF;AAGA,UAAM,aAAa,MAAM,UAAU,eAAe;AAAA,MAChD,aAAa;AAAA,MACb;AAAA,IACF,CAAC;AACD,UAAM;AAAA,MACJA,MAAK,KAAK,YAAY,OAAO,gBAAgB;AAAA,MAC7C;AAAA,IACF;AAGA,UAAM,gBAAgB,MAAM,UAAU,kBAAkB;AAAA,MACtD,aAAa;AAAA,MACb;AAAA,IACF,CAAC;AACD,UAAM;AAAA,MACJA,MAAK,KAAK,YAAY,OAAO,YAAY,UAAU;AAAA,MACnD;AAAA,IACF;AAGA,UAAM,aAAa,MAAM,UAAU,eAAe;AAAA,MAChD,aAAa;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AACD,UAAM,UAAUA,MAAK,KAAK,YAAY,cAAc,GAAG,UAAU;AAGjE,UAAM,UAAUA,MAAK,KAAK,YAAY,MAAM,GAAG,UAAU;AAGzD,UAAM,eAAe,MAAM,UAAU,iBAAiB;AAAA,MACpD,aAAa;AAAA,MACb;AAAA,IACF,CAAC;AACD,UAAM,UAAUA,MAAK,KAAK,YAAY,kBAAkB,GAAG,YAAY;AAGvE,UAAM,eAAe,MAAM,UAAU,iBAAiB;AAAA,MACpD,aAAa;AAAA,MACb;AAAA,IACF,CAAC;AACD,UAAM,UAAUA,MAAK,KAAK,YAAY,kBAAkB,GAAG,YAAY;AAGvE,UAAM,YAAY,MAAM,UAAU,cAAc;AAAA,MAC9C,aAAa;AAAA,MACb;AAAA,IACF,CAAC;AACD,UAAM,UAAUA,MAAK,KAAK,YAAY,OAAO,QAAQ,UAAU,GAAG,SAAS;AAG3E,UAAM,iBAAiB,MAAM,UAAU,oBAAoB;AAAA,MACzD,aAAa;AAAA,MACb;AAAA,IACF,CAAC;AACD,UAAM,UAAUA,MAAK,KAAK,YAAY,UAAU,YAAY,GAAG,cAAc;AAG7E,UAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4BlB,UAAM,UAAUA,MAAK,KAAK,YAAY,YAAY,GAAG,SAAS;AAE9D,MAAE,KAAK,kBAAkB;AAGzB,QAAI,QAAQ,KAAK;AACf,YAAM,UAAU,QAAQ,MACpB,OACA,MAAMH,SAAQ;AAAA,QACZ,SAAS;AAAA,QACT,cAAc;AAAA,MAChB,CAAC;AAEL,UAAI,SAAS;AACX,YAAI;AACF,mBAAS,oBAAoB,EAAE,KAAK,YAAY,OAAO,SAAS,CAAC;AACjE,mBAAS,aAAa,EAAE,KAAK,YAAY,OAAO,SAAS,CAAC;AAC1D,mBAAS,kCAAkC,EAAE,KAAK,YAAY,OAAO,SAAS,CAAC;AAC/E,iBAAO,QAAQ,gDAAgD;AAAA,QACjE,QAAQ;AACN,iBAAO,KAAK,qCAAqC;AAAA,QACnD;AAAA,MACF;AAAA,IACF;AAGA,IAAAI;AAAA,MACE,MAAM,IAAI;AAAA;AAAA;AAAA,MAGV;AAAA,IACF;AAEA,IAAAC,OAAM,eAAe;AAAA,EACvB,SAAS,OAAO;AACd,MAAE,KAAK,0BAA0B;AACjC,WAAO;AAAA,MACL,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAC3C;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;;;AK9aA,OAAOC,WAAU;AA+EjB,eAAsB,YACpB,UACA,SACe;AACf,EAAAC,OAAM,UAAU;AAGhB,QAAM,YAAY,MAAM,mBAAmB;AAC3C,MAAI,CAAC,WAAW;AACd,WAAO,KAAK,kDAAkD;AAC9D,WAAO,KAAK,8CAA8C;AAC1D,IAAAC,QAAO;AAAA,EACT;AAGA,MAAI,OAAO;AACX,MAAI,CAAC,QAAQ,CAAC,QAAQ,KAAK;AACzB,WAAO,MAAMC,MAAK;AAAA,MAChB,SAAS;AAAA,MACT,aAAa;AAAA,MACb,UAAU,CAAC,UAAU;AACnB,YAAI,CAAC,MAAO,QAAO;AACnB,YAAI,CAAC,yBAAyB,KAAK,KAAK,GAAG;AACzC,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAEA,MAAI,CAAC,MAAM;AACT,WAAO,MAAM,uBAAuB;AACpC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,aAAa,aAAa,IAAI;AACpC,QAAM,YAAY,YAAY,IAAI;AAClC,QAAM,YAAY,YAAY,IAAI;AAGlC,MAAI,WAAW,QAAQ;AACvB,MAAI,CAAC,QAAQ,KAAK;AAChB,eAAW,MAAMC,QAAO;AAAA,MACtB,SAAS;AAAA,MACT,SAAS;AAAA,QACP;AAAA,UACE,OAAO;AAAA,UACP,OAAO;AAAA,UACP,MAAM;AAAA,QACR;AAAA,QACA;AAAA,UACE,OAAO;AAAA,UACP,OAAO;AAAA,UACP,MAAM;AAAA,QACR;AAAA,QACA;AAAA,UACE,OAAO;AAAA,UACP,OAAO;AAAA,UACP,MAAM;AAAA,QACR;AAAA,MACF;AAAA,MACA,cAAc,QAAQ;AAAA,IACxB,CAAC;AAAA,EACH;AAGA,MAAI,QAAQ;AACZ,MAAI,CAAC,QAAQ,KAAK;AAChB,YAAQ,MAAMD,MAAK;AAAA,MACjB,SAAS;AAAA,MACT,cAAc;AAAA,IAChB,CAAC;AAAA,EACH;AAGA,MAAI,cAAc,UAAU,UAAU,QAAQ,MAAM,GAAG,CAAC;AACxD,MAAI,CAAC,QAAQ,KAAK;AAChB,kBAAc,MAAMA,MAAK;AAAA,MACvB,SAAS;AAAA,MACT,aAAa;AAAA,MACb,cAAc;AAAA,IAChB,CAAC;AAAA,EACH;AAGA,MAAI,OAAO;AACX,MAAI,gBAAgB,WAAW,SAAS,GAAG,IAAI,WAAW,MAAM,GAAG,EAAE,IAAI;AAEzE,MAAI,aAAa,UAAU,CAAC,QAAQ,KAAK;AACvC,WAAO,MAAMA,MAAK;AAAA,MAChB,SAAS;AAAA,MACT,aAAa;AAAA,MACb,cAAc;AAAA,IAChB,CAAC;AAED,oBAAgB,MAAMA,MAAK;AAAA,MACzB,SAAS;AAAA,MACT,aAAa;AAAA,MACb,cAAc;AAAA,IAChB,CAAC;AAAA,EACH;AAGA,MAAI,WAAW,QAAQ,OAAO,YAAY,QAAQ,IAAI,IAAI;AAC1D,MAAI,CAAC,UAAU;AACb,eAAW,MAAM,aAAa;AAC9B,QAAI,CAAC,UAAU;AACb,iBAAW,YAAY,OAAO,OAAO;AAAA,IACvC;AAAA,EACF;AAEA,QAAM,WAAWE,MAAK,KAAK,UAAU,GAAG,UAAU,MAAM;AAGxD,MAAI,MAAM,WAAW,QAAQ,GAAG;AAC9B,WAAO,MAAM,wBAAwB,QAAQ,EAAE;AAC/C,IAAAH,QAAO;AAAA,EACT;AAEA,MAAI;AAEF,UAAM,UAAU,MAAM,WAAW,UAAU;AAAA,MACzC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAGD,UAAM,UAAU,UAAU,OAAO;AAEjC,WAAO,QAAQ,WAAWG,MAAK,SAAS,OAAO,GAAG,QAAQ,CAAC,EAAE;AAG7D,IAAAC;AAAA,MACE;AAAA;AAAA,SAEG,UAAU,kBAAkB,UAAU;AAAA;AAAA,eAEhC,SAAS,eAAe,UAAU;AAAA;AAAA;AAAA,SAGxC,SAAS,aAAa,SAAS,cAAc,KAAK;AAAA,MACrD;AAAA,IACF;AAEA,IAAAC,OAAM,eAAe;AAAA,EACvB,SAAS,OAAO;AACd,WAAO;AAAA,MACL,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAC3C;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;;;AC5OA,OAAOC,WAAU;AAkEjB,eAAsB,iBACpB,eACA,SACe;AACf,EAAAC,OAAM,eAAe;AAGrB,QAAM,YAAY,MAAM,mBAAmB;AAC3C,MAAI,CAAC,WAAW;AACd,WAAO,KAAK,kDAAkD;AAC9D,WAAO,KAAK,8CAA8C;AAC1D,IAAAC,QAAO;AAAA,EACT;AAGA,MAAI,OAAO;AACX,MAAI,CAAC,MAAM;AACT,WAAO,MAAMC,MAAK;AAAA,MAChB,SAAS;AAAA,MACT,aAAa;AAAA,MACb,UAAU,CAAC,UAAU;AACnB,YAAI,CAAC,MAAO,QAAO;AACnB,YAAI,CAAC,yBAAyB,KAAK,KAAK,GAAG;AACzC,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,aAAa,aAAa,IAAI;AAGpC,QAAM,WAAW,MAAMC,QAAO;AAAA,IAC5B,SAAS;AAAA,IACT,SAAS;AAAA,MACP;AAAA,QACE,OAAO;AAAA,QACP,OAAO;AAAA,QACP,MAAM;AAAA,MACR;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,OAAO;AAAA,QACP,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,cAAc,QAAQ;AAAA,EACxB,CAAC;AAGD,MAAI,gBAAgB,QAAQ,OAAO,YAAY,QAAQ,IAAI,IAAI;AAC/D,MAAI,CAAC,eAAe;AAClB,oBAAgB,MAAM,kBAAkB;AACxC,QAAI,CAAC,eAAe;AAClB,sBAAgB,YAAY,OAAO,YAAY;AAAA,IACjD;AAAA,EACF;AAEA,QAAM,WAAWC,MAAK,KAAK,eAAe,GAAG,UAAU,MAAM;AAG7D,MAAI,MAAM,WAAW,QAAQ,GAAG;AAC9B,WAAO,MAAM,wBAAwB,QAAQ,EAAE;AAC/C,IAAAH,QAAO;AAAA,EACT;AAEA,MAAI;AAEF,UAAM,UAAU,MAAM,gBAAgB,UAAU;AAAA,MAC9C;AAAA,MACA;AAAA,IACF,CAAC;AAGD,UAAM,UAAU,UAAU,OAAO;AAEjC,WAAO,QAAQ,WAAWG,MAAK,SAAS,OAAO,GAAG,QAAQ,CAAC,EAAE;AAG7D,IAAAC;AAAA,MACE;AAAA;AAAA,WAEK,UAAU,yBAAyB,UAAU;AAAA;AAAA,GAErD,UAAU;AAAA,MACP;AAAA,IACF;AAEA,IAAAC,OAAM,oBAAoB;AAAA,EAC5B,SAAS,OAAO;AACd,WAAO;AAAA,MACL,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAC3C;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;;;ACvKO,IAAM,aAAa;AAAA,EACxB,MAAM;AAAA,EACN,WAAW;AACb;;;ARFA,IAAM,UAAU;AAET,SAAS,YAAqB;AACnC,QAAM,UAAU,IAAI,QAAQ;AAE5B,UACG,KAAK,WAAW,EAChB,YAAY,iDAAiD,EAC7D,QAAQ,SAAS,iBAAiB,wBAAwB;AAG7D,UACG,QAAQ,qBAAqB,EAC7B,MAAM,QAAQ,EACd,YAAY,gDAAgD,EAC5D,OAAO,YAAY,yBAAyB,EAC5C,OAAO,aAAa,+BAA+B,EACnD,OAAO,WAAW;AAGrB,UACG,QAAQ,KAAK,EACb,MAAM,UAAU,EAChB,MAAM,GAAG,EACT,YAAY,wCAAwC,EACpD;AAAA,IACC,IAAI,QAAQ,MAAM,EACf,SAAS,UAAU,WAAW,EAC9B,YAAY,gBAAgB,EAC5B,OAAO,yBAAyB,6CAA6C,WAAW,EACxF,OAAO,oBAAoB,wBAAwB,EACnD,OAAO,aAAa,+BAA+B,EACnD,OAAO,WAAW,IAAI;AAAA,EAC3B,EACC;AAAA,IACC,IAAI,QAAQ,WAAW,EACpB,SAAS,UAAU,gBAAgB,EACnC,YAAY,qBAAqB,EACjC,OAAO,yBAAyB,yCAAyC,QAAQ,EACjF,OAAO,oBAAoB,6BAA6B,EACxD,OAAO,WAAW,SAAS;AAAA,EAChC;AAEF,SAAO;AACT;;;ASlBA,IAAM,MAAM,UAAU;AACtB,IAAI,MAAM;","names":["path","intro","outro","cancel","isCancel","text","select","confirm","spinner","note","path","path","pc","intro","text","confirm","cancel","spinner","path","note","outro","path","intro","cancel","text","select","path","note","outro","path","intro","cancel","text","select","path","note","outro"]}
@@ -1,4 +1,4 @@
1
- import { useState, useMemo, useCallback, useEffect } from 'react';
1
+ import { useState, useMemo, useCallback, useEffect, useRef } from 'react';
2
2
  import { useNavigate, useSearchParams, Link } from 'react-router-dom';
3
3
  import { cn } from '@elsapiens/utils';
4
4
  import { usePageHeader } from '@elsapiens/providers';
@@ -89,7 +89,36 @@ export function <%= pascalName %>ListPage({ className }: <%= pascalName %>ListPa
89
89
  const [itemToDelete, setItemToDelete] = useState<<%= pascalName %>Row | null>(null);
90
90
 
91
91
  // Loading state - set to true when fetching data
92
- const [loading] = useState(false);
92
+ const [loading, setLoading] = useState(false);
93
+ // Minimum loading time to prevent flickering
94
+ const [showLoading, setShowLoading] = useState(false);
95
+ const loadingStartTimeRef = useRef<number | null>(null);
96
+ const MIN_LOADING_TIME = 300; // milliseconds
97
+
98
+ useEffect(() => {
99
+ if (loading) {
100
+ // Start loading - record the start time
101
+ loadingStartTimeRef.current = Date.now();
102
+ setShowLoading(true);
103
+ } else if (loadingStartTimeRef.current !== null) {
104
+ // Loading finished - ensure minimum display time
105
+ const elapsed = Date.now() - loadingStartTimeRef.current;
106
+ const remaining = MIN_LOADING_TIME - elapsed;
107
+
108
+ if (remaining > 0) {
109
+ // Keep showing loading for the remaining time
110
+ const timer = setTimeout(() => {
111
+ setShowLoading(false);
112
+ loadingStartTimeRef.current = null;
113
+ }, remaining);
114
+ return () => clearTimeout(timer);
115
+ } else {
116
+ // Already past minimum time, hide immediately
117
+ setShowLoading(false);
118
+ loadingStartTimeRef.current = null;
119
+ }
120
+ }
121
+ }, [loading]);
93
122
 
94
123
  // Collapsible info state (persisted in localStorage)
95
124
  const [isInfoCollapsed, setIsInfoCollapsed] = useState(() => {
@@ -293,7 +322,7 @@ export function <%= pascalName %>ListPage({ className }: <%= pascalName %>ListPa
293
322
  key: 'status',
294
323
  header: 'Status',
295
324
  render: (item: <%= pascalName %>Row) => (
296
- <Badge className={cn('text-xs', statusColors[item.status])}>
325
+ <Badge className={cn(statusColors[item.status])}>
297
326
  {statusLabels[item.status]}
298
327
  </Badge>
299
328
  ),
@@ -408,21 +437,49 @@ export function <%= pascalName %>ListPage({ className }: <%= pascalName %>ListPa
408
437
  />
409
438
 
410
439
  {/* Table */}
411
- {loading && paginatedData.length === 0 ? (
412
- <div className="el-p-md el-space-y-md">
413
- {Array.from({ length: 5 }).map((_, i) => (
414
- <div key={i} className="flex items-center el-gap-md">
415
- <Skeleton className="h-8 w-8 rounded" />
416
- <Skeleton className="h-4 w-40 flex-1" />
417
- <Skeleton className="h-4 w-24" />
418
- <Skeleton className="h-4 w-20" />
419
- <Skeleton className="h-4 w-24" />
420
- </div>
421
- ))}
440
+ {showLoading && paginatedData.length === 0 ? (
441
+ <div className="el-table-aligned" style={{ marginLeft: 'calc(var(--el-table-spacer-width, var(--el-container-px)) * -1)', marginRight: 'calc(var(--el-table-spacer-width, var(--el-container-px)) * -1)', marginBottom: 'calc(var(--el-table-spacer-width, var(--el-container-px)) * -1)', width: 'calc(100% + var(--el-table-spacer-width, var(--el-container-px)) * 2)' }}>
442
+ <table className="w-full border-collapse el-table">
443
+ <thead>
444
+ <tr className="border-b border-border bg-wisteria-100 dark:bg-wisteria-800">
445
+ <th className="p-0 border-none" style={{ width: 'var(--el-table-spacer-width)' }} />
446
+ <th className="font-medium text-foreground text-left" style={{ paddingLeft: 0, paddingRight: 'var(--table-cell-padding-x)', paddingTop: 'var(--table-header-padding-y)', paddingBottom: 'var(--table-header-padding-y)', fontSize: 'var(--table-font-size)' }}>Name</th>
447
+ <th className="font-medium text-foreground text-left" style={{ width: '120px', paddingLeft: 'var(--table-cell-padding-x)', paddingRight: 'var(--table-cell-padding-x)', paddingTop: 'var(--table-header-padding-y)', paddingBottom: 'var(--table-header-padding-y)', fontSize: 'var(--table-font-size)' }}>Category</th>
448
+ <th className="font-medium text-foreground text-left" style={{ width: '100px', paddingLeft: 'var(--table-cell-padding-x)', paddingRight: 'var(--table-cell-padding-x)', paddingTop: 'var(--table-header-padding-y)', paddingBottom: 'var(--table-header-padding-y)', fontSize: 'var(--table-font-size)' }}>Status</th>
449
+ <th className="font-medium text-foreground text-left" style={{ width: '120px', paddingLeft: 'var(--table-cell-padding-x)', paddingRight: 'var(--table-cell-padding-x)', paddingTop: 'var(--table-header-padding-y)', paddingBottom: 'var(--table-header-padding-y)', fontSize: 'var(--table-font-size)' }}>Created</th>
450
+ <th style={{ width: '100px', paddingLeft: 'var(--table-cell-padding-x)', paddingRight: 0, paddingTop: 'var(--table-header-padding-y)', paddingBottom: 'var(--table-header-padding-y)' }} />
451
+ <th className="p-0 border-none" style={{ width: 'var(--el-table-spacer-width)' }} />
452
+ </tr>
453
+ </thead>
454
+ <tbody>
455
+ {Array.from({ length: 5 }).map((_, i) => (
456
+ <tr key={i} className="border-b border-border">
457
+ <td className="p-0 border-none" />
458
+ <td style={{ paddingLeft: 0, paddingRight: 'var(--table-cell-padding-x)', paddingTop: 'var(--table-cell-padding-y)', paddingBottom: 'var(--table-cell-padding-y)' }}>
459
+ <div className="flex items-center el-gap-sm">
460
+ <Skeleton className="h-8 w-8 rounded flex-shrink-0" />
461
+ <Skeleton className="h-4 w-32" />
462
+ </div>
463
+ </td>
464
+ <td style={{ paddingLeft: 'var(--table-cell-padding-x)', paddingRight: 'var(--table-cell-padding-x)', paddingTop: 'var(--table-cell-padding-y)', paddingBottom: 'var(--table-cell-padding-y)' }}>
465
+ <Skeleton className="h-6 w-20 rounded-full" />
466
+ </td>
467
+ <td style={{ paddingLeft: 'var(--table-cell-padding-x)', paddingRight: 'var(--table-cell-padding-x)', paddingTop: 'var(--table-cell-padding-y)', paddingBottom: 'var(--table-cell-padding-y)' }}>
468
+ <Skeleton className="h-6 w-14 rounded-full" />
469
+ </td>
470
+ <td style={{ paddingLeft: 'var(--table-cell-padding-x)', paddingRight: 'var(--table-cell-padding-x)', paddingTop: 'var(--table-cell-padding-y)', paddingBottom: 'var(--table-cell-padding-y)' }}>
471
+ <Skeleton className="h-4 w-20" />
472
+ </td>
473
+ <td style={{ paddingLeft: 'var(--table-cell-padding-x)', paddingRight: 0, paddingTop: 'var(--table-cell-padding-y)', paddingBottom: 'var(--table-cell-padding-y)' }} />
474
+ <td className="p-0 border-none" />
475
+ </tr>
476
+ ))}
477
+ </tbody>
478
+ </table>
422
479
  </div>
423
480
  ) : paginatedData.length === 0 && hasActiveFilters ? (
424
481
  /* Filtered empty state - no results for current filters */
425
- <div className="flex-1 flex flex-col items-center justify-center el-gap-md el-py-xl">
482
+ <div className="flex-1 flex flex-col items-center justify-center el-gap-lg el-py-xl">
426
483
  <div className="w-16 h-16 rounded-full bg-muted/50 flex items-center justify-center">
427
484
  <SearchX className="w-8 h-8 text-muted-foreground" />
428
485
  </div>
@@ -432,9 +489,11 @@ export function <%= pascalName %>ListPage({ className }: <%= pascalName %>ListPa
432
489
  No <%= title.toLowerCase() %> match your current filters. Try adjusting your search or filters.
433
490
  </p>
434
491
  </div>
435
- <Button variant="outline" onClick={clearFilters}>
436
- Clear filters
437
- </Button>
492
+ <div className="flex items-center el-gap-field">
493
+ <Button variant="outline" onClick={clearFilters}>
494
+ Clear filters
495
+ </Button>
496
+ </div>
438
497
  </div>
439
498
  ) : paginatedData.length === 0 ? (
440
499
  /* True empty state - no data at all */
@@ -495,8 +554,8 @@ export function <%= pascalName %>ListPage({ className }: <%= pascalName %>ListPa
495
554
  </div>
496
555
  )}
497
556
 
498
- {/* Collapsible Page Info - shows when items exist */}
499
- {paginatedData.length > 0 && (
557
+ {/* Collapsible Page Info - shows when data exists in the system */}
558
+ {sampleData.length > 0 && (
500
559
  <div className="relative">
501
560
  {isInfoCollapsed ? (
502
561
  <button
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elsapiens/cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "CLI scaffolding tool for elSapiens SDK projects",
5
5
  "type": "module",
6
6
  "bin": {