udpate imports

This commit is contained in:
Nate Kelley 2025-09-05 11:29:29 -06:00
parent b778192148
commit 01130b9d7d
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
22 changed files with 2119 additions and 5994 deletions

View File

@ -1,18 +1,82 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"extends": ["../../biome.json"],
"$schema": "https://biomejs.dev/schemas/2.2.3/schema.json",
"files": {
"ignoreUnknown": false,
"includes": [
"src/**/*",
".vscode/**/*",
"index.html",
"vite.config.ts",
"vitest.config.ts",
"vitest.setup.ts",
"!**/node_modules",
"!**/dist",
"!**/build",
"!**/coverage"
]
},
"linter": {
"enabled": true,
"rules": {
"suspicious": {
"noConsoleLog": "off",
"noConsole": "off"
"recommended": true,
"correctness": {
"noUnusedVariables": "off",
"useExhaustiveDependencies": "off"
},
"style": {
"useImportType": "off"
"noNonNullAssertion": "error",
"useImportType": "warn",
"useNodejsImportProtocol": "error",
"useConsistentArrayType": "error",
"noUnusedTemplateLiteral": "off"
},
"suspicious": {
"noExplicitAny": "error",
"noConsole": "off"
},
"complexity": {
"noExcessiveCognitiveComplexity": "off",
"noForEach": "off"
},
"performance": {
"noDelete": "error"
}
}
},
"files": {
"include": ["src/**/*", "scripts/**/*"]
"overrides": [
{
"includes": ["**/*.test.ts", "**/*.test.tsx", "**/*.stories.tsx"],
"linter": {
"rules": {
"suspicious": {
"noExplicitAny": "off"
},
"style": {
"noNonNullAssertion": "off"
},
"correctness": {
"noUnusedFunctionParameters": "off",
"noUnusedVariables": "off"
}
}
}
}
],
"formatter": {
"enabled": true,
"formatWithErrors": false,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100,
"lineEnding": "lf"
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"jsxQuoteStyle": "double",
"trailingCommas": "es5",
"semicolons": "always",
"arrowParentheses": "always"
}
}
}

View File

