shoko 0.1.4 → 0.1.5

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 (815) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -0
  3. data/README.md +100 -1
  4. data/Rakefile +80 -1
  5. data/bin/shoko +10 -4
  6. data/bin/start +10 -4
  7. data/docs/architecture/hexagonal-adherence-checklist.md +173 -0
  8. data/lib/{zip.rb → shoko/adapters/book_sources/archive/zip_reader.rb} +46 -32
  9. data/lib/shoko/adapters/book_sources/book_document.rb +177 -0
  10. data/lib/shoko/adapters/book_sources/book_file_probe.rb +24 -0
  11. data/lib/shoko/adapters/book_sources/book_finder/directory_scanner.rb +152 -0
  12. data/lib/shoko/adapters/book_sources/book_finder/scanner_context.rb +30 -0
  13. data/lib/shoko/adapters/book_sources/{epub_finder.rb → book_finder.rb} +62 -44
  14. data/lib/shoko/adapters/book_sources/cache_import_adapter.rb +30 -0
  15. data/lib/shoko/adapters/book_sources/document_loader_adapter.rb +72 -0
  16. data/lib/shoko/adapters/book_sources/document_service.rb +132 -185
  17. data/lib/shoko/adapters/book_sources/download_service.rb +72 -67
  18. data/lib/shoko/adapters/book_sources/epub/epub_importer.rb +266 -0
  19. data/lib/shoko/adapters/book_sources/epub/epub_resource_loader.rb +138 -128
  20. data/lib/shoko/adapters/book_sources/fb2/fb2_importer.rb +336 -0
  21. data/lib/shoko/adapters/book_sources/folder_scanner.rb +92 -0
  22. data/lib/shoko/adapters/book_sources/gutendex_client.rb +136 -125
  23. data/lib/shoko/adapters/book_sources/kindle/kindle_importer.rb +422 -0
  24. data/lib/shoko/adapters/book_sources/library_scanner.rb +137 -101
  25. data/lib/shoko/adapters/book_sources/metadata_reader_adapter.rb +63 -12
  26. data/lib/shoko/adapters/book_sources/pdf/pdf_importer.rb +364 -0
  27. data/lib/shoko/adapters/book_sources/rtf/rtf_importer.rb +303 -0
  28. data/lib/shoko/adapters/input/annotations/mouse_handler.rb +130 -126
  29. data/lib/shoko/adapters/input/cli.rb +439 -0
  30. data/lib/shoko/adapters/input/command_factory.rb +266 -286
  31. data/lib/shoko/adapters/input/commands.rb +74 -46
  32. data/lib/shoko/adapters/input/controllers/annotation_overlay_controller.rb +362 -0
  33. data/lib/shoko/adapters/input/controllers/dependencies/menu_controller_dependencies.rb +134 -0
  34. data/lib/shoko/adapters/input/controllers/dependencies/reader_controller_dependencies.rb +390 -0
  35. data/lib/shoko/adapters/input/controllers/dictionary/constants.rb +80 -0
  36. data/lib/shoko/adapters/input/controllers/dictionary/controller_support.rb +117 -0
  37. data/lib/shoko/adapters/input/controllers/dictionary/display_mode_support.rb +65 -0
  38. data/lib/shoko/adapters/input/controllers/dictionary/index.rb +7 -0
  39. data/lib/shoko/adapters/input/controllers/dictionary/language_pair_support.rb +190 -0
  40. data/lib/shoko/adapters/input/controllers/dictionary/setup_flow/download_support.rb +97 -0
  41. data/lib/shoko/adapters/input/controllers/dictionary/setup_flow/interaction_handlers.rb +106 -0
  42. data/lib/shoko/adapters/input/controllers/dictionary/setup_flow/lookup_flow.rb +106 -0
  43. data/lib/shoko/adapters/input/controllers/dictionary/setup_flow/popup_state_support.rb +99 -0
  44. data/lib/shoko/adapters/input/controllers/dictionary/setup_flow/submission_flow.rb +100 -0
  45. data/lib/shoko/adapters/input/controllers/dictionary/setup_flow_support.rb +26 -0
  46. data/lib/shoko/adapters/input/controllers/dictionary_controller.rb +304 -0
  47. data/lib/shoko/adapters/input/controllers/in_book_search_controller.rb +202 -0
  48. data/lib/shoko/adapters/input/controllers/menu/actions/dictionary_actions.rb +121 -0
  49. data/lib/shoko/adapters/input/controllers/menu/actions/download_actions.rb +74 -0
  50. data/lib/shoko/adapters/input/controllers/menu/actions/lifecycle_actions.rb +157 -0
  51. data/lib/shoko/adapters/input/controllers/menu/actions/navigation_actions.rb +141 -0
  52. data/lib/shoko/adapters/input/controllers/menu/actions/settings_actions.rb +128 -0
  53. data/lib/shoko/adapters/input/controllers/menu/controller.rb +430 -0
  54. data/lib/shoko/adapters/input/controllers/menu/input_controller.rb +210 -0
  55. data/lib/shoko/adapters/input/controllers/menu/intent_executor_bridge.rb +115 -0
  56. data/lib/shoko/adapters/input/controllers/menu/menu_workflow_bridges.rb +100 -0
  57. data/lib/shoko/adapters/input/controllers/menu/reader_launch_bridges.rb +78 -0
  58. data/lib/shoko/adapters/input/controllers/menu/state_controller.rb +123 -0
  59. data/lib/shoko/adapters/input/controllers/mouseable_reader.rb +243 -0
  60. data/lib/shoko/adapters/input/controllers/reader/event_loop.rb +100 -0
  61. data/lib/shoko/adapters/input/controllers/reader/input_router.rb +68 -0
  62. data/lib/shoko/adapters/input/controllers/reader/intent_executor_bridge.rb +107 -0
  63. data/lib/shoko/adapters/input/controllers/reader/lifecycle_runner.rb +117 -0
  64. data/lib/shoko/adapters/input/controllers/reader/render_metrics.rb +54 -0
  65. data/lib/shoko/adapters/input/controllers/reader/render_requester_bridge.rb +35 -0
  66. data/lib/shoko/adapters/input/controllers/reader/startup_loader.rb +61 -0
  67. data/lib/shoko/adapters/input/controllers/reader/startup_sequence.rb +64 -0
  68. data/lib/shoko/adapters/input/controllers/reader_controller.rb +519 -0
  69. data/lib/shoko/adapters/input/controllers/selection_mouse_handler.rb +212 -0
  70. data/lib/shoko/adapters/input/controllers/sidebar/anchor_resolver.rb +88 -0
  71. data/lib/shoko/adapters/input/controllers/sidebar/tab_state_orchestrator.rb +151 -0
  72. data/lib/shoko/adapters/input/controllers/sidebar/toc_facade.rb +71 -0
  73. data/lib/shoko/adapters/input/controllers/sidebar/toc_navigation.rb +77 -0
  74. data/lib/shoko/adapters/input/controllers/sidebar_controller.rb +309 -0
  75. data/lib/shoko/adapters/input/controllers/sidebar_mouse_handler.rb +201 -0
  76. data/lib/shoko/adapters/input/controllers/state_controller/annotation_actions.rb +119 -0
  77. data/lib/shoko/adapters/input/controllers/state_controller/bookmark_actions.rb +154 -0
  78. data/lib/shoko/adapters/input/controllers/state_controller/progress_actions.rb +149 -0
  79. data/lib/shoko/adapters/input/controllers/state_controller.rb +97 -0
  80. data/lib/shoko/adapters/input/controllers/support/message_notifier.rb +29 -0
  81. data/lib/shoko/adapters/input/controllers/ui_controller/delegation_facade.rb +325 -0
  82. data/lib/shoko/adapters/input/controllers/ui_controller/mode_switching.rb +62 -0
  83. data/lib/shoko/adapters/input/controllers/ui_controller/popup_actions.rb +84 -0
  84. data/lib/shoko/adapters/input/controllers/ui_controller.rb +109 -0
  85. data/lib/shoko/adapters/input/dispatcher.rb +50 -48
  86. data/lib/shoko/adapters/input/input_system_factory_adapter.rb +21 -16
  87. data/lib/shoko/adapters/input/key_classifier_adapter.rb +44 -46
  88. data/lib/shoko/adapters/input/reader_input_controller.rb +342 -0
  89. data/lib/shoko/adapters/input/validators/file_path_validator.rb +69 -62
  90. data/lib/shoko/adapters/input/validators/terminal_size_validator.rb +64 -59
  91. data/lib/shoko/adapters/monitoring/logger_adapter.rb +50 -8
  92. data/lib/shoko/adapters/monitoring/perf_tracer.rb +145 -138
  93. data/lib/shoko/adapters/monitoring/performance_monitor.rb +98 -96
  94. data/lib/shoko/adapters/output/clipboard/clipboard_service.rb +3 -3
  95. data/lib/shoko/adapters/output/formatting/formatting_service/line_assembler/image_builder.rb +135 -135
  96. data/lib/shoko/adapters/output/formatting/formatting_service/line_assembler/table_renderer.rb +304 -300
  97. data/lib/shoko/adapters/output/formatting/formatting_service/line_assembler/text_wrapper.rb +172 -108
  98. data/lib/shoko/adapters/output/formatting/formatting_service/line_assembler/tokenizer.rb +210 -71
  99. data/lib/shoko/adapters/output/formatting/formatting_service/line_assembler.rb +270 -215
  100. data/lib/shoko/adapters/output/formatting/formatting_service/plain_lines_builder.rb +49 -41
  101. data/lib/shoko/adapters/output/formatting/formatting_service.rb +276 -253
  102. data/lib/shoko/adapters/output/formatting/wrapped_lines_provider_adapter.rb +43 -0
  103. data/lib/shoko/adapters/output/formatting/wrapping_service.rb +75 -34
  104. data/lib/shoko/adapters/output/instrumentation_service.rb +66 -49
  105. data/lib/shoko/adapters/output/kitty/display_capabilities.rb +2 -2
  106. data/lib/shoko/adapters/output/kitty/image_transcoder.rb +53 -49
  107. data/lib/shoko/adapters/output/kitty/kitty_graphics.rb +96 -92
  108. data/lib/shoko/adapters/output/kitty/kitty_image_renderer.rb +215 -231
  109. data/lib/shoko/adapters/output/kitty/kitty_unicode_placeholders.rb +4 -129
  110. data/lib/shoko/adapters/output/kitty/resource_loader.rb +34 -0
  111. data/lib/shoko/adapters/output/layout/layout_metrics_adapter.rb +68 -0
  112. data/lib/shoko/adapters/output/notification_service.rb +41 -54
  113. data/lib/shoko/adapters/output/terminal/buffer.rb +348 -259
  114. data/lib/shoko/adapters/output/terminal/cli_progress_renderer.rb +104 -0
  115. data/lib/shoko/adapters/output/terminal/constants/terminal_defaults.rb +9 -5
  116. data/lib/shoko/adapters/output/terminal/input/decoder.rb +364 -362
  117. data/lib/shoko/adapters/output/terminal/input.rb +195 -190
  118. data/lib/shoko/adapters/output/terminal/null_runtime_config.rb +23 -0
  119. data/lib/shoko/adapters/output/terminal/output.rb +42 -97
  120. data/lib/shoko/adapters/output/terminal/terminal.rb +199 -183
  121. data/lib/shoko/adapters/output/terminal/terminal_sanitizer.rb +7 -232
  122. data/lib/shoko/adapters/output/terminal/terminal_service.rb +144 -143
  123. data/lib/shoko/adapters/output/terminal/terminal_session_adapter.rb +32 -0
  124. data/lib/shoko/adapters/output/terminal/text_metrics.rb +4 -262
  125. data/lib/shoko/adapters/output/terminal/text_metrics_port_adapter.rb +41 -0
  126. data/lib/shoko/adapters/output/terminal/text_sanitizer_adapter.rb +13 -9
  127. data/lib/shoko/adapters/output/terminal_capabilities_adapter.rb +2 -2
  128. data/lib/shoko/adapters/runtime/app_mode_runner_adapter.rb +27 -0
  129. data/lib/shoko/adapters/runtime/env_runtime_config_adapter.rb +169 -0
  130. data/lib/shoko/adapters/runtime/monotonic_clock_adapter.rb +18 -0
  131. data/lib/shoko/adapters/runtime/null_runtime_config.rb +21 -0
  132. data/lib/shoko/adapters/runtime/process_control_adapter.rb +18 -0
  133. data/lib/shoko/adapters/runtime/rexml_security_limits_adapter.rb +35 -0
  134. data/lib/shoko/adapters/runtime/session_state/actions/base_action.rb +28 -0
  135. data/lib/shoko/adapters/runtime/session_state/actions/quit_to_menu_action.rb +20 -0
  136. data/lib/shoko/adapters/runtime/session_state/actions/switch_reader_mode_action.rb +26 -0
  137. data/lib/shoko/adapters/runtime/session_state/actions/toggle_view_mode_action.rb +35 -0
  138. data/lib/shoko/adapters/runtime/session_state/actions/update_config_action.rb +30 -0
  139. data/lib/shoko/adapters/runtime/session_state/actions/update_field_helpers.rb +30 -0
  140. data/lib/shoko/adapters/runtime/session_state/actions/update_menu_action.rb +21 -0
  141. data/lib/shoko/adapters/runtime/session_state/actions/update_message_action.rb +26 -0
  142. data/lib/shoko/adapters/runtime/session_state/actions/update_pagination_state_action.rb +23 -0
  143. data/lib/shoko/adapters/runtime/session_state/actions/update_reader_meta_action.rb +23 -0
  144. data/lib/shoko/adapters/runtime/session_state/actions/update_rendered_lines_action.rb +34 -0
  145. data/lib/shoko/adapters/runtime/session_state/actions/update_sidebar_action.rb +42 -0
  146. data/lib/shoko/adapters/runtime/session_state/actions/update_state_action.rb +76 -0
  147. data/lib/shoko/adapters/runtime/session_state/actions/update_ui_loading_action.rb +23 -0
  148. data/lib/shoko/adapters/runtime/session_state/config_reader_adapter.rb +82 -0
  149. data/lib/shoko/adapters/runtime/session_state/event_bus.rb +85 -0
  150. data/lib/shoko/adapters/runtime/session_state/event_publisher_adapter.rb +24 -0
  151. data/lib/shoko/adapters/runtime/session_state/menu_launch_state_adapter.rb +32 -0
  152. data/lib/shoko/adapters/runtime/session_state/menu_state_reader_adapter.rb +246 -0
  153. data/lib/shoko/adapters/runtime/session_state/menu_state_writer_adapter.rb +157 -0
  154. data/lib/shoko/adapters/runtime/session_state/notification_writer_adapter.rb +39 -0
  155. data/lib/shoko/adapters/runtime/session_state/observer_registry_adapter.rb +32 -0
  156. data/lib/shoko/adapters/runtime/session_state/observer_state_store.rb +129 -0
  157. data/lib/shoko/adapters/runtime/session_state/reader_launch_state_adapter.rb +45 -0
  158. data/lib/shoko/adapters/runtime/session_state/reader_state_reader_adapter.rb +162 -0
  159. data/lib/shoko/adapters/runtime/session_state/render_state_writer_adapter.rb +58 -0
  160. data/lib/shoko/adapters/runtime/session_state/rendered_content_reader_adapter.rb +29 -0
  161. data/lib/shoko/adapters/runtime/session_state/selectors/config_selectors.rb +82 -0
  162. data/lib/shoko/adapters/runtime/session_state/selectors/menu_selectors.rb +90 -0
  163. data/lib/shoko/adapters/runtime/session_state/selectors/reader_selectors.rb +191 -0
  164. data/lib/shoko/adapters/runtime/session_state/sidebar_state_reader_adapter.rb +67 -0
  165. data/lib/shoko/adapters/runtime/session_state/state_store/change_event_builder.rb +35 -0
  166. data/lib/shoko/adapters/runtime/session_state/state_store/config_persistence.rb +91 -0
  167. data/lib/shoko/adapters/runtime/session_state/state_store/initial_state_builder.rb +113 -0
  168. data/lib/shoko/adapters/runtime/session_state/state_store/transition_validator.rb +83 -0
  169. data/lib/shoko/adapters/runtime/session_state/state_store.rb +328 -0
  170. data/lib/shoko/adapters/runtime/session_state/state_writer_adapter.rb +134 -0
  171. data/lib/shoko/adapters/runtime/session_state/ui_state_reader_adapter.rb +50 -0
  172. data/lib/shoko/adapters/runtime/system_wall_clock_adapter.rb +18 -0
  173. data/lib/shoko/adapters/runtime/uuid_generator_adapter.rb +19 -0
  174. data/lib/shoko/adapters/storage/atomic_file_writer.rb +52 -24
  175. data/lib/shoko/adapters/storage/background_worker.rb +49 -46
  176. data/lib/shoko/adapters/storage/background_worker_builder_adapter.rb +24 -0
  177. data/lib/shoko/adapters/storage/book_cache_pipeline/cache_integrity_checker.rb +36 -0
  178. data/lib/shoko/adapters/storage/book_cache_pipeline/cache_session.rb +150 -0
  179. data/lib/shoko/adapters/storage/book_cache_pipeline/cache_status.rb +34 -0
  180. data/lib/shoko/adapters/storage/book_cache_pipeline/fingerprint_filter.rb +52 -0
  181. data/lib/shoko/adapters/storage/book_cache_pipeline/load_error_handler.rb +25 -0
  182. data/lib/shoko/adapters/storage/book_cache_pipeline/manifest_row.rb +74 -0
  183. data/lib/shoko/adapters/storage/book_cache_pipeline/manifest_sha_finder.rb +169 -0
  184. data/lib/shoko/adapters/storage/book_cache_pipeline/payload_context.rb +21 -0
  185. data/lib/shoko/adapters/storage/book_cache_pipeline/pointer_file_ensurer.rb +49 -0
  186. data/lib/shoko/adapters/storage/book_cache_pipeline.rb +119 -601
  187. data/lib/shoko/adapters/storage/book_cache_pipeline_factory_adapter.rb +23 -0
  188. data/lib/shoko/adapters/storage/cache/epub/memory_cache.rb +73 -71
  189. data/lib/shoko/adapters/storage/cache/epub/persistence.rb +124 -122
  190. data/lib/shoko/adapters/storage/cache/epub/serializer/deserialize.rb +195 -191
  191. data/lib/shoko/adapters/storage/cache/epub/serializer/helpers.rb +52 -47
  192. data/lib/shoko/adapters/storage/cache/epub/serializer/serialize.rb +65 -62
  193. data/lib/shoko/adapters/storage/cache/epub/source_reference.rb +43 -41
  194. data/lib/shoko/adapters/storage/cache_availability_adapter.rb +18 -13
  195. data/lib/shoko/adapters/storage/cache_manager_adapter.rb +17 -15
  196. data/lib/shoko/adapters/storage/cache_paths.rb +15 -13
  197. data/lib/shoko/adapters/storage/cache_pointer_manager.rb +42 -40
  198. data/lib/shoko/adapters/storage/cache_pointer_resolver.rb +15 -5
  199. data/lib/shoko/adapters/storage/config_paths.rb +22 -20
  200. data/lib/shoko/adapters/storage/config_storage_adapter.rb +14 -8
  201. data/lib/shoko/adapters/storage/data_cleanup_adapter.rb +83 -0
  202. data/lib/shoko/adapters/storage/dictionary_availability_adapter.rb +13 -23
  203. data/lib/shoko/adapters/storage/dictionary_catalog_service.rb +13 -5
  204. data/lib/shoko/adapters/storage/dictionary_storage_adapter.rb +84 -0
  205. data/lib/shoko/adapters/storage/epub_cache.rb +133 -150
  206. data/lib/shoko/adapters/storage/file_probe_adapter.rb +26 -0
  207. data/lib/shoko/adapters/storage/file_writer_service.rb +27 -27
  208. data/lib/shoko/adapters/storage/json_cache_store/chapters.rb +104 -102
  209. data/lib/shoko/adapters/storage/json_cache_store/layouts.rb +52 -50
  210. data/lib/shoko/adapters/storage/json_cache_store/manifest.rb +125 -30
  211. data/lib/shoko/adapters/storage/json_cache_store/payload_helpers.rb +109 -106
  212. data/lib/shoko/adapters/storage/json_cache_store/resources.rb +62 -60
  213. data/lib/shoko/adapters/storage/json_cache_store.rb +138 -137
  214. data/lib/shoko/adapters/storage/lazy_file_string.rb +39 -50
  215. data/lib/shoko/adapters/storage/pagination_cache.rb +107 -96
  216. data/lib/shoko/adapters/storage/path_ops_adapter.rb +30 -0
  217. data/lib/shoko/adapters/storage/recent_files.rb +98 -62
  218. data/lib/shoko/adapters/storage/recent_files_repository.rb +2 -2
  219. data/lib/shoko/adapters/storage/repositories/annotation_repository.rb +149 -162
  220. data/lib/shoko/adapters/storage/repositories/base_repository.rb +70 -58
  221. data/lib/shoko/adapters/storage/repositories/bookmark_repository.rb +113 -109
  222. data/lib/shoko/adapters/storage/repositories/cached_library_repository.rb +116 -114
  223. data/lib/shoko/adapters/storage/repositories/progress_repository.rb +139 -152
  224. data/lib/shoko/adapters/storage/repositories/storage/annotation_file_store.rb +80 -83
  225. data/lib/shoko/adapters/storage/repositories/storage/base_file_store.rb +24 -18
  226. data/lib/shoko/adapters/storage/repositories/storage/bookmark_file_store.rb +75 -67
  227. data/lib/shoko/adapters/storage/repositories/storage/file_store_utils.rb +13 -9
  228. data/lib/shoko/adapters/storage/repositories/storage/progress_file_store.rb +31 -25
  229. data/lib/shoko/adapters/storage/sqlite_dictionary_adapter.rb +29 -35
  230. data/lib/shoko/adapters/support/lifecycle_helpers.rb +73 -0
  231. data/lib/shoko/adapters/ui/builders/page_setup_builder.rb +51 -0
  232. data/lib/shoko/adapters/ui/component_factory.rb +103 -0
  233. data/lib/shoko/adapters/ui/components/annotation_editor_overlay/footer_renderer.rb +84 -0
  234. data/lib/shoko/adapters/ui/components/annotation_editor_overlay/geometry.rb +65 -0
  235. data/lib/shoko/adapters/ui/components/annotation_editor_overlay/note_renderer.rb +91 -0
  236. data/lib/shoko/adapters/ui/components/annotation_editor_overlay_component.rb +415 -0
  237. data/lib/shoko/adapters/ui/components/annotations_overlay/list_renderer.rb +148 -0
  238. data/lib/shoko/adapters/ui/components/annotations_overlay_component.rb +210 -0
  239. data/lib/shoko/adapters/ui/components/base_component.rb +118 -0
  240. data/lib/shoko/adapters/ui/components/content_component.rb +67 -0
  241. data/lib/shoko/adapters/ui/components/dictionary/entry_formatter.rb +232 -0
  242. data/lib/shoko/adapters/ui/components/dictionary_panel_component.rb +290 -0
  243. data/lib/shoko/adapters/ui/components/dictionary_popup/results_flow.rb +122 -0
  244. data/lib/shoko/adapters/ui/components/dictionary_popup/setup_flow.rb +479 -0
  245. data/lib/shoko/adapters/ui/components/dictionary_popup_component.rb +280 -0
  246. data/lib/shoko/adapters/ui/components/enhanced_popup_menu.rb +345 -0
  247. data/lib/shoko/adapters/ui/components/footer_component.rb +126 -0
  248. data/lib/shoko/adapters/ui/components/header_component.rb +48 -0
  249. data/lib/shoko/adapters/ui/components/in_book_search_popup_component.rb +509 -0
  250. data/lib/shoko/adapters/ui/components/layouts/horizontal.rb +67 -0
  251. data/lib/shoko/adapters/ui/components/layouts/horizontal_three.rb +94 -0
  252. data/lib/shoko/adapters/ui/components/layouts/vertical.rb +77 -0
  253. data/lib/shoko/adapters/ui/components/main_menu_component.rb +130 -0
  254. data/lib/shoko/adapters/ui/components/menu_design/frame_renderer.rb +93 -0
  255. data/lib/shoko/adapters/ui/components/menu_design/icon_set.rb +69 -0
  256. data/lib/shoko/adapters/ui/components/menu_design/layout.rb +33 -0
  257. data/lib/shoko/adapters/ui/components/menu_design/progress_renderer.rb +36 -0
  258. data/lib/shoko/adapters/ui/components/menu_design/search_field_renderer.rb +41 -0
  259. data/lib/shoko/adapters/ui/components/menu_design/status_renderer.rb +49 -0
  260. data/lib/shoko/adapters/ui/components/menu_design/table_renderer.rb +59 -0
  261. data/lib/shoko/adapters/ui/components/menu_design/theme_tokens.rb +73 -0
  262. data/lib/shoko/adapters/ui/components/rect.rb +19 -0
  263. data/lib/shoko/adapters/ui/components/render_style.rb +125 -0
  264. data/lib/shoko/adapters/ui/components/screen_component.rb +28 -0
  265. data/lib/shoko/adapters/ui/components/screens/annotation_detail_screen_component.rb +193 -0
  266. data/lib/shoko/adapters/ui/components/screens/annotation_edit_screen_component.rb +298 -0
  267. data/lib/shoko/adapters/ui/components/screens/annotation_editor_screen_component.rb +268 -0
  268. data/lib/shoko/adapters/ui/components/screens/annotation_rendering_helpers.rb +462 -0
  269. data/lib/shoko/adapters/ui/components/screens/annotations_screen_component.rb +398 -0
  270. data/lib/shoko/adapters/ui/components/screens/base_screen_component.rb +53 -0
  271. data/lib/shoko/adapters/ui/components/screens/browse_screen_component.rb +384 -0
  272. data/lib/shoko/adapters/ui/components/screens/dictionary_settings_screen_component.rb +501 -0
  273. data/lib/shoko/adapters/ui/components/screens/download_books_screen_component.rb +352 -0
  274. data/lib/shoko/adapters/ui/components/screens/library_screen_component.rb +383 -0
  275. data/lib/shoko/adapters/ui/components/screens/loading_overlay_component.rb +63 -0
  276. data/lib/shoko/adapters/ui/components/screens/menu_screen_component.rb +103 -0
  277. data/lib/shoko/adapters/ui/components/screens/settings_screen_component.rb +324 -0
  278. data/lib/shoko/adapters/ui/components/sidebar/annotations_tab_renderer.rb +170 -0
  279. data/lib/shoko/adapters/ui/components/sidebar/bookmarks_tab_renderer.rb +160 -0
  280. data/lib/shoko/adapters/ui/components/sidebar/tab_header_component.rb +168 -0
  281. data/lib/shoko/adapters/ui/components/sidebar/toc/constants.rb +23 -0
  282. data/lib/shoko/adapters/ui/components/sidebar/toc/context.rb +101 -0
  283. data/lib/shoko/adapters/ui/components/sidebar/toc/document_model.rb +59 -0
  284. data/lib/shoko/adapters/ui/components/sidebar/toc/entries_calculator.rb +99 -0
  285. data/lib/shoko/adapters/ui/components/sidebar/toc/entry_rendering.rb +185 -0
  286. data/lib/shoko/adapters/ui/components/sidebar/toc/index.rb +11 -0
  287. data/lib/shoko/adapters/ui/components/sidebar/toc/layout/entries_list_layout.rb +81 -0
  288. data/lib/shoko/adapters/ui/components/sidebar/toc/layout/entry_layout_helper.rb +48 -0
  289. data/lib/shoko/adapters/ui/components/sidebar/toc/layout/line_index.rb +63 -0
  290. data/lib/shoko/adapters/ui/components/sidebar/toc/layout/visible_entry_item.rb +97 -0
  291. data/lib/shoko/adapters/ui/components/sidebar/toc/layout/visible_items_calculator.rb +87 -0
  292. data/lib/shoko/adapters/ui/components/sidebar/toc/layout.rb +7 -0
  293. data/lib/shoko/adapters/ui/components/sidebar/toc/renderers/component_orchestrator.rb +27 -0
  294. data/lib/shoko/adapters/ui/components/sidebar/toc/renderers/empty_state_renderer.rb +51 -0
  295. data/lib/shoko/adapters/ui/components/sidebar/toc/renderers/entries_list_renderer.rb +28 -0
  296. data/lib/shoko/adapters/ui/components/sidebar/toc/renderers/filter_input_renderer.rb +52 -0
  297. data/lib/shoko/adapters/ui/components/sidebar/toc/renderers/header_renderer.rb +176 -0
  298. data/lib/shoko/adapters/ui/components/sidebar/toc/renderers/scrollbar_renderer.rb +51 -0
  299. data/lib/shoko/adapters/ui/components/sidebar/toc/renderers.rb +8 -0
  300. data/lib/shoko/adapters/ui/components/sidebar/toc/scroll_metrics.rb +152 -0
  301. data/lib/shoko/adapters/ui/components/sidebar/toc/tree_formatting.rb +202 -0
  302. data/lib/shoko/adapters/ui/components/sidebar/toc_tab_renderer.rb +132 -0
  303. data/lib/shoko/adapters/ui/components/sidebar_panel_component.rb +229 -0
  304. data/lib/shoko/adapters/ui/components/surface.rb +92 -0
  305. data/lib/shoko/adapters/ui/components/tooltip_overlay_component.rb +251 -0
  306. data/lib/shoko/adapters/ui/components/ui/annotation_list_input.rb +102 -0
  307. data/lib/shoko/adapters/ui/components/ui/annotation_markup.rb +456 -0
  308. data/lib/shoko/adapters/ui/components/ui/box_drawer.rb +37 -0
  309. data/lib/shoko/adapters/ui/components/ui/cursor_blink.rb +104 -0
  310. data/lib/shoko/adapters/ui/components/ui/list_helpers.rb +37 -0
  311. data/lib/shoko/adapters/ui/components/ui/overlay_layout.rb +92 -0
  312. data/lib/shoko/adapters/ui/components/ui/text_utils.rb +50 -0
  313. data/lib/shoko/adapters/ui/constants/highlighting.rb +23 -0
  314. data/lib/shoko/adapters/ui/constants/messages.rb +14 -0
  315. data/lib/shoko/adapters/ui/constants/themes.rb +81 -0
  316. data/lib/shoko/adapters/ui/constants/ui_constants.rb +155 -0
  317. data/lib/shoko/adapters/ui/dependency_sets.rb +49 -0
  318. data/lib/shoko/adapters/ui/menu_visual_profile.rb +10 -0
  319. data/lib/shoko/adapters/ui/render_registry.rb +42 -0
  320. data/lib/shoko/adapters/ui/rendering/frame_coordinator.rb +55 -0
  321. data/lib/shoko/adapters/ui/rendering/line/config_helpers.rb +58 -0
  322. data/lib/shoko/adapters/ui/rendering/line/inline_segment_highlighter.rb +163 -0
  323. data/lib/shoko/adapters/ui/rendering/line/kitty_image_line_renderer.rb +256 -0
  324. data/lib/shoko/adapters/ui/rendering/line/line_content_composer.rb +253 -0
  325. data/lib/shoko/adapters/ui/rendering/line/line_drawer.rb +96 -0
  326. data/lib/shoko/adapters/ui/rendering/line/line_geometry_builder.rb +133 -0
  327. data/lib/shoko/adapters/ui/rendering/line/render_dependencies.rb +30 -0
  328. data/lib/shoko/adapters/ui/rendering/line/rendered_lines_recorder.rb +73 -0
  329. data/lib/shoko/adapters/ui/rendering/line/wrapped_lines_fetcher.rb +140 -0
  330. data/lib/shoko/adapters/{output → ui}/rendering/models/line_geometry.rb +1 -1
  331. data/lib/shoko/adapters/{output → ui}/rendering/models/page_rendering_context.rb +1 -1
  332. data/lib/shoko/adapters/{output → ui}/rendering/models/render_params.rb +1 -1
  333. data/lib/shoko/adapters/{output → ui}/rendering/models/rendering_context.rb +12 -8
  334. data/lib/shoko/adapters/ui/rendering/reader_render_coordinator.rb +272 -0
  335. data/lib/shoko/adapters/ui/rendering/render_pipeline.rb +75 -0
  336. data/lib/shoko/adapters/ui/rendering/views/base_view_renderer.rb +218 -0
  337. data/lib/shoko/adapters/ui/rendering/views/help_renderer.rb +67 -0
  338. data/lib/shoko/adapters/ui/rendering/views/single_view_renderer.rb +190 -0
  339. data/lib/shoko/adapters/ui/rendering/views/split_view_renderer.rb +261 -0
  340. data/lib/shoko/adapters/ui/rendering/views/view_renderer_factory.rb +27 -0
  341. data/lib/shoko/adapters/ui/rendering_factory.rb +48 -0
  342. data/lib/shoko/adapters/ui/sessions/annotation_editor_launcher_adapter.rb +29 -0
  343. data/lib/shoko/adapters/ui/sessions/annotation_overlay_ui_session_adapter.rb +294 -0
  344. data/lib/shoko/adapters/ui/sessions/dictionary_ui_session_adapter.rb +342 -0
  345. data/lib/shoko/adapters/ui/sessions/in_book_search_ui_session_adapter.rb +189 -0
  346. data/lib/shoko/adapters/ui/view_models/reader_view_model.rb +179 -0
  347. data/lib/shoko/adapters/ui/view_models/reader_view_model_builder.rb +64 -0
  348. data/lib/shoko/application/cli_progress_presenter.rb +48 -0
  349. data/lib/shoko/application/pending_jump_handler.rb +45 -63
  350. data/lib/shoko/{core → application}/services/pagination/page_info_calculator.rb +36 -19
  351. data/lib/shoko/{core → application}/services/pagination/pagination_cache_preloader.rb +64 -36
  352. data/lib/shoko/{core → application}/services/pagination/pagination_coordinator.rb +89 -41
  353. data/lib/shoko/{core → application}/services/pagination/pagination_orchestrator.rb +112 -45
  354. data/lib/shoko/application/services/popup_position_service.rb +35 -0
  355. data/lib/shoko/application/services/reader/annotation_state_service.rb +53 -0
  356. data/lib/shoko/application/services/reader/bookmark_service.rb +318 -0
  357. data/lib/shoko/application/services/reader/navigation/absolute_change_applier.rb +98 -0
  358. data/lib/shoko/application/services/reader/navigation/absolute_layout.rb +128 -0
  359. data/lib/shoko/application/services/reader/navigation/absolute_strategy.rb +181 -0
  360. data/lib/shoko/application/services/reader/navigation/context_builder.rb +77 -0
  361. data/lib/shoko/application/services/reader/navigation/context_helpers.rb +77 -0
  362. data/lib/shoko/application/services/reader/navigation/dynamic_change_applier.rb +49 -0
  363. data/lib/shoko/application/services/reader/navigation/dynamic_strategy.rb +53 -0
  364. data/lib/shoko/application/services/reader/navigation/image_offset_snapper.rb +183 -0
  365. data/lib/shoko/application/services/reader/navigation/nav_context.rb +29 -0
  366. data/lib/shoko/application/services/reader/navigation/state_updater.rb +26 -0
  367. data/lib/shoko/application/services/reader/navigation/strategy_factory.rb +22 -0
  368. data/lib/shoko/application/services/reader/navigation_service.rb +169 -0
  369. data/lib/shoko/application/unified_application.rb +77 -37
  370. data/lib/shoko/application/use_cases/catalog_service.rb +101 -81
  371. data/lib/shoko/application/use_cases/command_bus.rb +170 -0
  372. data/lib/shoko/application/use_cases/commands/base_command.rb +125 -130
  373. data/lib/shoko/application/use_cases/commands/bookmark_commands.rb +70 -82
  374. data/lib/shoko/application/use_cases/commands/input_command_payload.rb +13 -0
  375. data/lib/shoko/application/use_cases/commands/menu_intent_command.rb +51 -0
  376. data/lib/shoko/application/use_cases/commands/navigation_commands.rb +154 -124
  377. data/lib/shoko/application/use_cases/commands/reader_intent_command.rb +51 -0
  378. data/lib/shoko/application/use_cases/commands/shared_intent_command.rb +55 -0
  379. data/lib/shoko/application/use_cases/intents/menu_intent_handler.rb +47 -0
  380. data/lib/shoko/application/use_cases/intents/reader_intent_handler.rb +47 -0
  381. data/lib/shoko/application/use_cases/settings_service.rb +203 -267
  382. data/lib/shoko/application/workflows/cli/folder_import_workflow.rb +164 -0
  383. data/lib/shoko/application/workflows/menu/annotation_workflow.rb +132 -0
  384. data/lib/shoko/application/workflows/menu/dictionary_workflow.rb +203 -0
  385. data/lib/shoko/application/workflows/menu/download_workflow.rb +181 -0
  386. data/lib/shoko/application/workflows/menu/menu_progress_presenter.rb +85 -0
  387. data/lib/shoko/application/workflows/menu/null_progress_presenter.rb +24 -0
  388. data/lib/shoko/application/workflows/menu/reader_launch/contracts.rb +70 -0
  389. data/lib/shoko/application/workflows/menu/reader_launch/document_preparation.rb +137 -0
  390. data/lib/shoko/application/workflows/menu/reader_launch/path_resolution.rb +76 -0
  391. data/lib/shoko/application/workflows/menu/reader_launch/progress_orchestration.rb +203 -0
  392. data/lib/shoko/application/workflows/menu/reader_launch/runtime_execution.rb +106 -0
  393. data/lib/shoko/application/workflows/menu/reader_launch_service.rb +161 -0
  394. data/lib/shoko/bootstrap/container_factory/controller_composition/menu_builder.rb +163 -0
  395. data/lib/shoko/bootstrap/container_factory/controller_composition/menu_state_controller_composer.rb +174 -0
  396. data/lib/shoko/bootstrap/container_factory/controller_composition/reader_builder.rb +400 -0
  397. data/lib/shoko/bootstrap/container_factory/controller_composition/reader_runtime_assembler.rb +267 -0
  398. data/lib/shoko/bootstrap/container_factory/controller_composition.rb +19 -0
  399. data/lib/shoko/bootstrap/container_factory/domain_application_registration.rb +340 -0
  400. data/lib/shoko/bootstrap/container_factory/infrastructure_registration.rb +123 -0
  401. data/lib/shoko/bootstrap/container_factory/port_and_repository_registration.rb +241 -0
  402. data/lib/shoko/bootstrap/container_factory/test_container_registration.rb +198 -0
  403. data/lib/shoko/bootstrap/container_factory.rb +174 -0
  404. data/lib/shoko/bootstrap/dependency_container.rb +142 -0
  405. data/lib/shoko/bootstrap/format_registry_bootstrap.rb +137 -0
  406. data/lib/shoko/bootstrap/runtime_bootstrap.rb +86 -0
  407. data/lib/shoko/core/book_formats/epub/html_processor.rb +146 -0
  408. data/lib/shoko/core/book_formats/epub/metadata_extractor.rb +65 -0
  409. data/lib/shoko/core/book_formats/epub/opf/entry_reader.rb +120 -0
  410. data/lib/shoko/core/book_formats/epub/opf/metadata_extractor.rb +71 -0
  411. data/lib/shoko/core/book_formats/epub/opf/navigation_context.rb +90 -0
  412. data/lib/shoko/core/book_formats/epub/opf/navigation_document_index.rb +79 -0
  413. data/lib/shoko/core/book_formats/epub/opf/navigation_document_scanner.rb +51 -0
  414. data/lib/shoko/core/book_formats/epub/opf/navigation_extractor.rb +50 -0
  415. data/lib/shoko/core/book_formats/epub/opf/navigation_label_resolver.rb +91 -0
  416. data/lib/shoko/core/book_formats/epub/opf/navigation_list_item.rb +59 -0
  417. data/lib/shoko/core/book_formats/epub/opf/navigation_result.rb +12 -0
  418. data/lib/shoko/core/book_formats/epub/opf/navigation_selector.rb +104 -0
  419. data/lib/shoko/core/book_formats/epub/opf/navigation_source_locator.rb +97 -0
  420. data/lib/shoko/core/book_formats/epub/opf/navigation_traversal.rb +108 -0
  421. data/lib/shoko/core/book_formats/epub/opf/navigation_walker.rb +60 -0
  422. data/lib/shoko/core/book_formats/epub/opf_processor.rb +115 -0
  423. data/lib/shoko/core/book_formats/epub/rexml_safe_parser.rb +22 -0
  424. data/lib/shoko/core/book_formats/epub/xhtml_content_parser.rb +803 -0
  425. data/lib/shoko/core/book_formats/epub/xml_text_normalizer.rb +45 -0
  426. data/lib/shoko/core/book_formats/fb2/fb2_content_parser.rb +324 -0
  427. data/lib/shoko/core/book_formats/fb2/fb2_inline_parser.rb +117 -0
  428. data/lib/shoko/core/book_formats/fb2/fb2_metadata_extractor.rb +68 -0
  429. data/lib/shoko/core/book_formats/fb2/fb2_section_flattener.rb +133 -0
  430. data/lib/shoko/core/book_formats/fb2/metadata_parser.rb +96 -0
  431. data/lib/shoko/core/book_formats/format_registry.rb +157 -0
  432. data/lib/shoko/core/book_formats/kindle/exth_parser.rb +169 -0
  433. data/lib/shoko/core/book_formats/kindle/kindle_content_parser.rb +31 -0
  434. data/lib/shoko/core/book_formats/kindle/kindle_metadata_extractor.rb +79 -0
  435. data/lib/shoko/core/book_formats/kindle/metadata_parser.rb +61 -0
  436. data/lib/shoko/core/book_formats/kindle/mobi_header_parser.rb +224 -0
  437. data/lib/shoko/core/book_formats/kindle/palmdoc_decompressor.rb +142 -0
  438. data/lib/shoko/core/book_formats/kindle/pdb_header_parser.rb +103 -0
  439. data/lib/shoko/core/book_formats/pdf/metadata_parser.rb +57 -0
  440. data/lib/shoko/core/book_formats/pdf/pdf_content_parser.rb +687 -0
  441. data/lib/shoko/core/book_formats/pdf/pdf_metadata_extractor.rb +64 -0
  442. data/lib/shoko/core/book_formats/pdf/pdf_reader.rb +485 -0
  443. data/lib/shoko/core/book_formats/pdf/pdf_text_extractor.rb +629 -0
  444. data/lib/shoko/core/book_formats/rtf/metadata_parser.rb +121 -0
  445. data/lib/shoko/core/book_formats/rtf/rtf_content_parser.rb +29 -0
  446. data/lib/shoko/core/book_formats/rtf/rtf_metadata_extractor.rb +73 -0
  447. data/lib/shoko/core/book_formats/rtf/rtf_parser.rb +767 -0
  448. data/lib/shoko/core/errors/dictionary_failure.rb +30 -0
  449. data/lib/shoko/core/events/base_domain_event.rb +22 -5
  450. data/lib/shoko/core/events/domain_event_bus.rb +10 -13
  451. data/lib/shoko/core/events/event_factory.rb +23 -0
  452. data/lib/shoko/core/models/annotation_selection.rb +59 -0
  453. data/lib/shoko/core/models/block_type.rb +51 -0
  454. data/lib/shoko/core/models/book_data.rb +29 -0
  455. data/lib/shoko/core/models/chapter.rb +5 -1
  456. data/lib/shoko/core/models/dictionary_catalog_entry.rb +52 -0
  457. data/lib/shoko/core/models/menu_book.rb +36 -0
  458. data/lib/shoko/core/models/pending_jump_payload.rb +58 -0
  459. data/lib/shoko/core/models/reading_progress.rb +30 -0
  460. data/lib/shoko/core/models/selection_anchor.rb +1 -1
  461. data/lib/shoko/core/ports/inbound/command_bus.rb +39 -0
  462. data/lib/shoko/core/ports/inbound/input_command_payload.rb +55 -0
  463. data/lib/shoko/core/ports/inbound/intent_dispatch_context.rb +24 -0
  464. data/lib/shoko/core/ports/inbound/menu_intent_handler.rb +82 -0
  465. data/lib/shoko/core/ports/inbound/reader_bookmark_command_context.rb +16 -0
  466. data/lib/shoko/core/ports/inbound/reader_intent_handler.rb +70 -0
  467. data/lib/shoko/core/ports/inbound/reader_navigation_command_context.rb +20 -0
  468. data/lib/shoko/core/ports/outbound/annotation_editor_launcher.rb +16 -0
  469. data/lib/shoko/core/ports/outbound/annotation_repository.rb +96 -0
  470. data/lib/shoko/core/ports/outbound/annotation_selection_reader.rb +17 -0
  471. data/lib/shoko/core/ports/outbound/annotation_view_refresher.rb +16 -0
  472. data/lib/shoko/core/ports/outbound/app_mode_runner.rb +20 -0
  473. data/lib/shoko/core/ports/outbound/async_executor.rb +26 -0
  474. data/lib/shoko/core/ports/outbound/background_worker_builder.rb +19 -0
  475. data/lib/shoko/core/ports/outbound/book_cache_pipeline_factory.rb +16 -0
  476. data/lib/shoko/core/ports/outbound/bookmark_repository.rb +79 -0
  477. data/lib/shoko/core/ports/outbound/cache_availability.rb +20 -0
  478. data/lib/shoko/core/ports/outbound/cache_manager.rb +28 -0
  479. data/lib/shoko/core/ports/outbound/cache_pointer_resolver.rb +29 -0
  480. data/lib/shoko/core/ports/outbound/chapter_formatter.rb +38 -0
  481. data/lib/shoko/core/ports/outbound/clock.rb +16 -0
  482. data/lib/shoko/core/ports/outbound/config_reader.rb +60 -0
  483. data/lib/shoko/core/ports/outbound/config_storage.rb +71 -0
  484. data/lib/shoko/core/ports/outbound/data_cleanup.rb +34 -0
  485. data/lib/shoko/core/ports/outbound/dictionary_availability.rb +21 -0
  486. data/lib/shoko/core/ports/outbound/dictionary_repository.rb +78 -0
  487. data/lib/shoko/core/ports/outbound/dictionary_storage.rb +47 -0
  488. data/lib/shoko/core/ports/outbound/display_capabilities.rb +20 -0
  489. data/lib/shoko/core/ports/outbound/document_loader.rb +20 -0
  490. data/lib/shoko/core/ports/outbound/event_publisher.rb +18 -0
  491. data/lib/shoko/core/ports/outbound/file_probe.rb +24 -0
  492. data/lib/shoko/core/ports/outbound/folder_importer.rb +17 -0
  493. data/lib/shoko/core/ports/outbound/folder_scanner.rb +18 -0
  494. data/lib/shoko/core/ports/outbound/id_generator.rb +16 -0
  495. data/lib/shoko/core/ports/outbound/instrumentation.rb +33 -0
  496. data/lib/shoko/core/ports/outbound/layout_metrics.rb +80 -0
  497. data/lib/shoko/core/ports/outbound/library_scanner.rb +48 -0
  498. data/lib/shoko/core/ports/outbound/line_wrapper.rb +36 -0
  499. data/lib/shoko/core/ports/outbound/logging.rb +67 -0
  500. data/lib/shoko/core/ports/outbound/menu_book_selection.rb +22 -0
  501. data/lib/shoko/core/ports/outbound/menu_intent_executor.rb +16 -0
  502. data/lib/shoko/core/ports/outbound/menu_launch_state.rb +24 -0
  503. data/lib/shoko/core/ports/outbound/menu_mode_switcher.rb +16 -0
  504. data/lib/shoko/core/ports/outbound/menu_progress_presenters.rb +16 -0
  505. data/lib/shoko/core/ports/outbound/menu_reader_runtime.rb +24 -0
  506. data/lib/shoko/core/ports/outbound/menu_workflow_runtime.rb +20 -0
  507. data/lib/shoko/core/ports/outbound/menu_workflow_state_reader.rb +36 -0
  508. data/lib/shoko/core/ports/outbound/menu_workflow_state_writer.rb +28 -0
  509. data/lib/shoko/core/ports/outbound/metadata_reader.rb +22 -0
  510. data/lib/shoko/core/ports/outbound/notification_writer.rb +20 -0
  511. data/lib/shoko/core/ports/outbound/observer_registry.rb +32 -0
  512. data/lib/shoko/core/ports/outbound/pagination_state_writer.rb +24 -0
  513. data/lib/shoko/core/ports/outbound/path_ops.rb +28 -0
  514. data/lib/shoko/core/ports/outbound/process_control.rb +16 -0
  515. data/lib/shoko/core/ports/outbound/progress_repository.rb +57 -0
  516. data/lib/shoko/core/ports/outbound/reader_chapter.rb +24 -0
  517. data/lib/shoko/core/ports/outbound/reader_document.rb +32 -0
  518. data/lib/shoko/core/ports/outbound/reader_intent_executor.rb +16 -0
  519. data/lib/shoko/core/ports/outbound/reader_launch_state.rb +36 -0
  520. data/lib/shoko/core/ports/outbound/reader_navigation_reader.rb +56 -0
  521. data/lib/shoko/core/ports/outbound/reader_render_requester.rb +18 -0
  522. data/lib/shoko/core/ports/outbound/reader_runner.rb +16 -0
  523. data/lib/shoko/core/ports/outbound/reader_state_writer.rb +48 -0
  524. data/lib/shoko/core/ports/outbound/recent_files_repository.rb +27 -0
  525. data/lib/shoko/core/ports/outbound/render_state_writer.rb +22 -0
  526. data/lib/shoko/core/ports/outbound/rendered_content_reader.rb +34 -0
  527. data/lib/shoko/core/ports/outbound/runtime_config.rb +113 -0
  528. data/lib/shoko/core/ports/outbound/sidebar_state_reader.rb +48 -0
  529. data/lib/shoko/core/ports/outbound/terminal_capabilities.rb +35 -0
  530. data/lib/shoko/core/ports/outbound/terminal_session.rb +24 -0
  531. data/lib/shoko/core/ports/outbound/text_metrics.rb +21 -0
  532. data/lib/shoko/core/ports/outbound/text_sanitizer.rb +24 -0
  533. data/lib/shoko/core/ports/outbound/ui_loading_writer.rb +16 -0
  534. data/lib/shoko/core/ports/outbound/ui_state_reader.rb +32 -0
  535. data/lib/shoko/core/ports/outbound/wall_clock.rb +16 -0
  536. data/lib/shoko/core/ports/outbound/wrapped_lines_provider.rb +25 -0
  537. data/lib/shoko/core/services/annotation_service.rb +31 -43
  538. data/lib/shoko/core/services/coordinate_service.rb +2 -27
  539. data/lib/shoko/core/services/default_display_capabilities.rb +2 -2
  540. data/lib/shoko/core/services/default_layout_metrics.rb +2 -2
  541. data/lib/shoko/core/services/default_terminal_capabilities.rb +2 -2
  542. data/lib/shoko/core/services/default_text_metrics.rb +2 -2
  543. data/lib/shoko/core/services/dictionary_service.rb +31 -19
  544. data/lib/shoko/core/services/document_path_resolver.rb +62 -0
  545. data/lib/shoko/core/services/in_book_search_service.rb +188 -0
  546. data/lib/shoko/core/services/inline_executor.rb +2 -2
  547. data/lib/shoko/core/services/layout_service.rb +36 -4
  548. data/lib/shoko/core/services/null_instrumentation.rb +2 -2
  549. data/lib/shoko/core/services/null_logger.rb +2 -2
  550. data/lib/shoko/core/services/page_calculator_service.rb +274 -57
  551. data/lib/shoko/core/services/pagination/internal/absolute_page_map_builder.rb +26 -2
  552. data/lib/shoko/core/services/pagination/internal/chapter_cache.rb +1 -1
  553. data/lib/shoko/core/services/pagination/internal/dynamic_page_map_builder.rb +14 -12
  554. data/lib/shoko/core/services/pagination/internal/layout_metrics_calculator.rb +19 -28
  555. data/lib/shoko/core/services/pagination/internal/page_hydrator.rb +38 -32
  556. data/lib/shoko/core/services/pagination/internal/pagination_workflow.rb +47 -31
  557. data/lib/shoko/core/services/selection_service.rb +3 -16
  558. data/lib/shoko/core/services/toc_tree_service.rb +196 -0
  559. data/lib/shoko/shared/contracts/session_outcome.rb +18 -0
  560. data/lib/shoko/shared/errors.rb +48 -3
  561. data/lib/shoko/{adapters/input → shared}/key_definitions.rb +18 -22
  562. data/lib/shoko/shared/menu_definitions.rb +76 -0
  563. data/lib/shoko/shared/optional_dependency.rb +25 -5
  564. data/lib/shoko/shared/runtime/null_runtime_config.rb +44 -0
  565. data/lib/shoko/{adapters/book_sources → shared}/source_fingerprint.rb +5 -5
  566. data/lib/shoko/shared/terminal/ansi.rb +73 -0
  567. data/lib/shoko/shared/terminal/kitty_unicode_placeholders.rb +137 -0
  568. data/lib/shoko/shared/terminal/text_metrics.rb +490 -0
  569. data/lib/shoko/shared/terminal/text_sanitizer.rb +12 -0
  570. data/lib/shoko/shared/text_sanitizer.rb +239 -0
  571. data/lib/shoko/shared/ui_constraints.rb +11 -0
  572. data/lib/shoko/shared/version.rb +1 -1
  573. data/lib/shoko/test_support/terminal_double.rb +1 -1
  574. data/lib/shoko/test_support/test_mode.rb +5 -19
  575. data/lib/shoko.rb +4 -270
  576. data/script/architecture/fallback_report.rb +105 -0
  577. data/script/bench/sidebar_toggle_layout_benchmark.rb +194 -0
  578. data/script/bench/snappiness_benchmark.rb +443 -0
  579. data/script/bench/startup_menu_benchmark.rb +114 -0
  580. data/tmp/rspec-results/fixtures.json +1 -0
  581. data/tmp/rspec-results/full-seed-10101.log +10 -0
  582. data/tmp/rspec-results/full-seed-1559.log +10 -0
  583. data/tmp/rspec-results/full-seed-20202.log +10 -0
  584. data/tmp/rspec-results/full-seed-30303.log +10 -0
  585. data/tmp/rspec-results/full-seed-49446.log +10 -0
  586. data/tmp/rspec-results/full-seed-52407.log +10 -0
  587. data/tmp/rspec-results/full-seed-5517.log +10 -0
  588. data/tmp/rspec-results/full-seed-56072.log +10 -0
  589. data/tmp/rspec-results/full-seed-61968.log +10 -0
  590. data/tmp/rspec-results/full-seed-63851.log +10 -0
  591. data/tmp/rspec-results/full-seed-65033.log +10 -0
  592. data/tmp/rspec-results/full-seed-87095.log +10 -0
  593. data/tmp/rspec-results/full-seed-97342.log +10 -0
  594. data/tmp/rspec-results/guardrails.json +1 -0
  595. data/tmp/rspec-results/required-seed-10101.json +1 -0
  596. data/tmp/rspec-results/required-seed-20202.json +1 -0
  597. data/tmp/rspec-results/required-seed-30303.json +1 -0
  598. data/tmp/rubocop-metrics.json +1 -0
  599. metadata +473 -229
  600. data/lib/shoko/adapters/book_sources/epub/parsers/html_processor.rb +0 -146
  601. data/lib/shoko/adapters/book_sources/epub/parsers/metadata_extractor.rb +0 -54
  602. data/lib/shoko/adapters/book_sources/epub/parsers/opf/entry_reader.rb +0 -77
  603. data/lib/shoko/adapters/book_sources/epub/parsers/opf/metadata_extractor.rb +0 -67
  604. data/lib/shoko/adapters/book_sources/epub/parsers/opf/navigation_context.rb +0 -86
  605. data/lib/shoko/adapters/book_sources/epub/parsers/opf/navigation_document_index.rb +0 -75
  606. data/lib/shoko/adapters/book_sources/epub/parsers/opf/navigation_document_scanner.rb +0 -47
  607. data/lib/shoko/adapters/book_sources/epub/parsers/opf/navigation_extractor.rb +0 -46
  608. data/lib/shoko/adapters/book_sources/epub/parsers/opf/navigation_label_resolver.rb +0 -83
  609. data/lib/shoko/adapters/book_sources/epub/parsers/opf/navigation_list_item.rb +0 -55
  610. data/lib/shoko/adapters/book_sources/epub/parsers/opf/navigation_result.rb +0 -8
  611. data/lib/shoko/adapters/book_sources/epub/parsers/opf/navigation_selector.rb +0 -100
  612. data/lib/shoko/adapters/book_sources/epub/parsers/opf/navigation_source_locator.rb +0 -93
  613. data/lib/shoko/adapters/book_sources/epub/parsers/opf/navigation_traversal.rb +0 -104
  614. data/lib/shoko/adapters/book_sources/epub/parsers/opf/navigation_walker.rb +0 -56
  615. data/lib/shoko/adapters/book_sources/epub/parsers/opf_processor.rb +0 -111
  616. data/lib/shoko/adapters/book_sources/epub/parsers/rexml_safe_parser.rb +0 -50
  617. data/lib/shoko/adapters/book_sources/epub/parsers/xhtml_content_parser.rb +0 -784
  618. data/lib/shoko/adapters/book_sources/epub/parsers/xml_text_normalizer.rb +0 -41
  619. data/lib/shoko/adapters/book_sources/epub_document.rb +0 -266
  620. data/lib/shoko/adapters/book_sources/epub_finder/directory_scanner.rb +0 -135
  621. data/lib/shoko/adapters/book_sources/epub_finder/scanner_context.rb +0 -28
  622. data/lib/shoko/adapters/book_sources/epub_importer.rb +0 -278
  623. data/lib/shoko/adapters/input/command_bridge.rb +0 -81
  624. data/lib/shoko/adapters/input/input_controller.rb +0 -330
  625. data/lib/shoko/adapters/monitoring/logger.rb +0 -150
  626. data/lib/shoko/adapters/output/render_registry.rb +0 -40
  627. data/lib/shoko/adapters/output/ui/builders/page_setup_builder.rb +0 -47
  628. data/lib/shoko/adapters/output/ui/component_factory.rb +0 -84
  629. data/lib/shoko/adapters/output/ui/components/annotation_editor_overlay/footer_renderer.rb +0 -80
  630. data/lib/shoko/adapters/output/ui/components/annotation_editor_overlay/geometry.rb +0 -61
  631. data/lib/shoko/adapters/output/ui/components/annotation_editor_overlay/note_renderer.rb +0 -87
  632. data/lib/shoko/adapters/output/ui/components/annotation_editor_overlay_component.rb +0 -407
  633. data/lib/shoko/adapters/output/ui/components/annotations_overlay/list_renderer.rb +0 -144
  634. data/lib/shoko/adapters/output/ui/components/annotations_overlay_component.rb +0 -195
  635. data/lib/shoko/adapters/output/ui/components/base_component.rb +0 -114
  636. data/lib/shoko/adapters/output/ui/components/content_component.rb +0 -69
  637. data/lib/shoko/adapters/output/ui/components/dictionary/entry_formatter.rb +0 -228
  638. data/lib/shoko/adapters/output/ui/components/dictionary_panel_component.rb +0 -266
  639. data/lib/shoko/adapters/output/ui/components/dictionary_popup_component.rb +0 -251
  640. data/lib/shoko/adapters/output/ui/components/enhanced_popup_menu.rb +0 -203
  641. data/lib/shoko/adapters/output/ui/components/footer_component.rb +0 -122
  642. data/lib/shoko/adapters/output/ui/components/header_component.rb +0 -46
  643. data/lib/shoko/adapters/output/ui/components/layouts/horizontal.rb +0 -63
  644. data/lib/shoko/adapters/output/ui/components/layouts/horizontal_three.rb +0 -90
  645. data/lib/shoko/adapters/output/ui/components/layouts/vertical.rb +0 -73
  646. data/lib/shoko/adapters/output/ui/components/main_menu_component.rb +0 -106
  647. data/lib/shoko/adapters/output/ui/components/reading/base_view_renderer.rb +0 -250
  648. data/lib/shoko/adapters/output/ui/components/reading/config_helpers.rb +0 -50
  649. data/lib/shoko/adapters/output/ui/components/reading/help_renderer.rb +0 -62
  650. data/lib/shoko/adapters/output/ui/components/reading/inline_segment_highlighter.rb +0 -165
  651. data/lib/shoko/adapters/output/ui/components/reading/kitty_image_line_renderer.rb +0 -274
  652. data/lib/shoko/adapters/output/ui/components/reading/line_content_composer.rb +0 -114
  653. data/lib/shoko/adapters/output/ui/components/reading/line_drawer.rb +0 -95
  654. data/lib/shoko/adapters/output/ui/components/reading/line_geometry_builder.rb +0 -41
  655. data/lib/shoko/adapters/output/ui/components/reading/rendered_lines_recorder.rb +0 -64
  656. data/lib/shoko/adapters/output/ui/components/reading/single_view_renderer.rb +0 -156
  657. data/lib/shoko/adapters/output/ui/components/reading/split_view_renderer.rb +0 -219
  658. data/lib/shoko/adapters/output/ui/components/reading/view_renderer_factory.rb +0 -29
  659. data/lib/shoko/adapters/output/ui/components/reading/wrapped_lines_fetcher.rb +0 -139
  660. data/lib/shoko/adapters/output/ui/components/rect.rb +0 -15
  661. data/lib/shoko/adapters/output/ui/components/render_style.rb +0 -84
  662. data/lib/shoko/adapters/output/ui/components/screen_component.rb +0 -24
  663. data/lib/shoko/adapters/output/ui/components/screens/annotation_detail_screen_component.rb +0 -103
  664. data/lib/shoko/adapters/output/ui/components/screens/annotation_edit_screen_component.rb +0 -179
  665. data/lib/shoko/adapters/output/ui/components/screens/annotation_editor_screen_component.rb +0 -239
  666. data/lib/shoko/adapters/output/ui/components/screens/annotation_rendering_helpers.rb +0 -466
  667. data/lib/shoko/adapters/output/ui/components/screens/annotations_screen_component.rb +0 -201
  668. data/lib/shoko/adapters/output/ui/components/screens/base_screen_component.rb +0 -49
  669. data/lib/shoko/adapters/output/ui/components/screens/browse_screen_component.rb +0 -332
  670. data/lib/shoko/adapters/output/ui/components/screens/dictionary_settings_screen_component.rb +0 -457
  671. data/lib/shoko/adapters/output/ui/components/screens/download_books_screen_component.rb +0 -357
  672. data/lib/shoko/adapters/output/ui/components/screens/library_screen_component.rb +0 -233
  673. data/lib/shoko/adapters/output/ui/components/screens/loading_overlay_component.rb +0 -58
  674. data/lib/shoko/adapters/output/ui/components/screens/menu_screen_component.rb +0 -115
  675. data/lib/shoko/adapters/output/ui/components/screens/settings_screen_component.rb +0 -321
  676. data/lib/shoko/adapters/output/ui/components/sidebar/annotations_tab_renderer.rb +0 -169
  677. data/lib/shoko/adapters/output/ui/components/sidebar/bookmarks_tab_renderer.rb +0 -156
  678. data/lib/shoko/adapters/output/ui/components/sidebar/tab_header_component.rb +0 -167
  679. data/lib/shoko/adapters/output/ui/components/sidebar/toc_tab_renderer.rb +0 -122
  680. data/lib/shoko/adapters/output/ui/components/sidebar/toc_tab_support.rb +0 -1603
  681. data/lib/shoko/adapters/output/ui/components/sidebar_panel_component.rb +0 -220
  682. data/lib/shoko/adapters/output/ui/components/surface.rb +0 -88
  683. data/lib/shoko/adapters/output/ui/components/tooltip_overlay_component.rb +0 -250
  684. data/lib/shoko/adapters/output/ui/components/ui/annotation_list_input.rb +0 -98
  685. data/lib/shoko/adapters/output/ui/components/ui/annotation_markup.rb +0 -452
  686. data/lib/shoko/adapters/output/ui/components/ui/box_drawer.rb +0 -33
  687. data/lib/shoko/adapters/output/ui/components/ui/cursor_blink.rb +0 -46
  688. data/lib/shoko/adapters/output/ui/components/ui/list_helpers.rb +0 -33
  689. data/lib/shoko/adapters/output/ui/components/ui/overlay_layout.rb +0 -88
  690. data/lib/shoko/adapters/output/ui/components/ui/text_utils.rb +0 -46
  691. data/lib/shoko/adapters/output/ui/constants/highlighting.rb +0 -21
  692. data/lib/shoko/adapters/output/ui/constants/messages.rb +0 -12
  693. data/lib/shoko/adapters/output/ui/constants/themes.rb +0 -79
  694. data/lib/shoko/adapters/output/ui/constants/ui_constants.rb +0 -104
  695. data/lib/shoko/adapters/output/ui/rendering/frame_coordinator.rb +0 -44
  696. data/lib/shoko/adapters/output/ui/rendering/reader_render_coordinator.rb +0 -255
  697. data/lib/shoko/adapters/output/ui/rendering/render_pipeline.rb +0 -87
  698. data/lib/shoko/adapters/output/ui/rendering_factory_adapter.rb +0 -34
  699. data/lib/shoko/adapters/storage/repositories/config_repository.rb +0 -266
  700. data/lib/shoko/application/adapters/command_port_adapter.rb +0 -133
  701. data/lib/shoko/application/adapters/config_reader_adapter.rb +0 -80
  702. data/lib/shoko/application/adapters/layout_metrics_adapter.rb +0 -66
  703. data/lib/shoko/application/adapters/menu_state_reader_adapter.rb +0 -204
  704. data/lib/shoko/application/adapters/menu_state_writer_adapter.rb +0 -134
  705. data/lib/shoko/application/adapters/notification_writer_adapter.rb +0 -37
  706. data/lib/shoko/application/adapters/progress_state_reader_adapter.rb +0 -35
  707. data/lib/shoko/application/adapters/reader_state_reader_adapter.rb +0 -150
  708. data/lib/shoko/application/adapters/render_state_writer_adapter.rb +0 -51
  709. data/lib/shoko/application/adapters/rendered_content_reader_adapter.rb +0 -27
  710. data/lib/shoko/application/adapters/sidebar_state_reader_adapter.rb +0 -65
  711. data/lib/shoko/application/adapters/state_writer_adapter.rb +0 -98
  712. data/lib/shoko/application/adapters/ui_state_reader_adapter.rb +0 -39
  713. data/lib/shoko/application/adapters/wrapped_lines_provider_adapter.rb +0 -35
  714. data/lib/shoko/application/annotation_editor_overlay_session.rb +0 -158
  715. data/lib/shoko/application/cli.rb +0 -134
  716. data/lib/shoko/application/cli_progress_renderer.rb +0 -151
  717. data/lib/shoko/application/controllers/annotation_overlay_controller.rb +0 -259
  718. data/lib/shoko/application/controllers/dictionary_controller.rb +0 -437
  719. data/lib/shoko/application/controllers/document_path_resolver.rb +0 -69
  720. data/lib/shoko/application/controllers/menu/input_controller.rb +0 -218
  721. data/lib/shoko/application/controllers/menu/state_controller.rb +0 -777
  722. data/lib/shoko/application/controllers/menu_controller.rb +0 -668
  723. data/lib/shoko/application/controllers/mouseable_reader.rb +0 -221
  724. data/lib/shoko/application/controllers/reader_controller.rb +0 -584
  725. data/lib/shoko/application/controllers/selection_mouse_handler.rb +0 -172
  726. data/lib/shoko/application/controllers/sidebar_controller.rb +0 -491
  727. data/lib/shoko/application/controllers/sidebar_mouse_handler.rb +0 -184
  728. data/lib/shoko/application/controllers/state_controller.rb +0 -444
  729. data/lib/shoko/application/controllers/ui_controller.rb +0 -443
  730. data/lib/shoko/application/dependency_container.rb +0 -943
  731. data/lib/shoko/application/infrastructure/event_bus.rb +0 -82
  732. data/lib/shoko/application/infrastructure/observer_state_store.rb +0 -139
  733. data/lib/shoko/application/infrastructure/state_store.rb +0 -584
  734. data/lib/shoko/application/main_menu/menu_progress_presenter.rb +0 -83
  735. data/lib/shoko/application/reader_lifecycle.rb +0 -58
  736. data/lib/shoko/application/reader_startup_orchestrator.rb +0 -74
  737. data/lib/shoko/application/selectors/config_selectors.rb +0 -78
  738. data/lib/shoko/application/selectors/menu_selectors.rb +0 -82
  739. data/lib/shoko/application/selectors/reader_selectors.rb +0 -183
  740. data/lib/shoko/application/state/actions/base_action.rb +0 -24
  741. data/lib/shoko/application/state/actions/quit_to_menu_action.rb +0 -16
  742. data/lib/shoko/application/state/actions/switch_reader_mode_action.rb +0 -22
  743. data/lib/shoko/application/state/actions/toggle_view_mode_action.rb +0 -31
  744. data/lib/shoko/application/state/actions/update_config_action.rb +0 -26
  745. data/lib/shoko/application/state/actions/update_field_helpers.rb +0 -26
  746. data/lib/shoko/application/state/actions/update_menu_action.rb +0 -17
  747. data/lib/shoko/application/state/actions/update_message_action.rb +0 -22
  748. data/lib/shoko/application/state/actions/update_page_action.rb +0 -14
  749. data/lib/shoko/application/state/actions/update_pagination_state_action.rb +0 -19
  750. data/lib/shoko/application/state/actions/update_reader_meta_action.rb +0 -19
  751. data/lib/shoko/application/state/actions/update_rendered_lines_action.rb +0 -30
  752. data/lib/shoko/application/state/actions/update_selection_action.rb +0 -24
  753. data/lib/shoko/application/state/actions/update_selections_action.rb +0 -14
  754. data/lib/shoko/application/state/actions/update_sidebar_action.rb +0 -38
  755. data/lib/shoko/application/state/actions/update_state_action.rb +0 -72
  756. data/lib/shoko/application/state/actions/update_ui_loading_action.rb +0 -19
  757. data/lib/shoko/application/ui/reader_view_model_builder.rb +0 -74
  758. data/lib/shoko/application/ui/view_models/reader_view_model.rb +0 -177
  759. data/lib/shoko/application/use_cases/commands/annotation_editor_commands.rb +0 -165
  760. data/lib/shoko/application/use_cases/commands/application_commands.rb +0 -181
  761. data/lib/shoko/application/use_cases/commands/conditional_navigation_commands.rb +0 -69
  762. data/lib/shoko/application/use_cases/commands/menu_commands.rb +0 -266
  763. data/lib/shoko/application/use_cases/commands/reader_commands.rb +0 -39
  764. data/lib/shoko/application/use_cases/commands/sidebar_commands.rb +0 -56
  765. data/lib/shoko/core/ports/annotation_repository.rb +0 -94
  766. data/lib/shoko/core/ports/async_executor.rb +0 -24
  767. data/lib/shoko/core/ports/bookmark_repository.rb +0 -77
  768. data/lib/shoko/core/ports/cache_availability.rb +0 -18
  769. data/lib/shoko/core/ports/cache_manager.rb +0 -26
  770. data/lib/shoko/core/ports/cache_pointer_resolver.rb +0 -27
  771. data/lib/shoko/core/ports/command_port.rb +0 -55
  772. data/lib/shoko/core/ports/config_reader.rb +0 -109
  773. data/lib/shoko/core/ports/config_storage.rb +0 -61
  774. data/lib/shoko/core/ports/dictionary_availability.rb +0 -41
  775. data/lib/shoko/core/ports/dictionary_repository.rb +0 -69
  776. data/lib/shoko/core/ports/display_capabilities.rb +0 -18
  777. data/lib/shoko/core/ports/input_system_factory.rb +0 -36
  778. data/lib/shoko/core/ports/instrumentation.rb +0 -31
  779. data/lib/shoko/core/ports/key_classifier.rb +0 -107
  780. data/lib/shoko/core/ports/layout_metrics.rb +0 -78
  781. data/lib/shoko/core/ports/logging.rb +0 -65
  782. data/lib/shoko/core/ports/menu_state_reader.rb +0 -291
  783. data/lib/shoko/core/ports/menu_state_writer.rb +0 -136
  784. data/lib/shoko/core/ports/metadata_reader.rb +0 -20
  785. data/lib/shoko/core/ports/notification_writer.rb +0 -40
  786. data/lib/shoko/core/ports/progress_state_reader.rb +0 -47
  787. data/lib/shoko/core/ports/reader_state_reader.rb +0 -207
  788. data/lib/shoko/core/ports/recent_files_repository.rb +0 -25
  789. data/lib/shoko/core/ports/render_state_writer.rb +0 -31
  790. data/lib/shoko/core/ports/rendered_content_reader.rb +0 -32
  791. data/lib/shoko/core/ports/rendering_factory.rb +0 -38
  792. data/lib/shoko/core/ports/sidebar_state_reader.rb +0 -88
  793. data/lib/shoko/core/ports/state_writer.rb +0 -152
  794. data/lib/shoko/core/ports/terminal_capabilities.rb +0 -33
  795. data/lib/shoko/core/ports/text_metrics.rb +0 -19
  796. data/lib/shoko/core/ports/text_sanitizer.rb +0 -22
  797. data/lib/shoko/core/ports/ui_component_factory.rb +0 -61
  798. data/lib/shoko/core/ports/ui_state_reader.rb +0 -53
  799. data/lib/shoko/core/ports/wrapped_lines_provider.rb +0 -23
  800. data/lib/shoko/core/services/bookmark_service.rb +0 -292
  801. data/lib/shoko/core/services/config_bridge.rb +0 -27
  802. data/lib/shoko/core/services/navigation/absolute_change_applier.rb +0 -96
  803. data/lib/shoko/core/services/navigation/absolute_layout.rb +0 -131
  804. data/lib/shoko/core/services/navigation/absolute_strategy.rb +0 -179
  805. data/lib/shoko/core/services/navigation/context_builder.rb +0 -77
  806. data/lib/shoko/core/services/navigation/context_helpers.rb +0 -78
  807. data/lib/shoko/core/services/navigation/dynamic_change_applier.rb +0 -47
  808. data/lib/shoko/core/services/navigation/dynamic_strategy.rb +0 -51
  809. data/lib/shoko/core/services/navigation/image_offset_snapper.rb +0 -169
  810. data/lib/shoko/core/services/navigation/nav_context.rb +0 -27
  811. data/lib/shoko/core/services/navigation/state_updater.rb +0 -32
  812. data/lib/shoko/core/services/navigation/strategy_factory.rb +0 -20
  813. data/lib/shoko/core/services/navigation_service.rb +0 -161
  814. data/zip.rb +0 -5
  815. /data/lib/shoko/{adapters/output/kitty → shared/terminal}/kitty_unicode_placeholders_diacritic_codepoints.txt +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a7a36fd324fdba1eed316344cfab543f5fef1bfa863c1b25fc3e644502978054
