pageflow 17.1.0 → 17.2.0

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 (302) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +319 -345
  3. data/README.md +3 -4
  4. data/admins/pageflow/accounts.rb +6 -6
  5. data/admins/pageflow/entry.rb +10 -8
  6. data/admins/pageflow/membership.rb +11 -11
  7. data/admins/pageflow/translations.rb +5 -5
  8. data/app/assets/images/pageflow/admin/icons/comment.svg +1 -0
  9. data/app/assets/javascripts/pageflow/dist/ui.js +1350 -630
  10. data/app/assets/javascripts/pageflow/editor/vendor.js +0 -1
  11. data/app/assets/javascripts/pageflow/ui.js +0 -1
  12. data/app/assets/stylesheets/pageflow/admin/entry_comments_indicator.scss +45 -0
  13. data/app/assets/stylesheets/pageflow/admin/permalink_input.scss +8 -1
  14. data/app/assets/stylesheets/pageflow/admin.scss +1 -0
  15. data/app/assets/stylesheets/pageflow/animations/spin.scss +9 -0
  16. data/app/assets/stylesheets/pageflow/animations.scss +1 -0
  17. data/app/assets/stylesheets/pageflow/editor/background_positioning.scss +21 -5
  18. data/app/assets/stylesheets/pageflow/editor/base.scss +9 -1
  19. data/app/assets/stylesheets/pageflow/editor/drop_down_button.scss +13 -1
  20. data/app/assets/stylesheets/pageflow/editor/edit_configuration_view.scss +5 -0
  21. data/app/assets/stylesheets/pageflow/editor/file_folders.scss +127 -0
  22. data/app/assets/stylesheets/pageflow/editor/file_meta_data.scss +0 -1
  23. data/app/assets/stylesheets/pageflow/editor/file_meta_data_overlay.scss +45 -0
  24. data/app/assets/stylesheets/pageflow/editor/file_preview.scss +138 -0
  25. data/app/assets/stylesheets/pageflow/editor/file_references.scss +68 -0
  26. data/app/assets/stylesheets/pageflow/editor/file_settings_dialog.scss +1 -1
  27. data/app/assets/stylesheets/pageflow/editor/file_stage_icons.scss +17 -0
  28. data/app/assets/stylesheets/pageflow/editor/file_stages.scss +25 -32
  29. data/app/assets/stylesheets/pageflow/editor/file_thumbnails.scss +31 -48
  30. data/app/assets/stylesheets/pageflow/editor/file_type_pills.scss +73 -0
  31. data/app/assets/stylesheets/pageflow/editor/files.scss +230 -50
  32. data/app/assets/stylesheets/pageflow/editor/filtered_files.scss +142 -14
  33. data/app/assets/stylesheets/pageflow/editor/folder_breadcrumb.scss +40 -0
  34. data/app/assets/stylesheets/pageflow/editor/info_box.scss +12 -0
  35. data/app/assets/stylesheets/pageflow/editor/inputs/edit_defaults_button.scss +15 -0
  36. data/app/assets/stylesheets/pageflow/editor/inputs.scss +1 -0
  37. data/app/assets/stylesheets/pageflow/editor/list.scss +88 -38
  38. data/app/assets/stylesheets/pageflow/editor/manage_files.scss +9 -0
  39. data/app/assets/stylesheets/pageflow/editor/menu.scss +14 -0
  40. data/app/assets/stylesheets/pageflow/editor/move_to_folder_dialog.scss +83 -0
  41. data/app/assets/stylesheets/pageflow/mixins/pageflow.scss +9 -5
  42. data/app/assets/stylesheets/pageflow/ui/color_picker.scss +230 -0
  43. data/app/assets/stylesheets/pageflow/ui/forms.scss +34 -4
  44. data/app/assets/stylesheets/pageflow/ui/functions.scss +12 -2
  45. data/app/assets/stylesheets/pageflow/ui/input/color_input.scss +9 -16
  46. data/app/assets/stylesheets/pageflow/ui/input/text_area_input.scss +15 -0
  47. data/app/assets/stylesheets/pageflow/ui/overrides.scss +8 -0
  48. data/app/assets/stylesheets/pageflow/ui/properties.scss +29 -0
  49. data/app/assets/stylesheets/pageflow/ui/tabs_view.scss +8 -3
  50. data/app/assets/stylesheets/pageflow/ui.scss +4 -2
  51. data/app/controllers/pageflow/editor/entries_controller.rb +1 -0
  52. data/app/controllers/pageflow/editor/file_folders_controller.rb +80 -0
  53. data/app/controllers/pageflow/editor/file_import_controller.rb +1 -0
  54. data/app/controllers/pageflow/editor/files_controller.rb +15 -6
  55. data/app/controllers/pageflow/editor/oembeds_controller.rb +21 -0
  56. data/app/controllers/pageflow/entries_controller.rb +17 -13
  57. data/app/controllers/pageflow/review/comment_thread_reads_controller.rb +30 -0
  58. data/app/controllers/pageflow/review/comment_threads_controller.rb +60 -0
  59. data/app/controllers/pageflow/review/comments_controller.rb +53 -0
  60. data/app/controllers/pageflow/revisions_controller.rb +4 -1
  61. data/app/helpers/pageflow/admin/entries_helper.rb +40 -0
  62. data/app/helpers/pageflow/admin/widgets_helper.rb +2 -2
  63. data/app/helpers/pageflow/common_entry_seed_helper.rb +5 -3
  64. data/app/helpers/pageflow/editor/config_helper.rb +3 -1
  65. data/app/helpers/pageflow/entries_helper.rb +24 -42
  66. data/app/helpers/pageflow/entry_json_seed_helper.rb +4 -4
  67. data/app/helpers/pageflow/public_i18n_helper.rb +9 -8
  68. data/app/helpers/pageflow/structured_data_helper.rb +4 -1
  69. data/app/helpers/pageflow/themes_helper.rb +1 -3
  70. data/app/helpers/pageflow/widgets_helper.rb +2 -1
  71. data/app/inputs/pageflow_permalink_input.rb +6 -2
  72. data/app/models/concerns/pageflow/auto_generated_perma_id.rb +11 -4
  73. data/app/models/concerns/pageflow/reusable_file.rb +1 -1
  74. data/app/models/concerns/pageflow/serialization_blacklist.rb +2 -4
  75. data/app/models/concerns/pageflow/translated_attributes.rb +101 -0
  76. data/app/models/concerns/pageflow/uploadable_file.rb +1 -1
  77. data/app/models/pageflow/comment.rb +30 -0
  78. data/app/models/pageflow/comment_thread.rb +65 -0
  79. data/app/models/pageflow/comment_thread_read.rb +22 -0
  80. data/app/models/pageflow/customized_theme.rb +3 -2
  81. data/app/models/pageflow/draft_entry.rb +20 -8
  82. data/app/models/pageflow/entry.rb +2 -0
  83. data/app/models/pageflow/entry_at_revision.rb +9 -1
  84. data/app/models/pageflow/entry_comment_summary.rb +114 -0
  85. data/app/models/pageflow/file_folder.rb +74 -0
  86. data/app/models/pageflow/file_reuse.rb +3 -2
  87. data/app/models/pageflow/file_usage.rb +8 -0
  88. data/app/models/pageflow/image_file.rb +24 -20
  89. data/app/models/pageflow/image_file_url_templates.rb +3 -3
  90. data/app/models/pageflow/legal_info.rb +38 -0
  91. data/app/models/pageflow/nested_revision_component_copy.rb +1 -1
  92. data/app/models/pageflow/permalink.rb +1 -0
  93. data/app/models/pageflow/revision.rb +6 -2
  94. data/app/models/pageflow/site.rb +10 -0
  95. data/app/models/pageflow/used_file.rb +8 -2
  96. data/app/models/pageflow/widget.rb +2 -2
  97. data/app/views/pageflow/audio_files/_audio_file.json.jbuilder +0 -1
  98. data/app/views/pageflow/editor/config/_seeds.json.jbuilder +4 -2
  99. data/app/views/pageflow/editor/entries/_entry.json.jbuilder +2 -1
  100. data/app/views/pageflow/editor/entries/seed.json.erb +1 -0
  101. data/app/views/pageflow/editor/file_folders/_file_folder.json.jbuilder +5 -0
  102. data/app/views/pageflow/editor/file_folders/create.json.jbuilder +1 -0
  103. data/app/views/pageflow/editor/file_folders/index.json.jbuilder +3 -0
  104. data/app/views/pageflow/editor/files/_file.json.jbuilder +2 -1
  105. data/app/views/pageflow/editor/image_files/_image_file.json.jbuilder +3 -0
  106. data/app/views/pageflow/editor/video_files/_video_file.json.jbuilder +3 -0
  107. data/app/views/pageflow/files/_file.json.jbuilder +2 -0
  108. data/app/views/pageflow/image_files/_image_file.json.jbuilder +0 -1
  109. data/app/views/pageflow/review/comment_threads/_comment_thread.json.jbuilder +20 -0
  110. data/app/views/pageflow/review/comment_threads/create.json.jbuilder +2 -0
  111. data/app/views/pageflow/review/comment_threads/index.json.jbuilder +13 -0
  112. data/app/views/pageflow/review/comments/_comment.json.jbuilder +13 -0
  113. data/app/views/pageflow/review/comments/create.json.jbuilder +1 -0
  114. data/app/views/pageflow/structured_data/_entry.json.jbuilder +2 -3
  115. data/app/views/pageflow/video_files/_video_file.json.jbuilder +0 -1
  116. data/config/locales/de.yml +152 -14
  117. data/config/locales/en.yml +150 -14
  118. data/config/routes.rb +14 -0
  119. data/db/migrate/20140418225525_setup_schema.rb +2 -2
  120. data/db/migrate/20251015000000_add_structured_data_type_name_to_revisions.rb +5 -0
  121. data/db/migrate/20251029142849_convert_legacy_social_embeds_to_unified_social_embed.rb +38 -0
  122. data/db/migrate/20251119083347_fix_main_storyline_positions.rb +16 -0
  123. data/db/migrate/20251119083348_ensure_scrolled_entries_have_excursion_storyline.rb +15 -0
  124. data/db/migrate/20260303000000_add_layout_version_to_revisions.rb +6 -0
  125. data/db/migrate/20260323000000_create_comment_threads_and_comments.rb +30 -0
  126. data/db/migrate/20260414000000_add_subject_range_to_comment_threads.rb +5 -0
  127. data/db/migrate/20260415000000_add_section_perma_id_to_comment_threads.rb +5 -0
  128. data/db/migrate/20260727000000_add_quote_to_comments.rb +5 -0
  129. data/db/migrate/20260728000000_create_file_folders.rb +17 -0
  130. data/db/migrate/20260805000000_add_attribute_translations_to_sites.rb +5 -0
  131. data/db/migrate/20260806000000_add_edited_at_to_comments.rb +5 -0
  132. data/db/migrate/20260810000000_create_pageflow_scrolled_lottie_files.rb +24 -0
  133. data/db/migrate/20260817000000_create_comment_thread_reads.rb +16 -0
  134. data/db/migrate/20260819000000_add_unread_comments_since_at_to_users.rb +18 -0
  135. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/editor.js +8665 -3890
  136. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/frontend.js +979 -3571
  137. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-client.js +20199 -28120
  138. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-server.js +18652 -26584
  139. data/entry_types/scrolled/app/assets/stylesheets/pageflow_scrolled/ui.scss +2 -0
  140. data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/content_elements_controller.rb +33 -3
  141. data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/sections_controller.rb +2 -3
  142. data/entry_types/scrolled/app/controllers/pageflow_scrolled/entries_controller.rb +21 -1
  143. data/entry_types/scrolled/app/helpers/pageflow_scrolled/cache_helper.rb +3 -3
  144. data/entry_types/scrolled/app/helpers/pageflow_scrolled/editor/entry_json_seed_helper.rb +7 -5
  145. data/entry_types/scrolled/app/helpers/pageflow_scrolled/editor/seed_html_helper.rb +5 -2
  146. data/entry_types/scrolled/app/helpers/pageflow_scrolled/entry_json_seed_helper.rb +13 -5
  147. data/entry_types/scrolled/app/helpers/pageflow_scrolled/i18n_helper.rb +28 -13
  148. data/entry_types/scrolled/app/helpers/pageflow_scrolled/packs_helper.rb +26 -11
  149. data/entry_types/scrolled/app/helpers/pageflow_scrolled/react_server_side_rendering_helper.rb +2 -2
  150. data/entry_types/scrolled/app/helpers/pageflow_scrolled/sprockets_helper.rb +16 -0
  151. data/entry_types/scrolled/app/helpers/pageflow_scrolled/themes_helper.rb +174 -3
  152. data/entry_types/scrolled/app/models/pageflow_scrolled/content_element.rb +49 -7
  153. data/entry_types/scrolled/app/models/pageflow_scrolled/lottie_file.rb +6 -0
  154. data/entry_types/scrolled/app/models/pageflow_scrolled/lottie_file_url_templates.rb +16 -0
  155. data/entry_types/scrolled/app/models/pageflow_scrolled/storyline.rb +1 -0
  156. data/entry_types/scrolled/app/views/pageflow_scrolled/entries/show.html.erb +13 -12
  157. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_entry.json.jbuilder +30 -10
  158. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_theme.json.jbuilder +9 -0
  159. data/entry_types/scrolled/app/views/pageflow_scrolled/favicons/_entry.html.erb +1 -1
  160. data/entry_types/scrolled/config/configuration_schemas/pageflow-scrolled.json +414 -0
  161. data/entry_types/scrolled/config/locales/de.yml +512 -65
  162. data/entry_types/scrolled/config/locales/en.yml +518 -52
  163. data/entry_types/scrolled/lib/pageflow_scrolled/additional_seed_data.rb +2 -2
  164. data/entry_types/scrolled/lib/pageflow_scrolled/configuration.rb +51 -6
  165. data/entry_types/scrolled/lib/pageflow_scrolled/configuration_schemas.rb +91 -0
  166. data/entry_types/scrolled/lib/pageflow_scrolled/engine.rb +2 -1
  167. data/entry_types/scrolled/lib/pageflow_scrolled/entry_file_reference_locations.rb +71 -0
  168. data/entry_types/scrolled/lib/pageflow_scrolled/entry_structured_data_types/faq_page.rb +58 -0
  169. data/entry_types/scrolled/lib/pageflow_scrolled/file_reference_locations.rb +69 -0
  170. data/entry_types/scrolled/lib/pageflow_scrolled/plugin.rb +205 -22
  171. data/entry_types/scrolled/lib/pageflow_scrolled/react_widget_type.rb +2 -4
  172. data/entry_types/scrolled/lib/pageflow_scrolled/seeds.rb +56 -36
  173. data/entry_types/scrolled/lib/pageflow_scrolled/theme_options_default_scale.rb +35 -0
  174. data/entry_types/scrolled/lib/pageflow_scrolled.rb +13 -0
  175. data/entry_types/scrolled/lib/tasks/pageflow_scrolled/storybook.rake +19 -1
  176. data/entry_types/scrolled/package/config/configurationSchemas.js +70 -0
  177. data/entry_types/scrolled/package/config/rollup/configurationSchemas.js +19 -0
  178. data/entry_types/scrolled/package/config/webpack.js +72 -0
  179. data/entry_types/scrolled/package/contentElements/hotspots-frontend.css +1 -1
  180. data/entry_types/scrolled/package/contentElements/hotspots-frontend.js +1181 -915
  181. data/entry_types/scrolled/package/contentElements/inlineBeforeAfter-frontend.css +1 -0
  182. data/entry_types/scrolled/package/contentElements/inlineBeforeAfter-frontend.js +219 -0
  183. data/entry_types/scrolled/package/contentElements/lottieAnimation-frontend.css +1 -0
  184. data/entry_types/scrolled/package/contentElements/lottieAnimation-frontend.js +244 -0
  185. data/entry_types/scrolled/package/contentElements/socialEmbed-frontend.css +1 -0
  186. data/entry_types/scrolled/package/contentElements/socialEmbed-frontend.js +823 -0
  187. data/entry_types/scrolled/package/contentElements/tikTokEmbed-frontend.js +71 -21
  188. data/entry_types/scrolled/package/contentElements/twitterEmbed-frontend.css +1 -0
  189. data/entry_types/scrolled/package/contentElements/twitterEmbed-frontend.js +277 -0
  190. data/entry_types/scrolled/package/contentElements/videoEmbed-frontend.css +1 -0
  191. data/entry_types/scrolled/package/contentElements/videoEmbed-frontend.js +244 -0
  192. data/entry_types/scrolled/package/contentElements-editor.js +1915 -1097
  193. data/entry_types/scrolled/package/contentElements-frontend.css +1 -1
  194. data/entry_types/scrolled/package/contentElements-frontend.js +3093 -1679
  195. data/entry_types/scrolled/package/editor.css +1 -0
  196. data/entry_types/scrolled/package/editor.js +8667 -4077
  197. data/entry_types/scrolled/package/entryState.js +1600 -0
  198. data/entry_types/scrolled/package/frontend/FloatingPortalRootProvider-20c600de.js +1979 -0
  199. data/entry_types/scrolled/package/frontend/FloatingPortalRootProvider-58189220.js +2538 -0
  200. data/entry_types/scrolled/package/frontend/{PhonePlatformContext-9f76033e.js → PhonePlatformContext-035a99fa.js} +7 -12
  201. data/entry_types/scrolled/package/frontend/PhonePlatformContext-9da43797.js +33 -0
  202. data/entry_types/scrolled/package/frontend/Placeholder.module-bf0966b4.js +5267 -0
  203. data/entry_types/scrolled/package/frontend/Placeholder.module-d22a9335.js +4180 -0
  204. data/entry_types/scrolled/package/frontend/ThemeIcon-ab834849.js +754 -0
  205. data/entry_types/scrolled/package/frontend/ThemeIcon-f6cb3a8f.js +773 -0
  206. data/entry_types/scrolled/package/frontend/{ToggleFullscreenCornerButton-788e9cdb.js → ToggleFullscreenCornerButton-30fd1124.js} +6 -6
  207. data/entry_types/scrolled/package/frontend/ToggleFullscreenCornerButton-61c25224.js +72 -0
  208. data/entry_types/scrolled/package/frontend/Viewer-1074fd59.js +147 -0
  209. data/entry_types/scrolled/package/frontend/Viewer-1482ca34.js +480 -0
  210. data/entry_types/scrolled/package/frontend/Viewer-70504231.js +210 -0
  211. data/entry_types/scrolled/package/frontend/{Viewer-0345ce57.js → Viewer-cc8d4072.js} +84 -92
  212. data/entry_types/scrolled/package/frontend/Wavesurfer-656e3c36.js +302 -0
  213. data/entry_types/scrolled/package/frontend/Wavesurfer-b753b838.js +385 -0
  214. data/entry_types/scrolled/package/frontend/commenting.css +1 -0
  215. data/entry_types/scrolled/package/frontend/extensions-167ae143.js +1455 -0
  216. data/entry_types/scrolled/package/frontend/extensions-2318f92b.js +5918 -0
  217. data/entry_types/scrolled/package/frontend/extensions-a461fd13.js +4890 -0
  218. data/entry_types/scrolled/package/frontend/extensions-c98269fd.js +1982 -0
  219. data/entry_types/scrolled/package/frontend/i18n-493cd2a6.js +93 -0
  220. data/entry_types/scrolled/package/frontend/i18n-e06cac14.js +100 -0
  221. data/entry_types/scrolled/package/frontend/{index-eb670c2f.js → index-2e5546f7.js} +36 -44
  222. data/entry_types/scrolled/package/frontend/index-de2b418d.js +130 -0
  223. data/entry_types/scrolled/package/frontend/index.css +1 -1
  224. data/entry_types/scrolled/package/frontend/index.js +1870 -875
  225. data/entry_types/scrolled/package/frontend/inlineEditing.css +1 -0
  226. data/entry_types/scrolled/package/frontend/useContentElementEditorState-a084912e.js +25 -0
  227. data/entry_types/scrolled/package/frontend/useDelayedBoolean-a387d85b.js +24 -0
  228. data/entry_types/scrolled/package/frontend/{usePhonePlatform-c2ba875d.js → usePhonePlatform-4ecb96d4.js} +1 -1
  229. data/entry_types/scrolled/package/frontend/usePhonePlatform-ce9ff08d.js +8 -0
  230. data/entry_types/scrolled/package/frontend-server.js +20 -209
  231. data/entry_types/scrolled/package/package.json +5 -3
  232. data/entry_types/scrolled/package/review.css +1 -0
  233. data/entry_types/scrolled/package/review.js +3306 -0
  234. data/entry_types/scrolled/package/testHelpers.js +836 -359
  235. data/entry_types/scrolled/package/widgets/consentBar.css +1 -1
  236. data/entry_types/scrolled/package/widgets/consentBar.js +269 -290
  237. data/entry_types/scrolled/package/widgets/defaultNavigation.css +2 -2
  238. data/entry_types/scrolled/package/widgets/defaultNavigation.js +911 -352
  239. data/entry_types/scrolled/package/widgets/excursionSheet.css +1 -1
  240. data/entry_types/scrolled/package/widgets/excursionSheet.js +87 -124
  241. data/entry_types/scrolled/package/widgets/iconInlineFileRights.css +1 -1
  242. data/entry_types/scrolled/package/widgets/iconInlineFileRights.js +62 -29
  243. data/entry_types/scrolled/package/widgets/iconScrollIndicator.js +32 -13
  244. data/entry_types/scrolled/package/widgets/mainStorylineSheet.css +1 -1
  245. data/entry_types/scrolled/package/widgets/mainStorylineSheet.js +56 -100
  246. data/entry_types/scrolled/package/widgets/textInlineFileRights.css +1 -1
  247. data/entry_types/scrolled/package/widgets/textInlineFileRights.js +33 -28
  248. data/entry_types/scrolled/package/widgets-server.js +1 -0
  249. data/entry_types/scrolled/spec/factories/lottie_files.rb +29 -0
  250. data/entry_types/scrolled/spec/fixtures/animation.lottie +0 -0
  251. data/entry_types/scrolled/spec/fixtures/font.woff +0 -0
  252. data/entry_types/scrolled/spec/fixtures/font.woff2 +0 -0
  253. data/lib/generators/pageflow/resque/resque_generator.rb +0 -2
  254. data/lib/pageflow/configuration.rb +49 -7
  255. data/lib/pageflow/deprecation.rb +19 -0
  256. data/lib/pageflow/engine.rb +1 -1
  257. data/lib/pageflow/entries_controller_env_helper.rb +32 -2
  258. data/lib/pageflow/entry_export_import/entry_serialization.rb +4 -4
  259. data/lib/pageflow/entry_export_import/page_type_versions.rb +2 -2
  260. data/lib/pageflow/entry_export_import/revision_serialization/import.rb +10 -0
  261. data/lib/pageflow/entry_export_import/revision_serialization.rb +3 -0
  262. data/lib/pageflow/entry_structured_data_types.rb +43 -0
  263. data/lib/pageflow/entry_type_configuration.rb +1 -0
  264. data/lib/pageflow/file_type.rb +2 -2
  265. data/lib/pageflow/file_types.rb +1 -1
  266. data/lib/pageflow/oembed_providers.rb +42 -0
  267. data/lib/pageflow/primary_domain_entry_redirect.rb +6 -0
  268. data/lib/pageflow/rails_version.rb +13 -2
  269. data/lib/pageflow/themes.rb +41 -0
  270. data/lib/pageflow/user_mixin.rb +12 -0
  271. data/lib/pageflow/version.rb +1 -1
  272. data/lib/pageflow.rb +1 -0
  273. data/package/config/eslint-rules/documented-in-toc.js +125 -0
  274. data/package/config/jest/index.js +0 -1
  275. data/package/config/webpack.js +0 -1
  276. data/package/config/webpack5.js +0 -1
  277. data/package/editor.js +4378 -1897
  278. data/package/frontend.js +647 -886
  279. data/package/package.json +2 -1
  280. data/package/review.js +384 -0
  281. data/package/testHelpers.js +248 -188
  282. data/package/ui.js +1346 -630
  283. data/package/vendor/stubs/wysihtml5.js +2 -0
  284. data/spec/factories/comment_thread_reads.rb +10 -0
  285. data/spec/factories/comment_threads.rb +10 -0
  286. data/spec/factories/comments.rb +9 -0
  287. data/spec/factories/file_folders.rb +8 -0
  288. data/spec/factories/published_entries.rb +2 -0
  289. metadata +188 -67
  290. data/app/assets/stylesheets/pageflow/editor/select_button.scss +0 -51
  291. data/entry_types/scrolled/app/helpers/pageflow_scrolled/generated_media_queries_helper.rb +0 -55
  292. data/entry_types/scrolled/package/frontend/FloatingPortalRootProvider-51914be7.js +0 -5439
  293. data/entry_types/scrolled/package/frontend/ThemeIcon-81f2f066.js +0 -1932
  294. data/entry_types/scrolled/package/frontend/Viewer-cdc549cc.js +0 -147
  295. data/entry_types/scrolled/package/frontend/Wavesurfer-1cdc3925.js +0 -313
  296. data/entry_types/scrolled/package/frontend/components-96660ffd.js +0 -3803
  297. data/entry_types/scrolled/package/frontend/inherits-539844a6.js +0 -99
  298. data/entry_types/scrolled/package/frontend/useContentElementEditorState-77fe6c79.js +0 -51
  299. data/entry_types/scrolled/package/widgets/excursionDialog.css +0 -1
  300. data/entry_types/scrolled/package/widgets/excursionDialog.js +0 -109
  301. data/lib/generators/pageflow/resque/templates/resque_enqueue_after_commit_patch.rb +0 -25
  302. data/package/vendor/jquery.minicolors.js +0 -1108
