frontengine 1.0.38__tar.gz → 1.0.39__tar.gz

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.
Files changed (238) hide show
  1. {frontengine-1.0.38 → frontengine-1.0.39}/PKG-INFO +12 -1
  2. {frontengine-1.0.38 → frontengine-1.0.39}/README.md +11 -0
  3. frontengine-1.0.39/frontengine/ui/dialog/screen_text_dialog.py +120 -0
  4. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/tools/tools_setting_ui.py +206 -16
  5. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/user_setting/user_setting_file.py +3 -0
  6. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/multi_language/english.py +19 -0
  7. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/multi_language/traditional_chinese.py +19 -0
  8. frontengine-1.0.39/frontengine/utils/recording/frame_recorder.py +199 -0
  9. frontengine-1.0.39/frontengine/utils/recording/gif_writer.py +203 -0
  10. frontengine-1.0.39/frontengine/worker/__init__.py +0 -0
  11. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine.egg-info/PKG-INFO +12 -1
  12. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine.egg-info/SOURCES.txt +4 -0
  13. {frontengine-1.0.38 → frontengine-1.0.39}/pyproject.toml +1 -1
  14. {frontengine-1.0.38 → frontengine-1.0.39}/LICENSE +0 -0
  15. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/__init__.py +0 -0
  16. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/__main__.py +0 -0
  17. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/__init__.py +0 -0
  18. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/base_widget.py +0 -0
  19. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/camera/__init__.py +0 -0
  20. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/camera/camera_widget.py +0 -0
  21. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/capture/__init__.py +0 -0
  22. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/capture/region_capture.py +0 -0
  23. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/focus_shield/__init__.py +0 -0
  24. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/focus_shield/focus_shield_widget.py +0 -0
  25. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/gif/__init__.py +0 -0
  26. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/gif/paint_gif.py +0 -0
  27. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/image/__init__.py +0 -0
  28. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/image/paint_image.py +0 -0
  29. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/load/__init__.py +0 -0
  30. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/load/load_someone_make_ui.py +0 -0
  31. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/measure/__init__.py +0 -0
  32. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/measure/measure_widget.py +0 -0
  33. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/monitor/__init__.py +0 -0
  34. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/monitor/monitor_widget.py +0 -0
  35. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/notes/__init__.py +0 -0
  36. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/notes/sticky_note_widget.py +0 -0
  37. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/overlay_factory.py +0 -0
  38. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/particle/__init__.py +0 -0
  39. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/particle/particle_ui.py +0 -0
  40. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/pet/__init__.py +0 -0
  41. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/pet/desktop_pet.py +0 -0
  42. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/presentation/__init__.py +0 -0
  43. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/presentation/annotation_overlay.py +0 -0
  44. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/presentation/cursor_effects.py +0 -0
  45. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/presentation/keystroke_display.py +0 -0
  46. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/presentation/magnifier.py +0 -0
  47. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/scene/__init__.py +0 -0
  48. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/scene/extend_graphic_scene.py +0 -0
  49. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/scene/extend_graphic_view.py +0 -0
  50. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/scene/scene.py +0 -0
  51. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/screen_care/__init__.py +0 -0
  52. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/screen_care/color_vision_widget.py +0 -0
  53. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/screen_care/screen_filter.py +0 -0
  54. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/sound_player/__init__.py +0 -0
  55. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/sound_player/sound_effect.py +0 -0
  56. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/sound_player/sound_player.py +0 -0
  57. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/spectrum/__init__.py +0 -0
  58. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/spectrum/spectrum_widget.py +0 -0
  59. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/text/__init__.py +0 -0
  60. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/text/draw_text.py +0 -0
  61. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/toast/__init__.py +0 -0
  62. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/toast/toast_widget.py +0 -0
  63. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/video/__init__.py +0 -0
  64. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/video/video_player.py +0 -0
  65. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/wallpaper/__init__.py +0 -0
  66. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/wallpaper/wallpaper_widget.py +0 -0
  67. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/web/__init__.py +0 -0
  68. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/web/webview.py +0 -0
  69. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/show/window_helpers.py +0 -0
  70. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/system_tray/__init__.py +0 -0
  71. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/system_tray/extend_system_tray.py +0 -0
  72. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/__init__.py +0 -0
  73. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/color/__init__.py +0 -0
  74. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/color/global_color.py +0 -0
  75. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/dialog/__init__.py +0 -0
  76. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/dialog/app_profile_dialog.py +0 -0
  77. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/dialog/choose_file_dialog.py +0 -0
  78. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/dialog/clipboard_dialog.py +0 -0
  79. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/dialog/hotkey_settings_dialog.py +0 -0
  80. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/dialog/reminder_dialog.py +0 -0
  81. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/dialog/save_file_dialog.py +0 -0
  82. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/dialog/smart_pause_dialog.py +0 -0
  83. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/dialog/usage_report_dialog.py +0 -0
  84. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/dialog/window_pin_dialog.py +0 -0
  85. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/main_ui.py +0 -0
  86. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/menu/__init__.py +0 -0
  87. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/menu/help_menu.py +0 -0
  88. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/menu/how_to_menu.py +0 -0
  89. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/menu/language_menu.py +0 -0
  90. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/menu/preset_menu.py +0 -0
  91. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/menu/settings_menu.py +0 -0
  92. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/__init__.py +0 -0
  93. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/control_center/__init__.py +0 -0
  94. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/control_center/control_center_ui.py +0 -0
  95. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/focus/__init__.py +0 -0
  96. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/focus/focus_setting_ui.py +0 -0
  97. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/gif/__init__.py +0 -0
  98. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/gif/gif_setting_ui.py +0 -0
  99. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/image/__init__.py +0 -0
  100. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/image/image_setting_ui.py +0 -0
  101. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/particle/__init__.py +0 -0
  102. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/particle/particle_setting_ui.py +0 -0
  103. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/pet/__init__.py +0 -0
  104. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/pet/pet_setting_ui.py +0 -0
  105. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/presentation/__init__.py +0 -0
  106. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/presentation/presentation_setting_ui.py +0 -0
  107. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/__init__.py +0 -0
  108. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_manager.py +0 -0
  109. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/__init__.py +0 -0
  110. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/base_scene_page.py +0 -0
  111. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/gif.py +0 -0
  112. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/image.py +0 -0
  113. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/registry.py +0 -0
  114. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/sound.py +0 -0
  115. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/text.py +0 -0
  116. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/video.py +0 -0
  117. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/web.py +0 -0
  118. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_setting_ui.py +0 -0
  119. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/screen_care/__init__.py +0 -0
  120. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/screen_care/screen_care_setting_ui.py +0 -0
  121. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/sound_player/__init__.py +0 -0
  122. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/sound_player/sound_player_setting_ui.py +0 -0
  123. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/text/__init__.py +0 -0
  124. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/text/text_setting_ui.py +0 -0
  125. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/tools/__init__.py +0 -0
  126. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/utils.py +0 -0
  127. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/video/__init__.py +0 -0
  128. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/video/video_setting_ui.py +0 -0
  129. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/wallpaper/__init__.py +0 -0
  130. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/wallpaper/wallpaper_setting_ui.py +0 -0
  131. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/web/__init__.py +0 -0
  132. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/web/web_setting_ui.py +0 -0
  133. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/widgets/__init__.py +0 -0
  134. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/ui/page/widgets/widgets_setting_ui.py +0 -0
  135. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/user_setting/__init__.py +0 -0
  136. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/user_setting/preset_repository.py +0 -0
  137. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/user_setting/scene_setting.py +0 -0
  138. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/__init__.py +0 -0
  139. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/app_profile/__init__.py +0 -0
  140. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/app_profile/app_profile_service.py +0 -0
  141. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/audio_meter/__init__.py +0 -0
  142. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/audio_meter/audio_envelope.py +0 -0
  143. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/audio_meter/loopback_capture.py +0 -0
  144. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/audio_meter/screen_audio.py +0 -0
  145. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/audio_meter/spectrum_analyzer.py +0 -0
  146. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/audio_meter/system_audio_meter.py +0 -0
  147. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/autostart/__init__.py +0 -0
  148. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/autostart/autostart_service.py +0 -0
  149. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/break_reminder/__init__.py +0 -0
  150. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/break_reminder/break_reminder.py +0 -0
  151. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/browser/__init__.py +0 -0
  152. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/browser/browser.py +0 -0
  153. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/clipboard/__init__.py +0 -0
  154. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/clipboard/clipboard_history.py +0 -0
  155. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/clipboard/clipboard_watcher.py +0 -0
  156. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/color_vision/__init__.py +0 -0
  157. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/color_vision/color_vision.py +0 -0
  158. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/critical_exit/__init__.py +0 -0
  159. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/critical_exit/check_key_is_press.py +0 -0
  160. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/critical_exit/critical_exit.py +0 -0
  161. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/critical_exit/win32_vk.py +0 -0
  162. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/exception/__init__.py +0 -0
  163. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/exception/exception_tags.py +0 -0
  164. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/exception/exceptions.py +0 -0
  165. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/file/__init__.py +0 -0
  166. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/file/open/__init__.py +0 -0
  167. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/file/open/open_file.py +0 -0
  168. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/file/save/__init__.py +0 -0
  169. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/file/save/save_file.py +0 -0
  170. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/focus_shield/__init__.py +0 -0
  171. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/focus_shield/focus_shield.py +0 -0
  172. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/focus_timer/__init__.py +0 -0
  173. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/focus_timer/focus_timer.py +0 -0
  174. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/hotkey/__init__.py +0 -0
  175. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/hotkey/hotkey_service.py +0 -0
  176. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/input_watch/__init__.py +0 -0
  177. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/input_watch/input_watch_service.py +0 -0
  178. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/json/__init__.py +0 -0
  179. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/json/json_file.py +0 -0
  180. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/json/json_repository.py +0 -0
  181. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/json_format/__init__.py +0 -0
  182. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/json_format/json_process.py +0 -0
  183. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/logging/__init__.py +0 -0
  184. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/logging/loggin_instance.py +0 -0
  185. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/measure/__init__.py +0 -0
  186. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/measure/measure.py +0 -0
  187. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/multi_language/__init__.py +0 -0
  188. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/multi_language/france.py +0 -0
  189. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/multi_language/germany.py +0 -0
  190. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/multi_language/italy.py +0 -0
  191. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/multi_language/language_wrapper.py +0 -0
  192. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/multi_language/russian.py +0 -0
  193. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/multi_language/simplified_chinese.py +0 -0
  194. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/now_playing/__init__.py +0 -0
  195. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/now_playing/now_playing.py +0 -0
  196. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/pet_chat/__init__.py +0 -0
  197. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/pet_chat/pet_chat_service.py +0 -0
  198. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/platform_info/__init__.py +0 -0
  199. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/platform_info/platform_info.py +0 -0
  200. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/playlist/__init__.py +0 -0
  201. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/playlist/playlist.py +0 -0
  202. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/plugins/__init__.py +0 -0
  203. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/plugins/plugin_loader.py +0 -0
  204. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/power_mode/__init__.py +0 -0
  205. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/power_mode/power_mode.py +0 -0
  206. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/preset_schedule/__init__.py +0 -0
  207. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/preset_schedule/preset_schedule_service.py +0 -0
  208. {frontengine-1.0.38/frontengine/utils/redirect_manager → frontengine-1.0.39/frontengine/utils/recording}/__init__.py +0 -0
  209. {frontengine-1.0.38/frontengine/utils/reminder → frontengine-1.0.39/frontengine/utils/redirect_manager}/__init__.py +0 -0
  210. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/redirect_manager/redirect_manager_class.py +0 -0
  211. {frontengine-1.0.38/frontengine/utils/smart_pause → frontengine-1.0.39/frontengine/utils/reminder}/__init__.py +0 -0
  212. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/reminder/reminder_service.py +0 -0
  213. {frontengine-1.0.38/frontengine/utils/system_stats → frontengine-1.0.39/frontengine/utils/smart_pause}/__init__.py +0 -0
  214. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/smart_pause/pause_rules.py +0 -0
  215. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/smart_pause/smart_pause_service.py +0 -0
  216. {frontengine-1.0.38/frontengine/utils/text_source → frontengine-1.0.39/frontengine/utils/system_stats}/__init__.py +0 -0
  217. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/system_stats/system_stats.py +0 -0
  218. {frontengine-1.0.38/frontengine/utils/theme_schedule → frontengine-1.0.39/frontengine/utils/text_source}/__init__.py +0 -0
  219. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/text_source/text_source.py +0 -0
  220. {frontengine-1.0.38/frontengine/utils/usage_tracking → frontengine-1.0.39/frontengine/utils/theme_schedule}/__init__.py +0 -0
  221. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/theme_schedule/theme_schedule_service.py +0 -0
  222. {frontengine-1.0.38/frontengine/utils/weather → frontengine-1.0.39/frontengine/utils/usage_tracking}/__init__.py +0 -0
  223. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/usage_tracking/usage_service.py +0 -0
  224. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/usage_tracking/usage_tracker.py +0 -0
  225. {frontengine-1.0.38/frontengine/utils/window_pin → frontengine-1.0.39/frontengine/utils/weather}/__init__.py +0 -0
  226. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/weather/weather_service.py +0 -0
  227. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/web_url.py +0 -0
  228. {frontengine-1.0.38/frontengine/utils/workshop → frontengine-1.0.39/frontengine/utils/window_pin}/__init__.py +0 -0
  229. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/window_pin/window_layout.py +0 -0
  230. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/window_pin/window_pin.py +0 -0
  231. {frontengine-1.0.38/frontengine/worker → frontengine-1.0.39/frontengine/utils/workshop}/__init__.py +0 -0
  232. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/utils/workshop/workshop_content.py +0 -0
  233. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine/worker/qthread_worker.py +0 -0
  234. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine.egg-info/dependency_links.txt +0 -0
  235. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine.egg-info/entry_points.txt +0 -0
  236. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine.egg-info/requires.txt +0 -0
  237. {frontengine-1.0.38 → frontengine-1.0.39}/frontengine.egg-info/top_level.txt +0 -0
  238. {frontengine-1.0.38 → frontengine-1.0.39}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frontengine
