livechat 0.4.5 → 0.4.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4a3607a4c4f442eea6743e28545ee7fc731118d6d7c967c48f44956479164fa1
4
- data.tar.gz: 2a5c9ac8ced8c0a0ab24c3dda2892dd89fff4af3a2eaba07618987538572d7ae
3
+ metadata.gz: '09c824179376138fe658faa40d6cc57949c642e10f42338d442152d50ab4db38'
4
+ data.tar.gz: 0477a0b2e15986595aec854184f0d97ab72dc92fd8ced2738145cc985589490c
5
5
  SHA512:
6
- metadata.gz: d2f1f8f74451e5c7b4d6520f523e0822f2cb9c9324789e9c659e93b4064e990529b330eba75ccd947ac883b81e5037f28c1f7a3c1f0be3062b5b91cfeae551ee
7
- data.tar.gz: 159e74cf7ba33baf90e78f8ed6c256eb9a348605e8edce6b690ca9e34fad484292ba5ff6ad9bdbc2ff389e8618484edd9930b714c2102b7c0e6811f78a9365c1
6
+ metadata.gz: f356b2a55640050e11d295ccded406fe4f4698543241ed04bf0754ff9a11b079a566e889391c04bb4b9558fc5989c7a52fcd4a7fa36627ef506a0479e4e419c9
7
+ data.tar.gz: fd1edc5d3a0cd36fd8f03cf8ac7946d5615249e1d8da69bc43a9e6d2393aa61a1bde6c39ae65acc71aba70e21a26c80e60fb089433e4a87b0c654eff4c1c0266
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.7
4
+
5
+ - The inbox reply composer now matches the visitor widget: one bordered box
6
+ with the reply on top and a toolbar row (paperclip left, circular send
7
+ right) inside it, lit on focus — instead of a textarea flanked by loose
8
+ buttons. The reply field auto-grows as you type, and file chips ride inside
9
+ the box.
10
+
11
+ ## 0.4.6
12
+
13
+ - Inbox conversation page no longer scrolls the whole page. It now fills the
14
+ viewport with the message thread as the only scrolling region and the reply
15
+ composer pinned at the bottom — so sending a reply keeps you at the newest
16
+ message instead of jumping the page up to the redirect anchor. (A real
17
+ chat-app layout, matching the visitor widget.)
18
+
3
19
  ## 0.4.5
4
20
 
5
21
  - The widget's injected stylesheet now refreshes itself when its content
@@ -93,31 +93,46 @@
93
93
  .thread .msg.agent .att-file { background: rgba(255,255,255,.2); }
94
94
  #new-messages { margin: 12px 0 0; }
95
95
  #new-messages a { font-weight: 600; }
96
- .reply { display: flex; gap: 8px; padding: 12px; border-top: 1px solid var(--border); }
97
- .reply .compose { flex: 1; display: flex; flex-direction: column; gap: 6px; }
98
- .reply textarea { resize: vertical; min-height: 44px; max-height: 200px; padding: 8px 10px;
99
- border: 1px solid var(--border); border-radius: 10px; font: inherit;
100
- background: var(--bg); color: var(--text); }
96
+ /* Unified composer box (matches the visitor widget): the reply on top, a
97
+ toolbar row below (tools left, send right), all inside one bordered box
98
+ that lights up on focus. */
99
+ .reply { display: flex; flex-direction: column; gap: 6px; margin: 10px 12px; padding: 8px 10px;
100
+ border: 1px solid var(--border); border-radius: 14px; background: var(--bg); }
101
+ .reply:focus-within { border-color: var(--accent); }
102
+ .reply textarea { -webkit-appearance: none; appearance: none; border: none; background: none;
103
+ resize: none; outline: none; font: inherit; color: var(--text);
104
+ padding: 4px 4px 0; min-height: 40px; max-height: 160px; overflow-y: auto; }
101
105
  .reply input[type=file].reply-file { font-size: 12px; color: var(--muted); }
