turbo_chat 0.2.0 → 0.3.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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -1
  3. data/README.md +178 -190
  4. data/app/assets/config/turbo_chat_manifest.js +3 -0
  5. data/app/assets/javascripts/turbo_chat/application.js +3 -0
  6. data/app/assets/javascripts/turbo_chat/invite_picker.js +19 -392
  7. data/app/assets/javascripts/turbo_chat/member_sync.js +426 -0
  8. data/app/assets/javascripts/turbo_chat/mentions.js +366 -0
  9. data/app/assets/javascripts/turbo_chat/messages.js +18 -370
  10. data/app/assets/javascripts/turbo_chat/realtime.js +3 -10
  11. data/app/assets/javascripts/turbo_chat/scroll_proxy.js +379 -0
  12. data/app/assets/javascripts/turbo_chat/shared.js +7 -383
  13. data/app/assets/stylesheets/turbo_chat/application.css +9 -1646
  14. data/app/assets/stylesheets/turbo_chat/base.css +84 -0
  15. data/app/assets/stylesheets/turbo_chat/components.css +193 -0
  16. data/app/assets/stylesheets/turbo_chat/composer.css +241 -0
  17. data/app/assets/stylesheets/turbo_chat/layout.css +307 -0
  18. data/app/assets/stylesheets/turbo_chat/members.css +264 -0
  19. data/app/assets/stylesheets/turbo_chat/menus.css +172 -0
  20. data/app/assets/stylesheets/turbo_chat/messages.css +430 -0
  21. data/app/controllers/turbo_chat/application_controller.rb +3 -7
  22. data/app/controllers/turbo_chat/chat_memberships_controller.rb +35 -1
  23. data/app/controllers/turbo_chat/chat_messages_controller.rb +4 -8
  24. data/app/controllers/turbo_chat/chats_controller.rb +10 -12
  25. data/app/helpers/turbo_chat/application_helper/config_support.rb +42 -32
  26. data/app/helpers/turbo_chat/application_helper/mention_support.rb +3 -3
  27. data/app/helpers/turbo_chat/application_helper/message_rendering.rb +24 -13
  28. data/app/models/turbo_chat/chat.rb +43 -20
  29. data/app/models/turbo_chat/chat_membership.rb +1 -1
  30. data/app/models/turbo_chat/chat_message/blocked_words_moderation.rb +9 -25
  31. data/app/models/turbo_chat/chat_message/body_length_validation.rb +1 -1
  32. data/app/models/turbo_chat/chat_message/broadcasting.rb +2 -6
  33. data/app/models/turbo_chat/chat_message/formatting.rb +3 -7
  34. data/app/models/turbo_chat/chat_message/mention_validation.rb +1 -1
  35. data/app/models/turbo_chat/chat_message/signals.rb +1 -1
  36. data/app/models/turbo_chat/chat_message.rb +3 -8
  37. data/app/views/turbo_chat/chat_messages/_form.html.erb +9 -9
  38. data/app/views/turbo_chat/chat_messages/_message.html.erb +2 -2
  39. data/app/views/turbo_chat/chat_messages/_signals.html.erb +11 -13
  40. data/app/views/turbo_chat/chat_messages/_system.html.erb +1 -1
  41. data/app/views/turbo_chat/chats/_invite_form.html.erb +1 -1
  42. data/app/views/turbo_chat/chats/_member_entries.html.erb +15 -1
  43. data/app/views/turbo_chat/chats/index.html.erb +1 -1
  44. data/app/views/turbo_chat/chats/new.html.erb +4 -7
  45. data/app/views/turbo_chat/chats/show.html.erb +29 -27
  46. data/config/routes.rb +6 -1
  47. data/db/migrate/20260325000016_add_chat_mode_to_turbo_chat_chats.rb +6 -0
  48. data/lib/generators/turbo_chat/install/templates/turbo_chat.rb +8 -0
  49. data/lib/turbo_chat/configuration/defaults.rb +21 -0
  50. data/lib/turbo_chat/configuration.rb +105 -0
  51. data/lib/turbo_chat/moderation/chat_actions.rb +2 -2
  52. data/lib/turbo_chat/moderation/member_actions.rb +2 -1
  53. data/lib/turbo_chat/moderation/support.rb +5 -9
  54. data/lib/turbo_chat/permission/support.rb +6 -2
  55. data/lib/turbo_chat/permission.rb +1 -5
  56. data/lib/turbo_chat/signals.rb +1 -1
  57. data/lib/turbo_chat/version.rb +1 -1
  58. metadata +13 -2
