collavre 0.12.3 → 0.14.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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/collavre/actiontext.css +103 -9
  3. data/app/assets/stylesheets/collavre/code_highlight.css +4 -0
  4. data/app/assets/stylesheets/collavre/comments_popup.css +43 -15
  5. data/app/assets/stylesheets/collavre/creatives.css +64 -0
  6. data/app/assets/stylesheets/collavre/design_tokens.css +1 -0
  7. data/app/assets/stylesheets/collavre/modal_dialog.css +340 -0
  8. data/app/assets/stylesheets/collavre/popup.css +6 -0
  9. data/app/assets/stylesheets/collavre/search_popup.css +63 -76
  10. data/app/components/collavre/command_menu_component.rb +8 -0
  11. data/app/components/collavre/inbox/badge_component.rb +1 -2
  12. data/app/controllers/collavre/comment_read_pointers_controller.rb +1 -1
  13. data/app/controllers/collavre/comments/reactions_controller.rb +2 -16
  14. data/app/controllers/collavre/comments/versions_controller.rb +2 -15
  15. data/app/controllers/collavre/comments_controller.rb +5 -17
  16. data/app/controllers/collavre/creatives_controller.rb +4 -4
  17. data/app/controllers/collavre/tasks_controller.rb +30 -0
  18. data/app/controllers/collavre/topics_controller.rb +7 -3
  19. data/app/controllers/concerns/collavre/comments/batch_operations.rb +14 -8
  20. data/app/controllers/concerns/collavre/comments/comment_scoping.rb +20 -0
  21. data/app/controllers/concerns/collavre/integration_permission.rb +31 -0
  22. data/app/controllers/concerns/collavre/users_controller/ai_user_management.rb +1 -1
  23. data/app/controllers/concerns/collavre/users_controller/profile_and_settings.rb +2 -0
  24. data/app/helpers/collavre/application_helper.rb +3 -3
  25. data/app/helpers/collavre/creatives_helper.rb +1 -9
  26. data/app/helpers/collavre/navigation_helper.rb +0 -6
  27. data/app/javascript/collavre.js +1 -0
  28. data/app/javascript/components/ImageResizer.jsx +206 -0
  29. data/app/javascript/components/InlineLexicalEditor.jsx +2 -13
  30. data/app/javascript/components/plugins/image_upload_plugin.jsx +5 -2
  31. data/app/javascript/controllers/agent_trigger_controller.js +94 -0
  32. data/app/javascript/controllers/comment_controller.js +2 -2
  33. data/app/javascript/controllers/comments/__tests__/form_controller_review.test.js +15 -0
  34. data/app/javascript/controllers/comments/drop_trigger_controller.js +3 -1
  35. data/app/javascript/controllers/comments/form_controller.js +85 -0
  36. data/app/javascript/controllers/comments/list_controller.js +63 -29
  37. data/app/javascript/controllers/comments/popup_controller.js +7 -0
  38. data/app/javascript/controllers/comments/presence_controller.js +56 -2
  39. data/app/javascript/controllers/comments/topics_controller.js +16 -4
  40. data/app/javascript/controllers/index.js +3 -0
  41. data/app/javascript/creatives/drag_drop/event_handlers.js +15 -0
  42. data/app/javascript/lib/lexical/image_node.jsx +6 -16
  43. data/app/javascript/modules/command_args_form.js +539 -0
  44. data/app/javascript/modules/command_menu.js +49 -13
  45. data/app/javascript/modules/modal_dialog.js +40 -0
  46. data/app/javascript/utils/drag_bundle_image.js +54 -0
  47. data/app/jobs/collavre/trigger_loop_check_job.rb +8 -8
  48. data/app/jobs/collavre/trigger_loop_verify_job.rb +11 -0
  49. data/app/models/collavre/comment/broadcastable.rb +1 -1
  50. data/app/models/collavre/comment.rb +65 -0
  51. data/app/models/collavre/creative/describable.rb +2 -1
  52. data/app/models/collavre/creative/permissible.rb +23 -0
  53. data/app/models/collavre/creative.rb +0 -6
  54. data/app/models/collavre/inbox_item.rb +0 -2
  55. data/app/models/collavre/topic.rb +2 -0
  56. data/app/models/collavre/user.rb +7 -0
  57. data/app/services/collavre/ai_agent/message_builder.rb +55 -7
  58. data/app/services/collavre/ai_agent_service.rb +16 -5
  59. data/app/services/collavre/ai_client.rb +36 -37
  60. data/app/services/collavre/command_menu_service.rb +53 -5
  61. data/app/services/collavre/comment_move_service.rb +1 -4
  62. data/app/services/collavre/inbox_reply_service.rb +87 -0
  63. data/app/services/collavre/openclaw_abort_service.rb +45 -0
  64. data/app/services/collavre/orchestration/agent_context_builder.rb +5 -3
  65. data/app/services/collavre/orchestration/stuck_detector.rb +7 -11
  66. data/app/services/collavre/topic_branch_service.rb +112 -0
  67. data/app/views/collavre/comments/_comments_popup.html.erb +5 -3
  68. data/app/views/collavre/creatives/_inline_edit_form.html.erb +2 -2
  69. data/app/views/collavre/shared/_custom_theme_style.html.erb +6 -2
  70. data/app/views/collavre/shared/navigation/_inbox_button.html.erb +0 -1
  71. data/app/views/collavre/shared/navigation/_mobile_inbox_button.html.erb +0 -1
  72. data/app/views/collavre/shared/navigation/_search_form.html.erb +1 -18
  73. data/app/views/collavre/users/_trigger_field.html.erb +51 -0
  74. data/app/views/collavre/users/edit_ai.html.erb +5 -10
  75. data/app/views/collavre/users/new_ai.html.erb +5 -10
  76. data/config/locales/comments.en.yml +18 -0
  77. data/config/locales/comments.ko.yml +18 -0
  78. data/config/locales/drop_trigger.en.yml +3 -0
  79. data/config/locales/drop_trigger.ko.yml +3 -0
  80. data/config/locales/users.en.yml +10 -0
  81. data/config/locales/users.ko.yml +10 -0
  82. data/config/routes.rb +6 -1
  83. data/db/migrate/20260409000000_add_source_topic_id_to_topics.rb +5 -0
  84. data/lib/collavre/version.rb +1 -1
  85. metadata +15 -4
  86. data/app/helpers/collavre/user_themes_helper.rb +0 -4
  87. data/app/views/collavre/creatives/_mobile_actions_menu.html.erb +0 -36
  88. data/app/views/collavre/creatives/_share_button.html.erb +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 61eb7c1c09136cc7e5e15121a76f89f27da2232fe9202a3d909488fe0df5045e
