@qamposer/react 0.1.0

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/dist/index.cjs ADDED
@@ -0,0 +1,3028 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ // src/context/QamposerProvider.tsx
7
+ var QamposerContext = react.createContext(null);
8
+
9
+ // src/utils/openqasm.ts
10
+ var QASM_HEADER = 'OPENQASM 2.0;\ninclude "qelib1.inc";\n';
11
+ var GATE_TO_QASM = {
12
+ H: "h",
13
+ X: "x",
14
+ Y: "y",
15
+ Z: "z",
16
+ CNOT: "cx",
17
+ RX: "rx",
18
+ RY: "ry",
19
+ RZ: "rz"
20
+ };
21
+ var QASM_TO_GATE = {
22
+ h: "H",
23
+ x: "X",
24
+ y: "Y",
25
+ z: "Z",
26
+ cx: "CNOT",
27
+ rx: "RX",
28
+ ry: "RY",
29
+ rz: "RZ"
30
+ };
31
+ function circuitToQasm(circuit) {
32
+ const { qubits, gates } = circuit;
33
+ const lines = [QASM_HEADER];
34
+ lines.push(`qreg q[${qubits}];`);
35
+ lines.push(`creg c[${qubits}];`);
36
+ if (gates.length === 0) {
37
+ return lines.join("\n") + "\n";
38
+ }
39
+ lines.push("");
40
+ const sortedGates = [...gates].sort((a, b) => a.position - b.position);
41
+ for (const gate of sortedGates) {
42
+ const instruction = gateToQasmInstruction(gate);
43
+ if (instruction) {
44
+ lines.push(instruction);
45
+ }
46
+ }
47
+ return lines.join("\n") + "\n";
48
+ }
49
+ function gateToQasmInstruction(gate) {
50
+ const qasmGate = GATE_TO_QASM[gate.type];
51
+ if (!qasmGate) return null;
52
+ if (gate.type === "CNOT") {
53
+ if (gate.control !== void 0 && gate.target !== void 0) {
54
+ return `cx q[${gate.control}], q[${gate.target}];`;
55
+ }
56
+ return null;
57
+ }
58
+ if (["RX", "RY", "RZ"].includes(gate.type)) {
59
+ const param = gate.parameter ?? 0;
60
+ return `${qasmGate}(${formatParameter(param)}) q[${gate.qubit}];`;
61
+ }
62
+ if (gate.qubit !== void 0) {
63
+ return `${qasmGate} q[${gate.qubit}];`;
64
+ }
65
+ return null;
66
+ }
67
+ function formatParameter(value) {
68
+ const pi = Math.PI;
69
+ const tolerance = 1e-4;
70
+ const fractions = [
71
+ { val: pi, str: "pi" },
72
+ { val: -pi, str: "-pi" },
73
+ { val: pi / 2, str: "pi/2" },
74
+ { val: -pi / 2, str: "-pi/2" },
75
+ { val: pi / 4, str: "pi/4" },
76
+ { val: -pi / 4, str: "-pi/4" },
77
+ { val: pi / 3, str: "pi/3" },
78
+ { val: -pi / 3, str: "-pi/3" },
79
+ { val: 2 * pi / 3, str: "2*pi/3" },
80
+ { val: -2 * pi / 3, str: "-2*pi/3" }
81
+ ];
82
+ for (const { val, str } of fractions) {
83
+ if (Math.abs(value - val) < tolerance) {
84
+ return str;
85
+ }
86
+ }
87
+ return value.toFixed(6).replace(/\.?0+$/, "");
88
+ }
89
+ function getGateQubits(gate) {
90
+ if (gate.type === "CNOT" && gate.control !== void 0 && gate.target !== void 0) {
91
+ const minQubit = Math.min(gate.control, gate.target);
92
+ const maxQubit = Math.max(gate.control, gate.target);
93
+ const qubits = [];
94
+ for (let q = minQubit; q <= maxQubit; q++) {
95
+ qubits.push(q);
96
+ }
97
+ return qubits;
98
+ }
99
+ return gate.qubit !== void 0 ? [gate.qubit] : [];
100
+ }
101
+ function compactGates(gatesToCompact) {
102
+ const qubitMinPositions = /* @__PURE__ */ new Map();
103
+ const sorted = [...gatesToCompact].sort((a, b) => a.position - b.position);
104
+ return sorted.map((gate) => {
105
+ const gateQubits = getGateQubits(gate);
106
+ let newPosition = 0;
107
+ gateQubits.forEach((q) => {
108
+ const minPos = qubitMinPositions.get(q) || 0;
109
+ newPosition = Math.max(newPosition, minPos);
110
+ });
111
+ gateQubits.forEach((q) => {
112
+ qubitMinPositions.set(q, newPosition + 1);
113
+ });
114
+ return { ...gate, position: newPosition };
115
+ });
116
+ }
117
+ function validateGateQubits(gate, qubits, lineNum) {
118
+ const gateQubits = getGateQubits(gate);
119
+ for (const qubitIndex of gateQubits) {
120
+ if (qubitIndex >= qubits) {
121
+ return `Line ${lineNum}: Qubit index q[${qubitIndex}] exceeds defined qubits (q[0]-q[${qubits - 1}])`;
122
+ }
123
+ if (qubitIndex < 0) {
124
+ return `Line ${lineNum}: Invalid qubit index q[${qubitIndex}]`;
125
+ }
126
+ }
127
+ return null;
128
+ }
129
+ function qasmToCircuit(qasm) {
130
+ const errors = [];
131
+ const gates = [];
132
+ let qubits = 2;
133
+ const lines = qasm.split("\n");
134
+ let position = 0;
135
+ for (const line of lines) {
136
+ const trimmed = line.trim();
137
+ const qregMatch = trimmed.match(/^qreg\s+q\[(\d+)\];?$/);
138
+ if (qregMatch) {
139
+ qubits = parseInt(qregMatch[1], 10);
140
+ break;
141
+ }
142
+ }
143
+ for (let lineNum = 0; lineNum < lines.length; lineNum++) {
144
+ const line = lines[lineNum].trim();
145
+ if (!line || line.startsWith("//") || line.startsWith("OPENQASM") || line.startsWith("include")) {
146
+ continue;
147
+ }
148
+ if (line.match(/^qreg\s+/) || line.match(/^creg\s+/)) {
149
+ continue;
150
+ }
151
+ const gate = parseGateInstruction(line, position, lineNum + 1, errors);
152
+ if (gate) {
153
+ const qubitError = validateGateQubits(gate, qubits, lineNum + 1);
154
+ if (qubitError) {
155
+ errors.push(qubitError);
156
+ continue;
157
+ }
158
+ gates.push(gate);
159
+ position++;
160
+ }
161
+ }
162
+ const compactedGates = compactGates(gates);
163
+ return {
164
+ success: errors.length === 0,
165
+ circuit: { qubits, gates: compactedGates },
166
+ errors
167
+ };
168
+ }
169
+ function parseGateInstruction(line, position, lineNum, errors) {
170
+ if (!line.endsWith(";")) {
171
+ errors.push(`Line ${lineNum}: Missing semicolon at end of statement`);
172
+ return null;
173
+ }
174
+ const instruction = line.replace(/;$/, "").trim();
175
+ const cxMatch = instruction.match(/^cx\s+q\[(\d+)\]\s*,\s*q\[(\d+)\]$/);
176
+ if (cxMatch) {
177
+ return {
178
+ id: generateGateId(),
179
+ type: "CNOT",
180
+ control: parseInt(cxMatch[1], 10),
181
+ target: parseInt(cxMatch[2], 10),
182
+ position
183
+ };
184
+ }
185
+ const rotMatch = instruction.match(/^(rx|ry|rz)\s*\(\s*([^)]+)\s*\)\s*q\[(\d+)\]$/);
186
+ if (rotMatch) {
187
+ const gateType = QASM_TO_GATE[rotMatch[1]];
188
+ const paramValue = parseParameter(rotMatch[2]);
189
+ if (gateType && paramValue !== null) {
190
+ return {
191
+ id: generateGateId(),
192
+ type: gateType,
193
+ qubit: parseInt(rotMatch[3], 10),
194
+ parameter: paramValue,
195
+ position
196
+ };
197
+ }
198
+ }
199
+ const singleMatch = instruction.match(/^(h|x|y|z)\s+q\[(\d+)\]$/);
200
+ if (singleMatch) {
201
+ const gateType = QASM_TO_GATE[singleMatch[1]];
202
+ if (gateType) {
203
+ return {
204
+ id: generateGateId(),
205
+ type: gateType,
206
+ qubit: parseInt(singleMatch[2], 10),
207
+ position
208
+ };
209
+ }
210
+ }
211
+ if (instruction && !instruction.match(/^(barrier|measure|reset)/)) {
212
+ errors.push(`Line ${lineNum}: Unknown instruction "${instruction}"`);
213
+ }
214
+ return null;
215
+ }
216
+ function parseParameter(str) {
217
+ const trimmed = str.trim();
218
+ const piPatterns = [
219
+ [/^-?pi$/, (m) => m[0].startsWith("-") ? -Math.PI : Math.PI],
220
+ [/^pi\/(\d+)$/, (m) => Math.PI / parseInt(m[1], 10)],
221
+ [/^-pi\/(\d+)$/, (m) => -Math.PI / parseInt(m[1], 10)],
222
+ [/^(-?\d+)\*?pi\/(\d+)$/, (m) => parseInt(m[1], 10) * Math.PI / parseInt(m[2], 10)]
223
+ ];
224
+ for (const [pattern, calc] of piPatterns) {
225
+ const match = trimmed.match(pattern);
226
+ if (match) {
227
+ return calc(match);
228
+ }
229
+ }
230
+ const num = parseFloat(trimmed);
231
+ return isNaN(num) ? null : num;
232
+ }
233
+ function generateGateId() {
234
+ return `gate-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
235
+ }
236
+ function createDefaultCircuit(qubits = 2) {
237
+ return {
238
+ qubits: Math.max(2, Math.min(qubits, 5)),
239
+ // Clamp to 2-5 qubits
240
+ gates: []
241
+ };
242
+ }
243
+
244
+ // src/adapters/noop.ts
245
+ var noopAdapter = {
246
+ name: "Editor Only",
247
+ async simulate() {
248
+ throw new Error(
249
+ "Simulation is not available in editor-only mode. Provide a simulation adapter to enable this feature."
250
+ );
251
+ },
252
+ async isAvailable() {
253
+ return false;
254
+ }
255
+ };
256
+ var DEFAULT_CONFIG = {
257
+ maxQubits: 5,
258
+ maxGates: 500,
259
+ maxShots: 1e4
260
+ };
261
+ var DEFAULT_CIRCUIT = {
262
+ qubits: 2,
263
+ gates: []
264
+ };
265
+ function QamposerProvider({
266
+ circuit: controlledCircuit,
267
+ onCircuitChange,
268
+ defaultCircuit = DEFAULT_CIRCUIT,
269
+ adapter = noopAdapter,
270
+ onSimulationComplete,
271
+ config: userConfig,
272
+ children
273
+ }) {
274
+ const config = react.useMemo(() => ({ ...DEFAULT_CONFIG, ...userConfig }), [userConfig]);
275
+ const isControlled = controlledCircuit !== void 0;
276
+ const [internalCircuit, setInternalCircuit] = react.useState(defaultCircuit);
277
+ const circuit = isControlled ? controlledCircuit : internalCircuit;
278
+ const updateCircuit = react.useCallback(
279
+ (newCircuit) => {
280
+ if (isControlled) {
281
+ onCircuitChange?.(newCircuit);
282
+ } else {
283
+ setInternalCircuit(newCircuit);
284
+ onCircuitChange?.(newCircuit);
285
+ }
286
+ },
287
+ [isControlled, onCircuitChange]
288
+ );
289
+ const [result, setResult] = react.useState(null);
290
+ const [status, setStatus] = react.useState("idle");
291
+ const [error, setError] = react.useState(null);
292
+ const [editingGate, setEditingGate] = react.useState(null);
293
+ const [qasmCode, setQasmCodeState] = react.useState(() => circuitToQasm(circuit));
294
+ const [parseError, setParseError] = react.useState(null);
295
+ const isUpdatingFromCode = react.useRef(false);
296
+ react.useEffect(() => {
297
+ if (isUpdatingFromCode.current) {
298
+ isUpdatingFromCode.current = false;
299
+ return;
300
+ }
301
+ setQasmCodeState(circuitToQasm(circuit));
302
+ setParseError(null);
303
+ }, [circuit]);
304
+ const setQasmCode = react.useCallback(
305
+ (code) => {
306
+ setQasmCodeState(code);
307
+ const parseResult = qasmToCircuit(code);
308
+ if (parseResult.errors.length > 0) {
309
+ setParseError(parseResult.errors[0]);
310
+ } else {
311
+ setParseError(null);
312
+ }
313
+ if (parseResult.circuit && (parseResult.success || parseResult.circuit.gates.length > 0)) {
314
+ isUpdatingFromCode.current = true;
315
+ updateCircuit(parseResult.circuit);
316
+ }
317
+ },
318
+ [updateCircuit]
319
+ );
320
+ const [canSimulate, setCanSimulate] = react.useState(false);
321
+ react.useEffect(() => {
322
+ adapter.isAvailable().then(setCanSimulate);
323
+ }, [adapter]);
324
+ const addGate = react.useCallback(
325
+ (gate) => {
326
+ if (circuit.gates.length >= config.maxGates) {
327
+ console.warn(`Maximum gate limit (${config.maxGates}) reached`);
328
+ return;
329
+ }
330
+ const newGate = {
331
+ ...gate,
332
+ id: generateGateId()
333
+ };
334
+ updateCircuit({
335
+ ...circuit,
336
+ gates: [...circuit.gates, newGate]
337
+ });
338
+ },
339
+ [circuit, config.maxGates, updateCircuit]
340
+ );
341
+ const removeGate = react.useCallback(
342
+ (id) => {
343
+ updateCircuit({
344
+ ...circuit,
345
+ gates: circuit.gates.filter((g) => g.id !== id)
346
+ });
347
+ if (editingGate?.id === id) {
348
+ setEditingGate(null);
349
+ }
350
+ },
351
+ [circuit, editingGate, updateCircuit]
352
+ );
353
+ const updateGate = react.useCallback(
354
+ (id, updates) => {
355
+ const targetGate = circuit.gates.find((g) => g.id === id);
356
+ if (!targetGate) return;
357
+ const updatedGate = { ...targetGate, ...updates };
358
+ const getGateQubits2 = (gate) => {
359
+ if (gate.type === "CNOT" && gate.control !== void 0 && gate.target !== void 0) {
360
+ const minQ = Math.min(gate.control, gate.target);
361
+ const maxQ = Math.max(gate.control, gate.target);
362
+ const qubits = [];
363
+ for (let q = minQ; q <= maxQ; q++) {
364
+ qubits.push(q);
365
+ }
366
+ return qubits;
367
+ }
368
+ return gate.qubit !== void 0 ? [gate.qubit] : [];
369
+ };
370
+ const isCnotUpdate = targetGate.type === "CNOT" && (updates.control !== void 0 || updates.target !== void 0);
371
+ let updatedGates;
372
+ if (isCnotUpdate) {
373
+ const newQubits = getGateQubits2(updatedGate);
374
+ const gatePosition = updatedGate.position;
375
+ updatedGates = circuit.gates.map((g) => {
376
+ if (g.id === id) {
377
+ return updatedGate;
378
+ }
379
+ if (g.position === gatePosition) {
380
+ const gateQubits = getGateQubits2(g);
381
+ const hasConflict = gateQubits.some((q) => newQubits.includes(q));
382
+ if (hasConflict) {
383
+ return { ...g, position: g.position + 1 };
384
+ }
385
+ }
386
+ return g;
387
+ });
388
+ updatedGates = compactGates(updatedGates);
389
+ } else {
390
+ updatedGates = circuit.gates.map((g) => g.id === id ? updatedGate : g);
391
+ }
392
+ updateCircuit({
393
+ ...circuit,
394
+ gates: updatedGates
395
+ });
396
+ if (editingGate?.id === id) {
397
+ setEditingGate({ ...editingGate, ...updates });
398
+ }
399
+ },
400
+ [circuit, editingGate, updateCircuit]
401
+ );
402
+ const updateGates = react.useCallback(
403
+ (gates) => {
404
+ updateCircuit({
405
+ ...circuit,
406
+ gates
407
+ });
408
+ },
409
+ [circuit, updateCircuit]
410
+ );
411
+ const setQubits = react.useCallback(
412
+ (count) => {
413
+ const newCount = Math.min(Math.max(1, count), config.maxQubits);
414
+ updateCircuit({
415
+ ...circuit,
416
+ qubits: newCount
417
+ });
418
+ },
419
+ [circuit, config.maxQubits, updateCircuit]
420
+ );
421
+ const addQubit = react.useCallback(() => {
422
+ if (circuit.qubits < config.maxQubits) {
423
+ updateCircuit({
424
+ ...circuit,
425
+ qubits: circuit.qubits + 1
426
+ });
427
+ }
428
+ }, [circuit, config.maxQubits, updateCircuit]);
429
+ const removeQubit = react.useCallback(
430
+ (qubitIndex) => {
431
+ if (circuit.qubits <= 1) {
432
+ return;
433
+ }
434
+ const removedIndex = qubitIndex ?? circuit.qubits - 1;
435
+ const updatedGates = circuit.gates.filter((gate) => {
436
+ if (gate.type === "CNOT") {
437
+ return gate.control !== removedIndex && gate.target !== removedIndex;
438
+ }
439
+ return gate.qubit !== removedIndex;
440
+ }).map((gate) => {
441
+ if (gate.type === "CNOT") {
442
+ return {
443
+ ...gate,
444
+ control: gate.control !== void 0 && gate.control > removedIndex ? gate.control - 1 : gate.control,
445
+ target: gate.target !== void 0 && gate.target > removedIndex ? gate.target - 1 : gate.target
446
+ };
447
+ }
448
+ return {
449
+ ...gate,
450
+ qubit: gate.qubit !== void 0 && gate.qubit > removedIndex ? gate.qubit - 1 : gate.qubit
451
+ };
452
+ });
453
+ const compactedGates = compactGates(updatedGates);
454
+ updateCircuit({
455
+ ...circuit,
456
+ qubits: circuit.qubits - 1,
457
+ gates: compactedGates
458
+ });
459
+ setResult(null);
460
+ },
461
+ [circuit, updateCircuit]
462
+ );
463
+ const clearCircuit = react.useCallback(() => {
464
+ updateCircuit({
465
+ qubits: circuit.qubits,
466
+ gates: []
467
+ });
468
+ setResult(null);
469
+ setEditingGate(null);
470
+ }, [circuit.qubits, updateCircuit]);
471
+ const importQasm = react.useCallback(
472
+ (code) => {
473
+ setQasmCodeState(code);
474
+ const parseResult = qasmToCircuit(code);
475
+ if (parseResult.errors.length > 0) {
476
+ setParseError(parseResult.errors[0]);
477
+ } else {
478
+ setParseError(null);
479
+ }
480
+ if (parseResult.circuit && (parseResult.success || parseResult.circuit.gates.length > 0)) {
481
+ isUpdatingFromCode.current = true;
482
+ updateCircuit(parseResult.circuit);
483
+ }
484
+ return parseResult;
485
+ },
486
+ [updateCircuit]
487
+ );
488
+ const exportQasm = react.useCallback(() => {
489
+ return circuitToQasm(circuit);
490
+ }, [circuit]);
491
+ const simulate = react.useCallback(
492
+ async (shots = 1024, profile) => {
493
+ if (!canSimulate) {
494
+ throw new Error("Simulation adapter is not available");
495
+ }
496
+ if (circuit.gates.length === 0) {
497
+ throw new Error("Circuit has no gates");
498
+ }
499
+ const validShots = Math.min(Math.max(1, shots), config.maxShots);
500
+ setStatus("simulating");
501
+ setError(null);
502
+ try {
503
+ const request = {
504
+ qubits: circuit.qubits,
505
+ gates: circuit.gates.map(({ id: _, ...gate }) => gate),
506
+ shots: validShots,
507
+ profile
508
+ };
509
+ const simulationResult = await adapter.simulate(request);
510
+ setResult(simulationResult);
511
+ setStatus("idle");
512
+ const event = {
513
+ result: simulationResult,
514
+ circuit: { ...circuit },
515
+ qasm: circuitToQasm(circuit)
516
+ };
517
+ onSimulationComplete?.(event);
518
+ return simulationResult;
519
+ } catch (err) {
520
+ const simulationError = err instanceof Error ? err : new Error(String(err));
521
+ setError(simulationError);
522
+ setStatus("error");
523
+ throw simulationError;
524
+ }
525
+ },
526
+ [adapter, canSimulate, circuit, config.maxShots, onSimulationComplete]
527
+ );
528
+ const contextValue = react.useMemo(
529
+ () => ({
530
+ // State
531
+ circuit,
532
+ result,
533
+ status,
534
+ error,
535
+ qasmCode,
536
+ parseError,
537
+ editingGate,
538
+ // Circuit Actions
539
+ addGate,
540
+ removeGate,
541
+ updateGate,
542
+ updateGates,
543
+ setQubits,
544
+ addQubit,
545
+ removeQubit,
546
+ clearCircuit,
547
+ // Gate Editing
548
+ setEditingGate,
549
+ // QASM
550
+ importQasm,
551
+ exportQasm,
552
+ setQasmCode,
553
+ // Simulation
554
+ simulate,
555
+ canSimulate,
556
+ // Adapter
557
+ adapter,
558
+ // Config
559
+ config
560
+ }),
561
+ [
562
+ circuit,
563
+ result,
564
+ status,
565
+ error,
566
+ qasmCode,
567
+ parseError,
568
+ editingGate,
569
+ addGate,
570
+ removeGate,
571
+ updateGate,
572
+ updateGates,
573
+ setQubits,
574
+ addQubit,
575
+ removeQubit,
576
+ clearCircuit,
577
+ importQasm,
578
+ exportQasm,
579
+ simulate,
580
+ canSimulate,
581
+ adapter,
582
+ config
583
+ ]
584
+ );
585
+ return /* @__PURE__ */ jsxRuntime.jsx(QamposerContext.Provider, { value: contextValue, children });
586
+ }
587
+ var ThemeContext = react.createContext(null);
588
+ var THEME_STORAGE_KEY = "qamposer-theme";
589
+ var themes = {
590
+ dark: {
591
+ "--qamposer-bg-primary": "#161616",
592
+ "--qamposer-bg-secondary": "#262626",
593
+ "--qamposer-bg-tertiary": "#1a1a1a",
594
+ "--qamposer-text-primary": "#f4f4f4",
595
+ "--qamposer-text-secondary": "#a8a8a8",
596
+ "--qamposer-text-tertiary": "#6f6f6f",
597
+ "--qamposer-border": "#525252",
598
+ "--qamposer-border-strong": "#6f6f6f",
599
+ "--qamposer-accent": "#4285f4",
600
+ "--qamposer-accent-hover": "#3367d6",
601
+ "--qamposer-hover": "rgba(255, 255, 255, 0.1)",
602
+ "--qamposer-selected": "rgba(66, 133, 244, 0.2)",
603
+ "--qamposer-error": "#da1e28"
604
+ },
605
+ light: {
606
+ "--qamposer-bg-primary": "#ffffff",
607
+ "--qamposer-bg-secondary": "#f4f4f4",
608
+ "--qamposer-bg-tertiary": "#e8e8e8",
609
+ "--qamposer-text-primary": "#161616",
610
+ "--qamposer-text-secondary": "#525252",
611
+ "--qamposer-text-tertiary": "#8d8d8d",
612
+ "--qamposer-border": "#d1d1d1",
613
+ "--qamposer-border-strong": "#a8a8a8",
614
+ "--qamposer-accent": "#0f62fe",
615
+ "--qamposer-accent-hover": "#0043ce",
616
+ "--qamposer-hover": "rgba(0, 0, 0, 0.05)",
617
+ "--qamposer-selected": "rgba(15, 98, 254, 0.1)",
618
+ "--qamposer-error": "#da1e28"
619
+ }
620
+ };
621
+ function ThemeProvider({ children, defaultTheme = "dark" }) {
622
+ const [theme, setThemeState] = react.useState(() => {
623
+ if (typeof window !== "undefined") {
624
+ const stored = localStorage.getItem(THEME_STORAGE_KEY);
625
+ if (stored === "light" || stored === "dark") {
626
+ return stored;
627
+ }
628
+ }
629
+ return defaultTheme;
630
+ });
631
+ const applyTheme = react.useCallback((newTheme) => {
632
+ const themeVars = themes[newTheme];
633
+ Object.entries(themeVars).forEach(([key, value]) => {
634
+ document.documentElement.style.setProperty(key, value);
635
+ });
636
+ }, []);
637
+ react.useEffect(() => {
638
+ applyTheme(theme);
639
+ localStorage.setItem(THEME_STORAGE_KEY, theme);
640
+ }, [theme, applyTheme]);
641
+ const toggleTheme = react.useCallback(() => {
642
+ setThemeState((prev) => prev === "dark" ? "light" : "dark");
643
+ }, []);
644
+ const setTheme = react.useCallback((newTheme) => {
645
+ setThemeState(newTheme);
646
+ }, []);
647
+ return /* @__PURE__ */ jsxRuntime.jsx(ThemeContext.Provider, { value: { theme, toggleTheme, setTheme }, children });
648
+ }
649
+ function useTheme() {
650
+ const context = react.useContext(ThemeContext);
651
+ if (!context) {
652
+ throw new Error("useTheme must be used within a ThemeProvider");
653
+ }
654
+ return context;
655
+ }
656
+ function useQamposer() {
657
+ const context = react.useContext(QamposerContext);
658
+ if (context === null) {
659
+ throw new Error(
660
+ "useQamposer must be used within a QamposerProvider. Wrap your component tree with <QamposerProvider>."
661
+ );
662
+ }
663
+ return context;
664
+ }
665
+
666
+ // src/components/Operations/Operations.scss
667
+ var css = `.operations {
668
+ width: 100%;
669
+ height: 100%;
670
+ background: var(--qamposer-bg-secondary, #262626);
671
+ display: flex;
672
+ flex-direction: column;
673
+ overflow: hidden;
674
+ }
675
+ .operations__header {
676
+ padding: 1rem;
677
+ border-bottom: 1px solid var(--qamposer-border, #525252);
678
+ display: flex;
679
+ justify-content: space-between;
680
+ align-items: center;
681
+ }
682
+ .operations__header h3 {
683
+ margin: 0;
684
+ font-size: 1rem;
685
+ font-weight: 600;
686
+ color: var(--qamposer-text-primary, #f4f4f4);
687
+ }
688
+ .operations__close-btn {
689
+ display: flex;
690
+ align-items: center;
691
+ justify-content: center;
692
+ width: 32px;
693
+ height: 32px;
694
+ padding: 0;
695
+ border: none;
696
+ background: transparent;
697
+ border-radius: 4px;
698
+ color: var(--qamposer-text-secondary, #a8a8a8);
699
+ cursor: pointer;
700
+ transition: all 0.15s;
701
+ }
702
+ .operations__close-btn:hover {
703
+ background: var(--qamposer-hover, rgba(255, 255, 255, 0.1));
704
+ color: var(--qamposer-text-primary, #f4f4f4);
705
+ }
706
+ .operations__sections {
707
+ flex: 1;
708
+ overflow-y: auto;
709
+ }
710
+ .operations__section {
711
+ border-bottom: 1px solid var(--qamposer-border, #525252);
712
+ }
713
+ .operations__section-header {
714
+ width: 100%;
715
+ display: flex;
716
+ align-items: center;
717
+ gap: 0.5rem;
718
+ padding: 0.75rem 1rem;
719
+ border: none;
720
+ background: transparent;
721
+ color: var(--qamposer-text-primary, #f4f4f4);
722
+ font-size: 0.875rem;
723
+ font-weight: 500;
724
+ cursor: pointer;
725
+ transition: background-color 0.15s;
726
+ text-align: left;
727
+ }
728
+ .operations__section-header:hover {
729
+ background: var(--qamposer-hover, rgba(255, 255, 255, 0.1));
730
+ }
731
+ .operations__grid {
732
+ display: grid;
733
+ grid-template-columns: repeat(3, 32px);
734
+ gap: 0.5rem;
735
+ padding: 0.75rem 1rem 1rem;
736
+ justify-content: start;
737
+ }
738
+ .operations__gate {
739
+ width: 32px;
740
+ height: 32px;
741
+ display: flex;
742
+ justify-content: center;
743
+ align-items: center;
744
+ border-radius: 4px;
745
+ cursor: grab;
746
+ transition: all 0.2s;
747
+ user-select: none;
748
+ }
749
+ .operations__gate:hover {
750
+ transform: scale(1.05);
751
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
752
+ }
753
+ .operations__gate:active {
754
+ cursor: grabbing;
755
+ transform: scale(0.95);
756
+ }
757
+ .operations__gate--cnot {
758
+ background: transparent;
759
+ padding: 0;
760
+ }
761
+ .operations__gate--cnot svg {
762
+ display: block;
763
+ border-radius: 4px;
764
+ overflow: hidden;
765
+ }
766
+ .operations__gate--disabled {
767
+ opacity: 0.4;
768
+ cursor: not-allowed;
769
+ }
770
+ .operations__gate--disabled:hover {
771
+ transform: none;
772
+ box-shadow: none;
773
+ }
774
+ .operations__gate--disabled:active {
775
+ cursor: not-allowed;
776
+ transform: none;
777
+ }
778
+ .operations__gate-label {
779
+ font-size: 0.875rem;
780
+ font-weight: 600;
781
+ letter-spacing: 0.5px;
782
+ color: white;
783
+ }
784
+ .operations--editor .operations__header h3 {
785
+ font-size: 1.125rem;
786
+ }
787
+ .operations__content {
788
+ flex: 1;
789
+ overflow-y: auto;
790
+ padding: 1.5rem;
791
+ }
792
+ .operations__field label {
793
+ display: block;
794
+ margin-bottom: 0.5rem;
795
+ font-size: 0.875rem;
796
+ font-weight: 500;
797
+ color: var(--qamposer-text-primary, #f4f4f4);
798
+ }
799
+ .operations__input {
800
+ width: 100%;
801
+ padding: 0.75rem 1rem;
802
+ border: 1px solid var(--qamposer-border, #525252);
803
+ border-radius: 4px;
804
+ background: var(--qamposer-bg-primary, #161616);
805
+ color: var(--qamposer-text-primary, #f4f4f4);
806
+ font-size: 0.875rem;
807
+ font-family: "IBM Plex Mono", "Menlo", "Monaco", monospace;
808
+ outline: none;
809
+ transition: border-color 0.15s;
810
+ }
811
+ .operations__input:focus {
812
+ border-color: var(--qamposer-accent, #4285f4);
813
+ }
814
+ .operations__input::placeholder {
815
+ color: var(--qamposer-text-secondary, #a8a8a8);
816
+ }
817
+ .operations__helper {
818
+ margin-top: 0.5rem;
819
+ font-size: 0.75rem;
820
+ color: var(--qamposer-text-secondary, #a8a8a8);
821
+ line-height: 1.4;
822
+ }
823
+ .operations__field + .operations__field {
824
+ margin-top: 1rem;
825
+ }
826
+ .operations__select {
827
+ width: 100%;
828
+ padding: 0.75rem 1rem;
829
+ border: 1px solid var(--qamposer-border, #525252);
830
+ border-radius: 4px;
831
+ background: var(--qamposer-bg-primary, #161616);
832
+ color: var(--qamposer-text-primary, #f4f4f4);
833
+ font-size: 0.875rem;
834
+ font-family: "IBM Plex Mono", "Menlo", "Monaco", monospace;
835
+ outline: none;
836
+ cursor: pointer;
837
+ transition: border-color 0.15s;
838
+ }
839
+ .operations__select:focus {
840
+ border-color: var(--qamposer-accent, #4285f4);
841
+ }
842
+ .operations__apply-btn {
843
+ width: 100%;
844
+ margin-top: 1.5rem;
845
+ padding: 0.75rem 1rem;
846
+ border: none;
847
+ border-radius: 4px;
848
+ background: var(--qamposer-accent, #4285f4);
849
+ color: white;
850
+ font-size: 0.875rem;
851
+ font-weight: 500;
852
+ cursor: pointer;
853
+ transition: all 0.15s;
854
+ }
855
+ .operations__apply-btn:hover:not(:disabled) {
856
+ background: var(--qamposer-accent-hover, #3367d6);
857
+ }
858
+ .operations__apply-btn:disabled {
859
+ opacity: 0.5;
860
+ cursor: not-allowed;
861
+ }
862
+ .operations__error {
863
+ margin-top: 0.5rem;
864
+ font-size: 0.75rem;
865
+ color: var(--qamposer-error, #da1e28);
866
+ }`;
867
+ document.head.appendChild(document.createElement("style")).appendChild(document.createTextNode(css));
868
+ var GATE_DEFINITIONS = [
869
+ { type: "H", label: "H", description: "Hadamard", category: "single", color: "#fa4d56" },
870
+ { type: "X", label: "X", description: "Pauli-X (NOT)", category: "single", color: "#002d9c" },
871
+ { type: "Y", label: "Y", description: "Pauli-Y", category: "single", color: "#9f1853" },
872
+ { type: "Z", label: "Z", description: "Pauli-Z", category: "single", color: "#33b1ff" },
873
+ { type: "RX", label: "RX", description: "Rotate X", category: "rotation", color: "#9f1853" },
874
+ { type: "RY", label: "RY", description: "Rotate Y", category: "rotation", color: "#9f1853" },
875
+ { type: "RZ", label: "RZ", description: "Rotate Z", category: "rotation", color: "#33b1ff" },
876
+ {
877
+ type: "CNOT",
878
+ label: "CNOT",
879
+ description: "Controlled-NOT",
880
+ category: "multi",
881
+ color: "#002d9c"
882
+ }
883
+ ];
884
+ function Operations({ className = "" } = {}) {
885
+ const { editingGate, setEditingGate, updateGate } = useQamposer();
886
+ if (editingGate) {
887
+ return /* @__PURE__ */ jsxRuntime.jsx(
888
+ GateEditor,
889
+ {
890
+ gate: editingGate,
891
+ onUpdate: updateGate,
892
+ onClose: () => setEditingGate(null),
893
+ className
894
+ }
895
+ );
896
+ }
897
+ return /* @__PURE__ */ jsxRuntime.jsx(GateLibrary, { className });
898
+ }
899
+ function GateLibrary({ className = "" }) {
900
+ const { circuit } = useQamposer();
901
+ const canUseCnot = circuit.qubits >= 2;
902
+ const [expandedSections, setExpandedSections] = react.useState({
903
+ single: true,
904
+ multi: false
905
+ });
906
+ const handleDragStart = (event, gateType) => {
907
+ if (gateType === "CNOT" && !canUseCnot) {
908
+ event.preventDefault();
909
+ return;
910
+ }
911
+ event.dataTransfer.setData("gateType", gateType);
912
+ event.dataTransfer.setData(`application/x-gate-${gateType.toLowerCase()}`, "");
913
+ event.dataTransfer.effectAllowed = "copy";
914
+ };
915
+ const toggleSection = (section) => {
916
+ setExpandedSections((prev) => ({
917
+ ...prev,
918
+ [section]: !prev[section]
919
+ }));
920
+ };
921
+ const renderGate = (gate) => {
922
+ if (gate.type === "CNOT") {
923
+ const isDisabled = !canUseCnot;
924
+ return /* @__PURE__ */ jsxRuntime.jsx(
925
+ "div",
926
+ {
927
+ className: `operations__gate operations__gate--cnot ${isDisabled ? "operations__gate--disabled" : ""}`,
928
+ draggable: !isDisabled,
929
+ onDragStart: (e) => handleDragStart(e, gate.type),
930
+ title: isDisabled ? "CNOT requires at least 2 qubits" : gate.description,
931
+ children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 32 32", xmlns: "http://www.w3.org/2000/svg", width: "32", height: "32", children: [
932
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "0", y: "0", width: "32", height: "32", fill: "#002d9c", rx: "4" }),
933
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "16", cy: "8", r: "2", fill: "white" }),
934
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "16", cy: "20.667", r: "5.333", stroke: "white", fill: "none", strokeWidth: "1.25" }),
935
+ /* @__PURE__ */ jsxRuntime.jsx(
936
+ "line",
937
+ {
938
+ x1: "10.667",
939
+ x2: "21.333",
940
+ y1: "20.667",
941
+ y2: "20.667",
942
+ stroke: "white",
943
+ strokeWidth: "1.25"
944
+ }
945
+ ),
946
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "16", x2: "16", y1: "6", y2: "26", stroke: "white", strokeWidth: "1.25" })
947
+ ] })
948
+ },
949
+ gate.type
950
+ );
951
+ }
952
+ return /* @__PURE__ */ jsxRuntime.jsx(
953
+ "div",
954
+ {
955
+ className: "operations__gate",
956
+ draggable: true,
957
+ onDragStart: (e) => handleDragStart(e, gate.type),
958
+ style: { backgroundColor: gate.color },
959
+ title: gate.description,
960
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "operations__gate-label", children: gate.label })
961
+ },
962
+ gate.type
963
+ );
964
+ };
965
+ const singleQubitGates = GATE_DEFINITIONS.filter(
966
+ (g) => g.category === "single" || g.category === "rotation"
967
+ );
968
+ const multiQubitGates = GATE_DEFINITIONS.filter((g) => g.category === "multi");
969
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `operations ${className}`.trim(), children: [
970
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "operations__header", children: /* @__PURE__ */ jsxRuntime.jsx("h3", { children: "Operations" }) }),
971
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "operations__sections", children: [
972
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "operations__section", children: [
973
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { className: "operations__section-header", onClick: () => toggleSection("single"), children: [
974
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronIcon, { expanded: expandedSections.single }),
975
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Single-Qubit Gates" })
976
+ ] }),
977
+ expandedSections.single && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "operations__grid", children: singleQubitGates.map(renderGate) })
978
+ ] }),
979
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "operations__section", children: [
980
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { className: "operations__section-header", onClick: () => toggleSection("multi"), children: [
981
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronIcon, { expanded: expandedSections.multi }),
982
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Multi-Qubit Gates" })
983
+ ] }),
984
+ expandedSections.multi && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "operations__grid", children: multiQubitGates.map(renderGate) })
985
+ ] })
986
+ ] })
987
+ ] });
988
+ }
989
+ function GateEditor({ gate, onUpdate, onClose, className = "" }) {
990
+ const { circuit } = useQamposer();
991
+ const numQubits = circuit.qubits;
992
+ const [parameterValue, setParameterValue] = react.useState("");
993
+ const [controlQubit, setControlQubit] = react.useState(gate.control ?? 0);
994
+ const [targetQubit, setTargetQubit] = react.useState(gate.target ?? 1);
995
+ react.useEffect(() => {
996
+ if (gate.parameter !== void 0) {
997
+ const value = gate.parameter;
998
+ const piRatio = value / Math.PI;
999
+ if (Math.abs(piRatio - Math.round(piRatio)) < 1e-4) {
1000
+ setParameterValue(piRatio === 0 ? "0" : piRatio === 1 ? "pi" : `${Math.round(piRatio)}*pi`);
1001
+ } else if (Math.abs(piRatio * 2 - Math.round(piRatio * 2)) < 1e-4) {
1002
+ const ratio = Math.round(piRatio * 2);
1003
+ setParameterValue(ratio === 1 ? "pi/2" : `${ratio}*pi/2`);
1004
+ } else {
1005
+ setParameterValue(value.toFixed(4));
1006
+ }
1007
+ } else {
1008
+ setParameterValue("0");
1009
+ }
1010
+ }, [gate.parameter]);
1011
+ react.useEffect(() => {
1012
+ if (gate.type === "CNOT") {
1013
+ setControlQubit(gate.control ?? 0);
1014
+ setTargetQubit(gate.target ?? 1);
1015
+ }
1016
+ }, [gate.type, gate.control, gate.target]);
1017
+ const isRotationGate = ["RX", "RY", "RZ"].includes(gate.type);
1018
+ const isCnotGate = gate.type === "CNOT";
1019
+ if (!isRotationGate && !isCnotGate) {
1020
+ return null;
1021
+ }
1022
+ const handleRotationSave = () => {
1023
+ let radians = 0;
1024
+ try {
1025
+ const normalized = parameterValue.toLowerCase().replace(/\s/g, "");
1026
+ if (normalized.includes("pi")) {
1027
+ const piValue = Math.PI;
1028
+ let expr = normalized.replace(/pi/g, String(piValue));
1029
+ expr = expr.replace(/\*/g, "*").replace(/\//g, "/");
1030
+ radians = Function(`"use strict"; return (${expr})`)();
1031
+ } else {
1032
+ radians = parseFloat(normalized);
1033
+ }
1034
+ if (!isNaN(radians)) {
1035
+ onUpdate(gate.id, { parameter: radians });
1036
+ }
1037
+ } catch (error) {
1038
+ console.error("Invalid parameter expression:", error);
1039
+ }
1040
+ };
1041
+ const handleCnotSave = () => {
1042
+ if (controlQubit !== targetQubit) {
1043
+ onUpdate(gate.id, { control: controlQubit, target: targetQubit });
1044
+ }
1045
+ };
1046
+ const qubitOptions = Array.from({ length: numQubits }, (_, i) => i);
1047
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `operations operations--editor ${className}`.trim(), children: [
1048
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "operations__header", children: [
1049
+ /* @__PURE__ */ jsxRuntime.jsxs("h3", { children: [
1050
+ "Edit ",
1051
+ gate.type
1052
+ ] }),
1053
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: "operations__close-btn", onClick: onClose, title: "Close", children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, {}) })
1054
+ ] }),
1055
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "operations__content", children: [
1056
+ isRotationGate && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "operations__field", children: [
1057
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "theta-input", children: "theta (rotation)" }),
1058
+ /* @__PURE__ */ jsxRuntime.jsx(
1059
+ "input",
1060
+ {
1061
+ id: "theta-input",
1062
+ type: "text",
1063
+ className: "operations__input",
1064
+ value: parameterValue,
1065
+ onChange: (e) => setParameterValue(e.target.value),
1066
+ onBlur: handleRotationSave,
1067
+ onKeyDown: (e) => {
1068
+ if (e.key === "Enter") handleRotationSave();
1069
+ },
1070
+ placeholder: "e.g., pi/2, 1.5708, 2*pi"
1071
+ }
1072
+ ),
1073
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "operations__helper", children: "Enter angle in radians or use pi expressions" })
1074
+ ] }),
1075
+ isCnotGate && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1076
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "operations__field", children: [
1077
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "control-select", children: "Control qubit" }),
1078
+ /* @__PURE__ */ jsxRuntime.jsx(
1079
+ "select",
1080
+ {
1081
+ id: "control-select",
1082
+ className: "operations__select",
1083
+ value: controlQubit,
1084
+ onChange: (e) => {
1085
+ const newControl = parseInt(e.target.value, 10);
1086
+ setControlQubit(newControl);
1087
+ if (newControl === targetQubit) {
1088
+ setTargetQubit(newControl === 0 ? 1 : 0);
1089
+ }
1090
+ },
1091
+ children: qubitOptions.map((q) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: q, children: [
1092
+ "q[",
1093
+ q,
1094
+ "]"
1095
+ ] }, q))
1096
+ }
1097
+ )
1098
+ ] }),
1099
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "operations__field", children: [
1100
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "target-select", children: "Target qubit" }),
1101
+ /* @__PURE__ */ jsxRuntime.jsx(
1102
+ "select",
1103
+ {
1104
+ id: "target-select",
1105
+ className: "operations__select",
1106
+ value: targetQubit,
1107
+ onChange: (e) => {
1108
+ const newTarget = parseInt(e.target.value, 10);
1109
+ setTargetQubit(newTarget);
1110
+ if (newTarget === controlQubit) {
1111
+ setControlQubit(newTarget === 0 ? 1 : 0);
1112
+ }
1113
+ },
1114
+ children: qubitOptions.map((q) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: q, children: [
1115
+ "q[",
1116
+ q,
1117
+ "]"
1118
+ ] }, q))
1119
+ }
1120
+ )
1121
+ ] }),
1122
+ /* @__PURE__ */ jsxRuntime.jsx(
1123
+ "button",
1124
+ {
1125
+ className: "operations__apply-btn",
1126
+ onClick: handleCnotSave,
1127
+ disabled: controlQubit === targetQubit,
1128
+ children: "Apply"
1129
+ }
1130
+ ),
1131
+ controlQubit === targetQubit && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "operations__error", children: "Control and target must be different qubits" })
1132
+ ] })
1133
+ ] })
1134
+ ] });
1135
+ }
1136
+ function ChevronIcon({ expanded }) {
1137
+ return /* @__PURE__ */ jsxRuntime.jsx(
1138
+ "svg",
1139
+ {
1140
+ width: "16",
1141
+ height: "16",
1142
+ viewBox: "0 0 16 16",
1143
+ fill: "currentColor",
1144
+ style: {
1145
+ transform: expanded ? "rotate(90deg)" : "rotate(0deg)",
1146
+ transition: "transform 0.2s"
1147
+ },
1148
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1149
+ "path",
1150
+ {
1151
+ fillRule: "evenodd",
1152
+ d: "M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z"
1153
+ }
1154
+ )
1155
+ }
1156
+ );
1157
+ }
1158
+ function CloseIcon() {
1159
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx(
1160
+ "path",
1161
+ {
1162
+ fillRule: "evenodd",
1163
+ d: "M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"
1164
+ }
1165
+ ) });
1166
+ }
1167
+
1168
+ // src/components/CircuitEditor/CircuitEditor.scss
1169
+ var css2 = `.circuit-editor {
1170
+ flex: 1;
1171
+ background: var(--qamposer-bg-secondary, #262626);
1172
+ overflow: auto;
1173
+ position: relative;
1174
+ height: 100%;
1175
+ }
1176
+ .circuit-editor__canvas {
1177
+ position: relative;
1178
+ display: flex;
1179
+ width: 100%;
1180
+ min-height: 100%;
1181
+ padding: 2rem;
1182
+ padding-right: 72px;
1183
+ }
1184
+ .circuit-editor__scroll-container {
1185
+ flex: 1;
1186
+ overflow-x: auto;
1187
+ overflow-y: visible;
1188
+ position: relative;
1189
+ }
1190
+ .circuit-editor__scroll-container::-webkit-scrollbar {
1191
+ height: 8px;
1192
+ }
1193
+ .circuit-editor__scroll-container::-webkit-scrollbar-track {
1194
+ background: var(--qamposer-bg-primary, #161616);
1195
+ border-radius: 4px;
1196
+ }
1197
+ .circuit-editor__scroll-container::-webkit-scrollbar-thumb {
1198
+ background: var(--qamposer-border-strong, #6f6f6f);
1199
+ border-radius: 4px;
1200
+ }
1201
+ .circuit-editor__scroll-container::-webkit-scrollbar-thumb:hover {
1202
+ background: var(--qamposer-text-secondary, #a8a8a8);
1203
+ }
1204
+ .circuit-editor__circuit-area {
1205
+ position: relative;
1206
+ }
1207
+ .circuit-editor__lanes {
1208
+ position: relative;
1209
+ }
1210
+ .circuit-editor__lane {
1211
+ position: relative;
1212
+ height: 80px;
1213
+ display: flex;
1214
+ align-items: center;
1215
+ }
1216
+ .circuit-editor__lane--classical {
1217
+ border-top: 1px solid var(--qamposer-border, #525252);
1218
+ margin-top: 1rem;
1219
+ }
1220
+ .circuit-editor__lane-label {
1221
+ width: 60px;
1222
+ font-family: "IBM Plex Mono", "Menlo", "Monaco", monospace;
1223
+ font-size: 0.875rem;
1224
+ color: var(--qamposer-text-secondary, #a8a8a8);
1225
+ padding-right: 1rem;
1226
+ text-align: right;
1227
+ flex-shrink: 0;
1228
+ cursor: pointer;
1229
+ border-radius: 4px;
1230
+ transition: background-color 0.15s;
1231
+ }
1232
+ .circuit-editor__lane-label:hover {
1233
+ background-color: var(--qamposer-hover, rgba(255, 255, 255, 0.1));
1234
+ }
1235
+ .circuit-editor__lane-label--selected {
1236
+ background-color: var(--qamposer-selected, rgba(66, 133, 244, 0.2));
1237
+ color: var(--qamposer-text-primary, #f4f4f4);
1238
+ }
1239
+ .circuit-editor__lane-line {
1240
+ flex: 1;
1241
+ height: 2px;
1242
+ background: var(--qamposer-border, #525252);
1243
+ position: relative;
1244
+ }
1245
+ .circuit-editor__lane-line--classical {
1246
+ height: 4px;
1247
+ background: var(--qamposer-border-strong, #6f6f6f);
1248
+ }
1249
+ .circuit-editor__drop-zone-continuous {
1250
+ position: absolute;
1251
+ left: 60px;
1252
+ right: 0;
1253
+ top: 0;
1254
+ height: 100%;
1255
+ pointer-events: all;
1256
+ z-index: 1;
1257
+ }
1258
+ .circuit-editor__preview {
1259
+ position: absolute;
1260
+ top: 0;
1261
+ left: 0;
1262
+ right: 0;
1263
+ bottom: 0;
1264
+ pointer-events: none;
1265
+ z-index: 3;
1266
+ }
1267
+ .circuit-editor__gates {
1268
+ position: absolute;
1269
+ top: 0;
1270
+ left: 0;
1271
+ right: 0;
1272
+ bottom: 0;
1273
+ pointer-events: none;
1274
+ z-index: 2;
1275
+ }
1276
+ .circuit-editor__gate {
1277
+ position: absolute;
1278
+ width: 32px;
1279
+ height: 32px;
1280
+ border-radius: 4px;
1281
+ display: flex;
1282
+ align-items: center;
1283
+ justify-content: center;
1284
+ transform: translate(-50%, -50%);
1285
+ cursor: pointer;
1286
+ pointer-events: all;
1287
+ transition: all 0.2s;
1288
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
1289
+ }
1290
+ .circuit-editor__gate:hover {
1291
+ transform: translate(-50%, -50%) scale(1.1);
1292
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
1293
+ }
1294
+ .circuit-editor__gate--selected {
1295
+ outline: 2px solid var(--qamposer-accent, #4285f4);
1296
+ outline-offset: 2px;
1297
+ }
1298
+ .circuit-editor__gate--rotation {
1299
+ width: auto;
1300
+ min-width: 32px;
1301
+ padding: 0 8px;
1302
+ white-space: nowrap;
1303
+ }
1304
+ .circuit-editor__gate--preview {
1305
+ background: #8d8d8d !important;
1306
+ opacity: 0.7;
1307
+ cursor: default;
1308
+ pointer-events: none;
1309
+ }
1310
+ .circuit-editor__gate--preview:hover {
1311
+ transform: translate(-50%, -50%);
1312
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
1313
+ }
1314
+ .circuit-editor__gate--shifted-preview {
1315
+ opacity: 0.5;
1316
+ cursor: default;
1317
+ pointer-events: none;
1318
+ }
1319
+ .circuit-editor__gate--shifted-preview:hover {
1320
+ transform: translate(-50%, -50%);
1321
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
1322
+ }
1323
+ .circuit-editor__gate-label {
1324
+ color: white;
1325
+ font-weight: 600;
1326
+ font-size: 0.875rem;
1327
+ letter-spacing: 0.5px;
1328
+ pointer-events: none;
1329
+ display: flex;
1330
+ flex-direction: column;
1331
+ align-items: center;
1332
+ justify-content: center;
1333
+ gap: 2px;
1334
+ }
1335
+ .circuit-editor__gate-param {
1336
+ font-size: 0.5rem;
1337
+ font-weight: 400;
1338
+ opacity: 0.9;
1339
+ font-family: "IBM Plex Mono", "Menlo", "Monaco", monospace;
1340
+ }
1341
+ .circuit-editor__cnot {
1342
+ position: absolute;
1343
+ transform: translateX(-50%);
1344
+ cursor: pointer;
1345
+ pointer-events: all;
1346
+ width: 40px;
1347
+ }
1348
+ .circuit-editor__cnot--selected .circuit-editor__cnot-line {
1349
+ stroke: var(--qamposer-accent, #4285f4);
1350
+ stroke-width: 3;
1351
+ }
1352
+ .circuit-editor__cnot--preview {
1353
+ cursor: default;
1354
+ pointer-events: none;
1355
+ }
1356
+ .circuit-editor__cnot--preview .circuit-editor__cnot-line {
1357
+ background: #8d8d8d;
1358
+ opacity: 0.7;
1359
+ }
1360
+ .circuit-editor__cnot--preview .circuit-editor__cnot-control {
1361
+ background: #8d8d8d;
1362
+ opacity: 0.7;
1363
+ }
1364
+ .circuit-editor__cnot--preview .circuit-editor__cnot-target {
1365
+ border-color: #8d8d8d;
1366
+ background: rgba(255, 255, 255, 0.7);
1367
+ opacity: 0.7;
1368
+ }
1369
+ .circuit-editor__cnot--preview .circuit-editor__cnot-target::before, .circuit-editor__cnot--preview .circuit-editor__cnot-target::after {
1370
+ background: #8d8d8d;
1371
+ }
1372
+ .circuit-editor__cnot--shifted-preview {
1373
+ opacity: 0.5;
1374
+ cursor: default;
1375
+ pointer-events: none;
1376
+ }
1377
+ .circuit-editor__cnot-line {
1378
+ position: absolute;
1379
+ left: 50%;
1380
+ top: 0;
1381
+ width: 2px;
1382
+ height: 100%;
1383
+ background: #002d9c;
1384
+ transform: translateX(-50%);
1385
+ }
1386
+ .circuit-editor__cnot-control {
1387
+ position: absolute;
1388
+ left: 50%;
1389
+ width: 8px;
1390
+ height: 8px;
1391
+ background: #002d9c;
1392
+ border-radius: 50%;
1393
+ transform: translate(-50%, -50%);
1394
+ }
1395
+ .circuit-editor__cnot-target {
1396
+ position: absolute;
1397
+ left: 50%;
1398
+ width: 26px;
1399
+ height: 26px;
1400
+ border: 2px solid #002d9c;
1401
+ border-radius: 50%;
1402
+ background: white;
1403
+ transform: translate(-50%, -50%);
1404
+ }
1405
+ .circuit-editor__cnot-target::before, .circuit-editor__cnot-target::after {
1406
+ content: "";
1407
+ position: absolute;
1408
+ background: #002d9c;
1409
+ }
1410
+ .circuit-editor__cnot-target::before {
1411
+ left: 50%;
1412
+ top: 20%;
1413
+ width: 2px;
1414
+ height: 60%;
1415
+ transform: translateX(-50%);
1416
+ }
1417
+ .circuit-editor__cnot-target::after {
1418
+ top: 50%;
1419
+ left: 20%;
1420
+ height: 2px;
1421
+ width: 60%;
1422
+ transform: translateY(-50%);
1423
+ }
1424
+ .circuit-editor__measurements {
1425
+ position: absolute;
1426
+ top: 2rem;
1427
+ right: 2rem;
1428
+ pointer-events: none;
1429
+ z-index: 10;
1430
+ }
1431
+ .circuit-editor__measurement {
1432
+ position: absolute;
1433
+ right: 0;
1434
+ transform: translateY(-50%);
1435
+ }
1436
+ .circuit-editor__measurement-icon {
1437
+ width: 32px;
1438
+ height: 32px;
1439
+ border: 2px solid var(--qamposer-border-strong, #6f6f6f);
1440
+ border-radius: 50%;
1441
+ background: var(--qamposer-bg-secondary, #262626);
1442
+ position: relative;
1443
+ }
1444
+ .circuit-editor__measurement-icon::after {
1445
+ content: "";
1446
+ position: absolute;
1447
+ top: 50%;
1448
+ left: 50%;
1449
+ width: 0;
1450
+ height: 0;
1451
+ border-left: 6px solid transparent;
1452
+ border-right: 6px solid transparent;
1453
+ border-bottom: 10px solid var(--qamposer-text-primary, #f4f4f4);
1454
+ transform: translate(-50%, -60%);
1455
+ }
1456
+ .circuit-editor__toolbar {
1457
+ position: absolute;
1458
+ display: flex;
1459
+ align-items: center;
1460
+ gap: 4px;
1461
+ background: var(--qamposer-bg-primary, #161616);
1462
+ border: 1px solid var(--qamposer-border, #525252);
1463
+ border-radius: 4px;
1464
+ padding: 4px;
1465
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
1466
+ z-index: 1000;
1467
+ pointer-events: auto;
1468
+ transform: translate(-50%, calc(-100% - 12px));
1469
+ }
1470
+ .circuit-editor__toolbar--below {
1471
+ transform: translate(-50%, 48px);
1472
+ }
1473
+ .circuit-editor__toolbar--qubit {
1474
+ transform: translate(0, -50%);
1475
+ }
1476
+ .circuit-editor__toolbar-btn {
1477
+ display: flex;
1478
+ align-items: center;
1479
+ justify-content: center;
1480
+ width: 32px;
1481
+ height: 32px;
1482
+ padding: 0;
1483
+ border: none;
1484
+ background: transparent;
1485
+ border-radius: 4px;
1486
+ color: var(--qamposer-text-secondary, #a8a8a8);
1487
+ cursor: pointer;
1488
+ transition: all 0.15s;
1489
+ }
1490
+ .circuit-editor__toolbar-btn:hover:not(:disabled) {
1491
+ background: var(--qamposer-hover, rgba(255, 255, 255, 0.1));
1492
+ color: var(--qamposer-text-primary, #f4f4f4);
1493
+ }
1494
+ .circuit-editor__toolbar-btn:disabled {
1495
+ opacity: 0.4;
1496
+ cursor: not-allowed;
1497
+ }
1498
+ .circuit-editor__toolbar-btn--danger:hover:not(:disabled) {
1499
+ background: rgba(218, 30, 40, 0.2);
1500
+ color: #fa4d56;
1501
+ }`;
1502
+ document.head.appendChild(document.createElement("style")).appendChild(document.createTextNode(css2));
1503
+ var GATE_COLORS = {
1504
+ H: "#fa4d56",
1505
+ X: "#002d9c",
1506
+ Y: "#9f1853",
1507
+ Z: "#33b1ff",
1508
+ RX: "#9f1853",
1509
+ RY: "#9f1853",
1510
+ RZ: "#33b1ff",
1511
+ CNOT: "#002d9c"
1512
+ };
1513
+ var QUBIT_HEIGHT = 80;
1514
+ var MAX_POSITIONS = 20;
1515
+ var LABEL_WIDTH = 60;
1516
+ var COLUMN_GAP = 20;
1517
+ var MIN_LEFT_MARGIN = 16;
1518
+ function CircuitEditor({ className = "" } = {}) {
1519
+ const { circuit, updateGates, addQubit, removeQubit, setEditingGate, config } = useQamposer();
1520
+ const { qubits, gates } = circuit;
1521
+ const [dragOverQubit, setDragOverQubit] = react.useState(null);
1522
+ const [dragOverPosition, setDragOverPosition] = react.useState(null);
1523
+ const [selectedGateId, setSelectedGateId] = react.useState(null);
1524
+ const [selectedQubitIndex, setSelectedQubitIndex] = react.useState(null);
1525
+ const [draggingGateType, setDraggingGateType] = react.useState(null);
1526
+ const [previewShiftedGates, setPreviewShiftedGates] = react.useState([]);
1527
+ const canvasRef = react.useRef(null);
1528
+ const columnWidths = react.useMemo(() => {
1529
+ const widths = {};
1530
+ for (let pos = 0; pos < MAX_POSITIONS; pos++) {
1531
+ let maxWidth = 32;
1532
+ gates.forEach((g) => {
1533
+ if (g.position === pos) {
1534
+ if (["RX", "RY", "RZ"].includes(g.type) && g.parameter !== void 0) {
1535
+ const parameterLabel = `(${(g.parameter / Math.PI).toFixed(2)}\u03C0)`;
1536
+ const gateTypeWidth = 32;
1537
+ const paramWidth = parameterLabel.length * 6;
1538
+ const padding = 16;
1539
+ const estimatedWidth = Math.max(gateTypeWidth, paramWidth) + padding;
1540
+ maxWidth = Math.max(maxWidth, estimatedWidth);
1541
+ } else if (g.type !== "CNOT") {
1542
+ maxWidth = Math.max(maxWidth, 32);
1543
+ }
1544
+ }
1545
+ });
1546
+ widths[pos] = maxWidth;
1547
+ }
1548
+ return widths;
1549
+ }, [gates]);
1550
+ const columnLeftXs = react.useMemo(() => {
1551
+ const leftXs = {};
1552
+ for (let pos = 0; pos < MAX_POSITIONS; pos++) {
1553
+ if (pos === 0) {
1554
+ leftXs[pos] = LABEL_WIDTH + MIN_LEFT_MARGIN;
1555
+ } else {
1556
+ const prevLeftX = leftXs[pos - 1];
1557
+ const prevWidth = columnWidths[pos - 1];
1558
+ const prevRightEdge = prevLeftX + prevWidth;
1559
+ leftXs[pos] = prevRightEdge + COLUMN_GAP;
1560
+ }
1561
+ }
1562
+ return leftXs;
1563
+ }, [columnWidths]);
1564
+ const columnCenterXs = react.useMemo(() => {
1565
+ const centerXs = {};
1566
+ for (let pos = 0; pos < MAX_POSITIONS; pos++) {
1567
+ centerXs[pos] = columnLeftXs[pos] + columnWidths[pos] / 2;
1568
+ }
1569
+ return centerXs;
1570
+ }, [columnLeftXs, columnWidths]);
1571
+ const getGateQubits2 = (gate) => {
1572
+ if (gate.type === "CNOT" && gate.control !== void 0 && gate.target !== void 0) {
1573
+ const minQubit = Math.min(gate.control, gate.target);
1574
+ const maxQubit = Math.max(gate.control, gate.target);
1575
+ const qubits2 = [];
1576
+ for (let q = minQubit; q <= maxQubit; q++) {
1577
+ qubits2.push(q);
1578
+ }
1579
+ return qubits2;
1580
+ }
1581
+ return gate.qubit !== void 0 ? [gate.qubit] : [];
1582
+ };
1583
+ const calculateDropPosition = (mouseX, qubit, gateType) => {
1584
+ let closestPos = 0;
1585
+ let minDistance = Infinity;
1586
+ for (let pos = 0; pos < MAX_POSITIONS; pos++) {
1587
+ const distance = Math.abs(columnCenterXs[pos] - mouseX);
1588
+ if (distance < minDistance) {
1589
+ minDistance = distance;
1590
+ closestPos = pos;
1591
+ }
1592
+ }
1593
+ let targetQubits;
1594
+ let control;
1595
+ let target;
1596
+ if (gateType === "CNOT") {
1597
+ control = Math.min(qubit, qubits - 2);
1598
+ target = control + 1;
1599
+ targetQubits = [control, target];
1600
+ } else {
1601
+ targetQubits = [qubit];
1602
+ }
1603
+ const rightWall = gates.filter((g) => {
1604
+ if (g.type !== "CNOT" || g.control === void 0 || g.target === void 0) {
1605
+ return false;
1606
+ }
1607
+ const gateQubits = getGateQubits2(g);
1608
+ return targetQubits.some((q) => gateQubits.includes(q)) && g.position > closestPos;
1609
+ }).sort((a, b) => a.position - b.position)[0];
1610
+ let initialPosition;
1611
+ if (rightWall) {
1612
+ initialPosition = Math.min(closestPos, rightWall.position - 1);
1613
+ } else {
1614
+ initialPosition = closestPos;
1615
+ }
1616
+ initialPosition = Math.max(0, initialPosition);
1617
+ const shiftedGatesForInsert = gates.map((g) => {
1618
+ const gateQubits = getGateQubits2(g);
1619
+ const overlapsQubit = targetQubits.some((q) => gateQubits.includes(q));
1620
+ if (overlapsQubit && g.position >= initialPosition) {
1621
+ return { ...g, position: g.position + 1 };
1622
+ }
1623
+ return g;
1624
+ });
1625
+ const tempGate = {
1626
+ id: "temp",
1627
+ type: gateType,
1628
+ position: initialPosition,
1629
+ ...gateType === "CNOT" ? { control, target } : { qubit },
1630
+ ...["RX", "RY", "RZ"].includes(gateType) ? { parameter: Math.PI / 2 } : {}
1631
+ };
1632
+ const compacted = compactGates([...shiftedGatesForInsert, tempGate]);
1633
+ const finalGate = compacted.find((g) => g.id === "temp");
1634
+ const finalPosition = finalGate ? finalGate.position : initialPosition;
1635
+ const shiftedGates = [];
1636
+ compacted.forEach((compactedGate) => {
1637
+ if (compactedGate.id === "temp") return;
1638
+ const originalGate = gates.find((g) => g.id === compactedGate.id);
1639
+ if (originalGate && originalGate.position !== compactedGate.position) {
1640
+ shiftedGates.push({
1641
+ id: compactedGate.id,
1642
+ newPosition: compactedGate.position
1643
+ });
1644
+ }
1645
+ });
1646
+ return { initialPosition, finalPosition, shiftedGates, control, target };
1647
+ };
1648
+ const handleDragOver = (event, qubit) => {
1649
+ event.preventDefault();
1650
+ event.dataTransfer.dropEffect = "copy";
1651
+ let gateType = draggingGateType;
1652
+ if (!gateType) {
1653
+ const mimeType = event.dataTransfer.types.find((t) => t.startsWith("application/x-gate-"));
1654
+ if (mimeType) {
1655
+ gateType = mimeType.replace("application/x-gate-", "").toUpperCase();
1656
+ setDraggingGateType(gateType);
1657
+ }
1658
+ }
1659
+ setDragOverQubit(qubit);
1660
+ if (canvasRef.current && gateType) {
1661
+ const rect = canvasRef.current.getBoundingClientRect();
1662
+ const mouseX = event.clientX - rect.left;
1663
+ const { finalPosition, shiftedGates } = calculateDropPosition(mouseX, qubit, gateType);
1664
+ setDragOverPosition(finalPosition);
1665
+ setPreviewShiftedGates(shiftedGates);
1666
+ }
1667
+ };
1668
+ const handleDragLeave = () => {
1669
+ setDragOverQubit(null);
1670
+ setDragOverPosition(null);
1671
+ setPreviewShiftedGates([]);
1672
+ };
1673
+ const handleDragEnd = () => {
1674
+ setDragOverQubit(null);
1675
+ setDragOverPosition(null);
1676
+ setPreviewShiftedGates([]);
1677
+ setDraggingGateType(null);
1678
+ };
1679
+ const handleDrop = (event, qubit) => {
1680
+ event.preventDefault();
1681
+ setDragOverQubit(null);
1682
+ setDragOverPosition(null);
1683
+ setDraggingGateType(null);
1684
+ setPreviewShiftedGates([]);
1685
+ const gateType = event.dataTransfer.getData("gateType");
1686
+ if (!gateType || !canvasRef.current) return;
1687
+ if (gates.length >= config.maxGates) {
1688
+ console.warn(`Maximum gate limit (${config.maxGates}) reached`);
1689
+ return;
1690
+ }
1691
+ const rect = canvasRef.current.getBoundingClientRect();
1692
+ const mouseX = event.clientX - rect.left;
1693
+ const { initialPosition, control, target } = calculateDropPosition(mouseX, qubit, gateType);
1694
+ const targetQubits = gateType === "CNOT" && control !== void 0 && target !== void 0 ? [control, target] : [qubit];
1695
+ const shiftedGates = gates.map((g) => {
1696
+ const gateQubits = getGateQubits2(g);
1697
+ const overlapsQubit = targetQubits.some((q) => gateQubits.includes(q));
1698
+ if (overlapsQubit && g.position >= initialPosition) {
1699
+ return { ...g, position: g.position + 1 };
1700
+ }
1701
+ return g;
1702
+ });
1703
+ const newGate = {
1704
+ id: generateGateId(),
1705
+ type: gateType,
1706
+ position: initialPosition,
1707
+ ...gateType === "CNOT" ? { control, target } : { qubit },
1708
+ ...["RX", "RY", "RZ"].includes(gateType) ? { parameter: Math.PI / 2 } : {}
1709
+ };
1710
+ const updatedGates = compactGates([...shiftedGates, newGate]);
1711
+ updateGates(updatedGates);
1712
+ };
1713
+ const handleGateClick = (gateId) => {
1714
+ setSelectedGateId(selectedGateId === gateId ? null : gateId);
1715
+ setSelectedQubitIndex(null);
1716
+ };
1717
+ const handleGateDelete = (gateId) => {
1718
+ const remainingGates = gates.filter((g) => g.id !== gateId);
1719
+ const compactedGates = compactGates(remainingGates);
1720
+ updateGates(compactedGates);
1721
+ setSelectedGateId(null);
1722
+ };
1723
+ const handleGateEdit = (gate) => {
1724
+ setEditingGate(gate);
1725
+ setSelectedGateId(null);
1726
+ };
1727
+ const handleQubitClick = (qubitIndex) => {
1728
+ setSelectedQubitIndex(selectedQubitIndex === qubitIndex ? null : qubitIndex);
1729
+ setSelectedGateId(null);
1730
+ };
1731
+ const handleQubitAdd = () => {
1732
+ addQubit();
1733
+ setSelectedQubitIndex(null);
1734
+ };
1735
+ const handleQubitRemove = () => {
1736
+ if (selectedQubitIndex === null) return;
1737
+ removeQubit(selectedQubitIndex);
1738
+ setSelectedQubitIndex(null);
1739
+ };
1740
+ const renderGate = (gate) => {
1741
+ const isSelected = selectedGateId === gate.id;
1742
+ if (gate.type === "CNOT" && gate.control !== void 0 && gate.target !== void 0) {
1743
+ const top = Math.min(gate.control, gate.target) * QUBIT_HEIGHT + QUBIT_HEIGHT / 2;
1744
+ const height = Math.abs(gate.target - gate.control) * QUBIT_HEIGHT;
1745
+ const left = columnCenterXs[gate.position];
1746
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1747
+ "div",
1748
+ {
1749
+ className: `circuit-editor__cnot ${isSelected ? "circuit-editor__cnot--selected" : ""}`,
1750
+ style: {
1751
+ left: `${left}px`,
1752
+ top: `${top}px`,
1753
+ height: `${height}px`
1754
+ },
1755
+ onClick: () => handleGateClick(gate.id),
1756
+ children: [
1757
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "circuit-editor__cnot-line" }),
1758
+ /* @__PURE__ */ jsxRuntime.jsx(
1759
+ "div",
1760
+ {
1761
+ className: "circuit-editor__cnot-control",
1762
+ style: {
1763
+ top: gate.control < gate.target ? "0" : "100%"
1764
+ }
1765
+ }
1766
+ ),
1767
+ /* @__PURE__ */ jsxRuntime.jsx(
1768
+ "div",
1769
+ {
1770
+ className: "circuit-editor__cnot-target",
1771
+ style: {
1772
+ top: gate.target < gate.control ? "0" : "100%"
1773
+ }
1774
+ }
1775
+ )
1776
+ ]
1777
+ },
1778
+ gate.id
1779
+ );
1780
+ }
1781
+ if (gate.qubit === void 0) return null;
1782
+ const isRotationGate = ["RX", "RY", "RZ"].includes(gate.type);
1783
+ const parameterLabel = gate.parameter !== void 0 ? `(${(gate.parameter / Math.PI).toFixed(2)}\u03C0)` : "";
1784
+ const centerX = columnCenterXs[gate.position];
1785
+ return /* @__PURE__ */ jsxRuntime.jsx(
1786
+ "div",
1787
+ {
1788
+ className: `circuit-editor__gate ${isSelected ? "circuit-editor__gate--selected" : ""} ${isRotationGate ? "circuit-editor__gate--rotation" : ""}`,
1789
+ style: {
1790
+ left: `${centerX}px`,
1791
+ top: `${gate.qubit * QUBIT_HEIGHT + QUBIT_HEIGHT / 2}px`,
1792
+ backgroundColor: GATE_COLORS[gate.type]
1793
+ },
1794
+ onClick: () => handleGateClick(gate.id),
1795
+ children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "circuit-editor__gate-label", children: [
1796
+ gate.type,
1797
+ isRotationGate && gate.parameter !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "circuit-editor__gate-param", children: parameterLabel })
1798
+ ] })
1799
+ },
1800
+ gate.id
1801
+ );
1802
+ };
1803
+ const selectedGate = selectedGateId ? gates.find((g) => g.id === selectedGateId) : null;
1804
+ const maxGatePosition = gates.length > 0 ? Math.max(...gates.map((g) => g.position)) : 0;
1805
+ const lastColumnRightEdge = columnLeftXs[maxGatePosition] + columnWidths[maxGatePosition] + COLUMN_GAP;
1806
+ const minCircuitWidth = Math.max(lastColumnRightEdge, 400);
1807
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `circuit-editor ${className}`.trim(), onDragEnd: handleDragEnd, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "circuit-editor__canvas", children: [
1808
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "circuit-editor__scroll-container", children: /* @__PURE__ */ jsxRuntime.jsxs(
1809
+ "div",
1810
+ {
1811
+ ref: canvasRef,
1812
+ className: "circuit-editor__circuit-area",
1813
+ style: { minWidth: `${minCircuitWidth}px` },
1814
+ children: [
1815
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "circuit-editor__lanes", children: [
1816
+ Array.from({ length: qubits }).map((_, qubitIndex) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "circuit-editor__lane", children: [
1817
+ /* @__PURE__ */ jsxRuntime.jsxs(
1818
+ "div",
1819
+ {
1820
+ className: `circuit-editor__lane-label ${selectedQubitIndex === qubitIndex ? "circuit-editor__lane-label--selected" : ""}`,
1821
+ onClick: () => handleQubitClick(qubitIndex),
1822
+ children: [
1823
+ "q[",
1824
+ qubitIndex,
1825
+ "]"
1826
+ ]
1827
+ }
1828
+ ),
1829
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "circuit-editor__lane-line" }),
1830
+ /* @__PURE__ */ jsxRuntime.jsx(
1831
+ "div",
1832
+ {
1833
+ className: "circuit-editor__drop-zone-continuous",
1834
+ onDragOver: (e) => handleDragOver(e, qubitIndex),
1835
+ onDragLeave: handleDragLeave,
1836
+ onDrop: (e) => handleDrop(e, qubitIndex)
1837
+ }
1838
+ )
1839
+ ] }, qubitIndex)),
1840
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "circuit-editor__lane circuit-editor__lane--classical", children: [
1841
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "circuit-editor__lane-label", children: [
1842
+ "c",
1843
+ qubits
1844
+ ] }),
1845
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "circuit-editor__lane-line circuit-editor__lane-line--classical" })
1846
+ ] })
1847
+ ] }),
1848
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "circuit-editor__gates", children: gates.filter((gate) => {
1849
+ const isBeingShifted = previewShiftedGates.some((sg) => sg.id === gate.id);
1850
+ return !isBeingShifted;
1851
+ }).map(renderGate) }),
1852
+ dragOverQubit !== null && dragOverPosition !== null && draggingGateType && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "circuit-editor__preview", children: draggingGateType === "CNOT" ? (() => {
1853
+ const control = Math.min(dragOverQubit, qubits - 2);
1854
+ const target = control + 1;
1855
+ const top = control * QUBIT_HEIGHT + QUBIT_HEIGHT / 2;
1856
+ const height = QUBIT_HEIGHT;
1857
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1858
+ "div",
1859
+ {
1860
+ className: "circuit-editor__cnot circuit-editor__cnot--preview",
1861
+ style: {
1862
+ left: `${columnCenterXs[dragOverPosition]}px`,
1863
+ top: `${top}px`,
1864
+ height: `${height}px`
1865
+ },
1866
+ children: [
1867
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "circuit-editor__cnot-line" }),
1868
+ /* @__PURE__ */ jsxRuntime.jsx(
1869
+ "div",
1870
+ {
1871
+ className: "circuit-editor__cnot-control",
1872
+ style: { top: control < target ? "0" : "100%" }
1873
+ }
1874
+ ),
1875
+ /* @__PURE__ */ jsxRuntime.jsx(
1876
+ "div",
1877
+ {
1878
+ className: "circuit-editor__cnot-target",
1879
+ style: { top: target < control ? "0" : "100%" }
1880
+ }
1881
+ )
1882
+ ]
1883
+ }
1884
+ );
1885
+ })() : /* @__PURE__ */ jsxRuntime.jsx(
1886
+ "div",
1887
+ {
1888
+ className: "circuit-editor__gate circuit-editor__gate--preview",
1889
+ style: {
1890
+ left: `${columnCenterXs[dragOverPosition]}px`,
1891
+ top: `${dragOverQubit * QUBIT_HEIGHT + QUBIT_HEIGHT / 2}px`
1892
+ },
1893
+ children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "circuit-editor__gate-label", children: [
1894
+ draggingGateType,
1895
+ ["RX", "RY", "RZ"].includes(draggingGateType) && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "circuit-editor__gate-param", children: "(0.50\u03C0)" })
1896
+ ] })
1897
+ }
1898
+ ) }),
1899
+ previewShiftedGates.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "circuit-editor__preview circuit-editor__preview--shifted", children: previewShiftedGates.map((shiftedGate) => {
1900
+ const originalGate = gates.find((g) => g.id === shiftedGate.id);
1901
+ if (!originalGate) return null;
1902
+ if (originalGate.type === "CNOT" && originalGate.control !== void 0 && originalGate.target !== void 0) {
1903
+ const top = Math.min(originalGate.control, originalGate.target) * QUBIT_HEIGHT + QUBIT_HEIGHT / 2;
1904
+ const height = Math.abs(originalGate.target - originalGate.control) * QUBIT_HEIGHT;
1905
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1906
+ "div",
1907
+ {
1908
+ className: "circuit-editor__cnot circuit-editor__cnot--shifted-preview",
1909
+ style: {
1910
+ left: `${columnCenterXs[shiftedGate.newPosition]}px`,
1911
+ top: `${top}px`,
1912
+ height: `${height}px`
1913
+ },
1914
+ children: [
1915
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "circuit-editor__cnot-line" }),
1916
+ /* @__PURE__ */ jsxRuntime.jsx(
1917
+ "div",
1918
+ {
1919
+ className: "circuit-editor__cnot-control",
1920
+ style: {
1921
+ top: originalGate.control < originalGate.target ? "0" : "100%"
1922
+ }
1923
+ }
1924
+ ),
1925
+ /* @__PURE__ */ jsxRuntime.jsx(
1926
+ "div",
1927
+ {
1928
+ className: "circuit-editor__cnot-target",
1929
+ style: {
1930
+ top: originalGate.target < originalGate.control ? "0" : "100%"
1931
+ }
1932
+ }
1933
+ )
1934
+ ]
1935
+ },
1936
+ shiftedGate.id
1937
+ );
1938
+ }
1939
+ if (originalGate.qubit === void 0) return null;
1940
+ const isRotationGate = ["RX", "RY", "RZ"].includes(originalGate.type);
1941
+ const parameterLabel = originalGate.parameter !== void 0 ? `(${(originalGate.parameter / Math.PI).toFixed(2)}\u03C0)` : "";
1942
+ return /* @__PURE__ */ jsxRuntime.jsx(
1943
+ "div",
1944
+ {
1945
+ className: `circuit-editor__gate circuit-editor__gate--shifted-preview ${isRotationGate ? "circuit-editor__gate--rotation" : ""}`,
1946
+ style: {
1947
+ left: `${columnCenterXs[shiftedGate.newPosition]}px`,
1948
+ top: `${originalGate.qubit * QUBIT_HEIGHT + QUBIT_HEIGHT / 2}px`,
1949
+ backgroundColor: GATE_COLORS[originalGate.type]
1950
+ },
1951
+ children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "circuit-editor__gate-label", children: [
1952
+ originalGate.type,
1953
+ isRotationGate && originalGate.parameter !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "circuit-editor__gate-param", children: parameterLabel })
1954
+ ] })
1955
+ },
1956
+ shiftedGate.id
1957
+ );
1958
+ }) }),
1959
+ selectedGate && /* @__PURE__ */ jsxRuntime.jsx(
1960
+ GateToolbar,
1961
+ {
1962
+ gate: selectedGate,
1963
+ position: {
1964
+ top: selectedGate.type === "CNOT" && selectedGate.control !== void 0 && selectedGate.target !== void 0 ? Math.min(selectedGate.control, selectedGate.target) * QUBIT_HEIGHT + QUBIT_HEIGHT / 2 : (selectedGate.qubit ?? 0) * QUBIT_HEIGHT + QUBIT_HEIGHT / 2,
1965
+ left: columnCenterXs[selectedGate.position]
1966
+ },
1967
+ showBelow: selectedGate.type === "CNOT" ? Math.min(selectedGate.control ?? 0, selectedGate.target ?? 0) === 0 : selectedGate.qubit === 0,
1968
+ onEdit: ["RX", "RY", "RZ", "CNOT"].includes(selectedGate.type) ? () => handleGateEdit(selectedGate) : void 0,
1969
+ onDelete: () => handleGateDelete(selectedGate.id)
1970
+ }
1971
+ ),
1972
+ selectedQubitIndex !== null && /* @__PURE__ */ jsxRuntime.jsx(
1973
+ QubitToolbar,
1974
+ {
1975
+ position: {
1976
+ top: selectedQubitIndex * QUBIT_HEIGHT + QUBIT_HEIGHT / 2,
1977
+ left: LABEL_WIDTH
1978
+ },
1979
+ canAdd: qubits < config.maxQubits,
1980
+ canRemove: qubits > 1,
1981
+ onAddQubit: handleQubitAdd,
1982
+ onDeleteQubit: handleQubitRemove
1983
+ }
1984
+ )
1985
+ ]
1986
+ }
1987
+ ) }),
1988
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "circuit-editor__measurements", children: Array.from({ length: qubits }).map((_, qubitIndex) => /* @__PURE__ */ jsxRuntime.jsx(
1989
+ "div",
1990
+ {
1991
+ className: "circuit-editor__measurement",
1992
+ style: {
1993
+ top: `${qubitIndex * QUBIT_HEIGHT + QUBIT_HEIGHT / 2}px`
1994
+ },
1995
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "circuit-editor__measurement-icon" })
1996
+ },
1997
+ qubitIndex
1998
+ )) })
1999
+ ] }) });
2000
+ }
2001
+ function GateToolbar({ gate, position, showBelow = false, onEdit, onDelete }) {
2002
+ const isEditable = ["RX", "RY", "RZ", "CNOT"].includes(gate.type);
2003
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2004
+ "div",
2005
+ {
2006
+ className: `circuit-editor__toolbar ${showBelow ? "circuit-editor__toolbar--below" : ""}`,
2007
+ style: {
2008
+ top: `${position.top}px`,
2009
+ left: `${position.left}px`
2010
+ },
2011
+ onClick: (e) => e.stopPropagation(),
2012
+ children: [
2013
+ isEditable && onEdit && /* @__PURE__ */ jsxRuntime.jsx(
2014
+ "button",
2015
+ {
2016
+ className: "circuit-editor__toolbar-btn",
2017
+ onClick: (e) => {
2018
+ e.stopPropagation();
2019
+ onEdit();
2020
+ },
2021
+ title: "Edit operation",
2022
+ children: /* @__PURE__ */ jsxRuntime.jsx(EditIcon, {})
2023
+ }
2024
+ ),
2025
+ /* @__PURE__ */ jsxRuntime.jsx(
2026
+ "button",
2027
+ {
2028
+ className: "circuit-editor__toolbar-btn circuit-editor__toolbar-btn--danger",
2029
+ onClick: (e) => {
2030
+ e.stopPropagation();
2031
+ onDelete();
2032
+ },
2033
+ title: "Delete",
2034
+ children: /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {})
2035
+ }
2036
+ )
2037
+ ]
2038
+ }
2039
+ );
2040
+ }
2041
+ function QubitToolbar({
2042
+ position,
2043
+ canAdd,
2044
+ canRemove,
2045
+ onAddQubit,
2046
+ onDeleteQubit
2047
+ }) {
2048
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2049
+ "div",
2050
+ {
2051
+ className: "circuit-editor__toolbar circuit-editor__toolbar--qubit",
2052
+ style: {
2053
+ top: `${position.top}px`,
2054
+ left: `${position.left}px`
2055
+ },
2056
+ onClick: (e) => e.stopPropagation(),
2057
+ children: [
2058
+ /* @__PURE__ */ jsxRuntime.jsx(
2059
+ "button",
2060
+ {
2061
+ className: "circuit-editor__toolbar-btn",
2062
+ onClick: (e) => {
2063
+ e.stopPropagation();
2064
+ onAddQubit();
2065
+ },
2066
+ disabled: !canAdd,
2067
+ title: "Add qubit",
2068
+ children: /* @__PURE__ */ jsxRuntime.jsx(AddIcon, {})
2069
+ }
2070
+ ),
2071
+ /* @__PURE__ */ jsxRuntime.jsx(
2072
+ "button",
2073
+ {
2074
+ className: "circuit-editor__toolbar-btn",
2075
+ onClick: (e) => {
2076
+ e.stopPropagation();
2077
+ onDeleteQubit();
2078
+ },
2079
+ disabled: !canRemove,
2080
+ title: "Delete qubit",
2081
+ children: /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {})
2082
+ }
2083
+ )
2084
+ ]
2085
+ }
2086
+ );
2087
+ }
2088
+ function EditIcon() {
2089
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12.146 0.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z" }) });
2090
+ }
2091
+ function TrashIcon() {
2092
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: [
2093
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z" }),
2094
+ /* @__PURE__ */ jsxRuntime.jsx(
2095
+ "path",
2096
+ {
2097
+ fillRule: "evenodd",
2098
+ d: "M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"
2099
+ }
2100
+ )
2101
+ ] });
2102
+ }
2103
+ function AddIcon() {
2104
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx(
2105
+ "path",
2106
+ {
2107
+ fillRule: "evenodd",
2108
+ d: "M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2z"
2109
+ }
2110
+ ) });
2111
+ }
2112
+
2113
+ // src/components/SimulationControls/SimulationControls.scss
2114
+ var css3 = `.simulation-controls__run-btn {
2115
+ display: inline-flex;
2116
+ align-items: center;
2117
+ gap: 0.5rem;
2118
+ padding: 0.5rem 1rem;
2119
+ font-size: 0.875rem;
2120
+ font-weight: 500;
2121
+ border: none;
2122
+ border-radius: 4px;
2123
+ cursor: pointer;
2124
+ transition: background-color 0.15s, opacity 0.15s;
2125
+ }
2126
+ .simulation-controls__run-btn--primary {
2127
+ background: var(--qamposer-accent, #4285f4);
2128
+ color: white;
2129
+ }
2130
+ .simulation-controls__run-btn--primary:hover:not(:disabled) {
2131
+ background: var(--qamposer-accent-hover, #3367d6);
2132
+ }
2133
+ .simulation-controls__run-btn--secondary {
2134
+ background: var(--qamposer-bg-secondary, #262626);
2135
+ color: var(--qamposer-text-primary, #f4f4f4);
2136
+ border: 1px solid var(--qamposer-border, #525252);
2137
+ }
2138
+ .simulation-controls__run-btn--secondary:hover:not(:disabled) {
2139
+ background: var(--qamposer-hover, rgba(255, 255, 255, 0.1));
2140
+ }
2141
+ .simulation-controls__run-btn:disabled {
2142
+ opacity: 0.5;
2143
+ cursor: not-allowed;
2144
+ }
2145
+ .simulation-controls__spinner {
2146
+ animation: spin 1s linear infinite;
2147
+ }
2148
+ .simulation-controls__overlay {
2149
+ position: fixed;
2150
+ top: 0;
2151
+ left: 0;
2152
+ right: 0;
2153
+ bottom: 0;
2154
+ background: rgba(0, 0, 0, 0.65);
2155
+ display: flex;
2156
+ align-items: center;
2157
+ justify-content: center;
2158
+ z-index: 1000;
2159
+ }
2160
+ .simulation-controls__dialog {
2161
+ background: var(--qamposer-bg-secondary, #262626);
2162
+ border: 1px solid var(--qamposer-border, #525252);
2163
+ border-radius: 8px;
2164
+ width: 100%;
2165
+ max-width: 480px;
2166
+ max-height: 90vh;
2167
+ overflow-y: auto;
2168
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
2169
+ }
2170
+ .simulation-controls__dialog-header {
2171
+ display: flex;
2172
+ align-items: center;
2173
+ justify-content: space-between;
2174
+ padding: 1rem 1.5rem;
2175
+ border-bottom: 1px solid var(--qamposer-border, #525252);
2176
+ }
2177
+ .simulation-controls__dialog-header h2 {
2178
+ margin: 0;
2179
+ font-size: 1.125rem;
2180
+ font-weight: 600;
2181
+ color: var(--qamposer-text-primary, #f4f4f4);
2182
+ }
2183
+ .simulation-controls__close-btn {
2184
+ display: flex;
2185
+ align-items: center;
2186
+ justify-content: center;
2187
+ width: 32px;
2188
+ height: 32px;
2189
+ padding: 0;
2190
+ background: transparent;
2191
+ border: none;
2192
+ border-radius: 4px;
2193
+ color: var(--qamposer-text-secondary, #a8a8a8);
2194
+ cursor: pointer;
2195
+ }
2196
+ .simulation-controls__close-btn:hover {
2197
+ background: var(--qamposer-hover, rgba(255, 255, 255, 0.1));
2198
+ color: var(--qamposer-text-primary, #f4f4f4);
2199
+ }
2200
+ .simulation-controls__dialog-content {
2201
+ padding: 1.5rem;
2202
+ }
2203
+ .simulation-controls__section {
2204
+ margin-bottom: 1.5rem;
2205
+ }
2206
+ .simulation-controls__section:last-child {
2207
+ margin-bottom: 0;
2208
+ }
2209
+ .simulation-controls__section h3 {
2210
+ margin: 0 0 1rem 0;
2211
+ font-size: 0.875rem;
2212
+ font-weight: 600;
2213
+ color: var(--qamposer-text-primary, #f4f4f4);
2214
+ }
2215
+ .simulation-controls__field label {
2216
+ display: block;
2217
+ margin-bottom: 0.5rem;
2218
+ font-size: 0.75rem;
2219
+ color: var(--qamposer-text-secondary, #a8a8a8);
2220
+ }
2221
+ .simulation-controls__field input {
2222
+ width: 100%;
2223
+ padding: 0.5rem 0.75rem;
2224
+ font-size: 0.875rem;
2225
+ color: var(--qamposer-text-primary, #f4f4f4);
2226
+ background: var(--qamposer-bg-primary, #161616);
2227
+ border: 1px solid var(--qamposer-border, #525252);
2228
+ border-radius: 4px;
2229
+ }
2230
+ .simulation-controls__field input:focus {
2231
+ outline: 2px solid var(--qamposer-accent, #4285f4);
2232
+ outline-offset: -2px;
2233
+ }
2234
+ .simulation-controls__loading {
2235
+ display: flex;
2236
+ align-items: center;
2237
+ gap: 0.5rem;
2238
+ padding: 1rem;
2239
+ color: var(--qamposer-text-secondary, #a8a8a8);
2240
+ font-size: 0.875rem;
2241
+ }
2242
+ .simulation-controls__error {
2243
+ display: flex;
2244
+ align-items: flex-start;
2245
+ gap: 0.75rem;
2246
+ padding: 1rem;
2247
+ background: rgba(218, 30, 40, 0.1);
2248
+ border: 1px solid rgba(218, 30, 40, 0.3);
2249
+ border-radius: 4px;
2250
+ }
2251
+ .simulation-controls__error-icon {
2252
+ display: flex;
2253
+ align-items: center;
2254
+ justify-content: center;
2255
+ flex-shrink: 0;
2256
+ width: 20px;
2257
+ height: 20px;
2258
+ background: #da1e28;
2259
+ color: white;
2260
+ font-size: 0.75rem;
2261
+ font-weight: 700;
2262
+ border-radius: 50%;
2263
+ }
2264
+ .simulation-controls__error-content {
2265
+ flex: 1;
2266
+ min-width: 0;
2267
+ }
2268
+ .simulation-controls__error-title {
2269
+ margin: 0 0 0.25rem 0;
2270
+ font-size: 0.875rem;
2271
+ font-weight: 600;
2272
+ color: #fa4d56;
2273
+ }
2274
+ .simulation-controls__error-desc {
2275
+ margin: 0;
2276
+ font-size: 0.75rem;
2277
+ color: var(--qamposer-text-secondary, #a8a8a8);
2278
+ line-height: 1.4;
2279
+ }
2280
+ .simulation-controls__backend-list {
2281
+ display: flex;
2282
+ flex-direction: column;
2283
+ gap: 0.5rem;
2284
+ max-height: 240px;
2285
+ overflow-y: auto;
2286
+ }
2287
+ .simulation-controls__backend-item {
2288
+ display: flex;
2289
+ align-items: center;
2290
+ width: 100%;
2291
+ padding: 0.75rem 1rem;
2292
+ text-align: left;
2293
+ background: var(--qamposer-bg-primary, #161616);
2294
+ border: 1px solid var(--qamposer-border, #525252);
2295
+ border-radius: 4px;
2296
+ cursor: pointer;
2297
+ transition: border-color 0.15s, background-color 0.15s;
2298
+ }
2299
+ .simulation-controls__backend-item:hover {
2300
+ background: var(--qamposer-hover, rgba(255, 255, 255, 0.05));
2301
+ }
2302
+ .simulation-controls__backend-item--selected {
2303
+ border-color: var(--qamposer-accent, #4285f4);
2304
+ background: rgba(66, 133, 244, 0.1);
2305
+ }
2306
+ .simulation-controls__backend-item-content {
2307
+ flex: 1;
2308
+ min-width: 0;
2309
+ }
2310
+ .simulation-controls__backend-item-header {
2311
+ display: flex;
2312
+ align-items: center;
2313
+ gap: 0.5rem;
2314
+ margin-bottom: 0.25rem;
2315
+ }
2316
+ .simulation-controls__backend-name {
2317
+ font-size: 0.875rem;
2318
+ font-weight: 500;
2319
+ color: var(--qamposer-text-primary, #f4f4f4);
2320
+ }
2321
+ .simulation-controls__backend-type {
2322
+ padding: 0.125rem 0.375rem;
2323
+ font-size: 0.625rem;
2324
+ font-weight: 600;
2325
+ text-transform: uppercase;
2326
+ letter-spacing: 0.5px;
2327
+ border-radius: 2px;
2328
+ }
2329
+ .simulation-controls__backend-type--ideal {
2330
+ background: rgba(72, 187, 120, 0.2);
2331
+ color: #48bb78;
2332
+ }
2333
+ .simulation-controls__backend-type--noisy_fake {
2334
+ background: rgba(237, 137, 54, 0.2);
2335
+ color: #ed8936;
2336
+ }
2337
+ .simulation-controls__backend-desc {
2338
+ margin: 0;
2339
+ font-size: 0.75rem;
2340
+ color: var(--qamposer-text-secondary, #a8a8a8);
2341
+ white-space: nowrap;
2342
+ overflow: hidden;
2343
+ text-overflow: ellipsis;
2344
+ }
2345
+ .simulation-controls__backend-check {
2346
+ display: flex;
2347
+ align-items: center;
2348
+ justify-content: center;
2349
+ flex-shrink: 0;
2350
+ width: 20px;
2351
+ height: 20px;
2352
+ margin-left: 0.75rem;
2353
+ color: var(--qamposer-accent, #4285f4);
2354
+ }
2355
+ .simulation-controls__dialog-footer {
2356
+ display: flex;
2357
+ justify-content: flex-end;
2358
+ gap: 0.75rem;
2359
+ padding: 1rem 1.5rem;
2360
+ border-top: 1px solid var(--qamposer-border, #525252);
2361
+ }
2362
+ .simulation-controls__btn {
2363
+ display: inline-flex;
2364
+ align-items: center;
2365
+ gap: 0.5rem;
2366
+ padding: 0.625rem 1rem;
2367
+ font-size: 0.875rem;
2368
+ font-weight: 500;
2369
+ border: none;
2370
+ border-radius: 4px;
2371
+ cursor: pointer;
2372
+ transition: background-color 0.15s;
2373
+ }
2374
+ .simulation-controls__btn--primary {
2375
+ background: var(--qamposer-accent, #4285f4);
2376
+ color: white;
2377
+ }
2378
+ .simulation-controls__btn--primary:hover:not(:disabled) {
2379
+ background: var(--qamposer-accent-hover, #3367d6);
2380
+ }
2381
+ .simulation-controls__btn--secondary {
2382
+ background: transparent;
2383
+ color: var(--qamposer-text-primary, #f4f4f4);
2384
+ border: 1px solid var(--qamposer-border, #525252);
2385
+ }
2386
+ .simulation-controls__btn--secondary:hover:not(:disabled) {
2387
+ background: var(--qamposer-hover, rgba(255, 255, 255, 0.1));
2388
+ }
2389
+ .simulation-controls__btn:disabled {
2390
+ opacity: 0.5;
2391
+ cursor: not-allowed;
2392
+ }
2393
+
2394
+ @keyframes spin {
2395
+ from {
2396
+ transform: rotate(0deg);
2397
+ }
2398
+ to {
2399
+ transform: rotate(360deg);
2400
+ }
2401
+ }`;
2402
+ document.head.appendChild(document.createElement("style")).appendChild(document.createTextNode(css3));
2403
+ var PlayIcon = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 32 32", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 28a1 1 0 01-1-1V5a1 1 0 011.5-.87l21 11a1 1 0 010 1.74l-21 11A1 1 0 017 28z" }) });
2404
+ var CloseIcon2 = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 32 32", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M24 9.4L22.6 8 16 14.6 9.4 8 8 9.4l6.6 6.6L8 22.6 9.4 24l6.6-6.6 6.6 6.6 1.4-1.4-6.6-6.6L24 9.4z" }) });
2405
+ var LoadingSpinner = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", className: "simulation-controls__spinner", children: /* @__PURE__ */ jsxRuntime.jsx(
2406
+ "circle",
2407
+ {
2408
+ cx: "8",
2409
+ cy: "8",
2410
+ r: "6",
2411
+ fill: "none",
2412
+ stroke: "currentColor",
2413
+ strokeWidth: "2",
2414
+ strokeDasharray: "25 75"
2415
+ }
2416
+ ) });
2417
+ var CheckIcon = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 32 32", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M13 24l-9-9 1.4-1.4L13 21.2 26.6 7.6 28 9z" }) });
2418
+ function SimulationControls({
2419
+ buttonOnly = false,
2420
+ buttonLabel = "Set up and run",
2421
+ className = ""
2422
+ }) {
2423
+ const { simulate, status, circuit, adapter, canSimulate } = useQamposer();
2424
+ const isSimulating = status === "simulating";
2425
+ const [isDialogOpen, setIsDialogOpen] = react.useState(false);
2426
+ const [shots, setShots] = react.useState(1024);
2427
+ const [backends, setBackends] = react.useState([]);
2428
+ const [selectedBackendId, setSelectedBackendId] = react.useState("ideal");
2429
+ const [isLoadingBackends, setIsLoadingBackends] = react.useState(false);
2430
+ const [backendError, setBackendError] = react.useState(false);
2431
+ react.useEffect(() => {
2432
+ if (isDialogOpen && adapter.getBackends) {
2433
+ setIsLoadingBackends(true);
2434
+ setBackendError(false);
2435
+ adapter.getBackends().then((result) => {
2436
+ setBackends(result);
2437
+ if (result.length === 1 && result[0].id === "ideal" && !canSimulate) {
2438
+ setBackendError(true);
2439
+ }
2440
+ }).finally(() => setIsLoadingBackends(false));
2441
+ }
2442
+ }, [isDialogOpen, adapter, canSimulate]);
2443
+ const selectedBackend = backends.find((b) => b.id === selectedBackendId);
2444
+ const buildProfile = () => {
2445
+ if (!selectedBackend || selectedBackend.backend_type === "ideal") {
2446
+ return { type: "ideal" };
2447
+ }
2448
+ return {
2449
+ type: "noisy_fake",
2450
+ backend_name: selectedBackend.id
2451
+ };
2452
+ };
2453
+ const handleRun = async () => {
2454
+ if (circuit.gates.length === 0) {
2455
+ alert("Please add gates to the circuit before running simulation");
2456
+ return;
2457
+ }
2458
+ const profile = buildProfile();
2459
+ await simulate(shots, profile);
2460
+ setIsDialogOpen(false);
2461
+ };
2462
+ const handleQuickRun = async () => {
2463
+ if (circuit.gates.length === 0) {
2464
+ alert("Please add gates to the circuit before running simulation");
2465
+ return;
2466
+ }
2467
+ await simulate(shots);
2468
+ };
2469
+ if (buttonOnly) {
2470
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2471
+ "button",
2472
+ {
2473
+ className: `simulation-controls__run-btn simulation-controls__run-btn--primary ${className}`.trim(),
2474
+ onClick: handleQuickRun,
2475
+ disabled: isSimulating,
2476
+ children: [
2477
+ isSimulating ? /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner, {}) : /* @__PURE__ */ jsxRuntime.jsx(PlayIcon, {}),
2478
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: isSimulating ? "Running..." : "Run" })
2479
+ ]
2480
+ }
2481
+ );
2482
+ }
2483
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2484
+ /* @__PURE__ */ jsxRuntime.jsxs(
2485
+ "button",
2486
+ {
2487
+ className: `simulation-controls__run-btn simulation-controls__run-btn--primary ${className}`.trim(),
2488
+ onClick: () => setIsDialogOpen(true),
2489
+ disabled: isSimulating,
2490
+ children: [
2491
+ /* @__PURE__ */ jsxRuntime.jsx(PlayIcon, {}),
2492
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: buttonLabel })
2493
+ ]
2494
+ }
2495
+ ),
2496
+ isDialogOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "simulation-controls__overlay", onClick: () => setIsDialogOpen(false), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "simulation-controls__dialog", onClick: (e) => e.stopPropagation(), children: [
2497
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "simulation-controls__dialog-header", children: [
2498
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { children: "Set up and run your circuit" }),
2499
+ /* @__PURE__ */ jsxRuntime.jsx(
2500
+ "button",
2501
+ {
2502
+ className: "simulation-controls__close-btn",
2503
+ onClick: () => setIsDialogOpen(false),
2504
+ "aria-label": "Close",
2505
+ children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon2, {})
2506
+ }
2507
+ )
2508
+ ] }),
2509
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "simulation-controls__dialog-content", children: [
2510
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "simulation-controls__section", children: [
2511
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { children: "Step 1: Configure settings" }),
2512
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "simulation-controls__field", children: [
2513
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "shots-input", children: "Shots" }),
2514
+ /* @__PURE__ */ jsxRuntime.jsx(
2515
+ "input",
2516
+ {
2517
+ id: "shots-input",
2518
+ type: "number",
2519
+ value: shots,
2520
+ min: 1,
2521
+ max: 1e4,
2522
+ onChange: (e) => setShots(Number(e.target.value))
2523
+ }
2524
+ )
2525
+ ] })
2526
+ ] }),
2527
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "simulation-controls__section", children: [
2528
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { children: "Step 2: Choose a backend" }),
2529
+ isLoadingBackends ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "simulation-controls__loading", children: [
2530
+ /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner, {}),
2531
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Loading backends..." })
2532
+ ] }) : backendError ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "simulation-controls__error", children: [
2533
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "simulation-controls__error-icon", children: "!" }),
2534
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "simulation-controls__error-content", children: [
2535
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "simulation-controls__error-title", children: "Backend unavailable" }),
2536
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "simulation-controls__error-desc", children: "Cannot connect to the simulation backend. Please check that the server is running and CORS is configured correctly." })
2537
+ ] })
2538
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "simulation-controls__backend-list", children: backends.map((backend) => /* @__PURE__ */ jsxRuntime.jsxs(
2539
+ "button",
2540
+ {
2541
+ type: "button",
2542
+ className: `simulation-controls__backend-item ${selectedBackendId === backend.id ? "simulation-controls__backend-item--selected" : ""}`,
2543
+ onClick: () => setSelectedBackendId(backend.id),
2544
+ children: [
2545
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "simulation-controls__backend-item-content", children: [
2546
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "simulation-controls__backend-item-header", children: [
2547
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "simulation-controls__backend-name", children: backend.name }),
2548
+ /* @__PURE__ */ jsxRuntime.jsx(
2549
+ "span",
2550
+ {
2551
+ className: `simulation-controls__backend-type simulation-controls__backend-type--${backend.backend_type}`,
2552
+ children: backend.backend_type === "ideal" ? "Ideal" : "Noisy"
2553
+ }
2554
+ )
2555
+ ] }),
2556
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "simulation-controls__backend-desc", children: backend.description || `${backend.num_qubits} qubits` })
2557
+ ] }),
2558
+ selectedBackendId === backend.id && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "simulation-controls__backend-check", children: /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, {}) })
2559
+ ]
2560
+ },
2561
+ backend.id
2562
+ )) })
2563
+ ] })
2564
+ ] }),
2565
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "simulation-controls__dialog-footer", children: [
2566
+ /* @__PURE__ */ jsxRuntime.jsx(
2567
+ "button",
2568
+ {
2569
+ className: "simulation-controls__btn simulation-controls__btn--secondary",
2570
+ onClick: () => setIsDialogOpen(false),
2571
+ disabled: isSimulating,
2572
+ children: "Cancel"
2573
+ }
2574
+ ),
2575
+ /* @__PURE__ */ jsxRuntime.jsx(
2576
+ "button",
2577
+ {
2578
+ className: "simulation-controls__btn simulation-controls__btn--primary",
2579
+ onClick: handleRun,
2580
+ disabled: isSimulating || backendError,
2581
+ children: isSimulating ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2582
+ /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner, {}),
2583
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Running..." })
2584
+ ] }) : "Run circuit"
2585
+ }
2586
+ )
2587
+ ] })
2588
+ ] }) })
2589
+ ] });
2590
+ }
2591
+
2592
+ // src/presets/QamposerMicro/QamposerMicro.scss
2593
+ var css4 = `.qamposer-micro {
2594
+ display: flex;
2595
+ flex-direction: column;
2596
+ width: 100%;
2597
+ height: 100%;
2598
+ background: var(--qamposer-bg-primary, #161616);
2599
+ overflow: hidden;
2600
+ }
2601
+ .qamposer-micro__header {
2602
+ display: flex;
2603
+ align-items: center;
2604
+ justify-content: space-between;
2605
+ padding: 0 1rem;
2606
+ height: 48px;
2607
+ background: var(--qamposer-bg-secondary, #262626);
2608
+ border-bottom: 1px solid var(--qamposer-border, #525252);
2609
+ flex-shrink: 0;
2610
+ }
2611
+ .qamposer-micro__title {
2612
+ margin: 0;
2613
+ font-size: 1rem;
2614
+ font-weight: 600;
2615
+ color: var(--qamposer-text-primary, #f4f4f4);
2616
+ }
2617
+ .qamposer-micro__actions {
2618
+ display: flex;
2619
+ align-items: center;
2620
+ gap: 0.5rem;
2621
+ }
2622
+ .qamposer-micro__theme-toggle {
2623
+ display: flex;
2624
+ align-items: center;
2625
+ justify-content: center;
2626
+ width: 36px;
2627
+ height: 36px;
2628
+ padding: 0;
2629
+ border: none;
2630
+ border-radius: 4px;
2631
+ background: transparent;
2632
+ color: var(--qamposer-text-secondary, #a8a8a8);
2633
+ cursor: pointer;
2634
+ transition: all 0.15s;
2635
+ }
2636
+ .qamposer-micro__theme-toggle:hover {
2637
+ background: var(--qamposer-hover, rgba(255, 255, 255, 0.1));
2638
+ color: var(--qamposer-text-primary, #f4f4f4);
2639
+ }
2640
+ .qamposer-micro__layout {
2641
+ flex: 1;
2642
+ display: grid;
2643
+ grid-template-columns: 1fr 3fr;
2644
+ overflow: hidden;
2645
+ min-height: 0;
2646
+ }
2647
+ .qamposer-micro__operations {
2648
+ grid-column: 1;
2649
+ border-right: 2px solid var(--qamposer-border, #525252);
2650
+ overflow-y: auto;
2651
+ overflow-x: hidden;
2652
+ min-height: 0;
2653
+ }
2654
+ .qamposer-micro__circuit {
2655
+ grid-column: 2;
2656
+ overflow: auto;
2657
+ min-height: 0;
2658
+ }`;
2659
+ document.head.appendChild(document.createElement("style")).appendChild(document.createTextNode(css4));
2660
+ function QamposerMicro({
2661
+ className = "",
2662
+ showHeader = true,
2663
+ title = "Qamposer",
2664
+ gridTemplate = "1fr 3fr",
2665
+ defaultTheme = "dark",
2666
+ showThemeToggle = true,
2667
+ ...providerProps
2668
+ }) {
2669
+ return /* @__PURE__ */ jsxRuntime.jsx(ThemeProvider, { defaultTheme, children: /* @__PURE__ */ jsxRuntime.jsx(QamposerProvider, { ...providerProps, children: /* @__PURE__ */ jsxRuntime.jsx(
2670
+ QamposerMicroContent,
2671
+ {
2672
+ className,
2673
+ showHeader,
2674
+ title,
2675
+ gridTemplate,
2676
+ showThemeToggle
2677
+ }
2678
+ ) }) });
2679
+ }
2680
+ function QamposerMicroContent({
2681
+ className,
2682
+ showHeader,
2683
+ title,
2684
+ gridTemplate,
2685
+ showThemeToggle
2686
+ }) {
2687
+ const { theme, toggleTheme } = useTheme();
2688
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `qamposer-micro ${className}`.trim(), children: [
2689
+ showHeader && /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "qamposer-micro__header", children: [
2690
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "qamposer-micro__title", children: title }),
2691
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "qamposer-micro__actions", children: [
2692
+ showThemeToggle && /* @__PURE__ */ jsxRuntime.jsx(
2693
+ "button",
2694
+ {
2695
+ className: "qamposer-micro__theme-toggle",
2696
+ onClick: toggleTheme,
2697
+ title: theme === "dark" ? "Switch to light mode" : "Switch to dark mode",
2698
+ "aria-label": "Toggle theme",
2699
+ children: theme === "dark" ? /* @__PURE__ */ jsxRuntime.jsx(SunIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(MoonIcon, {})
2700
+ }
2701
+ ),
2702
+ /* @__PURE__ */ jsxRuntime.jsx(SimulationControls, {})
2703
+ ] })
2704
+ ] }),
2705
+ /* @__PURE__ */ jsxRuntime.jsxs(
2706
+ "div",
2707
+ {
2708
+ className: "qamposer-micro__layout",
2709
+ style: { gridTemplateColumns: gridTemplate },
2710
+ children: [
2711
+ /* @__PURE__ */ jsxRuntime.jsx("aside", { className: "qamposer-micro__operations", children: /* @__PURE__ */ jsxRuntime.jsx(Operations, {}) }),
2712
+ /* @__PURE__ */ jsxRuntime.jsx("main", { className: "qamposer-micro__circuit", children: /* @__PURE__ */ jsxRuntime.jsx(CircuitEditor, {}) })
2713
+ ]
2714
+ }
2715
+ )
2716
+ ] });
2717
+ }
2718
+ function SunIcon() {
2719
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2720
+ "svg",
2721
+ {
2722
+ width: "18",
2723
+ height: "18",
2724
+ viewBox: "0 0 24 24",
2725
+ fill: "none",
2726
+ stroke: "currentColor",
2727
+ strokeWidth: "2",
2728
+ children: [
2729
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "5" }),
2730
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "1", x2: "12", y2: "3" }),
2731
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "21", x2: "12", y2: "23" }),
2732
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "4.22", y1: "4.22", x2: "5.64", y2: "5.64" }),
2733
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "18.36", y1: "18.36", x2: "19.78", y2: "19.78" }),
2734
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "1", y1: "12", x2: "3", y2: "12" }),
2735
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "21", y1: "12", x2: "23", y2: "12" }),
2736
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "4.22", y1: "19.78", x2: "5.64", y2: "18.36" }),
2737
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "18.36", y1: "5.64", x2: "19.78", y2: "4.22" })
2738
+ ]
2739
+ }
2740
+ );
2741
+ }
2742
+ function MoonIcon() {
2743
+ return /* @__PURE__ */ jsxRuntime.jsx(
2744
+ "svg",
2745
+ {
2746
+ width: "18",
2747
+ height: "18",
2748
+ viewBox: "0 0 24 24",
2749
+ fill: "none",
2750
+ stroke: "currentColor",
2751
+ strokeWidth: "2",
2752
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" })
2753
+ }
2754
+ );
2755
+ }
2756
+
2757
+ // src/components/CodeEditor/CodeEditor.scss
2758
+ var css5 = `.code-editor {
2759
+ display: flex;
2760
+ flex-direction: column;
2761
+ height: 100%;
2762
+ width: 100%;
2763
+ background: var(--qamposer-bg-secondary, #262626);
2764
+ }
2765
+ .code-editor__header {
2766
+ display: flex;
2767
+ align-items: center;
2768
+ justify-content: space-between;
2769
+ padding: 0.5rem 0.75rem;
2770
+ border-bottom: 1px solid var(--qamposer-border, #525252);
2771
+ gap: 0.5rem;
2772
+ flex-shrink: 0;
2773
+ }
2774
+ .code-editor__title {
2775
+ display: flex;
2776
+ align-items: center;
2777
+ gap: 0.5rem;
2778
+ font-size: 0.875rem;
2779
+ font-weight: 500;
2780
+ color: var(--qamposer-text-primary, #f4f4f4);
2781
+ }
2782
+ .code-editor__title svg {
2783
+ color: var(--qamposer-text-secondary, #a8a8a8);
2784
+ }
2785
+ .code-editor__format select {
2786
+ min-width: 120px;
2787
+ height: 2rem;
2788
+ padding: 0 2rem 0 0.75rem;
2789
+ font-size: 0.75rem;
2790
+ color: var(--qamposer-text-primary, #f4f4f4);
2791
+ background: var(--qamposer-bg-primary, #161616);
2792
+ border: 1px solid var(--qamposer-border, #525252);
2793
+ border-radius: 4px;
2794
+ cursor: pointer;
2795
+ appearance: none;
2796
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E%3Cpath fill='%23a8a8a8' d='M8 11L3 6h10z'/%3E%3C/svg%3E");
2797
+ background-repeat: no-repeat;
2798
+ background-position: right 0.5rem center;
2799
+ }
2800
+ .code-editor__format select:disabled {
2801
+ opacity: 0.5;
2802
+ cursor: not-allowed;
2803
+ }
2804
+ .code-editor__format select:focus {
2805
+ outline: 2px solid var(--qamposer-accent, #4285f4);
2806
+ outline-offset: -2px;
2807
+ }
2808
+ .code-editor__body {
2809
+ flex: 1;
2810
+ display: flex;
2811
+ overflow: hidden;
2812
+ min-height: 0;
2813
+ background: var(--qamposer-bg-tertiary, #1a1a1a);
2814
+ border: 1px solid transparent;
2815
+ }
2816
+ .code-editor__body--error {
2817
+ border-color: var(--qamposer-error, #da1e28);
2818
+ }
2819
+ .code-editor__line-numbers {
2820
+ flex-shrink: 0;
2821
+ padding: 0.75rem 0;
2822
+ background: var(--qamposer-bg-secondary, #262626);
2823
+ border-right: 1px solid var(--qamposer-border, #525252);
2824
+ overflow: hidden;
2825
+ user-select: none;
2826
+ }
2827
+ .code-editor__line-number {
2828
+ padding: 0 0.75rem;
2829
+ font-family: "IBM Plex Mono", "Consolas", "Monaco", monospace;
2830
+ font-size: 0.75rem;
2831
+ line-height: 1.5rem;
2832
+ color: var(--qamposer-text-tertiary, #6f6f6f);
2833
+ text-align: right;
2834
+ min-width: 2.5rem;
2835
+ }
2836
+ .code-editor__textarea {
2837
+ flex: 1;
2838
+ padding: 0.75rem;
2839
+ font-family: "IBM Plex Mono", "Consolas", "Monaco", monospace;
2840
+ font-size: 0.75rem;
2841
+ line-height: 1.5rem;
2842
+ color: var(--qamposer-text-primary, #f4f4f4);
2843
+ background: transparent;
2844
+ border: none;
2845
+ outline: none;
2846
+ resize: none;
2847
+ overflow: auto;
2848
+ white-space: pre;
2849
+ tab-size: 2;
2850
+ }
2851
+ .code-editor__textarea::placeholder {
2852
+ color: var(--qamposer-text-tertiary, #6f6f6f);
2853
+ }
2854
+ .code-editor__textarea:focus {
2855
+ outline: none;
2856
+ }
2857
+ .code-editor__textarea[readonly] {
2858
+ cursor: default;
2859
+ }
2860
+ .code-editor__error {
2861
+ display: flex;
2862
+ align-items: center;
2863
+ gap: 0.5rem;
2864
+ padding: 0.5rem 0.75rem;
2865
+ font-size: 0.75rem;
2866
+ color: var(--qamposer-error, #da1e28);
2867
+ background: rgba(218, 30, 40, 0.1);
2868
+ border-top: 1px solid var(--qamposer-error, #da1e28);
2869
+ }
2870
+ .code-editor__error-icon {
2871
+ flex-shrink: 0;
2872
+ }`;
2873
+ document.head.appendChild(document.createElement("style")).appendChild(document.createTextNode(css5));
2874
+ var CodeIcon = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4.708 5.578L2.061 8.224l2.647 2.646-.708.708L1 8.224l3.354-3.354.354.708zm6.584 0l2.647 2.646-2.647 2.646.708.708L15 8.224l-3.354-3.354-.354.708zM6 13l3-10h1l-3 10H6z" }) });
2875
+ function CodeEditor({
2876
+ className = "",
2877
+ showHeader = true,
2878
+ placeholder = "Enter OpenQASM 2.0 code...",
2879
+ readOnly = false
2880
+ }) {
2881
+ const { qasmCode, setQasmCode, parseError } = useQamposer();
2882
+ const textareaRef = react.useRef(null);
2883
+ const lineNumbersRef = react.useRef(null);
2884
+ const handleScroll = react.useCallback(() => {
2885
+ if (textareaRef.current && lineNumbersRef.current) {
2886
+ lineNumbersRef.current.scrollTop = textareaRef.current.scrollTop;
2887
+ }
2888
+ }, []);
2889
+ const lineCount = qasmCode.split("\n").length;
2890
+ const lineNumbers = Array.from({ length: lineCount }, (_, i) => i + 1);
2891
+ const handleChange = react.useCallback(
2892
+ (e) => {
2893
+ setQasmCode(e.target.value);
2894
+ },
2895
+ [setQasmCode]
2896
+ );
2897
+ const handleKeyDown = react.useCallback(
2898
+ (e) => {
2899
+ if (e.key === "Tab") {
2900
+ e.preventDefault();
2901
+ const textarea = textareaRef.current;
2902
+ if (!textarea) return;
2903
+ const start = textarea.selectionStart;
2904
+ const end = textarea.selectionEnd;
2905
+ const newCode = qasmCode.substring(0, start) + " " + qasmCode.substring(end);
2906
+ setQasmCode(newCode);
2907
+ requestAnimationFrame(() => {
2908
+ textarea.selectionStart = textarea.selectionEnd = start + 2;
2909
+ });
2910
+ }
2911
+ },
2912
+ [qasmCode, setQasmCode]
2913
+ );
2914
+ react.useEffect(() => {
2915
+ handleScroll();
2916
+ }, [qasmCode, handleScroll]);
2917
+ const hasErrors = !!parseError;
2918
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `code-editor ${className}`.trim(), children: [
2919
+ showHeader && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "code-editor__header", children: [
2920
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "code-editor__title", children: [
2921
+ /* @__PURE__ */ jsxRuntime.jsx(CodeIcon, {}),
2922
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Code" })
2923
+ ] }),
2924
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "code-editor__format", children: /* @__PURE__ */ jsxRuntime.jsx("select", { disabled: true, defaultValue: "openqasm2", children: /* @__PURE__ */ jsxRuntime.jsx("option", { value: "openqasm2", children: "OpenQASM 2.0" }) }) })
2925
+ ] }),
2926
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `code-editor__body ${hasErrors ? "code-editor__body--error" : ""}`, children: [
2927
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "code-editor__line-numbers", ref: lineNumbersRef, children: lineNumbers.map((num) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "code-editor__line-number", children: num }, num)) }),
2928
+ /* @__PURE__ */ jsxRuntime.jsx(
2929
+ "textarea",
2930
+ {
2931
+ ref: textareaRef,
2932
+ className: "code-editor__textarea",
2933
+ value: qasmCode,
2934
+ onChange: handleChange,
2935
+ onScroll: handleScroll,
2936
+ onKeyDown: handleKeyDown,
2937
+ placeholder,
2938
+ spellCheck: false,
2939
+ autoCapitalize: "off",
2940
+ autoCorrect: "off",
2941
+ readOnly
2942
+ }
2943
+ )
2944
+ ] }),
2945
+ parseError && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "code-editor__error", children: [
2946
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "code-editor__error-icon", children: "\u26A0" }),
2947
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: parseError })
2948
+ ] })
2949
+ ] });
2950
+ }
2951
+
2952
+ // src/adapters/qiskit.ts
2953
+ function qiskitAdapter(configOrUrl) {
2954
+ const config = typeof configOrUrl === "string" ? { baseUrl: configOrUrl } : configOrUrl;
2955
+ const { baseUrl, headers = {}, timeout = 3e4 } = config;
2956
+ return {
2957
+ name: "Qiskit Simulator",
2958
+ async simulate(request) {
2959
+ const controller = new AbortController();
2960
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
2961
+ try {
2962
+ const response = await fetch(`${baseUrl}/api/circuit/simulate`, {
2963
+ method: "POST",
2964
+ headers: {
2965
+ "Content-Type": "application/json",
2966
+ ...headers
2967
+ },
2968
+ body: JSON.stringify(request),
2969
+ signal: controller.signal
2970
+ });
2971
+ if (!response.ok) {
2972
+ const errorText = await response.text().catch(() => response.statusText);
2973
+ throw new Error(`Simulation failed: ${errorText}`);
2974
+ }
2975
+ return response.json();
2976
+ } catch (error) {
2977
+ if (error instanceof Error && error.name === "AbortError") {
2978
+ throw new Error(`Simulation timed out after ${timeout}ms`);
2979
+ }
2980
+ throw error;
2981
+ } finally {
2982
+ clearTimeout(timeoutId);
2983
+ }
2984
+ },
2985
+ async isAvailable() {
2986
+ try {
2987
+ const response = await fetch(`${baseUrl}/health`, {
2988
+ method: "GET",
2989
+ headers
2990
+ });
2991
+ return response.ok;
2992
+ } catch {
2993
+ return false;
2994
+ }
2995
+ },
2996
+ async getBackends() {
2997
+ try {
2998
+ const response = await fetch(`${baseUrl}/api/circuit/backends`, {
2999
+ method: "GET",
3000
+ headers
3001
+ });
3002
+ if (!response.ok) {
3003
+ console.warn("Failed to fetch backends, using default");
3004
+ return [{ id: "ideal", name: "Ideal Simulator", num_qubits: 32, backend_type: "ideal" }];
3005
+ }
3006
+ return response.json();
3007
+ } catch {
3008
+ return [{ id: "ideal", name: "Ideal Simulator", num_qubits: 32, backend_type: "ideal" }];
3009
+ }
3010
+ }
3011
+ };
3012
+ }
3013
+
3014
+ exports.CircuitEditor = CircuitEditor;
3015
+ exports.CodeEditor = CodeEditor;
3016
+ exports.Operations = Operations;
3017
+ exports.QamposerMicro = QamposerMicro;
3018
+ exports.QamposerProvider = QamposerProvider;
3019
+ exports.SimulationControls = SimulationControls;
3020
+ exports.ThemeProvider = ThemeProvider;
3021
+ exports.circuitToQasm = circuitToQasm;
3022
+ exports.createDefaultCircuit = createDefaultCircuit;
3023
+ exports.generateGateId = generateGateId;
3024
+ exports.noopAdapter = noopAdapter;
3025
+ exports.qasmToCircuit = qasmToCircuit;
3026
+ exports.qiskitAdapter = qiskitAdapter;
3027
+ exports.useQamposer = useQamposer;
3028
+ exports.useTheme = useTheme;