@@ -0,0 +1,2 @@
1
+ @import "pageflow/ui/properties";
2
+ @import "pageflow/ui/overrides";
@@ -17,17 +17,26 @@ module PageflowScrolled
17
17
  section = Section.all_for_revision(@entry.draft).find(params[:section_id])
18
18
 
19
19
  items = params.require(:content_elements).map do |item|
20
- item.transform_keys(&:underscore).permit(:id, :type_name, :_delete, configuration: {})
20
+ item.transform_keys(&:underscore).permit(:id, :type_name, :_delete,
21
+ configuration: {},
22
+ migrate_comment_threads: [])
21
23
  end
22
24
 
23
- @content_elements = ContentElement::Batch.new(section, items).save!
25
+ @content_elements = ContentElement::Batch.new(
26
+ section, items,
27
+ comment_thread_subject_ranges: permitted_subject_ranges
28
+ ).save!
24
29
  rescue ActiveRecord::RecordNotFound
25
30
  head :not_found
26
31
  end
27
32
 
28
33
  def update
29
34
  content_element = ContentElement.all_for_revision(@entry.draft).find(params[:id])
30
- content_element.update(content_element_params)
35
+
36
+ ContentElement.transaction do
37
+ content_element.update(content_element_params)
38
+ update_comment_thread_subject_ranges(content_element)
39
+ end
31
40
 