4
- data.tar.gz: 5fc5820176929bd037c545901cdc0c611e98b593a90fc8da7a54d4a01b0cc359
3
+ metadata.gz: a52dba22d1ec1059543f62821ab8e1bf7236def9a0cb8ef2376b13434faf2f03
4
+ data.tar.gz: 2da5e4f6c6b7a31702f7b9c9fe1786b02895f1588415a215ab0130068eebfb33
5
5
  SHA512:
6
- metadata.gz: e29b072b4f0dc64eb6a18f36d3fcc1bf37fa9efa904b1827cccbe85c4c37860073e04c3eddc87fdab827b9d02e03958f54bd1714d58644cdff026caaacf93bfe
7
- data.tar.gz: 0ffb950539fe1440f26149b1d75755811f2aaf541f900f6ab89051dc9e4e2550c1a07a9de32d83a3d6bac5a5f25a1184f7830cf3b39866eed458b71081fe9248
6
+ metadata.gz: acbbf2c9f160186d0cf41a3a8ed41efeb60be2f261398f80d2e9318eb0573f9c1ef47bb6f01851c9cdec6a42f8409b99b4b789d36e9d0af36491c3000cfbaa88
7
+ data.tar.gz: 6756ad75a6ffcfe850b99958f30440f35800a602d910115b1e79a59bfdb0afc3d06d4558c5f8f114eac15a3281acccfd8948b10a508c22b1aee952c66c41d4c8
data/Gemfile CHANGED
@@ -17,4 +17,5 @@ group :test do
17
17
  gem 'fakefs', require: 'fakefs/spec_helpers'
