@qamposer/react 0.1.0

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