@tavus/cvi-ui 0.0.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/.prettierrc.js +24 -0
  2. package/LICENSE +21 -0
  3. package/README.md +91 -0
  4. package/dev-components/components/cvi-provider/index.tsx +9 -0
  5. package/dev-components/hooks/README.md +499 -0
  6. package/dev-components/hooks/cvi-events-hooks.tsx +168 -0
  7. package/dev-components/hooks/use-cvi-call.tsx +24 -0
  8. package/dev-components/hooks/use-local-camera.tsx +20 -0
  9. package/dev-components/hooks/use-local-microphone.tsx +20 -0
  10. package/dev-components/hooks/use-local-screenshare.tsx +32 -0
  11. package/dev-components/hooks/use-remote-participant-ids.tsx +7 -0
  12. package/dev-components/hooks/use-replica-ids.tsx +9 -0
  13. package/dev-components/hooks/use-request-permissions.tsx +24 -0
  14. package/dev-components/hooks/use-start-haircheck.tsx +60 -0
  15. package/dist/index.js +237334 -0
  16. package/dist/types/cli/add.d.ts +20 -0
  17. package/dist/types/cli/info.d.ts +2 -0
  18. package/dist/types/cli/init.d.ts +23 -0
  19. package/dist/types/components/highlighter.d.ts +6 -0
  20. package/dist/types/components/logger.d.ts +8 -0
  21. package/dist/types/components/spinner.d.ts +4 -0
  22. package/dist/types/constants/components.d.ts +59 -0
  23. package/dist/types/constants/config.d.ts +5 -0
  24. package/dist/types/constants/errors.d.ts +5 -0
  25. package/dist/types/constants/frameworks.d.ts +39 -0
  26. package/dist/types/index.d.ts +1 -0
  27. package/dist/types/preflights/preflight-add.d.ts +15 -0
  28. package/dist/types/preflights/preflight-init.d.ts +9 -0
  29. package/dist/types/utils/add-components.d.ts +5 -0
  30. package/dist/types/utils/get-config.d.ts +51 -0
  31. package/dist/types/utils/get-package-info.d.ts +2 -0
  32. package/dist/types/utils/get-package-manager.d.ts +4 -0
  33. package/dist/types/utils/get-project-info.d.ts +10 -0
  34. package/dist/types/utils/handle-error.d.ts +1 -0
  35. package/dist/types/utils/resolve-components-tree.d.ts +4 -0
  36. package/dist/types/utils/resolve-import.d.ts +2 -0
  37. package/dist/types/utils/update-dependencies.d.ts +4 -0
  38. package/dist/types/utils/update-files.d.ts +14 -0
  39. package/dist/typescript-DhnEO4aV.js +12 -0
  40. package/dist/typescript-XxXP1Woc.js +14 -0
  41. package/eslint.config.js +12 -0
  42. package/package.json +67 -0
  43. package/prepare-scripts/convert-to-js.js +152 -0
  44. package/prepare-scripts/create-templates.js +265 -0
  45. package/rollup.config.js +28 -0
  46. package/src/cli/add.ts +124 -0
  47. package/src/cli/info.ts +21 -0
  48. package/src/cli/init.ts +131 -0
  49. package/src/components/highlighter.ts +8 -0
  50. package/src/components/logger.ts +22 -0
  51. package/src/components/spinner.ts +13 -0
  52. package/src/constants/config.ts +7 -0
  53. package/src/constants/errors.ts +5 -0
  54. package/src/constants/frameworks.ts +40 -0
  55. package/src/index.ts +26 -0
  56. package/src/preflights/preflight-add.ts +56 -0
  57. package/src/preflights/preflight-init.ts +77 -0
  58. package/src/utils/add-components.ts +52 -0
  59. package/src/utils/get-config.ts +60 -0
  60. package/src/utils/get-package-info.ts +14 -0
  61. package/src/utils/get-package-manager.ts +45 -0
  62. package/src/utils/get-project-info.ts +144 -0
  63. package/src/utils/handle-error.ts +34 -0
  64. package/src/utils/resolve-components-tree.ts +35 -0
  65. package/src/utils/update-dependencies.ts +37 -0
  66. package/src/utils/update-files.ts +212 -0
  67. package/tsconfig.json +23 -0
