collavre 0.3.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/collavre/actiontext.css +73 -71
  3. data/app/assets/stylesheets/collavre/activity_logs.css +18 -45
  4. data/app/assets/stylesheets/collavre/comments_popup.css +197 -35
  5. data/app/assets/stylesheets/collavre/creatives.css +101 -51
  6. data/app/assets/stylesheets/collavre/dark_mode.css +221 -88
  7. data/app/assets/stylesheets/collavre/design_tokens.css +334 -0
  8. data/app/assets/stylesheets/collavre/mention_menu.css +13 -9
  9. data/app/assets/stylesheets/collavre/popup.css +57 -27
  10. data/app/assets/stylesheets/collavre/slide_view.css +6 -6
  11. data/app/assets/stylesheets/collavre/user_menu.css +4 -5
  12. data/app/components/collavre/plans_timeline_component.html.erb +2 -2
  13. data/app/controllers/collavre/admin/orchestration_controller.rb +9 -2
  14. data/app/controllers/collavre/admin/settings_controller.rb +199 -0
  15. data/app/controllers/collavre/comments/reactions_controller.rb +1 -9
  16. data/app/controllers/collavre/comments_controller.rb +39 -162
  17. data/app/controllers/collavre/creatives_controller.rb +18 -58
  18. data/app/controllers/collavre/users_controller.rb +31 -3
  19. data/app/helpers/collavre/application_helper.rb +97 -0
  20. data/app/helpers/collavre/creatives_helper.rb +10 -202
  21. data/app/javascript/collavre.js +0 -1
  22. data/app/javascript/components/creative_tree_row.js +3 -2
  23. data/app/javascript/controllers/comment_controller.js +309 -4
  24. data/app/javascript/controllers/comments/form_controller.js +52 -0
  25. data/app/javascript/controllers/comments/presence_controller.js +13 -0
  26. data/app/javascript/controllers/creatives/tree_controller.js +2 -1
  27. data/app/javascript/controllers/link_creative_controller.js +29 -3
  28. data/app/javascript/lib/__tests__/html_code_block_wrapper.test.js +201 -0
  29. data/app/javascript/lib/html_code_block_wrapper.js +168 -0
  30. data/app/javascript/lib/utils/markdown.js +2 -1
  31. data/app/javascript/modules/creative_row_editor.js +5 -1
  32. data/app/javascript/utils/emoji_parser.js +21 -0
  33. data/app/jobs/collavre/ai_agent_job.rb +6 -2
  34. data/app/jobs/collavre/cron_action_job.rb +18 -6
  35. data/app/jobs/collavre/cron_scheduler_job.rb +112 -0
  36. data/app/models/collavre/comment/approvable.rb +50 -0
  37. data/app/models/collavre/comment/broadcastable.rb +119 -0
  38. data/app/models/collavre/comment/notifiable.rb +111 -0
  39. data/app/models/collavre/comment.rb +13 -258
  40. data/app/models/collavre/comment_reaction.rb +15 -0
  41. data/app/models/collavre/creative/describable.rb +86 -0
  42. data/app/models/collavre/creative/linkable.rb +77 -0
  43. data/app/models/collavre/creative/permissible.rb +103 -0
  44. data/app/models/collavre/creative.rb +3 -289
  45. data/app/models/collavre/orchestrator_policy.rb +1 -1
  46. data/app/models/collavre/system_setting.rb +27 -1
  47. data/app/models/collavre/user.rb +42 -0
  48. data/app/models/collavre/user_theme.rb +10 -0
  49. data/app/services/collavre/ai_agent/approval_handler.rb +110 -0
  50. data/app/services/collavre/ai_agent/message_builder.rb +129 -0
  51. data/app/services/collavre/ai_agent/review_handler.rb +70 -0
  52. data/app/services/collavre/ai_agent_service.rb +93 -150
  53. data/app/services/collavre/ai_client.rb +23 -4
  54. data/app/services/collavre/auto_theme_generator.rb +168 -50
  55. data/app/services/collavre/command_menu_service.rb +70 -0
  56. data/app/services/collavre/comment_move_service.rb +94 -0
  57. data/app/services/collavre/comments/action_executor.rb +10 -0
  58. data/app/services/collavre/comments/mcp_command.rb +1 -2
  59. data/app/services/collavre/creatives/create_service.rb +86 -0
  60. data/app/services/collavre/creatives/destroy_service.rb +41 -0
  61. data/app/services/collavre/creatives/index_query.rb +3 -0
  62. data/app/services/collavre/markdown_converter.rb +240 -0
  63. data/app/services/collavre/mention_parser.rb +63 -0
  64. data/app/services/collavre/orchestration/agent_context_builder.rb +24 -8
  65. data/app/services/collavre/orchestration/agent_orchestrator.rb +59 -10
  66. data/app/services/collavre/orchestration/loop_breaker.rb +12 -7
  67. data/app/services/collavre/orchestration/policy_resolver.rb +16 -2
  68. data/app/services/collavre/orchestration/scheduler.rb +4 -3
  69. data/app/services/collavre/orchestration/stuck_detector.rb +1 -1
  70. data/app/services/collavre/system_events/context_builder.rb +1 -6
  71. data/app/services/collavre/tools/creative_batch_service.rb +107 -0
  72. data/app/services/collavre/tools/creative_update_service.rb +17 -12
  73. data/app/services/collavre/tools/cron_create_service.rb +17 -5
  74. data/app/views/admin/shared/_tabs.html.erb +2 -1
  75. data/app/views/collavre/admin/orchestration/show.html.erb +11 -0
  76. data/app/views/collavre/admin/settings/_system_tab.html.erb +138 -0
  77. data/app/views/collavre/admin/settings/_uiux_tab.html.erb +44 -0
  78. data/app/views/collavre/admin/settings/index.html.erb +11 -0
  79. data/app/views/collavre/admin/settings/uiux.html.erb +11 -0
  80. data/app/views/collavre/comments/_comment.html.erb +15 -5
  81. data/app/views/collavre/comments/_comments_popup.html.erb +9 -2
  82. data/app/views/collavre/creatives/_mobile_actions_menu.html.erb +0 -3
  83. data/app/views/collavre/creatives/_share_button.html.erb +0 -52
  84. data/app/views/collavre/creatives/_share_modal.html.erb +52 -0
  85. data/app/views/collavre/creatives/index.html.erb +5 -8
  86. data/app/views/collavre/shared/navigation/_panels.html.erb +2 -2
  87. data/app/views/collavre/user_themes/index.html.erb +7 -9
  88. data/app/views/collavre/users/_contact_management.html.erb +2 -1
  89. data/app/views/collavre/users/edit_ai.html.erb +7 -0
  90. data/app/views/collavre/users/index.html.erb +16 -1
  91. data/app/views/collavre/users/new_ai.html.erb +18 -8
  92. data/app/views/collavre/users/passkeys.html.erb +1 -1
  93. data/app/views/collavre/users/show.html.erb +1 -1
  94. data/app/views/layouts/collavre/slide.html.erb +8 -1
  95. data/config/locales/admin.en.yml +88 -0
  96. data/config/locales/admin.ko.yml +88 -0
  97. data/config/locales/ai_agent.en.yml +5 -1
  98. data/config/locales/ai_agent.ko.yml +5 -1
  99. data/config/locales/comments.en.yml +5 -1
  100. data/config/locales/comments.ko.yml +5 -1
  101. data/config/locales/orchestration.en.yml +8 -0
  102. data/config/locales/orchestration.ko.yml +8 -0
  103. data/config/locales/users.en.yml +12 -0
  104. data/config/locales/users.ko.yml +12 -0
  105. data/config/routes.rb +7 -1
  106. data/db/migrate/20260212011655_add_quoted_comment_to_comments.rb +7 -0
  107. data/db/migrate/20260213044247_add_agent_conf_to_users.rb +5 -0
  108. data/lib/collavre/engine.rb +25 -0
  109. data/lib/collavre/version.rb +1 -1
  110. metadata +32 -1
