Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
8a0657d
chore: propagate the reviewed installation to develop
efraespada Nov 4, 2026
7427d80
docs: bootstrap the proposed shared UI specification catalog
efraespada Nov 4, 2026
d885bcc
Merge pull request #9 from vypdev/docs/8-bootstrap-the-proposed-share…
efraespada Nov 4, 2026
2c14ddc
docs(sdd): specify issue contract in specs/shared-ui-framework-delive…
github-actions[bot] Nov 4, 2026
2fd60c6
feat: establish tested shared navigation contracts and package qualit…
efraespada Nov 4, 2026
f981e2c
fix: reject C1 control characters throughout navigation input
efraespada Nov 4, 2026
3045e42
feat: add a controlled shared dashboard navigation component
efraespada Nov 4, 2026
2c8e7ba
fix: freeze packed consumer dependencies without ambient metadata
efraespada Nov 4, 2026
a875c8d
docs: reflect the implemented navigation preview in current guides
efraespada Nov 4, 2026
be482f1
feat: deliver navigation bindings for React Vue Angular and Svelte
efraespada Nov 4, 2026
213f527
feat: add controlled form-associated text fields
efraespada Nov 4, 2026
8aefb26
docs: fix companion compatibility record link
efraespada Nov 4, 2026
ecb57a8
feat: bridge text fields to four framework form models
efraespada Nov 4, 2026
abd53f6
test: acknowledge captured pre-render restoration requests
efraespada Nov 4, 2026
b753030
feat: add a production-backed accessible Storybook catalog
efraespada Nov 4, 2026
8cdf397
test: cover server registration without a custom-element registry
efraespada Nov 4, 2026
4c43b23
feat: add shared native-form buttons and catalog states
efraespada Nov 4, 2026
0955d71
fix: preserve implicit submission with form method names
efraespada Nov 4, 2026
209b5d8
feat: bind shared native-form buttons across four frameworks
efraespada Nov 4, 2026
433f5c7
test: verify native readonly field submission without altering validity
efraespada Nov 4, 2026
913bab7
feat: share typed single and multiple selects across four frameworks
efraespada Nov 4, 2026
64adc3a
feat: add shared checkboxes and calibrate the HA visual baseline
efraespada Nov 4, 2026
c81b559
fix: align shared dashboard text tabs with Home Assistant
efraespada Nov 4, 2026
8109d91
test: verify dashboard tabs against measured Linux and macOS references
efraespada Nov 4, 2026
d66dc65
test: prove native form actions do not synthesize proxy clicks
efraespada Nov 4, 2026
54144cb
docs: anchor delivery baseline and restore the path inventory table
efraespada Nov 4, 2026
0a64a33
feat: add local icons and accessible labels to shared dashboard navig…
efraespada Nov 4, 2026
0bc84f4
feat: match HA dashboard overflow with accessible logical paging
efraespada Nov 4, 2026
6c3df6a
feat: calibrate shared buttons against Home Assistant
efraespada Nov 4, 2026
5517469
feat: calibrate shared text fields against Home Assistant
efraespada Nov 4, 2026
931ae3b
fix: preserve catalog status across native form reset
efraespada Nov 4, 2026
61baa50
test: verify native pointer boundaries and fresh navigation gestures
efraespada Nov 4, 2026
fc143a7
fix: preserve native host attributes and fresh catalog completion
efraespada Nov 4, 2026
13f17c0
test: enforce CommonJS dependency boundaries and verify runner semantics
efraespada Nov 4, 2026
594b7be
feat: calibrate the shared single picker against Home Assistant
efraespada Nov 5, 2026
fba84b8
fix: track shadow-root scroll and select reference architecture
efraespada Nov 5, 2026
fb7445e
feat: add Home Assistant multiple select chips and search
efraespada Nov 5, 2026
47e9e1b
ci: allow bounded catalog checks to finish
efraespada Nov 5, 2026
6cce69b
feat: add Home Assistant visible select lists
efraespada Nov 5, 2026
1fbe096
fix: reject sparse navigation and speed exact list checks
efraespada Nov 5, 2026
e3f49b5
feat: add Home Assistant native dialogs
efraespada Nov 5, 2026
449a268
feat: add Home Assistant card surfaces
efraespada Nov 5, 2026
41b84aa
test: preserve original visual captures in verified archives
efraespada Nov 5, 2026
1d5db3b
fix: keep the initial select option uncommitted on Tab
efraespada Nov 5, 2026
fbab810
feat: add Home Assistant rows and responsive grids
efraespada Nov 5, 2026
0c72565
test: stabilize the row action icon across runners
efraespada Nov 5, 2026
0867b41
feat: add Home Assistant feedback states across adapters
efraespada Nov 5, 2026
982dfaa
test: close the catalog server when Chromium setup fails
efraespada Nov 5, 2026
4e65277
test: verify packaged adapters in server-rendered apps
efraespada Nov 5, 2026
df8363c
test: enforce browser and package delivery gates
efraespada Nov 5, 2026
539017b
fix: scope adapter requests to their originating host
efraespada Nov 5, 2026
14259d7
fix: make package archives identical across hosts
efraespada Nov 5, 2026
7ee7082
test: import the React style type explicitly
efraespada Nov 5, 2026
63331e4
fix: scope form requests and declare example languages
efraespada Nov 5, 2026
be489fa
Merge pull request #10 from vypdev/feature/7-build-shared-home-assist…
efraespada Nov 5, 2026
d85aa2d
ci: publish the verified npm prerelease cohort
efraespada Nov 5, 2026
56656b5
fix: reject invalid numeric npm release tags
efraespada Nov 5, 2026
7d56566
Merge pull request #12 from vypdev/chore/11-automate-npm-releases-for…
efraespada Nov 5, 2026
46bea5a
test: compare Home Assistant navigation pixels
efraespada Nov 5, 2026
202fe15
Merge pull request #15 from vypdev/bugfix/14-compare-navigation-scree…
efraespada Nov 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
81 changes: 81 additions & 0 deletions .github/workflows/ci_check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: CI Check
on:
pull_request:
types: [opened, reopened, synchronize, ready_for_review, converted_to_draft]
push:
branches: [develop, master]
merge_group:
types: [checks_requested]
env:
STORYBOOK_DISABLE_TELEMETRY: "1"
permissions:
contents: read
jobs:
quality:
name: CI Check
runs-on: ubuntu-latest
# The catalog has its own phase deadlines; this job also installs browsers and verifies packages.
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version-file: .node-version
package-manager-cache: false
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
with:
run_install: false
cache: false
- run: pnpm install --frozen-lockfile --ignore-scripts
- run: pnpm check
- run: pnpm exec playwright install --with-deps chromium --only-shell
- run: pnpm exec playwright install --with-deps firefox webkit
- run: pnpm test
- run: pnpm build
- run: pnpm test:package
- run: pnpm test:ssr
- run: pnpm storybook:build
- run: pnpm test:storybook
- run: pnpm test:engines
# Read the original catalog SDK at the reviewed revision; never rewrite it.
- name: Read the original specification SDK
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: vypdev/copilot
ref: 3e7ceea08e6131360e026937d1c0cc1b120472e0
path: .validation/copilot-sdk
persist-credentials: false
sparse-checkout-cone-mode: false
sparse-checkout: |
src/infrastructure/specification_catalog_validator.cjs
src/infrastructure/specification_catalog_publication.cjs
src/domain/specification_catalog_path_policy.cjs
- name: Validate the actual specification catalog
run: >-
node -e "require('./.validation/copilot-sdk/src/infrastructure/specification_catalog_validator.cjs').main([], process.cwd())"
- name: Retain source coverage and compiled package evidence
if: ${{ always() }}
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: shared-ui-validation-${{ github.sha }}
path: |
build/storybook/
coverage/
packages/*/dist/
test-results/
.vitest/attachments/
if-no-files-found: error
include-hidden-files: true
retention-days: 7
- name: Retain the verified npm candidate for the production SHA
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: npm-candidate-${{ github.sha }}
path: |
test-results/packed-cohort.json
test-results/packages/*.tgz
if-no-files-found: error
retention-days: 30
90 changes: 90 additions & 0 deletions .github/workflows/npm_release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: Publish ha-ui npm prerelease

on:
push:
tags: ['v*-next.*']

concurrency:
group: ha-ui-npm-release
cancel-in-progress: false

permissions:
contents: read

jobs:
candidate:
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
actions: read
contents: read
outputs:
run-id: ${{ steps.ci.outputs.run-id }}
steps:
- name: Find the successful CI run for this production commit
id: ci
env:
GH_TOKEN: ${{ github.token }}
run: |
[[ "$GITHUB_REF_NAME" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)-next\.(0|[1-9][0-9]*)$ ]]
run_id=$(gh api "repos/$GITHUB_REPOSITORY/actions/workflows/ci_check.yml/runs?event=push&branch=master&head_sha=$GITHUB_SHA&status=success&per_page=10" \
--jq '.workflow_runs[0].id // empty')
[[ "$run_id" =~ ^[1-9][0-9]*$ ]]
echo "run-id=$run_id" >> "$GITHUB_OUTPUT"

publish:
needs: candidate
runs-on: ubuntu-latest
environment: npm
timeout-minutes: 10
permissions:
actions: read
contents: read
id-token: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
ref: ${{ github.sha }}
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.0.1
with:
node-version-file: .node-version
registry-url: https://registry.npmjs.org
package-manager-cache: false
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: npm-candidate-${{ github.sha }}
path: ${{ runner.temp }}/ha-ui-npm-candidate
github-token: ${{ github.token }}
run-id: ${{ needs.candidate.outputs.run-id }}
- name: Publish the verified archives through npm Trusted Publishing
env:
NPM_CANDIDATE_DIR: ${{ runner.temp }}/ha-ui-npm-candidate
run: |
node --input-type=module <<'NODE'
import {execFileSync} from 'node:child_process';
import {appendFileSync,readFileSync} from 'node:fs';
import {join} from 'node:path';
import {packageCoordinates,sha256} from './tooling/packaging.mjs';
import {observeNpmVersion,publishNpmPrerelease} from './tooling/npm-prerelease.mjs';

const directory=process.env.NPM_CANDIDATE_DIR;
const {candidate}=JSON.parse(readFileSync(join(directory,'packed-cohort.json'),'utf8'));
const tag=process.env.GITHUB_REF_NAME;
const version=tag.slice(1);
const paths=new Map(packageCoordinates.map(name=>[
name,join(directory,'packages',name.slice(1).replace('/','-')+'-'+version+'.tgz'),
]));
const archives=new Map([...paths].map(([name,path])=>[name,readFileSync(path)]));
const result=await publishNpmPrerelease({
record:candidate,sourceRevision:process.env.GITHUB_SHA,lockSha256:sha256(readFileSync('pnpm-lock.yaml')),tag,
oidc:Boolean(process.env.ACTIONS_ID_TOKEN_REQUEST_URL&&process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN),
archives,observe:observeNpmVersion,
publish:async entry=>execFileSync('npm',[
'publish',paths.get(entry.coordinate),'--access','public','--tag','next','--ignore-scripts',
],{stdio:'inherit',timeout:120_000}),
append:async entry=>appendFileSync(process.env.GITHUB_STEP_SUMMARY,
'- '+entry.coordinate+'@'+entry.version+' `'+entry.sha256+'`\n'),
});
if(result.state!=='published')throw new Error(result.state+': '+result.reason);
NODE
56 changes: 56 additions & 0 deletions .github/workflows/storybook_pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Storybook Pages
on:
push:
branches: [master]
workflow_dispatch:
permissions:
contents: read
env:
STORYBOOK_DISABLE_TELEMETRY: "1"
concurrency:
group: storybook-pages
cancel-in-progress: false
jobs:
build:
if: github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
# The published catalog runs the same browser gates as CI.
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version-file: .node-version
package-manager-cache: false
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
with:
run_install: false
cache: false
- run: pnpm install --frozen-lockfile --ignore-scripts
- run: pnpm check
- run: pnpm exec playwright install --with-deps chromium --only-shell
- run: pnpm exec playwright install --with-deps firefox webkit
- run: pnpm test
- run: pnpm test:package
- run: pnpm test:ssr
- run: pnpm storybook:build
- run: pnpm test:storybook
- run: pnpm test:engines
- uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4
with:
path: build/storybook
deploy:
needs: build
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Publish the verified static catalog
id: deployment
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
node_modules/
coverage/
.validation/
work/
**/dist/
**/test-results/
**/.vite/

