Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ jobs:
pnpm run lint
pnpm run build

# The parity contract. These read the pinned v3.2.4 bundle checked in at
# The parity contract. These read the pinned v3.2.5 bundle checked in at
# .shots/heroui-bundle.txt.gz, so they need no network and no Rust build --
# they read the Rust sources as text. Kept off the Rust jobs so a parity
# regression and a compile error report separately.
parity:
name: HeroUI v3.2.4 parity audits
name: HeroUI v3.2.5 parity audits
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
Expand All @@ -78,6 +78,11 @@ jobs:
with:
python-version: "3.12"

- name: Pinned input and metric reader regressions
run: |
python .shots/test_bundle.py
python .shots/design_audit.py --self-test

- name: Audit set
run: |
set -euo pipefail
Expand Down Expand Up @@ -385,4 +390,3 @@ jobs:
run: |
echo "$RESULTS"
echo "$RESULTS" | jq -e 'all(.[] | .result; . == "success" or . == "skipped")'

2 changes: 1 addition & 1 deletion .shots/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ running a driver.
and report unreadable inputs separately from legitimate absence.
- When changing a parser or evidence map, demonstrate a known-negative failure
as well as the passing repository result.
- Keep tagged inputs pinned to HeroUI v3.2.4 and inherited behavior evidence
- Keep tagged inputs pinned to HeroUI v3.2.5 and inherited behavior evidence
pinned to the versions in the root parity guide.
- Drivers must capture the GPUI window with `PrintWindow`, never the user's
screen. Prefer off-screen posted input; foreground capture is opt-in because it
Expand Down
8 changes: 5 additions & 3 deletions .shots/a11y_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,11 @@
# --------------------------------------------------------------------------
# (module, struct) -> the role, and the pinned upstream source that decides it.
#
# Every reference is a file under `web/node_modules`, at the versions
# `docs/agents/parity.md` pins: react-aria 3.51.0, react-aria-components 1.20.0,
# @heroui/react 3.2.4.
# References name files under `web/node_modules`. The original role review
# used react-aria 3.51.0, react-aria-components 1.20.0,
# and @heroui/react 3.2.4. The active v3.2.5 target and dependency versions
# are in `docs/agents/parity.md`; these role mappings still require the
# per-component inherited-behavior review during that migration.
# --------------------------------------------------------------------------
EXPOSES_A_ROLE = {
('button.rs', 'Button'):
Expand Down
2 changes: 1 addition & 1 deletion .shots/anatomy_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@

sys.stdout.reconfigure(encoding='utf-8', errors='replace')

CSS = os.path.join(os.environ.get('TEMP', '/tmp'), 'heroui-css')
from bundle import CSS_CACHE as CSS
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from component_source import list_modules, read_module, read_path
from bundle import css_cache as _css_cache
Expand Down
4 changes: 2 additions & 2 deletions .shots/anim_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@
from component_source import list_modules, read_module, read_path
from bundle import css_cache as _css_cache, resolve as _resolve_bundle

# The pinned v3.2.4 bundle. See .shots/bundle.py: reading upstream live would
# The pinned v3.2.5 bundle. See .shots/bundle.py: reading upstream live would
# measure this port against whatever HeroUI shipped most recently.
BUNDLE = _resolve_bundle()
SRC = 'crates/herogpui-components/src/'
THEME = 'crates/herogpui-theme/src/'
# The v3 stylesheets `design_audit.py --fetch` caches. Motion lives in the CSS,
# not the docs bundle, so the per-overlay checks read from here.
CACHE = os.path.join(os.environ.get('TEMP', '/tmp'), 'heroui-css')
from bundle import CSS_CACHE as CACHE
# An empty cache is not a clean motion report: without the stylesheets this
# audit measured every timing against nothing and called 22 of them wrong.
if not _css_cache():
Expand Down
3 changes: 2 additions & 1 deletion .shots/api_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from component_source import list_modules, read_module, read_path
from bundle import resolve as _resolve_bundle

# The pinned v3.2.4 bundle. See .shots/bundle.py: reading upstream live would
# The pinned v3.2.5 bundle. See .shots/bundle.py: reading upstream live would
# measure this port against whatever HeroUI shipped most recently.
BUNDLE = _resolve_bundle()
SRC = 'crates/herogpui-components/src/'
Expand Down Expand Up @@ -622,6 +622,7 @@ def dead_v2_aliases(aliases=None):
# monolithic root, whose builders can only be the parts' props.
'RangeCalendar.YearPickerGrid': ['RangeCalendar'],
'RangeCalendar.YearPickerTriggerHeading': ['RangeCalendar'],
'Select.ClearButton': ['SelectClearButton'],
'Select.Popover': ['Select'],
'Slider.Thumb': [],
'Table.Body': ['Table'],
Expand Down
2 changes: 1 addition & 1 deletion .shots/behaviour_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from bundle import resolve as _resolve_bundle
from design_audit import mask_literals, strip_cfg_test

# The pinned v3.2.4 bundle. See .shots/bundle.py: reading upstream live would
# The pinned v3.2.5 bundle. See .shots/bundle.py: reading upstream live would
# measure this port against whatever HeroUI shipped most recently.
BUNDLE = _resolve_bundle()
SRC = 'crates/herogpui-components/src/'
Expand Down
20 changes: 15 additions & 5 deletions .shots/bundle.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""The pinned HeroUI documentation bundle every prop and prose audit reads.

The repository ports HeroUI v3.2.4. `heroui.com/react/llms-full.txt` serves the
The repository ports HeroUI v3.2.5. `heroui.com/react/llms-full.txt` serves the
`v3` *branch*, not that tag, so reading it live measures the port against
whatever upstream shipped most recently. Nine audits did exactly that: the day
HeroUI publishes v3.3, they would quietly start reporting gaps against a
Expand All @@ -20,14 +20,15 @@
"""

import gzip
import hashlib
import io
import os
import re
import shutil
import sys
import tarfile

PINNED_RELEASE = 'v3.2.4'
PINNED_RELEASE = 'v3.2.5'

_HERE = os.path.dirname(os.path.abspath(__file__))
ARCHIVE = os.path.join(_HERE, 'heroui-bundle.txt.gz')
Expand Down Expand Up @@ -90,12 +91,21 @@ def read():
return io.open(resolve(), encoding='utf-8', errors='replace').read()


# The v3.2.4 component stylesheets, vendored the same way and for the same
# The v3.2.5 component stylesheets, vendored the same way and for the same
# reason: design_audit, anim_audit and anatomy_audit all read them, and an
# empty cache does not read as "no findings" in any of the three -- anim_audit
# reported 22 phantom motion mismatches against one.
CSS_ARCHIVE = os.path.join(_HERE, 'heroui-css-v3.2.4.tar.gz')
CSS_CACHE = os.path.join(os.environ.get('TEMP', '/tmp'), 'heroui-css')
CSS_ARCHIVE = os.path.join(_HERE, 'heroui-css-v3.2.5.tar.gz')
# Separate releases and archive revisions, including different worktrees.
# An existing unversioned cache must never satisfy a newly pinned contract.
if os.path.isfile(CSS_ARCHIVE):
with open(CSS_ARCHIVE, 'rb') as _archive:
_css_digest = hashlib.sha256(_archive.read()).hexdigest()[:16]
else:
_css_digest = 'missing'
CSS_CACHE = os.path.join(
os.environ.get('TEMP', '/tmp'), 'heroui-css-%s-%s' % (PINNED_RELEASE, _css_digest)
)


def css_cache():
Expand Down
37 changes: 28 additions & 9 deletions .shots/design_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
The prop and animation audits both read documentation, and neither says anything
about whether a control is the right *size*: `api_audit.py` was perfectly happy
with a button whose corner radius was a third of v3's. This reads the real
stylesheets from the v3.2.4 tag of the React repo, resolves the Tailwind
stylesheets from the v3.2.5 tag of the React repo, resolves the Tailwind
utilities through v3's own token scales, and compares the result with the
constants this port renders from.

Expand Down Expand Up @@ -32,11 +32,11 @@

sys.stdout.reconfigure(encoding='utf-8', errors='replace')

CACHE = os.path.join(os.environ.get('TEMP', '/tmp'), 'heroui-css')
COMPONENTS = ('https://raw.githubusercontent.com/heroui-inc/heroui/v3.2.4'
from bundle import CSS_CACHE as CACHE
COMPONENTS = ('https://raw.githubusercontent.com/heroui-inc/heroui/v3.2.5'
'/packages/styles/components/%s.css')
# The same stylesheets are vendored (47KB) so the audit needs no network and
# every run measures the same v3.2.4 tag. `--fetch` still refreshes upstream.
# The same stylesheets are vendored so the audit needs no network and
# every run measures the same v3.2.5 tag. `--fetch` still refreshes upstream.
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from component_source import list_modules, read_module, read_path
from bundle import css_cache as unpack
Expand Down Expand Up @@ -948,7 +948,7 @@ def swatch_picker_radius(_group):
('tabs', '.tabs__tab', 'text', 'Tabs tab text', SRC + 'tabs.rs',
'tabs_md_text', None),
('tabs', '.tabs__tab', 'radius', 'Tabs tab -> util::_radius', SRC + 'tabs.rs',
r'\.justify_center\(\)[\s\S]{0,40}?\.rounded\(crate::util::(\w+_radius)\(cx\)\)', helper_px),
r'\.map\(\|tab\| self\.align\.apply\(tab\)\)\s*\.rounded\(crate::util::(\w+_radius)\(cx\)\)', helper_px),
('tabs', '.tabs__panel', 'p', 'Tabs panel padding', SRC + 'tabs.rs',
r'`\.tabs__panel` is `w-full p-2`[\s\S]{0,240}?\.p\(px\((\d+(?:\.\d*)?)\.\)\)', None),
('tabs', '.tabs__list-container', 'radius', 'Tabs list container radius',
Expand Down Expand Up @@ -1121,7 +1121,7 @@ def swatch_picker_radius(_group):
('tag', '.tag--lg', 'radius', 'Tag Lg -> util::_radius', SRC + 'tag_group.rs',
r'Size::Lg => crate::util::(\w+_radius)', helper_px),
('tag', '.tag__remove-button', 'size', 'Tag remove button', SRC + 'tag_group.rs',
r'is `size-3`\.[\s\S]{0,40}?\.size\(px\((\d+(?:\.\d*)?)\.\)\)', None),
r'let mut remove_visual = div\(\)\s*\.size\(px\((\d+(?:\.\d*)?)\.\)\)', None),
# --- The three dialogs ------------------------------------------------
# v3's dialog is one padded box with unpadded parts, and the spacing between
# them comes from `+` rules rather than a gap. This port had a padded header,
Expand Down Expand Up @@ -1982,9 +1982,9 @@ def swatch_picker_radius(_group):


THEME_FILES = (
('variables.css', 'https://raw.githubusercontent.com/heroui-inc/heroui/v3.2.4'
('variables.css', 'https://raw.githubusercontent.com/heroui-inc/heroui/v3.2.5'
'/packages/styles/themes/default/variables.css'),
('shared_theme.css', 'https://raw.githubusercontent.com/heroui-inc/heroui/v3.2.4'
('shared_theme.css', 'https://raw.githubusercontent.com/heroui-inc/heroui/v3.2.5'
'/packages/styles/themes/shared/theme.css'),
)

Expand Down Expand Up @@ -4502,6 +4502,25 @@ def metrics_fixture(owner, values):
'a changed grouped fallback must be followed, so the metric check '
'sees the drift')

tag_pattern = next(row[5] for row in CHECKS if row[3] == 'Tag remove button')
tag_visual = 'let mut remove_visual = div().size(px(12.));'
tag_target = 'let mut close = div().size(px(24.));'
expect(re.search(tag_pattern, tag_target + tag_visual).group(1) == '12',
'Tag size must read the visual, not the expanded hit target')
expect(re.search(tag_pattern, tag_visual.replace('12.', '24.')).group(1) == '24',
'Tag visual size drift must remain visible to the metric comparison')
expect(re.search(tag_pattern, tag_target) is None,
'a missing Tag visual must not be satisfied by the hit target')

tab_radius_pattern = next(row[5] for row in CHECKS if row[3] == 'Tabs tab -> util::_radius')
tab_radius = '.map(|tab| self.align.apply(tab)).rounded(crate::util::control_radius(cx))'
expect(re.search(tab_radius_pattern, tab_radius).group(1) == 'control_radius',
'Tabs radius must remain readable for configurable alignment')
expect(re.search(tab_radius_pattern, tab_radius.replace('control_radius', 'soft_radius')).group(1) == 'soft_radius',
'Tabs radius drift must remain visible to the metric comparison')
expect(re.search(tab_radius_pattern, '.rounded(crate::util::control_radius(cx))') is None,
'a foreign radius must not satisfy the tab radius reader')

if failures:
for failure in failures:
print('! self-test: ' + failure)
Expand Down
6 changes: 6 additions & 0 deletions .shots/element_id_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@
'do share this focus scope -- a real gap, recorded here rather than '
'hidden, and fixable only by making the id required, which is a '
'public-API change of its own.',
('crates/herogpui-components/src/toast.rs', 'toast-region'):
'the fallback seed for `ToastViewport`\'s own id, which `.id(..)` '
'overrides; see the `modal` entry. Every other id in the viewport -- '
'pointer state, focus handle, hotkey interceptor, and each toast\'s '
'parts -- derives from it, so a named viewport moves its whole '
'subtree.',
}

# (path, part-of-the-line) -> why this id may still be built with `format!`.
Expand Down
2 changes: 1 addition & 1 deletion .shots/example_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
from bundle import resolve as _resolve_bundle
from gallery_pages import page_sources

# The pinned v3.2.4 bundle. See .shots/bundle.py: reading upstream live would
# The pinned v3.2.5 bundle. See .shots/bundle.py: reading upstream live would
# measure this port against whatever HeroUI shipped most recently.
BUNDLE = _resolve_bundle()
PAGES = page_sources()
Expand Down
2 changes: 1 addition & 1 deletion .shots/example_src.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from bundle import resolve as _resolve_bundle

# The pinned v3.2.4 bundle. See .shots/bundle.py: reading upstream live would
# The pinned v3.2.5 bundle. See .shots/bundle.py: reading upstream live would
# measure this port against whatever HeroUI shipped most recently.
BUNDLE = _resolve_bundle()

Expand Down
4 changes: 4 additions & 0 deletions .shots/extra_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,9 @@ def ban_scan():
'Dropdown.exiting': 'composition',
# v3 positions Toast.Provider through layout classes.
'Toast.inset': 'no-classname',
# Documented on the toast function as `toast.promise()`, not as a root
# Toast prop. The helper owns loading → in-place success/error.
'Toast.promise': 'documented-by-example',
# v3 composes `<Pagination.Summary>Page 1 of 10</Pagination.Summary>`.
'Pagination.summary': 'composition',
'Pagination.previous_icon': 'composition',
Expand Down Expand Up @@ -751,6 +754,7 @@ def ban_scan():
'Slider.thumb_names': 'composition',
'Select.indicator': 'composition',
'Select.value_content': 'composition',
'Select.clear_button': 'composition',
'Autocomplete.value_content': 'composition',
'ComboBox.value_content': 'composition',
'Checkbox.is_round': 'no-classname',
Expand Down
2 changes: 1 addition & 1 deletion .shots/gap_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from bundle import resolve as _resolve_bundle

# The pinned v3.2.4 bundle. See .shots/bundle.py: reading upstream live would
# The pinned v3.2.5 bundle. See .shots/bundle.py: reading upstream live would
# measure this port against whatever HeroUI shipped most recently.
BUNDLE = _resolve_bundle()
bundle = io.open(BUNDLE, encoding='utf-8', errors='replace').read()
Expand Down
Binary file modified .shots/heroui-bundle.txt.gz
Binary file not shown.
Binary file removed .shots/heroui-css-v3.2.4.tar.gz
Binary file not shown.
Binary file added .shots/heroui-css-v3.2.5.tar.gz
Binary file not shown.
5 changes: 4 additions & 1 deletion .shots/part_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from component_source import list_modules, read_module, read_path

CACHE = os.path.join(os.environ.get('TEMP', '/tmp'), 'heroui-css')
from bundle import CSS_CACHE as CACHE, css_cache

if not css_cache():
raise SystemExit("part_audit: pinned stylesheet archive is missing")
SRC = 'crates/herogpui-components/src/'
THEME = 'crates/herogpui-theme/src/'
CORE = 'crates/herogpui-core/src/'
Expand Down
2 changes: 1 addition & 1 deletion .shots/reason_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from bundle import resolve as _resolve_bundle

# The pinned v3.2.4 bundle. See .shots/bundle.py: reading upstream live would
# The pinned v3.2.5 bundle. See .shots/bundle.py: reading upstream live would
# measure this port against whatever HeroUI shipped most recently.
BUNDLE = _resolve_bundle()
bundle = io.open(BUNDLE, encoding='utf-8', errors='replace').read()
Expand Down
6 changes: 3 additions & 3 deletions .shots/reference_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -584,8 +584,8 @@ def main():

for key in ("docs_source", "api_source", "style_source"):
url = field(block, key) or ""
if "/blob/v3.2.4/" not in url:
errors.append(f"{page}: {key} is not pinned to v3.2.4")
if "/blob/v3.2.5/" not in url:
errors.append(f"{page}: {key} is not pinned to v3.2.5")

for page, count in seen_pages.items():
if count != 1:
Expand All @@ -604,7 +604,7 @@ def main():
**totals
)
)
print("contract : HeroUI v3.2.4, checked in; runtime network calls: 0")
print("contract : HeroUI v3.2.5, checked in; runtime network calls: 0")
if errors:
print("FAIL")
for error in errors:
Expand Down
7 changes: 5 additions & 2 deletions .shots/state_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,15 @@

sys.stdout.reconfigure(encoding='utf-8', errors='replace')

CSS = os.path.join(os.environ.get('TEMP', '/tmp'), 'heroui-css')
from bundle import CSS_CACHE as CSS, css_cache

if not css_cache():
raise SystemExit("state_audit: pinned stylesheet archive is missing")
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from component_source import list_modules, module_exists, read_module, read_path
from bundle import resolve as _resolve_bundle

# The pinned v3.2.4 bundle. See .shots/bundle.py: reading upstream live would
# The pinned v3.2.5 bundle. See .shots/bundle.py: reading upstream live would
# measure this port against whatever HeroUI shipped most recently.
BUNDLE = _resolve_bundle()
SRC = 'crates/herogpui-components/src/'
Expand Down
Loading
Loading