@owlmeans/web-panel 0.1.18-rc.4 → 0.1.18-rc.41
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.
- package/README.md +415 -84
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/web-panel/SKILL.md +419 -8
- package/build/@/components/ui/alert-dialog.d.ts +15 -0
- package/build/@/components/ui/alert-dialog.d.ts.map +1 -0
- package/build/@/components/ui/alert-dialog.js +39 -0
- package/build/@/components/ui/alert-dialog.js.map +1 -0
- package/build/@/components/ui/alert.js +1 -1
- package/build/@/components/ui/alert.js.map +1 -1
- package/build/@/components/ui/button.js +1 -1
- package/build/@/components/ui/button.js.map +1 -1
- package/build/@/components/ui/card.js +1 -1
- package/build/@/components/ui/card.js.map +1 -1
- package/build/@/components/ui/dropdown-menu.d.ts +26 -0
- package/build/@/components/ui/dropdown-menu.d.ts.map +1 -0
- package/build/@/components/ui/dropdown-menu.js +51 -0
- package/build/@/components/ui/dropdown-menu.js.map +1 -0
- package/build/@/components/ui/input.js +1 -1
- package/build/@/components/ui/input.js.map +1 -1
- package/build/@/components/ui/label.js +1 -1
- package/build/@/components/ui/label.js.map +1 -1
- package/build/@/components/ui/navigation-menu.d.ts +15 -0
- package/build/@/components/ui/navigation-menu.d.ts.map +1 -0
- package/build/@/components/ui/navigation-menu.js +32 -0
- package/build/@/components/ui/navigation-menu.js.map +1 -0
- package/build/@/components/ui/progress.js +1 -1
- package/build/@/components/ui/progress.js.map +1 -1
- package/build/@/components/ui/separator.js +1 -1
- package/build/@/components/ui/separator.js.map +1 -1
- package/build/auth/context.d.ts.map +1 -1
- package/build/auth/context.js +0 -1
- package/build/auth/context.js.map +1 -1
- package/build/auth/entrypoints.d.ts +10 -0
- package/build/auth/entrypoints.d.ts.map +1 -0
- package/build/auth/entrypoints.js +4 -0
- package/build/auth/entrypoints.js.map +1 -0
- package/build/auth/exports.d.ts +3 -3
- package/build/auth/exports.d.ts.map +1 -1
- package/build/auth/exports.js +3 -3
- package/build/auth/exports.js.map +1 -1
- package/build/auth/plugins/basic-ed25519.js +4 -4
- package/build/auth/plugins/basic-ed25519.js.map +1 -1
- package/build/auth/plugins/re-captcha.js +3 -3
- package/build/auth/plugins/re-captcha.js.map +1 -1
- package/build/auth/plugins/tunnel-consumer.js +1 -1
- package/build/auth/plugins/tunnel-consumer.js.map +1 -1
- package/build/components/block.js +2 -2
- package/build/components/block.js.map +1 -1
- package/build/components/footer/component.d.ts +12 -0
- package/build/components/footer/component.d.ts.map +1 -0
- package/build/components/footer/component.js +24 -0
- package/build/components/footer/component.js.map +1 -0
- package/build/components/footer/index.d.ts +3 -0
- package/build/components/footer/index.d.ts.map +1 -0
- package/build/components/footer/index.js +2 -0
- package/build/components/footer/index.js.map +1 -0
- package/build/components/footer/types.d.ts +11 -0
- package/build/components/footer/types.d.ts.map +1 -0
- package/build/components/footer/types.js +2 -0
- package/build/components/footer/types.js.map +1 -0
- package/build/components/form/button/component.js +2 -2
- package/build/components/form/button/component.js.map +1 -1
- package/build/components/form/component.js +2 -2
- package/build/components/form/component.js.map +1 -1
- package/build/components/form/text/component.js +2 -2
- package/build/components/form/text/component.js.map +1 -1
- package/build/components/index.d.ts +5 -0
- package/build/components/index.d.ts.map +1 -1
- package/build/components/index.js +5 -0
- package/build/components/index.js.map +1 -1
- package/build/components/link.js +3 -3
- package/build/components/link.js.map +1 -1
- package/build/components/login/append.d.ts +22 -0
- package/build/components/login/append.d.ts.map +1 -0
- package/build/components/login/append.js +26 -0
- package/build/components/login/append.js.map +1 -0
- package/build/components/login/credit.d.ts +17 -0
- package/build/components/login/credit.d.ts.map +1 -0
- package/build/components/login/credit.js +17 -0
- package/build/components/login/credit.js.map +1 -0
- package/build/components/login/icons.d.ts +14 -0
- package/build/components/login/icons.d.ts.map +1 -0
- package/build/components/login/icons.js +29 -0
- package/build/components/login/icons.js.map +1 -0
- package/build/components/login/index.d.ts +7 -0
- package/build/components/login/index.d.ts.map +1 -0
- package/build/components/login/index.js +7 -0
- package/build/components/login/index.js.map +1 -0
- package/build/components/login/notify.d.ts +18 -0
- package/build/components/login/notify.d.ts.map +1 -0
- package/build/components/login/notify.js +42 -0
- package/build/components/login/notify.js.map +1 -0
- package/build/components/login/screen.d.ts +23 -0
- package/build/components/login/screen.d.ts.map +1 -0
- package/build/components/login/screen.js +73 -0
- package/build/components/login/screen.js.map +1 -0
- package/build/components/login/terms.d.ts +9 -0
- package/build/components/login/terms.d.ts.map +1 -0
- package/build/components/login/terms.js +33 -0
- package/build/components/login/terms.js.map +1 -0
- package/build/components/menu/component.d.ts +15 -0
- package/build/components/menu/component.d.ts.map +1 -0
- package/build/components/menu/component.js +146 -0
- package/build/components/menu/component.js.map +1 -0
- package/build/components/menu/index.d.ts +3 -0
- package/build/components/menu/index.d.ts.map +1 -0
- package/build/components/menu/index.js +3 -0
- package/build/components/menu/index.js.map +1 -0
- package/build/components/menu/types.d.ts +138 -0
- package/build/components/menu/types.d.ts.map +1 -0
- package/build/components/menu/types.js +17 -0
- package/build/components/menu/types.js.map +1 -0
- package/build/components/nav/index.d.ts +5 -0
- package/build/components/nav/index.d.ts.map +1 -0
- package/build/components/nav/index.js +4 -0
- package/build/components/nav/index.js.map +1 -0
- package/build/components/nav/layout.d.ts +12 -0
- package/build/components/nav/layout.d.ts.map +1 -0
- package/build/components/nav/layout.js +40 -0
- package/build/components/nav/layout.js.map +1 -0
- package/build/components/nav/side.d.ts +11 -0
- package/build/components/nav/side.d.ts.map +1 -0
- package/build/components/nav/side.js +25 -0
- package/build/components/nav/side.js.map +1 -0
- package/build/components/nav/top.d.ts +10 -0
- package/build/components/nav/top.d.ts.map +1 -0
- package/build/components/nav/top.js +44 -0
- package/build/components/nav/top.js.map +1 -0
- package/build/components/nav/types.d.ts +55 -0
- package/build/components/nav/types.d.ts.map +1 -0
- package/build/components/nav/types.js +2 -0
- package/build/components/nav/types.js.map +1 -0
- package/build/components/panel-app/component.d.ts.map +1 -1
- package/build/components/panel-app/component.js +4 -3
- package/build/components/panel-app/component.js.map +1 -1
- package/build/components/socket/i18n/be.json +7 -0
- package/build/components/socket/i18n/de.json +7 -0
- package/build/components/socket/i18n/en.json +7 -0
- package/build/components/socket/i18n/es.json +7 -0
- package/build/components/socket/i18n/pl.json +7 -0
- package/build/components/socket/i18n/ru.json +7 -0
- package/build/components/socket/i18n/uk.json +7 -0
- package/build/components/socket/i18n.d.ts +2 -0
- package/build/components/socket/i18n.d.ts.map +1 -0
- package/build/components/socket/i18n.js +16 -0
- package/build/components/socket/i18n.js.map +1 -0
- package/build/components/socket/index.d.ts +3 -0
- package/build/components/socket/index.d.ts.map +1 -0
- package/build/components/socket/index.js +3 -0
- package/build/components/socket/index.js.map +1 -0
- package/build/components/socket/reload-dialog.d.ts +18 -0
- package/build/components/socket/reload-dialog.d.ts.map +1 -0
- package/build/components/socket/reload-dialog.js +26 -0
- package/build/components/socket/reload-dialog.js.map +1 -0
- package/build/components/status.js +1 -1
- package/build/components/status.js.map +1 -1
- package/build/components/text.js +1 -1
- package/build/components/text.js.map +1 -1
- package/build/components/toaster.d.ts +16 -0
- package/build/components/toaster.d.ts.map +1 -0
- package/build/components/toaster.js +52 -0
- package/build/components/toaster.js.map +1 -0
- package/build/components/uploader/image.js +1 -1
- package/build/components/uploader/image.js.map +1 -1
- package/build/consent/component.d.ts +16 -0
- package/build/consent/component.d.ts.map +1 -0
- package/build/consent/component.js +39 -0
- package/build/consent/component.js.map +1 -0
- package/build/consent/consts.d.ts +2 -0
- package/build/consent/consts.d.ts.map +1 -0
- package/build/consent/consts.js +2 -0
- package/build/consent/consts.js.map +1 -0
- package/build/consent/index.d.ts +9 -0
- package/build/consent/index.d.ts.map +1 -0
- package/build/consent/index.js +7 -0
- package/build/consent/index.js.map +1 -0
- package/build/consent/menu-widget.d.ts +13 -0
- package/build/consent/menu-widget.d.ts.map +1 -0
- package/build/consent/menu-widget.js +20 -0
- package/build/consent/menu-widget.js.map +1 -0
- package/build/consent/presence.d.ts +24 -0
- package/build/consent/presence.d.ts.map +1 -0
- package/build/consent/presence.js +34 -0
- package/build/consent/presence.js.map +1 -0
- package/build/consent/service.d.ts +18 -0
- package/build/consent/service.d.ts.map +1 -0
- package/build/consent/service.js +53 -0
- package/build/consent/service.js.map +1 -0
- package/build/consent/translate.d.ts +14 -0
- package/build/consent/translate.d.ts.map +1 -0
- package/build/consent/translate.js +28 -0
- package/build/consent/translate.js.map +1 -0
- package/build/consent/types.d.ts +19 -0
- package/build/consent/types.d.ts.map +1 -0
- package/build/consent/types.js +2 -0
- package/build/consent/types.js.map +1 -0
- package/build/context.d.ts.map +1 -1
- package/build/context.js +10 -0
- package/build/context.js.map +1 -1
- package/build/entrypoints.d.ts +8 -0
- package/build/entrypoints.d.ts.map +1 -0
- package/build/entrypoints.js +4 -0
- package/build/entrypoints.js.map +1 -0
- package/build/exports.d.ts +3 -4
- package/build/exports.d.ts.map +1 -1
- package/build/exports.js +3 -4
- package/build/exports.js.map +1 -1
- package/build/hooks/index.d.ts +2 -0
- package/build/hooks/index.d.ts.map +1 -0
- package/build/hooks/index.js +2 -0
- package/build/hooks/index.js.map +1 -0
- package/build/hooks/use-mobile.d.ts +16 -0
- package/build/hooks/use-mobile.d.ts.map +1 -0
- package/build/hooks/use-mobile.js +30 -0
- package/build/hooks/use-mobile.js.map +1 -0
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -1
- package/build/index.js.map +1 -1
- package/build/jobs/component.d.ts +19 -0
- package/build/jobs/component.d.ts.map +1 -0
- package/build/jobs/component.js +55 -0
- package/build/jobs/component.js.map +1 -0
- package/build/jobs/index.d.ts +4 -0
- package/build/jobs/index.d.ts.map +1 -0
- package/build/jobs/index.js +3 -0
- package/build/jobs/index.js.map +1 -0
- package/build/jobs/toasts.d.ts +14 -0
- package/build/jobs/toasts.d.ts.map +1 -0
- package/build/jobs/toasts.js +39 -0
- package/build/jobs/toasts.js.map +1 -0
- package/build/jobs/types.d.ts +21 -0
- package/build/jobs/types.d.ts.map +1 -0
- package/build/jobs/types.js +2 -0
- package/build/jobs/types.js.map +1 -0
- package/build/types.d.ts +11 -1
- package/build/types.d.ts.map +1 -1
- package/build/utils.d.ts +13 -0
- package/build/utils.d.ts.map +1 -0
- package/build/utils.js +14 -0
- package/build/utils.js.map +1 -0
- package/package.json +48 -33
- package/src/@/components/ui/alert-dialog.tsx +156 -0
- package/src/@/components/ui/alert.tsx +1 -1
- package/src/@/components/ui/button.tsx +1 -1
- package/src/@/components/ui/card.tsx +1 -1
- package/src/@/components/ui/input.tsx +1 -1
- package/src/@/components/ui/label.tsx +1 -1
- package/src/@/components/ui/navigation-menu.tsx +169 -0
- package/src/@/components/ui/progress.tsx +1 -1
- package/src/@/components/ui/separator.tsx +1 -1
- package/src/auth/context.ts +0 -2
- package/src/auth/entrypoints.ts +5 -0
- package/src/auth/exports.ts +3 -3
- package/src/auth/plugins/basic-ed25519.tsx +4 -4
- package/src/auth/plugins/re-captcha.tsx +3 -3
- package/src/auth/plugins/tunnel-consumer.tsx +1 -1
- package/src/components/block.tsx +2 -2
- package/src/components/footer/component.tsx +37 -0
- package/src/components/footer/index.ts +3 -0
- package/src/components/footer/types.ts +11 -0
- package/src/components/form/button/component.tsx +2 -2
- package/src/components/form/component.tsx +2 -2
- package/src/components/form/text/component.tsx +2 -2
- package/src/components/index.ts +5 -0
- package/src/components/link.tsx +3 -3
- package/src/components/login/append.ts +40 -0
- package/src/components/login/credit.tsx +34 -0
- package/src/components/login/icons.tsx +39 -0
- package/src/components/login/index.ts +6 -0
- package/src/components/login/notify.ts +53 -0
- package/src/components/login/screen.tsx +140 -0
- package/src/components/login/terms.tsx +74 -0
- package/src/components/nav/index.ts +5 -0
- package/src/components/nav/layout.tsx +85 -0
- package/src/components/nav/side.tsx +52 -0
- package/src/components/nav/top.tsx +62 -0
- package/src/components/nav/types.ts +56 -0
- package/src/components/panel-app/component.tsx +9 -1
- package/src/components/socket/i18n/be.json +7 -0
- package/src/components/socket/i18n/de.json +7 -0
- package/src/components/socket/i18n/en.json +7 -0
- package/src/components/socket/i18n/es.json +7 -0
- package/src/components/socket/i18n/pl.json +7 -0
- package/src/components/socket/i18n/ru.json +7 -0
- package/src/components/socket/i18n/uk.json +7 -0
- package/src/components/socket/i18n.ts +17 -0
- package/src/components/socket/index.ts +2 -0
- package/src/components/socket/reload-dialog.tsx +45 -0
- package/src/components/status.tsx +1 -1
- package/src/components/text.tsx +1 -1
- package/src/components/toaster.tsx +68 -0
- package/src/components/uploader/image.tsx +1 -1
- package/src/consent/component.tsx +49 -0
- package/src/consent/consts.ts +1 -0
- package/src/consent/index.ts +18 -0
- package/src/consent/menu-widget.tsx +22 -0
- package/src/consent/presence.ts +39 -0
- package/src/consent/service.ts +75 -0
- package/src/consent/translate.ts +31 -0
- package/src/consent/types.ts +21 -0
- package/src/context.ts +12 -0
- package/src/entrypoints.ts +5 -0
- package/src/exports.ts +3 -4
- package/src/index.ts +1 -1
- package/src/types.ts +11 -1
- package/tests/context.ts +43 -0
- package/tests/harness/index.html +11 -0
- package/tests/harness/mount.tsx +230 -0
- package/tests/login.spec.ts +188 -0
- package/tests/nav.spec.ts +252 -0
- package/tests/package-boundary.spec.ts +19 -0
- package/tests/smoke.spec.ts +5 -1
- package/tests/socket-dialog.spec.ts +69 -0
- package/tests/toaster.spec.ts +59 -0
- package/build/.gitkeep +0 -0
- package/build/auth/modules.d.ts +0 -2
- package/build/auth/modules.d.ts.map +0 -1
- package/build/auth/modules.js +0 -4
- package/build/auth/modules.js.map +0 -1
- package/build/modules.d.ts +0 -2
- package/build/modules.d.ts.map +0 -1
- package/build/modules.js +0 -4
- package/build/modules.js.map +0 -1
- package/src/auth/modules.ts +0 -5
- package/src/modules.ts +0 -5
package/README.md
CHANGED
|
@@ -1,51 +1,50 @@
|
|
|
1
1
|
# @owlmeans/web-panel
|
|
2
2
|
|
|
3
|
-
shadcn UI + Tailwind v4 implementation of the OwlMeans web panel layer.
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
shadcn UI + Tailwind v4 implementation of the OwlMeans web panel layer. It wraps the headless
|
|
4
|
+
`@owlmeans/client-panel` logic and `@owlmeans/web-client`'s context with shadcn primitives: the base
|
|
5
|
+
context factory, the navigation shell, forms, panels, the sign-in screen, toasts and the socket
|
|
6
|
+
reload prompt. New OwlMeans web apps build on it. An app that renders only its own components and
|
|
7
|
+
needs no panel can stay on [`@owlmeans/web-client`](../web-client). Material UI apps already on
|
|
8
|
+
[`@owlmeans/mui-panel`](../mui-panel) are legacy: the same surface rendered with MUI, maintained
|
|
9
|
+
but never started.
|
|
6
10
|
|
|
7
|
-
##
|
|
8
|
-
|
|
9
|
-
- New OwlMeans web apps where the consumer owns its shadcn primitives and
|
|
10
|
-
Tailwind v4 theme.
|
|
11
|
-
- Drop-in replacement for the previous Material-UI based `@owlmeans/web-panel`
|
|
12
|
-
(which has been renamed to [`@owlmeans/mui-panel`](../mui-panel) for
|
|
13
|
-
backwards-compatibility consumers).
|
|
14
|
-
|
|
15
|
-
## Install
|
|
11
|
+
## Installation
|
|
16
12
|
|
|
17
13
|
```sh
|
|
18
|
-
bun add @owlmeans/web-panel
|
|
14
|
+
bun add @owlmeans/web-panel@^0.1.18-rc.31
|
|
19
15
|
```
|
|
20
16
|
|
|
21
17
|
Peer requirements (the consuming app provides these): `react`, `react-dom`,
|
|
22
18
|
`react-hook-form`, `ajv`, `tailwindcss@^4`, `lucide-react`, `clsx`,
|
|
23
19
|
`tailwind-merge`, `class-variance-authority`, plus the radix primitives
|
|
24
|
-
listed in `peerDependencies
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
This package imports its shadcn primitives as `@/components/ui/<name>` and
|
|
29
|
-
its utility as `@/lib/utils`. Build emits these specifiers verbatim
|
|
30
|
-
(TypeScript `moduleResolution: Bundler`). The consumer's bundler must
|
|
31
|
-
resolve `@/*` to its own shadcn primitive copy.
|
|
32
|
-
|
|
33
|
-
### 1. Add the package's primitives to your app
|
|
20
|
+
listed in `peerDependencies` (`alert-dialog`, `label`, `navigation-menu`, `progress`, `separator`,
|
|
21
|
+
`slot`). Also declare `ajv-formats` next to `ajv`: the form model imports it, but no dependency
|
|
22
|
+
section of this manifest lists it.
|
|
34
23
|
|
|
35
|
-
|
|
36
|
-
from this package's `src/@/components/ui/` or use the shadcn CLI:
|
|
24
|
+
## Concepts
|
|
37
25
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
26
|
+
- **Panel context** — `makeContext(cfg)` is `@owlmeans/web-client`'s context plus
|
|
27
|
+
`apiConfigMiddleware`, the flow service (`context.flow()`), the socket-status service and the
|
|
28
|
+
default shadcn sign-in screen. The app factory calls it and appends its own mixins.
|
|
29
|
+
- **Package boundary** — the shadcn primitives and `cn` are private files under the package's own
|
|
30
|
+
`build/@/` tree, imported relatively. A consumer needs no `@` alias and vendors nothing. It
|
|
31
|
+
supplies the peers, its Tailwind theme tokens and an `@source` line for this package.
|
|
32
|
+
- **Two-layer navigation** — the top menu lists sections and the side menu lists the active
|
|
33
|
+
section's screens. A section holding a single screen renders no side menu. The model
|
|
34
|
+
(`usePanelNav`, `PanelNav*`) is headless in `@owlmeans/client-panel`.
|
|
35
|
+
- **Style slots** — `className` (page root), `headerClassName` (sticky bar), `contentClassName`
|
|
36
|
+
and `containerClassName` (width and padding of all three rows) are merged over their defaults
|
|
37
|
+
with tailwind-merge, never substituted.
|
|
38
|
+
- **Global overlays** — `PanelApp` renders its `children` and `SocketReloadDialog` beside the
|
|
39
|
+
router, so a dialog survives navigation.
|
|
40
|
+
- **`translate` props** — the navigation and login components take a
|
|
41
|
+
`(key, defaultValue) => string` resolver as a prop instead of reading an i18n provider implicitly.
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
`baseColor: neutral`. See `components.json` for the exact config.
|
|
43
|
+
## Usage
|
|
44
44
|
|
|
45
|
-
###
|
|
45
|
+
### Consumer setup — theme tokens and `@source`
|
|
46
46
|
|
|
47
|
-
The components
|
|
48
|
-
`@theme` in your app's globals.css):
|
|
47
|
+
The components paint with these CSS variables, defined inside `@theme` in the app's globals.css:
|
|
49
48
|
|
|
50
49
|
- `--color-background`, `--color-foreground`
|
|
51
50
|
- `--color-card`, `--color-card-foreground`
|
|
@@ -60,67 +59,399 @@ The components rely on the following CSS variables (defined inside
|
|
|
60
59
|
- `--animate-progress-indeterminate` + the `@keyframes progress-indeterminate`
|
|
61
60
|
rule for the `Progress` component's indeterminate mode
|
|
62
61
|
|
|
63
|
-
A working set is shipped at `src/@/globals.css` (for dev/test only).
|
|
62
|
+
A working set is shipped at `src/@/globals.css` (for dev/test only). Toasts additionally read
|
|
63
|
+
`--popover`, `--popover-foreground` and `--border`.
|
|
64
|
+
|
|
65
|
+
Tailwind's scanner reads your CSS root plus its `@source` directives, and it excludes
|
|
66
|
+
`node_modules`. Classes that exist only inside this package's components — the whole navigation
|
|
67
|
+
shell and footer — therefore never reach your stylesheet unless you point Tailwind at the
|
|
68
|
+
installed package's `src`:
|
|
69
|
+
|
|
70
|
+
```css
|
|
71
|
+
@import "tailwindcss";
|
|
72
|
+
|
|
73
|
+
@source "../../../node_modules/@owlmeans/web-panel/src";
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Adjust the relative depth to your own layout. An app using `./consent` adds a second `@source` for
|
|
77
|
+
`@owlmeans/web-consent`'s `src`.
|
|
64
78
|
|
|
65
|
-
###
|
|
79
|
+
### 1. Config and context factory
|
|
66
80
|
|
|
67
81
|
```ts
|
|
68
|
-
//
|
|
69
|
-
|
|
82
|
+
// src/config.ts
|
|
83
|
+
import { config } from '@owlmeans/web-panel'
|
|
84
|
+
import { commonConfig, MY_APP_WEB } from 'my-app-common'
|
|
85
|
+
import type { Config } from './types.js'
|
|
86
|
+
|
|
87
|
+
const cfg: Config = config(MY_APP_WEB, commonConfig as Config)
|
|
88
|
+
// Block the screen with a "reload the page" prompt once every socket has given up reconnecting.
|
|
89
|
+
cfg.socket = { ...cfg.socket, reloadDialog: true }
|
|
90
|
+
|
|
91
|
+
export default cfg
|
|
70
92
|
```
|
|
71
93
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
Consumers that need the previous MUI behaviour should swap to
|
|
100
|
-
[`@owlmeans/mui-panel`](../mui-panel) — same exports, MUI-rendered.
|
|
101
|
-
|
|
102
|
-
## Public exports
|
|
94
|
+
```ts
|
|
95
|
+
// src/context.ts
|
|
96
|
+
import { appendLoginScreen, makeContext as makePanelContext, useContext as usePanelContext } from '@owlmeans/web-panel'
|
|
97
|
+
import type { AppConfig, AppContext } from '@owlmeans/web-panel'
|
|
98
|
+
import { appendConsentWidgetService } from '@owlmeans/web-panel/consent'
|
|
99
|
+
import { appendStateResource } from '@owlmeans/state'
|
|
100
|
+
import { BrandMark } from './components/brand.js'
|
|
101
|
+
import { PROJECT_STATE } from './consts.js'
|
|
102
|
+
|
|
103
|
+
export interface Config extends AppConfig {}
|
|
104
|
+
export interface Context<C extends Config = Config> extends AppContext<C> {}
|
|
105
|
+
|
|
106
|
+
export const useContext = (): Context => usePanelContext<Config, Context>()
|
|
107
|
+
|
|
108
|
+
export const makeContext = <C extends Config, T extends Context<C>>(cfg: C): T => {
|
|
109
|
+
const context = makePanelContext<C, T>(cfg)
|
|
110
|
+
// Idempotent: makeContext registered the default screen; this puts the app's mark on it.
|
|
111
|
+
appendLoginScreen<C, T>(context, { Logo: BrandMark })
|
|
112
|
+
appendConsentWidgetService<C, T>(context)
|
|
113
|
+
appendStateResource<C, T>(context, PROJECT_STATE)
|
|
114
|
+
|
|
115
|
+
return context
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 2. Entrypoints and mounting with overlays
|
|
103
120
|
|
|
104
121
|
```ts
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
} from '
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
122
|
+
// src/entrypoints.ts
|
|
123
|
+
import { bindAll, bindScreen, entrypoints as panelEntrypoints, handler } from '@owlmeans/web-panel'
|
|
124
|
+
import { entrypoints as authEntrypoints } from '@owlmeans/client-auth'
|
|
125
|
+
import { apiProtocols, webProtocols } from 'my-app-common'
|
|
126
|
+
import { MainLayout } from './layout/main.js'
|
|
127
|
+
import { HomeScreen } from './screens/home.js'
|
|
128
|
+
import { CookiePolicyScreen } from './screens/legal/cookies.js'
|
|
129
|
+
|
|
130
|
+
export const appEntrypoints = [
|
|
131
|
+
...authEntrypoints,
|
|
132
|
+
...panelEntrypoints,
|
|
133
|
+
...bindAll(apiProtocols),
|
|
134
|
+
bindScreen(webProtocols.base, handler(MainLayout)),
|
|
135
|
+
bindScreen(webProtocols.home, handler(HomeScreen)),
|
|
136
|
+
bindScreen(webProtocols.legal.cookies, handler(CookiePolicyScreen)),
|
|
137
|
+
]
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
`render(context, { rootClassName })` mounts `PanelApp` with the browser language detector
|
|
141
|
+
installed. An app with global overlays of its own mounts `PanelApp` itself:
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
// src/render.tsx
|
|
145
|
+
import type { FC } from 'react'
|
|
146
|
+
import { render as mount } from '@owlmeans/web-client'
|
|
147
|
+
import type { AppContext } from '@owlmeans/web-client'
|
|
148
|
+
import { useI18nInstance } from '@owlmeans/client-i18n/utils'
|
|
149
|
+
import { PanelApp } from '@owlmeans/web-panel'
|
|
150
|
+
import { PanelCookieConsent } from '@owlmeans/web-panel/consent'
|
|
151
|
+
import detector from 'i18next-browser-languagedetector'
|
|
152
|
+
|
|
153
|
+
const App: FC<{ context: AppContext }> = ({ context }) => {
|
|
154
|
+
useI18nInstance(context.cfg).use(detector)
|
|
155
|
+
|
|
156
|
+
// Children of PanelApp sit beside the router, so the dialog survives navigation.
|
|
157
|
+
return <PanelApp context={context} rootClassName="dark">
|
|
158
|
+
<PanelCookieConsent policyHref="/legal/cookies" />
|
|
159
|
+
</PanelApp>
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export const render = (context: AppContext) => mount(<App context={context} />)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
// src/index.tsx
|
|
167
|
+
const context = makeContext(config)
|
|
168
|
+
context.registerEntrypoints(appEntrypoints)
|
|
169
|
+
context.serviceRoute(MY_APP_WEB, true)
|
|
170
|
+
render(context)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### 3. Navigation shell, toasts and a guarded layout
|
|
174
|
+
|
|
175
|
+
`NavLayout` is the standard shell: header, section menu, the active section's screen menu, content
|
|
176
|
+
and footer. A layout entrypoint binds a component that renders it; the matched screen arrives as
|
|
177
|
+
`children`. Keep the navigation as data in its own module, so screens, entrypoints and the shell
|
|
178
|
+
read the same aliases.
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
import type { FC, PropsWithChildren } from 'react'
|
|
182
|
+
import { HOME, NavLayout, Toaster, useI18nApp } from '@owlmeans/web-panel'
|
|
183
|
+
import type { PanelNavConfig, PanelNavLink } from '@owlmeans/web-panel'
|
|
184
|
+
import { useSelfAuth } from '@owlmeans/client-auth'
|
|
185
|
+
import { webProtocols } from 'my-app-common'
|
|
186
|
+
|
|
187
|
+
export const navConfig: PanelNavConfig = {
|
|
188
|
+
sections: [
|
|
189
|
+
{ name: 'home', label: 'Home', items: [{ alias: HOME, label: 'Overview' }] },
|
|
190
|
+
{
|
|
191
|
+
name: 'projects', items: [
|
|
192
|
+
{ alias: webProtocols.projects.alias },
|
|
193
|
+
{ alias: webProtocols.archive.alias },
|
|
194
|
+
]
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const footerLinks: PanelNavLink[] = [
|
|
200
|
+
{ alias: HOME, label: 'My App' },
|
|
201
|
+
{ href: 'https://example.com/docs', label: 'Docs', open: true },
|
|
202
|
+
]
|
|
203
|
+
|
|
204
|
+
export const MainLayout: FC<PropsWithChildren> = ({ children }) => {
|
|
205
|
+
useSelfAuth(true)
|
|
206
|
+
const t = useI18nApp('menu')
|
|
207
|
+
|
|
208
|
+
return <>
|
|
209
|
+
<NavLayout nav={navConfig} title="My App" footer={footerLinks}
|
|
210
|
+
translate={(key, defaultValue) => t(key, { defaultValue })}
|
|
211
|
+
headerClassName="bg-secondary text-secondary-foreground"
|
|
212
|
+
containerClassName="max-w-[1280px]">
|
|
213
|
+
{children}
|
|
214
|
+
</NavLayout>
|
|
215
|
+
{/* Exactly once, in the layout. Messages come from `toast.success(...)` in `sonner`. */}
|
|
216
|
+
<Toaster />
|
|
217
|
+
</>
|
|
218
|
+
}
|
|
114
219
|
```
|
|
115
220
|
|
|
221
|
+
Labels resolve as literal `label`, then `translate(key, humanized alias)`, then the humanized
|
|
222
|
+
alias. Default keys are `nav.<section>` and `modules.<alias>`.
|
|
223
|
+
|
|
224
|
+
| Component | Props |
|
|
225
|
+
|---|---|
|
|
226
|
+
| `NavLayout` | `nav`, `translate?`, `title?`, `home?` (brand target; defaults to the first section's first item), `actions?`, `footer?` (`PanelNavLink[]` renders the standard footer, a node replaces it), `headerClassName?`, `contentClassName?`, `containerClassName?`, `className?`, `style?` |
|
|
227
|
+
| `TopNav` | `config`, `translate?`, `ariaLabel?`, `className?`, `style?` |
|
|
228
|
+
| `SideNav` | the same, plus `variant?: 'side' \| 'bar'` |
|
|
229
|
+
| `Footer` | `links?`, `translate?`, `containerClassName?`, `children?`, `className?`, `style?` |
|
|
230
|
+
|
|
231
|
+
### 4. Forms, panels and status
|
|
232
|
+
|
|
233
|
+
`Form` owns the whole model: `useForm` with an `ajvResolver` over `validation`, published through
|
|
234
|
+
`FormProvider` and `FormContext`, so every control below reads one form.
|
|
235
|
+
|
|
236
|
+
```tsx
|
|
237
|
+
import type { FC } from 'react'
|
|
238
|
+
import { useCallback } from 'react'
|
|
239
|
+
import { toast } from 'sonner'
|
|
240
|
+
import { Block, BlockScaling, Button, Form, PanelContext, SubmitButton, TextInput, useFormRef } from '@owlmeans/web-panel'
|
|
241
|
+
import { apiProtocols, ProjectCreateSchema } from 'my-app-common'
|
|
242
|
+
import type { ProjectCreate } from 'my-app-common'
|
|
243
|
+
import { useContext } from '../context.js'
|
|
244
|
+
|
|
245
|
+
export const CreateProject: FC<{ onCancel: () => void }> = ({ onCancel }) => {
|
|
246
|
+
const context = useContext()
|
|
247
|
+
const formRef = useFormRef<ProjectCreate>()
|
|
248
|
+
|
|
249
|
+
const onSubmit = useCallback(async (data: ProjectCreate) => {
|
|
250
|
+
try {
|
|
251
|
+
await context.entrypoint(apiProtocols.project.create).call({ body: data })
|
|
252
|
+
toast.success('Project created')
|
|
253
|
+
} catch (error) {
|
|
254
|
+
// Plants a root error; Form shows it through Status when decorated.
|
|
255
|
+
formRef.current?.error(error)
|
|
256
|
+
}
|
|
257
|
+
}, [])
|
|
258
|
+
|
|
259
|
+
return <PanelContext resource="my-app" prefix="project-create">
|
|
260
|
+
{/* `decorate` renders a Card, the root error and a SubmitButton in the footer. */}
|
|
261
|
+
<Form decorate name="project" formRef={formRef} validation={ProjectCreateSchema}
|
|
262
|
+
horizontal={BlockScaling.Half} onSubmit={onSubmit}>
|
|
263
|
+
<TextInput name="name" label placeholder hint />
|
|
264
|
+
<TextInput name="repository" type="url" label="Repository URL" disableAutocomplete />
|
|
265
|
+
</Form>
|
|
266
|
+
<Button label="cancel" variant="text" onClick={onCancel} />
|
|
267
|
+
</PanelContext>
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
- `TextInput` takes `label`, `placeholder` and `hint` as `string | boolean`: `true` resolves
|
|
272
|
+
`<name>.label` / `.placeholder` / `.hint` from the form namespace, and a string is used verbatim.
|
|
273
|
+
- Without `decorate`, `Form` is a bare flex column with no card, no root-error surface and no submit
|
|
274
|
+
button, so render your own `SubmitButton` inside it.
|
|
275
|
+
- `formRef` (from `useFormRef()`) is filled with `{ form, update, loader, error }`.
|
|
276
|
+
- `Button` maps `contained` / `outlined` / `text` onto shadcn `default` / `outline` / `ghost`, and
|
|
277
|
+
forwards any other shadcn variant name unchanged.
|
|
278
|
+
|
|
279
|
+
Panels and feedback:
|
|
280
|
+
|
|
281
|
+
```tsx
|
|
282
|
+
import { Block, ButtonSelector, ImageUploader, Link, Status, Text } from '@owlmeans/web-panel'
|
|
283
|
+
|
|
284
|
+
<Block horizontal={BlockScaling.Wide} Actions={() => <Link module={webProtocols.projects.alias} />}>
|
|
285
|
+
<Text variant="h3" name="title" />
|
|
286
|
+
<ButtonSelector name="period" options={['day', 'week', 'month']} current={period} onSelect={setPeriod} />
|
|
287
|
+
<ImageUploader maxFiles={1} previewUrl={logoUrl} onDrop={files => upload(files[0])} />
|
|
288
|
+
{result != null && <Status ok={result.ok} error={result.error} message={result.message} />}
|
|
289
|
+
</Block>
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### 5. Socket connection state
|
|
293
|
+
|
|
294
|
+
With `cfg.socket.reloadDialog` set, `PanelApp` mounts `SocketReloadDialog`. It is a blocking
|
|
295
|
+
`AlertDialog` that opens when every `ws()` / `useWs()` connection has exhausted its retry budget
|
|
296
|
+
(`useSocketStatus() === 'lost'`), and its only action reloads the page. An app that prefers an
|
|
297
|
+
inline state leaves the flag off and reads the same status:
|
|
298
|
+
|
|
299
|
+
```tsx
|
|
300
|
+
import type { FC } from 'react'
|
|
301
|
+
import { useSocketStatus } from '@owlmeans/client-socket'
|
|
302
|
+
import { useI18nApp } from '@owlmeans/web-panel'
|
|
303
|
+
|
|
304
|
+
export const ConnectionBadge: FC = () => {
|
|
305
|
+
const status = useSocketStatus() // 'online' | 'reconnecting' | 'lost'
|
|
306
|
+
const t = useI18nApp('connection')
|
|
307
|
+
|
|
308
|
+
return status === 'online' ? null : <span role="status" data-state={status}>{t(status)}</span>
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
The dialog's strings are the lib-tier `socket` namespace (`reload.title`, `reload.description`,
|
|
313
|
+
`reload.action`), shipped in seven languages.
|
|
314
|
+
|
|
315
|
+
## API
|
|
316
|
+
|
|
317
|
+
### Context and mounting
|
|
318
|
+
|
|
319
|
+
| Symbol | Kind | Purpose |
|
|
320
|
+
|---|---|---|
|
|
321
|
+
| `makeContext<C, T>(cfg)` | function | Panel context factory — see *Concepts* |
|
|
322
|
+
| `useContext<C, T>()` | hook | The current panel context |
|
|
323
|
+
| `render(context, opts?)` | function | Mount `PanelApp` with the language detector; `opts` is `WebRenderOptions` |
|
|
324
|
+
| `WebRenderOptions` | type | `@owlmeans/web-client`'s `RenderOptions` plus `rootClassName` |
|
|
325
|
+
| `PanelApp`, `PanelAppProps` | component, type | Themed root `div`, i18n provider, app and router, overlays |
|
|
326
|
+
| `entrypoints` | const | `@owlmeans/web-client`'s entrypoints plus `@owlmeans/api-config-client`'s |
|
|
327
|
+
| `AppConfig`, `AppContext` | type | Adds `socket?: SocketClientSettings & { reloadDialog? }`, `flow()` and the socket-status append |
|
|
328
|
+
|
|
329
|
+
### Components
|
|
330
|
+
|
|
331
|
+
| Symbol | Kind | Purpose |
|
|
332
|
+
|---|---|---|
|
|
333
|
+
| `NavLayout`, `TopNav`, `SideNav`, `Footer` | component | Navigation shell and its pieces |
|
|
334
|
+
| `NavLayoutProps`, `TopNavProps`, `SideNavProps`, `FooterProps` | type | Their props |
|
|
335
|
+
| `Layout`, `LayoutProps` | component, type | Plain content wrapper |
|
|
336
|
+
| `Form`, `WebFormProps` | component, type | Web form; `FormProps` plus `className` / `style` |
|
|
337
|
+
| `TextInput`, `TextInputProps` | component, type | `react-hook-form` controlled input |
|
|
338
|
+
| `Button`, `SubmitButton`, `ButtonProps`, `SubmitProps` | component, type | Action buttons; `SubmitButton` is bound to `handleSubmit` |
|
|
339
|
+
| `ButtonSelector`, `SelectorProps` | component, type | One button per option, the current one `contained` |
|
|
340
|
+
| `Block`, `Text`, `Link`, `Status` | component | Card panel with `Actions`, translated text, entrypoint link, translated `Alert` |
|
|
341
|
+
| `BlockProps`, `TextProps`, `LinkProps`, `StatusProps`, `StyledProps`, `TextVariant` | type | Their props |
|
|
342
|
+
| `ImageUploader`, `ImageUploaderProps` | component, type | Drop target with a `previewUrl` |
|
|
343
|
+
| `Toaster` | component | `sonner` surface themed from app tokens; follows `.dark` on the document element |
|
|
344
|
+
| `SocketReloadDialog` | component | Blocking reload prompt for `'lost'` sockets |
|
|
345
|
+
| `LoginScreen`, `LocalizedLoginScreen` | component | Identity-provider choice screen; the localized one binds `translate` to the app's resources |
|
|
346
|
+
| `appendLoginScreen(ctx, setup?)`, `LoginScreenSetup` | function, type | Register the screen on the login service, with `Logo` and other `LoginScreenProps` |
|
|
347
|
+
| `LoginTerms`, `LoginCredit`, `LoginMethodIcon` | component | The screen's terms sentence, credit line and method icons |
|
|
348
|
+
| `LoginTermsProps`, `LoginCreditProps` | type | Their props |
|
|
349
|
+
| `scalingToStyles(horizontal?, vertical?)` | function | `BlockScaling` to Tailwind width/height classes |
|
|
350
|
+
| `useBreakPoint()` | hook | The current Tailwind breakpoint name |
|
|
351
|
+
| `useMapBreakpoint(map, def?, breakpoint?)` | hook | The map entry for the current breakpoint; throws `SyntaxError` when nothing matches and no `def` is given |
|
|
352
|
+
|
|
353
|
+
### Re-exports
|
|
354
|
+
|
|
355
|
+
| Symbol | From |
|
|
356
|
+
|---|---|
|
|
357
|
+
| everything (`usePanelNav`, `PanelNavConfig` / `PanelNavItem` / `PanelNavSection` / `PanelNavLink` / `NavTranslate`, `PanelContext`, `BlockScaling`, `useFormRef`, `FormContext`, ...) | `@owlmeans/client-panel` |
|
|
358
|
+
| `handler`, `useNavigate`, `useValue`, `useEntrypoint` | `@owlmeans/client` |
|
|
359
|
+
| `bind`, `bindAll`, `bindScreen`, `provideRequest`, `stab`; type `Module` | `@owlmeans/client-entrypoint` |
|
|
360
|
+
| `route`, `frontend` / `croute` | `@owlmeans/route` / `@owlmeans/client-route` |
|
|
361
|
+
| `config` / `service` / `addWebService` | `@owlmeans/client-context` / `@owlmeans/config` / `@owlmeans/client-config` |
|
|
362
|
+
| `AppType`, `HOME`, `ROOT`, `BASE`, `GUEST` | `@owlmeans/context` |
|
|
363
|
+
| `DISPATCHER`, `CAUTHEN_FLOW_ENTER`; type `AuthToken` | `@owlmeans/auth` |
|
|
364
|
+
| `DAUTH_GUARD` (`DEFAULT_ALIAS`), `bindExternalAuthentication` | `@owlmeans/client-auth` |
|
|
365
|
+
| `Dispatcher`, `appendWebAuthService` | `@owlmeans/web-client` |
|
|
366
|
+
| `composePrefix`, `useI18n`, `useI18nApp`, `useI18nLib`, `useLanguage` | `@owlmeans/client-i18n` |
|
|
367
|
+
| `addI18nApp`, `addI18nLib`, `SUPPORTED_LNGS` | `@owlmeans/i18n` |
|
|
368
|
+
| `flow`, `configureFlows` / `FLOW_PARAM`, `SERVICE_PARAM`, `useFlow` | `@owlmeans/flow` / `@owlmeans/web-flow` |
|
|
369
|
+
|
|
370
|
+
### `@owlmeans/web-panel/auth`
|
|
371
|
+
|
|
372
|
+
The context and screens for an authentication manager app — the identity-provider side, not a
|
|
373
|
+
relying party.
|
|
374
|
+
|
|
375
|
+
| Symbol | Kind | Purpose |
|
|
376
|
+
|---|---|---|
|
|
377
|
+
| `makeContext`, `useContext` | function, hook | Client context with IndexedDB, router, `apiConfigMiddleware` and flow service, without the relying-party auth service |
|
|
378
|
+
| `AppConfig`, `AppContext` | type | With `flow()` |
|
|
379
|
+
| `render` | function | Same as the root `render` |
|
|
380
|
+
| `plugins` | registry | `@owlmeans/client-auth/manager`'s registry with the shadcn renderers assigned |
|
|
381
|
+
| `Ed22519BasicAuthUIPlugin`, `ReCaptchaAuthUIPlugin`, `TunnelConsumerUIPlugin` | component | Those renderers |
|
|
382
|
+
| `AuthenticationHOC`, `AuthenticationType`, `DISPATCHER`, `CAUTHEN`, `EntrypointOutcome`, `RouteMethod`, `FLOW_ALIAS`, type `Navigator` / `AbstractRequest` / `Request` / `ServiceRoute` / `FlowService`, and the `@owlmeans/client-panel/auth` exports | re-export | Authentication-manager vocabulary |
|
|
383
|
+
|
|
384
|
+
`@owlmeans/web-panel/auth/entrypoints` exports `entrypoints`: `@owlmeans/client-auth/manager`'s
|
|
385
|
+
bindings plus `@owlmeans/api-config-client`'s.
|
|
386
|
+
|
|
387
|
+
### `@owlmeans/web-panel/consent`
|
|
388
|
+
|
|
389
|
+
| Symbol | Kind | Purpose |
|
|
390
|
+
|---|---|---|
|
|
391
|
+
| `PanelCookieConsent`, `PanelCookiePolicy` | component | `@owlmeans/web-consent` components bound to the app's language and translations |
|
|
392
|
+
| `PanelConsentMenuWidget` | component | The cookie-preferences row for a host menu |
|
|
393
|
+
| `appendConsentWidgetService(ctx, alias?)`, `createConsentWidgetService` | function | Ref-counted presence service over a state resource |
|
|
394
|
+
| `useConsentMenuPresence()`, `useConsentWidgetPresent()` | hook | Declare that a menu shows the row; read whether one does (hides the floating button) |
|
|
395
|
+
| `CONSENT_WIDGET_SERVICE`, `CONSENT_WIDGET_STATE` | const | Service alias and state alias |
|
|
396
|
+
| `ConsentWidgetPresenceRecord`, `ConsentWidgetService`, `ConsentWidgetServiceAppend` | type | Service types |
|
|
397
|
+
| `useConsent`, `useConsentCategory`, `consentStore`, `openConsent`, `isConsented`, `readConsent`, `writeConsent`, `clearConsent`, `consentBootstrapScript`, `DEFAULT_CONSENT_CATEGORIES`, `CONSENT_KEY`, `CONSENT_ESSENTIAL`, `CONSENT_ANALYTICS`, `CONSENT_MARKETING` and the consent types | re-export | `@owlmeans/web-consent` |
|
|
398
|
+
|
|
399
|
+
### Differences from `@owlmeans/mui-panel`
|
|
400
|
+
|
|
401
|
+
The public names match the MUI implementation; these props and types differ:
|
|
402
|
+
|
|
403
|
+
- **`styles?: SxProps` is removed.** Use `className?: string` and `style?: React.CSSProperties`
|
|
404
|
+
(`BlockProps`, `TextProps`, `LinkProps`, `WebFormProps`, `LayoutProps`).
|
|
405
|
+
- **`variant` on `Text` / `Link` is `TextVariant`**: `'h1' | 'h2' | 'h3' | 'h4' | 'p' | 'lead' |
|
|
406
|
+
'large' | 'small' | 'muted' | 'blockquote'`.
|
|
407
|
+
- **`PanelAppProps.theme?: Theme` is replaced with `rootClassName?: string`.** Apply the theme
|
|
408
|
+
through Tailwind classes and CSS variables.
|
|
409
|
+
- **`render(context, theme?, opts?)` is now `render(context, opts?)`**, where `opts` includes
|
|
410
|
+
`rootClassName`.
|
|
411
|
+
- **`scalingToStyles()` returns a class-name string** composable with other classes, not an
|
|
412
|
+
`SxProps`; its `theme` parameter is gone.
|
|
413
|
+
- **`useBreakPoint` / `useMapBreakpoint` use Tailwind's default breakpoints**
|
|
414
|
+
(`xs/sm/md/lg/xl`) instead of MUI's `Theme.breakpoints`.
|
|
415
|
+
- **Button props stay compatible** (`'small' | 'medium' | 'large'`, `contained` / `outlined` /
|
|
416
|
+
`text` or shadcn variant names).
|
|
417
|
+
|
|
418
|
+
## Common pitfalls
|
|
419
|
+
|
|
420
|
+
- **Add the `@source` line** for this package's `src` (and for `@owlmeans/web-consent` when using
|
|
421
|
+
`./consent`). Without it the navigation shell renders unstyled.
|
|
422
|
+
- **A parent frontend route needs a `default: true` child.** A grouping screen with children but no
|
|
423
|
+
default child renders blank at its own path.
|
|
424
|
+
- **Menu and login labels need a `translate` prop to be localized.** The components never read the
|
|
425
|
+
i18n context implicitly, so without the prop they show literal labels or humanized aliases.
|
|
426
|
+
- **Mount `Toaster` exactly once, in the layout.** Two surfaces render every toast twice. An app
|
|
427
|
+
raising its own toasts declares `sonner` at a range that resolves to the same copy.
|
|
428
|
+
- **Mount dialogs beside the router** (`PanelApp` children or the web-client overlay slot), never
|
|
429
|
+
inside a route.
|
|
430
|
+
- **Give a dark top bar both halves through `headerClassName`** (`bg-secondary
|
|
431
|
+
text-secondary-foreground`). The header is its own surface; colouring only the root leaves its
|
|
432
|
+
menu unreadable.
|
|
433
|
+
- **Name only the utility you want to move in `containerClassName`.** It merges over
|
|
434
|
+
`mx-auto w-full max-w-6xl px-4`, so `max-w-[1280px]` changes only the width.
|
|
435
|
+
- **`Link` resolves its `href` asynchronously.** Never key a test or layout on the anchor having an
|
|
436
|
+
`href` at first paint.
|
|
437
|
+
- **`reloadDialog` is off by default.** The dialog renders nothing unless `cfg.socket.reloadDialog`
|
|
438
|
+
is `true`.
|
|
439
|
+
- **Run `useConsentMenuPresence()` from the menu's always-mounted component**, never from inside a
|
|
440
|
+
dropdown row, which mounts only while the menu is open.
|
|
441
|
+
- **Never import `@/…` from package code or vendor its primitives in the app.** The `@` alias
|
|
442
|
+
belongs to the consumer.
|
|
443
|
+
- **Every override of a packaged string covers all seven languages** (`SUPPORTED_LNGS`).
|
|
444
|
+
|
|
116
445
|
## Related packages
|
|
117
446
|
|
|
118
|
-
- [`@owlmeans/
|
|
119
|
-
|
|
120
|
-
- [`@owlmeans/
|
|
121
|
-
|
|
122
|
-
- [`@owlmeans/
|
|
123
|
-
|
|
447
|
+
- [`@owlmeans/web-client`](../web-client) — the web context and mount helpers this package builds on
|
|
448
|
+
- [`@owlmeans/client-panel`](../client-panel) — headless form, layout, navigation and login models
|
|
449
|
+
- [`@owlmeans/client-auth`](../client-auth) — auth service, login hooks and plugin registry
|
|
450
|
+
- [`@owlmeans/client-socket`](../client-socket) — `useSocketStatus` behind the reload prompt
|
|
451
|
+
- [`@owlmeans/web-consent`](../web-consent) — cookie consent components behind `./consent`
|
|
452
|
+
- [`@owlmeans/web-oidc-rp`](../web-oidc-rp) — OIDC relying party UI on the same Tailwind theme
|
|
453
|
+
- [`@owlmeans/web-router`](../web-router) — default OwlMeans routing plugin
|
|
454
|
+
- [`@owlmeans/mui-panel`](../mui-panel) — legacy Material UI implementation of the same surface
|
|
124
455
|
|
|
125
456
|
<!-- owlmeans:agent-guidance:start -->
|
|
126
457
|
## Agent guidance
|
|
@@ -130,7 +461,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
130
461
|
your project's skill store (`.agents/skills/`):
|
|
131
462
|
|
|
132
463
|
```sh
|
|
133
|
-
npx @owlmeans/agent-skills
|
|
464
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.20
|
|
134
465
|
```
|
|
135
466
|
|
|
136
467
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
3
|
"package": "@owlmeans/web-panel",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-
|
|
4
|
+
"version": "0.1.18-rc.41",
|
|
5
|
+
"generatedAt": "2026-09-14T19:17:35.200Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|