avo 4.1.10 → 4.1.12

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.
@@ -122,12 +122,13 @@ class Avo::FieldWrapperComponent < Avo::BaseComponent
122
122
  end
123
123
 
124
124
  def stacked?
125
- # Override on the declaration level
126
- return @stacked unless @stacked.nil?
127
-
128
- # Fetch it from the field
125
+ # The field declaration is the user's explicit intent, so it wins over the
126
+ # layout default a parent component passes in (sidebars, preview, etc.).
129
127
  return @field.stacked unless @field.stacked.nil?
130
128
 
129
+ # Fallback to what the rendering component asked for
130
+ return @stacked unless @stacked.nil?
131
+
131
132
  # Fallback to defaults
132
133
  Avo.configuration.field_wrapper_layout == :stacked
133
134
  end
@@ -81,12 +81,10 @@ export default class extends Controller {
81
81
  return obj
82
82
  }, {})
83
83
 
84
- let encodedFilters
85
-
86
- // Encode the filters and their values
87
- if (filtered && Object.keys(filtered).length > 0) {
88
- encodedFilters = this.encode(filtered)
89
- }
84
+ // Encode the filters and their values. The empty set is encoded too, so
85
+ // the backend can tell "the user cleared the filters" apart from "the URL
86
+ // carries no filters" and overwrite the session-persisted value.
87
+ const encodedFilters = this.encode(filtered)
90
88
 
91
89
  this.navigateToURLWithFilters(encodedFilters)
92
90
  }
@@ -5,7 +5,7 @@
5
5
  data-boolean-filter-keep-filters-panel-open-value="<%= @resource.keep_filters_panel_open %>">
6
6
  <div class="flex items-center">
7
7
  <% if filter.options.empty? %>
8
- <div class="filters-section__empty-state"><%= filter.class.empty_message %></div>
8
+ <div class="filters-section__empty-state"><%= filter.class.get_empty_message %></div>
9
9
  <% else %>
10
10
  <div class="filters-boolean-options">
11
11
  <% filter.options.each do |value, label| %>
@@ -117,7 +117,7 @@ module Avo
117
117
 
118
118
  @width = args[:width] || 100
119
119
 
120
- if width_option.present? && width_option != 100
120
+ if @stacked.nil? && width_option.present? && width_option != 100
121
121
  @stacked = true
122
122
  end
123
123
 
