@pyreon/ui-core 0.22.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +113 -77
  2. package/package.json +7 -7
package/README.md CHANGED
@@ -1,150 +1,186 @@
1
1
  # @pyreon/ui-core
2
2
 
3
- Shared foundation for the Pyreon UI System ecosystem.
3
+ Foundation layer for Pyreon's UI system — `PyreonUI` provider, config singleton, utilities.
4
4
 
5
- Provides utility functions, a styling engine bridge, theme context, and HTML tag definitions used across all `@pyreon` packages. No external utility dependenciesall implementations are built-in with prototype pollution protection where applicable.
5
+ `@pyreon/ui-core` is the cross-cutting layer that ties Pyreon's UI packages (`styler`, `unistyle`, `elements`, `rocketstyle`, `coolgrid`, …) together. It ships the unified `<PyreonUI>` provider (theme + light/dark/system mode in one component), the `config` styling-engine singleton (a thin facade over `@pyreon/styler`), helper utilities (`omit` / `pick` / `merge` / `compose` / `throttle` / `set` / `get` / `isEmpty` / `isEqual` / `hoistNonReactStatics`), and the canonical HTML tag arrays + types. No external utility depsevery helper is built-in with prototype-pollution protection where it matters.
6
6
 
7
- ## Installation
7
+ ## Install
8
8
 
9
9
  ```bash
10
- bun add @pyreon/ui-core
10
+ bun add @pyreon/ui-core @pyreon/core @pyreon/styler
11
11
  ```
12
12
 
13
- ## API
13
+ ## Quick start
14
14
 
15
- ### Provider & Context
15
+ ```tsx
16
+ import { PyreonUI, useMode } from '@pyreon/ui-core'
17
+ import { theme } from '@pyreon/ui-theme'
16
18
 
17
- ```ts
18
- import { Provider, context, config, init } from '@pyreon/ui-core'
19
+ <PyreonUI theme={theme} mode="system">
20
+ <App />
21
+ </PyreonUI>
22
+
23
+ // In any descendant:
24
+ function ModeAware() {
25
+ const mode = useMode() // "light" | "dark" — reactive
26
+ return <span>Current mode: {mode}</span>
27
+ }
28
+ ```
29
+
30
+ ## `<PyreonUI>` — unified provider
31
+
32
+ Single provider that replaces the previous trio of theme / mode / config providers.
33
+
34
+ ```tsx
35
+ <PyreonUI
36
+ theme={theme} // Theme object (breakpoints, rootSize, custom keys)
37
+ mode="light" // "light" | "dark" | "system" — also accepts () => mode
38
+ inversed // Flip mode for a nested section (dark sidebar in light app)
39
+ >
40
+ <App />
41
+ </PyreonUI>
19
42
  ```
20
43
 
21
- **Provider** wraps your app with a theme context. It bridges `@pyreon/styler`'s theming with the internal context system.
44
+ - `mode="system"` auto-detects OS dark mode via `matchMedia('(prefers-color-scheme: dark)')` and updates reactively when the user changes their OS preference.
45
+ - `inversed` — flips the resolved mode for the subtree (e.g. a dark hero on a light page).
46
+ - `mode` accepts a signal accessor — `<PyreonUI mode={() => userPref()}>` re-renders the entire subtree's CSS on change WITHOUT remounting (the theme is wrapped in `computed()`).
47
+ - `PyreonUI` calls `init()` internally — you don't normally need to call it yourself.
48
+
49
+ `PyreonUI` is marked `nativeCompat` so it works correctly inside compat-mode apps (`@pyreon/{react,preact,vue,solid,svelte}-compat`) — its `provide()` calls land in Pyreon's setup frame, not inside the compat wrapper.
50
+
51
+ ## `useMode()`
52
+
53
+ Reactive — returns `'light'` or `'dark'`, updates on `mode` prop changes AND OS preference changes (when `mode="system"`).
22
54
 
23
55
  ```ts
