@owlmeans/web-panel 0.1.18-rc.9 → 0.1.18

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 (669) hide show
  1. package/README.md +510 -143
  2. package/THIRD-PARTY-NOTICES.txt +24 -0
  3. package/agent-meta/manifest.json +163 -4
  4. package/agent-meta/skills/web-panel/SKILL.md +136 -83
  5. package/agent-meta/skills/web-panel/parts/consent.md +114 -0
  6. package/agent-meta/skills/web-panel/parts/editor.md +47 -0
  7. package/agent-meta/skills/web-panel/parts/forms.md +85 -0
  8. package/agent-meta/skills/web-panel/parts/navigation.md +203 -0
  9. package/agent-meta/skills/web-panel/parts/picker.md +164 -0
  10. package/agent-meta/skills/web-panel/parts/reload.md +46 -0
  11. package/agent-meta/skills/web-panel/parts/sign-in.md +65 -0
  12. package/agent-meta/skills/web-panel/parts/theme.md +74 -0
  13. package/build/@/components/ui/alert-dialog.d.ts +15 -0
  14. package/build/@/components/ui/alert-dialog.d.ts.map +1 -0
  15. package/build/@/components/ui/alert-dialog.js +39 -0
  16. package/build/@/components/ui/alert-dialog.js.map +1 -0
  17. package/build/@/components/ui/alert.js +1 -1
  18. package/build/@/components/ui/alert.js.map +1 -1
  19. package/build/@/components/ui/button.js +1 -1
  20. package/build/@/components/ui/button.js.map +1 -1
  21. package/build/@/components/ui/card.js +1 -1
  22. package/build/@/components/ui/card.js.map +1 -1
  23. package/build/@/components/ui/input.js +1 -1
  24. package/build/@/components/ui/input.js.map +1 -1
  25. package/build/@/components/ui/label.js +1 -1
  26. package/build/@/components/ui/label.js.map +1 -1
  27. package/build/@/components/ui/navigation-menu.js +1 -1
  28. package/build/@/components/ui/navigation-menu.js.map +1 -1
  29. package/build/@/components/ui/popover.d.ts +8 -0
  30. package/build/@/components/ui/popover.d.ts.map +1 -0
  31. package/build/@/components/ui/popover.js +17 -0
  32. package/build/@/components/ui/popover.js.map +1 -0
  33. package/build/@/components/ui/progress.js +1 -1
  34. package/build/@/components/ui/progress.js.map +1 -1
  35. package/build/@/components/ui/separator.js +1 -1
  36. package/build/@/components/ui/separator.js.map +1 -1
  37. package/build/@/components/ui/sheet.d.ts +15 -0
  38. package/build/@/components/ui/sheet.d.ts.map +1 -0
  39. package/build/@/components/ui/sheet.js +40 -0
  40. package/build/@/components/ui/sheet.js.map +1 -0
  41. package/build/auth/context.d.ts.map +1 -1
  42. package/build/auth/context.js +0 -1
  43. package/build/auth/context.js.map +1 -1
  44. package/build/auth/entrypoints.d.ts +10 -0
  45. package/build/auth/entrypoints.d.ts.map +1 -0
  46. package/build/auth/entrypoints.js +4 -0
  47. package/build/auth/entrypoints.js.map +1 -0
  48. package/build/auth/exports.d.ts +4 -4
  49. package/build/auth/exports.d.ts.map +1 -1
  50. package/build/auth/exports.js +3 -3
  51. package/build/auth/exports.js.map +1 -1
  52. package/build/auth/modules.d.ts +1 -1
  53. package/build/auth/modules.d.ts.map +1 -1
  54. package/build/auth/plugins/basic-ed25519.js +4 -4
  55. package/build/auth/plugins/basic-ed25519.js.map +1 -1
  56. package/build/auth/plugins/re-captcha.js +3 -3
  57. package/build/auth/plugins/re-captcha.js.map +1 -1
  58. package/build/auth/plugins/tunnel-consumer.js +1 -1
  59. package/build/auth/plugins/tunnel-consumer.js.map +1 -1
  60. package/build/components/block.js +2 -2
  61. package/build/components/block.js.map +1 -1
  62. package/build/components/consts.local.d.ts +14 -0
  63. package/build/components/consts.local.d.ts.map +1 -0
  64. package/build/components/consts.local.js +26 -0
  65. package/build/components/consts.local.js.map +1 -0
  66. package/build/components/footer/component.d.ts +19 -1
  67. package/build/components/footer/component.d.ts.map +1 -1
  68. package/build/components/footer/component.js +38 -11
  69. package/build/components/footer/component.js.map +1 -1
  70. package/build/components/footer/credit.d.ts +27 -0
  71. package/build/components/footer/credit.d.ts.map +1 -0
  72. package/build/components/footer/credit.js +42 -0
  73. package/build/components/footer/credit.js.map +1 -0
  74. package/build/components/footer/index.d.ts +1 -0
  75. package/build/components/footer/index.d.ts.map +1 -1
  76. package/build/components/footer/index.js +1 -0
  77. package/build/components/footer/index.js.map +1 -1
  78. package/build/components/footer/types.d.ts +22 -1
  79. package/build/components/footer/types.d.ts.map +1 -1
  80. package/build/components/form/button/component.d.ts.map +1 -1
  81. package/build/components/form/button/component.js +5 -3
  82. package/build/components/form/button/component.js.map +1 -1
  83. package/build/components/form/component.js +2 -2
  84. package/build/components/form/component.js.map +1 -1
  85. package/build/components/form/text/component.js +2 -2
  86. package/build/components/form/text/component.js.map +1 -1
  87. package/build/components/helper.d.ts.map +1 -1
  88. package/build/components/helper.js +1 -12
  89. package/build/components/helper.js.map +1 -1
  90. package/build/components/index.d.ts +4 -0
  91. package/build/components/index.d.ts.map +1 -1
  92. package/build/components/index.js +4 -0
  93. package/build/components/index.js.map +1 -1
  94. package/build/components/link.js +3 -3
  95. package/build/components/link.js.map +1 -1
  96. package/build/components/login/append.d.ts +15 -0
  97. package/build/components/login/append.d.ts.map +1 -0
  98. package/build/components/login/append.js +26 -0
  99. package/build/components/login/append.js.map +1 -0
  100. package/build/components/login/consts.d.ts +3 -0
  101. package/build/components/login/consts.d.ts.map +1 -0
  102. package/build/components/login/consts.js +3 -0
  103. package/build/components/login/consts.js.map +1 -0
  104. package/build/components/login/consts.local.d.ts +4 -0
  105. package/build/components/login/consts.local.d.ts.map +1 -0
  106. package/build/components/login/consts.local.js +8 -0
  107. package/build/components/login/consts.local.js.map +1 -0
  108. package/build/components/login/credit.d.ts +12 -0
  109. package/build/components/login/credit.d.ts.map +1 -0
  110. package/build/components/login/credit.js +18 -0
  111. package/build/components/login/credit.js.map +1 -0
  112. package/build/components/login/i18n/be.json +10 -0
  113. package/build/components/login/i18n/de.json +10 -0
  114. package/build/components/login/i18n/en.json +10 -0
  115. package/build/components/login/i18n/es.json +10 -0
  116. package/build/components/login/i18n/fr.json +10 -0
  117. package/build/components/login/i18n/pl.json +10 -0
  118. package/build/components/login/i18n/ru.json +10 -0
  119. package/build/components/login/i18n/uk.json +10 -0
  120. package/build/components/login/i18n.d.ts +2 -0
  121. package/build/components/login/i18n.d.ts.map +1 -0
  122. package/build/components/login/i18n.js +28 -0
  123. package/build/components/login/i18n.js.map +1 -0
  124. package/build/components/login/icons.d.ts +14 -0
  125. package/build/components/login/icons.d.ts.map +1 -0
  126. package/build/components/login/icons.js +29 -0
  127. package/build/components/login/icons.js.map +1 -0
  128. package/build/components/login/index.d.ts +11 -0
  129. package/build/components/login/index.d.ts.map +1 -0
  130. package/build/components/login/index.js +10 -0
  131. package/build/components/login/index.js.map +1 -0
  132. package/build/components/login/notify.d.ts +18 -0
  133. package/build/components/login/notify.d.ts.map +1 -0
  134. package/build/components/login/notify.js +41 -0
  135. package/build/components/login/notify.js.map +1 -0
  136. package/build/components/login/provider.d.ts +20 -0
  137. package/build/components/login/provider.d.ts.map +1 -0
  138. package/build/components/login/provider.js +31 -0
  139. package/build/components/login/provider.js.map +1 -0
  140. package/build/components/login/screen.d.ts +24 -0
  141. package/build/components/login/screen.d.ts.map +1 -0
  142. package/build/components/login/screen.js +92 -0
  143. package/build/components/login/screen.js.map +1 -0
  144. package/build/components/login/terms.d.ts +19 -0
  145. package/build/components/login/terms.d.ts.map +1 -0
  146. package/build/components/login/terms.js +32 -0
  147. package/build/components/login/terms.js.map +1 -0
  148. package/build/components/login/types.d.ts +28 -0
  149. package/build/components/login/types.d.ts.map +1 -0
  150. package/build/components/login/types.js +2 -0
  151. package/build/components/login/types.js.map +1 -0
  152. package/build/components/nav/consts.local.d.ts +41 -0
  153. package/build/components/nav/consts.local.d.ts.map +1 -0
  154. package/build/components/nav/consts.local.js +60 -0
  155. package/build/components/nav/consts.local.js.map +1 -0
  156. package/build/components/nav/index.d.ts +1 -0
  157. package/build/components/nav/index.d.ts.map +1 -1
  158. package/build/components/nav/index.js +1 -0
  159. package/build/components/nav/index.js.map +1 -1
  160. package/build/components/nav/layout.d.ts +9 -2
  161. package/build/components/nav/layout.d.ts.map +1 -1
  162. package/build/components/nav/layout.js +48 -7
  163. package/build/components/nav/layout.js.map +1 -1
  164. package/build/components/nav/mobile.d.ts +21 -0
  165. package/build/components/nav/mobile.d.ts.map +1 -0
  166. package/build/components/nav/mobile.js +55 -0
  167. package/build/components/nav/mobile.js.map +1 -0
  168. package/build/components/nav/side.d.ts.map +1 -1
  169. package/build/components/nav/side.js +10 -10
  170. package/build/components/nav/side.js.map +1 -1
  171. package/build/components/nav/top.d.ts.map +1 -1
  172. package/build/components/nav/top.js +12 -8
  173. package/build/components/nav/top.js.map +1 -1
  174. package/build/components/nav/types.d.ts +64 -1
  175. package/build/components/nav/types.d.ts.map +1 -1
  176. package/build/components/nav/viewport.d.ts +3 -0
  177. package/build/components/nav/viewport.d.ts.map +1 -0
  178. package/build/components/nav/viewport.js +14 -0
  179. package/build/components/nav/viewport.js.map +1 -0
  180. package/build/components/panel-app/component.d.ts.map +1 -1
  181. package/build/components/panel-app/component.js +4 -3
  182. package/build/components/panel-app/component.js.map +1 -1
  183. package/build/components/picker/color-picker.d.ts +9 -0
  184. package/build/components/picker/color-picker.d.ts.map +1 -0
  185. package/build/components/picker/color-picker.js +9 -0
  186. package/build/components/picker/color-picker.js.map +1 -0
  187. package/build/components/picker/consts.local.d.ts +76 -0
  188. package/build/components/picker/consts.local.d.ts.map +1 -0
  189. package/build/components/picker/consts.local.js +214 -0
  190. package/build/components/picker/consts.local.js.map +1 -0
  191. package/build/components/picker/dot.d.ts +8 -0
  192. package/build/components/picker/dot.d.ts.map +1 -0
  193. package/build/components/picker/dot.js +10 -0
  194. package/build/components/picker/dot.js.map +1 -0
  195. package/build/components/picker/grid.d.ts +17 -0
  196. package/build/components/picker/grid.d.ts.map +1 -0
  197. package/build/components/picker/grid.js +73 -0
  198. package/build/components/picker/grid.js.map +1 -0
  199. package/build/components/picker/i18n/be.json +60 -0
  200. package/build/components/picker/i18n/de.json +60 -0
  201. package/build/components/picker/i18n/en.json +60 -0
  202. package/build/components/picker/i18n/es.json +60 -0
  203. package/build/components/picker/i18n/fr.json +60 -0
  204. package/build/components/picker/i18n/pl.json +60 -0
  205. package/build/components/picker/i18n/ru.json +60 -0
  206. package/build/components/picker/i18n/uk.json +60 -0
  207. package/build/components/picker/i18n.d.ts +2 -0
  208. package/build/components/picker/i18n.d.ts.map +1 -0
  209. package/build/components/picker/i18n.js +18 -0
  210. package/build/components/picker/i18n.js.map +1 -0
  211. package/build/components/picker/icon-color-picker.d.ts +14 -0
  212. package/build/components/picker/icon-color-picker.d.ts.map +1 -0
  213. package/build/components/picker/icon-color-picker.js +15 -0
  214. package/build/components/picker/icon-color-picker.js.map +1 -0
  215. package/build/components/picker/icon-picker.d.ts +8 -0
  216. package/build/components/picker/icon-picker.d.ts.map +1 -0
  217. package/build/components/picker/icon-picker.js +8 -0
  218. package/build/components/picker/icon-picker.js.map +1 -0
  219. package/build/components/picker/index.d.ts +11 -0
  220. package/build/components/picker/index.d.ts.map +1 -0
  221. package/build/components/picker/index.js +10 -0
  222. package/build/components/picker/index.js.map +1 -0
  223. package/build/components/picker/panel.d.ts +9 -0
  224. package/build/components/picker/panel.d.ts.map +1 -0
  225. package/build/components/picker/panel.js +23 -0
  226. package/build/components/picker/panel.js.map +1 -0
  227. package/build/components/picker/root.d.ts +13 -0
  228. package/build/components/picker/root.d.ts.map +1 -0
  229. package/build/components/picker/root.js +55 -0
  230. package/build/components/picker/root.js.map +1 -0
  231. package/build/components/picker/roving.d.ts +8 -0
  232. package/build/components/picker/roving.d.ts.map +1 -0
  233. package/build/components/picker/roving.js +62 -0
  234. package/build/components/picker/roving.js.map +1 -0
  235. package/build/components/picker/surface.d.ts +18 -0
  236. package/build/components/picker/surface.d.ts.map +1 -0
  237. package/build/components/picker/surface.js +36 -0
  238. package/build/components/picker/surface.js.map +1 -0
  239. package/build/components/picker/swatches/custom.d.ts +10 -0
  240. package/build/components/picker/swatches/custom.d.ts.map +1 -0
  241. package/build/components/picker/swatches/custom.js +65 -0
  242. package/build/components/picker/swatches/custom.js.map +1 -0
  243. package/build/components/picker/swatches.d.ts +16 -0
  244. package/build/components/picker/swatches.d.ts.map +1 -0
  245. package/build/components/picker/swatches.js +35 -0
  246. package/build/components/picker/swatches.js.map +1 -0
  247. package/build/components/picker/text.d.ts +5 -0
  248. package/build/components/picker/text.d.ts.map +1 -0
  249. package/build/components/picker/text.js +29 -0
  250. package/build/components/picker/text.js.map +1 -0
  251. package/build/components/picker/tile.d.ts +11 -0
  252. package/build/components/picker/tile.d.ts.map +1 -0
  253. package/build/components/picker/tile.js +19 -0
  254. package/build/components/picker/tile.js.map +1 -0
  255. package/build/components/picker/translate.d.ts +9 -0
  256. package/build/components/picker/translate.d.ts.map +1 -0
  257. package/build/components/picker/translate.js +13 -0
  258. package/build/components/picker/translate.js.map +1 -0
  259. package/build/components/picker/trigger.d.ts +12 -0
  260. package/build/components/picker/trigger.d.ts.map +1 -0
  261. package/build/components/picker/trigger.js +29 -0
  262. package/build/components/picker/trigger.js.map +1 -0
  263. package/build/components/picker/types.d.ts +114 -0
  264. package/build/components/picker/types.d.ts.map +1 -0
  265. package/build/components/picker/types.js +2 -0
  266. package/build/components/picker/types.js.map +1 -0
  267. package/build/components/picker/types.local.d.ts +143 -0
  268. package/build/components/picker/types.local.d.ts.map +1 -0
  269. package/build/components/picker/types.local.js +2 -0
  270. package/build/components/picker/types.local.js.map +1 -0
  271. package/build/components/scheme/consts.local.d.ts +8 -0
  272. package/build/components/scheme/consts.local.d.ts.map +1 -0
  273. package/build/components/scheme/consts.local.js +13 -0
  274. package/build/components/scheme/consts.local.js.map +1 -0
  275. package/build/components/scheme/hook.d.ts +16 -0
  276. package/build/components/scheme/hook.d.ts.map +1 -0
  277. package/build/components/scheme/hook.js +52 -0
  278. package/build/components/scheme/hook.js.map +1 -0
  279. package/build/components/scheme/index.d.ts +4 -0
  280. package/build/components/scheme/index.d.ts.map +1 -0
  281. package/build/components/scheme/index.js +3 -0
  282. package/build/components/scheme/index.js.map +1 -0
  283. package/build/components/scheme/toggle.d.ts +14 -0
  284. package/build/components/scheme/toggle.d.ts.map +1 -0
  285. package/build/components/scheme/toggle.js +24 -0
  286. package/build/components/scheme/toggle.js.map +1 -0
  287. package/build/components/scheme/types.d.ts +21 -0
  288. package/build/components/scheme/types.d.ts.map +1 -0
  289. package/build/components/scheme/types.js +2 -0
  290. package/build/components/scheme/types.js.map +1 -0
  291. package/build/components/socket/i18n/be.json +9 -0
  292. package/build/components/socket/i18n/de.json +9 -0
  293. package/build/components/socket/i18n/en.json +9 -0
  294. package/build/components/socket/i18n/es.json +9 -0
  295. package/build/components/socket/i18n/fr.json +9 -0
  296. package/build/components/socket/i18n/pl.json +9 -0
  297. package/build/components/socket/i18n/ru.json +9 -0
  298. package/build/components/socket/i18n/uk.json +9 -0
  299. package/build/components/socket/i18n.d.ts +2 -0
  300. package/build/components/socket/i18n.d.ts.map +1 -0
  301. package/build/components/socket/i18n.js +18 -0
  302. package/build/components/socket/i18n.js.map +1 -0
  303. package/build/components/socket/index.d.ts +3 -0
  304. package/build/components/socket/index.d.ts.map +1 -0
  305. package/build/components/socket/index.js +3 -0
  306. package/build/components/socket/index.js.map +1 -0
  307. package/build/components/socket/reload-dialog.d.ts +22 -0
  308. package/build/components/socket/reload-dialog.d.ts.map +1 -0
  309. package/build/components/socket/reload-dialog.js +34 -0
  310. package/build/components/socket/reload-dialog.js.map +1 -0
  311. package/build/components/status.js +1 -1
  312. package/build/components/status.js.map +1 -1
  313. package/build/components/text.d.ts.map +1 -1
  314. package/build/components/text.js +2 -13
  315. package/build/components/text.js.map +1 -1
  316. package/build/components/toaster.d.ts +16 -0
  317. package/build/components/toaster.d.ts.map +1 -0
  318. package/build/components/toaster.js +52 -0
  319. package/build/components/toaster.js.map +1 -0
  320. package/build/components/uploader/consts.local.d.ts +4 -0
  321. package/build/components/uploader/consts.local.d.ts.map +1 -0
  322. package/build/components/uploader/consts.local.js +6 -0
  323. package/build/components/uploader/consts.local.js.map +1 -0
  324. package/build/components/uploader/image.d.ts.map +1 -1
  325. package/build/components/uploader/image.js +2 -6
  326. package/build/components/uploader/image.js.map +1 -1
  327. package/build/consent/component.d.ts +27 -0
  328. package/build/consent/component.d.ts.map +1 -0
  329. package/build/consent/component.js +60 -0
  330. package/build/consent/component.js.map +1 -0
  331. package/build/consent/consts.d.ts +2 -0
  332. package/build/consent/consts.d.ts.map +1 -0
  333. package/build/consent/consts.js +2 -0
  334. package/build/consent/consts.js.map +1 -0
  335. package/build/consent/consts.local.d.ts +9 -0
  336. package/build/consent/consts.local.d.ts.map +1 -0
  337. package/build/consent/consts.local.js +9 -0
  338. package/build/consent/consts.local.js.map +1 -0
  339. package/build/consent/geo.d.ts +17 -0
  340. package/build/consent/geo.d.ts.map +1 -0
  341. package/build/consent/geo.js +19 -0
  342. package/build/consent/geo.js.map +1 -0
  343. package/build/consent/index.d.ts +11 -0
  344. package/build/consent/index.d.ts.map +1 -0
  345. package/build/consent/index.js +9 -0
  346. package/build/consent/index.js.map +1 -0
  347. package/build/consent/language.d.ts +12 -0
  348. package/build/consent/language.d.ts.map +1 -0
  349. package/build/consent/language.js +11 -0
  350. package/build/consent/language.js.map +1 -0
  351. package/build/consent/menu-widget.d.ts +13 -0
  352. package/build/consent/menu-widget.d.ts.map +1 -0
  353. package/build/consent/menu-widget.js +20 -0
  354. package/build/consent/menu-widget.js.map +1 -0
  355. package/build/consent/presence.d.ts +30 -0
  356. package/build/consent/presence.d.ts.map +1 -0
  357. package/build/consent/presence.js +40 -0
  358. package/build/consent/presence.js.map +1 -0
  359. package/build/consent/service.d.ts +18 -0
  360. package/build/consent/service.d.ts.map +1 -0
  361. package/build/consent/service.js +53 -0
  362. package/build/consent/service.js.map +1 -0
  363. package/build/consent/translate.d.ts +14 -0
  364. package/build/consent/translate.d.ts.map +1 -0
  365. package/build/consent/translate.js +28 -0
  366. package/build/consent/translate.js.map +1 -0
  367. package/build/consent/types.d.ts +39 -0
  368. package/build/consent/types.d.ts.map +1 -0
  369. package/build/consent/types.js +2 -0
  370. package/build/consent/types.js.map +1 -0
  371. package/build/consent/types.local.d.ts +6 -0
  372. package/build/consent/types.local.d.ts.map +1 -0
  373. package/build/consent/types.local.js +2 -0
  374. package/build/consent/types.local.js.map +1 -0
  375. package/build/context.d.ts.map +1 -1
  376. package/build/context.js +10 -0
  377. package/build/context.js.map +1 -1
  378. package/build/editor/component.d.ts +4 -0
  379. package/build/editor/component.d.ts.map +1 -0
  380. package/build/editor/component.js +103 -0
  381. package/build/editor/component.js.map +1 -0
  382. package/build/editor/consts.local.d.ts +6 -0
  383. package/build/editor/consts.local.d.ts.map +1 -0
  384. package/build/editor/consts.local.js +58 -0
  385. package/build/editor/consts.local.js.map +1 -0
  386. package/build/editor/index.d.ts +3 -0
  387. package/build/editor/index.d.ts.map +1 -0
  388. package/build/editor/index.js +2 -0
  389. package/build/editor/index.js.map +1 -0
  390. package/build/editor/types.d.ts +24 -0
  391. package/build/editor/types.d.ts.map +1 -0
  392. package/build/editor/types.js +2 -0
  393. package/build/editor/types.js.map +1 -0
  394. package/build/entrypoints.d.ts +8 -0
  395. package/build/entrypoints.d.ts.map +1 -0
  396. package/build/entrypoints.js +4 -0
  397. package/build/entrypoints.js.map +1 -0
  398. package/build/exports.d.ts +6 -5
  399. package/build/exports.d.ts.map +1 -1
  400. package/build/exports.js +5 -5
  401. package/build/exports.js.map +1 -1
  402. package/build/index.d.ts +1 -1
  403. package/build/index.d.ts.map +1 -1
  404. package/build/index.js +1 -1
  405. package/build/index.js.map +1 -1
  406. package/build/main.d.ts +1 -4
  407. package/build/main.d.ts.map +1 -1
  408. package/build/main.js.map +1 -1
  409. package/build/modules.d.ts +1 -1
  410. package/build/modules.d.ts.map +1 -1
  411. package/build/permission/i18n/be.json +13 -0
  412. package/build/permission/i18n/de.json +13 -0
  413. package/build/permission/i18n/en.json +13 -0
  414. package/build/permission/i18n/es.json +13 -0
  415. package/build/permission/i18n/fr.json +13 -0
  416. package/build/permission/i18n/pl.json +13 -0
  417. package/build/permission/i18n/ru.json +13 -0
  418. package/build/permission/i18n/uk.json +13 -0
  419. package/build/permission/i18n.d.ts +2 -0
  420. package/build/permission/i18n.d.ts.map +1 -0
  421. package/build/permission/i18n.js +13 -0
  422. package/build/permission/i18n.js.map +1 -0
  423. package/build/permission/index.d.ts +6 -0
  424. package/build/permission/index.d.ts.map +1 -0
  425. package/build/permission/index.js +27 -0
  426. package/build/permission/index.js.map +1 -0
  427. package/build/permission/toast.d.ts +5 -0
  428. package/build/permission/toast.d.ts.map +1 -0
  429. package/build/permission/toast.js +8 -0
  430. package/build/permission/toast.js.map +1 -0
  431. package/build/permission/types.d.ts +6 -0
  432. package/build/permission/types.d.ts.map +1 -0
  433. package/build/permission/types.js +2 -0
  434. package/build/permission/types.js.map +1 -0
  435. package/build/picker/icons/consts.d.ts +9 -0
  436. package/build/picker/icons/consts.d.ts.map +1 -0
  437. package/build/picker/icons/consts.js +264 -0
  438. package/build/picker/icons/consts.js.map +1 -0
  439. package/build/picker/icons/types.d.ts +18 -0
  440. package/build/picker/icons/types.d.ts.map +1 -0
  441. package/build/picker/icons/types.js +2 -0
  442. package/build/picker/icons/types.js.map +1 -0
  443. package/build/picker/icons.d.ts +4 -0
  444. package/build/picker/icons.d.ts.map +1 -0
  445. package/build/picker/icons.js +54 -0
  446. package/build/picker/icons.js.map +1 -0
  447. package/build/picker/index.d.ts +11 -0
  448. package/build/picker/index.d.ts.map +1 -0
  449. package/build/picker/index.js +10 -0
  450. package/build/picker/index.js.map +1 -0
  451. package/build/picker/utils/color/types.d.ts +35 -0
  452. package/build/picker/utils/color/types.d.ts.map +1 -0
  453. package/build/picker/utils/color/types.js +2 -0
  454. package/build/picker/utils/color/types.js.map +1 -0
  455. package/build/picker/utils/color.d.ts +4 -0
  456. package/build/picker/utils/color.d.ts.map +1 -0
  457. package/build/picker/utils/color.js +96 -0
  458. package/build/picker/utils/color.js.map +1 -0
  459. package/build/picker/vocabulary/consts.d.ts +26 -0
  460. package/build/picker/vocabulary/consts.d.ts.map +1 -0
  461. package/build/picker/vocabulary/consts.js +336 -0
  462. package/build/picker/vocabulary/consts.js.map +1 -0
  463. package/build/picker/vocabulary/types.d.ts +67 -0
  464. package/build/picker/vocabulary/types.d.ts.map +1 -0
  465. package/build/picker/vocabulary/types.js +2 -0
  466. package/build/picker/vocabulary/types.js.map +1 -0
  467. package/build/picker/vocabulary.d.ts +11 -0
  468. package/build/picker/vocabulary.d.ts.map +1 -0
  469. package/build/picker/vocabulary.js +83 -0
  470. package/build/picker/vocabulary.js.map +1 -0
  471. package/build/scheme/consts.d.ts +8 -0
  472. package/build/scheme/consts.d.ts.map +1 -0
  473. package/build/scheme/consts.js +8 -0
  474. package/build/scheme/consts.js.map +1 -0
  475. package/build/scheme/index.d.ts +4 -0
  476. package/build/scheme/index.d.ts.map +1 -0
  477. package/build/scheme/index.js +3 -0
  478. package/build/scheme/index.js.map +1 -0
  479. package/build/scheme/scheme.d.ts +16 -0
  480. package/build/scheme/scheme.d.ts.map +1 -0
  481. package/build/scheme/scheme.js +58 -0
  482. package/build/scheme/scheme.js.map +1 -0
  483. package/build/scheme/types.d.ts +30 -0
  484. package/build/scheme/types.d.ts.map +1 -0
  485. package/build/scheme/types.js +2 -0
  486. package/build/scheme/types.js.map +1 -0
  487. package/build/types.d.ts +18 -2
  488. package/build/types.d.ts.map +1 -1
  489. package/package.json +107 -52
  490. package/src/@/components/ui/alert-dialog.tsx +156 -0
  491. package/src/@/components/ui/alert.tsx +1 -1
  492. package/src/@/components/ui/button.tsx +1 -1
  493. package/src/@/components/ui/card.tsx +1 -1
  494. package/src/@/components/ui/input.tsx +1 -1
  495. package/src/@/components/ui/label.tsx +1 -1
  496. package/src/@/components/ui/navigation-menu.tsx +1 -1
  497. package/src/@/components/ui/popover.tsx +47 -0
  498. package/src/@/components/ui/progress.tsx +1 -1
  499. package/src/@/components/ui/separator.tsx +1 -1
  500. package/src/@/components/ui/sheet.tsx +142 -0
  501. package/src/auth/context.ts +0 -2
  502. package/src/auth/entrypoints.ts +5 -0
  503. package/src/auth/exports.ts +4 -4
  504. package/src/auth/plugins/basic-ed25519.tsx +4 -4
  505. package/src/auth/plugins/re-captcha.tsx +3 -3
  506. package/src/auth/plugins/tunnel-consumer.tsx +1 -1
  507. package/src/components/block.tsx +2 -2
  508. package/src/components/consts.local.ts +29 -0
  509. package/src/components/footer/component.tsx +58 -16
  510. package/src/components/footer/credit.tsx +61 -0
  511. package/src/components/footer/index.ts +1 -0
  512. package/src/components/footer/types.ts +22 -1
  513. package/src/components/form/button/component.tsx +7 -4
  514. package/src/components/form/component.tsx +2 -2
  515. package/src/components/form/text/component.tsx +2 -2
  516. package/src/components/helper.ts +1 -13
  517. package/src/components/index.ts +4 -0
  518. package/src/components/link.tsx +3 -3
  519. package/src/components/login/append.ts +34 -0
  520. package/src/components/login/consts.local.ts +10 -0
  521. package/src/components/login/consts.ts +2 -0
  522. package/src/components/login/credit.tsx +33 -0
  523. package/src/components/login/i18n/be.json +10 -0
  524. package/src/components/login/i18n/de.json +10 -0
  525. package/src/components/login/i18n/en.json +10 -0
  526. package/src/components/login/i18n/es.json +10 -0
  527. package/src/components/login/i18n/fr.json +10 -0
  528. package/src/components/login/i18n/pl.json +10 -0
  529. package/src/components/login/i18n/ru.json +10 -0
  530. package/src/components/login/i18n/uk.json +10 -0
  531. package/src/components/login/i18n.ts +29 -0
  532. package/src/components/login/icons.tsx +39 -0
  533. package/src/components/login/index.ts +11 -0
  534. package/src/components/login/notify.ts +48 -0
  535. package/src/components/login/provider.tsx +57 -0
  536. package/src/components/login/screen.tsx +178 -0
  537. package/src/components/login/terms.tsx +75 -0
  538. package/src/components/login/types.ts +30 -0
  539. package/src/components/nav/consts.local.ts +64 -0
  540. package/src/components/nav/index.ts +1 -0
  541. package/src/components/nav/layout.tsx +184 -26
  542. package/src/components/nav/mobile.tsx +165 -0
  543. package/src/components/nav/side.tsx +43 -27
  544. package/src/components/nav/top.tsx +44 -22
  545. package/src/components/nav/types.ts +65 -2
  546. package/src/components/nav/viewport.ts +16 -0
  547. package/src/components/panel-app/component.tsx +9 -1
  548. package/src/components/picker/color-picker.tsx +15 -0
  549. package/src/components/picker/consts.local.ts +267 -0
  550. package/src/components/picker/dot.tsx +21 -0
  551. package/src/components/picker/grid.tsx +138 -0
  552. package/src/components/picker/i18n/be.json +60 -0
  553. package/src/components/picker/i18n/de.json +60 -0
  554. package/src/components/picker/i18n/en.json +60 -0
  555. package/src/components/picker/i18n/es.json +60 -0
  556. package/src/components/picker/i18n/fr.json +60 -0
  557. package/src/components/picker/i18n/pl.json +60 -0
  558. package/src/components/picker/i18n/ru.json +60 -0
  559. package/src/components/picker/i18n/uk.json +60 -0
  560. package/src/components/picker/i18n.ts +19 -0
  561. package/src/components/picker/icon-color-picker.tsx +25 -0
  562. package/src/components/picker/icon-picker.tsx +13 -0
  563. package/src/components/picker/index.ts +11 -0
  564. package/src/components/picker/panel.tsx +61 -0
  565. package/src/components/picker/root.tsx +81 -0
  566. package/src/components/picker/roving.ts +70 -0
  567. package/src/components/picker/surface.tsx +62 -0
  568. package/src/components/picker/swatches/custom.tsx +110 -0
  569. package/src/components/picker/swatches.tsx +67 -0
  570. package/src/components/picker/text.ts +46 -0
  571. package/src/components/picker/tile.tsx +35 -0
  572. package/src/components/picker/translate.ts +15 -0
  573. package/src/components/picker/trigger.tsx +54 -0
  574. package/src/components/picker/types.local.ts +154 -0
  575. package/src/components/picker/types.ts +125 -0
  576. package/src/components/scheme/consts.local.ts +13 -0
  577. package/src/components/scheme/hook.ts +62 -0
  578. package/src/components/scheme/index.ts +4 -0
  579. package/src/components/scheme/toggle.tsx +32 -0
  580. package/src/components/scheme/types.ts +23 -0
  581. package/src/components/socket/i18n/be.json +9 -0
  582. package/src/components/socket/i18n/de.json +9 -0
  583. package/src/components/socket/i18n/en.json +9 -0
  584. package/src/components/socket/i18n/es.json +9 -0
  585. package/src/components/socket/i18n/fr.json +9 -0
  586. package/src/components/socket/i18n/pl.json +9 -0
  587. package/src/components/socket/i18n/ru.json +9 -0
  588. package/src/components/socket/i18n/uk.json +9 -0
  589. package/src/components/socket/i18n.ts +19 -0
  590. package/src/components/socket/index.ts +2 -0
  591. package/src/components/socket/reload-dialog.tsx +61 -0
  592. package/src/components/status.tsx +1 -1
  593. package/src/components/text.tsx +2 -14
  594. package/src/components/toaster.tsx +65 -0
  595. package/src/components/uploader/consts.local.ts +7 -0
  596. package/src/components/uploader/image.tsx +2 -6
  597. package/src/consent/component.tsx +84 -0
  598. package/src/consent/consts.local.ts +10 -0
  599. package/src/consent/consts.ts +1 -0
  600. package/src/consent/geo.ts +26 -0
  601. package/src/consent/index.ts +24 -0
  602. package/src/consent/language.ts +12 -0
  603. package/src/consent/menu-widget.tsx +22 -0
  604. package/src/consent/presence.ts +45 -0
  605. package/src/consent/service.ts +75 -0
  606. package/src/consent/translate.ts +31 -0
  607. package/src/consent/types.local.ts +6 -0
  608. package/src/consent/types.ts +43 -0
  609. package/src/context.ts +12 -0
  610. package/src/editor/component.tsx +172 -0
  611. package/src/editor/consts.local.ts +62 -0
  612. package/src/editor/index.ts +2 -0
  613. package/src/editor/types.ts +24 -0
  614. package/src/entrypoints.ts +5 -0
  615. package/src/exports.ts +6 -5
  616. package/src/index.ts +1 -1
  617. package/src/main.tsx +2 -6
  618. package/src/permission/i18n/be.json +13 -0
  619. package/src/permission/i18n/de.json +13 -0
  620. package/src/permission/i18n/en.json +13 -0
  621. package/src/permission/i18n/es.json +13 -0
  622. package/src/permission/i18n/fr.json +13 -0
  623. package/src/permission/i18n/pl.json +13 -0
  624. package/src/permission/i18n/ru.json +13 -0
  625. package/src/permission/i18n/uk.json +13 -0
  626. package/src/permission/i18n.ts +13 -0
  627. package/src/permission/index.ts +27 -0
  628. package/src/permission/toast.tsx +20 -0
  629. package/src/permission/types.ts +5 -0
  630. package/src/picker/icons/consts.ts +309 -0
  631. package/src/picker/icons/types.ts +18 -0
  632. package/src/picker/icons.ts +70 -0
  633. package/src/picker/index.ts +11 -0
  634. package/src/picker/utils/color/types.ts +37 -0
  635. package/src/picker/utils/color.ts +119 -0
  636. package/src/picker/vocabulary/consts.ts +347 -0
  637. package/src/picker/vocabulary/types.ts +72 -0
  638. package/src/picker/vocabulary.ts +111 -0
  639. package/src/scheme/consts.ts +8 -0
  640. package/src/scheme/index.ts +3 -0
  641. package/src/scheme/scheme.ts +66 -0
  642. package/src/scheme/types.ts +30 -0
  643. package/src/types.ts +19 -2
  644. package/tests/consent-language.spec.ts +31 -0
  645. package/tests/consent.spec.ts +112 -0
  646. package/tests/context.ts +4 -0
  647. package/tests/editor.spec.ts +58 -0
  648. package/tests/harness/editor.tsx +32 -0
  649. package/tests/harness/index.html +1 -0
  650. package/tests/harness/lazy-screen.tsx +9 -0
  651. package/tests/harness/mount.tsx +370 -29
  652. package/tests/harness/picker.tsx +110 -0
  653. package/tests/lazy.spec.ts +104 -0
  654. package/tests/login-provider.spec.ts +114 -0
  655. package/tests/login.spec.ts +447 -0
  656. package/tests/nav.spec.ts +449 -3
  657. package/tests/package-boundary.spec.ts +19 -0
  658. package/tests/picker-shots.spec.ts +174 -0
  659. package/tests/picker-vocabulary.spec.ts +217 -0
  660. package/tests/picker.spec.ts +333 -0
  661. package/tests/scheme.spec.ts +149 -0
  662. package/tests/smoke.spec.ts +2 -2
  663. package/tests/socket-dialog.spec.ts +154 -0
  664. package/tests/toaster.spec.ts +59 -0
  665. package/zz-tw.d.mts +2 -0
  666. package/zz-tw.d.mts.map +1 -0
  667. package/build/.gitkeep +0 -0
  668. package/src/auth/modules.ts +0 -5
  669. package/src/modules.ts +0 -5
