@multiplatform.one/backoffice 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (414) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +310 -0
  3. package/dist/cjs/chrome/BackofficeAwesomebar.cjs +95 -0
  4. package/dist/cjs/chrome/BackofficeAwesomebar.native.js +106 -0
  5. package/dist/cjs/chrome/BackofficeAwesomebar.native.js.map +1 -0
  6. package/dist/cjs/chrome/CommandPalette.cjs +453 -0
  7. package/dist/cjs/chrome/CommandPalette.native.js +505 -0
  8. package/dist/cjs/chrome/CommandPalette.native.js.map +1 -0
  9. package/dist/cjs/chrome/HelpMenu.cjs +131 -0
  10. package/dist/cjs/chrome/HelpMenu.native.js +145 -0
  11. package/dist/cjs/chrome/HelpMenu.native.js.map +1 -0
  12. package/dist/cjs/chrome/NotificationsButton.cjs +112 -0
  13. package/dist/cjs/chrome/NotificationsButton.native.js +115 -0
  14. package/dist/cjs/chrome/NotificationsButton.native.js.map +1 -0
  15. package/dist/cjs/chrome/UserMenu.cjs +158 -0
  16. package/dist/cjs/chrome/UserMenu.native.js +186 -0
  17. package/dist/cjs/chrome/UserMenu.native.js.map +1 -0
  18. package/dist/cjs/chrome/awesomebar-data.cjs +173 -0
  19. package/dist/cjs/chrome/awesomebar-data.native.js +233 -0
  20. package/dist/cjs/chrome/awesomebar-data.native.js.map +1 -0
  21. package/dist/cjs/chrome/command-palette-model.cjs +208 -0
  22. package/dist/cjs/chrome/command-palette-model.native.js +301 -0
  23. package/dist/cjs/chrome/command-palette-model.native.js.map +1 -0
  24. package/dist/cjs/chrome/help-about-dialog.cjs +136 -0
  25. package/dist/cjs/chrome/help-about-dialog.native.js +146 -0
  26. package/dist/cjs/chrome/help-about-dialog.native.js.map +1 -0
  27. package/dist/cjs/chrome/help-shortcuts-dialog.cjs +155 -0
  28. package/dist/cjs/chrome/help-shortcuts-dialog.native.js +168 -0
  29. package/dist/cjs/chrome/help-shortcuts-dialog.native.js.map +1 -0
  30. package/dist/cjs/chrome/notifications-data.cjs +190 -0
  31. package/dist/cjs/chrome/notifications-data.native.js +271 -0
  32. package/dist/cjs/chrome/notifications-data.native.js.map +1 -0
  33. package/dist/cjs/chrome/notifications-panel.cjs +171 -0
  34. package/dist/cjs/chrome/notifications-panel.native.js +189 -0
  35. package/dist/cjs/chrome/notifications-panel.native.js.map +1 -0
  36. package/dist/cjs/chrome/notifications-utils.cjs +43 -0
  37. package/dist/cjs/chrome/notifications-utils.native.js +46 -0
  38. package/dist/cjs/chrome/notifications-utils.native.js.map +1 -0
  39. package/dist/cjs/chrome/shortcuts.cjs +58 -0
  40. package/dist/cjs/chrome/shortcuts.native.js +72 -0
  41. package/dist/cjs/chrome/shortcuts.native.js.map +1 -0
  42. package/dist/cjs/chrome/user-session.cjs +100 -0
  43. package/dist/cjs/chrome/user-session.native.js +120 -0
  44. package/dist/cjs/chrome/user-session.native.js.map +1 -0
  45. package/dist/cjs/config/BackofficeProvider.cjs +164 -0
  46. package/dist/cjs/config/BackofficeProvider.native.js +217 -0
  47. package/dist/cjs/config/BackofficeProvider.native.js.map +1 -0
  48. package/dist/cjs/config/capabilities.cjs +123 -0
  49. package/dist/cjs/config/capabilities.native.js +155 -0
  50. package/dist/cjs/config/capabilities.native.js.map +1 -0
  51. package/dist/cjs/config/events.cjs +18 -0
  52. package/dist/cjs/config/events.native.js +21 -0
  53. package/dist/cjs/config/events.native.js.map +1 -0
  54. package/dist/cjs/config/slots.cjs +18 -0
  55. package/dist/cjs/config/slots.native.js +21 -0
  56. package/dist/cjs/config/slots.native.js.map +1 -0
  57. package/dist/cjs/i18n/en.cjs +101 -0
  58. package/dist/cjs/i18n/en.native.js +104 -0
  59. package/dist/cjs/i18n/en.native.js.map +1 -0
  60. package/dist/cjs/i18n/index.cjs +77 -0
  61. package/dist/cjs/i18n/index.native.js +102 -0
  62. package/dist/cjs/i18n/index.native.js.map +1 -0
  63. package/dist/cjs/index.cjs +109 -0
  64. package/dist/cjs/index.native.js +112 -0
  65. package/dist/cjs/index.native.js.map +1 -0
  66. package/dist/cjs/navigation/navigator.cjs +144 -0
  67. package/dist/cjs/navigation/navigator.native.js +307 -0
  68. package/dist/cjs/navigation/navigator.native.js.map +1 -0
  69. package/dist/cjs/navigation/paths.cjs +103 -0
  70. package/dist/cjs/navigation/paths.native.js +143 -0
  71. package/dist/cjs/navigation/paths.native.js.map +1 -0
  72. package/dist/cjs/oneAdapter/index.cjs +53 -0
  73. package/dist/cjs/oneAdapter/index.native.js +64 -0
  74. package/dist/cjs/oneAdapter/index.native.js.map +1 -0
  75. package/dist/cjs/shared/useFrappeApp.cjs +56 -0
  76. package/dist/cjs/shared/useFrappeApp.native.js +60 -0
  77. package/dist/cjs/shared/useFrappeApp.native.js.map +1 -0
  78. package/dist/cjs/shell/BackofficeShell.cjs +194 -0
  79. package/dist/cjs/shell/BackofficeShell.native.js +223 -0
  80. package/dist/cjs/shell/BackofficeShell.native.js.map +1 -0
  81. package/dist/cjs/shell/WorkspaceSidebar.cjs +115 -0
  82. package/dist/cjs/shell/WorkspaceSidebar.native.js +143 -0
  83. package/dist/cjs/shell/WorkspaceSidebar.native.js.map +1 -0
  84. package/dist/cjs/shell/useWorkspaces.cjs +131 -0
  85. package/dist/cjs/shell/useWorkspaces.native.js +173 -0
  86. package/dist/cjs/shell/useWorkspaces.native.js.map +1 -0
  87. package/dist/cjs/shell/workspaceIcons.cjs +64 -0
  88. package/dist/cjs/shell/workspaceIcons.native.js +67 -0
  89. package/dist/cjs/shell/workspaceIcons.native.js.map +1 -0
  90. package/dist/esm/chrome/BackofficeAwesomebar.mjs +70 -0
  91. package/dist/esm/chrome/BackofficeAwesomebar.mjs.map +1 -0
  92. package/dist/esm/chrome/BackofficeAwesomebar.native.js +78 -0
  93. package/dist/esm/chrome/BackofficeAwesomebar.native.js.map +1 -0
  94. package/dist/esm/chrome/CommandPalette.mjs +428 -0
  95. package/dist/esm/chrome/CommandPalette.mjs.map +1 -0
  96. package/dist/esm/chrome/CommandPalette.native.js +477 -0
  97. package/dist/esm/chrome/CommandPalette.native.js.map +1 -0
  98. package/dist/esm/chrome/HelpMenu.mjs +106 -0
  99. package/dist/esm/chrome/HelpMenu.mjs.map +1 -0
  100. package/dist/esm/chrome/HelpMenu.native.js +117 -0
  101. package/dist/esm/chrome/HelpMenu.native.js.map +1 -0
  102. package/dist/esm/chrome/NotificationsButton.mjs +87 -0
  103. package/dist/esm/chrome/NotificationsButton.mjs.map +1 -0
  104. package/dist/esm/chrome/NotificationsButton.native.js +87 -0
  105. package/dist/esm/chrome/NotificationsButton.native.js.map +1 -0
  106. package/dist/esm/chrome/UserMenu.mjs +133 -0
  107. package/dist/esm/chrome/UserMenu.mjs.map +1 -0
  108. package/dist/esm/chrome/UserMenu.native.js +147 -0
  109. package/dist/esm/chrome/UserMenu.native.js.map +1 -0
  110. package/dist/esm/chrome/awesomebar-data.mjs +144 -0
  111. package/dist/esm/chrome/awesomebar-data.mjs.map +1 -0
  112. package/dist/esm/chrome/awesomebar-data.native.js +190 -0
  113. package/dist/esm/chrome/awesomebar-data.native.js.map +1 -0
  114. package/dist/esm/chrome/command-palette-model.mjs +183 -0
  115. package/dist/esm/chrome/command-palette-model.mjs.map +1 -0
  116. package/dist/esm/chrome/command-palette-model.native.js +273 -0
  117. package/dist/esm/chrome/command-palette-model.native.js.map +1 -0
  118. package/dist/esm/chrome/help-about-dialog.mjs +111 -0
  119. package/dist/esm/chrome/help-about-dialog.mjs.map +1 -0
  120. package/dist/esm/chrome/help-about-dialog.native.js +118 -0
  121. package/dist/esm/chrome/help-about-dialog.native.js.map +1 -0
  122. package/dist/esm/chrome/help-shortcuts-dialog.mjs +130 -0
  123. package/dist/esm/chrome/help-shortcuts-dialog.mjs.map +1 -0
  124. package/dist/esm/chrome/help-shortcuts-dialog.native.js +140 -0
  125. package/dist/esm/chrome/help-shortcuts-dialog.native.js.map +1 -0
  126. package/dist/esm/chrome/notifications-data.mjs +165 -0
  127. package/dist/esm/chrome/notifications-data.mjs.map +1 -0
  128. package/dist/esm/chrome/notifications-data.native.js +232 -0
  129. package/dist/esm/chrome/notifications-data.native.js.map +1 -0
  130. package/dist/esm/chrome/notifications-panel.mjs +146 -0
  131. package/dist/esm/chrome/notifications-panel.mjs.map +1 -0
  132. package/dist/esm/chrome/notifications-panel.native.js +161 -0
  133. package/dist/esm/chrome/notifications-panel.native.js.map +1 -0
  134. package/dist/esm/chrome/notifications-utils.mjs +17 -0
  135. package/dist/esm/chrome/notifications-utils.mjs.map +1 -0
  136. package/dist/esm/chrome/notifications-utils.native.js +17 -0
  137. package/dist/esm/chrome/notifications-utils.native.js.map +1 -0
  138. package/dist/esm/chrome/shortcuts.mjs +33 -0
  139. package/dist/esm/chrome/shortcuts.mjs.map +1 -0
  140. package/dist/esm/chrome/shortcuts.native.js +44 -0
  141. package/dist/esm/chrome/shortcuts.native.js.map +1 -0
  142. package/dist/esm/chrome/user-session.mjs +73 -0
  143. package/dist/esm/chrome/user-session.mjs.map +1 -0
  144. package/dist/esm/chrome/user-session.native.js +79 -0
  145. package/dist/esm/chrome/user-session.native.js.map +1 -0
  146. package/dist/esm/config/BackofficeProvider.mjs +132 -0
  147. package/dist/esm/config/BackofficeProvider.mjs.map +1 -0
  148. package/dist/esm/config/BackofficeProvider.native.js +171 -0
  149. package/dist/esm/config/BackofficeProvider.native.js.map +1 -0
  150. package/dist/esm/config/capabilities.mjs +97 -0
  151. package/dist/esm/config/capabilities.mjs.map +1 -0
  152. package/dist/esm/config/capabilities.native.js +126 -0
  153. package/dist/esm/config/capabilities.native.js.map +1 -0
  154. package/dist/esm/config/events.mjs +2 -0
  155. package/dist/esm/config/events.mjs.map +1 -0
  156. package/dist/esm/config/events.native.js +2 -0
  157. package/dist/esm/config/events.native.js.map +1 -0
  158. package/dist/esm/config/slots.mjs +2 -0
  159. package/dist/esm/config/slots.mjs.map +1 -0
  160. package/dist/esm/config/slots.native.js +2 -0
  161. package/dist/esm/config/slots.native.js.map +1 -0
  162. package/dist/esm/i18n/en.mjs +76 -0
  163. package/dist/esm/i18n/en.mjs.map +1 -0
  164. package/dist/esm/i18n/en.native.js +76 -0
  165. package/dist/esm/i18n/en.native.js.map +1 -0
  166. package/dist/esm/i18n/index.mjs +40 -0
  167. package/dist/esm/i18n/index.mjs.map +1 -0
  168. package/dist/esm/i18n/index.native.js +62 -0
  169. package/dist/esm/i18n/index.native.js.map +1 -0
  170. package/dist/esm/index.mjs +26 -0
  171. package/dist/esm/index.mjs.map +1 -0
  172. package/dist/esm/index.native.js +26 -0
  173. package/dist/esm/index.native.js.map +1 -0
  174. package/dist/esm/navigation/navigator.mjs +115 -0
  175. package/dist/esm/navigation/navigator.mjs.map +1 -0
  176. package/dist/esm/navigation/navigator.native.js +275 -0
  177. package/dist/esm/navigation/navigator.native.js.map +1 -0
  178. package/dist/esm/navigation/paths.mjs +66 -0
  179. package/dist/esm/navigation/paths.mjs.map +1 -0
  180. package/dist/esm/navigation/paths.native.js +103 -0
  181. package/dist/esm/navigation/paths.native.js.map +1 -0
  182. package/dist/esm/oneAdapter/index.mjs +28 -0
  183. package/dist/esm/oneAdapter/index.mjs.map +1 -0
  184. package/dist/esm/oneAdapter/index.native.js +36 -0
  185. package/dist/esm/oneAdapter/index.native.js.map +1 -0
  186. package/dist/esm/shared/useFrappeApp.mjs +31 -0
  187. package/dist/esm/shared/useFrappeApp.mjs.map +1 -0
  188. package/dist/esm/shared/useFrappeApp.native.js +32 -0
  189. package/dist/esm/shared/useFrappeApp.native.js.map +1 -0
  190. package/dist/esm/shell/BackofficeShell.mjs +169 -0
  191. package/dist/esm/shell/BackofficeShell.mjs.map +1 -0
  192. package/dist/esm/shell/BackofficeShell.native.js +195 -0
  193. package/dist/esm/shell/BackofficeShell.native.js.map +1 -0
  194. package/dist/esm/shell/WorkspaceSidebar.mjs +89 -0
  195. package/dist/esm/shell/WorkspaceSidebar.mjs.map +1 -0
  196. package/dist/esm/shell/WorkspaceSidebar.native.js +114 -0
  197. package/dist/esm/shell/WorkspaceSidebar.native.js.map +1 -0
  198. package/dist/esm/shell/useWorkspaces.mjs +104 -0
  199. package/dist/esm/shell/useWorkspaces.mjs.map +1 -0
  200. package/dist/esm/shell/useWorkspaces.native.js +143 -0
  201. package/dist/esm/shell/useWorkspaces.native.js.map +1 -0
  202. package/dist/esm/shell/workspaceIcons.mjs +39 -0
  203. package/dist/esm/shell/workspaceIcons.mjs.map +1 -0
  204. package/dist/esm/shell/workspaceIcons.native.js +39 -0
  205. package/dist/esm/shell/workspaceIcons.native.js.map +1 -0
  206. package/dist/jsx/chrome/BackofficeAwesomebar.mjs +70 -0
  207. package/dist/jsx/chrome/BackofficeAwesomebar.mjs.map +1 -0
  208. package/dist/jsx/chrome/BackofficeAwesomebar.native.js +106 -0
  209. package/dist/jsx/chrome/BackofficeAwesomebar.native.js.map +1 -0
  210. package/dist/jsx/chrome/CommandPalette.mjs +428 -0
  211. package/dist/jsx/chrome/CommandPalette.mjs.map +1 -0
  212. package/dist/jsx/chrome/CommandPalette.native.js +505 -0
  213. package/dist/jsx/chrome/CommandPalette.native.js.map +1 -0
  214. package/dist/jsx/chrome/HelpMenu.mjs +106 -0
  215. package/dist/jsx/chrome/HelpMenu.mjs.map +1 -0
  216. package/dist/jsx/chrome/HelpMenu.native.js +145 -0
  217. package/dist/jsx/chrome/HelpMenu.native.js.map +1 -0
  218. package/dist/jsx/chrome/NotificationsButton.mjs +87 -0
  219. package/dist/jsx/chrome/NotificationsButton.mjs.map +1 -0
  220. package/dist/jsx/chrome/NotificationsButton.native.js +115 -0
  221. package/dist/jsx/chrome/NotificationsButton.native.js.map +1 -0
  222. package/dist/jsx/chrome/UserMenu.mjs +133 -0
  223. package/dist/jsx/chrome/UserMenu.mjs.map +1 -0
  224. package/dist/jsx/chrome/UserMenu.native.js +186 -0
  225. package/dist/jsx/chrome/UserMenu.native.js.map +1 -0
  226. package/dist/jsx/chrome/awesomebar-data.mjs +144 -0
  227. package/dist/jsx/chrome/awesomebar-data.mjs.map +1 -0
  228. package/dist/jsx/chrome/awesomebar-data.native.js +233 -0
  229. package/dist/jsx/chrome/awesomebar-data.native.js.map +1 -0
  230. package/dist/jsx/chrome/command-palette-model.mjs +183 -0
  231. package/dist/jsx/chrome/command-palette-model.mjs.map +1 -0
  232. package/dist/jsx/chrome/command-palette-model.native.js +301 -0
  233. package/dist/jsx/chrome/command-palette-model.native.js.map +1 -0
  234. package/dist/jsx/chrome/help-about-dialog.mjs +111 -0
  235. package/dist/jsx/chrome/help-about-dialog.mjs.map +1 -0
  236. package/dist/jsx/chrome/help-about-dialog.native.js +146 -0
  237. package/dist/jsx/chrome/help-about-dialog.native.js.map +1 -0
  238. package/dist/jsx/chrome/help-shortcuts-dialog.mjs +130 -0
  239. package/dist/jsx/chrome/help-shortcuts-dialog.mjs.map +1 -0
  240. package/dist/jsx/chrome/help-shortcuts-dialog.native.js +168 -0
  241. package/dist/jsx/chrome/help-shortcuts-dialog.native.js.map +1 -0
  242. package/dist/jsx/chrome/notifications-data.mjs +165 -0
  243. package/dist/jsx/chrome/notifications-data.mjs.map +1 -0
  244. package/dist/jsx/chrome/notifications-data.native.js +271 -0
  245. package/dist/jsx/chrome/notifications-data.native.js.map +1 -0
  246. package/dist/jsx/chrome/notifications-panel.mjs +146 -0
  247. package/dist/jsx/chrome/notifications-panel.mjs.map +1 -0
  248. package/dist/jsx/chrome/notifications-panel.native.js +189 -0
  249. package/dist/jsx/chrome/notifications-panel.native.js.map +1 -0
  250. package/dist/jsx/chrome/notifications-utils.mjs +17 -0
  251. package/dist/jsx/chrome/notifications-utils.mjs.map +1 -0
  252. package/dist/jsx/chrome/notifications-utils.native.js +46 -0
  253. package/dist/jsx/chrome/notifications-utils.native.js.map +1 -0
  254. package/dist/jsx/chrome/shortcuts.mjs +33 -0
  255. package/dist/jsx/chrome/shortcuts.mjs.map +1 -0
  256. package/dist/jsx/chrome/shortcuts.native.js +72 -0
  257. package/dist/jsx/chrome/shortcuts.native.js.map +1 -0
  258. package/dist/jsx/chrome/user-session.mjs +73 -0
  259. package/dist/jsx/chrome/user-session.mjs.map +1 -0
  260. package/dist/jsx/chrome/user-session.native.js +120 -0
  261. package/dist/jsx/chrome/user-session.native.js.map +1 -0
  262. package/dist/jsx/config/BackofficeProvider.mjs +132 -0
  263. package/dist/jsx/config/BackofficeProvider.mjs.map +1 -0
  264. package/dist/jsx/config/BackofficeProvider.native.js +217 -0
  265. package/dist/jsx/config/BackofficeProvider.native.js.map +1 -0
  266. package/dist/jsx/config/capabilities.mjs +97 -0
  267. package/dist/jsx/config/capabilities.mjs.map +1 -0
  268. package/dist/jsx/config/capabilities.native.js +155 -0
  269. package/dist/jsx/config/capabilities.native.js.map +1 -0
  270. package/dist/jsx/config/events.mjs +2 -0
  271. package/dist/jsx/config/events.mjs.map +1 -0
  272. package/dist/jsx/config/events.native.js +21 -0
  273. package/dist/jsx/config/events.native.js.map +1 -0
  274. package/dist/jsx/config/slots.mjs +2 -0
  275. package/dist/jsx/config/slots.mjs.map +1 -0
  276. package/dist/jsx/config/slots.native.js +21 -0
  277. package/dist/jsx/config/slots.native.js.map +1 -0
  278. package/dist/jsx/i18n/en.mjs +76 -0
  279. package/dist/jsx/i18n/en.mjs.map +1 -0
  280. package/dist/jsx/i18n/en.native.js +104 -0
  281. package/dist/jsx/i18n/en.native.js.map +1 -0
  282. package/dist/jsx/i18n/index.mjs +40 -0
  283. package/dist/jsx/i18n/index.mjs.map +1 -0
  284. package/dist/jsx/i18n/index.native.js +102 -0
  285. package/dist/jsx/i18n/index.native.js.map +1 -0
  286. package/dist/jsx/index.js +26 -0
  287. package/dist/jsx/index.js.map +1 -0
  288. package/dist/jsx/index.mjs +26 -0
  289. package/dist/jsx/index.mjs.map +1 -0
  290. package/dist/jsx/index.native.js +112 -0
  291. package/dist/jsx/index.native.js.map +1 -0
  292. package/dist/jsx/navigation/navigator.mjs +115 -0
  293. package/dist/jsx/navigation/navigator.mjs.map +1 -0
  294. package/dist/jsx/navigation/navigator.native.js +307 -0
  295. package/dist/jsx/navigation/navigator.native.js.map +1 -0
  296. package/dist/jsx/navigation/paths.mjs +66 -0
  297. package/dist/jsx/navigation/paths.mjs.map +1 -0
  298. package/dist/jsx/navigation/paths.native.js +143 -0
  299. package/dist/jsx/navigation/paths.native.js.map +1 -0
  300. package/dist/jsx/oneAdapter/index.mjs +28 -0
  301. package/dist/jsx/oneAdapter/index.mjs.map +1 -0
  302. package/dist/jsx/oneAdapter/index.native.js +64 -0
  303. package/dist/jsx/oneAdapter/index.native.js.map +1 -0
  304. package/dist/jsx/shared/useFrappeApp.mjs +31 -0
  305. package/dist/jsx/shared/useFrappeApp.mjs.map +1 -0
  306. package/dist/jsx/shared/useFrappeApp.native.js +60 -0
  307. package/dist/jsx/shared/useFrappeApp.native.js.map +1 -0
  308. package/dist/jsx/shell/BackofficeShell.mjs +169 -0
  309. package/dist/jsx/shell/BackofficeShell.mjs.map +1 -0
  310. package/dist/jsx/shell/BackofficeShell.native.js +223 -0
  311. package/dist/jsx/shell/BackofficeShell.native.js.map +1 -0
  312. package/dist/jsx/shell/WorkspaceSidebar.mjs +89 -0
  313. package/dist/jsx/shell/WorkspaceSidebar.mjs.map +1 -0
  314. package/dist/jsx/shell/WorkspaceSidebar.native.js +143 -0
  315. package/dist/jsx/shell/WorkspaceSidebar.native.js.map +1 -0
  316. package/dist/jsx/shell/useWorkspaces.mjs +104 -0
  317. package/dist/jsx/shell/useWorkspaces.mjs.map +1 -0
  318. package/dist/jsx/shell/useWorkspaces.native.js +173 -0
  319. package/dist/jsx/shell/useWorkspaces.native.js.map +1 -0
  320. package/dist/jsx/shell/workspaceIcons.mjs +39 -0
  321. package/dist/jsx/shell/workspaceIcons.mjs.map +1 -0
  322. package/dist/jsx/shell/workspaceIcons.native.js +67 -0
  323. package/dist/jsx/shell/workspaceIcons.native.js.map +1 -0
  324. package/package.json +123 -0
  325. package/src/chrome/BackofficeAwesomebar.tsx +89 -0
  326. package/src/chrome/CommandPalette.spec.tsx +252 -0
  327. package/src/chrome/CommandPalette.tsx +520 -0
  328. package/src/chrome/HelpMenu.tsx +119 -0
  329. package/src/chrome/NotificationsButton.tsx +87 -0
  330. package/src/chrome/UserMenu.tsx +173 -0
  331. package/src/chrome/awesomebar-data.tsx +252 -0
  332. package/src/chrome/command-palette-model.ts +291 -0
  333. package/src/chrome/help-about-dialog.tsx +130 -0
  334. package/src/chrome/help-shortcuts-dialog.tsx +144 -0
  335. package/src/chrome/notifications-data.tsx +287 -0
  336. package/src/chrome/notifications-panel.tsx +175 -0
  337. package/src/chrome/notifications-utils.tsx +44 -0
  338. package/src/chrome/shortcuts.ts +53 -0
  339. package/src/chrome/user-session.tsx +116 -0
  340. package/src/config/BackofficeProvider.spec.tsx +126 -0
  341. package/src/config/BackofficeProvider.tsx +315 -0
  342. package/src/config/capabilities.ts +172 -0
  343. package/src/config/events.ts +19 -0
  344. package/src/config/slots.ts +28 -0
  345. package/src/i18n/en.ts +83 -0
  346. package/src/i18n/index.ts +69 -0
  347. package/src/index.ts +150 -0
  348. package/src/navigation/navigator.spec.ts +106 -0
  349. package/src/navigation/navigator.ts +209 -0
  350. package/src/navigation/paths.ts +146 -0
  351. package/src/oneAdapter/index.tsx +61 -0
  352. package/src/shared/useFrappeApp.ts +39 -0
  353. package/src/shell/BackofficeShell.tsx +222 -0
  354. package/src/shell/WorkspaceSidebar.tsx +119 -0
  355. package/src/shell/useWorkspaces.ts +202 -0
  356. package/src/shell/workspaceIcons.ts +76 -0
  357. package/types/chrome/BackofficeAwesomebar.d.ts +12 -0
  358. package/types/chrome/BackofficeAwesomebar.d.ts.map +1 -0
  359. package/types/chrome/CommandPalette.d.ts +6 -0
  360. package/types/chrome/CommandPalette.d.ts.map +1 -0
  361. package/types/chrome/HelpMenu.d.ts +2 -0
  362. package/types/chrome/HelpMenu.d.ts.map +1 -0
  363. package/types/chrome/NotificationsButton.d.ts +2 -0
  364. package/types/chrome/NotificationsButton.d.ts.map +1 -0
  365. package/types/chrome/UserMenu.d.ts +6 -0
  366. package/types/chrome/UserMenu.d.ts.map +1 -0
  367. package/types/chrome/awesomebar-data.d.ts +52 -0
  368. package/types/chrome/awesomebar-data.d.ts.map +1 -0
  369. package/types/chrome/command-palette-model.d.ts +47 -0
  370. package/types/chrome/command-palette-model.d.ts.map +1 -0
  371. package/types/chrome/help-about-dialog.d.ts +12 -0
  372. package/types/chrome/help-about-dialog.d.ts.map +1 -0
  373. package/types/chrome/help-shortcuts-dialog.d.ts +6 -0
  374. package/types/chrome/help-shortcuts-dialog.d.ts.map +1 -0
  375. package/types/chrome/notifications-data.d.ts +34 -0
  376. package/types/chrome/notifications-data.d.ts.map +1 -0
  377. package/types/chrome/notifications-panel.d.ts +11 -0
  378. package/types/chrome/notifications-panel.d.ts.map +1 -0
  379. package/types/chrome/notifications-utils.d.ts +21 -0
  380. package/types/chrome/notifications-utils.d.ts.map +1 -0
  381. package/types/chrome/shortcuts.d.ts +6 -0
  382. package/types/chrome/shortcuts.d.ts.map +1 -0
  383. package/types/chrome/user-session.d.ts +19 -0
  384. package/types/chrome/user-session.d.ts.map +1 -0
  385. package/types/config/BackofficeProvider.d.ts +128 -0
  386. package/types/config/BackofficeProvider.d.ts.map +1 -0
  387. package/types/config/capabilities.d.ts +109 -0
  388. package/types/config/capabilities.d.ts.map +1 -0
  389. package/types/config/events.d.ts +39 -0
  390. package/types/config/events.d.ts.map +1 -0
  391. package/types/config/slots.d.ts +32 -0
  392. package/types/config/slots.d.ts.map +1 -0
  393. package/types/i18n/en.d.ts +83 -0
  394. package/types/i18n/en.d.ts.map +1 -0
  395. package/types/i18n/index.d.ts +27 -0
  396. package/types/i18n/index.d.ts.map +1 -0
  397. package/types/index.d.ts +55 -0
  398. package/types/index.d.ts.map +1 -0
  399. package/types/navigation/navigator.d.ts +98 -0
  400. package/types/navigation/navigator.d.ts.map +1 -0
  401. package/types/navigation/paths.d.ts +60 -0
  402. package/types/navigation/paths.d.ts.map +1 -0
  403. package/types/oneAdapter/index.d.ts +8 -0
  404. package/types/oneAdapter/index.d.ts.map +1 -0
  405. package/types/shared/useFrappeApp.d.ts +10 -0
  406. package/types/shared/useFrappeApp.d.ts.map +1 -0
  407. package/types/shell/BackofficeShell.d.ts +10 -0
  408. package/types/shell/BackofficeShell.d.ts.map +1 -0
  409. package/types/shell/WorkspaceSidebar.d.ts +9 -0
  410. package/types/shell/WorkspaceSidebar.d.ts.map +1 -0
  411. package/types/shell/useWorkspaces.d.ts +43 -0
  412. package/types/shell/useWorkspaces.d.ts.map +1 -0
  413. package/types/shell/workspaceIcons.d.ts +9 -0
  414. package/types/shell/workspaceIcons.d.ts.map +1 -0
@@ -0,0 +1,87 @@
1
+ // Navbar bell button + notifications panel (Notification Log, unread badge,
2
+ // mark-as-read; desk parity). Self-contained: reads frappe context + the
3
+ // backoffice navigator internally; the shell mounts it props-less.
4
+ import { Badge, Button } from "@multiplatform.one/components";
5
+ import { FloatingPanel, useViewportGtSm } from "@multiplatform.one/forms";
6
+ import { BellIcon } from "@phosphor-icons/react";
7
+ import { useCallback, useState } from "react";
8
+ import { useTranslation } from "react-i18next";
9
+ import {
10
+ useBackofficeConfig,
11
+ useBackofficeNavigator,
12
+ useBackofficePaths,
13
+ } from "../config/BackofficeProvider";
14
+ import { BACKOFFICE_I18N_NAMESPACE } from "../i18n";
15
+ import { useNotifications, type NotificationItem } from "./notifications-data";
16
+ import { NotificationsPanel } from "./notifications-panel";
17
+
18
+ /** Fixed content width when the panel floats (sheet mode fills instead). */
19
+ const PANEL_WIDTH = 400;
20
+
21
+ export function NotificationsButton() {
22
+ const { t } = useTranslation(BACKOFFICE_I18N_NAMESPACE);
23
+ const navigator = useBackofficeNavigator();
24
+ const paths = useBackofficePaths();
25
+ const { emitEvent } = useBackofficeConfig();
26
+ const [open, setOpen] = useState(false);
27
+ const { items, unreadCount, isLoading, markAsRead, markAllAsRead } = useNotifications();
28
+ // FloatingPanel pivots float↔sheet on the same viewport check.
29
+ const floating = useViewportGtSm();
30
+
31
+ const handleItemPress = useCallback(
32
+ (item: NotificationItem) => {
33
+ if (item.unread) {
34
+ // Fire-and-forget (desk parity): navigation must not wait on the RPC;
35
+ // the optimistic override reverts itself if the write fails.
36
+ markAsRead(item.name).catch((error) => {
37
+ emitEvent({ type: "error", error, context: "notifications.markAsRead" });
38
+ });
39
+ }
40
+ if (item.document_type && item.document_name) {
41
+ navigator.navigate(paths.form(item.document_type, item.document_name));
42
+ }
43
+ setOpen(false);
44
+ },
45
+ [markAsRead, navigator, paths, emitEvent],
46
+ );
47
+
48
+ const handleMarkAllRead = useCallback(() => {
49
+ markAllAsRead().catch((error) => {
50
+ emitEvent({ type: "error", error, context: "notifications.markAllAsRead" });
51
+ });
52
+ }, [markAllAsRead, emitEvent]);
53
+
54
+ const label = t("notifications.title", "Notifications");
55
+
56
+ return (
57
+ <FloatingPanel
58
+ open={open}
59
+ onOpenChange={setOpen}
60
+ scrollable
61
+ sizing="fill"
62
+ fitContent
63
+ contentPadding="none"
64
+ triggerA11y={{ label }}
65
+ trigger={
66
+ <Badge count={unreadCount} max={99}>
67
+ <Button
68
+ chromeless
69
+ circular
70
+ size="$3"
71
+ icon={BellIcon}
72
+ aria-label={label}
73
+ data-testid="backoffice-notifications-button"
74
+ />
75
+ </Badge>
76
+ }
77
+ >
78
+ <NotificationsPanel
79
+ items={items}
80
+ isLoading={isLoading}
81
+ width={floating ? PANEL_WIDTH : "100%"}
82
+ onItemPress={handleItemPress}
83
+ onMarkAllRead={handleMarkAllRead}
84
+ />
85
+ </FloatingPanel>
86
+ );
87
+ }
@@ -0,0 +1,173 @@
1
+ // Navbar avatar menu (session user, my settings, toggle theme, logout;
2
+ // desk parity). Theme toggling rides the provider's `appearance` adapter —
3
+ // hosts without one get a disabled item (GJS/story parity). The logout and
4
+ // theme items are individually gated by the userMenu capability config.
5
+ import React from "react";
6
+ import {
7
+ Avatar,
8
+ Button,
9
+ DropdownMenu,
10
+ SizableText,
11
+ YStack,
12
+ isWeb,
13
+ } from "@multiplatform.one/components";
14
+ import { useFrappeConfig } from "@multiplatform.one/frappe-ui";
15
+ import { useCallback } from "react";
16
+ import { useTranslation } from "react-i18next";
17
+ import {
18
+ useBackofficeCapabilities,
19
+ useBackofficeConfig,
20
+ useBackofficeNavigator,
21
+ useBackofficePaths,
22
+ } from "../config/BackofficeProvider";
23
+ import { BACKOFFICE_I18N_NAMESPACE } from "../i18n";
24
+ import { useFrappeApp } from "../shared/useFrappeApp";
25
+ import { clearSessionUserCache, useSessionUserInfo } from "./user-session";
26
+
27
+ export interface UserMenuProps {
28
+ /** Open the menu on mount (stories/tests only). */
29
+ defaultOpen?: boolean;
30
+ }
31
+
32
+ export function UserMenu({ defaultOpen }: UserMenuProps = {}) {
33
+ const { t } = useTranslation(BACKOFFICE_I18N_NAMESPACE);
34
+ const navigator = useBackofficeNavigator();
35
+ const paths = useBackofficePaths();
36
+ const { appearance } = useBackofficeConfig();
37
+ const { userMenu } = useBackofficeCapabilities();
38
+ const app = useFrappeApp();
39
+ const frappeConfig = useFrappeConfig();
40
+ const { info } = useSessionUserInfo();
41
+ const sessionUser = info?.user;
42
+
43
+ const handleLogout = useCallback(async () => {
44
+ try {
45
+ await app?.auth().logout();
46
+ } catch {
47
+ // Best effort: even when the bench rejects the call (expired session,
48
+ // network), still clear local state and leave the desk.
49
+ }
50
+ // Cache clear scope: this package's session-user cache only. The frappe
51
+ // `sid` cookie is invalidated server-side by /api/method/logout itself.
52
+ clearSessionUserCache();
53
+ navigator.replace(paths.home());
54
+ }, [app, navigator, paths]);
55
+
56
+ const handleReload = useCallback(() => {
57
+ if (typeof location !== "undefined" && typeof location.reload === "function") {
58
+ location.reload();
59
+ }
60
+ }, []);
61
+
62
+ const websiteURL = frappeConfig?.baseURL;
63
+ const handleViewWebsite = useCallback(() => {
64
+ if (websiteURL && typeof window !== "undefined" && typeof window.open === "function") {
65
+ window.open(websiteURL, "_blank", "noopener,noreferrer");
66
+ }
67
+ }, [websiteURL]);
68
+
69
+ const handleToggleTheme = useCallback(() => {
70
+ if (!appearance) return;
71
+ appearance.setScheme(appearance.scheme === "dark" ? "light" : "dark");
72
+ }, [appearance]);
73
+
74
+ const exitToSite = userMenu.exitToSite;
75
+ const handleExitToSite = useCallback(() => {
76
+ navigator.navigate(exitToSite.path);
77
+ }, [navigator, exitToSite.path]);
78
+
79
+ const sessionGate = sessionUser
80
+ ? {}
81
+ : {
82
+ disabled: true,
83
+ disabledReason: t("userMenu.sessionUnavailable", "Session not loaded"),
84
+ };
85
+
86
+ return (
87
+ <DropdownMenu placement="bottom-end" defaultOpen={defaultOpen}>
88
+ <DropdownMenu.Trigger asChild>
89
+ <Button chromeless circular size="$3" aria-label={t("userMenu.label", "User menu")}>
90
+ <Avatar size={26} src={info?.imageUrl} name={info?.fullName ?? info?.user} />
91
+ </Button>
92
+ </DropdownMenu.Trigger>
93
+ <DropdownMenu.Content minWidth={240}>
94
+ {info ? (
95
+ <>
96
+ <YStack paddingHorizontal="$3" paddingVertical="$2.5" gap="$0.5">
97
+ <SizableText size="$3" fontWeight="600" numberOfLines={1}>
98
+ {info.fullName}
99
+ </SizableText>
100
+ {info.email ? (
101
+ <SizableText size="$2" color="$color10" numberOfLines={1}>
102
+ {info.email}
103
+ </SizableText>
104
+ ) : null}
105
+ </YStack>
106
+ <DropdownMenu.Separator />
107
+ </>
108
+ ) : null}
109
+ <DropdownMenu.Item
110
+ {...sessionGate}
111
+ onSelect={() => sessionUser && navigator.navigate(paths.form("User", sessionUser))}
112
+ >
113
+ {t("userMenu.myProfile", "My Profile")}
114
+ </DropdownMenu.Item>
115
+ <DropdownMenu.Item
116
+ {...sessionGate}
117
+ onSelect={() => sessionUser && navigator.navigate(paths.form("User", sessionUser))}
118
+ >
119
+ {t("userMenu.mySettings", "My Settings")}
120
+ </DropdownMenu.Item>
121
+ {/* GAP: desk's Session Defaults dialog (frappe.core.doctype.session_default_settings)
122
+ has no backoffice surface yet — placeholder until a dialog exists. */}
123
+ <DropdownMenu.Item
124
+ disabled
125
+ disabledReason={t("userMenu.sessionDefaultsGap", "Not implemented yet")}
126
+ >
127
+ {t("userMenu.sessionDefaults", "Session Defaults")}
128
+ </DropdownMenu.Item>
129
+ {isWeb ? (
130
+ <DropdownMenu.Item onSelect={handleReload}>
131
+ {t("userMenu.reload", "Reload")}
132
+ </DropdownMenu.Item>
133
+ ) : null}
134
+ {userMenu.themeToggle ? (
135
+ <DropdownMenu.Item
136
+ {...(appearance
137
+ ? {}
138
+ : {
139
+ disabled: true,
140
+ disabledReason: t(
141
+ "userMenu.themeUnavailable",
142
+ "Theme switching is unavailable on this platform",
143
+ ),
144
+ })}
145
+ onSelect={handleToggleTheme}
146
+ >
147
+ {t("userMenu.toggleTheme", "Toggle Theme")}
148
+ </DropdownMenu.Item>
149
+ ) : null}
150
+ {isWeb && websiteURL ? (
151
+ <DropdownMenu.Item onSelect={handleViewWebsite}>
152
+ {t("userMenu.viewWebsite", "View Website")}
153
+ </DropdownMenu.Item>
154
+ ) : null}
155
+ {/* MOD-02: embedded shells are full-bleed (no host navbar), so the
156
+ way back into the host app lives here. */}
157
+ {isWeb && exitToSite.enabled ? (
158
+ <DropdownMenu.Item onSelect={handleExitToSite}>
159
+ {exitToSite.label ?? t("userMenu.exitToSite", "← multiplatform.one")}
160
+ </DropdownMenu.Item>
161
+ ) : null}
162
+ {userMenu.logout ? (
163
+ <>
164
+ <DropdownMenu.Separator />
165
+ <DropdownMenu.Item onSelect={handleLogout}>
166
+ {t("userMenu.logOut", "Log Out")}
167
+ </DropdownMenu.Item>
168
+ </>
169
+ ) : null}
170
+ </DropdownMenu.Content>
171
+ </DropdownMenu>
172
+ );
173
+ }
@@ -0,0 +1,252 @@
1
+ // Data layer for the backoffice awesomebar: a session-cached DocType +
2
+ // Workspace name catalog, the frappe.utils.global_search document search,
3
+ // and the shared desk recents store (same storage key frappe-ui's
4
+ // DeskSidebar reads).
5
+ //
6
+ // Connection comes exclusively from the ambient FrappeProvider (mounted by
7
+ // BackofficeProvider); without one the catalog/search stay empty and inert.
8
+ import React from "react";
9
+ import { HttpClient, type AuthConfig, type FixtureProvider } from "@multiplatform.one/frappe";
10
+ import { useFrappeConfig, type FrappeConfig } from "@multiplatform.one/frappe-ui";
11
+ import { storage } from "@multiplatform.one/store";
12
+ import { useEffect, useMemo, useState } from "react";
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Catalog (DocType names + Workspaces), fetched once per session
16
+ // ---------------------------------------------------------------------------
17
+
18
+ export interface AwesomebarWorkspace {
19
+ name: string;
20
+ title: string;
21
+ }
22
+
23
+ export interface AwesomebarCatalog {
24
+ doctypes: string[];
25
+ workspaces: AwesomebarWorkspace[];
26
+ }
27
+
28
+ interface CatalogSource {
29
+ baseURL: string;
30
+ auth?: FrappeConfig["auth"];
31
+ fixtures?: FixtureProvider;
32
+ }
33
+
34
+ /** Module-level cache: one fetch per (baseURL, fixture-mode) per session. */
35
+ const catalogCache = new Map<string, Promise<AwesomebarCatalog>>();
36
+
37
+ async function fetchCatalog(source: CatalogSource): Promise<AwesomebarCatalog> {
38
+ if (source.fixtures) {
39
+ const [doctypes, workspaces] = await Promise.all([
40
+ source.fixtures.getList("DocType", { fields: ["name"] }),
41
+ source.fixtures.getList("Workspace", { fields: ["name", "title"] }),
42
+ ]);
43
+ return {
44
+ doctypes: (doctypes as { name?: string }[]).flatMap((row) => (row.name ? [row.name] : [])),
45
+ workspaces: (workspaces as { name?: string; title?: string }[]).flatMap((row) =>
46
+ row.name ? [{ name: row.name, title: row.title || row.name }] : [],
47
+ ),
48
+ };
49
+ }
50
+ const client = new HttpClient(source.baseURL, source.auth as AuthConfig | undefined);
51
+ const [doctypesRes, workspacesRes] = await Promise.all([
52
+ client.get<{ message?: { name: string }[] }>("/api/method/frappe.client.get_list", {
53
+ doctype: "DocType",
54
+ fields: '["name"]',
55
+ limit_page_length: "0",
56
+ }),
57
+ client.get<{ data?: { name: string; title?: string }[] }>("/api/resource/Workspace", {
58
+ fields: '["name","title"]',
59
+ limit_page_length: "0",
60
+ }),
61
+ ]);
62
+ return {
63
+ doctypes: (doctypesRes.payload?.message ?? []).flatMap((row) => (row.name ? [row.name] : [])),
64
+ workspaces: (workspacesRes.payload?.data ?? []).flatMap((row) =>
65
+ row.name ? [{ name: row.name, title: row.title || row.name }] : [],
66
+ ),
67
+ };
68
+ }
69
+
70
+ function getCatalog(source: CatalogSource): Promise<AwesomebarCatalog> {
71
+ const key = `${source.baseURL}:${source.fixtures ? "fixtures" : "live"}`;
72
+ let promise = catalogCache.get(key);
73
+ if (!promise) {
74
+ promise = fetchCatalog(source);
75
+ // Failed fetches are evicted so a later mount can retry.
76
+ promise.catch(() => {
77
+ if (catalogCache.get(key) === promise) catalogCache.delete(key);
78
+ });
79
+ catalogCache.set(key, promise);
80
+ }
81
+ return promise;
82
+ }
83
+
84
+ export interface AwesomebarCatalogState {
85
+ catalog?: AwesomebarCatalog;
86
+ /** Catalog fetch failed (e.g. Guest 403 on the DocType list). */
87
+ error: boolean;
88
+ }
89
+
90
+ /**
91
+ * DocType + Workspace name catalog from the ambient FrappeProvider (fixtures
92
+ * honored in stories/tests), fetched once per session. SSR-safe: the fetch
93
+ * runs in an effect. Without a provider the catalog stays empty.
94
+ */
95
+ export function useAwesomebarCatalog(): AwesomebarCatalogState {
96
+ const config = useFrappeConfig();
97
+ const baseURL = config?.baseURL;
98
+ const fixtures = config?.fixtures;
99
+ const auth = config?.auth;
100
+
101
+ const [catalog, setCatalog] = useState<AwesomebarCatalog | undefined>(undefined);
102
+ const [error, setError] = useState(false);
103
+
104
+ useEffect(() => {
105
+ if (!baseURL) return;
106
+ let cancelled = false;
107
+ setError(false);
108
+ getCatalog({ baseURL, auth, fixtures })
109
+ .then((result) => {
110
+ if (!cancelled) setCatalog(result);
111
+ })
112
+ .catch(() => {
113
+ if (!cancelled) setError(true);
114
+ });
115
+ return () => {
116
+ cancelled = true;
117
+ };
118
+ // `auth` is intentionally not a dependency: the cache is keyed by
119
+ // baseURL/fixtures and provider auth is stable for the session.
120
+ // oxlint-disable-next-line exhaustive-deps
121
+ }, [baseURL, fixtures]);
122
+
123
+ return { catalog, error };
124
+ }
125
+
126
+ // ---------------------------------------------------------------------------
127
+ // Global search (frappe.utils.global_search.search)
128
+ // ---------------------------------------------------------------------------
129
+
130
+ /** Row shape returned by frappe.utils.global_search.search. */
131
+ export interface GlobalSearchHit {
132
+ doctype: string;
133
+ name: string;
134
+ content: string;
135
+ }
136
+
137
+ export interface GlobalSearchState {
138
+ hits: GlobalSearchHit[];
139
+ searching: boolean;
140
+ }
141
+
142
+ /**
143
+ * Document hits from the desk full-text index for an (already debounced)
144
+ * query. Empty query clears without fetching. Failures degrade to no hits —
145
+ * the nav-command and workspace sections still render.
146
+ */
147
+ export function useGlobalSearch(query: string, limit = 10): GlobalSearchState {
148
+ const config = useFrappeConfig();
149
+ const baseURL = config?.baseURL;
150
+ const auth = config?.auth;
151
+
152
+ const client = useMemo(
153
+ () => (baseURL ? new HttpClient(baseURL, auth as AuthConfig | undefined) : undefined),
154
+ // Provider auth is session-stable; key the client by baseURL only.
155
+ // oxlint-disable-next-line exhaustive-deps
156
+ [baseURL],
157
+ );
158
+
159
+ const [hits, setHits] = useState<GlobalSearchHit[]>([]);
160
+ const [searching, setSearching] = useState(false);
161
+
162
+ useEffect(() => {
163
+ if (!query || !client) {
164
+ setHits([]);
165
+ setSearching(false);
166
+ return;
167
+ }
168
+ let cancelled = false;
169
+ setSearching(true);
170
+ client
171
+ .get<{ message?: GlobalSearchHit[] }>("/api/method/frappe.utils.global_search.search", {
172
+ text: query,
173
+ start: 0,
174
+ limit,
175
+ })
176
+ .then((response) => {
177
+ if (!cancelled) setHits(response.payload?.message ?? []);
178
+ })
179
+ .catch(() => {
180
+ if (!cancelled) setHits([]);
181
+ })
182
+ .finally(() => {
183
+ if (!cancelled) setSearching(false);
184
+ });
185
+ return () => {
186
+ cancelled = true;
187
+ };
188
+ }, [client, query, limit]);
189
+
190
+ return { hits, searching };
191
+ }
192
+
193
+ /**
194
+ * Global-search `content` is "Label: value ||| Label2: value2" — flatten the
195
+ * separators to a middot snippet and cap the length for one-line display.
196
+ */
197
+ export function toContentSnippet(content: string, maxLength = 120): string {
198
+ const flat = content
199
+ .replace(/<[^>]*>/g, " ")
200
+ .split("|||")
201
+ .map((part) => part.trim())
202
+ .filter(Boolean)
203
+ .join(" · ")
204
+ .replace(/\s+/g, " ")
205
+ .trim();
206
+ return flat.length > maxLength ? `${flat.slice(0, maxLength - 1)}…` : flat;
207
+ }
208
+
209
+ // ---------------------------------------------------------------------------
210
+ // Recents (shared desk store — same key frappe-ui's DeskSidebar/addToRecent
211
+ // writes, so navbar recents and sidebar recents stay one list)
212
+ // ---------------------------------------------------------------------------
213
+
214
+ export interface RecentItem {
215
+ label: string;
216
+ path: string;
217
+ }
218
+
219
+ const recentStorageKey = "frappe-desk-recent";
220
+
221
+ /** Newest-first recent documents from the shared desk store. */
222
+ export async function readRecentItems(): Promise<RecentItem[]> {
223
+ try {
224
+ const raw = await Promise.resolve(storage.getItem(recentStorageKey));
225
+ return raw ? (JSON.parse(raw) as RecentItem[]) : [];
226
+ } catch {
227
+ return [];
228
+ }
229
+ }
230
+
231
+ // ---------------------------------------------------------------------------
232
+ // Matching
233
+ // ---------------------------------------------------------------------------
234
+
235
+ /**
236
+ * Case-insensitive substring match over names: prefix matches rank first,
237
+ * shorter names break ties (best-match-first, desk-style).
238
+ */
239
+ export function rankNameMatches(query: string, names: readonly string[], limit: number): string[] {
240
+ const q = query.trim().toLowerCase();
241
+ if (!q) return [];
242
+ const prefixed: string[] = [];
243
+ const contained: string[] = [];
244
+ for (const name of names) {
245
+ const lower = name.toLowerCase();
246
+ if (lower.startsWith(q)) prefixed.push(name);
247
+ else if (lower.includes(q)) contained.push(name);
248
+ }
249
+ prefixed.sort((a, b) => a.length - b.length);
250
+ contained.sort((a, b) => a.length - b.length);
251
+ return [...prefixed, ...contained].slice(0, limit);
252
+ }