Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
16a6fa9
set updating on click
SharonStrats Mar 19, 2026
abb83dd
Merge branch 'main' into fix/checkbox-dblclk
SharonStrats Mar 24, 2026
70f846e
add test for double click
SharonStrats Mar 25, 2026
d36adb0
checked login status
SharonStrats Mar 26, 2026
d137978
still throw if no pref or error
SharonStrats Mar 26, 2026
6e9b8a0
Update src/login/login.ts
SharonStrats Mar 26, 2026
05965e0
unit test
SharonStrats Mar 26, 2026
d71f51e
Merge branch 'fix/pref-error-msg' of https://github.com/SolidOS/solid…
SharonStrats Mar 26, 2026
db5bcef
return when not logged in
SharonStrats Mar 29, 2026
2d4aca1
Merge branch 'fix/pref-error-msg' of https://github.com/solidos/solid…
SharonStrats Mar 29, 2026
a288ede
Merge branch 'main' into fix/pref-error-msg
SharonStrats Mar 29, 2026
51d40cf
fix test
SharonStrats Mar 29, 2026
96842f8
foundation of shared select component
SharonStrats Apr 28, 2026
21d4030
Add a select webcomponent
SharonStrats Apr 29, 2026
45abfa7
export select webcomponent
SharonStrats Apr 29, 2026
41845b7
minor adj to select
SharonStrats Apr 29, 2026
2367666
fix typescript errors test file
SharonStrats Apr 29, 2026
24aceee
move arrowicon file
SharonStrats Apr 29, 2026
b67db1c
combobox web component
SharonStrats Apr 29, 2026
a1cd4d3
tests
SharonStrats Apr 29, 2026
f2403c7
lint fix
SharonStrats Apr 29, 2026
9711f44
cleanup
SharonStrats Apr 29, 2026
fe509c0
photoCapture web component
SharonStrats Apr 30, 2026
84053fd
lint fix
SharonStrats Apr 30, 2026
5c2b0fa
refactor: add manifest-driven v2 component build and export sync
SharonStrats May 1, 2026
0da9a83
add button web comp
SharonStrats May 1, 2026
1ea3dcb
refactor: reorganize v2 components into grouped source folders
SharonStrats May 1, 2026
b1cfdc0
remove first comp button
SharonStrats May 1, 2026
8a567c0
lint fix
SharonStrats May 1, 2026
5a3d725
fix loginButton back to orig
SharonStrats May 1, 2026
4dbc0e2
refactor: add theming hooks for button and select
SharonStrats May 2, 2026
1c410df
add hover on select
SharonStrats May 4, 2026
20da9c9
make bullseye drop friends work a little better
SharonStrats May 4, 2026
5ba5ac5
button type for using in js
SharonStrats May 4, 2026
040da0a
dragdrop button improvement
SharonStrats May 4, 2026
116c32c
Merge branch 'main' into fix/pref-error-msg
SharonStrats May 4, 2026
3cd29aa
Merge branch 'fix/pref-error-msg' into post-milestone3m
SharonStrats May 4, 2026
5db3791
Merge branch 'main' into fix/checkbox-dblclk
SharonStrats May 4, 2026
e710b1b
Merge branch 'fix/checkbox-dblclk' into post-milestone3m
SharonStrats May 4, 2026
cf94f33
test lint error
SharonStrats May 5, 2026
cb84838
lint error in login
SharonStrats May 5, 2026
f499141
Make login status background optional
SharonStrats May 5, 2026
340f7c8
Prevent early account label truncation
SharonStrats May 5, 2026
1f9713b
revert login
SharonStrats May 5, 2026
270c319
Merge branch 'post-milestone3m' of https://github.com/SolidOS/solid-u…
SharonStrats May 5, 2026
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
26 changes: 22 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,9 +322,13 @@ import { SignupButton } from 'solid-ui/components/signup-button'

Web components use a two-stage build to produce a clean public runtime layout while keeping internal TypeScript artifacts separate:

1. **webpack** (`npm run build-dist`) bundles each component entrypoint and emits the runtime files to `dist/components/<name>/index.js` and `dist/components/<name>/index.esm.js`.
2. **tsc** (`npm run build-js`) emits internal declaration and JS artifacts mirroring the source tree under `dist/v2/components/<name>/`.
3. **`scripts/build-component-dts.mjs`** (runs automatically after tsc as part of `postbuild-js`) writes thin public declaration wrappers at `dist/components/<name>/index.d.ts`, re-exporting from the internal `dist/v2/components/<name>/` output.
1. **`scripts/component-manifest.mjs`** is the source of truth for v2 web components. It defines the component entrypoints used by webpack and the public subpath names exposed from the package.
2. **webpack** (`npm run build-dist`) bundles each component entrypoint from the manifest and emits the runtime files to `dist/components/<name>/index.js` and `dist/components/<name>/index.esm.js`.
3. **tsc** (`npm run build-js`) emits internal declaration and JS artifacts mirroring the source tree under `dist/v2/components/<name>/`.
4. **`scripts/build-component-dts.mjs`** (runs automatically after tsc as part of `postbuild-js`) writes thin public declaration wrappers at `dist/components/<name>/index.d.ts`, re-exporting from the internal `dist/v2/components/<name>/` output.
5. **`scripts/sync-component-exports.mjs`** keeps the `package.json` `exports` map aligned with the manifest. It runs automatically as part of `npm run build` and `npm version` workflows.

