mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-07 07:55:13 +02:00
feat(cli): add intro step and project dir to demo tour
Show the target project directory in the demo banner and add an introductory screen before the first setup card so users understand where demo artifacts will land. Also simplify stdin key detection by comparing raw byte values instead of string conversions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
556563d654
commit
262276dcd7
2 changed files with 30 additions and 21 deletions
|
|
@ -209,7 +209,7 @@ describe('runDemoTour', () => {
|
|||
},
|
||||
);
|
||||
expect(result).toBe(0);
|
||||
// Navigation called once for databases step, then exits
|
||||
// Navigation called once for intro, then exits on back
|
||||
expect(navigation).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
|
|
@ -218,10 +218,11 @@ describe('runDemoTour', () => {
|
|||
let callCount = 0;
|
||||
const navigation = vi.fn().mockImplementation(() => {
|
||||
callCount++;
|
||||
// First call (databases): forward
|
||||
// Second call (sources): back
|
||||
// Third call (databases again): back (exit)
|
||||
if (callCount === 1) return Promise.resolve('forward');
|
||||
// First call (intro): forward
|
||||
// Second call (databases): forward
|
||||
// Third call (sources): back
|
||||
// Fourth call (databases again): back (exit)
|
||||
if (callCount <= 2) return Promise.resolve('forward');
|
||||
return Promise.resolve('back');
|
||||
});
|
||||
|
||||
|
|
@ -235,7 +236,7 @@ describe('runDemoTour', () => {
|
|||
},
|
||||
);
|
||||
expect(result).toBe(0);
|
||||
expect(navigation).toHaveBeenCalledTimes(3);
|
||||
expect(navigation).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
|
||||
it('handles agent step returning back', async () => {
|
||||
|
|
@ -243,10 +244,10 @@ describe('runDemoTour', () => {
|
|||
let navCount = 0;
|
||||
const navigation = vi.fn().mockImplementation(() => {
|
||||
navCount++;
|
||||
// Forward through databases, sources, context
|
||||
// Forward through intro, databases, sources, context
|
||||
// Then back from context (after agents returns back)
|
||||
// Then back from sources, then back from databases (exit)
|
||||
if (navCount <= 3) return Promise.resolve('forward');
|
||||
if (navCount <= 4) return Promise.resolve('forward');
|
||||
return Promise.resolve('back');
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -62,12 +62,15 @@ function createTargetState(target: KtxPublicIngestPlanTarget): ContextBuildTarge
|
|||
// Pure rendering functions
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function renderDemoBanner(): string {
|
||||
export function renderDemoBanner(projectDir?: string): string {
|
||||
const lines = [
|
||||
'',
|
||||
`┌ ${cyan('Demo mode')} — data has been pre-processed and KTX context is already built.`,
|
||||
'│ This walkthrough illustrates the setup steps. Selections are pre-filled and read-only.',
|
||||
];
|
||||
if (projectDir) {
|
||||
lines.push(`│ Project directory: ${dim(projectDir)}`);
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
|
|
@ -144,16 +147,15 @@ export async function waitForDemoNavigation(
|
|||
};
|
||||
|
||||
const onData = (data: Buffer) => {
|
||||
const char = data.toString();
|
||||
if (char === '\r' || char === '\n') {
|
||||
cleanup();
|
||||
resolve('forward');
|
||||
} else if (char === '\x1b') {
|
||||
cleanup();
|
||||
resolve('back');
|
||||
} else if (char === '\x03') {
|
||||
if (data[0] === 0x03) {
|
||||
cleanup();
|
||||
reject(new KtxSetupExitError());
|
||||
} else if (data[0] === 0x0d || data[0] === 0x0a) {
|
||||
cleanup();
|
||||
resolve('forward');
|
||||
} else if (data[0] === 0x1b) {
|
||||
cleanup();
|
||||
resolve('back');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -171,8 +173,9 @@ export async function renderDemoCard(
|
|||
io: KtxCliIo,
|
||||
stdin?: NodeJS.ReadStream,
|
||||
waitNav: (stdin?: NodeJS.ReadStream) => Promise<'forward' | 'back'> = waitForDemoNavigation,
|
||||
projectDir?: string,
|
||||
): Promise<'forward' | 'back'> {
|
||||
io.stdout.write(renderDemoBanner() + '\n\n');
|
||||
io.stdout.write(renderDemoBanner(projectDir) + '\n\n');
|
||||
io.stdout.write(renderDemoCardContent(title, selections) + '\n');
|
||||
return waitNav(stdin);
|
||||
}
|
||||
|
|
@ -337,6 +340,11 @@ export async function runDemoTour(
|
|||
const projectDir = defaultDemoProjectDir();
|
||||
await ensureProject({ projectDir, force: false });
|
||||
|
||||
io.stdout.write(renderDemoBanner(projectDir) + '\n');
|
||||
io.stdout.write(`\n│ ${dim('Press Enter to continue, Escape to go back')}\n└\n`);
|
||||
const introDirection = await waitNav();
|
||||
if (introDirection === 'back') return 0;
|
||||
|
||||
let stepIndex = 0;
|
||||
|
||||
while (stepIndex < DEMO_STEPS.length) {
|
||||
|
|
@ -344,11 +352,11 @@ export async function runDemoTour(
|
|||
let direction: 'forward' | 'back';
|
||||
|
||||
if (step === 'databases') {
|
||||
direction = await renderDemoCard('Database connection', ['PostgreSQL — Orbit Analytics (56 tables, 2 schemas)'], io, undefined, waitNav);
|
||||
direction = await renderDemoCard('Database connection', ['PostgreSQL — Orbit Analytics (56 tables, 2 schemas)'], io, undefined, waitNav, projectDir);
|
||||
} else if (step === 'sources') {
|
||||
direction = await renderDemoCard('Context sources', ['dbt — 34 transformation models', 'Metabase — 80 dashboard cards', 'Notion — 9 knowledge pages'], io, undefined, waitNav);
|
||||
direction = await renderDemoCard('Context sources', ['dbt — 34 transformation models', 'Metabase — 80 dashboard cards', 'Notion — 9 knowledge pages'], io, undefined, waitNav, projectDir);
|
||||
} else if (step === 'context') {
|
||||
io.stdout.write(renderDemoBanner() + '\n\n');
|
||||
io.stdout.write(renderDemoBanner(projectDir) + '\n\n');
|
||||
if (deps.skipReplayAnimation) {
|
||||
direction = await waitNav();
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue