@multiplatform.one/backoffice 6.4.0 → 6.4.2

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 (325) hide show
  1. package/dist/cjs/chrome/CommandPalette.cjs +20 -9
  2. package/dist/cjs/chrome/CommandPalette.native.js +18 -10
  3. package/dist/cjs/chrome/CommandPalette.native.js.map +1 -1
  4. package/dist/cjs/i18n/en.cjs +21 -0
  5. package/dist/cjs/i18n/en.native.js +21 -0
  6. package/dist/cjs/i18n/en.native.js.map +1 -1
  7. package/dist/cjs/index.cjs +49 -1
  8. package/dist/cjs/index.native.js +48 -0
  9. package/dist/cjs/index.native.js.map +1 -1
  10. package/dist/cjs/shared/types.cjs +18 -0
  11. package/dist/cjs/shared/types.native.js +21 -0
  12. package/dist/cjs/shared/types.native.js.map +1 -0
  13. package/dist/cjs/shared/useDocinfo.cjs +116 -0
  14. package/dist/cjs/shared/useDocinfo.native.js +167 -0
  15. package/dist/cjs/shared/useDocinfo.native.js.map +1 -0
  16. package/dist/cjs/shared/useDoctypeMeta.cjs +124 -0
  17. package/dist/cjs/shared/useDoctypeMeta.native.js +158 -0
  18. package/dist/cjs/shared/useDoctypeMeta.native.js.map +1 -0
  19. package/dist/cjs/shared/useRecentVisit.cjs +40 -0
  20. package/dist/cjs/shared/useRecentVisit.native.js +43 -0
  21. package/dist/cjs/shared/useRecentVisit.native.js.map +1 -0
  22. package/dist/cjs/shared/useSessionUserId.cjs +54 -0
  23. package/dist/cjs/shared/useSessionUserId.native.js +59 -0
  24. package/dist/cjs/shared/useSessionUserId.native.js.map +1 -0
  25. package/dist/cjs/shared/useSlugTarget.cjs +138 -0
  26. package/dist/cjs/shared/useSlugTarget.native.js +166 -0
  27. package/dist/cjs/shared/useSlugTarget.native.js.map +1 -0
  28. package/dist/cjs/shared/useUserRoles.cjs +72 -0
  29. package/dist/cjs/shared/useUserRoles.native.js +79 -0
  30. package/dist/cjs/shared/useUserRoles.native.js.map +1 -0
  31. package/dist/cjs/workspace/WorkspaceChartBlock.cjs +136 -0
  32. package/dist/cjs/workspace/WorkspaceChartBlock.native.js +144 -0
  33. package/dist/cjs/workspace/WorkspaceChartBlock.native.js.map +1 -0
  34. package/dist/cjs/workspace/WorkspaceGrid.cjs +91 -0
  35. package/dist/cjs/workspace/WorkspaceGrid.native.js +126 -0
  36. package/dist/cjs/workspace/WorkspaceGrid.native.js.map +1 -0
  37. package/dist/cjs/workspace/WorkspaceLinkCard.cjs +139 -0
  38. package/dist/cjs/workspace/WorkspaceLinkCard.native.js +155 -0
  39. package/dist/cjs/workspace/WorkspaceLinkCard.native.js.map +1 -0
  40. package/dist/cjs/workspace/WorkspaceLinkDirectory.cjs +110 -0
  41. package/dist/cjs/workspace/WorkspaceLinkDirectory.native.js +127 -0
  42. package/dist/cjs/workspace/WorkspaceLinkDirectory.native.js.map +1 -0
  43. package/dist/cjs/workspace/WorkspaceNumberCard.cjs +133 -0
  44. package/dist/cjs/workspace/WorkspaceNumberCard.native.js +138 -0
  45. package/dist/cjs/workspace/WorkspaceNumberCard.native.js.map +1 -0
  46. package/dist/cjs/workspace/WorkspaceQuickList.cjs +112 -0
  47. package/dist/cjs/workspace/WorkspaceQuickList.native.js +130 -0
  48. package/dist/cjs/workspace/WorkspaceQuickList.native.js.map +1 -0
  49. package/dist/cjs/workspace/WorkspaceRecents.cjs +116 -0
  50. package/dist/cjs/workspace/WorkspaceRecents.native.js +132 -0
  51. package/dist/cjs/workspace/WorkspaceRecents.native.js.map +1 -0
  52. package/dist/cjs/workspace/WorkspaceScreen.cjs +291 -0
  53. package/dist/cjs/workspace/WorkspaceScreen.native.js +323 -0
  54. package/dist/cjs/workspace/WorkspaceScreen.native.js.map +1 -0
  55. package/dist/cjs/workspace/WorkspaceShortcut.cjs +185 -0
  56. package/dist/cjs/workspace/WorkspaceShortcut.native.js +202 -0
  57. package/dist/cjs/workspace/WorkspaceShortcut.native.js.map +1 -0
  58. package/dist/cjs/workspace/parseContent.cjs +180 -0
  59. package/dist/cjs/workspace/parseContent.native.js +299 -0
  60. package/dist/cjs/workspace/parseContent.native.js.map +1 -0
  61. package/dist/cjs/workspace/types.cjs +18 -0
  62. package/dist/cjs/workspace/types.native.js +21 -0
  63. package/dist/cjs/workspace/types.native.js.map +1 -0
  64. package/dist/cjs/workspace/useDashboardChart.cjs +96 -0
  65. package/dist/cjs/workspace/useDashboardChart.native.js +110 -0
  66. package/dist/cjs/workspace/useDashboardChart.native.js.map +1 -0
  67. package/dist/cjs/workspace/useWorkspaceDoc.cjs +103 -0
  68. package/dist/cjs/workspace/useWorkspaceDoc.native.js +120 -0
  69. package/dist/cjs/workspace/useWorkspaceDoc.native.js.map +1 -0
  70. package/dist/esm/chrome/CommandPalette.mjs +20 -9
  71. package/dist/esm/chrome/CommandPalette.mjs.map +1 -1
  72. package/dist/esm/chrome/CommandPalette.native.js +18 -10
  73. package/dist/esm/chrome/CommandPalette.native.js.map +1 -1
  74. package/dist/esm/i18n/en.mjs +21 -0
  75. package/dist/esm/i18n/en.mjs.map +1 -1
  76. package/dist/esm/i18n/en.native.js +21 -0
  77. package/dist/esm/i18n/en.native.js.map +1 -1
  78. package/dist/esm/index.mjs +19 -1
  79. package/dist/esm/index.mjs.map +1 -1
  80. package/dist/esm/index.native.js +19 -1
  81. package/dist/esm/index.native.js.map +1 -1
  82. package/dist/esm/shared/types.mjs +2 -0
  83. package/dist/esm/shared/types.mjs.map +1 -0
  84. package/dist/esm/shared/types.native.js +2 -0
  85. package/dist/esm/shared/types.native.js.map +1 -0
  86. package/dist/esm/shared/useDocinfo.mjs +90 -0
  87. package/dist/esm/shared/useDocinfo.mjs.map +1 -0
  88. package/dist/esm/shared/useDocinfo.native.js +138 -0
  89. package/dist/esm/shared/useDocinfo.native.js.map +1 -0
  90. package/dist/esm/shared/useDoctypeMeta.mjs +99 -0
  91. package/dist/esm/shared/useDoctypeMeta.mjs.map +1 -0
  92. package/dist/esm/shared/useDoctypeMeta.native.js +130 -0
  93. package/dist/esm/shared/useDoctypeMeta.native.js.map +1 -0
  94. package/dist/esm/shared/useRecentVisit.mjs +15 -0
  95. package/dist/esm/shared/useRecentVisit.mjs.map +1 -0
  96. package/dist/esm/shared/useRecentVisit.native.js +15 -0
  97. package/dist/esm/shared/useRecentVisit.native.js.map +1 -0
  98. package/dist/esm/shared/useSessionUserId.mjs +29 -0
  99. package/dist/esm/shared/useSessionUserId.mjs.map +1 -0
  100. package/dist/esm/shared/useSessionUserId.native.js +31 -0
  101. package/dist/esm/shared/useSessionUserId.native.js.map +1 -0
  102. package/dist/esm/shared/useSlugTarget.mjs +113 -0
  103. package/dist/esm/shared/useSlugTarget.mjs.map +1 -0
  104. package/dist/esm/shared/useSlugTarget.native.js +138 -0
  105. package/dist/esm/shared/useSlugTarget.native.js.map +1 -0
  106. package/dist/esm/shared/useUserRoles.mjs +47 -0
  107. package/dist/esm/shared/useUserRoles.mjs.map +1 -0
  108. package/dist/esm/shared/useUserRoles.native.js +51 -0
  109. package/dist/esm/shared/useUserRoles.native.js.map +1 -0
  110. package/dist/esm/workspace/WorkspaceChartBlock.mjs +111 -0
  111. package/dist/esm/workspace/WorkspaceChartBlock.mjs.map +1 -0
  112. package/dist/esm/workspace/WorkspaceChartBlock.native.js +116 -0
  113. package/dist/esm/workspace/WorkspaceChartBlock.native.js.map +1 -0
  114. package/dist/esm/workspace/WorkspaceGrid.mjs +66 -0
  115. package/dist/esm/workspace/WorkspaceGrid.mjs.map +1 -0
  116. package/dist/esm/workspace/WorkspaceGrid.native.js +98 -0
  117. package/dist/esm/workspace/WorkspaceGrid.native.js.map +1 -0
  118. package/dist/esm/workspace/WorkspaceLinkCard.mjs +113 -0
  119. package/dist/esm/workspace/WorkspaceLinkCard.mjs.map +1 -0
  120. package/dist/esm/workspace/WorkspaceLinkCard.native.js +126 -0
  121. package/dist/esm/workspace/WorkspaceLinkCard.native.js.map +1 -0
  122. package/dist/esm/workspace/WorkspaceLinkDirectory.mjs +84 -0
  123. package/dist/esm/workspace/WorkspaceLinkDirectory.mjs.map +1 -0
  124. package/dist/esm/workspace/WorkspaceLinkDirectory.native.js +98 -0
  125. package/dist/esm/workspace/WorkspaceLinkDirectory.native.js.map +1 -0
  126. package/dist/esm/workspace/WorkspaceNumberCard.mjs +108 -0
  127. package/dist/esm/workspace/WorkspaceNumberCard.mjs.map +1 -0
  128. package/dist/esm/workspace/WorkspaceNumberCard.native.js +110 -0
  129. package/dist/esm/workspace/WorkspaceNumberCard.native.js.map +1 -0
  130. package/dist/esm/workspace/WorkspaceQuickList.mjs +87 -0
  131. package/dist/esm/workspace/WorkspaceQuickList.mjs.map +1 -0
  132. package/dist/esm/workspace/WorkspaceQuickList.native.js +102 -0
  133. package/dist/esm/workspace/WorkspaceQuickList.native.js.map +1 -0
  134. package/dist/esm/workspace/WorkspaceRecents.mjs +91 -0
  135. package/dist/esm/workspace/WorkspaceRecents.mjs.map +1 -0
  136. package/dist/esm/workspace/WorkspaceRecents.native.js +104 -0
  137. package/dist/esm/workspace/WorkspaceRecents.native.js.map +1 -0
  138. package/dist/esm/workspace/WorkspaceScreen.mjs +266 -0
  139. package/dist/esm/workspace/WorkspaceScreen.mjs.map +1 -0
  140. package/dist/esm/workspace/WorkspaceScreen.native.js +295 -0
  141. package/dist/esm/workspace/WorkspaceScreen.native.js.map +1 -0
  142. package/dist/esm/workspace/WorkspaceShortcut.mjs +159 -0
  143. package/dist/esm/workspace/WorkspaceShortcut.mjs.map +1 -0
  144. package/dist/esm/workspace/WorkspaceShortcut.native.js +173 -0
  145. package/dist/esm/workspace/WorkspaceShortcut.native.js.map +1 -0
  146. package/dist/esm/workspace/parseContent.mjs +148 -0
  147. package/dist/esm/workspace/parseContent.mjs.map +1 -0
  148. package/dist/esm/workspace/parseContent.native.js +264 -0
  149. package/dist/esm/workspace/parseContent.native.js.map +1 -0
  150. package/dist/esm/workspace/types.mjs +2 -0
  151. package/dist/esm/workspace/types.mjs.map +1 -0
  152. package/dist/esm/workspace/types.native.js +2 -0
  153. package/dist/esm/workspace/types.native.js.map +1 -0
  154. package/dist/esm/workspace/useDashboardChart.mjs +71 -0
  155. package/dist/esm/workspace/useDashboardChart.mjs.map +1 -0
  156. package/dist/esm/workspace/useDashboardChart.native.js +82 -0
  157. package/dist/esm/workspace/useDashboardChart.native.js.map +1 -0
  158. package/dist/esm/workspace/useWorkspaceDoc.mjs +77 -0
  159. package/dist/esm/workspace/useWorkspaceDoc.mjs.map +1 -0
  160. package/dist/esm/workspace/useWorkspaceDoc.native.js +91 -0
  161. package/dist/esm/workspace/useWorkspaceDoc.native.js.map +1 -0
  162. package/dist/jsx/chrome/CommandPalette.mjs +20 -9
  163. package/dist/jsx/chrome/CommandPalette.mjs.map +1 -1
  164. package/dist/jsx/chrome/CommandPalette.native.js +18 -10
  165. package/dist/jsx/chrome/CommandPalette.native.js.map +1 -1
  166. package/dist/jsx/i18n/en.mjs +21 -0
  167. package/dist/jsx/i18n/en.mjs.map +1 -1
  168. package/dist/jsx/i18n/en.native.js +21 -0
  169. package/dist/jsx/i18n/en.native.js.map +1 -1
  170. package/dist/jsx/index.js +19 -1
  171. package/dist/jsx/index.js.map +1 -1
  172. package/dist/jsx/index.mjs +19 -1
  173. package/dist/jsx/index.mjs.map +1 -1
  174. package/dist/jsx/index.native.js +48 -0
  175. package/dist/jsx/index.native.js.map +1 -1
  176. package/dist/jsx/shared/types.mjs +2 -0
  177. package/dist/jsx/shared/types.mjs.map +1 -0
  178. package/dist/jsx/shared/types.native.js +21 -0
  179. package/dist/jsx/shared/types.native.js.map +1 -0
  180. package/dist/jsx/shared/useDocinfo.mjs +90 -0
  181. package/dist/jsx/shared/useDocinfo.mjs.map +1 -0
  182. package/dist/jsx/shared/useDocinfo.native.js +167 -0
  183. package/dist/jsx/shared/useDocinfo.native.js.map +1 -0
  184. package/dist/jsx/shared/useDoctypeMeta.mjs +99 -0
  185. package/dist/jsx/shared/useDoctypeMeta.mjs.map +1 -0
  186. package/dist/jsx/shared/useDoctypeMeta.native.js +158 -0
  187. package/dist/jsx/shared/useDoctypeMeta.native.js.map +1 -0
  188. package/dist/jsx/shared/useRecentVisit.mjs +15 -0
  189. package/dist/jsx/shared/useRecentVisit.mjs.map +1 -0
  190. package/dist/jsx/shared/useRecentVisit.native.js +43 -0
  191. package/dist/jsx/shared/useRecentVisit.native.js.map +1 -0
  192. package/dist/jsx/shared/useSessionUserId.mjs +29 -0
  193. package/dist/jsx/shared/useSessionUserId.mjs.map +1 -0
  194. package/dist/jsx/shared/useSessionUserId.native.js +59 -0
  195. package/dist/jsx/shared/useSessionUserId.native.js.map +1 -0
  196. package/dist/jsx/shared/useSlugTarget.mjs +113 -0
  197. package/dist/jsx/shared/useSlugTarget.mjs.map +1 -0
  198. package/dist/jsx/shared/useSlugTarget.native.js +166 -0
  199. package/dist/jsx/shared/useSlugTarget.native.js.map +1 -0
  200. package/dist/jsx/shared/useUserRoles.mjs +47 -0
  201. package/dist/jsx/shared/useUserRoles.mjs.map +1 -0
  202. package/dist/jsx/shared/useUserRoles.native.js +79 -0
  203. package/dist/jsx/shared/useUserRoles.native.js.map +1 -0
  204. package/dist/jsx/workspace/WorkspaceChartBlock.mjs +111 -0
  205. package/dist/jsx/workspace/WorkspaceChartBlock.mjs.map +1 -0
  206. package/dist/jsx/workspace/WorkspaceChartBlock.native.js +144 -0
  207. package/dist/jsx/workspace/WorkspaceChartBlock.native.js.map +1 -0
  208. package/dist/jsx/workspace/WorkspaceGrid.mjs +66 -0
  209. package/dist/jsx/workspace/WorkspaceGrid.mjs.map +1 -0
  210. package/dist/jsx/workspace/WorkspaceGrid.native.js +126 -0
  211. package/dist/jsx/workspace/WorkspaceGrid.native.js.map +1 -0
  212. package/dist/jsx/workspace/WorkspaceLinkCard.mjs +113 -0
  213. package/dist/jsx/workspace/WorkspaceLinkCard.mjs.map +1 -0
  214. package/dist/jsx/workspace/WorkspaceLinkCard.native.js +155 -0
  215. package/dist/jsx/workspace/WorkspaceLinkCard.native.js.map +1 -0
  216. package/dist/jsx/workspace/WorkspaceLinkDirectory.mjs +84 -0
  217. package/dist/jsx/workspace/WorkspaceLinkDirectory.mjs.map +1 -0
  218. package/dist/jsx/workspace/WorkspaceLinkDirectory.native.js +127 -0
  219. package/dist/jsx/workspace/WorkspaceLinkDirectory.native.js.map +1 -0
  220. package/dist/jsx/workspace/WorkspaceNumberCard.mjs +108 -0
  221. package/dist/jsx/workspace/WorkspaceNumberCard.mjs.map +1 -0
  222. package/dist/jsx/workspace/WorkspaceNumberCard.native.js +138 -0
  223. package/dist/jsx/workspace/WorkspaceNumberCard.native.js.map +1 -0
  224. package/dist/jsx/workspace/WorkspaceQuickList.mjs +87 -0
  225. package/dist/jsx/workspace/WorkspaceQuickList.mjs.map +1 -0
  226. package/dist/jsx/workspace/WorkspaceQuickList.native.js +130 -0
  227. package/dist/jsx/workspace/WorkspaceQuickList.native.js.map +1 -0
  228. package/dist/jsx/workspace/WorkspaceRecents.mjs +91 -0
  229. package/dist/jsx/workspace/WorkspaceRecents.mjs.map +1 -0
  230. package/dist/jsx/workspace/WorkspaceRecents.native.js +132 -0
  231. package/dist/jsx/workspace/WorkspaceRecents.native.js.map +1 -0
  232. package/dist/jsx/workspace/WorkspaceScreen.mjs +266 -0
  233. package/dist/jsx/workspace/WorkspaceScreen.mjs.map +1 -0
  234. package/dist/jsx/workspace/WorkspaceScreen.native.js +323 -0
  235. package/dist/jsx/workspace/WorkspaceScreen.native.js.map +1 -0
  236. package/dist/jsx/workspace/WorkspaceShortcut.mjs +159 -0
  237. package/dist/jsx/workspace/WorkspaceShortcut.mjs.map +1 -0
  238. package/dist/jsx/workspace/WorkspaceShortcut.native.js +202 -0
  239. package/dist/jsx/workspace/WorkspaceShortcut.native.js.map +1 -0
  240. package/dist/jsx/workspace/parseContent.mjs +148 -0
  241. package/dist/jsx/workspace/parseContent.mjs.map +1 -0
  242. package/dist/jsx/workspace/parseContent.native.js +299 -0
  243. package/dist/jsx/workspace/parseContent.native.js.map +1 -0
  244. package/dist/jsx/workspace/types.mjs +2 -0
  245. package/dist/jsx/workspace/types.mjs.map +1 -0
  246. package/dist/jsx/workspace/types.native.js +21 -0
  247. package/dist/jsx/workspace/types.native.js.map +1 -0
  248. package/dist/jsx/workspace/useDashboardChart.mjs +71 -0
  249. package/dist/jsx/workspace/useDashboardChart.mjs.map +1 -0
  250. package/dist/jsx/workspace/useDashboardChart.native.js +110 -0
  251. package/dist/jsx/workspace/useDashboardChart.native.js.map +1 -0
  252. package/dist/jsx/workspace/useWorkspaceDoc.mjs +77 -0
  253. package/dist/jsx/workspace/useWorkspaceDoc.mjs.map +1 -0
  254. package/dist/jsx/workspace/useWorkspaceDoc.native.js +120 -0
  255. package/dist/jsx/workspace/useWorkspaceDoc.native.js.map +1 -0
  256. package/package.json +12 -12
  257. package/src/chrome/CommandPalette.tsx +35 -15
  258. package/src/i18n/en.ts +21 -0
  259. package/src/index.ts +69 -0
  260. package/src/shared/types.ts +14 -0
  261. package/src/shared/useDocinfo.ts +163 -0
  262. package/src/shared/useDoctypeMeta.ts +243 -0
  263. package/src/shared/useRecentVisit.ts +33 -0
  264. package/src/shared/useSessionUserId.ts +42 -0
  265. package/src/shared/useSlugTarget.ts +143 -0
  266. package/src/shared/useUserRoles.ts +66 -0
  267. package/src/workspace/WorkspaceChartBlock.tsx +115 -0
  268. package/src/workspace/WorkspaceGrid.tsx +82 -0
  269. package/src/workspace/WorkspaceLinkCard.tsx +134 -0
  270. package/src/workspace/WorkspaceLinkDirectory.tsx +126 -0
  271. package/src/workspace/WorkspaceNumberCard.tsx +126 -0
  272. package/src/workspace/WorkspaceQuickList.tsx +100 -0
  273. package/src/workspace/WorkspaceRecents.tsx +100 -0
  274. package/src/workspace/WorkspaceScreen.tsx +322 -0
  275. package/src/workspace/WorkspaceShortcut.tsx +223 -0
  276. package/src/workspace/parseContent.spec.ts +327 -0
  277. package/src/workspace/parseContent.ts +240 -0
  278. package/src/workspace/types.ts +135 -0
  279. package/src/workspace/useDashboardChart.ts +115 -0
  280. package/src/workspace/useWorkspaceDoc.ts +111 -0
  281. package/types/chrome/CommandPalette.d.ts.map +1 -1
  282. package/types/i18n/en.d.ts +19 -0
  283. package/types/i18n/en.d.ts.map +1 -1
  284. package/types/index.d.ts +34 -0
  285. package/types/index.d.ts.map +1 -1
  286. package/types/shared/types.d.ts +21 -0
  287. package/types/shared/types.d.ts.map +1 -0
  288. package/types/shared/useDocinfo.d.ts +61 -0
  289. package/types/shared/useDocinfo.d.ts.map +1 -0
  290. package/types/shared/useDoctypeMeta.d.ts +118 -0
  291. package/types/shared/useDoctypeMeta.d.ts.map +1 -0
  292. package/types/shared/useRecentVisit.d.ts +10 -0
  293. package/types/shared/useRecentVisit.d.ts.map +1 -0
  294. package/types/shared/useSessionUserId.d.ts +6 -0
  295. package/types/shared/useSessionUserId.d.ts.map +1 -0
  296. package/types/shared/useSlugTarget.d.ts +18 -0
  297. package/types/shared/useSlugTarget.d.ts.map +1 -0
  298. package/types/shared/useUserRoles.d.ts +15 -0
  299. package/types/shared/useUserRoles.d.ts.map +1 -0
  300. package/types/workspace/WorkspaceChartBlock.d.ts +6 -0
  301. package/types/workspace/WorkspaceChartBlock.d.ts.map +1 -0
  302. package/types/workspace/WorkspaceGrid.d.ts +23 -0
  303. package/types/workspace/WorkspaceGrid.d.ts.map +1 -0
  304. package/types/workspace/WorkspaceLinkCard.d.ts +20 -0
  305. package/types/workspace/WorkspaceLinkCard.d.ts.map +1 -0
  306. package/types/workspace/WorkspaceLinkDirectory.d.ts +19 -0
  307. package/types/workspace/WorkspaceLinkDirectory.d.ts.map +1 -0
  308. package/types/workspace/WorkspaceNumberCard.d.ts +19 -0
  309. package/types/workspace/WorkspaceNumberCard.d.ts.map +1 -0
  310. package/types/workspace/WorkspaceQuickList.d.ts +6 -0
  311. package/types/workspace/WorkspaceQuickList.d.ts.map +1 -0
  312. package/types/workspace/WorkspaceRecents.d.ts +2 -0
  313. package/types/workspace/WorkspaceRecents.d.ts.map +1 -0
  314. package/types/workspace/WorkspaceScreen.d.ts +24 -0
  315. package/types/workspace/WorkspaceScreen.d.ts.map +1 -0
  316. package/types/workspace/WorkspaceShortcut.d.ts +27 -0
  317. package/types/workspace/WorkspaceShortcut.d.ts.map +1 -0
  318. package/types/workspace/parseContent.d.ts +77 -0
  319. package/types/workspace/parseContent.d.ts.map +1 -0
  320. package/types/workspace/types.d.ts +112 -0
  321. package/types/workspace/types.d.ts.map +1 -0
  322. package/types/workspace/useDashboardChart.d.ts +29 -0
  323. package/types/workspace/useDashboardChart.d.ts.map +1 -0
  324. package/types/workspace/useWorkspaceDoc.d.ts +18 -0
  325. package/types/workspace/useWorkspaceDoc.d.ts.map +1 -0