32
41
  head :no_content
33
42
  rescue ActiveRecord::RecordNotFound
@@ -66,6 +75,27 @@ module PageflowScrolled
66
75
  .permit(:type_name, :position)
67
76
  .merge(configuration:)
68
77
  end
78
+
79
+ def update_comment_thread_subject_ranges(content_element)
80
+ ranges = permitted_subject_ranges
81
+ return if ranges.blank?
82
+
83
+ Pageflow::CommentThread.update_subject_ranges_for(
84
+ revision: @entry.draft,
85
+ subject_type: 'ContentElement',
86
+ subject_id: content_element.perma_id,
87
+ ranges:
88
+ )
89
+ end
90
+
91
+ def permitted_subject_ranges
92
+ ranges = params[:comment_thread_subject_ranges]
93
+ return nil if ranges.blank?
94
+
95
+ ranges.to_unsafe_h.transform_values do |range|
96
+ range.is_a?(Hash) ? range.slice('anchor', 'focus') : {}
97
+ end
98
+ end
69
99
  end
70
100
  end
71
101
  end
@@ -41,11 +41,10 @@ module PageflowScrolled
41
41
 
42
42
  def order
43
43
  chapter = Chapter.all_for_revision(@entry.draft).find(params[:chapter_id])
44
- storyline = chapter.storyline
45
44
 