@@ -3,7 +3,7 @@
3
3
  position: absolute;
4
4
  right: 2em;
5
5
  /* top: 10em; */
6
- z-index: 1000;
6
+ z-index: var(--layer-modal);
7
7
  width: 420px;
8
8
  height: 640px;
9
9
  max-height: 80vh;
@@ -35,7 +35,7 @@ body.chat-fullscreen {
35
35
  border: none;
36
36
  z-index: 9999;
37
37
  box-sizing: border-box;
38
- padding: 1em;
38
+ padding: 0.5em;
39
39
  }
40
40
 
41
41
  .comments-popup-header {
@@ -139,7 +139,7 @@ body.chat-fullscreen {
139
139
  #new-comment-form textarea {
140
140
  width: 96%;
141
141
  resize: vertical;
142
- font-size: 16px;
142
+ font-size: var(--text-1);
143
143
  /* Prevent iOS zoom on focus */
144
144
  }
145
145
 
@@ -180,6 +180,84 @@ body.chat-fullscreen {
180
180
  margin-top: 0.2em;
181
181
  }
182
182
 
183
+ .comment-content pre {
184
+ white-space: pre-wrap;
185
+ word-break: break-word;
186
+ overflow-x: auto;
187
+ }
188
+
189
+ .comment-content pre code {
190
+ white-space: pre-wrap;
191
+ word-break: break-word;
192
+ }
193
+
194
+ .comment-quoted-block {
195
+ margin-top: 0.3em;
196
+ margin-bottom: 0.2em;
197
+ }
198
+
199
+ .comment-quoted-text {
200
+ border-left: 3px solid var(--color-border);
201
+ padding: 0.3em 0.6em;
202
+ margin: 0;
203
+ color: var(--color-muted);
204
+ font-size: 0.9em;
205
+ background: color-mix(in srgb, var(--color-section-bg) 50%, transparent);
206
+ border-radius: 0 4px 4px 0;
207
+ white-space: pre-wrap;
208
+ word-break: break-word;
209
+ }
210
+
211
+ /* Quote indicator in form */
212
+ .comment-quote-indicator {
213
+ display: flex;
214
+ align-items: center;
215
+ justify-content: space-between;
216
+ padding: 0.3em 0.6em;
217
+ margin-bottom: 0.3em;
218
+ background: color-mix(in srgb, var(--color-section-bg) 50%, transparent);
219
+ border-left: 3px solid var(--color-active);
220
+ border-radius: 0 4px 4px 0;
221
+ font-size: 0.85em;
222
+ color: var(--color-muted);
223
+ }
224
+
225
+ .comment-quote-indicator-text {
226
+ overflow: hidden;
227
+ text-overflow: ellipsis;
228
+ white-space: nowrap;
229
+ flex: 1;
230
+ min-width: 0;
231
+ }
232
+
233
+ .comment-quote-cancel {
234
+ border: none;
235
+ background: none;
236
+ cursor: pointer;
237
+ color: var(--color-muted);
238
+ font-size: 1.1em;
239
+ padding: 0 0.2em;
240
+ line-height: 1;
241
+ flex-shrink: 0;
242
+ }
243
+
244
+ .comment-quote-cancel:hover {
245
+ color: var(--color-text);
246
+ }
247
+
248
+ /* Review popup (uses common-popup) */
249
+ .comment-review-popup {
250
+ min-width: auto;
251
+ }
252
+
253
+ .comment-review-popup .common-popup-item {
254
+ padding: 0.3em 0.8em;
255
+ font-size: 0.85em;
256
+ cursor: pointer;
257
+ border-radius: 4px;
258
+ white-space: nowrap;
259
+ }
260
+
183
261
  .comment-reactions {
184
262
  margin-top: 0.4em;
185
263
  display: flex;
@@ -230,7 +308,7 @@ body.chat-fullscreen {
230
308
  background: var(--color-section-bg);
231
309
  border: 1px solid var(--color-border);
232
310
  border-radius: 10px;
233
- padding: 0 4px;
311
+ padding: 0 var(--space-1);
234
312
  font-size: 0.7em;
235
313
  color: var(--color-muted);
236
314
  min-width: 14px;
@@ -238,7 +316,7 @@ body.chat-fullscreen {
238
316
  line-height: 1.4;
239
317
  visibility: hidden;
240
318
  z-index: 2;
241
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
319
+ box-shadow: var(--shadow-1);
242
320
  }
243
321
 
244
322
  .comment-reaction-count.is-visible {
@@ -282,7 +360,7 @@ body.chat-fullscreen {
282
360
  background: var(--color-section-bg);
283
361
  border: 1px solid var(--color-border);
284
362
  border-radius: 8px;
285
- box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);
363
+ box-shadow: var(--shadow-3);
286
364
  z-index: 9999;
287
365
  }
288
366
 
@@ -315,7 +393,7 @@ body.chat-fullscreen {
315
393
  }
316
394
 
317
395
  .comment-topic-switch {
318
- color: var(--color-accent, #007bff);
396
+ color: var(--color-active);
319
397
  text-decoration: none;
320
398
  font-weight: 600;
321
399
  }
@@ -414,8 +492,35 @@ body.chat-fullscreen {
414
492
  gap: 0.5em;
415
493
  }
416
494
 
495
+ .edit-comment-action-btn,
496
+ .approve-comment-btn {
497
+ padding: 0.15em 0.4em;
498
+ border-radius: var(--radius-2);
499
+ font-size: 0.75rem;
500
+ line-height: 1.3;
501
+ cursor: pointer;
502
+ }
503
+
417
504
  .edit-comment-action-btn {
418
- margin-top: 0.6em;
505
+ border: 1px solid var(--border-color);
506
+ background: var(--surface-btn);
507
+ color: var(--text-on-btn);
508
+ }
509
+
510
+ .edit-comment-action-btn:hover {
511
+ filter: brightness(var(--hover-brightness, 0.95));
512
+ }
513
+
514
+ .approve-comment-btn {
515
+ border: 1px solid color-mix(in srgb, var(--color-success) 35%, transparent);
516
+ background: color-mix(in srgb, var(--color-success) 15%, transparent);
517
+ color: var(--color-success);
518
+ }
519
+
520
+ .approve-comment-btn:hover {
521
+ background: var(--color-success);
522
+ color: var(--text-on-badge);
523
+ border-color: var(--color-success);
419
524
  }
420
525
 
421
526
  .comment-status-label {
@@ -493,6 +598,8 @@ body.chat-fullscreen {
493
598
  .convert-comment-btn,
494
599
  .delete-comment-btn,
495
600
  .edit-comment-btn,
601
+ .review-comment-btn,
602
+ .replace-comment-btn,
496
603
  .copy-comment-link-btn {
497
604
  border: none;
498
605
  background: none;
@@ -500,7 +607,6 @@ body.chat-fullscreen {
500
607
  cursor: pointer;
501
608
  color: var(--color-chat-btn-text);
502
609
  }
503
- }
504
610
 
505
611
  .comment-owner-only,
506
612
  .comment-delete-hidden,
@@ -508,6 +614,11 @@ body.chat-fullscreen {
508
614
  display: none;
509
615
  }
510
616
 
617
+ .replace-comment-btn:disabled {
618
+ opacity: 0.35;
619
+ cursor: default;
620
+ }
621
+
511
622
  .comment-copy-notice {
512
623
  position: absolute;
513
624
  right: 0.2em;
@@ -604,13 +715,7 @@ body.chat-fullscreen {
604
715
  }
605
716
 
606
717
  #comments-popup[data-fullscreen="true"] {
607
- display: flex !important;
608
718
  transform: none;
609
- border-radius: 0;
610
- padding: 1em;
611
- width: 100%;
612
- height: 100%;
613
- box-sizing: border-box;
614
719
  }
615
720
  }
616
721
 
@@ -620,7 +725,7 @@ body.chat-fullscreen {
620
725
 
621
726
  @keyframes comment-flash {
622
727
  from {
623
- background: #ffff99;
728
+ background: var(--color-highlight);
624
729
  }
625
730
 
626
731
  to {
@@ -715,7 +820,7 @@ body.chat-fullscreen {
715
820
  display: inline-flex;
716
821
  align-items: center;
717
822
  flex-shrink: 0;
718
- gap: 4px;
823
+ gap: var(--space-1);
719
824
  padding: 0.2em 0.6em;
720
825
  border-radius: 12px;
721
826
  background: var(--color-secondary-background);
@@ -741,7 +846,7 @@ body.chat-fullscreen {
741
846
 
742
847
  .topic-tag.has-new-messages {
743
848
  position: relative;
744
- border-color: var(--color-accent, #007bff);
849
+ border-color: var(--color-active);
745
850
  }
746
851
 
747
852
  .topic-tag.has-new-messages::after {
@@ -749,7 +854,7 @@ body.chat-fullscreen {
749
854
  position: absolute;
750
855
  top: -8px;
751
856
  right: -10px;
752
- background-color: #ff3b30;
857
+ background-color: var(--color-danger);
753
858
  color: white;
754
859
  font-size: 0.6em;
755
860
  padding: 0.1em 0.4em;
@@ -759,9 +864,9 @@ body.chat-fullscreen {
759
864
 
760
865
  /* Drag and drop styles for moving comments to topics */
761
866
  .topic-tag.drag-over {
762
- background-color: var(--color-accent, #007bff);
867
+ background-color: var(--color-active);
763
868
  color: white;
764
- border-color: var(--color-accent, #007bff);
869
+ border-color: var(--color-active);
765
870
  transform: scale(1.05);
766
871
  transition: all 0.15s ease;
767
872
  }
@@ -784,12 +889,12 @@ body.chat-fullscreen {
784
889
  }
785
890
 
786
891
  .topic-tag.topic-drag-over-left {
787
- border-left: 3px solid var(--color-accent, #007bff);
892
+ border-left: 3px solid var(--color-active);
788
893
  margin-left: -1px;
789
894
  }
790
895
 
791
896
  .topic-tag.topic-drag-over-right {
792
- border-right: 3px solid var(--color-accent, #007bff);
897
+ border-right: 3px solid var(--color-active);
793
898
  margin-right: -1px;
794
899
  }
795
900
 
@@ -809,8 +914,8 @@ body.chat-fullscreen {
809
914
  position: fixed;
810
915
  top: -1000px;
811
916
  left: -1000px;
812
- padding: 8px 16px;
813
- background-color: var(--color-accent, #007bff);
917
+ padding: var(--space-2) var(--space-3);
918
+ background-color: var(--color-active);
814
919
  color: white;
815
920
  border-radius: 4px;
816
921
  font-size: 0.9em;
@@ -898,7 +1003,7 @@ body.chat-fullscreen {
898
1003
  padding: 0.2em 0.6em;
899
1004
  border-radius: 12px;
900
1005
  background: var(--color-bg);
901
- border: 1px solid var(--color-accent, #007bff);
1006
+ border: 1px solid var(--color-active);
902
1007
  color: var(--color-text);
903
1008
  font-size: 0.85em;
904
1009
  width: 100px;
@@ -910,7 +1015,7 @@ body.chat-fullscreen {
910
1015
  padding: 0.1em 0.4em;
911
1016
  border-radius: 8px;
912
1017
  background: var(--color-bg);
913
- border: 1px solid var(--color-accent, #007bff);
1018
+ border: 1px solid var(--color-active);
914
1019
  color: var(--color-text);
915
1020
  font-size: 0.85em;
916
1021
  width: 80px;
@@ -920,11 +1025,11 @@ body.chat-fullscreen {
920
1025
  /* Selection hint popup */
921
1026
  .selection-hint-popup {
922
1027
  position: fixed;
923
- background: var(--color-bg-secondary, #f8f9fa);
924
- border: 1px solid var(--color-border, #dee2e6);
1028
+ background: var(--surface-section);
1029
+ border: 1px solid var(--border-color);
925
1030
  border-radius: 8px;
926
- padding: 8px 12px;
927
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
1031
+ padding: var(--space-2) 12px;
1032
+ box-shadow: var(--shadow-2);
928
1033
  white-space: nowrap;
929
1034
  z-index: 10000;
930
1035
  animation: hint-fade-in 0.2s ease-out;
@@ -933,9 +1038,9 @@ body.chat-fullscreen {
933
1038
  .selection-hint-popup .hint-content {
934
1039
  display: flex;
935
1040
  flex-direction: column;
936
- gap: 4px;
1041
+ gap: var(--space-1);
937
1042
  font-size: 0.8em;
938
- color: var(--color-text-secondary, #6c757d);
1043
+ color: var(--text-muted);
939
1044
  }
940
1045
 
941
1046
  .selection-hint-popup .hint-content span {
@@ -956,7 +1061,64 @@ body.chat-fullscreen {
956
1061
  /* Dark mode support */
957
1062
  @media (prefers-color-scheme: dark) {
958
1063
  .selection-hint-popup {
959
- background: var(--color-bg-secondary, #2d2d2d);
960
- border-color: var(--color-border, #444);
1064
+ background: var(--surface-section);
1065
+ border-color: var(--border-color);
1066
+ }
1067
+ }
1068
+
1069
+ /* Override popup-close-btn absolute positioning inside comments popup header */
1070
+ .comments-popup-actions .popup-close-btn {
1071
+ position: static;
1072
+ }
1073
+
1074
+ /* Streaming indicator for AI comments */
1075
+ .comment-content.streaming {
1076
+ min-height: 1.5em;
1077
+ }
1078
+
1079
+ .streaming-dots {
1080
+ display: inline-flex;
1081
+ gap: 2px;
1082
+ font-size: 1.5em;
1083
+ line-height: 1;
1084
+ }
1085
+
1086
+ .streaming-dots span {
1087
+ animation: streaming-bounce 1.4s infinite ease-in-out both;
1088
+ }
1089
+
1090
+ .streaming-dots span:nth-child(1) {
1091
+ animation-delay: 0s;
1092
+ }
1093
+
1094
+ .streaming-dots span:nth-child(2) {
1095
+ animation-delay: 0.2s;
1096
+ }
1097
+
1098
+ .streaming-dots span:nth-child(3) {
1099
+ animation-delay: 0.4s;
1100
+ }
1101
+
1102
+ @keyframes streaming-bounce {
1103
+ 0%, 80%, 100% {
1104
+ opacity: 0.3;
1105
+ transform: translateY(0);
961
1106
  }
1107
+ 40% {
1108
+ opacity: 1;
1109
+ transform: translateY(-4px);
1110
+ }
1111
+ }
1112
+
1113
+ /* Blinking cursor inline at the end of streaming content */
1114
+ .streaming-cursor {
1115
+ animation: streaming-blink 1s infinite;
1116
+ color: var(--color-text, #333);
1117
+ margin-left: 1px;
1118
+ font-weight: normal;
1119
+ }
1120
+
1121
+ @keyframes streaming-blink {
1122
+ 0%, 100% { opacity: 1; }
1123
+ 50% { opacity: 0; }
962
1124
  }