4
- data.tar.gz: b2fa13c57e9394a2d9736361c2bb13f62dd5122fde785a8c885ab7ae88e84dd2
3
+ metadata.gz: e56759c8d6833552a9d5e49b19745aca7d0a58d56ad3c3a7e6d502ea54d6b156
4
+ data.tar.gz: 37a2481e6fc5abc1077167d481abb51b118f256de05c704cdd64381eb090562a
5
5
  SHA512:
6
- metadata.gz: 3a0a984f9b5074aefd2f95819c28df6421445bb26a39ae0cf69a395df1b72334037a0917cc3790b164beb29d95998dcf51fb3958f8cd8b3b5832a646a3ba9dc6
7
- data.tar.gz: 4a1552462be73e3aeb0deba68260da717a101368130bc984a82ebff2bf78e6ec46dc363332632f976cb82535d083d279bcd33a5b208116e90c0ecb0358ae1362
6
+ metadata.gz: 9261164e029e28e9303f30879740f5dbd3ccbb6779b0767cd8488920e56f0037f63159fcdf64bd12dbb7788a22f5e1718fc468f616a02f59553a67713dfa422c
7
+ data.tar.gz: 5856204cfe3ff27016d1e0f5a54f3de43cf839911fae1dba656200679c941c837b4f30ded9e9f94153ecea2972c8797a0554f81c58df27123f1dc0d66ed7b30a
@@ -279,15 +279,51 @@
279
279
  }
280
280
 
281
281
  /* stylelint-disable color-no-hex -- syntax highlight theme colors are intentionally hardcoded */