@@ -0,0 +1,265 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { parse } from '@babel/parser';
4
+ import traverse from '@babel/traverse';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+ const projectRoot = path.resolve(__dirname, '..');
10
+
11
+ const SKIP_DEPENDENCIES = new Set(['react', 'react-dom', '@daily-co/daily-react']);
12
+
13
+ // Function to extract imports from TSX file
14
+ function extractImports(content) {
15
+ const ast = parse(content, {
16
+ sourceType: 'module',
17
+ plugins: ['typescript', 'jsx'],
18
+ });
19
+
20
+ const dependencies = {
21
+ npm: new Set(),
22
+ local: new Set(),
23
+ };
24
+
25
+ traverse.default(ast, {
26
+ ImportDeclaration(path) {
27
+ const source = path.node.source.value;
28
+ // Skip default imports
29
+ if (SKIP_DEPENDENCIES.has(source)) {
30
+ return;
31
+ }
32
+ if (source.endsWith('.css')) {
33
+ return;
34
+ }
35
+ if (source.startsWith('.')) {
36
+ // Extract only the component name from the path
37
+ const componentName = source
38
+ .split('/')
39
+ .pop()
40
+ .replace(/\.(tsx|ts|jsx|js)$/, '');
41
+ dependencies.local.add(componentName);
42
+ } else {
43
+ dependencies.npm.add(source);
44
+ }
45
+ },
46
+ });
47
+
48
+ return dependencies;
49
+ }
50
+
51
+ // Function to get type from file path
52
+ function getTypeFromPath(filePath, templatesDir) {
53
+ const relativePath = path.relative(templatesDir, filePath);
54
+ const parts = relativePath.split(path.sep);
55
+ // The type is the first folder after templates
56
+ return parts[0];
57
+ }
58
+
59
+ // Function to process a TSX file and its associated CSS
60
+ function processTemplateFile(tsxPath, templatesDir) {
61
+ const content = fs.readFileSync(tsxPath, 'utf-8');
62
+ const dirPath = path.dirname(tsxPath);
63
+ let styles = '';
64
+
65
+ // Check if there's a CSS file in the same directory
66
+ const files = fs.readdirSync(dirPath);
67
+ const cssFile = files.find((file) => file.endsWith('.css'));
68
+
69
+ if (cssFile) {
70
+ const cssPath = path.join(dirPath, cssFile);
71
+ styles = fs.readFileSync(cssPath, 'utf-8');
72
+ }
73
+
74
+ const dependencies = extractImports(content);
75
+ const type = getTypeFromPath(tsxPath, templatesDir);
76
+
77
+ // Get component name from file path
78
+ const fileName = path.basename(tsxPath, path.extname(tsxPath));
79
+ const componentName = fileName === 'index' ? path.basename(path.dirname(tsxPath)) : fileName;
80
+
81
+ // Add cvi-provider to local dependencies for specific components
82
+ if (componentName === 'hair-check' || componentName === 'conversation') {
83
+ dependencies.local.add('cvi-provider');
84
+ }
85
+
86
+ const templateData = {
87
+ type,
88
+ content,
89
+ styles,
90
+ ...(dependencies.local.size > 0 && { componentsDependencies: Array.from(dependencies.local) }),
91
+ ...(dependencies.npm.size > 0 && { dependencies: Array.from(dependencies.npm) }),
92
+ };
93
+
94
+ return templateData;
95
+ }
96
+
97
+ // Function to create components list file
98
+ function createComponentsList(components, outputDir) {
99
+ const componentsList = components
100
+ .map((comp) => {
101
+ const name = path.basename(comp, path.extname(comp));
102
+ const relativePath = path
103
+ .relative(outputDir, comp)
104
+ .replace(/\\/g, '/')
105
+ .replace('.json', '')
106
+ .replace(/^tsx\//, '');
107
+ return ` { name: '${name}', path: '${relativePath}' }`;
108
+ })
109
+ .join(',\n');
110
+
111
+ const content = `export const components = [\n${componentsList}\n] as const;\n\nexport type Component = typeof components[number];\n`;
112
+
113
+ fs.writeFileSync(path.join(outputDir, 'components.ts'), content);
114
+ console.log('Created components list: components.ts');
115
+ }
116
+
117
+ // Function to create index file for components
118
+ function createComponentsIndex(components, outputDir) {
119
+ const exports = components
120
+ .map((comp) => {
121
+ const name = path.basename(comp, '.json');
122
+ const relativePath = path.relative(outputDir, comp).replace(/\\/g, '/');
123
+ return `export { default as "${name}" } from './${relativePath}';`;
124
+ })
125
+ .join('\n');
126
+
127
+ const content = `${exports}\n`;
128
+ const indexPath = path.join(outputDir, `index.ts`);
129
+ fs.writeFileSync(indexPath, content);
130
+ console.log(`Created index file: ${indexPath}`);
131
+ }
132
+
133
+ // Function to create main templates index file
134
+ function createTemplatesIndex(tsxDir, jsxDir, outputDir) {
135
+ const content = `export * as tsx from './tsx';\nexport * as jsx from './jsx';\n`;
136
+ const indexPath = path.join(outputDir, 'index.ts');
137
+ fs.writeFileSync(indexPath, content);
138
+ console.log(`Created main index file: ${indexPath}`);
139
+ }
140
+
141
+ // Main function to process all templates
142
+ async function convertTemplates() {
143
+ const tsxTemplatesDir = path.join(projectRoot, 'dev-components');
144
+ const jsxTemplatesDir = path.join(projectRoot, 'jsx-templates');
145
+ const outputDir = path.join(projectRoot, 'src', 'templates');
146
+ const tsxOutputDir = path.join(outputDir, 'tsx');
147
+ const jsxOutputDir = path.join(outputDir, 'jsx');
148
+ const tsxComponents = [];
149
+ const jsxComponents = [];
150
+
151
+ // Create output directories if they don't exist
152
+ if (!fs.existsSync(tsxOutputDir)) {
153
+ fs.mkdirSync(tsxOutputDir, { recursive: true });
154
+ }
155
+ if (!fs.existsSync(jsxOutputDir)) {
156
+ fs.mkdirSync(jsxOutputDir, { recursive: true });
157
+ }
158
+
159
+ // Process all TSX files in the dev-components directory
160
+ async function processTSXDirectory(dir) {
161
+ const items = fs.readdirSync(dir);
162
+
163
+ for (const item of items) {
164
+ const itemPath = path.join(dir, item);
165
+ const stat = fs.statSync(itemPath);
166
+
167
+ if (stat.isDirectory()) {
168
+ await processTSXDirectory(itemPath);
169
+ // TODO: Add support for .ts files
170
+ } else if (item.endsWith('.tsx')) {
171
+ const templateData = processTemplateFile(itemPath, tsxTemplatesDir);
172
+ const relativePath = path.relative(tsxTemplatesDir, itemPath);
173
+
174
+ // Check if this is an index file
175
+ const isIndexFile = path.basename(itemPath, path.extname(itemPath)) === 'index';
176
+
177
+ let outputPath;
178
+ if (isIndexFile) {
179
+ // For index files, create a file with the folder name instead of creating a folder
180
+ const parentDir = path.dirname(relativePath);
181
+ outputPath = path.join(tsxOutputDir, `${parentDir}.json`);
182
+ } else {
183
+ // For regular files, use the normal path structure
184
+ outputPath = path.join(tsxOutputDir, relativePath.replace('.tsx', '.json'));
185
+ }
186
+
187
+ // Ensure the output directory structure exists
188
+ const outputDirPath = path.dirname(outputPath);
189
+ if (!fs.existsSync(outputDirPath)) {
190
+ fs.mkdirSync(outputDirPath, { recursive: true });
191
+ }
192
+
193
+ fs.writeFileSync(outputPath, JSON.stringify(templateData, null, 2));
194
+ console.log(`Created TSX template: ${outputPath}`);
195
+
196
+ // Add to components list
197
+ tsxComponents.push(outputPath);
198
+ }
199
+ }
200
+ }
201
+
202
+ // Process all JSX files in the jsx-templates directory
203
+ async function processJSXDirectory(dir) {
204
+ const items = fs.readdirSync(dir);
205
+
206
+ for (const item of items) {
207
+ const itemPath = path.join(dir, item);
208
+ const stat = fs.statSync(itemPath);
209
+
210
+ if (stat.isDirectory()) {
211
+ await processJSXDirectory(itemPath);
212
+ } else if (item.endsWith('.jsx')) {
213
+ const templateData = processTemplateFile(itemPath, jsxTemplatesDir);
214
+ const relativePath = path.relative(jsxTemplatesDir, itemPath);
215
+
216
+ // Check if this is an index file
217
+ const isIndexFile = path.basename(itemPath, path.extname(itemPath)) === 'index';
218
+
219
+ let outputPath;
220
+ if (isIndexFile) {
221
+ // For index files, create a file with the folder name instead of creating a folder
222
+ const parentDir = path.dirname(relativePath);
223
+ outputPath = path.join(jsxOutputDir, `${parentDir}.json`);
224
+ } else {
225
+ // For regular files, use the normal path structure
226
+ outputPath = path.join(jsxOutputDir, relativePath.replace('.jsx', '.json'));
227
+ }
228
+
229
+ // Ensure the output directory structure exists
230
+ const outputDirPath = path.dirname(outputPath);
231
+ if (!fs.existsSync(outputDirPath)) {
232
+ fs.mkdirSync(outputDirPath, { recursive: true });
233
+ }
234
+
235
+ fs.writeFileSync(outputPath, JSON.stringify(templateData, null, 2));
236
+ console.log(`Created JSX template: ${outputPath}`);
237
+
238
+ // Add to components list
239
+ jsxComponents.push(outputPath);
240
+ }
241
+ }
242
+ }
243
+
244
+ // Process both TSX and JSX templates
245
+ if (fs.existsSync(tsxTemplatesDir)) {
246
+ await processTSXDirectory(tsxTemplatesDir);
247
+ // Create components list file only for TSX components
248
+ if (tsxComponents.length > 0) {
249
+ createComponentsList(tsxComponents, outputDir);
250
+ createComponentsIndex(tsxComponents, tsxOutputDir);
251
+ }
252
+ }
253
+
254
+ if (fs.existsSync(jsxTemplatesDir)) {
255
+ await processJSXDirectory(jsxTemplatesDir);
256
+ if (jsxComponents.length > 0) {
257
+ createComponentsIndex(jsxComponents, jsxOutputDir);
258
+ }
259
+ }
260
+
261
+ // Create main templates index file
262
+ createTemplatesIndex(tsxOutputDir, jsxOutputDir, outputDir);
263
+ }
264
+
265
+ convertTemplates().catch(console.error);
@@ -0,0 +1,28 @@
1
+ import resolve from '@rollup/plugin-node-resolve';
2
+ import commonjs from '@rollup/plugin-commonjs';
3
+ import typescript from '@rollup/plugin-typescript';
4
+ import json from '@rollup/plugin-json';
5
+
6
+ export default {
7
+ input: 'src/index.ts',
8
+ output: {
9
+ dir: 'dist',
10
+ entryFileNames: 'index.js',
11
+ format: 'es',
12
+ banner: '#!/usr/bin/env node',
13
+ },
14
+ external: [
15
+ 'react',
16
+ '@daily-co/daily-js',
17
+ 'daily-js',
18
+ 'npm-run-path',
19
+ 'unicorn-magic',
20
+ 'execa',
21
+ ],
22
+ plugins: [
23
+ json(),
24
+ resolve({ preferBuiltins: true }),
25
+ commonjs(),
26
+ typescript({ tsconfig: './tsconfig.json' }),
27
+ ],
28
+ };
package/src/cli/add.ts ADDED
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env node
2
+
3
+ import path from 'node:path';
4
+ import { preFlightAdd } from '@/src/preflights/preflight-add';
5
+ import { handleError } from '@/src/utils/handle-error';
6
+ import { highlighter } from '@/src/components/highlighter';
7
+ import { logger } from '@/src/components/logger';
8
+ import { z } from 'zod';
9
+ import { Command } from 'commander';
10
+ import prompts from 'prompts';
11
+ import * as ERRORS from '@/src/constants/errors';
12
+ import { CONFIG_NAME } from '@/src/constants/config';
13
+ import { runInit } from '@/src/cli/init';
14
+ import { addComponents } from '@/src/utils/add-components';
15
+ import { components as COMPONENTS } from '../templates/components';
16
+
17
+ export const addOptionsSchema = z.object({
18
+ components: z.array(z.string()).optional(),
19
+ overwrite: z.boolean(),
20
+ cwd: z.string(),
21
+ // TODO: Add path option
22
+ // path: z.string().optional(),
23
+ silent: z.boolean(),
24
+ });
25
+
26
+ export const add = new Command()
27
+ .name('add')
28
+ .description('add a CVI component to your project')
29
+ .argument('[components...]', 'the components to add.')
30
+ .option('-o, --overwrite', 'overwrite existing files.', false)
31
+ .option(
32
+ '-c, --cwd <cwd>',
33
+ 'the working directory. defaults to the current directory.',
34
+ process.cwd()
35
+ )
36
+ // TODO: Add path option
37
+ // .option('-p, --path <path>', 'the path to add the component to.')
38
+ .option('-s, --silent', 'mute output.', false)
39
+ .action(async (components, opts) => {
40
+ try {
41
+ const options = addOptionsSchema.parse({
42
+ components,
43
+ cwd: path.resolve(opts.cwd),
44
+ ...opts,
45
+ });
46
+
47
+ if (!options.components?.length) {
48
+ options.components = await showComponentsPrompt(options);
49
+ }
50
+
51
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, prefer-const
52
+ let { errors, config } = await preFlightAdd(options);
53
+
54
+ // No cvi-components.json file. Prompt the user to run init.
55
+ if (errors[ERRORS.MISSING_CONFIG]) {
56
+ const { proceed } = await prompts({
57
+ type: 'confirm',
58
+ name: 'proceed',
59
+ message: `You need to create a ${highlighter.info(
60
+ CONFIG_NAME
61
+ )} file to add components. Proceed?`,
62
+ initial: true,
63
+ });
64
+
65
+ if (!proceed) {
66
+ logger.break();
67
+ process.exit(1);
68
+ }
69
+
70
+ config = await runInit({
71
+ cwd: options.cwd,
72
+ components: undefined,
73
+ silent: options.silent,
74
+ });
75
+ }
76
+
77
+ if (errors[ERRORS.MISSING_DIR_OR_EMPTY_PROJECT]) {
78
+ logger.break();
79
+ process.exit(1);
80
+ }
81
+
82
+ if (!config) {
83
+ throw new Error(`Failed to read config at ${highlighter.info(options.cwd)}.`);
84
+ }
85
+
86
+ await addComponents(options.components, config, options);
87
+ } catch (error) {
88
+ logger.break();
89
+ handleError(error);
90
+ }
91
+ });
92
+
93
+ async function showComponentsPrompt(options: z.infer<typeof addOptionsSchema>) {
94
+ if (options.components?.length) {
95
+ return options.components;
96
+ }
97
+
98
+ const { components } = await prompts({
99
+ type: 'multiselect',
100
+ name: 'components',
101
+ message: 'Which components would you like to add?',
102
+ hint: 'Space to select. A to toggle all. Enter to submit.',
103
+ instructions: false,
104
+ choices: COMPONENTS.map((entry) => ({
105
+ title: entry.name,
106
+ value: entry.name,
107
+ selected: options.components?.includes(entry.name),
108
+ })),
109
+ });
110
+
111
+ if (!components?.length) {
112
+ logger.warn('No components selected. Exiting.');
113
+ logger.info('');
114
+ process.exit(1);
115
+ }
116
+
117
+ const result = z.array(z.string()).safeParse(components);
118
+ if (!result.success) {
119
+ logger.error('');
120
+ handleError(new Error('Something went wrong. Please try again.'));
121
+ return [];
122
+ }
123
+ return result.data;
124
+ }
@@ -0,0 +1,21 @@
1
+ import { getConfig } from '@/src/utils/get-config';
2
+ import { getProjectInfo } from '@/src/utils/get-project-info';
3
+ import { logger } from '@/src/components/logger';
4
+ import { Command } from 'commander';
5
+ import { CONFIG_NAME } from '../constants/config';
6
+
7
+ export const info = new Command()
8
+ .name('info')
9
+ .description('get information about your project')
10
+ .option(
11
+ '-c, --cwd <cwd>',
12
+ 'the working directory. defaults to the current directory.',
13
+ process.cwd()
14
+ )
15
+ .action(async (opts) => {
16
+ logger.info('> project info');
17
+ console.log(await getProjectInfo(opts.cwd));
18
+ logger.break();
19
+ logger.info(`> ${CONFIG_NAME}`);
20
+ console.log(await getConfig(opts.cwd));
21
+ });
@@ -0,0 +1,131 @@
1
+ import { promises as fs } from 'fs';
2
+ import path from 'node:path';
3
+ import { preFlightInit } from '@/src/preflights/preflight-init';
4
+ import { addComponents } from '@/src/utils/add-components';
5
+ import * as ERRORS from '@/src/constants/errors';
6
+ import {
7
+ getConfig,
8
+ rawConfigSchema,
9
+ resolveConfigPaths,
10
+ type Config,
11
+ } from '@/src/utils/get-config';
12
+ import { ProjectInfo } from '@/src/utils/get-project-info';
13
+ import { handleError } from '@/src/utils/handle-error';
14
+ import { highlighter } from '@/src/components/highlighter';
15
+ import { logger } from '@/src/components/logger';
16
+ import { spinner } from '@/src/components/spinner';
17
+ import { Command } from 'commander';
18
+ import prompts from 'prompts';
19
+ import { z } from 'zod';
20
+ import { CONFIG_NAME, DEFAULT_DEPENDENCIES } from '../constants/config';
21
+ import { updateDependencies } from '../utils/update-dependencies';
22
+
23
+ export const initOptionsSchema = z.object({
24
+ cwd: z.string(),
25
+ silent: z.boolean(),
26
+ components: z.array(z.string()).optional(),
27
+ });
28
+
29
+ export const init = new Command()
30
+ .name('init')
31
+ .description('initialize your project and install dependencies')
32
+ .option(
33
+ '-c, --cwd <cwd>',
34
+ 'the working directory. defaults to the current directory.',
35
+ process.cwd()
36
+ )
37
+ .argument('[components...]', 'the components to add.')
38
+ .option('-s, --silent', 'mute output.', false)
39
+ .action(async (components, opts) => {
40
+ try {
41
+ const options = initOptionsSchema.parse({
42
+ cwd: path.resolve(opts.cwd),
43
+ ...opts,
44
+ components,
45
+ });
46
+
47
+ await runInit(options);
48
+
49
+ logger.log(
50
+ `${highlighter.success(
51
+ 'Success!'
52
+ )} Project initialization completed.\nYou may now add components.`
53
+ );
54
+ logger.break();
55
+ } catch (error) {
56
+ logger.break();
57
+ handleError(error);
58
+ }
59
+ });
60
+
61
+ export async function runInit(options: z.infer<typeof initOptionsSchema>) {
62
+ let projectInfo;
63
+ const preflight = await preFlightInit(options);
64
+ if (preflight.errors[ERRORS.MISSING_DIR_OR_EMPTY_PROJECT]) {
65
+ process.exit(1);
66
+ } else {
67
+ projectInfo = preflight.projectInfo;
68
+ }
69
+
70
+ const config = projectInfo
71
+ ? await promptForMinimalConfig(projectInfo)
72
+ : await promptForConfig(await getConfig(options.cwd));
73
+
74
+ const { proceed } = await prompts({
75
+ type: 'confirm',
76
+ name: 'proceed',
77
+ message: `Write configuration to ${highlighter.info(CONFIG_NAME)}. Proceed?`,
78
+ initial: true,
79
+ });
80
+
81
+ if (!proceed) {
82
+ process.exit(0);
83
+ }
84
+
85
+ // Write cvi-components.json.
86
+ const componentSpinner = spinner(`Writing ${CONFIG_NAME}.`).start();
87
+ const targetPath = path.resolve(options.cwd, CONFIG_NAME);
88
+ await fs.writeFile(targetPath, JSON.stringify(config, null, 2), 'utf8');
89
+ componentSpinner.succeed();
90
+
91
+ const fullConfig = await resolveConfigPaths(options.cwd, config);
92
+
93
+ // Install default dependencies
94
+ await updateDependencies(DEFAULT_DEPENDENCIES, fullConfig, {
95
+ silent: options.silent,
96
+ });
97
+
98
+ // Add components.
99
+ if (options.components?.length) {
100
+ await addComponents(options.components, fullConfig, {
101
+ overwrite: true,
102
+ silent: options.silent,
103
+ });
104
+ }
105
+
106
+ return fullConfig;
107
+ }
108
+
109
+ async function promptForConfig(defaultConfig: Config | null = null) {
110
+ logger.info('');
111
+ const options = await prompts([
112
+ {
113
+ type: 'toggle',
114
+ name: 'typescript',
115
+ message: `Would you like to use ${highlighter.info('TypeScript')} (recommended)?`,
116
+ initial: defaultConfig?.tsx ?? true,
117
+ active: 'yes',
118
+ inactive: 'no',
119
+ },
120
+ ]);
121
+
122
+ return rawConfigSchema.parse({
123
+ tsx: options.typescript,
124
+ });
125
+ }
126
+
127
+ async function promptForMinimalConfig(defaultConfig: ProjectInfo) {
128
+ return rawConfigSchema.parse({
129
+ tsx: defaultConfig?.isTsx,
130
+ });
131
+ }
@@ -0,0 +1,8 @@
1
+ import { cyan, green, red, yellow } from "kleur/colors"
2
+
3
+ export const highlighter = {
4
+ error: red,
5
+ warn: yellow,
6
+ info: cyan,
7
+ success: green,
8
+ }
@@ -0,0 +1,22 @@
1
+ import { highlighter } from "./highlighter"
2
+
3
+ export const logger = {
4
+ error(...args: unknown[]) {
5
+ console.log(highlighter.error(args.join(" ")))
6
+ },
7
+ warn(...args: unknown[]) {
8
+ console.log(highlighter.warn(args.join(" ")))
9
+ },
10
+ info(...args: unknown[]) {
11
+ console.log(highlighter.info(args.join(" ")))
12
+ },
13
+ success(...args: unknown[]) {
14
+ console.log(highlighter.success(args.join(" ")))
15
+ },
16
+ log(...args: unknown[]) {
17
+ console.log(args.join(" "))
18
+ },
19
+ break() {
20
+ console.log("")
21
+ },
22
+ }
@@ -0,0 +1,13 @@
1
+ import ora, { type Options } from "ora"
2
+
3
+ export function spinner(
4
+ text: Options["text"],
5
+ options?: {
6
+ silent?: boolean
7
+ }
8
+ ) {
9
+ return ora({
10
+ text,
11
+ isSilent: options?.silent,
12
+ })
13
+ }
@@ -0,0 +1,7 @@
1
+ export const CONFIG = {
2
+ tsx: true,
3
+ };
4
+
5
+ export const CONFIG_NAME = 'cvi-components.json';
6
+
7
+ export const DEFAULT_DEPENDENCIES = ['@daily-co/daily-react', '@daily-co/daily-js', 'jotai'];
@@ -0,0 +1,5 @@
1
+ export const MISSING_DIR_OR_EMPTY_PROJECT = "1"
2
+ export const EXISTING_CONFIG = "2"
3
+ export const MISSING_CONFIG = "3"
4
+ export const FAILED_CONFIG_READ = "4"
5
+ export const UNSUPPORTED_FRAMEWORK = "7"
@@ -0,0 +1,40 @@
1
+ export const FRAMEWORKS = {
2
+ 'next-app': {
3
+ name: 'next-app',
4
+ label: 'Next.js',
5
+ },
6
+ 'next-pages': {
7
+ name: 'next-pages',
8
+ label: 'Next.js',
9
+ },
10
+ remix: {
11
+ name: 'remix',
12
+ label: 'Remix',
13
+ },
14
+ 'react-router': {
15
+ name: 'react-router',
16
+ label: 'React Router',
17
+ },
18
+ vite: {
19
+ name: 'vite',
20
+ label: 'Vite',
21
+ },
22
+ astro: {
23
+ name: 'astro',
24
+ label: 'Astro',
25
+ },
26
+ 'tanstack-start': {
27
+ name: 'tanstack-start',
28
+ label: 'TanStack Start',
29
+ },
30
+ gatsby: {
31
+ name: 'gatsby',
32
+ label: 'Gatsby',
33
+ },
34
+ manual: {
35
+ name: 'manual',
36
+ label: 'Manual',
37
+ },
38
+ } as const;
39
+
40
+ export type Framework = (typeof FRAMEWORKS)[keyof typeof FRAMEWORKS];
package/src/index.ts ADDED
@@ -0,0 +1,26 @@
1
+ import { init } from '@/src/cli/init';
2
+ import { add } from '@/src/cli/add';
3
+ import { info } from '@/src/cli/info';
4
+ import { Command } from 'commander';
5
+
6
+ import packageJson from '../package.json';
7
+
8
+ process.on('SIGINT', () => process.exit(0));
9
+ process.on('SIGTERM', () => process.exit(0));
10
+
11
+ async function main() {
12
+ const program = new Command()
13
+ .name('tavus-cvi')
14
+ .description('A CLI tool for installing and managing CVI components')
15
+ .version(
16
+ packageJson.version || '0.0.1',
17
+ '-v, --version',
18
+ 'display the version number'
19
+ );
20
+
21
+ program.addCommand(init).addCommand(add).addCommand(info);
22
+
23
+ program.parse();
24
+ }
25
+
26
+ main();