@@ -143,7 +143,7 @@ Every field accepts these common options (full list + types at the field-options
143
143
  - **`copyable:`** — clipboard icon on Show/Index. Copies the **displayed** (formatted) value.
144
144
  - **`link_to_record:`** — make the Index cell a link to the record. Only on `:id`, `:text`, `:gravatar`, and `belongs_to`.
145
145
  - **`for_attribute:`** — back the field with a different attribute than its id (lets you show one column two ways).
146
- - **`width:`** / **`stacked:`** — column width (`25/33/50/66/75/100`; any value <100 auto-stacks) and label-above-value layout.
146
+ - **`width:`** / **`stacked:`** — column width (`25/33/50/66/75/100`; any value <100 auto-stacks) and label-above-value layout. `stacked:` on the field wins everywhere, so `stacked: false` opts out of the auto-stacking and of sidebars/preview stacking too.
147
147
  - **`html:`** — attach `style`/`classes`/`data` to the field's wrapper/label/input per view (e.g. right-align a number: `html: {index: {wrapper: {classes: "text-right"}}}`). See the html page.
148
148
 
149
149
  ### Computed (block) fields
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env ruby
2
+ # Which docs pages changed since the last indexing run, and which shipped skills
3
+ # cite them. The docs repo already hashes every page for us — its git history
4
+ # *is* the lock file, so `docs-index.json` only has to remember one commit.
5
+ #
6
+ # Audits the skills that ship in this gem (the directories beside this bin/).
7
+ # Add-on gems carry their own skills and are out of this script's scope.
8
+ #
9
+ # ruby lib/avo/skills/bin/docs_drift.rb # report; finds the docs checkout itself
10
+ # ruby lib/avo/skills/bin/docs_drift.rb path/to/docs # or point it at one ($AVO_DOCS_PATH works too)
11
+ # ruby lib/avo/skills/bin/docs_drift.rb --update # mark the checkout's HEAD as indexed
12
+ require "json"
13
+
14
+ skills_root = File.expand_path("..", __dir__)
15
+ repo_root = File.expand_path("../../..", skills_root)
16
+ marker_path = "#{skills_root}/docs-index.json"
17
+ marker = JSON.parse(File.read(marker_path))
18
+
19
+ update = ARGV.delete("--update")
20
+
21
+ def git(dir, *args) = `git -C '#{dir}' #{args.join(" ")}`.strip
22
+
23
+ # A docs checkout is any clone of the marker's repo that has the docs path in it.
24
+ # Look next to and above this checkout so the script works from whatever layout a
25
+ # machine or CI runner happens to use.
26
+ def find_docs(repo_root, marker)
27
+ search = ["#{repo_root}/..", "#{repo_root}/../..", "#{repo_root}/../../.."]
28
+ candidates = search.flat_map { |base| Dir.glob("#{base}/*") }.uniq.sort
29
+ candidates.find do |dir|
30
+ next false if dir == repo_root || !Dir.exist?("#{dir}/.git") || !Dir.exist?("#{dir}/#{marker["docs_path"]}")
31
+
32
+ git(dir, "remote", "get-url", "origin").include?(marker["repo"])
33
+ end
34
+ end
35
+
36
+ docs = ARGV[0] || ENV["AVO_DOCS_PATH"] || find_docs(repo_root, marker)
37
+
38
+ if docs.nil? || !Dir.exist?("#{docs}/.git")
39
+ abort "No #{marker["repo"]} checkout found near #{repo_root} — pass its path as an argument or set AVO_DOCS_PATH."
40
+ end
41
+
42
+ docs = File.expand_path(docs)
43
+
44
+ head = git(docs, "rev-parse", "HEAD")
45
+ head_date = git(docs, "log", "-1", "--format=%cs", "HEAD")
46
+
47
+ if update
48
+ File.write(marker_path, JSON.pretty_generate(marker.merge("commit" => head, "date" => head_date)) + "\n")
49
+ puts "Indexed at #{head[0, 12]} (#{head_date})."
50
+ exit
51
+ end
52
+
53
+ unless git(docs, "cat-file", "-t", marker["commit"]) == "commit"
54
+ abort "#{docs} doesn't have commit #{marker["commit"]} — `git fetch` it first."
55
+ end
56
+
57
+ if head == marker["commit"]
58
+ puts "Up to date with #{head[0, 12]} (#{head_date})."
59
+ exit
60
+ end
61
+
62
+ # --no-renames: a renamed page is a delete + an add, which is exactly how a skill
63
+ # citing the old URL has to treat it.
64
+ changed = git(docs, "diff", "--name-status", "--no-renames", "#{marker["commit"]}..HEAD", "--", marker["docs_path"])
65
+ .lines.map { |line| line.split("\t") }
66
+ .map { |status, path| [status[0], File.basename(path.strip)] }
67
+ .reject { |_, page| page == "docs-map.md" }
68
+ .uniq
69
+
70
+ # The Docs lines in each SKILL.md are the provenance record. Skills cite pages
71
+ # both with and without the .md extension; normalize to the diff's basename form.
72
+ version = Regexp.escape(File.basename(marker["docs_path"]))
73
+ citations = Dir.glob("#{skills_root}/*/SKILL.md").each_with_object(Hash.new { |h, k| h[k] = [] }) do |path, acc|
74
+ skill = File.basename(File.dirname(path))
75
+ File.read(path).scan(%r{docs\.avohq\.io/#{version}/([A-Za-z0-9_/-]+)}).flatten.uniq.each do |page|
76
+ acc["#{File.basename(page)}.md"] << skill
77
+ end
78
+ end
79
+
80
+ puts "#{changed.size} pages changed between #{marker["commit"][0, 12]} (#{marker["date"]}) and #{head[0, 12]} (#{head_date}):", ""
81
+
82
+ stale = []
83
+ changed.sort_by(&:last).each do |status, page|
84
+ skills = citations[page]
85
+ stale.concat(skills)
86
+ puts " #{status} #{page.ljust(34)} #{skills.empty? ? "— not cited by any skill" : skills.join(", ")}"
87
+ end
88
+
89
+ puts "", "Skills to review: #{stale.uniq.sort.join(", ")}" if stale.any?
90
+ puts "", "Commits: git -C #{docs} log --oneline #{marker["commit"][0, 12]}..HEAD -- #{marker["docs_path"]}"
91
+ puts "Then: ruby lib/avo/skills/bin/docs_drift.rb --update"
@@ -0,0 +1,7 @@
1
+ {
2
+ "repo": "avo-hq/docs.avohq.io",
3
+ "docs_path": "docs/4.0",
4
+ "commit": "3350dbef7b2da94b410d6a678f9551e16901411e",
5
+ "date": "2026-07-28",
6
+ "note": "The docs commit these skills were last indexed from. Run ruby lib/avo/skills/bin/docs_drift.rb to see what changed since, and --update after a refresh."
7
+ }
data/lib/avo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Avo
2
- VERSION = "4.1.10" unless const_defined?(:VERSION)
2
+ VERSION = "4.1.12" unless const_defined?(:VERSION)
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: avo
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.10
4
+ version: 4.1.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adrian Marin
@@ -1123,6 +1123,8 @@ files:
1123
1123
  - lib/avo/skills/avo-troubleshoot/SKILL.md
1124
1124
  - lib/avo/skills/avo-update/SKILL.md
1125
1125
  - lib/avo/skills/bin/avo-skills-resolve
1126
+ - lib/avo/skills/bin/docs_drift.rb
1127
+ - lib/avo/skills/docs-index.json
1126
1128
  - lib/avo/skills/index.md
1127
1129
  - lib/avo/skills/package-map.md
1128
1130
  - lib/avo/table_row_options.rb