46
45
  params.require(:ids).each_with_index do |id, index|
47
- storyline.sections.find(id).update(chapter_id: chapter.id,
48
- position: index)
46
+ Section.all_for_revision(@entry.draft).find(id).update(chapter_id: chapter.id,
47
+ position: index)
49
48
  end
50
49
 
51
50
  head :no_content
@@ -15,15 +15,35 @@ module PageflowScrolled
15
15
 
16
16
  def show
17
17
  entry = get_published_entry_from_env
18
+ mode = get_entry_mode_from_env
18
19
 
19
20
  I18n.locale = entry.locale
20
21
 
21
22
  render(
22
23
  locals: {
23
24
  entry:,
24
- widget_scope: get_entry_mode_from_env
25
+ entry_mode: mode,
26
+ seed_options: seed_options(entry, mode)
25
27
  }
26
28
  )
27
29
  end
30
+
31
+ private
32
+
33
+ def seed_options(entry, mode)
34
+ options = {
35
+ embed: get_embed_from_env,
36
+ origin_url: request.original_url
37
+ }
38
+
39
+ if mode == :preview
40
+ # Published entries are cached without taking the requesting
41
+ # user into account. Only previews may depend on their locale.
42
+ options[:ui_locale] = get_ui_locale_from_env
43
+ options[:load_commenting] = true if entry.feature_state('commenting')
44
+ end
45
+
46
+ options
47
+ end
28
48
  end
