@owlmeans/web-panel 0.1.18-rc.2 → 0.1.18-rc.21

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 (113) hide show
  1. package/README.md +85 -2
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/web-panel/SKILL.md +157 -4
  4. package/build/@/components/ui/navigation-menu.d.ts +15 -0
  5. package/build/@/components/ui/navigation-menu.d.ts.map +1 -0
  6. package/build/@/components/ui/navigation-menu.js +32 -0
  7. package/build/@/components/ui/navigation-menu.js.map +1 -0
  8. package/build/auth/plugins/basic-ed25519.js +1 -1
  9. package/build/auth/plugins/basic-ed25519.js.map +1 -1
  10. package/build/components/footer/component.d.ts +12 -0
  11. package/build/components/footer/component.d.ts.map +1 -0
  12. package/build/components/footer/component.js +24 -0
  13. package/build/components/footer/component.js.map +1 -0
  14. package/build/components/footer/index.d.ts +3 -0
  15. package/build/components/footer/index.d.ts.map +1 -0
  16. package/build/components/footer/index.js +2 -0
  17. package/build/components/footer/index.js.map +1 -0
  18. package/build/components/footer/types.d.ts +11 -0
  19. package/build/components/footer/types.d.ts.map +1 -0
  20. package/build/components/footer/types.js +2 -0
  21. package/build/components/footer/types.js.map +1 -0
  22. package/build/components/index.d.ts +4 -0
  23. package/build/components/index.d.ts.map +1 -1
  24. package/build/components/index.js +4 -0
  25. package/build/components/index.js.map +1 -1
  26. package/build/components/login/append.d.ts +22 -0
  27. package/build/components/login/append.d.ts.map +1 -0
  28. package/build/components/login/append.js +21 -0
  29. package/build/components/login/append.js.map +1 -0
  30. package/build/components/login/credit.d.ts +17 -0
  31. package/build/components/login/credit.d.ts.map +1 -0
  32. package/build/components/login/credit.js +17 -0
  33. package/build/components/login/credit.js.map +1 -0
  34. package/build/components/login/icons.d.ts +14 -0
  35. package/build/components/login/icons.d.ts.map +1 -0
  36. package/build/components/login/icons.js +29 -0
  37. package/build/components/login/icons.js.map +1 -0
  38. package/build/components/login/index.d.ts +6 -0
  39. package/build/components/login/index.d.ts.map +1 -0
  40. package/build/components/login/index.js +6 -0
  41. package/build/components/login/index.js.map +1 -0
  42. package/build/components/login/screen.d.ts +23 -0
  43. package/build/components/login/screen.d.ts.map +1 -0
  44. package/build/components/login/screen.js +54 -0
  45. package/build/components/login/screen.js.map +1 -0
  46. package/build/components/login/terms.d.ts +9 -0
  47. package/build/components/login/terms.d.ts.map +1 -0
  48. package/build/components/login/terms.js +30 -0
  49. package/build/components/login/terms.js.map +1 -0
  50. package/build/components/nav/index.d.ts +5 -0
  51. package/build/components/nav/index.d.ts.map +1 -0
  52. package/build/components/nav/index.js +4 -0
  53. package/build/components/nav/index.js.map +1 -0
  54. package/build/components/nav/layout.d.ts +12 -0
  55. package/build/components/nav/layout.d.ts.map +1 -0
  56. package/build/components/nav/layout.js +40 -0
  57. package/build/components/nav/layout.js.map +1 -0
  58. package/build/components/nav/side.d.ts +11 -0
  59. package/build/components/nav/side.d.ts.map +1 -0
  60. package/build/components/nav/side.js +25 -0
  61. package/build/components/nav/side.js.map +1 -0
  62. package/build/components/nav/top.d.ts +10 -0
  63. package/build/components/nav/top.d.ts.map +1 -0
  64. package/build/components/nav/top.js +44 -0
  65. package/build/components/nav/top.js.map +1 -0
  66. package/build/components/nav/types.d.ts +55 -0
  67. package/build/components/nav/types.d.ts.map +1 -0
  68. package/build/components/nav/types.js +2 -0
  69. package/build/components/nav/types.js.map +1 -0
  70. package/build/components/toaster.d.ts +16 -0
  71. package/build/components/toaster.d.ts.map +1 -0
  72. package/build/components/toaster.js +52 -0
  73. package/build/components/toaster.js.map +1 -0
  74. package/build/consent/component.d.ts +11 -0
  75. package/build/consent/component.d.ts.map +1 -0
  76. package/build/consent/component.js +53 -0
  77. package/build/consent/component.js.map +1 -0
  78. package/build/consent/index.d.ts +4 -0
  79. package/build/consent/index.d.ts.map +1 -0
  80. package/build/consent/index.js +3 -0
  81. package/build/consent/index.js.map +1 -0
  82. package/build/context.d.ts.map +1 -1
  83. package/build/context.js +4 -0
  84. package/build/context.js.map +1 -1
  85. package/package.json +38 -27
  86. package/src/@/components/ui/navigation-menu.tsx +169 -0
  87. package/src/auth/plugins/basic-ed25519.tsx +1 -1
  88. package/src/components/footer/component.tsx +37 -0
  89. package/src/components/footer/index.ts +3 -0
  90. package/src/components/footer/types.ts +11 -0
  91. package/src/components/index.ts +4 -0
  92. package/src/components/login/append.ts +34 -0
  93. package/src/components/login/credit.tsx +34 -0
  94. package/src/components/login/icons.tsx +39 -0
  95. package/src/components/login/index.ts +5 -0
  96. package/src/components/login/screen.tsx +110 -0
  97. package/src/components/login/terms.tsx +71 -0
  98. package/src/components/nav/index.ts +5 -0
  99. package/src/components/nav/layout.tsx +85 -0
  100. package/src/components/nav/side.tsx +52 -0
  101. package/src/components/nav/top.tsx +62 -0
  102. package/src/components/nav/types.ts +56 -0
  103. package/src/components/toaster.tsx +68 -0
  104. package/src/consent/component.tsx +62 -0
  105. package/src/consent/index.ts +11 -0
  106. package/src/context.ts +5 -0
  107. package/tests/context.ts +43 -0
  108. package/tests/harness/index.html +11 -0
  109. package/tests/harness/mount.tsx +183 -0
  110. package/tests/login.spec.ts +115 -0
  111. package/tests/nav.spec.ts +252 -0
  112. package/tests/smoke.spec.ts +5 -1
  113. package/tests/toaster.spec.ts +59 -0
