Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions packages/inquirerer/dev/demo-chat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/usr/bin/env node
/**
* Demo: Chat AI Prompt Box with Streaming
*
* Run with: pnpm dev:chat
* Or: npx ts-node dev/demo-chat.ts
*/

import { createStream, createSpinner } from '../src/ui';
import { cyan, dim, green, white } from 'yanse';

const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));

// Simulated AI responses
const AI_RESPONSES = [
"Hello! I'm an AI assistant. I can help you with coding questions, explain concepts, or assist with various tasks. What would you like to know?",
"TypeScript is a strongly typed programming language that builds on JavaScript. It adds optional static typing and class-based object-oriented programming to the language. Here are some key benefits:\n\n1. **Type Safety**: Catch errors at compile time rather than runtime\n2. **Better IDE Support**: Enhanced autocomplete and refactoring\n3. **Improved Readability**: Types serve as documentation\n4. **Modern Features**: Access to latest ECMAScript features",
"Here's a simple example of a TypeScript function:\n\n```typescript\nfunction greet(name: string): string {\n return `Hello, ${name}!`;\n}\n\nconst message = greet('World');\nconsole.log(message); // Output: Hello, World!\n```\n\nThe `: string` after the parameter and function declaration specifies the types.",
];

/**
* Simulate streaming text character by character
*/
async function streamText(stream: ReturnType<typeof createStream>, text: string) {
const words = text.split(' ');

for (let i = 0; i < words.length; i++) {
const word = words[i];

// Add word character by character for realistic effect
for (const char of word) {
stream.append(char);
await sleep(15 + Math.random() * 25); // Variable typing speed
}

// Add space after word (except last)
if (i < words.length - 1) {
stream.append(' ');
await sleep(10);
}
}
}

async function main() {
console.log('\n' + white('═'.repeat(60)));
console.log(white(' 🤖 AI Chat Demo - Streaming Response Simulation'));
console.log(white('═'.repeat(60)) + '\n');

for (let i = 0; i < AI_RESPONSES.length; i++) {
const response = AI_RESPONSES[i];

// Show user prompt
console.log(cyan('You: ') + dim(`[Question ${i + 1}]`));
console.log('');

// Show thinking spinner
const thinking = createSpinner('Thinking...', { interval: 80 });
thinking.start();
await sleep(800 + Math.random() * 500);
thinking.stop('info', 'Generating response...');

// Stream the response
console.log('');
const stream = createStream({ prefix: green('AI: ') });
stream.start();

await streamText(stream, response);

stream.done();
console.log('\n' + dim('─'.repeat(60)) + '\n');

await sleep(500);
}

console.log(white('═'.repeat(60)));
console.log(white(' ✨ Demo complete!'));
console.log(white('═'.repeat(60)) + '\n');
}

main().catch(console.error);
85 changes: 85 additions & 0 deletions packages/inquirerer/dev/demo-spinner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/usr/bin/env node
/**
* Demo: Spinners and Loaders
*
* Run with: pnpm dev:spinner
* Or: npx ts-node dev/demo-spinner.ts
*/

import { createSpinner, SPINNER_STYLES } from '../src/ui';

const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));

async function main() {
console.log('\n🎨 Spinner Demo\n');
console.log('This demo shows various spinner styles and states.\n');

// Basic spinner
const spinner1 = createSpinner('Loading packages...');
spinner1.start();
await sleep(2000);
spinner1.succeed('Packages loaded successfully');

await sleep(500);

// Spinner with text updates
const spinner2 = createSpinner('Connecting to server...');
spinner2.start();
await sleep(1000);
spinner2.text('Authenticating...');
await sleep(1000);
spinner2.text('Fetching data...');
await sleep(1000);
spinner2.succeed('Data fetched');

await sleep(500);

// Error state
const spinner3 = createSpinner('Installing dependencies...');
spinner3.start();
await sleep(1500);
spinner3.fail('Failed to install: network error');

await sleep(500);

// Warning state
const spinner4 = createSpinner('Checking for updates...');
spinner4.start();
await sleep(1500);
spinner4.warn('Updates available but not critical');

await sleep(500);

// Info state
const spinner5 = createSpinner('Scanning project...');
spinner5.start();
await sleep(1500);
spinner5.info('Found 42 files');

await sleep(500);

// Different spinner styles
console.log('\n📊 Spinner Styles:\n');

const styles: Array<[string, string[]]> = [
['dots', SPINNER_STYLES.dots],
['line', SPINNER_STYLES.line],
['arc', SPINNER_STYLES.arc],
['circle', SPINNER_STYLES.circle],
['bounce', SPINNER_STYLES.bounce],
['arrow', SPINNER_STYLES.arrow],
['dots2', SPINNER_STYLES.dots2],
];

for (const [name, frames] of styles) {
const spinner = createSpinner(`Style: ${name}`, { frames, interval: 100 });
spinner.start();
await sleep(1500);
spinner.succeed(`${name} complete`);
await sleep(200);
}

console.log('\n✨ Demo complete!\n');
}

main().catch(console.error);
81 changes: 81 additions & 0 deletions packages/inquirerer/dev/demo-upgrade.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/usr/bin/env node
/**
* Demo: Interactive Dependency Upgrade UI
*
* Run with: pnpm dev:upgrade
* Or: npx ts-node dev/demo-upgrade.ts
*/

import { upgradePrompt, PackageInfo, createSpinner } from '../src/ui';
import { cyan, green, yellow, dim, white } from 'yanse';

const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));

// Simulated package data (like pnpm outdated would return)
const MOCK_PACKAGES: PackageInfo[] = [
{ name: 'typescript', current: '5.2.2', latest: '5.7.2', type: 'devDependencies' },
{ name: 'react', current: '18.2.0', latest: '19.0.0', type: 'dependencies' },
{ name: 'react-dom', current: '18.2.0', latest: '19.0.0', type: 'dependencies' },
{ name: '@types/node', current: '20.8.0', latest: '22.10.2', type: 'devDependencies' },
{ name: 'eslint', current: '8.50.0', latest: '9.17.0', type: 'devDependencies' },
{ name: 'prettier', current: '3.0.3', latest: '3.4.2', type: 'devDependencies' },
{ name: 'jest', current: '29.6.4', latest: '29.7.0', type: 'devDependencies' },
{ name: 'lodash', current: '4.17.20', latest: '4.17.21', type: 'dependencies' },
{ name: 'axios', current: '1.5.0', latest: '1.7.9', type: 'dependencies' },
{ name: 'zod', current: '3.22.2', latest: '3.24.1', type: 'dependencies' },
{ name: 'vitest', current: '0.34.4', latest: '2.1.8', type: 'devDependencies' },
{ name: '@tanstack/react-query', current: '4.35.3', latest: '5.62.8', type: 'dependencies' },
{ name: 'tailwindcss', current: '3.3.3', latest: '3.4.17', type: 'devDependencies' },
{ name: 'next', current: '13.5.2', latest: '15.1.3', type: 'dependencies' },
{ name: 'prisma', current: '5.3.1', latest: '6.1.0', type: 'devDependencies' },
];

async function main() {
console.log('\n' + white('═'.repeat(70)));
console.log(white(' 📦 Interactive Dependency Upgrade Demo'));
console.log(white('═'.repeat(70)) + '\n');

// Show loading spinner first
const spinner = createSpinner('Checking for outdated packages...');
spinner.start();
await sleep(1500);
spinner.succeed(`Found ${MOCK_PACKAGES.length} packages with updates available`);

console.log('');
console.log(dim('Controls:'));
console.log(dim(' ↑/↓ Navigate packages'));
console.log(dim(' SPACE Toggle selection'));
console.log(dim(' → Change target version'));
console.log(dim(' ENTER Confirm selection'));
console.log(dim(' ESC Cancel'));
console.log(dim(' Type Filter packages'));
console.log('');

try {
const result = await upgradePrompt(MOCK_PACKAGES, 10);

console.log('');

if (result.updates.length === 0) {
console.log(yellow('No packages selected for upgrade.'));
} else {
console.log(green(`\n✔ Selected ${result.updates.length} packages for upgrade:\n`));

for (const update of result.updates) {
console.log(` ${cyan(update.name.padEnd(30))} ${dim(update.from)} ${dim('→')} ${green(update.to)}`);
}

console.log('');
console.log(dim('In a real scenario, this would run:'));
console.log(dim(` pnpm update ${result.updates.map(u => `${u.name}@${u.to}`).join(' ')}`));
}
} catch (error) {
console.error('Error:', error);
}

console.log('\n' + white('═'.repeat(70)));
console.log(white(' ✨ Demo complete!'));
console.log(white('═'.repeat(70)) + '\n');
}

main().catch(console.error);
21 changes: 12 additions & 9 deletions packages/inquirerer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,18 @@
"bugs": {
"url": "https://github.com/constructive-io/dev-utils/issues"
},
"scripts": {
"copy": "makage assets",
"clean": "makage clean",
"prepublishOnly": "npm run build",
"build": "makage build",
"dev": "ts-node dev/index",
"test": "jest",
"test:watch": "jest --watch"
},
"scripts": {
"copy": "makage assets",
"clean": "makage clean",
"prepublishOnly": "npm run build",
"build": "makage build",
"dev": "ts-node dev/index",
"dev:spinner": "ts-node dev/demo-spinner",
"dev:chat": "ts-node dev/demo-chat",
"dev:upgrade": "ts-node dev/demo-upgrade",
"test": "jest",
"test:watch": "jest --watch"
},
"dependencies": {
"deepmerge": "^4.3.1",
"find-and-require-package-json": "workspace:*",
Expand Down
3 changes: 2 additions & 1 deletion packages/inquirerer/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './commander';
export * from './prompt';
export * from './question';
export * from './resolvers';
export * from './resolvers';
export * from './ui';
Loading