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