create-izi-noir 0.2.2 → 0.2.6

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 (2) hide show
  1. package/dist/index.js +1185 -131
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { Command } from "commander";
6
6
  // src/commands/init.ts
7
7
  import path2 from "path";
8
8
  import { execSync } from "child_process";
9
- import pc4 from "picocolors";
9
+ import pc3 from "picocolors";
10
10
 
11
11
  // src/prompts/project.ts
12
12
  import prompts from "prompts";
@@ -17,8 +17,7 @@ var TEMPLATES = [
17
17
  { title: "Balance Proof only", value: "balance-proof" }
18
18
  ];
19
19
  var NETWORKS = [
20
- { title: "Solana (recommended)", value: "solana", description: "Uses Groth16 for on-chain verification" },
21
- { title: "EVM (Ethereum, Base, etc.)", value: "evm", description: "Uses UltraHonk proofs" }
20
+ { title: "Solana", value: "solana", description: "Uses Groth16 for on-chain verification" }
22
21
  ];
23
22
  function networkToProvider(network) {
24
23
  return network === "solana" ? "arkworks" : "barretenberg";
@@ -69,6 +68,16 @@ async function promptProjectOptions(defaults) {
69
68
  name: "initGit",
70
69
  message: "Initialize git repository?",
71
70
  initial: !defaults.skipGit
71
+ },
72
+ {
73
+ type: "select",
74
+ name: "aiTool",
75
+ message: "Which AI coding assistant do you use?",
76
+ choices: [
77
+ { title: "Claude Code", value: "claude", description: "Install IZI-NOIR circuit patterns skill" },
78
+ { title: "None / Other", value: "none", description: "Skip AI assistant configuration" }
79
+ ],
80
+ initial: 0
72
81
  }
73
82
  );
74
83
  try {
@@ -83,7 +92,8 @@ async function promptProjectOptions(defaults) {
83
92
  template: response.template || defaults.template || "default",
84
93
  provider: networkToProvider(network),
85
94
  skipInstall: response.installDeps === false,
86
- skipGit: response.initGit === false
95
+ skipGit: response.initGit === false,
96
+ aiTool: response.aiTool || "none"
87
97
  };
88
98
  } catch {
89
99
  return null;
@@ -117,44 +127,8 @@ async function isDirectoryEmpty(dir) {
117
127
  }
118
128
  }
119
129
 
120
- // src/utils/spinner.ts
121
- import pc2 from "picocolors";
122
- var frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
123
- var Spinner = class {
124
- message;
125
- interval = null;
126
- frameIndex = 0;
127
- constructor(message) {
128
- this.message = message;
129
- }
130
- start() {
131
- process.stdout.write("\x1B[?25l");
132
- this.interval = setInterval(() => {
133
- const frame = pc2.cyan(frames[this.frameIndex]);
134
- process.stdout.write(`\r${frame} ${this.message}`);
135
- this.frameIndex = (this.frameIndex + 1) % frames.length;
136
- }, 80);
137
- }
138
- stop(success = true) {
139
- if (this.interval) {
140
- clearInterval(this.interval);
141
- this.interval = null;
142
- }
143
- process.stdout.write("\x1B[?25h");
144
- const icon = success ? pc2.green("\u2713") : pc2.red("\u2717");
145
- process.stdout.write(`\r${icon} ${this.message}
146
- `);
147
- }
148
- update(message) {
149
- this.message = message;
150
- }
151
- };
152
- function createSpinner(message) {
153
- return new Spinner(message);
154
- }
155
-
156
130
  // src/utils/progress.ts