@@ -0,0 +1,327 @@
1
+ /**
2
+ * parseContent — workspace content-block JSON parsing, HTML stripping,
3
+ * link grouping, and frappe filter-string normalization. Filter fixtures
4
+ * are the bench-verified shapes documented in the module header and
5
+ * p-results/backoffice/integration-notes.md (dicts for stats_filter /
6
+ * quick_list_filter, doctype-prefixed quads for Number Card filters_json).
7
+ */
8
+ import { describe, expect, it } from "vitest";
9
+ import {
10
+ composeWorkspaceBlocks,
11
+ findLinkGroup,
12
+ findShortcut,
13
+ groupLinks,
14
+ headerLevel,
15
+ parseFrappeFilters,
16
+ parseWorkspaceContent,
17
+ stripHtml,
18
+ } from "./parseContent";
19
+ import type { WorkspaceBlock, WorkspaceDoc, WorkspaceLinkRow } from "./types";
20
+
21
+ describe("parseWorkspaceContent", () => {
22
+ it("parses a desk-shaped block array", () => {
23
+ const content = JSON.stringify([
24
+ {
25
+ id: "abc123",
26
+ type: "header",
27
+ data: { text: '<span class="h4"><b>Your Shortcuts</b></span>', col: 12 },
28
+ },
29
+ { id: "def456", type: "shortcut", data: { shortcut_name: "User", col: 3 } },
30
+ { id: "ghi789", type: "card", data: { card_name: "Masters", col: 4 } },
31
+ ]);
32
+ const blocks = parseWorkspaceContent(content);
33
+ expect(blocks).toHaveLength(3);
34
+ expect(blocks[0].type).toBe("header");
35
+ expect(blocks[1].data.shortcut_name).toBe("User");
36
+ expect(blocks[2].data.card_name).toBe("Masters");
37
+ });
38
+
39
+ it("drops entries that are not blocks (null, scalars, missing type)", () => {
40
+ const content = JSON.stringify([
41
+ null,
42
+ "string",
43
+ 42,
44
+ { id: "x", data: {} },
45
+ { id: "y", type: 5, data: {} },
46
+ { id: "z", type: "spacer", data: {} },
47
+ ]);
48
+ const blocks = parseWorkspaceContent(content);
49
+ expect(blocks).toHaveLength(1);
50
+ expect(blocks[0].id).toBe("z");
51
+ });
52
+
53
+ it("returns [] for empty, malformed, or non-array content", () => {
54
+ expect(parseWorkspaceContent(undefined)).toEqual([]);
55
+ expect(parseWorkspaceContent(null)).toEqual([]);
56
+ expect(parseWorkspaceContent("")).toEqual([]);
57
+ expect(parseWorkspaceContent("{not json")).toEqual([]);
58
+ expect(parseWorkspaceContent('{"type":"header"}')).toEqual([]);
59
+ });
60
+ });
61
+
62
+ describe("stripHtml", () => {
63
+ it("strips desk rich-text wrappers (documented example)", () => {
64
+ expect(stripHtml('<span class="h4"><b>Reports &amp; Masters</b></span>')).toBe(
65
+ "Reports & Masters",
66
+ );
67
+ });
68
+
69
+ it("decodes named entities", () => {
70
+ expect(stripHtml("&lt;tag&gt;")).toBe("<tag>");
71
+ expect(stripHtml("&quot;quoted&quot; &apos;s&apos;")).toBe("\"quoted\" 's'");
72
+ expect(stripHtml("a&nbsp;b")).toBe("a b");
73
+ });
74
+
75
+ it("decodes decimal and hex numeric entities", () => {
76
+ expect(stripHtml("&#65;&#66;")).toBe("AB");
77
+ expect(stripHtml("&#x41;&#x42;")).toBe("AB");
78
+ });
79
+
80
+ it("leaves unknown entities untouched", () => {
81
+ expect(stripHtml("&unknown;")).toBe("&unknown;");
82
+ });
83
+
84
+ it("converts <br> variants to newlines", () => {
85
+ expect(stripHtml("a<br>b")).toBe("a\nb");
86
+ expect(stripHtml("a<br/>b")).toBe("a\nb");
87
+ expect(stripHtml("a<br />b")).toBe("a\nb");
88
+ });
89
+
90
+ it("trims and handles empty input", () => {
91
+ expect(stripHtml(" <b> x </b> ")).toBe("x");
92
+ expect(stripHtml("")).toBe("");
93
+ expect(stripHtml(null)).toBe("");
94
+ expect(stripHtml(undefined)).toBe("");
95
+ });
96
+ });
97
+
98
+ describe("headerLevel", () => {
99
+ it("reads the hN class from the wrapper", () => {
100
+ expect(headerLevel('<span class="h1">t</span>')).toBe(1);
101
+ expect(headerLevel('<span class="h2">t</span>')).toBe(2);
102
+ expect(headerLevel('<span class="h6">t</span>')).toBe(6);
103
+ });
104
+
105
+ it("finds hN among multiple classes (word boundary)", () => {
106
+ expect(headerLevel('<span class="ce-header h3 muted">t</span>')).toBe(3);
107
+ });
108
+
109
+ it("falls back to 4 for unclassed, styled, or missing input", () => {
110
+ expect(headerLevel("<b>plain</b>")).toBe(4);
111
+ expect(headerLevel('<span style="font-size: 18px">t</span>')).toBe(4);
112
+ expect(headerLevel('<span class="h7">t</span>')).toBe(4);
113
+ expect(headerLevel(null)).toBe(4);
114
+ expect(headerLevel(undefined)).toBe(4);
115
+ });
116
+ });
117
+
118
+ describe("groupLinks", () => {
119
+ const row = (partial: Partial<WorkspaceLinkRow> & { type: string; label: string }) =>
120
+ ({ name: partial.label, ...partial }) as WorkspaceLinkRow;
121
+
122
+ it("groups Link rows under the preceding Card Break", () => {
123
+ const groups = groupLinks([
124
+ row({ type: "Card Break", label: "Masters", link_count: 2 }),
125
+ row({ type: "Link", label: "User", link_type: "DocType", link_to: "User" }),
126
+ row({ type: "Link", label: "Role", link_type: "DocType", link_to: "Role" }),
127
+ row({ type: "Card Break", label: "Settings" }),
128
+ row({ type: "Link", label: "System Settings", link_to: "System Settings" }),
129
+ ]);
130
+ expect(groups).toHaveLength(2);
131
+ expect(groups[0].label).toBe("Masters");
132
+ expect(groups[0].breakRow.link_count).toBe(2);
133
+ expect(groups[0].links.map((l) => l.label)).toEqual(["User", "Role"]);
134
+ expect(groups[1].label).toBe("Settings");
135
+ expect(groups[1].links.map((l) => l.label)).toEqual(["System Settings"]);
136
+ });
137
+
138
+ it("drops hidden rows", () => {
139
+ const groups = groupLinks([
140
+ row({ type: "Card Break", label: "Masters" }),
141
+ row({ type: "Link", label: "Visible" }),
142
+ row({ type: "Link", label: "Hidden", hidden: 1 }),
143
+ ]);
144
+ expect(groups[0].links.map((l) => l.label)).toEqual(["Visible"]);
145
+ });
146
+
147
+ it("ignores Link rows before any Card Break (desk parity)", () => {
148
+ const groups = groupLinks([
149
+ row({ type: "Link", label: "Orphan" }),
150
+ row({ type: "Card Break", label: "Masters" }),
151
+ row({ type: "Link", label: "User" }),
152
+ ]);
153
+ expect(groups).toHaveLength(1);
154
+ expect(groups[0].links.map((l) => l.label)).toEqual(["User"]);
155
+ });
156
+
157
+ it("returns [] for undefined or empty links", () => {
158
+ expect(groupLinks(undefined)).toEqual([]);
159
+ expect(groupLinks([])).toEqual([]);
160
+ });
161
+ });
162
+
163
+ describe("parseFrappeFilters (bench-verified shapes)", () => {
164
+ it("normalizes a plain dict to = triples", () => {
165
+ expect(parseFrappeFilters('{"status":"Open"}')).toEqual([["status", "=", "Open"]]);
166
+ expect(parseFrappeFilters('{"enabled":1}')).toEqual([["enabled", "=", 1]]);
167
+ });
168
+
169
+ it("normalizes a dict of [op, value] tuples", () => {
170
+ expect(parseFrappeFilters('{"status":["=","Open"]}')).toEqual([["status", "=", "Open"]]);
171
+ expect(parseFrappeFilters('{"status":["in",["Open","Overdue"]]}')).toEqual([
172
+ ["status", "in", ["Open", "Overdue"]],
173
+ ]);
174
+ });
175
+
176
+ it("treats non-operator array values as = values", () => {
177
+ // 3-element array value is not an [op, value] tuple.
178
+ expect(parseFrappeFilters('{"role":["a","b","c"]}')).toEqual([["role", "=", ["a", "b", "c"]]]);
179
+ });
180
+
181
+ it("passes triples through as-is", () => {
182
+ expect(parseFrappeFilters('[["enabled","=",1]]')).toEqual([["enabled", "=", 1]]);
183
+ });
184
+
185
+ it("strips the doctype element from quads (Number Card filters_json)", () => {
186
+ expect(parseFrappeFilters('[["User","enabled","=",1]]')).toEqual([["enabled", "=", 1]]);
187
+ expect(
188
+ parseFrappeFilters('[["ToDo","status","=","Open"],["ToDo","priority","!=","Low"]]'),
189
+ ).toEqual([
190
+ ["status", "=", "Open"],
191
+ ["priority", "!=", "Low"],
192
+ ]);
193
+ });
194
+
195
+ it("handles mixed triples and quads, skipping malformed entries", () => {
196
+ expect(
197
+ parseFrappeFilters('[["status","=","Open"],["User","enabled","=",1],"junk",["short","="]]'),
198
+ ).toEqual([
199
+ ["status", "=", "Open"],
200
+ ["enabled", "=", 1],
201
+ ]);
202
+ });
203
+
204
+ it("returns undefined for empty, malformed, or valueless input", () => {
205
+ expect(parseFrappeFilters(undefined)).toBeUndefined();
206
+ expect(parseFrappeFilters(null)).toBeUndefined();
207
+ expect(parseFrappeFilters("")).toBeUndefined();
208
+ expect(parseFrappeFilters("{not json")).toBeUndefined();
209
+ expect(parseFrappeFilters("[]")).toBeUndefined();
210
+ expect(parseFrappeFilters("{}")).toBeUndefined();
211
+ expect(parseFrappeFilters('"scalar"')).toBeUndefined();
212
+ expect(parseFrappeFilters("[1,2]")).toBeUndefined();
213
+ });
214
+ });
215
+
216
+ describe("composeWorkspaceBlocks (dashboard-first, MOD-11)", () => {
217
+ const block = (type: string, data: Record<string, unknown>, id: string) =>
218
+ ({ id, type, data }) as WorkspaceBlock;
219
+
220
+ it("reorders by class: data (cards → charts → quick lists), shortcuts, cards", () => {
221
+ const composed = composeWorkspaceBlocks([
222
+ block("header", { text: '<span class="h4"><b>Your Shortcuts</b></span>' }, "h1"),
223
+ block("shortcut", { shortcut_name: "User" }, "s1"),
224
+ block("spacer", {}, "sp1"),
225
+ block("header", { text: '<span class="h4"><b>Reports &amp; Masters</b></span>' }, "h2"),
226
+ block("card", { card_name: "Users" }, "c1"),
227
+ block("quick_list", { quick_list_name: "ToDo" }, "q1"),
228
+ block("chart", { chart_name: "Signups" }, "ch1"),
229
+ block("number_card", { number_card_name: "Enabled Users" }, "n1"),
230
+ ]);
231
+ expect(composed.data.map((b) => b.id)).toEqual(["n1", "ch1", "q1"]);
232
+ expect(composed.shortcuts.map((b) => b.id)).toEqual(["s1"]);
233
+ expect(composed.cards.map((b) => b.id)).toEqual(["c1"]);
234
+ expect(composed.text).toEqual([]);
235
+ });
236
+
237
+ it("keeps authored order within each data class", () => {
238
+ const composed = composeWorkspaceBlocks([
239
+ block("chart", { chart_name: "B" }, "ch2"),
240
+ block("number_card", { number_card_name: "N1" }, "n1"),
241
+ block("chart", { chart_name: "A" }, "ch1"),
242
+ block("number_card", { number_card_name: "N2" }, "n2"),
243
+ ]);
244
+ expect(composed.data.map((b) => b.id)).toEqual(["n1", "n2", "ch2", "ch1"]);
245
+ });
246
+
247
+ it("drops headers that label widget runs, keeps text-only headers", () => {
248
+ const composed = composeWorkspaceBlocks([
249
+ // Welcome-Workspace shape: header + paragraph only → both kept.
250
+ block("header", { text: '<span class="h2">Hi,</span>' }, "keep"),
251
+ block("paragraph", { text: "You can create a workspace." }, "p1"),
252
+ // Section label directly over shortcuts → dropped.
253
+ block("header", { text: "<b>Your Shortcuts</b>" }, "drop1"),
254
+ block("shortcut", { shortcut_name: "User" }, "s1"),
255
+ // Label separated from its cards by a spacer → still dropped.
256
+ block("header", { text: "<b>Masters</b>" }, "drop2"),
257
+ block("spacer", {}, "sp"),
258
+ block("card", { card_name: "Users" }, "c1"),
259
+ ]);
260
+ expect(composed.text.map((b) => b.id)).toEqual(["keep", "p1"]);
261
+ });
262
+
263
+ it("keeps a trailing header with no following blocks", () => {
264
+ const composed = composeWorkspaceBlocks([
265
+ block("shortcut", { shortcut_name: "User" }, "s1"),
266
+ block("header", { text: "<b>Notes</b>" }, "h1"),
267
+ ]);
268
+ expect(composed.text.map((b) => b.id)).toEqual(["h1"]);
269
+ });
270
+
271
+ it("drops headers labeling onboarding/custom blocks (which never render)", () => {
272
+ const composed = composeWorkspaceBlocks([
273
+ block("header", { text: "<b>Getting Started</b>" }, "h1"),
274
+ block("onboarding", { onboarding_name: "Setup" }, "o1"),
275
+ ]);
276
+ expect(composed.text).toEqual([]);
277
+ expect(composed.data).toEqual([]);
278
+ });
279
+
280
+ it("drops spacers and unknown block types", () => {
281
+ const composed = composeWorkspaceBlocks([
282
+ block("spacer", {}, "sp1"),
283
+ block("custom_block", {}, "cb1"),
284
+ block("paragraph", { text: "kept" }, "p1"),
285
+ ]);
286
+ expect(composed.text.map((b) => b.id)).toEqual(["p1"]);
287
+ expect(composed.data).toEqual([]);
288
+ expect(composed.shortcuts).toEqual([]);
289
+ expect(composed.cards).toEqual([]);
290
+ });
291
+
292
+ it("returns all-empty sections for empty input", () => {
293
+ expect(composeWorkspaceBlocks([])).toEqual({
294
+ data: [],
295
+ shortcuts: [],
296
+ cards: [],
297
+ text: [],
298
+ });
299
+ });
300
+ });
301
+
302
+ describe("findShortcut / findLinkGroup", () => {
303
+ const doc: WorkspaceDoc = {
304
+ name: "Users",
305
+ doctype: "Workspace",
306
+ shortcuts: [
307
+ { name: "s1", type: "DocType", label: "User", link_to: "User" },
308
+ { name: "s2", type: "DocType", label: "Role", link_to: "Role" },
309
+ ],
310
+ };
311
+
312
+ it("finds shortcut rows by the block's shortcut_name (label match)", () => {
313
+ expect(findShortcut(doc, "Role")?.link_to).toBe("Role");
314
+ expect(findShortcut(doc, "Missing")).toBeUndefined();
315
+ expect(findShortcut(doc, undefined)).toBeUndefined();
316
+ });
317
+
318
+ it("finds link groups by the card block's card_name", () => {
319
+ const groups = groupLinks([
320
+ { name: "b1", type: "Card Break", label: "Masters" },
321
+ { name: "l1", type: "Link", label: "User" },
322
+ ]);
323
+ expect(findLinkGroup(groups, "Masters")?.links).toHaveLength(1);
324
+ expect(findLinkGroup(groups, "Nope")).toBeUndefined();
325
+ expect(findLinkGroup(groups, undefined)).toBeUndefined();
326
+ });
327
+ });
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Pure parsing helpers for the Workspace `content` block JSON and the
3
+ * JSON filter strings carried by shortcuts / quick lists / number cards.
4
+ * No DOM: HTML in header/paragraph blocks is stripped with a regex-based
5
+ * text extractor so this runs on native and gjs too.
6
+ */
7
+ import type {
8
+ FrappeFilterTuple,
9
+ WorkspaceBlock,
10
+ WorkspaceDoc,
11
+ WorkspaceLinkGroup,
12
+ WorkspaceLinkRow,
13
+ } from "./types";
14
+
15
+ /** Parse the workspace `content` JSON string into blocks (bad JSON → []). */
16
+ export function parseWorkspaceContent(content: string | undefined | null): WorkspaceBlock[] {
17
+ if (!content) return [];
18
+ try {
19
+ const parsed = JSON.parse(content);
20
+ if (!Array.isArray(parsed)) return [];
21
+ return parsed.filter(
22
+ (block): block is WorkspaceBlock =>
23
+ !!block && typeof block === "object" && typeof block.type === "string",
24
+ );
25
+ } catch {
26
+ return [];
27
+ }
28
+ }
29
+
30
+ const NAMED_ENTITIES: Record<string, string> = {
31
+ amp: "&",
32
+ lt: "<",
33
+ gt: ">",
34
+ quot: '"',
35
+ apos: "'",
36
+ nbsp: " ",
37
+ };
38
+
39
+ /**
40
+ * Extract plain text from desk's rich-text wrappers, e.g.
41
+ * `<span class="h4"><b>Reports &amp; Masters</b></span>` → "Reports & Masters".
42
+ */
43
+ export function stripHtml(html: string | undefined | null): string {
44
+ if (!html) return "";
45
+ const withoutTags = html.replace(/<br\s*\/?>/gi, "\n").replace(/<[^>]*>/g, "");
46
+ return withoutTags
47
+ .replace(/&(#[xX]?[0-9a-fA-F]+|[a-zA-Z]+);/g, (match, entity: string) => {
48
+ if (entity.startsWith("#x") || entity.startsWith("#X")) {
49
+ const code = Number.parseInt(entity.slice(2), 16);
50
+ return Number.isNaN(code) ? match : String.fromCodePoint(code);
51
+ }
52
+ if (entity.startsWith("#")) {
53
+ const code = Number.parseInt(entity.slice(1), 10);
54
+ return Number.isNaN(code) ? match : String.fromCodePoint(code);
55
+ }
56
+ return NAMED_ENTITIES[entity.toLowerCase()] ?? match;
57
+ })
58
+ .trim();
59
+ }
60
+
61
+ /**
62
+ * Heading level from a desk header block's HTML wrapper: `class="h4"` →
63
+ * 4. Desk also emits raw font-size styles (Build workspace uses
64
+ * `font-size: 18px`); those and unclassed headers fall back to 4 — the
65
+ * visual scale desk uses for workspace section headings.
66
+ */
67
+ export function headerLevel(html: string | undefined | null): 1 | 2 | 3 | 4 | 5 | 6 {
68
+ const match = (html ?? "").match(/class="[^"]*\bh([1-6])\b[^"]*"/);
69
+ if (match) return Number(match[1]) as 1 | 2 | 3 | 4 | 5 | 6;
70
+ return 4;
71
+ }
72
+
73
+ /**
74
+ * Group the flat `links` child table into cards: each "Card Break" row
75
+ * opens a group that collects the following "Link" rows (hidden rows are
76
+ * dropped). Link rows before any Card Break are ignored — desk does the
77
+ * same (they are unreachable from card blocks).
78
+ */
79
+ export function groupLinks(links: WorkspaceLinkRow[] | undefined): WorkspaceLinkGroup[] {
80
+ const groups: WorkspaceLinkGroup[] = [];
81
+ let current: WorkspaceLinkGroup | undefined;
82
+ for (const row of links ?? []) {
83
+ if (row.type === "Card Break") {
84
+ // A hidden Card Break hides its whole card: close the current group so
85
+ // its following Link rows are dropped, not leaked into the prior card.
86
+ if (row.hidden) {
87
+ current = undefined;
88
+ continue;
89
+ }
90
+ current = { label: row.label, breakRow: row, links: [] };
91
+ groups.push(current);
92
+ } else if (row.type === "Link" && current && !row.hidden) {
93
+ current.links.push(row);
94
+ }
95
+ }
96
+ return groups;
97
+ }
98
+
99
+ /**
100
+ * Normalize a frappe JSON filter string to `[field, op, value]` triples.
101
+ * Real-world shapes (all seen on the control bench):
102
+ * - dict: `{"status":"Open"}` → `[["status","=","Open"]]`
103
+ * - dict of tuples: `{"status":["=","Open"]}` → `[["status","=","Open"]]`
104
+ * - list of triples: `[["enabled","=",1]]` → as-is
105
+ * - list of quads (doctype-prefixed, Number Card `filters_json`):
106
+ * `[["User","enabled","=",1]]` → doctype element stripped.
107
+ */
108
+ export function parseFrappeFilters(
109
+ raw: string | undefined | null,
110
+ ): FrappeFilterTuple[] | undefined {
111
+ if (!raw) return undefined;
112
+ let parsed: unknown;
113
+ try {
114
+ parsed = JSON.parse(raw);
115
+ } catch {
116
+ return undefined;
117
+ }
118
+ if (Array.isArray(parsed)) {
119
+ const tuples: FrappeFilterTuple[] = [];
120
+ for (const entry of parsed) {
121
+ if (!Array.isArray(entry)) continue;
122
+ if (entry.length >= 4) {
123
+ // [doctype, field, op, value] — drop the doctype element.
124
+ tuples.push([String(entry[1]), String(entry[2]), entry[3]]);
125
+ } else if (entry.length === 3) {
126
+ tuples.push([String(entry[0]), String(entry[1]), entry[2]]);
127
+ }
128
+ }
129
+ return tuples.length > 0 ? tuples : undefined;
130
+ }
131
+ if (parsed && typeof parsed === "object") {
132
+ const tuples: FrappeFilterTuple[] = [];
133
+ for (const [field, value] of Object.entries(parsed as Record<string, unknown>)) {
134
+ if (Array.isArray(value) && value.length === 2 && typeof value[0] === "string") {
135
+ tuples.push([field, value[0], value[1]]);
136
+ } else {
137
+ tuples.push([field, "=", value]);
138
+ }
139
+ }
140
+ return tuples.length > 0 ? tuples : undefined;
141
+ }
142
+ return undefined;
143
+ }
144
+
145
+ // ---------------------------------------------------------------------------
146
+ // Dashboard-first composition (MOD-11)
147
+ // ---------------------------------------------------------------------------
148
+
149
+ /**
150
+ * Workspace blocks partitioned by class for the dashboard-first layout:
151
+ * data widgets render first, then shortcuts, then the link-card directory.
152
+ */
153
+ export interface WorkspaceComposition {
154
+ /**
155
+ * Live data widgets, ordered number cards → charts → quick lists
156
+ * (authored order within each class).
157
+ */
158
+ data: WorkspaceBlock[];
159
+ /** Shortcut blocks in authored order. */
160
+ shortcuts: WorkspaceBlock[];
161
+ /** Link-card blocks in authored order. */
162
+ cards: WorkspaceBlock[];
163
+ /**
164
+ * Narrative content in authored order: paragraphs, plus headers that do
165
+ * NOT label a widget run (see keep rule below). Section-label headers
166
+ * ("Your Shortcuts", "Reports & Masters") are dropped — the recomposed
167
+ * sections carry their own canonical headings.
168
+ */
169
+ text: WorkspaceBlock[];
170
+ }
171
+
172
+ const DATA_BLOCK_RANK: Record<string, number> = { number_card: 0, chart: 1, quick_list: 2 };
173
+
174
+ function isDataBlock(block: WorkspaceBlock): boolean {
175
+ return block.type in DATA_BLOCK_RANK;
176
+ }
177
+
178
+ /**
179
+ * Partition parsed content blocks for the dashboard-first workspace layout
180
+ * (data → shortcuts → link cards), instead of desk's authoring order.
181
+ *
182
+ * Header keep rule: a header labels the blocks that follow it up to the next
183
+ * header. If that run contains any widget block (shortcut, card, chart,
184
+ * number card, quick list, onboarding, custom block), the header is a section
185
+ * label and is dropped; if the run holds only paragraphs (or nothing — e.g.
186
+ * the text-only "Welcome Workspace"), the header is real content and is kept.
187
+ * Spacers are dropped — section spacing owns the rhythm.
188
+ */
189
+ export function composeWorkspaceBlocks(blocks: WorkspaceBlock[]): WorkspaceComposition {
190
+ const shortcuts: WorkspaceBlock[] = [];
191
+ const cards: WorkspaceBlock[] = [];
192
+ const text: WorkspaceBlock[] = [];
193
+ const dataBlocks: WorkspaceBlock[] = [];
194
+
195
+ for (const [index, block] of blocks.entries()) {
196
+ if (isDataBlock(block)) {
197
+ dataBlocks.push(block);
198
+ } else if (block.type === "shortcut") {
199
+ shortcuts.push(block);
200
+ } else if (block.type === "card") {
201
+ cards.push(block);
202
+ } else if (block.type === "paragraph") {
203
+ text.push(block);
204
+ } else if (block.type === "header") {
205
+ let labelsWidgets = false;
206
+ for (const following of blocks.slice(index + 1)) {
207
+ if (following.type === "header") break;
208
+ if (following.type === "paragraph" || following.type === "spacer") continue;
209
+ labelsWidgets = true;
210
+ break;
211
+ }
212
+ if (!labelsWidgets) text.push(block);
213
+ }
214
+ // spacer / onboarding / custom_block / unknown — dropped (see module doc).
215
+ }
216
+
217
+ // Stable class ordering: number cards → charts → quick lists.
218
+ const data = dataBlocks
219
+ .map((block, index) => ({ block, index }))
220
+ .sort(
221
+ (a, b) =>
222
+ (DATA_BLOCK_RANK[a.block.type] ?? 0) - (DATA_BLOCK_RANK[b.block.type] ?? 0) ||
223
+ a.index - b.index,
224
+ )
225
+ .map(({ block }) => block);
226
+
227
+ return { data, shortcuts, cards, text };
228
+ }
229
+
230
+ /** Find a workspace shortcut row by a shortcut block's `shortcut_name`. */
231
+ export function findShortcut(doc: WorkspaceDoc, shortcutName: string | undefined) {
232
+ if (!shortcutName) return undefined;
233
+ return doc.shortcuts?.find((row) => row.label === shortcutName);
234
+ }
235
+
236
+ /** Find a link group by a card block's `card_name`. */
237
+ export function findLinkGroup(groups: WorkspaceLinkGroup[], cardName: string | undefined) {
238
+ if (!cardName) return undefined;
239
+ return groups.find((group) => group.label === cardName);
240
+ }