282
+ :root {
283
+ --lexical-token-comment: #6a737d;
284
+ --lexical-token-punctuation: #393a34;
285
+ --lexical-token-property: #b31d28;
286
+ --lexical-token-string: #1a7f37;
287
+ --lexical-token-operator: #9a6e3a;
288
+ --lexical-token-keyword: #0550ae;
289
+ --lexical-token-function: #bc4c00;
290
+ --lexical-token-regex: #1a7f37;
291
+ }
292
+
293
+ body.dark-mode {
294
+ --lexical-token-comment: #8b949e;
295
+ --lexical-token-punctuation: #c9d1d9;
296
+ --lexical-token-property: #ff7b72;
297
+ --lexical-token-string: #7ee787;
298
+ --lexical-token-operator: #ffa657;
299
+ --lexical-token-keyword: #79c0ff;
300
+ --lexical-token-function: #d2a8ff;
301
+ --lexical-token-regex: #7ee787;
302
+ }
303
+
304
+ @media (prefers-color-scheme: dark) {
305
+ body:not(.light-mode):not(.dark-mode) {
306
+ --lexical-token-comment: #8b949e;
307
+ --lexical-token-punctuation: #c9d1d9;
308
+ --lexical-token-property: #ff7b72;
309
+ --lexical-token-string: #7ee787;
310
+ --lexical-token-operator: #ffa657;
311
+ --lexical-token-keyword: #79c0ff;
312
+ --lexical-token-function: #d2a8ff;
313
+ --lexical-token-regex: #7ee787;
314
+ }
315
+ }
316
+ /* stylelint-enable color-no-hex */
317
+
282
318
  .lexical-token-comment,
283
319
  .lexical-token-prolog,
284
320
  .lexical-token-doctype,
285
321
  .lexical-token-cdata {
286
- color: #6a737d;
322
+ color: var(--lexical-token-comment);
287
323
  }
288
324
 
289
325
  .lexical-token-punctuation {
290
- color: #393a34;
326
+ color: var(--lexical-token-punctuation);
291
327
  }
292
328
 
293
329
  .lexical-token-property,
@@ -297,7 +333,7 @@
297
333
  .lexical-token-deleted,
298
334
  .lexical-token-boolean,
299
335
  .lexical-token-number {
300
- color: #b31d28;
336
+ color: var(--lexical-token-property);
301
337
  }
302
338
 
303
339
  .lexical-token-selector,
@@ -306,14 +342,14 @@
306
342
  .lexical-token-char,
307
343
  .lexical-token-builtin,
308
344
  .lexical-token-inserted {
309
- color: #1a7f37;
345
+ color: var(--lexical-token-string);
310
346
  }
311
347
 
312
348
  .lexical-token-operator,
313
349
  .lexical-token-entity,
314
350
  .lexical-token-url,
315
351
  .lexical-token-variable {
316
- color: #9a6e3a;
352
+ color: var(--lexical-token-operator);
317
353
  }
318
354
 
319
355
  .lexical-token-atrule,
@@ -321,16 +357,16 @@
321
357
  .lexical-token-class,
322
358
  .lexical-token-classname,
323
359
  .lexical-token-namespace {
324
- color: #0550ae;
360
+ color: var(--lexical-token-keyword);
325
361
  }
326
362
 
327
363
  .lexical-token-function,
328
364
  .lexical-token-important {
329
- color: #bc4c00;
365
+ color: var(--lexical-token-function);
330
366
  }
331
367
 
332
368
  .lexical-token-regex {
333
- color: #1a7f37;
369
+ color: var(--lexical-token-regex);
334
370
  }
335
371
 
336
372
  .lexical-token-tag,
@@ -338,7 +374,6 @@
338
374
  .lexical-token-keyword {
339
375
  font-weight: var(--weight-6);
340
376
  }
341
- /* stylelint-enable color-no-hex */
342
377
 
343
378
  .lexical-attachment {
344
379
  position: relative;
@@ -626,3 +661,62 @@
626
661
  flex-basis: 50%;
627
662
  max-width: 50%;
628
663
  }
