create-termui-app 0.1.4 → 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
@@ -54,9 +54,9 @@ function generateProject(config) {
54
54
  private: true,
55
55
  type: "module",
56
56
  scripts: {
57
- dev: "tsx --watch src/index.tsx",
57
+ dev: "bun --watch src/index.tsx",
58
58
  build: "tsup src/index.tsx --format esm",
59
- start: "node dist/index.js"
59
+ start: "bun dist/index.js"
60
60
  },
61
61
  dependencies: {
62
62
  "@termuijs/core": "latest",
@@ -70,9 +70,12 @@ function generateProject(config) {
70
70
  ...config.features.router ? { "@termuijs/router": "latest" } : {}
71
71
  },
72
72
  devDependencies: {
73
- tsx: "^4.0.0",
73
+ "@types/bun": "latest",
74
74
  tsup: "^8.0.0",
75
75
  typescript: "^5.3.0"
76
+ },
77
+ engines: {
78
+ bun: ">=1.3.0"
76
79
  }
77
80
  }, null, 2) + "\n"
78
81
  });
@@ -553,8 +556,8 @@ async function main() {
553
556
  console.log();
554
557
  console.log(` Next steps:`);
555
558
  console.log(` cd ${projectName}`);
556
- console.log(` npm install`);
557
- console.log(` npm run dev`);
559
+ console.log(` bun install`);
560
+ console.log(` bun run dev`);
558
561
  console.log();
559
562
  }