24
- import { Provider } from '@pyreon/ui-core'
25
-
26
- Provider({
27
- theme: { rootSize: 16, breakpoints: { xs: 0, md: 768 } },
28
- children: [
29
- /* your app */
30
- ],
31
- })
56
+ const mode = useMode()
32
57
  ```
33
58
 
34
- **config**the styling engine singleton. Pyreon uses `@pyreon/styler` directly — no connector abstraction needed.
59
+ ## `config` — styling engine singleton
35
60
 
36
61
  ```ts
37
62
  import { config } from '@pyreon/ui-core'
38
63
 
39
- // Access the engine from anywhere
40
64
  const { styled, css, keyframes } = config
41
65
  ```
42
66
 
43
- ### Utilities
67
+ Pyreon uses `@pyreon/styler` directly — `config` is a thin facade exposed for symmetry across the UI packages. `init()` is the (idempotent) bootstrap call `PyreonUI` invokes internally.
44
68
 
45
- #### compose
69
+ ## Utility helpers
46
70
 
47
- Right-to-left function composition.
71
+ ### `compose(...fns)` — right-to-left function composition
48
72
 
49
73
  ```ts
50
- import { compose } from '@pyreon/ui-core'
51
-
52
74
  const transform = compose(toUpperCase, trim, normalize)
53
- transform(' hello ') // => 'HELLO'
75
+ transform(' hello ') // 'HELLO'
54
76
  ```
55
77
 
56
- #### render
78
+ ### `render(value)` — flexible value/element renderer
57
79
 
58
- Flexible element renderer. Handles components, elements, primitives, and arrays.
80
+ Handles components, primitives, arrays, null. Useful when authoring helper components that accept a polymorphic content prop.
59
81
 
60
82
  ```ts
61
- import { render } from '@pyreon/ui-core'
62
-
63
- render('hello') // => 'hello'
64
- render(MyComponent) // => MyComponent({})
65
- render(null) // => null
83
+ render('hello') // 'hello'
84
+ render(MyComponent) // MyComponent({})
85
+ render(null) // null
66
86
  ```
67
87
 
68
- #### isEmpty
88
+ ### `isEmpty(value)` / `isEqual(a, b)`
69
89
 
70
- Type-safe emptiness check. Returns `true` for `null`, `undefined`, `{}`, `[]`, and non-object primitives.
90
+ Type-safe checks. `isEmpty` returns `true` for `null`, `undefined`, `{}`, `[]`, and non-object primitives. `isEqual` performs a deep structural compare.
71
91
 
72
92
  ```ts
73
- import { isEmpty } from '@pyreon/ui-core'
74
-
75
- isEmpty({}) // => true
76
- isEmpty([]) // => true
77
- isEmpty(null) // => true
78
- isEmpty({ a: 1 }) // => false
93
+ isEmpty({}) // true
94
+ isEmpty([]) // true
95
+ isEmpty({ a: 1 }) // false
96
+ isEqual({a:1}, {a:1}) // true
79
97
  ```
80
98
 
81
- #### omit / pick
99
+ ### `omit(obj, keys)` / `pick(obj, keys)`
82
100
 
83
- Create objects without or with only specified keys. Accept nullable inputs. `omit()` also accepts a pre-built `Set<string>` for hot paths where the same key list is reused across many calls (avoids per-call Set allocation).
101
+ Object key filtering. Accept nullable inputs. **`omit()` also accepts a pre-built `Set<string>`** for hot paths where the same key list is reused across many calls (used by rocketstyle to avoid per-mount Set allocation):
84
102
 
85
103
  ```ts
