collavre 0.24.0 → 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 +165 -99
- 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 +28 -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 +245 -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__/prev_message_navigator.test.js +129 -0
- data/app/javascript/controllers/comments/__tests__/topics_controller_action_errors.test.js +184 -0
- data/app/javascript/controllers/comments/__tests__/topics_controller_archived.test.js +879 -0
- data/app/javascript/controllers/comments/__tests__/topics_controller_creation_container.test.js +154 -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 +256 -0
- 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 +795 -14
- data/app/javascript/controllers/comments/list_controller.js +338 -52
- 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/prev_message_navigator.js +94 -0
- data/app/javascript/controllers/comments/topics_controller.js +1772 -100
- 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 +32 -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 +72 -30
- data/config/locales/comments.ko.yml +72 -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 +228 -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
|
@@ -61,9 +61,7 @@ nav #search {
|
|
|
61
61
|
.progress-filter-btn {
|
|
62
62
|
border-radius: var(--radius-3);
|
|
63
63
|
border: none;
|
|
64
|
-
background: transparent;
|
|
65
64
|
padding: var(--space-1) var(--space-2);
|
|
66
|
-
transition: background 0.2s var(--ease-out-2);
|
|
67
65
|
}
|
|
68
66
|
|
|
69
67
|
.progress-filter-btn:first-child {
|
|
@@ -79,11 +77,74 @@ nav #search {
|
|
|
79
77
|
}
|
|
80
78
|
|
|
81
79
|
.progress-filter-btn.active {
|
|
82
|
-
background: var(--surface-hover);
|
|
83
80
|
color: var(--text-primary);
|
|
84
81
|
font-weight: var(--weight-5);
|
|
85
82
|
}
|
|
86
83
|
|
|
84
|
+
/*
|
|
85
|
+
* Nav pills that toggle their selected surface in place
|
|
86
|
+
* -----------------------------------------------------
|
|
87
|
+
* The chat filter and the search trigger flip `.active` on a live page during
|
|
88
|
+
* turbo-frame navigation, so their surface changes where it sits instead of
|
|
89
|
+
* arriving already painted in a fresh document. Animating `background` there
|
|
90
|
+
* re-rasterises the pill's rounded corners on every frame of the transition,
|
|
91
|
+
* and mobile GPU rasterisers have been observed to mis-composite a corner arc
|
|
92
|
+
* under that load: the bottom-left arc is replaced by its own chord and the
|
|
93
|
+
* clipped sliver is drawn back on top, which reads as a folded corner.
|
|
94
|
+
*
|
|
95
|
+
* Put the surface on an overlay pseudo-element and cross-fade it with
|
|
96
|
+
* `opacity`, which the compositor animates without repainting. The rounded
|
|
97
|
+
* rect is rasterised once and only its alpha moves.
|
|
98
|
+
*
|
|
99
|
+
* `z-index: -1` keeps the overlay above the button's own background box and
|
|
100
|
+
* below its text; that needs the button to be a stacking context, hence
|
|
101
|
+
* `position: relative` + `z-index: 0`.
|
|
102
|
+
*/
|
|
103
|
+
.progress-filter-btn,
|
|
104
|
+
.search-popup-trigger {
|
|
105
|
+
position: relative;
|
|
106
|
+
z-index: 0;
|
|
107
|
+
transition: color 0.2s var(--ease-out-2);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.progress-filter-btn::before,
|
|
111
|
+
.search-popup-trigger::before {
|
|
112
|
+
content: "";
|
|
113
|
+
position: absolute;
|
|
114
|
+
inset: 0;
|
|
115
|
+
z-index: -1;
|
|
116
|
+
border-radius: inherit;
|
|
117
|
+
background: var(--surface-hover);
|
|
118
|
+
opacity: 0;
|
|
119
|
+
transition: opacity 0.2s var(--ease-out-2);
|
|
120
|
+
pointer-events: none;
|
|
121
|
+
will-change: opacity;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.progress-filter-btn:hover::before,
|
|
125
|
+
.progress-filter-btn:active::before,
|
|
126
|
+
.progress-filter-btn.active::before,
|
|
127
|
+
.search-popup-trigger:hover::before,
|
|
128
|
+
.search-popup-trigger:active::before,
|
|
129
|
+
.search-popup-trigger.active::before {
|
|
130
|
+
opacity: 1;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/*
|
|
134
|
+
* The button itself has to stay transparent in every state, otherwise the
|
|
135
|
+
* `nav a/button` surfaces above would stack on top of the overlay.
|
|
136
|
+
*/
|
|
137
|
+
.progress-filter-btn,
|
|
138
|
+
.progress-filter-btn:hover,
|
|
139
|
+
.progress-filter-btn:active,
|
|
140
|
+
.progress-filter-btn.active,
|
|
141
|
+
.search-popup-trigger,
|
|
142
|
+
.search-popup-trigger:hover,
|
|
143
|
+
.search-popup-trigger:active,
|
|
144
|
+
.search-popup-trigger.active {
|
|
145
|
+
background: transparent;
|
|
146
|
+
}
|
|
147
|
+
|
|
87
148
|
/* ChatGPT-style popup menu overrides */
|
|
88
149
|
nav .popup-menu-wrapper .popup-menu {
|
|
89
150
|
border: 1px solid var(--border-color);
|
|
@@ -16,9 +16,12 @@
|
|
|
16
16
|
scroll-behavior: smooth;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
.
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
/* This file is linked only from layouts/collavre/landing.html.erb, which renders
|
|
20
|
+
`<main><%= yield %></main>` and no app navigation — that lives in the host's
|
|
21
|
+
application layout, which never loads this stylesheet. So a blanket
|
|
22
|
+
`.landing-page nav { display: none }` could only ever hide a <nav> authored by
|
|
23
|
+
a landing-layout view (the feature-guide breadcrumbs), which is the opposite
|
|
24
|
+
of what it was for. Removed rather than narrowed. */
|
|
22
25
|
|
|
23
26
|
.landing {
|
|
24
27
|
font-family: var(--font-sans);
|
|
@@ -336,6 +339,7 @@
|
|
|
336
339
|
margin-bottom: var(--space-3);
|
|
337
340
|
}
|
|
338
341
|
|
|
342
|
+
.landing-feature-card h2,
|
|
339
343
|
.landing-feature-card h3 { /* L3 */
|
|
340
344
|
font-size: var(--text-3);
|
|
341
345
|
font-weight: var(--weight-6);
|
|
@@ -597,3 +601,104 @@ body.dark-mode .landing-shot-dark { display: block; }
|
|
|
597
601
|
.landing-hero-sub { animation-delay: 0.1s; }
|
|
598
602
|
.landing-hero-brand { animation-delay: 0.2s; }
|
|
599
603
|
.landing-hero-actions { animation-delay: 0.3s; }
|
|
604
|
+
|
|
605
|
+
/* ─── Feature Guides (/features, /features/:key) ─────────────────────────────
|
|
606
|
+
The public long-form counterpart to the empty-chat feature cards. Shares the
|
|
607
|
+
landing layout and tokens so a guide reached from a shared link reads as the
|
|
608
|
+
same product surface as the landing page it sits beside. */
|
|
609
|
+
.landing-features-more {
|
|
610
|
+
margin-top: var(--space-6);
|
|
611
|
+
text-align: center;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
.feature-guide-breadcrumb {
|
|
615
|
+
display: flex;
|
|
616
|
+
align-items: center;
|
|
617
|
+
justify-content: center;
|
|
618
|
+
gap: var(--space-2);
|
|
619
|
+
font-size: var(--text-1);
|
|
620
|
+
color: var(--text-muted);
|
|
621
|
+
margin-bottom: var(--space-6);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
.feature-guide-breadcrumb a {
|
|
625
|
+
color: var(--text-muted);
|
|
626
|
+
text-decoration: none;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
.feature-guide-breadcrumb a:hover {
|
|
630
|
+
color: var(--color-active);
|
|
631
|
+
text-decoration: underline;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/* The whole card is the link, so strip the anchor's own presentation and let
|
|
635
|
+
.landing-feature-card keep supplying it. */
|
|
636
|
+
.feature-guide-link-card {
|
|
637
|
+
display: block;
|
|
638
|
+
color: inherit;
|
|
639
|
+
text-decoration: none;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
.feature-guide-more {
|
|
643
|
+
display: inline-block;
|
|
644
|
+
margin-top: var(--space-3);
|
|
645
|
+
font-size: var(--text-1);
|
|
646
|
+
font-weight: var(--weight-6);
|
|
647
|
+
color: var(--color-active);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/* A guide is read top to bottom rather than scanned, so it drops the centered
|
|
651
|
+
landing rhythm for a single measured column. */
|
|
652
|
+
.feature-guide-container {
|
|
653
|
+
max-width: 720px;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
.feature-guide-header {
|
|
657
|
+
text-align: center;
|
|
658
|
+
margin-bottom: var(--space-8);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
.feature-guide-title { /* L2 */
|
|
662
|
+
font-size: var(--text-5);
|
|
663
|
+
font-weight: var(--weight-7);
|
|
664
|
+
margin-bottom: var(--space-3);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
.feature-guide-tagline { /* L4 */
|
|
668
|
+
font-size: var(--text-3);
|
|
669
|
+
color: var(--text-muted);
|
|
670
|
+
line-height: var(--leading-2);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
.feature-guide-section {
|
|
674
|
+
margin-bottom: var(--space-7);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
.feature-guide-heading { /* L3 */
|
|
678
|
+
font-size: var(--text-3);
|
|
679
|
+
font-weight: var(--weight-6);
|
|
680
|
+
margin-bottom: var(--space-3);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
.feature-guide-body { /* L4 */
|
|
684
|
+
font-size: var(--text-2);
|
|
685
|
+
color: var(--text-muted);
|
|
686
|
+
line-height: var(--leading-3);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
.feature-guide-tips {
|
|
690
|
+
padding-left: var(--space-5);
|
|
691
|
+
margin: 0;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
.feature-guide-tips li { /* L4 */
|
|
695
|
+
font-size: var(--text-2);
|
|
696
|
+
color: var(--text-muted);
|
|
697
|
+
line-height: var(--leading-3);
|
|
698
|
+
margin-bottom: var(--space-2);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
.feature-guide-back {
|
|
702
|
+
text-align: center;
|
|
703
|
+
margin-top: var(--space-8);
|
|
704
|
+
}
|
|
@@ -34,6 +34,38 @@
|
|
|
34
34
|
cursor: pointer;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
.llm-model-item {
|
|
38
|
+
display: flex;
|
|
39
|
+
gap: 0.75em;
|
|
40
|
+
align-items: center;
|
|
41
|
+
justify-content: space-between;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.llm-model-name {
|
|
45
|
+
min-width: 0;
|
|
46
|
+
overflow-wrap: anywhere;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.llm-model-delete {
|
|
50
|
+
flex: 0 0 auto;
|
|
51
|
+
width: 1.75em;
|
|
52
|
+
height: 1.75em;
|
|
53
|
+
padding: 0;
|
|
54
|
+
color: var(--text-muted);
|
|
55
|
+
font: inherit;
|
|
56
|
+
line-height: 1;
|
|
57
|
+
cursor: pointer;
|
|
58
|
+
background: transparent;
|
|
59
|
+
border: 0;
|
|
60
|
+
border-radius: var(--radius-round);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.llm-model-delete:hover,
|
|
64
|
+
.llm-model-delete:focus-visible {
|
|
65
|
+
color: var(--text-primary);
|
|
66
|
+
background: var(--surface-hover);
|
|
67
|
+
}
|
|
68
|
+
|
|
37
69
|
.mention-item:hover,
|
|
38
70
|
.mention-item.active,
|
|
39
71
|
.common-popup-item:hover,
|
|
@@ -105,6 +105,20 @@ body.chat-fullscreen #topic-list-modal {
|
|
|
105
105
|
display: flex;
|
|
106
106
|
flex-direction: column;
|
|
107
107
|
max-width: 90vw;
|
|
108
|
+
/* CommonPopup sets max-height inline from the space available beside the
|
|
109
|
+
anchor. Border-box keeps that number the rendered height (it is measured
|
|
110
|
+
back via offsetHeight), and hiding overflow stops content from spilling
|
|
111
|
+
past the cap instead of scrolling inside it. */
|
|
112
|
+
box-sizing: border-box;
|
|
113
|
+
overflow: hidden;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* The scrollable list is what gives when the popup is capped: flex items do not
|
|
117
|
+
shrink past their content by default, so without this the list keeps its
|
|
118
|
+
max-height and the bottom of it is simply clipped away. */
|
|
119
|
+
.common-popup-list {
|
|
120
|
+
flex: 1 1 auto;
|
|
121
|
+
min-height: 0;
|
|
108
122
|
}
|
|
109
123
|
|
|
110
124
|
.common-popup input[type="text"] {
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
body.creative-workspace {
|
|
2
|
+
height: 100dvh;
|
|
3
|
+
overflow: hidden;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.creative-workspace-shell {
|
|
7
|
+
display: grid;
|
|
8
|
+
grid-template-columns: minmax(240px, 300px) minmax(0, 1fr) minmax(0, 1fr);
|
|
9
|
+
flex: 1;
|
|
10
|
+
width: 100%;
|
|
11
|
+
min-height: 0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
body.creative-workspace > nav {
|
|
15
|
+
max-width: none;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.creative-workspace-shell > main {
|
|
19
|
+
grid-column: 2;
|
|
20
|
+
width: 100%;
|
|
21
|
+
max-width: none;
|
|
22
|
+
min-width: 0;
|
|
23
|
+
overflow: auto;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#creative-workspace-content {
|
|
27
|
+
display: block;
|
|
28
|
+
min-width: 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
body.creative-workspace .creative-content,
|
|
32
|
+
body.creative-workspace .creative-title-content {
|
|
33
|
+
min-width: 0;
|
|
34
|
+
max-width: none;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* The rule above drops the viewport-based max-width that used to cap row width.
|
|
38
|
+
Without it, the flex wrappers between the grid column and .creative-content
|
|
39
|
+
keep their default min-width: auto, so they refuse to shrink below the
|
|
40
|
+
min-content width of their contents. Text still wraps (min-content is one
|
|
41
|
+
word), but unbreakable blocks -- code blocks and tables -- overflow the
|
|
42
|
+
column. Zeroing min-width lets the flex chain shrink to the column.
|
|
43
|
+
|
|
44
|
+
The title row (the root creative) has its own chain: .creative-row-start >
|
|
45
|
+
h1.page-title > .creative-title-content. Shrinking only .creative-row-start
|
|
46
|
+
would let the title's min-content width paint through the shrunken parent,
|
|
47
|
+
so .page-title belongs in the same list. */
|
|
48
|
+
body.creative-workspace .creative-row-start,
|
|
49
|
+
body.creative-workspace .creative-tree-li,
|
|
50
|
+
body.creative-workspace .page-title,
|
|
51
|
+
body.creative-workspace .indent1,
|
|
52
|
+
body.creative-workspace .indent2,
|
|
53
|
+
body.creative-workspace .indent3 {
|
|
54
|
+
min-width: 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.creative-workspace-tree-region {
|
|
58
|
+
grid-column: 1;
|
|
59
|
+
min-width: 0;
|
|
60
|
+
overflow: hidden;
|
|
61
|
+
background: var(--surface-section);
|
|
62
|
+
border-right: var(--border-1) solid var(--border-color);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
#creative-workspace-tree {
|
|
66
|
+
display: flex;
|
|
67
|
+
flex-direction: column;
|
|
68
|
+
height: 100%;
|
|
69
|
+
min-height: 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.creative-workspace-tree-header {
|
|
73
|
+
padding: var(--space-3);
|
|
74
|
+
border-bottom: var(--border-1) solid var(--border-color);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.creative-workspace-tree-header h2 {
|
|
78
|
+
margin: 0;
|
|
79
|
+
font-size: var(--text-2);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.creative-workspace-tree-nav {
|
|
83
|
+
display: block;
|
|
84
|
+
flex: 1;
|
|
85
|
+
max-width: none;
|
|
86
|
+
min-height: 0;
|
|
87
|
+
padding: var(--space-2);
|
|
88
|
+
overflow: auto;
|
|
89
|
+
overscroll-behavior-y: contain;
|
|
90
|
+
background: transparent;
|
|
91
|
+
border: 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.creative-workspace-tree-list {
|
|
95
|
+
margin: 0;
|
|
96
|
+
padding: 0 0 0 var(--space-3);
|
|
97
|
+
list-style: none;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.creative-workspace-tree-nav > .creative-workspace-tree-list {
|
|
101
|
+
padding-left: 0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.creative-workspace-tree-row {
|
|
105
|
+
display: flex;
|
|
106
|
+
align-items: center;
|
|
107
|
+
min-width: 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.creative-workspace-tree-branch-toggle,
|
|
111
|
+
.creative-workspace-tree-branch-spacer {
|
|
112
|
+
width: 1.75rem;
|
|
113
|
+
min-width: 1.75rem;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.creative-workspace-tree-branch-toggle {
|
|
117
|
+
padding: var(--space-1);
|
|
118
|
+
color: var(--text-muted);
|
|
119
|
+
background: transparent;
|
|
120
|
+
border: 0;
|
|
121
|
+
cursor: pointer;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.creative-workspace-tree-link {
|
|
125
|
+
display: block;
|
|
126
|
+
flex: 1;
|
|
127
|
+
min-width: 0;
|
|
128
|
+
padding: var(--space-2);
|
|
129
|
+
overflow: hidden;
|
|
130
|
+
color: var(--text-primary);
|
|
131
|
+
text-overflow: ellipsis;
|
|
132
|
+
white-space: nowrap;
|
|
133
|
+
border-radius: var(--radius-2);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.creative-workspace-tree-link:hover,
|
|
137
|
+
.creative-workspace-tree-link.is-current {
|
|
138
|
+
background: var(--surface-hover);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.creative-workspace-tree-link.is-current {
|
|
142
|
+
color: var(--color-active);
|
|
143
|
+
font-weight: var(--weight-6);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.creative-workspace-tree-status {
|
|
147
|
+
padding: var(--space-3);
|
|
148
|
+
color: var(--text-muted);
|
|
149
|
+
text-align: center;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.creative-workspace-tree-toggle {
|
|
153
|
+
display: none;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
#comments-list.docked-empty {
|
|
157
|
+
align-items: center;
|
|
158
|
+
justify-content: center;
|
|
159
|
+
color: var(--text-muted);
|
|
160
|
+
text-align: center;
|
|
161
|
+
background: transparent;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
#comments-list.docked-empty::before {
|
|
165
|
+
display: none;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
@media (min-width: 768px) {
|
|
169
|
+
.creative-workspace-shell > #comments-popup[data-docked="true"]:not([data-fullscreen="true"]) {
|
|
170
|
+
display: flex;
|
|
171
|
+
position: static;
|
|
172
|
+
grid-column: 3;
|
|
173
|
+
width: auto;
|
|
174
|
+
height: auto;
|
|
175
|
+
max-width: none !important;
|
|
176
|
+
max-height: none;
|
|
177
|
+
min-width: 0;
|
|
178
|
+
min-height: 0;
|
|
179
|
+
z-index: auto;
|
|
180
|
+
border-width: 0 0 0 var(--border-1);
|
|
181
|
+
border-color: var(--border-color);
|
|
182
|
+
border-radius: 0;
|
|
183
|
+
box-shadow: none;
|
|
184
|
+
transition: none;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.creative-workspace-shell > #comments-popup[data-docked="true"]:not([data-fullscreen="true"]) .resize-handle {
|
|
188
|
+
display: none;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.creative-workspace-shell:has(#comments-popup.docked-collapsed) {
|
|
192
|
+
grid-template-columns: minmax(240px, 300px) minmax(0, 1fr) 3rem;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
#comments-popup.docked-collapsed:not([data-fullscreen="true"]) {
|
|
196
|
+
padding: var(--space-2);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
#comments-popup.docked-collapsed:not([data-fullscreen="true"]) > :not(.comments-popup-header),
|
|
200
|
+
#comments-popup.docked-collapsed:not([data-fullscreen="true"]) .comments-popup-header > :not(.comments-popup-actions),
|
|
201
|
+
#comments-popup.docked-collapsed:not([data-fullscreen="true"]) .comments-popup-actions > :not(#close-comments-btn) {
|
|
202
|
+
display: none !important;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
@media (min-width: 768px) and (max-width: 1279px) {
|
|
207
|
+
.creative-workspace-shell {
|
|
208
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.creative-workspace-shell > main {
|
|
212
|
+
grid-column: 1;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.creative-workspace-shell > #comments-popup[data-docked="true"]:not([data-fullscreen="true"]) {
|
|
216
|
+
grid-column: 2;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.creative-workspace-shell:has(#comments-popup.docked-collapsed) {
|
|
220
|
+
grid-template-columns: minmax(0, 1fr) 3rem;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/* Below the three-column breakpoint the tree gives up its own column and
|
|
225
|
+
becomes an off-canvas drawer with a toggle handle. Shared by the two-panel
|
|
226
|
+
band and the one-panel (mobile) band so both collapse the same way — and so
|
|
227
|
+
the tree stays mounted in both, preserving its expansion state. */
|
|
228
|
+
@media (max-width: 1279px) {
|
|
229
|
+
.creative-workspace-tree-region {
|
|
230
|
+
position: fixed;
|
|
231
|
+
top: 0;
|
|
232
|
+
bottom: 0;
|
|
233
|
+
left: 0;
|
|
234
|
+
z-index: var(--layer-modal);
|
|
235
|
+
width: min(300px, 80vw);
|
|
236
|
+
overflow: visible;
|
|
237
|
+
box-shadow: var(--shadow-3);
|
|
238
|
+
transform: translateX(-100%);
|
|
239
|
+
transition: transform 0.2s var(--ease-out-2);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.creative-workspace-tree-region > aside {
|
|
243
|
+
visibility: hidden;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.creative-workspace-tree-region.is-open {
|
|
247
|
+
transform: translateX(0);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.creative-workspace-tree-region.is-open > aside {
|
|
251
|
+
visibility: visible;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.creative-workspace-tree-toggle {
|
|
255
|
+
position: absolute;
|
|
256
|
+
top: 5rem;
|
|
257
|
+
right: -2.75rem;
|
|
258
|
+
display: inline-flex;
|
|
259
|
+
align-items: center;
|
|
260
|
+
justify-content: center;
|
|
261
|
+
width: 2.75rem;
|
|
262
|
+
height: 2.75rem;
|
|
263
|
+
padding: 0;
|
|
264
|
+
color: var(--text-primary);
|
|
265
|
+
background: var(--surface-section);
|
|
266
|
+
border: var(--border-1) solid var(--border-color);
|
|
267
|
+
border-left: 0;
|
|
268
|
+
border-radius: 0 var(--radius-2) var(--radius-2) 0;
|
|
269
|
+
cursor: pointer;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.creative-workspace-tree-region.is-open .creative-workspace-tree-toggle-icon {
|
|
273
|
+
transform: rotate(180deg);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
@media (max-width: 767px) {
|
|
278
|
+
body.creative-workspace {
|
|
279
|
+
height: auto;
|
|
280
|
+
overflow: visible;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/* The floating chat is rendered after the tree in the workspace shell and
|
|
284
|
+
uses the modal layer. Keep the opened tree above it so its rows remain
|
|
285
|
+
visible and tappable on one-panel screens. */
|
|
286
|
+
.creative-workspace-tree-region {
|
|
287
|
+
z-index: calc(var(--layer-modal) + 1);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.creative-workspace-shell {
|
|
291
|
+
display: block;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
body.creative-workspace > nav,
|
|
295
|
+
.creative-workspace-shell > main {
|
|
296
|
+
max-width: var(--max-width);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/* In the two-panel band the handle's default offset lands in the left
|
|
300
|
+
gutter main leaves itself. One-panel main runs flush to the edge, so the
|
|
301
|
+
same offset would sit on top of the breadcrumb row — park the handle in
|
|
302
|
+
the nav's empty left corner instead, where it reads as the menu button
|
|
303
|
+
it effectively is. */
|
|
304
|
+
.creative-workspace-tree-toggle {
|
|
305
|
+
top: var(--space-3);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
@@ -28,14 +28,14 @@ module Collavre
|
|
|
28
28
|
|
|
29
29
|
# When the MCP process crashes or the WebSocket drops before the plugin
|
|
30
30
|
# can call DELETE /api/v1/agent/:id, a Claude Channel session would
|
|
31
|
-
# otherwise stay matchable
|
|
31
|
+
# otherwise stay matchable and any future
|
|
32
32
|
# comment on a creative still shared with the agent would dispatch into an
|
|
33
33
|
# empty stream — delegated work that only clears via stuck recovery.
|
|
34
34
|
#
|
|
35
35
|
# One shared agent can have MANY concurrent sessions, so routing is gated
|
|
36
36
|
# on PRESENCE: this session drops its own AgentSubscription row, and
|
|
37
|
-
# routing is
|
|
38
|
-
# keeps its row, so
|
|
37
|
+
# routing is enabled only while a row is live. A still-live sibling session
|
|
38
|
+
# keeps its row, so dispatch remains active and its in-flight work is never
|
|
39
39
|
# cancelled. Done under the agent's row lock so a concurrent subscribe on
|
|
40
40
|
# the same agent serializes (no clear-vs-activate race).
|
|
41
41
|
#
|
|
@@ -47,7 +47,7 @@ module Collavre
|
|
|
47
47
|
def unsubscribed
|
|
48
48
|
return unless @session_agent && @subscription_token
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
last_session_disconnected = false
|
|
51
51
|
dropped_session_id = nil
|
|
52
52
|
@session_agent.with_lock do
|
|
53
53
|
scope = AgentSubscription.where(agent_id: @session_agent.id, token: @subscription_token)
|
|
@@ -67,25 +67,21 @@ module Collavre
|
|
|
67
67
|
# row cannot masquerade as a live sibling and pin routing on forever.
|
|
68
68
|
AgentSubscription.reap_stale!(@session_agent.id)
|
|
69
69
|
|
|
70
|
-
# Another session is still LIVE on this shared agent.
|
|
71
|
-
# active
|
|
70
|
+
# Another session is still LIVE on this shared agent. Its presence keeps
|
|
71
|
+
# dispatch active, so only the final disconnect owns agent-wide cleanup.
|
|
72
72
|
next if AgentSubscription.live.where(agent_id: @session_agent.id).exists?
|
|
73
73
|
|
|
74
|
-
@session_agent.update_columns(
|
|
75
|
-
|
|
76
|
-
routing_subscription_token: nil
|
|
77
|
-
)
|
|
78
|
-
cleared = true
|
|
74
|
+
@session_agent.update_columns(routing_subscription_token: nil)
|
|
75
|
+
last_session_disconnected = true
|
|
79
76
|
end
|
|
80
77
|
|
|
81
|
-
if
|
|
78
|
+
if last_session_disconnected
|
|
82
79
|
# Reconnect-grace cancellation (last session): clearing routing only
|
|
83
80
|
# makes the agent unroutable. Any task already "delegated" still holds
|
|
84
81
|
# its ResourceTracker slot — the dispatch was broadcast to a now-dead
|
|
85
82
|
# stream so no client remains to call /reply, and the slot would stay
|
|
86
83
|
# held until StuckDetectorJob times out. The job cancels those tasks
|
|
87
|
-
# after a grace window, but only if the agent is still offline
|
|
88
|
-
# rechecks routing_expression AND that no session has resubscribed).
|
|
84
|
+
# after a grace window, but only if the agent is still offline.
|
|
89
85
|
CancelOfflineDelegatedTasksJob
|
|
90
86
|
.set(wait: CancelOfflineDelegatedTasksJob::GRACE_SECONDS.seconds)
|
|
91
87
|
.perform_later(@session_agent.id, @subscription_token)
|
|
@@ -153,8 +149,8 @@ module Collavre
|
|
|
153
149
|
return reject unless agent&.ai_user?
|
|
154
150
|
return reject unless agent.created_by_id == current_user.id
|
|
155
151
|
|
|
156
|
-
# Attach the stream BEFORE
|
|
157
|
-
# can pick this agent as soon as
|
|
152
|
+
# Attach the stream BEFORE recording presence. Orchestration::Matcher
|
|
153
|
+
# can pick this agent as soon as its presence row is committed;
|
|
158
154
|
# broadcasts to agent:user:<id> in the window between the UPDATE
|
|
159
155
|
# committing and stream_from registering the subscription would land
|
|
160
156
|
# in a stream with no subscriber, leaving the new delegated task
|
|
@@ -165,7 +161,7 @@ module Collavre
|
|
|
165
161
|
@subscription_token = SecureRandom.hex(8) if agent.claude_channel_agent?
|
|
166
162
|
stream_from "agent:user:#{agent.id}"
|
|
167
163
|
|
|
168
|
-
# Record this session's presence row
|
|
164
|
+
# Record this session's presence row under the
|
|
169
165
|
# agent's row lock, so a concurrent unsubscribe on the same shared agent
|
|
170
166
|
# serializes against this and cannot clear routing out from under a live
|
|
171
167
|
# session. routing_subscription_token is kept as the most-recent-session
|
|
@@ -185,10 +181,7 @@ module Collavre
|
|
|
185
181
|
token: @subscription_token,
|
|
186
182
|
session_id: params[:session_id].presence
|
|
187
183
|
)
|
|
188
|
-
agent.update_columns(
|
|
189
|
-
routing_subscription_token: @subscription_token,
|
|
190
|
-
routing_expression: "true"
|
|
191
|
-
)
|
|
184
|
+
agent.update_columns(routing_subscription_token: @subscription_token)
|
|
192
185
|
end
|
|
193
186
|
end
|
|
194
187
|
end
|