frontengine 1.0.37__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.37 → frontengine-1.0.39}/PKG-INFO +37 -1
  2. {frontengine-1.0.37 → frontengine-1.0.39}/README.md +36 -0
  3. frontengine-1.0.39/frontengine/show/screen_care/color_vision_widget.py +147 -0
  4. frontengine-1.0.39/frontengine/ui/dialog/clipboard_dialog.py +147 -0
  5. frontengine-1.0.39/frontengine/ui/dialog/screen_text_dialog.py +120 -0
  6. frontengine-1.0.39/frontengine/ui/dialog/usage_report_dialog.py +120 -0
  7. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/main_ui.py +58 -0
  8. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/menu/settings_menu.py +35 -0
  9. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/screen_care/screen_care_setting_ui.py +78 -5
  10. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/tools/tools_setting_ui.py +260 -11
  11. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/user_setting/user_setting_file.py +15 -0
  12. frontengine-1.0.39/frontengine/utils/clipboard/clipboard_history.py +179 -0
  13. frontengine-1.0.39/frontengine/utils/clipboard/clipboard_watcher.py +75 -0
  14. frontengine-1.0.39/frontengine/utils/color_vision/color_vision.py +113 -0
  15. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/multi_language/english.py +50 -0
  16. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/multi_language/traditional_chinese.py +50 -0
  17. frontengine-1.0.39/frontengine/utils/recording/frame_recorder.py +199 -0
  18. frontengine-1.0.39/frontengine/utils/recording/gif_writer.py +203 -0
  19. frontengine-1.0.39/frontengine/utils/usage_tracking/usage_service.py +85 -0
  20. frontengine-1.0.39/frontengine/utils/usage_tracking/usage_tracker.py +218 -0
  21. frontengine-1.0.39/frontengine/utils/weather/__init__.py +0 -0
  22. frontengine-1.0.39/frontengine/utils/window_pin/__init__.py +0 -0
  23. frontengine-1.0.39/frontengine/utils/window_pin/window_layout.py +135 -0
  24. frontengine-1.0.39/frontengine/utils/workshop/__init__.py +0 -0
  25. frontengine-1.0.39/frontengine/worker/__init__.py +0 -0
  26. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine.egg-info/PKG-INFO +37 -1
  27. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine.egg-info/SOURCES.txt +16 -0
  28. {frontengine-1.0.37 → frontengine-1.0.39}/pyproject.toml +1 -1
  29. {frontengine-1.0.37 → frontengine-1.0.39}/LICENSE +0 -0
  30. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/__init__.py +0 -0
  31. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/__main__.py +0 -0
  32. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/__init__.py +0 -0
  33. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/base_widget.py +0 -0
  34. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/camera/__init__.py +0 -0
  35. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/camera/camera_widget.py +0 -0
  36. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/capture/__init__.py +0 -0
  37. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/capture/region_capture.py +0 -0
  38. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/focus_shield/__init__.py +0 -0
  39. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/focus_shield/focus_shield_widget.py +0 -0
  40. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/gif/__init__.py +0 -0
  41. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/gif/paint_gif.py +0 -0
  42. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/image/__init__.py +0 -0
  43. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/image/paint_image.py +0 -0
  44. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/load/__init__.py +0 -0
  45. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/load/load_someone_make_ui.py +0 -0
  46. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/measure/__init__.py +0 -0
  47. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/measure/measure_widget.py +0 -0
  48. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/monitor/__init__.py +0 -0
  49. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/monitor/monitor_widget.py +0 -0
  50. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/notes/__init__.py +0 -0
  51. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/notes/sticky_note_widget.py +0 -0
  52. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/overlay_factory.py +0 -0
  53. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/particle/__init__.py +0 -0
  54. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/particle/particle_ui.py +0 -0
  55. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/pet/__init__.py +0 -0
  56. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/pet/desktop_pet.py +0 -0
  57. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/presentation/__init__.py +0 -0
  58. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/presentation/annotation_overlay.py +0 -0
  59. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/presentation/cursor_effects.py +0 -0
  60. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/presentation/keystroke_display.py +0 -0
  61. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/presentation/magnifier.py +0 -0
  62. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/scene/__init__.py +0 -0
  63. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/scene/extend_graphic_scene.py +0 -0
  64. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/scene/extend_graphic_view.py +0 -0
  65. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/scene/scene.py +0 -0
  66. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/screen_care/__init__.py +0 -0
  67. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/screen_care/screen_filter.py +0 -0
  68. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/sound_player/__init__.py +0 -0
  69. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/sound_player/sound_effect.py +0 -0
  70. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/sound_player/sound_player.py +0 -0
  71. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/spectrum/__init__.py +0 -0
  72. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/spectrum/spectrum_widget.py +0 -0
  73. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/text/__init__.py +0 -0
  74. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/text/draw_text.py +0 -0
  75. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/toast/__init__.py +0 -0
  76. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/toast/toast_widget.py +0 -0
  77. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/video/__init__.py +0 -0
  78. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/video/video_player.py +0 -0
  79. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/wallpaper/__init__.py +0 -0
  80. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/wallpaper/wallpaper_widget.py +0 -0
  81. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/web/__init__.py +0 -0
  82. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/web/webview.py +0 -0
  83. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/window_helpers.py +0 -0
  84. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/system_tray/__init__.py +0 -0
  85. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/system_tray/extend_system_tray.py +0 -0
  86. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/__init__.py +0 -0
  87. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/color/__init__.py +0 -0
  88. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/color/global_color.py +0 -0
  89. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/dialog/__init__.py +0 -0
  90. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/dialog/app_profile_dialog.py +0 -0
  91. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/dialog/choose_file_dialog.py +0 -0
  92. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/dialog/hotkey_settings_dialog.py +0 -0
  93. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/dialog/reminder_dialog.py +0 -0
  94. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/dialog/save_file_dialog.py +0 -0
  95. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/dialog/smart_pause_dialog.py +0 -0
  96. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/dialog/window_pin_dialog.py +0 -0
  97. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/menu/__init__.py +0 -0
  98. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/menu/help_menu.py +0 -0
  99. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/menu/how_to_menu.py +0 -0
  100. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/menu/language_menu.py +0 -0
  101. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/menu/preset_menu.py +0 -0
  102. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/__init__.py +0 -0
  103. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/control_center/__init__.py +0 -0
  104. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/control_center/control_center_ui.py +0 -0
  105. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/focus/__init__.py +0 -0
  106. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/focus/focus_setting_ui.py +0 -0
  107. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/gif/__init__.py +0 -0
  108. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/gif/gif_setting_ui.py +0 -0
  109. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/image/__init__.py +0 -0
  110. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/image/image_setting_ui.py +0 -0
  111. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/particle/__init__.py +0 -0
  112. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/particle/particle_setting_ui.py +0 -0
  113. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/pet/__init__.py +0 -0
  114. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/pet/pet_setting_ui.py +0 -0
  115. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/presentation/__init__.py +0 -0
  116. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/presentation/presentation_setting_ui.py +0 -0
  117. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/__init__.py +0 -0
  118. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_manager.py +0 -0
  119. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/__init__.py +0 -0
  120. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/base_scene_page.py +0 -0
  121. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/gif.py +0 -0
  122. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/image.py +0 -0
  123. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/registry.py +0 -0
  124. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/sound.py +0 -0
  125. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/text.py +0 -0
  126. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/video.py +0 -0
  127. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/web.py +0 -0
  128. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_setting_ui.py +0 -0
  129. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/screen_care/__init__.py +0 -0
  130. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/sound_player/__init__.py +0 -0
  131. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/sound_player/sound_player_setting_ui.py +0 -0
  132. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/text/__init__.py +0 -0
  133. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/text/text_setting_ui.py +0 -0
  134. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/tools/__init__.py +0 -0
  135. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/utils.py +0 -0
  136. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/video/__init__.py +0 -0
  137. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/video/video_setting_ui.py +0 -0
  138. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/wallpaper/__init__.py +0 -0
  139. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/wallpaper/wallpaper_setting_ui.py +0 -0
  140. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/web/__init__.py +0 -0
  141. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/web/web_setting_ui.py +0 -0
  142. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/widgets/__init__.py +0 -0
  143. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/widgets/widgets_setting_ui.py +0 -0
  144. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/user_setting/__init__.py +0 -0
  145. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/user_setting/preset_repository.py +0 -0
  146. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/user_setting/scene_setting.py +0 -0
  147. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/__init__.py +0 -0
  148. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/app_profile/__init__.py +0 -0
  149. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/app_profile/app_profile_service.py +0 -0
  150. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/audio_meter/__init__.py +0 -0
  151. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/audio_meter/audio_envelope.py +0 -0
  152. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/audio_meter/loopback_capture.py +0 -0
  153. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/audio_meter/screen_audio.py +0 -0
  154. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/audio_meter/spectrum_analyzer.py +0 -0
  155. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/audio_meter/system_audio_meter.py +0 -0
  156. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/autostart/__init__.py +0 -0
  157. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/autostart/autostart_service.py +0 -0
  158. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/break_reminder/__init__.py +0 -0
  159. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/break_reminder/break_reminder.py +0 -0
  160. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/browser/__init__.py +0 -0
  161. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/browser/browser.py +0 -0
  162. {frontengine-1.0.37/frontengine/utils/critical_exit → frontengine-1.0.39/frontengine/utils/clipboard}/__init__.py +0 -0
  163. {frontengine-1.0.37/frontengine/utils/file → frontengine-1.0.39/frontengine/utils/color_vision}/__init__.py +0 -0
  164. {frontengine-1.0.37/frontengine/utils/focus_shield → frontengine-1.0.39/frontengine/utils/critical_exit}/__init__.py +0 -0
  165. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/critical_exit/check_key_is_press.py +0 -0
  166. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/critical_exit/critical_exit.py +0 -0
  167. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/critical_exit/win32_vk.py +0 -0
  168. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/exception/__init__.py +0 -0
  169. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/exception/exception_tags.py +0 -0
  170. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/exception/exceptions.py +0 -0
  171. {frontengine-1.0.37/frontengine/utils/focus_timer → frontengine-1.0.39/frontengine/utils/file}/__init__.py +0 -0
  172. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/file/open/__init__.py +0 -0
  173. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/file/open/open_file.py +0 -0
  174. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/file/save/__init__.py +0 -0
  175. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/file/save/save_file.py +0 -0
  176. {frontengine-1.0.37/frontengine/utils/hotkey → frontengine-1.0.39/frontengine/utils/focus_shield}/__init__.py +0 -0
  177. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/focus_shield/focus_shield.py +0 -0
  178. {frontengine-1.0.37/frontengine/utils/input_watch → frontengine-1.0.39/frontengine/utils/focus_timer}/__init__.py +0 -0
  179. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/focus_timer/focus_timer.py +0 -0
  180. {frontengine-1.0.37/frontengine/utils/json → frontengine-1.0.39/frontengine/utils/hotkey}/__init__.py +0 -0
  181. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/hotkey/hotkey_service.py +0 -0
  182. {frontengine-1.0.37/frontengine/utils/logging → frontengine-1.0.39/frontengine/utils/input_watch}/__init__.py +0 -0
  183. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/input_watch/input_watch_service.py +0 -0
  184. {frontengine-1.0.37/frontengine/utils/measure → frontengine-1.0.39/frontengine/utils/json}/__init__.py +0 -0
  185. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/json/json_file.py +0 -0
  186. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/json/json_repository.py +0 -0
  187. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/json_format/__init__.py +0 -0
  188. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/json_format/json_process.py +0 -0
  189. {frontengine-1.0.37/frontengine/utils/multi_language → frontengine-1.0.39/frontengine/utils/logging}/__init__.py +0 -0
  190. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/logging/loggin_instance.py +0 -0
  191. {frontengine-1.0.37/frontengine/utils/now_playing → frontengine-1.0.39/frontengine/utils/measure}/__init__.py +0 -0
  192. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/measure/measure.py +0 -0
  193. {frontengine-1.0.37/frontengine/utils/pet_chat → frontengine-1.0.39/frontengine/utils/multi_language}/__init__.py +0 -0
  194. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/multi_language/france.py +0 -0
  195. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/multi_language/germany.py +0 -0
  196. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/multi_language/italy.py +0 -0
  197. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/multi_language/language_wrapper.py +0 -0
  198. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/multi_language/russian.py +0 -0
  199. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/multi_language/simplified_chinese.py +0 -0
  200. {frontengine-1.0.37/frontengine/utils/platform_info → frontengine-1.0.39/frontengine/utils/now_playing}/__init__.py +0 -0
  201. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/now_playing/now_playing.py +0 -0
  202. {frontengine-1.0.37/frontengine/utils/playlist → frontengine-1.0.39/frontengine/utils/pet_chat}/__init__.py +0 -0
  203. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/pet_chat/pet_chat_service.py +0 -0
  204. {frontengine-1.0.37/frontengine/utils/plugins → frontengine-1.0.39/frontengine/utils/platform_info}/__init__.py +0 -0
  205. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/platform_info/platform_info.py +0 -0
  206. {frontengine-1.0.37/frontengine/utils/power_mode → frontengine-1.0.39/frontengine/utils/playlist}/__init__.py +0 -0
  207. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/playlist/playlist.py +0 -0
  208. {frontengine-1.0.37/frontengine/utils/preset_schedule → frontengine-1.0.39/frontengine/utils/plugins}/__init__.py +0 -0
  209. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/plugins/plugin_loader.py +0 -0
  210. {frontengine-1.0.37/frontengine/utils/redirect_manager → frontengine-1.0.39/frontengine/utils/power_mode}/__init__.py +0 -0
  211. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/power_mode/power_mode.py +0 -0
  212. {frontengine-1.0.37/frontengine/utils/reminder → frontengine-1.0.39/frontengine/utils/preset_schedule}/__init__.py +0 -0
  213. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/preset_schedule/preset_schedule_service.py +0 -0
  214. {frontengine-1.0.37/frontengine/utils/smart_pause → frontengine-1.0.39/frontengine/utils/recording}/__init__.py +0 -0
  215. {frontengine-1.0.37/frontengine/utils/system_stats → frontengine-1.0.39/frontengine/utils/redirect_manager}/__init__.py +0 -0
  216. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/redirect_manager/redirect_manager_class.py +0 -0
  217. {frontengine-1.0.37/frontengine/utils/text_source → frontengine-1.0.39/frontengine/utils/reminder}/__init__.py +0 -0
  218. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/reminder/reminder_service.py +0 -0
  219. {frontengine-1.0.37/frontengine/utils/theme_schedule → frontengine-1.0.39/frontengine/utils/smart_pause}/__init__.py +0 -0
  220. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/smart_pause/pause_rules.py +0 -0
  221. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/smart_pause/smart_pause_service.py +0 -0
  222. {frontengine-1.0.37/frontengine/utils/weather → frontengine-1.0.39/frontengine/utils/system_stats}/__init__.py +0 -0
  223. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/system_stats/system_stats.py +0 -0
  224. {frontengine-1.0.37/frontengine/utils/window_pin → frontengine-1.0.39/frontengine/utils/text_source}/__init__.py +0 -0
  225. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/text_source/text_source.py +0 -0
  226. {frontengine-1.0.37/frontengine/utils/workshop → frontengine-1.0.39/frontengine/utils/theme_schedule}/__init__.py +0 -0
  227. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/theme_schedule/theme_schedule_service.py +0 -0
  228. {frontengine-1.0.37/frontengine/worker → frontengine-1.0.39/frontengine/utils/usage_tracking}/__init__.py +0 -0
  229. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/weather/weather_service.py +0 -0
  230. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/web_url.py +0 -0
  231. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/window_pin/window_pin.py +0 -0
  232. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/workshop/workshop_content.py +0 -0
  233. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/worker/qthread_worker.py +0 -0
  234. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine.egg-info/dependency_links.txt +0 -0
  235. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine.egg-info/entry_points.txt +0 -0
  236. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine.egg-info/requires.txt +0 -0
  237. {frontengine-1.0.37 → frontengine-1.0.39}/frontengine.egg-info/top_level.txt +0 -0
  238. {frontengine-1.0.37 → 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.37
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.
@@ -166,6 +177,31 @@ that caps every overlay's refresh rate and drops its render resolution.
166
177
 
167
178
  ---
168
179
 
180
+ ## Screen time, clipboard and layouts
181
+
182
+ Three things that remember, all of them **off until you switch them on** and
183
+ all of them **local — nothing here is ever sent anywhere**:
184
+
185
+ - **Screen time** (Settings → Screen time) — which apps had focus and for how
186
+ long, with a daily breakdown and a seven-day summary. It pauses while you are
187
+ away from the keyboard, keeps 60 days at most, and **clearing deletes the
188
+ file itself**. While it is off, no file is created at all.
189
+ - **Clipboard history** (Settings → Clipboard history) — search what you copied
190
+ and pin the phrases you reuse. Clipboards routinely hold passwords, so this
191
+ is kept **in memory only** unless you separately tick "keep between
192
+ sessions".
193
+ - **Window layouts** (Tools tab) — save where every window sits and put them
194
+ back later. Windows are matched by title, and one that is not on screen is
195
+ skipped rather than guessed at. Windows only.
196
+
197
+ The **Screen care** tab also gains **colour-vision simulation** — protanopia,
198
+ deuteranopia, tritanopia and achromatopsia at an adjustable severity, using the
199
+ Machado et al. (2009) model. Unlike the other overlays it is opaque, because
200
+ showing what someone else sees means repainting the screen rather than tinting
201
+ it.
202
+
203
+ ---
204
+
169
205
  ## Community content
170
206
 
171
207
  - **Steam Workshop** — subscribed items are picked up from Steam's own
@@ -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.
@@ -142,6 +153,31 @@ that caps every overlay's refresh rate and drops its render resolution.
142
153
 
143
154
  ---
144
155
 
156
+ ## Screen time, clipboard and layouts
157
+
158
+ Three things that remember, all of them **off until you switch them on** and
159
+ all of them **local — nothing here is ever sent anywhere**:
160
+
161
+ - **Screen time** (Settings → Screen time) — which apps had focus and for how
162
+ long, with a daily breakdown and a seven-day summary. It pauses while you are
163
+ away from the keyboard, keeps 60 days at most, and **clearing deletes the
164
+ file itself**. While it is off, no file is created at all.
165
+ - **Clipboard history** (Settings → Clipboard history) — search what you copied
166
+ and pin the phrases you reuse. Clipboards routinely hold passwords, so this
167
+ is kept **in memory only** unless you separately tick "keep between
168
+ sessions".
169
+ - **Window layouts** (Tools tab) — save where every window sits and put them
170
+ back later. Windows are matched by title, and one that is not on screen is
171
+ skipped rather than guessed at. Windows only.
172
+
173
+ The **Screen care** tab also gains **colour-vision simulation** — protanopia,
174
+ deuteranopia, tritanopia and achromatopsia at an adjustable severity, using the
175
+ Machado et al. (2009) model. Unlike the other overlays it is opaque, because
176
+ showing what someone else sees means repainting the screen rather than tinting
177
+ it.
178
+
179
+ ---
180
+
145
181
  ## Community content
146
182
 
147
183
  - **Steam Workshop** — subscribed items are picked up from Steam's own
@@ -0,0 +1,147 @@
1
+ """
2
+ 色覺模擬覆蓋層:抓一張畫面、套上色覺缺陷的轉換、再整片蓋回去。
3
+
4
+ 這一層和其他覆蓋層不同:它必須是不透明的,因為「模擬看不清楚的顏色」沒辦法
5
+ 用半透明疊色做到——得真的把底下的畫面重畫一次。畫面只在記憶體裡轉換,不存檔。
6
+
7
+ A colour-vision overlay: grab the screen, apply the deficiency transform, and
8
+ paint the result back over everything.
9
+
10
+ It is opaque, unlike the other overlays: simulating a colour deficiency cannot
11
+ be done by tinting, it needs the picture underneath redrawn. The grab is
12
+ transformed in memory and never saved.
13
+ """
14
+ from __future__ import annotations
15
+
16
+ from typing import Optional
17
+
18
+ import numpy
19
+ from PySide6.QtCore import Qt, QTimer
20
+ from PySide6.QtGui import QGuiApplication, QImage, QPainter, QPixmap
21
+
22
+ from frontengine.show.base_widget import BaseWidget
23
+ from frontengine.utils.color_vision.color_vision import (
24
+ KIND_DEUTERANOPIA, clamp_severity, normalize_kind, simulate_array,
25
+ )
26
+ from frontengine.utils.logging.loggin_instance import front_engine_logger
27
+ from frontengine.utils.power_mode.power_mode import tier_interval
28
+
29
+ # 色覺模擬不需要很高的更新率,五分之一秒足夠,也省下大量重繪
30
+ # The simulation does not need a high refresh rate; five per second is plenty
31
+ # and saves a great deal of redrawing.
32
+ REFRESH_INTERVAL_MS = 200
33
+
34
+
35
+ def image_to_array(image: QImage) -> Optional[numpy.ndarray]:
36
+ """
37
+ QImage -> H x W x 3 的 uint8 陣列。每列可能有補齊位元組,所以要照
38
+ bytesPerLine 切,不能直接 reshape。
39
+ QImage to an H x W x 3 uint8 array. Rows can be padded, so slice by
40
+ bytesPerLine rather than reshaping the raw buffer.
41
+ """
42
+ if image is None or image.isNull():
43
+ return None
44
+ converted = image.convertToFormat(QImage.Format.Format_RGB888)
45
+ width, height, stride = converted.width(), converted.height(), converted.bytesPerLine()
46
+ if width <= 0 or height <= 0:
47
+ return None
48
+ buffer = numpy.frombuffer(converted.constBits(), dtype=numpy.uint8, count=stride * height)
49
+ return buffer.reshape((height, stride))[:, : width * 3].reshape(
50
+ (height, width, 3)).copy()
51
+
52
+
53
+ def array_to_image(pixels: numpy.ndarray) -> Optional[QImage]:
54
+ """H x W x 3 的 uint8 陣列 -> QImage(複製一份,不依賴呼叫端的緩衝區)。"""
55
+ data = numpy.ascontiguousarray(pixels, dtype=numpy.uint8)
56
+ if data.ndim != 3 or data.shape[2] != 3:
57
+ return None
58
+ height, width, _channels = data.shape
59
+ image = QImage(data.tobytes(), width, height, width * 3, QImage.Format.Format_RGB888)
60
+ return image.copy()
61
+
62
+
63
+ class ColorVisionWidget(BaseWidget):
64
+ """
65
+ 色覺模擬層。擷取函式可注入,所以不需要真的螢幕也測得到轉換流程。
66
+ """
67
+
68
+ def __init__(self, kind: str = KIND_DEUTERANOPIA, severity: float = 1.0) -> None:
69
+ front_engine_logger.info(f"[ColorVisionWidget] Init | kind={kind}")
70
+ super().__init__()
71
+ self.kind = normalize_kind(kind)
72
+ self.severity = clamp_severity(severity)
73
+ self.opacity = 1.0
74
+ self.simulated: Optional[QPixmap] = None
75
+ self._grabber = self._default_grabber
76
+ self._timer = QTimer(self)
77
+ self._timer.timeout.connect(self.refresh)
78
+ # 這一層是不透明的:它要蓋掉底下的畫面,而不是疊在上面
79
+ # This layer is opaque - it replaces what is underneath rather than
80
+ # tinting it.
81
+ self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground, False)
82
+
83
+ @staticmethod
84
+ def _default_grabber(rect): # pragma: no cover - needs a real screen
85
+ screen = QGuiApplication.primaryScreen()
86
+ if screen is None:
87
+ return None
88
+ return screen.grabWindow(0, rect.x(), rect.y(), rect.width(), rect.height())
89
+
90
+ def set_grabber(self, grabber) -> None:
91
+ """設定螢幕擷取函式(測試會換成假的)。"""
92
+ self._grabber = grabber or self._default_grabber
93
+
94
+ def set_simulation(self, kind: Optional[str] = None,
95
+ severity: Optional[float] = None) -> None:
96
+ """換一種色覺類型或嚴重程度,立刻重畫。"""
97
+ if kind is not None:
98
+ self.kind = normalize_kind(kind)
99
+ if severity is not None:
100
+ self.severity = clamp_severity(severity)
101
+ self.refresh()
102
+
103
+ def start(self) -> None:
104
+ self.refresh()
105
+ self._timer.start(tier_interval(REFRESH_INTERVAL_MS, self.quality_tier))
106
+
107
+ def stop(self) -> None:
108
+ self._timer.stop()
109
+
110
+ def apply_quality_tier(self) -> None:
111
+ if self._timer.isActive():
112
+ self._timer.start(tier_interval(REFRESH_INTERVAL_MS, self.quality_tier))
113
+
114
+ def refresh(self) -> None:
115
+ """抓一次畫面、轉換、準備重畫。"""
116
+ geometry = self.geometry()
117
+ try:
118
+ # 擷取時先把自己藏起來,否則會拍到上一次的模擬結果,越疊越糊
119
+ # Hide first, or the grab catches the previous simulation and the
120
+ # picture degrades with every pass.
121
+ was_visible = self.isVisible()
122
+ if was_visible:
123
+ self.setVisible(False)
124
+ pixmap = self._grabber(geometry)
125
+ if was_visible:
126
+ self.setVisible(True)
127
+ except Exception as error: # pragma: no cover - screen grab boundary
128
+ front_engine_logger.warning(f"[ColorVisionWidget] grab failed: {error!r}")
129
+ return
130
+ if pixmap is None or pixmap.isNull():
131
+ return
132
+ pixels = image_to_array(pixmap.toImage())
133
+ if pixels is None:
134
+ return
135
+ image = array_to_image(simulate_array(pixels, self.kind, self.severity))
136
+ if image is not None:
137
+ self.simulated = QPixmap.fromImage(image)
138
+ self.update()
139
+
140
+ def draw_content(self, painter: QPainter) -> None:
141
+ if self.simulated is None or self.simulated.isNull():
142
+ return
143
+ painter.drawPixmap(self.rect(), self.simulated)
144
+
145
+ def closeEvent(self, event) -> None:
146
+ self.stop()
147
+ super().closeEvent(event)
@@ -0,0 +1,147 @@
1
+ """
2
+ 剪貼簿歷史視窗:搜尋、釘選、貼回去。
3
+
4
+ 預設不記錄;要記錄得自己在這裡打開。除非再額外勾選「保存到檔案」,否則
5
+ 內容只留在記憶體,關掉程式就沒了——剪貼簿裡常有密碼,這是刻意的預設。
6
+
7
+ The clipboard history window: search, pin, put back.
8
+
9
+ Recording is off until switched on here, and unless "keep between sessions" is
10
+ also ticked the history lives in memory only and is gone when the app closes.
11
+ Clipboards routinely hold passwords; that default is deliberate.
12
+ """
13
+ from __future__ import annotations
14
+
15
+ from typing import Optional
16
+
17
+ from PySide6.QtCore import Qt
18
+ from PySide6.QtGui import QGuiApplication
19
+ from PySide6.QtWidgets import (
20
+ QAbstractItemView, QCheckBox, QDialog, QDialogButtonBox, QGridLayout, QLabel, QLineEdit,
21
+ QListWidget, QListWidgetItem, QPushButton, QWidget,
22
+ )
23
+
24
+ from frontengine.user_setting.user_setting_file import user_setting_dict, write_user_setting
25
+ from frontengine.utils.clipboard.clipboard_history import ClipboardHistory, preview
26
+ from frontengine.utils.logging.loggin_instance import front_engine_logger
27
+ from frontengine.utils.multi_language.language_wrapper import language_wrapper
28
+
29
+ _PIN_MARK = "📌 "
30
+
31
+
32
+ def _t(key: str, fallback: str) -> str:
33
+ return language_wrapper.language_word_dict.get(key, fallback)
34
+
35
+
36
+ class ClipboardDialog(QDialog):
37
+ """瀏覽剪貼簿歷史並貼回去。"""
38
+
39
+ def __init__(self, history: ClipboardHistory, parent: Optional[QWidget] = None) -> None:
40
+ front_engine_logger.info("[ClipboardDialog] Init")
41
+ super().__init__(parent)
42
+ self.setWindowTitle(_t("clipboard_title", "Clipboard history"))
43
+ self.history = history
44
+
45
+ self.search_edit = QLineEdit()
46
+ self.search_edit.setPlaceholderText(_t("clipboard_search", "Search..."))
47
+ self.search_edit.textChanged.connect(self.reload_entries)
48
+
49
+ self.entry_list = QListWidget()
50
+ self.entry_list.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
51
+ self.entry_list.itemDoubleClicked.connect(lambda _item: self.copy_selected())
52
+
53
+ self.copy_button = QPushButton(_t("clipboard_copy", "Copy"))
54
+ self.copy_button.clicked.connect(self.copy_selected)
55
+ self.pin_button = QPushButton(_t("clipboard_pin", "Pin / unpin"))
56
+ self.pin_button.clicked.connect(self.toggle_pin_selected)
57
+ self.remove_button = QPushButton(_t("clipboard_remove", "Remove"))
58
+ self.remove_button.clicked.connect(self.remove_selected)
59
+ self.clear_button = QPushButton(_t("clipboard_clear", "Clear (keep pinned)"))
60
+ self.clear_button.clicked.connect(self.clear_history)
61
+
62
+ self.persist_checkbox = QCheckBox(
63
+ _t("clipboard_persist", "Keep this history between sessions"))
64
+ self.persist_checkbox.setChecked(bool(user_setting_dict.get("clipboard_persist")))
65
+ self.persist_checkbox.toggled.connect(self._store_persist)
66
+ self.hint_label = QLabel(
67
+ _t("clipboard_hint",
68
+ "History stays on this machine and is never sent anywhere. Unless you tick "
69
+ "the box above it is kept in memory only, because clipboards often hold "
70
+ "passwords."))
71
+ self.hint_label.setWordWrap(True)
72
+
73
+ self.button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Close)
74
+ self.button_box.rejected.connect(self.reject)
75
+
76
+ layout = QGridLayout(self)
77
+ layout.addWidget(self.search_edit, 0, 0, 1, 4)
78
+ layout.addWidget(self.entry_list, 1, 0, 1, 4)
79
+ layout.addWidget(self.copy_button, 2, 0)
80
+ layout.addWidget(self.pin_button, 2, 1)
81
+ layout.addWidget(self.remove_button, 2, 2)
82
+ layout.addWidget(self.clear_button, 2, 3)
83
+ layout.addWidget(self.persist_checkbox, 3, 0, 1, 4)
84
+ layout.addWidget(self.hint_label, 4, 0, 1, 4)
85
+ layout.addWidget(self.button_box, 5, 0, 1, 4)
86
+
87
+ self.reload_entries()
88
+
89
+ def reload_entries(self) -> int:
90
+ """依搜尋字串重畫清單,回傳顯示的筆數。"""
91
+ self.entry_list.clear()
92
+ for entry in self.history.search(self.search_edit.text()):
93
+ label = (_PIN_MARK if entry["pinned"] else "") + preview(entry["text"])
94
+ item = QListWidgetItem(label)
95
+ item.setData(Qt.ItemDataRole.UserRole, entry["text"])
96
+ item.setToolTip(entry["text"])
97
+ self.entry_list.addItem(item)
98
+ return self.entry_list.count()
99
+
100
+ def selected_text(self) -> Optional[str]:
101
+ item = self.entry_list.currentItem()
102
+ if item is None:
103
+ return None
104
+ return str(item.data(Qt.ItemDataRole.UserRole))
105
+
106
+ def copy_selected(self) -> bool:
107
+ """把選到的內容放回剪貼簿。"""
108
+ text = self.selected_text()
109
+ if text is None:
110
+ return False
111
+ clipboard = QGuiApplication.clipboard()
112
+ if clipboard is None: # pragma: no cover - no clipboard at all
113
+ return False
114
+ clipboard.setText(text)
115
+ return True
116
+
117
+ def toggle_pin_selected(self) -> bool:
118
+ text = self.selected_text()
119
+ if text is None:
120
+ return False
121
+ current = next((entry for entry in self.history.entries if entry["text"] == text), None)
122
+ if current is None:
123
+ return False
124
+ self.history.set_pinned(text, not current["pinned"])
125
+ self.reload_entries()
126
+ return True
127
+
128
+ def remove_selected(self) -> bool:
129
+ text = self.selected_text()
130
+ if text is None:
131
+ return False
132
+ removed = self.history.remove(text)
133
+ self.reload_entries()
134
+ return removed
135
+
136
+ def clear_history(self) -> None:
137
+ """清空未釘選的紀錄。"""
138
+ self.history.clear(keep_pinned=True)
139
+ self.reload_entries()
140
+
141
+ def _store_persist(self, enabled: bool) -> None:
142
+ user_setting_dict["clipboard_persist"] = bool(enabled)
143
+ if not enabled:
144
+ # 取消保存時把已經寫下去的內容一併移除,不留殘影
145
+ # Turning saving off also clears what was already written down.
146
+ user_setting_dict["clipboard_entries"] = []
147
+ write_user_setting()
@@ -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
@@ -0,0 +1,120 @@
1
+ """
2
+ 使用時間報告:今天各程式用了多久,以及最近七天的總量。
3
+
4
+ 這份資料只存在本機的 frontengine_usage.json,不會傳到任何地方;統計預設關閉,
5
+ 而且這裡就有「清除」按鈕,按下去連檔案一起刪掉。
6
+
7
+ The usage report: how long each app had focus today, plus the last seven days.
8
+
9
+ The data lives only in the local frontengine_usage.json and goes nowhere else.
10
+ Tracking is off by default, and the clear button here deletes the file itself.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ from typing import Optional
15
+
16
+ from PySide6.QtWidgets import (
17
+ QCheckBox, QDialog, QDialogButtonBox, QGridLayout, QHeaderView, QLabel, QPushButton,
18
+ QTableWidget, QTableWidgetItem, QWidget,
19
+ )
20
+
21
+ from frontengine.utils.logging.loggin_instance import front_engine_logger
22
+ from frontengine.utils.multi_language.language_wrapper import language_wrapper
23
+ from frontengine.utils.usage_tracking.usage_tracker import UsageTracker, format_duration
24
+
25
+
26
+ def _t(key: str, fallback: str) -> str:
27
+ return language_wrapper.language_word_dict.get(key, fallback)
28
+
29
+
30
+ def share_percent(seconds: float, total: float) -> int:
31
+ """某個程式佔今天的百分比;今天還沒有紀錄就是 0。"""
32
+ if total <= 0:
33
+ return 0
34
+ return int(round(max(0.0, min(1.0, seconds / total)) * 100))
35
+
36
+
37
+ class UsageReportDialog(QDialog):
38
+ """顯示今天的使用時間,並提供開關與清除。"""
39
+
40
+ def __init__(self, tracker: UsageTracker, parent: Optional[QWidget] = None,
41
+ enabled: bool = False, on_toggle=None, on_clear=None) -> None:
42
+ front_engine_logger.info("[UsageReportDialog] Init")
43
+ super().__init__(parent)
44
+ self.setWindowTitle(_t("usage_title", "Screen time"))
45
+ self.tracker = tracker
46
+ self._on_toggle = on_toggle
47
+ self._on_clear = on_clear
48
+
49
+ self.enable_checkbox = QCheckBox(_t("usage_enable", "Track which apps I use"))
50
+ self.enable_checkbox.setChecked(bool(enabled))
51
+ self.enable_checkbox.toggled.connect(self._toggle)
52
+
53
+ self.total_label = QLabel()
54
+ self.table = QTableWidget(0, 3)
55
+ self.table.setHorizontalHeaderLabels([
56
+ _t("usage_app_column", "Application"),
57
+ _t("usage_time_column", "Time"),
58
+ _t("usage_share_column", "Share"),
59
+ ])
60
+ self.table.horizontalHeader().setSectionResizeMode(
61
+ 0, QHeaderView.ResizeMode.Stretch)
62
+ self.table.verticalHeader().setVisible(False)
63
+
64
+ self.week_label = QLabel()
65
+ self.week_label.setWordWrap(True)
66
+ self.refresh_button = QPushButton(_t("usage_refresh", "Refresh"))
67
+ self.refresh_button.clicked.connect(self.reload_report)
68
+ self.clear_button = QPushButton(_t("usage_clear", "Clear history"))
69
+ self.clear_button.clicked.connect(self.clear_history)
70
+ self.hint_label = QLabel(
71
+ _t("usage_hint",
72
+ "This is stored on this machine only and is never sent anywhere. "
73
+ "Clearing deletes the file itself."))
74
+ self.hint_label.setWordWrap(True)
75
+
76
+ self.button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Close)
77
+ self.button_box.rejected.connect(self.reject)
78
+
79
+ layout = QGridLayout(self)
80
+ layout.addWidget(self.enable_checkbox, 0, 0, 1, 2)
81
+ layout.addWidget(self.total_label, 1, 0, 1, 2)
82
+ layout.addWidget(self.table, 2, 0, 1, 2)
83
+ layout.addWidget(self.week_label, 3, 0, 1, 2)
84
+ layout.addWidget(self.refresh_button, 4, 0)
85
+ layout.addWidget(self.clear_button, 4, 1)
86
+ layout.addWidget(self.hint_label, 5, 0, 1, 2)
87
+ layout.addWidget(self.button_box, 6, 0, 1, 2)
88
+
89
+ self.reload_report()
90
+
91
+ def reload_report(self) -> int:
92
+ """重畫今天的排行,回傳列數。"""
93
+ total = self.tracker.total_seconds()
94
+ self.total_label.setText(
95
+ _t("usage_total", "Today: {total}").format(total=format_duration(total)))
96
+ rows = self.tracker.top_apps(limit=12)
97
+ self.table.setRowCount(len(rows))
98
+ for index, (app, seconds) in enumerate(rows):
99
+ self.table.setItem(index, 0, QTableWidgetItem(app))
100
+ self.table.setItem(index, 1, QTableWidgetItem(format_duration(seconds)))
101
+ self.table.setItem(index, 2, QTableWidgetItem(f"{share_percent(seconds, total)}%"))
102
+ self.week_label.setText(self.week_summary())
103
+ return len(rows)
104
+
105
+ def week_summary(self) -> str:
106
+ """最近七天的一行摘要。"""
107
+ parts = [f"{day[5:]} {format_duration(seconds)}"
108
+ for day, seconds in self.tracker.recent_days(7)]
109
+ return " · ".join(parts)
110
+
111
+ def clear_history(self) -> None:
112
+ """清掉所有統計(含磁碟上的檔案)。"""
113
+ self.tracker.clear()
114
+ if self._on_clear is not None:
115
+ self._on_clear()
116
+ self.reload_report()
117
+
118
+ def _toggle(self, enabled: bool) -> None:
119
+ if self._on_toggle is not None:
120
+ self._on_toggle(bool(enabled))