86
- import { omit, pick } from '@pyreon/ui-core'
87
-
88
- omit({ a: 1, b: 2, c: 3 }, ['b']) // => { a: 1, c: 3 }
89
- pick({ a: 1, b: 2, c: 3 }, ['a', 'b']) // => { a: 1, b: 2 }
104
+ omit({ a:1, b:2, c:3 }, ['b']) // { a:1, c:3 }
105
+ pick({ a:1, b:2, c:3 }, ['a','b']) // { a:1, b:2 }
90
106
 
91
- // Hot-path usage with pre-built Set (used by rocketstyle):
92
- const omitSet = new Set(['b', 'c'])
93
- omit({ a: 1, b: 2, c: 3 }, omitSet) // => { a: 1 }
107
+ const omitSet = new Set(['b','c'])
108
+ omit({ a:1, b:2, c:3 }, omitSet) // { a:1 }
94
109
  ```
95
110
 
96
- #### set / get
111
+ ### `set(obj, path, value)` / `get(obj, path, default?)`
97
112
 
98
- Nested property access and mutation by dot/bracket path. `set` has built-in prototype pollution protection keys like `__proto__`, `constructor`, and `prototype` are blocked.
113
+ Nested property access by dot/bracket path. **`set` blocks prototype pollution** — `__proto__`, `constructor`, `prototype` keys are silently ignored.
99
114
 
100
115
  ```ts
101
- import { set, get } from '@pyreon/ui-core'
102
-
103
- const obj = {}
104
- set(obj, 'a.b.c', 42) // => { a: { b: { c: 42 } } }
105
- get(obj, 'a.b.c') // => 42
106
- get(obj, 'a.x', 'default') // => 'default'
116
+ const o = {}
117
+ set(o, 'a.b.c', 42) // { a: { b: { c: 42 } } }
118
+ get(o, 'a.b.c') // 42
119
+ get(o, 'a.x', 'default') // 'default'
107
120
  ```
108
121
 
109
- #### merge
122
+ ### `merge(...objects)` — left-to-right deep merge
110
123
 
111
- Deep merge objects left-to-right. Only plain objects are recursed into; arrays are replaced wholesale. Prototype pollution keys are blocked.
124
+ Only plain objects are recursed; arrays are replaced wholesale. Prototype-pollution keys are blocked.
112
125
 
113
126
  ```ts
114
- import { merge } from '@pyreon/ui-core'
127
+ merge({ a: { x: 1 } }, { a: { y: 2 } }) // { a: { x: 1, y: 2 } }
128
+ ```
129
+
130
+ ### `throttle(fn, ms)`
131
+
132
+ Limits execution to at most once per window. Returns a function with `.cancel()`.
115
133
 
116
- merge({ a: { x: 1 } }, { a: { y: 2 } }) // => { a: { x: 1, y: 2 } }
134
+ ```ts
135
+ const onResize = throttle(handleResize, 200)
136
+ window.addEventListener('resize', onResize)
137
+ // onResize.cancel()
117
138
  ```
118
139
 
119
- #### throttle
140
+ ### `useStableValue(value)`
141
+
142
+ Returns a stable accessor that emits only when the input changes by `isEqual`. Useful for deriving signal-shaped values from props that may re-construct identical objects every render.
143
+
144
+ ### `hoistNonReactStatics(target, source)`
120
145
 
121
- Limits function execution to at most once per wait period. Returns a throttled function with a `.cancel()` method.
146
+ Standard "hoist non-React statics" copy used by HOC factories so wrapped components retain their static methods + display names.
147
+
148
+ ## HTML tag constants
122
149
 
123
150
  ```ts
124
- import { throttle } from '@pyreon/ui-core'
151
+ import { HTML_TAGS, HTML_TEXT_TAGS, HTMLTags, HTMLTextTags } from '@pyreon/ui-core'
125
152
 