3
- Version: 1.0.38
3
+ Version: 1.0.39
4
4
  Summary: FrontEngine is use-define frontview or only use like screen saver
5
5
  Author-email: JE-Chen <jechenmailman@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/Intergration-Automation-Testing/FrontEngine
@@ -143,6 +143,17 @@ Measuring and capture tools on the **Tools** tab:
143
143
  `hsl(...)` or a CSS custom property.
144
144
  - **Region capture** — drag out an area; it lands on the clipboard and can be
145
145
  saved to a file.
146
+ - **Read text** — drag out an area to copy the text in it, translate it, or ask
147
+ a question about it.
148
+ > This is the **only** feature here that sends screen content off the
149
+ > machine: the selection goes to Anthropic's API using your own
150
+ > `ANTHROPIC_API_KEY`. It asks once before the first send, remembers the
151
+ > answer, and the consent can be withdrawn from the result window at any
152
+ > time. Without consent or without a key, nothing is sent.
153
+ - **Record area** — drag out an area and record it to an animated GIF, with the
154
+ camera composited into the corner if you want the reaction-video look.
155
+ Recording is capped by both length and frame count, because every frame is
156
+ held in memory.
146
157
  - **Pin a window** — keep another program's window on top, or fade it, while
147
158
  you work against it. Only stacking and opacity are touched, never window