@@ -0,0 +1,11 @@
1
+ import type { FC } from 'react';
2
+ import type { CookieConsentProps, CookiePolicyProps } from '@owlmeans/web-consent';
3
+ /**
4
+ * The consent dialog, bound to this application's language and translations.
5
+ *
6
+ * `@owlmeans/web-consent` deliberately knows nothing about OwlMeans i18n — one of its consumers is
7
+ * an Astro site with none — so this is where the two meet.
8
+ */
9
+ export declare const PanelCookieConsent: FC<CookieConsentProps>;
10
+ export declare const PanelCookiePolicy: FC<CookiePolicyProps>;
11
+ //# sourceMappingURL=component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../src/consent/component.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;AAE/B,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAiB,MAAM,uBAAuB,CAAA;AA6BjG;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,EAAE,CAAC,kBAAkB,CAMrD,CAAA;AAED,eAAO,MAAM,iBAAiB,EAAE,EAAE,CAAC,iBAAiB,CAMnD,CAAA"}
@@ -0,0 +1,53 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCallback } from 'react';
3
+ import { CookieConsent, CookiePolicy, defaultConsentTranslate } from '@owlmeans/web-consent';
4
+ import { useI18nLib, useLanguage } from '@owlmeans/client-i18n';
5
+ import { SUPPORTED_LNGS } from '@owlmeans/i18n';
6
+ /**
7
+ * Resolve consent copy through the application first, and the packaged bundle second.
8
+ *
9
+ * The chain matters. `@owlmeans/web-consent` takes `translate` as a prop and, once given one,
10
+ * stops consulting its own translations — so a wrapper that forwarded the framework resolver
11
+ * alone would render the ENGLISH default for every key the application had not overridden, in
12
+ * every language. Falling through to the packaged bundle for the current locale means an
13
+ * application overrides what it wants to and inherits seven languages for the rest.
14
+ */
15
+ const useConsentTranslate = (locale, override) => {
16
+ const t = useI18nLib('consent');
17
+ return useCallback((key, defaultValue) => {
18
+ if (override != null) {
19
+ return override(key, defaultValue);
20
+ }
21
+ const packaged = defaultConsentTranslate(locale)(key, defaultValue);
22
+ // `t` answers with whatever it is given when the key is unknown, so the packaged string is
23
+ // what it is given — an application's override wins, and everything else stays translated.
24
+ return t(key, { defaultValue: packaged });
25
+ }, [t, locale, override]);
26
+ };
27
+ /**
28
+ * The consent dialog, bound to this application's language and translations.
29
+ *
30
+ * `@owlmeans/web-consent` deliberately knows nothing about OwlMeans i18n — one of its consumers is
31
+ * an Astro site with none — so this is where the two meet.
32
+ */
33
+ export const PanelCookieConsent = props => {
34
+ const [lng] = useLanguage();
35
+ const locale = props.locale ?? lng;
36
+ const translate = useConsentTranslate(locale, props.translate);
37
+ return _jsx(CookieConsent, { ...props, locale: locale, translate: translate });
38
+ };
39
+ export const PanelCookiePolicy = props => {
40
+ const [lng] = useLanguage();
41
+ const locale = props.locale ?? lng;
42
+ const translate = useConsentTranslate(locale, props.translate);
43
+ return _jsx(CookiePolicy, { ...props, locale: locale, translate: translate });
44
+ };
45
+ /**
46
+ * The consent package carries its own locale list because it must build with no dependency on the
47
+ * i18n package at all. This assertion is what keeps the two from drifting: a language added to the
48
+ * framework and not to the bundle fails here, at build time, rather than as a dialog rendering
49
+ * English to the one reader who cannot report it.
50
+ */
51
+ const _localeParity = SUPPORTED_LNGS;
52
+ void _localeParity;
53
+ //# sourceMappingURL=component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component.js","sourceRoot":"","sources":["../../src/consent/component.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAEnC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AAE5F,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAE/C;;;;;;;;GAQG;AACH,MAAM,mBAAmB,GAAG,CAC1B,MAAc,EAAE,QAAwD,EACvB,EAAE;IACnD,MAAM,CAAC,GAAG,UAAU,CAAC,SAAS,CAAC,CAAA;IAE/B,OAAO,WAAW,CAAC,CAAC,GAAW,EAAE,YAAoB,EAAE,EAAE;QACvD,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;YACrB,OAAO,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,CAAA;QACpC,CAAC;QACD,MAAM,QAAQ,GAAG,uBAAuB,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,YAAY,CAAC,CAAA;QACnE,2FAA2F;QAC3F,2FAA2F;QAC3F,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,CAAA;IAC3C,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAA;AAC3B,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAA2B,KAAK,CAAC,EAAE;IAChE,MAAM,CAAC,GAAG,CAAC,GAAG,WAAW,EAAE,CAAA;IAC3B,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,GAAG,CAAA;IAClC,MAAM,SAAS,GAAG,mBAAmB,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,CAAA;IAE9D,OAAO,KAAC,aAAa,OAAK,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAI,CAAA;AAC3E,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,iBAAiB,GAA0B,KAAK,CAAC,EAAE;IAC9D,MAAM,CAAC,GAAG,CAAC,GAAG,WAAW,EAAE,CAAA;IAC3B,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,GAAG,CAAA;IAClC,MAAM,SAAS,GAAG,mBAAmB,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,CAAA;IAE9D,OAAO,KAAC,YAAY,OAAK,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAI,CAAA;AAC1E,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,aAAa,GAA6B,cAA0C,CAAA;AAC1F,KAAK,aAAa,CAAA"}
@@ -0,0 +1,4 @@
1
+ export * from './component.js';
2
+ export { useConsent, useConsentCategory, consentStore, openConsent, isConsented, readConsent, writeConsent, clearConsent, DEFAULT_CONSENT_CATEGORIES, CONSENT_KEY, CONSENT_ESSENTIAL, CONSENT_ANALYTICS, CONSENT_MARKETING, consentBootstrapScript, } from '@owlmeans/web-consent';
3
+ export type { ConsentCategory, ConsentOptions, ConsentRecord, ConsentSignal, ConsentState, CookieConsentProps, CookiePolicyProps, } from '@owlmeans/web-consent';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/consent/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,OAAO,EACL,UAAU,EAAE,kBAAkB,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EACtE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,0BAA0B,EACnE,WAAW,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB,EACpE,sBAAsB,GACvB,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EACV,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAC3E,kBAAkB,EAAE,iBAAiB,GACtC,MAAM,uBAAuB,CAAA"}
@@ -0,0 +1,3 @@
1
+ export * from './component.js';
2
+ export { useConsent, useConsentCategory, consentStore, openConsent, isConsented, readConsent, writeConsent, clearConsent, DEFAULT_CONSENT_CATEGORIES, CONSENT_KEY, CONSENT_ESSENTIAL, CONSENT_ANALYTICS, CONSENT_MARKETING, consentBootstrapScript, } from '@owlmeans/web-consent';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/consent/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,OAAO,EACL,UAAU,EAAE,kBAAkB,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EACtE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,0BAA0B,EACnE,WAAW,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB,EACpE,sBAAsB,GACvB,MAAM,uBAAuB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAG,UAAU,EAAE,MAAM,YAAY,CAAA;AAIxD,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,SAAS,EAAE,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,KAAG,CASlF,CAAA;AAED,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,SAAS,GAAG,SAAS,EAAC,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,QACnF,CAAA"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAG,UAAU,EAAE,MAAM,YAAY,CAAA;AAKxD,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,SAAS,EAAE,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,KAAG,CAalF,CAAA;AAED,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,SAAS,GAAG,SAAS,EAAC,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,QACnF,CAAA"}
package/build/context.js CHANGED
@@ -1,11 +1,15 @@
1
1
  import { appendFlowService } from '@owlmeans/web-flow';