18
18
  gem 'rspec'
19
19
  gem 'simplecov', require: false
20
+ gem 'webmock', require: false
20
21
  end
data/README.md CHANGED
@@ -15,10 +15,42 @@ Terminal ebook reader for EPUB files.
15
15
  ## How it works
16
16
 
17
17
  - `bin/start` runs the CLI and enters menu mode or opens a file directly.
18
+ - `bootstrap` is the only runtime wiring boundary (`Bootstrap::ContainerFactory`).
18
19
  - State lives in a single store; actions update state and selectors read it.
19
20
  - Rendering is component-based and drawn through a terminal buffer with diff updates.
20
21
  - Selection/highlighting uses recorded line geometry from the render pass.
21
22
 
23
+ ## Architecture boundaries
24
+
25
+ - Hexagonal layering is enforced.
26
+ - `core` contains domain models/services and the single ports root.
27
+ - Ports are split by direction only:
28
+ - `Core::Ports::Inbound::*` (driving boundary into application use cases).
29
+ - `Core::Ports::Outbound::*` (driven dependencies implemented by adapters).
30
+ - `application` contains use-case/workflow/service orchestration only.
31
+ - `adapters` contains all input, UI, output, runtime, monitoring, and storage implementations.
32
+ - `bootstrap` is the only composition root and the only layer that mutates/resolves the container.
33
+ - Reader runtime controller graph composition is bootstrap-only (`ContainerFactory::ControllerComposition::ReaderBuilder`).
34
+
35
+ Canonical runtime layout:
36
+
37
+ ```text
38
+ lib/shoko/
39
+ core/ports/{inbound,outbound}
40
+ application/{use_cases,services,workflows}
41
+ adapters/{input,ui,output,runtime,storage,monitoring}
42
+ bootstrap/{container_factory,dependencies}
43
+ shared/
44
+ ```
45
+
46
+ Inbound command boundary:
47
+
48
+ - `Core::Ports::Inbound::CommandBus`
49
+ - `Core::Ports::Inbound::ReaderIntentHandler`
50
+ - `Core::Ports::Inbound::MenuIntentHandler`
51
+ - Implemented by `Application::UseCases::CommandBus`
52
+ - Input symbols are dispatched through `ReaderIntentCommand`, `MenuIntentCommand`, and `SharedIntentCommand`
53
+
22
54
  ## Usage
