Commit Graph

296 Commits

Author SHA1 Message Date
Sam Ruby 1cb2f69786 Compile the PWA help and account settings through Herb
Rails main compiles HTML templates through Herb under the 8.2 framework
defaults, which Campfire loads. Herb rejects `case` and its first `when`
in a single ERB tag, so the three pwa/ partials failed to compile, and
`herb:check` rejects ERB output in attribute names, which the account
settings' switch used for `checked`. Give `case` its own tag and build
the switch with tag.input; both render the same under Erubi.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 244e77241b)
2026-10-05 11:45:06 -04:00
Stanko Krtalić d2155e85a0 Merge pull request #306 from namespaceMarcello/post-webhook-attachments-only-on-success
Post a webhook reply as an attachment only when the bot answered 200
2026-10-05 17:13:57 +02:00
Stanko Krtalić 5c72c35775 Merge pull request #291 from basecamp/dependabot/bundler/selenium-webdriver-4.49.0
build(deps-dev): bump selenium-webdriver from 4.35.0 to 4.49.0
2026-10-05 17:10:16 +02:00
Stanko Krtalić 21c49b3ccc Merge pull request #289 from basecamp/dependabot/bundler/faker-3.8.0
build(deps-dev): bump faker from 3.5.2 to 3.8.0
2026-10-05 17:06:49 +02:00
Stanko Krtalić c09c464e9a Merge pull request #288 from basecamp/dependabot/github_actions/github-actions-e631a66382
build(deps): bump ruby/setup-ruby from 1.324.0 to 1.327.0 in the github-actions group
2026-10-05 17:06:28 +02:00
Stanko Krtalić b39766c8d6 Merge pull request #287 from basecamp/dependabot/bundler/propshaft-dc979db
build(deps): bump propshaft from `e49a9de` to `dc979db`
2026-10-05 17:06:15 +02:00
Stanko Krtalić b67a0fb706 Merge pull request #285 from cattekin/test-performance
Hash fixture passwords at minimum `bcrypt` cost
2026-10-05 17:05:57 +02:00
Stanko Krtalić 4690de5057 Merge pull request #301 from rubys/fix-edge-install-icon-path
Find the Edge install icon under images/external
2026-10-05 17:04:04 +02:00
Stanko Krtalić 77c5234cb6 Merge pull request #304 from namespaceMarcello/search-newest-matches-by-rowid
Read the newest search matches off the index instead of sorting them all
2026-10-05 16:59:44 +02:00
Stanko Krtalić 2393f01ba2 Merge pull request #302 from rubys/fix-layout-stray-spans
Drop two unmatched closing spans from the flash
2026-10-05 16:46:27 +02:00
Stanko Krtalić d1f09b4276 Merge pull request #297 from namespaceMarcello/check-paging-without-counting
Check for a second page of messages without counting the room
2026-10-05 16:45:50 +02:00
Stanko K.R. 259c06bea1 Remove trailing whitespace 2026-10-05 16:30:18 +02:00
Stanko Krtalić 6913afa864 Merge branch 'main' into check-paging-without-counting 2026-10-05 16:18:32 +02:00
Stanko Krtalić c73ea37e0c Merge pull request #300 from rubys/fix-autocomplete-json-accept
Ask for JSON when autocompleting people to ping
2026-10-05 16:17:10 +02:00
Stanko Krtalić e0e31846c9 Merge pull request #299 from rubys/fix-boost-soft-keyboard-action
Wire the inline boost link to the soft keyboard
2026-10-05 16:16:25 +02:00
Stanko Krtalić f2bdfde610 Merge pull request #298 from namespaceMarcello/search-operator-words
Search for operator words instead of parsing them
2026-10-05 16:15:23 +02:00
Stanko Krtalić acef0c71cf Merge pull request #295 from namespaceMarcello/messages-room-created-at-index
Index messages by room and creation time
2026-10-05 15:53:19 +02:00
David Heinemeier Hansson 345e63718f Update README with latest Campfire benchmark comparison (#315) 2026-10-05 07:52:23 -04:00
Marcello Costagliola e12651240c Drop the query plan test
The index is a schema change; a test that pins SQLite's plan for each
pagination scope locks in more than the change needs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0142qgjggdJ2KDdGk7RF9Xm9
2026-10-05 13:51:56 +02:00
Marcello Costagliola 6288a10633 Post a webhook reply as an attachment only when the bot answered 200
A bot's reply becomes a message when the status is 200 and the type is text,
and an attachment otherwise, but the attachment branch never looked at the
status. Whenever a bot's endpoint failed, its error page landed in the room as
a file: a proxy's 502 page as attachment.html, a 404 as attachment.text.

Apply the text branch's 200 check to attachments too. The error reply test
answered without a content type, which skipped the attachment branch, so it
now answers with an HTML error page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JVFo3Lt9T8M5NR7KxVvsZ2
2026-10-05 02:58:11 +02:00
Marcello Costagliola edaab3e5d1 Read the newest search matches off the index instead of sorting them all
Search showed the last 100 matches by created_at, so SQLite collected every
message containing the words and sorted them before keeping a page. A common
word in a large account meant sorting most of its history on every search.

Ordering by the full-text index's rowid, which is the message id, lets SQLite
walk the index from the newest match and stop once the page is full.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JVFo3Lt9T8M5NR7KxVvsZ2
2026-10-05 02:50:48 +02:00
Sam Ruby 955d799d11 Find the Edge install icon under images/external
install-edge.svg lives in app/assets/images/external/, alongside the
other install icons, but the Edge branch of pwa/_install_instructions
asked for it at the top level. Propshaft raises MissingAssetError, so a
browser the useragent gem reports as Edge got a 500 on the profile page
and anywhere else the partial renders.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 20:06:53 -04:00
Sam Ruby 1d39b8cbe1 Drop two unmatched closing spans from the flash
The flash icons were followed by `</span>` with no opening tag.
Browsers discard them, so nothing renders differently, but HTML-aware
tools such as Herb report them as errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 20:06:53 -04:00
Sam Ruby b8be941b99 Ask for JSON when autocompleting people to ping
`as: "json"` is a @rails/request.js option; plain fetch ignores it and
sends `Accept: */*`, so Autocompletable::UsersController answered with
its HTML format and response.json() threw. The new ping form never
showed any suggestions. Send an explicit Accept header instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 20:06:53 -04:00
Sam Ruby c057484dfb Wire the inline boost link to the soft keyboard
link_to passed `action: "soft-keyboard#open"` as a plain option, so it
rendered as an `action` attribute, which Stimulus never reads. The link
in messages/_actions.html.erb already passes it under `data:`; this one
now does too, so tapping "Add a boost" on a touch device opens the
keyboard for the boost form as the menu's link does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 20:06:53 -04:00
Marcello Costagliola 74bdbb0764 Keep the room_id index and date the migration in UTC
The composite index doesn't serve everything the room_id one did. In
SQLite that index is (room_id, rowid), so it also reads a room's messages
in id order, and it is the narrower one to count a room with. Keeping both,
as memberships already does, leaves those lookups as they were.

The migration was named after local time, two hours ahead of the UTC
timestamp Rails generates; it now carries a UTC one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NuTtwJKhb77Dv7EQqv2C3C
2026-10-05 01:39:36 +02:00
Marcello Costagliola 6cdcb459b9 Search for operator words instead of parsing them
The search box keeps only word characters, but FTS5 still reads AND, OR,
NOT and NEAR in the query as operators, so searching for "AND" or for
"salt AND" raised "fts5: syntax error" and answered with a 500. Quoting
each word makes FTS5 look for it as text, which is how it already treats
the same words in lower case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NuTtwJKhb77Dv7EQqv2C3C
2026-10-05 01:08:53 +02:00
Marcello Costagliola cf0ba58d8d Check for a second page of messages without counting the room
The original room shows its welcome box until it holds more than a page of
messages, and paged? answered that with a COUNT(*) over every message in
the room on each visit. Asking whether there is a row past the first page
answers the same question while reading at most a page of index entries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NuTtwJKhb77Dv7EQqv2C3C
2026-10-05 01:03:39 +02:00
Marcello Costagliola 5cb4316133 Index messages by room and creation time
Every page of a room's messages (opening the room, scrolling back, the
refresh after reconnecting, the bot API) selects WHERE room_id = ? ORDER BY
created_at LIMIT 40. With only the room_id index, SQLite reads every
message in the room and sorts them in a temporary B-tree to return 40, so
these requests grow with the room's history.

