@tscircuit/ti 1.0.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 ADDED
@@ -0,0 +1,54 @@
1
+ # @tscircuit/ti
2
+
3
+ Texas Instruments chip components and reusable reference subcircuits for
4
+ [tscircuit](https://github.com/tscircuit/ti).
5
+
6
+ ## Install in a circuit project
7
+
8
+ ```sh
9
+ npm install @tscircuit/ti tscircuit react
10
+ ```
11
+
12
+ ```tsx
13
+ import { BQ24074, PowerMonitor_INA237 } from "@tscircuit/ti";
14
+
15
+ export default () => (
16
+ <board width="30mm" height="20mm">
17
+ <BQ24074 name="U1" />
18
+ <PowerMonitor_INA237 name="Monitor" />
19
+ </board>
20
+ );
21
+ ```
22
+
23
+ The package includes ESM, CommonJS, and TypeScript declarations. Node.js 22.14
24
+ or newer is required. React, tscircuit, and `@tscircuit/props` are peer dependencies so the library
25
+ uses the same runtime as your circuit project.
26
+
27
+ ## Global installation
28
+
29
+ ```sh
30
+ npm install -g @tscircuit/ti
31
+ ti search "buck converter"
32
+ ti search --json "buck converter"
33
+ ti import TPS62160DSGR
34
+ ```
35
+
36
+ The `ti search` command searches the same Texas Instruments catalog as
37
+ `tsci search --ti`, returning up to 10 components. Queries can be quoted or
38
+ passed as separate words. Use `--json` for `{ query, results }` output, including
39
+ TI metadata and `source: "ti"` on every result, or `ti --help` for usage.
40
+
41
+ `ti import TPS62160DSGR` finds the exact manufacturer part in LCSC/JLCPCB,
42
+ converts its EasyEDA symbol and footprint, and writes `imports/TPS62160DSGR.tsx`.
43
+ You can also use an LCSC ID directly: `ti import C324077`.
44
+ Use the result with `import { TPS62160DSGR } from "./imports/TPS62160DSGR"`.
45
+ Existing files are never overwritten. Imports require an internet connection
46
+ and an available EasyEDA part. They contain individual chips, not complete
47
+ reference subcircuits; available 3D models remain remote links.
48
+
49
+ Global installation does not make imports resolve in local projects; install
50
+ the package in each project where you use its components.
51
+
52
+ See the [library documentation](https://github.com/tscircuit/ti#readme) for the
53
+ available chips, reference subcircuits, and connection examples. Examples using
54
+ `@tsci/tscircuit.ti` have the same exports; use `@tscircuit/ti` for npm imports.
package/cli/import.mjs ADDED
@@ -0,0 +1,104 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { parseArgs } from "node:util";
4
+ import {
5
+ convertRawEasyEdaToTs,
6
+ EasyEdaJsonSchema,
7
+ fetchEasyEDAComponent,
8
+ normalizeManufacturerPartNumber,
9
+ } from "easyeda";
10
+
11
+ const help = `Usage: ti import <part-number>
12
+
13
+ Import an EasyEDA chip as editable TSX into ./imports/.
14
+ Use an exact manufacturer part number or an LCSC ID (C-number).
15
+ Existing files are not overwritten.
16
+
17
+ Examples:
18
+ ti import TPS62160DSGR
19
+ ti import C324077`;
20
+
21
+ export async function runImport(args, { fetch, stdout, stderr, cwd }) {
22
+ try {
23
+ const { values, positionals } = parseArgs({
24
+ args,
25
+ allowPositionals: true,
26
+ options: { help: { type: "boolean", short: "h" } },
27
+ });
28
+ if (values.help) {
29
+ stdout(help);
30
+ return 0;
31
+ }
32
+ if (positionals.length !== 1 || !positionals[0].trim()) {
33
+ throw new Error(
34
+ "An exact part number is required. Usage: ti import <part-number>",
35
+ );
36
+ }
37
+
38
+ const query = positionals[0].trim();
39
+ const directLcsc = /^C[1-9]\d*$/i.test(query);
40
+ let lcscPartNumber = query.toUpperCase();
41
+ if (!directLcsc) {
42
+ const response = await fetch(
43
+ "https://jlcsearch.tscircuit.com/api/search?limit=10&q=" +
44
+ encodeURIComponent(query),
45
+ );
46
+ if (!response.ok)
47
+ throw new Error(`LCSC search failed (HTTP ${response.status})`);
48
+ const data = await response.json();
49
+ if (!Array.isArray(data?.components))
50
+ throw new Error("LCSC search returned an invalid response");
51
+ const match = data.components.find(
52
+ (part) =>
53
+ part?.mfr?.trim().toUpperCase() === query.toUpperCase() &&
54
+ Number.isSafeInteger(part.lcsc) &&
55
+ part.lcsc > 0,
56
+ );
57
+ if (!match) {
58
+ throw new Error(
59
+ `No exact LCSC match for "${query}". Use the full manufacturer part number or an LCSC ID (C-number).`,
60
+ );
61
+ }
62
+ lcscPartNumber = `C${match.lcsc}`;
63
+ }
64
+
65
+ const rawEasy = await fetchEasyEDAComponent(lcscPartNumber, { fetch });
66
+ const component = EasyEdaJsonSchema.parse(rawEasy);
67
+ const manufacturerPart = component.dataStr.head.c_para["Manufacturer Part"];
68
+ if (
69
+ !manufacturerPart?.trim() ||
70
+ component.lcsc.number.toUpperCase() !== lcscPartNumber ||
71
+ (!directLcsc &&
72
+ manufacturerPart.trim().toUpperCase() !== query.toUpperCase())
73
+ ) {
74
+ throw new Error(
75
+ `EasyEDA returned a different or missing part identity for "${query}"`,
76
+ );
77
+ }
78
+
79
+ const componentName = normalizeManufacturerPartNumber(manufacturerPart);
80
+ const tsx = await convertRawEasyEdaToTs({ rawEasy });
81
+ const directory = join(cwd, "imports");
82
+ const filename = `${componentName}.tsx`;
83
+ await mkdir(directory, { recursive: true });
84
+ try {
85
+ await writeFile(join(directory, filename), `${tsx}\n`, { flag: "wx" });
86
+ } catch (error) {
87
+ if (error?.code === "EEXIST")
88
+ throw new Error(
89
+ `imports/${filename} already exists; no files were overwritten.`,
90
+ );
91
+ throw error;
92
+ }
93
+ stdout(`Imported imports/${filename} from EasyEDA (${lcscPartNumber}).`);
94
+ stdout(
95
+ `Use: import { ${componentName} } from "./imports/${componentName}"`,
96
+ );
97
+ return 0;
98
+ } catch (error) {
99
+ stderr(
100
+ `Failed to import: ${error instanceof Error ? error.message : String(error)}`,
101
+ );
102
+ return 1;
103
+ }
104
+ }
package/cli/main.mjs ADDED
@@ -0,0 +1,111 @@
1
+ import { parseArgs } from "node:util";
2
+
3
+ const help = `Usage: ti search [options] <query...>
4
+ ti import <part-number>
5
+
6
+ Search Texas Instruments components or import a chip from EasyEDA.
7
+
8
+ Options:
9
+ --json Output search results as JSON
10
+ -h, --help Show help
11
+
12
+ Examples:
13
+ ti search "buck converter"
14
+ ti search --json TPS62160
15
+ ti import TPS62160DSGR
16
+ ti import C324077`;
17
+
18
+ export async function runCli(
19
+ argv,
20
+ {
21
+ fetch = globalThis.fetch,
22
+ stdout = console.log,
23
+ stderr = console.error,
24
+ cwd = process.cwd(),
25
+ } = {},
26
+ ) {
27
+ const [command, ...args] = argv;
28
+ if (!command || command === "--help" || command === "-h") {
29
+ stdout(help);
30
+ return 0;
31
+ }
32
+ if (command === "import") {
33
+ const { runImport } = await import("./import.mjs");
34
+ return runImport(args, { fetch, stdout, stderr, cwd });
35
+ }
36
+ if (command !== "search") {
37
+ stderr(`Unknown command "${command}". Run ti --help for usage.`);
38
+ return 1;
39
+ }
40
+
41
+ let parsed;
42
+ try {
43
+ parsed = parseArgs({
44
+ args,
45
+ allowPositionals: true,
46
+ options: {
47
+ json: { type: "boolean" },
48
+ help: { type: "boolean", short: "h" },
49
+ },
50
+ });
51
+ } catch (error) {
52
+ stderr(error instanceof Error ? error.message : String(error));
53
+ return 1;
54
+ }
55
+ if (parsed.values.help) {
56
+ stdout(help);
57
+ return 0;
58
+ }
59
+
60
+ const query = parsed.positionals.join(" ").trim();
61
+ if (!query) {
62
+ stderr("A search query is required. Usage: ti search [options] <query...>");
63
+ return 1;
64
+ }
65
+
66
+ try {
67
+ const response = await fetch(
68
+ "https://tisearch.tscircuit.com/api/search?limit=10&q=" +
69
+ encodeURIComponent(query),
70
+ );
71
+ if (!response.ok)
72
+ throw new Error(`TI search failed (HTTP ${response.status})`);
73
+ const data = await response.json();
74
+ if (!Array.isArray(data?.components))
75
+ throw new Error("TI search returned an invalid response");
76
+
77
+ if (parsed.values.json) {
78
+ stdout(
79
+ JSON.stringify(
80
+ {
81
+ query,
82
+ results: data.components.map((component) => ({
83
+ ...component,
84
+ source: "ti",
85
+ })),
86
+ },
87
+ null,
88
+ 2,
89
+ ),
90
+ );
91
+ } else if (!data.components.length) {
92
+ stdout(`No results found for "${query}" in Texas Instruments.`);
93
+ } else {
94
+ stdout(
95
+ [
96
+ `Found ${data.components.length} component(s) in TI search:`,
97
+ ...data.components.map(
98
+ (component, index) =>
99
+ `${index + 1}. ${component.mfr} - ${component.description} (stock: ${component.stock.toLocaleString("en-US")})`,
100
+ ),
101
+ ].join("\n"),
102
+ );
103
+ }
104
+ return 0;
105
+ } catch (error) {
106
+ stderr(
107
+ `Failed to search: ${error instanceof Error ? error.message : String(error)}`,
108
+ );
109
+ return 1;
110
+ }
111
+ }
package/cli/ti.mjs ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from "./main.mjs";
3
+
4
+ process.exitCode = await runCli(process.argv.slice(2));