chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 11:58:09 +08:00
commit c48e26cdd0
2909 changed files with 1591131 additions and 0 deletions
@@ -0,0 +1,102 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { useState } from 'react';
import { render, Box, Text } from 'ink';
import { AskUserDialog } from '../src/ui/components/AskUserDialog.js';
import { KeypressProvider } from '../src/ui/contexts/KeypressContext.js';
import { QuestionType, type Question } from '@google/gemini-cli-core';
const DEMO_QUESTIONS: Question[] = [
{
question: 'What type of project are you building?',
header: 'Project Type',
options: [
{ label: 'Web Application', description: 'React, Next.js, or similar' },
{ label: 'CLI Tool', description: 'Command-line interface with Node.js' },
{ label: 'Library', description: 'NPM package or shared utility' },
],
multiSelect: false,
},
{
question: 'Which features should be enabled?',
header: 'Features',
options: [
{ label: 'TypeScript', description: 'Add static typing' },
{ label: 'ESLint', description: 'Add linting and formatting' },
{ label: 'Unit Tests', description: 'Add Vitest setup' },
{ label: 'CI/CD', description: 'Add GitHub Actions' },
],
multiSelect: true,
},
{
question: 'What is the project name?',
header: 'Name',
type: QuestionType.TEXT,
placeholder: 'my-awesome-project',
},
{
question: 'Initialize git repository?',
header: 'Git',
type: QuestionType.YESNO,
},
];
const Demo = () => {
const [result, setResult] = useState<null | { [key: string]: string }>(null);
const [cancelled, setCancelled] = useState(false);
if (cancelled) {
return (
<Box padding={1}>
<Text color="red">
Dialog was cancelled. Project initialization aborted.
</Text>
</Box>
);
}
if (result) {
return (
<Box
flexDirection="column"
padding={1}
borderStyle="single"
borderColor="green"
>
<Text bold color="green">
Success! Project Configuration:
</Text>
{DEMO_QUESTIONS.map((q, i) => (
<Box key={i} marginTop={1}>
<Text color="gray">{q.header}: </Text>
<Text>{result[i] || '(not answered)'}</Text>
</Box>
))}
<Box marginTop={1}>
<Text color="dim">Press Ctrl+C to exit</Text>
</Box>
</Box>
);
}
return (
<KeypressProvider>
<Box padding={1} flexDirection="column">
<Text bold marginBottom={1}>
AskUserDialog Demo
</Text>
<AskUserDialog
questions={DEMO_QUESTIONS}
onSubmit={setResult}
onCancel={() => setCancelled(true)}
/>
</Box>
</KeypressProvider>
);
};
render(<Demo />);
@@ -0,0 +1,165 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { useState, useEffect, useRef } from 'react';
import { render, Box, Text, useInput, useStdout } from 'ink';
import {
ScrollableList,
type ScrollableListRef,
} from '../src/ui/components/shared/ScrollableList.js';
import { ScrollProvider } from '../src/ui/contexts/ScrollProvider.js';
import { MouseProvider } from '../src/ui/contexts/MouseContext.js';
import { KeypressProvider } from '../src/ui/contexts/KeypressContext.js';
import {
enableMouseEvents,
disableMouseEvents,
} from '../src/ui/utils/mouse.js';
interface Item {
id: string;
title: string;
}
const getLorem = (index: number) =>
Array(10)
.fill(null)
.map(() => 'lorem ipsum '.repeat((index % 3) + 1).trim())
.join('\n');
const Demo = () => {
const { stdout } = useStdout();
const [size, setSize] = useState({
columns: stdout.columns,
rows: stdout.rows,
});
useEffect(() => {
const onResize = () => {
setSize({
columns: stdout.columns,
rows: stdout.rows,
});
};
stdout.on('resize', onResize);
return () => {
stdout.off('resize', onResize);
};
}, [stdout]);
const [items, setItems] = useState<Item[]>(() =>
Array.from({ length: 1000 }, (_, i) => ({
id: String(i),
title: `Item ${i + 1}`,
})),
);
const listRef = useRef<ScrollableListRef<Item>>(null);
useInput((input, key) => {
if (input === 'a' || input === 'A') {
setItems((prev) => [
...prev,
{ id: String(prev.length), title: `Item ${prev.length + 1}` },
]);
}
if ((input === 'e' || input === 'E') && !key.ctrl) {
setItems((prev) => {
if (prev.length === 0) return prev;
const lastIndex = prev.length - 1;
const lastItem = prev[lastIndex]!;
const newItem = { ...lastItem, title: lastItem.title + 'e' };
return [...prev.slice(0, lastIndex), newItem];
});
}
if (key.ctrl && input === 'e') {
listRef.current?.scrollToEnd();
}
// Let Ink handle Ctrl+C via exitOnCtrlC (default true) or handle explicitly if needed.
// For alternate buffer, explicit handling is often safer for cleanup.
if (key.escape || (key.ctrl && input === 'c')) {
process.exit(0);
}
});
return (
<MouseProvider mouseEventsEnabled={true}>
<KeypressProvider>
<ScrollProvider>
<Box
flexDirection="column"
width={size.columns}
height={size.rows - 1}
padding={1}
>
<Text>
Press &apos;A&apos; to add an item. Press &apos;E&apos; to edit
last item. Press &apos;Ctrl+E&apos; to scroll to end. Press
&apos;Esc&apos; to exit. Mouse wheel or Shift+Up/Down to scroll.
</Text>
<Box flexGrow={1} borderStyle="round" borderColor="cyan">
<ScrollableList
ref={listRef}
data={items}
renderItem={({ item, index }) => (
<Box flexDirection="column" paddingBottom={2}>
<Box
sticky
flexDirection="column"
width={size.columns - 2}
opaque
stickyChildren={
<Box
flexDirection="column"
width={size.columns - 2}
opaque
>
<Text>{item.title}</Text>
<Box
borderStyle="single"
borderTop={true}
borderBottom={false}
borderLeft={false}
borderRight={false}
borderColor="gray"
/>
</Box>
}
>
<Text>{item.title}</Text>
</Box>
<Text color="gray">{getLorem(index)}</Text>
</Box>
)}
estimatedItemHeight={() => 14}
keyExtractor={(item) => item.id}
hasFocus={true}
initialScrollIndex={Number.MAX_SAFE_INTEGER}
initialScrollOffsetInIndex={Number.MAX_SAFE_INTEGER}
/>
</Box>
<Text>Count: {items.length}</Text>
</Box>
</ScrollProvider>
</KeypressProvider>
</MouseProvider>
);
};
// Enable mouse reporting before rendering
enableMouseEvents();
// Ensure cleanup happens on exit
process.on('exit', () => {
disableMouseEvents();
});
// Handle SIGINT explicitly to ensure cleanup runs if Ink doesn't catch it in time
process.on('SIGINT', () => {
process.exit(0);
});
render(<Demo />, { alternateBuffer: true });