126
- const throttled = throttle(handleResize, 200)
127
- window.addEventListener('resize', throttled)
128
- // cleanup: throttled.cancel()
153
+ HTML_TAGS // array of 100+ valid HTML tag names
154
+ HTML_TEXT_TAGS // text-content tags: h1-h6, p, span, strong, em, ...
155
+ type Tag = HTMLTags // narrowed union type
156
+ type TextTag = HTMLTextTags
129
157
  ```
130
158
 
131
- ### HTML Constants
159
+ Used by `@pyreon/elements` to constrain the `tag` prop and by the styler's `as` polymorphism.
160
+
161
+ ## Types
132
162
 
133
163
  ```ts
134
- import { HTML_TAGS, HTML_TEXT_TAGS } from '@pyreon/ui-core'
164
+ import type {
165
+ BreakpointKeys, Breakpoints,
166
+ CoreContextValue,
167
+ HTMLElementAttrs, HTMLTagAttrsByTag, HTMLTags, HTMLTextTags,
168
+ IsEmpty,
169
+ PyreonUIProps, ThemeMode, ThemeModeInput,
170
+ Render,
171
+ } from '@pyreon/ui-core'
135
172
  ```
136
173
 
137
- - **HTML_TAGS** — array of 100+ valid HTML tag names
138
- - **HTML_TEXT_TAGS** — array of text-content tags (h1–h6, p, span, strong, em, etc.)
174
+ ## Gotchas
139
175
 
140
- Both have corresponding TypeScript union types: `HTMLTags` and `HTMLTextTags`.
176
+ - **`<PyreonUI>` is the canonical app-root provider.** Internal sub-providers (`CoreProvider` / `UnistyleProvider` / `ThemeProvider` / `Provider` from rocketstyle / `OverlayContextProvider`) are marked `nativeCompat` even though they're internal — never mount a sub-provider manually if `<PyreonUI>` is already in the tree.
177
+ - **`set` / `merge` silently drop prototype-pollution keys.** If a user-supplied key matches `__proto__` / `constructor` / `prototype` it is ignored. Don't rely on these utilities for arbitrary JSON merging that might intentionally need those keys.
178
+ - **`omit` with a Set is faster than with an array** only at scale. For one-off calls the array form is fine.
179
+ - **`useMode()` is reactive — call inside a tracking scope** (JSX expression, effect, computed). Reading at component setup top-level captures the initial value.
141
180
 
142
- ## Peer Dependencies
181
+ ## Documentation
143
182
 
144
- | Package | Version |
145
- | -------------- | -------- |
146
- | @pyreon/core | >= 0.0.1 |
147
- | @pyreon/styler | >= 0.0.1 |
183
+ Full docs: [docs.pyreon.dev/docs/ui-core](https://docs.pyreon.dev/docs/ui-core) (or `docs/docs/ui-core.md` in this repo).
148
184
 
149
185
  ## License
150
186
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyreon/ui-core",
3
- "version": "0.22.0",
3
+ "version": "0.24.0",
4
4
  "description": "Core utilities, config, and context for Pyreon UI System",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -38,16 +38,16 @@
38
38
  },
39
39
  "devDependencies": {
40
40
  "@pyreon/manifest": "0.13.1",
41
- "@pyreon/typescript": "^0.22.0",
42
- "@vitus-labs/tools-rolldown": "^2.3.0"
41
+ "@pyreon/typescript": "^0.24.0",
42
+ "@vitus-labs/tools-rolldown": "^2.4.0"
43
43
  },
44
44
  "engines": {
45
45
  "node": ">= 22"
46
46
  },
47
47
  "dependencies": {
48
- "@pyreon/core": "^0.22.0",
49
- "@pyreon/reactivity": "^0.22.0",
50
- "@pyreon/styler": "^0.22.0",
51
- "@pyreon/unistyle": "^0.22.0"
48
+ "@pyreon/core": "^0.24.0",
49
+ "@pyreon/reactivity": "^0.24.0",
50
+ "@pyreon/styler": "^0.24.0",
51
+ "@pyreon/unistyle": "^0.24.0"
52
52
  }
53
53
  }