664
+
665
+ /* ── Lexical Image Resizer ── */
666
+
667
+ .lexical-image-container {
668
+ position: relative;
669
+ display: inline-block;
670
+ cursor: default;
671
+ user-select: none;
672
+ line-height: 0;
673
+ }
674
+
675
+ .lexical-image-container img {
676
+ display: block;
677
+ }
678
+
679
+ .lexical-image-selected {
680
+ outline: 2px solid var(--color-active, #3b82f6);
681
+ outline-offset: 2px;
682
+ border-radius: var(--radius-1, 2px);
683
+ }
684
+
685
+ .lexical-image-resizing {
686
+ cursor: nwse-resize;
687
+ }
688
+
689
+ .lexical-image-handle {
690
+ position: absolute;
691
+ width: 10px;
692
+ height: 10px;
693
+ background: var(--surface-bg, #fff);
694
+ border: 2px solid var(--color-active, #3b82f6);
695
+ border-radius: var(--radius-1, 2px);
696
+ z-index: 2;
697
+ box-sizing: border-box;
698
+ }
699
+
700
+ .lexical-image-handle--nw {
701
+ top: -5px;
702
+ left: -5px;
703
+ cursor: nwse-resize;
704
+ }
705
+
706
+ .lexical-image-handle--ne {
707
+ top: -5px;
708
+ right: -5px;
709
+ cursor: nesw-resize;
710
+ }
711
+
712
+ .lexical-image-handle--sw {
713
+ bottom: -5px;
714
+ left: -5px;
715
+ cursor: nesw-resize;
716
+ }
717
+
718
+ .lexical-image-handle--se {
719
+ bottom: -5px;
720
+ right: -5px;
721
+ cursor: nwse-resize;
722
+ }
@@ -189,6 +189,10 @@ body.dark-mode {
189
189
  color: var(--syntax-meta);
190
190
  }
191
191
 
192
+ .comment-content .hljs-punctuation {
193
+ color: var(--color-code-text);
194
+ }
195
+
192
196
  .comment-content .hljs-deletion {
193
197
  color: var(--syntax-deletion-text);
194
198
  background: var(--syntax-deletion-bg);
@@ -43,7 +43,7 @@ body.chat-fullscreen {
43
43
  border-radius: 0;
44
44
  box-shadow: none;
45
45
  border: none;
46
- z-index: 9999;
46
+ z-index: var(--layer-fullscreen);
47
47
  box-sizing: border-box;
48
48
  padding: 0.5em;
49
49
  }
@@ -478,6 +478,11 @@ body.chat-fullscreen {
478
478
  white-space: nowrap;
479
479
  }
480
480
 
481
+ .inbox-reply-btn {
482
+ font-size: var(--text-0) !important;
483
+ white-space: nowrap;
484
+ }
485
+
481
486
  .comment-highlight {
482
487
  background: color-mix(in srgb, var(--color-active) 15%, transparent) !important;
483
488
  transition: background 0.3s ease;
@@ -599,7 +604,7 @@ body.chat-fullscreen {
599
604
  border: 1px solid var(--color-border);
600
605
  border-radius: 8px;
601
606
  box-shadow: var(--shadow-3);
602
- z-index: 9999;
607
+ z-index: var(--layer-fullscreen);
603
608
  }
604
609
 
605
610
  #global-reaction-picker {
@@ -1030,6 +1035,33 @@ body.chat-fullscreen {
1030
1035
  filter: grayscale(1);
1031
1036
  }
1032
1037
 
1038
+ .agent-stop-btn {
1039
+ background: var(--surface-btn);
1040
+ border: 1px solid var(--border-color);
1041
+ border-radius: var(--radius-2);
1042
+ color: var(--text-muted);
1043
+ cursor: pointer;
1044
+ font-size: var(--text-00);
1045
+ padding: var(--space-px-1) var(--space-px-2);
1046
+ display: inline-flex;
1047
+ align-items: center;
1048
+ gap: var(--space-px-1);
1049
+ margin-right: var(--space-px-1);
1050
+ flex-shrink: 0;
1051
+ transition: background 0.15s, color 0.15s;
1052
+ white-space: nowrap;
1053
+ line-height: 1;
1054
+ }
1055
+
1056
+ .agent-stop-btn .agent-stop-icon {
1057
+ font-size: var(--text-00);
1058
+ }
1059
+
1060
+ .agent-stop-btn:hover {
1061
+ background: var(--color-danger);
1062
+ color: var(--text-on-btn);
1063
+ }
1064
+
1033
1065
  /* --- Context Chips --- */
1034
1066
  .comment-contexts-list {
1035
1067
  display: flex;
@@ -1365,19 +1397,6 @@ body.chat-fullscreen {
1365
1397
  opacity: 0.5;
1366
1398
  }
1367
1399
 
1368
- .comment-drag-image {
1369
- position: fixed;
1370
- top: -1000px;
1371
- left: -1000px;
1372
- padding: var(--space-2) var(--space-3);
1373
- background-color: var(--color-active);
1374
- color: var(--color-badge-text);
1375
- border-radius: var(--radius-2);
1376
- font-size: 0.9em;
1377
- font-weight: 500;
1378
- white-space: nowrap;
1379
- }
1380
-
1381
1400
  /* Hide the topic link if the message is displayed within that topic's context */
1382
1401
  /* Use Javascript to set data-current-topic-id on the list container */
1383
1402
  .comment-topic-link {
@@ -1496,6 +1515,15 @@ body.chat-fullscreen {
1496
1515
  opacity: 0.5;
1497
1516
  font-style: italic;
1498
1517
  }
1518
+ .topic-branch-icon {
1519
+ font-size: 0.75em;
1520
+ opacity: 0.6;
1521
+ margin-right: 1px;
1522
+ cursor: pointer;
1523
+ }
1524
+ .topic-branch-icon:hover {
1525
+ opacity: 1;
1526
+ }
1499
1527
  .archive-topic-btn,
1500
1528
  .unarchive-topic-btn {
1501
1529
  background: none;
@@ -976,3 +976,67 @@ creative-tree-row.is-being-edited .creative-tree {
976
976
  .creative-progress-area {
977
977
  display: contents;
978
978
  }
979
+
980
+ /* ─── Bundle drag image (shared by creatives + comments) ─── */
981
+
982
+ .drag-bundle-image {
983
+ position: fixed;
984
+ top: -1000px;
985
+ left: -1000px;
986
+ width: 220px;
987
+ pointer-events: none;
988
+ }
989
+
990
+ .drag-bundle-card {
991
+ position: absolute;
992
+ top: 0;
993
+ left: 0;
994
+ width: 100%;
995
+ height: 42px;
996
+ border-radius: var(--radius-2);
997
+ border: var(--border-1) solid var(--border-muted);
998
+ background-color: var(--surface-base);
999
+ box-shadow: var(--shadow-2);
1000
+ }
1001
+
1002
+ .drag-bundle-card--back {
1003
+ transform: translate(6px, -6px) rotate(2deg);
1004
+ opacity: 0.5;
1005
+ }
1006
+
1007
+ .drag-bundle-card--mid {
1008
+ transform: translate(3px, -3px) rotate(1deg);
1009
+ opacity: 0.75;
1010
+ }
1011
+
1012
+ .drag-bundle-card--front {
1013
+ position: relative;
1014
+ display: flex;
1015
+ align-items: center;
1016
+ padding: 0 var(--space-3);
1017
+ font-size: var(--text-1);
1018
+ font-weight: 500;
1019
+ color: var(--text-primary);
1020
+ overflow: hidden;
1021
+ text-overflow: ellipsis;
1022
+ white-space: nowrap;
1023
+ background-color: var(--surface-base);
1024
+ }
1025
+
1026
+ .drag-bundle-badge {
1027
+ position: absolute;
1028
+ top: -8px;
1029
+ right: -8px;
1030
+ min-width: 22px;
1031
+ height: 22px;
1032
+ padding: 0 6px;
1033
+ border-radius: var(--radius-round);
1034
+ background-color: var(--color-active);
1035
+ color: var(--color-badge-text);
1036
+ font-size: var(--text-0);
1037
+ font-weight: 700;
1038
+ display: flex;
1039
+ align-items: center;
1040
+ justify-content: center;
1041
+ box-shadow: var(--shadow-2);
1042
+ }
@@ -145,6 +145,7 @@
145
145
  --layer-popup: 100;
146
146
  --layer-modal: 1000;
147
147
  --layer-toast: 2000;
148
+ --layer-fullscreen: 9999;
148
149
  --layer-important: 2147483647;
149
150
 
150
151
  /* ============================================================================
@@ -0,0 +1,340 @@
1
+ /* ── Modal Dialog ──
2
+ * Shared centered-panel style for command palette, forms, and dialogs.
3
+ * Used by: search-popup, command-args-form, and any future centered modal.
4
+ *
5
+ * Structure:
6
+ * .modal-dialog-overlay (backdrop)
7
+ * .modal-dialog (centered panel)
8
+ * .modal-dialog-header (title area, border-bottom)
9
+ * .modal-dialog-body (scrollable content)
10
+ * .modal-dialog-footer (actions / hints, border-top)
11
+ * .modal-dialog-input (standard text input inside dialog)
12
+ */
13
+
14
+ /* ── Overlay ── */
15
+ .modal-dialog-overlay {
16
+ display: none;
17
+ position: fixed;
18
+ inset: 0;
19
+ z-index: calc(var(--layer-modal, 1000) + 1);
20
+ background: rgba(0, 0, 0, 0.5);
21
+ backdrop-filter: blur(4px);
22
+ -webkit-backdrop-filter: blur(4px);
23
+ animation: modal-dialog-fade-in 0.15s var(--ease-out-3, ease-out);
24
+ }
25
+
26
+ .modal-dialog-overlay.open {
27
+ display: block;
28
+ }
29
+
30
+ @keyframes modal-dialog-fade-in {
31
+ from { opacity: 0; }
32
+ to { opacity: 1; }
33
+ }
34
+
35
+ /* ── Panel ── */
36
+ .modal-dialog {
37
+ display: none;
38
+ position: fixed;
39
+ top: 15vh;
40
+ left: 50%;
41
+ transform: translateX(-50%);
42
+ z-index: calc(var(--layer-modal, 1000) + 2);
43
+ width: 520px;
44
+ max-width: calc(100vw - 2rem);
45
+ max-height: 70vh;
46
+ overflow-y: auto;
47
+ box-sizing: border-box;
48
+ background: var(--color-bg);
49
+ border: 1px solid var(--color-border);
50
+ border-radius: var(--radius-4, 16px);
51
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
52
+ padding: 0;
53
+ font-size: initial;
54
+ line-height: normal;
55
+ text-align: left;
56
+ animation: modal-dialog-slide-up 0.2s var(--ease-out-3, ease-out);
57
+ }
58
+
59
+ .modal-dialog.open {
60
+ display: block;
61
+ }
62
+
63
+ /* Compact variant — narrower for small forms */
64
+ .modal-dialog.modal-dialog-compact {
65
+ width: 400px;
66
+ }
67
+
68
+ @keyframes modal-dialog-slide-up {
69
+ from {
70
+ opacity: 0;
71
+ transform: translateX(-50%) translateY(8px) scale(0.98);
72
+ }
73
+ to {
74
+ opacity: 1;
75
+ transform: translateX(-50%) translateY(0) scale(1);
76
+ }
77
+ }
78
+
79
+ /* ── Header ── */
80
+ .modal-dialog-header {
81
+ display: flex;
82
+ flex-direction: column;
83
+ gap: var(--space-1);
84
+ padding: var(--space-3) var(--space-4);
85
+ border-bottom: 1px solid var(--color-border);
86
+ }
87
+
88
+ .modal-dialog-title {
89
+ font-weight: var(--weight-6);
90
+ font-size: var(--text-2, 1.125rem);
91
+ color: var(--text-primary);
92
+ }
93
+
94
+ .modal-dialog-desc {
95
+ font-size: var(--text-0);
96
+ color: var(--text-muted);
97
+ }
98
+
99
+ /* ── Body ── */
100
+ .modal-dialog-body {
101
+ padding: var(--space-3) var(--space-4);
102
+ }
103
+
104
+ /* ── Footer ── */
105
+ .modal-dialog-footer {
106
+ display: flex;
107
+ align-items: center;
108
+ justify-content: flex-end;
109
+ padding: var(--space-2) var(--space-4);
110
+ border-top: 1px solid var(--color-border);
111
+ gap: var(--space-2);
112
+ }
113
+
114
+ /* ── Input ── */
115
+ .modal-dialog-input {
116
+ display: block;
117
+ width: 100%;
118
+ box-sizing: border-box;
119
+ padding: var(--space-2) var(--space-3);
120
+ border: 1px solid var(--color-border);
121
+ border-radius: var(--radius-2);
122
+ background: var(--surface-input, var(--color-bg));
123
+ color: var(--text-primary);
124
+ font-size: var(--text-1);
125
+ font-family: inherit;
126
+ outline: none;
127
+ transition: border-color 0.15s var(--ease-2);
128
+ }
129
+
130
+ .modal-dialog-input:focus {
131
+ border-color: var(--color-active);
132
+ box-shadow: 0 0 0 1px var(--color-active);
133
+ }
134
+
135
+ .modal-dialog-input::placeholder {
136
+ color: var(--text-muted);
137
+ }
138
+
139
+ .modal-dialog-input.modal-dialog-error {
140
+ border-color: var(--color-danger);
141
+ box-shadow: 0 0 0 1px var(--color-danger);
142
+ }
143
+
144
+ textarea.modal-dialog-input {
145
+ resize: none;
146
+ overflow: hidden;
147
+ min-height: 1.6em;
148
+ max-height: 8em;
149
+ line-height: var(--leading-3);
150
+ field-sizing: content;
151
+ }
152
+
153
+ /* ── Buttons ── */
154
+ .modal-dialog-btn {
155
+ padding: var(--space-1) var(--space-3);
156
+ border-radius: var(--radius-2);
157
+ font-size: var(--text-0);
158
+ font-family: inherit;
159
+ cursor: pointer;
160
+ border: 1px solid var(--color-border);
161
+ transition: background 0.15s var(--ease-2);
162
+ }
163
+
164
+ .modal-dialog-btn-secondary {
165
+ background: var(--surface-section);
166
+ color: var(--text-muted);
167
+ }
168
+
169
+ .modal-dialog-btn-secondary:hover {
170
+ background: var(--surface-hover);
171
+ }
172
+
173
+ .modal-dialog-btn-primary {
174
+ background: var(--color-active);
175
+ color: var(--text-on-badge);
176
+ border-color: var(--color-active);
177
+ }
178
+
179
+ .modal-dialog-btn-primary:hover {
180
+ opacity: 0.9;
181
+ }
182
+
183
+ /* ── Footer hints (keyboard shortcuts) ── */
184
+ .modal-dialog-hints {
185
+ display: flex;
186
+ gap: var(--space-3);
187
+ font-size: var(--text-0);
188
+ color: var(--text-muted);
189
+ }
190
+
191
+ .modal-dialog-hint {
192
+ display: inline-flex;
193
+ align-items: center;
194
+ gap: var(--space-1);
195
+ }
196
+
197
+ .modal-dialog-hint kbd {
198
+ display: inline-flex;
199
+ align-items: center;
200
+ justify-content: center;
201
+ min-width: 20px;
202
+ height: 20px;
203
+ padding: 0 var(--space-1);
204
+ background: var(--surface-input, var(--color-bg));
205
+ border: 1px solid var(--color-border);
206
+ border-radius: var(--radius-1, 4px);
207
+ font-size: var(--text-00, 11px);
208
+ font-family: inherit;
209
+ color: var(--text-muted);
210
+ line-height: 1;
211
+ }
212
+
213
+ /* ── Field layout (for form dialogs) ── */
214
+ .modal-dialog-fields {
215
+ display: flex;
216
+ flex-direction: column;
217
+ gap: var(--space-3);
218
+ }
219
+
220
+ .modal-dialog-field {
221
+ display: flex;
222
+ flex-direction: column;
223
+ gap: var(--space-1);
224
+ }
225
+
226
+ .modal-dialog-label {
227
+ font-size: var(--text-0);
228
+ font-weight: var(--weight-5);
229
+ color: var(--text-muted);
230
+ }
231
+
232
+ .modal-dialog-required {
233
+ color: var(--color-danger);
234
+ margin-left: 0.15em;
235
+ }
236
+
237
+ /* ── Mention dropdown (inside form fields) ── */
238
+ .modal-dialog-mention-field {
239
+ position: relative;
240
+ }
241
+
242
+ .modal-dialog-mention-dropdown {
243
+ position: absolute;
244
+ top: 100%;
245
+ left: 0;
246
+ right: 0;
247
+ z-index: 10;
248
+ margin: var(--space-1) 0 0;
249
+ padding: var(--space-1) 0;
250
+ list-style: none;
251
+ background: var(--color-bg);
252
+ border: 1px solid var(--color-border);
253
+ border-radius: var(--radius-2);
254
+ box-shadow: var(--shadow-2, 0 4px 12px rgba(0, 0, 0, 0.15));
255
+ max-height: 160px;
256
+ overflow-y: auto;
257
+ }
258
+
259
+ .modal-dialog-mention-item {
260
+ display: flex;
261
+ align-items: center;
262
+ gap: var(--space-2);
263
+ padding: var(--space-1) var(--space-2);
264
+ cursor: pointer;
265
+ font-size: var(--text-1);
266
+ color: var(--text-primary);
267
+ }
268
+
269
+ .modal-dialog-mention-item:hover,
270
+ .modal-dialog-mention-item.active {
271
+ background: var(--surface-hover);
272
+ }
273
+
274
+ .modal-dialog-mention-item .avatar {
275
+ border-radius: var(--radius-round, 50%);
276
+ flex-shrink: 0;
277
+ }
278
+
279
+ .modal-dialog-mention-name {
280
+ overflow: hidden;
281
+ text-overflow: ellipsis;
282
+ white-space: nowrap;
283
+ }
284
+
285
+ /* ── Scoped variant ──
286
+ * When a container element is specified, the modal is rendered inside that
287
+ * element instead of the full viewport. The container gets `position: relative`
288
+ * so overlay + dialog stay contained within it.
289
+ */
290
+ .modal-dialog-container {
291
+ position: relative;
292
+ }
293
+
294
+ .modal-dialog-overlay--scoped {
295
+ position: absolute;
296
+ z-index: 1; /* relative to the container, not the page */
297
+ border-radius: inherit; /* match container shape */
298
+ }
299
+
300
+ .modal-dialog--scoped {
301
+ position: absolute;
302
+ z-index: 2;
303
+ top: 15%;
304
+ left: 50%;
305
+ transform: translateX(-50%);
306
+ max-height: 80%;
307
+ animation: modal-dialog-scoped-slide 0.15s var(--ease-out-3, ease-out);
308
+ }
309
+
310
+ .modal-dialog--scoped.open {
311
+ display: block;
312
+ }
313
+
314
+ @keyframes modal-dialog-scoped-slide {
315
+ from {
316
+ opacity: 0;
317
+ transform: translateX(-50%) translateY(8px) scale(0.98);
318
+ }
319
+ to {
320
+ opacity: 1;
321
+ transform: translateX(-50%) translateY(0) scale(1);
322
+ }
323
+ }
324
+
325
+ /* ── Mobile ── */
326
+ @media (max-width: 768px) {
327
+ .modal-dialog {
328
+ width: calc(100vw - 2rem);
329
+ top: 10vh;
330
+ border-radius: var(--radius-3, 12px);
331
+ }
332
+
333
+ .modal-dialog--scoped {
334
+ top: 10%;
335
+ }
336
+
337
+ .modal-dialog-hints {
338
+ display: none;
339
+ }
340
+ }
@@ -86,6 +86,12 @@
86
86
  z-index: calc(var(--layer-modal) + 10);
87
87
  }
88
88
 
89
+ /* When chat is fullscreen, these modals must sit above it */
90
+ body.chat-fullscreen #link-creative-modal,
91
+ body.chat-fullscreen #topic-search-modal {
92
+ z-index: calc(var(--layer-fullscreen) + 1);
93
+ }
94
+
89
95
  .common-popup {
90
96
  position: absolute;
91
97
  z-index: var(--layer-modal);