qtimetrap 0.1.1

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 (130) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +25 -0
  3. data/README.md +92 -0
  4. data/Rakefile +105 -0
  5. data/app/assets/icons/qtimetrap-icon-128.png +0 -0
  6. data/app/assets/icons/qtimetrap-icon-256.png +0 -0
  7. data/app/assets/icons/qtimetrap-icon-512.png +0 -0
  8. data/app/assets/icons/qtimetrap-icon.svg +123 -0
  9. data/app/components/entries/branch_hierarchy_helpers.rb +41 -0
  10. data/app/components/entries/leaf_archive_helpers.rb +23 -0
  11. data/app/components/entries/leaf_note_helpers.rb +118 -0
  12. data/app/components/entries/leaf_task_helpers.rb +81 -0
  13. data/app/components/entries/leaf_time_helpers.rb +105 -0
  14. data/app/components/entries/list_component.rb +118 -0
  15. data/app/components/entries/list_host_helpers.rb +27 -0
  16. data/app/components/entries/list_state_helpers.rb +59 -0
  17. data/app/components/entries/node_presentation_helpers.rb +29 -0
  18. data/app/components/entries/render_helpers.rb +25 -0
  19. data/app/components/entries/tree_helpers.rb +133 -0
  20. data/app/components/entries/tree_toolbar_helpers.rb +98 -0
  21. data/app/components/project_sidebar/archive_toggle_helpers.rb +22 -0
  22. data/app/components/project_sidebar/component.rb +125 -0
  23. data/app/components/project_sidebar/logo_helpers.rb +53 -0
  24. data/app/components/project_sidebar/project_button_helpers.rb +72 -0
  25. data/app/components/project_sidebar/project_selection_helpers.rb +94 -0
  26. data/app/components/project_sidebar/task_helpers.rb +94 -0
  27. data/app/components/project_sidebar/task_selection_helpers.rb +72 -0
  28. data/app/components/qt_ui_helpers.rb +27 -0
  29. data/app/components/tracker_controls/component.rb +80 -0
  30. data/app/components/tracker_controls/layout_builder.rb +100 -0
  31. data/app/components/tracker_controls/layout_helpers.rb +88 -0
  32. data/app/models/null_settings_store.rb +12 -0
  33. data/app/models/time_entry.rb +59 -0
  34. data/app/services/archived_entries_store.rb +71 -0
  35. data/app/services/formatters.rb +29 -0
  36. data/app/services/settings_store.rb +83 -0
  37. data/app/services/timetrap_gateway.rb +89 -0
  38. data/app/services/timetrap_gateway_logger.rb +54 -0
  39. data/app/services/timetrap_gateway_query_helpers.rb +102 -0
  40. data/app/services/timetrap_gateway_start_helpers.rb +28 -0
  41. data/app/services/timetrap_gateway_update_note_helpers.rb +48 -0
  42. data/app/services/timetrap_gateway_update_task_helpers.rb +47 -0
  43. data/app/services/timetrap_gateway_update_time_helpers.rb +50 -0
  44. data/app/styles/theme.rb +61 -0
  45. data/app/styles/themes/dark/application.qss +8 -0
  46. data/app/styles/themes/dark/entries_list.qss +235 -0
  47. data/app/styles/themes/dark/project_sidebar.qss +99 -0
  48. data/app/styles/themes/dark/snippets/app_background.qss +2 -0
  49. data/app/styles/themes/dark/snippets/button_ghost.qss +5 -0
  50. data/app/styles/themes/dark/snippets/button_start.qss +5 -0
  51. data/app/styles/themes/dark/snippets/button_stop.qss +5 -0
  52. data/app/styles/themes/dark/snippets/entries_host.qss +2 -0
  53. data/app/styles/themes/dark/snippets/entries_scroll.qss +16 -0
  54. data/app/styles/themes/dark/snippets/entry_row_day.qss +6 -0
  55. data/app/styles/themes/dark/snippets/entry_row_detail.qss +5 -0
  56. data/app/styles/themes/dark/snippets/entry_row_project.qss +6 -0
  57. data/app/styles/themes/dark/snippets/project_button.qss +5 -0
  58. data/app/styles/themes/dark/snippets/project_button_active.qss +5 -0
  59. data/app/styles/themes/dark/snippets/project_sidebar_heading.qss +2 -0
  60. data/app/styles/themes/dark/snippets/project_sidebar_logo.qss +3 -0
  61. data/app/styles/themes/dark/snippets/project_sidebar_panel.qss +2 -0
  62. data/app/styles/themes/dark/snippets/tracker_clock.qss +2 -0
  63. data/app/styles/themes/dark/snippets/tracker_input.qss +5 -0
  64. data/app/styles/themes/dark/snippets/tracker_project_label.qss +5 -0
  65. data/app/styles/themes/dark/snippets/tracker_row.qss +3 -0
  66. data/app/styles/themes/dark/snippets/tracker_summary.qss +3 -0
  67. data/app/styles/themes/dark/snippets/tracker_timer.qss +3 -0
  68. data/app/styles/themes/dark/snippets/tracker_title.qss +3 -0
  69. data/app/styles/themes/dark/snippets/tracker_topbar.qss +2 -0
  70. data/app/styles/themes/dark/tracker_controls.qss +84 -0
  71. data/app/styles/themes/light/application.qss +8 -0
  72. data/app/styles/themes/light/entries_list.qss +235 -0
  73. data/app/styles/themes/light/project_sidebar.qss +99 -0
  74. data/app/styles/themes/light/snippets/app_background.qss +2 -0
  75. data/app/styles/themes/light/snippets/button_ghost.qss +5 -0
  76. data/app/styles/themes/light/snippets/button_start.qss +5 -0
  77. data/app/styles/themes/light/snippets/button_stop.qss +5 -0
  78. data/app/styles/themes/light/snippets/entries_host.qss +2 -0
  79. data/app/styles/themes/light/snippets/entries_scroll.qss +16 -0
  80. data/app/styles/themes/light/snippets/entry_row_day.qss +6 -0
  81. data/app/styles/themes/light/snippets/entry_row_detail.qss +5 -0
  82. data/app/styles/themes/light/snippets/entry_row_project.qss +6 -0
  83. data/app/styles/themes/light/snippets/project_button.qss +5 -0
  84. data/app/styles/themes/light/snippets/project_button_active.qss +5 -0
  85. data/app/styles/themes/light/snippets/project_sidebar_heading.qss +2 -0
  86. data/app/styles/themes/light/snippets/project_sidebar_logo.qss +3 -0
  87. data/app/styles/themes/light/snippets/project_sidebar_panel.qss +2 -0
  88. data/app/styles/themes/light/snippets/tracker_clock.qss +2 -0
  89. data/app/styles/themes/light/snippets/tracker_input.qss +5 -0
  90. data/app/styles/themes/light/snippets/tracker_project_label.qss +5 -0
  91. data/app/styles/themes/light/snippets/tracker_row.qss +3 -0
  92. data/app/styles/themes/light/snippets/tracker_summary.qss +3 -0
  93. data/app/styles/themes/light/snippets/tracker_timer.qss +3 -0
  94. data/app/styles/themes/light/snippets/tracker_title.qss +3 -0
  95. data/app/styles/themes/light/snippets/tracker_topbar.qss +2 -0
  96. data/app/styles/themes/light/tracker_controls.qss +84 -0
  97. data/app/view_models/entry_nodes_builder.rb +109 -0
  98. data/app/view_models/main_view_model.rb +135 -0
  99. data/app/view_models/main_view_model_archive_mode_helpers.rb +102 -0
  100. data/app/view_models/main_view_model_entry_note_helpers.rb +30 -0
  101. data/app/view_models/main_view_model_entry_task_helpers.rb +39 -0
  102. data/app/view_models/main_view_model_entry_time_helpers.rb +45 -0
  103. data/app/view_models/main_view_model_sheet_helpers.rb +74 -0
  104. data/app/view_models/main_view_model_task_filter_helpers.rb +58 -0
  105. data/app/view_models/main_view_model_time_range_filter_helpers.rb +23 -0
  106. data/app/views/main_window.rb +126 -0
  107. data/app/views/main_window_layout_builder.rb +114 -0
  108. data/app/views/main_window_runtime.rb +123 -0
  109. data/app/views/main_window_runtime_archive_helpers.rb +26 -0
  110. data/app/views/main_window_runtime_entry_task_helpers.rb +21 -0
  111. data/app/views/main_window_runtime_entry_time_helpers.rb +17 -0
  112. data/app/views/main_window_runtime_key_helpers.rb +55 -0
  113. data/app/views/main_window_runtime_render_helpers.rb +50 -0
  114. data/app/views/main_window_splitter_toggle_bootstrap_helpers.rb +34 -0
  115. data/app/views/main_window_splitter_toggle_helpers.rb +103 -0
  116. data/app/views/main_window_splitter_toggle_hover_helpers.rb +71 -0
  117. data/app/views/main_window_splitter_toggle_layout_helpers.rb +24 -0
  118. data/app/views/main_window_ui_helpers.rb +45 -0
  119. data/app/views/window_icon_loader.rb +37 -0
  120. data/bin/qtimetrap +7 -0
  121. data/config/application.rb +87 -0
  122. data/config/environments/development.rb +5 -0
  123. data/config/environments/production.rb +5 -0
  124. data/config/initializers/theme.rb +5 -0
  125. data/config/initializers/timetrap.rb +5 -0
  126. data/lib/qtimetrap/configuration.rb +15 -0
  127. data/lib/qtimetrap/container.rb +57 -0
  128. data/lib/qtimetrap/version.rb +5 -0
  129. data/lib/qtimetrap.rb +25 -0
  130. metadata +207 -0
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QTimetrap
4
+ module Views
5
+ # Hover and delayed hide behavior for splitter toggle button.
6
+ module MainWindowSplitterToggleHoverHelpers
7
+ SIDEBAR_TOGGLE_HIDE_DELAY_MS = 40
8
+
9
+ private
10
+
11
+ def bind_toggle_zone_events(zone:, button:, state:)
12
+ zone.on(:enter) { |_| on_toggle_zone_enter(button: button, state: state) }
13
+ zone.on(:leave) { |_| on_toggle_zone_leave(button: button, state: state) }
14
+ end
15
+
16
+ def bind_toggle_button_events(button:, state:)
17
+ button.on(:enter) { |_| on_toggle_button_enter(button: button, state: state) }
18
+ button.on(:leave) { |_| on_toggle_button_leave(button: button, state: state) }
19
+ end
20
+
21
+ def on_toggle_zone_enter(button:, state:)
22
+ state[:zone_hovered] = true
23
+ cancel_toggle_hide(state)
24
+ button.show
25
+ button.raise
26
+ end
27
+
28
+ def on_toggle_zone_leave(button:, state:)
29
+ state[:zone_hovered] = false
30
+ schedule_toggle_hide(button: button, state: state)
31
+ end
32
+
33
+ def on_toggle_button_enter(button:, state:)
34
+ state[:button_hovered] = true
35
+ cancel_toggle_hide(state)
36
+ button.raise
37
+ end
38
+
39
+ def on_toggle_button_leave(button:, state:)
40
+ state[:button_hovered] = false
41
+ schedule_toggle_hide(button: button, state: state)
42
+ end
43
+
44
+ def hide_toggle_button_unless_hovered(button:, state:)
45
+ show = state[:button_hovered] || state[:zone_hovered]
46
+ button.set_visible(show)
47
+ end
48
+
49
+ def schedule_toggle_hide(button:, state:)
50
+ timer = state[:hide_timer] ||= build_toggle_hide_timer(button, state)
51
+ timer.stop
52
+ timer.start
53
+ end
54
+
55
+ def cancel_toggle_hide(state)
56
+ timer = state[:hide_timer]
57
+ timer&.stop
58
+ end
59
+
60
+ def build_toggle_hide_timer(button, state)
61
+ QTimer.new(button).tap do |timer|
62
+ timer.set_interval(SIDEBAR_TOGGLE_HIDE_DELAY_MS)
63
+ timer.connect('timeout') do |_|
64
+ timer.stop
65
+ hide_toggle_button_unless_hovered(button: button, state: state)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QTimetrap
4
+ module Views
5
+ # Coordinate helpers for splitter toggle affordance.
6
+ module MainWindowSplitterToggleLayoutHelpers
7
+ SIDEBAR_TOGGLE_W = 20
8
+ SIDEBAR_TOGGLE_H = 56
9
+
10
+ private
11
+
12
+ def toggle_x(splitter:, sidebar_widget:)
13
+ sidebar_width = sidebar_widget.is_visible ? sidebar_widget.width : 0
14
+ splitter.x + [sidebar_width - (SIDEBAR_TOGGLE_W / 2), 0].max
15
+ end
16
+
17
+ def toggle_y(splitter_height:)
18
+ y_center = ((splitter_height - SIDEBAR_TOGGLE_H) / 2)
19
+ max_y = [splitter_height - SIDEBAR_TOGGLE_H - 8, 8].max
20
+ y_center.clamp(8, max_y)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QTimetrap
4
+ module Views
5
+ # UI helper methods extracted from MainWindow runtime logic.
6
+ module MainWindowUiHelpers
7
+ private
8
+
9
+ def next_theme_name
10
+ themes = self.class::THEMES
11
+ current = themes.index(theme.name) || 0
12
+ themes[(current + 1) % themes.length]
13
+ end
14
+
15
+ def set_window_icon
16
+ WindowIconLoader.new(window: window, root: Application.root).apply
17
+ rescue StandardError => e
18
+ warn("[qtimetrap] icon load failed: #{e.class}: #{e.message}")
19
+ end
20
+
21
+ def extract_event_value(event, key)
22
+ return unless event.respond_to?(:[])
23
+
24
+ value = event[key]
25
+ return value.to_i if value
26
+
27
+ nil
28
+ end
29
+
30
+ def build_base_window
31
+ QWidget.new do |widget|
32
+ widget.set_window_title('QTimetrap')
33
+ widget.set_object_name('main_window')
34
+ widget.set_geometry(40, 40, self.class::WINDOW_W, self.class::WINDOW_H)
35
+ end
36
+ end
37
+
38
+ def apply_theme
39
+ Application.configuration.theme_name = theme.name
40
+ window.set_style_sheet(theme.application_stylesheet)
41
+ settings_store.write_theme_name(theme.name)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QTimetrap
4
+ module Views
5
+ # Loads the app icon with SVG primary source and PNG fallback.
6
+ class WindowIconLoader
7
+ def initialize(window:, root:)
8
+ @window = window
9
+ @root = root
10
+ end
11
+
12
+ def apply
13
+ candidates = icon_candidates
14
+ return if candidates.empty?
15
+
16
+ icon = QIcon.new(candidates.first)
17
+ add_fallbacks(icon, candidates.drop(1))
18
+ window.set_window_icon(icon)
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :window, :root
24
+
25
+ def icon_candidates
26
+ icons_dir = File.join(root, 'app', 'assets', 'icons')
27
+ svg_path = File.join(icons_dir, 'qtimetrap-icon.svg')
28
+ png_fallback = File.join(icons_dir, 'qtimetrap-icon-256.png')
29
+ [svg_path, png_fallback].select { |path| File.exist?(path) }
30
+ end
31
+
32
+ def add_fallbacks(icon, paths)
33
+ paths.each { |path| icon.add_file(path) }
34
+ end
35
+ end
36
+ end
37
+ end
data/bin/qtimetrap ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
5
+ require 'qtimetrap'
6
+
7
+ QTimetrap::CLI.start(ARGV)
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QTimetrap
4
+ # Bootstraps app runtime, configuration, loader, and Qt application object.
5
+ module Application
6
+ module_function
7
+
8
+ def boot!
9
+ setup_qt
10
+ loader.setup
11
+ load_environment!
12
+ load_initializers!
13
+ load_persisted_configuration!
14
+ qt_app
15
+ end
16
+
17
+ def root
18
+ @root ||= File.expand_path('..', __dir__)
19
+ end
20
+
21
+ def environment
22
+ ENV.fetch('QTIMETRAP_ENV', 'development')
23
+ end
24
+
25
+ def configuration
26
+ @configuration ||= Configuration.new(environment: environment)
27
+ end
28
+
29
+ def configure
30
+ yield(configuration)
31
+ end
32
+
33
+ def container
34
+ @container ||= Container.new(config: configuration)
35
+ end
36
+
37
+ def loader
38
+ @loader ||= Zeitwerk::Loader.new.tap do |autoload|
39
+ autoload.push_dir(File.join(root, 'app'), namespace: QTimetrap)
40
+ autoload.collapse(File.join(root, 'app', 'components'))
41
+ autoload.enable_reloading if configuration.enable_reloading
42
+ end
43
+ end
44
+
45
+ def setup_qt
46
+ return if @qt_app
47
+
48
+ @qt_app = QApplication.new(0, [])
49
+ apply_qt_identity!
50
+ rescue StandardError
51
+ @qt_app ||= nil
52
+ end
53
+
54
+ def qt_app
55
+ @qt_app
56
+ end
57
+
58
+ def load_environment!
59
+ path = File.join(root, 'config', 'environments', "#{environment}.rb")
60
+ require path if File.exist?(path)
61
+ end
62
+
63
+ def load_initializers!
64
+ Dir[File.join(root, 'config', 'initializers', '*.rb')].each do |file|
65
+ require file
66
+ end
67
+ end
68
+
69
+ def load_persisted_configuration!
70
+ return if env_theme_name?
71
+
72
+ persisted_theme = Services::SettingsStore.new.read_theme_name
73
+ configuration.theme_name = persisted_theme if persisted_theme
74
+ end
75
+
76
+ def env_theme_name?
77
+ ENV.fetch('QTIMETRAP_THEME', '').strip != ''
78
+ end
79
+
80
+ def apply_qt_identity!
81
+ QApplication.set_application_name('qtimetrap')
82
+ QApplication.set_desktop_file_name('qtimetrap')
83
+ QApplication.set_application_display_name('QTimetrap')
84
+ QApplication.set_organization_name('mveynberg')
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ QTimetrap::Application.configure do |config|
4
+ config.enable_reloading = ENV['QTIMETRAP_RELOAD'] == '1'
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ QTimetrap::Application.configure do |config|
4
+ config.enable_reloading = false
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ QTimetrap::Application.configure do |config|
4
+ config.theme_name = ENV.fetch('QTIMETRAP_THEME', config.theme_name)
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ QTimetrap::Application.configure do |config|
4
+ config.timetrap_bin = ENV.fetch('TIMETRAP_BIN', config.timetrap_bin)
5
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QTimetrap
4
+ # Mutable runtime configuration used during application boot.
5
+ class Configuration
6
+ attr_accessor :environment, :theme_name, :timetrap_bin, :enable_reloading
7
+
8
+ def initialize(environment: 'development')
9
+ @environment = environment
10
+ @theme_name = 'light'
11
+ @timetrap_bin = 't'
12
+ @enable_reloading = environment == 'development'
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QTimetrap
4
+ # Tiny DI container that registers app services and memoized singletons.
5
+ class Container
6
+ def initialize(config:)
7
+ @config = config
8
+ @providers = {}
9
+ @memoized = {}
10
+ register_defaults
11
+ end
12
+
13
+ def register(key, &provider)
14
+ providers[key.to_sym] = provider
15
+ end
16
+
17
+ def fetch(key)
18
+ name = key.to_sym
19
+ return memoized[name] if memoized.key?(name)
20
+
21
+ provider = providers.fetch(name)
22
+ memoized[name] = provider.call
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :config, :providers, :memoized
28
+
29
+ def register_defaults
30
+ register_core
31
+ register_ui
32
+ end
33
+
34
+ def register_core
35
+ register(:theme) { Styles::Theme.new(name: config.theme_name, root: Application.root) }
36
+ register(:settings_store) { Services::SettingsStore.new }
37
+ register(:archived_entries_store) { Services::ArchivedEntriesStore.new }
38
+ register(:timetrap_gateway) { Services::TimetrapGateway.new(bin: config.timetrap_bin) }
39
+ register(:main_view_model) do
40
+ ViewModels::MainViewModel.new(
41
+ gateway: fetch(:timetrap_gateway),
42
+ archived_entries_store: fetch(:archived_entries_store)
43
+ )
44
+ end
45
+ end
46
+
47
+ def register_ui
48
+ register(:main_window) do
49
+ Views::MainWindow.new(
50
+ view_model: fetch(:main_view_model),
51
+ theme: fetch(:theme),
52
+ settings_store: fetch(:settings_store)
53
+ )
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QTimetrap
4
+ VERSION = '0.1.1'
5
+ end
data/lib/qtimetrap.rb ADDED
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'qt'
4
+ require 'zeitwerk'
5
+
6
+ require_relative 'qtimetrap/version'
7
+ require_relative 'qtimetrap/configuration'
8
+ require_relative 'qtimetrap/container'
9
+ require_relative '../config/application'
10
+
11
+ module QTimetrap
12
+ # CLI entrypoint for launching the Qt desktop application.
13
+ class CLI
14
+ def self.start(_argv = [])
15
+ app = QTimetrap::Application.boot!
16
+ main_window = QTimetrap::Application.container.fetch(:main_window)
17
+ previous_int = Signal.trap('INT') { main_window.request_shutdown }
18
+ main_window.show
19
+ app.exec
20
+ ensure
21
+ Signal.trap('INT', previous_int) if defined?(previous_int) && previous_int
22
+ app&.dispose
23
+ end
24
+ end
25
+ end
metadata ADDED
@@ -0,0 +1,207 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: qtimetrap
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Maksim Veynberg
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: qt
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '0.1'
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: 0.1.4
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: '0.1'
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: 0.1.4
32
+ - !ruby/object:Gem::Dependency
33
+ name: zeitwerk
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - "~>"
37
+ - !ruby/object:Gem::Version
38
+ version: '2.6'
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - "~>"
44
+ - !ruby/object:Gem::Version
45
+ version: '2.6'
46
+ description: MVVM Ruby desktop app for Timetrap built with Qt and Zeitwerk.
47
+ email:
48
+ - mv@cj264.ru
49
+ executables:
50
+ - qtimetrap
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - LICENSE
55
+ - README.md
56
+ - Rakefile
57
+ - app/assets/icons/qtimetrap-icon-128.png
58
+ - app/assets/icons/qtimetrap-icon-256.png
59
+ - app/assets/icons/qtimetrap-icon-512.png
60
+ - app/assets/icons/qtimetrap-icon.svg
61
+ - app/components/entries/branch_hierarchy_helpers.rb
62
+ - app/components/entries/leaf_archive_helpers.rb
63
+ - app/components/entries/leaf_note_helpers.rb
64
+ - app/components/entries/leaf_task_helpers.rb
65
+ - app/components/entries/leaf_time_helpers.rb
66
+ - app/components/entries/list_component.rb
67
+ - app/components/entries/list_host_helpers.rb
68
+ - app/components/entries/list_state_helpers.rb
69
+ - app/components/entries/node_presentation_helpers.rb
70
+ - app/components/entries/render_helpers.rb
71
+ - app/components/entries/tree_helpers.rb
72
+ - app/components/entries/tree_toolbar_helpers.rb
73
+ - app/components/project_sidebar/archive_toggle_helpers.rb
74
+ - app/components/project_sidebar/component.rb
75
+ - app/components/project_sidebar/logo_helpers.rb
76
+ - app/components/project_sidebar/project_button_helpers.rb
77
+ - app/components/project_sidebar/project_selection_helpers.rb
78
+ - app/components/project_sidebar/task_helpers.rb
79
+ - app/components/project_sidebar/task_selection_helpers.rb
80
+ - app/components/qt_ui_helpers.rb
81
+ - app/components/tracker_controls/component.rb
82
+ - app/components/tracker_controls/layout_builder.rb
83
+ - app/components/tracker_controls/layout_helpers.rb
84
+ - app/models/null_settings_store.rb
85
+ - app/models/time_entry.rb
86
+ - app/services/archived_entries_store.rb
87
+ - app/services/formatters.rb
88
+ - app/services/settings_store.rb
89
+ - app/services/timetrap_gateway.rb
90
+ - app/services/timetrap_gateway_logger.rb
91
+ - app/services/timetrap_gateway_query_helpers.rb
92
+ - app/services/timetrap_gateway_start_helpers.rb
93
+ - app/services/timetrap_gateway_update_note_helpers.rb
94
+ - app/services/timetrap_gateway_update_task_helpers.rb
95
+ - app/services/timetrap_gateway_update_time_helpers.rb
96
+ - app/styles/theme.rb
97
+ - app/styles/themes/dark/application.qss
98
+ - app/styles/themes/dark/entries_list.qss
99
+ - app/styles/themes/dark/project_sidebar.qss
100
+ - app/styles/themes/dark/snippets/app_background.qss
101
+ - app/styles/themes/dark/snippets/button_ghost.qss
102
+ - app/styles/themes/dark/snippets/button_start.qss
103
+ - app/styles/themes/dark/snippets/button_stop.qss
104
+ - app/styles/themes/dark/snippets/entries_host.qss
105
+ - app/styles/themes/dark/snippets/entries_scroll.qss
106
+ - app/styles/themes/dark/snippets/entry_row_day.qss
107
+ - app/styles/themes/dark/snippets/entry_row_detail.qss
108
+ - app/styles/themes/dark/snippets/entry_row_project.qss
109
+ - app/styles/themes/dark/snippets/project_button.qss
110
+ - app/styles/themes/dark/snippets/project_button_active.qss
111
+ - app/styles/themes/dark/snippets/project_sidebar_heading.qss
112
+ - app/styles/themes/dark/snippets/project_sidebar_logo.qss
113
+ - app/styles/themes/dark/snippets/project_sidebar_panel.qss
114
+ - app/styles/themes/dark/snippets/tracker_clock.qss
115
+ - app/styles/themes/dark/snippets/tracker_input.qss
116
+ - app/styles/themes/dark/snippets/tracker_project_label.qss
117
+ - app/styles/themes/dark/snippets/tracker_row.qss
118
+ - app/styles/themes/dark/snippets/tracker_summary.qss
119
+ - app/styles/themes/dark/snippets/tracker_timer.qss
120
+ - app/styles/themes/dark/snippets/tracker_title.qss
121
+ - app/styles/themes/dark/snippets/tracker_topbar.qss
122
+ - app/styles/themes/dark/tracker_controls.qss
123
+ - app/styles/themes/light/application.qss
124
+ - app/styles/themes/light/entries_list.qss
125
+ - app/styles/themes/light/project_sidebar.qss
126
+ - app/styles/themes/light/snippets/app_background.qss
127
+ - app/styles/themes/light/snippets/button_ghost.qss
128
+ - app/styles/themes/light/snippets/button_start.qss
129
+ - app/styles/themes/light/snippets/button_stop.qss
130
+ - app/styles/themes/light/snippets/entries_host.qss
131
+ - app/styles/themes/light/snippets/entries_scroll.qss
132
+ - app/styles/themes/light/snippets/entry_row_day.qss
133
+ - app/styles/themes/light/snippets/entry_row_detail.qss
134
+ - app/styles/themes/light/snippets/entry_row_project.qss
135
+ - app/styles/themes/light/snippets/project_button.qss
136
+ - app/styles/themes/light/snippets/project_button_active.qss
137
+ - app/styles/themes/light/snippets/project_sidebar_heading.qss
138
+ - app/styles/themes/light/snippets/project_sidebar_logo.qss
139
+ - app/styles/themes/light/snippets/project_sidebar_panel.qss
140
+ - app/styles/themes/light/snippets/tracker_clock.qss
141
+ - app/styles/themes/light/snippets/tracker_input.qss
142
+ - app/styles/themes/light/snippets/tracker_project_label.qss
143
+ - app/styles/themes/light/snippets/tracker_row.qss
144
+ - app/styles/themes/light/snippets/tracker_summary.qss
145
+ - app/styles/themes/light/snippets/tracker_timer.qss
146
+ - app/styles/themes/light/snippets/tracker_title.qss
147
+ - app/styles/themes/light/snippets/tracker_topbar.qss
148
+ - app/styles/themes/light/tracker_controls.qss
149
+ - app/view_models/entry_nodes_builder.rb
150
+ - app/view_models/main_view_model.rb
151
+ - app/view_models/main_view_model_archive_mode_helpers.rb
152
+ - app/view_models/main_view_model_entry_note_helpers.rb
153
+ - app/view_models/main_view_model_entry_task_helpers.rb
154
+ - app/view_models/main_view_model_entry_time_helpers.rb
155
+ - app/view_models/main_view_model_sheet_helpers.rb
156
+ - app/view_models/main_view_model_task_filter_helpers.rb
157
+ - app/view_models/main_view_model_time_range_filter_helpers.rb
158
+ - app/views/main_window.rb
159
+ - app/views/main_window_layout_builder.rb
160
+ - app/views/main_window_runtime.rb
161
+ - app/views/main_window_runtime_archive_helpers.rb
162
+ - app/views/main_window_runtime_entry_task_helpers.rb
163
+ - app/views/main_window_runtime_entry_time_helpers.rb
164
+ - app/views/main_window_runtime_key_helpers.rb
165
+ - app/views/main_window_runtime_render_helpers.rb
166
+ - app/views/main_window_splitter_toggle_bootstrap_helpers.rb
167
+ - app/views/main_window_splitter_toggle_helpers.rb
168
+ - app/views/main_window_splitter_toggle_hover_helpers.rb
169
+ - app/views/main_window_splitter_toggle_layout_helpers.rb
170
+ - app/views/main_window_ui_helpers.rb
171
+ - app/views/window_icon_loader.rb
172
+ - bin/qtimetrap
173
+ - config/application.rb
174
+ - config/environments/development.rb
175
+ - config/environments/production.rb
176
+ - config/initializers/theme.rb
177
+ - config/initializers/timetrap.rb
178
+ - lib/qtimetrap.rb
179
+ - lib/qtimetrap/configuration.rb
180
+ - lib/qtimetrap/container.rb
181
+ - lib/qtimetrap/version.rb
182
+ homepage: https://github.com/CyJimmy264/qtimetrap
183
+ licenses:
184
+ - BSD-2-Clause
185
+ metadata:
186
+ source_code_uri: https://github.com/CyJimmy264/qtimetrap
187
+ changelog_uri: https://github.com/CyJimmy264/qtimetrap/releases
188
+ bug_tracker_uri: https://github.com/CyJimmy264/qtimetrap/issues
189
+ rubygems_mfa_required: 'true'
190
+ rdoc_options: []
191
+ require_paths:
192
+ - lib
193
+ required_ruby_version: !ruby/object:Gem::Requirement
194
+ requirements:
195
+ - - ">="
196
+ - !ruby/object:Gem::Version
197
+ version: '3.2'
198
+ required_rubygems_version: !ruby/object:Gem::Requirement
199
+ requirements:
200
+ - - ">="
201
+ - !ruby/object:Gem::Version
202
+ version: '0'
203
+ requirements: []
204
+ rubygems_version: 3.6.7
205
+ specification_version: 4
206
+ summary: Desktop Timetrap UI on Qt
207
+ test_files: []