inconvo 1.1.2 → 1.2.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.
package/README.md CHANGED
@@ -13,9 +13,12 @@ npx inconvo@latest add assistant-ui-tool-components
13
13
  Options:
14
14
 
15
15
  - `--cwd <path>` – run the installer against a different project directory
16
- - `--path <relative>` – override the target folder (defaults to `src/components/assistant-ui/tools`)
16
+ - `--path <relative>` – override the root folder used for file output (defaults to `src`)
17
17
  - `--overwrite` – replace any files that already exist without prompting
18
18
  - `--yes` – answer "yes" to interactive prompts
19
+ - `--skip-install` – copy files only and skip dependency installation
20
+
21
+ By default the installer writes everything under `src/`, including the tool components and their supporting `src/lib/inconvo/types.ts`. It also installs external dependencies such as `recharts` and `@tanstack/react-table` unless you opt out with `--skip-install`.
19
22
 
20
23
  ## Development
21
24
 
@@ -3,6 +3,7 @@ import path from "node:path";
3
3
  import fs from "node:fs/promises";
4
4
  import { logger } from "../lib/logger.js";
5
5
  import { confirmPrompt } from "../lib/prompt.js";
6
+ import { installDependencies } from "../lib/install-dependencies.js";
6
7
  import { componentPacks, getPack } from "../templates/packs.js";
7
8
  export const addCommand = new Command()
8
9
  .name("add")
@@ -12,6 +13,7 @@ export const addCommand = new Command()
12
13
  .option("-o, --overwrite", "overwrite existing files without prompting", false)
13
14
  .option("-c, --cwd <cwd>", "working directory that will receive the files", process.cwd())
14
15
  .option("-p, --path <path>", "relative path where the pack should be written")