.vitest/

**/.svelte-kit/

build/
.storybook-cache/
1 change: 1 addition & 0 deletions .node-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
24.20.0
3 changes: 3 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
engine-strict=true
strict-peer-dependencies=true
auto-install-peers=false
9 changes: 9 additions & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type {StorybookConfig} from '@storybook/web-components-vite';
const config:StorybookConfig={
framework:'@storybook/web-components-vite',stories:['../stories/**/*.stories.ts'],
staticDirs:[{from:'../LICENSES',to:'/licenses'}],
addons:['@storybook/addon-docs','@storybook/addon-a11y','@storybook/addon-vitest'],
core:{disableTelemetry:true,enableCrashReports:false},
viteFinal:async config=>({...config,base:'./'}),
};
export default config;
25 changes: 25 additions & 0 deletions .storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {addons} from 'storybook/preview-api';
import {observeCatalogCompletion} from '../stories/catalog-completion.js';
import type {Preview} from '@storybook/web-components-vite';
import {registerTabs} from '@vypdev/ha-ui/tabs';
import {registerButton} from '@vypdev/ha-ui/button';
import {registerCheckbox} from '@vypdev/ha-ui/checkbox';
import {registerSelect} from '@vypdev/ha-ui/select';
import {registerField} from '@vypdev/ha-ui/field';
import {registerDialog} from '@vypdev/ha-ui/dialog';
import {registerCard} from '@vypdev/ha-ui/card';
import {registerGrid,registerRow} from '@vypdev/ha-ui/layout';
import {registerFeedback} from '@vypdev/ha-ui/feedback';
import {catalogFrame} from '../stories/catalog-frame.js';
const preview:Preview={
beforeAll:()=>{registerTabs();registerField();registerButton();registerSelect();registerCheckbox();registerDialog();registerCard();registerGrid();registerRow();registerFeedback();return observeCatalogCompletion(addons.getChannel(),document.documentElement);},decorators:[catalogFrame],
initialGlobals:{theme:'light',locale:'en',width:'wide',motion:'system'},
globalTypes:{
theme:{description:'HA-compatible theme',toolbar:{title:'Theme',icon:'circlehollow',items:[{value:'light',title:'Light'},{value:'dark',title:'Dark'}],dynamicTitle:true}},
locale:{description:'Example language; English fallback',toolbar:{title:'Language',icon:'globe',items:[{value:'en',title:'English'},{value:'es',title:'Español'}],dynamicTitle:true}},
width:{description:'Example width',toolbar:{title:'Width',icon:'mobile',items:[{value:'wide',title:'Wide'},{value:'narrow',title:'Narrow'}],dynamicTitle:true}},
motion:{description:'System preference or stronger reduced motion',toolbar:{title:'Motion',icon:'lightning',items:[{value:'system',title:'System'},{value:'reduced',title:'Reduced'}],dynamicTitle:true}},
},
parameters:{layout:'fullscreen',a11y:{test:'error'},docs:{codePanel:true},controls:{expanded:true}},
};
export default preview;
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 vypdev

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Loading
Loading