-
Notifications
You must be signed in to change notification settings - Fork 0
fix: comprehensive audit — core correctness, 30-100x smaller PNGs, react stability, packaging & CI fixes #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| --- | ||
| "@react-pixel-ui/core": minor | ||
| --- | ||
|
|
||
| Core engine overhaul: real PNG compression, full CSS color support, gradient correctness, and graceful degradation. | ||
|
|
||
| **Performance** | ||
|
|
||
| - The PNG encoder now uses a built-in dependency-free DEFLATE (fixed Huffman + LZ77) instead of uncompressed stored blocks — generated data URLs are **30–100× smaller** (e.g. a 600×400 card went from ~78 KB to ~2.4 KB), with a stored-block fallback for incompressible data. | ||
| - `generatePixelArt` accepts `options.want: 'styles' | 'composite' | 'both'` so consumers skip generating the PNG they discard (~2× faster for gradient cases). Default `'both'` keeps backward compatibility. | ||
| - `generatePixelArt` results are cached in a small LRU keyed by inputs — hover/theme/re-render churn no longer regenerates identical art. Treat returned objects as immutable. | ||
|
|
||
| **Color parsing** | ||
|
|
||
| - All 148 CSS named colors are now supported (previously 14 — `lime`, `navy`, `teal`, `tomato`, `gold`, `rebeccapurple`, … no longer fail to parse). | ||
| - New: `color(srgb … )`, `color(srgb-linear …)`, and `color(display-p3 …)` — Chromium serializes computed `color-mix()` results as `color(srgb …)`, so `color-mix()` now works end-to-end through `<Pixel>`/`usePixelRef`. | ||
| - `rgb()`/`rgba()` patterns are anchored, so colors embedded in unsupported values (e.g. `conic-gradient(rgb(255,0,0), …)`) are no longer misparsed as a solid color. | ||
|
|
||
| **Gradients** | ||
|
|
||
| - Tailwind v4 interpolation hints are handled: `linear-gradient(to right in oklab, …)` no longer silently falls back to `to bottom`. | ||
| - Angle units `turn`, `rad`, and `grad` are converted correctly (previously only `deg` worked). | ||
| - Corner keywords (`to top right`, …) now resolve to the aspect-ratio-dependent "magic corner" angle per CSS spec via the new `resolveGradientAngle(gradient, width, height)` export, instead of a fixed 45° multiple. | ||
| - Double-position stops (`red 0% 50%`) expand into two stops, preserving hard color edges. | ||
| - Stops with `px` positions or one unparseable stop no longer corrupt the whole gradient (the `-1` position sentinel can no longer leak into sampling). | ||
|
|
||
| **Correctness & safety** | ||
|
|
||
| - `generatePixelShadow` no longer forces zero offsets to `pixelSize` — `box-shadow: 0 4px 0` stays a straight shadow instead of turning diagonal. Offsets snap to the nearest grid line; all-zero shadows are skipped entirely. | ||
| - `generateCompositePixelImage` keeps the interior **transparent** for border-only elements (previously it filled the content area with the border color), and returns `null` when a background is present but unparseable (`url()`, `conic-gradient()`, …) so consumers can leave the original styling untouched. | ||
| - `parseComputedStyles` resolves percentage `border-radius` (e.g. `border-radius: 50%` avatars) against the element size via a new optional `size` parameter, and handles elliptical two-value radii. | ||
| - `parseBoxShadow` detects the `inset` keyword anywhere in the serialization — real browsers put it at the END in computed styles, so inset shadows were previously converted into outer drop-shadows. Multiple shadows are split correctly and the first non-inset one is used. | ||
| - Every exported generator sanitizes `pixelSize` — `pixelSize: 0` no longer hangs the tab (infinite loop) or throws `RangeError`. | ||
|
|
||
| **Packaging** | ||
|
|
||
| - ESM consumers under `moduleResolution: node16/nodenext` now get the ESM declaration file (`index.d.mts`) via per-condition `exports` types — fixes the attw "Masquerading as CJS" failure and wrong CJS-interop type errors. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| --- | ||
| "@react-pixel-ui/react": minor | ||
| --- | ||
|
|
||
| React layer fixes: stable DOM across re-measures, resize awareness, StrictMode-safe observers, working responsive mode, and API hardening. | ||
|
|
||
| **`<Pixel>`** | ||
|
|
||
| - The drop-shadow wrapper no longer remounts the child DOM node on every re-measure (theme toggle, style change). Once a shadow wrapper exists it stays in the tree (`display: contents` while measuring), so uncontrolled `<input>` values, focus, and selection survive. | ||
| - A `ResizeObserver` now watches the child: responsive/percentage-width elements re-measure when their size changes (stale absolute-px clip-paths are regenerated), and elements that mount at zero size (inside `display: none` tabs/accordions) get pixel art as soon as they become visible — previously they never did. | ||
| - Backgrounds the engine can't pixelate (`url()` images, `conic-gradient()`) are left untouched instead of being replaced with `background: none` + a transparent PNG (elements no longer go invisible or flash solid red). | ||
| - Author `box-shadow` is only cleared when it is actually replaced by a pixel shadow — inset-only shadows are preserved. | ||
|
|
||
| **`usePixelRef`** | ||
|
|
||
| - The style observer is created inside an effect instead of the ref callback. Under React 18 StrictMode (the Vite/Next dev default) the simulated remount previously disconnected the observer permanently, freezing all hover/focus/resize updates in development. | ||
| - Borderless elements with a `box-shadow` get their pixelated drop-shadow again — the hook only read `wrapperStyle.filter`, which the composer only sets when a border wrapper is needed, so shadows were silently deleted. When the element has no clip-path the filter now applies to the element itself instead of its parent. | ||
| - Same graceful-degradation behavior as `<Pixel>` for unparseable backgrounds. | ||
|
|
||
| **`<PixelBox>`** | ||
|
|
||
| - `responsive` mode actually works now: the component no longer pins its own measured fallback (`200×100`) as inline px width/height, which made the ResizeObserver only ever read back the forced size. Size the box with your own CSS (`style={{ width: '100%', height: 120 }}`) and the pixel art follows it. Measurement uses the border box. | ||
| - `className`, `style`, and other HTML props now consistently land on the **root** element (previously `style` landed on the inner content div when a border was used, so `margin` etc. silently did nothing). | ||
|
|
||
| **`<PixelButton>`** | ||
|
|
||
| - Unknown `variant` values fall back to `primary` with a dev warning instead of crashing with a `TypeError`. | ||
| - The rendered `<button>` defaults to `type="button"` so dropping it into a form no longer submits the form; an explicit `type` prop still wins. | ||
|
|
||
| **Misc** | ||
|
|
||
| - `PixelConfigProvider` no longer crashes when rendered without a `config` prop (it is now optional). | ||
| - `usePixelArt` memoizes by the radius values, so inline per-corner arrays (`borderRadius={[8,8,0,0]}`) no longer regenerate the art on every parent re-render; it also skips generating the unused composite PNG. | ||
| - `PixelBox`/`PixelButton` expose proper `displayName`s in React DevTools (no more `PixelBox2`). | ||
| - ESM consumers under `moduleResolution: node16/nodenext` get the ESM declaration file via per-condition `exports` types, and the `"use client"` banner no longer shifts source maps by one line. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,64 +1,8 @@ | ||
| # Changelog | ||
|
|
||
| ## 2.0.0 (2026-04-03) | ||
| Release notes are maintained per package by [changesets](https://github.com/changesets/changesets): | ||
|
|
||
| ### Breaking Changes | ||
| - [`@react-pixel-ui/react` CHANGELOG](./packages/react/CHANGELOG.md) | ||
| - [`@react-pixel-ui/core` CHANGELOG](./packages/core/CHANGELOG.md) | ||
|
|
||
| - **Removed Canvas-based rendering** — No more `html2canvas` dependency, no `document.createElement('canvas')` calls | ||
| - **Removed `usePixelCSS` hook** — Replaced by `usePixelArt` hook and `PixelBox` component | ||
| - **Removed CSS string input** — New API uses explicit props instead of CSS string parsing | ||
| - **New API surface** — All exports changed (see Migration Guide below) | ||
|
|
||
| ### New Features | ||
|
|
||
| - **Pure CSS output** — Uses `clip-path: polygon()`, BMP data URL gradients, and `filter: drop-shadow()` | ||
| - **SSR compatible** — All computation is pure math, zero browser API dependency | ||
| - **`PixelBox` component** — Drop-in pixel art container with automatic wrapper div for borders | ||
| - **`PixelButton` component** — Pre-styled button with `primary`, `secondary`, `danger` variants | ||
| - **`usePixelArt` hook** — Low-level hook returning `wrapperStyle`, `contentStyle`, `needsWrapper` | ||
| - **`useStaircaseClip` hook** — Generate clip-path polygon for staircase corners only | ||
| - **`useSteppedGradient` hook** — Convert CSS gradient to stepped bands only | ||
| - **`useResponsiveSize` hook** — ResizeObserver-based auto-sizing with pixel grid snapping | ||
| - **`PixelConfigProvider`** — React context for global defaults (pixelSize, borderColor) | ||
| - **Per-corner border radius** — `borderRadius={[tl, tr, br, bl]}` array syntax | ||
| - **Pixel grid snapping** — `borderWidth` and `borderRadius` auto-snap to `pixelSize` multiples | ||
| - **2D pixel gradients** — BMP data URL with `image-rendering: pixelated` for true square blocks | ||
| - **Hard pixel shadows** — `drop-shadow(blur=0)` follows clip-path contour | ||
|
|
||
| ### Migration from v1.x | ||
|
|
||
| **Before (v1):** | ||
| ```tsx | ||
| import { usePixelCSS } from '@react-pixel-ui/react'; | ||
|
|
||
| const { pixelStyle } = usePixelCSS(` | ||
| background: linear-gradient(45deg, #ff6b6b, #4ecdc4); | ||
| border: 2px solid #333; | ||
| border-radius: 12px; | ||
| `, { width: 280, height: 120, pixelSize: 4 }); | ||
|
|
||
| <div style={pixelStyle}>Content</div> | ||
| ``` | ||
|
|
||
| **After (v2):** | ||
| ```tsx | ||
| import { PixelBox } from '@react-pixel-ui/react'; | ||
|
|
||
| <PixelBox | ||
| width={280} height={120} pixelSize={4} | ||
| borderRadius={12} borderWidth={2} borderColor="#333" | ||
| background="linear-gradient(45deg, #ff6b6b, #4ecdc4)" | ||
| > | ||
| Content | ||
| </PixelBox> | ||
| ``` | ||
|
|
||
| Or with the hook: | ||
| ```tsx | ||
| import { usePixelArt } from '@react-pixel-ui/react'; | ||
|
|
||
| const { wrapperStyle, contentStyle, needsWrapper } = usePixelArt(280, 120, { | ||
| pixelSize: 4, borderRadius: 12, borderWidth: 2, | ||
| borderColor: '#333', backgroundColor: 'linear-gradient(45deg, #ff6b6b, #4ecdc4)', | ||
| }); | ||
| ``` | ||
| Migrating from v1? See the [v1 → v2 migration guide](./docs/MIGRATION.md). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| # v1 → v2 Migration Guide | ||
|
|
||
| v2.0.0 (2026-04-03) was a full rewrite. This guide covers what changed | ||
| and how to migrate. For ongoing release notes, see the per-package | ||
| changelogs: | ||
|
|
||
| - [`@react-pixel-ui/react` CHANGELOG](../packages/react/CHANGELOG.md) | ||
| - [`@react-pixel-ui/core` CHANGELOG](../packages/core/CHANGELOG.md) | ||
|
|
||
| ## What changed in 2.0.0 | ||
|
|
||
| ### Breaking Changes | ||
|
|
||
| - **Removed Canvas-based rendering** — No more `html2canvas` dependency, no `document.createElement('canvas')` calls | ||
| - **Removed `usePixelCSS` hook** — Replaced by `usePixelArt` hook and `PixelBox` component | ||
| - **Removed CSS string input** — New API uses explicit props instead of CSS string parsing | ||
| - **New API surface** — All exports changed (see Migration Guide below) | ||
|
|
||
| ### New Features | ||
|
|
||
| - **Pure CSS output** — Uses `clip-path: polygon()`, BMP data URL gradients, and `filter: drop-shadow()` | ||
| - **SSR compatible** — All computation is pure math, zero browser API dependency | ||
| - **`PixelBox` component** — Drop-in pixel art container with automatic wrapper div for borders | ||
| - **`PixelButton` component** — Pre-styled button with `primary`, `secondary`, `danger` variants | ||
| - **`usePixelArt` hook** — Low-level hook returning `wrapperStyle`, `contentStyle`, `needsWrapper` | ||
| - **`useStaircaseClip` hook** — Generate clip-path polygon for staircase corners only | ||
| - **`useSteppedGradient` hook** — Convert CSS gradient to stepped bands only | ||
| - **`useResponsiveSize` hook** — ResizeObserver-based auto-sizing with pixel grid snapping | ||
| - **`PixelConfigProvider`** — React context for global defaults (pixelSize, borderColor) | ||
| - **Per-corner border radius** — `borderRadius={[tl, tr, br, bl]}` array syntax | ||
| - **Pixel grid snapping** — `borderWidth` and `borderRadius` auto-snap to `pixelSize` multiples | ||
| - **2D pixel gradients** — BMP data URL with `image-rendering: pixelated` for true square blocks | ||
| - **Hard pixel shadows** — `drop-shadow(blur=0)` follows clip-path contour | ||
|
|
||
| ### Migration from v1.x | ||
|
|
||
| **Before (v1):** | ||
| ```tsx | ||
| import { usePixelCSS } from '@react-pixel-ui/react'; | ||
|
|
||
| const { pixelStyle } = usePixelCSS(` | ||
| background: linear-gradient(45deg, #ff6b6b, #4ecdc4); | ||
| border: 2px solid #333; | ||
| border-radius: 12px; | ||
| `, { width: 280, height: 120, pixelSize: 4 }); | ||
|
|
||
| <div style={pixelStyle}>Content</div> | ||
| ``` | ||
|
|
||
| **After (v2):** | ||
| ```tsx | ||
| import { PixelBox } from '@react-pixel-ui/react'; | ||
|
|
||
| <PixelBox | ||
| width={280} height={120} pixelSize={4} | ||
| borderRadius={12} borderWidth={2} borderColor="#333" | ||
| background="linear-gradient(45deg, #ff6b6b, #4ecdc4)" | ||
| > | ||
| Content | ||
| </PixelBox> | ||
| ``` | ||
|
|
||
| Or with the hook: | ||
| ```tsx | ||
| import { usePixelArt } from '@react-pixel-ui/react'; | ||
|
|
||
| const { wrapperStyle, contentStyle, needsWrapper } = usePixelArt(280, 120, { | ||
| pixelSize: 4, borderRadius: 12, borderWidth: 2, | ||
| borderColor: '#333', backgroundColor: 'linear-gradient(45deg, #ff6b6b, #4ecdc4)', | ||
| }); | ||
| ``` | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
릴리스 날짜를 확인하세요.
문서에
2026-04-03이 v2.0.0 릴리스 날짜로 명시되어 있습니다. 현재 6월이고 PR이 아직 open 상태이므로, 실제 릴리스 시 날짜를 업데이트해야 할 수 있습니다. 또는 플레이스홀더를 사용하는 것도 고려하세요.🤖 Prompt for AI Agents