Skip to content

Releases: EchoTechFE/devicekit

v0.4.0

Choose a tag to compare

@lbb00 lbb00 released this 14 Sep 07:26
a4d6588

What's Changed

  • fix(devices): correct iPhone Duo outer screen to its D-shaped corners by @lbb00 in #24
  • chore(lint): drop ESLint in favor of oxlint by @lbb00 in #25
  • chore(release): v0.4.0 by @lbb00 in #26

Full Changelog: v0.3.0...v0.4.0

@devicekit/frame@0.4.1-alpha.0

Pre-release

Choose a tag to compare

@github-actions github-actions released this 14 Sep 09:04
67a0526

Patch Changes

  • 39f32b3: Test changeset to verify the OIDC trusted-publishing release pipeline end to end via an alpha prerelease. No functional change.
  • Updated dependencies [39f32b3]
    • @devicekit/devices@0.4.1-alpha.0

@devicekit/devices@0.4.1-alpha.0

Pre-release

Choose a tag to compare

@github-actions github-actions released this 14 Sep 09:04
67a0526

Patch Changes

  • 39f32b3: Test changeset to verify the OIDC trusted-publishing release pipeline end to end via an alpha prerelease. No functional change.

v0.3.0

Choose a tag to compare

@lbb00 lbb00 released this 11 Sep 12:47
c881dc2

Added

  • @devicekit/frame: add interaction-mode (mobile, mobile-no-touch, desktop, desktop-touch) for touch or mouse preview affordances, including the frame demo control and React prop. Default mobile now shows a centered touch-ring cursor over the screen, where previous releases used the normal cursor.
  • @devicekit/devices: bundled profiles now include releaseYear, and the preset exports use PresetDeviceProfile so selectors can sort models chronologically without handling a missing year.
  • @devicekit/devices and @devicekit/frame: add iPhone 18 Pro, iPhone 18 Pro Max, and beta iPhone Duo inner/outer profiles with foldable geometry, orientation-aware camera cutouts, Duo status chrome, and 1:1 CSS scaling in the frame demo.
  • @devicekit/frame: rebuild the hosted demo with Astro, with a static page, canonical URL and JSON-LD for every device profile, plus sitemap and robots routes.

Changed

  • @devicekit/frame: sort each demo device group by release year, newest first, and select iPhone 18 Pro by default.
  • @devicekit/devices and @devicekit/frame: render separate iOS, stock Android, Samsung, HyperOS and HarmonyOS status-bar typography, spacing and project-owned glyph masks. The frame demo applies the matching UI family and profile system version to its Light/Dark preview chrome.

Fixed

  • @devicekit/frame: shells with asymmetric bezel insets now derive concentric per-corner radii without overriding an explicit bodyRadius.

v0.2.3

Choose a tag to compare

@lbb00 lbb00 released this 08 Sep 09:13
659ec26

Fixed

  • Home-button iPhones now render with thick top and bottom bezels and a physical Home button, including the correct chassis rotation in landscape.
  • Hiding status-bar text no longer removes a phone's physical notch or Dynamic Island.
  • Documented the per-edge shell geometry and reflected bezel variables.

v0.2.2

Choose a tag to compare

@lbb00 lbb00 released this 04 Sep 10:59

Fixed

  • @devicekit/devices: the 13 Home-button iPhones (iPhone 4 through iPhone 8 Plus, both SE models) had no shell, so they took the iOS default 38px screen radius and the rounded corner clipped the status-bar icons. Their flat LCD panels have square corners; they now resolve to screenRadius: 0 with a 38px body radius.

Also in this release: rewritten READMEs with rendered device images.

v0.2.1

Choose a tag to compare

@lbb00 lbb00 released this 04 Sep 09:57

Fixed

  • The screen now paints its own background (--device-screen-background, default #ffffff). Without a navigation-bar slot the transparent status bar used to show the near-black frame body through, hiding black status-bar text. A dark page should set the variable to its own background so white status-bar text keeps its contrast.

v0.2.0

Choose a tag to compare

@lbb00 lbb00 released this 04 Sep 09:47

Added

  • @devicekit/devices: DEVICE_NAMES — every device name as a constant, so DEVICE_NAMES.iPhone_16_Pro replaces the hand-typed 'iPhone 16 Pro' and the editor can autocomplete and check it. Keys are derived from the name by deviceNameKey (runs of characters outside [A-Za-z0-9] become one _, so 'iPhone 12/13 (Pro)' is iPhone_12_13_Pro); the table lives in a generated file kept in sync by pnpm --filter @devicekit/devices generate:device-names and guarded by a test.
  • @devicekit/devices: DeviceName, the union of every name in the table.
  • @devicekit/frame/react: the device prop accepts DeviceName for autocomplete while still taking any string.

Changed

  • @devicekit/frame now depends on @devicekit/devices with a caret range instead of an exact version.

v0.1.0

Choose a tag to compare

@lbb00 lbb00 released this 04 Sep 09:32

Added

  • @devicekit/devices: device presets for iOS, Android and HarmonyOS phones and tablets, with screen, pixel ratio, status bar, safe area and user agent data.
  • @devicekit/frame: a framework-agnostic <device-frame> custom element (plus a React wrapper) that draws a bezel, status bar and home indicator around any previewed page.
  • @devicekit/frame: the default slot is laid out by the element itself — below the status and navigation bars, above the tab bar, the whole screen under immersive — so a host no longer pads its own content.
  • @devicekit/frame: refreshContentRect(), for re-measuring after a change a ResizeObserver cannot see, such as a CSS transform: scale() on an ancestor.
  • @devicekit/frame: DeviceFrameElementEventMap, so addEventListener('contentrectchange', ...) gives event.detail its ContentRect type.
  • @devicekit/frame/react: os, cutout, width, height, pixelRatio, userAgent, statusBarHeight and safeAreaTop / -Right / -Bottom / -Left props; statusBar now takes boolean | 'live' | string.
  • @devicekit/frame/react: JSX types for the raw <device-frame> tag under both React 18 and React 19.
  • @devicekit/frame: importing the package under Node or during SSR no longer throws — defineDeviceFrame() is a no-op where customElements does not exist.
  • @devicekit/frame: device, orientation, embedded and immersive are writable properties that reflect to the same-named attributes, so el.device = 'iPhone 15' works like setting the attribute.
  • pnpm run verify:pack: packs every workspace package, unpacks the tarball and imports dist/index.js from plain Node, so a broken publishConfig cannot reach npm. The publish workflow runs it before publishing.

Fixed

  • @devicekit/frame: default-slot content was drawn under the status and navigation bars instead of below them.
  • @devicekit/frame/react: under React 19 the wrapper threw TypeError: Cannot set property device ... which has only a getter, because React 19 assigns props that exist as element properties instead of setting attributes. Verified against the packed tarball with react-dom 19.2.
  • @devicekit/devices: in landscape, Android and HarmonyOS devices without an explicit safeAreaInsetsLandscape reported a safe-area top of 0 while the landscape status bar was 24 / 36 px, so immersive content was drawn under the status bar. The landscape default now mirrors portrait: top equals the landscape status bar height.
  • @devicekit/devices: Surface Duo (inner) and HUAWEI Pura X Max (inner) were stored wider than tall; the table stores portrait sizes, so orientation="landscape" now yields the unfolded shape.
  • @devicekit/frame: a contentrectchange listener that changed an attribute (flipping immersive, say) made listeners registered after it receive the new rect first and the stale one second, so a host positioning a native view from the event ended one step behind. Nested changes are now queued and re-dispatched after the outer event, in the order they happened.
  • @devicekit/frame: the home indicator was drawn under the tab bar; it now sits above the tab bar and below the overlay, as on a real device.
  • @devicekit/frame: status-bar="live" started a clock timer even when the element was not in the document (attribute set before connect, or after disconnect); the clock now only runs while connected.
  • @devicekit/frame: defineDeviceFrame(tag) silently did nothing when the tag was already taken by an unrelated element class; it now throws a clear error, while re-registering the same implementation (hot reload, duplicate bundles) stays a no-op.
  • Publish workflow: the npm dist-tag is now decided from the package versions themselves — any pre-release version publishes to next, and asking for latest with a pre-release version fails the job instead of publishing.
  • Publish workflow: a manual run from any branch could publish that branch to npm; the job now only runs for a GitHub Release or a dispatch on main.
  • Workflows: every uses: was a movable major tag (actions/checkout@v6); all are pinned to commit SHAs, with Dependabot moving the pins.
  • Pages workflow: the build job held pages: write and id-token: write it never used; only the deploy job has them now.
  • @devicekit/devices: the README and the source comment claimed iOS 26 added a 20 px top inset in landscape. No iPhone has a landscape top inset; the iPhone 17 line moved the landscape bottom inset from 21 to 20. The README also said Android and HarmonyOS landscape values equal their portrait values; unset landscape fields fall back to the platform defaults (24 / 36 px status bar) instead.
  • @devicekit/frame/react: the statusBarTextStyle JSDoc claimed the default ink color "follows the device"; statusBarTextColor only ever branches on 'white' and otherwise returns black, with no device, platform or background input.
  • @devicekit/frame: the READMEs were missing DeviceFrameIntrinsicAttributes from the API reference, and listed the device / orientation property setters as taking string | null when the d.ts is string | null | undefined.
  • @devicekit/frame: two contentrectchange listeners that each flipped a different attribute in response to the other could keep the element re-measuring forever and freeze the tab. The element now remembers every rect it published during one synchronous batch, stops as soon as the same rect would go out a third time, and logs one console.error naming the contentrectchange listeners as the cause; refreshContentRect() re-syncs afterwards.
  • @devicekit/frame: package.json declared sideEffects: false, but @devicekit/frame/react registers <device-frame> at import time; a bundler could drop that registration. The field now lists the React entry as the only file with side effects.
  • @devicekit/frame: styles were injected as an inline <style> in the shadow root, which a strict style-src Content Security Policy blocks. Where the browser supports constructable stylesheets the styles are now attached through adoptedStyleSheets (one shared sheet for every instance); the <style> fallback remains for older engines.
  • @devicekit/frame: assigning a malformed deviceProfile ({}, { screen: null }, a negative width) crashed later inside layout with Cannot read properties of undefined (reading 'width'). The setter and resolveDevice() now throw a TypeError naming the bad field, such as deviceProfile.screen.width must be a finite number greater than 0, got -1.
  • @devicekit/devices: resolveWindowSize() accepted a negative or non-finite navigationBar / tabBarHeight and returned a window taller than the screen; it now throws a RangeError naming the option.
  • @devicekit/frame/react: every optional prop of DeviceFrameProps and DeviceFrameIntrinsicAttributes now allows undefined explicitly, so a consumer compiling with exactOptionalPropertyTypes: true can pass orientation={maybeOrientation} without TS2375. A type-test fixture compiled with that option is part of check-types.
  • Publish workflow: a GitHub Release created from a tag that is not on main could still publish; the job now refuses any commit that is not an ancestor of origin/main.
  • @devicekit/devices: assertDeviceProfile() let a profile through without name or pixelRatio, accepted cutout: null and shell: null, and never looked inside cutout or shell; a profile with cutout: { shape: 'bogus' } then reached the status bar as a broken shape. Every required field is now required, and nested cutout / shell fields are checked for type and range.
  • @devicekit/devices: Galaxy Z Fold 6 (inner) was stored as 744 x 860 @ 2.625, about 5 % larger than the panel's 1856 x 2160 physical pixels; it is now 707 x 823 @ 2.625.
  • @devicekit/frame: the shared constructed stylesheet was built with the page's global CSSStyleSheet and reused for every shadow root, so a <device-frame> created in, or moved into, another document (an iframe, a window opened by the host) either threw NotAllowedError or silently lost its styles. Sheets are now built per document with that document's own CSSStyleSheet, and the element re-checks its sheet every time it connects.
  • @devicekit/frame: the iOS status-bar layout table was keyed by pixelRatio as well as screen width and status bar height, so changing only the pixel ratio of a profile moved the clock and battery by tens of pixels. The lookup now uses width and status bar height alone, which already identify every row.
  • @devicekit/frame/react: <DeviceFrame onContentRectChange={...}> was neither typed nor wired, so the handler never ran. It is now a declared prop, registered once on the element and always calling the latest handler, and removed when the prop is dropped.
  • @devicekit/frame: a property assigned before the element was defined (el.device = 'iPhone 15' and then customElements.define(...)) shadowed the class setter, so device stayed a string, deviceProfile was silently lost, and the first render threw Cannot read properties of undefined (reading 'width'). On connect the element now moves every such own property through its setter.
  • @devicekit/devices: the generated iOS user agent said iPhone OS 26_0 for iOS 26 devices; Safari 26 froze that token at 18_6 and only bumps Version/26.0. iPads reported the mobile (iPad; CPU OS ...) form, while Safari on iPadOS 13 and later sends the desktop Macintosh user agent by default; iPads now generate that.
  • @devicekit/devices: every generated HarmonyOS user agent ended in HuaweiBrowser/5.0.4.303, which is the Huawei Browser app's own token and not part of the plain ArkWeb container's user agent. It is gone; a profile that needs it sets userAgent.
  • @devicekit/devices: the Android user agent always carried Mobile, so a custom tablet profile without its own userAgent looked like a phone. Prof...
Read more