Implement dev mode nice UI (#17471)

Implement a nice terminal UI for dev mode using INK

<img width="1512" height="721" alt="image"
src="https://github.com/user-attachments/assets/79a71f37-1b31-4761-9e8d-718ef029ceb8"
/>
This commit is contained in:
martmull
2026-01-27 16:34:28 +01:00
committed by GitHub
parent bc7791871f
commit f4ca69a474
31 changed files with 911 additions and 258 deletions
@@ -13,7 +13,6 @@ describe('rich-app app:dev', () => {
beforeAll(async () => {
result = await runAppDev({ appPath: APP_PATH });
expect(result.success).toBe(true);
}, 60000);
@@ -1,36 +1,35 @@
import { getOutputByPrefix } from '@/cli/__tests__/integration/utils/get-output-by-prefix.util';
import { type RunCliCommandResult } from '@/cli/__tests__/integration/utils/run-cli-command.util';
import { sanitizeAnsi } from '@/cli/__tests__/integration/utils/sanitize-ansi.util';
export const defineConsoleOutputTests = (
getResult: () => RunCliCommandResult,
): void => {
describe('console output', () => {
it('should contain init messages', () => {
const output = getOutputByPrefix(getResult().output, 'init');
const output = sanitizeAnsi(getResult().output);
expect(output).toContain(
'[init] 🚀 Starting Twenty Application Development Mode',
);
expect(output).toContain('[init] 📁 App Path:');
expect(output).toContain('Application');
expect(output).toContain('Name: Loading...');
expect(output).toContain('Status: o Idle');
});
it('should contain dev-mode build messages', () => {
const output = getOutputByPrefix(getResult().output, 'dev-mode');
const output = sanitizeAnsi(getResult().output);
expect(output).toContain('[dev-mode] Building manifest...');
expect(output).toContain('[dev-mode] Successfully built manifest');
expect(output).toContain('Building manifest');
expect(output).toContain('Successfully built manifest');
});
it('should contain dev-mode function build messages', () => {
const output = getOutputByPrefix(getResult().output, 'dev-mode');
const output = sanitizeAnsi(getResult().output);
expect(output).toContain('[dev-mode] ✓ Successfully built');
expect(output).toContain('Successfully built');
});
it('should contain dev-mode sync messages', () => {
const output = getOutputByPrefix(getResult().output, 'dev-mode');
const output = sanitizeAnsi(getResult().output);
expect(output).toContain('[dev-mode] ✓ Synced');
expect(output).toContain('✓ Synced');
});
});
};
@@ -14,7 +14,6 @@ describe('root-app app:dev', () => {
beforeAll(async () => {
result = await runAppDev({ appPath: APP_PATH });
expect(result.success).toBe(true);
}, 60000);
@@ -1,36 +1,35 @@
import { getOutputByPrefix } from '@/cli/__tests__/integration/utils/get-output-by-prefix.util';
import { type RunCliCommandResult } from '@/cli/__tests__/integration/utils/run-cli-command.util';
import { sanitizeAnsi } from '@/cli/__tests__/integration/utils/sanitize-ansi.util';
export const defineConsoleOutputTests = (
getResult: () => RunCliCommandResult,
): void => {
describe('console output', () => {
it('should contain init messages', () => {
const output = getOutputByPrefix(getResult().output, 'init');
const output = sanitizeAnsi(getResult().output);
expect(output).toContain(
'[init] 🚀 Starting Twenty Application Development Mode',
);
expect(output).toContain('[init] 📁 App Path:');
expect(output).toContain('Application');
expect(output).toContain('Name: Loading...');
expect(output).toContain('Status: o Idle');
});
it('should contain dev-mode build messages', () => {
const output = getOutputByPrefix(getResult().output, 'dev-mode');
const output = sanitizeAnsi(getResult().output);
expect(output).toContain('[dev-mode] Building manifest...');
expect(output).toContain('[dev-mode] Successfully built manifest');
expect(output).toContain('Building manifest');
expect(output).toContain('Successfully built manifest');
});
it('should contain dev-mode function build messages', () => {
const output = getOutputByPrefix(getResult().output, 'dev-mode');
const output = sanitizeAnsi(getResult().output);
expect(output).toContain('[dev-mode] ✓ Successfully built');
expect(output).toContain('Successfully built');
});
it('should contain dev-mode sync messages', () => {
const output = getOutputByPrefix(getResult().output, 'dev-mode');
const output = sanitizeAnsi(getResult().output);
expect(output).toContain('[dev-mode] ✓ Synced');
expect(output).toContain('✓ Synced');
});
});
};
@@ -16,7 +16,7 @@ export const runAppDev = (
return runCliCommand({
command: 'app:dev',
args: [appPath],
waitForOutput: ['[dev-mode] ✓ Synced'],
waitForOutput: ['✓ Synced'],
timeout,
});
};
@@ -36,7 +36,6 @@ export const runCliCommand = (
env: {
...process.env,
FORCE_COLOR: '0',
TWENTY_SKIP_SERVER_CHECK: 'true',
},
},
);
@@ -0,0 +1,2 @@
export const sanitizeAnsi = (output: string): string =>
output.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, '');