@ -25,35 +25,32 @@
"dependencies": {
"@buster/sdk": "workspace:*",
"@buster/server-shared": "workspace:*",
"chalk": "^5.3.0",
"commander": "^12.1.0",
"chalk": "^5.6.0",
"commander": "^14.0.0",
"fast-glob": "^3.3.3",
"ink": "^5.0.1",
"ink": "^6.2.3",
"ink-big-text": "^2.0.0",
"ink-gradient": "^3.0.0",
"ink-spinner": "^5.0.0",
"ink-text-input": "^6.0.0",
"js-yaml": "^4.1.0",
"micromatch": "^4.0.8",
"react": "^18.3.1",
"zod": "^3.24.1"
"react": "^19.1.1",
"zod": "catalog:"
},
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"@biomejs/biome": "^2.2.3",
"@buster/env-utils": "workspace:*",
"@buster/typescript-config": "workspace:*",
"@buster/vitest-config": "workspace:*",
"@types/bun": "^1.1.14",
"@types/bun": "^1.2.21",
"@types/js-yaml": "^4.0.9",
"@types/micromatch": "^4.0.9",
"@types/node": "^22.10.5",
"@types/react": "^18.3.17",
"@vitest/coverage-v8": "^2.1.8",
"@types/react": "^19.1.12",
"ink-testing-library": "^4.0.0",
"react-devtools-core": "^6.1.5",
"tsx": "^4.19.2",
"typescript": "^5.7.3",
"vitest": "^2.1.8"
"tsx": "catalog:"
},
"engines": {
"bun": ">=1.0.0"

View File

@ -2,7 +2,7 @@ import { createBusterSDK } from '@buster/sdk';
import { Box, Text, useApp, useInput } from 'ink';
import Spinner from 'ink-spinner';
import TextInput from 'ink-text-input';
import React, { useState, useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import {
type Credentials,
deleteCredentials,
@ -205,10 +205,10 @@ export function Auth({ apiKey, host, local, cloud, clear, noSave, show }: AuthPr
if (step === 'host') {
return (
<Box flexDirection='column'>
<Box flexDirection="column">
{_existingCreds && (
<Box marginBottom={1}>
<Text color='yellow'> Existing credentials found. They will be overwritten.</Text>
<Text color="yellow"> Existing credentials found. They will be overwritten.</Text>
</Box>
)}
@ -227,17 +227,17 @@ export function Auth({ apiKey, host, local, cloud, clear, noSave, show }: AuthPr
if (step === 'apikey') {
const displayHost = (hostInput || DEFAULT_HOST).replace(/^https?:\/\//, '');
return (
<Box flexDirection='column'>
<Box flexDirection="column">
{_error && (
<Box marginBottom={1}>
<Text color='red'> {_error}</Text>
<Text color="red"> {_error}</Text>
</Box>
)}
<Box>
<Text>Enter your API key: </Text>
</Box>
<TextInput value={apiKeyInput} onChange={setApiKeyInput} mask='*' />
<TextInput value={apiKeyInput} onChange={setApiKeyInput} mask="*" />
<Box marginTop={1}>
<Text dimColor>Find your API key at https://{displayHost}/app/settings/api-keys</Text>
@ -254,7 +254,7 @@ export function Auth({ apiKey, host, local, cloud, clear, noSave, show }: AuthPr
return (
<Box>
<Text>
<Spinner type='dots' /> Validating API key...
<Spinner type="dots" /> Validating API key...
</Text>
</Box>
);
@ -264,7 +264,7 @@ export function Auth({ apiKey, host, local, cloud, clear, noSave, show }: AuthPr
return (
<Box>
<Text>
<Spinner type='dots' /> Saving credentials...
<Spinner type="dots" /> Saving credentials...
</Text>
</Box>
);

View File

@ -25,9 +25,9 @@ export function DeployProgress({
const percentage = total > 0 ? Math.round((current / total) * 100) : 0;
return (
<Box flexDirection='column' marginY={1}>
<Box flexDirection="column" marginY={1}>
<Box>
{!isComplete && <Spinner type='dots' />}
{!isComplete && <Spinner type="dots" />}
<Text color={isComplete ? 'green' : 'cyan'}>
{' '}
[{current}/{total}] {status}
@ -36,14 +36,14 @@ export function DeployProgress({
{currentFile && (
<Box marginLeft={2}>
<Text color='dim'>File: {currentFile}</Text>
<Text color="dim">File: {currentFile}</Text>
</Box>
)}
{currentModel && (
<Box marginLeft={2}>
<Text color='dim'>Model: </Text>
<Text color='magenta'>{currentModel}</Text>
<Text color="dim">Model: </Text>
<Text color="magenta">{currentModel}</Text>
</Box>
)}
@ -64,9 +64,9 @@ function ProgressBar({ percentage, width }: { percentage: number; width: number
return (
<Box>
<Text color='green'>{'█'.repeat(filled)}</Text>
<Text color='dim'>{'░'.repeat(empty)}</Text>
<Text color='cyan'> {percentage}%</Text>
<Text color="green">{'█'.repeat(filled)}</Text>
<Text color="dim">{'░'.repeat(empty)}</Text>
<Text color="cyan"> {percentage}%</Text>
</Box>
);
}

View File

@ -14,80 +14,80 @@ export function DeploySummary({ result }: DeploySummaryProps) {
const hasFailures = result.failures.length > 0;
return (
<Box flexDirection='column' marginY={1}>
<Text bold color='cyan'>
<Box flexDirection="column" marginY={1}>
<Text bold color="cyan">
📊 Deployment Summary
</Text>
<Text color='dim'>{'='.repeat(40)}</Text>
<Text color="dim">{'='.repeat(40)}</Text>
{/* Success metrics */}
<Box flexDirection='column' marginY={1}>
<Text color='green'> Successfully deployed: {totalDeployed} models</Text>
<Box flexDirection="column" marginY={1}>
<Text color="green"> Successfully deployed: {totalDeployed} models</Text>
{result.success.length > 0 && (
<Box marginLeft={2}>
<Text color='green'> New models: {result.success.length}</Text>
<Text color="green"> New models: {result.success.length}</Text>
</Box>
)}
{result.updated.length > 0 && (
<Box marginLeft={2}>
<Text color='cyan'>🔄 Updated models: {result.updated.length}</Text>
<Text color="cyan">🔄 Updated models: {result.updated.length}</Text>
</Box>
)}
{result.noChange.length > 0 && (
<Box marginLeft={2}>
<Text color='dim'> No changes: {result.noChange.length}</Text>
<Text color="dim"> No changes: {result.noChange.length}</Text>
</Box>
)}
</Box>
{/* Exclusions */}
{result.excluded.length > 0 && (
<Text color='yellow'> Excluded: {result.excluded.length} files</Text>
<Text color="yellow"> Excluded: {result.excluded.length} files</Text>
)}
{/* Failures */}
{hasFailures && (
<Box flexDirection='column' marginTop={1}>
<Text color='red'> Failed: {result.failures.length} models</Text>
<Text color='dim'>{'-'.repeat(40)}</Text>
<Box flexDirection="column" marginTop={1}>
<Text color="red"> Failed: {result.failures.length} models</Text>
<Text color="dim">{'-'.repeat(40)}</Text>
{result.failures.slice(0, 5).map((failure) => (
<Box
key={`${failure.file}-${failure.modelName}`}
flexDirection='column'
flexDirection="column"
marginLeft={2}
marginY={0.5}
>
<Text color='yellow'>File: {failure.file}</Text>
<Text color='magenta'>Model: {failure.modelName}</Text>
<Text color="yellow">File: {failure.file}</Text>
<Text color="magenta">Model: {failure.modelName}</Text>
{failure.errors.map((error: string) => (
<Box key={error} marginLeft={2}>
<Text color='red'> {error}</Text>
<Text color="red"> {error}</Text>
</Box>
))}
</Box>
))}
{result.failures.length > 5 && (
<Text color='dim' italic>
<Text color="dim" italic>
...and {result.failures.length - 5} more failures
</Text>
)}
</Box>
)}
<Text color='dim'>{'='.repeat(40)}</Text>
<Text color="dim">{'='.repeat(40)}</Text>
{/* Final status */}
{hasFailures ? (
<Text color='yellow' bold>
<Text color="yellow" bold>
Some models failed to deploy. Please check the errors above.
</Text>
) : (
<Text color='green' bold>
<Text color="green" bold>
🎉 All models processed successfully!
</Text>
)}

View File

@ -1,5 +1,5 @@
import { existsSync } from 'node:fs';
import { readFile, readdir, stat } from 'node:fs/promises';
import { readdir, readFile, stat } from 'node:fs/promises';
import { join, relative, resolve } from 'node:path';
import yaml from 'js-yaml';
import {

View File

@ -7,9 +7,9 @@ import {
processDeploymentResponse,
} from './deployment/results';
import {
type DeployFunction,
createAuthenticatedDeployer,
createDryRunDeployer,
type DeployFunction,
} from './deployment/strategies';
import {
createModelFileMap,

View File

@ -65,7 +65,7 @@ export function DeployCommand(props: DeployCommandProps) {
// Always show the banner at the top
return (
<Box flexDirection='column'>
<Box flexDirection="column">
<BusterBanner showSubtitle={false} />
{/* Error state */}
@ -74,16 +74,16 @@ export function DeployCommand(props: DeployCommandProps) {
{/* Check if it's a buster.yml not found error */}
{error?.includes('No buster.yml found') ? (
<Box paddingX={2}>
<Text color='red'>No buster.yml found</Text>
<Text color="red">No buster.yml found</Text>
</Box>
) : (
<Box flexDirection='column' paddingX={2}>
<Text color='red' bold>
<Box flexDirection="column" paddingX={2}>
<Text color="red" bold>
Deployment Error
</Text>
<Text color='red'>{error}</Text>
<Text color="red">{error}</Text>
<Box marginTop={1}>
<Text color='dim'>Please check your configuration and try again.</Text>
<Text color="dim">Please check your configuration and try again.</Text>
</Box>
</Box>
)}
@ -108,7 +108,7 @@ export function DeployCommand(props: DeployCommandProps) {
{/* Initializing state - show spinner */}
{status === 'initializing' && (
<Box paddingX={2}>
<Spinner label='Loading configuration...' />
<Spinner label="Loading configuration..." />
</Box>
)}
</Box>

View File

@ -1,5 +1,5 @@
import { relative } from 'node:path';
import type { CLIDeploymentResult, DeployResponse, DeploymentExcluded, Model } from '../schemas';
import type { CLIDeploymentResult, DeploymentExcluded, DeployResponse, Model } from '../schemas';
/**
* Pure function to merge multiple deployment results into one

View File

@ -1,6 +1,6 @@
import { type BusterSDK, createBusterSDK } from '@buster/sdk';
import { loadCredentials } from '../../../utils/credentials';
import type { DeployRequest, DeployResponse, DeploymentFailure, DeploymentItem } from '../schemas';
import type { DeploymentFailure, DeploymentItem, DeployRequest, DeployResponse } from '../schemas';
/**
* Type definition for a deployment function

View File

@ -5,11 +5,11 @@ import yaml from 'js-yaml';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import type { Model } from '../schemas';
import {
ModelParsingError,
fileContainsTodo,
findTodoMarkers,
formatZodIssues,
generateDefaultSQL,
ModelParsingError,
parseModelFile,
parseModelFileStrict,
resolveModelConfig,

View File

@ -9,14 +9,14 @@ import {
DeployColumnSchema,
type DeployModel,
DeployModelSchema,
type DeployRequest,
DeployRequestSchema,
type DeployResponse,
DeployResponseSchema,
type DeploymentFailure,
DeploymentFailureSchema,
type DeploymentItem,
DeploymentItemSchema,
type DeployRequest,
DeployRequestSchema,
type DeployResponse,
DeployResponseSchema,
type Dimension,
DimensionSchema,
type Filter,

View File

@ -5,20 +5,20 @@ import { HelloCommand } from './hello';
describe('HelloCommand', () => {
it('should render greeting with default name', () => {
const { lastFrame } = render(<HelloCommand name='World' />);
const { lastFrame } = render(<HelloCommand name="World" />);
expect(lastFrame()).toContain('Hello, World!');
expect(lastFrame()).toContain('Buster CLI');
});
it('should render greeting in uppercase when flag is set', () => {
const { lastFrame } = render(<HelloCommand name='Claude' uppercase={true} />);
const { lastFrame } = render(<HelloCommand name="Claude" uppercase={true} />);
expect(lastFrame()).toContain('HELLO, CLAUDE!');
});
it('should render greeting in normal case when uppercase flag is false', () => {
const { lastFrame } = render(<HelloCommand name='Claude' uppercase={false} />);
const { lastFrame } = render(<HelloCommand name="Claude" uppercase={false} />);
expect(lastFrame()).toContain('Hello, Claude!');
expect(lastFrame()).not.toContain('HELLO, CLAUDE!');

View File

@ -1,6 +1,7 @@
import chalk from 'chalk';
import { Box, Text } from 'ink';
import React, { useEffect } from 'react';
import type React from 'react';
import { useEffect } from 'react';
interface HelloCommandProps {
name: string;
@ -19,8 +20,8 @@ export const HelloCommand: React.FC<HelloCommandProps> = ({ name, uppercase }) =
}, []);
return (
<Box flexDirection='column'>
<Text color='green'>{chalk.bold('🚀 Buster CLI')}</Text>
<Box flexDirection="column">
<Text color="green">{chalk.bold('🚀 Buster CLI')}</Text>
<Text>{displayText}</Text>
</Box>
);

View File

@ -4,7 +4,7 @@ import { createBusterSDK } from '@buster/sdk';
import { Box, Text, useApp, useInput } from 'ink';
import Spinner from 'ink-spinner';
import TextInput from 'ink-text-input';
import React, { useState, useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import { BusterBanner } from '../components/banner.js';
import { type Credentials, getCredentials, saveCredentials } from '../utils/credentials.js';
@ -436,26 +436,26 @@ export function InitCommand({ apiKey, host, local, path: providedPath }: InitPro
// Always show the banner at the top
return (
<Box flexDirection='column'>
<Box flexDirection="column">
<BusterBanner />
{step === 'check' && (
<Box paddingX={2}>
<Text>
<Spinner type='dots' /> Checking configuration...
<Spinner type="dots" /> Checking configuration...
</Text>
</Box>
)}
{step === 'prompt-auth' && (
<Box flexDirection='column' paddingX={2}>
<Box flexDirection="column" paddingX={2}>
<Box marginBottom={1}>
<Text>Let's get you connected to Buster.</Text>
</Box>
{error && (
<Box marginBottom={1}>
<Text color='red'> {error}</Text>
<Text color="red"> {error}</Text>
</Box>
)}
@ -470,7 +470,7 @@ export function InitCommand({ apiKey, host, local, path: providedPath }: InitPro
<Text>Enter your API key: </Text>
</Box>
<TextInput value={apiKeyInput} onChange={setApiKeyInput} mask='*' placeholder='sk_...' />
<TextInput value={apiKeyInput} onChange={setApiKeyInput} mask="*" placeholder="sk_..." />
<Box marginTop={1}>
<Text dimColor>Find your API key at {hostInput}/app/settings/api-keys</Text>
@ -485,7 +485,7 @@ export function InitCommand({ apiKey, host, local, path: providedPath }: InitPro
{step === 'validate' && (
<Box paddingX={2}>
<Text>
<Spinner type='dots' /> Validating your API key...
<Spinner type="dots" /> Validating your API key...
</Text>
</Box>
)}
@ -493,20 +493,20 @@ export function InitCommand({ apiKey, host, local, path: providedPath }: InitPro
{step === 'save' && (
<Box paddingX={2}>
<Text>
<Spinner type='dots' /> Saving your configuration...
<Spinner type="dots" /> Saving your configuration...
</Text>
</Box>
)}
{step === 'prompt-location' && (
<Box flexDirection='column' paddingX={2}>
<Box flexDirection="column" paddingX={2}>
<Box marginBottom={1}>
<Text>Where would you like to create your Buster project?</Text>
</Box>
{error && (
<Box marginBottom={1}>
<Text color='red'> {error}</Text>
<Text color="red"> {error}</Text>
</Box>
)}
@ -514,8 +514,8 @@ export function InitCommand({ apiKey, host, local, path: providedPath }: InitPro
<Text>Project location: </Text>
</Box>
<Box borderStyle='single' borderColor='#7C3AED' paddingX={1}>
<TextInput value={projectPath} onChange={setProjectPath} placeholder='./' />
<Box borderStyle="single" borderColor="#7C3AED" paddingX={1}>
<TextInput value={projectPath} onChange={setProjectPath} placeholder="./" />
</Box>
<Box marginTop={1}>
@ -531,22 +531,22 @@ export function InitCommand({ apiKey, host, local, path: providedPath }: InitPro
{step === 'creating' && (
<Box paddingX={2}>
<Text>
<Spinner type='dots' /> Creating project structure...
<Spinner type="dots" /> Creating project structure...
</Text>
</Box>
)}
{step === 'done' && (
<Box flexDirection='column' paddingX={2}>
<Box flexDirection="column" paddingX={2}>
<Box marginBottom={1}>
<Text color='green'> Created example project</Text>
<Text color="green"> Created example project</Text>
</Box>
<Box marginBottom={1}>
<Text>Project structure:</Text>
</Box>
<Box flexDirection='column' marginLeft={2}>
<Box flexDirection="column" marginLeft={2}>
<Text>📁 {join(resolve(projectPath), 'buster')}/</Text>
<Text> 📄 buster.yml</Text>
<Text> 📁 docs/</Text>
@ -562,7 +562,7 @@ export function InitCommand({ apiKey, host, local, path: providedPath }: InitPro
<Text bold>📚 Next steps:</Text>
</Box>
<Box flexDirection='column' marginLeft={2}>
<Box flexDirection="column" marginLeft={2}>
<Text>1. cd {join(resolve(projectPath), 'buster')}</Text>
<Text>2. Configure buster.yml for your data source</Text>
<Text>3. Populate docs/ with your documentation files</Text>

View File

@ -1,6 +1,7 @@
import chalk from 'chalk';
import { Box, Text, useApp, useInput } from 'ink';
import React, { useState } from 'react';
import type React from 'react';
import { useState } from 'react';
export const InteractiveCommand: React.FC = () => {
const [selectedOption, setSelectedOption] = useState(0);
@ -27,15 +28,15 @@ export const InteractiveCommand: React.FC = () => {
});
return (
<Box flexDirection='column'>
<Box flexDirection="column">
<Box marginBottom={1}>
<Text color='cyan' bold>
<Text color="cyan" bold>
🚀 Buster CLI - Interactive Mode
</Text>
</Box>
<Text dimColor>Use arrow keys to navigate, Enter to select, Q to quit</Text>
<Box marginTop={1} flexDirection='column'>
<Box marginTop={1} flexDirection="column">
{options.map((option, index) => (
<Box key={option}>
<Text {...(selectedOption === index ? { color: 'green' } : {})}>

View File

@ -1,9 +1,8 @@
import { createBusterSDK } from '@buster/sdk';
import { Box, Text, useApp, useInput } from 'ink';
import { render } from 'ink';
import { Box, render, Text, useApp, useInput } from 'ink';
import Spinner from 'ink-spinner';
import TextInput from 'ink-text-input';
import React, { useState, useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import { BusterBanner } from '../components/banner.js';
import {
type Credentials,
@ -21,11 +20,11 @@ const _LOCAL_HOST = 'http://localhost:3001';
// Component for the welcome screen header with additional help text
function WelcomeHeader() {
return (
<Box paddingY={2} paddingX={2} alignItems='center'>
<Box paddingY={2} paddingX={2} alignItems="center">
<Box marginRight={4}>
<BusterBanner showSubtitle={false} inline={true} />
</Box>
<Box flexDirection='column' justifyContent='center'>
<Box flexDirection="column" justifyContent="center">
<Text bold>Welcome to Buster</Text>
<Box marginTop={1}>
<Text dimColor>Type / to use slash commands</Text>
@ -40,7 +39,7 @@ function WelcomeHeader() {
<Text dimColor>/help for more</Text>
</Box>
<Box marginTop={2}>
<Text color='#7C3AED'>"Run `buster` and fix all the errors"</Text>
<Text color="#7C3AED">"Run `buster` and fix all the errors"</Text>
</Box>
</Box>
</Box>
@ -115,21 +114,21 @@ function CommandInput({ onSubmit }: { onSubmit: (input: string) => void }) {
});
return (
<Box flexDirection='column' paddingX={2} paddingBottom={1}>
<Box borderStyle='single' borderColor='#7C3AED' paddingX={1} width='100%'>
<Text color='#7C3AED'> </Text>
<Box flexDirection="column" paddingX={2} paddingBottom={1}>
<Box borderStyle="single" borderColor="#7C3AED" paddingX={1} width="100%">
<Text color="#7C3AED"> </Text>
<TextInput
value={input}
onChange={handleChange}
onSubmit={handleSubmit}
placeholder='Enter a command or question...'
placeholder="Enter a command or question..."
/>
</Box>
{/* Show command suggestions */}
{showSuggestions && filteredCommands.length > 0 && (
<Box flexDirection='column' marginTop={1} paddingX={1}>
<Text color='#7C3AED' bold>
<Box flexDirection="column" marginTop={1} paddingX={1}>
<Text color="#7C3AED" bold>
Available Commands:
</Text>
{filteredCommands.map((cmd, index) => (
@ -194,21 +193,21 @@ function AuthPrompt({ onAuth }: { onAuth: (creds: Credentials) => void }) {
return (
<Box paddingX={2}>
<Text>
<Spinner type='dots' /> Validating your API key...
<Spinner type="dots" /> Validating your API key...
</Text>
</Box>
);
}
return (
<Box flexDirection='column' paddingX={2}>
<Box flexDirection="column" paddingX={2}>
<Box marginBottom={1}>
<Text>Let's get you connected to Buster.</Text>
</Box>
{error && (
<Box marginBottom={1}>
<Text color='red'> {error}</Text>
<Text color="red"> {error}</Text>
</Box>
)}
@ -216,13 +215,13 @@ function AuthPrompt({ onAuth }: { onAuth: (creds: Credentials) => void }) {
<Text>Enter your API key: </Text>
</Box>
<Box borderStyle='single' borderColor='#7C3AED' paddingX={1}>
<Box borderStyle="single" borderColor="#7C3AED" paddingX={1}>
<TextInput
value={apiKey}
onChange={setApiKey}
onSubmit={handleSubmit}
mask='*'
placeholder='sk_...'
mask="*"
placeholder="sk_..."
/>
</Box>
@ -334,11 +333,11 @@ export function Main() {
if (checkingAuth) {
return (
<Box flexDirection='column'>
<Box flexDirection="column">
<WelcomeHeader />
<Box paddingX={2}>
<Text>
<Spinner type='dots' /> Checking configuration...
<Spinner type="dots" /> Checking configuration...
</Text>
</Box>
</Box>
@ -346,12 +345,12 @@ export function Main() {
}
return (
<Box flexDirection='column'>
<Box flexDirection="column">
<WelcomeHeader />
{/* Show command history if authenticated */}
{isAuthenticated && commandHistory.length > 0 && (
<Box flexDirection='column' paddingX={2} marginBottom={1}>
<Box flexDirection="column" paddingX={2} marginBottom={1}>
{commandHistory.slice(-5).map((cmd, idx) => (
<Box key={`cmd-${idx}-${cmd}`}>
<Text dimColor> {cmd}</Text>

View File

@ -17,9 +17,9 @@ export function Welcome() {
return (
<Box paddingY={2} paddingX={2}>
<Box marginRight={4}>
<AnimatedLogo color='#7C3AED' />
<AnimatedLogo color="#7C3AED" />
</Box>
<Box flexDirection='column' justifyContent='center'>
<Box flexDirection="column" justifyContent="center">
<Text bold>Welcome to Buster</Text>
<Box marginTop={1}>
<Text dimColor>Type / to use slash commands</Text>
@ -34,7 +34,7 @@ export function Welcome() {
<Text dimColor>/help for more</Text>
</Box>
<Box marginTop={2}>
<Text color='#7C3AED'>"Run `buster` and fix all the errors"</Text>
<Text color="#7C3AED">"Run `buster` and fix all the errors"</Text>
</Box>
</Box>
</Box>

View File

@ -1,5 +1,5 @@
import { Box, Text } from 'ink';
import React, { useState, useEffect } from 'react';
import React, { useEffect, useState } from 'react';
// ASCII art for the Buster "b" logo - compact version
const BUSTER_LOGO_FRAMES = [
@ -84,7 +84,7 @@ export function AnimatedLogo({ color = '#7C3AED' }: AnimatedLogoProps) {
const currentFrame = BUSTER_LOGO_FRAMES[opacity];
return (
<Box flexDirection='column' alignItems='center'>
<Box flexDirection="column" alignItems="center">
<Text color={color}>{currentFrame}</Text>
</Box>
);

View File

@ -14,8 +14,8 @@ export function BusterBanner({ showSubtitle = true, inline = false }: BannerProp
const content = (
<>
<Box>
<Text color='#7C3AED'>
<BigText text='BUSTER' font='block' />
<Text color="#7C3AED">
<BigText text="BUSTER" font="block" />
</Text>
</Box>
{showSubtitle && (
@ -28,12 +28,12 @@ export function BusterBanner({ showSubtitle = true, inline = false }: BannerProp
// For inline mode (root command), don't add padding
if (inline) {
return <Box flexDirection='column'>{content}</Box>;
return <Box flexDirection="column">{content}</Box>;
}
// For centered mode (init, deploy commands), add padding and center
return (
<Box paddingY={2} paddingX={2} flexDirection='column' alignItems='center'>
<Box paddingY={2} paddingX={2} flexDirection="column" alignItems="center">
{content}
</Box>
);

View File

@ -37,7 +37,7 @@ export function Spinner({ label = 'Loading', type = 'dots' }: SpinnerProps) {
}, [spinner]);
return (
<Text color='cyan'>
<Text color="cyan">
{spinner.frames[frame]} {label}
</Text>
);

File diff suppressed because it is too large Load Diff