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