The legacy main bundle remains a special case. In [webpack.config.mjs](webpack.config.mjs) only the `main` entry keeps the UMD `UI` global export; component entries are generated from the manifest and built as standalone scripts so they do not clobber one another when loaded directly.

This keeps the `package.json` subpath export fully aligned while exposing only the public `dist/components/...` layout:

Expand All @@ -338,6 +342,16 @@ This keeps the `package.json` subpath export fully aligned while exposing only t

Consumers never import from `dist/v2/components/...`; that path is an internal build artifact only.

### Adding a new web component

When adding a new v2 component:

1. Create the component folder under `src/v2/components/<ComponentName>/` with its `index.ts` entrypoint.
2. Add one record to `scripts/component-manifest.mjs`.
3. Run `npm run sync-component-exports` if you want to update `package.json` immediately, or just run `npm run build` and let the build do it automatically.

You should not need to hand-edit the webpack component entry list or the `package.json` component export map anymore.

## Development

When developing a component in solid-ui you can test it in isolation using storybook
Expand Down Expand Up @@ -411,4 +425,8 @@ You are logged in as nameOfLoggedIn user.

* Raptor mini: add a readme to the Footer component with example.

* Claude Sonnet 4.6: Make the dop down as a list under the input field and entlarge the pop up, make it higher, adjustable to fit the drop down. And make the drop down arrow area larger
* Claude Sonnet 4.6: Make the dop down as a list under the input field and entlarge the pop up, make it higher, adjustable to fit the drop down. And make the drop down arrow area larger

* GPT-5.4 Model: can you wire up the keyboard interactions and aria attributes for Select.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* GPT-5.4 Model: can you wire up the keyboard interactions and aria attributes for Select.
* GPT-5.4 Model: can you wire up the keyboard interactions and aria attributes for Select?