560
563
  main().catch((err) => {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/prompts.ts","../src/templates.ts"],"sourcesContent":["// ─────────────────────────────────────────────────────\n// create-termui-app — Interactive CLI scaffolding tool\n// ─────────────────────────────────────────────────────\n\nimport { resolve, join } from 'node:path';\nimport { mkdirSync, writeFileSync, existsSync } from 'node:fs';\nimport { getBuiltinThemeNames } from '@termuijs/tss';\nimport { textPrompt, selectPrompt, multiSelectPrompt } from './prompts.js';\nimport { generateProject, type ProjectConfig } from './templates.js';\n\nconst TEMPLATES = ['Empty (start from scratch)', 'Dashboard (real-time data)', 'Interactive Tool (forms, prompts)', 'CLI Wrapper (wrap existing CLI)'];\nconst TEMPLATE_KEYS = ['empty', 'dashboard', 'interactive-tool', 'cli-wrapper'] as const;\nconst FEATURES = ['Screen Router', 'Data Providers', 'Hot Reload'];\n\nasync function main() {\n console.log();\n console.log(' ┌──────────────────────────────────┐');\n console.log(' │ create-termui-app │');\n console.log(' │ The React/Next.js for CLI apps │');\n console.log(' └──────────────────────────────────┘');\n console.log();\n\n // ── Get project name from args or prompt ──\n let projectName = process.argv[2];\n if (!projectName) {\n projectName = await textPrompt('Project name', 'my-termui-app');\n }\n\n // ── Template selection ──\n const templateIdx = await selectPrompt('What kind of app?', TEMPLATES);\n const template = TEMPLATE_KEYS[templateIdx];\n\n // ── Theme selection ──\n const themes = getBuiltinThemeNames();\n const themeIdx = await selectPrompt('Choose a theme', themes.map(t => t.charAt(0).toUpperCase() + t.slice(1)));\n const theme = themes[themeIdx];\n\n // ── Feature selection ──\n const featureDefaults = [false, template === 'dashboard', true]; // Router off, Data on for dashboard, HotReload on\n const featureFlags = await multiSelectPrompt('Features to include', FEATURES, featureDefaults);\n\n const config: ProjectConfig = {\n name: projectName,\n template,\n theme,\n features: {\n router: featureFlags[0],\n dataProviders: featureFlags[1],\n hotReload: featureFlags[2],\n },\n };\n\n // ── Generate project ──\n const projectDir = resolve(process.cwd(), projectName);\n if (existsSync(projectDir)) {\n console.log(`\\n ⚠ Directory \"${projectName}\" already exists. Files may be overwritten.\\n`);\n }\n\n console.log(`\\n Creating ${projectName}...`);\n\n const files = generateProject(config);\n\n for (const file of files) {\n const fullPath = join(projectDir, file.path);\n const dir = fullPath.substring(0, fullPath.lastIndexOf('/'));\n mkdirSync(dir, { recursive: true });\n writeFileSync(fullPath, file.content, 'utf-8');\n console.log(` ✓ ${file.path}`);\n }\n\n console.log();\n console.log(' ┌──────────────────────────────────┐');\n console.log(' │ ✅ Project created successfully! │');\n console.log(' └──────────────────────────────────┘');\n console.log();\n console.log(` Next steps:`);\n console.log(` cd ${projectName}`);\n console.log(` npm install`);\n console.log(` npm run dev`);\n console.log();\n}\n\nmain().catch(err => {\n console.error('Error:', err.message);\n process.exit(1);\n});\n","// ─────────────────────────────────────────────────────\n// Minimal interactive prompts (no external deps)\n// ─────────────────────────────────────────────────────\n\nimport { createInterface } from 'node:readline';\n\nconst rl = () => createInterface({ input: process.stdin, output: process.stdout });\n\nexport async function textPrompt(question: string, defaultValue?: string): Promise<string> {\n return new Promise(resolve => {\n const r = rl();\n const suffix = defaultValue ? ` (${defaultValue})` : '';\n r.question(` ${question}${suffix}: `, answer => {\n r.close();\n resolve(answer.trim() || defaultValue || '');\n });\n });\n}\n\nexport async function selectPrompt(question: string, options: string[]): Promise<number> {\n console.log(`\\n ${question}`);\n for (let i = 0; i < options.length; i++) {\n console.log(` ${i + 1}) ${options[i]}`);\n }\n const answer = await textPrompt('Enter number', '1');\n const idx = parseInt(answer) - 1;\n return Math.max(0, Math.min(idx, options.length - 1));\n}\n\nexport async function confirmPrompt(question: string, defaultValue = true): Promise<boolean> {\n const suffix = defaultValue ? '(Y/n)' : '(y/N)';\n const answer = await textPrompt(`${question} ${suffix}`);\n if (!answer) return defaultValue;\n return answer.toLowerCase().startsWith('y');\n}\n\nexport async function multiSelectPrompt(question: string, options: string[], defaults: boolean[] = []): Promise<boolean[]> {\n console.log(`\\n ${question} (comma-separated numbers, or 'all')`);\n for (let i = 0; i < options.length; i++) {\n const def = defaults[i] ? '✓' : ' ';\n console.log(` ${i + 1}) [${def}] ${options[i]}`);\n }\n const answer = await textPrompt('Enter numbers', defaults.some(d => d) ? 'keep defaults' : 'all');\n if (answer === 'all') return options.map(() => true);\n if (answer === 'keep defaults' || answer === '') return defaults.length ? defaults : options.map(() => true);\n const selected = answer.split(',').map(s => parseInt(s.trim()) - 1);\n return options.map((_, i) => selected.includes(i));\n}\n","// ─────────────────────────────────────────────────────\n// Project Templates — generates files for new apps\n// ─────────────────────────────────────────────────────\n\nimport { getBuiltinTheme } from '@termuijs/tss';\n\nexport interface ProjectConfig {\n name: string;\n template: 'empty' | 'dashboard' | 'interactive-tool' | 'cli-wrapper';\n theme: string;\n features: {\n router: boolean;\n dataProviders: boolean;\n hotReload: boolean;\n };\n}\n\nexport interface GeneratedFile {\n path: string;\n content: string;\n}\n\nexport function generateProject(config: ProjectConfig): GeneratedFile[] {\n const files: GeneratedFile[] = [];\n\n // ── package.json ──\n files.push({\n path: 'package.json',\n content: JSON.stringify({\n name: config.name,\n version: '0.1.0',\n private: true,\n type: 'module',\n scripts: {\n dev: 'tsx --watch src/index.tsx',\n build: 'tsup src/index.tsx --format esm',\n start: 'node dist/index.js',\n },\n dependencies: {\n '@termuijs/core': 'latest',\n '@termuijs/widgets': 'latest',\n '@termuijs/ui': 'latest',\n '@termuijs/jsx': 'latest',\n '@termuijs/tss': 'latest',\n '@termuijs/quick': 'latest',\n '@termuijs/motion': 'latest',\n ...(config.features.dataProviders ? { '@termuijs/data': 'latest' } : {}),\n ...(config.features.router ? { '@termuijs/router': 'latest' } : {}),\n },\n devDependencies: {\n tsx: '^4.0.0',\n tsup: '^8.0.0',\n typescript: '^5.3.0',\n },\n }, null, 2) + '\\n',\n });\n\n // ── tsconfig.json ──\n files.push({\n path: 'tsconfig.json',\n content: JSON.stringify({\n compilerOptions: {\n target: 'ES2022',\n module: 'ESNext',\n moduleResolution: 'bundler',\n jsx: 'react-jsx',\n jsxImportSource: '@termuijs/jsx',\n strict: true,\n esModuleInterop: true,\n outDir: 'dist',\n rootDir: 'src',\n },\n include: ['src'],\n }, null, 2) + '\\n',\n });\n\n // ── termui.config.ts ──\n files.push({\n path: 'termui.config.ts',\n content: `import { defineConfig } from '@termuijs/core';\n\nexport default defineConfig({\n theme: '${config.theme}',\n ${config.features.hotReload ? \"hotReload: true,\" : ''}\n ${config.features.router ? \"router: { dir: './screens' },\" : ''}\n});\n`,\n });\n\n // ── Theme file ──\n const themeSrc = getBuiltinTheme(config.theme);\n if (themeSrc) {\n files.push({ path: `themes/${config.theme}.tss`, content: themeSrc.trim() + '\\n' });\n }\n\n // ── Template-specific files ──\n switch (config.template) {\n case 'dashboard':\n files.push(...generateDashboardTemplate(config));\n break;\n case 'interactive-tool':\n files.push(...generateInteractiveTemplate(config));\n break;\n case 'cli-wrapper':\n files.push(...generateCliWrapperTemplate(config));\n break;\n default:\n files.push(...generateEmptyTemplate(config));\n }\n\n return files;\n}\n\nfunction generateEmptyTemplate(config: ProjectConfig): GeneratedFile[] {\n return [{\n path: 'src/index.tsx',\n content: `/** @jsxImportSource @termuijs/jsx */\nimport { render, useState, useKeymap, ErrorBoundary } from '@termuijs/jsx';\nimport { AutoThemeProvider } from '@termuijs/tss';\n\nfunction App() {\n const [count, setCount] = useState(0);\n\n useKeymap([\n { key: 'q', action: () => process.exit(0), description: 'Quit' },\n { key: 'c', ctrl: true, action: () => process.exit(0), description: 'Quit' },\n { key: '+', action: () => setCount(c => c + 1), description: 'Increment' },\n { key: '-', action: () => setCount(c => Math.max(0, c - 1)), description: 'Decrement' },\n ]);\n\n return (\n <AutoThemeProvider>\n <ErrorBoundary fallback={(err) => <text color=\"red\">{err.message}</text>}>\n <box border=\"single\" padding={1}>\n <text bold>Welcome to ${config.name}!</text>\n <text>Edit src/index.tsx to get started.</text>\n <text>Count: {count} (+/- to change, q to quit)</text>\n </box>\n </ErrorBoundary>\n </AutoThemeProvider>\n );\n}\n\nrender(<App />, { title: '${config.name}' });\n`,\n }];\n}\n\nfunction generateDashboardTemplate(config: ProjectConfig): GeneratedFile[] {\n return [{\n path: 'src/index.tsx',\n content: `/** @jsxImportSource @termuijs/jsx */\nimport { render, useState, useEffect, useKeymap, ErrorBoundary } from '@termuijs/jsx';\nimport { AutoThemeProvider, useTheme } from '@termuijs/tss';\n${config.features.dataProviders ? \"import { useCpu, useMemory, useDisk } from '@termuijs/data';\" : ''}\n\n// ── Sample static data (replace with live hooks when dataProviders = true) ──\n${config.features.dataProviders ? '' : `const SAMPLE_PROCS = [\n { Name: 'node', PID: 1234, 'CPU%': '5.0', 'MEM%': '2.1' },\n { Name: 'chrome', PID: 5678, 'CPU%': '12.3', 'MEM%': '8.4' },\n { Name: 'bash', PID: 9012, 'CPU%': '0.1', 'MEM%': '0.3' },\n];`}\n\nfunction GaugeRow({ label, value }: { label: string; value: number }) {\n const theme = useTheme();\n const filled = Math.round(value * 20);\n const empty = 20 - filled;\n const bar = '[' + '#'.repeat(filled) + '-'.repeat(empty) + ']';\n return (\n <row gap={2}>\n <text color={theme.colors.primary}>{label.padEnd(4)}</text>\n <text>{bar}</text>\n <text>{(value * 100).toFixed(1).padStart(5)}%</text>\n </row>\n );\n}\n\nfunction Dashboard() {\n const [tick, setTick] = useState(0);\n${config.features.dataProviders\n ? ` const cpu = useCpu();\n const mem = useMemory();\n const disk = useDisk();\n const cpuVal = (cpu.percent ?? 0) / 100;\n const memVal = (mem.percent ?? 0) / 100;\n const diskVal = (disk.percent ?? 0) / 100;`\n : ` const [cpuVal, setCpuVal] = useState(0.45);\n const [memVal, setMemVal] = useState(0.62);\n const [diskVal, setDiskVal] = useState(0.38);\n\n // Simulate live updates\n useEffect(() => {\n const id = setInterval(() => {\n setCpuVal(v => Math.min(1, Math.max(0, v + (Math.random() - 0.5) * 0.05)));\n setMemVal(v => Math.min(1, Math.max(0, v + (Math.random() - 0.5) * 0.02)));\n setDiskVal(v => Math.min(1, Math.max(0, v + (Math.random() - 0.5) * 0.01)));\n setTick(t => t + 1);\n }, 1000);\n return () => clearInterval(id);\n }, []);`}\n\n useKeymap([\n { key: 'q', action: () => process.exit(0), description: 'Quit' },\n { key: 'c', ctrl: true, action: () => process.exit(0), description: 'Quit' },\n { key: 'r', action: () => setTick(t => t + 1), description: 'Refresh' },\n ]);\n\n const theme = useTheme();\n\n return (\n <box flexDirection=\"column\" padding={1}>\n <text bold color={theme.colors.primary}>${config.name} Dashboard</text>\n <divider />\n\n <grid columns={12} gap={1}>\n {/* Gauges — top row */}\n <box width=\"100%\" flexDirection=\"column\" border=\"single\" padding={1} flexGrow={4}>\n <text bold>System Resources</text>\n <GaugeRow label=\"CPU\" value={cpuVal} />\n <GaugeRow label=\"MEM\" value={memVal} />\n <GaugeRow label=\"DISK\" value={diskVal} />\n </box>\n\n {/* Info panel */}\n <box width=\"100%\" flexDirection=\"column\" border=\"single\" padding={1} flexGrow={8}>\n <text bold>Process Summary</text>\n <text color={theme.colors.muted}>Press r to refresh, q to quit</text>\n <text>Tick: {tick}</text>\n${config.features.dataProviders\n ? ` <skeleton variant=\"shimmer\" />`\n : ` <text>node PID:1234 CPU: {(cpuVal * 100).toFixed(1)}%</text>\n <text>chrome PID:5678 MEM: {(memVal * 100).toFixed(1)}%</text>`}\n </box>\n </grid>\n </box>\n );\n}\n\nfunction App() {\n return (\n <AutoThemeProvider>\n <ErrorBoundary fallback={(err) => (\n <box border=\"single\" borderColor=\"red\" padding={1}>\n <text color=\"red\" bold>Dashboard Error</text>\n <text>{err.message}</text>\n </box>\n )}>\n <Dashboard />\n </ErrorBoundary>\n </AutoThemeProvider>\n );\n}\n\nrender(<App />, { title: '${config.name}' });\n`,\n }];\n}\n\nfunction generateInteractiveTemplate(config: ProjectConfig): GeneratedFile[] {\n return [{\n path: 'src/index.tsx',\n content: `/** @jsxImportSource @termuijs/jsx */\nimport { render, useState, useKeymap, useRef, ErrorBoundary } from '@termuijs/jsx';\nimport { AutoThemeProvider, useTheme } from '@termuijs/tss';\nimport { caps } from '@termuijs/core';\n\n// ASCII-safe symbols\nconst CHECK = caps.unicode ? '✓' : 'v';\nconst BULLET = caps.unicode ? '›' : '>';\nconst SEP = caps.unicode ? '─'.repeat(40) : '-'.repeat(40);\n\nconst INITIAL_ITEMS = ['Option A', 'Option B', 'Option C'];\n\nfunction InteractiveTool() {\n const [items, setItems] = useState<string[]>(INITIAL_ITEMS);\n const [selected, setSelected] = useState(0);\n const [input, setInput] = useState('');\n const [done, setDone] = useState<string[]>([]);\n const theme = useTheme();\n\n useKeymap([\n { key: 'q', action: () => process.exit(0), description: 'Quit' },\n { key: 'c', ctrl: true, action: () => process.exit(0), description: 'Quit' },\n { key: 'ArrowUp', action: () => setSelected(s => Math.max(0, s - 1)), description: 'Move up' },\n { key: 'ArrowDown', action: () => setSelected(s => Math.min(items.length - 1, s + 1)), description: 'Move down' },\n { key: 'k', action: () => setSelected(s => Math.max(0, s - 1)), description: 'Move up (vim)' },\n { key: 'j', action: () => setSelected(s => Math.min(items.length - 1, s + 1)), description: 'Move down (vim)' },\n { key: 'Enter', action: () => {\n const item = items[selected];\n if (item) setDone(d => d.includes(item) ? d.filter(x => x !== item) : [...d, item]);\n }, description: 'Toggle selected' },\n { key: 'Backspace', action: () => setInput(v => v.slice(0, -1)), description: 'Delete char' },\n { key: 'n', action: () => {\n if (input.trim()) {\n setItems(prev => [...prev, input.trim()]);\n setInput('');\n }\n }, description: 'Add new item' },\n ]);\n\n return (\n <box flexDirection=\"column\" padding={1}>\n <text bold color={theme.colors.primary}>${config.name}</text>\n <text color={theme.colors.muted}>j/k or arrows: navigate | Enter: toggle | n: add | q: quit</text>\n <text>{SEP}</text>\n\n <box flexDirection=\"column\">\n {items.map((item, i) => (\n <row key={item} gap={1}>\n <text color={i === selected ? theme.colors.primary : undefined}>\n {i === selected ? BULLET : ' '}\n </text>\n <text color={done.includes(item) ? theme.colors.success : undefined}>\n {done.includes(item) ? CHECK + ' ' : ' '}{item}\n </text>\n </row>\n ))}\n </box>\n\n <text>{SEP}</text>\n <row gap={1}>\n <text color={theme.colors.muted}>New item:</text>\n <text>{input}_</text>\n </row>\n <text color={theme.colors.muted} dim>Type letters then press n to add</text>\n </box>\n );\n}\n\nfunction App() {\n return (\n <AutoThemeProvider>\n <ErrorBoundary fallback={(err) => (\n <box border=\"single\" borderColor=\"red\" padding={1}>\n <text color=\"red\" bold>Error</text>\n <text>{err.message}</text>\n </box>\n )}>\n <InteractiveTool />\n </ErrorBoundary>\n </AutoThemeProvider>\n );\n}\n\nrender(<App />, { title: '${config.name}' });\n`,\n }];\n}\n\nfunction generateCliWrapperTemplate(config: ProjectConfig): GeneratedFile[] {\n return [{\n path: 'src/index.tsx',\n content: `/** @jsxImportSource @termuijs/jsx */\nimport { render, useState, useEffect, useKeymap, ErrorBoundary } from '@termuijs/jsx';\nimport { AutoThemeProvider, useTheme } from '@termuijs/tss';\nimport { caps } from '@termuijs/core';\nimport { spawn } from 'node:child_process';\n\n// ASCII-safe symbols for terminals without full unicode support\nconst ICON_RUN = caps.unicode ? '>' : '>';\nconst ICON_DONE = caps.unicode ? '*' : '*';\nconst ICON_ERR = caps.unicode ? '!' : '!';\nconst SEP = '-'.repeat(60);\n\ntype LogLevel = 'info' | 'debug' | 'error' | 'warn';\ninterface LogLine {\n level: LogLevel;\n text: string;\n ts: number;\n}\n\nfunction levelColor(level: LogLevel): string {\n switch (level) {\n case 'info': return 'green';\n case 'debug': return 'cyan';\n case 'warn': return 'yellow';\n case 'error': return 'red';\n }\n}\n\nfunction CliWrapper() {\n const [logs, setLogs] = useState<LogLine[]>([\n { level: 'info', text: 'Application started', ts: Date.now() },\n { level: 'debug', text: 'Press r to re-run, q to quit', ts: Date.now() },\n ]);\n const [running, setRunning] = useState(false);\n const [exitCode, setExitCode] = useState<number | null>(null);\n const procRef = useRef<any>(null);\n const theme = useTheme();\n\n const addLog = (level: LogLevel, text: string) =>\n setLogs(prev => [...prev.slice(-200), { level, text, ts: Date.now() }]);\n\n // Example: run 'echo hello' — replace with your real command\n const runCommand = () => {\n if (running) return;\n setRunning(true);\n setExitCode(null);\n addLog('info', \\`\\${ICON_RUN} Running command...\\`);\n\n const proc = spawn('echo', ['Hello from CLI wrapper!']);\n procRef.current = proc;\n proc.stdout.on('data', (d: Buffer) => {\n for (const line of d.toString().split('\\\\n').filter(Boolean)) {\n addLog('info', line);\n }\n });\n proc.stderr.on('data', (d: Buffer) => {\n for (const line of d.toString().split('\\\\n').filter(Boolean)) {\n addLog('error', line);\n }\n });\n proc.on('close', (code: number | null) => {\n setRunning(false);\n setExitCode(code);\n addLog(code === 0 ? 'info' : 'error',\n \\`\\${code === 0 ? ICON_DONE : ICON_ERR} Process exited with code \\${code ?? 'null'}\\`);\n procRef.current = null;\n });\n };\n\n // Auto-run on mount, kill process on unmount\n useEffect(() => {\n runCommand();\n return () => {\n if (procRef.current) {\n procRef.current.kill();\n procRef.current = null;\n }\n };\n }, []);\n\n useKeymap([\n { key: 'q', action: () => process.exit(0), description: 'Quit' },\n { key: 'c', ctrl: true, action: () => process.exit(0), description: 'Quit' },\n { key: 'r', action: runCommand, description: 'Re-run command' },\n { key: 'l', action: () => setLogs([]), description: 'Clear logs' },\n ]);\n\n return (\n <box flexDirection=\"column\" padding={1}>\n <row gap={2}>\n <text bold color={theme.colors.primary}>${config.name}</text>\n <text color={running ? theme.colors.warning : theme.colors.muted}>\n {running ? 'Running...' : exitCode === null ? 'Ready' : \\`Exit: \\${exitCode}\\`}\n </text>\n <spacer />\n <text color={theme.colors.muted}>r: re-run | l: clear | q: quit</text>\n </row>\n <text>{SEP}</text>\n\n <box flexDirection=\"column\" flexGrow={1}>\n {logs.map((line, i) => (\n <row key={i} gap={1}>\n <text color={theme.colors.muted} dim>\n {new Date(line.ts).toLocaleTimeString()}\n </text>\n <text color={levelColor(line.level)} bold>\n {line.level.toUpperCase().padEnd(5)}\n </text>\n <text>{line.text}</text>\n </row>\n ))}\n </box>\n\n {!caps.color && (\n <text color=\"yellow\" dim>\n Note: running in a terminal without color support (TERM={process.env.TERM ?? 'unset'})\n </text>\n )}\n </box>\n );\n}\n\nfunction App() {\n return (\n <AutoThemeProvider>\n <ErrorBoundary fallback={(err) => (\n <box border=\"single\" borderColor=\"red\" padding={1}>\n <text color=\"red\" bold>CLI Wrapper Error</text>\n <text>{err.message}</text>\n <text color=\"yellow\">Check that the command exists and is executable.</text>\n </box>\n )}>\n <CliWrapper />\n </ErrorBoundary>\n </AutoThemeProvider>\n );\n}\n\nrender(<App />, { title: '${config.name}' });\n`,\n }];\n}\n\n"],"mappings":"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","names":["resolve"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/prompts.ts","../src/templates.ts"],"sourcesContent":["// ─────────────────────────────────────────────────────\n// create-termui-app — Interactive CLI scaffolding tool\n// ─────────────────────────────────────────────────────\n\nimport { resolve, join } from 'node:path';\nimport { mkdirSync, writeFileSync, existsSync } from 'node:fs';\nimport { getBuiltinThemeNames } from '@termuijs/tss';\nimport { textPrompt, selectPrompt, multiSelectPrompt } from './prompts.js';\nimport { generateProject, type ProjectConfig } from './templates.js';\n\nconst TEMPLATES = ['Empty (start from scratch)', 'Dashboard (real-time data)', 'Interactive Tool (forms, prompts)', 'CLI Wrapper (wrap existing CLI)'];\nconst TEMPLATE_KEYS = ['empty', 'dashboard', 'interactive-tool', 'cli-wrapper'] as const;\nconst FEATURES = ['Screen Router', 'Data Providers', 'Hot Reload'];\n\nasync function main() {\n console.log();\n console.log(' ┌──────────────────────────────────┐');\n console.log(' │ create-termui-app │');\n console.log(' │ The React/Next.js for CLI apps │');\n console.log(' └──────────────────────────────────┘');\n console.log();\n\n // ── Get project name from args or prompt ──\n let projectName = process.argv[2];\n if (!projectName) {\n projectName = await textPrompt('Project name', 'my-termui-app');\n }\n\n // ── Template selection ──\n const templateIdx = await selectPrompt('What kind of app?', TEMPLATES);\n const template = TEMPLATE_KEYS[templateIdx];\n\n // ── Theme selection ──\n const themes = getBuiltinThemeNames();\n const themeIdx = await selectPrompt('Choose a theme', themes.map(t => t.charAt(0).toUpperCase() + t.slice(1)));\n const theme = themes[themeIdx];\n\n // ── Feature selection ──\n const featureDefaults = [false, template === 'dashboard', true]; // Router off, Data on for dashboard, HotReload on\n const featureFlags = await multiSelectPrompt('Features to include', FEATURES, featureDefaults);\n\n const config: ProjectConfig = {\n name: projectName,\n template,\n theme,\n features: {\n router: featureFlags[0],\n dataProviders: featureFlags[1],\n hotReload: featureFlags[2],\n },\n };\n\n // ── Generate project ──\n const projectDir = resolve(process.cwd(), projectName);\n if (existsSync(projectDir)) {\n console.log(`\\n ⚠ Directory \"${projectName}\" already exists. Files may be overwritten.\\n`);\n }\n\n console.log(`\\n Creating ${projectName}...`);\n\n const files = generateProject(config);\n\n for (const file of files) {\n const fullPath = join(projectDir, file.path);\n const dir = fullPath.substring(0, fullPath.lastIndexOf('/'));\n mkdirSync(dir, { recursive: true });\n writeFileSync(fullPath, file.content, 'utf-8');\n console.log(` ✓ ${file.path}`);\n }\n\n console.log();\n console.log(' ┌──────────────────────────────────┐');\n console.log(' │ ✅ Project created successfully! │');\n console.log(' └──────────────────────────────────┘');\n console.log();\n console.log(` Next steps:`);\n console.log(` cd ${projectName}`);\n console.log(` bun install`);\n console.log(` bun run dev`);\n console.log();\n}\n\nmain().catch(err => {\n console.error('Error:', err.message);\n process.exit(1);\n});\n","// ─────────────────────────────────────────────────────\n// Minimal interactive prompts (no external deps)\n// ─────────────────────────────────────────────────────\n\nimport { createInterface } from 'node:readline';\n\nconst rl = () => createInterface({ input: process.stdin, output: process.stdout });\n\nexport async function textPrompt(question: string, defaultValue?: string): Promise<string> {\n return new Promise(resolve => {\n const r = rl();\n const suffix = defaultValue ? ` (${defaultValue})` : '';\n r.question(` ${question}${suffix}: `, answer => {\n r.close();\n resolve(answer.trim() || defaultValue || '');\n });\n });\n}\n\nexport async function selectPrompt(question: string, options: string[]): Promise<number> {\n console.log(`\\n ${question}`);\n for (let i = 0; i < options.length; i++) {\n console.log(` ${i + 1}) ${options[i]}`);\n }\n const answer = await textPrompt('Enter number', '1');\n const idx = parseInt(answer) - 1;\n return Math.max(0, Math.min(idx, options.length - 1));\n}\n\nexport async function confirmPrompt(question: string, defaultValue = true): Promise<boolean> {\n const suffix = defaultValue ? '(Y/n)' : '(y/N)';\n const answer = await textPrompt(`${question} ${suffix}`);\n if (!answer) return defaultValue;\n return answer.toLowerCase().startsWith('y');\n}\n\nexport async function multiSelectPrompt(question: string, options: string[], defaults: boolean[] = []): Promise<boolean[]> {\n console.log(`\\n ${question} (comma-separated numbers, or 'all')`);\n for (let i = 0; i < options.length; i++) {\n const def = defaults[i] ? '✓' : ' ';\n console.log(` ${i + 1}) [${def}] ${options[i]}`);\n }\n const answer = await textPrompt('Enter numbers', defaults.some(d => d) ? 'keep defaults' : 'all');\n if (answer === 'all') return options.map(() => true);\n if (answer === 'keep defaults' || answer === '') return defaults.length ? defaults : options.map(() => true);\n const selected = answer.split(',').map(s => parseInt(s.trim()) - 1);\n return options.map((_, i) => selected.includes(i));\n}\n","// ─────────────────────────────────────────────────────\n// Project Templates — generates files for new apps\n// ─────────────────────────────────────────────────────\n\nimport { getBuiltinTheme } from '@termuijs/tss';\n\nexport interface ProjectConfig {\n name: string;\n template: 'empty' | 'dashboard' | 'interactive-tool' | 'cli-wrapper';\n theme: string;\n features: {\n router: boolean;\n dataProviders: boolean;\n hotReload: boolean;\n };\n}\n\nexport interface GeneratedFile {\n path: string;\n content: string;\n}\n\nexport function generateProject(config: ProjectConfig): GeneratedFile[] {\n const files: GeneratedFile[] = [];\n\n // ── package.json ──\n files.push({\n path: 'package.json',\n content: JSON.stringify({\n name: config.name,\n version: '0.1.0',\n private: true,\n type: 'module',\n scripts: {\n dev: 'bun --watch src/index.tsx',\n build: 'tsup src/index.tsx --format esm',\n start: 'bun dist/index.js',\n },\n dependencies: {\n '@termuijs/core': 'latest',\n '@termuijs/widgets': 'latest',\n '@termuijs/ui': 'latest',\n '@termuijs/jsx': 'latest',\n '@termuijs/tss': 'latest',\n '@termuijs/quick': 'latest',\n '@termuijs/motion': 'latest',\n ...(config.features.dataProviders ? { '@termuijs/data': 'latest' } : {}),\n ...(config.features.router ? { '@termuijs/router': 'latest' } : {}),\n },\n devDependencies: {\n '@types/bun': 'latest',\n tsup: '^8.0.0',\n typescript: '^5.3.0',\n },\n engines: {\n bun: '>=1.3.0',\n },\n }, null, 2) + '\\n',\n });\n\n // ── tsconfig.json ──\n files.push({\n path: 'tsconfig.json',\n content: JSON.stringify({\n compilerOptions: {\n target: 'ES2022',\n module: 'ESNext',\n moduleResolution: 'bundler',\n jsx: 'react-jsx',\n jsxImportSource: '@termuijs/jsx',\n strict: true,\n esModuleInterop: true,\n outDir: 'dist',\n rootDir: 'src',\n },\n include: ['src'],\n }, null, 2) + '\\n',\n });\n\n // ── termui.config.ts ──\n files.push({\n path: 'termui.config.ts',\n content: `import { defineConfig } from '@termuijs/core';\n\nexport default defineConfig({\n theme: '${config.theme}',\n ${config.features.hotReload ? \"hotReload: true,\" : ''}\n ${config.features.router ? \"router: { dir: './screens' },\" : ''}\n});\n`,\n });\n\n // ── Theme file ──\n const themeSrc = getBuiltinTheme(config.theme);\n if (themeSrc) {\n files.push({ path: `themes/${config.theme}.tss`, content: themeSrc.trim() + '\\n' });\n }\n\n // ── Template-specific files ──\n switch (config.template) {\n case 'dashboard':\n files.push(...generateDashboardTemplate(config));\n break;\n case 'interactive-tool':\n files.push(...generateInteractiveTemplate(config));\n break;\n case 'cli-wrapper':\n files.push(...generateCliWrapperTemplate(config));\n break;\n default:\n files.push(...generateEmptyTemplate(config));\n }\n\n return files;\n}\n\nfunction generateEmptyTemplate(config: ProjectConfig): GeneratedFile[] {\n return [{\n path: 'src/index.tsx',\n content: `/** @jsxImportSource @termuijs/jsx */\nimport { render, useState, useKeymap, ErrorBoundary } from '@termuijs/jsx';\nimport { AutoThemeProvider } from '@termuijs/tss';\n\nfunction App() {\n const [count, setCount] = useState(0);\n\n useKeymap([\n { key: 'q', action: () => process.exit(0), description: 'Quit' },\n { key: 'c', ctrl: true, action: () => process.exit(0), description: 'Quit' },\n { key: '+', action: () => setCount(c => c + 1), description: 'Increment' },\n { key: '-', action: () => setCount(c => Math.max(0, c - 1)), description: 'Decrement' },\n ]);\n\n return (\n <AutoThemeProvider>\n <ErrorBoundary fallback={(err) => <text color=\"red\">{err.message}</text>}>\n <box border=\"single\" padding={1}>\n <text bold>Welcome to ${config.name}!</text>\n <text>Edit src/index.tsx to get started.</text>\n <text>Count: {count} (+/- to change, q to quit)</text>\n </box>\n </ErrorBoundary>\n </AutoThemeProvider>\n );\n}\n\nrender(<App />, { title: '${config.name}' });\n`,\n }];\n}\n\nfunction generateDashboardTemplate(config: ProjectConfig): GeneratedFile[] {\n return [{\n path: 'src/index.tsx',\n content: `/** @jsxImportSource @termuijs/jsx */\nimport { render, useState, useEffect, useKeymap, ErrorBoundary } from '@termuijs/jsx';\nimport { AutoThemeProvider, useTheme } from '@termuijs/tss';\n${config.features.dataProviders ? \"import { useCpu, useMemory, useDisk } from '@termuijs/data';\" : ''}\n\n// ── Sample static data (replace with live hooks when dataProviders = true) ──\n${config.features.dataProviders ? '' : `const SAMPLE_PROCS = [\n { Name: 'node', PID: 1234, 'CPU%': '5.0', 'MEM%': '2.1' },\n { Name: 'chrome', PID: 5678, 'CPU%': '12.3', 'MEM%': '8.4' },\n { Name: 'bash', PID: 9012, 'CPU%': '0.1', 'MEM%': '0.3' },\n];`}\n\nfunction GaugeRow({ label, value }: { label: string; value: number }) {\n const theme = useTheme();\n const filled = Math.round(value * 20);\n const empty = 20 - filled;\n const bar = '[' + '#'.repeat(filled) + '-'.repeat(empty) + ']';\n return (\n <row gap={2}>\n <text color={theme.colors.primary}>{label.padEnd(4)}</text>\n <text>{bar}</text>\n <text>{(value * 100).toFixed(1).padStart(5)}%</text>\n </row>\n );\n}\n\nfunction Dashboard() {\n const [tick, setTick] = useState(0);\n${config.features.dataProviders\n ? ` const cpu = useCpu();\n const mem = useMemory();\n const disk = useDisk();\n const cpuVal = (cpu.percent ?? 0) / 100;\n const memVal = (mem.percent ?? 0) / 100;\n const diskVal = (disk.percent ?? 0) / 100;`\n : ` const [cpuVal, setCpuVal] = useState(0.45);\n const [memVal, setMemVal] = useState(0.62);\n const [diskVal, setDiskVal] = useState(0.38);\n\n // Simulate live updates\n useEffect(() => {\n const id = setInterval(() => {\n setCpuVal(v => Math.min(1, Math.max(0, v + (Math.random() - 0.5) * 0.05)));\n setMemVal(v => Math.min(1, Math.max(0, v + (Math.random() - 0.5) * 0.02)));\n setDiskVal(v => Math.min(1, Math.max(0, v + (Math.random() - 0.5) * 0.01)));\n setTick(t => t + 1);\n }, 1000);\n return () => clearInterval(id);\n }, []);`}\n\n useKeymap([\n { key: 'q', action: () => process.exit(0), description: 'Quit' },\n { key: 'c', ctrl: true, action: () => process.exit(0), description: 'Quit' },\n { key: 'r', action: () => setTick(t => t + 1), description: 'Refresh' },\n ]);\n\n const theme = useTheme();\n\n return (\n <box flexDirection=\"column\" padding={1}>\n <text bold color={theme.colors.primary}>${config.name} Dashboard</text>\n <divider />\n\n <grid columns={12} gap={1}>\n {/* Gauges — top row */}\n <box width=\"100%\" flexDirection=\"column\" border=\"single\" padding={1} flexGrow={4}>\n <text bold>System Resources</text>\n <GaugeRow label=\"CPU\" value={cpuVal} />\n <GaugeRow label=\"MEM\" value={memVal} />\n <GaugeRow label=\"DISK\" value={diskVal} />\n </box>\n\n {/* Info panel */}\n <box width=\"100%\" flexDirection=\"column\" border=\"single\" padding={1} flexGrow={8}>\n <text bold>Process Summary</text>\n <text color={theme.colors.muted}>Press r to refresh, q to quit</text>\n <text>Tick: {tick}</text>\n${config.features.dataProviders\n ? ` <skeleton variant=\"shimmer\" />`\n : ` <text>node PID:1234 CPU: {(cpuVal * 100).toFixed(1)}%</text>\n <text>chrome PID:5678 MEM: {(memVal * 100).toFixed(1)}%</text>`}\n </box>\n </grid>\n </box>\n );\n}\n\nfunction App() {\n return (\n <AutoThemeProvider>\n <ErrorBoundary fallback={(err) => (\n <box border=\"single\" borderColor=\"red\" padding={1}>\n <text color=\"red\" bold>Dashboard Error</text>\n <text>{err.message}</text>\n </box>\n )}>\n <Dashboard />\n </ErrorBoundary>\n </AutoThemeProvider>\n );\n}\n\nrender(<App />, { title: '${config.name}' });\n`,\n }];\n}\n\nfunction generateInteractiveTemplate(config: ProjectConfig): GeneratedFile[] {\n return [{\n path: 'src/index.tsx',\n content: `/** @jsxImportSource @termuijs/jsx */\nimport { render, useState, useKeymap, useRef, ErrorBoundary } from '@termuijs/jsx';\nimport { AutoThemeProvider, useTheme } from '@termuijs/tss';\nimport { caps } from '@termuijs/core';\n\n// ASCII-safe symbols\nconst CHECK = caps.unicode ? '✓' : 'v';\nconst BULLET = caps.unicode ? '›' : '>';\nconst SEP = caps.unicode ? '─'.repeat(40) : '-'.repeat(40);\n\nconst INITIAL_ITEMS = ['Option A', 'Option B', 'Option C'];\n\nfunction InteractiveTool() {\n const [items, setItems] = useState<string[]>(INITIAL_ITEMS);\n const [selected, setSelected] = useState(0);\n const [input, setInput] = useState('');\n const [done, setDone] = useState<string[]>([]);\n const theme = useTheme();\n\n useKeymap([\n { key: 'q', action: () => process.exit(0), description: 'Quit' },\n { key: 'c', ctrl: true, action: () => process.exit(0), description: 'Quit' },\n { key: 'ArrowUp', action: () => setSelected(s => Math.max(0, s - 1)), description: 'Move up' },\n { key: 'ArrowDown', action: () => setSelected(s => Math.min(items.length - 1, s + 1)), description: 'Move down' },\n { key: 'k', action: () => setSelected(s => Math.max(0, s - 1)), description: 'Move up (vim)' },\n { key: 'j', action: () => setSelected(s => Math.min(items.length - 1, s + 1)), description: 'Move down (vim)' },\n { key: 'Enter', action: () => {\n const item = items[selected];\n if (item) setDone(d => d.includes(item) ? d.filter(x => x !== item) : [...d, item]);\n }, description: 'Toggle selected' },\n { key: 'Backspace', action: () => setInput(v => v.slice(0, -1)), description: 'Delete char' },\n { key: 'n', action: () => {\n if (input.trim()) {\n setItems(prev => [...prev, input.trim()]);\n setInput('');\n }\n }, description: 'Add new item' },\n ]);\n\n return (\n <box flexDirection=\"column\" padding={1}>\n <text bold color={theme.colors.primary}>${config.name}</text>\n <text color={theme.colors.muted}>j/k or arrows: navigate | Enter: toggle | n: add | q: quit</text>\n <text>{SEP}</text>\n\n <box flexDirection=\"column\">\n {items.map((item, i) => (\n <row key={item} gap={1}>\n <text color={i === selected ? theme.colors.primary : undefined}>\n {i === selected ? BULLET : ' '}\n </text>\n <text color={done.includes(item) ? theme.colors.success : undefined}>\n {done.includes(item) ? CHECK + ' ' : ' '}{item}\n </text>\n </row>\n ))}\n </box>\n\n <text>{SEP}</text>\n <row gap={1}>\n <text color={theme.colors.muted}>New item:</text>\n <text>{input}_</text>\n </row>\n <text color={theme.colors.muted} dim>Type letters then press n to add</text>\n </box>\n );\n}\n\nfunction App() {\n return (\n <AutoThemeProvider>\n <ErrorBoundary fallback={(err) => (\n <box border=\"single\" borderColor=\"red\" padding={1}>\n <text color=\"red\" bold>Error</text>\n <text>{err.message}</text>\n </box>\n )}>\n <InteractiveTool />\n </ErrorBoundary>\n </AutoThemeProvider>\n );\n}\n\nrender(<App />, { title: '${config.name}' });\n`,\n }];\n}\n\nfunction generateCliWrapperTemplate(config: ProjectConfig): GeneratedFile[] {\n return [{\n path: 'src/index.tsx',\n content: `/** @jsxImportSource @termuijs/jsx */\nimport { render, useState, useEffect, useKeymap, ErrorBoundary } from '@termuijs/jsx';\nimport { AutoThemeProvider, useTheme } from '@termuijs/tss';\nimport { caps } from '@termuijs/core';\nimport { spawn } from 'node:child_process';\n\n// ASCII-safe symbols for terminals without full unicode support\nconst ICON_RUN = caps.unicode ? '>' : '>';\nconst ICON_DONE = caps.unicode ? '*' : '*';\nconst ICON_ERR = caps.unicode ? '!' : '!';\nconst SEP = '-'.repeat(60);\n\ntype LogLevel = 'info' | 'debug' | 'error' | 'warn';\ninterface LogLine {\n level: LogLevel;\n text: string;\n ts: number;\n}\n\nfunction levelColor(level: LogLevel): string {\n switch (level) {\n case 'info': return 'green';\n case 'debug': return 'cyan';\n case 'warn': return 'yellow';\n case 'error': return 'red';\n }\n}\n\nfunction CliWrapper() {\n const [logs, setLogs] = useState<LogLine[]>([\n { level: 'info', text: 'Application started', ts: Date.now() },\n { level: 'debug', text: 'Press r to re-run, q to quit', ts: Date.now() },\n ]);\n const [running, setRunning] = useState(false);\n const [exitCode, setExitCode] = useState<number | null>(null);\n const procRef = useRef<any>(null);\n const theme = useTheme();\n\n const addLog = (level: LogLevel, text: string) =>\n setLogs(prev => [...prev.slice(-200), { level, text, ts: Date.now() }]);\n\n // Example: run 'echo hello' — replace with your real command\n const runCommand = () => {\n if (running) return;\n setRunning(true);\n setExitCode(null);\n addLog('info', \\`\\${ICON_RUN} Running command...\\`);\n\n const proc = spawn('echo', ['Hello from CLI wrapper!']);\n procRef.current = proc;\n proc.stdout.on('data', (d: Buffer) => {\n for (const line of d.toString().split('\\\\n').filter(Boolean)) {\n addLog('info', line);\n }\n });\n proc.stderr.on('data', (d: Buffer) => {\n for (const line of d.toString().split('\\\\n').filter(Boolean)) {\n addLog('error', line);\n }\n });\n proc.on('close', (code: number | null) => {\n setRunning(false);\n setExitCode(code);\n addLog(code === 0 ? 'info' : 'error',\n \\`\\${code === 0 ? ICON_DONE : ICON_ERR} Process exited with code \\${code ?? 'null'}\\`);\n procRef.current = null;\n });\n };\n\n // Auto-run on mount, kill process on unmount\n useEffect(() => {\n runCommand();\n return () => {\n if (procRef.current) {\n procRef.current.kill();\n procRef.current = null;\n }\n };\n }, []);\n\n useKeymap([\n { key: 'q', action: () => process.exit(0), description: 'Quit' },\n { key: 'c', ctrl: true, action: () => process.exit(0), description: 'Quit' },\n { key: 'r', action: runCommand, description: 'Re-run command' },\n { key: 'l', action: () => setLogs([]), description: 'Clear logs' },\n ]);\n\n return (\n <box flexDirection=\"column\" padding={1}>\n <row gap={2}>\n <text bold color={theme.colors.primary}>${config.name}</text>\n <text color={running ? theme.colors.warning : theme.colors.muted}>\n {running ? 'Running...' : exitCode === null ? 'Ready' : \\`Exit: \\${exitCode}\\`}\n </text>\n <spacer />\n <text color={theme.colors.muted}>r: re-run | l: clear | q: quit</text>\n </row>\n <text>{SEP}</text>\n\n <box flexDirection=\"column\" flexGrow={1}>\n {logs.map((line, i) => (\n <row key={i} gap={1}>\n <text color={theme.colors.muted} dim>\n {new Date(line.ts).toLocaleTimeString()}\n </text>\n <text color={levelColor(line.level)} bold>\n {line.level.toUpperCase().padEnd(5)}\n </text>\n <text>{line.text}</text>\n </row>\n ))}\n </box>\n\n {!caps.color && (\n <text color=\"yellow\" dim>\n Note: running in a terminal without color support (TERM={process.env.TERM ?? 'unset'})\n </text>\n )}\n </box>\n );\n}\n\nfunction App() {\n return (\n <AutoThemeProvider>\n <ErrorBoundary fallback={(err) => (\n <box border=\"single\" borderColor=\"red\" padding={1}>\n <text color=\"red\" bold>CLI Wrapper Error</text>\n <text>{err.message}</text>\n <text color=\"yellow\">Check that the command exists and is executable.</text>\n </box>\n )}>\n <CliWrapper />\n </ErrorBoundary>\n </AutoThemeProvider>\n );\n}\n\nrender(<App />, { title: '${config.name}' });\n`,\n }];\n}\n\n"],"mappings":"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","names":["resolve"]}
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "type": "git",
6
6
  "url": "https://github.com/Karanjot786/TermUI"
7
7
  },
8
- "version": "0.1.4",
8
+ "version": "0.1.5",
9
9
  "description": "Scaffold a new TermUI project with templates, themes, and dev server",
10
10
  "type": "module",
11
11
  "bin": {
@@ -15,6 +15,10 @@
15
15
  "files": [
16
16
  "dist"
17
17
  ],
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "dev": "tsup --watch"
21
+ },
18
22
  "dependencies": {
19
23
  "@termuijs/tss": "0.1.4"
20
24
  },
@@ -30,9 +34,8 @@
30
34
  "create-app",
31
35
  "generator"
32
36
  ],
33
- "license": "MIT",
34
- "scripts": {
35
- "build": "tsup",
36
- "dev": "tsup --watch"
37
- }
38
- }
37
+ "engines": {
38
+ "bun": ">=1.3.0"
39
+ },
40
+ "license": "MIT"
41
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Karanjot Singh
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.