wabi 0.3.0 → 0.5.0

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: abcbfe9f2785a854e687d5f992d6ceb32c4e4fb67ba0d081416ff4bd9b383988
4
- data.tar.gz: 6706563e1af726d59eafc0dbee7020a1fe565ed51cde9846020dcd74239b97e3
3
+ metadata.gz: 0660623d8f6e54a685e9793a3e1ca16e9ac56fcf7150e061c0e80835b8fbe9bd
4
+ data.tar.gz: ecbbce636fe38adf58c7931c208dbbe7000cc6b737ce516c286396c483f8e2e9
5
5
  SHA512:
6
- metadata.gz: a367a2820c7e1276ce580cae7f577fca70d0f2f2c2f211a222f97dffc8920b1d4bbdfd3745c3ac76a921e654b74cf958b465d7fcd3501bbedfbe8aa474b8a387
7
- data.tar.gz: c1de47b6186629b44e908fb3a364a564605a5b34c3551307057909fec013ae4e71b2c0a12f42e69d714f88a94a1f1ef38be755e4c1206a577edf668e469317e4
6
+ metadata.gz: 7c197e08b3f8cdea3f5587fc265495af390b12a185a030daf7add62fcf17dafd156d7d0466c39f0c943fff8400b486b46a09ce20a85c34fa89bd463056897ff3
7
+ data.tar.gz: f8f0d96bad57dc8889221364e06e594bec7f8762ad1373715a308f5a312751e3335347d9b54895dae4c6c29da441900220e318e9d389c65bbd65295efd4eb924
data/CHANGELOG.md CHANGED
@@ -2,6 +2,108 @@
2
2
 
3
3
  All notable changes to Wabi land here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
4
4
 
