collavre 0.12.3 → 0.13.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 (49) 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 +27 -13
  5. data/app/assets/stylesheets/collavre/creatives.css +64 -0
  6. data/app/assets/stylesheets/collavre/modal_dialog.css +340 -0
  7. data/app/assets/stylesheets/collavre/search_popup.css +63 -76
  8. data/app/components/collavre/command_menu_component.rb +8 -0
  9. data/app/controllers/collavre/creatives_controller.rb +1 -1
  10. data/app/controllers/collavre/tasks_controller.rb +54 -0
  11. data/app/helpers/collavre/application_helper.rb +1 -0
  12. data/app/javascript/collavre.js +1 -0
  13. data/app/javascript/components/ImageResizer.jsx +206 -0
  14. data/app/javascript/components/InlineLexicalEditor.jsx +2 -13
  15. data/app/javascript/components/plugins/image_upload_plugin.jsx +5 -2
  16. data/app/javascript/controllers/comments/__tests__/form_controller_review.test.js +15 -0
  17. data/app/javascript/controllers/comments/drop_trigger_controller.js +3 -1
  18. data/app/javascript/controllers/comments/form_controller.js +5 -0
  19. data/app/javascript/controllers/comments/list_controller.js +5 -7
  20. data/app/javascript/controllers/comments/presence_controller.js +56 -2
  21. data/app/javascript/creatives/drag_drop/event_handlers.js +15 -0
  22. data/app/javascript/lib/lexical/image_node.jsx +6 -16
  23. data/app/javascript/modules/command_args_form.js +539 -0
  24. data/app/javascript/modules/command_menu.js +49 -13
  25. data/app/javascript/modules/modal_dialog.js +40 -0
  26. data/app/javascript/utils/drag_bundle_image.js +54 -0
  27. data/app/jobs/collavre/trigger_loop_check_job.rb +8 -8
  28. data/app/jobs/collavre/trigger_loop_verify_job.rb +11 -0
  29. data/app/models/collavre/comment.rb +58 -0
  30. data/app/models/collavre/creative/describable.rb +2 -1
  31. data/app/models/collavre/user.rb +7 -0
  32. data/app/services/collavre/ai_agent/message_builder.rb +55 -7
  33. data/app/services/collavre/ai_agent_service.rb +27 -5
  34. data/app/services/collavre/ai_client.rb +36 -37
  35. data/app/services/collavre/command_menu_service.rb +53 -5
  36. data/app/views/collavre/comments/_comments_popup.html.erb +1 -1
  37. data/app/views/collavre/creatives/_inline_edit_form.html.erb +2 -2
  38. data/app/views/collavre/shared/navigation/_inbox_button.html.erb +0 -1
  39. data/app/views/collavre/shared/navigation/_mobile_inbox_button.html.erb +0 -1
  40. data/app/views/collavre/shared/navigation/_search_form.html.erb +1 -1
  41. data/app/views/collavre/users/edit_ai.html.erb +4 -5
  42. data/app/views/collavre/users/new_ai.html.erb +4 -5
  43. data/config/locales/comments.en.yml +10 -0
  44. data/config/locales/comments.ko.yml +10 -0
  45. data/config/locales/drop_trigger.en.yml +3 -0
  46. data/config/locales/drop_trigger.ko.yml +3 -0
  47. data/config/routes.rb +5 -0
  48. data/lib/collavre/version.rb +1 -1
  49. metadata +7 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 61eb7c1c09136cc7e5e15121a76f89f27da2232fe9202a3d909488fe0df5045e
4
- data.tar.gz: b2fa13c57e9394a2d9736361c2bb13f62dd5122fde785a8c885ab7ae88e84dd2
3
+ metadata.gz: e41b11d4cc034b5427a84b2bcc4b2061d2400ec13a3fd05baf2783a37124c6c5
4
+ data.tar.gz: f50467778a86b0dc89a1bea7f2c684f1c9bad3828db6b0c49f6c284c1cd7709c
5
5
  SHA512:
6
- metadata.gz: 3a0a984f9b5074aefd2f95819c28df6421445bb26a39ae0cf69a395df1b72334037a0917cc3790b164beb29d95998dcf51fb3958f8cd8b3b5832a646a3ba9dc6
7
- data.tar.gz: 4a1552462be73e3aeb0deba68260da717a101368130bc984a82ebff2bf78e6ec46dc363332632f976cb82535d083d279bcd33a5b208116e90c0ecb0358ae1362
6
+ metadata.gz: 70a7f5c8b10e0fb95d3cc7e3ca739c2d894e418e8936f141c99b69a339ec811556564c376e4e4b3624c8a375d8477dc676f915e4bcbdc1c532789b7234ee1b26
7
+ data.tar.gz: 867618e8aff1be6f2d24735eceae6aeb6544ce85dccf7b7f23bbad9a4d0cb985f6131ed0a18fc6752ad897dc0e42c52446556fff9dab25c5d5ed5849c73e714e
@@ -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);
@@ -1030,6 +1030,33 @@ body.chat-fullscreen {
1030
1030
  filter: grayscale(1);
1031
1031
  }
1032
1032
 
1033
+ .agent-stop-btn {
1034
+ background: var(--surface-btn);
1035
+ border: 1px solid var(--border-color);
1036
+ border-radius: var(--radius-2);
1037
+ color: var(--text-muted);
1038
+ cursor: pointer;
1039
+ font-size: var(--text-00);
1040
+ padding: var(--space-px-1) var(--space-px-2);
1041
+ display: inline-flex;
1042
+ align-items: center;
1043
+ gap: var(--space-px-1);
1044
+ margin-right: var(--space-px-1);
1045
+ flex-shrink: 0;
1046
+ transition: background 0.15s, color 0.15s;
1047
+ white-space: nowrap;
1048
+ line-height: 1;
1049
+ }
1050
+
1051
+ .agent-stop-btn .agent-stop-icon {
1052
+ font-size: var(--text-00);
1053
+ }
1054
+
1055
+ .agent-stop-btn:hover {
1056
+ background: var(--color-danger);
1057
+ color: var(--text-on-btn);
1058
+ }
1059
+
1033
1060
  /* --- Context Chips --- */
1034
1061
  .comment-contexts-list {
1035
1062
  display: flex;
@@ -1365,19 +1392,6 @@ body.chat-fullscreen {
1365
1392
  opacity: 0.5;
1366
1393
  }
1367
1394
 
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
1395
  /* Hide the topic link if the message is displayed within that topic's context */
1382
1396
  /* Use Javascript to set data-current-topic-id on the list container */
1383
1397
  .comment-topic-link {
@@ -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
+ }
@@ -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
+ }