2
2
  import { makeContext as makeClientContext, useContext as useCtx } from '@owlmeans/web-client';
3
3
  import { apiConfigMiddleware } from '@owlmeans/api-config-client';
4
+ import { appendLoginScreen } from './components/login/append.js';
4
5
  export const makeContext = (cfg) => {
5
6
  const context = makeClientContext(cfg);
6
7
  context.registerMiddleware(apiConfigMiddleware);
7
8
  appendFlowService(context);
8
9
  context.flow = () => context.service('flow');
10
+ // Every app on this package gets the shadcn sign-in screen with no wiring of its own; one that
11
+ // wants its logo on it calls `appendLoginScreen` again, which is idempotent.
12
+ appendLoginScreen(context);
9
13
  return context;
10
14
  };
11
15
  export const useContext = () => useCtx();
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAEtD,OAAO,EAAE,WAAW,IAAI,iBAAiB,EAAE,UAAU,IAAI,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAC7F,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AAEjE,MAAM,CAAC,MAAM,WAAW,GAAG,CAA+C,GAAM,EAAK,EAAE;IACrF,MAAM,OAAO,GAAG,iBAAiB,CAAC,GAAG,CAAM,CAAA;IAE3C,OAAO,CAAC,kBAAkB,CAAC,mBAAmB,CAAC,CAAA;IAE/C,iBAAiB,CAAO,OAAO,CAAC,CAAA;IAChC,OAAO,CAAC,IAAI,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;IAE5C,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,GAA4E,EAAE,CACtG,MAAM,EAAO,CAAA"}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAEtD,OAAO,EAAE,WAAW,IAAI,iBAAiB,EAAE,UAAU,IAAI,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAC7F,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AAEhE,MAAM,CAAC,MAAM,WAAW,GAAG,CAA+C,GAAM,EAAK,EAAE;IACrF,MAAM,OAAO,GAAG,iBAAiB,CAAC,GAAG,CAAM,CAAA;IAE3C,OAAO,CAAC,kBAAkB,CAAC,mBAAmB,CAAC,CAAA;IAE/C,iBAAiB,CAAO,OAAO,CAAC,CAAA;IAChC,OAAO,CAAC,IAAI,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;IAE5C,+FAA+F;IAC/F,6EAA6E;IAC7E,iBAAiB,CAAO,OAAO,CAAC,CAAA;IAEhC,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,GAA4E,EAAE,CACtG,MAAM,EAAO,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/web-panel",
3
- "version": "0.1.18-rc.2",
3
+ "version": "0.1.18-rc.21",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -33,40 +33,50 @@
33
33
  "default": "./build/auth/modules.js",
