1+ from PySide6 .QtWidgets import (QDialog , QVBoxLayout , QHBoxLayout , QLabel ,
2+ QLineEdit , QPushButton , QMessageBox , QProgressDialog , QProgressBar )
3+ from PySide6 .QtCore import Qt
4+ from PySide6 .QtGui import QPixmap
5+ from binaryninja import log_info , log_error , log_warn
6+ import os
7+
8+ class ApiKeyWizard (QDialog ):
9+ def __init__ (self , config ):
10+ super ().__init__ ()
11+ self .config = config
12+ self .init_ui ()
13+
14+ def init_ui (self ):
15+ self .setWindowTitle ("RevEng.AI Configuration Wizard" )
16+ self .setMinimumWidth (500 )
17+
18+ layout = QVBoxLayout ()
19+
20+ header_layout = QHBoxLayout ()
21+
22+ logo_label = QLabel ()
23+ logo_path = os .path .join (os .path .dirname (os .path .dirname (os .path .dirname (__file__ ))), "images" , "logo.png" )
24+ if os .path .exists (logo_path ):
25+ pixmap = QPixmap (logo_path )
26+ pixmap = pixmap .scaled (100 , 100 , Qt .KeepAspectRatio , Qt .SmoothTransformation )
27+ logo_label .setPixmap (pixmap )
28+ else :
29+ log_info ("RevEng.AI logo not found at: " + logo_path )
30+ header_layout .addWidget (logo_label )
31+
32+ welcome_layout = QVBoxLayout ()
33+ title_label = QLabel ("Welcome to RevEng.AI!" )
34+ title_label .setStyleSheet ("font-size: 18px; font-weight: bold;" )
35+ description_label = QLabel (
36+ "To get started, you'll need to configure your API key and host URL.\n "
37+ "You can get your API key from your RevEng.AI account settings."
38+ )
39+ description_label .setWordWrap (True )
40+ welcome_layout .addWidget (title_label )
41+ welcome_layout .addWidget (description_label )
42+ header_layout .addLayout (welcome_layout , stretch = 1 )
43+
44+ layout .addLayout (header_layout )
45+ layout .addSpacing (20 )
46+
47+ api_key_label = QLabel ("API Key:" )
48+ api_key_label .setStyleSheet ("font-weight: bold;" )
49+ self .api_key_input = QLineEdit ()
50+ self .api_key_input .setPlaceholderText ("Enter your RevEng.AI API key" )
51+ self .api_key_input .setText (self .config .api_key if self .config .api_key else "" )
52+
53+ layout .addWidget (api_key_label )
54+ layout .addWidget (self .api_key_input )
55+ layout .addSpacing (10 )
56+
57+ host_label = QLabel ("Host URL:" )
58+ host_label .setStyleSheet ("font-weight: bold;" )
59+ self .host_input = QLineEdit ()
60+ self .host_input .setPlaceholderText ("Enter the RevEng.AI host URL" )
61+ self .host_input .setText (self .config .host if self .config .host else "" )
62+
63+ layout .addWidget (host_label )
64+ layout .addWidget (self .host_input )
65+ layout .addSpacing (20 )
66+
67+ button_layout = QHBoxLayout ()
68+ self .save_button = QPushButton ("Save Configuration" )
69+ self .save_button .setStyleSheet ("""
70+ QPushButton {
71+ background-color: #007bff;
72+ color: white;
73+ padding: 8px 16px;
74+ border-radius: 4px;
75+ }
76+ QPushButton:hover {
77+ background-color: #4400ff;
78+ }
79+ """ )
80+ self .save_button .clicked .connect (self .save_config )
81+ self .cancel_button = QPushButton ("Cancel" )
82+ self .cancel_button .setStyleSheet ("""
83+ QPushButton {
84+ padding: 8px 16px;
85+ border-radius: 4px;
86+ }
87+ """ )
88+ self .cancel_button .clicked .connect (self .reject )
89+
90+ button_layout .addWidget (self .save_button )
91+ button_layout .addWidget (self .cancel_button )
92+ layout .addLayout (button_layout )
93+
94+ self .setLayout (layout )
95+
96+ def save_config (self ):
97+ api_key = self .api_key_input .text ().strip ()
98+ host = self .host_input .text ().strip ()
99+
100+ if not api_key :
101+ log_warn ("API Key field is empty" )
102+ QMessageBox .warning (
103+ self ,
104+ "RevEng.AI Configuration" ,
105+ "API Key is required to use RevEng.AI services." ,
106+ QMessageBox .Ok
107+ )
108+ return
109+
110+ if not host :
111+ log_warn ("Host URL field is empty" )
112+ QMessageBox .warning (
113+ self ,
114+ "RevEng.AI Configuration" ,
115+ "Host URL is required to connect to RevEng.AI services." ,
116+ QMessageBox .Ok
117+ )
118+ return
119+
120+ try :
121+ progress = QProgressDialog ("Testing API key..." , None , 0 , 0 , self )
122+ progress .setWindowTitle ("RevEng.AI Configuration" )
123+ progress .setWindowModality (Qt .WindowModal )
124+ progress .setCancelButton (None )
125+ progress .setMinimumWidth (400 )
126+ progress .setMinimumHeight (100 )
127+ progress .findChild (QProgressBar ).setMinimumWidth (250 )
128+ progress .findChild (QProgressBar ).setMinimumHeight (20 )
129+ progress .setStyleSheet ("""
130+ QProgressDialog {
131+ background-color: white;
132+ color: black;
133+ }
134+ QProgressBar {
135+ border: 1px solid #cccccc;
136+ border-radius: 4px;
137+ text-align: center;
138+ background-color: #f0f0f0;
139+ min-width: 250px;
140+ min-height: 20px;
141+ }
142+ QProgressBar::chunk {
143+ background-color: #007bff;
144+ border-radius: 3px;
145+ }
146+ QLabel {
147+ color: black;
148+ font-size: 13px;
149+ padding: 10px;
150+ }
151+ """ )
152+ progress .show ()
153+
154+ self .config .api_key = api_key
155+ self .config .host = host
156+ key_try = self .config .save_config ()
157+
158+ progress .close ()
159+
160+ if not key_try :
161+ raise Exception ("API key not valid or host not reachable." )
162+
163+ log_info ("RevEng.AI configuration saved successfully!" )
164+ QMessageBox .information (
165+ self ,
166+ "RevEng.AI Configuration" ,
167+ "Configuration saved successfully!\n You can now start using RevEng.AI services." ,
168+ QMessageBox .Ok
169+ )
170+ self .accept ()
171+
172+ except Exception as e :
173+ log_error (f"Failed to save RevEng.AI configuration: { str (e )} " )
174+ QMessageBox .critical (
175+ self ,
176+ "RevEng.AI Configuration Error" ,
177+ f"Failed to save configuration: { str (e )} " ,
178+ QMessageBox .Ok
179+ )
0 commit comments