diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md new file mode 100644 index 000000000..dba71e970 --- /dev/null +++ b/.claude/CLAUDE.md @@ -0,0 +1 @@ +@../AGENTS.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 43c994c2d..000000000 --- a/CLAUDE.md +++ /dev/null @@ -1 +0,0 @@ -@AGENTS.md diff --git a/app/assets/stylesheets/card-perma.css b/app/assets/stylesheets/card-perma.css index d3b671b9a..e68706765 100644 --- a/app/assets/stylesheets/card-perma.css +++ b/app/assets/stylesheets/card-perma.css @@ -25,7 +25,7 @@ position: relative; &:has(dialog[open]) { - z-index: 2; + z-index: 3; } &:has(.card-perma__star-input:checked) { diff --git a/app/assets/stylesheets/dialog.css b/app/assets/stylesheets/dialog.css index 5d6a7eb0e..1336cad9b 100644 --- a/app/assets/stylesheets/dialog.css +++ b/app/assets/stylesheets/dialog.css @@ -1,4 +1,9 @@ @layer components { + /* Prevent page scrolling when modal dialog is open */ + html:has(dialog:modal) { + overflow: hidden; + } + :is(.dialog) { border: 0; opacity: 0; @@ -35,4 +40,9 @@ } } } + + /* Ensure padding from viewport edges */ + .dialog.panel { + max-inline-size: calc(100vw - var(--inline-space-double) * 2); + } } diff --git a/app/javascript/controllers/form_controller.js b/app/javascript/controllers/form_controller.js index beca1f08f..2ba58ab89 100644 --- a/app/javascript/controllers/form_controller.js +++ b/app/javascript/controllers/form_controller.js @@ -54,6 +54,10 @@ export default class extends Controller { } submitToTopTarget(event) { + const value = event.target.value?.trim() + + if (!value) return false + this.element.setAttribute("data-turbo-frame", "_top") this.submit() } diff --git a/app/models/concerns/searchable.rb b/app/models/concerns/searchable.rb index 8b39504ef..17d1f186a 100644 --- a/app/models/concerns/searchable.rb +++ b/app/models/concerns/searchable.rb @@ -1,6 +1,8 @@ module Searchable extend ActiveSupport::Concern + SEARCH_CONTENT_LIMIT = 32.kilobytes + included do after_create_commit :create_in_search_index after_update_commit :update_in_search_index @@ -32,11 +34,15 @@ module Searchable card_id: search_card_id, board_id: search_board_id, title: search_title, - content: search_content, + content: search_record_content, created_at: created_at } end + def search_record_content + search_content&.truncate_bytes(SEARCH_CONTENT_LIMIT, omission: "") + end + def search_record_class Search::Record.for(account_id) end diff --git a/app/views/boards/edit/_delete.html.erb b/app/views/boards/edit/_delete.html.erb index ec346f590..9d9b7f441 100644 --- a/app/views/boards/edit/_delete.html.erb +++ b/app/views/boards/edit/_delete.html.erb @@ -3,7 +3,7 @@ <%= icon_tag "trash" %> Delete this board - +

Delete this board?

Are you sure you want to permanently delete this board and all the cards on it? This can't be undone.

diff --git a/app/views/cards/_delete.html.erb b/app/views/cards/_delete.html.erb index 43afbbad2..e6b999ee0 100644 --- a/app/views/cards/_delete.html.erb +++ b/app/views/cards/_delete.html.erb @@ -3,7 +3,7 @@ <%= icon_tag "trash" %> Delete this card - +

Delete this card?

Are you sure you want to permanently delete this card?

diff --git a/app/views/filters/settings/_manage.html.erb b/app/views/filters/settings/_manage.html.erb index ef800df72..11b351709 100644 --- a/app/views/filters/settings/_manage.html.erb +++ b/app/views/filters/settings/_manage.html.erb @@ -1,7 +1,8 @@ <% filter = user_filtering.filter %> +<% clear_url = filter.single_board ? board_path(filter.single_board) : no_filtering_url %>
- <%= link_to no_filtering_url, class: "btn btn--remove txt-x-small", data: { controller: "hotkey tooltip", action: "keydown.esc@document->hotkey#click"} do %> + <%= link_to clear_url, class: "btn btn--remove txt-x-small", data: { controller: "hotkey tooltip", action: "keydown.esc@document->hotkey#click"} do %> <%= icon_tag "close" %> Clear all <% end %> diff --git a/test/models/card/searchable_test.rb b/test/models/card/searchable_test.rb index d6dcdf60c..5c7ca9c86 100644 --- a/test/models/card/searchable_test.rb +++ b/test/models/card/searchable_test.rb @@ -32,6 +32,24 @@ class Card::SearchableTest < ActiveSupport::TestCase assert_not_includes results, card_in_other_board end + test "search content is truncated to a reasonable limit" do + search_record_class = Search::Record.for(@user.account_id) + + # Create a card with unreasonably long content + long_content = "asdf " * Searchable::SEARCH_CONTENT_LIMIT + card = @board.cards.create!(title: "Card with long description", creator: @user) + card.description = ActionText::Content.new(long_content) + card.save! + + # Check if was indexed + results = Card.mentioning("asdf", user: @user) + assert_includes results, card + + # Check the content length was within the limit + search_record = search_record_class.find_by(searchable_type: "Card", searchable_id: card.id) + assert search_record.content.bytesize <= Searchable::SEARCH_CONTENT_LIMIT + end + test "deleting card removes search record and FTS entry" do search_record_class = Search::Record.for(@user.account_id) card = @board.cards.create!(title: "Card to delete", creator: @user)