@@ -0,0 +1,104 @@
1
+ import { afterAll, describe, expect, test } from 'bun:test'
2
+ import { mountComponent, browserHelper } from '@owlmeans/test-ui'
3
+ import { HARNESS_URL } from './context.js'
4
+
5
+ // Browser work does not fit the 5s default: a cold harness compiles the app on first request.
6
+ const TIMEOUT = 30_000
7
+
8
+ // The lazy screen's alias as the harness declares it (`${SERVICE}:web:lazy`).
9
+ const LAZY_ALIAS = 'web-panel-test:web:lazy'
10
+ // The request the lazy screen's chunk arrives by — the harness's own module, served by Vite.
11
+ const LAZY_MODULE = '**/lazy-screen.tsx*'
12
+
13
+ afterAll(async () => {
14
+ await browserHelper.closeBrowser()
15
+ })
16
+
17
+ const open = async (path: string) => mountComponent({ url: `${HARNESS_URL.replace(/\/$/, '')}${path}` })
18
+
19
+ describe('@owlmeans/web-panel — lazily-loaded screens', () => {
20
+ test('the fallback renders inside the layout, and the layout survives the load', async () => {
21
+ const { page, close } = await open('/lazy-entry')
22
+ try {
23
+ await page.waitForSelector('#lazy-entry')
24
+
25
+ // Hold the chunk's request open, so the fallback is observable for as long as the test needs.
26
+ let release!: () => void
27
+ const held = new Promise<void>(resolve => { release = resolve })
28
+ await page.route(LAZY_MODULE, async route => {
29
+ await held
30
+ await route.continue()
31
+ })
32
+
33
+ await page.locator('#to-lazy').click()
34
+ await page.waitForSelector('#lazy-fallback')
35
+ // The boundary is the lazy screen's own: the shell stays up and the fallback sits in it.
36
+ expect(await page.locator('header nav').first().isVisible()).toBe(true)
37
+ expect(await page.locator('main #lazy-fallback').count()).toBe(1)
38
+
39
+ // Keep the layout's header node itself — a remount would replace it with a fresh one.
40
+ await page.evaluate(() => {
41
+ const header = document.querySelector('header')!
42
+ header.setAttribute('data-lazy-mark', '1')
43
+ ;(window as unknown as { __header: Element }).__header = header
44
+ })
45
+
46
+ release()
47
+ await page.waitForSelector('#lazy-screen')
48
+
49
+ expect(await page.locator('#lazy-fallback').count()).toBe(0)
50
+ expect(await page.locator('header[data-lazy-mark]').count()).toBe(1)
51
+ expect(await page.evaluate(() => {
52
+ const kept = (window as unknown as { __header: Element }).__header
53
+ return kept.isConnected && document.querySelector('header') === kept
54
+ })).toBe(true)
55
+ expect(await page.locator('#lazy-screen').getAttribute('data-alias')).toBe(LAZY_ALIAS)
56
+ } finally {
57
+ await close()
58
+ }
59
+ }, TIMEOUT)
60
+
61
+ test('a preloaded screen renders without its fallback', async () => {
62
+ const { page, close } = await open('/lazy-entry')
63
+ try {
64
+ await page.waitForSelector('#lazy-entry')
65
+ expect(await page.evaluate(() => (window as unknown as { __lazyPreload: () => Promise<boolean> }).__lazyPreload())).toBe(true)
66
+
67
+ // Records every node ever added, so a fallback that is mounted and replaced in the same
68
+ // task still counts — reading the DOM afterwards would miss it.
69
+ await page.evaluate(() => {
70
+ const flags = window as unknown as { __fallbackSeen: boolean }
71
+ flags.__fallbackSeen = false
72
+ new MutationObserver(records => {
73
+ for (const record of records) {
74
+ for (const node of Array.from(record.addedNodes)) {
75
+ if (node instanceof Element && (node.id === 'lazy-fallback' || node.querySelector('#lazy-fallback') != null)) {
76
+ flags.__fallbackSeen = true
77
+ }
78
+ }
79
+ }
80
+ }).observe(document.body, { childList: true, subtree: true })
81
+ })
82
+
83
+ await page.locator('#to-lazy').click()
84
+ await page.waitForSelector('#lazy-screen')
85
+ expect(await page.evaluate(() => (window as unknown as { __fallbackSeen: boolean }).__fallbackSeen)).toBe(false)
86
+ expect(await page.locator('#lazy-screen').getAttribute('data-alias')).toBe(LAZY_ALIAS)
87
+ } finally {
88
+ await close()
89
+ }
90
+ }, TIMEOUT)
91
+
92
+ test('a failed load is retried, not cached', async () => {
93
+ const { page, close } = await open('/lazy-entry')
94
+ try {
95
+ await page.waitForSelector('#lazy-entry')
96
+ const preload = () => page.evaluate(() => (window as unknown as { __flakyPreload: () => Promise<string> }).__flakyPreload())
97
+
98
+ expect(await preload()).toBe('first-load-fails')
99
+ expect(await preload()).toBe('loaded')
100
+ } finally {
101
+ await close()
102
+ }
103
+ }, TIMEOUT)
104
+ })
@@ -0,0 +1,114 @@
1
+ import { describe, expect, test } from 'bun:test'
2
+ import { readdirSync, readFileSync } from 'node:fs'
3
+ import { join } from 'node:path'
4
+ import { loginProviderHelper } from '@owlmeans/client-panel/auth'
5
+ import type { LoginProviderModel } from '@owlmeans/client-panel/auth'
6
+
7
+ const brand = { name: 'Ovenlore' }
8
+
9
+ describe('loginProviderHelper.resolve — who signs the person in', () => {
10
+ test('resolves a full disclosure, keeping every configured value', () => {
11
+ expect(loginProviderHelper.resolve(
12
+ { name: 'OwlMeans IAM', operator: 'OwlMeans', info: 'https://owlmeans.com/iam/about', placement: 'top' },
13
+ brand
14
+ )).toEqual({
15
+ name: 'OwlMeans IAM', operator: 'OwlMeans', product: 'Ovenlore',
16
+ info: 'https://owlmeans.com/iam/about', placement: 'top',
17
+ })
18
+ })
19
+
20
+ test('the operator defaults to the provider and the placement to inline', () => {
21
+ expect(loginProviderHelper.resolve({ name: 'OwlMeans IAM' }, brand)).toEqual({
22
+ name: 'OwlMeans IAM', operator: 'OwlMeans IAM', product: 'Ovenlore', info: null, placement: 'inline',
23
+ })
24
+ })
25
+
26
+ test('null without a provider name, a product, or any configuration at all', () => {
27
+ expect(loginProviderHelper.resolve(undefined, brand)).toBeNull()
28
+ expect(loginProviderHelper.resolve({ name: 'OwlMeans IAM' }, undefined)).toBeNull()
29
+ expect(loginProviderHelper.resolve({ name: 'OwlMeans IAM' }, {})).toBeNull()
30
+ })
31
+
32
+ test('blank strings are unset — the shape an undelivered build-time variable arrives in', () => {
33
+ expect(loginProviderHelper.resolve({ name: ' ' }, brand)).toBeNull()
34
+ expect(loginProviderHelper.resolve({ name: 'OwlMeans IAM' }, { name: '' })).toBeNull()
35
+ expect(loginProviderHelper.resolve({ name: 'OwlMeans IAM', operator: '', info: ' ' }, brand))
36
+ .toMatchObject({ operator: 'OwlMeans IAM', info: null })
37
+ })
38
+
39
+ test('an unknown placement falls back to inline', () => {
40
+ expect(loginProviderHelper.resolve({ name: 'IAM', placement: 'side' as never }, brand)?.placement)
41
+ .toBe('inline')
42
+ })
43
+
44
+ test('the info link is an absolute http(s) URL or a root-relative path, and nothing else', () => {
45
+ const info = (value: string) => loginProviderHelper.resolve({ name: 'IAM', info: value }, brand)?.info
46
+
47
+ expect(info('https://owlmeans.com')).toBe('https://owlmeans.com')
48
+ expect(info('http://localhost:3000/iam/about')).toBe('http://localhost:3000/iam/about')
49
+ expect(info('/iam/about')).toBe('/iam/about')
50
+
51
+ expect(info('javascript:alert(1)')).toBeNull()
52
+ expect(info('data:text/html,hi')).toBeNull()
53
+ expect(info('//evil.test/about')).toBeNull()
54
+ expect(info('/\\evil.test')).toBeNull()
55
+ expect(info('iam/about')).toBeNull()
56
+ expect(info('mailto:someone@example.test')).toBeNull()
57
+ })
58
+ })
59
+
60
+ describe('loginProviderHelper.fill — names put in after translation', () => {
61
+ const model: LoginProviderModel = {
62
+ name: 'OwlMeans IAM', operator: 'OwlMeans', product: 'Ovenlore', info: null, placement: 'top',
63
+ }
64
+
65
+ test('replaces every placeholder it knows', () => {
66
+ expect(loginProviderHelper.fill(
67
+ 'This app is hosted by {{operator}} on behalf of {{product}}. You sign in with {{provider}}.', model
68
+ )).toBe('This app is hosted by OwlMeans on behalf of Ovenlore. You sign in with OwlMeans IAM.')
69
+ })
70
+
71
+ test('one pass: a name carrying a placeholder is never expanded again', () => {
72
+ expect(loginProviderHelper.fill('{{product}} / {{provider}}', { ...model, product: '{{provider}}' }))
73
+ .toBe('{{provider}} / OwlMeans IAM')
74
+ })
75
+
76
+ test('leaves an unknown placeholder alone', () => {
77
+ expect(loginProviderHelper.fill('{{product}} {{other}}', model)).toBe('Ovenlore {{other}}')
78
+ })
79
+ })
80
+
81
+ describe('login.provider.* — eight languages, one shape', () => {
82
+ const dir = join(import.meta.dir, '../src/components/login/i18n')
83
+ const LANGUAGES = ['be', 'de', 'en', 'es', 'fr', 'pl', 'ru', 'uk']
84
+ const read = (lng: string) =>
85
+ JSON.parse(readFileSync(join(dir, `${lng}.json`), 'utf8')) as { login: { provider: Record<string, string> } }
86
+ const placeholders = (text: string) => [...text.matchAll(/\{\{(\w+)\}\}/g)].map(match => match[1]).sort()
87
+
88
+ test('ships exactly the eight languages', () => {
89
+ expect(readdirSync(dir).map(file => file.replace(/\.json$/, '')).sort()).toEqual(LANGUAGES)
90
+ })
91
+
92
+ test('every language carries the same keys, each translated, with the same placeholders', () => {
93
+ const en = read('en').login.provider
94
+ expect(Object.keys(en).sort()).toEqual(['inline', 'more', 'title', 'top'])
95
+
96
+ for (const lng of LANGUAGES) {
97
+ const bundle = read(lng)
98
+ // Nothing but `login.provider` — the rest of `login` belongs to `@owlmeans/client-auth`.
99
+ expect({ lng, roots: Object.keys(bundle), login: Object.keys(bundle.login) })
100
+ .toEqual({ lng, roots: ['login'], login: ['provider'] })
101
+ expect({ lng, keys: Object.keys(bundle.login.provider).sort() })
102
+ .toEqual({ lng, keys: Object.keys(en).sort() })
103
+
104
+ for (const [key, text] of Object.entries(bundle.login.provider)) {
105
+ expect({ lng, key, blank: text.trim() === '' }).toEqual({ lng, key, blank: false })
106
+ expect({ lng, key, placeholders: placeholders(text) })
107
+ .toEqual({ lng, key, placeholders: placeholders(en[key]) })
108
+ if (lng !== 'en' && key !== 'more') {
109
+ expect({ lng, key, untranslated: text === en[key] }).toEqual({ lng, key, untranslated: false })
110
+ }
111
+ }
112
+ }
113
+ })
114
+ })
@@ -0,0 +1,447 @@
1
+ import { afterAll, beforeEach, describe, expect, test } from 'bun:test'
2
+ import { mountComponent, browserHelper } from '@owlmeans/test-ui'
3
+ import { HARNESS_URL } from './context.js'
4
+
5
+ const TIMEOUT = 30_000
6
+
7
+ afterAll(async () => {
8
+ await browserHelper.closeBrowser()
9
+ })
10
+
11
+ const open = async (query?: string) => {
12
+ const mounted = await mountComponent({ url: `${HARNESS_URL}login${query ?? ''}` })
13
+ await mounted.page.waitForSelector('[data-login-method]')
14
+ // A fresh acceptance for every test: the confirmation is remembered per browser, and a test
15
+ // that inherited the previous one would assert the unblocked path while claiming the other.
16
+ await mounted.page.evaluate(() => { window.localStorage.clear() })
17
+ await mounted.page.reload()
18
+ await mounted.page.waitForSelector('[data-login-method]')
19
+
20
+ return mounted
21
+ }
22
+
23
+ describe('@owlmeans/web-panel — the sign-in screen', () => {
24
+ test('offers one control per method, and starts NOTHING on its own', async () => {
25
+ const { page, close } = await open()
26
+ try {
27
+ // `operator` is restricted and the configuration never named it, so it must not appear.
28
+ expect(await page.locator('[data-login-method]').count()).toBe(2)
29
+ expect(await page.locator('[data-login-method="primary"]').count()).toBe(1)
30
+ expect(await page.locator('[data-login-method="operator"]').count()).toBe(0)
31
+
32
+ // Nothing left the document. This is requirement one of the whole feature.
33
+ expect(await page.evaluate(() => (window as never as { __loginStarted: string[] }).__loginStarted))
34
+ .toEqual([])
35
+ } finally {
36
+ await close()
37
+ }
38
+ }, TIMEOUT)
39
+
40
+ test('renders the logo the app supplied and the credit line', async () => {
41
+ const { page, close } = await open()
42
+ try {
43
+ expect(await page.locator('#login-logo').count()).toBe(1)
44
+ const credit = await page.locator('[data-login-credit]').textContent()
45
+ expect(credit).toContain('Powered by OwlMeans')
46
+ expect(await page.locator('[data-login-credit] a[data-login-powered]').getAttribute('href'))
47
+ .toBe('https://owlmeans.com')
48
+ expect(credit).toContain('Harness')
49
+ // A copyright NOTICE, not a name: the mark and the year are what make it one.
50
+ expect(credit).toContain(`© ${new Date().getFullYear()} Acme`)
51
+ } finally {
52
+ await close()
53
+ }
54
+ }, TIMEOUT)
55
+
56
+ test('centres the card in the viewport, rather than stacking it at the top', async () => {
57
+ const { page, close } = await open()
58
+ try {
59
+ const screen = await page.locator('[data-login-screen]').boundingBox()
60
+ const viewport = page.viewportSize()
61
+
62
+ // A percentage minimum height resolves against a parent that HAS a height, and the
63
+ // dispatcher renders this screen into a chain with none — so `min-h-full` collapsed the
64
+ // box to its content and left the card at the top of an otherwise empty page.
65
+ expect(screen?.height).toBeGreaterThanOrEqual((viewport?.height ?? 0) - 1)
66
+
67
+ // The height on its own is not the requirement — where the CARD ends up is. Asserted from
68
+ // the card's own centre so the test fails on a lost `items-center` too, not only on a lost
69
+ // height, and against the SCREEN box rather than the window because this harness mounts the
70
+ // screen inside the app shell, one header down. A dispatcher renders it as the whole page,
71
+ // where the two coincide.
72
+ const card = await page.locator('[data-login-screen] > *').first().boundingBox()
73
+ const cardCentre = (card?.y ?? 0) + (card?.height ?? 0) / 2
74
+ const screenCentre = (screen?.y ?? 0) + (screen?.height ?? 0) / 2
75
+
76
+ expect(Math.abs(cardCentre - screenCentre)).toBeLessThanOrEqual(2)
77
+ } finally {
78
+ await close()
79
+ }
80
+ }, TIMEOUT)
81
+
82
+ test('an attempt that goes nowhere SAYS so, instead of reading as a dead button', async () => {
83
+ const { page, close } = await open()
84
+ try {
85
+ await page.locator('[data-login-terms]').check()
86
+ expect(await page.locator('[data-login-error]').count()).toBe(0)
87
+
88
+ // `secondary` finishes as `Passed` — it ran, and the document did not move. That is a valid
89
+ // answer to a dispatcher, which has a continuation, and a dead end on a screen.
90
+ await page.locator('[data-login-method="secondary"]').click()
91
+ await page.waitForSelector('[data-login-error]', { timeout: 10_000 })
92
+
93
+ expect(await page.evaluate(() => (window as never as { __loginStarted: string[] }).__loginStarted))
94
+ .toContain('secondary')
95
+ expect(await page.locator('[data-login-error]').textContent()).toBeTruthy()
96
+ } finally {
97
+ await close()
98
+ }
99
+ }, TIMEOUT)
100
+
101
+ test('offers the methods with a pointer cursor', async () => {
102
+ const { page, close } = await open()
103
+ try {
104
+ // The screen renders through the CONSUMER's vendored button, and an older shadcn copy has
105
+ // no cursor rule — so the one control on the page showed an arrow.
106
+ const cursor = await page.locator('[data-login-method]').first()
107
+ .evaluate(node => window.getComputedStyle(node).cursor)
108
+
109
+ expect(cursor).toBe('pointer')
110
+ } finally {
111
+ await close()
112
+ }
113
+ }, TIMEOUT)
114
+
115
+ test('centres the terms confirmation, like every other row in the card', async () => {
116
+ const { page, close } = await open()
117
+ try {
118
+ const align = await page.locator('[data-login-terms]').evaluate(
119
+ node => window.getComputedStyle(node.closest('label') as Element).textAlign
120
+ )
121
+
122
+ expect(align).toBe('center')
123
+ } finally {
124
+ await close()
125
+ }
126
+ }, TIMEOUT)
127
+
128
+ test('a click while the terms are unconfirmed starts nothing AND says why', async () => {
129
+ const { page, close } = await open()
130
+ try {
131
+ // `force`, because the control reports itself `aria-disabled` and Playwright's actionability
132
+ // check honours that — which is the point: the button IS clickable (it keeps its pointer
133
+ // events precisely so the refusal can explain itself), it merely announces that acting on it
134
+ // will not proceed. A real user's click lands here too.
135
+ await page.locator('[data-login-method="primary"]').click({ force: true })
136
+
137
+ // Nothing started…
138
+ expect(await page.evaluate(() => (window as never as { __loginStarted: string[] }).__loginStarted))
139
+ .toEqual([])
140
+ // …and the reason is announced, rather than the button silently doing nothing. This is why
141
+ // the control carries `aria-disabled` and not `disabled`: a disabled button eats the click.
142
+ expect(await page.locator('[role="alert"]').count()).toBe(1)
143
+ expect(await page.locator('[data-login-method="primary"]').getAttribute('aria-disabled'))
144
+ .toBe('true')
145
+ } finally {
146
+ await close()
147
+ }
148
+ }, TIMEOUT)
149
+
150
+ test('confirming the terms unblocks the methods', async () => {
151
+ const { page, close } = await open()
152
+ try {
153
+ await page.locator('[data-login-terms]').check()
154
+ await page.locator('[data-login-method="primary"]').click()
155
+
156
+ expect(await page.evaluate(() => (window as never as { __loginStarted: string[] }).__loginStarted))
157
+ .toEqual(['primary'])
158
+ } finally {
159
+ await close()
160
+ }
161
+ }, TIMEOUT)
162
+
163
+ test('the confirmation is remembered across a reload', async () => {
164
+ const { page, close } = await open()
165
+ try {
166
+ await page.locator('[data-login-terms]').check()
167
+ await page.reload()
168
+ await page.waitForSelector('[data-login-method]')
169
+
170
+ expect(await page.locator('[data-login-terms]').isChecked()).toBe(true)
171
+ // Omitted rather than `"false"`: `aria-disabled={blocked || undefined}` drops the attribute
172
+ // entirely once unblocked, which is what lets a deferred screen (no `blocked` state at all)
173
+ // render the same way with no special case.
174
+ expect(await page.locator('[data-login-method="primary"]').getAttribute('aria-disabled'))
175
+ .toBeNull()
176
+ } finally {
177
+ await close()
178
+ }
179
+ }, TIMEOUT)
180
+
181
+ test('the terms links point where the configuration says', async () => {
182
+ const { page, close } = await open()
183
+ try {
184
+ const hrefs = await page.locator('a[target="_blank"]').evaluateAll(
185
+ nodes => nodes.map(node => (node as HTMLAnchorElement).href)
186
+ )
187
+ expect(hrefs).toContain('https://example.test/terms')
188
+ expect(hrefs).toContain('https://example.test/privacy')
189
+ } finally {
190
+ await close()
191
+ }
192
+ }, TIMEOUT)
193
+
194
+ test('login documents and the separate privacy notice follow an explicit live PL/FR locale map', async () => {
195
+ const { page, close } = await open('?terms=localized')
196
+ try {
197
+ for (const key of ['terms', 'billing', 'privacy', 'cookies']) {
198
+ expect(await page.locator(`[data-login-document="${key}"]`).getAttribute('href'))
199
+ .toBe(`https://example.test/pl/${key}`)
200
+ }
201
+ expect(await page.locator('[data-login-privacy] a').count()).toBe(2)
202
+ expect(await page.locator('label:has([data-login-terms]) [data-login-privacy]').count()).toBe(0)
203
+ const revision = await page.locator('[data-login-revised]').textContent()
204
+ await page.locator('#login-language-fr').click()
205
+ for (const key of ['terms', 'billing', 'privacy', 'cookies']) {
206
+ expect(await page.locator(`[data-login-document="${key}"]`).getAttribute('href'))
207
+ .toBe(`https://example.test/fr/${key}`)
208
+ }
209
+ expect(await page.locator('[data-login-revised]').textContent()).toBe(revision)
210
+ expect(await page.locator('[data-login-terms]').count()).toBe(1)
211
+ } finally {
212
+ await close()
213
+ }
214
+ }, TIMEOUT)
215
+
216
+ test('[data-login-revised] is absent when the configuration never asked to show it', async () => {
217
+ const { page, close } = await open()
218
+ try {
219
+ expect(await page.locator('[data-login-revised]').count()).toBe(0)
220
+ } finally {
221
+ await close()
222
+ }
223
+ }, TIMEOUT)
224
+
225
+ describe('with billing, product and custom documents configured', () => {
226
+ test('there is still exactly one [data-login-terms] checkbox', async () => {
227
+ const { page, close } = await open('?terms=extended')
228
+ try {
229
+ expect(await page.locator('[data-login-terms]').count()).toBe(1)
230
+ } finally {
231
+ await close()
232
+ }
233
+ }, TIMEOUT)
234
+
235
+ test('[data-login-privacy] renders OUTSIDE the checkbox\'s label', async () => {
236
+ const { page, close } = await open('?terms=extended')
237
+ try {
238
+ expect(await page.locator('label:has([data-login-terms]) [data-login-privacy]').count())
239
+ .toBe(0)
240
+ expect(await page.locator('[data-login-privacy]').count()).toBe(1)
241
+ } finally {
242
+ await close()
243
+ }
244
+ }, TIMEOUT)
245
+
246
+ test('[data-login-revised] shows the latest revision date', async () => {
247
+ const { page, close } = await open('?terms=extended')
248
+ try {
249
+ expect(await page.locator('[data-login-revised]').count()).toBe(1)
250
+ expect(await page.locator('[data-login-revised]').textContent()).toContain('2026-01-01')
251
+ } finally {
252
+ await close()
253
+ }
254
+ }, TIMEOUT)
255
+
256
+ test('every configured document renders its own [data-login-document] link', async () => {
257
+ const { page, close } = await open('?terms=extended')
258
+ try {
259
+ const hrefByKey = async (key: string) =>
260
+ await page.locator(`[data-login-document="${key}"]`).getAttribute('href')
261
+
262
+ expect(await hrefByKey('terms')).toBe('https://example.test/terms')
263
+ expect(await hrefByKey('billing')).toBe('https://example.test/billing')
264
+ expect(await hrefByKey('product')).toBe('https://example.test/product')
265
+ expect(await hrefByKey('custom-a')).toBe('https://example.test/custom-a')
266
+ expect(await hrefByKey('custom-b')).toBe('https://example.test/custom-b')
267
+ // The notice line, not the consented one.
268
+ expect(await hrefByKey('privacy')).toBe('https://example.test/privacy')
269
+ } finally {
270
+ await close()
271
+ }
272
+ }, TIMEOUT)
273
+
274
+ test('confirming the single checkbox still unblocks the methods', async () => {
275
+ const { page, close } = await open('?terms=extended')
276
+ try {
277
+ await page.locator('[data-login-terms]').check()
278
+ await page.locator('[data-login-method="primary"]').click()
279
+
280
+ expect(await page.evaluate(() => (window as never as { __loginStarted: string[] }).__loginStarted))
281
+ .toEqual(['primary'])
282
+ } finally {
283
+ await close()
284
+ }
285
+ }, TIMEOUT)
286
+ })
287
+
288
+ describe('the Terms confirmation deferred to a step', () => {
289
+ test('bound: no checkbox, the privacy notice still renders, and methods are never blocked', async () => {
290
+ const { page, close } = await open('?defer=bound')
291
+ try {
292
+ expect(await page.locator('[data-login-terms]').count()).toBe(0)
293
+ expect(await page.locator('[data-login-privacy]').count()).toBe(1)
294
+ expect(await page.locator('[role="alert"]').count()).toBe(0)
295
+
296
+ const disabled = await page.locator('[data-login-method="primary"]').getAttribute('aria-disabled')
297
+ expect(disabled).not.toBe('true')
298
+
299
+ await page.locator('[data-login-method="primary"]').click()
300
+ expect(await page.evaluate(() => (window as never as { __loginStarted: string[] }).__loginStarted))
301
+ .toEqual(['primary'])
302
+ } finally {
303
+ await close()
304
+ }
305
+ }, TIMEOUT)
306
+
307
+ test('bound + extended documents: still no checkbox, still no blocking', async () => {
308
+ const { page, close } = await open('?defer=bound&terms=extended')
309
+ try {
310
+ expect(await page.locator('[data-login-terms]').count()).toBe(0)
311
+ expect(await page.locator('[data-login-privacy]').count()).toBe(1)
312
+
313
+ await page.locator('[data-login-method="primary"]').click()
314
+ expect(await page.evaluate(() => (window as never as { __loginStarted: string[] }).__loginStarted))
315
+ .toEqual(['primary'])
316
+ } finally {
317
+ await close()
318
+ }
319
+ }, TIMEOUT)
320
+
321
+ test('unbound: the checkbox stays — a step nobody can reach never removes the confirmation', async () => {
322
+ const { page, close } = await open('?defer=unbound')
323
+ try {
324
+ expect(await page.locator('[data-login-terms]').count()).toBe(1)
325
+ expect(await page.locator('[data-login-method="primary"]').getAttribute('aria-disabled'))
326
+ .toBe('true')
327
+ } finally {
328
+ await close()
329
+ }
330
+ }, TIMEOUT)
331
+ })
332
+
333
+ describe('the provider disclosure', () => {
334
+ test('no provider configured: no note, no stage, the ordinary title', async () => {
335
+ const { page, close } = await open()
336
+ try {
337
+ expect(await page.locator('[data-login-provider]').count()).toBe(0)
338
+ expect(await page.locator('[data-login-stage]').count()).toBe(0)
339
+ expect(await page.locator('[data-login-screen] [data-slot="card-title"]').textContent())
340
+ .toBe('Sign in')
341
+ } finally {
342
+ await close()
343
+ }
344
+ }, TIMEOUT)
345
+
346
+ test('top: the strip is the FIRST child of the screen and sits at its top edge, full width', async () => {
347
+ const { page, close } = await open('?provider=top')
348
+ try {
349
+ expect(await page.locator('[data-login-provider]').count()).toBe(1)
350
+ expect(await page.locator('[data-login-screen] > :first-child').getAttribute('data-login-provider'))
351
+ .not.toBeNull()
352
+ expect(await page.locator('[data-login-provider]').getAttribute('data-placement')).toBe('top')
353
+ expect(await page.locator('[data-login-provider]').getAttribute('role')).toBe('note')
354
+
355
+ const screen = await page.locator('[data-login-screen]').boundingBox()
356
+ const strip = await page.locator('[data-login-provider]').boundingBox()
357
+ expect(Math.abs((strip?.y ?? -1) - (screen?.y ?? 0))).toBeLessThanOrEqual(1)
358
+ expect(Math.abs((strip?.width ?? 0) - (screen?.width ?? 0))).toBeLessThanOrEqual(1)
359
+
360
+ // A disclosure, not page chrome: no landmark inside the screen.
361
+ expect(await page.locator('[data-login-screen] header, [data-login-screen] nav').count()).toBe(0)
362
+ } finally {
363
+ await close()
364
+ }
365
+ }, TIMEOUT)
366
+
367
+ test('top: names both parties, links to more information and titles the card with the product', async () => {
368
+ const { page, close } = await open('?provider=top')
369
+ try {
370
+ const note = await page.locator('[data-login-provider]').textContent()
371
+ expect(note).toContain('This app is hosted by Harness Hosting on behalf of Harness App.')
372
+ expect(note).toContain('You sign in with Harness IAM.')
373
+
374
+ const info = page.locator('[data-login-provider] a[data-login-provider-info]')
375
+ expect(await info.count()).toBe(1)
376
+ expect(await info.getAttribute('href')).toBe('https://example.test/about')
377
+ expect(await info.getAttribute('target')).toBe('_blank')
378
+ expect(await info.getAttribute('rel')).toBe('noopener noreferrer')
379
+ expect(await info.textContent()).toBe('More information')
380
+
381
+ expect(await page.locator('[data-login-screen] [data-slot="card-title"]').textContent())
382
+ .toBe('Sign in to Harness App')
383
+ } finally {
384
+ await close()
385
+ }
386
+ }, TIMEOUT)
387
+
388
+ test('top: the card is centred within [data-login-stage], with exactly one terms checkbox', async () => {
389
+ const { page, close } = await open('?provider=top')
390
+ try {
391
+ const screen = await page.locator('[data-login-screen]').boundingBox()
392
+ const strip = await page.locator('[data-login-provider]').boundingBox()
393
+ const stage = await page.locator('[data-login-screen] > [data-login-stage]').boundingBox()
394
+ const card = await page.locator('[data-login-stage] > *').first().boundingBox()
395
+
396
+ // The stage takes the rest of the screen, below the strip.
397
+ expect(Math.abs((stage?.y ?? 0) - ((strip?.y ?? 0) + (strip?.height ?? 0)))).toBeLessThanOrEqual(1)
398
+ expect(Math.abs(((stage?.y ?? 0) + (stage?.height ?? 0)) - ((screen?.y ?? 0) + (screen?.height ?? 0))))
399
+ .toBeLessThanOrEqual(1)
400
+
401
+ const cardCentreY = (card?.y ?? 0) + (card?.height ?? 0) / 2
402
+ const stageCentreY = (stage?.y ?? 0) + (stage?.height ?? 0) / 2
403
+ const cardCentreX = (card?.x ?? 0) + (card?.width ?? 0) / 2
404
+ const stageCentreX = (stage?.x ?? 0) + (stage?.width ?? 0) / 2
405
+ expect(Math.abs(cardCentreY - stageCentreY)).toBeLessThanOrEqual(2)
406
+ expect(Math.abs(cardCentreX - stageCentreX)).toBeLessThanOrEqual(2)
407
+
408
+ expect(await page.locator('[data-login-terms]').count()).toBe(1)
409
+ // Inside the card there is no second note.
410
+ expect(await page.locator('[data-login-stage] [data-login-provider]').count()).toBe(0)
411
+ } finally {
412
+ await close()
413
+ }
414
+ }, TIMEOUT)
415
+
416
+ test('inline: one note inside the card, under the methods; the screen keeps its ordinary shape', async () => {
417
+ const { page, close } = await open('?provider=inline')
418
+ try {
419
+ expect(await page.locator('[data-login-provider]').count()).toBe(1)
420
+ expect(await page.locator('[data-login-provider]').getAttribute('data-placement')).toBe('inline')
421
+ expect(await page.locator('[data-login-stage]').count()).toBe(0)
422
+ // Inside the card, not a child of the screen.
423
+ expect(await page.locator('[data-login-screen] > [data-login-provider]').count()).toBe(0)
424
+ expect(await page.locator('[data-login-screen] > * [data-login-provider]').count()).toBe(1)
425
+
426
+ const note = await page.locator('[data-login-provider]').boundingBox()
427
+ const lastMethod = await page.locator('[data-login-method]').last().boundingBox()
428
+ expect(note?.y ?? 0).toBeGreaterThanOrEqual((lastMethod?.y ?? 0) + (lastMethod?.height ?? 0))
429
+
430
+ expect(await page.locator('[data-login-provider]').textContent())
431
+ .toContain('Sign-in is provided by Harness IAM on behalf of Harness App.')
432
+ expect(await page.locator('[data-login-provider] a[data-login-provider-info]').getAttribute('href'))
433
+ .toBe('https://example.test/about')
434
+ expect(await page.locator('[data-login-terms]').count()).toBe(1)
435
+
436
+ // Still centred exactly as without a provider.
437
+ const screen = await page.locator('[data-login-screen]').boundingBox()
438
+ const card = await page.locator('[data-login-screen] > *').first().boundingBox()
439
+ const cardCentre = (card?.y ?? 0) + (card?.height ?? 0) / 2
440
+ const screenCentre = (screen?.y ?? 0) + (screen?.height ?? 0) / 2
441
+ expect(Math.abs(cardCentre - screenCentre)).toBeLessThanOrEqual(2)
442
+ } finally {
443
+ await close()
444
+ }
445
+ }, TIMEOUT)
446
+ })
447
+ })