29
49
  end
@@ -1,11 +1,11 @@
1
1
  module PageflowScrolled
2
2
  # @api private
3
3
  module CacheHelper
4
- def cache_scrolled_entry(entry:, widget_scope:, &)
4
+ def cache_scrolled_entry(entry:, entry_mode:, &)
5
5
  condition =
6
- widget_scope == :published &&
6
+ entry_mode == :published &&
7
7
  entry.feature_state('scrolled_entry_fragment_caching')
8
- cache_if(condition, [entry, :head_and_body, widget_scope], &)
8
+ cache_if(condition, [entry, :head_and_body, entry_mode], &)
9
9
  end
10
10
  end
11
11
  end
@@ -11,14 +11,16 @@ module PageflowScrolled
11
11
  entry_config = Pageflow.config_for(scrolled_entry)
12
12
 
13
13
  scrolled_entry_editor_legacy_typography_variants_seed(json, entry_config)
14
- scrolled_entry_editor_consent_vendor_host_matchers_seed(json, entry_config)
14
+ scrolled_entry_editor_consent_vendor_url_matchers_seed(json, entry_config)
15
15
 
16
16
  scrolled_entry_json_seed(json,
17
17
  scrolled_entry,
18
18
  skip_files: true,
19
19
  skip_i18n: true,
20
20
  include_hidden_sections: true,
21
- include_unused_additional_seed_data: true)
21
+ include_unused_additional_seed_data: true,
22
+ include_unused_file_reference_locations: true,
23
+ include_theme_translations: true)
22
24
  end
23
25
 
24
26
  private