A composite index on (room_id, created_at) lets SQLite read the 40 rows in
order straight from the index. It covers every lookup the room_id index
served, so that one goes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NuTtwJKhb77Dv7EQqv2C3C
2026-10-05 00:55:24 +02:00
David Heinemeier Hansson 254dd1d46f Add Django and Laravel benchmarks and label the baseline Rails (#294) 2026-10-04 18:18:31 -04:00
David Heinemeier Hansson 6c7f8fa15f Document other Campfire implementations and benchmarks (#293)
* Document other Campfire implementations and benchmarks

* Remeasure README benchmarks with the latest Ruby optimizations

* Shorten the README benchmark context
2026-10-04 14:10:38 -04:00
David Heinemeier Hansson 659f95748a Preload only uncached messages and reduce rendering overhead (#292)
* Preload only uncached messages and reduce rendering overhead

* Keep benchmark summaries without raw JSON results

* Use Ruby benchmark drivers and keep generated results out of the repo
2026-10-04 12:36:36 -04:00
dependabot[bot] 67b6df3847 build(deps-dev): bump selenium-webdriver from 4.35.0 to 4.49.0
Bumps [selenium-webdriver](https://github.com/SeleniumHQ/selenium) from 4.35.0 to 4.49.0.
- [Release notes](https://github.com/SeleniumHQ/selenium/releases)
- [Changelog](https://github.com/SeleniumHQ/selenium/blob/trunk/rb/CHANGES)
- [Commits](https://github.com/SeleniumHQ/selenium/compare/selenium-4.35.0...selenium-4.49.0)

---
updated-dependencies:
- dependency-name: selenium-webdriver
  dependency-version: 4.49.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-02 19:36:16 +00:00
dependabot[bot] 957c9da9c4 build(deps-dev): bump faker from 3.5.2 to 3.8.0
Bumps [faker](https://github.com/faker-ruby/faker) from 3.5.2 to 3.8.0.
- [Release notes](https://github.com/faker-ruby/faker/releases)
- [Changelog](https://github.com/faker-ruby/faker/blob/main/CHANGELOG.md)
- [Commits](https://github.com/faker-ruby/faker/compare/v3.5.2...v3.8.0)

---
updated-dependencies:
- dependency-name: faker
  dependency-version: 3.8.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-02 19:34:50 +00:00
dependabot[bot] 862e128722 build(deps): bump ruby/setup-ruby
Bumps the github-actions group with 1 update: [ruby/setup-ruby](https://github.com/ruby/setup-ruby).


Updates `ruby/setup-ruby` from 1.324.0 to 1.327.0
- [Release notes](https://github.com/ruby/setup-ruby/releases)
- [Changelog](https://github.com/ruby/setup-ruby/blob/master/release.rb)
- [Commits](https://github.com/ruby/setup-ruby/compare/a0102e0972be65f351c307e2d64b9314a57c8073...14594264cd68ce8a2345dd349bc3d138a4ef85c8)

---
updated-dependencies:
- dependency-name: ruby/setup-ruby
  dependency-version: 1.327.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-02 19:34:48 +00:00
dependabot[bot] 26c70b3254 build(deps): bump propshaft from e49a9de to dc979db
Bumps [propshaft](https://github.com/rails/propshaft) from `e49a9de` to `dc979db`.
- [Release notes](https://github.com/rails/propshaft/releases)
- [Commits](https://github.com/rails/propshaft/compare/e49a9de659ff27462015e54dd832e86e762a6ddc...dc979db89cd07c72ee4d11d415ae1cb4fd072623)

---
updated-dependencies:
- dependency-name: propshaft
  dependency-version: dc979db89cd07c72ee4d11d415ae1cb4fd072623
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-02 19:34:42 +00:00
Edward Tippett ae149fca92 Hash fixture passwords at minimum bcrypt cost
The fixture calls BCrypt directly, bypassing Rails' test configuration.
2026-09-29 09:53:39 +07:00
Stanko Krtalić 90b330024d Merge pull request #284 from basecamp/mention-menu-spacing
Fix mention menu spacing and check the registry login before releasing
v1.5.1
2026-09-26 13:03:34 +02:00
Stanko K.R. e5cfffda67 Check the registry login before releasing
The GitHub release was created before the image push failed on a missing
docker login, leaving the release half done.
2026-09-26 11:16:03 +02:00
Stanko K.R. 87ba7aa3cc Keep the message body list styles out of the mention menu
The composer editor carries the lexxy-content class, so the !important
list rules for message bodies hit the menu's ul and li too, leaving the
items cramped and pushed in from the left.
2026-09-26 11:16:03 +02:00
Stanko Krtalić 12249b9b3a Merge pull request #283 from basecamp/composer-drafts
Keep an unsent message as a draft while switching rooms
v1.5.0
2026-09-26 10:43:55 +02:00
Stanko K.R. 5c5821a395 Keep an unsent message as a draft while switching rooms
The composer stores what's being written in localStorage per room and
restores it when the room is opened again. Sending clears it.
2026-09-26 10:38:13 +02:00
Stanko Krtalić 8fa6138fb9 Merge pull request #254 from basecamp/dependabot/bundler/web-push-3.1.0
build(deps): bump web-push from 3.0.2 to 3.1.0
2026-09-26 10:28:20 +02:00
Stanko Krtalić beaf467a38 Merge pull request #257 from basecamp/dependabot/bundler/mocha-3.1.0
build(deps-dev): bump mocha from 2.7.1 to 3.1.0
2026-09-26 10:27:58 +02:00
Stanko Krtalić 9a258bd1a0 Merge pull request #224 from basecamp/replace-trix-with-lexxy
Replace Trix with Lexxy
2026-09-26 10:27:29 +02:00
Stanko Krtalić f8e36e9761 Merge pull request #282 from basecamp/dependabot/bundler/rubyzip-3.4.0
build(deps-dev): bump rubyzip from 3.0.2 to 3.4.0
2026-09-26 10:23:25 +02:00
Stanko Krtalić 83e7a7f4c9 Merge pull request #281 from basecamp/dependabot/github_actions/github-actions-f89073ee97
build(deps): bump the github-actions group with 4 updates
2026-09-26 10:23:12 +02:00
Stanko Krtalić eadbaab7d2 Merge pull request #262 from excid3/redis-url-env
Support Redis configuration with REDIS_URL env var
2026-09-26 10:22:53 +02:00
Stanko K.R. b5d3543e64 Test that Trix-formatted messages render and survive editing 2026-09-26 10:21:40 +02:00
Stanko K.R. 1694accbf8 Let tables through the message sanitizers
Lexxy imports pasted and Markdown tables, and the tag sanitizer dropped
them with everything in them on render.
2026-09-26 10:04:38 +02:00