* GPT-5.4 Model: Take the code from /Users/sharon/2025Dev/solid-ui/src/media/media-capture.ts and make it a web component. Make it work in forms as well as not. Make it configurable and follow LoginButton.
75 changes: 71 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,85 @@
"import": "./dist/components/header/index.esm.js",
"require": "./dist/components/header/index.js"
},
"./components/layout/header": {
"types": "./dist/components/header/index.d.ts",
"import": "./dist/components/header/index.esm.js",
"require": "./dist/components/header/index.js"
},
"./components/loginButton": {
"types": "./dist/components/loginButton/index.d.ts",
"import": "./dist/components/loginButton/index.esm.js",
"require": "./dist/components/loginButton/index.js"
},
"./components/login-button": {
"types": "./dist/components/loginButton/index.d.ts",
"import": "./dist/components/loginButton/index.esm.js",
"require": "./dist/components/loginButton/index.js"
},
"./components/auth/login-button": {
"types": "./dist/components/loginButton/index.d.ts",
"import": "./dist/components/loginButton/index.esm.js",
"require": "./dist/components/loginButton/index.js"
},
"./components/auth/signup-button": {
"types": "./dist/components/signupButton/index.d.ts",
"import": "./dist/components/signupButton/index.esm.js",
"require": "./dist/components/signupButton/index.js"
},
"./components/signup-button": {
"types": "./dist/components/signupButton/index.d.ts",
"import": "./dist/components/signupButton/index.esm.js",
"require": "./dist/components/signupButton/index.js"
},
"./components/media/photo-capture": {
"types": "./dist/components/photoCapture/index.d.ts",
"import": "./dist/components/photoCapture/index.esm.js",
"require": "./dist/components/photoCapture/index.js"
},
"./components/photo-capture": {
"types": "./dist/components/photoCapture/index.d.ts",
"import": "./dist/components/photoCapture/index.esm.js",
"require": "./dist/components/photoCapture/index.js"
},
"./components/actions/button": {
"types": "./dist/components/button/index.d.ts",
"import": "./dist/components/button/index.esm.js",
"require": "./dist/components/button/index.js"
},
"./components/button": {
"types": "./dist/components/button/index.d.ts",
"import": "./dist/components/button/index.esm.js",
"require": "./dist/components/button/index.js"
},
"./components/footer": {
"types": "./dist/components/footer/index.d.ts",
"import": "./dist/components/footer/index.esm.js",
"require": "./dist/components/footer/index.js"
},
"./components/layout/footer": {
"types": "./dist/components/footer/index.d.ts",
"import": "./dist/components/footer/index.esm.js",
"require": "./dist/components/footer/index.js"
},
"./components/forms/select": {
"types": "./dist/components/select/index.d.ts",
"import": "./dist/components/select/index.esm.js",
"require": "./dist/components/select/index.js"
},
"./components/select": {
"types": "./dist/components/select/index.d.ts",
"import": "./dist/components/select/index.esm.js",
"require": "./dist/components/select/index.js"
},
"./components/forms/combobox": {
"types": "./dist/components/combobox/index.d.ts",
"import": "./dist/components/combobox/index.esm.js",
"require": "./dist/components/combobox/index.js"
},
"./components/combobox": {
"types": "./dist/components/combobox/index.d.ts",
"import": "./dist/components/combobox/index.esm.js",
"require": "./dist/components/combobox/index.js"
}
},
"files": [
Expand All @@ -38,10 +103,11 @@
],
"scripts": {
"clean": "rm -rf ./dist ./src/versionInfo.ts ./docs/api .tsbuildinfo",
"build": "npm run clean && npm run typecheck && npm run build-version && npm run build-dist && npm run build-js && npm run postbuild-js && npm run build-storybook",
"build": "npm run clean && npm run sync-component-exports && npm run typecheck && npm run build-version && npm run build-dist && npm run build-js && npm run postbuild-js && npm run build-storybook",
"build-version": "sh ./timestamp.sh > src/versionInfo.ts && eslint 'src/versionInfo.ts' --fix",
"prebuild-js": "rm -f .tsbuildinfo",
"build-js": "tsc",
"sync-component-exports": "node scripts/sync-component-exports.mjs",
"postbuild-js": "rm -f dist/versionInfo.d.ts dist/versionInfo.d.ts.map && node scripts/build-component-dts.mjs",
"build-dist": "webpack --progress",
"build-form-examples": "npm run build-js && npm run build-version && npm run build-dist && cp ./dist/solid-ui.js ./docs/form-examples/",
Expand All @@ -54,12 +120,13 @@
"test-debug": "node --inspect-brk ./node_modules/jest/bin/jest.js --runInBand --watch",
"watch:js": "tsc --watch --preserveWatchOutput",
"watch:component-dts": "node scripts/watch-component-dts.mjs",
"watch:component-exports": "node scripts/watch-component-exports.mjs",
"watch:dist": "webpack --watch --mode development",
"watch": "npm run build-version && npm run build-js && npm run postbuild-js && sh -c 'npm run watch:js & npm run watch:component-dts & npm run watch:dist & wait'",
"dev": "npm run build-version && sh -c 'npm run watch:js & npm run watch:component-dts & npm run watch:dist & wait'",
"watch": "npm run sync-component-exports && npm run build-version && npm run build-js && npm run postbuild-js && sh -c 'npm run watch:js & npm run watch:component-dts & npm run watch:component-exports & npm run watch:dist & wait'",
"dev": "npm run sync-component-exports && npm run build-version && sh -c 'npm run watch:js & npm run watch:component-dts & npm run watch:component-exports & npm run watch:dist & wait'",
"doc": "typedoc --out ./docs/api/ ./src/ --excludeInternal",
"prepublishOnly": "npm run build && npm run lint && npm test && npm run doc",
"preversion": "npm run lint && npm run typecheck && npm test",
"preversion": "npm run sync-component-exports && npm run lint && npm run typecheck && npm test",
"postpublish": "git push origin main --follow-tags",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build --output-dir ./examples/storybook"
Expand Down
23 changes: 17 additions & 6 deletions scripts/build-component-dts.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { existsSync, mkdirSync, readdirSync, statSync, writeFileSync } from 'fs'
import path from 'path'
import { v2Components } from './component-manifest.mjs'

const distDir = path.resolve(process.cwd(), 'dist')
const v2ComponentsDir = path.join(distDir, 'v2', 'components')
Expand All @@ -9,21 +10,31 @@ if (!existsSync(v2ComponentsDir)) {
throw new Error(`Missing expected directory: ${v2ComponentsDir}`)
}

const componentDirs = readdirSync(v2ComponentsDir).filter(name => {
const manifestComponents = v2Components.map(({ sourceDir, sourcePath = sourceDir }) => ({
publicDir: sourceDir,
sourcePath
}))

const fallbackComponentDirs = readdirSync(v2ComponentsDir).filter(name => {
const fullPath = path.join(v2ComponentsDir, name)
return statSync(fullPath).isDirectory()
})
}).map(name => ({
publicDir: name,
sourcePath: name
}))

const componentDirs = manifestComponents.length > 0 ? manifestComponents : fallbackComponentDirs

for (const componentDir of componentDirs) {
const sourceIndex = path.join(v2ComponentsDir, componentDir, 'index.d.ts')
for (const { publicDir, sourcePath } of componentDirs) {
const sourceIndex = path.join(v2ComponentsDir, sourcePath, 'index.d.ts')
if (!existsSync(sourceIndex)) {
continue
}

const outputDir = path.join(publicComponentsDir, componentDir)
const outputDir = path.join(publicComponentsDir, publicDir)
mkdirSync(outputDir, { recursive: true })

const relativePath = path.relative(outputDir, path.join(v2ComponentsDir, componentDir, 'index.d.ts'))
const relativePath = path.relative(outputDir, sourceIndex)
.replace(/\\/g, '/')
.replace(/\.d\.ts$/, '')

Expand Down
64 changes: 64 additions & 0 deletions scripts/component-manifest.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
export const v2Components = [
{
sourceDir: 'header',
sourcePath: 'layout/header',
exportNames: ['header', 'layout/header']
},
{
sourceDir: 'loginButton',
sourcePath: 'auth/loginButton',
exportNames: ['loginButton', 'login-button', 'auth/login-button']
},
{
sourceDir: 'signupButton',
sourcePath: 'auth/signupButton',
exportNames: ['auth/signup-button', 'signup-button']
},
{
sourceDir: 'photoCapture',
sourcePath: 'media/photoCapture',
exportNames: ['media/photo-capture', 'photo-capture']
},
{
sourceDir: 'button',
sourcePath: 'actions/button',
exportNames: ['actions/button', 'button']
},
{
sourceDir: 'footer',
sourcePath: 'layout/footer',
exportNames: ['footer', 'layout/footer']
},
{
sourceDir: 'select',
sourcePath: 'forms/select',
exportNames: ['forms/select', 'select']
},
{
sourceDir: 'combobox',
sourcePath: 'forms/combobox',
exportNames: ['forms/combobox', 'combobox']
}
]

export const componentEntries = Object.fromEntries(
v2Components.map(({ sourceDir, sourcePath = sourceDir }) => [
sourceDir,
{
import: `./src/v2/components/${sourcePath}/index.ts`
}
])
)

export const componentExports = Object.fromEntries(
v2Components.flatMap(({ sourceDir, exportNames }) =>
exportNames.map(exportName => [
`./components/${exportName}`,
{
types: `./dist/components/${sourceDir}/index.d.ts`,
import: `./dist/components/${sourceDir}/index.esm.js`,
require: `./dist/components/${sourceDir}/index.js`
}
])
)
)
17 changes: 17 additions & 0 deletions scripts/sync-component-exports.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { readFileSync, writeFileSync } from 'fs'
import path from 'path'
import { componentExports } from './component-manifest.mjs'

const packageJsonPath = path.resolve(process.cwd(), 'package.json')
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'))

const preservedExports = Object.fromEntries(
Object.entries(packageJson.exports || {}).filter(([subpath]) => !subpath.startsWith('./components/'))
)

packageJson.exports = {
...preservedExports,
...componentExports
}

writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`)
58 changes: 58 additions & 0 deletions scripts/watch-component-exports.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { spawn } from 'child_process'
import { existsSync, watch } from 'fs'
import path from 'path'

const scriptsDir = path.resolve(process.cwd(), 'scripts')
const manifestFile = 'component-manifest.mjs'
const syncScript = path.resolve(scriptsDir, 'sync-component-exports.mjs')
let syncTimer = null
let running = false
let rerunRequested = false

const runSync = () => {
if (running) {
rerunRequested = true
return
}

running = true
rerunRequested = false

const child = spawn(process.execPath, [syncScript], {
stdio: 'inherit'
})

child.on('exit', code => {
running = false

if (code !== 0) {
console.error(`sync-component-exports exited with code ${code}`)
}

if (rerunRequested) {
runSync()
}
})
}

const scheduleSync = () => {
clearTimeout(syncTimer)
syncTimer = setTimeout(() => {
runSync()
}, 150)
}

if (!existsSync(scriptsDir)) {
throw new Error(`Missing expected directory: ${scriptsDir}`)
}

console.log(`Watching ${path.join(scriptsDir, manifestFile)} for export manifest changes...`)

runSync()

watch(scriptsDir, (eventType, filename) => {
if (!filename) return
if (filename === manifestFile) {
scheduleSync()
}
})
2 changes: 1 addition & 1 deletion src/login/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,7 @@ export function selectWorkspace (
const box = dom.createElement('div')
const context: AuthenticationContext = { me, dom, div: box }

function say (s, background) {
function say (s, background?) {
box.appendChild(widgets.errorMessageBlock(dom, s, background))
}

Expand Down
Loading
Loading