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.
- {frontengine-1.0.37 → frontengine-1.0.39}/PKG-INFO +37 -1
- {frontengine-1.0.37 → frontengine-1.0.39}/README.md +36 -0
- frontengine-1.0.39/frontengine/show/screen_care/color_vision_widget.py +147 -0
- frontengine-1.0.39/frontengine/ui/dialog/clipboard_dialog.py +147 -0
- frontengine-1.0.39/frontengine/ui/dialog/screen_text_dialog.py +120 -0
- frontengine-1.0.39/frontengine/ui/dialog/usage_report_dialog.py +120 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/main_ui.py +58 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/menu/settings_menu.py +35 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/screen_care/screen_care_setting_ui.py +78 -5
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/tools/tools_setting_ui.py +260 -11
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/user_setting/user_setting_file.py +15 -0
- frontengine-1.0.39/frontengine/utils/clipboard/clipboard_history.py +179 -0
- frontengine-1.0.39/frontengine/utils/clipboard/clipboard_watcher.py +75 -0
- frontengine-1.0.39/frontengine/utils/color_vision/color_vision.py +113 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/multi_language/english.py +50 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/multi_language/traditional_chinese.py +50 -0
- frontengine-1.0.39/frontengine/utils/recording/frame_recorder.py +199 -0
- frontengine-1.0.39/frontengine/utils/recording/gif_writer.py +203 -0
- frontengine-1.0.39/frontengine/utils/usage_tracking/usage_service.py +85 -0
- frontengine-1.0.39/frontengine/utils/usage_tracking/usage_tracker.py +218 -0
- frontengine-1.0.39/frontengine/utils/weather/__init__.py +0 -0
- frontengine-1.0.39/frontengine/utils/window_pin/__init__.py +0 -0
- frontengine-1.0.39/frontengine/utils/window_pin/window_layout.py +135 -0
- frontengine-1.0.39/frontengine/utils/workshop/__init__.py +0 -0
- frontengine-1.0.39/frontengine/worker/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine.egg-info/PKG-INFO +37 -1
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine.egg-info/SOURCES.txt +16 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/pyproject.toml +1 -1
- {frontengine-1.0.37 → frontengine-1.0.39}/LICENSE +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/__main__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/base_widget.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/camera/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/camera/camera_widget.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/capture/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/capture/region_capture.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/focus_shield/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/focus_shield/focus_shield_widget.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/gif/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/gif/paint_gif.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/image/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/image/paint_image.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/load/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/load/load_someone_make_ui.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/measure/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/measure/measure_widget.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/monitor/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/monitor/monitor_widget.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/notes/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/notes/sticky_note_widget.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/overlay_factory.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/particle/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/particle/particle_ui.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/pet/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/pet/desktop_pet.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/presentation/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/presentation/annotation_overlay.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/presentation/cursor_effects.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/presentation/keystroke_display.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/presentation/magnifier.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/scene/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/scene/extend_graphic_scene.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/scene/extend_graphic_view.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/scene/scene.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/screen_care/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/screen_care/screen_filter.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/sound_player/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/sound_player/sound_effect.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/sound_player/sound_player.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/spectrum/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/spectrum/spectrum_widget.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/text/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/text/draw_text.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/toast/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/toast/toast_widget.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/video/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/video/video_player.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/wallpaper/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/wallpaper/wallpaper_widget.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/web/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/web/webview.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/show/window_helpers.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/system_tray/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/system_tray/extend_system_tray.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/color/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/color/global_color.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/dialog/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/dialog/app_profile_dialog.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/dialog/choose_file_dialog.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/dialog/hotkey_settings_dialog.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/dialog/reminder_dialog.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/dialog/save_file_dialog.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/dialog/smart_pause_dialog.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/dialog/window_pin_dialog.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/menu/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/menu/help_menu.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/menu/how_to_menu.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/menu/language_menu.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/menu/preset_menu.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/control_center/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/control_center/control_center_ui.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/focus/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/focus/focus_setting_ui.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/gif/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/gif/gif_setting_ui.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/image/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/image/image_setting_ui.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/particle/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/particle/particle_setting_ui.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/pet/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/pet/pet_setting_ui.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/presentation/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/presentation/presentation_setting_ui.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_manager.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/base_scene_page.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/gif.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/image.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/registry.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/sound.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/text.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/video.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_page/web.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/scene_setting/scene_setting_ui.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/screen_care/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/sound_player/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/sound_player/sound_player_setting_ui.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/text/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/text/text_setting_ui.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/tools/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/utils.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/video/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/video/video_setting_ui.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/wallpaper/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/wallpaper/wallpaper_setting_ui.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/web/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/web/web_setting_ui.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/widgets/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/ui/page/widgets/widgets_setting_ui.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/user_setting/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/user_setting/preset_repository.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/user_setting/scene_setting.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/app_profile/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/app_profile/app_profile_service.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/audio_meter/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/audio_meter/audio_envelope.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/audio_meter/loopback_capture.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/audio_meter/screen_audio.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/audio_meter/spectrum_analyzer.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/audio_meter/system_audio_meter.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/autostart/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/autostart/autostart_service.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/break_reminder/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/break_reminder/break_reminder.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/browser/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/browser/browser.py +0 -0
- {frontengine-1.0.37/frontengine/utils/critical_exit → frontengine-1.0.39/frontengine/utils/clipboard}/__init__.py +0 -0
- {frontengine-1.0.37/frontengine/utils/file → frontengine-1.0.39/frontengine/utils/color_vision}/__init__.py +0 -0
- {frontengine-1.0.37/frontengine/utils/focus_shield → frontengine-1.0.39/frontengine/utils/critical_exit}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/critical_exit/check_key_is_press.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/critical_exit/critical_exit.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/critical_exit/win32_vk.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/exception/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/exception/exception_tags.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/exception/exceptions.py +0 -0
- {frontengine-1.0.37/frontengine/utils/focus_timer → frontengine-1.0.39/frontengine/utils/file}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/file/open/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/file/open/open_file.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/file/save/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/file/save/save_file.py +0 -0
- {frontengine-1.0.37/frontengine/utils/hotkey → frontengine-1.0.39/frontengine/utils/focus_shield}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/focus_shield/focus_shield.py +0 -0
- {frontengine-1.0.37/frontengine/utils/input_watch → frontengine-1.0.39/frontengine/utils/focus_timer}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/focus_timer/focus_timer.py +0 -0
- {frontengine-1.0.37/frontengine/utils/json → frontengine-1.0.39/frontengine/utils/hotkey}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/hotkey/hotkey_service.py +0 -0
- {frontengine-1.0.37/frontengine/utils/logging → frontengine-1.0.39/frontengine/utils/input_watch}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/input_watch/input_watch_service.py +0 -0
- {frontengine-1.0.37/frontengine/utils/measure → frontengine-1.0.39/frontengine/utils/json}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/json/json_file.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/json/json_repository.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/json_format/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/json_format/json_process.py +0 -0
- {frontengine-1.0.37/frontengine/utils/multi_language → frontengine-1.0.39/frontengine/utils/logging}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/logging/loggin_instance.py +0 -0
- {frontengine-1.0.37/frontengine/utils/now_playing → frontengine-1.0.39/frontengine/utils/measure}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/measure/measure.py +0 -0
- {frontengine-1.0.37/frontengine/utils/pet_chat → frontengine-1.0.39/frontengine/utils/multi_language}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/multi_language/france.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/multi_language/germany.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/multi_language/italy.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/multi_language/language_wrapper.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/multi_language/russian.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/multi_language/simplified_chinese.py +0 -0
- {frontengine-1.0.37/frontengine/utils/platform_info → frontengine-1.0.39/frontengine/utils/now_playing}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/now_playing/now_playing.py +0 -0
- {frontengine-1.0.37/frontengine/utils/playlist → frontengine-1.0.39/frontengine/utils/pet_chat}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/pet_chat/pet_chat_service.py +0 -0
- {frontengine-1.0.37/frontengine/utils/plugins → frontengine-1.0.39/frontengine/utils/platform_info}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/platform_info/platform_info.py +0 -0
- {frontengine-1.0.37/frontengine/utils/power_mode → frontengine-1.0.39/frontengine/utils/playlist}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/playlist/playlist.py +0 -0
- {frontengine-1.0.37/frontengine/utils/preset_schedule → frontengine-1.0.39/frontengine/utils/plugins}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/plugins/plugin_loader.py +0 -0
- {frontengine-1.0.37/frontengine/utils/redirect_manager → frontengine-1.0.39/frontengine/utils/power_mode}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/power_mode/power_mode.py +0 -0
- {frontengine-1.0.37/frontengine/utils/reminder → frontengine-1.0.39/frontengine/utils/preset_schedule}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/preset_schedule/preset_schedule_service.py +0 -0
- {frontengine-1.0.37/frontengine/utils/smart_pause → frontengine-1.0.39/frontengine/utils/recording}/__init__.py +0 -0
- {frontengine-1.0.37/frontengine/utils/system_stats → frontengine-1.0.39/frontengine/utils/redirect_manager}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/redirect_manager/redirect_manager_class.py +0 -0
- {frontengine-1.0.37/frontengine/utils/text_source → frontengine-1.0.39/frontengine/utils/reminder}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/reminder/reminder_service.py +0 -0
- {frontengine-1.0.37/frontengine/utils/theme_schedule → frontengine-1.0.39/frontengine/utils/smart_pause}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/smart_pause/pause_rules.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/smart_pause/smart_pause_service.py +0 -0
- {frontengine-1.0.37/frontengine/utils/weather → frontengine-1.0.39/frontengine/utils/system_stats}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/system_stats/system_stats.py +0 -0
- {frontengine-1.0.37/frontengine/utils/window_pin → frontengine-1.0.39/frontengine/utils/text_source}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/text_source/text_source.py +0 -0
- {frontengine-1.0.37/frontengine/utils/workshop → frontengine-1.0.39/frontengine/utils/theme_schedule}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/theme_schedule/theme_schedule_service.py +0 -0
- {frontengine-1.0.37/frontengine/worker → frontengine-1.0.39/frontengine/utils/usage_tracking}/__init__.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/weather/weather_service.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/web_url.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/window_pin/window_pin.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/utils/workshop/workshop_content.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine/worker/qthread_worker.py +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine.egg-info/dependency_links.txt +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine.egg-info/entry_points.txt +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine.egg-info/requires.txt +0 -0
- {frontengine-1.0.37 → frontengine-1.0.39}/frontengine.egg-info/top_level.txt +0 -0
- {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.
|
|
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))
|