102
- .reply button.attach { align-self: flex-end; width: 42px; min-width: 42px; height: 42px; padding: 0;
103
- display: flex; align-items: center; justify-content: center; color: var(--muted);
104
- background: var(--surface); border: 1px solid var(--border); border-radius: 10px; }
105
- .reply button.attach:hover { color: var(--text); background: var(--bg); }
106
+ .reply-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
107
+ .reply-tools { display: flex; align-items: center; gap: 2px; }
108
+ .reply .attach { width: 32px; height: 32px; padding: 0; border: none; background: none;
109
+ color: var(--muted); border-radius: 8px; cursor: pointer;
110
+ display: flex; align-items: center; justify-content: center; }
111
+ .reply .attach:hover { background: var(--border); color: var(--text); }
106
112
  .reply .chips { display: flex; flex-wrap: wrap; gap: 6px; }
107
113
  .reply .chip { display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
108
114
  padding: 4px 6px 4px 10px; border: 1px solid var(--border); border-radius: 999px;
109
- background: var(--bg); font-size: 12px; }
115
+ background: var(--surface); font-size: 12px; }
110
116
  .reply .chip-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 220px; }
111
117
  .reply .chip-x { padding: 0 2px; border: none; background: none; color: var(--muted);
112
118
  font-size: 16px; line-height: 1; cursor: pointer; }
113
119
  .reply .chip-x:hover { color: var(--text); }