16
+ .option("--skip-install", "skip installing npm dependencies", false)
15
17
  .action(async (packName, opts) => {
16
18
  const pack = getPack(packName);
17
19
  if (!pack) {
@@ -68,6 +70,24 @@ export const addCommand = new Command()
68
70
  if (replaced.size) {
69
71
  logger.info(`Overwrote ${replaced.size} file(s).`);
70
72
  }
73
+ if (pack.dependencies) {
74
+ const depList = Object.entries(pack.dependencies).map(([name, version]) => `${name}@${version}`);
75
+ if (!depList.length)
76
+ return;
77
+ if (opts.skipInstall) {
78
+ logger.warn(`Skipping dependency installation (--skip-install). Please install manually:\n ${depList.join(" ")}`);
79
+ }
80
+ else {
81
+ try {
82
+ await installDependencies(pack.dependencies, cwd);
83
+ logger.success("Dependencies installed.");
84
+ }
85
+ catch (error) {
86
+ logger.error(`Failed to install dependencies automatically. Please install manually: ${depList.join(" ")}`);
87
+ logger.error(error?.message ?? String(error));
88
+ }
89
+ }
90
+ }
71
91
  logger.success(`Installed ${pack.name}!`);
72
92
  });
73
93
  async function collectFiles(dir) {
@@ -0,0 +1,38 @@
1
+ import { spawn } from "node:child_process";
2
+ import { detect } from "detect-package-manager";
3
+ import { logger } from "./logger.js";
4
+ const installArgsMap = {
5
+ npm: ["install", "--save"],
6
+ pnpm: ["add"],
7
+ yarn: ["add"],
8
+ bun: ["add"],
9
+ };
10
+ export async function installDependencies(dependencies, cwd) {
11
+ const depList = Object.entries(dependencies).map(([name, version]) => version ? `${name}@${version}` : name);
12
+ if (!depList.length)
13
+ return;
14
+ const packageManager = await detect({ cwd });
15
+ const args = installArgsMap[packageManager]
16
+ ? [...installArgsMap[packageManager], ...depList]
17
+ : ["install", ...depList];
18
+ logger.step(`Installing dependencies with ${packageManager}: ${depList.join(", ")}`);
19
+ await runCommand(packageManager, args, cwd);
20
+ }
21
+ function runCommand(command, args, cwd) {
22
+ return new Promise((resolve, reject) => {
23
+ const child = spawn(command, args, {
24
+ cwd,
25
+ stdio: "inherit",
26
+ shell: process.platform === "win32",
27
+ });
28
+ child.on("close", (code) => {
29
+ if (code === 0) {
30
+ resolve();
31
+ }
32
+ else {
33
+ reject(new Error(`${command} exited with code ${code}`));
34
+ }
35
+ });
36
+ child.on("error", (error) => reject(error));
37
+ });
38
+ }
@@ -3,8 +3,12 @@ export const componentPacks = {
3
3
  "assistant-ui-tool-components": {
4
4
  name: "assistant-ui-tool-components",
5
5
  description: "Inconvo-flavored assistant-ui tools and supporting components.",
6
- templateDir: resolveFromRoot("templates", "assistant-ui", "tools"),
7
- targetDir: "src/components/assistant-ui/tools",
6
+ templateDir: resolveFromRoot("templates", "assistant-ui", "src"),
7
+ targetDir: "src",
8
+ dependencies: {
9
+ recharts: "^3.5.1",
10
+ "@tanstack/react-table": "^8.21.3",
11
+ },
8
12
  },
9
13
  };
10
14
  export function getPack(name) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inconvo",
3
- "version": "1.1.2",
3
+ "version": "1.2.1",
4
4
  "description": "CLI for installing Inconvo assistant-ui tool components into any project.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -18,7 +18,8 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "chalk": "^5.3.0",
21
- "commander": "^14.0.0"
21
+ "commander": "^14.0.0",
22
+ "detect-package-manager": "^3.0.2"
22
23
  },
23
24
  "devDependencies": {
24
25
  "@semantic-release/commit-analyzer": "^13.0.1",
@@ -0,0 +1,15 @@
1
+ const chartColorVars = [
2
+ "var(--chart-1)",
3
+ "var(--chart-2)",
4
+ "var(--chart-3)",
5
+ "var(--chart-4)",
6
+ "var(--chart-5)",
7
+ ];
8
+
9
+ export const buildChartPalette = (seriesCount: number) => {
10
+ if (seriesCount <= 0) return [];
11
+ return Array.from({ length: seriesCount }, (_, index) => {
12
+ const colorIndex = index % chartColorVars.length;
13
+ return chartColorVars[colorIndex] ?? "var(--chart-series-primary)";
14
+ });
15
+ };
@@ -1,7 +1,6 @@
1
1
  "use client";
2
2
 
3
3
  import { useMemo, type ReactNode } from "react";
4
- import { useTheme } from "next-themes";
5
4
  import {
6
5
  ResponsiveContainer,
7
6
  LineChart as RechartsLineChart,
@@ -17,7 +16,7 @@ import {
17
16
  } from "recharts";
18
17
 
19
18
  import type { InconvoChartData, InconvoChartType } from "~/lib/inconvo/types";
20
- import { buildGreyscalePalette } from "~/components/assistant-ui/tools/inconvo-chart-colors";
19
+ import { buildChartPalette } from "~/components/assistant-ui/tools/inconvo-chart-colors";
21
20
 
22
21
  interface InconvoChartProps {
23
22
  data: InconvoChartData;
@@ -107,8 +106,6 @@ export const InconvoChart = ({
107
106
  xLabel,
108
107
  yLabel,
109
108
  }: InconvoChartProps) => {
110
- const { resolvedTheme } = useTheme();
111
-
112
109
  const chartData = useMemo(() => {
113
110
  return data.labels.map((label, index) => {
114
111
  const row: { name: string; [key: string]: string | number } = {
@@ -121,10 +118,10 @@ export const InconvoChart = ({
121
118
  });
122
119
  }, [data]);
123
120
 
124
- const palette = useMemo(() => {
125
- const isDarkMode = resolvedTheme === "dark";
126
- return buildGreyscalePalette(data.datasets.length, isDarkMode);
127
- }, [data.datasets.length, resolvedTheme]);
121
+ const palette = useMemo(
122
+ () => buildChartPalette(data.datasets.length),
123
+ [data.datasets.length],
124
+ );
128
125
 
129
126
  const axisColor = "var(--muted-foreground)";
130
127
  const textColor = "var(--foreground)";
@@ -0,0 +1,118 @@
1
+ export type InconvoChartType = "bar" | "line";
2
+
3
+ export interface InconvoChartDataset {
4
+ name: string;
5
+ values: number[];
6
+ }
7
+
8
+ export interface InconvoChartData {
9
+ labels: string[];
10
+ datasets: InconvoChartDataset[];
11
+ }
12
+
13
+ export interface InconvoChart {
14
+ data: InconvoChartData;
15
+ title?: string;
16
+ xLabel?: string;
17
+ yLabel?: string;
18
+ type: InconvoChartType;
19
+ }
20
+
21
+ export interface InconvoTable {
22
+ head: string[];
23
+ body: string[][];
24
+ }
25
+
26
+ export type InconvoResponseType = "text" | "chart" | "table";
27
+
28
+ export interface InconvoResponse {
29
+ id?: string;
30
+ conversationId?: string;
31
+ message: string;
32
+ type: InconvoResponseType;
33
+ chart?: InconvoChart;
34
+ table?: InconvoTable;
35
+ }
36
+
37
+ const isStringArray = (value: unknown): value is string[] => {
38
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
39
+ };
40
+
41
+ const isNumberArray = (value: unknown): value is number[] => {
42
+ return Array.isArray(value) && value.every((item) => typeof item === "number");
43
+ };
44
+
45
+ const isDataset = (value: unknown): value is InconvoChartDataset => {
46
+ return (
47
+ typeof value === "object" &&
48
+ value !== null &&
49
+ typeof (value as InconvoChartDataset).name === "string" &&
50
+ isNumberArray((value as InconvoChartDataset).values)
51
+ );
52
+ };
53
+
54
+ const isChartData = (value: unknown): value is InconvoChartData => {
55
+ return (
56
+ typeof value === "object" &&
57
+ value !== null &&
58
+ isStringArray((value as InconvoChartData).labels) &&
59
+ Array.isArray((value as InconvoChartData).datasets) &&
60
+ (value as InconvoChartData).datasets.every(isDataset)
61
+ );
62
+ };
63
+
64
+ const isChart = (value: unknown): value is InconvoChart => {
65
+ return (
66
+ typeof value === "object" &&
67
+ value !== null &&
68
+ ((value as InconvoChart).type === "bar" || (value as InconvoChart).type === "line") &&
69
+ isChartData((value as InconvoChart).data)
70
+ );
71
+ };
72
+
73
+ const isTable = (value: unknown): value is InconvoTable => {
74
+ return (
75
+ typeof value === "object" &&
76
+ value !== null &&
77
+ isStringArray((value as InconvoTable).head) &&
78
+ Array.isArray((value as InconvoTable).body) &&
79
+ (value as InconvoTable).body.every(isStringArray)
80
+ );
81
+ };
82
+
83
+ const isInconvoResponse = (value: unknown): value is InconvoResponse => {
84
+ if (typeof value !== "object" || value === null) {
85
+ return false;
86
+ }
87
+
88
+ const candidate = value as InconvoResponse;
89
+ if (typeof candidate.message !== "string") {
90
+ return false;
91
+ }
92
+
93
+ if (candidate.type !== "text" && candidate.type !== "chart" && candidate.type !== "table") {
94
+ return false;
95
+ }
96
+
97
+ if (candidate.type === "chart" && !isChart(candidate.chart)) {
98
+ return false;
99
+ }
100
+
101
+ if (candidate.type === "table" && !isTable(candidate.table)) {
102
+ return false;
103
+ }
104
+
105
+ return true;
106
+ };
107
+
108
+ export const parseInconvoResponse = (
109
+ value: unknown,
110
+ ): InconvoResponse | null => {
111
+ try {
112
+ const parsed: unknown =
113
+ typeof value === "string" ? JSON.parse(value) : value;
114
+ return isInconvoResponse(parsed) ? parsed : null;
115
+ } catch {
116
+ return null;
117
+ }
118
+ };
@@ -1,27 +0,0 @@
1
- const clamp = (value: number, min = 0, max = 1) =>
2
- Math.min(Math.max(value, min), max);
3
-
4
- const lightnessToGrayHex = (lightness: number) => {
5
- const channel = Math.round(clamp(lightness) * 255)
6
- .toString(16)
7
- .padStart(2, "0");
8
- return `#${channel}${channel}${channel}`;
9
- };
10
-
11
- export const buildGreyscalePalette = (
12
- seriesCount: number,
13
- isDarkMode: boolean,
14
- ) => {
15
- if (seriesCount <= 0) return [];
16
- if (seriesCount === 1) {
17
- return [lightnessToGrayHex(isDarkMode ? 0.78 : 0.5)];
18
- }
19
-
20
- const start = isDarkMode ? 0.95 : 0.8;
21
- const end = isDarkMode ? 0.55 : 0.15;
22
- const step = (end - start) / (seriesCount - 1);
23
-
24
- return Array.from({ length: seriesCount }, (_, index) =>
25
- lightnessToGrayHex(start + step * index),
26
- );
27
- };