148
159
  content. Windows only.
@@ -119,6 +119,17 @@ Measuring and capture tools on the **Tools** tab:
119
119
  `hsl(...)` or a CSS custom property.
120
120
  - **Region capture** — drag out an area; it lands on the clipboard and can be
121
121
  saved to a file.
122
+ - **Read text** — drag out an area to copy the text in it, translate it, or ask
123
+ a question about it.
124
+ > This is the **only** feature here that sends screen content off the
125
+ > machine: the selection goes to Anthropic's API using your own
126
+ > `ANTHROPIC_API_KEY`. It asks once before the first send, remembers the
127
+ > answer, and the consent can be withdrawn from the result window at any
128
+ > time. Without consent or without a key, nothing is sent.
129
+ - **Record area** — drag out an area and record it to an animated GIF, with the
130
+ camera composited into the corner if you want the reaction-video look.
131
+ Recording is capped by both length and frame count, because every frame is
132
+ held in memory.
122
133
  - **Pin a window** — keep another program's window on top, or fade it, while
123
134
  you work against it. Only stacking and opacity are touched, never window
124
135
  content. Windows only.
@@ -0,0 +1,120 @@
1
+ """
2
+ 畫面文字視窗:顯示 Claude 從截圖讀出來的結果,並提供複製。
3
+
4
+ 第一次使用會先問一次「要把截圖送到 Anthropic 的 API 嗎」,同意後才會送,
5
+ 而且這個選擇記在設定裡,隨時可以在這裡收回。
6
+
7
+ The screen-text window: show what Claude read from the capture, and offer to
8
+ copy it.
9
+
10
+ The first use asks whether the screenshot may be sent to Anthropic's API.
11
+ Nothing is sent before that is answered, the choice is remembered, and it can
12
+ be withdrawn from right here.
13
+ """
14
+ from __future__ import annotations
15
+
16
+ from typing import Optional
17
+
18
+ from PySide6.QtGui import QGuiApplication
19
+ from PySide6.QtWidgets import (
20
+ QCheckBox, QDialog, QDialogButtonBox, QGridLayout, QLabel, QMessageBox, QPlainTextEdit,
21
+ QPushButton, QWidget,
22
+ )
23
+
24
+ from frontengine.user_setting.user_setting_file import user_setting_dict, write_user_setting
25
+ from frontengine.utils.logging.loggin_instance import front_engine_logger
26
+ from frontengine.utils.multi_language.language_wrapper import language_wrapper
27
+ from frontengine.utils.screen_text.screen_text_service import API_KEY_ENV, api_key
28
+
29
+ CONSENT_KEY = "screen_text_consent"
30
+
31
+
32
+ def _t(key: str, fallback: str) -> str:
33
+ return language_wrapper.language_word_dict.get(key, fallback)
34
+
35
+
36
+ def has_consent() -> bool:
37
+ """使用者是否已經同意把截圖送出去。"""
38
+ return bool(user_setting_dict.get(CONSENT_KEY))
39
+
40
+
41
+ def set_consent(granted: bool) -> None:
42
+ """記住(或收回)同意。"""
43
+ user_setting_dict[CONSENT_KEY] = bool(granted)
44
+ write_user_setting()
45
+ front_engine_logger.info(f"[ScreenText] consent = {bool(granted)}")
46
+
47
+
48
+ def ask_for_consent(parent: Optional[QWidget] = None) -> bool:
49
+ """
50
+ 問使用者要不要把截圖送到 Anthropic。已經同意過就直接回 True,不再打擾。
51
+ Ask whether screenshots may go to Anthropic. Already-granted consent returns
52
+ True without asking again.
53
+ """
54
+ if has_consent():
55
+ return True
56
+ answer = QMessageBox.question(
57
+ parent,
58
+ _t("screen_text_consent_title", "Send this screenshot?"),
59
+ _t("screen_text_consent_body",
60
+ "Reading text needs the captured area sent to Anthropic's API. It leaves this "
61
+ "machine, and anything visible in the selection goes with it. Your own "
62
+ "ANTHROPIC_API_KEY is used. Send captures?"),
63
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
64
+ QMessageBox.StandardButton.No)
65
+ granted = answer == QMessageBox.StandardButton.Yes
66
+ set_consent(granted)
67
+ return granted
68
+
69
+
70
+ class ScreenTextDialog(QDialog):
71
+ """顯示讀到的文字,可複製,也可以在這裡收回同意。"""
72
+
73
+ def __init__(self, text: str = "", parent: Optional[QWidget] = None) -> None:
74
+ front_engine_logger.info("[ScreenTextDialog] Init")
75
+ super().__init__(parent)
76
+ self.setWindowTitle(_t("screen_text_title", "Text on screen"))
77
+
78
+ self.text_edit = QPlainTextEdit(str(text))
79
+ self.text_edit.setReadOnly(False)
80
+ self.copy_button = QPushButton(_t("screen_text_copy", "Copy"))
81
+ self.copy_button.clicked.connect(self.copy_text)
82
+ self.consent_checkbox = QCheckBox(
83
+ _t("screen_text_consent_toggle", "Allow sending captures to Anthropic"))
84
+ self.consent_checkbox.setChecked(has_consent())
85
+ self.consent_checkbox.toggled.connect(set_consent)
86
+ self.hint_label = QLabel(self.status_text())
87
+ self.hint_label.setWordWrap(True)
88
+
89
+ self.button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Close)
90
+ self.button_box.rejected.connect(self.reject)
91
+
92
+ layout = QGridLayout(self)
93
+ layout.addWidget(self.text_edit, 0, 0, 1, 2)
94
+ layout.addWidget(self.copy_button, 1, 0)
95
+ layout.addWidget(self.consent_checkbox, 1, 1)
96
+ layout.addWidget(self.hint_label, 2, 0, 1, 2)
97
+ layout.addWidget(self.button_box, 3, 0, 1, 2)
98
+
99
+ @staticmethod
100
+ def status_text() -> str:
101
+ """說明目前為什麼可用或不可用。"""
102
+ if api_key() is None:
103
+ return _t("screen_text_no_key",
104
+ "Set {env} in your environment to use this.").format(env=API_KEY_ENV)
105
+ return _t("screen_text_ready",
106
+ "Captures are sent to Anthropic only while the box above is ticked.")
107
+
108
+ def set_text(self, text: str) -> None:
109
+ self.text_edit.setPlainText(str(text or ""))
110
+
111
+ def text(self) -> str:
112
+ return self.text_edit.toPlainText()
113
+
114
+ def copy_text(self) -> bool:
115
+ """把結果放到剪貼簿。"""
116
+ clipboard = QGuiApplication.clipboard()
117
+ if clipboard is None: # pragma: no cover - no clipboard at all
118
+ return False
119
+ clipboard.setText(self.text())
120
+ return True
@@ -8,18 +8,23 @@ arranging - as opposed to the other pages, which are for showing.
8
8
  """
9
9
  from typing import List, Optional
10
10
 
11
+ from PySide6.QtCore import QBuffer, QIODevice, QTimer
11
12
  from PySide6.QtGui import QGuiApplication
12
13
  from PySide6.QtWidgets import (
13
- QCheckBox, QComboBox, QGridLayout, QLabel, QLineEdit, QPushButton, QSpinBox, QWidget,
14
+ QCheckBox, QComboBox, QFileDialog, QGridLayout, QLabel, QLineEdit, QPushButton, QSpinBox,
15
+ QWidget,
14
16
  )
15
17
 
16
18
  from frontengine.show.camera.camera_widget import (
17
19
  SHAPE_CIRCLE, SHAPE_RECTANGLE, SHAPE_ROUNDED, CameraWidget, list_cameras,
18
20
  )
19
- from frontengine.show.capture.region_capture import RegionCaptureWidget
21
+ from frontengine.show.capture.region_capture import RegionCaptureWidget, is_usable
20
22
  from frontengine.show.measure.measure_widget import (
21
23
  MODE_ANGLE, MODE_COLOR, MODE_RULER, MeasureWidget,
22
24
  )
25
+ from frontengine.ui.dialog.screen_text_dialog import (
26
+ ScreenTextDialog, ask_for_consent, has_consent,
27
+ )
23
28
  from frontengine.ui.dialog.window_pin_dialog import WindowPinDialog
24
29
  from frontengine.user_setting.user_setting_file import user_setting_dict, write_user_setting
25
30
  from frontengine.ui.page.utils import coerce_int
@@ -28,6 +33,12 @@ from frontengine.utils.measure.measure import (
28
33
  FORMAT_CSS_VAR, FORMAT_HEX, FORMAT_HSL, FORMAT_RGB,
29
34
  )
30
35
  from frontengine.utils.multi_language.language_wrapper import language_wrapper
36
+ from frontengine.utils.screen_text.screen_text_service import (
37
+ ACTION_ASK, ACTION_EXTRACT, ACTION_TRANSLATE, DEFAULT_LANGUAGE, ScreenTextService,
38
+ )
39
+ from frontengine.utils.recording.frame_recorder import (
40
+ DEFAULT_FPS, DEFAULT_MAX_SECONDS, MAX_FPS, MIN_FPS, FrameRecorder,
41
+ )
31
42
  from frontengine.utils.window_pin import window_pin
32
43
  from frontengine.utils.window_pin.window_layout import capture_layout, restore_layout
33
44
 
@@ -46,6 +57,10 @@ class ToolsSettingUI(QWidget):
46
57
  self.grid_layout.setContentsMargins(0, 0, 0, 0)
47
58
 
48
59
  self.measure_widget_list: List[MeasureWidget] = []
60
+ self.recorder = FrameRecorder(self)
61
+ self.screen_text_service = ScreenTextService(consent_provider=has_consent)
62
+ self.last_screen_text: Optional[str] = None
63
+ self.last_recording: Optional[str] = None
49
64
  self.capture_widget_list: List[RegionCaptureWidget] = []
50
65
  self.camera_widget_list: List[CameraWidget] = []
51
66
  self.last_capture: Optional[RegionCaptureWidget] = None
@@ -53,6 +68,8 @@ class ToolsSettingUI(QWidget):
53
68
 
54
69
  self._build_measure_row()
55
70
  self._build_capture_row()
71
+ self._build_screen_text_row()
72
+ self._build_record_row()
56
73
  self._build_camera_row()
57
74
  self.pin_button = QPushButton(_t("tools_pin_window", "Pin a window..."))
58
75
  self.pin_button.clicked.connect(self.open_pin_dialog)
@@ -84,20 +101,29 @@ class ToolsSettingUI(QWidget):
84
101
  self.grid_layout.addWidget(self.capture_label, 2, 0)
85
102
  self.grid_layout.addWidget(self.capture_button, 2, 1)
86
103
  self.grid_layout.addWidget(self.capture_copy_button, 2, 2)
87
- self.grid_layout.addWidget(self.camera_label, 3, 0)
88
- self.grid_layout.addWidget(self.camera_shape_combobox, 3, 1)
89
- self.grid_layout.addWidget(self.camera_button, 3, 2)
90
- self.grid_layout.addWidget(self.camera_device_combobox, 4, 0, 1, 2)
91
- self.grid_layout.addWidget(self.camera_mirror_checkbox, 4, 2)
92
- self.grid_layout.addWidget(self.camera_border_label, 5, 0)
93
- self.grid_layout.addWidget(self.camera_border_spinbox, 5, 1)
94
- self.grid_layout.addWidget(self.pin_button, 6, 0)
95
- self.grid_layout.addWidget(self.layout_label, 7, 0)
96
- self.grid_layout.addWidget(self.layout_name_edit, 7, 1)
97
- self.grid_layout.addWidget(self.layout_save_button, 7, 2)
98
- self.grid_layout.addWidget(self.layout_combobox, 8, 0, 1, 2)
99
- self.grid_layout.addWidget(self.layout_restore_button, 8, 2)
100
- self.grid_layout.addWidget(self.hint_label, 9, 0, 1, 3)
104
+ self.grid_layout.addWidget(self.screen_text_label, 3, 0)
105
+ self.grid_layout.addWidget(self.screen_text_combobox, 3, 1)
106
+ self.grid_layout.addWidget(self.screen_text_button, 3, 2)
107
+ self.grid_layout.addWidget(self.screen_text_input, 4, 1, 1, 2)
108
+ self.grid_layout.addWidget(self.record_label, 5, 0)
109
+ self.grid_layout.addWidget(self.record_fps_spinbox, 5, 1)
110
+ self.grid_layout.addWidget(self.record_button, 5, 2)
111
+ self.grid_layout.addWidget(self.record_seconds_spinbox, 6, 1)
112
+ self.grid_layout.addWidget(self.record_camera_checkbox, 6, 2)
113
+ self.grid_layout.addWidget(self.camera_label, 7, 0)
114
+ self.grid_layout.addWidget(self.camera_shape_combobox, 7, 1)
115
+ self.grid_layout.addWidget(self.camera_button, 7, 2)
116
+ self.grid_layout.addWidget(self.camera_device_combobox, 8, 0, 1, 2)
117
+ self.grid_layout.addWidget(self.camera_mirror_checkbox, 8, 2)
118
+ self.grid_layout.addWidget(self.camera_border_label, 9, 0)
119
+ self.grid_layout.addWidget(self.camera_border_spinbox, 9, 1)
120
+ self.grid_layout.addWidget(self.pin_button, 10, 0)
121
+ self.grid_layout.addWidget(self.layout_label, 11, 0)
122
+ self.grid_layout.addWidget(self.layout_name_edit, 11, 1)
123
+ self.grid_layout.addWidget(self.layout_save_button, 11, 2)
124
+ self.grid_layout.addWidget(self.layout_combobox, 12, 0, 1, 2)
125
+ self.grid_layout.addWidget(self.layout_restore_button, 12, 2)
126
+ self.grid_layout.addWidget(self.hint_label, 13, 0, 1, 3)
101
127
 
102
128
  # --- construction helpers -------------------------------------------
103
129
  def _build_measure_row(self) -> None:
@@ -124,6 +150,32 @@ class ToolsSettingUI(QWidget):
124
150
  self.capture_copy_button = QPushButton(_t("tools_capture_copy", "Copy last"))
125
151
  self.capture_copy_button.clicked.connect(self.copy_last_capture)
126
152
 
153
+ def _build_screen_text_row(self) -> None:
154
+ self.screen_text_label = QLabel(_t("tools_screen_text_label", "Read text"))
155
+ self.screen_text_combobox = QComboBox()
156
+ for action, key, fallback in ((ACTION_EXTRACT, "tools_screen_text_extract", "Copy text"),
157
+ (ACTION_TRANSLATE, "tools_screen_text_translate", "Translate"),
158
+ (ACTION_ASK, "tools_screen_text_ask", "Ask about it")):
159
+ self.screen_text_combobox.addItem(_t(key, fallback), action)
160
+ self.screen_text_input = QLineEdit()
161
+ self.screen_text_input.setPlaceholderText(
162
+ _t("tools_screen_text_input", "Language, or your question"))
163
+ self.screen_text_input.setText(DEFAULT_LANGUAGE)
164
+ self.screen_text_button = QPushButton(_t("tools_screen_text_start", "Read an area"))
165
+ self.screen_text_button.clicked.connect(self.start_screen_text)
166
+
167
+ def _build_record_row(self) -> None:
168
+ self.record_label = QLabel(_t("tools_record_label", "Record area"))
169
+ self.record_fps_spinbox = QSpinBox()
170
+ self.record_fps_spinbox.setRange(MIN_FPS, MAX_FPS)
171
+ self.record_fps_spinbox.setValue(DEFAULT_FPS)
172
+ self.record_seconds_spinbox = QSpinBox()
173
+ self.record_seconds_spinbox.setRange(1, 120)
174
+ self.record_seconds_spinbox.setValue(DEFAULT_MAX_SECONDS)
175
+ self.record_camera_checkbox = QCheckBox(_t("tools_record_camera", "Include camera"))
176
+ self.record_button = QPushButton(_t("tools_record_start", "Record area"))
177
+ self.record_button.clicked.connect(self.toggle_recording)
178
+
127
179
  def _build_camera_row(self) -> None:
128
180
  self.camera_label = QLabel(_t("tools_camera_label", "Camera"))
129
181
  self.camera_shape_combobox = QComboBox()
@@ -275,6 +327,104 @@ class ToolsSettingUI(QWidget):
275
327
  except RuntimeError:
276
328
  self.pin_dialog = None
277
329
 
330
+ # --- reading text on screen ------------------------------------------
331
+ def start_screen_text(self):
332
+ """
333
+ 框一塊畫面來讀文字。第一次會先問過同意,沒同意就什麼都不做——
334
+ 這是唯一會把畫面內容送出機器的功能。
335
+ Pick an area to read. The first use asks for consent and does nothing
336
+ without it: this is the only feature that sends screen content off the
337
+ machine.
338
+ """
339
+ if not ask_for_consent(self):
340
+ front_engine_logger.info("[ToolsSettingUI] screen text cancelled: no consent")
341
+ return None
342
+ picker = RegionCaptureWidget(on_captured=lambda pixmap, rect: self._read_capture(pixmap))
343
+ screen = QGuiApplication.primaryScreen()
344
+ if screen is not None:
345
+ picker.setGeometry(screen.geometry())
346
+ picker.setMouseTracking(True)
347
+ picker.show()
348
+ self.capture_widget_list.append(picker)
349
+ return picker
350
+
351
+ def _read_capture(self, pixmap) -> None:
352
+ """把框到的畫面送出去讀(在背景執行緒,UI 不會卡住)。"""
353
+ data = pixmap_to_png(pixmap)
354
+ if not data:
355
+ return
356
+ self.screen_text_service.read_async(
357
+ data, self.show_screen_text,
358
+ action=self.screen_text_combobox.currentData(),
359
+ language=self.screen_text_input.text(),
360
+ question=self.screen_text_input.text())
361
+
362
+ def show_screen_text(self, text) -> None:
363
+ """收到結果:切回 UI 執行緒再開視窗(回呼來自背景執行緒)。"""
364
+ self.last_screen_text = text
365
+ QTimer.singleShot(0, lambda: self._present_screen_text(text))
366
+
367
+ def _present_screen_text(self, text):
368
+ dialog = ScreenTextDialog(
369
+ text or _t("tools_screen_text_empty", "Nothing came back."), self)
370
+ dialog.exec()
371
+ return dialog
372
+
373
+ # --- recording -------------------------------------------------------
374
+ def toggle_recording(self) -> None:
375
+ if self.recorder.running:
376
+ self.finish_recording()
377
+ else:
378
+ self.start_recording()
379
+
380
+ def start_recording(self) -> RegionCaptureWidget:
381
+ """先框一塊範圍,放開滑鼠後就從那塊開始錄。"""
382
+ front_engine_logger.info("[ToolsSettingUI] start_recording")
383
+ picker = RegionCaptureWidget()
384
+ picker.finish = _recording_region_picker(self, picker)
385
+ screen = QGuiApplication.primaryScreen()
386
+ if screen is not None:
387
+ picker.setGeometry(screen.geometry())
388
+ picker.setMouseTracking(True)
389
+ picker.show()
390
+ self.capture_widget_list.append(picker)
391
+ return picker
392
+
393
+ def begin_recording(self, region) -> bool:
394
+ """對指定範圍開始錄製。"""
395
+ inset = self.camera_inset if self.record_camera_checkbox.isChecked() else None
396
+ self.recorder.set_inset_provider(inset)
397
+ started = self.recorder.start(region, self.record_fps_spinbox.value(),
398
+ self.record_seconds_spinbox.value())
399
+ if started:
400
+ self.record_button.setText(_t("tools_record_stop", "Stop recording"))
401
+ return started
402
+
403
+ def camera_inset(self) -> Optional[object]:
404
+ """錄影時要疊上去的攝影機畫面(沒開攝影機就沒有)。"""
405
+ for widget in list(self.camera_widget_list):
406
+ try:
407
+ if widget.frame is not None:
408
+ return widget.frame
409
+ except RuntimeError:
410
+ self.camera_widget_list.remove(widget)
411
+ return None
412
+
413
+ def finish_recording(self) -> Optional[str]:
414
+ """停止錄製並存成 GIF,回傳檔案路徑。"""
415
+ self.recorder.stop()
416
+ self.record_button.setText(_t("tools_record_start", "Record area"))
417
+ if not self.recorder.frames:
418
+ return None
419
+ target = QFileDialog.getSaveFileName(
420
+ self, _t("tools_record_save", "Save recording"), "recording.gif", "GIF (*.gif)")[0]
421
+ if not target:
422
+ self.recorder.clear()
423
+ return None
424
+ self.last_recording = self.recorder.save_gif(target)
425
+ self.recorder.clear()
426
+ return self.last_recording
427
+
278
428
  # --- window layouts --------------------------------------------------
279
429
  def saved_layouts(self) -> dict:
280
430
  """設定裡目前存了哪些版面。"""
@@ -336,3 +486,43 @@ class ToolsSettingUI(QWidget):
336
486
  self.camera_border_spinbox.setValue(max(0, min(20, border)))
337
487
  if "camera_mirror" in state:
338
488
  self.camera_mirror_checkbox.setChecked(bool(state["camera_mirror"]))
489
+
490
+
491
+ def _recording_region_picker(page: "ToolsSettingUI", picker: RegionCaptureWidget):
492
+ """
493
+ 把框選層的 finish() 換成「不擷取畫面、只回報範圍給錄影」。錄影要的是
494
+ 連續的畫面,不是放開當下那一張。
495
+ Replace the picker's finish() so it reports the region to the recorder
496
+ instead of grabbing one still: a recording wants the frames that follow.
497
+ """
498
+ original_close = picker.close
499
+
500
+ def finish(point):
501
+ picker.extend(point)
502
+ region = picker.selection()
503
+ picker.origin = picker.current = None
504
+ if picker in page.capture_widget_list:
505
+ page.capture_widget_list.remove(picker)
506
+ original_close()
507
+ if region is not None and is_usable(region):
508
+ page.begin_recording(region)
509
+ return None
510
+
511
+ return finish
512
+
513
+
514
+ def pixmap_to_png(pixmap) -> bytes:
515
+ """
516
+ 把擷取到的畫面轉成 PNG 位元組(送出去用)。轉不出來就回傳空的,
517
+ 呼叫端會因此什麼都不送。
518
+ The capture as PNG bytes, ready to send. An empty result means nothing is
519
+ sent at all.
520
+ """
521
+ if pixmap is None or pixmap.isNull():
522
+ return b""
523
+ buffer = QBuffer()
524
+ buffer.open(QIODevice.OpenModeFlag.WriteOnly)
525
+ saved = pixmap.save(buffer, "PNG")
526
+ data = bytes(buffer.data()) if saved else b""
527
+ buffer.close()
528
+ return data
@@ -66,6 +66,9 @@ user_setting_dict: Dict[str, Any] = {
66
66
  # 已儲存的視窗版面:名稱 -> [{title, x, y, width, height}]
67
67
  # Saved window layouts: name -> [{title, x, y, width, height}]
68
68
  "window_layouts": {},
69
+ # 是否同意把擷取畫面送到 Anthropic 讀取文字(預設不同意)
70
+ # Consent to send captures to Anthropic for reading text (off by default).
71
+ "screen_text_consent": False,
69
72
  # 覆蓋層畫質檔位:high / balanced / saver
70
73
  # Overlay quality tier: high / balanced / saver.
71
74
  "quality_tier": "high",
@@ -499,4 +499,23 @@ english_word_dict = {
499
499
  "tools_layout_name": 'Layout name',
500
500
  "tools_layout_save": 'Save layout',
501
501
  "tools_layout_restore": 'Restore',
502
+ "tools_record_label": 'Record area',
503
+ "tools_record_start": 'Record area',
504
+ "tools_record_stop": 'Stop recording',
505
+ "tools_record_camera": 'Include camera',
506
+ "tools_record_save": 'Save recording',
507
+ "tools_screen_text_label": 'Read text',
508
+ "tools_screen_text_extract": 'Copy text',
509
+ "tools_screen_text_translate": 'Translate',
510
+ "tools_screen_text_ask": 'Ask about it',
511
+ "tools_screen_text_input": 'Language, or your question',
512
+ "tools_screen_text_start": 'Read an area',
513
+ "tools_screen_text_empty": 'Nothing came back.',
514
+ "screen_text_title": 'Text on screen',
515
+ "screen_text_copy": 'Copy',
516
+ "screen_text_consent_toggle": 'Allow sending captures to Anthropic',
517
+ "screen_text_consent_title": 'Send this screenshot?',
518
+ "screen_text_consent_body": "Reading text needs the captured area sent to Anthropic's API. It leaves this machine, and anything visible in the selection goes with it. Your own ANTHROPIC_API_KEY is used. Send captures?",
519
+ "screen_text_no_key": 'Set {env} in your environment to use this.',
520
+ "screen_text_ready": 'Captures are sent to Anthropic only while the box above is ticked.',
502
521
  }
@@ -498,4 +498,23 @@ traditional_chinese_word_dict = {
498
498
  "tools_layout_name": '版面名稱',
499
499
  "tools_layout_save": '儲存版面',
500
500
  "tools_layout_restore": '還原',
501
+ "tools_record_label": '區域錄影',
502
+ "tools_record_start": '框選錄影',
503
+ "tools_record_stop": '停止錄影',
504
+ "tools_record_camera": '疊上攝影機',
505
+ "tools_record_save": '儲存錄影',
506
+ "tools_screen_text_label": '讀取文字',
507
+ "tools_screen_text_extract": '複製文字',
508
+ "tools_screen_text_translate": '翻譯',
509
+ "tools_screen_text_ask": '問它問題',
510
+ "tools_screen_text_input": '語言,或你的問題',
511
+ "tools_screen_text_start": '框選讀取',
512
+ "tools_screen_text_empty": '沒有得到結果。',
513
+ "screen_text_title": '畫面上的文字',
514
+ "screen_text_copy": '複製',
515
+ "screen_text_consent_toggle": '允許把擷取畫面送到 Anthropic',
516
+ "screen_text_consent_title": '要送出這張截圖嗎?',
517
+ "screen_text_consent_body": '讀取文字需要把框選到的畫面送到 Anthropic 的 API。這份畫面會離開這台電腦,框選範圍內看得到的東西都會一起送出,使用的是你自己的 ANTHROPIC_API_KEY。要送出嗎?',
518
+ "screen_text_no_key": '請先在環境變數設定 {env} 才能使用。',
519
+ "screen_text_ready": '只有在上面那個選項打勾時,擷取畫面才會送到 Anthropic。',
501
520
  }