@nvidia-elements/core 2.23.4 → 2.23.6

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 (358) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/accordion/accordion2.js +49 -49
  3. package/dist/accordion/accordion2.js.map +1 -1
  4. package/dist/alert/alert-group2.js +22 -21
  5. package/dist/alert/alert-group2.js.map +1 -1
  6. package/dist/alert/alert2.js +29 -28
  7. package/dist/alert/alert2.js.map +1 -1
  8. package/dist/avatar/avatar-group2.js +9 -9
  9. package/dist/avatar/avatar-group2.js.map +1 -1
  10. package/dist/avatar/avatar2.js +12 -11
  11. package/dist/avatar/avatar2.js.map +1 -1
  12. package/dist/badge/badge2.js +27 -26
  13. package/dist/badge/badge2.js.map +1 -1
  14. package/dist/breadcrumb/breadcrumb2.js +17 -17
  15. package/dist/breadcrumb/breadcrumb2.js.map +1 -1
  16. package/dist/bundles/index.js +10 -10
  17. package/dist/button/button2.js +12 -12
  18. package/dist/button/button2.js.map +1 -1
  19. package/dist/button-group/button-group2.js +18 -18
  20. package/dist/button-group/button-group2.js.map +1 -1
  21. package/dist/card/card2.js +28 -28
  22. package/dist/card/card2.js.map +1 -1
  23. package/dist/chat-message/chat-message2.js +15 -14
  24. package/dist/chat-message/chat-message2.js.map +1 -1
  25. package/dist/checkbox/checkbox-group2.js +8 -8
  26. package/dist/checkbox/checkbox-group2.js.map +1 -1
  27. package/dist/checkbox/checkbox2.js +8 -8
  28. package/dist/checkbox/checkbox2.js.map +1 -1
  29. package/dist/color/color2.js +21 -21
  30. package/dist/color/color2.js.map +1 -1
  31. package/dist/combobox/combobox2.js +37 -37
  32. package/dist/combobox/combobox2.js.map +1 -1
  33. package/dist/copy-button/copy-button.d.ts +1 -1
  34. package/dist/copy-button/copy-button2.js +21 -21
  35. package/dist/copy-button/copy-button2.js.map +1 -1
  36. package/dist/custom-elements.json +1032 -337
  37. package/dist/date/date2.js +19 -19
  38. package/dist/date/date2.js.map +1 -1
  39. package/dist/datetime/datetime2.js +19 -19
  40. package/dist/datetime/datetime2.js.map +1 -1
  41. package/dist/dialog/dialog-footer2.js +7 -7
  42. package/dist/dialog/dialog-footer2.js.map +1 -1
  43. package/dist/dialog/dialog-header2.js +7 -7
  44. package/dist/dialog/dialog-header2.js.map +1 -1
  45. package/dist/dialog/dialog2.js +25 -25
  46. package/dist/dialog/dialog2.js.map +1 -1
  47. package/dist/divider/divider2.js +12 -12
  48. package/dist/divider/divider2.js.map +1 -1
  49. package/dist/dot/dot2.js +17 -16
  50. package/dist/dot/dot2.js.map +1 -1
  51. package/dist/drag-handle/drag-handle2.js +12 -12
  52. package/dist/drag-handle/drag-handle2.js.map +1 -1
  53. package/dist/drawer/drawer-content2.js +7 -7
  54. package/dist/drawer/drawer-content2.js.map +1 -1
  55. package/dist/drawer/drawer-footer2.js +7 -7
  56. package/dist/drawer/drawer-footer2.js.map +1 -1
  57. package/dist/drawer/drawer-header2.js +7 -7
  58. package/dist/drawer/drawer-header2.js.map +1 -1
  59. package/dist/drawer/drawer2.js +21 -21
  60. package/dist/drawer/drawer2.js.map +1 -1
  61. package/dist/dropdown/dropdown-footer2.js +7 -7
  62. package/dist/dropdown/dropdown-footer2.js.map +1 -1
  63. package/dist/dropdown/dropdown-header2.js +7 -7
  64. package/dist/dropdown/dropdown-header2.js.map +1 -1
  65. package/dist/dropdown/dropdown2.js +21 -21
  66. package/dist/dropdown/dropdown2.js.map +1 -1
  67. package/dist/dropdown-group/dropdown-group.js +10 -10
  68. package/dist/dropdown-group/dropdown-group.js.map +1 -1
  69. package/dist/dropzone/dropzone2.js +19 -19
  70. package/dist/dropzone/dropzone2.js.map +1 -1
  71. package/dist/file/file2.js +8 -8
  72. package/dist/file/file2.js.map +1 -1
  73. package/dist/format-bytes/format-bytes2.js +13 -13
  74. package/dist/format-bytes/format-bytes2.js.map +1 -1
  75. package/dist/format-datetime/format-datetime2.js +14 -14
  76. package/dist/format-datetime/format-datetime2.js.map +1 -1
  77. package/dist/format-number/format-number2.js +20 -20
  78. package/dist/format-number/format-number2.js.map +1 -1
  79. package/dist/format-relative-time/format-relative-time2.js +11 -11
  80. package/dist/format-relative-time/format-relative-time2.js.map +1 -1
  81. package/dist/format-truncate/format-truncate2.js +21 -21
  82. package/dist/format-truncate/format-truncate2.js.map +1 -1
  83. package/dist/forms/control/control2.js +30 -29
  84. package/dist/forms/control/control2.js.map +1 -1
  85. package/dist/forms/control-group/control-group2.js +19 -18
  86. package/dist/forms/control-group/control-group2.js.map +1 -1
  87. package/dist/forms/control-message/control-message2.js +15 -15
  88. package/dist/forms/control-message/control-message2.js.map +1 -1
  89. package/dist/gauge/gauge2.js +20 -20
  90. package/dist/gauge/gauge2.js.map +1 -1
  91. package/dist/ghost/ghost2.js +10 -10
  92. package/dist/ghost/ghost2.js.map +1 -1
  93. package/dist/grid/cell/cell2.js +9 -9
  94. package/dist/grid/cell/cell2.js.map +1 -1
  95. package/dist/grid/column/column2.js +16 -16
  96. package/dist/grid/column/column2.js.map +1 -1
  97. package/dist/grid/footer/footer2.js +12 -12
  98. package/dist/grid/footer/footer2.js.map +1 -1
  99. package/dist/grid/grid2.js +30 -30
  100. package/dist/grid/grid2.js.map +1 -1
  101. package/dist/grid/header/header2.js +20 -20
  102. package/dist/grid/header/header2.js.map +1 -1
  103. package/dist/grid/placeholder/placeholder2.js +9 -9
  104. package/dist/grid/placeholder/placeholder2.js.map +1 -1
  105. package/dist/grid/row/row2.js +15 -15
  106. package/dist/grid/row/row2.js.map +1 -1
  107. package/dist/icon/icon2.js +40 -40
  108. package/dist/icon/icon2.js.map +1 -1
  109. package/dist/icon-button/icon-button2.js +17 -17
  110. package/dist/icon-button/icon-button2.js.map +1 -1
  111. package/dist/iframe/iframe2.js +7 -7
  112. package/dist/iframe/iframe2.js.map +1 -1
  113. package/dist/index.js +1 -1
  114. package/dist/input/input-group2.js +8 -8
  115. package/dist/input/input-group2.js.map +1 -1
  116. package/dist/input/input2.js +10 -10
  117. package/dist/input/input2.js.map +1 -1
  118. package/dist/internal/controllers/state-expanded.controller.js +1 -1
  119. package/dist/internal/controllers/state-expanded.controller.js.map +1 -1
  120. package/dist/internal/controllers/state-highlighted.controller.js +1 -1
  121. package/dist/internal/controllers/state-highlighted.controller.js.map +1 -1
  122. package/dist/internal/controllers/state-scroll.controller.js +1 -1
  123. package/dist/internal/controllers/state-scroll.controller.js.map +1 -1
  124. package/dist/internal/controllers/state-selected.controller.js +1 -1
  125. package/dist/internal/controllers/state-selected.controller.js.map +1 -1
  126. package/dist/internal/controllers/type-anchor.controller.js +1 -1
  127. package/dist/internal/controllers/type-anchor.controller.js.map +1 -1
  128. package/dist/internal/controllers/type-closable.controller.js +1 -1
  129. package/dist/internal/controllers/type-closable.controller.js.map +1 -1
  130. package/dist/internal/controllers/type-expandable.controller.js +1 -1
  131. package/dist/internal/controllers/type-expandable.controller.js.map +1 -1
  132. package/dist/internal/controllers/type-native-popover.controller.js +6 -6
  133. package/dist/internal/controllers/type-native-popover.controller.js.map +1 -1
  134. package/dist/internal/controllers/type-popover.controller.examples.js.map +1 -1
  135. package/dist/internal/controllers/type-selectable.controller.js +1 -1
  136. package/dist/internal/controllers/type-selectable.controller.js.map +1 -1
  137. package/dist/internal/index.d.ts +0 -1
  138. package/dist/internal/index.js +57 -61
  139. package/dist/internal/index.js.map +1 -1
  140. package/dist/internal/styles/index.d.ts +0 -1
  141. package/dist/internal/styles/index.js +5 -15
  142. package/dist/internal/styles/index.js.map +1 -1
  143. package/dist/internal/utils/a11y.d.ts +0 -3
  144. package/dist/internal/utils/a11y.js +6 -9
  145. package/dist/internal/utils/a11y.js.map +1 -1
  146. package/dist/joystick/joystick2.js +19 -19
  147. package/dist/joystick/joystick2.js.map +1 -1
  148. package/dist/logo/logo2.js +14 -13
  149. package/dist/logo/logo2.js.map +1 -1
  150. package/dist/menu/menu-group2.js +20 -20
  151. package/dist/menu/menu-group2.js.map +1 -1
  152. package/dist/menu/menu-item2.js +11 -11
  153. package/dist/menu/menu-item2.js.map +1 -1
  154. package/dist/menu/menu2.js +14 -14
  155. package/dist/menu/menu2.js.map +1 -1
  156. package/dist/month/month2.js +19 -19
  157. package/dist/month/month2.js.map +1 -1
  158. package/dist/notification/notification-group2.js +13 -13
  159. package/dist/notification/notification-group2.js.map +1 -1
  160. package/dist/notification/notification2.js +31 -31
  161. package/dist/notification/notification2.js.map +1 -1
  162. package/dist/page/page-panel/page-panel-content2.js +7 -7
  163. package/dist/page/page-panel/page-panel-content2.js.map +1 -1
  164. package/dist/page/page-panel/page-panel-footer2.js +9 -9
  165. package/dist/page/page-panel/page-panel-footer2.js.map +1 -1
  166. package/dist/page/page-panel/page-panel-header2.js +9 -9
  167. package/dist/page/page-panel/page-panel-header2.js.map +1 -1
  168. package/dist/page/page-panel/page-panel2.js +15 -15
  169. package/dist/page/page-panel/page-panel2.js.map +1 -1
  170. package/dist/page/page2.js +13 -13
  171. package/dist/page/page2.js.map +1 -1
  172. package/dist/page-header/page-header2.js +10 -10
  173. package/dist/page-header/page-header2.js.map +1 -1
  174. package/dist/page-loader/page-loader2.js +13 -13
  175. package/dist/page-loader/page-loader2.js.map +1 -1
  176. package/dist/pagination/pagination2.js +34 -34
  177. package/dist/pagination/pagination2.js.map +1 -1
  178. package/dist/panel/panel2.js +36 -36
  179. package/dist/panel/panel2.js.map +1 -1
  180. package/dist/password/password2.js +19 -19
  181. package/dist/password/password2.js.map +1 -1
  182. package/dist/preferences-input/preferences-input.d.ts +1 -1
  183. package/dist/preferences-input/preferences-input2.js +25 -25
  184. package/dist/preferences-input/preferences-input2.js.map +1 -1
  185. package/dist/progress-bar/progress-bar2.js +13 -13
  186. package/dist/progress-bar/progress-bar2.js.map +1 -1
  187. package/dist/progress-dot/progress-dot2.js +15 -15
  188. package/dist/progress-dot/progress-dot2.js.map +1 -1
  189. package/dist/progress-ring/progress-ring2.js +20 -20
  190. package/dist/progress-ring/progress-ring2.js.map +1 -1
  191. package/dist/progressive-filter-chip/progressive-filter-chip2.js +28 -28
  192. package/dist/progressive-filter-chip/progressive-filter-chip2.js.map +1 -1
  193. package/dist/pulse/pulse2.js +15 -15
  194. package/dist/pulse/pulse2.js.map +1 -1
  195. package/dist/radio/radio-group2.js +8 -8
  196. package/dist/radio/radio-group2.js.map +1 -1
  197. package/dist/radio/radio2.js +8 -8
  198. package/dist/radio/radio2.js.map +1 -1
  199. package/dist/range/range2.js +16 -16
  200. package/dist/range/range2.js.map +1 -1
  201. package/dist/resize-handle/resize-handle2.js +17 -17
  202. package/dist/resize-handle/resize-handle2.js.map +1 -1
  203. package/dist/search/search2.js +20 -20
  204. package/dist/search/search2.js.map +1 -1
  205. package/dist/select/select2.js +32 -32
  206. package/dist/select/select2.js.map +1 -1
  207. package/dist/skeleton/skeleton2.js +14 -14
  208. package/dist/skeleton/skeleton2.js.map +1 -1
  209. package/dist/sort-button/sort-button.d.ts +1 -1
  210. package/dist/sort-button/sort-button2.js +16 -16
  211. package/dist/sort-button/sort-button2.js.map +1 -1
  212. package/dist/sparkline/sparkline2.js +45 -44
  213. package/dist/sparkline/sparkline2.js.map +1 -1
  214. package/dist/star-rating/star-rating2.js +14 -14
  215. package/dist/star-rating/star-rating2.js.map +1 -1
  216. package/dist/steps/steps2.js +32 -32
  217. package/dist/steps/steps2.js.map +1 -1
  218. package/dist/switch/switch-group2.js +8 -8
  219. package/dist/switch/switch-group2.js.map +1 -1
  220. package/dist/switch/switch2.js +8 -8
  221. package/dist/switch/switch2.js.map +1 -1
  222. package/dist/tabs/tabs-group2.js +18 -18
  223. package/dist/tabs/tabs-group2.js.map +1 -1
  224. package/dist/tabs/tabs-item-group2.js +9 -9
  225. package/dist/tabs/tabs-item-group2.js.map +1 -1
  226. package/dist/tabs/tabs2.js +28 -28
  227. package/dist/tabs/tabs2.js.map +1 -1
  228. package/dist/tag/tag2.js +19 -18
  229. package/dist/tag/tag2.js.map +1 -1
  230. package/dist/textarea/textarea2.js +9 -9
  231. package/dist/textarea/textarea2.js.map +1 -1
  232. package/dist/time/time2.js +19 -19
  233. package/dist/time/time2.js.map +1 -1
  234. package/dist/toast/toast2.js +28 -28
  235. package/dist/toast/toast2.js.map +1 -1
  236. package/dist/toggletip/toggletip-footer2.js +7 -7
  237. package/dist/toggletip/toggletip-footer2.js.map +1 -1
  238. package/dist/toggletip/toggletip-header2.js +7 -7
  239. package/dist/toggletip/toggletip-header2.js.map +1 -1
  240. package/dist/toggletip/toggletip2.js +23 -23
  241. package/dist/toggletip/toggletip2.js.map +1 -1
  242. package/dist/toolbar/toolbar2.js +20 -19
  243. package/dist/toolbar/toolbar2.js.map +1 -1
  244. package/dist/tooltip/tooltip2.js +20 -20
  245. package/dist/tooltip/tooltip2.js.map +1 -1
  246. package/dist/tree/tree-node2.js +39 -39
  247. package/dist/tree/tree-node2.js.map +1 -1
  248. package/dist/tree/tree2.js +20 -20
  249. package/dist/tree/tree2.js.map +1 -1
  250. package/dist/week/week2.js +19 -19
  251. package/dist/week/week2.js.map +1 -1
  252. package/dist/workspace/controllers/workspace-composition.controller.d.ts +4 -1
  253. package/dist/workspace/controllers/workspace-composition.controller.js +69 -39
  254. package/dist/workspace/controllers/workspace-composition.controller.js.map +1 -1
  255. package/dist/workspace/controllers/workspace-rearrange.controller.js +27 -27
  256. package/dist/workspace/controllers/workspace-rearrange.controller.js.map +1 -1
  257. package/dist/workspace/controllers/workspace-restoration.controller.js +2 -2
  258. package/dist/workspace/controllers/workspace-restoration.controller.js.map +1 -1
  259. package/dist/workspace/panel/panel2.js +15 -15
  260. package/dist/workspace/panel/panel2.js.map +1 -1
  261. package/dist/workspace/panel-content/panel-content2.js +7 -7
  262. package/dist/workspace/panel-content/panel-content2.js.map +1 -1
  263. package/dist/workspace/panel-footer/panel-footer2.js +9 -9
  264. package/dist/workspace/panel-footer/panel-footer2.js.map +1 -1
  265. package/dist/workspace/panel-header/panel-header2.js +16 -16
  266. package/dist/workspace/panel-header/panel-header2.js.map +1 -1
  267. package/dist/workspace/utils/comparison.utils.d.ts +3 -0
  268. package/dist/workspace/utils/comparison.utils.js +13 -0
  269. package/dist/workspace/utils/comparison.utils.js.map +1 -0
  270. package/dist/workspace/utils/evaluation.utils.d.ts +65 -6
  271. package/dist/workspace/utils/evaluation.utils.js +334 -99
  272. package/dist/workspace/utils/evaluation.utils.js.map +1 -1
  273. package/dist/workspace/utils/geometry/allocation.utils.js +15 -13
  274. package/dist/workspace/utils/geometry/allocation.utils.js.map +1 -1
  275. package/dist/workspace/utils/geometry/sizing.utils.d.ts +2 -0
  276. package/dist/workspace/utils/geometry/sizing.utils.js +37 -17
  277. package/dist/workspace/utils/geometry/sizing.utils.js.map +1 -1
  278. package/dist/workspace/utils/interaction/boundary.utils.d.ts +2 -2
  279. package/dist/workspace/utils/interaction/boundary.utils.js +2 -2
  280. package/dist/workspace/utils/interaction/boundary.utils.js.map +1 -1
  281. package/dist/workspace/utils/interaction/drag-preview.utils.d.ts +3 -3
  282. package/dist/workspace/utils/interaction/drag-preview.utils.js.map +1 -1
  283. package/dist/workspace/utils/interaction/insertion-offers.utils.d.ts +11 -9
  284. package/dist/workspace/utils/interaction/insertion-offers.utils.js +37 -43
  285. package/dist/workspace/utils/interaction/insertion-offers.utils.js.map +1 -1
  286. package/dist/workspace/utils/interaction/placement-decision.utils.d.ts +6 -1
  287. package/dist/workspace/utils/interaction/placement-decision.utils.js +94 -87
  288. package/dist/workspace/utils/interaction/placement-decision.utils.js.map +1 -1
  289. package/dist/workspace/utils/model/layout.utils.d.ts +2 -1
  290. package/dist/workspace/utils/model/layout.utils.js +12 -10
  291. package/dist/workspace/utils/model/layout.utils.js.map +1 -1
  292. package/dist/workspace/utils/model/membership.utils.d.ts +4 -0
  293. package/dist/workspace/utils/model/membership.utils.js +13 -4
  294. package/dist/workspace/utils/model/membership.utils.js.map +1 -1
  295. package/dist/workspace/utils/model/operations.utils.d.ts +1 -0
  296. package/dist/workspace/utils/model/operations.utils.js +51 -46
  297. package/dist/workspace/utils/model/operations.utils.js.map +1 -1
  298. package/dist/workspace/utils/model/projection.utils.d.ts +4 -2
  299. package/dist/workspace/utils/model/projection.utils.js +49 -40
  300. package/dist/workspace/utils/model/projection.utils.js.map +1 -1
  301. package/dist/workspace/utils/model/value.utils.d.ts +4 -3
  302. package/dist/workspace/utils/model/value.utils.js +15 -50
  303. package/dist/workspace/utils/model/value.utils.js.map +1 -1
  304. package/dist/workspace/utils/placement/acquisition-sizing.utils.d.ts +2 -0
  305. package/dist/workspace/utils/placement/acquisition-sizing.utils.js +30 -22
  306. package/dist/workspace/utils/placement/acquisition-sizing.utils.js.map +1 -1
  307. package/dist/workspace/utils/placement/group-insertion.utils.d.ts +19 -13
  308. package/dist/workspace/utils/placement/group-insertion.utils.js +50 -47
  309. package/dist/workspace/utils/placement/group-insertion.utils.js.map +1 -1
  310. package/dist/workspace/utils/placement/planner.utils.d.ts +13 -2
  311. package/dist/workspace/utils/placement/planner.utils.js +79 -73
  312. package/dist/workspace/utils/placement/planner.utils.js.map +1 -1
  313. package/dist/workspace/utils/placement/preparation.utils.d.ts +5 -2
  314. package/dist/workspace/utils/placement/preparation.utils.js +45 -41
  315. package/dist/workspace/utils/placement/preparation.utils.js.map +1 -1
  316. package/dist/workspace/utils/placement/region.utils.js +32 -32
  317. package/dist/workspace/utils/placement/region.utils.js.map +1 -1
  318. package/dist/workspace/utils/placement/scope.utils.d.ts +34 -0
  319. package/dist/workspace/utils/placement/scope.utils.js +155 -0
  320. package/dist/workspace/utils/placement/scope.utils.js.map +1 -0
  321. package/dist/workspace/utils/placement/session.utils.d.ts +11 -0
  322. package/dist/workspace/utils/placement/session.utils.js +78 -0
  323. package/dist/workspace/utils/placement/session.utils.js.map +1 -0
  324. package/dist/workspace/utils/placement/split-sizing.utils.d.ts +2 -2
  325. package/dist/workspace/utils/placement/split-sizing.utils.js +19 -19
  326. package/dist/workspace/utils/placement/split-sizing.utils.js.map +1 -1
  327. package/dist/workspace/utils/presentation/composition.utils.d.ts +1 -1
  328. package/dist/workspace/utils/presentation/composition.utils.js +4 -2
  329. package/dist/workspace/utils/presentation/composition.utils.js.map +1 -1
  330. package/dist/workspace/utils/presentation/groups.utils.d.ts +3 -3
  331. package/dist/workspace/utils/presentation/groups.utils.js +40 -30
  332. package/dist/workspace/utils/presentation/groups.utils.js.map +1 -1
  333. package/dist/workspace/utils/presentation/measurements.utils.d.ts +1 -0
  334. package/dist/workspace/utils/presentation/measurements.utils.js +6 -14
  335. package/dist/workspace/utils/presentation/measurements.utils.js.map +1 -1
  336. package/dist/workspace/utils/presentation/offers.utils.d.ts +1 -1
  337. package/dist/workspace/utils/presentation/offers.utils.js +3 -3
  338. package/dist/workspace/utils/presentation/offers.utils.js.map +1 -1
  339. package/dist/workspace/utils/presentation/preview.utils.d.ts +6 -0
  340. package/dist/workspace/utils/presentation/preview.utils.js +14 -3
  341. package/dist/workspace/utils/presentation/preview.utils.js.map +1 -1
  342. package/dist/workspace/workspace.examples.js.map +1 -1
  343. package/dist/workspace/workspace.examples.json +4 -4
  344. package/dist/workspace/workspace.js +1 -1
  345. package/dist/workspace/workspace.js.map +1 -1
  346. package/dist/workspace/workspace2.js +233 -239
  347. package/dist/workspace/workspace2.js.map +1 -1
  348. package/package.json +2 -2
  349. package/dist/internal/decorators/scoped-registry.d.ts +0 -2
  350. package/dist/internal/decorators/scoped-registry.js +0 -36
  351. package/dist/internal/decorators/scoped-registry.js.map +0 -1
  352. package/dist/internal/styles/base.js +0 -5
  353. package/dist/internal/styles/base.js.map +0 -1
  354. package/dist/internal/styles/interaction-state.js +0 -5
  355. package/dist/internal/styles/interaction-state.js.map +0 -1
  356. package/dist/workspace/utils/placement/context.utils.d.ts +0 -36
  357. package/dist/workspace/utils/placement/context.utils.js +0 -135
  358. package/dist/workspace/utils/placement/context.utils.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"split-sizing.utils.js","names":["sizePreferredSplit","input","preference","plan","visible","projectLayout","available","branch","sourceBranch","source","sizing","splitSizingInput","sizes","preferredSizes","layout","rebaseProjectedSplit","axis","extent","getAllocatedContextRect","getPanelIds","id","entries","splitEntries","recipientPreference","recipients","rect","members","child","moving","donor","mixed","getMinimumWorkspaceDimensions","entry","preferred","budget","fixed","sum","donors","maximum","incoming","minimumPressureSizes","fitSizes","index","allocatedSize","desired","pressureSize","value","node","found"],"sources":["../../../../src/workspace/utils/placement/split-sizing.utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { fitSizes } from '../geometry/partition.utils.js';\nimport { getMinimumWorkspaceDimensions } from '../geometry/sizing.utils.js';\nimport { getPanelIds } from '../model/layout.utils.js';\nimport { projectLayout } from '../model/operations.utils.js';\nimport { rebaseProjectedSplit } from '../model/projection.utils.js';\nimport { getAllocatedContextRect } from './context.utils.js';\n\nimport type { WorkspacePlacedRegion } from '../geometry/allocation.utils.js';\nimport type { WorkspaceRequirements } from '../geometry/sizing.utils.js';\nimport type { WorkspaceNode, WorkspaceSplit } from '../model/layout.utils.js';\nimport type { WorkspaceLayoutProjector } from '../model/projection.utils.js';\nimport type { WorkspaceContextRectResolver } from './context.utils.js';\nimport type { WorkspacePlacementPlan } from './planner.utils.js';\n\nexport interface WorkspaceSplitPreference {\n allocation: WorkspacePlacedRegion[];\n requirements: WorkspaceRequirements;\n contextRect?: WorkspaceContextRectResolver;\n}\n\n/** Fits placements to the final normalized branch budget, including every separator. */\nexport function sizePreferredSplit(input: {\n plan: WorkspacePlacementPlan;\n preference?: WorkspaceSplitPreference;\n source: string[];\n available: string[];\n projectLayout?: WorkspaceLayoutProjector;\n}): WorkspacePlacementPlan {\n const { plan, preference, source, available } = input;\n if (!preference || !plan.context || plan.kind !== 'split') return plan;\n const visible = (input.projectLayout ?? projectLayout)(plan.layout, available).root;\n const branch = sourceBranch(visible, source);\n if (!branch) return plan;\n const sizing = splitSizingInput({ branch, preference, source, recipients: plan.context.members });\n if (!sizing) return plan;\n const sizes = preferredSizes(sizing);\n if (!sizes) return plan;\n const layout = rebaseProjectedSplit(plan.layout, branch, sizes, available, input.projectLayout);\n return layout ? { ...plan, layout } : plan;\n}\n\nfunction splitSizingInput(input: {\n branch: WorkspaceSplit;\n preference: WorkspaceSplitPreference;\n source: string[];\n recipients: string[];\n}) {\n const { branch, preference, source, recipients } = input;\n const axis = branch.axis === 'horizontal' ? 'width' : 'height';\n const extent = (preference.contextRect ?? getAllocatedContextRect)({\n members: getPanelIds(branch).filter(id => !source.includes(id)),\n allocation: preference.allocation,\n source\n });\n if (!extent) return undefined;\n const entries = splitEntries({ branch, preference, source, recipients });\n if (!entries) return undefined;\n return {\n entries,\n preferred: recipientPreference({ members: recipients, preference, source, axis }),\n budget: extent[axis] - preference.requirements.gap * (entries.length - 1)\n };\n}\n\nfunction recipientPreference(input: {\n members: string[];\n preference: WorkspaceSplitPreference;\n source: string[];\n axis: 'width' | 'height';\n}) {\n const rect = (input.preference.contextRect ?? getAllocatedContextRect)({\n members: input.members,\n allocation: input.preference.allocation,\n source: input.source\n });\n return rect ? Math.max(0, (rect[input.axis] - input.preference.requirements.gap) / 2) : Number.NaN;\n}\n\nfunction splitEntries(input: {\n branch: WorkspaceSplit;\n preference: WorkspaceSplitPreference;\n source: string[];\n recipients: string[];\n}) {\n const { branch, preference, source, recipients } = input;\n const axis = branch.axis === 'horizontal' ? 'width' : 'height';\n const entries = branch.children.map(child => {\n const members = getPanelIds(child);\n const moving = members.every(id => source.includes(id));\n const donor = members.every(id => recipients.includes(id));\n const mixed = !donor && members.some(id => recipients.includes(id));\n const rect = moving\n ? undefined\n : (preference.contextRect ?? getAllocatedContextRect)({ members, allocation: preference.allocation, source });\n return {\n moving,\n donor,\n minimum: getMinimumWorkspaceDimensions(child, preference.requirements)[axis],\n extent: rect?.[axis],\n mixed\n };\n });\n return entries.some(entry => entry.mixed || (!entry.moving && entry.extent === undefined)) ? undefined : entries;\n}\n\nfunction preferredSizes(input: {\n entries: NonNullable<ReturnType<typeof splitEntries>>;\n preferred: number;\n budget: number;\n}) {\n const { entries, preferred, budget } = input;\n const moving = entries.find(entry => entry.moving);\n if (!moving || !Number.isFinite(preferred) || budget <= 0) return undefined;\n const fixed = entries.reduce((sum, entry) => sum + (!entry.moving && !entry.donor ? entry.extent! : 0), 0);\n const donors = entries.filter(entry => entry.donor && !entry.moving);\n const maximum = budget - fixed - donors.reduce((sum, entry) => sum + entry.minimum, 0);\n if (!donors.length) return undefined;\n const incoming = Math.max(moving.minimum, Math.min(preferred, maximum));\n if (maximum < moving.minimum) return minimumPressureSizes(entries, incoming, budget);\n const sizes = fitSizes(\n donors.map(entry => entry.extent!),\n donors.map(entry => entry.minimum),\n budget - fixed - incoming\n );\n let index = 0;\n\n function allocatedSize(entry: (typeof entries)[number]) {\n if (entry.moving) return incoming;\n if (entry.donor) return sizes[index++]!;\n return entry.extent!;\n }\n\n return entries.map(allocatedSize);\n}\n\n/** Reaches beyond the donor only for the unavoidable minimum shortfall. */\nfunction minimumPressureSizes(entries: NonNullable<ReturnType<typeof splitEntries>>, incoming: number, budget: number) {\n const desired = entries.map(entry => pressureSize(entry, incoming));\n if (desired.some(value => value <= 0)) return undefined;\n return fitSizes(\n desired,\n entries.map(entry => entry.minimum),\n budget\n );\n}\n\nfunction pressureSize(entry: NonNullable<ReturnType<typeof splitEntries>>[number], incoming: number) {\n if (entry.moving) return incoming;\n if (entry.donor) return entry.minimum;\n return entry.extent!;\n}\n\nfunction sourceBranch(node: WorkspaceNode | null, source: string[]): WorkspaceSplit | undefined {\n if (!node || node.type === 'group') return undefined;\n if (node.children.some(child => child.type === 'group' && child.panels.some(id => source.includes(id)))) return node;\n for (const child of node.children) {\n const found = sourceBranch(child, source);\n if (found) return found;\n }\n return undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"split-sizing.utils.js","names":["sizePreferredSplit","input","preference","plan","visible","projectLayout","available","branch","sourceBranch","source","sizing","splitSizingInput","sizes","preferredSizes","layout","rebaseProjectedSplit","axis","extent","getAllocatedScopeRect","getPanelIds","id","entries","splitEntries","recipientPreference","recipients","rect","members","child","moving","donor","mixed","getMinimumWorkspaceDimensions","entry","preferred","budget","fixed","sum","donors","maximum","incoming","minimumPressureSizes","fitSizes","index","allocatedSize","desired","pressureSize","value","node","found"],"sources":["../../../../src/workspace/utils/placement/split-sizing.utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { fitSizes } from '../geometry/partition.utils.js';\nimport { getMinimumWorkspaceDimensions } from '../geometry/sizing.utils.js';\nimport { getPanelIds } from '../model/layout.utils.js';\nimport { projectLayout } from '../model/operations.utils.js';\nimport { rebaseProjectedSplit } from '../model/projection.utils.js';\nimport { getAllocatedScopeRect } from './scope.utils.js';\n\nimport type { WorkspacePlacedRegion } from '../geometry/allocation.utils.js';\nimport type { WorkspaceRequirements } from '../geometry/sizing.utils.js';\nimport type { WorkspaceNode, WorkspaceSplit } from '../model/layout.utils.js';\nimport type { WorkspaceLayoutProjector } from '../model/projection.utils.js';\nimport type { WorkspacePlacementPlan } from './planner.utils.js';\nimport type { WorkspaceScopeRectResolver } from './scope.utils.js';\n\nexport interface WorkspaceSplitPreference {\n allocation: WorkspacePlacedRegion[];\n requirements: WorkspaceRequirements;\n resolveScopeRect?: WorkspaceScopeRectResolver;\n}\n\n/** Fits placements to the final normalized branch budget, including every separator. */\nexport function sizePreferredSplit(input: {\n plan: WorkspacePlacementPlan;\n preference?: WorkspaceSplitPreference;\n source: string[];\n available: string[];\n projectLayout?: WorkspaceLayoutProjector;\n}): WorkspacePlacementPlan {\n const { plan, preference, source, available } = input;\n if (!preference || !plan.scope || plan.kind !== 'split') return plan;\n const visible = (input.projectLayout ?? projectLayout)(plan.layout, available).root;\n const branch = sourceBranch(visible, source);\n if (!branch) return plan;\n const sizing = splitSizingInput({ branch, preference, source, recipients: plan.scope.members });\n if (!sizing) return plan;\n const sizes = preferredSizes(sizing);\n if (!sizes) return plan;\n const layout = rebaseProjectedSplit(plan.layout, branch, sizes, available, input.projectLayout);\n return layout ? { ...plan, layout } : plan;\n}\n\nfunction splitSizingInput(input: {\n branch: WorkspaceSplit;\n preference: WorkspaceSplitPreference;\n source: string[];\n recipients: string[];\n}) {\n const { branch, preference, source, recipients } = input;\n const axis = branch.axis === 'horizontal' ? 'width' : 'height';\n const extent = (preference.resolveScopeRect ?? getAllocatedScopeRect)({\n members: getPanelIds(branch).filter(id => !source.includes(id)),\n allocation: preference.allocation,\n source\n });\n if (!extent) return undefined;\n const entries = splitEntries({ branch, preference, source, recipients });\n if (!entries) return undefined;\n return {\n entries,\n preferred: recipientPreference({ members: recipients, preference, source, axis }),\n budget: extent[axis] - preference.requirements.gap * (entries.length - 1)\n };\n}\n\nfunction recipientPreference(input: {\n members: string[];\n preference: WorkspaceSplitPreference;\n source: string[];\n axis: 'width' | 'height';\n}) {\n const rect = (input.preference.resolveScopeRect ?? getAllocatedScopeRect)({\n members: input.members,\n allocation: input.preference.allocation,\n source: input.source\n });\n return rect ? Math.max(0, (rect[input.axis] - input.preference.requirements.gap) / 2) : Number.NaN;\n}\n\nfunction splitEntries(input: {\n branch: WorkspaceSplit;\n preference: WorkspaceSplitPreference;\n source: string[];\n recipients: string[];\n}) {\n const { branch, preference, source, recipients } = input;\n const axis = branch.axis === 'horizontal' ? 'width' : 'height';\n const entries = branch.children.map(child => {\n const members = getPanelIds(child);\n const moving = members.every(id => source.includes(id));\n const donor = members.every(id => recipients.includes(id));\n const mixed = !donor && members.some(id => recipients.includes(id));\n const rect = moving\n ? undefined\n : (preference.resolveScopeRect ?? getAllocatedScopeRect)({ members, allocation: preference.allocation, source });\n return {\n moving,\n donor,\n minimum: getMinimumWorkspaceDimensions(child, preference.requirements)[axis],\n extent: rect?.[axis],\n mixed\n };\n });\n return entries.some(entry => entry.mixed || (!entry.moving && entry.extent === undefined)) ? undefined : entries;\n}\n\nfunction preferredSizes(input: {\n entries: NonNullable<ReturnType<typeof splitEntries>>;\n preferred: number;\n budget: number;\n}) {\n const { entries, preferred, budget } = input;\n const moving = entries.find(entry => entry.moving);\n if (!moving || !Number.isFinite(preferred) || budget <= 0) return undefined;\n const fixed = entries.reduce((sum, entry) => sum + (!entry.moving && !entry.donor ? entry.extent! : 0), 0);\n const donors = entries.filter(entry => entry.donor && !entry.moving);\n const maximum = budget - fixed - donors.reduce((sum, entry) => sum + entry.minimum, 0);\n if (!donors.length) return undefined;\n const incoming = Math.max(moving.minimum, Math.min(preferred, maximum));\n if (maximum < moving.minimum) return minimumPressureSizes(entries, incoming, budget);\n const sizes = fitSizes(\n donors.map(entry => entry.extent!),\n donors.map(entry => entry.minimum),\n budget - fixed - incoming\n );\n let index = 0;\n\n function allocatedSize(entry: (typeof entries)[number]) {\n if (entry.moving) return incoming;\n if (entry.donor) return sizes[index++]!;\n return entry.extent!;\n }\n\n return entries.map(allocatedSize);\n}\n\n/** Reaches beyond the donor only for the unavoidable minimum shortfall. */\nfunction minimumPressureSizes(entries: NonNullable<ReturnType<typeof splitEntries>>, incoming: number, budget: number) {\n const desired = entries.map(entry => pressureSize(entry, incoming));\n if (desired.some(value => value <= 0)) return undefined;\n return fitSizes(\n desired,\n entries.map(entry => entry.minimum),\n budget\n );\n}\n\nfunction pressureSize(entry: NonNullable<ReturnType<typeof splitEntries>>[number], incoming: number) {\n if (entry.moving) return incoming;\n if (entry.donor) return entry.minimum;\n return entry.extent!;\n}\n\nfunction sourceBranch(node: WorkspaceNode | null, source: string[]): WorkspaceSplit | undefined {\n if (!node || node.type === 'group') return undefined;\n if (node.children.some(child => child.type === 'group' && child.panels.some(id => source.includes(id)))) return node;\n for (const child of node.children) {\n const found = sourceBranch(child, source);\n if (found) return found;\n }\n return undefined;\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import type { ReactiveControllerHost } from 'lit';
2
2
  import { WorkspacePanel } from '../../panel/panel.js';
3
- export declare function getPanelLabel(panels: WorkspacePanel[], id: string): string;
3
+ export declare function getPanelLabel(panel: WorkspacePanel): string;
4
4
  /** Recognizes authored chrome mutations while excluding nested workspace content. */
5
5
  export declare function isWorkspaceCompositionMutation(host: HTMLElement, record: MutationRecord): boolean;
6
6
  export declare function captureWorkspaceFocus(host: HTMLElement): (() => void) | undefined;
@@ -2,8 +2,10 @@ import { getActiveElement as e, isFocusable as t } from "../../../internal/utils
2
2
  import { matchesElementName as n } from "../../../internal/utils/dom.js";
3
3
  import { WorkspacePanel as r } from "../../panel/panel2.js";
4
4
  // src/workspace/utils/presentation/composition.utils.ts
5
- function i(e, t) {
6
- return [...e.find((e) => e.id === t)?.querySelector(":scope > nve-workspace-panel-header")?.childNodes ?? []].filter((e) => !(e instanceof Element) || !e.hasAttribute("slot")).map((e) => e.textContent).join(" ").trim() || t;
5
+ function i(e) {
6
+ let t = e.querySelector(":scope > nve-workspace-panel-header"), n = "", r = "";
7
+ for (let e = t?.firstChild; e; e = e.nextSibling) e.nodeType === Node.COMMENT_NODE || e instanceof Element && e.hasAttribute("slot") || (n += r + (e.textContent ?? ""), r = " ");
8
+ return n.trim() || e.id;
7
9
  }
8
10
  function a(e, t) {
9
11
  let i = (t) => !!t && n(t, r) && t.parentElement === e;
@@ -1 +1 @@
1
- {"version":3,"file":"composition.utils.js","names":["getPanelLabel","panels","item","id","node","isWorkspaceCompositionMutation","owns","matchesElementName","WorkspacePanel","host","record","element","captureWorkspaceFocus","document","previous","getActiveElement","selector","focusControlSelector","active","meaningfulFocus","target","capturedFocusTarget","isFocusable","captureTabScroll","positions","strip","restoreTabScroll","key","left","shouldFocusAfterWorkspaceClose","workspacePanel","focusWorkspacePanel","options","selectedWorkspacePanel","workspacePanelFocusTarget","scrollWorkspaceHeaderIntoView","attribute","child","root","escaped","group","grip","control"],"sources":["../../../../src/workspace/utils/presentation/composition.utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { ReactiveControllerHost } from 'lit';\n\nimport { getActiveElement, isFocusable, matchesElementName } from '@nvidia-elements/core/internal';\n\nimport { WorkspacePanel } from '../../panel/panel.js';\n\nexport function getPanelLabel(panels: WorkspacePanel[], id: string) {\n const panel = panels.find(item => item.id === id);\n const header = panel?.querySelector(':scope > nve-workspace-panel-header');\n return (\n [...(header?.childNodes ?? [])]\n .filter(node => !(node instanceof Element) || !node.hasAttribute('slot'))\n .map(node => node.textContent)\n .join(' ')\n .trim() || id\n );\n}\n\n/** Recognizes authored chrome mutations while excluding nested workspace content. */\nexport function isWorkspaceCompositionMutation(host: HTMLElement, record: MutationRecord) {\n const owns = (node: Node | null | undefined) =>\n !!node && matchesElementName(node as Element, WorkspacePanel) && node.parentElement === host;\n if (owns(record.target))\n return [...record.addedNodes, ...record.removedNodes].some(\n node =>\n node instanceof Element && ['nve-workspace-panel-header', 'nve-workspace-panel-footer'].includes(node.localName)\n );\n const element = record.target instanceof Element ? record.target : record.target.parentElement;\n if (record.attributeName === 'slot' && owns(element?.parentElement)) return true;\n return owns(element?.closest('nve-workspace-panel-header')?.parentElement);\n}\n\nexport function captureWorkspaceFocus(host: HTMLElement): (() => void) | undefined {\n const document = host.ownerDocument;\n const previous = getActiveElement(document);\n if (!(previous instanceof HTMLElement) || [document.body, document.documentElement, host].includes(previous)) return;\n const selector = focusControlSelector(previous, host);\n return () => {\n if (!host.isConnected || host.ownerDocument !== document) return;\n const active = getActiveElement(document);\n if (active === previous || meaningfulFocus(active, host)) return;\n const target = capturedFocusTarget(host, previous, selector);\n if (\n target &&\n target.ownerDocument === document &&\n isFocusable(target) &&\n target.checkVisibility({ visibilityProperty: true })\n ) {\n target.focus({ preventScroll: true });\n }\n };\n}\n\n/** Captures only native strip positions for the lifetime of one gesture. */\nexport function captureTabScroll(host: HTMLElement & { updateComplete: Promise<unknown>; requestUpdate(): void }) {\n const positions = [...(host.shadowRoot?.querySelectorAll<HTMLElement>('.tablist[data-group]') ?? [])].map(strip => ({\n key: strip.dataset.group!,\n left: strip.scrollLeft\n }));\n return () => {\n void restoreTabScroll(host, positions);\n };\n}\n\nasync function restoreTabScroll(\n host: HTMLElement & { updateComplete: Promise<unknown>; requestUpdate(): void },\n positions: { key: string; left: number }[]\n) {\n await host.updateComplete;\n\n for (const { key, left } of positions) {\n const strip = host.shadowRoot?.querySelector<HTMLElement>(`[data-group=\"${CSS.escape(key)}\"]`);\n if (strip) {\n strip.scrollLeft = left;\n }\n }\n\n host.requestUpdate();\n}\n\nexport function shouldFocusAfterWorkspaceClose(host: HTMLElement, id: string): boolean {\n if (!host.ownerDocument.activeElement || host.ownerDocument.activeElement === host.ownerDocument.body) return true;\n if (workspacePanel(host, id)?.matches(':focus-within')) return true;\n const active = host.shadowRoot?.activeElement;\n return active instanceof HTMLElement && (active.dataset.panel === id || active.dataset.close === id);\n}\n\nexport async function focusWorkspacePanel(\n host: HTMLElement & ReactiveControllerHost,\n options: { id?: string; group?: boolean; preserveScroll?: boolean } = {}\n) {\n await host.updateComplete;\n const id = options.id ?? selectedWorkspacePanel(host);\n if (!id) return;\n const target = workspacePanelFocusTarget(host, id, options.group);\n if (!target) return;\n target.focus({ preventScroll: options.preserveScroll });\n if (!options.preserveScroll) {\n scrollWorkspaceHeaderIntoView(host, id);\n }\n}\n\nfunction capturedFocusTarget(host: HTMLElement, previous: HTMLElement, selector: string | undefined) {\n if (previous.isConnected) return previous;\n return selector ? host.shadowRoot?.querySelector<HTMLElement>(selector) : undefined;\n}\n\nfunction meaningfulFocus(element: Element | null, host: HTMLElement) {\n return (\n element instanceof HTMLElement &&\n element !== host &&\n element !== host.ownerDocument.body &&\n element !== host.ownerDocument.documentElement &&\n element.isConnected &&\n isFocusable(element) &&\n element.checkVisibility({ visibilityProperty: true })\n );\n}\n\nfunction focusControlSelector(element: HTMLElement, host: HTMLElement) {\n if (element.getRootNode() !== host.shadowRoot) return undefined;\n const attribute = element.hasAttribute('data-panel') ? 'data-panel' : 'data-drag-group';\n const id = element.getAttribute(attribute);\n return id ? `${element.localName}[${attribute}=\"${CSS.escape(id)}\"]` : undefined;\n}\n\nfunction workspacePanel(host: HTMLElement, id: string): WorkspacePanel | undefined {\n return [...host.children].find(\n (child): child is WorkspacePanel => matchesElementName(child, WorkspacePanel) && child.id === id\n );\n}\n\nfunction selectedWorkspacePanel(host: HTMLElement): string | undefined {\n return host.shadowRoot?.querySelector<HTMLElement>('nve-tabs-item[selected], .panel-title')?.dataset.panel;\n}\n\nfunction workspacePanelFocusTarget(host: HTMLElement, id: string, group?: boolean): HTMLElement | null | undefined {\n const root = host.shadowRoot;\n if (!root) return;\n const escaped = CSS.escape(id);\n if (group) {\n const grip = root.querySelector<HTMLElement>(`[data-drag-group=\"${escaped}\"]`);\n if (grip) return grip;\n }\n const control = root.querySelector<HTMLElement>(`[data-panel=\"${escaped}\"]`);\n if (control?.localName === 'nve-tabs-item') return control;\n return workspacePanel(host, id)?.shadowRoot?.querySelector<HTMLElement>('.body');\n}\n\nfunction scrollWorkspaceHeaderIntoView(host: HTMLElement, id: string) {\n host.shadowRoot\n ?.querySelector(`[data-panel=\"${CSS.escape(id)}\"]`)\n ?.closest('.tab-entry')\n ?.scrollIntoView({ block: 'nearest', inline: 'nearest' });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"composition.utils.js","names":["getPanelLabel","header","panel","label","separator","node","isWorkspaceCompositionMutation","owns","matchesElementName","WorkspacePanel","host","record","element","captureWorkspaceFocus","document","previous","getActiveElement","selector","focusControlSelector","active","meaningfulFocus","target","capturedFocusTarget","isFocusable","captureTabScroll","positions","strip","restoreTabScroll","key","left","shouldFocusAfterWorkspaceClose","workspacePanel","id","focusWorkspacePanel","options","selectedWorkspacePanel","workspacePanelFocusTarget","scrollWorkspaceHeaderIntoView","attribute","child","root","escaped","group","grip","control"],"sources":["../../../../src/workspace/utils/presentation/composition.utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { ReactiveControllerHost } from 'lit';\n\nimport { getActiveElement, isFocusable, matchesElementName } from '@nvidia-elements/core/internal';\n\nimport { WorkspacePanel } from '../../panel/panel.js';\n\nexport function getPanelLabel(panel: WorkspacePanel) {\n const header = panel.querySelector(':scope > nve-workspace-panel-header');\n let label = '';\n let separator = '';\n for (let node = header?.firstChild; node; node = node.nextSibling) {\n if (node.nodeType === Node.COMMENT_NODE || (node instanceof Element && node.hasAttribute('slot'))) continue;\n label += separator + (node.textContent ?? '');\n separator = ' ';\n }\n return label.trim() || panel.id;\n}\n\n/** Recognizes authored chrome mutations while excluding nested workspace content. */\nexport function isWorkspaceCompositionMutation(host: HTMLElement, record: MutationRecord) {\n const owns = (node: Node | null | undefined) =>\n !!node && matchesElementName(node as Element, WorkspacePanel) && node.parentElement === host;\n if (owns(record.target))\n return [...record.addedNodes, ...record.removedNodes].some(\n node =>\n node instanceof Element && ['nve-workspace-panel-header', 'nve-workspace-panel-footer'].includes(node.localName)\n );\n const element = record.target instanceof Element ? record.target : record.target.parentElement;\n if (record.attributeName === 'slot' && owns(element?.parentElement)) return true;\n return owns(element?.closest('nve-workspace-panel-header')?.parentElement);\n}\n\nexport function captureWorkspaceFocus(host: HTMLElement): (() => void) | undefined {\n const document = host.ownerDocument;\n const previous = getActiveElement(document);\n if (!(previous instanceof HTMLElement) || [document.body, document.documentElement, host].includes(previous)) return;\n const selector = focusControlSelector(previous, host);\n return () => {\n if (!host.isConnected || host.ownerDocument !== document) return;\n const active = getActiveElement(document);\n if (active === previous || meaningfulFocus(active, host)) return;\n const target = capturedFocusTarget(host, previous, selector);\n if (\n target &&\n target.ownerDocument === document &&\n isFocusable(target) &&\n target.checkVisibility({ visibilityProperty: true })\n ) {\n target.focus({ preventScroll: true });\n }\n };\n}\n\n/** Captures only native strip positions for the lifetime of one gesture. */\nexport function captureTabScroll(host: HTMLElement & { updateComplete: Promise<unknown>; requestUpdate(): void }) {\n const positions = [...(host.shadowRoot?.querySelectorAll<HTMLElement>('.tablist[data-group]') ?? [])].map(strip => ({\n key: strip.dataset.group!,\n left: strip.scrollLeft\n }));\n return () => {\n void restoreTabScroll(host, positions);\n };\n}\n\nasync function restoreTabScroll(\n host: HTMLElement & { updateComplete: Promise<unknown>; requestUpdate(): void },\n positions: { key: string; left: number }[]\n) {\n await host.updateComplete;\n\n for (const { key, left } of positions) {\n const strip = host.shadowRoot?.querySelector<HTMLElement>(`[data-group=\"${CSS.escape(key)}\"]`);\n if (strip) {\n strip.scrollLeft = left;\n }\n }\n\n host.requestUpdate();\n}\n\nexport function shouldFocusAfterWorkspaceClose(host: HTMLElement, id: string): boolean {\n if (!host.ownerDocument.activeElement || host.ownerDocument.activeElement === host.ownerDocument.body) return true;\n if (workspacePanel(host, id)?.matches(':focus-within')) return true;\n const active = host.shadowRoot?.activeElement;\n return active instanceof HTMLElement && (active.dataset.panel === id || active.dataset.close === id);\n}\n\nexport async function focusWorkspacePanel(\n host: HTMLElement & ReactiveControllerHost,\n options: { id?: string; group?: boolean; preserveScroll?: boolean } = {}\n) {\n await host.updateComplete;\n const id = options.id ?? selectedWorkspacePanel(host);\n if (!id) return;\n const target = workspacePanelFocusTarget(host, id, options.group);\n if (!target) return;\n target.focus({ preventScroll: options.preserveScroll });\n if (!options.preserveScroll) {\n scrollWorkspaceHeaderIntoView(host, id);\n }\n}\n\nfunction capturedFocusTarget(host: HTMLElement, previous: HTMLElement, selector: string | undefined) {\n if (previous.isConnected) return previous;\n return selector ? host.shadowRoot?.querySelector<HTMLElement>(selector) : undefined;\n}\n\nfunction meaningfulFocus(element: Element | null, host: HTMLElement) {\n return (\n element instanceof HTMLElement &&\n element !== host &&\n element !== host.ownerDocument.body &&\n element !== host.ownerDocument.documentElement &&\n element.isConnected &&\n isFocusable(element) &&\n element.checkVisibility({ visibilityProperty: true })\n );\n}\n\nfunction focusControlSelector(element: HTMLElement, host: HTMLElement) {\n if (element.getRootNode() !== host.shadowRoot) return undefined;\n const attribute = element.hasAttribute('data-panel') ? 'data-panel' : 'data-drag-group';\n const id = element.getAttribute(attribute);\n return id ? `${element.localName}[${attribute}=\"${CSS.escape(id)}\"]` : undefined;\n}\n\nfunction workspacePanel(host: HTMLElement, id: string): WorkspacePanel | undefined {\n return [...host.children].find(\n (child): child is WorkspacePanel => matchesElementName(child, WorkspacePanel) && child.id === id\n );\n}\n\nfunction selectedWorkspacePanel(host: HTMLElement): string | undefined {\n return host.shadowRoot?.querySelector<HTMLElement>('nve-tabs-item[selected], .panel-title')?.dataset.panel;\n}\n\nfunction workspacePanelFocusTarget(host: HTMLElement, id: string, group?: boolean): HTMLElement | null | undefined {\n const root = host.shadowRoot;\n if (!root) return;\n const escaped = CSS.escape(id);\n if (group) {\n const grip = root.querySelector<HTMLElement>(`[data-drag-group=\"${escaped}\"]`);\n if (grip) return grip;\n }\n const control = root.querySelector<HTMLElement>(`[data-panel=\"${escaped}\"]`);\n if (control?.localName === 'nve-tabs-item') return control;\n return workspacePanel(host, id)?.shadowRoot?.querySelector<HTMLElement>('.body');\n}\n\nfunction scrollWorkspaceHeaderIntoView(host: HTMLElement, id: string) {\n host.shadowRoot\n ?.querySelector(`[data-panel=\"${CSS.escape(id)}\"]`)\n ?.closest('.tab-entry')\n ?.scrollIntoView({ block: 'nearest', inline: 'nearest' });\n}\n"],"mappings":"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"}
@@ -42,8 +42,8 @@ export declare function projectTabMembership(spatial: WorkspacePresentedGroup[],
42
42
  panels: WorkspacePanel[];
43
43
  }[];
44
44
  export declare function getAllocatedTabWidth(group: WorkspacePresentedGroup, measurements: WorkspaceMeasurements): number;
45
- /** Keeps a recipient strip stable while carried members enter or leave its preview. */
46
- export declare function getTabStripIdentity(root: WorkspaceNode | null, members: string[], source: string[]): string;
45
+ /** Indexes strip ownership for one committed root. */
46
+ export declare function createTabStripIdentityResolver(root: WorkspaceNode | null): (members: string[], source: string[]) => string;
47
47
  /** Resolves native-scrolled tab controls from computed positions, never animated client rectangles. */
48
48
  export declare function findTabHeaderTarget(input: {
49
49
  groups: WorkspacePresentedGroup[];
@@ -68,7 +68,7 @@ export declare function getInitialDragCue(input: {
68
68
  kind: 'group' | 'tab';
69
69
  rect: Rect;
70
70
  };
71
- /** The computed carried tab slot owns exactly the same visible rectangle as its landing cue. */
71
+ /** The computed carried tab reservation owns exactly the same visible rectangle as its landing cue. */
72
72
  export declare function getTabReservationHit(input: Parameters<typeof findTabHeaderTarget>[0]): "tab" | undefined;
73
73
  export declare function getTabCue(input: {
74
74
  group: WorkspacePresentedGroup;
@@ -1,13 +1,14 @@
1
1
  import { findGroup as e } from "../model/operations.utils.js";
2
2
  import { clipCue as t } from "../interaction/drag-preview.utils.js";
3
+ import { getPanelMembershipKey as n } from "../model/membership.utils.js";
3
4
  // src/workspace/utils/presentation/groups.utils.ts
4
- function n(e) {
5
+ function r(e) {
5
6
  let t = e.panels.map((e) => e.id).filter((t) => !e.closed.includes(t));
6
7
  return e.groups.map(({ key: n, group: r, rect: i }) => ({
7
8
  key: n,
8
9
  rect: i,
9
10
  active: r.active,
10
- ...d({
11
+ ...f({
11
12
  root: e.root,
12
13
  members: r.panels,
13
14
  source: e.source,
@@ -17,10 +18,10 @@ function n(e) {
17
18
  panels: r.panels.flatMap((t) => e.panels.filter((e) => e.id === t))
18
19
  }));
19
20
  }
20
- function r(e, t, n) {
21
+ function i(e, t, n) {
21
22
  return n ? e.map((e) => ({
22
23
  ...e,
23
- active: p({
24
+ active: m({
24
25
  root: t,
25
26
  source: n,
26
27
  members: e.panels.map((e) => e.id),
@@ -28,29 +29,38 @@ function r(e, t, n) {
28
29
  })
29
30
  })) : e;
30
31
  }
31
- function i(e, t) {
32
+ function a(e, t) {
32
33
  return e.map((e) => ({
33
34
  ...e,
34
35
  panels: t.find((t) => t.panels.some((t) => e.panels.includes(t)))?.panels ?? e.panels
35
36
  }));
36
37
  }
37
- function a(e, t) {
38
+ function o(e, t) {
38
39
  let n = t.panels?.[e.active]?.suffixWidth ?? 0;
39
40
  return Math.max(t.tabWidth, e.tabbed || e.panels.length > 1 ? t.tabMinWidth : 0, (e.rect.width - e.gripWidth - n - (t.groupActionWidth ?? 0)) / e.panels.length);
40
41
  }
41
- function o(e, t, n) {
42
- return JSON.stringify([...f(e, t, n)].sort());
42
+ function s(t) {
43
+ let r = /* @__PURE__ */ new Map();
44
+ return (i, a) => {
45
+ let o = i.find((e) => !a.includes(e)) ?? i[0], s = o && r.get(o);
46
+ if (s) return s;
47
+ let c = o ? e(t, o) : void 0;
48
+ if (!c) return n(i);
49
+ let l = n(c.panels);
50
+ for (let e of c.panels) r.set(e, l);
51
+ return l;
52
+ };
43
53
  }
44
- function s(e) {
54
+ function c(e) {
45
55
  for (let n of e.groups) {
46
- let r = e.client(n.rect), { headerHeight: i } = e.measurements, o = (e.measurements.panels?.[n.active]?.suffixWidth ?? 0) + (e.measurements.groupActionWidth ?? 0), { gripWidth: s } = n, c = t({
56
+ let r = e.client(n.rect), { headerHeight: i } = e.measurements, a = (e.measurements.panels?.[n.active]?.suffixWidth ?? 0) + (e.measurements.groupActionWidth ?? 0), { gripWidth: s } = n, c = t({
47
57
  x: r.x + s,
48
58
  y: r.y,
49
- width: Math.max(0, r.width - s - o),
59
+ width: Math.max(0, r.width - s - a),
50
60
  height: i
51
61
  }, e.bounds);
52
- if (!g(c, e.point)) continue;
53
- let l = a(n, e.measurements), u = n.panels.flatMap((t, a) => e.source.includes(t.id) ? [] : [{
62
+ if (!_(c, e.point)) continue;
63
+ let l = o(n, e.measurements), u = n.panels.flatMap((t, a) => e.source.includes(t.id) ? [] : [{
54
64
  id: t.id,
55
65
  rect: {
56
66
  x: r.x + s + a * l - e.scroll(n),
@@ -67,7 +77,7 @@ function s(e) {
67
77
  };
68
78
  }
69
79
  }
70
- function c(e) {
80
+ function l(e) {
71
81
  let n = e.groups.find((t) => t.panels.some((t) => e.source.includes(t.id)));
72
82
  if (!n) return {
73
83
  kind: "group",
@@ -89,53 +99,53 @@ function c(e) {
89
99
  }, e.bounds)
90
100
  };
91
101
  }
92
- function l(e) {
102
+ function u(e) {
93
103
  return e.groups.some((t) => {
94
104
  if (!t.panels.some((t) => e.source.includes(t.id))) return !1;
95
- let { cue: n, bounds: r } = h({
105
+ let { cue: n, bounds: r } = g({
96
106
  rect: e.client(t.rect),
97
107
  viewport: e.bounds,
98
108
  grip: t.gripWidth,
99
109
  suffixWidth: (e.measurements.panels?.[t.active]?.suffixWidth ?? 0) + (e.measurements.groupActionWidth ?? 0),
100
110
  headerHeight: e.measurements.headerHeight,
101
- tabWidth: a(t, e.measurements),
111
+ tabWidth: o(t, e.measurements),
102
112
  panels: t.panels.map((e) => e.id),
103
113
  source: e.source,
104
114
  scroll: e.scroll(t)
105
115
  });
106
- return g(r, e.point) && g(n, e.point);
116
+ return _(r, e.point) && _(n, e.point);
107
117
  }) ? "tab" : void 0;
108
118
  }
109
- function u(e) {
110
- let t = r([m(e)], e.root, e.source)[0];
111
- return h({
119
+ function d(e) {
120
+ let t = i([h(e)], e.root, e.source)[0];
121
+ return g({
112
122
  rect: e.client(t.rect),
113
123
  viewport: e.viewport,
114
124
  grip: t.gripWidth,
115
125
  suffixWidth: (e.measurements.panels?.[t.active]?.suffixWidth ?? 0) + (e.measurements.groupActionWidth ?? 0),
116
126
  headerHeight: e.measurements.headerHeight,
117
- tabWidth: a(t, e.measurements),
127
+ tabWidth: o(t, e.measurements),
118
128
  panels: t.panels.map((e) => e.id),
119
129
  source: e.source,
120
130
  scroll: e.scroll(t)
121
131
  });
122
132
  }
123
- function d(e) {
124
- let t = f(e.root, e.members, e.source).filter((t) => e.visible.includes(t)).length > 1;
133
+ function f(e) {
134
+ let t = p(e.root, e.members, e.source).filter((t) => e.visible.includes(t)).length > 1;
125
135
  return {
126
136
  tabbed: t,
127
137
  gripWidth: t ? e.width : 0
128
138
  };
129
139
  }
130
- function f(t, n, r) {
140
+ function p(t, n, r) {
131
141
  let i = n.find((e) => !r.includes(e)) ?? n[0];
132
142
  return (i ? e(t, i) : void 0)?.panels ?? n;
133
143
  }
134
- function p(t) {
144
+ function m(t) {
135
145
  let { root: n, members: r, source: i, active: a } = t, o = r.filter((e) => !i.includes(e)), s = o[0], c = s ? e(n, s)?.active : void 0;
136
146
  return c && o.includes(c) ? c : o[0] ?? a;
137
147
  }
138
- function m(e) {
148
+ function h(e) {
139
149
  if (e.intent.kind !== "tab") return e.group;
140
150
  let t = e.intent.destination.target, n = e.spatial().find((e) => e.panels.some((e) => e.id === t));
141
151
  return n ? {
@@ -144,7 +154,7 @@ function m(e) {
144
154
  gripWidth: n.gripWidth
145
155
  } : e.group;
146
156
  }
147
- function h(e) {
157
+ function g(e) {
148
158
  let { rect: n, viewport: r, grip: i, suffixWidth: a, headerHeight: o, tabWidth: s, panels: c, source: l, scroll: u } = e;
149
159
  return {
150
160
  cue: {
@@ -161,9 +171,9 @@ function h(e) {
161
171
  }, r)
162
172
  };
163
173
  }
164
- function g(e, t) {
174
+ function _(e, t) {
165
175
  return e.width > 0 && e.height > 0 && t.x >= e.x && t.x < e.x + e.width && t.y >= e.y && t.y < e.y + e.height;
166
176
  }
167
- export { n as createPresentedGroups, s as findTabHeaderTarget, a as getAllocatedTabWidth, c as getInitialDragCue, u as getTabCue, l as getTabReservationHit, o as getTabStripIdentity, r as preserveRecipientSelection, i as projectTabMembership };
177
+ export { r as createPresentedGroups, s as createTabStripIdentityResolver, c as findTabHeaderTarget, o as getAllocatedTabWidth, l as getInitialDragCue, d as getTabCue, u as getTabReservationHit, i as preserveRecipientSelection, a as projectTabMembership };
168
178
 
169
179
  //# sourceMappingURL=groups.utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"groups.utils.js","names":["createPresentedGroups","visible","input","panel","id","group","tabStripMembership","preserveRecipientSelection","source","groups","presentedTabActive","projectTabMembership","spatial","proposed","candidate","getAllocatedTabWidth","suffix","measurements","getTabStripIdentity","committedTabMembers","root","members","findTabHeaderTarget","rect","strip","clipCue","gripWidth","headerHeight","contains","width","tabs","index","tab","getInitialDragCue","item","getTabReservationHit","reservedTabCue","bounds","cue","getTabCue","tabRecipient","tabbed","anchor","findGroup","remaining","original","active","target","recipient","grip","panels","tabWidth","scroll","suffixWidth","viewport","point"],"sources":["../../../../src/workspace/utils/presentation/groups.utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Point, Rect } from '@nvidia-elements/core/internal';\n\nimport { clipCue } from '../interaction/drag-preview.utils.js';\nimport { findGroup } from '../model/operations.utils.js';\n\nimport type { WorkspacePlacedGroup } from '../geometry/allocation.utils.js';\nimport type { WorkspaceDropIntent } from '../interaction/drag-preview.utils.js';\nimport type { WorkspaceNode } from '../model/layout.utils.js';\nimport type { WorkspaceMeasurements } from './measurements.utils.js';\n\nimport type { WorkspacePanel } from '../../panel/panel.js';\n\nexport interface WorkspacePresentedGroup {\n key: string;\n panels: WorkspacePanel[];\n rect: Rect;\n active: string;\n gripWidth: number;\n tabbed: boolean;\n}\n\nexport interface WorkspaceTarget {\n panels: string[];\n headerHeight: number;\n rect: Rect;\n tabs: { id: string; rect: Rect }[];\n}\n\n/** Binds supplied panels and committed header ownership to the computed allocation. */\nexport function createPresentedGroups(input: {\n groups: WorkspacePlacedGroup[];\n panels: WorkspacePanel[];\n root: WorkspaceNode | null;\n closed: string[];\n source: string[];\n gripWidth: number;\n}): WorkspacePresentedGroup[] {\n const visible = input.panels.map(panel => panel.id).filter(id => !input.closed.includes(id));\n return input.groups.map(({ key, group, rect }) => ({\n key,\n rect,\n active: group.active,\n ...tabStripMembership({\n root: input.root,\n members: group.panels,\n source: input.source,\n visible,\n width: input.gripWidth\n }),\n panels: group.panels.flatMap(id => input.panels.filter(panel => panel.id === id))\n }));\n}\n\n/** Keeps the surviving recipient active while carrying one of its panels. */\nexport function preserveRecipientSelection(\n groups: WorkspacePresentedGroup[],\n root: WorkspaceNode | null,\n source?: string[]\n) {\n return source\n ? groups.map(group => ({\n ...group,\n active: presentedTabActive({ root, source, members: group.panels.map(panel => panel.id), active: group.active })\n }))\n : groups;\n}\n\n/** Reserves projected tab membership without changing its source-excluded group allocation. */\nexport function projectTabMembership(spatial: WorkspacePresentedGroup[], proposed: WorkspacePresentedGroup[]) {\n return spatial.map(group => ({\n ...group,\n panels:\n proposed.find(candidate => candidate.panels.some(panel => group.panels.includes(panel)))?.panels ?? group.panels\n }));\n}\n\nexport function getAllocatedTabWidth(group: WorkspacePresentedGroup, measurements: WorkspaceMeasurements) {\n const suffix = measurements.panels?.[group.active]?.suffixWidth ?? 0;\n return Math.max(\n measurements.tabWidth,\n group.tabbed || group.panels.length > 1 ? measurements.tabMinWidth : 0,\n (group.rect.width - group.gripWidth - suffix - (measurements.groupActionWidth ?? 0)) / group.panels.length\n );\n}\n\n/** Keeps a recipient strip stable while carried members enter or leave its preview. */\nexport function getTabStripIdentity(root: WorkspaceNode | null, members: string[], source: string[]): string {\n return JSON.stringify([...committedTabMembers(root, members, source)].sort());\n}\n\n/** Resolves native-scrolled tab controls from computed positions, never animated client rectangles. */\nexport function findTabHeaderTarget(input: {\n groups: WorkspacePresentedGroup[];\n measurements: WorkspaceMeasurements;\n source: string[];\n point: Point;\n bounds: Rect;\n client(rect: Rect): Rect;\n scroll(group: WorkspacePresentedGroup): number;\n}): WorkspaceTarget | undefined {\n for (const group of input.groups) {\n const rect = input.client(group.rect);\n const { headerHeight } = input.measurements;\n const suffix =\n (input.measurements.panels?.[group.active]?.suffixWidth ?? 0) + (input.measurements.groupActionWidth ?? 0);\n const { gripWidth } = group;\n const strip = clipCue(\n { x: rect.x + gripWidth, y: rect.y, width: Math.max(0, rect.width - gripWidth - suffix), height: headerHeight },\n input.bounds\n );\n if (!contains(strip, input.point)) continue;\n const width = getAllocatedTabWidth(group, input.measurements);\n const tabs = group.panels.flatMap((panel, index) =>\n input.source.includes(panel.id)\n ? []\n : [\n {\n id: panel.id,\n rect: {\n x: rect.x + gripWidth + index * width - input.scroll(group),\n y: rect.y,\n width,\n height: headerHeight\n }\n }\n ]\n );\n if (tabs.length) return { panels: tabs.map(tab => tab.id), rect: strip, headerHeight: strip.height, tabs };\n }\n return undefined;\n}\n\n/** Captures the initial source cue from the current committed group and native strip position. */\nexport function getInitialDragCue(input: {\n groups: WorkspacePresentedGroup[];\n source: string[];\n groupDrag: boolean;\n bounds: Rect;\n client(rect: Rect): Rect;\n measurements: WorkspaceMeasurements;\n width(group: WorkspacePresentedGroup): number;\n scroll(group: WorkspacePresentedGroup): number;\n}): { kind: 'group' | 'tab'; rect: Rect } {\n const group = input.groups.find(item => item.panels.some(panel => input.source.includes(panel.id)));\n if (!group) return { kind: 'group', rect: input.bounds };\n const rect = input.client(group.rect);\n if (input.groupDrag || group.panels.length === 1) return { kind: 'group', rect: clipCue(rect, input.bounds) };\n const width = input.width(group);\n const index = group.panels.findIndex(panel => input.source.includes(panel.id));\n return {\n kind: 'tab',\n rect: clipCue(\n {\n x: rect.x + group.gripWidth + index * width - input.scroll(group),\n y: rect.y,\n width,\n height: input.measurements.headerHeight\n },\n input.bounds\n )\n };\n}\n\n/** The computed carried tab slot owns exactly the same visible rectangle as its landing cue. */\nexport function getTabReservationHit(input: Parameters<typeof findTabHeaderTarget>[0]) {\n return input.groups.some(group => {\n if (!group.panels.some(panel => input.source.includes(panel.id))) return false;\n const rect = input.client(group.rect);\n const { cue, bounds } = reservedTabCue({\n rect,\n viewport: input.bounds,\n grip: group.gripWidth,\n suffixWidth:\n (input.measurements.panels?.[group.active]?.suffixWidth ?? 0) + (input.measurements.groupActionWidth ?? 0),\n headerHeight: input.measurements.headerHeight,\n tabWidth: getAllocatedTabWidth(group, input.measurements),\n panels: group.panels.map(panel => panel.id),\n source: input.source,\n scroll: input.scroll(group)\n });\n return contains(bounds, input.point) && contains(cue, input.point);\n })\n ? ('tab' as const)\n : undefined;\n}\n\nexport function getTabCue(input: {\n group: WorkspacePresentedGroup;\n root: WorkspaceNode | null;\n source: string[];\n intent: WorkspaceDropIntent;\n spatial(): WorkspacePresentedGroup[];\n measurements: WorkspaceMeasurements;\n viewport: Rect;\n client(rect: Rect): Rect;\n scroll(group: WorkspacePresentedGroup): number;\n}) {\n const tabs = preserveRecipientSelection([tabRecipient(input)], input.root, input.source)[0]!;\n return reservedTabCue({\n rect: input.client(tabs.rect),\n viewport: input.viewport,\n grip: tabs.gripWidth,\n suffixWidth:\n (input.measurements.panels?.[tabs.active]?.suffixWidth ?? 0) + (input.measurements.groupActionWidth ?? 0),\n headerHeight: input.measurements.headerHeight,\n tabWidth: getAllocatedTabWidth(tabs, input.measurements),\n panels: tabs.panels.map(panel => panel.id),\n source: input.source,\n scroll: input.scroll(tabs)\n });\n}\n\n/** Derives tab presentation from committed members, excluding closed and unavailable panels. */\nfunction tabStripMembership(input: {\n root: WorkspaceNode | null;\n members: string[];\n source: string[];\n visible: string[];\n width: number;\n}) {\n const members = committedTabMembers(input.root, input.members, input.source).filter(id => input.visible.includes(id));\n const tabbed = members.length > 1;\n return { tabbed, gripWidth: tabbed ? input.width : 0 };\n}\n\nfunction committedTabMembers(root: WorkspaceNode | null, members: string[], source: string[]) {\n const anchor = members.find(id => !source.includes(id)) ?? members[0];\n const group = anchor ? findGroup(root, anchor) : undefined;\n return group?.panels ?? members;\n}\n\n/** Preserves the recipient body until the workspace commits the placement. */\nfunction presentedTabActive(input: {\n root: WorkspaceNode | null;\n members: string[];\n source: string[];\n active: string;\n}): string {\n const { root, members, source, active } = input;\n const remaining = members.filter(id => !source.includes(id));\n const anchor = remaining[0];\n const original = anchor ? findGroup(root, anchor)?.active : undefined;\n return original && remaining.includes(original) ? original : (remaining[0] ?? active);\n}\n\n/** Resolves proposed tab membership onto its non-order recipient rectangle. */\nfunction tabRecipient(input: {\n group: WorkspacePresentedGroup;\n intent: WorkspaceDropIntent;\n spatial: () => WorkspacePresentedGroup[];\n}) {\n if (input.intent.kind !== 'tab') return input.group;\n const target = input.intent.destination.target;\n const recipient = input.spatial().find(group => group.panels.some(panel => panel.id === target));\n return recipient ? { ...input.group, rect: recipient.rect, gripWidth: recipient.gripWidth } : input.group;\n}\n\n/** Allocates a carried tab reservation inside its native scrolling strip. */\nfunction reservedTabCue(input: {\n rect: Rect;\n viewport: Rect;\n grip: number;\n suffixWidth: number;\n headerHeight: number;\n tabWidth: number;\n panels: string[];\n source: string[];\n scroll: number;\n}) {\n const { rect, viewport, grip, suffixWidth, headerHeight, tabWidth, panels, source, scroll } = input;\n return {\n cue: {\n x: rect.x + grip + panels.findIndex(id => source.includes(id)) * tabWidth - scroll,\n y: rect.y,\n width: panels.filter(id => source.includes(id)).length * tabWidth,\n height: headerHeight\n },\n bounds: clipCue(\n { x: rect.x + grip, y: rect.y, width: Math.max(0, rect.width - grip - suffixWidth), height: headerHeight },\n viewport\n )\n };\n}\n\nfunction contains(rect: Rect, point: Point) {\n return (\n rect.width > 0 &&\n rect.height > 0 &&\n point.x >= rect.x &&\n point.x < rect.x + rect.width &&\n point.y >= rect.y &&\n point.y < rect.y + rect.height\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"groups.utils.js","names":["createPresentedGroups","visible","input","panel","id","group","tabStripMembership","preserveRecipientSelection","source","groups","presentedTabActive","projectTabMembership","spatial","proposed","candidate","getAllocatedTabWidth","suffix","measurements","createTabStripIdentityResolver","identities","anchor","members","previous","findGroup","root","getPanelMembershipKey","identity","findTabHeaderTarget","rect","strip","clipCue","gripWidth","headerHeight","contains","width","tabs","index","tab","getInitialDragCue","item","getTabReservationHit","reservedTabCue","bounds","cue","getTabCue","tabRecipient","tabbed","committedTabMembers","remaining","original","active","target","recipient","grip","panels","tabWidth","scroll","suffixWidth","viewport","point"],"sources":["../../../../src/workspace/utils/presentation/groups.utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Point, Rect } from '@nvidia-elements/core/internal';\n\nimport { clipCue } from '../interaction/drag-preview.utils.js';\nimport { getPanelMembershipKey } from '../model/membership.utils.js';\nimport { findGroup } from '../model/operations.utils.js';\n\nimport type { WorkspacePlacedGroup } from '../geometry/allocation.utils.js';\nimport type { WorkspaceDropIntent } from '../interaction/drag-preview.utils.js';\nimport type { WorkspaceNode } from '../model/layout.utils.js';\nimport type { WorkspaceMeasurements } from './measurements.utils.js';\n\nimport type { WorkspacePanel } from '../../panel/panel.js';\n\nexport interface WorkspacePresentedGroup {\n key: string;\n panels: WorkspacePanel[];\n rect: Rect;\n active: string;\n gripWidth: number;\n tabbed: boolean;\n}\n\nexport interface WorkspaceTarget {\n panels: string[];\n headerHeight: number;\n rect: Rect;\n tabs: { id: string; rect: Rect }[];\n}\n\n/** Binds supplied panels and committed header ownership to the computed allocation. */\nexport function createPresentedGroups(input: {\n groups: WorkspacePlacedGroup[];\n panels: WorkspacePanel[];\n root: WorkspaceNode | null;\n closed: string[];\n source: string[];\n gripWidth: number;\n}): WorkspacePresentedGroup[] {\n const visible = input.panels.map(panel => panel.id).filter(id => !input.closed.includes(id));\n return input.groups.map(({ key, group, rect }) => ({\n key,\n rect,\n active: group.active,\n ...tabStripMembership({\n root: input.root,\n members: group.panels,\n source: input.source,\n visible,\n width: input.gripWidth\n }),\n panels: group.panels.flatMap(id => input.panels.filter(panel => panel.id === id))\n }));\n}\n\n/** Keeps the surviving recipient active while carrying one of its panels. */\nexport function preserveRecipientSelection(\n groups: WorkspacePresentedGroup[],\n root: WorkspaceNode | null,\n source?: string[]\n) {\n return source\n ? groups.map(group => ({\n ...group,\n active: presentedTabActive({ root, source, members: group.panels.map(panel => panel.id), active: group.active })\n }))\n : groups;\n}\n\n/** Reserves projected tab membership without changing its source-excluded group allocation. */\nexport function projectTabMembership(spatial: WorkspacePresentedGroup[], proposed: WorkspacePresentedGroup[]) {\n return spatial.map(group => ({\n ...group,\n panels:\n proposed.find(candidate => candidate.panels.some(panel => group.panels.includes(panel)))?.panels ?? group.panels\n }));\n}\n\nexport function getAllocatedTabWidth(group: WorkspacePresentedGroup, measurements: WorkspaceMeasurements) {\n const suffix = measurements.panels?.[group.active]?.suffixWidth ?? 0;\n return Math.max(\n measurements.tabWidth,\n group.tabbed || group.panels.length > 1 ? measurements.tabMinWidth : 0,\n (group.rect.width - group.gripWidth - suffix - (measurements.groupActionWidth ?? 0)) / group.panels.length\n );\n}\n\n/** Indexes strip ownership for one committed root. */\nexport function createTabStripIdentityResolver(root: WorkspaceNode | null) {\n const identities = new Map<string, string>();\n return (members: string[], source: string[]) => {\n const anchor = members.find(id => !source.includes(id)) ?? members[0];\n const previous = anchor && identities.get(anchor);\n if (previous) return previous;\n const group = anchor ? findGroup(root, anchor) : undefined;\n if (!group) return getPanelMembershipKey(members);\n const identity = getPanelMembershipKey(group.panels);\n for (const id of group.panels) identities.set(id, identity);\n return identity;\n };\n}\n\n/** Resolves native-scrolled tab controls from computed positions, never animated client rectangles. */\nexport function findTabHeaderTarget(input: {\n groups: WorkspacePresentedGroup[];\n measurements: WorkspaceMeasurements;\n source: string[];\n point: Point;\n bounds: Rect;\n client(rect: Rect): Rect;\n scroll(group: WorkspacePresentedGroup): number;\n}): WorkspaceTarget | undefined {\n for (const group of input.groups) {\n const rect = input.client(group.rect);\n const { headerHeight } = input.measurements;\n const suffix =\n (input.measurements.panels?.[group.active]?.suffixWidth ?? 0) + (input.measurements.groupActionWidth ?? 0);\n const { gripWidth } = group;\n const strip = clipCue(\n { x: rect.x + gripWidth, y: rect.y, width: Math.max(0, rect.width - gripWidth - suffix), height: headerHeight },\n input.bounds\n );\n if (!contains(strip, input.point)) continue;\n const width = getAllocatedTabWidth(group, input.measurements);\n const tabs = group.panels.flatMap((panel, index) =>\n input.source.includes(panel.id)\n ? []\n : [\n {\n id: panel.id,\n rect: {\n x: rect.x + gripWidth + index * width - input.scroll(group),\n y: rect.y,\n width,\n height: headerHeight\n }\n }\n ]\n );\n if (tabs.length) return { panels: tabs.map(tab => tab.id), rect: strip, headerHeight: strip.height, tabs };\n }\n return undefined;\n}\n\n/** Captures the initial source cue from the current committed group and native strip position. */\nexport function getInitialDragCue(input: {\n groups: WorkspacePresentedGroup[];\n source: string[];\n groupDrag: boolean;\n bounds: Rect;\n client(rect: Rect): Rect;\n measurements: WorkspaceMeasurements;\n width(group: WorkspacePresentedGroup): number;\n scroll(group: WorkspacePresentedGroup): number;\n}): { kind: 'group' | 'tab'; rect: Rect } {\n const group = input.groups.find(item => item.panels.some(panel => input.source.includes(panel.id)));\n if (!group) return { kind: 'group', rect: input.bounds };\n const rect = input.client(group.rect);\n if (input.groupDrag || group.panels.length === 1) return { kind: 'group', rect: clipCue(rect, input.bounds) };\n const width = input.width(group);\n const index = group.panels.findIndex(panel => input.source.includes(panel.id));\n return {\n kind: 'tab',\n rect: clipCue(\n {\n x: rect.x + group.gripWidth + index * width - input.scroll(group),\n y: rect.y,\n width,\n height: input.measurements.headerHeight\n },\n input.bounds\n )\n };\n}\n\n/** The computed carried tab reservation owns exactly the same visible rectangle as its landing cue. */\nexport function getTabReservationHit(input: Parameters<typeof findTabHeaderTarget>[0]) {\n return input.groups.some(group => {\n if (!group.panels.some(panel => input.source.includes(panel.id))) return false;\n const rect = input.client(group.rect);\n const { cue, bounds } = reservedTabCue({\n rect,\n viewport: input.bounds,\n grip: group.gripWidth,\n suffixWidth:\n (input.measurements.panels?.[group.active]?.suffixWidth ?? 0) + (input.measurements.groupActionWidth ?? 0),\n headerHeight: input.measurements.headerHeight,\n tabWidth: getAllocatedTabWidth(group, input.measurements),\n panels: group.panels.map(panel => panel.id),\n source: input.source,\n scroll: input.scroll(group)\n });\n return contains(bounds, input.point) && contains(cue, input.point);\n })\n ? ('tab' as const)\n : undefined;\n}\n\nexport function getTabCue(input: {\n group: WorkspacePresentedGroup;\n root: WorkspaceNode | null;\n source: string[];\n intent: WorkspaceDropIntent;\n spatial(): WorkspacePresentedGroup[];\n measurements: WorkspaceMeasurements;\n viewport: Rect;\n client(rect: Rect): Rect;\n scroll(group: WorkspacePresentedGroup): number;\n}) {\n const tabs = preserveRecipientSelection([tabRecipient(input)], input.root, input.source)[0]!;\n return reservedTabCue({\n rect: input.client(tabs.rect),\n viewport: input.viewport,\n grip: tabs.gripWidth,\n suffixWidth:\n (input.measurements.panels?.[tabs.active]?.suffixWidth ?? 0) + (input.measurements.groupActionWidth ?? 0),\n headerHeight: input.measurements.headerHeight,\n tabWidth: getAllocatedTabWidth(tabs, input.measurements),\n panels: tabs.panels.map(panel => panel.id),\n source: input.source,\n scroll: input.scroll(tabs)\n });\n}\n\n/** Derives tab presentation from committed members, excluding closed and unavailable panels. */\nfunction tabStripMembership(input: {\n root: WorkspaceNode | null;\n members: string[];\n source: string[];\n visible: string[];\n width: number;\n}) {\n const members = committedTabMembers(input.root, input.members, input.source).filter(id => input.visible.includes(id));\n const tabbed = members.length > 1;\n return { tabbed, gripWidth: tabbed ? input.width : 0 };\n}\n\nfunction committedTabMembers(root: WorkspaceNode | null, members: string[], source: string[]) {\n const anchor = members.find(id => !source.includes(id)) ?? members[0];\n const group = anchor ? findGroup(root, anchor) : undefined;\n return group?.panels ?? members;\n}\n\n/** Preserves the recipient body until the workspace commits the placement. */\nfunction presentedTabActive(input: {\n root: WorkspaceNode | null;\n members: string[];\n source: string[];\n active: string;\n}): string {\n const { root, members, source, active } = input;\n const remaining = members.filter(id => !source.includes(id));\n const anchor = remaining[0];\n const original = anchor ? findGroup(root, anchor)?.active : undefined;\n return original && remaining.includes(original) ? original : (remaining[0] ?? active);\n}\n\n/** Resolves proposed tab membership onto its non-order recipient rectangle. */\nfunction tabRecipient(input: {\n group: WorkspacePresentedGroup;\n intent: WorkspaceDropIntent;\n spatial: () => WorkspacePresentedGroup[];\n}) {\n if (input.intent.kind !== 'tab') return input.group;\n const target = input.intent.destination.target;\n const recipient = input.spatial().find(group => group.panels.some(panel => panel.id === target));\n return recipient ? { ...input.group, rect: recipient.rect, gripWidth: recipient.gripWidth } : input.group;\n}\n\n/** Allocates a carried tab reservation inside its native scrolling strip. */\nfunction reservedTabCue(input: {\n rect: Rect;\n viewport: Rect;\n grip: number;\n suffixWidth: number;\n headerHeight: number;\n tabWidth: number;\n panels: string[];\n source: string[];\n scroll: number;\n}) {\n const { rect, viewport, grip, suffixWidth, headerHeight, tabWidth, panels, source, scroll } = input;\n return {\n cue: {\n x: rect.x + grip + panels.findIndex(id => source.includes(id)) * tabWidth - scroll,\n y: rect.y,\n width: panels.filter(id => source.includes(id)).length * tabWidth,\n height: headerHeight\n },\n bounds: clipCue(\n { x: rect.x + grip, y: rect.y, width: Math.max(0, rect.width - grip - suffixWidth), height: headerHeight },\n viewport\n )\n };\n}\n\nfunction contains(rect: Rect, point: Point) {\n return (\n rect.width > 0 &&\n rect.height > 0 &&\n point.x >= rect.x &&\n point.x < rect.x + rect.width &&\n point.y >= rect.y &&\n point.y < rect.y + rect.height\n );\n}\n"],"mappings":"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"}
@@ -19,3 +19,4 @@ export interface WorkspaceMeasurementChange {
19
19
  readonly spaceChanged: boolean;
20
20
  }
21
21
  export declare function sameWorkspaceMeasurements(previous: WorkspaceMeasurements | undefined, current: WorkspaceMeasurements): boolean;
22
+ export declare function sameWorkspaceMeasurementScalars(previous: WorkspaceMeasurements, current: WorkspaceMeasurements): boolean;
@@ -1,17 +1,9 @@
1
1
  // src/workspace/utils/presentation/measurements.utils.ts
2
- var e = [
3
- "gap",
4
- "headerHeight",
5
- "gripWidth",
6
- "actionWidth",
7
- "groupActionWidth",
8
- "tabWidth",
9
- "tabMinWidth",
10
- "width",
11
- "height"
12
- ];
13
- function t(t, r) {
14
- return t !== void 0 && e.every((e) => t[e] === r[e]) && n(t.panels, r.panels);
2
+ function e(e, r) {
3
+ return e !== void 0 && t(e, r) && n(e.panels, r.panels);
4
+ }
5
+ function t(e, t) {
6
+ return e.gap === t.gap && e.headerHeight === t.headerHeight && e.gripWidth === t.gripWidth && e.actionWidth === t.actionWidth && e.groupActionWidth === t.groupActionWidth && e.tabWidth === t.tabWidth && e.tabMinWidth === t.tabMinWidth && e.width === t.width && e.height === t.height;
15
7
  }
16
8
  function n(e, t) {
17
9
  if (e === t) return !0;
@@ -23,6 +15,6 @@ function n(e, t) {
23
15
  for (let n in t) if (!Object.hasOwn(e, n)) return !1;
24
16
  return !0;
25
17
  }
26
- export { t as sameWorkspaceMeasurements };
18
+ export { t as sameWorkspaceMeasurementScalars, e as sameWorkspaceMeasurements };
27
19
 
28
20
  //# sourceMappingURL=measurements.utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"measurements.utils.js","names":["measurementProperties","sameWorkspaceMeasurements","previous","property","current","samePanelMeasurements","id","before","after"],"sources":["../../../../src/workspace/utils/presentation/measurements.utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nexport interface WorkspacePanelMeasurements {\n suffixWidth: number;\n footerHeight: number;\n}\n\nexport interface WorkspaceMeasurements {\n gap: number;\n panels?: Record<string, WorkspacePanelMeasurements>;\n headerHeight: number;\n gripWidth: number;\n actionWidth: number;\n groupActionWidth?: number;\n tabWidth: number;\n tabMinWidth: number;\n width: number;\n height: number;\n}\n\nexport interface WorkspaceMeasurementChange {\n readonly measurements: WorkspaceMeasurements;\n readonly spaceChanged: boolean;\n}\n\nconst measurementProperties = [\n 'gap',\n 'headerHeight',\n 'gripWidth',\n 'actionWidth',\n 'groupActionWidth',\n 'tabWidth',\n 'tabMinWidth',\n 'width',\n 'height'\n] as const;\n\nexport function sameWorkspaceMeasurements(previous: WorkspaceMeasurements | undefined, current: WorkspaceMeasurements) {\n return (\n previous !== undefined &&\n measurementProperties.every(property => previous[property] === current[property]) &&\n samePanelMeasurements(previous.panels, current.panels)\n );\n}\n\nfunction samePanelMeasurements(previous: WorkspaceMeasurements['panels'], current: WorkspaceMeasurements['panels']) {\n if (previous === current) return true;\n if (!previous || !current) return false;\n for (const id in previous) {\n const before = previous[id]!;\n const after = current[id];\n if (!after || before.suffixWidth !== after.suffixWidth || before.footerHeight !== after.footerHeight) {\n return false;\n }\n }\n for (const id in current) {\n if (!Object.hasOwn(previous, id)) return false;\n }\n return true;\n}\n"],"mappings":";AA0BA,IAAMA,IAAwB;CAC5B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAgBC,EAA0B,GAA6C,GAAgC;CACrH,OACEC,MAAa,KAAA,KACbF,EAAsB,OAAM,MAAYE,EAASC,OAAcC,EAAQD,EAAS,KAChFE,EAAsBH,EAAS,QAAQE,EAAQ,MAAM;AAEzD;AAEA,SAASC,EAAsB,GAA2C,GAA0C;CAClH,IAAIH,MAAaE,GAAS,OAAO;CACjC,IAAI,CAACF,KAAY,CAACE,GAAS,OAAO;CAClC,KAAK,IAAME,KAAMJ,GAAU;EACzB,IAAMK,IAASL,EAASI,IAClBE,IAAQJ,EAAQE;EACtB,IAAI,CAACE,KAASD,EAAO,gBAAgBC,EAAM,eAAeD,EAAO,iBAAiBC,EAAM,cACtF,OAAO;CAEX;CACA,KAAK,IAAMF,KAAMF,GACf,IAAI,CAAC,OAAO,OAAOF,GAAUI,CAAE,GAAG,OAAO;CAE3C,OAAO;AACT"}
1
+ {"version":3,"file":"measurements.utils.js","names":["sameWorkspaceMeasurements","previous","sameWorkspaceMeasurementScalars","current","samePanelMeasurements","id","before","after"],"sources":["../../../../src/workspace/utils/presentation/measurements.utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nexport interface WorkspacePanelMeasurements {\n suffixWidth: number;\n footerHeight: number;\n}\n\nexport interface WorkspaceMeasurements {\n gap: number;\n panels?: Record<string, WorkspacePanelMeasurements>;\n headerHeight: number;\n gripWidth: number;\n actionWidth: number;\n groupActionWidth?: number;\n tabWidth: number;\n tabMinWidth: number;\n width: number;\n height: number;\n}\n\nexport interface WorkspaceMeasurementChange {\n readonly measurements: WorkspaceMeasurements;\n readonly spaceChanged: boolean;\n}\n\nexport function sameWorkspaceMeasurements(previous: WorkspaceMeasurements | undefined, current: WorkspaceMeasurements) {\n return (\n previous !== undefined &&\n sameWorkspaceMeasurementScalars(previous, current) &&\n samePanelMeasurements(previous.panels, current.panels)\n );\n}\n\nexport function sameWorkspaceMeasurementScalars(previous: WorkspaceMeasurements, current: WorkspaceMeasurements) {\n return (\n previous.gap === current.gap &&\n previous.headerHeight === current.headerHeight &&\n previous.gripWidth === current.gripWidth &&\n previous.actionWidth === current.actionWidth &&\n previous.groupActionWidth === current.groupActionWidth &&\n previous.tabWidth === current.tabWidth &&\n previous.tabMinWidth === current.tabMinWidth &&\n previous.width === current.width &&\n previous.height === current.height\n );\n}\n\nfunction samePanelMeasurements(previous: WorkspaceMeasurements['panels'], current: WorkspaceMeasurements['panels']) {\n if (previous === current) return true;\n if (!previous || !current) return false;\n for (const id in previous) {\n const before = previous[id]!;\n const after = current[id];\n if (!after || before.suffixWidth !== after.suffixWidth || before.footerHeight !== after.footerHeight) {\n return false;\n }\n }\n for (const id in current) {\n if (!Object.hasOwn(previous, id)) return false;\n }\n return true;\n}\n"],"mappings":";AA0BA,SAAgBA,EAA0B,GAA6C,GAAgC;CACrH,OACEC,MAAa,KAAA,KACbC,EAAgCD,GAAUE,CAAO,KACjDC,EAAsBH,EAAS,QAAQE,EAAQ,MAAM;AAEzD;AAEA,SAAgBD,EAAgC,GAAiC,GAAgC;CAC/G,OACED,EAAS,QAAQE,EAAQ,OACzBF,EAAS,iBAAiBE,EAAQ,gBAClCF,EAAS,cAAcE,EAAQ,aAC/BF,EAAS,gBAAgBE,EAAQ,eACjCF,EAAS,qBAAqBE,EAAQ,oBACtCF,EAAS,aAAaE,EAAQ,YAC9BF,EAAS,gBAAgBE,EAAQ,eACjCF,EAAS,UAAUE,EAAQ,SAC3BF,EAAS,WAAWE,EAAQ;AAEhC;AAEA,SAASC,EAAsB,GAA2C,GAA0C;CAClH,IAAIH,MAAaE,GAAS,OAAO;CACjC,IAAI,CAACF,KAAY,CAACE,GAAS,OAAO;CAClC,KAAK,IAAME,KAAMJ,GAAU;EACzB,IAAMK,IAASL,EAASI,IAClBE,IAAQJ,EAAQE;EACtB,IAAI,CAACE,KAASD,EAAO,gBAAgBC,EAAM,eAAeD,EAAO,iBAAiBC,EAAM,cACtF,OAAO;CAEX;CACA,KAAK,IAAMF,KAAMF,GACf,IAAI,CAAC,OAAO,OAAOF,GAAUI,CAAE,GAAG,OAAO;CAE3C,OAAO;AACT"}
@@ -36,7 +36,7 @@ export declare function getOfferPresentation(input: {
36
36
  }): {
37
37
  cue: Rect;
38
38
  seams: WorkspaceSeamPreview | undefined;
39
- context: import("../placement/context.utils.js").WorkspacePlacementContext | undefined;
39
+ scope: import("../placement/scope.utils.js").WorkspacePlacementScope | undefined;
40
40
  };
41
41
  /** Chooses live reservation separately from the unchanged candidate and commitment. */
42
42
  export declare function getOfferAllocation(plan: WorkspacePlacementPlan, feasible: boolean): WorkspaceDragAllocation;
@@ -1,17 +1,17 @@
1
1
  import { clipCue as e } from "../interaction/drag-preview.utils.js";
2
- import { getAffectedSplitContext as t } from "../placement/context.utils.js";
2
+ import { getAffectedSplitScope as t } from "../placement/scope.utils.js";
3
3
  // src/workspace/utils/presentation/offers.utils.ts
4
4
  function n(n) {
5
5
  let r = n.split?.groups.find((e) => e.panels.some((e) => e.id === n.destination.target)), o = r && n.destination.position !== "tab" ? a(n.client(r.rect), n.destination.position) : void 0, s = e(o?.cue ?? n.cue, n.bounds);
6
6
  return {
7
7
  cue: s,
8
8
  seams: i(n, o, s),
9
- context: r && n.split ? t({
9
+ scope: r && n.split ? t({
10
10
  baseline: n.split.regions,
11
11
  achieved: n.regions,
12
12
  source: n.split.source,
13
13
  recipient: r.panels.map((e) => e.id)
14
- }) : n.plan.context
14
+ }) : n.plan.scope
15
15
  };
16
16
  }
17
17
  function r(e, t) {
@@ -1 +1 @@
1
- {"version":3,"file":"offers.utils.js","names":["getOfferPresentation","recipient","input","group","panel","intent","splitIntent","cue","clipCue","offerSeams","getAffectedSplitContext","getOfferAllocation","plan","feasible","intentSeam","planSeamPreview","horizontal","position","rect","edge","vertical","seamPoint","x","bounds","y","fraction","parts","sourceKey","index","parent","regions","region","source","before","after","facing","edges","neighbor","delta","side","describeEdge","halfGap","gaps","r","offset","inside","point","extent"],"sources":["../../../../src/workspace/utils/presentation/offers.utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Point, Rect } from '@nvidia-elements/core/internal';\n\nimport { clipCue } from '../interaction/drag-preview.utils.js';\nimport { getAffectedSplitContext } from '../placement/context.utils.js';\n\nimport type { WorkspacePlacedRegion } from '../geometry/allocation.utils.js';\nimport type { WorkspaceDragAllocation } from '../interaction/drag-preview.utils.js';\nimport type { WorkspaceDestination } from '../model/operations.utils.js';\nimport type { WorkspacePlacementPlan } from '../placement/planner.utils.js';\n\ntype WorkspaceSeamSide = 'left' | 'right' | 'top' | 'bottom';\n\nexport interface WorkspaceSeamPreview {\n kind: 'split' | 'insert';\n edges: { side: WorkspaceSeamSide; offset: number; fraction: number }[];\n}\n\ninterface SeamInput {\n plan: WorkspacePlacementPlan;\n regions: WorkspacePlacedRegion[];\n sourceKey: string;\n sourceRect: Rect;\n cue: Rect;\n bounds: Rect;\n position: WorkspaceDestination['position'];\n}\n\n/** Separates a group split's intended half from its constrained committed allocation. */\nexport function getOfferPresentation(input: {\n plan: WorkspacePlacementPlan;\n regions: WorkspacePlacedRegion[];\n sourceKey: string;\n sourceRect: Rect;\n cue: Rect;\n bounds: Rect;\n destination: WorkspaceDestination;\n feasible: boolean;\n split?: {\n source: string[];\n regions: WorkspacePlacedRegion[];\n groups: { panels: { id: string }[]; rect: Rect }[];\n };\n client(rect: Rect): Rect;\n}) {\n const recipient = input.split?.groups.find(group =>\n group.panels.some(panel => panel.id === input.destination.target)\n );\n const intent =\n recipient && input.destination.position !== 'tab'\n ? splitIntent(input.client(recipient.rect), input.destination.position)\n : undefined;\n const cue = clipCue(intent?.cue ?? input.cue, input.bounds);\n return {\n cue,\n seams: offerSeams(input, intent, cue),\n context:\n recipient && input.split\n ? getAffectedSplitContext({\n baseline: input.split.regions,\n achieved: input.regions,\n source: input.split.source,\n recipient: recipient.panels.map(panel => panel.id)\n })\n : input.plan.context\n };\n}\n\n/** Chooses live reservation separately from the unchanged candidate and commitment. */\nexport function getOfferAllocation(plan: WorkspacePlacementPlan, feasible: boolean): WorkspaceDragAllocation {\n return (plan.kind === 'reorder' || plan.kind === 'tab' || plan.kind === 'insert') && feasible\n ? { allocation: 'reserved', layout: plan.layout }\n : { allocation: 'overlay' };\n}\n\nfunction offerSeams(\n input: Parameters<typeof getOfferPresentation>[0],\n intent: ReturnType<typeof splitIntent> | undefined,\n cue: Rect\n): WorkspaceSeamPreview | undefined {\n if (!input.feasible) return undefined;\n if (intent) return intentSeam(intent, cue, input.bounds);\n return planSeamPreview({ ...input, cue, position: input.destination.position });\n}\n\nfunction splitIntent(rect: Rect, position: Exclude<WorkspaceDestination['position'], 'tab'>) {\n const horizontal = position === 'left' || position === 'right';\n const cue = {\n x: rect.x + (position === 'right' ? rect.width / 2 : 0),\n y: rect.y + (position === 'bottom' ? rect.height / 2 : 0),\n width: horizontal ? rect.width / 2 : rect.width,\n height: horizontal ? rect.height : rect.height / 2\n };\n const side = { left: 'right', right: 'left', top: 'bottom', bottom: 'top' } as const;\n const seams: WorkspaceSeamPreview = { kind: 'split', edges: [{ side: side[position], offset: 0, fraction: 0.5 }] };\n return { cue, seams };\n}\n\nfunction intentSeam(\n intent: { cue: Rect; seams: WorkspaceSeamPreview },\n cue: Rect,\n bounds: Rect\n): WorkspaceSeamPreview | undefined {\n const edge = intent.seams.edges[0]!;\n const vertical = edge.side === 'left' || edge.side === 'right';\n const { x, y } = seamPoint(intent.cue, edge.side, 0);\n if (x < bounds.x || x >= bounds.x + bounds.width || y < bounds.y || y >= bounds.y + bounds.height) return undefined;\n const fraction = vertical ? (y - cue.y) / cue.height : (x - cue.x) / cue.width;\n return Number.isFinite(fraction) ? { kind: 'split', edges: [{ ...edge, fraction }] } : undefined;\n}\n\n/** Describes only the actual adjoining seams of one evaluated candidate. */\nfunction planSeamPreview(input: SeamInput): WorkspaceSeamPreview | undefined {\n const { plan, regions, sourceKey, position } = input;\n if (plan.kind === 'tab' || position === 'tab') return undefined;\n const parts = sourceKey.split('.');\n const index = Number(parts.pop());\n const parent = regions.find(region => region.key === parts.join('.'));\n const source = regions.find(region => region.key === sourceKey);\n if (!source || parent?.node.type !== 'split') return undefined;\n const horizontal = parent.node.axis === 'horizontal';\n const [before, after] = horizontal ? (['left', 'right'] as const) : (['top', 'bottom'] as const);\n const facing = { left: 'right', right: 'left', top: 'bottom', bottom: 'top' }[position];\n const edges = [-1, 1].flatMap(delta => {\n const neighbor = regions.find(region => region.key === `${parent.key}.${index + delta}`);\n const side = delta < 0 ? before : after;\n if (!neighbor || (plan.kind === 'split' && side !== facing)) return [];\n const offset = halfGap(source.rect, neighbor.rect, side);\n const edge = describeEdge(input, side, offset);\n return edge ? [edge] : [];\n });\n const kind = { reorder: 'insert', insert: 'insert', split: 'split' } as const;\n return edges.length ? { kind: kind[plan.kind], edges } : undefined;\n}\n\nfunction halfGap(source: Rect, neighbor: Rect, side: WorkspaceSeamSide) {\n const gaps = {\n left: source.x - neighbor.x - neighbor.width,\n right: neighbor.x - source.x - source.width,\n top: source.y - neighbor.y - neighbor.height,\n bottom: neighbor.y - source.y - source.height\n };\n return Math.max(0, gaps[side]) / 2;\n}\n\nfunction seamPoint(r: Rect, side: WorkspaceSeamSide, offset: number) {\n switch (side) {\n case 'left':\n return { x: r.x - offset, y: r.y + r.height / 2 };\n case 'right':\n return { x: r.x + r.width + offset, y: r.y + r.height / 2 };\n case 'top':\n return { x: r.x + r.width / 2, y: r.y - offset };\n case 'bottom':\n return { x: r.x + r.width / 2, y: r.y + r.height + offset };\n }\n}\n\nfunction inside(rect: Rect, point: Point) {\n return point.x >= rect.x && point.x < rect.x + rect.width && point.y >= rect.y && point.y < rect.y + rect.height;\n}\n\nfunction describeEdge(input: SeamInput, side: WorkspaceSeamSide, offset: number) {\n const { cue, bounds } = input;\n const point = seamPoint(input.sourceRect, side, offset);\n if (!inside(bounds, point)) return undefined;\n const vertical = side === 'left' || side === 'right';\n const extent = vertical ? cue.height : cue.width;\n if (extent <= 0) return undefined;\n const fraction = ((vertical ? point.y : point.x) - (vertical ? cue.y : cue.x)) / extent;\n return fraction >= 0 && fraction <= 1 ? { side, offset, fraction } : undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"offers.utils.js","names":["getOfferPresentation","recipient","input","group","panel","intent","splitIntent","cue","clipCue","offerSeams","getAffectedSplitScope","getOfferAllocation","plan","feasible","intentSeam","planSeamPreview","horizontal","position","rect","edge","vertical","seamPoint","x","bounds","y","fraction","parts","sourceKey","index","parent","regions","region","source","before","after","facing","edges","neighbor","delta","side","describeEdge","halfGap","gaps","r","offset","inside","point","extent"],"sources":["../../../../src/workspace/utils/presentation/offers.utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Point, Rect } from '@nvidia-elements/core/internal';\n\nimport { clipCue } from '../interaction/drag-preview.utils.js';\nimport { getAffectedSplitScope } from '../placement/scope.utils.js';\n\nimport type { WorkspacePlacedRegion } from '../geometry/allocation.utils.js';\nimport type { WorkspaceDragAllocation } from '../interaction/drag-preview.utils.js';\nimport type { WorkspaceDestination } from '../model/operations.utils.js';\nimport type { WorkspacePlacementPlan } from '../placement/planner.utils.js';\n\ntype WorkspaceSeamSide = 'left' | 'right' | 'top' | 'bottom';\n\nexport interface WorkspaceSeamPreview {\n kind: 'split' | 'insert';\n edges: { side: WorkspaceSeamSide; offset: number; fraction: number }[];\n}\n\ninterface SeamInput {\n plan: WorkspacePlacementPlan;\n regions: WorkspacePlacedRegion[];\n sourceKey: string;\n sourceRect: Rect;\n cue: Rect;\n bounds: Rect;\n position: WorkspaceDestination['position'];\n}\n\n/** Separates a group split's intended half from its constrained committed allocation. */\nexport function getOfferPresentation(input: {\n plan: WorkspacePlacementPlan;\n regions: WorkspacePlacedRegion[];\n sourceKey: string;\n sourceRect: Rect;\n cue: Rect;\n bounds: Rect;\n destination: WorkspaceDestination;\n feasible: boolean;\n split?: {\n source: string[];\n regions: WorkspacePlacedRegion[];\n groups: { panels: { id: string }[]; rect: Rect }[];\n };\n client(rect: Rect): Rect;\n}) {\n const recipient = input.split?.groups.find(group =>\n group.panels.some(panel => panel.id === input.destination.target)\n );\n const intent =\n recipient && input.destination.position !== 'tab'\n ? splitIntent(input.client(recipient.rect), input.destination.position)\n : undefined;\n const cue = clipCue(intent?.cue ?? input.cue, input.bounds);\n return {\n cue,\n seams: offerSeams(input, intent, cue),\n scope:\n recipient && input.split\n ? getAffectedSplitScope({\n baseline: input.split.regions,\n achieved: input.regions,\n source: input.split.source,\n recipient: recipient.panels.map(panel => panel.id)\n })\n : input.plan.scope\n };\n}\n\n/** Chooses live reservation separately from the unchanged candidate and commitment. */\nexport function getOfferAllocation(plan: WorkspacePlacementPlan, feasible: boolean): WorkspaceDragAllocation {\n return (plan.kind === 'reorder' || plan.kind === 'tab' || plan.kind === 'insert') && feasible\n ? { allocation: 'reserved', layout: plan.layout }\n : { allocation: 'overlay' };\n}\n\nfunction offerSeams(\n input: Parameters<typeof getOfferPresentation>[0],\n intent: ReturnType<typeof splitIntent> | undefined,\n cue: Rect\n): WorkspaceSeamPreview | undefined {\n if (!input.feasible) return undefined;\n if (intent) return intentSeam(intent, cue, input.bounds);\n return planSeamPreview({ ...input, cue, position: input.destination.position });\n}\n\nfunction splitIntent(rect: Rect, position: Exclude<WorkspaceDestination['position'], 'tab'>) {\n const horizontal = position === 'left' || position === 'right';\n const cue = {\n x: rect.x + (position === 'right' ? rect.width / 2 : 0),\n y: rect.y + (position === 'bottom' ? rect.height / 2 : 0),\n width: horizontal ? rect.width / 2 : rect.width,\n height: horizontal ? rect.height : rect.height / 2\n };\n const side = { left: 'right', right: 'left', top: 'bottom', bottom: 'top' } as const;\n const seams: WorkspaceSeamPreview = { kind: 'split', edges: [{ side: side[position], offset: 0, fraction: 0.5 }] };\n return { cue, seams };\n}\n\nfunction intentSeam(\n intent: { cue: Rect; seams: WorkspaceSeamPreview },\n cue: Rect,\n bounds: Rect\n): WorkspaceSeamPreview | undefined {\n const edge = intent.seams.edges[0]!;\n const vertical = edge.side === 'left' || edge.side === 'right';\n const { x, y } = seamPoint(intent.cue, edge.side, 0);\n if (x < bounds.x || x >= bounds.x + bounds.width || y < bounds.y || y >= bounds.y + bounds.height) return undefined;\n const fraction = vertical ? (y - cue.y) / cue.height : (x - cue.x) / cue.width;\n return Number.isFinite(fraction) ? { kind: 'split', edges: [{ ...edge, fraction }] } : undefined;\n}\n\n/** Describes only the actual adjoining seams of one evaluated candidate. */\nfunction planSeamPreview(input: SeamInput): WorkspaceSeamPreview | undefined {\n const { plan, regions, sourceKey, position } = input;\n if (plan.kind === 'tab' || position === 'tab') return undefined;\n const parts = sourceKey.split('.');\n const index = Number(parts.pop());\n const parent = regions.find(region => region.key === parts.join('.'));\n const source = regions.find(region => region.key === sourceKey);\n if (!source || parent?.node.type !== 'split') return undefined;\n const horizontal = parent.node.axis === 'horizontal';\n const [before, after] = horizontal ? (['left', 'right'] as const) : (['top', 'bottom'] as const);\n const facing = { left: 'right', right: 'left', top: 'bottom', bottom: 'top' }[position];\n const edges = [-1, 1].flatMap(delta => {\n const neighbor = regions.find(region => region.key === `${parent.key}.${index + delta}`);\n const side = delta < 0 ? before : after;\n if (!neighbor || (plan.kind === 'split' && side !== facing)) return [];\n const offset = halfGap(source.rect, neighbor.rect, side);\n const edge = describeEdge(input, side, offset);\n return edge ? [edge] : [];\n });\n const kind = { reorder: 'insert', insert: 'insert', split: 'split' } as const;\n return edges.length ? { kind: kind[plan.kind], edges } : undefined;\n}\n\nfunction halfGap(source: Rect, neighbor: Rect, side: WorkspaceSeamSide) {\n const gaps = {\n left: source.x - neighbor.x - neighbor.width,\n right: neighbor.x - source.x - source.width,\n top: source.y - neighbor.y - neighbor.height,\n bottom: neighbor.y - source.y - source.height\n };\n return Math.max(0, gaps[side]) / 2;\n}\n\nfunction seamPoint(r: Rect, side: WorkspaceSeamSide, offset: number) {\n switch (side) {\n case 'left':\n return { x: r.x - offset, y: r.y + r.height / 2 };\n case 'right':\n return { x: r.x + r.width + offset, y: r.y + r.height / 2 };\n case 'top':\n return { x: r.x + r.width / 2, y: r.y - offset };\n case 'bottom':\n return { x: r.x + r.width / 2, y: r.y + r.height + offset };\n }\n}\n\nfunction inside(rect: Rect, point: Point) {\n return point.x >= rect.x && point.x < rect.x + rect.width && point.y >= rect.y && point.y < rect.y + rect.height;\n}\n\nfunction describeEdge(input: SeamInput, side: WorkspaceSeamSide, offset: number) {\n const { cue, bounds } = input;\n const point = seamPoint(input.sourceRect, side, offset);\n if (!inside(bounds, point)) return undefined;\n const vertical = side === 'left' || side === 'right';\n const extent = vertical ? cue.height : cue.width;\n if (extent <= 0) return undefined;\n const fraction = ((vertical ? point.y : point.x) - (vertical ? cue.y : cue.x)) / extent;\n return fraction >= 0 && fraction <= 1 ? { side, offset, fraction } : undefined;\n}\n"],"mappings":"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"}
@@ -1,6 +1,12 @@
1
1
  import type { Rect } from '@nvidia-elements/core/internal';
2
2
  import type { WorkspacePlacedRegion } from '../geometry/allocation.utils.js';
3
3
  import type { WorkspaceDragPreview } from '../interaction/drag-preview.utils.js';
4
+ export interface PreviewBoundary {
5
+ key: string;
6
+ rect: Rect;
7
+ style: string;
8
+ }
9
+ export declare function preparePreviewBoundaries(catalogRegions: WorkspacePlacedRegion[], placedRegions: WorkspacePlacedRegion[], sourcePanelIds: string[]): PreviewBoundary[];
4
10
  export declare function shouldEmphasizeReservedDestination(preview: WorkspaceDragPreview): boolean;
5
11
  /** Rounds only corners shared with the visible group geometry. */
6
12
  export declare function getRegionCornerStyle(rect: Rect, regions: WorkspacePlacedRegion[]): string;
@@ -1,8 +1,19 @@
1
+ import { getAllocatedScopes as e } from "../placement/scope.utils.js";
1
2
  // src/workspace/utils/presentation/preview.utils.ts
2
- function e(e) {
3
+ function t(t, n, i) {
4
+ return e({
5
+ catalog: t,
6
+ allocation: n,
7
+ source: i
8
+ }).map((e) => ({
9
+ ...e,
10
+ style: `left:${e.rect.x}px;top:${e.rect.y}px;width:${e.rect.width}px;height:${e.rect.height}px;${r(e.rect, n)}`
11
+ }));
12
+ }
13
+ function n(e) {
3
14
  return !e.blocked && "allocation" in e && e.allocation === "reserved" && (e.intent.kind === "order" || e.intent.kind === "insert" || e.phase === "detached" && e.intent.kind === "invalid" && e.cue.kind === "group");
4
15
  }
5
- function t(e, t) {
16
+ function r(e, t) {
6
17
  let n = [
7
18
  [
8
19
  "top-left",
@@ -31,6 +42,6 @@ function t(e, t) {
31
42
  }
32
43
  return r.join(";");
33
44
  }
34
- export { t as getRegionCornerStyle, e as shouldEmphasizeReservedDestination };
45
+ export { r as getRegionCornerStyle, t as preparePreviewBoundaries, n as shouldEmphasizeReservedDestination };
35
46
 
36
47
  //# sourceMappingURL=preview.utils.js.map