@@ -123,7 +123,7 @@ LANGUAGES: Dict[str, Dict[str, str]] = {
123123 "sound_on" : "Звук: ВКЛ" , "sound_off" : "Звук: ВЫКЛ" ,
124124 "theme_sys" : "Системная" , "theme_dark" : "Тёмная" , "theme_light" : "Светлая" ,
125125 "about_text" : "Secure Pass Pro v4.0\n \n Профессиональный инструмент для генерации паролей.\n Использует криптографически стойкие алгоритмы." ,
126- "copied" : "Скопировано! (60с )" ,
126+ "copied" : "Скопировано! ({0}с )" ,
127127 "strength" : "Стойкость: ~{0} вариантов" ,
128128 "crack_time" : "{0}" ,
129129 "time_sec" : "Несколько секунд на взлом" ,
@@ -179,6 +179,8 @@ LANGUAGES: Dict[str, Dict[str, str]] = {
179179 "integrity_fail" : "🚨 КРИТИЧЕСКАЯ ОШИБКА: Файл повреждён или подменён!\n Контрольная сумма не совпадает. Открытие отменено." ,
180180 "integrity_ok" : "✅ Целостность файла подтверждена." ,
181181 "tt_eye" : "Показать / скрыть пароль" ,
182+ "clip_timeout" : "Очистка буфера: {0} сек" ,
183+ "tt_clip_timeout" : "Через сколько секунд буфер обмена будет очищен после копирования" ,
182184 },
183185 "EN" : {
184186 "win_title" : "Secure Pass Pro v4.0" ,
@@ -207,7 +209,7 @@ LANGUAGES: Dict[str, Dict[str, str]] = {
207209 "sound_on" : "Sound: ON" , "sound_off" : "Sound: OFF" ,
208210 "theme_sys" : "System" , "theme_dark" : "Dark" , "theme_light" : "Light" ,
209211 "about_text" : "Secure Pass Pro v4.0\n \n Professional password generation tool.\n Uses cryptographically secure algorithms." ,
210- "copied" : "Copied! (60s )" ,
212+ "copied" : "Copied! ({0}s )" ,
211213 "strength" : "Strength: ~{0} combos" ,
212214 "crack_time" : "{0}" ,
213215 "time_sec" : "A few seconds to crack" ,
@@ -263,6 +265,8 @@ LANGUAGES: Dict[str, Dict[str, str]] = {
263265 "integrity_fail" : "🚨 CRITICAL ERROR: File is corrupted or tampered!\n Checksum mismatch. Opening cancelled." ,
264266 "integrity_ok" : "✅ File integrity verified." ,
265267 "tt_eye" : "Show / hide password" ,
268+ "clip_timeout" : "Clipboard clear: {0} sec" ,
269+ "tt_clip_timeout" : "Seconds before clipboard is cleared after copying" ,
266270 },
267271 "UA" : {
268272 "win_title" : "Secure Pass Pro v4.0" ,
@@ -291,7 +295,7 @@ LANGUAGES: Dict[str, Dict[str, str]] = {
291295 "sound_on" : "Звук: ВКЛ" , "sound_off" : "Звук: ВИКЛ" ,
292296 "theme_sys" : "Системна" , "theme_dark" : "Темна" , "theme_light" : "Світла" ,
293297 "about_text" : "Secure Pass Pro v4.0\n \n Професійний інструмент для генерації паролів.\n Використовує криптографічно стійкі алгоритми." ,
294- "copied" : "Скопійовано! (60с )" ,
298+ "copied" : "Скопійовано! ({0}с )" ,
295299 "strength" : "Стійкість: ~{0} варіантів" ,
296300 "crack_time" : "{0}" ,
297301 "time_sec" : "Кілька секунд на злам" ,
@@ -347,6 +351,8 @@ LANGUAGES: Dict[str, Dict[str, str]] = {
347351 "integrity_fail" : "🚨 КРИТИЧНА ПОМИЛКА: Файл пошкоджений або підмінений!\n Контрольна сума не збігається. Відкриття скасовано." ,
348352 "integrity_ok" : "✅ Цілісність файлу підтверджена." ,
349353 "tt_eye" : "Показати / приховати пароль" ,
354+ "clip_timeout" : "Очищення буфера: {0} сек" ,
355+ "tt_clip_timeout" : "Через скільки секунд буфер обміну буде очищено після копіювання" ,
350356 }
351357}
352358
@@ -480,6 +486,7 @@ class SecurePassPro(ctk.CTk):
480486 self .history = deque (maxlen = HISTORY_MAX )
481487 self ._radius_widgets = []
482488 self ._clipboard_timer = None
489+ self .clipboard_timeout = 60 # секунд / seconds / секунд (10–120)
483490 self ._tooltips = {}
484491 self ._icon_image = None
485492 self ._pdf_font_path = self ._get_resource_path ("DejaVuSans.ttf" )
@@ -646,6 +653,37 @@ class SecurePassPro(ctk.CTk):
646653 elif line .startswith ("SOUND=" ):
647654 sound_val = line .strip ().split ("=" )[1 ].lower () == "true"
648655 self .sound_enabled .set (sound_val )
656+ elif line .startswith ("CLIP_TIMEOUT=" ):
657+ try :
658+ t = int (line .strip ().split ("=" )[1 ])
659+ if 10 <= t <= 120 :
660+ self .clipboard_timeout = t
661+ except ValueError :
662+ pass
663+ except :
664+ pass
665+
666+ def _save_clipboard_timeout (self , seconds : int ):
667+ """
668+ Сохраняет таймаут ТОЛЬКО если config.txt уже существует.
669+ Saves timeout ONLY if config.txt already exists — never creates the file.
670+ Зберігає таймаут ТІЛЬКИ якщо config.txt вже існує.
671+ """
672+ try :
673+ config_dir = os .path .join (os .path .expanduser ("~" ), ".securepasspro" )
674+ config_file = os .path .join (config_dir , "config.txt" )
675+ if not os .path .exists (config_file ):
676+ return # Файл не существует — не создаём / Don't create
677+ existing = {}
678+ with open (config_file , "r" , encoding = "utf-8" ) as f :
679+ for line in f :
680+ if "=" in line :
681+ key , val = line .strip ().split ("=" , 1 )
682+ existing [key ] = val
683+ existing ["CLIP_TIMEOUT" ] = str (seconds )
684+ with open (config_file , "w" , encoding = "utf-8" ) as f :
685+ for key , val in existing .items ():
686+ f .write (f"{ key } ={ val } \n " )
649687 except :
650688 pass
651689
@@ -1050,7 +1088,7 @@ class SecurePassPro(ctk.CTk):
10501088 self .settings_window .focus_force ()
10511089 self .settings_window .grab_set ()
10521090
1053- self ._center_window_relative_to_parent (self .settings_window , 420 , 480 )
1091+ self ._center_window_relative_to_parent (self .settings_window , 420 , 580 )
10541092 self ._apply_window_rounding (self .settings_window )
10551093
10561094 self .settings_window .protocol ("WM_DELETE_WINDOW" , self ._close_settings )
@@ -1162,8 +1200,27 @@ class SecurePassPro(ctk.CTk):
11621200 corner_radius = 10
11631201 )
11641202 close_btn .pack (pady = (5 , 10 ))
1165-
1166- # --- Секция мастер-пароля / Master password section / Секція майстер-пароля ---
1203+
1204+ # --- Секция таймаута буфера / Clipboard timeout section / Секція таймауту буфера ---
1205+ sep_clip = ctk .CTkFrame (main_frame , height = 2 , fg_color = "gray" )
1206+ sep_clip .pack (fill = "x" , pady = 8 )
1207+
1208+ clip_timeout_label = ctk .CTkLabel (
1209+ main_frame ,
1210+ text = L ["clip_timeout" ].format (self .clipboard_timeout ),
1211+ font = ("Segoe UI" , 16 , "bold" )
1212+ )
1213+ clip_timeout_label .pack (pady = (8 , 5 ))
1214+ self ._clip_timeout_label_ref = clip_timeout_label
1215+
1216+ clip_slider = ctk .CTkSlider (
1217+ main_frame , from_ = 10 , to = 120 ,
1218+ number_of_steps = 110 ,
1219+ width = 300 ,
1220+ command = self ._on_clip_timeout_change
1221+ )
1222+ clip_slider .set (self .clipboard_timeout )
1223+ clip_slider .pack (pady = (0 , 10 ))
11671224 sep4 = ctk .CTkFrame (main_frame , height = 2 , fg_color = "gray" )
11681225 sep4 .pack (fill = "x" , pady = 8 )
11691226
@@ -1193,6 +1250,23 @@ class SecurePassPro(ctk.CTk):
11931250 'radius_slider' : radius_slider
11941251 }
11951252
1253+ def _on_clip_timeout_change (self , val ):
1254+ """
1255+ Обработчик слайдера таймаута буфера обмена.
1256+ Clipboard timeout slider handler.
1257+ Обробник слайдера таймауту буфера обміну.
1258+ """
1259+ seconds = int (val )
1260+ self .clipboard_timeout = seconds
1261+ if hasattr (self , '_clip_timeout_label_ref' ) and \
1262+ self ._clip_timeout_label_ref and \
1263+ self ._clip_timeout_label_ref .winfo_exists ():
1264+ L = LANGUAGES [self .current_lang ]
1265+ self ._clip_timeout_label_ref .configure (
1266+ text = L ["clip_timeout" ].format (seconds )
1267+ )
1268+ self ._save_clipboard_timeout (seconds )
1269+
11961270 def _on_radius_change (self , val ):
11971271 """Handle radius slider change"""
11981272 rad = int (val )
@@ -1491,10 +1565,13 @@ class SecurePassPro(ctk.CTk):
14911565 self .after_cancel (self ._clipboard_timer )
14921566 except :
14931567 pass
1494- self ._clipboard_timer = self .after (60000 , lambda value = pwd : self ._clear_clipboard_if_current (value ))
1495-
1568+ self ._clipboard_timer = self .after (
1569+ self .clipboard_timeout * 1000 ,
1570+ lambda value = pwd : self ._clear_clipboard_if_current (value )
1571+ )
1572+
14961573 old_text = L ["btn_copy" ]
1497- self .btn_copy .configure (text = L ["copied" ])
1574+ self .btn_copy .configure (text = L ["copied" ]. format ( self . clipboard_timeout ) )
14981575 self .after (2000 , lambda : self .btn_copy .configure (text = old_text ))
14991576
15001577 def _clear_clipboard_if_current (self , expected ):
0 commit comments