wabi 0.8.0 → 0.9.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 +4 -4
- data/CHANGELOG.md +46 -0
- data/lib/wabi/generators/add_generator.rb +4 -1
- data/lib/wabi/generators/update_generator.rb +67 -3
- data/lib/wabi/lockfile.rb +11 -0
- data/lib/wabi/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4427b2a777bf298e102d813c0a5ccb2b7919e2a76ed7bd3d20e56127671e5041
|
|
4
|
+
data.tar.gz: ddd81feca27e709970b46be2704a58939d10a8a92405d27438cbf546bf30b878
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e462360e2f326b745027ff02f126ca10a30c3456dfb5aec439b4f41e64548a39698c48274c145eb19ec3089cd654fb5139c426af7a0ec5f98d92e7dd2ff2ebfe
|
|
7
|
+
data.tar.gz: b2213c0ee4685e679df481349065e42a14c1112d9ea5be3866d7e2238e33c17cc35e310a7d76d44b0b7b49011b8a490316ded97ac4c8f921530e6d0a7de1f462
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,52 @@
|
|
|
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.9.0 - 2026-05-31
|
|
6
|
+
|
|
7
|
+
DX + polish, plus a full fix of the vertical Slider.
|
|
8
|
+
|
|
9
|
+
### Breaking
|
|
10
|
+
|
|
11
|
+
- **Slider `marks:` moved from `Slider` to `SliderControl`.** Marks now render
|
|
12
|
+
inside the control (the track's positioning context) so they align with the
|
|
13
|
+
track in both orientations. Update `Slider.new(marks: […])` →
|
|
14
|
+
`SliderControl.new(marks: […])` (and pass `orientation:` to `SliderControl`
|
|
15
|
+
for vertical). Marks were introduced in v0.8.
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
- **`wabi:update` three-way merge.** Locally-edited component files are now
|
|
20
|
+
3-way merged (base + local + new) via `git merge-file` instead of the
|
|
21
|
+
all-or-nothing prompt: non-conflicting registry changes auto-apply while your
|
|
22
|
+
edits are preserved; true conflicts are written with `<<<<<<<` markers. Falls
|
|
23
|
+
back to the y/n/d/q prompt when git is unavailable or the lockfile predates
|
|
24
|
+
this release. The lockfile now records `{ hash, content }` per file
|
|
25
|
+
(back-compat: legacy string-hash entries still load and use the fallback). A
|
|
26
|
+
data-safety guard never writes a file when `git merge-file` errors.
|
|
27
|
+
- **Combobox async error state.** New optional `ComboboxError` slot (hidden,
|
|
28
|
+
`aria-live`) — shown on async fetch failure (prior results kept), hidden on
|
|
29
|
+
the next successful fetch.
|
|
30
|
+
|
|
31
|
+
### Fixes
|
|
32
|
+
|
|
33
|
+
- **Vertical Slider now works end-to-end.** The v0.8 slider was effectively
|
|
34
|
+
horizontal-only; the vertical orientation didn't render usably. Fixed across
|
|
35
|
+
the board, each gated by orientation so horizontal is unchanged:
|
|
36
|
+
- `SliderControl` fills the column height in vertical (was collapsing to 0,
|
|
37
|
+
leaving the track 0px tall).
|
|
38
|
+
- Thumb centers on the cross-axis correctly per orientation (horizontal:
|
|
39
|
+
vertical-center; vertical: horizontal-center on the rail).
|
|
40
|
+
- `SliderRange` fill is `w-full` in vertical (was zero-width / invisible) with
|
|
41
|
+
Zag driving the height from the value.
|
|
42
|
+
- Marks render inside `SliderControl`, aligned with the track (below it for
|
|
43
|
+
horizontal, beside it spanning its height for vertical), with the tick +
|
|
44
|
+
label offset oriented accordingly.
|
|
45
|
+
|
|
46
|
+
### Deferred to v0.10
|
|
47
|
+
|
|
48
|
+
- Toast `@zag-js/toast` group machine (high-risk; failed twice).
|
|
49
|
+
- Phlex 2.4 Ruby 4 warnings (upstream).
|
|
50
|
+
|
|
5
51
|
## 0.8.0 - 2026-05-31
|
|
6
52
|
|
|
7
53
|
Focused high-value mix: one marquee feature, one self-contained feature, an
|
|
@@ -42,7 +42,10 @@ module Wabi
|
|
|
42
42
|
target = File.join(destination_root, file["path"])
|
|
43
43
|
FileUtils.mkdir_p(File.dirname(target))
|
|
44
44
|
File.write(target, file["content"])
|
|
45
|
-
files_map[file["path"]] =
|
|
45
|
+
files_map[file["path"]] = {
|
|
46
|
+
"hash" => Digest::SHA256.hexdigest(file["content"]),
|
|
47
|
+
"content" => file["content"],
|
|
48
|
+
}
|
|
46
49
|
say " create #{file["path"]}", :green
|
|
47
50
|
end
|
|
48
51
|
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
require "rails/generators"
|
|
4
4
|
require "digest"
|
|
5
5
|
require "json"
|
|
6
|
+
require "open3"
|
|
7
|
+
require "tempfile"
|
|
6
8
|
require "wabi/registry_client"
|
|
7
9
|
require "wabi/lockfile"
|
|
8
10
|
|
|
@@ -72,7 +74,7 @@ module Wabi
|
|
|
72
74
|
new_hash = Digest::SHA256.hexdigest(file["content"])
|
|
73
75
|
target = File.join(destination_root, path)
|
|
74
76
|
|
|
75
|
-
files_map[path] = new_hash
|
|
77
|
+
files_map[path] = { "hash" => new_hash, "content" => file["content"] }
|
|
76
78
|
|
|
77
79
|
if !File.exist?(target)
|
|
78
80
|
write_file(target, file["content"], reason: "create")
|
|
@@ -80,7 +82,7 @@ module Wabi
|
|
|
80
82
|
end
|
|
81
83
|
|
|
82
84
|
on_disk_hash = Digest::SHA256.hexdigest(File.read(target))
|
|
83
|
-
installed_hash = installed_map[path]
|
|
85
|
+
installed_hash = Wabi::Lockfile.file_entry(installed_map[path])[:hash]
|
|
84
86
|
|
|
85
87
|
if installed_hash.nil?
|
|
86
88
|
handle_conflict(path, target, file["content"], reason: "legacy lockfile has no per-file hash")
|
|
@@ -90,7 +92,12 @@ module Wabi
|
|
|
90
92
|
if on_disk_hash == installed_hash
|
|
91
93
|
write_file(target, file["content"], reason: "update")
|
|
92
94
|
else
|
|
93
|
-
|
|
95
|
+
base_content = Wabi::Lockfile.file_entry(installed_map[path])[:content]
|
|
96
|
+
if base_content && git_available? && !options[:force]
|
|
97
|
+
merge_file(path, target, base_content, file["content"])
|
|
98
|
+
else
|
|
99
|
+
handle_conflict(path, target, file["content"], reason: "edited locally")
|
|
100
|
+
end
|
|
94
101
|
end
|
|
95
102
|
end
|
|
96
103
|
|
|
@@ -150,6 +157,63 @@ module Wabi
|
|
|
150
157
|
ask(" (y)es / (n)o / (d)iff / (q)uit?", limited_to: %w[y n d q])
|
|
151
158
|
end
|
|
152
159
|
|
|
160
|
+
def merge_file(path, target, base_content, new_content)
|
|
161
|
+
if options[:dry_run]
|
|
162
|
+
say " would 3-way merge #{path}", :cyan
|
|
163
|
+
return
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
begin
|
|
167
|
+
merged, conflicts, err = three_way_merge(target, base_content, new_content)
|
|
168
|
+
rescue Errno::ENOENT, StandardError => e
|
|
169
|
+
# git vanished mid-run, or any other spawn/IO failure.
|
|
170
|
+
merged, conflicts, err = nil, 255, e.message
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# git merge-file returns 255 (an error, NOT a conflict count) on
|
|
174
|
+
# failure. Treat that — or empty output when we expected content — as a
|
|
175
|
+
# failure and DO NOT write: blanking the user's edited file would be
|
|
176
|
+
# irreversible data loss. Leave the file untouched; the user can re-run.
|
|
177
|
+
if conflicts == 255 || conflicts.negative? || (merged.to_s.empty? && !new_content.empty?)
|
|
178
|
+
detail = err.to_s.strip.empty? ? "" : ": #{err.to_s.strip.lines.first&.chomp}"
|
|
179
|
+
say " error #{path} (git merge-file failed, file unchanged#{detail})", :red
|
|
180
|
+
return
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
File.write(target, merged)
|
|
184
|
+
if conflicts.zero?
|
|
185
|
+
say " merged #{path}", :green
|
|
186
|
+
else
|
|
187
|
+
# git caps the exit code at 127, so report 127+ rather than lying.
|
|
188
|
+
count = conflicts >= 127 ? "127+ conflicts" : "#{conflicts} conflict#{'s' if conflicts != 1}"
|
|
189
|
+
say " merged #{path} (#{count} — resolve the <<<<<<< markers)", :yellow
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def git_available?
|
|
194
|
+
return @git_available unless @git_available.nil?
|
|
195
|
+
@git_available = system("git", "--version", out: File::NULL, err: File::NULL) || false
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# 3-way merge via `git merge-file`. Arg order is current/base/other =
|
|
199
|
+
# local/base/new (DO NOT reorder). Returns [merged_string, status, stderr]:
|
|
200
|
+
# status 0 = clean, 1..127 = number of conflicts (git caps at 127),
|
|
201
|
+
# 255 = error. stderr carries git's diagnostic on error.
|
|
202
|
+
def three_way_merge(local_path, base_content, new_content)
|
|
203
|
+
Tempfile.create("wabi-base") do |base_f|
|
|
204
|
+
Tempfile.create("wabi-new") do |new_f|
|
|
205
|
+
base_f.write(base_content); base_f.flush
|
|
206
|
+
new_f.write(new_content); new_f.flush
|
|
207
|
+
merged, err, status = Open3.capture3(
|
|
208
|
+
"git", "merge-file", "-p",
|
|
209
|
+
"-L", "local (your edits)", "-L", "base (original)", "-L", "new (registry)",
|
|
210
|
+
local_path, base_f.path, new_f.path
|
|
211
|
+
)
|
|
212
|
+
[merged, status.exitstatus, err]
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
153
217
|
def print_diff(target, new_content)
|
|
154
218
|
on_disk = File.exist?(target) ? File.read(target) : ""
|
|
155
219
|
on_disk_lines = on_disk.split("\n", -1)
|
data/lib/wabi/lockfile.rb
CHANGED
|
@@ -27,6 +27,17 @@ module Wabi
|
|
|
27
27
|
@components = data["components"] || {}
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
# Normalizes a per-file lockfile entry to { hash:, content: }, tolerating
|
|
31
|
+
# both the v0.9 object shape ({ "hash" =>, "content" => }) and the legacy
|
|
32
|
+
# string-hash shape (content: nil → caller falls back to the prompt).
|
|
33
|
+
def self.file_entry(raw)
|
|
34
|
+
case raw
|
|
35
|
+
when String then { hash: raw, content: nil }
|
|
36
|
+
when Hash then { hash: raw["hash"], content: raw["content"] }
|
|
37
|
+
else { hash: nil, content: nil }
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
30
41
|
def record(name, version:, hash:, files: nil, js_dependencies: nil)
|
|
31
42
|
entry = { "version" => version, "hash" => hash }
|
|
32
43
|
entry["files"] = files if files
|
data/lib/wabi/version.rb
CHANGED