@@ -31,10 +33,10 @@ module PageflowScrolled
31
33
  )
32
34
  end
33
35
 
34
- def scrolled_entry_editor_consent_vendor_host_matchers_seed(json, entry_config)
35
- json.consent_vendor_host_matchers(
36
+ def scrolled_entry_editor_consent_vendor_url_matchers_seed(json, entry_config)
37
+ json.consent_vendor_url_matchers(
36
38
  entry_config
37
- .consent_vendor_host_matchers
39
+ .consent_vendor_url_matchers
38
40
  .transform_keys { |regexp| regexp.inspect[1..-2] }
39
41
  )
40
42
  end
@@ -8,6 +8,7 @@ module PageflowScrolled
8
8
  include Pageflow::StructuredDataHelper
9
9
  include Pageflow::TextDirectionHelper
10
10
  include PageflowScrolled::CacheHelper
11
+ include PageflowScrolled::SprocketsHelper
11
12
  include FaviconHelper
12
13
  include PacksHelper
13
14
  include WebpackPublicPathHelper
@@ -16,7 +17,7 @@ module PageflowScrolled
16
17
  html = render(template: 'pageflow_scrolled/entries/show',
17
18
  locals: {
18
19
  entry:,
19
- widget_scope: :editor,
20
+ entry_mode: :editor,
20
21
  skip_ssr: true,
21
22
  skip_structured_data: true,
22
23
  skip_feed_link_tags: true,
@@ -24,7 +25,9 @@ module PageflowScrolled
24
25
  seed_options: {
25
26
  skip_collections: true,
26
27
  include_unused_additional_seed_data: true,
27
- translations: {include_inline_editing: true}
28
+ include_unused_file_reference_locations: true,
29
+ include_theme_translations: true,
30
+ load_inline_editing: true
28
31
  }
29
32
  })
30
33
 
@@ -18,22 +18,30 @@ module PageflowScrolled
18
18
  scrolled_entry_json_seed(json, scrolled_entry, options)
19
19
  end
20
20
 
21
- content_tag(:script, <<-JS.html_safe)
22
- pageflowScrolledRender(#{sanitize_json(seed_json)});
23
- JS
21
+ content_tag(:script,
22
+ sanitize_json(seed_json).html_safe,
23
+ type: 'application/json',
24
+ data: {pageflow_scrolled_seed: true})
24
25
  end
25
26
 
26
27
  def scrolled_entry_json_seed(json, scrolled_entry, options = {})
27
28
  revision = scrolled_entry.revision
28
29
  sections = scrolled_entry_json_seed_sections(scrolled_entry, options)
30
+ content_elements = scrolled_entry_json_seed_content_elements(revision, sections)
31
+ entry_config = Pageflow.config_for(scrolled_entry)
29
32
 
30
33
  json.partial!('pageflow_scrolled/entry_json_seed/entry',
31
34
  entry: scrolled_entry,
32
- entry_config: Pageflow.config_for(scrolled_entry),
35
+ entry_config:,
33
36
  storylines: scrolled_entry_json_seed_storylines(revision),
34
37
  chapters: scrolled_entry_json_seed_chapters(revision, options),
35
38
  sections:,
36
- content_elements: scrolled_entry_json_seed_content_elements(revision, sections),
39
+ content_elements:,
40
+ file_reference_locations: EntryFileReferenceLocations.new(
41
+ entry_config,
42
+ content_elements,
43
+ include_unused: options[:include_unused_file_reference_locations]
44
+ ),
37
45
  widgets: scrolled_entry.resolve_widgets(insert_point: :react),
38
46
  options:)
39
47
  end
@@ -1,16 +1,23 @@
1
1
  module PageflowScrolled
2
2
  # @api private
3
3
  module I18nHelper
4
- def scrolled_i18n_translations(entry, include_inline_editing: false)
5
- result = scrolled_i18n_public_translations(entry)
4
+ def scrolled_i18n_translations(entry,
5
+ ui_locale: I18n.locale,
6
+ include_inline_editing: false,
7
+ include_review: false)
8
+ ui_scopes = []
9
+ ui_scopes << :inline_editing if include_inline_editing
10
+ ui_scopes << :review if include_review
6
11
 
7
- return result unless include_inline_editing
8
-
9
- result.deep_merge(I18n.locale.to_s => {
10
- pageflow_scrolled: {
11
- inline_editing: I18n.t('pageflow_scrolled.inline_editing')
12
- }
13
- })
12
+ ui_scopes.reduce(scrolled_i18n_public_translations(entry)) do |result, scope|
13
+ result.deep_merge(
14
+ ui_locale.to_s => {
15
+ pageflow_scrolled: {
16
+ scope => I18n.t("pageflow_scrolled.#{scope}", locale: ui_locale)
17
+ }
18
+ }
19
+ )
20
+ end
14
21
  end
15
22
 
16
23
  private
@@ -19,11 +26,19 @@ module PageflowScrolled
19
26
  value.presence || fallback
20
27
  end
21
28
 
29
+ # Entry locales are not expected to be complete. Merge translations
30
+ # of all fallback locales since the frontend does not know about
31
+ # fallbacks.
22
32
  def scrolled_i18n_public_translations(entry)
23
- translations =
24
- I18n.t('pageflow_scrolled.public', locale: I18n.default_locale, default: {})
25
- .deep_merge(I18n.t('pageflow_scrolled.public', locale: entry.locale, default: {}),
26
- &MERGE_IGNORING_NIL)
33
+ locales = [I18n.default_locale, *I18n.fallbacks[entry.locale].reverse].uniq &
34
+ I18n.available_locales
35
+
36
+ translations = locales.reduce({}) do |result, locale|
37
+ result.deep_merge(
38
+ I18n.t('pageflow_scrolled.public', locale:, default: {}),
39
+ &MERGE_IGNORING_NIL
40
+ )
41
+ end
27
42
 
28
43
  {
29
44
  entry.locale => {
@@ -4,17 +4,24 @@ module PageflowScrolled
4
4
  def scrolled_frontend_javascript_packs_tag(entry, options)
5
5
  javascript_pack_tag(
6
6
  *scrolled_frontend_packs(entry, **options),
7
- defer: false
7
+ defer: true
8
8
  )
9
9
  end
10
10
 
11
- def scrolled_frontend_stylesheet_packs_tag(entry, options)
11
+ def scrolled_frontend_stylesheet_packs_tag(entry, **)
12
12
  stylesheet_pack_tag(
13
- *scrolled_frontend_packs(entry, **options),
13
+ *scrolled_frontend_stylesheet_packs(entry, **),
14
14
  media: 'all'
15
15
  )
16
16
  end
17
17
 
18
+ def scrolled_frontend_stylesheet_packs(entry, entry_mode:, seed_options: {})
19
+ packs = scrolled_frontend_packs(entry, entry_mode:, format: :css)
20
+ packs += ['pageflow-scrolled-frontend-inlineEditing'] if seed_options[:load_inline_editing]
21
+ packs += ['pageflow-scrolled-frontend-commenting'] if seed_options[:load_commenting]
22
+ packs
23
+ end
24
+
18
25
  def scrolled_editor_javascript_packs_tag(entry)
19
26
  javascript_pack_tag(
20
27
  *scrolled_editor_packs(entry),
@@ -29,10 +36,10 @@ module PageflowScrolled
29
36
  )
30
37
  end
31
38
 
32
- def scrolled_frontend_packs(entry, widget_scope:)
39
+ def scrolled_frontend_packs(entry, entry_mode:, format: :js)
33
40
  ['pageflow-scrolled-frontend'] +
34
- scrolled_additional_frontend_packs(entry, widget_scope) +
35
- scrolled_frontend_widget_type_packs(entry, widget_scope)
41
+ scrolled_additional_frontend_packs(entry, entry_mode) +
42
+ scrolled_frontend_widget_type_packs(entry, entry_mode, format)
36
43
  end
37
44
 
38
45
  def scrolled_editor_packs(entry)
@@ -43,7 +50,8 @@ module PageflowScrolled
43
50
  end
44
51
 
45
52
  def scrolled_editor_stylesheet_packs(entry)
46
- Pageflow.config_for(entry).additional_editor_packs.stylesheet_paths(entry) +
53
+ ['pageflow-scrolled-editor'] +
54
+ Pageflow.config_for(entry).additional_editor_packs.stylesheet_paths(entry) +
47
55
  Pageflow.config_for(entry).additional_frontend_packs.paths(entry)
48
56
  end
49
57
 
@@ -62,15 +70,22 @@ module PageflowScrolled
62
70
  )
63
71
  end
64
72
 
65
- def scrolled_frontend_widget_type_packs(entry, widget_scope)
66
- scrolled_frontend_pack_widget_types(entry, widget_scope).map(&:pack)
73
+ def scrolled_frontend_widget_type_packs(entry, widget_scope, format = :js)
74
+ scrolled_frontend_pack_widget_types(entry, widget_scope)
75
+ .select { |widget_type| widget_type.respond_to?(:packs) }
76
+ .flat_map { |widget_type| widget_type.packs(entry:, request:) }
77
+ .filter_map { |pack|
78
+ path, stylesheet = pack.is_a?(Hash) ? pack.values_at(:path, :stylesheet) : [pack, false]
79
+ path if format == :js || stylesheet
80
+ }
81
+ .uniq
67
82
  end
68
83
 
69
84
  def scrolled_frontend_pack_widget_types(entry, widget_scope)
70
85
  if widget_scope == :editor
71
- ReactWidgetType.all_for(entry)
86
+ Pageflow.config_for(entry).widget_types.select(&:enabled_in_editor?)
72
87
  else
73
- entry.resolve_widgets(insert_point: :react).map(&:widget_type)
88
+ entry.resolve_widgets.map(&:widget_type)
74
89
  end
75
90
  end
76
91
  end
@@ -4,9 +4,9 @@ module PageflowScrolled
4
4
  include Pageflow::RenderJsonHelper
5
5
  include PageflowScrolled::EntryJsonSeedHelper
6
6
 
7
- def render_scrolled_entry(entry)
7
+ def render_scrolled_entry(entry, options = {})
8
8
  seed_json = render_json do |json|
9
- scrolled_entry_json_seed(json, entry)
9
+ scrolled_entry_json_seed(json, entry, options)
10
10
  end
11
11
 
12
12
  ReactServerSideRenderingHelper.renderer.render('Root', "{seed: #{seed_json}}", {})
@@ -0,0 +1,16 @@
1
+ module PageflowScrolled
2
+ # @api private
3
+ module SprocketsHelper
4
+ def scrolled_sprockets_asset_tags(entry, entry_mode:)
5
+ tags = []
6
+
7
+ if Pageflow.config_for(entry).include_legacy_frontend_javascript
8
+ tags << javascript_include_tag('pageflow_scrolled/legacy')
9
+ end
10
+
11
+ tags << stylesheet_link_tag('pageflow_scrolled/ui', media: 'all') if entry_mode != :published
12
+
13
+ safe_join(tags)
14
+ end
15
+ end
16
+ end
@@ -32,6 +32,7 @@ module PageflowScrolled
32
32
  ].flatten
33
33
 
34
34
  content_tag('style', raw(<<~CSS), data: {theme: ''})
35
+ #{scrolled_theme_font_face_rules(theme)}
35
36
  :root {
36
37
  #{declarations.join("\n")}
37
38
  }
@@ -41,6 +42,23 @@ module PageflowScrolled
41
42
  CSS
42
43
  end
43
44
 
45
+ def scrolled_theme_font_face_rules(theme)
46
+ scrolled_theme_font_faces(theme).filter_map(&:generate).join("\n")
47
+ end
48
+
49
+ def scrolled_theme_font_preload_link_tags(theme)
50
+ safe_join(
51
+ scrolled_theme_font_faces(theme).filter_map(&:preload_source).map do |source|
52
+ tag.link(rel: 'preload',
53
+ as: 'font',
54
+ type: source[:type],
55
+ href: source[:url],
56
+ crossorigin: 'anonymous',
57
+ data: {theme: ''})
58
+ end
59
+ )
60
+ end
61
+
44
62
  def scrolled_theme_typography_rules(theme)
45
63
  RuleSet.new(prefix: 'typography').generate(theme.options.fetch(:typography, {}))
46
64
  end
@@ -52,6 +70,159 @@ module PageflowScrolled
52
70
 
53
71
  private
54
72
 
73
+ def scrolled_theme_font_faces(theme)
74
+ theme.options.fetch(:font_faces, []).map do |face|
75
+ FontFaceRule.new(face, theme:) { |path| scrolled_theme_asset_path(theme, path) }
76
+ end
77
+ end
78
+
79
+ # @api private
80
+ class FontFaceRule
81
+ FORMATS = {
82
+ '.woff2' => 'woff2',
83
+ '.woff' => 'woff',
84
+ '.ttf' => 'truetype',
85
+ '.otf' => 'opentype'
86
+ }.freeze
87
+
88
+ MIME_TYPES = {
89
+ 'woff2' => 'font/woff2',
90
+ 'woff' => 'font/woff',
91
+ 'truetype' => 'font/ttf',
92
+ 'opentype' => 'font/otf'
93
+ }.freeze
94
+
95
+ WEIGHT_PATTERN = /\A(normal|bold|\d{1,4}( \d{1,4})?)\z/
96
+ STYLE_PATTERN = /\A(normal|italic)\z/
97
+ FORMAT_PATTERN = /\A(woff2?|truetype|opentype|embedded-opentype|svg)(-variations)?\z/
98
+ UNICODE_RANGE_PATTERN = /\A\s*U\+[0-9a-f?]{1,6}(-[0-9a-f]{1,6})?
99
+ (\s*,\s*U\+[0-9a-f?]{1,6}(-[0-9a-f]{1,6})?)*\s*\z/xi
100
+
101
+ # Quotes and backslashes would allow breaking out of the quoted
102
+ # strings family names and urls are interpolated into.
103
+ UNSAFE_IN_FAMILY = /["'\\[[:cntrl:]]]/
104
+ UNSAFE_IN_URL = /["'\\\s]/
105
+
106
+ ABSOLUTE_URL = %r{\A((https?:)?//|/|data:)}
107
+
108
+ def initialize(face, theme:, &resolve_path)
109
+ @face = face
110
+ @theme = theme
111
+ @resolve_path = resolve_path
112
+ end
113
+
114
+ def generate
115
+ return unless valid?
116
+
117
+ <<~CSS
118
+ @font-face {
119
+ #{declarations.join("\n ")}
120
+ }
121
+ CSS
122
+ end
123
+
124
+ # Only the first source is preloaded since the browser downloads
125
+ # exactly one of the alternative formats.
126
+ def preload_source
127
+ return unless valid? && face[:preload]
128
+
129
+ source = sources_with_safe_urls.first
130
+
131
+ {url: source[:url], type: mime_type(source)}
132
+ end
133
+
134
+ private
135
+
136
+ attr_reader :face, :theme
137
+
138
+ def valid?
139
+ family.present? && sources_with_safe_urls.any?
140
+ end
141
+
142
+ def declarations
143
+ [
144
+ %(font-family: "#{family}";),
145
+ "src: #{source_values.join(', ')};",
146
+ 'font-display: swap;',
147
+ *descriptors
148
+ ]
149
+ end
150
+
151
+ def family
152
+ @family ||= face[:family].to_s.gsub(UNSAFE_IN_FAMILY, '').strip
153
+ end
154
+
155
+ def source_values
156
+ sources_with_safe_urls.map { |source| source_value(source) }
157
+ end
158
+
159
+ def sources_with_safe_urls
160
+ @sources_with_safe_urls ||= sources.reject { |source| source[:url].match?(UNSAFE_IN_URL) }
161
+ end
162
+
163
+ def sources
164
+ if face[:file_role]
165
+ sources_from_files
166
+ else
167
+ sources_from_src
168
+ end
169
+ end
170
+
171
+ def sources_from_files
172
+ Array(face[:file_role]).compact.filter_map do |role|
173
+ url = theme.files.dig(role.to_sym, :original)
174
+ {url: url.to_s, format: face[:format]} if url
175
+ end
176
+ end
177
+
178
+ def sources_from_src
179
+ Array(face[:src]).filter_map do |src|
180
+ src = {url: src} unless src.is_a?(Hash)
181
+ next if src[:url].blank?
182
+
183
+ {url: resolve_url(src[:url]), format: src.fetch(:format, face[:format])}
184
+ end
185
+ end
186
+
187
+ def resolve_url(url)
188
+ return url.to_s if url.to_s.match?(ABSOLUTE_URL)
189
+
190
+ @resolve_path.call(url).to_s
191
+ end
192
+
193
+ def source_value(source)
194
+ format = source_format(source)
195
+
196
+ return %(url("#{source[:url]}")) unless format
197
+
198
+ %(url("#{source[:url]}") format("#{format}"))
199
+ end
200
+
201
+ def mime_type(source)
202
+ MIME_TYPES[source_format(source).to_s.delete_suffix('-variations')]
203
+ end
204
+
205
+ def source_format(source)
206
+ [source[:format], FORMATS[extension(source[:url])]].find do |candidate|
207
+ candidate.to_s.match?(FORMAT_PATTERN)
208
+ end
209
+ end
210
+
211
+ def extension(url)
212
+ File.extname(url.split(/[?#]/).first.to_s).downcase
213
+ end
214
+
215
+ def descriptors
216
+ [
217
+ ['font-weight', face[:weight], WEIGHT_PATTERN],
218
+ ['font-style', face[:style], STYLE_PATTERN],
219
+ ['unicode-range', face[:unicode_range], UNICODE_RANGE_PATTERN]
220
+ ].filter_map do |property, value, pattern|
221
+ "#{property}: #{value.to_s.strip};" if value.to_s.match?(pattern)
222
+ end
223
+ end
224
+ end
225
+
55
226
  BREAKPOINTS = {
56
227
  sm: '640px',
57
228
  md: '768px',
@@ -83,8 +254,8 @@ module PageflowScrolled
83
254
  private
84
255
 
85
256
  def extract_breakpoint_declarations!(declarations)
86
- BREAKPOINTS.keys.each_with_object({}) { |breakpoint_name, result|
87
- result[breakpoint_name] = declarations.delete(breakpoint_name)
257
+ BREAKPOINTS.keys.to_h { |breakpoint_name|
258
+ [breakpoint_name, declarations.delete(breakpoint_name)]
88
259
  }.compact
89
260
  end
90
261
 
@@ -128,7 +299,7 @@ module PageflowScrolled
128
299
 
129
300
  def extract_theme_directory_from_scrolled_theme_asset_path(theme, path)
130
301
  if path.starts_with?('../shared/')
131
- ['shared', path.gsub!('../shared/', '')]
302
+ ['shared', path.sub('../shared/', '')]
132
303
  elsif path.starts_with?('../')
133
304
  raise(ArgumentError,
134
305
  'Upward navigation to other directory than the shared ' \