@@ -0,0 +1,84 @@
1
+ :root {
2
+ --chat-text: #12263f;
3
+ --chat-muted: #5f738a;
4
+ --chat-border: #c9d5e3;
5
+ --chat-border-strong: #b9c8d9;
6
+ --chat-surface: #ffffff;
7
+ --chat-surface-soft: #f6f9fd;
8
+ --chat-primary: #1f6edc;
9
+ --chat-primary-dark: #1452ac;
10
+ --chat-primary-soft: #e7f1ff;
11
+ --chat-mention-highlight-color: #b42318;
12
+ --chat-mention-mark-background: #b4231838;
13
+ --chat-danger: #b42318;
14
+ --chat-danger-soft: #fff1f3;
15
+ --chat-success: #1f7a40;
16
+ --chat-success-soft: #ecfdf3;
17
+ --chat-shadow: 0 20px 48px rgba(16, 36, 58, 0.12);
18
+ }
19
+
20
+ html,
21
+ body {
22
+ width: 100%;
23
+ max-width: 100%;
24
+ min-height: 100%;
25
+ overflow-x: hidden;
26
+ }
27
+
28
+ html.chat-page-scroll-locked,
29
+ body.chat-page-scroll-locked {
30
+ overflow: hidden;
31
+ overscroll-behavior: none;
32
+ }
33
+
34
+ .chat-global-scrollbar {
35
+ position: fixed;
36
+ top: 0;
37
+ right: 0;
38
+ bottom: 0;
39
+ width: 14px;
40
+ overflow-y: auto;
41
+ overflow-x: hidden;
42
+ z-index: 40;
43
+ background: transparent;
44
+ overscroll-behavior: none;
45
+ scrollbar-width: thin;
46
+ scrollbar-color: rgba(61, 94, 133, 0.42) transparent;
47
+ }
48
+
49
+ .chat-global-scrollbar::-webkit-scrollbar {
50
+ width: 12px;
51
+ }
52
+
53
+ .chat-global-scrollbar::-webkit-scrollbar-track {
54
+ background: transparent;
55
+ }
56
+
57
+ .chat-global-scrollbar::-webkit-scrollbar-thumb {
58
+ border-radius: 999px;
59
+ background: rgba(61, 94, 133, 0.42);
60
+ border: 3px solid transparent;
61
+ background-clip: content-box;
62
+ }
63
+
64
+ .chat-global-scrollbar-spacer {
65
+ width: 1px;
66
+ min-height: 100%;
67
+ }
68
+
69
+ .chat-global-scrollbar--hidden {
70
+ opacity: 0;
71
+ pointer-events: none;
72
+ }
73
+
74
+ body {
75
+ margin: 0;
76
+ min-height: 100dvh;
77
+ min-height: 100svh;
78
+ color: var(--chat-text);
79
+ background:
80
+ radial-gradient(960px circle at 0% 0%, #e6f1ff 0%, rgba(230, 241, 255, 0) 52%),
81
+ radial-gradient(920px circle at 100% 0%, #fff3e8 0%, rgba(255, 243, 232, 0) 48%),
82
+ linear-gradient(180deg, #f9fbff 0%, #f2f5fa 100%);
83
+ font-family: "Space Grotesk", "Avenir Next", "Trebuchet MS", sans-serif;
84
+ }
@@ -0,0 +1,193 @@
1
+ .chat-btn {
2
+ appearance: none;
3
+ border: 1px solid transparent;
4
+ border-radius: 12px;
5
+ padding: 9px 16px;
6
+ background: linear-gradient(135deg, var(--chat-primary) 0%, var(--chat-primary-dark) 100%);
7
+ color: #ffffff;
8
+ font: inherit;
9
+ font-weight: 700;
10
+ line-height: 1.2;
11
+ text-decoration: none;
12
+ cursor: pointer;
13
+ display: inline-flex;
14
+ align-items: center;
15
+ justify-content: center;
16
+ transition: transform 140ms ease, box-shadow 140ms ease, background 140ms ease, border-color 140ms ease, color 140ms ease;
17
+ box-shadow: 0 8px 16px rgba(20, 82, 172, 0.3);
18
+ }
19
+
20
+ .chat-btn:hover {
21
+ transform: translateY(-1px);
22
+ box-shadow: 0 10px 18px rgba(20, 82, 172, 0.34);
23
+ }
24
+
25
+ .chat-btn:active {
26
+ transform: translateY(0);
27
+ }
28
+
29
+ .chat-btn:focus-visible {
30
+ outline: none;
31
+ box-shadow: 0 0 0 3px rgba(31, 110, 220, 0.23), 0 8px 16px rgba(20, 82, 172, 0.3);
32
+ }
33
+
34
+ .chat-btn:disabled {
35
+ opacity: 0.56;
36
+ cursor: not-allowed;
37
+ transform: none;
38
+ }
39
+
40
+ .chat-btn--small {
41
+ border-radius: 10px;
42
+ padding: 7px 11px;
43
+ font-size: 13px;
44
+ }
45
+
46
+ .chat-btn--ghost {
47
+ background: #ffffff;
48
+ border-color: var(--chat-border);
49
+ color: var(--chat-text);
50
+ box-shadow: none;
51
+ }
52
+
53
+ .chat-btn--ghost:hover {
54
+ background: #f5f8fc;
55
+ border-color: #b7c8dd;
56
+ box-shadow: none;
57
+ }
58
+
59
+ .chat-btn--danger {
60
+ background: var(--chat-danger-soft);
61
+ border-color: #f0bcc3;
62
+ color: var(--chat-danger);
63
+ box-shadow: none;
64
+ }
65
+
66
+ .chat-btn--danger:hover {
67
+ background: #ffe6ea;
68
+ border-color: #e7a4ad;
69
+ color: #9f1d12;
70
+ box-shadow: none;
71
+ }
72
+
73
+ .chat-btn--success {
74
+ background: var(--chat-success-soft);
75
+ border-color: #a8dfbc;
76
+ color: var(--chat-success);
77
+ box-shadow: none;
78
+ }
79
+
80
+ .chat-btn--success:hover {
81
+ background: #ddf9e7;
82
+ border-color: #8fd2a8;
83
+ color: #1a6535;
84
+ box-shadow: none;
85
+ }
86
+
87
+ .chat-btn--send {
88
+ min-width: 38px;
89
+ min-height: 38px;
90
+ padding: 0;
91
+ border-radius: 999px;
92
+ box-shadow: 0 6px 16px rgba(20, 82, 172, 0.26);
93
+ }
94
+
95
+ .chat-banner {
96
+ margin: 14px 0 0;
97
+ padding: 11px 12px;
98
+ border-radius: 12px;
99
+ font-size: 14px;
100
+ }
101
+
102
+ .chat-banner--muted {
103
+ color: #7b5a00;
104
+ border: 1px solid #efd08f;
105
+ background: #fff8e6;
106
+ }
107
+
108
+ .chat-list {
109
+ list-style: none;
110
+ margin: 0;
111
+ padding: 0;
112
+ }
113
+
114
+ .chat-invitations {
115
+ margin: 0 0 16px;
116
+ }
117
+
118
+ .chat-invitations h2,
119
+ .chat-joined h2 {
120
+ margin: 0 0 9px;
121
+ font-size: 16px;
122
+ }
123
+
124
+ .chat-list-item--invitation {
125
+ display: flex;
126
+ align-items: center;
127
+ justify-content: space-between;
128
+ gap: 10px;
129
+ padding: 11px 13px;
130
+ border: 1px solid var(--chat-border);
131
+ border-radius: 12px;
132
+ margin-bottom: 8px;
133
+ background: #ffffff;
134
+ }
135
+
136
+ .chat-list-item--chat {
137
+ margin-bottom: 8px;
138
+ }
139
+
140
+ .chat-list-title {
141
+ font-weight: 700;
142
+ }
143
+
144
+ .chat-list-actions {
145
+ display: inline-flex;
146
+ align-items: center;
147
+ gap: 8px;
148
+ flex-wrap: wrap;
149
+ }
150
+
151
+ .chat-list-link {
152
+ display: flex;
153
+ align-items: center;
154
+ justify-content: space-between;
155
+ gap: 12px;
156
+ padding: 11px 13px;
157
+ border: 1px solid var(--chat-border);
158
+ border-radius: 12px;
159
+ text-decoration: none;
160
+ color: var(--chat-text);
161
+ background: #ffffff;
162
+ transition: border-color 120ms ease, box-shadow 120ms ease, transform 120ms ease;
163
+ }
164
+
165
+ .chat-list-meta {
166
+ color: var(--chat-muted);
167
+ font-size: 13px;
168
+ white-space: nowrap;
169
+ }
170
+
171
+ .chat-list-link:hover {
172
+ border-color: #a8bfdc;
173
+ box-shadow: 0 8px 18px rgba(22, 55, 95, 0.08);
174
+ transform: translateY(-1px);
175
+ }
176
+
177
+ .chat-visually-hidden {
178
+ position: absolute;
179
+ width: 1px;
180
+ height: 1px;
181
+ padding: 0;
182
+ margin: -1px;
183
+ overflow: hidden;
184
+ clip: rect(0, 0, 0, 0);
185
+ white-space: nowrap;
186
+ border: 0;
187
+ }
188
+
189
+ @media (max-width: 640px) {
190
+ .chat-list-item--invitation {
191
+ flex-wrap: wrap;
192
+ }
193
+ }
@@ -0,0 +1,241 @@
1
+ .chat-form textarea,
2
+ .chat-form select,
3
+ .chat-form input[type="search"],
4
+ .chat-field input,
5
+ .chat-inline-edit-form textarea {
6
+ width: 100%;
7
+ border: 1px solid var(--chat-border);
8
+ border-radius: 12px;
9
+ padding: 10px 12px;
10
+ font: inherit;
11
+ color: var(--chat-text);
12
+ background: #ffffff;
13
+ box-sizing: border-box;
14
+ }
15
+
16
+ .chat-form textarea:focus,
17
+ .chat-form select:focus,
18
+ .chat-form input[type="search"]:focus,
19
+ .chat-field input:focus,
20
+ .chat-inline-edit-form textarea:focus {
21
+ border-color: #87b6f7;
22
+ outline: none;
23
+ box-shadow: 0 0 0 3px rgba(31, 110, 220, 0.16);
24
+ }
25
+
26
+ .chat-form--create {
27
+ display: grid;
28
+ grid-template-columns: minmax(0, 1fr) auto;
29
+ align-items: start;
30
+ gap: 10px;
31
+ }
32
+
33
+ .chat-composer {
34
+ margin-top: 14px;
35
+ position: relative;
36
+ }
37
+
38
+ .chat-form {
39
+ display: flex;
40
+ gap: 9px;
41
+ }
42
+
43
+ .chat-composer .chat-form {
44
+ align-items: stretch;
45
+ gap: 0;
46
+ }
47
+
48
+ .chat-composer-shell {
49
+ flex: 1 1 auto;
50
+ display: flex;
51
+ align-items: flex-end;
52
+ gap: 10px;
53
+ border: 1px solid #c8d8ea;
54
+ border-radius: 28px;
55
+ background: #ffffff;
56
+ padding: 8px 10px;
57
+ box-shadow: 0 10px 26px rgba(20, 52, 92, 0.1);
58
+ transition: border-color 120ms ease, box-shadow 120ms ease;
59
+ }
60
+
61
+ .chat-composer-shell:focus-within {
62
+ border-color: #7daee7;
63
+ box-shadow: 0 0 0 3px rgba(31, 110, 220, 0.12), 0 10px 26px rgba(20, 52, 92, 0.12);
64
+ }
65
+
66
+ .chat-composer .chat-composer-input {
67
+ flex: 1 1 auto;
68
+ min-width: 0;
69
+ width: 100%;
70
+ min-height: 24px;
71
+ max-height: 210px;
72
+ border: none;
73
+ border-radius: 0;
74
+ box-shadow: none;
75
+ resize: none;
76
+ padding: 6px 2px;
77
+ margin: 0;
78
+ background: transparent;
79
+ overflow-y: hidden;
80
+ line-height: 1.35;
81
+ }
82
+
83
+ .chat-composer-shell--no-leading-tool .chat-composer-input {
84
+ padding-left: 10px;
85
+ padding-top: 5px;
86
+ padding-bottom: 7px;
87
+ }
88
+
89
+ .chat-composer .chat-composer-input:focus {
90
+ border: none;
91
+ outline: none;
92
+ box-shadow: none;
93
+ }
94
+
95
+ .chat-composer-tool {
96
+ appearance: none;
97
+ display: inline-flex;
98
+ align-items: center;
99
+ justify-content: center;
100
+ flex: 0 0 auto;
101
+ width: 38px;
102
+ height: 38px;
103
+ border-radius: 999px;
104
+ border: 1px solid #d2deed;
105
+ background: #ffffff;
106
+ color: #254569;
107
+ font: inherit;
108
+ font-size: 34px;
109
+ line-height: 1;
110
+ padding: 0;
111
+ cursor: pointer;
112
+ }
113
+
114
+ .chat-composer-tool:hover {
115
+ background: #f3f8ff;
116
+ border-color: #b8cae1;
117
+ }
118
+
119
+ .chat-composer-tool:focus-visible {
120
+ outline: none;
121
+ box-shadow: 0 0 0 3px rgba(31, 110, 220, 0.18);
122
+ }
123
+
124
+ .chat-composer-tool:disabled {
125
+ cursor: not-allowed;
126
+ opacity: 0.52;
127
+ }
128
+
129
+ .chat-composer-tool:disabled:hover {
130
+ background: #ffffff;
131
+ border-color: #d2deed;
132
+ }
133
+
134
+ .chat-composer-tool--add {
135
+ transform: translateY(1px);
136
+ }
137
+
138
+ .chat-composer-tool--voice {
139
+ font-size: 0;
140
+ }
141
+
142
+ .chat-composer-mic {
143
+ position: relative;
144
+ width: 11px;
145
+ height: 14px;
146
+ border: 2px solid currentColor;
147
+ border-radius: 999px;
148
+ box-sizing: border-box;
149
+ }
150
+
151
+ .chat-composer-mic::before {
152
+ content: "";
153
+ position: absolute;
154
+ left: 50%;
155
+ transform: translateX(-50%);
156
+ bottom: -6px;
157
+ width: 2px;
158
+ height: 4px;
159
+ background: currentColor;
160
+ }
161
+
162
+ .chat-composer-mic::after {
163
+ content: "";
164
+ position: absolute;
165
+ left: 50%;
166
+ transform: translateX(-50%);
167
+ bottom: -8px;
168
+ width: 10px;
169
+ height: 5px;
170
+ border: 2px solid currentColor;
171
+ border-top: none;
172
+ border-bottom-left-radius: 6px;
173
+ border-bottom-right-radius: 6px;
174
+ box-sizing: border-box;
175
+ }
176
+
177
+ .chat-composer-submit {
178
+ min-width: 38px;
179
+ width: 38px;
180
+ height: 38px;
181
+ padding: 0;
182
+ border-radius: 999px;
183
+ }
184
+
185
+ .chat-composer-send-icon {
186
+ width: 18px;
187
+ height: 18px;
188
+ display: block;
189
+ }
190
+
191
+ .chat-composer-send-icon path {
192
+ fill: none;
193
+ stroke: currentColor;
194
+ stroke-width: 2.2;
195
+ stroke-linecap: round;
196
+ stroke-linejoin: round;
197
+ }
198
+
199
+ @media (max-width: 640px) {
200
+ .chat-composer-shell {
201
+ border-radius: 22px;
202
+ gap: 8px;
203
+ padding: 7px 8px;
204
+ }
205
+
206
+ .chat-shell--style-unbounded .chat-composer {
207
+ bottom: max(8px, env(safe-area-inset-bottom), env(keyboard-inset-bottom, 0px));
208
+ }
209
+
210
+ .chat-shell--style-unbounded .chat-composer-shell {
211
+ border-radius: 18px;
212
+ }
213
+
214
+ .chat-composer .chat-composer-input {
215
+ min-height: 22px;
216
+ padding-top: 5px;
217
+ padding-bottom: 5px;
218
+ }
219
+
220
+ .chat-composer-tool {
221
+ width: 34px;
222
+ height: 34px;
223
+ font-size: 30px;
224
+ }
225
+
226
+ .chat-composer-mic {
227
+ width: 10px;
228
+ height: 13px;
229
+ }
230
+
231
+ .chat-composer-submit {
232
+ min-width: 34px;
233
+ width: 34px;
234
+ height: 34px;
235
+ }
236
+
237
+ .chat-composer-send-icon {
238
+ width: 16px;
239
+ height: 16px;
240
+ }
241
+ }