34
34
  "module": "./build/auth/modules.js",
35
35
  "types": "./build/auth/modules.d.ts"
36
+ },
37
+ "./consent": {
38
+ "import": "./build/consent/index.js",
39
+ "require": "./build/consent/index.js",
40
+ "default": "./build/consent/index.js",
41
+ "module": "./build/consent/index.js",
42
+ "types": "./build/consent/index.d.ts"
36
43
  }
37
44
  },
38
45
  "dependencies": {
39
46
  "@hookform/resolvers": "^5.2.2",
40
- "@owlmeans/api-config-client": "^0.1.18-rc.2",
41
- "@owlmeans/auth": "^0.1.18-rc.2",
42
- "@owlmeans/auth-common": "^0.1.18-rc.2",
43
- "@owlmeans/basic-envelope": "^0.1.18-rc.2",
44
- "@owlmeans/client": "^0.1.18-rc.2",
45
- "@owlmeans/client-auth": "^0.1.18-rc.2",
46
- "@owlmeans/client-config": "^0.1.18-rc.2",
47
- "@owlmeans/client-context": "^0.1.18-rc.2",
48
- "@owlmeans/client-flow": "^0.1.18-rc.2",
49
- "@owlmeans/client-i18n": "^0.1.18-rc.2",
50
- "@owlmeans/client-entrypoint": "^0.1.18-rc.2",
51
- "@owlmeans/client-panel": "^0.1.18-rc.2",
52
- "@owlmeans/client-route": "^0.1.18-rc.2",
53
- "@owlmeans/config": "^0.1.18-rc.2",
54
- "@owlmeans/context": "^0.1.18-rc.2",
55
- "@owlmeans/error": "^0.1.18-rc.2",
56
- "@owlmeans/flow": "^0.1.18-rc.2",
57
- "@owlmeans/i18n": "^0.1.18-rc.2",
58
- "@owlmeans/entrypoint": "^0.1.18-rc.2",
59
- "@owlmeans/route": "^0.1.18-rc.2",
60
- "@owlmeans/web-client": "^0.1.18-rc.2",
61
- "@owlmeans/web-db": "^0.1.18-rc.2",
62
- "@owlmeans/web-flow": "^0.1.18-rc.2",
63
- "@owlmeans/web-router": "^0.1.18-rc.2",
47
+ "@owlmeans/api-config-client": "^0.1.18-rc.9",
48
+ "@owlmeans/auth": "^0.1.18-rc.7",
49
+ "@owlmeans/auth-common": "^0.1.18-rc.9",
50
+ "@owlmeans/basic-envelope": "^0.1.18-rc.9",
51
+ "@owlmeans/client": "^0.1.18-rc.11",
52
+ "@owlmeans/client-auth": "^0.1.18-rc.13",
53
+ "@owlmeans/client-config": "^0.1.18-rc.8",
54
+ "@owlmeans/client-context": "^0.1.18-rc.9",
55
+ "@owlmeans/client-entrypoint": "^0.1.18-rc.9",
56
+ "@owlmeans/client-flow": "^0.1.18-rc.12",
57
+ "@owlmeans/client-i18n": "^0.1.18-rc.10",
58
+ "@owlmeans/client-panel": "^0.1.18-rc.17",
59
+ "@owlmeans/client-route": "^0.1.18-rc.9",
60
+ "@owlmeans/config": "^0.1.18-rc.8",
61
+ "@owlmeans/context": "^0.1.18-rc.6",
62
+ "@owlmeans/entrypoint": "^0.1.18-rc.8",
63
+ "@owlmeans/error": "^0.1.18-rc.6",
64
+ "@owlmeans/flow": "^0.1.18-rc.10",
65
+ "@owlmeans/i18n": "^0.1.18-rc.6",
66
+ "@owlmeans/route": "^0.1.18-rc.6",
67
+ "@owlmeans/web-client": "^0.1.18-rc.15",
68
+ "@owlmeans/web-consent": "^0.1.18-rc.3",
69
+ "@owlmeans/web-db": "^0.1.18-rc.10",
70
+ "@owlmeans/web-flow": "^0.1.18-rc.12",
71
+ "@owlmeans/web-router": "^0.1.18-rc.9",
64
72
  "i18next-browser-languagedetector": "^8.0.0",
65
73
  "qrcode.react": "^4.2.0",
66
- "react-google-recaptcha": "^3.1.0"
74
+ "react-google-recaptcha": "^3.1.0",
75
+ "sonner": "^2.0.7"
67
76
  },