5
+ ## 0.5.0 - 2026-05-27
6
+
7
+ ### Breaking
8
+
9
+ - **Overlays portal to `document.body` by default**. Dialog, Drawer,
10
+ Tooltip, Popover, DropdownMenu, and Select all move their content (and
11
+ backdrop/positioner where applicable) to `<body>` on connect. Pass
12
+ `portal: false` to keep v0.4 in-tree behavior. The shared
13
+ `WabiPortalRegistry` JS module is now part of every overlay's wabi:add
14
+ install — it lands at
15
+ `app/javascript/controllers/wabi/_shared/portal_registry.js`.
16
+
17
+ ### Features
18
+
19
+ - `wabi:update` generator. Per-component diff-aware updates with
20
+ per-file conflict detection. Flags: `--force`, `--dry-run`. Reads
21
+ and writes per-file SHA256 hashes in `wabi.lock.json` (additive
22
+ schema; legacy lockfiles fall back to prompting on every file).
23
+ - Real portal pattern for the 6 overlays. Resolves the v0.1 carryover
24
+ documented in `docs/V01-CARRYOVER.md` (#3). Overlays inside
25
+ transformed/scrollable ancestors now position relative to the
26
+ viewport.
27
+
28
+ ### Docs
29
+
30
+ - `+esm` jsdelivr footnote added to 5 component pages
31
+ (checkbox/select/switch/dialog/tabs) for parity with the other 5
32
+ Zag-backed components.
33
+
34
+ ### Deferred to v0.6
35
+
36
+ - Toast `@zag-js/toast` group machine. Attempted in v0.5 but reverted — the group machine's interaction with Phlex+Stimulus+Turbo caused an infinite render loop. v0.4 vanilla setTimeout controller restored; group features (max/gap/pause-on-group-hover/swipe) targeted for v0.6 with a different implementation strategy.
37
+ - Forms wave (RadioGroup/Toggle/ToggleGroup/Slider/Combobox/Command/Form).
38
+ - Nav wave (Sheet/ContextMenu/Pagination/NavigationMenu).
39
+ - Data wave (Calendar/DatePicker/DataTable).
40
+ - Three-way merge in `wabi:update`.
41
+ - Multi-level DropdownMenu nesting (sub-inside-sub).
42
+ - Phlex 2.4 Ruby 4 warnings (upstream).
43
+
44
+ ## [0.4.0] — 2026-05-27
45
+
46
+ Sprint 8 — docs completeness + theme polish. The docs site gains the
47
+ three-column shell (grouped sidebar, content, right-side TOC) on every
48
+ `/docs/*` route, a Pagefind-powered search, and detail pages for the
49
+ remaining 16 components — every component in the registry now has its
50
+ own page. Theming gains a tinted secondary/accent per color palette,
51
+ a quick sun/moon mode toggle in the header, and a swap of `slate` +
52
+ `zinc` for `yellow` + `orange`.
53
+
54
+ ### Added
55
+
56
+ - **`Components::Site::Sidebar` + `Sidebar::Group` + `Sidebar::Link`** — Grouped left navigation for `/docs/*`. 7 groups (Getting Started / Forms / Layout & Display / Overlays / Menus / Navigation / Feedback) listing all 20 components + the prose pages. Active link highlighted via `current_path:` kwarg.
57
+ - **`Components::Site::TableOfContents` + `site--toc` Stimulus controller** — Right-side TOC on `chrome: :full` pages. Empty `<aside>` filled client-side by scanning `main h2[id], main h3[id]`. Tracks the active section via `IntersectionObserver`.
58
+ - **`Components::Site::SidebarToggle` + `site--sidebar` controller** — Hamburger button visible below `lg` breakpoint; opens the sidebar as a fixed overlay with scroll lock + Escape-to-close.
59
+ - **`Components::Site::SearchBox` + `site--search` controller** — Header search input that lazy-loads PagefindUI as a classic script, mounts it on the input, and renders results as an absolute-positioned dropdown styled with Wabi popover tokens.
60
+ - **`Components::Site::ModeToggle`** — Sun/moon icon button next to the ThemePicker. One-click dark/light flip via the existing `wabi--theme#toggleMode` action.
61
+ - **`Wabi::Docs::Indexer` + `wabi:docs:index` rake task** — Crawls 26 docs routes via `Rack::Test` (with `HTTP_HOST=localhost` + Chrome UA to satisfy `ActionDispatch::HostAuthorization` and `allow_browser :modern`), writes the response HTML to files mirroring the URL structure, then shells out to `npx pagefind` to build the static index. Index lives at `docs/public/pagefind/` and ships in the repo — deploys stay Node-free.
62
+ - **`docs/spec/requests/docs_smoke_spec.rb`** — Request specs covering every documented route (3 chrome modes: bare / sidebar_only / full) and the SearchBox markup on non-bare routes.
63
+ - **16 new detail pages** under `/docs/components/`: checkbox, input, label, select, switch, textarea, alert, avatar, badge, card, separator, drawer, popover, tooltip, accordion, toast. Each follows a single canonical anatomy: Installation / Example (ComponentPreview) / Source / Accessibility.
64
+ - **Two new theme palettes**: `yellow` (vibrant amber primary) and `orange`. Both follow the standard light + dark structure.
65
+ - **`Site::Layout(chrome:)` kwarg** — `:bare` (default, no sidebar/TOC), `:sidebar_only`, `:full`. Drives where the sidebar, TOC, and SearchBox render. 11 views updated to pass the appropriate value.
66
+ - **`ROADMAP` updates** + **README "Working on docs" section** documenting the Pagefind workflow.
67
+
68
+ ### Changed
69
+
70
+ - **Theme palettes**: `slate` and `zinc` replaced with `yellow` and `orange`. Neutral baselines stay covered by `default` and `stone`.
71
+ - **Theme tinting**: the 6 color palettes (rose, blue, green, violet, yellow, orange) now redefine `--secondary` and `--accent` as pale tints of their primary hue (lightness ~92-96% light / ~18% dark) with foreground colors at the same hue darkened/lightened for AA contrast. Default and Stone stay neutral on purpose. `--muted` stays neutral across all themes.
72
+ - **Theme dark-mode propagation**: each theme's dark block now also matches `[data-mode="dark"] [data-theme="X"]:not([data-mode="light"])`, so dark mode cascades into nested `data-theme` cards (e.g. the `/docs/themes` palette grid).
73
+ - **Button `:outline` appearance**: `border-input` (neutral) replaced with `border-primary` + `text-primary`. Outline buttons now adopt the active theme color on their stroke and label.
74
+ - **`Site::ComponentPreview`**: replaced the ad-hoc `site--preview-tabs` Stimulus controller with the real `Wabi::UI::Tabs` (dogfooding). Switcher uses an underline pattern — active trigger gets a 3px primary-colored bottom border + bold primary-color text.
75
+ - **`Site::CodeBlock` copy button**: word "Copy" replaced with a 14px clipboard SVG inside a 28×28 button. On copy: swap to checkmark SVG for 1.2s. `pre` reserves the button's column with `pr-12` so long code lines never tuck under the button while scrolling.
76
+ - **Components index**: every component links to its detail page. The `DETAILED` constant gate was dropped from `ComponentsController#show`; the only guard is membership in `ALL`.
77
+ - **Site::Layout header**: sticky with `z-30`, gains the SearchBox + ModeToggle on `chrome != :bare` routes.
78
+
79
+ ### Fixed
80
+
81
+ - **Tailwind 4 migration — explicit `border` width on every `border-input` element.** TW3 implicitly set `border-width: 1px` on any element with a `border-{color}` class; TW4 removed that default. Components that only declared `border-input` rendered with width 0 — no visible outline. Affected: `select_trigger`, `select_content`, `dropdown_menu_content`, `dropdown_menu_sub_content`, `drawer_content` (4 side variants), `dialog_content`, `popover_content`, `toast`.
82
+ - **Tailwind 4 migration — cursor: pointer on interactive elements.** TW4 Preflight removed the legacy default on `<button>`. Added a CSS rule under `_shared.css` (`@layer base`) restoring `cursor: pointer` for `button`, `[role="button"/"switch"/"checkbox"/"tab"/"option"/"menuitem"/"menuitemradio"/"menuitemcheckbox"]`, plus `cursor: not-allowed` for `[disabled]` and `[aria-disabled="true"]`.
83
+ - **`Wabi::UI::TabsTrigger` active state**: switched all `data-[state=active]:*` variants to `aria-selected:*`. Zag.js's tabs API emits `aria-selected="true"` + `data-selected=""`, NOT `data-state="active"` — so the active styling never fired. Affected `TabsTrigger` base tokens (both light and dark mode active styles).
84
+ - **`Avatar` image/fallback overlap**: `AvatarImage` is now `absolute inset-0 object-cover` so it stacks over `AvatarFallback`. Previously both rendered as flow-positioned siblings inside the flex Avatar container.
85
+ - **`Toast` sticky mode**: `wabi--toast` controller treats `durationMsValue <= 0` as "no timer" — toasts render persistently until manually dismissed. The Toast detail-page preview now uses this so the 3 appearances stay visible.
86
+ - **`SidebarToggle` Stimulus wiring**: button declared `data-action` but no element declared `data-controller="site--sidebar"`, so the controller never instantiated and the hamburger didn't open the sidebar. Added `data-controller` to the button itself.
87
+ - **TOC anchor jumps**: sticky 56px header was tapping over scrolled-to headings. Added `scroll-margin-top: 5rem` to `main :is(h1, h2, h3, h4, h5, h6)[id]`.
88
+ - **SearchBox PagefindUI loader**: PagefindUI ships as classic IIFE scripts (sets `window.PagefindUI`), not ES modules. The original `await import("/pagefind/pagefind-ui.js")` returned an empty module. Rewrote the loader to inject a `<script>` tag and await its `load` event.
89
+ - **Search result URLs**: Indexer wrote `docs_components_button.html` (flattened with `_`), and Pagefind built result URLs from those filenames. Now writes files mirroring the URL structure (`docs/components/button.html` with `mkdir_p`) and PagefindUI's `processResult` callback strips `.html` from the displayed URL — clicks navigate to `/docs/components/button` directly.
90
+ - **Select example wiring**: `items:` array was missing on the `/docs/components/select` example, so Zag's collection was empty and clicks on SelectItem elements were no-ops. Added a `FRUITS` array passed via `items:` and iterated for the SelectItem children.
91
+
92
+ ### Spec totals
93
+
94
+ - Registry: **125/125**.
95
+ - Gem: **66/66**.
96
+ - Docs (request + component specs): **40/40** — every smoke route (`:bare` / `:sidebar_only` / `:full`) returns 200 with the expected sidebar/TOC/search markup; Indexer crawl writes one file per route, mirroring URL structure with `index.html` at root.
97
+
98
+ ### Known v0.4 deferrals → v0.5
99
+
100
+ - `@zag-js/toast` group machine — Wabi v0.4 Toast still uses a vanilla setTimeout-based controller. v0.5 will adopt the real Zag toast group machine for cross-toast coordination (max visible, stacking, etc.).
101
+ - `wabi:update` generator — Pulling registry updates back into a host app is still manual (re-run `wabi:add <name>`). v0.5 will add a generator that diffs registry vs. host and applies changes.
102
+ - Real portal pattern — Overlay components (Dialog, Drawer, Popover, Tooltip) currently render in-tree. A `<dialog>`-element-based or `Components::UI::Portal`-based pattern is queued for v0.5.
103
+ - Backfill `bin/importmap pin` footnote on the 5 pre-existing Zag pages (checkbox, select, switch, dialog, tabs). v0.4 added the warning to the new overlay/accordion pages; sweep across all pages happens in v0.5 docs polish.
104
+
105
+ ---
106
+
5
107
  ## [0.3.0] — 2026-05-26
6
108
 
7
109
  Sprint 7 (docs site polish). The docs site stops being a one-page kitchen
data/README.md CHANGED
@@ -4,15 +4,15 @@
4
4
 
5
5
  Wabi is an open-source UI component library for **Ruby on Rails 8**, built on **Phlex + Tailwind 4 + Stimulus + Hotwire**. Inspired by shadcn/ui, components are *copied* into your app — you own the code, customize freely, no upstream API to drift away from.
6
6
 
7
- 🎉 **Status:** v0.3.0 alpha. 20 components, 8 theme palettes, WCAG-AA targeted. Not yet on RubyGems.
7
+ 🎉 **Status:** v0.4.0 alpha — [available on RubyGems](https://rubygems.org/gems/wabi). 20 components, 8 theme palettes, WCAG-AA targeted, full docs site at the GitHub repo's `docs/` Rails app.
8
8
 
9
9
  ---
10
10
 
11
11
  ## Quick start
12
12
 
13
13
  ```bash
14
- # 1. Add the gem (path-based for now; RubyGems push pending)
15
- bundle add wabi --git https://github.com/wabikit/wabi --glob 'gem/*'
14
+ # 1. Add the gem
15
+ bundle add wabi
16
16
 
17
17
  # 2. Run the installer (copies tokens.css + theme controller + lockfile)
18
18
  bin/rails g wabi:install
@@ -153,15 +153,35 @@ Then visit:
153
153
 
154
154
  ---
155
155
 
156
+ ## Working on docs
157
+
158
+ The docs site is a Rails app under `docs/`. Local dev: `bin/dev` from
159
+ repo root. The static search index lives at `docs/public/pagefind/`
160
+ and is regenerated by a Rake task that crawls every docs route via
161
+ `Rack::Test`, dumps HTML to disk, then runs `npx pagefind` on it.
162
+
163
+ ```bash
164
+ cd docs && bin/rails wabi:docs:index
165
+ ```
166
+
167
+ Run this after touching any content under `docs/app/views/pages/` or
168
+ adding new component detail pages, then commit `docs/public/pagefind/`.
169
+ Requires Node 20+ in PATH (Pagefind is fetched via `npx` on demand).
170
+
171
+ ---
172
+
156
173
  ## Roadmap
157
174
 
158
175
  | Version | Target | Status |
159
176
  |---|---|---|
160
177
  | v0.1 | 20 components | ✅ shipped 2026-05-26 |
161
178
  | v0.2 | 8 themes + theme picker | ✅ shipped 2026-05-26 |
162
- | v0.3 | Real docs site (marketing + components index + 4 detailed pages + prose) | ✅ shipped 2026-05-26 |
163
- | v0.4 | Detailed pages for remaining 16 components; Pagefind search; sidebar nav | planned |
179
+ | v0.3 | Real docs site + RubyGems publish | ✅ shipped 2026-05-26 |
180
+ | v0.4 | Detailed pages for all 20 components; Pagefind search; sidebar nav; theme polish | shipped 2026-05-27 |
164
181
  | v0.5 | `@zag-js/toast` group machine; real portal pattern; `wabi:update` generator | planned |
182
+ | v0.6 | Forms expansion: RadioGroup, Toggle, ToggleGroup, Slider, Combobox, Command, Form | planned |
183
+ | v0.7 | Navigation & layout: Sheet, ContextMenu, Pagination, NavigationMenu | planned |
184
+ | v0.8 | Data + dates + ecosystem: Calendar, DatePicker, DataTable, Blocks, community registry | planned |
165
185
  | v1.0 | API stability; external a11y audit | 2027-04 target |
166
186
 
167
187
  See [ROADMAP.md](./ROADMAP.md) for the long-term view and [CHANGELOG.md](./CHANGELOG.md) for the per-release detail.
@@ -37,17 +37,20 @@ module Wabi
37
37
 
38
38
  Array(data["registry_dependencies"]).each { |dep| install_component(dep) }
39
39
 
40
+ files_map = {}
40
41
  data["files"].each do |file|
41
42
  target = File.join(destination_root, file["path"])
42
43
  FileUtils.mkdir_p(File.dirname(target))
43
44
  File.write(target, file["content"])
45
+ files_map[file["path"]] = Digest::SHA256.hexdigest(file["content"])
44
46
  say " create #{file["path"]}", :green
45
47
  end
46
48
 
47
49
  (data["js_dependencies"] || {}).each { |pkg, ver| @js_deps_to_pin[pkg] = ver }
48
50
 
49
51
  hash = Digest::SHA256.hexdigest(JSON.generate(data["files"]))
50
- lockfile.record(name, version: data["version"], hash: hash)
52
+ lockfile.record(name, version: data["version"], hash: hash, files: files_map,
53
+ js_dependencies: data["js_dependencies"])
51
54
  end
52
55
 
53
56
  def print_js_pin_instructions
@@ -0,0 +1,184 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "digest"
5
+ require "json"
6
+ require "wabi/registry_client"
7
+ require "wabi/lockfile"
8
+
9
+ module Wabi
10
+ module Generators
11
+ class UpdateGenerator < Rails::Generators::Base
12
+ argument :components, type: :array, default: [],
13
+ banner: "[component_name ...]"
14
+
15
+ class_option :force, type: :boolean, default: false,
16
+ desc: "Overwrite locally-edited files without prompting."
17
+ class_option :dry_run, type: :boolean, default: false,
18
+ desc: "Report planned changes, write nothing."
19
+
20
+ desc "Update installed Wabi components to the latest registry versions."
21
+
22
+ class UpdateAborted < StandardError; end
23
+
24
+ def update_components
25
+ names = target_names
26
+ if names.empty?
27
+ say " no components to update", :yellow
28
+ return
29
+ end
30
+ names.each { |name| update_component(name) }
31
+ lockfile.save unless options[:dry_run]
32
+ rescue UpdateAborted => e
33
+ say " aborted: #{e.message}", :red
34
+ end
35
+
36
+ private
37
+
38
+ def target_names
39
+ components.any? ? components : lockfile.components.keys
40
+ end
41
+
42
+ def lockfile
43
+ @lockfile ||= Wabi::Lockfile.load(File.join(destination_root, "config/wabi.lock.json"))
44
+ end
45
+
46
+ def client
47
+ @client ||= Wabi::RegistryClient.new(base_url: lockfile.registry)
48
+ end
49
+
50
+ def update_component(name)
51
+ unless lockfile.components.key?(name)
52
+ say " not installed: #{name} (use `wabi:add #{name}`)", :yellow
53
+ return
54
+ end
55
+
56
+ installed_version = lockfile.components[name]["version"]
57
+ data = client.fetch(name)
58
+ remote_version = data["version"]
59
+
60
+ if Gem::Version.new(remote_version) <= Gem::Version.new(installed_version)
61
+ say " up-to-date #{name} (#{installed_version})", :cyan
62
+ return
63
+ end
64
+
65
+ say " updating #{name} (#{installed_version} -> #{remote_version})", :green
66
+
67
+ files_map = {}
68
+ installed_map = lockfile.components[name]["files"] || {}
69
+
70
+ data["files"].each do |file|
71
+ path = file["path"]
72
+ new_hash = Digest::SHA256.hexdigest(file["content"])
73
+ target = File.join(destination_root, path)
74
+
75
+ files_map[path] = new_hash
76
+
77
+ if !File.exist?(target)
78
+ write_file(target, file["content"], reason: "create")
79
+ next
80
+ end
81
+
82
+ on_disk_hash = Digest::SHA256.hexdigest(File.read(target))
83
+ installed_hash = installed_map[path]
84
+
85
+ if installed_hash.nil?
86
+ handle_conflict(path, target, file["content"], reason: "legacy lockfile has no per-file hash")
87
+ next
88
+ end
89
+
90
+ if on_disk_hash == installed_hash
91
+ write_file(target, file["content"], reason: "update")
92
+ else
93
+ handle_conflict(path, target, file["content"], reason: "edited locally")
94
+ end
95
+ end
96
+
97
+ print_js_deps_diff(name, data["js_dependencies"])
98
+
99
+ lockfile.record(
100
+ name,
101
+ version: remote_version,
102
+ hash: Digest::SHA256.hexdigest(JSON.generate(data["files"])),
103
+ files: files_map,
104
+ js_dependencies: data["js_dependencies"],
105
+ )
106
+ end
107
+
108
+ def write_file(target, content, reason:)
109
+ if options[:dry_run]
110
+ say " would write #{target} (#{reason})", :cyan
111
+ return
112
+ end
113
+ FileUtils.mkdir_p(File.dirname(target))
114
+ File.write(target, content)
115
+ say " #{reason.ljust(10)} #{target}", :green
116
+ end
117
+
118
+ def handle_conflict(path, target, new_content, reason:)
119
+ if options[:force]
120
+ write_file(target, new_content, reason: "force-overwrite")
121
+ return
122
+ end
123
+ if options[:dry_run]
124
+ say " CONFLICT #{path} (#{reason}) - would prompt", :yellow
125
+ return
126
+ end
127
+
128
+ loop do
129
+ say ""
130
+ say " conflict #{path}", :yellow
131
+ say " #{reason}. Overwrite with new version?", :yellow
132
+ answer = prompt_conflict(path)
133
+ case answer
134
+ when "y"
135
+ write_file(target, new_content, reason: "overwrite")
136
+ return
137
+ when "n"
138
+ say " skipped #{path}", :cyan
139
+ return
140
+ when "d"
141
+ print_diff(target, new_content)
142
+ next
143
+ when "q"
144
+ raise UpdateAborted, "user aborted update"
145
+ end
146
+ end
147
+ end
148
+
149
+ def prompt_conflict(_path)
150
+ ask(" (y)es / (n)o / (d)iff / (q)uit?", limited_to: %w[y n d q])
151
+ end
152
+
153
+ def print_diff(target, new_content)
154
+ on_disk = File.exist?(target) ? File.read(target) : ""
155
+ on_disk_lines = on_disk.split("\n", -1)
156
+ new_lines = new_content.split("\n", -1)
157
+ say ""
158
+ say " --- on-disk", :red
159
+ on_disk_lines.each { |l| say " - #{l}", :red }
160
+ say " +++ new", :green
161
+ new_lines.each { |l| say " + #{l}", :green }
162
+ say ""
163
+ end
164
+
165
+ def print_js_deps_diff(name, new_deps)
166
+ new_deps ||= {}
167
+ old_deps = lockfile.components.dig(name, "js_dependencies") || {}
168
+
169
+ added = new_deps.reject { |pkg, _| old_deps.key?(pkg) }
170
+ changed = new_deps.select { |pkg, ver| old_deps.key?(pkg) && old_deps[pkg] != ver }
171
+
172
+ return if added.empty? && changed.empty?
173
+
174
+ say ""
175
+ say " JS pin changes for #{name}:", :yellow
176
+ (added.merge(changed)).each do |pkg, version|
177
+ v = version.to_s.sub(/\A[~^]/, "")
178
+ v = "1.0.0" if v.empty?
179
+ say %( pin "#{pkg}", to: "https://cdn.jsdelivr.net/npm/#{pkg}@#{v}/+esm")
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
data/lib/wabi/lockfile.rb CHANGED
@@ -27,8 +27,11 @@ module Wabi
27
27
  @components = data["components"] || {}
28
28
  end
29
29
 
30
- def record(name, version:, hash:)
31
- @components[name] = { "version" => version, "hash" => hash }
30
+ def record(name, version:, hash:, files: nil, js_dependencies: nil)
31
+ entry = { "version" => version, "hash" => hash }
32
+ entry["files"] = files if files
33
+ entry["js_dependencies"] = js_dependencies if js_dependencies
34
+ @components[name] = entry
32
35
  end
33
36
 
34
37
  def save
data/lib/wabi/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Wabi
4
- VERSION = "0.3.0"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/wabi.rb CHANGED
@@ -16,6 +16,7 @@ end
16
16
  if defined?(Rails::Generators)
17
17
  require_relative "wabi/generators/install_generator"
18
18
  require_relative "wabi/generators/add_generator"
19
+ require_relative "wabi/generators/update_generator"
19
20
  require_relative "wabi/generators/list_generator"
20
21
  require_relative "wabi/generators/registry_generator"
21
22
  require_relative "wabi/generators/theme_generator"
data/templates/tokens.css CHANGED
@@ -9,6 +9,30 @@
9
9
 
10
10
  @custom-variant dark (&:where([data-mode="dark"], [data-mode="dark"] *));
11
11
 
12
+ /* Tailwind 4 Preflight removed the legacy `cursor: pointer` on <button>.
13
+ * Wabi's stance: any element that's interactive (button, role=button,
14
+ * Zag-machine triggers like role=switch / role=tab / role=option /
15
+ * role=menuitem*) should signal that on hover. Disabled / aria-disabled
16
+ * elements opt out — their pointer-events are suppressed elsewhere but
17
+ * we also want the cursor to reflect "not clickable" for clarity. */
18
+ @layer base {
19
+ button:not(:disabled):not([aria-disabled="true"]),
20
+ [role="button"]:not([aria-disabled="true"]),
21
+ [role="switch"]:not([aria-disabled="true"]),
22
+ [role="checkbox"]:not([aria-disabled="true"]),
23
+ [role="tab"]:not([aria-disabled="true"]),
24
+ [role="option"]:not([aria-disabled="true"]),
25
+ [role="menuitem"]:not([aria-disabled="true"]),
26
+ [role="menuitemradio"]:not([aria-disabled="true"]),
27
+ [role="menuitemcheckbox"]:not([aria-disabled="true"]) {
28
+ cursor: pointer;
29
+ }
30
+ button[disabled],
31
+ [aria-disabled="true"] {
32
+ cursor: not-allowed;
33
+ }
34
+ }
35
+
12
36
  @theme inline {
13
37
  --color-background: hsl(var(--background));
14
38
  --color-foreground: hsl(var(--foreground));
@@ -64,7 +88,8 @@
64
88
  --popover-foreground: 240 10% 3.9%;
65
89
  }
66
90
 
67
- [data-theme="default"][data-mode="dark"] {
91
+ [data-theme="default"][data-mode="dark"],
92
+ [data-mode="dark"] [data-theme="default"]:not([data-mode="light"]) {
68
93
  --background: 240 10% 3.9%;
69
94
  --foreground: 0 0% 98%;
70
95
  --primary: 0 0% 98%;
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wabi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oscar Ortega
@@ -126,6 +126,7 @@ files:
126
126
  - lib/wabi/generators/list_generator.rb
127
127
  - lib/wabi/generators/registry_generator.rb
128
128
  - lib/wabi/generators/theme_generator.rb
129
+ - lib/wabi/generators/update_generator.rb
129
130
  - lib/wabi/lockfile.rb
130
131
  - lib/wabi/registry_client.rb
131
132
  - lib/wabi/turbo_stream_extensions.rb