23
55
 
24
56
  From source:
@@ -34,12 +66,27 @@ Open a file directly:
34
66
  bin/start /path/to/book.epub
35
67
  ```
36
68
 
69
+ Import a folder of ebooks from CLI:
70
+
71
+ ```bash
72
+ bin/start /path/to/books-directory
73
+ ```
74
+
75
+ This scans the directory recursively (skipping hidden files/folders), shows counts by format, and lets you:
76
+
77
+ - import all discovered files
78
+ - import only one file type
79
+ - exit without importing
80
+
81
+ After import, Shoko opens menu mode by default.
82
+
37
83
  Options:
38
84
 
39
85
  - `-d`, `--debug` Enable debug logging.
40
86
  - `--log PATH` Write JSON logs to PATH.
41
87
  - `--log-level LEVEL` Set log level (`debug`, `info`, `warn`, `error`, `fatal`).
42
88
  - `--profile PATH` Write a concise performance profile to PATH.
89
+ - `-v`, `--version` Show version.
43
90
  - `-h`, `--help` Show help.
44
91
 
45
92
  ## Controls (basics)
@@ -69,7 +116,6 @@ Reader:
69
116
  - `config.json`
70
117
  - `annotations.json`, `bookmarks.json`, `progress.json`, `recent.json`
71
118
  - `downloads/` (Gutendex downloads)
72
- - `epub_cache.json` (scan cache)
73
119
  - Cache: `~/.cache/shoko/`
74
120
 
75
121
  ## Logging and profiling
@@ -80,3 +126,56 @@ You can also configure logging with environment variables:
80
126
  - `SHOKO_LOG_PATH=/path/to/log` Write JSON logs to a file.
81
127
  - `SHOKO_LOG_LEVEL=info` Set log level.
82
128
  - `SHOKO_PROFILE_PATH=/path/to/profile` Write a performance profile.
129
+
130
+ ## Testing
131
+
132
+ Required guardrails lane:
133
+
134
+ ```bash
135
+ bundle exec rake test:guardrails
136
+ ```
137
+
138
+ Required non-fixture lane (runs 3 fixed seeds):
139
+
140
+ ```bash
141
+ bundle exec rake test:required
142
+ ```
143
+
144
+ Real-book fixture lane:
145
+
146
+ ```bash
147
+ SHOKO_BOOK_FIXTURES=1 SHOKO_FIXTURES_DIR=/path/to/book-fixtures bundle exec rake test:fixtures
148
+ ```
149
+
150
+ Full suite sanity:
151
+
152
+ ```bash
153
+ bundle exec rspec
154
+ ```
155
+
156
+ ## Benchmarking
157
+
158
+ Run the built-in snappiness benchmark:
159
+
160
+ ```bash
161
+ bundle exec ruby script/bench/snappiness_benchmark.rb
162
+ ```
163
+
164
+ The benchmark prints baseline vs optimized timings for:
165
+
166
+ - `TextMetrics.visible_length` cache impact
167
+ - `TextMetrics.visible_length` ASCII fast-path impact
168
+ - `TextMetrics.truncate_to` ASCII fast-path impact
169
+ - `TextMetrics.wrap_plain_text` cache impact
170
+ - `TextMetrics.wrap_plain_text` result-cache impact
171
+ - `LineAssembler.build` cache impact
172
+ - `LineAssembler::Tokenizer.tokenize` cache impact
173
+ - `LineAssembler.build` tokenize-cache impact
174
+ - `LineAssembler.build` token-width-hints impact
175
+ - `LineAssembler.build` token-pipeline combined impact
176
+ - `WrappingService.wrap_window` prefetch-range reuse impact
177
+ - `LineGeometryBuilder.build` repeated-line cell cache impact
178
+ - `TerminalBuffer::Frame.write` ASCII fast-path impact
179
+ - `LineContentComposer.compose` repeated-line cache impact
180
+ - `ManifestShaFinder.sha` large-manifest lookup impact
181
+ - `JsonCacheStore.manifest_rows` repeated-read cache impact
data/Rakefile CHANGED
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/gem_tasks'
4
+ require 'json'
5
+ require 'English'
6
+ require 'fileutils'
7
+ require 'shellwords'
4
8
  require 'rspec/core/rake_task'
5
9
  require 'rubocop/rake_task'
6
10
 
@@ -13,9 +17,84 @@ task quality: %i[spec rubocop]
13
17
  task default: :quality
14
18
 
15
19
  namespace :test do
20
+ module RSpecLane
21
+ module_function
22
+
23
+ RESULT_DIR = File.expand_path('tmp/rspec-results', __dir__)
24
+
25
+ def run!(name:, args:, env: {})
26
+ FileUtils.mkdir_p(RESULT_DIR)
27
+ output_path = File.join(RESULT_DIR, "#{name}.json")
28
+ command = ['bundle', 'exec', 'rspec', *args, '--format', 'json', '--out', output_path]
29
+
30
+ puts "==> #{name}: #{Shellwords.join(command)}"
31
+ success = system(env, *command)
32
+ pending = pending_count(output_path)
33
+
34
+ raise "Pending examples detected in #{name}: #{pending}" if pending.positive?
35
+ raise "RSpec failed in #{name} (exit #{$CHILD_STATUS.exitstatus})" unless success
36
+ end
37
+
38
+ def pending_count(path)
39
+ return 0 unless File.exist?(path)
40
+
41
+ payload = JSON.parse(File.read(path))
42
+ payload.dig('summary', 'pending_count').to_i
43
+ rescue JSON::ParserError => e
44
+ raise "Unable to parse RSpec JSON report at #{path}: #{e.message}"
45
+ end
46
+ end
47
+
48
+ desc 'Run architecture and wiring guardrail specs only'
49
+ task :guardrails do
50
+ FileUtils.mkdir_p(RSpecLane::RESULT_DIR)
51
+ fallback_report_path = File.join(RSpecLane::RESULT_DIR, 'fallback-report.json')
52
+ fallback_command = ['ruby', File.expand_path('script/architecture/fallback_report.rb', __dir__)]
53
+ puts "==> fallback-report: #{Shellwords.join(fallback_command)}"
54
+ fallback_payload = IO.popen(fallback_command, &:read)
55
+ File.write(fallback_report_path, fallback_payload)
56
+ raise 'Fallback report detected guardrail offenders' unless $CHILD_STATUS.success?
57
+
58
+ guardrail_targets = [
59
+ 'spec/core/architecture',
60
+ 'spec/adapters/input/command_bus_only_bindings_spec.rb',
61
+ 'spec/adapters/input/command_binding_completeness_spec.rb',
62
+ 'spec/bootstrap/dependencies/bundle_guardrails_spec.rb',
63
+ ]
64
+
65
+ RSpecLane.run!(
66
+ name: 'guardrails',
67
+ args: [*guardrail_targets, '--seed', '10101']
68
+ )
69
+ end
70
+
71
+ desc 'Run required non-fixture specs across fixed deterministic seeds'
72
+ task :required do
73
+ %w[10101 20202 30303].each do |seed|
74
+ RSpecLane.run!(
75
+ name: "required-seed-#{seed}",
76
+ args: ['--tag', '~requires_book_fixtures', '--seed', seed]
77
+ )
78
+ end
79
+ end
80
+
81
+ desc 'Run fixture-dependent specs only'
82
+ task :fixtures do
83
+ env = {
84
+ 'SHOKO_BOOK_FIXTURES' => '1',
85
+ }
86
+ env['SHOKO_FIXTURES_DIR'] = ENV['SHOKO_FIXTURES_DIR'] if ENV.key?('SHOKO_FIXTURES_DIR')
87
+
88
+ RSpecLane.run!(
89
+ name: 'fixtures',
90
+ env: env,
91
+ args: ['--tag', 'requires_book_fixtures', '--seed', '30303']
92
+ )
93
+ end
94
+
16
95
  desc 'Run tests with coverage'
17
96
  task :coverage do
18
- ENV['COVERAGE'] = 'true'
97
+ ENV['COVERAGE'] = '1'
19
98
  Rake::Task['spec'].invoke
20
99
  end
21
100
  end
data/bin/shoko CHANGED
@@ -7,13 +7,19 @@ $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
7
7
 
8
8
  begin
9
9
  # Only activate Bundler when explicitly running under it (e.g. bundle exec).
10
- if ENV['BUNDLE_GEMFILE'] || ENV['BUNDLE_BIN_PATH']
11
- require 'bundler/setup'
12
- end
10
+ require 'bundler/setup' if ENV['BUNDLE_GEMFILE'] || ENV['BUNDLE_BIN_PATH']
13
11
  rescue LoadError
14
12
  # Running without Bundler (e.g., installed gem).
15
13
  end
16
14
 
17
15
  require 'shoko'
18
16
 
19
- Shoko::CLI.run
17
+ Shoko::Adapters::Input::CLI.run(
18
+ ARGV,
19
+ app_factory: lambda do |epub_path:, log_config:|
20
+ Shoko::Bootstrap::ContainerFactory.build_unified_application(epub_path: epub_path, log_config: log_config)
21
+ end,
22
+ folder_import_factory: lambda do |log_config:|
23
+ Shoko::Bootstrap::ContainerFactory.build_cli_folder_import_context(log_config: log_config)
24
+ end
25
+ )
data/bin/start CHANGED
@@ -7,13 +7,19 @@ $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
7
7
 
8
8
  begin
9
9
  # Only activate Bundler when explicitly running under it (e.g. bundle exec).
10
- if ENV['BUNDLE_GEMFILE'] || ENV['BUNDLE_BIN_PATH']
11
- require 'bundler/setup'
12
- end
10
+ require 'bundler/setup' if ENV['BUNDLE_GEMFILE'] || ENV['BUNDLE_BIN_PATH']
13
11
  rescue LoadError
14
12
  # Running without Bundler (e.g., installed gem).
15
13
  end
16
14
 
17
15
  require 'shoko'
18
16
 
19
- Shoko::CLI.run
17
+ Shoko::Adapters::Input::CLI.run(
18
+ ARGV,
19
+ app_factory: lambda do |epub_path:, log_config:|
20
+ Shoko::Bootstrap::ContainerFactory.build_unified_application(epub_path: epub_path, log_config: log_config)
21
+ end,
22
+ folder_import_factory: lambda do |log_config:|
23
+ Shoko::Bootstrap::ContainerFactory.build_cli_folder_import_context(log_config: log_config)
24
+ end
25
+ )
@@ -0,0 +1,173 @@
1
+ # Hexagonal Architecture Adherence Checklist (Zero-Fallback Cutover)
2
+
3
+ Date: 2026-03-03
4
+ Scope: `lib/shoko/**` runtime + architecture guardrails/spec gates
5
+
6
+ ## Completed
7
+
8
+ - [x] Removed optional DI resolution usage:
9
+ - zero `resolve_optional(` occurrences in `lib/shoko`.
10
+ - `DependencyContainer` exposes mandatory `resolve` only.
11
+ - [x] Removed two-phase reader runtime wiring from public API:
12
+ - no `defer_runtime_setup`.
13
+ - no `attach_runtime_components!`.
14
+ - reader runtime graph is single-phase via constructor-time runtime factory.
15
+ - [x] Completed inbound boundary ownership migration:
16
+ - adapters no longer include inbound intent handler ports.
17
+ - adapter-owned intent handler classes removed.
18
+ - application intent handlers added under `application/use_cases/intents`.
19
+ - [x] Removed reflection-style probing/dispatch globally in runtime code:
20
+ - zero `respond_to?(` occurrences.
21
+ - zero `public_send` occurrences.
22
+ - zero dynamic `send(` occurrences.
23
+ - [x] Removed collaborator-probing rescues:
24
+ - zero `rescue NoMethodError` collaborator probing occurrences.
25
+ - [x] Removed known swallow/fallback runtime behaviors in strict paths:
26
+ - reader quit/save progress now fails fast.
27
+ - event bus subscriber failures are logged then re-raised.
28
+ - render state writer failures are logged then re-raised.
29
+ - document path resolver no longer masks resolver failures.
30
+ - kitty image line renderer no longer suppresses renderer exceptions.
31
+ - [x] Removed known mechanical migration violations:
32
+ - bare `rescue =>` patterns removed (runtime code).
33
+ - inline `... rescue ...` expressions removed from runtime code.
34
+ - [x] Boundary translation hardened:
35
+ - document load failures are translated to `Shoko::BookParseError`.
36
+ - dictionary catalog boundary uses typed `CatalogError < Shoko::Error`.
37
+ - cache import boundary propagates typed malformed-book failures (`BookParseError` / `MalformedBookInputError`) with no adapter shim fallback.
38
+ - [x] Completed inbound handler decoupling from adapter controllers:
39
+ - application intent handlers now depend on typed executors (`ReaderIntentExecutor` / `MenuIntentExecutor`).
40
+ - adapter controller dispatch moved to adapter bridges.
41
+ - no `@reader_controller.` / `@menu_controller.` loopback remains in app intent handlers.
42
+ - [x] Replaced bootstrap session contexts with launch-state ports/adapters:
43
+ - added `Core::Ports::Outbound::ReaderLaunchState` and `MenuLaunchState`.
44
+ - runtime adapters added in `adapters/runtime/session_state`.
45
+ - `ReaderSessionContext` / `MenuSessionContext` removed from bootstrap.
46
+ - application workflows and unified app now use launch-state ports.
47
+ - [x] Enforced symbol-only input command execution contract:
48
+ - `Adapters::Input::Commands.execute` now rejects non-symbol commands.
49
+ - legacy proc/object/array execution paths removed.
50
+ - [x] Optional dependency hard-error path introduced for sqlite execution:
51
+ - added `Shared::OptionalDependency.require_gem!`.
52
+ - added `DependencyUnavailableError` and used it in sqlite runtime load path.
53
+ - dictionary repository now remains wired in auto mode; missing sqlite fails explicitly on invocation.
54
+ - [x] Fixed reader relaunch crash after closing/opening another book:
55
+ - reader launch state now clears background worker on reader exit (`RuntimeExecution#run_reader` ensure block), preventing reuse of stopped workers.
56
+ - [x] Added canonical zero-fallback architecture guardrails:
57
+ - `spec/core/architecture/no_rescue_literal_default_spec.rb`
58
+ - `spec/core/architecture/no_overlapping_rescue_chains_spec.rb`
59
+ - `spec/core/architecture/no_stale_optional_resolution_spec.rb`
60
+ - `spec/core/architecture/no_standard_error_rescue_spec.rb`
61
+ - [x] Removed duplicate/overlapping rescue chains that created unreachable branches.
62
+ - importer translation chains fixed (`fb2`, `kindle`, `pdf`, `rtf`).
63
+ - cache/string translation chain fixed (`lazy_file_string`).
64
+ - CLI contract coercion rescue overlaps fixed.
65
+ - bootstrap background worker fallback contract rescue overlap fixed.
66
+ - [x] Removed stale optional-resolution scaffolding in reader composition.
67
+ - `ReaderBuilder#resolve_many` now has a single deterministic resolve path.
68
+ - [x] Eliminated explicit `rescue StandardError` usage in `lib/shoko/**`.
69
+ - fail-fast boundaries now use explicit/translated rescue flows without `rescue StandardError`.
70
+ - [x] Completed hard-cutover removal of legacy callable factories.
71
+ - removed DI keys/usages of `:document_service_factory` and `:background_worker_factory`.
72
+ - replaced with typed outbound ports `DocumentLoader` and `BackgroundWorkerBuilder`.
73
+ - [x] Added typed progress boundary and removed adapter-to-adapter coupling in the progress path.
74
+ - new port `Core::Ports::Outbound::ProgressRepository`.
75
+ - new core value `Core::Models::ReadingProgress`.
76
+ - input adapter progress actions now consume typed core progress data only.
77
+ - [x] Removed Proc-type migration fallbacks from runtime paths.
78
+ - zero `is_a?(Proc)` checks remain in `lib/shoko`.
79
+ - [x] Removed implicit `NullRuntimeConfig` fallback expressions from runtime code.
80
+ - runtime config is now explicit at composition boundaries and test builders.
81
+ - [x] Removed `rescue Exception` and bare string raises from `lib/shoko`.
82
+ - `rescue Exception`: `0`
83
+ - `raise '...'` / `raise "..."`: `0`
84
+ - [x] Added permanent guardrails for zero-fallback constraints.
85
+ - `spec/core/architecture/no_rescue_exception_spec.rb`
86
+ - `spec/core/architecture/no_bare_string_raise_spec.rb`
87
+ - `spec/core/architecture/no_implicit_null_runtime_config_spec.rb`
88
+ - `spec/core/architecture/no_legacy_factory_keys_spec.rb`
89
+ - `spec/core/architecture/no_proc_type_fallbacks_spec.rb`
90
+ - [x] Fixed post-cutover runtime regressions in browse/menu flow.
91
+ - directory scanner recursion now works (books populate in Browse).
92
+ - scanner roots narrowed to explicit book directories by default, with opt-in overrides via `SHOKO_BOOK_SCAN_DIRS`.
93
+ - metadata extraction wiring includes required `path_ops` / `file_probe`.
94
+ - terminal and menu styling normalize binary-encoded text safely for UTF-8 rendering.
95
+ - annotation editor session/controller event handling now enforces hash payloads; non-event return values (e.g. cursor timestamp floats) are ignored safely.
96
+ - [x] Completed fatal external-input hard-break semantics across runtime boundaries.
97
+ - added `FatalExternalInputError` hierarchy with typed subclasses (`MalformedBookInputError`, `MalformedMetadataInputError`, `MalformedDictionaryInputError`).
98
+ - top boundaries now terminate with exit code `2` on fatal external input (`CLI`, menu lifecycle, reader lifecycle).
99
+ - fatal logs use stable event IDs (`fatal.external_input.book|metadata|dictionary`).
100
+ - [x] Removed rescue-fallback defaults and hardened guardrails to enforce zero fallback.
101
+ - AST-based analyzer now detects literal defaults (`[]`, `{}`, `''`, `""`, booleans, symbols, numeric literals) and variable passthrough.
102
+ - added guardrails:
103
+ - `no_rescue_numeric_default_spec.rb`
104
+ - `no_swallowing_rescue_spec.rb`
105
+ - `no_noop_reraise_rescue_spec.rb`
106
+ - `no_mixed_symbol_string_key_reads_in_application_spec.rb`
107
+ - `no_cross_adapter_runtime_coupling_spec.rb`
108
+ - added deterministic report: `script/architecture/fallback_report.rb` (wired into `test:guardrails`).
109
+ - [x] Completed typed boundary migration for key menu/runtime flows.
110
+ - introduced typed models used across workflow boundaries:
111
+ - `MenuBook`
112
+ - `AnnotationSelection`
113
+ - `PendingJumpPayload`
114
+ - `DictionaryCatalogEntry`
115
+ - removed mixed symbol/string dual-key normalization from `application/**` (`x[:k] || x['k']`).
116
+ - [x] Removed cross-adapter runtime coupling in flagged paths.
117
+ - `DocumentLoaderAdapter` now depends on outbound `BookCachePipelineFactory` port.
118
+ - output terminal runtime config no longer depends on runtime-adapter null type directly.
119
+ - [x] Hard-deleted legacy command/fallback APIs.
120
+ - removed unused bookmark command factory methods (`remove_bookmark`, `toggle_bookmark`, `jump_to_bookmark`).
121
+ - removed cache-import compatibility wrapper error type (`CacheImportAdapter::ImportError`).
122
+
123
+ ## Open Items
124
+
125
+ - [ ] Persist benchmark baselines and enforce CI threshold checks.
126
+ - [ ] Fixture lane is blocked locally by missing required books (see verification snapshot).
127
+ - [ ] Extend optional dependency hard-error semantics to remaining optional capability execution paths (not only sqlite).
128
+
129
+ ## Verification Checklist
130
+
131
+ - [x] `bundle exec rspec spec/core/architecture spec/bootstrap/dependencies`
132
+ - [x] `bundle exec rake test:guardrails`
133
+ - [x] `bundle exec rake test:required`
134
+ - [x] `bundle exec rspec` (fixtures excluded by default tag filter)
135
+ - [ ] `bundle exec rake test:fixtures` (blocked locally)
136
+
137
+ ## Verification Snapshot
138
+
139
+ - `bundle exec rspec spec/core/architecture spec/bootstrap/dependencies`:
140
+ - 101 examples, 0 failures.
141
+ - `bundle exec rake test:guardrails`:
142
+ - pass (`fallback_report` summary all zero).
143
+ - `bundle exec rake test:required`:
144
+ - pass for seeds `10101`, `20202`, `30303` (973 examples/seed, 0 failures).
145
+ - `bundle exec rspec`:
146
+ - 973 examples, 0 failures (with `requires_book_fixtures` excluded by default).
147
+ - `bundle exec rake test:fixtures`:
148
+ - blocked by missing files:
149
+ - `Persuasion (Jane Austen).mobi`
150
+ - `Pride Prejudice (Jane Austen).azw`
151
+ - `Emma (Jane Austen).azw3`
152
+ - `Pride And Prejudice (Austen Jane).rtf`
153
+ - Artifact sweeps (`lib/shoko`):
154
+ - `resolve_optional(`: `0`
155
+ - `rescue StandardError`: `0`
156
+ - `rescue Exception`: `0`
157
+ - bare `rescue =>`: `0`
158
+ - bare string raise (`raise '...'` / `raise "..."`): `0`
159
+ - duplicate `rescue ArgumentError, ArgumentError`: `0`
160
+ - duplicate/overlapping same-class rescue chains with unreachable fallback branches: `0`
161
+ - stale optional resolution ternary (`optional ? resolve : resolve`): `0`
162
+ - inline `... rescue ...` expressions: `0`
163
+ - `rescue NoMethodError` probing: `0`
164
+ - `respond_to?` / `public_send` / dynamic `send(`: `0`
165
+ - `fallback_report` zero-fallback summary:
166
+ - `fallback_literal_defaults`: `0`
167
+ - `numeric_rescue_defaults`: `0`
168
+ - `no_op_reraise_rescues`: `0`
169
+ - `fatal_input_swallow_rescues`: `0`
170
+ - `mixed_key_reads_application`: `0`
171
+ - legacy factory keys/usages (`document_service_factory|background_worker_factory`): `0`
172
+ - proc-type fallbacks (`is_a?(Proc)`): `0`
173
+ - implicit null runtime fallback expression (`|| ...NullRuntimeConfig.instance`): `0`
@@ -5,6 +5,7 @@
5
5
  #
6
6
  # Public API:
7
7
  # Zip::File.open(path) { |zip| ... }
8
+ # zip.entries -> Array<Zip::Entry>
8
9
  # zip.read(entry_path) -> String (binary)
9
10
  # zip.find_entry(entry_path) -> entry or nil
10
11
  # zip.close ; zip.closed?
@@ -269,21 +270,9 @@ module Zip
269
270
  attr_reader :max_entry_compressed, :max_entry_uncompressed, :max_total_uncompressed
270
271
 
271
272
  def initialize(max_entry_uncompressed:, max_entry_compressed:, max_total_uncompressed:)
272
- @max_entry_uncompressed = LimitResolver.resolve(
273
- max_entry_uncompressed,
274
- env: 'SHOKO_ZIP_MAX_ENTRY_BYTES',
275
- default: Limits::MAX_ENTRY_UNCOMPRESSED
276
- )
277
- @max_entry_compressed = LimitResolver.resolve(
278
- max_entry_compressed,
279
- env: 'SHOKO_ZIP_MAX_ENTRY_COMPRESSED_BYTES',
280
- default: Limits::MAX_ENTRY_COMPRESSED
281
- )
282
- @max_total_uncompressed = LimitResolver.resolve(
283
- max_total_uncompressed,
284
- env: 'SHOKO_ZIP_MAX_TOTAL_BYTES',
285
- default: Limits::MAX_TOTAL_UNCOMPRESSED
286
- )
273
+ @max_entry_uncompressed = LimitResolver.resolve(max_entry_uncompressed, default: Limits::MAX_ENTRY_UNCOMPRESSED)
274
+ @max_entry_compressed = LimitResolver.resolve(max_entry_compressed, default: Limits::MAX_ENTRY_COMPRESSED)
275
+ @max_total_uncompressed = LimitResolver.resolve(max_total_uncompressed, default: Limits::MAX_TOTAL_UNCOMPRESSED)
287
276
  @total_uncompressed_bytes = 0
288
277
  end
289
278
 
@@ -351,34 +340,28 @@ module Zip
351
340
  end
352
341
  end
353
342
 
354
- # Resolves limit values from arguments, environment, or defaults
343
+ # Resolves limit values from explicit arguments or defaults.
355
344
  class LimitResolver
356
- def self.resolve(value, env:, default:)
357
- new(value, env, default).resolve
345
+ def self.resolve(value, default:)
346
+ new(value, default).resolve
358
347
  end
359
348
 
360
- def initialize(value, env, default)
349
+ def initialize(value, default)
361
350
  @value = value
362
- @env = env
363
351
  @default = default
364
352
  end
365
353
 
366
354
  def resolve
367
- candidate = value_or_env
368
- parsed = parse_integer(candidate)
355
+ parsed = parse_integer(@value)
369
356
  valid_positive_or_default(parsed)
370
357
  end
371
358
 
372
359
  private
373
360
 
374
- def value_or_env
375
- @value || ENV.fetch(@env, nil)
376
- end
377
-
378
361
  def parse_integer(candidate)
362
+ return nil if candidate.nil?
363
+
379
364
  Integer(candidate)
380
- rescue StandardError
381
- nil
382
365
  end
383
366
 
384
367
  def valid_positive_or_default(parsed)
@@ -479,8 +462,8 @@ module Zip
479
462
 
480
463
  def close_inflater(inflater)
481
464
  inflater&.close
482
- rescue StandardError
483
- nil
465
+ rescue Shoko::Error
466
+ raise
484
467
  end
485
468
 
486
469
  def decompress_all(inflater, entry, remaining_bytes)
@@ -657,7 +640,7 @@ module Zip
657
640
 
658
641
  def self.close_safely(zip_file)
659
642
  zip_file.close
660
- rescue StandardError
643
+ rescue Shoko::Error
661
644
  # ignore close errors
662
645
  end
663
646
 
@@ -674,7 +657,7 @@ module Zip
674
657
  @io = @state.io
675
658
  @entries = @state.entries
676
659
  build_index!
677
- rescue StandardError
660
+ rescue Shoko::Error
678
661
  close
679
662
  raise
680
663
  end
@@ -692,6 +675,10 @@ module Zip
692
675
  @entries[normalized_path]
693
676
  end
694
677
 
678
+ def entries
679
+ @entries.values
680
+ end
681
+
695
682
  def read(path)
696
683
  entry = find_entry_or_raise(path)
697
684
  validate_entry_readable(entry)
@@ -730,3 +717,30 @@ module Zip
730
717
  end
731
718
  end
732
719
  end
720
+
721
+ module Shoko
722
+ module Adapters
723
+ module BookSources
724
+ module Archive
725
+ # Adapter-owned archive facade used by book-source importers/loaders.
726
+ module ZipReader
727
+ module_function
728
+
729
+ def open(path, runtime_config: nil, &block)
730
+ Zip::File.open(path, **zip_limit_kwargs(runtime_config), &block)
731
+ end
732
+
733
+ def zip_limit_kwargs(runtime_config)
734
+ return {} unless runtime_config
735
+
736
+ {
737
+ max_entry_uncompressed_bytes: runtime_config.zip_max_entry_uncompressed_bytes,
738
+ max_entry_compressed_bytes: runtime_config.zip_max_entry_compressed_bytes,
739
+ max_total_uncompressed_bytes: runtime_config.zip_max_total_uncompressed_bytes
740
+ }
741
+ end
742
+ end
743
+ end
744
+ end
745
+ end
746
+ end