157
- import pc3 from "picocolors";
131
+ import pc2 from "picocolors";
158
132
  var THINKING_PHRASES = [
159
133
  "Initializing ZK environment",
160
134
  "Configuring proof system",
@@ -191,15 +165,15 @@ var ProgressReporter = class {
191
165
  this.showThinking();
192
166
  }
193
167
  showThinking() {
194
- const frames2 = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
168
+ const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
195
169
  let frameIndex = 0;
196
170
  let dotCount = 0;
197
171
  this.thinkingInterval = setInterval(() => {
198
- const frame = pc3.cyan(frames2[frameIndex]);
172
+ const frame = pc2.cyan(frames[frameIndex]);
199
173
  const phrase = THINKING_PHRASES[this.thinkingIndex % THINKING_PHRASES.length];
200
174
  const dots = ".".repeat(dotCount % 4);
201
- process.stdout.write(`\r${frame} ${pc3.dim(phrase)}${dots} `);
202
- frameIndex = (frameIndex + 1) % frames2.length;
175
+ process.stdout.write(`\r${frame} ${pc2.dim(phrase)}${dots} `);
176
+ frameIndex = (frameIndex + 1) % frames.length;
203
177
  dotCount++;
204
178
  if (dotCount % 12 === 0) {
205
179
  this.thinkingIndex++;
@@ -215,29 +189,29 @@ var ProgressReporter = class {
215
189
  }
216
190
  async reportFile(filename, isLast = false) {
217
191
  const icon = getFileIcon(filename);
218
- const line = ` ${icon} ${pc3.dim("created")} ${pc3.white(filename)}`;
219
- process.stdout.write(" " + icon + " " + pc3.dim("created") + " ");
192
+ const line = ` ${icon} ${pc2.dim("created")} ${pc2.white(filename)}`;
193
+ process.stdout.write(" " + icon + " " + pc2.dim("created") + " ");
220
194
  for (const char of filename) {
221
- process.stdout.write(pc3.white(char));
195
+ process.stdout.write(pc2.white(char));
222
196
  await sleep(8 + Math.random() * 12);
223
197
  }
224
198
  process.stdout.write("\n");
225
199
  this.currentLine = line;
226
200
  }
227
201
  async reportDirectory(dirname) {
228
- process.stdout.write(` \u{1F4C1} ${pc3.dim("mkdir")} ${pc3.blue(dirname)}/
202
+ process.stdout.write(` \u{1F4C1} ${pc2.dim("mkdir")} ${pc2.blue(dirname)}/
229
203
  `);
230
204
  await sleep(30);
231
205
  }
232
206
  showSuccess(message) {
233
207
  process.stdout.write("\x1B[?25h");
234
208
  console.log();
235
- console.log(pc3.green("\u2713") + " " + message);
209
+ console.log(pc2.green("\u2713") + " " + message);
236
210
  }
237
211
  showError(message) {
238
212
  process.stdout.write("\x1B[?25h");
239
213
  console.log();
240
- console.log(pc3.red("\u2717") + " " + message);
214
+ console.log(pc2.red("\u2717") + " " + message);
241
215
  }
242
216
  };
243
217
  function createProgressReporter() {
@@ -260,18 +234,18 @@ var InstallProgress = class {
260
234
  start() {
261
235
  process.stdout.write("\x1B[?25l");
262
236
  const barWidth = 30;
263
- const frames2 = ["\u25D0", "\u25D3", "\u25D1", "\u25D2"];
237
+ const frames = ["\u25D0", "\u25D3", "\u25D1", "\u25D2"];
264
238
  let frameIndex = 0;
265
239
  this.interval = setInterval(() => {
266
- const frame = pc3.cyan(frames2[frameIndex]);
240
+ const frame = pc2.cyan(frames[frameIndex]);
267
241
  const filled = Math.floor(this.progress / 100 * barWidth);
268
242
  const empty = barWidth - filled;
269
- const bar = pc3.green("\u2588".repeat(filled)) + pc3.dim("\u2591".repeat(empty));
243
+ const bar = pc2.green("\u2588".repeat(filled)) + pc2.dim("\u2591".repeat(empty));
270
244
  const pkg = this.packages[this.currentPackage % this.packages.length];
271
245
  process.stdout.write(
272
- `\r${frame} Installing dependencies ${bar} ${pc3.dim(pkg)} `
246
+ `\r${frame} Installing dependencies ${bar} ${pc2.dim(pkg)} `
273
247
  );
274
- frameIndex = (frameIndex + 1) % frames2.length;
248
+ frameIndex = (frameIndex + 1) % frames.length;
275
249
  if (this.progress < 95) {
276
250
  this.progress += Math.random() * 3;
277
251
  if (this.progress > (this.currentPackage + 1) * 12) {
@@ -287,7 +261,7 @@ var InstallProgress = class {
287
261
  }
288
262
  process.stdout.write("\x1B[?25h");
289
263
  process.stdout.write("\r\x1B[K");
290
- const icon = success ? pc3.green("\u2713") : pc3.red("\u2717");
264
+ const icon = success ? pc2.green("\u2713") : pc2.red("\u2717");
291
265
  const message = success ? "Dependencies installed" : "Failed to install dependencies";
292
266
  console.log(`${icon} ${message}`);
293
267
  }
@@ -295,12 +269,54 @@ var InstallProgress = class {
295
269
  function createInstallProgress() {
296
270
  return new InstallProgress();
297
271
  }
272
+ var GitProgress = class {
273
+ interval = null;
274
+ phases = [
275
+ "Initializing repository",
276
+ "Setting up version control",
277
+ "Creating .git directory",
278
+ "Configuring git objects",
279
+ "Preparing initial branch"
280
+ ];
281
+ phaseIndex = 0;
282
+ dotCount = 0;
283
+ start() {
284
+ process.stdout.write("\x1B[?25l");
285
+ const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
286
+ let frameIndex = 0;
287
+ this.interval = setInterval(() => {
288
+ const frame = pc2.cyan(frames[frameIndex]);
289
+ const phase = this.phases[this.phaseIndex % this.phases.length];
290
+ const dots = ".".repeat(this.dotCount % 4);
291
+ process.stdout.write(`\r${frame} ${pc2.dim(phase)}${dots} `);
292
+ frameIndex = (frameIndex + 1) % frames.length;
293
+ this.dotCount++;
294
+ if (this.dotCount % 8 === 0) {
295
+ this.phaseIndex++;
296
+ }
297
+ }, 80);
298
+ }
299
+ stop(success = true) {
300
+ if (this.interval) {
301
+ clearInterval(this.interval);
302
+ this.interval = null;
303
+ }
304
+ process.stdout.write("\x1B[?25h");
305
+ process.stdout.write("\r\x1B[K");
306
+ const icon = success ? pc2.green("\u2713") : pc2.red("\u2717");
307
+ const message = success ? "Git repository initialized" : "Failed to initialize git";
308
+ console.log(`${icon} ${message}`);
309
+ }
310
+ };
311
+ function createGitProgress() {
312
+ return new GitProgress();
313
+ }
298
314
 
299
315
  // src/generators/package-json.ts
300
316
  function generatePackageJson(options) {
301
317
  const isSolana = options.provider === "arkworks";
302
318
  const dependencies = {
303
- "@izi-noir/sdk": "^0.1.4",
319
+ "@izi-noir/sdk": "^0.1.5",
304
320
  "@noir-lang/acvm_js": "1.0.0-beta.13-1d260df.nightly",
305
321
  "@noir-lang/noirc_abi": "1.0.0-beta.13-1d260df.nightly",
306
322
  "react": "^18.3.1",
@@ -599,9 +615,17 @@ export default defineConfig({
599
615
  },
600
616
  define: {
601
617
  "global": "globalThis",
618
+ "process.env": {},
602
619
  },
603
620
  optimizeDeps: {
604
- exclude: ["@noir-lang/noir_wasm", "@aztec/bb.js", "@izi-noir/sdk"],
621
+ exclude: [
622
+ "@noir-lang/noir_wasm",
623
+ "@noir-lang/noir_js",
624
+ "@noir-lang/acvm_js",
625
+ "@noir-lang/noirc_abi",
626
+ "@aztec/bb.js",
627
+ "@izi-noir/sdk",
628
+ ],
605
629
  include: ["buffer", "util"],
606
630
  },
607
631
  build: {
@@ -636,6 +660,9 @@ function generateIndexHtml(options) {
636
660
  <link rel="icon" type="image/svg+xml" href="/vite.svg" />
637
661
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
638
662
  <title>${options.projectName} - ZK Proof Demo</title>
663
+ <link rel="preconnect" href="https://fonts.googleapis.com">
664
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
665
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
639
666
  </head>
640
667
  <body>
641
668
  <div id="root"></div>
@@ -768,10 +795,17 @@ import { Chain, Network } from '@izi-noir/sdk';` : "";
768
795
  </div>
769
796
 
770
797
  <div className="verify-box">
798
+ {publicInputs && (
799
+ <div className="public-inputs-display">
800
+ <span>Public inputs:</span>
801
+ <code>{JSON.stringify(publicInputs)}</code>
802
+ </div>
803
+ )}
771
804
  <button
772
805
  onClick={handleVerify}
773
806
  disabled={!vkAccount || isVerifying}
774
807
  className="btn btn-primary"
808
+ style={{ marginTop: publicInputs ? '0.5rem' : 0 }}
775
809
  >
776
810
  {isVerifying ? 'Verifying...' : verified ? 'Verified!' : 'Verify On-Chain'}
777
811
  </button>
@@ -839,6 +873,7 @@ function App() {
839
873
  const [proofTime, setProofTime] = useState<number | null>(null);
840
874
  const [proofError, setProofError] = useState<string | null>(null);
841
875
  const [localVerified, setLocalVerified] = useState<boolean | null>(null);
876
+ const [publicInputs, setPublicInputs] = useState<string[] | null>(null);
842
877
 
843
878
  // IziNoir instance
844
879
  const [iziInstance, setIziInstance] = useState<IziNoir | null>(null);
@@ -855,6 +890,7 @@ ${solanaState}
855
890
  setProof(null);
856
891
  setProofTime(null);
857
892
  setLocalVerified(null);
893
+ setPublicInputs(null);
858
894
  setNoirCode(null);
859
895
  }
860
896
  }, [selectedCircuit]);
@@ -927,6 +963,7 @@ ${solanaState}
927
963
  const endTime = performance.now();
928
964
  setProof(proofBytes);
929
965
  setProofTime(Math.round(endTime - startTime));
966
+ setPublicInputs(publicInputsHex);
930
967
  } catch (error) {
931
968
  console.error('Proof generation error:', error);
932
969
  setProofError((error as Error).message);
@@ -940,7 +977,7 @@ ${solanaHandlers}
940
977
  <div className="app">
941
978
  <header>
942
979
  <h1>${options.projectName}</h1>
943
- <p>Zero-Knowledge Proof Demo</p>
980
+ <p>Built with IZI-NOIR</p>
944
981
  </header>
945
982
 
946
983
  <main>
@@ -1103,29 +1140,65 @@ function capitalizeFirst(str) {
1103
1140
  return str.charAt(0).toUpperCase() + str.slice(1);
1104
1141
  }
1105
1142
  function generateAppCss() {
1106
- return `* {
1143
+ return `/* ================================
1144
+ CSS Variables - IZI-NOIR Theme
1145
+ ================================ */
1146
+ :root {
1147
+ --solana-purple: #9945FF;
1148
+ --solana-green: #14F195;
1149
+ --noir-primary: #050505;
1150
+ --noir-elevated: #0A0A0A;
1151
+ --noir-orange: #FF6B35;
1152
+ --izi-cyan: #00D4FF;
1153
+ --text: #ffffff;
1154
+ --text-muted: #888888;
1155
+ --border: rgba(255, 255, 255, 0.1);
1156
+ }
1157
+
1158
+ /* ================================
1159
+ Reset & Base Styles
1160
+ ================================ */
1161
+ * {
1107
1162
  box-sizing: border-box;
1108
1163
  margin: 0;
1109
1164
  padding: 0;
1110
1165
  }
1111
1166
 
1112
- :root {
1113
- --purple: #9945FF;
1114
- --green: #14F195;
1115
- --bg: #0a0a0a;
1116
- --bg-elevated: #111111;
1117
- --border: #222222;
1118
- --text: #ffffff;
1119
- --text-muted: #888888;
1167
+ html {
1168
+ scroll-behavior: smooth;
1120
1169
  }
1121
1170
 
1122
1171
  body {
1123
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
1124
- background: var(--bg);
1172
+ font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
1173
+ background-color: var(--noir-primary);
1174
+ /* Noise texture + radial gradients like the main frontend */
1175
+ background-image:
1176
+ url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.015'/%3E%3C/svg%3E"),
1177
+ radial-gradient(
1178
+ ellipse 60% 40% at 50% 30%,
1179
+ rgba(153, 69, 255, 0.06) 0%,
1180
+ transparent 50%
1181
+ ),
1182
+ radial-gradient(
1183
+ ellipse 80% 50% at 50% 110%,
1184
+ rgba(20, 241, 149, 0.04) 0%,
1185
+ transparent 40%
1186
+ );
1187
+ background-attachment: fixed;
1125
1188
  color: var(--text);
1126
1189
  line-height: 1.6;
1190
+ -webkit-font-smoothing: antialiased;
1191
+ -moz-osx-font-smoothing: grayscale;
1192
+ }
1193
+
1194
+ ::selection {
1195
+ background: rgba(153, 69, 255, 0.3);
1196
+ color: white;
1127
1197
  }
1128
1198
 
1199
+ /* ================================
1200
+ Layout
1201
+ ================================ */
1129
1202
  .app {
1130
1203
  min-height: 100vh;
1131
1204
  display: flex;
@@ -1136,11 +1209,14 @@ header {
1136
1209
  padding: 2rem;
1137
1210
  text-align: center;
1138
1211
  border-bottom: 1px solid var(--border);
1212
+ backdrop-filter: blur(8px);
1213
+ background: rgba(0, 0, 0, 0.3);
1139
1214
  }
1140
1215
 
1141
1216
  header h1 {
1142
1217
  font-size: 2rem;
1143
- background: linear-gradient(90deg, var(--purple), var(--green));
1218
+ font-weight: 700;
1219
+ background: linear-gradient(135deg, var(--solana-purple) 0%, var(--noir-orange) 100%);
1144
1220
  -webkit-background-clip: text;
1145
1221
  -webkit-text-fill-color: transparent;
1146
1222
  background-clip: text;
@@ -1159,34 +1235,51 @@ main {
1159
1235
  width: 100%;
1160
1236
  }
1161
1237
 
1238
+ /* ================================
1239
+ Section Cards - Glassmorphism
1240
+ ================================ */
1162
1241
  .section {
1163
1242
  margin-bottom: 2rem;
1164
1243
  padding: 1.5rem;
1165
- background: var(--bg-elevated);
1244
+ background: rgba(0, 0, 0, 0.3);
1245
+ backdrop-filter: blur(8px);
1166
1246
  border: 1px solid var(--border);
1167
- border-radius: 12px;
1247
+ border-radius: 16px;
1248
+ transition: border-color 0.3s ease, box-shadow 0.3s ease;
1249
+ }
1250
+
1251
+ .section:hover {
1252
+ border-color: rgba(255, 255, 255, 0.15);
1253
+ box-shadow: 0 0 30px rgba(153, 69, 255, 0.1);
1168
1254
  }
1169
1255
 
1170
1256
  .section h2 {
1171
1257
  font-size: 1.25rem;
1258
+ font-weight: 600;
1172
1259
  margin-bottom: 1rem;
1173
1260
  color: var(--text);
1174
1261
  }
1175
1262
 
1263
+ /* ================================
1264
+ Form Controls
1265
+ ================================ */
1176
1266
  .select {
1177
1267
  width: 100%;
1178
- padding: 0.75rem;
1179
- background: var(--bg);
1268
+ padding: 0.75rem 1rem;
1269
+ background: rgba(0, 0, 0, 0.4);
1180
1270
  border: 1px solid var(--border);
1181
- border-radius: 8px;
1271
+ border-radius: 12px;
1182
1272
  color: var(--text);
1273
+ font-family: inherit;
1183
1274
  font-size: 1rem;
1184
1275
  cursor: pointer;
1276
+ transition: border-color 0.3s ease, box-shadow 0.3s ease;
1185
1277
  }
1186
1278
 
1187
1279
  .select:focus {
1188
1280
  outline: none;
1189
- border-color: var(--purple);
1281
+ border-color: var(--solana-purple);
1282
+ box-shadow: 0 0 20px rgba(153, 69, 255, 0.2);
1190
1283
  }
1191
1284
 
1192
1285
  .inputs-grid {
@@ -1211,71 +1304,93 @@ main {
1211
1304
 
1212
1305
  .input-badge {
1213
1306
  font-size: 0.625rem;
1214
- padding: 0.125rem 0.375rem;
1215
- border-radius: 4px;
1307
+ padding: 0.125rem 0.5rem;
1308
+ border-radius: 6px;
1216
1309
  text-transform: uppercase;
1217
1310
  font-weight: 600;
1311
+ letter-spacing: 0.05em;
1218
1312
  }
1219
1313
 
1220
1314
  .input-badge.public {
1221
- background: var(--green);
1222
- color: #000;
1315
+ background: rgba(20, 241, 149, 0.2);
1316
+ color: var(--solana-green);
1317
+ border: 1px solid rgba(20, 241, 149, 0.3);
1223
1318
  }
1224
1319
 
1225
1320
  .input-badge.private {
1226
- background: var(--purple);
1227
- color: #fff;
1321
+ background: rgba(153, 69, 255, 0.2);
1322
+ color: var(--solana-purple);
1323
+ border: 1px solid rgba(153, 69, 255, 0.3);
1228
1324
  }
1229
1325
 
1230
1326
  .input-group input {
1231
- padding: 0.75rem;
1232
- background: var(--bg);
1327
+ padding: 0.75rem 1rem;
1328
+ background: rgba(0, 0, 0, 0.4);
1233
1329
  border: 1px solid var(--border);
1234
- border-radius: 8px;
1330
+ border-radius: 12px;
1235
1331
  color: var(--text);
1332
+ font-family: 'JetBrains Mono', 'Fira Code', monospace;
1236
1333
  font-size: 1rem;
1334
+ transition: border-color 0.3s ease, box-shadow 0.3s ease;
1237
1335
  }
1238
1336
 
1239
1337
  .input-group input:focus {
1240
1338
  outline: none;
1241
- border-color: var(--purple);
1339
+ border-color: var(--solana-purple);
1340
+ box-shadow: 0 0 20px rgba(153, 69, 255, 0.2);
1242
1341
  }
1243
1342
 
1343
+ /* ================================
1344
+ Buttons - Animated Gradient
1345
+ ================================ */
1244
1346
  .btn {
1245
1347
  padding: 0.75rem 1.5rem;
1246
1348
  font-size: 1rem;
1247
1349
  font-weight: 600;
1248
1350
  border: none;
1249
- border-radius: 8px;
1351
+ border-radius: 12px;
1250
1352
  cursor: pointer;
1251
- transition: all 0.2s;
1353
+ transition: all 0.3s ease;
1252
1354
  }
1253
1355
 
1254
1356
  .btn:disabled {
1255
- opacity: 0.5;
1357
+ opacity: 0.4;
1256
1358
  cursor: not-allowed;
1257
1359
  }
1258
1360
 
1259
1361
  .btn-primary {
1260
- background: linear-gradient(90deg, var(--purple), var(--green));
1362
+ background: linear-gradient(
1363
+ 90deg,
1364
+ var(--solana-purple) 0%,
1365
+ var(--solana-purple) 50%,
1366
+ var(--solana-green) 100%
1367
+ );
1368
+ background-size: 200% 100%;
1369
+ background-position: 0% 0%;
1261
1370
  color: white;
1371
+ box-shadow: 0 0 20px rgba(153, 69, 255, 0.3);
1262
1372
  }
1263
1373
 
1264
1374
  .btn-primary:hover:not(:disabled) {
1375
+ background-position: 100% 0%;
1265
1376
  transform: translateY(-2px);
1266
- box-shadow: 0 4px 20px rgba(153, 69, 255, 0.3);
1377
+ box-shadow: 0 0 30px rgba(20, 241, 149, 0.3);
1267
1378
  }
1268
1379
 
1269
1380
  .btn-secondary {
1270
- background: var(--bg);
1381
+ background: rgba(0, 0, 0, 0.4);
1271
1382
  border: 1px solid var(--border);
1272
1383
  color: var(--text);
1273
1384
  }
1274
1385
 
1275
1386
  .btn-secondary:hover:not(:disabled) {
1276
- border-color: var(--purple);
1387
+ border-color: var(--solana-purple);
1388
+ box-shadow: 0 0 20px rgba(153, 69, 255, 0.15);
1277
1389
  }
1278
1390
 
1391
+ /* ================================
1392
+ Results Grid
1393
+ ================================ */
1279
1394
  .results {
1280
1395
  display: grid;
1281
1396
  grid-template-columns: repeat(3, 1fr);
@@ -1283,22 +1398,36 @@ main {
1283
1398
  margin-top: 1rem;
1284
1399
  }
1285
1400
 
1401
+ @media (max-width: 640px) {
1402
+ .results {
1403
+ grid-template-columns: 1fr;
1404
+ }
1405
+ }
1406
+
1286
1407
  .result-card {
1287
- padding: 1rem;
1288
- background: var(--bg);
1408
+ padding: 1.25rem;
1409
+ background: rgba(0, 0, 0, 0.3);
1289
1410
  border: 1px solid var(--border);
1290
- border-radius: 8px;
1411
+ border-radius: 12px;
1291
1412
  text-align: center;
1413
+ transition: border-color 0.3s ease, transform 0.3s ease, box-shadow 0.3s ease;
1414
+ }
1415
+
1416
+ .result-card:hover {
1417
+ border-color: rgba(255, 255, 255, 0.15);
1418
+ transform: translateY(-4px);
1419
+ box-shadow: 0 0 25px rgba(153, 69, 255, 0.15);
1292
1420
  }
1293
1421
 
1294
1422
  .result-value {
1295
- font-size: 1.25rem;
1296
- font-weight: 600;
1423
+ font-size: 1.5rem;
1424
+ font-weight: 700;
1425
+ font-family: 'JetBrains Mono', monospace;
1297
1426
  display: block;
1298
1427
  }
1299
1428
 
1300
1429
  .result-value.success {
1301
- color: var(--green);
1430
+ color: var(--solana-green);
1302
1431
  }
1303
1432
 
1304
1433
  .result-value.error {
@@ -1309,22 +1438,40 @@ main {
1309
1438
  font-size: 0.75rem;
1310
1439
  color: var(--text-muted);
1311
1440
  text-transform: uppercase;
1312
- margin-top: 0.25rem;
1441
+ letter-spacing: 0.1em;
1442
+ margin-top: 0.5rem;
1313
1443
  display: block;
1314
1444
  }
1315
1445
 
1446
+ /* ================================
1447
+ Messages
1448
+ ================================ */
1316
1449
  .error {
1317
- color: #ff4444;
1450
+ color: #ff6b6b;
1318
1451
  font-size: 0.875rem;
1319
1452
  margin-top: 0.5rem;
1453
+ padding: 0.75rem 1rem;
1454
+ background: rgba(255, 107, 107, 0.1);
1455
+ border: 1px solid rgba(255, 107, 107, 0.2);
1456
+ border-radius: 8px;
1457
+ word-break: break-word;
1458
+ overflow-wrap: break-word;
1459
+ max-width: 100%;
1320
1460
  }
1321
1461
 
1322
1462
  .success {
1323
- color: var(--green);
1463
+ color: var(--solana-green);
1324
1464
  font-size: 0.875rem;
1325
1465
  margin-top: 0.5rem;
1466
+ padding: 0.75rem 1rem;
1467
+ background: rgba(20, 241, 149, 0.1);
1468
+ border: 1px solid rgba(20, 241, 149, 0.2);
1469
+ border-radius: 8px;
1326
1470
  }
1327
1471
 
1472
+ /* ================================
1473
+ Noir Code Details
1474
+ ================================ */
1328
1475
  .noir-details {
1329
1476
  margin-top: 1rem;
1330
1477
  }
@@ -1333,12 +1480,21 @@ main {
1333
1480
  cursor: pointer;
1334
1481
  color: var(--text-muted);
1335
1482
  font-size: 0.875rem;
1483
+ padding: 0.5rem 0;
1484
+ transition: color 0.3s ease;
1336
1485
  }
1337
1486
 
1338
1487
  .noir-details summary:hover {
1339
- color: var(--text);
1488
+ color: var(--solana-purple);
1340
1489
  }
1341
1490
 
1491
+ .noir-details[open] summary {
1492
+ color: var(--solana-purple);
1493
+ }
1494
+
1495
+ /* ================================
1496
+ Deploy & Verify Section
1497
+ ================================ */
1342
1498
  .deploy-verify-row {
1343
1499
  display: grid;
1344
1500
  grid-template-columns: 1fr 1fr;
@@ -1346,38 +1502,118 @@ main {
1346
1502
  margin-top: 1rem;
1347
1503
  }
1348
1504
 
1505
+ @media (max-width: 640px) {
1506
+ .deploy-verify-row {
1507
+ grid-template-columns: 1fr;
1508
+ }
1509
+ }
1510
+
1349
1511
  .deploy-box,
1350
1512
  .verify-box {
1351
- padding: 1rem;
1352
- background: var(--bg);
1513
+ padding: 1.25rem;
1514
+ background: rgba(0, 0, 0, 0.3);
1515
+ border: 1px solid var(--border);
1516
+ border-radius: 12px;
1517
+ overflow: hidden;
1518
+ word-break: break-word;
1519
+ transition: border-color 0.3s ease, box-shadow 0.3s ease;
1520
+ }
1521
+
1522
+ .deploy-box:hover,
1523
+ .verify-box:hover {
1524
+ border-color: rgba(255, 255, 255, 0.15);
1525
+ box-shadow: 0 0 20px rgba(153, 69, 255, 0.1);
1526
+ }
1527
+
1528
+ .public-inputs-display {
1529
+ margin-top: 0.75rem;
1530
+ font-size: 0.75rem;
1531
+ color: var(--text-muted);
1532
+ }
1533
+
1534
+ .public-inputs-display code {
1535
+ display: block;
1536
+ margin-top: 0.5rem;
1537
+ padding: 0.75rem;
1538
+ background: rgba(0, 0, 0, 0.4);
1353
1539
  border: 1px solid var(--border);
1354
1540
  border-radius: 8px;
1541
+ font-family: 'JetBrains Mono', monospace;
1542
+ font-size: 0.7rem;
1543
+ overflow-x: auto;
1544
+ max-width: 100%;
1355
1545
  }
1356
1546
 
1547
+ /* ================================
1548
+ Footer
1549
+ ================================ */
1357
1550
  footer {
1358
1551
  padding: 1.5rem;
1359
1552
  text-align: center;
1360
1553
  border-top: 1px solid var(--border);
1554
+ backdrop-filter: blur(8px);
1555
+ background: rgba(0, 0, 0, 0.3);
1361
1556
  color: var(--text-muted);
1362
1557
  font-size: 0.875rem;
1363
1558
  }
1364
1559
 
1365
1560
  footer a {
1366
- color: var(--purple);
1561
+ color: var(--solana-purple);
1367
1562
  text-decoration: none;
1563
+ transition: color 0.3s ease;
1368
1564
  }
1369
1565
 
1370
1566
  footer a:hover {
1371
- text-decoration: underline;
1567
+ color: var(--solana-green);
1568
+ }
1569
+
1570
+ /* ================================
1571
+ Custom Scrollbar
1572
+ ================================ */
1573
+ ::-webkit-scrollbar {
1574
+ width: 6px;
1575
+ height: 6px;
1576
+ }
1577
+
1578
+ ::-webkit-scrollbar-track {
1579
+ background: var(--noir-primary);
1580
+ }
1581
+
1582
+ ::-webkit-scrollbar-thumb {
1583
+ background: rgba(255, 255, 255, 0.1);
1584
+ border-radius: 9999px;
1585
+ }
1586
+
1587
+ ::-webkit-scrollbar-thumb:hover {
1588
+ background: rgba(255, 255, 255, 0.2);
1589
+ }
1590
+
1591
+ /* ================================
1592
+ Accessibility - Reduced Motion
1593
+ ================================ */
1594
+ @media (prefers-reduced-motion: reduce) {
1595
+ *,
1596
+ *::before,
1597
+ *::after {
1598
+ animation-duration: 0.01ms !important;
1599
+ animation-iteration-count: 1 !important;
1600
+ transition-duration: 0.01ms !important;
1601
+ }
1372
1602
  }
1373
1603
  `;
1374
1604
  }
1375
1605
  function generateIndexCss() {
1376
- return `body {
1606
+ return `/* Base styles - fonts are loaded in index.html */
1607
+ body {
1377
1608
  margin: 0;
1378
1609
  -webkit-font-smoothing: antialiased;
1379
1610
  -moz-osx-font-smoothing: grayscale;
1380
1611
  }
1612
+
1613
+ /* Ensure mono font is applied to code elements */
1614
+ code, pre, .mono {
1615
+ font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace;
1616
+ }
1381
1617
  `;
1382
1618
  }
1383
1619
  function generateViteEnvDts() {
@@ -1535,6 +1771,810 @@ export function EditableCodeBlock({
1535
1771
  `;
1536
1772
  }
1537
1773
 
1774
+ // src/generators/claude-skills.ts
1775
+ function generateClaudeSkills() {
1776
+ return {
1777
+ ".claude/skills/izi-noir-circuit-patterns/SKILL.md": SKILL_MD,
1778
+ ".claude/skills/izi-noir-circuit-patterns/assets/valid-examples.ts": VALID_EXAMPLES_TS,
1779
+ ".claude/skills/izi-noir-circuit-patterns/assets/operator-mapping.md": OPERATOR_MAPPING_MD
1780
+ };
1781
+ }
1782
+ var SKILL_MD = `---
1783
+ name: izi-noir-circuit-patterns
1784
+ description: >
1785
+ Patterns for writing JS/TS code that transpiles to Noir ZK circuits.
1786
+ Trigger: When writing circuit functions, IziNoir API, Solana deployment, Provider/Chain selection, or JS-to-Noir transformations.
1787
+ license: MIT
1788
+ metadata:
1789
+ author: izi-noir
1790
+ version: "2.0"
1791
+ scope: [sdk, frontend, solana-contracts]
1792
+ auto_invoke:
1793
+ - "circuit function"
1794
+ - "createProof"
1795
+ - "IziNoir"
1796
+ - "assert statement"
1797
+ - "JS to Noir"
1798
+ - "zero knowledge"
1799
+ - "Solana proof"
1800
+ - "Chain.Solana"
1801
+ - "Provider.Arkworks"
1802
+ allowed-tools: Read, Glob, Grep
1803
+ ---
1804
+
1805
+ # IZI-NOIR Circuit Patterns
1806
+
1807
+ Patterns for writing JavaScript/TypeScript code that the SDK can parse and transpile 1:1 to Noir ZK circuits, with Solana on-chain verification support.
1808
+
1809
+ ## When to Use
1810
+
1811
+ - Writing circuit functions for \`createProof()\`
1812
+ - Using the IziNoir class API (init, compile, prove, deploy, verifyOnChain)
1813
+ - Deploying proofs to Solana
1814
+ - Choosing between providers (Arkworks, Barretenberg)
1815
+ - Debugging parsing or Noir generation errors
1816
+ - Understanding JS \u2192 Noir type mapping
1817
+
1818
+ ## Architecture Overview
1819
+
1820
+ \`\`\`
1821
+ JS Function \u2192 AcornParser \u2192 NoirGenerator \u2192 Noir WASM \u2192 ArkworksWasm \u2192 Solana
1822
+ \`\`\`
1823
+
1824
+ **Pipeline layers:**
1825
+ 1. **Domain Layer** - Core types: Circuit, Proof, VerifyingKey, Provider, Chain, Network
1826
+ 2. **Application Layer** - Services: NoirGenerator, createProof orchestration
1827
+ 3. **Infrastructure Layer** - Parsers (Acorn), Compilers (Noir WASM), Provers (Arkworks/BB)
1828
+
1829
+ ## IziNoir Class API
1830
+
1831
+ ### Initialization
1832
+
1833
+ \`\`\`typescript
1834
+ import { IziNoir, Provider, Chain, Network } from '@izi-noir/sdk';
1835
+
1836
+ // Basic initialization (development)
1837
+ const izi = await IziNoir.init({
1838
+ provider: Provider.Arkworks,
1839
+ });
1840
+
1841
+ // With Solana chain targeting
1842
+ const izi = await IziNoir.init({
1843
+ provider: Provider.Arkworks,
1844
+ chain: Chain.Solana,
1845
+ network: Network.Devnet,
1846
+ });
1847
+ \`\`\`
1848
+
1849
+ ### Providers
1850
+
1851
+ | Provider | Use Case | Proof Size | Notes |
1852
+ |----------|----------|------------|-------|
1853
+ | \`Provider.Arkworks\` | **Solana production** | 256 bytes | Groth16, native syscalls |
1854
+ | \`Provider.Barretenberg\` | Development/testing | ~2KB | UltraPlonk, faster setup |
1855
+
1856
+ **Rule:** Always use \`Provider.Arkworks\` for Solana on-chain verification.
1857
+
1858
+ ### Chain and Network
1859
+
1860
+ \`\`\`typescript
1861
+ // Chain enum
1862
+ Chain.Solana // Format proofs for Solana verification
1863
+ Chain.EVM // Format proofs for EVM verification (future)
1864
+
1865
+ // Network enum
1866
+ Network.Devnet // Solana devnet
1867
+ Network.Mainnet // Solana mainnet
1868
+ Network.Localnet // Local validator
1869
+ \`\`\`
1870
+
1871
+ ### Creating Proofs
1872
+
1873
+ \`\`\`typescript
1874
+ // Define circuit function
1875
+ const balanceProof = (
1876
+ [threshold]: [number], // public inputs
1877
+ [balance]: [number] // private inputs
1878
+ ) => {
1879
+ assert(balance >= threshold);
1880
+ };
1881
+
1882
+ // Generate proof
1883
+ const result = await izi.createProof(
1884
+ balanceProof,
1885
+ [100], // public: threshold
1886
+ [1500] // private: actual balance
1887
+ );
1888
+
1889
+ console.log(result.verified); // true
1890
+ console.log(result.proof); // Uint8Array (256 bytes for Arkworks)
1891
+ \`\`\`
1892
+
1893
+ ### Solana Deployment
1894
+
1895
+ \`\`\`typescript
1896
+ // Deploy verifying key to Solana
1897
+ const deployment = await izi.deploy(balanceProof, {
1898
+ payer: wallet,
1899
+ network: Network.Devnet,
1900
+ });
1901
+
1902
+ console.log(deployment.vkAccount); // PublicKey of VK account
1903
+ console.log(deployment.programId); // Verifier program ID
1904
+
1905
+ // Verify proof on-chain
1906
+ const txSignature = await izi.verifyOnChain(result.proof, {
1907
+ vkAccount: deployment.vkAccount,
1908
+ payer: wallet,
1909
+ });
1910
+ \`\`\`
1911
+
1912
+ ### SolanaProofData Structure
1913
+
1914
+ When \`chain: Chain.Solana\`, proof data is formatted as:
1915
+
1916
+ \`\`\`typescript
1917
+ interface SolanaProofData {
1918
+ verifyingKey: {
1919
+ nr_pubinputs: number;
1920
+ vk_alpha_g1: number[]; // 64 bytes
1921
+ vk_beta_g2: number[]; // 128 bytes
1922
+ vk_gamma_g2: number[]; // 128 bytes
1923
+ vk_delta_g2: number[]; // 128 bytes
1924
+ vk_ic: number[][]; // (nr_pubinputs + 1) \xD7 64 bytes
1925
+ };
1926
+ proof: {
1927
+ ar: number[]; // 64 bytes - A point (G1)
1928
+ bs: number[]; // 128 bytes - B point (G2)
1929
+ krs: number[]; // 64 bytes - C point (G1)
1930
+ };
1931
+ publicInputs: number[][]; // Each input as 32-byte array
1932
+ }
1933
+ \`\`\`
1934
+
1935
+ ## Critical Patterns
1936
+
1937
+ ### 1. Function Signature (REQUIRED)
1938
+
1939
+ \`\`\`typescript
1940
+ // MUST use array destructuring for both parameters
1941
+ ([publicInputs], [privateInputs]) => {
1942
+ // body with assert statements
1943
+ }
1944
+
1945
+ // Example with multiple inputs
1946
+ ([expected, threshold], [secret, salt]) => {
1947
+ assert(secret * secret == expected);
1948
+ assert(secret > threshold);
1949
+ }
1950
+ \`\`\`
1951
+
1952
+ - First array: **public inputs** (marked \`pub\` in Noir)
1953
+ - Second array: **private inputs** (witness values)
1954
+ - Arrow function or function expression
1955
+ - Body: block statement \`{ }\` or single expression
1956
+
1957
+ ### 2. Mutability Convention
1958
+
1959
+ \`\`\`typescript
1960
+ // Immutable (default)
1961
+ let x = a + b; // \u2192 let x: Field = a + b;
1962
+
1963
+ // Mutable (use mut_ prefix)
1964
+ let mut_sum = 0; // \u2192 let mut sum: Field = 0;
1965
+ mut_sum = mut_sum + 1; // \u2192 sum = sum + 1;
1966
+ \`\`\`
1967
+
1968
+ **Rule:** Prefix variable name with \`mut_\` for mutable variables. The prefix is stripped in generated Noir.
1969
+
1970
+ ### 3. Operators
1971
+
1972
+ | JavaScript | Noir | Notes |
1973
+ |------------|------|-------|
1974
+ | \`==\` / \`===\` | \`==\` | Both map to equality |
1975
+ | \`!=\` / \`!==\` | \`!=\` | Both map to inequality |
1976
+ | \`+\` | \`+\` | Addition |
1977
+ | \`-\` | \`-\` | Subtraction |
1978
+ | \`*\` | \`*\` | Multiplication |
1979
+ | \`/\` | \`/\` | Division |
1980
+ | \`%\` | \`%\` | Modulo |
1981
+ | \`<\` | \`<\` | Less than |
1982
+ | \`>\` | \`>\` | Greater than |
1983
+ | \`<=\` | \`<=\` | Less than or equal |
1984
+ | \`>=\` | \`>=\` | Greater than or equal |
1985
+ | \`&&\` | \`&\` | **Converted to bitwise AND** |
1986
+ | \`\\|\\|\` | \`\\|\` | **Converted to bitwise OR** |
1987
+ | \`!\` | \`!\` | Logical NOT |
1988
+ | \`-x\` | \`-x\` | Negation |
1989
+
1990
+ **Warning:** \`&&\` and \`||\` are converted to bitwise operators in Noir!
1991
+
1992
+ ### 4. Statements
1993
+
1994
+ #### Assert
1995
+ \`\`\`typescript
1996
+ assert(condition); // Basic assertion
1997
+ assert(condition, "message"); // With error message
1998
+ \`\`\`
1999
+
2000
+ #### Variable Declaration
2001
+ \`\`\`typescript
2002
+ let x = a + b; // Immutable
2003
+ let mut_counter = 0; // Mutable (mut_ prefix)
2004
+ const y = 10; // Immutable (const supported)
2005
+ \`\`\`
2006
+
2007
+ #### Assignment (mutable only)
2008
+ \`\`\`typescript
2009
+ mut_x = mut_x + 1; // Only valid for mut_ variables
2010
+ \`\`\`
2011
+
2012
+ #### If/Else
2013
+ \`\`\`typescript
2014
+ if (condition) {
2015
+ // consequent
2016
+ } else {
2017
+ // alternate
2018
+ }
2019
+ \`\`\`
2020
+
2021
+ #### For Loop
2022
+ \`\`\`typescript
2023
+ // Exclusive range (i < end)
2024
+ for (let i = 0; i < 10; i++) { }
2025
+ // \u2192 for i in 0..10 { }
2026
+
2027
+ // Inclusive range (i <= end)
2028
+ for (let i = 1; i <= 5; i++) { }
2029
+ // \u2192 for i in 1..=5 { }
2030
+
2031
+ // Variable bounds
2032
+ for (let i = start; i < end; i++) { }
2033
+ // \u2192 for i in start..end { }
2034
+ \`\`\`
2035
+
2036
+ **Loop constraints:**
2037
+ - Init: \`let i = start\`
2038
+ - Test: \`i < end\` or \`i <= end\`
2039
+ - Update: \`i++\`, \`++i\`, or \`i = i + 1\`
2040
+
2041
+ ### 5. Expressions
2042
+
2043
+ #### Literals
2044
+ \`\`\`typescript
2045
+ 5 // number \u2192 Field
2046
+ 100n // bigint \u2192 Field
2047
+ "string" // string literal
2048
+ 0x1234 // hex value
2049
+ true / false // boolean \u2192 bool
2050
+ \`\`\`
2051
+
2052
+ #### Arrays
2053
+ \`\`\`typescript
2054
+ let arr = [a, b, c]; // \u2192 [Field; 3]
2055
+ arr[0] // static index
2056
+ arr[i] // dynamic index
2057
+ arr.length // \u2192 arr.len()
2058
+ \`\`\`
2059
+
2060
+ #### Ternary (conditional)
2061
+ \`\`\`typescript
2062
+ let result = cond ? a : b;
2063
+ // \u2192 let result: Field = if cond { a } else { b };
2064
+ \`\`\`
2065
+
2066
+ #### Method Calls
2067
+ \`\`\`typescript
2068
+ arr.len() // Array length
2069
+ \`\`\`
2070
+
2071
+ ### 6. Type Mapping
2072
+
2073
+ | JavaScript | Noir Type | Notes |
2074
+ |------------|-----------|-------|
2075
+ | \`number\` | \`Field\` | Default for all numerics |
2076
+ | \`bigint\` | \`Field\` | Converted to string |
2077
+ | \`boolean\` | \`bool\` | Only \`true\`/\`false\` literals |
2078
+ | \`[a,b,c]\` | \`[Field; 3]\` | Fixed-size array |
2079
+
2080
+ **Default type:** Everything is \`Field\` unless explicitly boolean.
2081
+
2082
+ ## NOT Supported
2083
+
2084
+ These JavaScript features **cannot** be parsed:
2085
+
2086
+ - Object literals \`{ key: value }\`
2087
+ - Destructuring (except function parameters)
2088
+ - Spread operator \`...\`
2089
+ - Rest parameters \`...args\`
2090
+ - Template literals \`\\\`\${x}\\\`\`
2091
+ - Async/await
2092
+ - While/do-while loops
2093
+ - Switch statements
2094
+ - Function declarations inside circuit
2095
+ - Closures over external variables
2096
+ - Class methods
2097
+ - Regular expressions
2098
+ - Try/catch
2099
+ - Break/continue
2100
+ - Return statements
2101
+ - Computed property assignment \`arr[i] = x\`
2102
+
2103
+ ## Code Examples
2104
+
2105
+ See [assets/valid-examples.ts](assets/valid-examples.ts) for complete working examples.
2106
+
2107
+ ## CLI: create-izi-noir
2108
+
2109
+ Scaffold new projects quickly:
2110
+
2111
+ \`\`\`bash
2112
+ # Interactive mode
2113
+ npx create-izi-noir
2114
+
2115
+ # Quick setup
2116
+ npx create-izi-noir my-project --yes
2117
+
2118
+ # With specific template
2119
+ npx create-izi-noir my-project --template balance-proof
2120
+
2121
+ # With specific provider
2122
+ npx create-izi-noir my-project --provider arkworks
2123
+ \`\`\`
2124
+
2125
+ **Templates:**
2126
+ - \`default\` - Balance proof + age verification circuits
2127
+ - \`minimal\` - Blank canvas with empty circuit
2128
+ - \`balance-proof\` - Just balance proof circuit
2129
+
2130
+ **Options:**
2131
+ - \`-t, --template <template>\` - Template to use
2132
+ - \`-p, --provider <provider>\` - Proving provider (arkworks/barretenberg)
2133
+ - \`-y, --yes\` - Skip prompts
2134
+ - \`--skip-install\` - Skip npm install
2135
+ - \`--skip-git\` - Skip git init
2136
+
2137
+ ## Frontend Integration (Vite)
2138
+
2139
+ \`\`\`typescript
2140
+ // vite.config.ts
2141
+ export default defineConfig({
2142
+ optimizeDeps: {
2143
+ exclude: ['@noir-lang/noirc_abi', '@noir-lang/acvm_js'],
2144
+ },
2145
+ });
2146
+
2147
+ // main.ts - Initialize WASM before use
2148
+ import initNoirC from '@noir-lang/noirc_abi';
2149
+ import initACVM from '@noir-lang/acvm_js';
2150
+ import acvm from '@noir-lang/acvm_js/web/acvm_js_bg.wasm?url';
2151
+ import noirc from '@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm?url';
2152
+ import { markWasmInitialized, IziNoir, Provider } from '@izi-noir/sdk';
2153
+
2154
+ await Promise.all([initACVM(fetch(acvm)), initNoirC(fetch(noirc))]);
2155
+ markWasmInitialized();
2156
+
2157
+ // Now IziNoir is ready
2158
+ const izi = await IziNoir.init({ provider: Provider.Arkworks });
2159
+ \`\`\`
2160
+
2161
+ ## Resources
2162
+
2163
+ - [Operator Mapping Table](assets/operator-mapping.md)
2164
+ - Parser: \`packages/sdk/src/infra/parser/AcornParser.ts\`
2165
+ - Generator: \`packages/sdk/src/application/services/NoirGenerator.ts\`
2166
+ - IziNoir Class: \`packages/sdk/src/IziNoir.ts\`
2167
+ `;
2168
+ var VALID_EXAMPLES_TS = `/**
2169
+ * Valid JS/TS Circuit Patterns for IZI-NOIR
2170
+ *
2171
+ * Each example shows the JS input and the generated Noir output.
2172
+ * All examples are valid and can be used with createProof().
2173
+ */
2174
+
2175
+ // Declare assert for TypeScript (SDK parses it, not executed)
2176
+ declare function assert(condition: boolean, message?: string): void;
2177
+
2178
+ // =============================================================================
2179
+ // Example 1: Basic Assertion (simplest circuit)
2180
+ // =============================================================================
2181
+ /**
2182
+ * JS Input:
2183
+ */
2184
+ export const basicAssertion = ([expected]: number[], [secret]: number[]) => {
2185
+ assert(secret * secret == expected);
2186
+ };
2187
+ /**
2188
+ * Generated Noir:
2189
+ * \`\`\`noir
2190
+ * fn main(secret: Field, expected: pub Field) {
2191
+ * assert(secret * secret == expected);
2192
+ * }
2193
+ * \`\`\`
2194
+ */
2195
+
2196
+ // =============================================================================
2197
+ // Example 2: Multiple Inputs with Arithmetic
2198
+ // =============================================================================
2199
+ /**
2200
+ * JS Input:
2201
+ */
2202
+ export const multipleInputs = (
2203
+ [sum, product]: number[],
2204
+ [a, b]: number[]
2205
+ ) => {
2206
+ assert(a + b == sum);
2207
+ assert(a * b == product);
2208
+ };
2209
+ /**
2210
+ * Generated Noir:
2211
+ * \`\`\`noir
2212
+ * fn main(a: Field, b: Field, sum: pub Field, product: pub Field) {
2213
+ * assert(a + b == sum);
2214
+ * assert(a * b == product);
2215
+ * }
2216
+ * \`\`\`
2217
+ */
2218
+
2219
+ // =============================================================================
2220
+ // Example 3: Variables and Mutability
2221
+ // =============================================================================
2222
+ /**
2223
+ * JS Input:
2224
+ */
2225
+ export const variablesAndMutability = (
2226
+ [expected]: number[],
2227
+ [a, b]: number[]
2228
+ ) => {
2229
+ // Immutable variable
2230
+ let sum = a + b;
2231
+
2232
+ // Mutable variable (mut_ prefix)
2233
+ let mut_result = 0;
2234
+ mut_result = sum * 2;
2235
+
2236
+ assert(mut_result == expected);
2237
+ };
2238
+ /**
2239
+ * Generated Noir:
2240
+ * \`\`\`noir
2241
+ * fn main(a: Field, b: Field, expected: pub Field) {
2242
+ * let sum: Field = a + b;
2243
+ * let mut result: Field = 0;
2244
+ * result = sum * 2;
2245
+ * assert(result == expected);
2246
+ * }
2247
+ * \`\`\`
2248
+ */
2249
+
2250
+ // =============================================================================
2251
+ // Example 4: Conditional Logic (if/else)
2252
+ // =============================================================================
2253
+ /**
2254
+ * JS Input:
2255
+ */
2256
+ export const conditionalLogic = (
2257
+ [threshold]: number[],
2258
+ [value]: number[]
2259
+ ) => {
2260
+ let mut_result = 0;
2261
+
2262
+ if (value > threshold) {
2263
+ mut_result = 1;
2264
+ } else {
2265
+ mut_result = 0;
2266
+ }
2267
+
2268
+ assert(mut_result == 1);
2269
+ };
2270
+ /**
2271
+ * Generated Noir:
2272
+ * \`\`\`noir
2273
+ * fn main(value: Field, threshold: pub Field) {
2274
+ * let mut result: Field = 0;
2275
+ * if value > threshold {
2276
+ * result = 1;
2277
+ * } else {
2278
+ * result = 0;
2279
+ * }
2280
+ * assert(result == 1);
2281
+ * }
2282
+ * \`\`\`
2283
+ */
2284
+
2285
+ // =============================================================================
2286
+ // Example 5: Ternary Expression
2287
+ // =============================================================================
2288
+ /**
2289
+ * JS Input:
2290
+ */
2291
+ export const ternaryExpression = (
2292
+ [threshold]: number[],
2293
+ [value]: number[]
2294
+ ) => {
2295
+ // Ternary converts to Noir if-expression
2296
+ let result = value > threshold ? 1 : 0;
2297
+ assert(result == 1);
2298
+ };
2299
+ /**
2300
+ * Generated Noir:
2301
+ * \`\`\`noir
2302
+ * fn main(value: Field, threshold: pub Field) {
2303
+ * let result: Field = if value > threshold { 1 } else { 0 };
2304
+ * assert(result == 1);
2305
+ * }
2306
+ * \`\`\`
2307
+ */
2308
+
2309
+ // =============================================================================
2310
+ // Example 6: For Loop with Array
2311
+ // =============================================================================
2312
+ /**
2313
+ * JS Input:
2314
+ */
2315
+ export const forLoopWithArray = (
2316
+ [expected]: number[],
2317
+ [a, b, c, d]: number[]
2318
+ ) => {
2319
+ let arr = [a, b, c, d];
2320
+ let mut_sum = 0;
2321
+
2322
+ // Exclusive range: i < 4 \u2192 for i in 0..4
2323
+ for (let i = 0; i < 4; i++) {
2324
+ mut_sum = mut_sum + arr[i];
2325
+ }
2326
+
2327
+ assert(mut_sum == expected);
2328
+ };
2329
+ /**
2330
+ * Generated Noir:
2331
+ * \`\`\`noir
2332
+ * fn main(a: Field, b: Field, c: Field, d: Field, expected: pub Field) {
2333
+ * let arr: [Field; 4] = [a, b, c, d];
2334
+ * let mut sum: Field = 0;
2335
+ * for i in 0..4 {
2336
+ * sum = sum + arr[i];
2337
+ * }
2338
+ * assert(sum == expected);
2339
+ * }
2340
+ * \`\`\`
2341
+ */
2342
+
2343
+ // =============================================================================
2344
+ // Example 7: Inclusive Range Loop
2345
+ // =============================================================================
2346
+ /**
2347
+ * JS Input:
2348
+ */
2349
+ export const inclusiveRangeLoop = ([expected]: number[], [n]: number[]) => {
2350
+ let mut_sum = 0;
2351
+
2352
+ // Inclusive range: i <= n \u2192 for i in 1..=n
2353
+ for (let i = 1; i <= n; i++) {
2354
+ mut_sum = mut_sum + i;
2355
+ }
2356
+
2357
+ assert(mut_sum == expected);
2358
+ };
2359
+ /**
2360
+ * Generated Noir:
2361
+ * \`\`\`noir
2362
+ * fn main(n: Field, expected: pub Field) {
2363
+ * let mut sum: Field = 0;
2364
+ * for i in 1..=n {
2365
+ * sum = sum + i;
2366
+ * }
2367
+ * assert(sum == expected);
2368
+ * }
2369
+ * \`\`\`
2370
+ */
2371
+
2372
+ // =============================================================================
2373
+ // Example 8: Nested Control Flow
2374
+ // =============================================================================
2375
+ /**
2376
+ * JS Input:
2377
+ */
2378
+ export const nestedControlFlow = ([max]: number[], []: number[]) => {
2379
+ let mut_evenSum = 0;
2380
+
2381
+ for (let i = 1; i <= max; i++) {
2382
+ // Nested if inside loop
2383
+ if (i % 2 == 0) {
2384
+ mut_evenSum = mut_evenSum + i;
2385
+ }
2386
+ }
2387
+
2388
+ assert(mut_evenSum > 0);
2389
+ };
2390
+ /**
2391
+ * Generated Noir:
2392
+ * \`\`\`noir
2393
+ * fn main(max: pub Field) {
2394
+ * let mut evenSum: Field = 0;
2395
+ * for i in 1..=max {
2396
+ * if i % 2 == 0 {
2397
+ * evenSum = evenSum + i;
2398
+ * }
2399
+ * }
2400
+ * assert(evenSum > 0);
2401
+ * }
2402
+ * \`\`\`
2403
+ */
2404
+
2405
+ // =============================================================================
2406
+ // Example 9: Comparison Operators
2407
+ // =============================================================================
2408
+ /**
2409
+ * JS Input:
2410
+ */
2411
+ export const comparisonOperators = (
2412
+ [min, max]: number[],
2413
+ [value]: number[]
2414
+ ) => {
2415
+ // All comparison operators
2416
+ assert(value >= min);
2417
+ assert(value <= max);
2418
+ assert(value != 0);
2419
+
2420
+ // Combined with arithmetic
2421
+ let doubled = value * 2;
2422
+ assert(doubled < max * 2);
2423
+ };
2424
+ /**
2425
+ * Generated Noir:
2426
+ * \`\`\`noir
2427
+ * fn main(value: Field, min: pub Field, max: pub Field) {
2428
+ * assert(value >= min);
2429
+ * assert(value <= max);
2430
+ * assert(value != 0);
2431
+ * let doubled: Field = value * 2;
2432
+ * assert(doubled < max * 2);
2433
+ * }
2434
+ * \`\`\`
2435
+ */
2436
+
2437
+ // =============================================================================
2438
+ // Example 10: Array Length Access
2439
+ // =============================================================================
2440
+ /**
2441
+ * JS Input:
2442
+ */
2443
+ export const arrayLengthAccess = (
2444
+ [expectedLen]: number[],
2445
+ [a, b, c]: number[]
2446
+ ) => {
2447
+ let arr = [a, b, c];
2448
+
2449
+ // .length converts to .len() in Noir
2450
+ assert(arr.length == expectedLen);
2451
+ };
2452
+ /**
2453
+ * Generated Noir:
2454
+ * \`\`\`noir
2455
+ * fn main(a: Field, b: Field, c: Field, expectedLen: pub Field) {
2456
+ * let arr: [Field; 3] = [a, b, c];
2457
+ * assert(arr.len() == expectedLen);
2458
+ * }
2459
+ * \`\`\`
2460
+ */
2461
+ `;
2462
+ var OPERATOR_MAPPING_MD = `# JavaScript to Noir Mapping Reference
2463
+
2464
+ Complete mapping of JavaScript constructs to their Noir equivalents.
2465
+
2466
+ ## Binary Operators
2467
+
2468
+ | JavaScript | Noir | Category | Example |
2469
+ |------------|------|----------|---------|
2470
+ | \`==\` | \`==\` | Equality | \`a == b\` \u2192 \`a == b\` |
2471
+ | \`===\` | \`==\` | Strict equality | \`a === b\` \u2192 \`a == b\` |
2472
+ | \`!=\` | \`!=\` | Inequality | \`a != b\` \u2192 \`a != b\` |
2473
+ | \`!==\` | \`!=\` | Strict inequality | \`a !== b\` \u2192 \`a != b\` |
2474
+ | \`+\` | \`+\` | Addition | \`a + b\` \u2192 \`a + b\` |
2475
+ | \`-\` | \`-\` | Subtraction | \`a - b\` \u2192 \`a - b\` |
2476
+ | \`*\` | \`*\` | Multiplication | \`a * b\` \u2192 \`a * b\` |
2477
+ | \`/\` | \`/\` | Division | \`a / b\` \u2192 \`a / b\` |
2478
+ | \`%\` | \`%\` | Modulo | \`a % b\` \u2192 \`a % b\` |
2479
+ | \`<\` | \`<\` | Less than | \`a < b\` \u2192 \`a < b\` |
2480
+ | \`>\` | \`>\` | Greater than | \`a > b\` \u2192 \`a > b\` |
2481
+ | \`<=\` | \`<=\` | Less or equal | \`a <= b\` \u2192 \`a <= b\` |
2482
+ | \`>=\` | \`>=\` | Greater or equal | \`a >= b\` \u2192 \`a >= b\` |
2483
+ | \`&&\` | \`&\` | Logical AND | \`a && b\` \u2192 \`a & b\` |
2484
+ | \`\\|\\|\` | \`\\|\` | Logical OR | \`a \\|\\| b\` \u2192 \`a \\| b\` |
2485
+
2486
+ > **Warning:** Logical operators \`&&\` and \`||\` are converted to bitwise operators \`&\` and \`|\` in Noir.
2487
+
2488
+ ## Unary Operators
2489
+
2490
+ | JavaScript | Noir | Category | Example |
2491
+ |------------|------|----------|---------|
2492
+ | \`!\` | \`!\` | Logical NOT | \`!a\` \u2192 \`!a\` |
2493
+ | \`-\` (prefix) | \`-\` | Negation | \`-a\` \u2192 \`-a\` |
2494
+
2495
+ ## Type Mapping
2496
+
2497
+ | JavaScript Type | Noir Type | Notes |
2498
+ |-----------------|-----------|-------|
2499
+ | \`number\` | \`Field\` | Default numeric type |
2500
+ | \`bigint\` | \`Field\` | Converted to string |
2501
+ | \`boolean\` | \`bool\` | Only \`true\`/\`false\` literals |
2502
+ | \`string\` | String literal | Quoted in Noir |
2503
+ | \`number[]\` | \`[Field; N]\` | Fixed-size array |
2504
+
2505
+ ## Statement Mapping
2506
+
2507
+ | JavaScript | Noir | Notes |
2508
+ |------------|------|-------|
2509
+ | \`assert(cond)\` | \`assert(cond)\` | Direct mapping |
2510
+ | \`assert(cond, "msg")\` | \`assert(cond, "msg")\` | With message |
2511
+ | \`let x = expr\` | \`let x: Type = expr\` | Immutable |
2512
+ | \`let mut_x = expr\` | \`let mut x: Type = expr\` | Mutable (prefix stripped) |
2513
+ | \`const x = expr\` | \`let x: Type = expr\` | Treated as let |
2514
+ | \`mut_x = expr\` | \`x = expr\` | Assignment (mutable only) |
2515
+ | \`if (c) { } else { }\` | \`if c { } else { }\` | No parens in Noir |
2516
+ | \`for (let i=s; i<e; i++)\` | \`for i in s..e { }\` | Exclusive range |
2517
+ | \`for (let i=s; i<=e; i++)\` | \`for i in s..=e { }\` | Inclusive range |
2518
+
2519
+ ## Expression Mapping
2520
+
2521
+ | JavaScript | Noir | Notes |
2522
+ |------------|------|-------|
2523
+ | \`x\` | \`x\` | Identifier |
2524
+ | \`5\` | \`5\` | Number literal |
2525
+ | \`100n\` | \`100\` | BigInt to Field |
2526
+ | \`"str"\` | \`"str"\` | String literal |
2527
+ | \`0x1234\` | \`0x1234\` | Hex preserved |
2528
+ | \`true\` / \`false\` | \`true\` / \`false\` | Boolean |
2529
+ | \`[a, b, c]\` | \`[a, b, c]\` | Array literal |
2530
+ | \`arr[i]\` | \`arr[i]\` | Index access |
2531
+ | \`arr.length\` | \`arr.len()\` | Length method |
2532
+ | \`c ? a : b\` | \`if c { a } else { b }\` | Ternary \u2192 if expr |
2533
+ | \`a + b * c\` | \`a + b * c\` | Precedence preserved |
2534
+
2535
+ ## Function Parameter Mapping
2536
+
2537
+ \`\`\`typescript
2538
+ // JavaScript
2539
+ ([pub1, pub2], [priv1, priv2]) => { ... }
2540
+
2541
+ // Noir (private params first, then public with 'pub')
2542
+ fn main(priv1: Field, priv2: Field, pub1: pub Field, pub2: pub Field) { ... }
2543
+ \`\`\`
2544
+
2545
+ ## Variable Naming Convention
2546
+
2547
+ | JavaScript | Noir | Rule |
2548
+ |------------|------|------|
2549
+ | \`x\` | \`x\` | Regular immutable |
2550
+ | \`mut_x\` | \`mut x\` | Mutable (prefix stripped) |
2551
+ | \`mut_counter\` | \`mut counter\` | Mutable (prefix stripped) |
2552
+
2553
+ ## For Loop Patterns
2554
+
2555
+ | JavaScript | Noir | Range Type |
2556
+ |------------|------|------------|
2557
+ | \`for (let i = 0; i < 10; i++)\` | \`for i in 0..10\` | Exclusive |
2558
+ | \`for (let i = 1; i <= 5; i++)\` | \`for i in 1..=5\` | Inclusive |
2559
+ | \`for (let i = start; i < end; i++)\` | \`for i in start..end\` | Variable bounds |
2560
+ | \`for (let i = 0; i < n; i = i + 1)\` | \`for i in 0..n\` | Alternative update |
2561
+
2562
+ ## Unsupported Operators
2563
+
2564
+ | JavaScript | Status | Alternative |
2565
+ |------------|--------|-------------|
2566
+ | \`**\` (exponent) | Not supported | Use multiplication loop |
2567
+ | \`<<\` (left shift) | Not supported | N/A |
2568
+ | \`>>\` (right shift) | Not supported | N/A |
2569
+ | \`>>>\` (unsigned shift) | Not supported | N/A |
2570
+ | \`&\` (bitwise AND) | Use \`&&\` | Converted to \`&\` |
2571
+ | \`\\|\` (bitwise OR) | Use \`\\|\\|\` | Converted to \`\\|\` |
2572
+ | \`^\` (XOR) | Not supported | N/A |
2573
+ | \`~\` (NOT) | Not supported | N/A |
2574
+ | \`in\` | Not supported | N/A |
2575
+ | \`instanceof\` | Not supported | N/A |
2576
+ `;
2577
+
1538
2578
  // src/commands/init.ts
1539
2579
  async function initCommand(projectName, options) {
1540
2580
  let projectOptions;
@@ -1544,7 +2584,8 @@ async function initCommand(projectName, options) {
1544
2584
  template: options.template,
1545
2585
  provider: options.provider,
1546
2586
  skipInstall: options.skipInstall,
1547
- skipGit: options.skipGit
2587
+ skipGit: options.skipGit,
2588
+ aiTool: "none"
1548
2589
  };
1549
2590
  } else {
1550
2591
  projectOptions = await promptProjectOptions({
@@ -1556,14 +2597,14 @@ async function initCommand(projectName, options) {
1556
2597
  });
1557
2598
  }
1558
2599
  if (!projectOptions) {
1559
- console.log(pc4.yellow("\nOperation cancelled."));
2600
+ console.log(pc3.yellow("\nOperation cancelled."));
1560
2601
  process.exit(0);
1561
2602
  }
1562
2603
  const projectDir = path2.resolve(process.cwd(), projectOptions.projectName);
1563
2604
  if (await directoryExists(projectDir)) {
1564
2605
  if (!await isDirectoryEmpty(projectDir)) {
1565
2606
  console.log(
1566
- pc4.red(`
2607
+ pc3.red(`
1567
2608
  Error: Directory "${projectOptions.projectName}" already exists and is not empty.`)
1568
2609
  );
1569
2610
  process.exit(1);
@@ -1575,23 +2616,23 @@ Error: Directory "${projectOptions.projectName}" already exists and is not empty
1575
2616
  await progress.startThinking();
1576
2617
  await new Promise((r) => setTimeout(r, 800));
1577
2618
  progress.stopThinking();
1578
- console.log(pc4.bold("\n Scaffolding your ZK project...\n"));
2619
+ console.log(pc3.bold("\n Scaffolding your ZK project...\n"));
1579
2620
  await createProjectStructure(projectDir, projectOptions, progress);
1580
2621
  progress.showSuccess("Project structure created");
1581
2622
  } catch (error) {
1582
2623
  progress.showError("Failed to create project structure");
1583
- console.error(pc4.red("\n"), error);
2624
+ console.error(pc3.red("\n"), error);
1584
2625
  process.exit(1);
1585
2626
  }
1586
2627
  if (!projectOptions.skipGit) {
1587
- const gitSpinner = createSpinner("Initializing git repository...");
1588
- gitSpinner.start();
2628
+ const gitProgress = createGitProgress();
2629
+ gitProgress.start();
1589
2630
  try {
1590
2631
  execSync("git init", { cwd: projectDir, stdio: "ignore" });
1591
- gitSpinner.stop(true);
2632
+ gitProgress.stop(true);
1592
2633
  } catch {
1593
- gitSpinner.stop(false);
1594
- console.log(pc4.yellow(" Warning: Failed to initialize git repository"));
2634
+ gitProgress.stop(false);
2635
+ console.log(pc3.yellow(" Warning: Failed to initialize git repository"));
1595
2636
  }
1596
2637
  }
1597
2638
  if (!projectOptions.skipInstall) {
@@ -1602,7 +2643,7 @@ Error: Directory "${projectOptions.projectName}" already exists and is not empty
1602
2643
  installProgress.stop(true);
1603
2644
  } catch {
1604
2645
  installProgress.stop(false);
1605
- console.log(pc4.yellow(' Run "npm install" manually.'));
2646
+ console.log(pc3.yellow(' Run "npm install" manually.'));
1606
2647
  }
1607
2648
  }
1608
2649
  printSuccessMessage(projectOptions);
@@ -1648,34 +2689,47 @@ async function createProjectStructure(projectDir, options, progress) {
1648
2689
  }
1649
2690
  files.push(["circuits/index.ts", generateCircuitsIndex(options.template)]);
1650
2691
  files.push(["circuits/types.d.ts", generateCircuitTypes()]);
2692
+ const hasSkills = options.aiTool === "claude";
1651
2693
  for (let i = 0; i < files.length; i++) {
1652
2694
  const [relativePath, content] = files[i];
1653
2695
  await writeFile(path2.join(projectDir, relativePath), content);
1654
- await progress.reportFile(relativePath, i === files.length - 1);
2696
+ await progress.reportFile(relativePath, !hasSkills && i === files.length - 1);
2697
+ }
2698
+ if (hasSkills) {
2699
+ const skillFiles = generateClaudeSkills();
2700
+ const skillEntries = Object.entries(skillFiles);
2701
+ for (let i = 0; i < skillEntries.length; i++) {
2702
+ const [relativePath, content] = skillEntries[i];
2703
+ await writeFile(path2.join(projectDir, relativePath), content);
2704
+ await progress.reportFile(relativePath, i === skillEntries.length - 1);
2705
+ }
1655
2706
  }
1656
2707
  }
1657
2708
  function printSuccessMessage(options) {
1658
2709
  console.log();
1659
- console.log(pc4.green("\u2713") + " Project created successfully!");
2710
+ console.log(pc3.green("\u2713") + " Project created successfully!");
2711
+ if (options.aiTool === "claude") {
2712
+ console.log(pc3.green("\u2713") + " Claude Code skill installed " + pc3.dim("(.claude/skills/izi-noir-circuit-patterns)"));
2713
+ }
1660
2714
  console.log();
1661
2715
  console.log("Next steps:");
1662
2716
  console.log();
1663
- console.log(pc4.cyan(` cd ${options.projectName}`));
2717
+ console.log(pc3.cyan(` cd ${options.projectName}`));
1664
2718
  if (options.skipInstall) {
1665
- console.log(pc4.cyan(" npm install"));
2719
+ console.log(pc3.cyan(" npm install"));
1666
2720
  }
1667
- console.log(pc4.cyan(" npm run dev"));
2721
+ console.log(pc3.cyan(" npm run dev"));
1668
2722
  console.log();
1669
- console.log("Then open " + pc4.blue("http://localhost:5173") + " in your browser.");
2723
+ console.log("Then open " + pc3.blue("http://localhost:5173") + " in your browser.");
1670
2724
  console.log();
1671
2725
  console.log("To add circuits:");
1672
2726
  console.log();
1673
- console.log(pc4.dim(" 1. Create a new circuit in circuits/*.ts"));
1674
- console.log(pc4.dim(" 2. Export it from circuits/index.ts"));
1675
- console.log(pc4.dim(" 3. Add it to CIRCUITS array in src/App.tsx"));
2727
+ console.log(pc3.dim(" 1. Create a new circuit in circuits/*.ts"));
2728
+ console.log(pc3.dim(" 2. Export it from circuits/index.ts"));
2729
+ console.log(pc3.dim(" 3. Add it to CIRCUITS array in src/App.tsx"));
1676
2730
  console.log();
1677
2731
  console.log(
1678
- pc4.dim("Learn more: ") + pc4.blue("https://github.com/izi-noir/izi-noir")
2732
+ pc3.dim("Learn more: ") + pc3.blue("https://github.com/izi-noir/izi-noir")
1679
2733
  );
1680
2734
  console.log();
1681
2735
  }