114
- .reply button.send { align-self: flex-end; width: 42px; min-width: 42px; height: 42px; padding: 0;
115
- display: flex; align-items: center; justify-content: center;
116
- background: var(--accent); border-color: var(--accent); color: var(--accent-text);
117
- border-radius: 10px; }
120
+ .reply .send { width: 32px; min-width: 32px; height: 32px; padding: 0; border: none; border-radius: 50%;
121
+ background: var(--accent); color: var(--accent-text); cursor: pointer;
122
+ display: flex; align-items: center; justify-content: center; }
123
+ /* Conversation page: fill the viewport and scroll the THREAD, not the
124
+ page — so a reply never jumps the page, and the composer stays put. */
125
+ .lvc-convo, .lvc-convo .container { height: 100vh; height: 100dvh; }
126
+ .lvc-convo { overflow: hidden; }
127
+ .lvc-convo .container { display: flex; flex-direction: column; padding-bottom: 16px; }
128
+ .lvc-convo .breadcrumb, .lvc-convo .head-row, .lvc-convo .context-line,
129
+ .lvc-convo .flash { flex: 0 0 auto; }
130
+ .lvc-convo .card { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
131
+ .lvc-convo .thread { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
132
+ .lvc-convo .reply { flex: 0 0 auto; }
118
133
  </style>
119
134
  </head>
120
- <body>
135
+ <body class="<%= yield(:body_class) %>">
121
136
  <div class="container">
122
137
  <% if flash[:alert].present? %>
123
138
  <p class="flash alert"><%= flash[:alert] %></p>
@@ -1,3 +1,6 @@
1
+ <%# Fill the viewport and scroll only the thread (see .lvc-convo in the layout). %>
2
+ <% content_for :body_class, 'lvc-convo' %>
3
+
1
4
  <p class="breadcrumb">
2
5
  <%= link_to "← #{t('livechat.dashboard.back', default: 'All conversations')}", conversations_path %>
3
6
  </p>
@@ -60,23 +63,26 @@
60
63
  <% end %>
61
64
  </div>
62
65
 
66
+ <%# One bordered composer box (matches the visitor widget): the reply on top,
67
+ a toolbar row below with tools on the left and send on the right. %>
63
68
  <%= form_with url: conversation_messages_path(@conversation), method: :post,
64
69
  multipart: true, class: 'reply' do |form| %>
65
- <div class="compose">
66
- <%= form.text_area :body, rows: 2, autofocus: true, required: !Livechat.attachments_enabled?,
67
- placeholder: t('livechat.dashboard.reply_placeholder', default: 'Write your reply…') %>
68
- <%# dashboard.js hides this and swaps in a paperclip button + file chips.
69
- Left plain as a no-JS fallback so agents can still attach without it. %>
70
- <% if Livechat.attachments_enabled? %>
71
- <%= form.file_field :files, multiple: true, class: 'reply-file',
72
- aria: { label: t('livechat.dashboard.attach', default: 'Attach files') } %>
73
- <% end %>
70
+ <%= form.text_area :body, rows: 2, autofocus: true, required: !Livechat.attachments_enabled?,
71
+ placeholder: t('livechat.dashboard.reply_placeholder', default: 'Write your reply…') %>
72
+ <%# dashboard.js hides this and swaps in a paperclip button (into .reply-tools)
73
+ plus file chips at the top of the box. Left plain as a no-JS fallback. %>
74
+ <% if Livechat.attachments_enabled? %>
75
+ <%= form.file_field :files, multiple: true, class: 'reply-file',
76
+ aria: { label: t('livechat.dashboard.attach', default: 'Attach files') } %>
77
+ <% end %>
78
+ <div class="reply-toolbar">
79
+ <div class="reply-tools"></div>
80
+ <button type="submit" class="send"
81
+ aria-label="<%= t('livechat.dashboard.send', default: 'Send') %>"
82
+ title="<%= t('livechat.dashboard.send', default: 'Send') %>">
83
+ <%# Paper airplane (Heroicons, MIT) — same icon as the widget. %>
84
+ <svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><path fill="currentColor" d="M3.478 2.404a.75.75 0 0 0-.926.941l2.432 7.905H13.5a.75.75 0 0 1 0 1.5H4.984l-2.432 7.905a.75.75 0 0 0 .926.94 60.519 60.519 0 0 0 18.445-8.986.75.75 0 0 0 0-1.218A60.517 60.517 0 0 0 3.478 2.404Z"/></svg>
85
+ </button>
74
86
  </div>
75
- <button type="submit" class="primary send"
76
- aria-label="<%= t('livechat.dashboard.send', default: 'Send') %>"
77
- title="<%= t('livechat.dashboard.send', default: 'Send') %>">
78
- <%# Paper airplane (Heroicons, MIT) — same icon as the widget. %>
79
- <svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><path fill="currentColor" d="M3.478 2.404a.75.75 0 0 0-.926.941l2.432 7.905H13.5a.75.75 0 0 1 0 1.5H4.984l-2.432 7.905a.75.75 0 0 0 .926.94 60.519 60.519 0 0 0 18.445-8.986.75.75 0 0 0 0-1.218A60.517 60.517 0 0 0 3.478 2.404Z"/></svg>
80
- </button>
81
87
  <% end %>
82
88
  </div>
@@ -46,18 +46,19 @@
46
46
  button.title = label;
47
47
  // Paperclip (Heroicons, MIT) — same icon as the widget.
48
48
  button.innerHTML =
49
- '<svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true">' +
49
+ '<svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">' +
50
50
  '<path fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" ' +
51
51
  'stroke-linejoin="round" d="M18.4 8.6 9.9 17a3.5 3.5 0 0 1-5-5l8.5-8.4a2.3 2.3 0 0 1 ' +
52
52
  '3.3 3.3l-8.5 8.4a1.1 1.1 0 0 1-1.6-1.6l7.8-7.8"/></svg>';
53
53
  button.addEventListener("click", function () { input.click(); });
54
- form.insertBefore(button, form.firstChild);
54
+ // The paperclip lives in the toolbar (left of send); chips sit at the top
55
+ // of the box — so it reads as one composer, like the visitor widget.
56
+ (form.querySelector(".reply-tools") || form).appendChild(button);
55
57
 
56
58
  var chips = document.createElement("div");
57
59
  chips.className = "chips";
58
60
  chips.hidden = true;
59
- var compose = form.querySelector(".compose") || form;
60
- compose.insertBefore(chips, compose.firstChild);
61
+ form.insertBefore(chips, form.firstChild);
61
62
 
62
63
  input.addEventListener("change", render);
63
64
 
@@ -143,6 +144,14 @@
143
144
  replyBox.form.requestSubmit();
144
145
  }
145
146
  });
147
+ // Grow the box with the reply, up to the CSS max-height — matching the
148
+ // widget composer instead of a fixed two-row field.
149
+ var autogrow = function () {
150
+ replyBox.style.height = "auto";
151
+ replyBox.style.height = Math.min(replyBox.scrollHeight, 160) + "px";
152
+ };
153
+ replyBox.addEventListener("input", autogrow);
154
+ autogrow();
146
155
  }
147
156
 
148
157
  // New messages are appended in place — never a reload — so a half-written
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Livechat
4
- VERSION = '0.4.5'
4
+ VERSION = '0.4.7'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: livechat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.5
4
+ version: 0.4.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yaroslav Shmarov