collavre 0.24.1 → 0.25.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.
- checksums.yaml +4 -4
- data/README.md +0 -1
- data/app/assets/stylesheets/collavre/comments_popup.css +160 -82
- data/app/assets/stylesheets/collavre/creatives.css +346 -37
- data/app/assets/stylesheets/collavre/desktop_setup.css +246 -0
- data/app/assets/stylesheets/collavre/gnb.css +64 -3
- data/app/assets/stylesheets/collavre/landing.css +108 -3
- data/app/assets/stylesheets/collavre/mention_menu.css +32 -0
- data/app/assets/stylesheets/collavre/popup.css +14 -0
- data/app/assets/stylesheets/collavre/print.css +0 -1
- data/app/assets/stylesheets/collavre/search_popup.css +1 -1
- data/app/assets/stylesheets/collavre/secret_fields.css +3 -0
- data/app/assets/stylesheets/collavre/workspace.css +307 -0
- data/app/channels/collavre/agent_channel.rb +14 -21
- data/app/channels/collavre/comments_presence_channel.rb +181 -21
- data/app/channels/collavre/inbox_badge_channel.rb +7 -1
- data/app/components/collavre/inbox/badge_component.rb +6 -13
- data/app/components/collavre/progress_filter_component.html.erb +2 -2
- data/app/components/collavre/progress_filter_component.rb +20 -0
- data/app/controllers/collavre/admin/settings_controller.rb +4 -1
- data/app/controllers/collavre/agent_connections_controller.rb +165 -0
- data/app/controllers/collavre/agent_gateways_controller.rb +132 -0
- data/app/controllers/collavre/agent_provisioning_controller.rb +79 -0
- data/app/controllers/collavre/api/v1/agents_controller.rb +45 -97
- data/app/controllers/collavre/application_controller.rb +13 -2
- data/app/controllers/collavre/comment_read_pointers_controller.rb +69 -40
- data/app/controllers/collavre/comments_controller.rb +45 -131
- data/app/controllers/collavre/concerns/shareable.rb +9 -3
- data/app/controllers/collavre/creatives_controller.rb +144 -11
- data/app/controllers/collavre/desktop_gateway_registrations_controller.rb +173 -0
- data/app/controllers/collavre/desktop_setup_controller.rb +119 -0
- data/app/controllers/collavre/desktop_sidecar_health_controller.rb +35 -0
- data/app/controllers/collavre/features_controller.rb +57 -0
- data/app/controllers/collavre/invites_controller.rb +12 -1
- data/app/controllers/collavre/llm_models_controller.rb +8 -0
- data/app/controllers/collavre/notices_controller.rb +20 -0
- data/app/controllers/collavre/tasks_controller.rb +40 -6
- data/app/controllers/collavre/topics_controller.rb +130 -53
- data/app/controllers/collavre/user_creative_preferences_controller.rb +214 -30
- data/app/controllers/concerns/collavre/comments/batch_operations.rb +15 -3
- data/app/controllers/concerns/collavre/comments/conversion.rb +1 -1
- data/app/controllers/concerns/collavre/users_controller/ai_user_management.rb +118 -8
- data/app/controllers/concerns/collavre/users_controller/profile_and_settings.rb +1 -15
- data/app/controllers/concerns/collavre/users_controller/registration.rb +2 -2
- data/app/errors/collavre/turn_deadline_error.rb +20 -0
- data/app/helpers/collavre/application_helper.rb +38 -0
- data/app/helpers/collavre/creatives_helper.rb +56 -22
- data/app/helpers/collavre/navigation_helper.rb +12 -0
- data/app/javascript/collavre.js +1 -2
- data/app/javascript/components/InlineLexicalEditor.jsx +30 -21
- data/app/javascript/components/__tests__/creative_tree_row_parent_id.test.js +32 -2
- data/app/javascript/components/__tests__/creative_tree_row_progress_toggle.test.js +185 -0
- data/app/javascript/components/creative_tree_row.js +62 -22
- data/app/javascript/controllers/__tests__/agent_connection_controller.test.js +195 -0
- data/app/javascript/controllers/__tests__/desktop_proxy_setup_controller.test.js +96 -0
- data/app/javascript/controllers/__tests__/index.test.js +42 -0
- data/app/javascript/controllers/__tests__/last_visited_creative_controller.test.js +103 -0
- data/app/javascript/controllers/__tests__/link_creative_controller.test.js +253 -2
- data/app/javascript/controllers/__tests__/progress_filter_controller.test.js +134 -0
- data/app/javascript/controllers/__tests__/search_popup_controller.test.js +310 -0
- data/app/javascript/controllers/__tests__/topic_list_controller.test.js +111 -0
- data/app/javascript/controllers/__tests__/workspace_tree_controller.test.js +1011 -0
- data/app/javascript/controllers/agent_connection_controller.js +357 -0
- data/app/javascript/controllers/agent_vendor_controller.js +23 -0
- data/app/javascript/controllers/comment_controller.js +3 -1
- data/app/javascript/controllers/comments/__tests__/feature_cards_controller.test.js +223 -0
- data/app/javascript/controllers/comments/__tests__/form_controller_default_placeholder.test.js +83 -0
- data/app/javascript/controllers/comments/__tests__/form_controller_draft.test.js +2069 -0
- data/app/javascript/controllers/comments/__tests__/form_controller_ime_send.test.js +324 -0
- data/app/javascript/controllers/comments/__tests__/form_controller_review.test.js +2 -2
- data/app/javascript/controllers/comments/__tests__/form_controller_search_submit.test.js +106 -0
- data/app/javascript/controllers/comments/__tests__/form_controller_stashed_draft.test.js +134 -0
- data/app/javascript/controllers/comments/__tests__/form_controller_stashed_draft_creative_switch.test.js +166 -0
- data/app/javascript/controllers/comments/__tests__/form_controller_stashed_draft_failure.test.js +183 -0
- data/app/javascript/controllers/comments/__tests__/list_controller_deep_link_topic.test.js +467 -0
- data/app/javascript/controllers/comments/__tests__/list_controller_docked.test.js +115 -0
- data/app/javascript/controllers/comments/__tests__/list_controller_prev_message.test.js +15 -0
- data/app/javascript/controllers/comments/__tests__/list_controller_read_pointer.test.js +276 -0
- data/app/javascript/controllers/comments/__tests__/list_controller_stream_archived.test.js +221 -0
- data/app/javascript/controllers/comments/__tests__/list_controller_stream_search.test.js +104 -0
- data/app/javascript/controllers/comments/__tests__/placeholder_controller.test.js +79 -0
- data/app/javascript/controllers/comments/__tests__/popup_controller.test.js +754 -1
- data/app/javascript/controllers/comments/__tests__/presence_controller.test.js +717 -15
- data/app/javascript/controllers/comments/__tests__/topics_controller_action_errors.test.js +6 -0
- data/app/javascript/controllers/comments/__tests__/topics_controller_archived.test.js +879 -0
- data/app/javascript/controllers/comments/__tests__/topics_controller_creation_scroll.test.js +92 -0
- data/app/javascript/controllers/comments/__tests__/topics_controller_delete.test.js +130 -0
- data/app/javascript/controllers/comments/__tests__/topics_controller_docked.test.js +32 -0
- data/app/javascript/controllers/comments/__tests__/topics_controller_last_topic_echo.test.js +1938 -0
- data/app/javascript/controllers/comments/__tests__/topics_controller_primary_agent.test.js +112 -2
- data/app/javascript/controllers/comments/__tests__/topics_controller_scroll.test.js +182 -0
- data/app/javascript/controllers/comments/__tests__/topics_controller_selection_race.test.js +981 -0
- data/app/javascript/controllers/comments/__tests__/topics_controller_topic_list.test.js +313 -0
- data/app/javascript/controllers/comments/feature_cards_controller.js +83 -0
- data/app/javascript/controllers/comments/form_controller.js +792 -14
- data/app/javascript/controllers/comments/list_controller.js +296 -31
- data/app/javascript/controllers/comments/placeholder_controller.js +30 -0
- data/app/javascript/controllers/comments/popup_controller.js +425 -83
- data/app/javascript/controllers/comments/presence_controller.js +317 -55
- data/app/javascript/controllers/comments/topics_controller.js +1705 -80
- data/app/javascript/controllers/common_popup_controller.js +2 -2
- data/app/javascript/controllers/creatives/__tests__/select_mode_controller.test.js +139 -0
- data/app/javascript/controllers/creatives/__tests__/tree_controller.test.js +605 -0
- data/app/javascript/controllers/creatives/__tests__/write_access_request_controller.test.js +125 -0
- data/app/javascript/controllers/creatives/select_mode_controller.js +7 -2
- data/app/javascript/controllers/creatives/tree_controller.js +140 -31
- data/app/javascript/controllers/creatives/write_access_request_controller.js +58 -0
- data/app/javascript/controllers/desktop_proxy_setup_controller.js +51 -0
- data/app/javascript/controllers/gateway_check_controller.js +36 -0
- data/app/javascript/controllers/index.js +25 -1
- data/app/javascript/controllers/last_visited_creative_controller.js +58 -0
- data/app/javascript/controllers/link_creative_controller.js +89 -18
- data/app/javascript/controllers/progress_filter_controller.js +12 -22
- data/app/javascript/controllers/search_popup_controller.js +64 -46
- data/app/javascript/controllers/share_user_search_controller.js +2 -2
- data/app/javascript/controllers/topic_list_controller.js +44 -7
- data/app/javascript/controllers/workspace_tree_controller.js +662 -0
- data/app/javascript/creatives/__tests__/tree_renderer.test.js +80 -0
- data/app/javascript/creatives/drag_drop/__tests__/drop_completion_empty_state.test.js +98 -0
- data/app/javascript/creatives/drag_drop/__tests__/topic_move_released_agent.test.js +125 -0
- data/app/javascript/creatives/drag_drop/event_handlers.js +12 -0
- data/app/javascript/creatives/tree_renderer.js +65 -22
- data/app/javascript/lib/__tests__/chat_drafts.test.js +1458 -0
- data/app/javascript/lib/__tests__/common_popup_bounds.test.js +8 -3
- data/app/javascript/lib/__tests__/common_popup_placement.test.js +374 -0
- data/app/javascript/lib/__tests__/common_popup_reshow.test.js +131 -0
- data/app/javascript/lib/__tests__/creative_link_prefetch.test.js +46 -0
- data/app/javascript/lib/__tests__/last_visited_creative.test.js +87 -0
- data/app/javascript/lib/__tests__/skill_html_entities.test.js +23 -0
- data/app/javascript/lib/__tests__/turbo_stream_actions.test.js +190 -0
- data/app/javascript/lib/api/__tests__/creatives.test.js +68 -0
- data/app/javascript/lib/api/__tests__/csrf_fetch.test.js +27 -0
- data/app/javascript/lib/api/__tests__/topics_save_last_topic.test.js +112 -0
- data/app/javascript/lib/api/creatives.js +32 -13
- data/app/javascript/lib/api/csrf_fetch.js +6 -4
- data/app/javascript/lib/api/topics.js +63 -5
- data/app/javascript/lib/chat_drafts.js +786 -0
- data/app/javascript/lib/common_popup.js +164 -25
- data/app/javascript/lib/creative_link_prefetch.js +27 -0
- data/app/javascript/lib/last_visited_creative.js +97 -0
- data/app/javascript/lib/lexical/__tests__/creative_link_node.test.js +162 -0
- data/app/javascript/lib/lexical/__tests__/creative_link_trigger.test.js +133 -0
- data/app/javascript/lib/lexical/__tests__/link_toolbar.test.js +62 -0
- data/app/javascript/lib/lexical/creative_link_node.js +101 -0
- data/app/javascript/lib/lexical/creative_link_trigger.js +98 -0
- data/app/javascript/lib/lexical/link_toolbar.js +23 -0
- data/app/javascript/lib/turbo_stream_actions.js +23 -16
- data/app/javascript/lib/utils/__tests__/filter_navigation.test.js +210 -0
- data/app/javascript/lib/utils/__tests__/markdown.test.js +44 -0
- data/app/javascript/lib/utils/filter_navigation.js +100 -0
- data/app/javascript/modules/__tests__/command_menu_creative_picker.test.js +62 -0
- data/app/javascript/modules/__tests__/command_menu_typeahead.test.js +267 -0
- data/app/javascript/modules/__tests__/creative_link_picker.test.js +85 -0
- data/app/javascript/modules/__tests__/creative_row_editor_archive.test.js +391 -0
- data/app/javascript/modules/__tests__/creative_row_editor_delegated_clicks.test.js +158 -0
- data/app/javascript/modules/__tests__/creative_row_editor_empty_state_recovery.test.js +519 -0
- data/app/javascript/modules/__tests__/creative_row_editor_empty_state_removal.test.js +346 -0
- data/app/javascript/modules/__tests__/creative_row_editor_session.test.js +144 -0
- data/app/javascript/modules/__tests__/creative_tree_dom.test.js +30 -0
- data/app/javascript/modules/__tests__/creative_tree_empty_state.test.js +186 -0
- data/app/javascript/modules/__tests__/dom_listener_registry.test.js +67 -0
- data/app/javascript/modules/__tests__/support/inline_editor_dom.js +159 -0
- data/app/javascript/modules/command_args_form.js +0 -12
- data/app/javascript/modules/command_menu.js +80 -38
- data/app/javascript/modules/creative_link_picker.js +56 -0
- data/app/javascript/modules/creative_row_editor.js +446 -243
- data/app/javascript/modules/creative_row_editor_delegated_clicks.js +87 -0
- data/app/javascript/modules/creative_tree_dom.js +4 -0
- data/app/javascript/modules/creative_tree_empty_state.js +80 -0
- data/app/javascript/modules/dom_listener_registry.js +23 -0
- data/app/javascript/modules/mention_menu.js +2 -3
- data/app/javascript/utils/__tests__/caret_anchor.test.js +75 -0
- data/app/javascript/utils/__tests__/chevron_icons.test.js +15 -0
- data/app/javascript/utils/caret_position.js +10 -0
- data/app/javascript/utils/chevron_icons.js +7 -0
- data/app/jobs/collavre/ai_agent_job.rb +262 -72
- data/app/jobs/collavre/cancel_offline_delegated_tasks_job.rb +11 -36
- data/app/jobs/collavre/comment_badges_broadcast_job.rb +28 -0
- data/app/jobs/collavre/comment_link_preview_job.rb +33 -0
- data/app/jobs/collavre/comment_notification_job.rb +14 -0
- data/app/jobs/collavre/comment_push_delivery_sweep_job.rb +17 -0
- data/app/jobs/collavre/compress_job.rb +33 -22
- data/app/jobs/collavre/creative_broadcast_job.rb +161 -48
- data/app/jobs/collavre/cron_action_job.rb +36 -18
- data/app/jobs/collavre/drop_trigger_job.rb +23 -9
- data/app/jobs/collavre/merge_comments_job.rb +33 -16
- data/app/jobs/collavre/push_notification_job.rb +62 -18
- data/app/jobs/collavre/restore_dropped_dispatches_job.rb +17 -0
- data/app/jobs/collavre/stuck_detector_job.rb +1 -1
- data/app/jobs/collavre/touch_creative_subtree_job.rb +14 -0
- data/app/models/collavre/agent_gateway.rb +200 -0
- data/app/models/collavre/agent_subscription.rb +4 -4
- data/app/models/collavre/agent_workspace.rb +210 -0
- data/app/models/collavre/comment/broadcastable.rb +22 -52
- data/app/models/collavre/comment/notifiable.rb +154 -77
- data/app/models/collavre/comment/topic_membership.rb +41 -0
- data/app/models/collavre/comment.rb +336 -31
- data/app/models/collavre/comment_notification_delivery.rb +61 -0
- data/app/models/collavre/comment_presence_store/cache_batches.rb +64 -0
- data/app/models/collavre/comment_presence_store.rb +272 -12
- data/app/models/collavre/comment_read_pointer.rb +9 -15
- data/app/models/collavre/creative/describable.rb +8 -2
- data/app/models/collavre/creative/permissible.rb +11 -0
- data/app/models/collavre/creative.rb +29 -4
- data/app/models/collavre/creative_share.rb +109 -4
- data/app/models/collavre/current.rb +1 -0
- data/app/models/collavre/llm_model.rb +62 -0
- data/app/models/collavre/system_setting.rb +12 -1
- data/app/models/collavre/task.rb +121 -2
- data/app/models/collavre/topic.rb +51 -0
- data/app/models/collavre/user.rb +101 -41
- data/app/models/collavre/user_creative_preference.rb +3 -1
- data/app/services/collavre/agent_provisioning/archive.rb +108 -0
- data/app/services/collavre/ai_agent/a2a_dispatcher.rb +16 -3
- data/app/services/collavre/ai_agent/agent_lifecycle_manager.rb +63 -6
- data/app/services/collavre/ai_agent/approval_handler.rb +12 -4
- data/app/services/collavre/ai_agent/claude_channel_adapter.rb +10 -5
- data/app/services/collavre/ai_agent/merged_trigger_comments.rb +164 -0
- data/app/services/collavre/ai_agent/message_builder.rb +103 -16
- data/app/services/collavre/ai_agent/session_provisioner.rb +3 -7
- data/app/services/collavre/ai_agent/task_claim_service.rb +29 -15
- data/app/services/collavre/ai_agent/task_reply_service.rb +67 -0
- data/app/services/collavre/ai_agent_service.rb +108 -20
- data/app/services/collavre/ai_client.rb +211 -21
- data/app/services/collavre/cli_proxy/client.rb +147 -0
- data/app/services/collavre/cli_proxy/endpoint_policy.rb +81 -0
- data/app/services/collavre/cli_proxy/identity.rb +47 -0
- data/app/services/collavre/cli_proxy/safe_net_http_adapter.rb +19 -0
- data/app/services/collavre/command_menu_service.rb +0 -10
- data/app/services/collavre/comment_move_service.rb +96 -7
- data/app/services/collavre/comment_snapshot_restore_service.rb +11 -5
- data/app/services/collavre/comments/action_executor.rb +48 -20
- data/app/services/collavre/comments/command_processor.rb +0 -1
- data/app/services/collavre/comments/list_response.rb +133 -0
- data/app/services/collavre/comments/read_pointer_writer.rb +210 -0
- data/app/services/collavre/comments/read_receipt_broadcaster.rb +157 -0
- data/app/services/collavre/comments/read_receipt_index.rb +152 -0
- data/app/services/collavre/comments/topic_command.rb +72 -1
- data/app/services/collavre/comments/topic_mutation.rb +20 -0
- data/app/services/collavre/creatives/breadcrumb_resolver.rb +1 -1
- data/app/services/collavre/creatives/comment_badge_index.rb +299 -27
- data/app/services/collavre/creatives/reorderer.rb +15 -3
- data/app/services/collavre/creatives/tree_builder.rb +1 -1
- data/app/services/collavre/creatives/tree_formatter.rb +5 -6
- data/app/services/collavre/creatives/workspace_path_resolver.rb +50 -0
- data/app/services/collavre/creatives/workspace_tree_builder.rb +92 -0
- data/app/services/collavre/crons/recurring_topic_tasks.rb +23 -0
- data/app/services/collavre/home_page_path.rb +55 -0
- data/app/services/collavre/html_text.rb +64 -0
- data/app/services/collavre/http_client.rb +11 -2
- data/app/services/collavre/inbox_reply_service.rb +1 -1
- data/app/services/collavre/markdown_converter.rb +13 -1
- data/app/services/collavre/orchestration/agent_orchestrator.rb +776 -81
- data/app/services/collavre/orchestration/arbiter.rb +48 -5
- data/app/services/collavre/orchestration/deferred_trigger_scope.rb +53 -0
- data/app/services/collavre/orchestration/delivery_record.rb +792 -0
- data/app/services/collavre/orchestration/loop_breaker.rb +10 -4
- data/app/services/collavre/orchestration/matcher.rb +132 -17
- data/app/services/collavre/orchestration/policy_resolver.rb +58 -10
- data/app/services/collavre/orchestration/scheduler.rb +10 -4
- data/app/services/collavre/orchestration/scheduling_hooks.rb +10 -0
- data/app/services/collavre/orchestration/selection.rb +43 -0
- data/app/services/collavre/orchestration/stuck_detector.rb +25 -112
- data/app/services/collavre/orchestration/task_coalescer.rb +355 -0
- data/app/services/collavre/orchestration/topic_slot.rb +73 -0
- data/app/services/collavre/system_events/context_builder.rb +60 -11
- data/app/services/collavre/system_events/dispatcher.rb +58 -12
- data/app/services/collavre/system_events/envelope.rb +73 -0
- data/app/services/collavre/system_events/vocabulary.rb +45 -0
- data/app/services/collavre/tools/creative_retrieval_service.rb +1 -1
- data/app/services/collavre/tools/cron_create_service.rb +32 -18
- data/app/services/collavre/tools/id_list.rb +38 -0
- data/app/services/collavre/tools/topic_authorizer.rb +61 -7
- data/app/services/collavre/tools/topic_branch_service.rb +85 -0
- data/app/services/collavre/tools/topic_create_service.rb +117 -0
- data/app/services/collavre/tools/topic_list_service.rb +101 -0
- data/app/services/collavre/tools/topic_message_create_service.rb +192 -0
- data/app/services/collavre/tools/topic_messages_service.rb +346 -0
- data/app/services/collavre/tools/topic_move_service.rb +108 -0
- data/app/services/collavre/tools/topic_selection.rb +59 -0
- data/app/services/collavre/tools/topic_update_service.rb +165 -0
- data/app/services/collavre/topic_branch_service.rb +39 -10
- data/app/services/collavre/topics/agent_resolver.rb +87 -0
- data/app/services/collavre/topics/char_budget.rb +70 -0
- data/app/services/collavre/topics/last_topic_preference_payload.rb +18 -0
- data/app/services/collavre/topics/message_page.rb +380 -0
- data/app/services/collavre/topics/message_page_markdown.rb +102 -0
- data/app/services/collavre/topics/message_scope.rb +61 -0
- data/app/services/collavre/topics/message_stats.rb +92 -0
- data/app/services/collavre/topics/move_blocker.rb +47 -0
- data/app/services/collavre/topics/next_name.rb +35 -0
- data/app/services/collavre/topics/pin_rejection.rb +25 -0
- data/app/services/collavre/topics/read_pointer_relocator.rb +71 -0
- data/app/services/collavre/topics/reserved_name.rb +18 -0
- data/app/services/collavre/topics/serializer.rb +92 -0
- data/app/services/collavre/topics/topic_destroy.rb +30 -0
- data/app/services/collavre/topics/topic_move.rb +80 -0
- data/app/services/collavre/topics/topic_move_effects.rb +35 -0
- data/app/services/collavre/topics/topic_mutation.rb +27 -0
- data/app/views/collavre/admin/integrations/_setting_row.html.erb +5 -8
- data/app/views/collavre/admin/settings/_system_tab.html.erb +7 -0
- data/app/views/collavre/agent_connections/show.html.erb +77 -0
- data/app/views/collavre/agent_gateways/_form.html.erb +71 -0
- data/app/views/collavre/agent_gateways/edit.html.erb +2 -0
- data/app/views/collavre/agent_gateways/index.html.erb +54 -0
- data/app/views/collavre/agent_gateways/new.html.erb +2 -0
- data/app/views/collavre/comments/_comments_popup.html.erb +24 -17
- data/app/views/collavre/comments/_empty_state.html.erb +33 -0
- data/app/views/collavre/comments/_empty_state_minimal.html.erb +7 -0
- data/app/views/collavre/comments/_feature_card.html.erb +35 -0
- data/app/views/collavre/comments/_list.html.erb +3 -2
- data/app/views/collavre/comments/_no_search_results.html.erb +3 -0
- data/app/views/collavre/creatives/_add_button.html.erb +17 -19
- data/app/views/collavre/creatives/_empty_state.html.erb +109 -0
- data/app/views/collavre/creatives/_inline_edit_form.html.erb +5 -5
- data/app/views/collavre/creatives/_tree_loading_placeholder.html.erb +16 -0
- data/app/views/collavre/creatives/_workspace_tree.html.erb +33 -0
- data/app/views/collavre/creatives/index.html.erb +79 -18
- data/app/views/collavre/desktop_setup/show.html.erb +114 -0
- data/app/views/collavre/features/index.html.erb +42 -0
- data/app/views/collavre/features/show.html.erb +52 -0
- data/app/views/collavre/invitation_mailer/invite.html.erb +1 -1
- data/app/views/collavre/invitation_mailer/invite.text.erb +1 -1
- data/app/views/collavre/invites/show.html.erb +14 -1
- data/app/views/collavre/landing/show.html.erb +5 -1
- data/app/views/collavre/shared/_link_creative_modal.html.erb +4 -1
- data/app/views/collavre/shared/navigation/_help_button.html.erb +3 -1
- data/app/views/collavre/shared/navigation/_panels.html.erb +1 -8
- data/app/views/collavre/shared/navigation/_search_form.html.erb +13 -2
- data/app/views/collavre/user_themes/index.html.erb +1 -1
- data/app/views/collavre/users/_llm_fields.html.erb +65 -0
- data/app/views/collavre/users/_org_chart_node.html.erb +1 -1
- data/app/views/collavre/users/edit_ai.html.erb +33 -31
- data/app/views/collavre/users/new.html.erb +4 -2
- data/app/views/collavre/users/new_ai.html.erb +14 -32
- data/app/views/collavre/users/show.html.erb +13 -1
- data/app/views/layouts/collavre/landing.html.erb +9 -4
- data/config/initializers/ruby_llm_openai_reasoning_content.rb +77 -0
- data/config/locales/admin.en.yml +3 -1
- data/config/locales/admin.ko.yml +3 -1
- data/config/locales/agent_gateway.en.yml +217 -0
- data/config/locales/agent_gateway.ko.yml +217 -0
- data/config/locales/ai_agent.en.yml +0 -1
- data/config/locales/ai_agent.ko.yml +0 -1
- data/config/locales/comments.en.yml +66 -30
- data/config/locales/comments.ko.yml +66 -30
- data/config/locales/creatives.en.yml +32 -1
- data/config/locales/creatives.ko.yml +32 -1
- data/config/locales/features.en.yml +140 -0
- data/config/locales/features.ko.yml +140 -0
- data/config/locales/invites.en.yml +3 -0
- data/config/locales/invites.ko.yml +3 -0
- data/config/locales/landing.en.yml +2 -1
- data/config/locales/landing.ko.yml +2 -1
- data/config/locales/tools.en.yml +20 -0
- data/config/locales/tools.ko.yml +20 -0
- data/config/locales/users.en.yml +15 -14
- data/config/locales/users.ko.yml +14 -14
- data/config/routes.rb +45 -19
- data/db/migrate/20250911084338_backfill_creative_in_comment_inbox_items.rb +9 -1
- data/db/migrate/20260724000000_add_creative_id_index_to_comments.rb +34 -0
- data/db/migrate/20260724000001_add_creative_private_id_index_to_comments.rb +50 -0
- data/db/migrate/20260724090000_add_async_comment_notification_state.rb +64 -0
- data/db/migrate/20260727000000_add_waiting_notice_scope_to_comments.rb +30 -0
- data/db/migrate/20260727010000_add_waiting_notice_scope_to_tasks.rb +26 -0
- data/db/migrate/20260805000000_add_creative_workspace_enabled_to_users.rb +5 -0
- data/db/migrate/20260807050000_add_dismissed_notices_to_users.rb +5 -0
- data/db/migrate/20260809000000_change_creative_workspace_enabled_default_to_true.rb +18 -0
- data/db/migrate/20260809000001_create_llm_models.rb +64 -0
- data/db/migrate/20260809000002_create_agent_gateways_and_workspaces.rb +35 -0
- data/db/migrate/20260809000003_hash_agent_workspace_callback_tokens.rb +44 -0
- data/db/migrate/20260809000004_encrypt_agent_workspace_manifest_tokens.rb +59 -0
- data/db/migrate/20260810000000_split_agent_workspace_identity_axes.rb +58 -0
- data/db/migrate/20260810000001_seed_codex_custom_proxy_llm_models.rb +28 -0
- data/db/migrate/20260810010000_add_last_visited_creative_to_users.rb +8 -0
- data/db/migrate/20260810020000_add_last_visited_creative_at_to_users.rb +5 -0
- data/db/migrate/20260810030000_add_last_visited_creative_navigation_order_to_users.rb +6 -0
- data/db/migrate/20260810060000_add_last_visited_creative_issued_sequence_to_users.rb +5 -0
- data/db/migrate/20260810100000_add_desktop_managed_to_agent_gateways.rb +40 -0
- data/db/migrate/20260810100001_add_desktop_preset_adapter_to_users.rb +38 -0
- data/db/migrate/20260811000000_make_agent_gateway_completion_key_nullable.rb +7 -0
- data/db/migrate/20260812000000_remove_workflow_columns_from_tasks.rb +101 -0
- data/db/migrate/20260812000001_remove_android_devices.rb +24 -0
- data/db/migrate/20260812010000_drop_inbox_items.rb +22 -0
- data/db/migrate/20260813000000_clear_claude_channel_presence_routing_expressions.rb +16 -0
- data/db/migrate/20260813000001_add_last_topic_revision_to_user_creative_preferences.rb +5 -0
- data/db/migrate/20260813000002_add_last_topic_save_order_to_user_creative_preferences.rb +6 -0
- data/db/migrate/20260813000003_add_last_topic_save_sequences_to_user_creative_preferences.rb +5 -0
- data/db/migrate/20260813000004_add_last_topic_save_fences_to_user_creative_preferences.rb +6 -0
- data/db/migrate/20260814000000_add_topic_to_comment_read_pointers.rb +79 -0
- data/db/migrate/20260814000001_add_creative_topic_private_id_index_to_comments.rb +37 -0
- data/db/migrate/20260816000000_add_watermark_index_to_comment_read_pointers.rb +27 -0
- data/db/migrate/20260816000001_add_private_recipient_topic_index_to_comments.rb +48 -0
- data/db/migrate/20260820000000_add_topic_assigned_at_to_comments.rb +13 -0
- data/db/seeds.rb +1 -51
- data/lib/collavre/engine.rb +108 -1
- data/lib/collavre/feature_card_registry.rb +168 -0
- data/lib/collavre/hashed_access_token_lookup.rb +22 -0
- data/lib/collavre/sensitive_request_silencer.rb +23 -0
- data/lib/collavre/user_extensions.rb +0 -2
- data/lib/collavre/version.rb +1 -1
- data/lib/collavre.rb +1 -0
- data/skills/collavre/SKILL.md +165 -0
- data/skills/collavre/package.json +8 -0
- data/skills/collavre/references/tool-reference.md +347 -0
- data/skills/collavre/scripts/collavre +634 -0
- data/skills/collavre/scripts/html_entities.js +24 -0
- metadata +222 -30
- data/app/controllers/collavre/api/v1/mobile/agent_events_controller.rb +0 -224
- data/app/controllers/collavre/api/v1/mobile/base_controller.rb +0 -95
- data/app/controllers/collavre/api/v1/mobile/devices_controller.rb +0 -31
- data/app/controllers/collavre/api/v1/mobile/voice_commands_controller.rb +0 -25
- data/app/controllers/collavre/inbox_items_controller.rb +0 -70
- data/app/controllers/collavre/typo_corrections_controller.rb +0 -39
- data/app/javascript/lib/__tests__/typo_correction.test.js +0 -192
- data/app/javascript/lib/typo_correction.js +0 -146
- data/app/javascript/modules/__tests__/typo_corrector.test.js +0 -365
- data/app/javascript/modules/creative_guide.js +0 -67
- data/app/javascript/modules/typo_corrector.js +0 -534
- data/app/jobs/collavre/inbox_summary_job.rb +0 -24
- data/app/mailers/collavre/inbox_mailer.rb +0 -19
- data/app/models/collavre/inbox_item.rb +0 -89
- data/app/services/collavre/comments/concerns/workflow_support.rb +0 -115
- data/app/services/collavre/comments/work_command.rb +0 -161
- data/app/services/collavre/comments/workflow_executor.rb +0 -276
- data/app/services/collavre/gemini_parent_recommender.rb +0 -77
- data/app/services/collavre/mobile/event_summarizer.rb +0 -40
- data/app/services/collavre/typo_corrector.rb +0 -188
- data/app/views/collavre/inbox_items/_item.html.erb +0 -18
- data/app/views/collavre/inbox_items/_items.html.erb +0 -3
- data/app/views/collavre/inbox_items/_list.html.erb +0 -7
- data/app/views/collavre/inbox_items/index.html.erb +0 -1
- data/app/views/collavre/inbox_mailer/daily_summary.html.erb +0 -11
- data/app/views/collavre/inbox_mailer/daily_summary.text.erb +0 -8
- data/app/views/collavre/users/typo_correction.html.erb +0 -50
- data/config/locales/mobile.en.yml +0 -16
- data/config/locales/mobile.ko.yml +0 -16
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus'
|
|
2
2
|
import chatHistory from '../../lib/chat_history'
|
|
3
|
+
import chatDrafts from '../../lib/chat_drafts'
|
|
3
4
|
|
|
4
5
|
const SIZE_STORAGE_KEY = 'commentsPopupSize'
|
|
5
6
|
const CREATIVE_CLICK_EVENT = 'creative-comments-click'
|
|
@@ -12,6 +13,8 @@ export default class extends Controller {
|
|
|
12
13
|
'list',
|
|
13
14
|
'form',
|
|
14
15
|
'closeButton',
|
|
16
|
+
'closeIcon',
|
|
17
|
+
'expandDockedIcon',
|
|
15
18
|
'leftHandle',
|
|
16
19
|
'rightHandle',
|
|
17
20
|
'fullscreenButton',
|
|
@@ -31,6 +34,9 @@ export default class extends Controller {
|
|
|
31
34
|
this.touchStartY = null
|
|
32
35
|
this.openFromUrlObserver = null
|
|
33
36
|
this.openFromUrlTimeout = null
|
|
37
|
+
this.dockedOpenTimeout = null
|
|
38
|
+
this.openGeneration = 0
|
|
39
|
+
this._wakeLockRequest = null
|
|
34
40
|
this.handleCreativeClick = this.handleCreativeClick.bind(this)
|
|
35
41
|
this.handleCreativeDestroyed = this.handleCreativeDestroyed.bind(this)
|
|
36
42
|
this.handleEditingStart = this.handleEditingStart.bind(this)
|
|
@@ -48,6 +54,7 @@ export default class extends Controller {
|
|
|
48
54
|
this.handlePopupWheel = this.handlePopupWheel.bind(this)
|
|
49
55
|
this.handleChatNavKeydown = this.handleChatNavKeydown.bind(this)
|
|
50
56
|
this.handleDropdownOutsideClick = this.handleDropdownOutsideClick.bind(this)
|
|
57
|
+
this.handleDockedMediaChange = this.handleDockedMediaChange.bind(this)
|
|
51
58
|
this._longPressTimer = null
|
|
52
59
|
this._longPressTriggered = false
|
|
53
60
|
this._isNavigating = false
|
|
@@ -64,6 +71,16 @@ export default class extends Controller {
|
|
|
64
71
|
document.addEventListener('visibilitychange', this.handleVisibilityChange)
|
|
65
72
|
window.addEventListener('popstate', this.handlePopState)
|
|
66
73
|
document.addEventListener('keydown', this.handleChatNavKeydown)
|
|
74
|
+
this.dockedMediaQuery = typeof window.matchMedia === 'function'
|
|
75
|
+
? window.matchMedia('(min-width: 768px)')
|
|
76
|
+
: {
|
|
77
|
+
matches: window.innerWidth >= 768,
|
|
78
|
+
addEventListener() {},
|
|
79
|
+
removeEventListener() {},
|
|
80
|
+
}
|
|
81
|
+
if (this.element.dataset.docked === 'true') {
|
|
82
|
+
this.dockedMediaQuery.addEventListener('change', this.handleDockedMediaChange)
|
|
83
|
+
}
|
|
67
84
|
|
|
68
85
|
// Long press on nav buttons
|
|
69
86
|
this._setupNavLongPress()
|
|
@@ -94,8 +111,16 @@ export default class extends Controller {
|
|
|
94
111
|
}
|
|
95
112
|
}
|
|
96
113
|
|
|
97
|
-
document.querySelectorAll('form[action
|
|
98
|
-
form.addEventListener('submit', () =>
|
|
114
|
+
document.querySelectorAll('form[action$="/session"]').forEach((form) => {
|
|
115
|
+
form.addEventListener('submit', () => {
|
|
116
|
+
this.formController?.discardDraft()
|
|
117
|
+
chatDrafts.clearAll()
|
|
118
|
+
try {
|
|
119
|
+
window.localStorage.removeItem(SIZE_STORAGE_KEY)
|
|
120
|
+
} catch {
|
|
121
|
+
// Storage can be unavailable on restricted origins; logout must continue.
|
|
122
|
+
}
|
|
123
|
+
})
|
|
99
124
|
})
|
|
100
125
|
|
|
101
126
|
if (this.element.dataset.autoFullscreen === 'true') {
|
|
@@ -115,6 +140,8 @@ export default class extends Controller {
|
|
|
115
140
|
this._syncFullscreenUI(true)
|
|
116
141
|
// Defer to ensure all sibling controllers are connected
|
|
117
142
|
requestAnimationFrame(() => this.openForCreative())
|
|
143
|
+
} else if (this.isDocked()) {
|
|
144
|
+
this.enterDockedMode()
|
|
118
145
|
} else {
|
|
119
146
|
this.openFromUrl()
|
|
120
147
|
}
|
|
@@ -123,6 +150,7 @@ export default class extends Controller {
|
|
|
123
150
|
disconnect() {
|
|
124
151
|
this._releaseWakeLock()
|
|
125
152
|
this.clearPendingOpenFromUrl()
|
|
153
|
+
if (this.dockedOpenTimeout) window.clearTimeout(this.dockedOpenTimeout)
|
|
126
154
|
document.removeEventListener(CREATIVE_CLICK_EVENT, this.handleCreativeClick)
|
|
127
155
|
document.removeEventListener(CREATIVE_DESTROYED_EVENT, this.handleCreativeDestroyed)
|
|
128
156
|
document.removeEventListener('creative-editing:start', this.handleEditingStart)
|
|
@@ -133,6 +161,7 @@ export default class extends Controller {
|
|
|
133
161
|
document.removeEventListener('visibilitychange', this.handleVisibilityChange)
|
|
134
162
|
window.removeEventListener('popstate', this.handlePopState)
|
|
135
163
|
document.removeEventListener('keydown', this.handleChatNavKeydown)
|
|
164
|
+
this.dockedMediaQuery?.removeEventListener('change', this.handleDockedMediaChange)
|
|
136
165
|
document.removeEventListener('click', this.handleDropdownOutsideClick)
|
|
137
166
|
this._clearLongPressTimer()
|
|
138
167
|
if (this.hasHeaderTarget) {
|
|
@@ -184,15 +213,97 @@ export default class extends Controller {
|
|
|
184
213
|
handleCreativeClick(event) {
|
|
185
214
|
const button = event.detail?.button
|
|
186
215
|
const creativeId = event.detail?.creativeId
|
|
216
|
+
const highlightId = event.detail?.highlightId
|
|
187
217
|
if (!button) return
|
|
218
|
+
if (event.detail?.workspaceSync && !this.isDocked()) {
|
|
219
|
+
const nextCreativeId = creativeId || button.dataset.creativeId || ''
|
|
220
|
+
if (
|
|
221
|
+
this.element.style.display === 'flex' &&
|
|
222
|
+
this.element.dataset.creativeId !== String(nextCreativeId)
|
|
223
|
+
) {
|
|
224
|
+
this.close()
|
|
225
|
+
}
|
|
226
|
+
return
|
|
227
|
+
}
|
|
228
|
+
if (!creativeId && button.dataset.workspaceNavigationState === 'true' && this.isDocked()) {
|
|
229
|
+
this.resetDockedToEmpty()
|
|
230
|
+
return
|
|
231
|
+
}
|
|
232
|
+
// A collapsed docked chat is a 3rem rail, so an explicit chat-icon click has
|
|
233
|
+
// to expand it. Workspace tree navigation must not: the user collapsed the
|
|
234
|
+
// chat on purpose and moving around the tree should leave it that way.
|
|
235
|
+
const userRequestedOpen = !event.detail?.workspaceSync
|
|
188
236
|
if (
|
|
189
237
|
this.element.style.display === 'flex' &&
|
|
190
238
|
this.element.dataset.creativeId === (creativeId || button.dataset.creativeId)
|
|
191
239
|
) {
|
|
240
|
+
if (this.isDocked()) {
|
|
241
|
+
if (userRequestedOpen) {
|
|
242
|
+
// Already loaded for this creative — expand only, so the draft and
|
|
243
|
+
// subscriptions survive.
|
|
244
|
+
this.expandDocked()
|
|
245
|
+
} else if (highlightId) {
|
|
246
|
+
this.reloadDockedHighlight(creativeId, highlightId)
|
|
247
|
+
}
|
|
248
|
+
return
|
|
249
|
+
}
|
|
192
250
|
this.close()
|
|
193
251
|
return
|
|
194
252
|
}
|
|
195
|
-
this.
|
|
253
|
+
if (userRequestedOpen) this.expandDocked()
|
|
254
|
+
const openOptions = { creativeId }
|
|
255
|
+
if (highlightId) openOptions.highlightId = highlightId
|
|
256
|
+
this.open(button, openOptions)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
reloadDockedHighlight(creativeId, highlightId) {
|
|
260
|
+
this.openGeneration += 1
|
|
261
|
+
const listController = this.listController
|
|
262
|
+
// This direct reload supersedes any full open that is still waiting for
|
|
263
|
+
// topics. That open will stop at its generation check, so release the
|
|
264
|
+
// suppression it installed before starting the replacement highlight load.
|
|
265
|
+
if (listController) listController.suppressTopicChangeLoad = false
|
|
266
|
+
const existingComment = document.getElementById(`comment_${highlightId}`)
|
|
267
|
+
if (existingComment && listController?.listTarget?.contains(existingComment)) {
|
|
268
|
+
listController.highlightComment(highlightId)
|
|
269
|
+
return
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
listController?.onPopupOpened({
|
|
273
|
+
creativeId,
|
|
274
|
+
highlightId,
|
|
275
|
+
topicId: this.topicsController?.currentTopicId,
|
|
276
|
+
})
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
resetDockedToEmpty() {
|
|
280
|
+
this.openGeneration += 1
|
|
281
|
+
|
|
282
|
+
if (this.isFullscreen()) this._exitFullscreenState()
|
|
283
|
+
|
|
284
|
+
if (this.dockedOpenTimeout) {
|
|
285
|
+
window.clearTimeout(this.dockedOpenTimeout)
|
|
286
|
+
this.dockedOpenTimeout = null
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
this.currentButton = null
|
|
290
|
+
this.element.dataset.creativeId = ''
|
|
291
|
+
this.element.dataset.canComment = 'false'
|
|
292
|
+
this.element.dataset.creativeSnippet = ''
|
|
293
|
+
this.titleTarget.textContent = this.element.dataset.defaultTitle || ''
|
|
294
|
+
this._clearChatActiveRow()
|
|
295
|
+
this._hideNavDropdown()
|
|
296
|
+
|
|
297
|
+
if (this.listController) this.listController.creativeId = null
|
|
298
|
+
this.closeChildControllers()
|
|
299
|
+
this.formController?.setCommentPermission(false)
|
|
300
|
+
this.element.querySelector('#comment-topics')?.replaceChildren()
|
|
301
|
+
if (this.hasListTarget) {
|
|
302
|
+
this.listTarget.classList.add('docked-empty')
|
|
303
|
+
this.listTarget.textContent = this.element.dataset.dockedEmptyText || ''
|
|
304
|
+
}
|
|
305
|
+
this.showPopup()
|
|
306
|
+
this.dispatchPopupClosed()
|
|
196
307
|
}
|
|
197
308
|
|
|
198
309
|
handleCreativeDestroyed(event) {
|
|
@@ -204,7 +315,11 @@ export default class extends Controller {
|
|
|
204
315
|
|
|
205
316
|
if (this.element.style.display !== 'flex') return
|
|
206
317
|
if (destroyedIds.includes(this.element.dataset.creativeId)) {
|
|
207
|
-
this.
|
|
318
|
+
if (this.isDocked() && !this.isFullscreen()) {
|
|
319
|
+
this.resetDockedToEmpty()
|
|
320
|
+
} else {
|
|
321
|
+
this.close()
|
|
322
|
+
}
|
|
208
323
|
}
|
|
209
324
|
}
|
|
210
325
|
|
|
@@ -219,6 +334,8 @@ export default class extends Controller {
|
|
|
219
334
|
}
|
|
220
335
|
|
|
221
336
|
async open(button, { creativeId, highlightId } = {}) {
|
|
337
|
+
const openGeneration = ++this.openGeneration
|
|
338
|
+
if (this.hasListTarget) this.listTarget.classList.remove('docked-empty')
|
|
222
339
|
this.currentButton = button
|
|
223
340
|
const resolvedCreativeId = creativeId || button?.dataset.creativeId
|
|
224
341
|
const canComment = button.dataset.canComment === 'true'
|
|
@@ -236,7 +353,13 @@ export default class extends Controller {
|
|
|
236
353
|
this.showPopup()
|
|
237
354
|
this.updatePosition()
|
|
238
355
|
|
|
239
|
-
await this.notifyChildControllers({
|
|
356
|
+
const opened = await this.notifyChildControllers({
|
|
357
|
+
creativeId: resolvedCreativeId,
|
|
358
|
+
canComment,
|
|
359
|
+
highlightId,
|
|
360
|
+
openGeneration,
|
|
361
|
+
})
|
|
362
|
+
if (!opened) return
|
|
240
363
|
|
|
241
364
|
// Track in chat navigation history (skip if navigating via back/forward)
|
|
242
365
|
if (!this._isNavigating) {
|
|
@@ -254,7 +377,9 @@ export default class extends Controller {
|
|
|
254
377
|
}))
|
|
255
378
|
}
|
|
256
379
|
|
|
257
|
-
async openForCreative() {
|
|
380
|
+
async openForCreative({ highlightId } = {}) {
|
|
381
|
+
const openGeneration = ++this.openGeneration
|
|
382
|
+
if (this.hasListTarget) this.listTarget.classList.remove('docked-empty')
|
|
258
383
|
const resolvedCreativeId = this.element.dataset.creativeId
|
|
259
384
|
const canComment = this.element.dataset.canComment === 'true'
|
|
260
385
|
const snippet = this.element.dataset.creativeSnippet || ''
|
|
@@ -269,7 +394,13 @@ export default class extends Controller {
|
|
|
269
394
|
|
|
270
395
|
this.showPopup()
|
|
271
396
|
|
|
272
|
-
await this.notifyChildControllers({
|
|
397
|
+
const opened = await this.notifyChildControllers({
|
|
398
|
+
creativeId: resolvedCreativeId,
|
|
399
|
+
canComment,
|
|
400
|
+
highlightId,
|
|
401
|
+
openGeneration,
|
|
402
|
+
})
|
|
403
|
+
if (!opened) return
|
|
273
404
|
|
|
274
405
|
// Track in chat navigation history
|
|
275
406
|
if (!this._isNavigating) {
|
|
@@ -287,7 +418,7 @@ export default class extends Controller {
|
|
|
287
418
|
}))
|
|
288
419
|
}
|
|
289
420
|
|
|
290
|
-
async notifyChildControllers({ creativeId, canComment, highlightId }) {
|
|
421
|
+
async notifyChildControllers({ creativeId, canComment, highlightId, openGeneration }) {
|
|
291
422
|
this.topicsController?.clearOverrideTopicId()
|
|
292
423
|
// Drop the previous creative's topic selection from the form controller
|
|
293
424
|
// synchronously, BEFORE topics loadTopics() dispatches `comments--topics:change`
|
|
@@ -295,6 +426,7 @@ export default class extends Controller {
|
|
|
295
426
|
// formController.onPopupOpened, which runs after the topics await — would
|
|
296
427
|
// erase the topic that restoreSelection() just restored from the server.
|
|
297
428
|
if (this.formController) {
|
|
429
|
+
this.formController.onChatWillOpen?.({ creativeId })
|
|
298
430
|
this.formController.currentTopicId = ''
|
|
299
431
|
this.formController._mainTopicId = null
|
|
300
432
|
}
|
|
@@ -307,18 +439,28 @@ export default class extends Controller {
|
|
|
307
439
|
// Without this, the topic change event fires loadInitialComments() before
|
|
308
440
|
// onPopupOpened sets highlightAfterLoad, causing a race where the non-highlight
|
|
309
441
|
// load can overwrite the deep-link highlight load.
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
442
|
+
const suppressedListController = this.listController
|
|
443
|
+
if (suppressedListController) {
|
|
444
|
+
suppressedListController.creativeId = creativeId
|
|
445
|
+
suppressedListController.suppressTopicChangeLoad = true
|
|
313
446
|
}
|
|
314
447
|
|
|
315
448
|
// Load topics first to establish context
|
|
316
|
-
|
|
317
|
-
|
|
449
|
+
try {
|
|
450
|
+
if (this.topicsController) {
|
|
451
|
+
await this.topicsController.onPopupOpened({ creativeId })
|
|
452
|
+
}
|
|
453
|
+
} catch (error) {
|
|
454
|
+
if (openGeneration === this.openGeneration && suppressedListController) {
|
|
455
|
+
suppressedListController.suppressTopicChangeLoad = false
|
|
456
|
+
}
|
|
457
|
+
throw error
|
|
318
458
|
}
|
|
319
459
|
|
|
320
|
-
if (this.
|
|
321
|
-
|
|
460
|
+
if (openGeneration !== this.openGeneration) return false
|
|
461
|
+
|
|
462
|
+
if (suppressedListController) {
|
|
463
|
+
suppressedListController.suppressTopicChangeLoad = false
|
|
322
464
|
}
|
|
323
465
|
|
|
324
466
|
if (this.formController) {
|
|
@@ -340,63 +482,21 @@ export default class extends Controller {
|
|
|
340
482
|
if (this.dropTriggerController) {
|
|
341
483
|
this.dropTriggerController.onPopupOpened({ creativeId })
|
|
342
484
|
}
|
|
485
|
+
return true
|
|
343
486
|
}
|
|
344
487
|
|
|
345
488
|
close() {
|
|
346
|
-
if (this.
|
|
347
|
-
this.
|
|
348
|
-
|
|
349
|
-
if (this.formController) {
|
|
350
|
-
this.formController.onPopupClosed()
|
|
351
|
-
}
|
|
352
|
-
if (this.listController) {
|
|
353
|
-
this.listController.onPopupClosed()
|
|
354
|
-
}
|
|
355
|
-
if (this.mentionMenuController) {
|
|
356
|
-
this.mentionMenuController.onPopupClosed()
|
|
357
|
-
}
|
|
358
|
-
if (this.topicsController) {
|
|
359
|
-
this.topicsController.onPopupClosed()
|
|
360
|
-
}
|
|
361
|
-
if (this.contextsController) {
|
|
362
|
-
this.contextsController.onPopupClosed()
|
|
363
|
-
}
|
|
364
|
-
if (this.dropTriggerController) {
|
|
365
|
-
this.dropTriggerController.onPopupClosed()
|
|
489
|
+
if (this.isDocked() && !this.isFullscreen()) {
|
|
490
|
+
this.toggleDocked()
|
|
491
|
+
return
|
|
366
492
|
}
|
|
367
493
|
|
|
368
|
-
|
|
369
|
-
this.element.dispatchEvent(new CustomEvent('comments-popup:closed', {
|
|
370
|
-
bubbles: true,
|
|
371
|
-
detail: {
|
|
372
|
-
badgeContainer: this.element.querySelector('[data-integration-badges]')
|
|
373
|
-
}
|
|
374
|
-
}))
|
|
494
|
+
this.openGeneration += 1
|
|
375
495
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
this.element.dataset.fullscreen = 'false'
|
|
379
|
-
document.body.classList.remove('chat-fullscreen')
|
|
380
|
-
this._syncFullscreenUI(false)
|
|
381
|
-
this._savedStyles = null
|
|
496
|
+
this.closeChildControllers()
|
|
497
|
+
this.dispatchPopupClosed()
|
|
382
498
|
|
|
383
|
-
|
|
384
|
-
// fullscreen history entry instead of pushing a new one, preventing a
|
|
385
|
-
// stale fullscreen entry from being reached via the Back button.
|
|
386
|
-
const creativeId = this.element.dataset.creativeId
|
|
387
|
-
const backUrl = this._previousUrl || (creativeId ? `/creatives/${creativeId}` : null)
|
|
388
|
-
if (backUrl) {
|
|
389
|
-
const url = new URL(backUrl, window.location.origin)
|
|
390
|
-
// Strip comment auto-open markers so a refresh after close doesn't
|
|
391
|
-
// re-open the popup (handles ?open_comments, ?comment_id, #comment_*).
|
|
392
|
-
url.searchParams.delete('open_comments')
|
|
393
|
-
url.searchParams.delete('comment_id')
|
|
394
|
-
const cleanPath = url.pathname.replace(/\/comments\/\d+$/, '')
|
|
395
|
-
url.hash = url.hash.replace(/^#comment_\d+$/, '')
|
|
396
|
-
window.history.replaceState({ fullscreen: false }, '', cleanPath + url.search + url.hash)
|
|
397
|
-
}
|
|
398
|
-
this._previousUrl = null
|
|
399
|
-
}
|
|
499
|
+
this._exitFullscreenState()
|
|
400
500
|
|
|
401
501
|
this._clearChatActiveRow()
|
|
402
502
|
this._hideNavDropdown()
|
|
@@ -414,7 +514,69 @@ export default class extends Controller {
|
|
|
414
514
|
delete this.element.dataset.resized
|
|
415
515
|
}
|
|
416
516
|
|
|
517
|
+
_exitFullscreenState() {
|
|
518
|
+
if (!this.isFullscreen()) return
|
|
519
|
+
|
|
520
|
+
if (this._enterCleanupTimer) {
|
|
521
|
+
window.clearTimeout(this._enterCleanupTimer)
|
|
522
|
+
this._enterCleanupTimer = null
|
|
523
|
+
}
|
|
524
|
+
if (this._enterCleanupFn) {
|
|
525
|
+
this.element.removeEventListener('transitionend', this._enterCleanupFn)
|
|
526
|
+
this._enterCleanupFn = null
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
this.element.dataset.fullscreen = 'false'
|
|
530
|
+
document.body.classList.remove('chat-fullscreen')
|
|
531
|
+
this._syncFullscreenUI(false)
|
|
532
|
+
this._savedStyles = null
|
|
533
|
+
this.element.style.transition = ''
|
|
534
|
+
this.element.style.position = ''
|
|
535
|
+
this.element.style.top = ''
|
|
536
|
+
this.element.style.left = ''
|
|
537
|
+
this.element.style.right = ''
|
|
538
|
+
this.element.style.bottom = ''
|
|
539
|
+
this.element.style.width = ''
|
|
540
|
+
this.element.style.height = ''
|
|
541
|
+
this.element.style.transform = ''
|
|
542
|
+
|
|
543
|
+
// Consume the fullscreen history entry and remove markers that could
|
|
544
|
+
// reopen the invalidated chat after a reset or close.
|
|
545
|
+
const creativeId = this.element.dataset.creativeId
|
|
546
|
+
const backUrl = this._previousUrl || (creativeId ? `/creatives/${creativeId}` : null)
|
|
547
|
+
if (backUrl) {
|
|
548
|
+
const url = new URL(backUrl, window.location.origin)
|
|
549
|
+
url.searchParams.delete('open_comments')
|
|
550
|
+
url.searchParams.delete('comment_id')
|
|
551
|
+
const cleanPath = url.pathname.replace(/\/comments\/\d+$/, '')
|
|
552
|
+
url.hash = url.hash.replace(/^#comment_\d+$/, '')
|
|
553
|
+
window.history.replaceState({ fullscreen: false }, '', cleanPath + url.search + url.hash)
|
|
554
|
+
}
|
|
555
|
+
this._previousUrl = null
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
closeChildControllers() {
|
|
559
|
+
this.presenceController?.onPopupClosed()
|
|
560
|
+
this.formController?.onPopupClosed()
|
|
561
|
+
this.listController?.onPopupClosed()
|
|
562
|
+
this.mentionMenuController?.onPopupClosed()
|
|
563
|
+
this.topicsController?.onPopupClosed()
|
|
564
|
+
this.contextsController?.onPopupClosed()
|
|
565
|
+
this.dropTriggerController?.onPopupClosed()
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
dispatchPopupClosed() {
|
|
569
|
+
this.element.dispatchEvent(new CustomEvent('comments-popup:closed', {
|
|
570
|
+
bubbles: true,
|
|
571
|
+
detail: {
|
|
572
|
+
badgeContainer: this.element.querySelector('[data-integration-badges]')
|
|
573
|
+
}
|
|
574
|
+
}))
|
|
575
|
+
}
|
|
576
|
+
|
|
417
577
|
prepareSize() {
|
|
578
|
+
if (this.isDocked()) return
|
|
579
|
+
|
|
418
580
|
const stored = window.localStorage.getItem(SIZE_STORAGE_KEY)
|
|
419
581
|
if (!stored) return
|
|
420
582
|
try {
|
|
@@ -432,10 +594,13 @@ export default class extends Controller {
|
|
|
432
594
|
|
|
433
595
|
showPopup() {
|
|
434
596
|
this.element.style.display = 'flex'
|
|
435
|
-
if (this.
|
|
597
|
+
if (this.isDocked()) {
|
|
598
|
+
this.element.classList.add('docked')
|
|
599
|
+
this.syncDockedUI()
|
|
600
|
+
} else if (this.isMobile()) {
|
|
436
601
|
this.element.classList.add('open')
|
|
437
602
|
}
|
|
438
|
-
this.
|
|
603
|
+
this._syncWakeLock()
|
|
439
604
|
}
|
|
440
605
|
|
|
441
606
|
isFullscreen() {
|
|
@@ -446,8 +611,97 @@ export default class extends Controller {
|
|
|
446
611
|
return window.innerWidth <= 600
|
|
447
612
|
}
|
|
448
613
|
|
|
614
|
+
isDocked() {
|
|
615
|
+
return this.element.dataset.docked === 'true' && this.dockedMediaQuery?.matches === true
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
enterDockedMode() {
|
|
619
|
+
const el = this.element
|
|
620
|
+
if (this.dockedOpenTimeout) window.clearTimeout(this.dockedOpenTimeout)
|
|
621
|
+
el.classList.add('docked')
|
|
622
|
+
el.classList.remove('open')
|
|
623
|
+
el.style.position = ''
|
|
624
|
+
el.style.top = ''
|
|
625
|
+
el.style.left = ''
|
|
626
|
+
el.style.right = ''
|
|
627
|
+
el.style.bottom = ''
|
|
628
|
+
el.style.width = ''
|
|
629
|
+
el.style.height = ''
|
|
630
|
+
delete el.dataset.resized
|
|
631
|
+
this.showPopup()
|
|
632
|
+
|
|
633
|
+
if (el.dataset.creativeId) {
|
|
634
|
+
requestAnimationFrame(() => {
|
|
635
|
+
this.dockedOpenTimeout = window.setTimeout(() => {
|
|
636
|
+
this.dockedOpenTimeout = null
|
|
637
|
+
if (!this.element.isConnected || !this.isDocked()) return
|
|
638
|
+
|
|
639
|
+
this.openForCreative({ highlightId: this.commentIdFromUrl() })
|
|
640
|
+
}, 0)
|
|
641
|
+
})
|
|
642
|
+
} else if (this.hasListTarget) {
|
|
643
|
+
this.listTarget.classList.add('docked-empty')
|
|
644
|
+
this.listTarget.textContent = el.dataset.dockedEmptyText || ''
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
handleDockedMediaChange(event) {
|
|
649
|
+
if (event.matches) {
|
|
650
|
+
this.enterDockedMode()
|
|
651
|
+
} else {
|
|
652
|
+
this.element.classList.remove('docked', 'docked-collapsed')
|
|
653
|
+
this.syncDockedUI()
|
|
654
|
+
this.close()
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
toggleDocked() {
|
|
659
|
+
if (!this.isDocked()) return
|
|
660
|
+
|
|
661
|
+
if (this.element.classList.contains('docked-collapsed')) {
|
|
662
|
+
this.expandDocked()
|
|
663
|
+
return
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
this.element.classList.add('docked-collapsed')
|
|
667
|
+
this.syncDockedUI()
|
|
668
|
+
this._syncWakeLock()
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
expandDocked() {
|
|
672
|
+
if (!this.isDocked()) return
|
|
673
|
+
if (!this.element.classList.contains('docked-collapsed')) return
|
|
674
|
+
|
|
675
|
+
this.element.classList.remove('docked-collapsed')
|
|
676
|
+
this.syncDockedUI()
|
|
677
|
+
this._syncWakeLock()
|
|
678
|
+
requestAnimationFrame(() => this.listController?.scrollToBottom())
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
syncDockedUI() {
|
|
682
|
+
if (!this.hasCloseButtonTarget) return
|
|
683
|
+
|
|
684
|
+
if (!this.isDocked()) {
|
|
685
|
+
const label = this.element.dataset.closeLabel || ''
|
|
686
|
+
this.closeIconTarget.style.display = ''
|
|
687
|
+
this.expandDockedIconTarget.style.display = 'none'
|
|
688
|
+
this.closeButtonTarget.setAttribute('aria-label', label)
|
|
689
|
+
this.closeButtonTarget.title = label
|
|
690
|
+
return
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
const collapsed = this.element.classList.contains('docked-collapsed')
|
|
694
|
+
const label = collapsed
|
|
695
|
+
? (this.element.dataset.expandDockedLabel || '')
|
|
696
|
+
: (this.element.dataset.collapseDockedLabel || '')
|
|
697
|
+
this.closeIconTarget.style.display = collapsed ? 'none' : ''
|
|
698
|
+
this.expandDockedIconTarget.style.display = collapsed ? '' : 'none'
|
|
699
|
+
this.closeButtonTarget.setAttribute('aria-label', label)
|
|
700
|
+
this.closeButtonTarget.title = label
|
|
701
|
+
}
|
|
702
|
+
|
|
449
703
|
updatePosition() {
|
|
450
|
-
if (this.isFullscreen() || !this.currentButton || this.isMobile() || this.element.dataset.resized === 'true') return
|
|
704
|
+
if (this.isDocked() || this.isFullscreen() || !this.currentButton || this.isMobile() || this.element.dataset.resized === 'true') return
|
|
451
705
|
const rect = this.currentButton.getBoundingClientRect()
|
|
452
706
|
const popupWidth = this.element.offsetWidth
|
|
453
707
|
const popupHeight = this.element.offsetHeight
|
|
@@ -473,6 +727,8 @@ export default class extends Controller {
|
|
|
473
727
|
}
|
|
474
728
|
|
|
475
729
|
startResize(event, direction) {
|
|
730
|
+
if (this.isDocked()) return
|
|
731
|
+
|
|
476
732
|
event.preventDefault()
|
|
477
733
|
const rect = this.element.getBoundingClientRect()
|
|
478
734
|
this.resizeStartX = event.clientX
|
|
@@ -542,7 +798,7 @@ export default class extends Controller {
|
|
|
542
798
|
this.touchStartY = null
|
|
543
799
|
return
|
|
544
800
|
}
|
|
545
|
-
if (event.target.closest('#comments-list
|
|
801
|
+
if (event.target.closest('#comments-list, .chat-nav-dropdown, #topic-list-modal')) {
|
|
546
802
|
this.touchStartY = null
|
|
547
803
|
} else {
|
|
548
804
|
this.touchStartY = event.touches[0].clientY
|
|
@@ -583,7 +839,7 @@ export default class extends Controller {
|
|
|
583
839
|
if (!document.hidden && this.element.style.display === 'flex') {
|
|
584
840
|
this.listController?.loadInitialComments()
|
|
585
841
|
// Re-acquire wake lock — released automatically when tab loses visibility
|
|
586
|
-
this.
|
|
842
|
+
this._syncWakeLock()
|
|
587
843
|
}
|
|
588
844
|
}
|
|
589
845
|
|
|
@@ -799,6 +1055,34 @@ export default class extends Controller {
|
|
|
799
1055
|
return
|
|
800
1056
|
}
|
|
801
1057
|
|
|
1058
|
+
if (this.isDocked()) {
|
|
1059
|
+
el.style.transition = 'none'
|
|
1060
|
+
el.dataset.fullscreen = 'false'
|
|
1061
|
+
document.body.classList.remove('chat-fullscreen')
|
|
1062
|
+
el.style.position = ''
|
|
1063
|
+
el.style.top = ''
|
|
1064
|
+
el.style.left = ''
|
|
1065
|
+
el.style.right = ''
|
|
1066
|
+
el.style.bottom = ''
|
|
1067
|
+
el.style.width = ''
|
|
1068
|
+
el.style.height = ''
|
|
1069
|
+
this._savedStyles = null
|
|
1070
|
+
this._syncFullscreenUI(false)
|
|
1071
|
+
this.syncDockedUI()
|
|
1072
|
+
el.offsetHeight // eslint-disable-line no-unused-expressions
|
|
1073
|
+
el.style.transition = ''
|
|
1074
|
+
|
|
1075
|
+
let backUrl = this._previousUrl || (creativeId ? `/creatives/${creativeId}` : null)
|
|
1076
|
+
if (backUrl) {
|
|
1077
|
+
const url = new URL(backUrl, window.location.origin)
|
|
1078
|
+
url.searchParams.set('open_comments', 'true')
|
|
1079
|
+
window.history.pushState({ fullscreen: false }, '', url.pathname + url.search)
|
|
1080
|
+
}
|
|
1081
|
+
this._previousUrl = null
|
|
1082
|
+
requestAnimationFrame(() => this.listController?.scrollToBottom())
|
|
1083
|
+
return
|
|
1084
|
+
}
|
|
1085
|
+
|
|
802
1086
|
// Desktop: animated exit to target position
|
|
803
1087
|
// Calculate target position using viewport-relative coords (popup is position: fixed)
|
|
804
1088
|
let finalTop = '' // px string (viewport-relative)
|
|
@@ -899,7 +1183,15 @@ export default class extends Controller {
|
|
|
899
1183
|
el.style.width = `${animWidth}px`
|
|
900
1184
|
el.style.height = `${animHeight}px`
|
|
901
1185
|
|
|
1186
|
+
let cleanupTimer = null
|
|
1187
|
+
let cleanedUp = false
|
|
902
1188
|
const cleanup = () => {
|
|
1189
|
+
if (cleanedUp) return
|
|
1190
|
+
cleanedUp = true
|
|
1191
|
+
if (cleanupTimer !== null) {
|
|
1192
|
+
clearTimeout(cleanupTimer)
|
|
1193
|
+
cleanupTimer = null
|
|
1194
|
+
}
|
|
903
1195
|
el.removeEventListener('transitionend', cleanup)
|
|
904
1196
|
// Popup is always position: fixed — just apply final coords
|
|
905
1197
|
el.style.transition = 'none'
|
|
@@ -940,7 +1232,7 @@ export default class extends Controller {
|
|
|
940
1232
|
this.topicsController?.scrollToActiveTopic()
|
|
941
1233
|
}
|
|
942
1234
|
el.addEventListener('transitionend', cleanup, { once: true })
|
|
943
|
-
setTimeout(cleanup, 300)
|
|
1235
|
+
cleanupTimer = setTimeout(cleanup, 300)
|
|
944
1236
|
|
|
945
1237
|
// Update URL — append open_comments=true so the popup stays open on refresh
|
|
946
1238
|
let backUrl = this._previousUrl || (creativeId ? `/creatives/${creativeId}` : null)
|
|
@@ -976,6 +1268,10 @@ export default class extends Controller {
|
|
|
976
1268
|
el.dataset.fullscreen = isFs ? 'true' : 'false'
|
|
977
1269
|
document.body.classList.toggle('chat-fullscreen', isFs)
|
|
978
1270
|
this._syncFullscreenUI(isFs)
|
|
1271
|
+
if (!isFs && this.isDocked()) {
|
|
1272
|
+
el.style.display = 'flex'
|
|
1273
|
+
this.syncDockedUI()
|
|
1274
|
+
}
|
|
979
1275
|
|
|
980
1276
|
if (!isFs && this._savedStyles) {
|
|
981
1277
|
Object.assign(el.style, this._savedStyles)
|
|
@@ -998,10 +1294,10 @@ export default class extends Controller {
|
|
|
998
1294
|
this.exitFullscreenIconTarget.style.display = entering ? '' : 'none'
|
|
999
1295
|
}
|
|
1000
1296
|
if (this.hasLeftHandleTarget) {
|
|
1001
|
-
this.leftHandleTarget.style.display = entering ? 'none' : ''
|
|
1297
|
+
this.leftHandleTarget.style.display = entering || this.isDocked() ? 'none' : ''
|
|
1002
1298
|
}
|
|
1003
1299
|
if (this.hasRightHandleTarget) {
|
|
1004
|
-
this.rightHandleTarget.style.display = entering ? 'none' : ''
|
|
1300
|
+
this.rightHandleTarget.style.display = entering || this.isDocked() ? 'none' : ''
|
|
1005
1301
|
}
|
|
1006
1302
|
if (this.hasCloseButtonTarget) {
|
|
1007
1303
|
this.closeButtonTarget.style.display = entering ? 'none' : ''
|
|
@@ -1012,11 +1308,11 @@ export default class extends Controller {
|
|
|
1012
1308
|
: (this.element.dataset.fullscreenLabel || 'Full screen')
|
|
1013
1309
|
this.fullscreenButtonTarget.setAttribute('aria-label', label)
|
|
1014
1310
|
}
|
|
1311
|
+
if (!entering) this.syncDockedUI()
|
|
1015
1312
|
}
|
|
1016
1313
|
|
|
1017
|
-
|
|
1314
|
+
commentIdFromUrl() {
|
|
1018
1315
|
const params = new URLSearchParams(window.location.search)
|
|
1019
|
-
const openComments = params.get('open_comments') === 'true'
|
|
1020
1316
|
let commentId = params.get('comment_id')
|
|
1021
1317
|
if (!commentId) {
|
|
1022
1318
|
const pathCommentMatch = window.location.pathname.match(/\/creatives\/\d+\/comments\/(\d+)/)
|
|
@@ -1031,6 +1327,14 @@ export default class extends Controller {
|
|
|
1031
1327
|
}
|
|
1032
1328
|
}
|
|
1033
1329
|
|
|
1330
|
+
return commentId || undefined
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
openFromUrl() {
|
|
1334
|
+
const params = new URLSearchParams(window.location.search)
|
|
1335
|
+
const openComments = params.get('open_comments') === 'true'
|
|
1336
|
+
const commentId = this.commentIdFromUrl()
|
|
1337
|
+
|
|
1034
1338
|
let creativeId = params.get('id')
|
|
1035
1339
|
if (!creativeId) {
|
|
1036
1340
|
const pathCreativeMatch = window.location.pathname.match(/\/creatives\/(\d+)/)
|
|
@@ -1359,15 +1663,53 @@ export default class extends Controller {
|
|
|
1359
1663
|
// is open. The browser automatically releases the lock when the tab
|
|
1360
1664
|
// loses visibility, so we re-acquire it in handleVisibilityChange().
|
|
1361
1665
|
|
|
1666
|
+
_shouldHoldWakeLock() {
|
|
1667
|
+
if (this.element.style.display !== 'flex') return false
|
|
1668
|
+
if (this.isFullscreen()) return true
|
|
1669
|
+
if (!this.isDocked()) return true
|
|
1670
|
+
|
|
1671
|
+
return Boolean(this.element.dataset.creativeId) &&
|
|
1672
|
+
!this.element.classList.contains('docked-collapsed')
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
_syncWakeLock() {
|
|
1676
|
+
if (this._shouldHoldWakeLock()) {
|
|
1677
|
+
this._requestWakeLock()
|
|
1678
|
+
} else {
|
|
1679
|
+
this._releaseWakeLock()
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1362
1683
|
async _requestWakeLock() {
|
|
1363
|
-
if (
|
|
1684
|
+
if (
|
|
1685
|
+
!this._shouldHoldWakeLock() ||
|
|
1686
|
+
!('wakeLock' in navigator) ||
|
|
1687
|
+
this._wakeLock ||
|
|
1688
|
+
this._wakeLockRequest
|
|
1689
|
+
) return
|
|
1364
1690
|
|
|
1691
|
+
let request
|
|
1365
1692
|
try {
|
|
1366
|
-
|
|
1367
|
-
this.
|
|
1368
|
-
|
|
1693
|
+
request = navigator.wakeLock.request('screen')
|
|
1694
|
+
this._wakeLockRequest = request
|
|
1695
|
+
const wakeLock = await request
|
|
1696
|
+
if (this._wakeLockRequest !== request) {
|
|
1697
|
+
wakeLock.release()
|
|
1698
|
+
return
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
this._wakeLockRequest = null
|
|
1702
|
+
if (!this._shouldHoldWakeLock()) {
|
|
1703
|
+
wakeLock.release()
|
|
1704
|
+
return
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
this._wakeLock = wakeLock
|
|
1708
|
+
wakeLock.addEventListener('release', () => {
|
|
1709
|
+
if (this._wakeLock === wakeLock) this._wakeLock = null
|
|
1369
1710
|
})
|
|
1370
1711
|
} catch (err) {
|
|
1712
|
+
if (this._wakeLockRequest === request) this._wakeLockRequest = null
|
|
1371
1713
|
// Wake lock request can fail (e.g. low battery, browser policy).
|
|
1372
1714
|
// This is non-critical — just log and continue.
|
|
1373
1715
|
console.debug('[chat] Wake lock request failed:', err.message)
|
|
@@ -1375,9 +1717,9 @@ export default class extends Controller {
|
|
|
1375
1717
|
}
|
|
1376
1718
|
|
|
1377
1719
|
_releaseWakeLock() {
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1720
|
+
this._wakeLockRequest = null
|
|
1721
|
+
const wakeLock = this._wakeLock
|
|
1722
|
+
this._wakeLock = null
|
|
1723
|
+
wakeLock?.release()
|
|
1382
1724
|
}
|
|
1383
1725
|
}
|