livechat 0.7.0 → 0.7.2
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 +4 -4
- data/AGENTS.md +173 -0
- data/CHANGELOG.md +18 -0
- data/README.md +5 -0
- data/lib/livechat/version.rb +1 -1
- data/lib/livechat/widget.js +1 -2
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 57a8fc1c73b96c5e4d063c004d7d9e22eb90f888a38bbdd11d568c9088cb2660
|
|
4
|
+
data.tar.gz: 6a1d40df302ab6ad64c6f94b5c98c8e29a9d2c7f4bc6f53e036a0a2fce5d5ea8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e366d6123965f80394da3fee7555d6f312dda5ff315887b168e3cc0eccd2beb89771c9da864d8eead4983b841cb1852566196a32680890e08112f44cf7779022
|
|
7
|
+
data.tar.gz: 216f73d82310d38bcb53345f95dfe3d3b0b22a38785ba8b2ab05f2d8dce2877cd2e22e77fc2b8e1da567fa2db0ca5c74524ba1f40056898172a7a60906fadd2b
|
data/AGENTS.md
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Instructions for coding agents. Two audiences:
|
|
4
|
+
|
|
5
|
+
- **[Installing livechat into a Rails app](#installing-into-a-rails-app)** — you are working in a host app and were asked to add support chat, live chat, or an in-app inbox.
|
|
6
|
+
- **[Working on the gem itself](#working-on-the-gem-itself)** — you are working in this repository.
|
|
7
|
+
|
|
8
|
+
Requirements: Ruby >= 3.2, Rails >= 7.1. Active Storage only for file attachments. **No Redis and no Action Cable** — the transport is polling unless you opt in.
|
|
9
|
+
|
|
10
|
+
If you are in a host app and this file is not in front of you, it ships inside the gem: `cat "$(bundle show livechat)/AGENTS.md"`.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Installing into a Rails app
|
|
15
|
+
|
|
16
|
+
### 1. Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
bundle add livechat
|
|
20
|
+
bin/rails generate livechat:install
|
|
21
|
+
bin/rails db:migrate
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The generator writes `config/initializers/livechat.rb`, one migration (`livechat_conversations`, `livechat_messages`), and `mount_livechat at: "/livechat"` into `config/routes.rb`. Read the initializer it wrote — every option is documented there in comments, and it is the source of truth over any summary of it, including this file.
|
|
25
|
+
|
|
26
|
+
Every `config.…` line below belongs inside the `Livechat.configure do |config|` block in that initializer. Uncomment and edit in place rather than appending a second `configure` block.
|
|
27
|
+
|
|
28
|
+
### 2. Wire the three things the generator cannot
|
|
29
|
+
|
|
30
|
+
**a. The widget tag.** Nothing appears until this is on the page:
|
|
31
|
+
|
|
32
|
+
```erb
|
|
33
|
+
<%# app/views/layouts/application.html.erb, before </body> %>
|
|
34
|
+
<%= livechat_tag %>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The helper is injected into ActionView by the engine — no include, no import, no asset pipeline entry. It renders the launcher bubble bottom-right.
|
|
38
|
+
|
|
39
|
+
**b. `authorize_agent` — do this before deploying.** The inbox at `/livechat` defaults to **development only**. It fails closed, so shipping without this is not an open inbox — it is a 403 reading "Forbidden. Set Livechat.config.authorize_agent to grant access."
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
config.authorize_agent = ->(request) { request.env["warden"]&.user&.admin? }
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**c. Visitor identity**, if the app has users. Without it every visitor is a cookie-tracked guest, and nobody in the inbox has a name.
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
config.current_user = ->(request) { request.env["warden"]&.user }
|
|
49
|
+
config.visitor_label = ->(user) { user.name } # what the inbox shows
|
|
50
|
+
config.agent_label = ->(user) { user.name } # signed onto each reply
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
> **`current_user`, `enabled` and `authorize_agent` receive the raw `request`, not a controller.** Writing `->(request) { current_user }` is the most common mistake here — that method does not exist in this scope. Resolve the user *from the request*: Warden env, a signed cookie, `Current.user` if middleware already set it. Note the different shapes: `visitor_label` and `agent_label` receive the **user**, while `agent_display_name` receives the already-stored **label string**.
|
|
54
|
+
|
|
55
|
+
Rails 8 built-in auth:
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
config.current_user = lambda do |request|
|
|
59
|
+
token = request.cookies["session_token"]
|
|
60
|
+
Session.find_signed(token)&.user if token
|
|
61
|
+
end
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 3. Verify
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
bin/rails routes | grep livechat # engine mounted
|
|
68
|
+
bin/rails livechat:seed_demo # optional sample conversations, idempotent
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Then in the running app: load any page, confirm the bubble appears bottom-right, send a message, and answer it at `/livechat`.
|
|
72
|
+
|
|
73
|
+
### Opening the widget
|
|
74
|
+
|
|
75
|
+
| Way | How |
|
|
76
|
+
| --- | --- |
|
|
77
|
+
| The launcher bubble | On by default. `config.show_launcher = false` to remove it |
|
|
78
|
+
| Your own element | `<%= livechat_button %>`, or any element with `data-livechat-open` |
|
|
79
|
+
| JavaScript | `window.Livechat.open()` |
|
|
80
|
+
|
|
81
|
+
A visitor has **one conversation**, not a queue of tickets — writing again reopens the same thread. Signed-in visitors keep it across devices (keyed by user id); guests are tracked by cookie.
|
|
82
|
+
|
|
83
|
+
### Email notifications need two settings, not one
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
config.mailer_from = "support@example.com" # required, or nothing sends
|
|
87
|
+
config.agent_emails = ["team@example.com"] # array, or a callable returning one
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Setting `agent_emails` alone sends nothing: `mailer_from` is what switches email on (`Livechat.config.emails_enabled?` is `mailer_from.present?`). Notification is one email per unread stretch, not one per message.
|
|
91
|
+
|
|
92
|
+
### Realtime is opt-in
|
|
93
|
+
|
|
94
|
+
Polling is the default transport, on purpose — a host with no Action Cable works untouched. Turning on push requires the host to actually mount a cable:
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
config.action_cable = true
|
|
98
|
+
config.action_cable_url = "/cable" # keep in sync with the mount in routes.rb
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Leave it off unless the app already has Action Cable working. Polling is not a degraded mode here.
|
|
102
|
+
|
|
103
|
+
### Attachments
|
|
104
|
+
|
|
105
|
+
`config.attach_files` is on by default but **silently inert without Active Storage** in the host app (`rails active_storage:install`) — the widget keeps working, just without a paperclip. Caps: `max_attachments` (5), `max_attachment_size` (10 MB), `allowed_attachment_types` (nil = any). Files are served through the engine at `/livechat/attachments/:id`, gated per request — never a public blob URL. Do not build your own blob links.
|
|
106
|
+
|
|
107
|
+
### Do not
|
|
108
|
+
|
|
109
|
+
- **Do not copy the widget JavaScript into `app/javascript`, or add a `<script>` tag for it.** `livechat_tag` renders what is needed, and the engine serves the code with a content fingerprint. There is no build step and nothing for esbuild/importmap/Tailwind to know about.
|
|
110
|
+
- **Do not build your own inbox.** Use the mounted one; `config.agent_layout = "admin/application"` renders it inside an existing admin shell.
|
|
111
|
+
- **Do not add an Action Cable mount to "make chat realtime"** unless you also set `config.action_cable = true`. Polling is the default and is not broken.
|
|
112
|
+
- **Do not set config outside the initializer.** `rate_limit` in particular is read when the controller class loads; assigning config per-request mutates it process-wide.
|
|
113
|
+
- **Do not expose attachments by blob URL** — the engine's gated route exists so a leaked signed URL cannot hand over a customer's file.
|
|
114
|
+
|
|
115
|
+
### Configuration worth knowing
|
|
116
|
+
|
|
117
|
+
Everything is optional; a fresh install works with zero config. Full list with comments is in the generated initializer.
|
|
118
|
+
|
|
119
|
+
| Option | Default | Note |
|
|
120
|
+
| --- | --- | --- |
|
|
121
|
+
| `authorize_agent` | development only | **Who can read the inbox. Set before deploying.** |
|
|
122
|
+
| `enabled` | everyone | Per-request gate for the widget and its endpoints |
|
|
123
|
+
| `current_user` | `nil` | Receives the request; nil means guest-by-cookie |
|
|
124
|
+
| `visitor_label`, `agent_label` | name/email/to_s | Receive the user |
|
|
125
|
+
| `agent_display_name` | the label unchanged | Receives the label; return "Support team" to keep agents anonymous |
|
|
126
|
+
| `app_name`, `greeting`, `reply_time_text`, `launcher_label` | localized defaults | Widget copy |
|
|
127
|
+
| `avatar_url`, `accent_color` | `nil` | Header avatar (URL or callable) and brand hex |
|
|
128
|
+
| `show_launcher` | `true` | `false` = open only from your own elements |
|
|
129
|
+
| `mailer_from` | `nil` | **Required for any email at all** |
|
|
130
|
+
| `agent_emails` | `nil` | Array or callable |
|
|
131
|
+
| `attach_files` | `true` | Needs Active Storage; inert without it |
|
|
132
|
+
| `storage_service` | app default | A `storage.yml` key for a dedicated bucket |
|
|
133
|
+
| `max_attachments`, `max_attachment_size` | `5`, `10.megabytes` | Enforced server-side |
|
|
134
|
+
| `allowed_attachment_types` | `nil` | Content-type allowlist |
|
|
135
|
+
| `action_cable`, `action_cable_url` | `false`, `"/cable"` | Opt-in push |
|
|
136
|
+
| `rate_limit` | `{ to: 30, within: 60 }` | Rails 7.2+; ignored on 7.1. `nil` disables |
|
|
137
|
+
| `mount_path` | `"/livechat"` | Keep in sync with `mount_livechat at:` |
|
|
138
|
+
| `on_visitor_message`, `on_agent_message` | no-ops | Run inline after save — Slack, Noticed, push |
|
|
139
|
+
|
|
140
|
+
Turbo Drive and strict nonce-based CSP work out of the box. 26 locales ship with the gem, RTL included.
|
|
141
|
+
|
|
142
|
+
### Common failure modes
|
|
143
|
+
|
|
144
|
+
| Symptom | Cause |
|
|
145
|
+
| --- | --- |
|
|
146
|
+
| `/livechat` returns 403 "Set Livechat.config.authorize_agent to grant access" | Exactly what it says: still at the development-only default |
|
|
147
|
+
| No bubble on the page | `livechat_tag` missing from the rendered layout, `config.enabled` false, or `show_launcher = false` with no opener of your own |
|
|
148
|
+
| No notification emails | `mailer_from` not set — `agent_emails` alone does nothing |
|
|
149
|
+
| Messages only appear on refresh | Expected: polling is the default. `config.action_cable = true` (with `/cable` mounted) for push |
|
|
150
|
+
| No attachment button | Active Storage not installed, or `attach_files = false` |
|
|
151
|
+
| `undefined local variable current_user` in the initializer | A gate lambda treated its argument as a controller. It is a `request` |
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Working on the gem itself
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
bundle exec rake test # minitest, dummy app under test/dummy
|
|
159
|
+
bundle exec rubocop # must be clean
|
|
160
|
+
BUNDLE_GEMFILE=gemfiles/rails_7.1.gemfile bundle exec rake test # 7.1, 7.2, 8.0, 8.1 in gemfiles/
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Layout: `app/` controllers, models, inbox views, mailer · `lib/livechat/` config, widget JS/CSS, seeds, engine, channels · `lib/generators/livechat/install/` the one generator · `config/locales/` 26 locales · `test/` minitest, `test/dummy` the host app.
|
|
164
|
+
|
|
165
|
+
Conventions this codebase holds to — follow them rather than the first thing that works:
|
|
166
|
+
|
|
167
|
+
- **Polling is the baseline, Action Cable is opt-in.** Nothing may require a cable to be mounted. The channel lives under `lib/` and is required only when `ActionCable` is defined, so eager-loading an app without it cannot fail.
|
|
168
|
+
- **Active Storage is optional at runtime.** Attachment code checks for it rather than assuming it; an app without Active Storage gets a working widget, not an exception.
|
|
169
|
+
- **The widget is plain ES5-style JS served by the engine**, no build step, no framework, config read from a JSON block so a Turbo visit re-reads the current page's settings.
|
|
170
|
+
- **Visitor scoping is never by conversation id.** The widget's endpoints resolve the thread from the signed-in id or the guest cookie, so no id in a request can address someone else's conversation. Keep it that way.
|
|
171
|
+
- **The dummy app pins `config.active_job.queue_adapter = :test`.** Do not remove it or let it drift back to the `:async` default. Attaching a file enqueues Active Storage's analysis job, and `:async` runs it on a background thread that checks out its own connection — writes no test transaction covers, landing in the middle of whatever runs next. That is a suite that fails order-dependently in a test which never created a row, and it is miserable to trace back.
|
|
172
|
+
- Every user-facing change bumps `lib/livechat/version.rb` and adds a `CHANGELOG.md` entry that says what it costs, not only what it adds.
|
|
173
|
+
- Commit messages are prose that explains the tradeoff — read `git log` before writing one.
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.2
|
|
4
|
+
|
|
5
|
+
- Adds `AGENTS.md`: install and integration instructions written for coding
|
|
6
|
+
agents — the request-shaped config lambdas, the two settings email needs, why
|
|
7
|
+
polling is the default and Action Cable is opt-in, and the mistakes agents
|
|
8
|
+
actually make. It ships inside the gem, so
|
|
9
|
+
`cat "$(bundle show livechat)/AGENTS.md"` works from a host app.
|
|
10
|
+
- The dummy app pins `queue_adapter = :test` for the test suite. Attaching a
|
|
11
|
+
file enqueues Active Storage's analysis job, and the default `:async` adapter
|
|
12
|
+
runs it on a background thread with its own database connection — writes no
|
|
13
|
+
test transaction covers, which is how a suite starts failing order-dependently
|
|
14
|
+
in a test that never created a row. No effect on the gem itself.
|
|
15
|
+
|
|
16
|
+
## 0.7.1
|
|
17
|
+
|
|
18
|
+
- Removed the translucent border and background from the customer-facing
|
|
19
|
+
avatar so app logos sit cleanly on the widget header.
|
|
20
|
+
|
|
3
21
|
## 0.7.0
|
|
4
22
|
|
|
5
23
|
- Added `config.avatar_url`, accepting a URL or per-request callable, to show
|
data/README.md
CHANGED
|
@@ -52,6 +52,11 @@ demo messages instead of duplicating conversations.
|
|
|
52
52
|
|
|
53
53
|
Ruby >= 3.2 · Rails >= 7.1 · Active Storage only if you want file attachments.
|
|
54
54
|
|
|
55
|
+
Installing with a coding agent? Point it at [AGENTS.md](AGENTS.md) — the same
|
|
56
|
+
steps in the order an agent needs them, plus the gates it tends to get wrong and
|
|
57
|
+
the things it should not do. It ships inside the gem, so
|
|
58
|
+
`cat "$(bundle show livechat)/AGENTS.md"` works from any app that bundles it.
|
|
59
|
+
|
|
55
60
|
## What you get
|
|
56
61
|
|
|
57
62
|
| | |
|
data/lib/livechat/version.rb
CHANGED
data/lib/livechat/widget.js
CHANGED
|
@@ -1233,8 +1233,7 @@
|
|
|
1233
1233
|
"justify-content:space-between;gap:8px;padding:14px 16px;background:var(--lvc-accent);" +
|
|
1234
1234
|
"color:var(--lvc-accent-text)}" +
|
|
1235
1235
|
"#lvc-identity{display:flex;align-items:center;gap:10px;min-width:0}" +
|
|
1236
|
-
"#lvc-avatar{width:44px;height:44px;flex:0 0 44px;border-radius:50%;object-fit:cover
|
|
1237
|
-
"border:2px solid rgba(255,255,255,.35);background:rgba(255,255,255,.15)}" +
|
|
1236
|
+
"#lvc-avatar{width:44px;height:44px;flex:0 0 44px;border-radius:50%;object-fit:cover}" +
|
|
1238
1237
|
"#lvc-titles{min-width:0}" +
|
|
1239
1238
|
"#lvc-header strong{display:block;font-size:15px}" +
|
|
1240
1239
|
"#lvc-header span{display:block;font-size:12px;opacity:.85;margin-top:2px}" +
|
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.7.
|
|
4
|
+
version: 0.7.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yaroslav Shmarov
|
|
@@ -38,6 +38,7 @@ executables: []
|
|
|
38
38
|
extensions: []
|
|
39
39
|
extra_rdoc_files: []
|
|
40
40
|
files:
|
|
41
|
+
- AGENTS.md
|
|
41
42
|
- CHANGELOG.md
|
|
42
43
|
- MIT-LICENSE
|
|
43
44
|
- README.md
|