68
77
  "peerDependencies": {
69
78
  "@radix-ui/react-label": "*",
79
+ "@radix-ui/react-navigation-menu": "*",
70
80
  "@radix-ui/react-progress": "*",
71
81
  "@radix-ui/react-separator": "*",
72
82
  "@radix-ui/react-slot": "*",
@@ -82,8 +92,9 @@
82
92
  },
83
93
  "devDependencies": {
84
94
  "@owlmeans/dep-config": "workspace:*",
85
- "@owlmeans/test-ui": "^0.1.18-rc.2",
95
+ "@owlmeans/test-ui": "^0.1.18-rc.9",
86
96
  "@radix-ui/react-label": "^2.1.0",
97
+ "@radix-ui/react-navigation-menu": "^1.2.14",
87
98
  "@radix-ui/react-progress": "^1.1.0",
88
99
  "@radix-ui/react-separator": "^1.1.0",
89
100
  "@radix-ui/react-slot": "^1.1.0",
@@ -0,0 +1,169 @@
1
+ // shadcn navigation-menu — sourced from shadcn/ui (new-york) 2026-08-24
2
+ import * as React from "react"
3
+ import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
4
+ import { cva } from "class-variance-authority"
5
+ import { ChevronDownIcon } from "lucide-react"
6
+
7
+ import { cn } from "@/lib/utils"
8
+
9
+ function NavigationMenu({
10
+ className,
11
+ children,
12
+ viewport = true,
13
+ ...props
14
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Root> & {
15
+ viewport?: boolean
16
+ }) {
17
+ return (
18
+ <NavigationMenuPrimitive.Root
19
+ data-slot="navigation-menu"
20
+ data-viewport={viewport}
21
+ className={cn(
22
+ "group/navigation-menu relative flex max-w-max flex-1 items-center justify-center",
23
+ className
24
+ )}
25
+ {...props}
26
+ >
27
+ {children}
28
+ {viewport && <NavigationMenuViewport />}
29
+ </NavigationMenuPrimitive.Root>
30
+ )
31
+ }
32
+
33
+ function NavigationMenuList({
34
+ className,
35
+ ...props
36
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.List>) {
37
+ return (
38
+ <NavigationMenuPrimitive.List
39
+ data-slot="navigation-menu-list"
40
+ className={cn(
41
+ "group flex flex-1 list-none items-center justify-center gap-1",
42
+ className
43
+ )}
44
+ {...props}
45
+ />
46
+ )
47
+ }
48
+
49
+ function NavigationMenuItem({
50
+ className,
51
+ ...props
52
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Item>) {
53
+ return (
54
+ <NavigationMenuPrimitive.Item
55
+ data-slot="navigation-menu-item"
56
+ className={cn("relative", className)}
57
+ {...props}
58
+ />
59
+ )
60
+ }
61
+
62
+ const navigationMenuTriggerStyle = cva(
63
+ "group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 focus-visible:ring-ring/50 outline-none transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1"
64
+ )
65
+
66
+ function NavigationMenuTrigger({
67
+ className,
68
+ children,
69
+ ...props
70
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>) {
71
+ return (
72
+ <NavigationMenuPrimitive.Trigger
73
+ data-slot="navigation-menu-trigger"
74
+ className={cn(navigationMenuTriggerStyle(), "group", className)}
75
+ {...props}
76
+ >
77
+ {children}{" "}
78
+ <ChevronDownIcon
79
+ className="relative top-[1px] ml-1 size-3 transition duration-300 group-data-[state=open]:rotate-180"
80
+ aria-hidden="true"
81
+ />
82
+ </NavigationMenuPrimitive.Trigger>
83
+ )
84
+ }
85
+
86
+ function NavigationMenuContent({
87
+ className,
88
+ ...props
89
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Content>) {
90
+ return (
91
+ <NavigationMenuPrimitive.Content
92
+ data-slot="navigation-menu-content"
93
+ className={cn(
94
+ "data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 top-0 left-0 w-full p-2 pr-2.5 md:absolute md:w-auto",
95
+ "group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none",
96
+ className
97
+ )}
98
+ {...props}
99
+ />
100
+ )
101
+ }
102
+
103
+ function NavigationMenuViewport({
104
+ className,
105
+ ...props
106
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Viewport>) {
107
+ return (
108
+ <div
109
+ className={cn(
110
+ "absolute top-full left-0 isolate z-50 flex justify-center"
111
+ )}
112
+ >
113
+ <NavigationMenuPrimitive.Viewport
114
+ data-slot="navigation-menu-viewport"
115
+ className={cn(
116
+ "origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow md:w-[var(--radix-navigation-menu-viewport-width)]",
117
+ className
118
+ )}
119
+ {...props}
120
+ />
121
+ </div>
122
+ )
123
+ }
124
+
125
+ function NavigationMenuLink({
126
+ className,
127
+ ...props
128
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Link>) {
129
+ return (
130
+ <NavigationMenuPrimitive.Link
131
+ data-slot="navigation-menu-link"
132
+ className={cn(
133
+ "data-[active=true]:focus:bg-accent data-[active=true]:hover:bg-accent data-[active=true]:bg-accent/50 data-[active=true]:text-accent-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus-visible:ring-ring/50 [&_svg:not([class*='text-'])]:text-muted-foreground flex flex-col gap-1 rounded-sm p-2 text-sm transition-all outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&_svg:not([class*='size-'])]:size-4",
134
+ className
135
+ )}
136
+ {...props}
137
+ />
138
+ )
139
+ }
140
+
141
+ function NavigationMenuIndicator({
142
+ className,
143
+ ...props
144
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Indicator>) {
145
+ return (
146
+ <NavigationMenuPrimitive.Indicator
147
+ data-slot="navigation-menu-indicator"
148
+ className={cn(
149
+ "data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden",
150
+ className
151
+ )}
152
+ {...props}
153
+ >
154
+ <div className="bg-border relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md" />
155
+ </NavigationMenuPrimitive.Indicator>
156
+ )
157
+ }
158
+
159
+ export {
160
+ NavigationMenu,
161
+ NavigationMenuList,
162
+ NavigationMenuItem,
163
+ NavigationMenuContent,
164
+ NavigationMenuTrigger,
165
+ NavigationMenuLink,
166
+ NavigationMenuIndicator,
167
+ NavigationMenuViewport,
168
+ navigationMenuTriggerStyle,
169
+ }
@@ -37,7 +37,7 @@ export const Ed22519BasicAuthUIPlugin: AuthenticationRenderer = ({ type, stage,
37
37
 
38
38
  const createSubmit = (context: AppContext, control: AuthenticationControl) => async (data: FormData) => {
39
39
  const token = await control.authenticate({
40
- entityId: data.entityId,
40
+ entitySlug: data.entityId,
41
41
  userId: data.address,
42
42
  credential: data.privateKey
43
43
  })
@@ -0,0 +1,37 @@
1
+ import type { FC } from 'react'
2
+ import { defaultNavTranslate, resolveNavLabel } from '@owlmeans/client-panel'
3
+ import { cn } from '@/lib/utils'
4
+ import { Link } from '../link.js'
5
+
6
+ import type { FooterProps } from './types.js'
7
+
8
+ /**
9
+ * The standard footer — a row of links plus whatever the app adds as children.
10
+ *
11
+ * Labels are resolved HERE and handed to `Link` as children: `Link`'s own default label
12
+ * looks up `modules.<alias>` with no fallback, so an app without i18n would print the raw
13
+ * key. Resolving first means a literal label, a translation, or a humanized alias — never
14
+ * a key.
15
+ */
16
+ export const Footer: FC<FooterProps> = ({
17
+ links, translate = defaultNavTranslate, children, className, style, containerClassName
18
+ }) => {
19
+ if ((links == null || links.length < 1) && children == null) {
20
+ return null
21
+ }
22
+
23
+ return <footer className={cn('border-t py-6', className)} style={style}>
24
+ <div className={cn('flex flex-wrap items-center gap-4 text-sm px-4', containerClassName)}>
25
+ {links?.map((link, idx) => {
26
+ const label = resolveNavLabel(
27
+ translate, link.label, `modules.${link.alias ?? link.href ?? ''}`, link.alias ?? link.href
28
+ )
29
+
30
+ return link.href != null
31
+ ? <Link key={`${link.href}:${idx}`} src={link.href} open={link.open}>{label}</Link>
32
+ : <Link key={`${link.alias}:${idx}`} module={link.alias} open={link.open}>{label}</Link>
33
+ })}
34
+ {children}
35
+ </div>
36
+ </footer>
37
+ }
@@ -0,0 +1,3 @@
1
+ export type * from './types.js'
2
+
3
+ export * from './component.js'
@@ -0,0 +1,11 @@
1
+ import type { PropsWithChildren } from 'react'
2
+ import type { NavTranslate, PanelNavLink } from '@owlmeans/client-panel'
3
+ import type { StyledProps } from '../types.js'
4
+
5
+ export interface FooterProps extends PropsWithChildren<StyledProps> {
6
+ links?: PanelNavLink[]
7
+ /** The shell's horizontal rhythm, so the footer row lines up with the header and the content. */
8
+ containerClassName?: string
9
+ /** See {@link NavTranslate} — omitted, literal labels and humanized aliases are used. */
10
+ translate?: NavTranslate
11
+ }
@@ -2,6 +2,9 @@ export type * from './types.js'
2
2
 
3
3
  export * from './panel-app/index.js'
4
4
  export * from './layout/index.js'
5
+ export * from './nav/index.js'
6
+ export * from './footer/index.js'
7
+ export * from './login/index.js'
5
8
 
6
9
  export * from './form/index.js'
7
10
  export * from './form/text/index.js'
@@ -15,3 +18,4 @@ export * from './text.js'
15
18
  export * from './link.js'
16
19
  export * from './helper.js'
17
20
  export * from './status.js'
21
+ export * from './toaster.js'
@@ -0,0 +1,34 @@
1
+ import type { ComponentType, ReactNode } from 'react'
2
+ import { ensureLoginService } from '@owlmeans/client-auth/login'
3
+ import type { LoginScreenProps } from '@owlmeans/client-auth/login'
4
+ import type { BasicConfig, BasicContext } from '@owlmeans/context'
5
+ import { LocalizedLoginScreen } from './screen.js'
6
+
7
+ export interface LoginScreenSetup extends Omit<LoginScreenProps, 'translate'> {
8
+ /** The one thing an application is expected to supply. */
9
+ Logo?: ComponentType<{ className?: string }> | ReactNode
10
+ }
11
+
12
+ /**
13
+ * Give this context the shadcn sign-in screen, with the application's own logo and copy on it.
14
+ *
15
+ * Registered on the login SERVICE rather than imported by a dispatcher, because a relying party
16
+ * (`web-oidc-rp`, `mui-oidc-rp`) must never depend on a UI family — that edge would make every
17
+ * relying party pick one, and there are two. The dispatcher asks `login().screen()` and falls back
18
+ * to the plain screen `@owlmeans/client-auth` ships, so an app that never calls this still gets a
19
+ * chooser rather than an auto-redirect.
20
+ *
21
+ * Idempotent: called by `makeContext` with defaults, and again by the app with its own logo.
22
+ */
23
+ export const appendLoginScreen = <C extends BasicConfig, T extends BasicContext<C>>(
24
+ ctx: T, setup?: LoginScreenSetup
25
+ ): T => {
26
+ const service = ensureLoginService(ctx)
27
+ service.registerScreen(
28
+ setup == null || Object.keys(setup).length < 1
29
+ ? LocalizedLoginScreen
30
+ : props => LocalizedLoginScreen({ ...setup, ...props })
31
+ )
32
+
33
+ return ctx
34
+ }
@@ -0,0 +1,34 @@
1
+ import type { FC } from 'react'
2
+ import { cn } from '@/lib/utils'
3
+ import type { LoginCreditModel } from '@owlmeans/client-panel/auth'
4
+
5
+ export interface LoginCreditProps {
6
+ model: LoginCreditModel
7
+ translate: (key: string, defaultValue: string) => string
8
+ className?: string
9
+ }
10
+
11
+ /**
12
+ * The line at the bottom of a sign-in screen.
13
+ *
14
+ * Two halves, and each may stand alone: the platform credit, and who the application belongs to.
15
+ * A generated application shows both — "Powered by OwlMeans" beside its own product and
16
+ * organization — and an application whose owner has paid to drop the credit shows only the second.
17
+ * Rendering nothing at all is also correct, for an app that is neither.
18
+ */
19
+ export const LoginCredit: FC<LoginCreditProps> = ({ model, translate, className }) => {
20
+ if (!model.poweredBy && model.line == null) {
21
+ return null
22
+ }
23
+
24
+ return <p
25
+ data-login-credit
26
+ className={cn('text-center text-xs text-muted-foreground', className)}
27
+ >
28
+ {model.poweredBy && <span data-login-powered>
29
+ {translate('login.credit.powered', 'Powered by OwlMeans')}
30
+ </span>}
31
+ {model.poweredBy && model.line != null && <span aria-hidden="true"> · </span>}
32
+ {model.line != null && <span>{model.line}</span>}
33
+ </p>
34
+ }
@@ -0,0 +1,39 @@
1
+ import type { FC } from 'react'
2
+ import { KeyRound, LogIn, Mail, ShieldCheck, Wallet } from 'lucide-react'
3
+
4
+ /**
5
+ * Google's mark, inline.
6
+ *
7
+ * Lucide carries no brand marks, and a remote image on a sign-in screen is a third-party request
8
+ * before consent. Four paths cost less than either.
9
+ */
10
+ const GoogleMark: FC<{ className?: string }> = ({ className }) => (
11
+ <svg className={className} viewBox="0 0 48 48" aria-hidden="true" role="presentation">
12
+ <path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z" />
13
+ <path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z" />
14
+ <path fill="#FBBC05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z" />
15
+ <path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z" />
16
+ </svg>
17
+ )
18
+
19
+ const ICONS: Record<string, FC<{ className?: string }>> = {
20
+ google: GoogleMark,
21
+ key: KeyRound,
22
+ shield: ShieldCheck,
23
+ wallet: Wallet,
24
+ mail: Mail,
25
+ }
26
+
27
+ /**
28
+ * A method's icon, resolved from a NAME.
29
+ *
30
+ * The registry carries names rather than components so that `@owlmeans/client-auth` and
31
+ * `@owlmeans/oidc` — neither of which may depend on an icon library — can describe a method
32
+ * completely. An unknown name renders the generic sign-in glyph rather than nothing, because a row
33
+ * of buttons where one has no icon reads as a rendering fault.
34
+ */
35
+ export const LoginMethodIcon: FC<{ name?: string, className?: string }> = ({ name, className }) => {
36
+ const Icon = (name != null ? ICONS[name] : undefined) ?? LogIn
37
+
38
+ return <Icon className={className} />
39
+ }
@@ -0,0 +1,5 @@
1
+ export * from './screen.js'
2
+ export * from './terms.js'
3
+ export * from './credit.js'
4
+ export * from './icons.js'
5
+ export * from './append.js'