@dxos/plugin-deck 0.9.0 → 0.9.1-staging.ee54ba693a

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 (314) hide show
  1. package/PLUGIN.mdl +3 -1
  2. package/dist/lib/neutral/{DeckLayout-DEURA3KR.mjs → DeckLayout-B3RUUV5P.mjs} +36 -22
  3. package/dist/lib/neutral/DeckLayout-B3RUUV5P.mjs.map +7 -0
  4. package/dist/lib/neutral/DeckPlugin.mjs +4 -8
  5. package/dist/lib/neutral/DeckPlugin.mjs.map +4 -4
  6. package/dist/lib/neutral/DeckSettings-Z7GMB7XJ.mjs +32 -0
  7. package/dist/lib/neutral/DeckSettings-Z7GMB7XJ.mjs.map +7 -0
  8. package/dist/lib/neutral/{add-toast-TNB6DXWU.mjs → add-toast-BHNEH5UY.mjs} +2 -2
  9. package/dist/lib/neutral/{adjust-HNU5CCRO.mjs → adjust-MJB6UMRQ.mjs} +16 -10
  10. package/dist/lib/neutral/adjust-MJB6UMRQ.mjs.map +7 -0
  11. package/dist/lib/neutral/{app-graph-builder-HMLT627T.mjs → app-graph-builder-2F7P4IFQ.mjs} +8 -7
  12. package/dist/lib/neutral/app-graph-builder-2F7P4IFQ.mjs.map +7 -0
  13. package/dist/lib/neutral/capabilities/index.mjs +8 -8
  14. package/dist/lib/neutral/{check-app-scheme-INSOF72J.mjs → check-app-scheme-HSAORHHX.mjs} +2 -2
  15. package/dist/lib/neutral/chunk-FKGYHBUB.mjs +8 -0
  16. package/dist/lib/neutral/{chunk-BS4EOYMK.mjs → chunk-ICOJ4GMB.mjs} +37 -19
  17. package/dist/lib/neutral/chunk-ICOJ4GMB.mjs.map +7 -0
  18. package/dist/lib/neutral/chunk-KY7LMF4D.mjs +46 -0
  19. package/dist/lib/neutral/chunk-KY7LMF4D.mjs.map +7 -0
  20. package/dist/lib/neutral/{chunk-324PPIZB.mjs → chunk-KZFJ4PBT.mjs} +4 -4
  21. package/dist/lib/neutral/chunk-KZFJ4PBT.mjs.map +7 -0
  22. package/dist/lib/neutral/{chunk-GBIGQKYW.mjs → chunk-PP32NEVY.mjs} +4 -4
  23. package/dist/lib/neutral/chunk-PP32NEVY.mjs.map +7 -0
  24. package/dist/lib/neutral/chunk-SONATB35.mjs +20 -0
  25. package/dist/lib/neutral/chunk-SONATB35.mjs.map +7 -0
  26. package/dist/lib/neutral/chunk-Z2B5RSVI.mjs +1096 -0
  27. package/dist/lib/neutral/chunk-Z2B5RSVI.mjs.map +7 -0
  28. package/dist/lib/neutral/{close-ASKR22A6.mjs → close-2YGB5IIP.mjs} +4 -3
  29. package/dist/lib/neutral/{close-ASKR22A6.mjs.map → close-2YGB5IIP.mjs.map} +1 -1
  30. package/dist/lib/neutral/components/index.mjs +201 -16
  31. package/dist/lib/neutral/components/index.mjs.map +4 -4
  32. package/dist/lib/neutral/containers/index.mjs +6 -11
  33. package/dist/lib/neutral/containers/index.mjs.map +3 -3
  34. package/dist/lib/neutral/hooks/index.mjs +101 -17
  35. package/dist/lib/neutral/hooks/index.mjs.map +4 -4
  36. package/dist/lib/neutral/index.mjs +18 -2
  37. package/dist/lib/neutral/meta.json +1 -1
  38. package/dist/lib/neutral/meta.mjs +1 -1
  39. package/dist/lib/neutral/{notification-tracker-P36322BH.mjs → notification-tracker-Y5CRTWJM.mjs} +12 -10
  40. package/dist/lib/neutral/notification-tracker-Y5CRTWJM.mjs.map +7 -0
  41. package/dist/lib/neutral/{open-5OYNO3RT.mjs → open-P6XQ56M7.mjs} +8 -7
  42. package/dist/lib/neutral/open-P6XQ56M7.mjs.map +7 -0
  43. package/dist/lib/neutral/operations/index.mjs +1 -1
  44. package/dist/lib/neutral/plugin.mjs +2 -2
  45. package/dist/lib/neutral/{react-root-HH5DEUOG.mjs → react-root-E6TAFHX6.mjs} +2 -2
  46. package/dist/lib/neutral/react-root-E6TAFHX6.mjs.map +7 -0
  47. package/dist/lib/neutral/{react-surface-3UVVCK3O.mjs → react-surface-H63FXUK3.mjs} +5 -6
  48. package/dist/lib/neutral/react-surface-H63FXUK3.mjs.map +7 -0
  49. package/dist/lib/neutral/{revert-workspace-B2QLT2C4.mjs → revert-workspace-RXWP2TEE.mjs} +2 -2
  50. package/dist/lib/neutral/{scroll-into-view-B52C3PJO.mjs → scroll-into-view-HFV5EGYZ.mjs} +2 -2
  51. package/dist/lib/neutral/{set-PA35ONXO.mjs → set-HOOVB7E4.mjs} +4 -3
  52. package/dist/lib/neutral/{set-PA35ONXO.mjs.map → set-HOOVB7E4.mjs.map} +1 -1
  53. package/dist/lib/neutral/{set-layout-mode-RPCCPQRB.mjs → set-layout-mode-XAGSZZGB.mjs} +2 -2
  54. package/dist/lib/neutral/{settings-EGNYUM4T.mjs → settings-6CDQEB6B.mjs} +3 -3
  55. package/dist/lib/neutral/settings-6CDQEB6B.mjs.map +7 -0
  56. package/dist/lib/neutral/{state-IIDXMQUO.mjs → state-F4X5GPGZ.mjs} +4 -3
  57. package/dist/lib/neutral/state-F4X5GPGZ.mjs.map +7 -0
  58. package/dist/lib/neutral/{switch-workspace-LZF4KZXH.mjs → switch-workspace-PA7N6EUX.mjs} +4 -4
  59. package/dist/lib/neutral/switch-workspace-PA7N6EUX.mjs.map +7 -0
  60. package/dist/lib/neutral/translations.mjs +3 -3
  61. package/dist/lib/neutral/translations.mjs.map +3 -3
  62. package/dist/lib/neutral/types/index.mjs +11 -1
  63. package/dist/lib/neutral/{update-companion-YUCZZVGY.mjs → update-companion-6A2SKQAI.mjs} +14 -6
  64. package/dist/lib/neutral/update-companion-6A2SKQAI.mjs.map +7 -0
  65. package/dist/lib/neutral/{update-complementary-7FZNB55J.mjs → update-complementary-K55I5EV5.mjs} +2 -2
  66. package/dist/lib/neutral/{update-dialog-FNQTSSAP.mjs → update-dialog-Z3GEXMVV.mjs} +2 -2
  67. package/dist/lib/neutral/{update-plank-size-3YW4NXEY.mjs → update-plank-size-BTESVV7N.mjs} +2 -2
  68. package/dist/lib/neutral/{update-popover-G2VUD7E6.mjs → update-popover-QEVDSZSH.mjs} +2 -2
  69. package/dist/lib/neutral/{update-sidebar-KRHPUHUB.mjs → update-sidebar-VSRCN47U.mjs} +2 -2
  70. package/dist/lib/neutral/{url-handler-A6HLW4RB.mjs → url-handler-ERZXXEYR.mjs} +11 -10
  71. package/dist/lib/neutral/url-handler-ERZXXEYR.mjs.map +7 -0
  72. package/dist/types/dx.config.d.ts +28 -0
  73. package/dist/types/dx.config.d.ts.map +1 -0
  74. package/dist/types/src/DeckPlugin.d.ts.map +1 -1
  75. package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -2
  76. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  77. package/dist/types/src/capabilities/index.d.ts +8 -196
  78. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  79. package/dist/types/src/capabilities/notification-tracker.d.ts +1 -1
  80. package/dist/types/src/capabilities/notification-tracker.d.ts.map +1 -1
  81. package/dist/types/src/capabilities/react-root.d.ts +1 -5
  82. package/dist/types/src/capabilities/react-root.d.ts.map +1 -1
  83. package/dist/types/src/capabilities/react-surface.d.ts +2 -2
  84. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  85. package/dist/types/src/capabilities/settings.d.ts +1 -3
  86. package/dist/types/src/capabilities/settings.d.ts.map +1 -1
  87. package/dist/types/src/capabilities/state.d.ts +1 -187
  88. package/dist/types/src/capabilities/state.d.ts.map +1 -1
  89. package/dist/types/src/capabilities/tools.d.ts +1 -1
  90. package/dist/types/src/capabilities/url-handler.d.ts +1 -1
  91. package/dist/types/src/components/Companion/Companion.d.ts +17 -0
  92. package/dist/types/src/components/Companion/Companion.d.ts.map +1 -0
  93. package/dist/types/src/components/Companion/index.d.ts +2 -0
  94. package/dist/types/src/components/Companion/index.d.ts.map +1 -0
  95. package/dist/types/src/components/Matrix/Matrix.d.ts +1 -1
  96. package/dist/types/src/components/Matrix/Matrix.d.ts.map +1 -1
  97. package/dist/types/src/components/Matrix/Matrix.stories.d.ts +2 -2
  98. package/dist/types/src/components/Matrix/Matrix.stories.d.ts.map +1 -1
  99. package/dist/types/src/components/Matrix/index.d.ts +1 -1
  100. package/dist/types/src/components/Matrix/index.d.ts.map +1 -1
  101. package/dist/types/src/components/Pane/Pane.d.ts +45 -0
  102. package/dist/types/src/components/Pane/Pane.d.ts.map +1 -0
  103. package/dist/types/src/components/Pane/Pane.stories.d.ts +7 -0
  104. package/dist/types/src/components/Pane/Pane.stories.d.ts.map +1 -0
  105. package/dist/types/src/components/Pane/index.d.ts +2 -0
  106. package/dist/types/src/components/Pane/index.d.ts.map +1 -0
  107. package/dist/types/src/components/Plank/Plank.d.ts +74 -0
  108. package/dist/types/src/components/Plank/Plank.d.ts.map +1 -0
  109. package/dist/types/src/components/Plank/Plank.stories.d.ts +6 -0
  110. package/dist/types/src/components/Plank/Plank.stories.d.ts.map +1 -0
  111. package/dist/types/src/components/Plank/index.d.ts +2 -0
  112. package/dist/types/src/components/Plank/index.d.ts.map +1 -0
  113. package/dist/types/src/components/index.d.ts +3 -2
  114. package/dist/types/src/components/index.d.ts.map +1 -1
  115. package/dist/types/src/containers/Deck/Banner.d.ts.map +1 -1
  116. package/dist/types/src/containers/Deck/Deck.d.ts +1 -1
  117. package/dist/types/src/containers/Deck/Deck.d.ts.map +1 -1
  118. package/dist/types/src/containers/Deck/Deck.stories.d.ts +2 -2
  119. package/dist/types/src/containers/Deck/DeckPlank.d.ts +21 -0
  120. package/dist/types/src/containers/Deck/DeckPlank.d.ts.map +1 -0
  121. package/dist/types/src/containers/Deck/DeckViewport.d.ts +3 -1
  122. package/dist/types/src/containers/Deck/DeckViewport.d.ts.map +1 -1
  123. package/dist/types/src/containers/{Plank → Deck}/PlankControls.d.ts +3 -10
  124. package/dist/types/src/containers/Deck/PlankControls.d.ts.map +1 -0
  125. package/dist/types/src/containers/Deck/PlankFallback.d.ts +6 -0
  126. package/dist/types/src/containers/Deck/PlankFallback.d.ts.map +1 -0
  127. package/dist/types/src/containers/Deck/StatusBar.d.ts.map +1 -1
  128. package/dist/types/src/containers/Deck/useDeckPlank.d.ts +49 -0
  129. package/dist/types/src/containers/Deck/useDeckPlank.d.ts.map +1 -0
  130. package/dist/types/src/containers/DeckLayout/DeckLayout.stories.d.ts +2 -2
  131. package/dist/types/src/containers/DeckLayout/DeckLayout.stories.d.ts.map +1 -1
  132. package/dist/types/src/containers/DeckLayout/Popover.d.ts.map +1 -1
  133. package/dist/types/src/{components → containers}/DeckSettings/DeckSettings.d.ts +1 -1
  134. package/dist/types/src/containers/DeckSettings/DeckSettings.d.ts.map +1 -0
  135. package/dist/types/src/{components → containers}/DeckSettings/DeckSettings.stories.d.ts +2 -2
  136. package/dist/types/src/{components → containers}/DeckSettings/DeckSettings.stories.d.ts.map +1 -1
  137. package/dist/types/src/containers/DeckSettings/index.d.ts.map +1 -0
  138. package/dist/types/src/containers/Sidebar/ComplementarySidebar.d.ts.map +1 -1
  139. package/dist/types/src/containers/index.d.ts +1 -1
  140. package/dist/types/src/containers/index.d.ts.map +1 -1
  141. package/dist/types/src/hooks/index.d.ts +2 -0
  142. package/dist/types/src/hooks/index.d.ts.map +1 -1
  143. package/dist/types/src/hooks/useCompanionSplit.d.ts +28 -0
  144. package/dist/types/src/hooks/useCompanionSplit.d.ts.map +1 -0
  145. package/dist/types/src/hooks/useCompanions.d.ts +11 -7
  146. package/dist/types/src/hooks/useCompanions.d.ts.map +1 -1
  147. package/dist/types/src/hooks/useDeckCompanions.d.ts +2 -2
  148. package/dist/types/src/hooks/useDeckCompanions.d.ts.map +1 -1
  149. package/dist/types/src/hooks/useDeckState.d.ts +1 -1
  150. package/dist/types/src/hooks/useDeckState.d.ts.map +1 -1
  151. package/dist/types/src/hooks/useSelectedCompanionVariant.d.ts +6 -0
  152. package/dist/types/src/hooks/useSelectedCompanionVariant.d.ts.map +1 -0
  153. package/dist/types/src/index.d.ts +1 -0
  154. package/dist/types/src/index.d.ts.map +1 -1
  155. package/dist/types/src/meta.d.ts +32 -2
  156. package/dist/types/src/meta.d.ts.map +1 -1
  157. package/dist/types/src/operations/adjust.d.ts.map +1 -1
  158. package/dist/types/src/operations/open.d.ts.map +1 -1
  159. package/dist/types/src/operations/switch-workspace.d.ts.map +1 -1
  160. package/dist/types/src/operations/update-companion.d.ts.map +1 -1
  161. package/dist/types/src/translations.d.ts +2 -2
  162. package/dist/types/src/translations.d.ts.map +1 -1
  163. package/dist/types/src/types/DeckCapabilities.d.ts +4 -4
  164. package/dist/types/src/types/DeckCapabilities.d.ts.map +1 -1
  165. package/dist/types/src/types/DeckOperation.d.ts +2 -2
  166. package/dist/types/src/types/DeckOperation.d.ts.map +1 -1
  167. package/dist/types/src/types/index.d.ts +1 -0
  168. package/dist/types/src/types/index.d.ts.map +1 -1
  169. package/dist/types/src/types/schema.d.ts +11 -9
  170. package/dist/types/src/types/schema.d.ts.map +1 -1
  171. package/dist/types/src/types/surface.d.ts +12 -0
  172. package/dist/types/src/types/surface.d.ts.map +1 -0
  173. package/dist/types/src/util/companion-view-state.d.ts +16 -0
  174. package/dist/types/src/util/companion-view-state.d.ts.map +1 -0
  175. package/dist/types/src/util/index.d.ts +1 -0
  176. package/dist/types/src/util/index.d.ts.map +1 -1
  177. package/dist/types/src/util/plank-url-params.d.ts +2 -0
  178. package/dist/types/src/util/plank-url-params.d.ts.map +1 -1
  179. package/dist/types/tsconfig.tsbuildinfo +1 -1
  180. package/dx.config.ts +35 -0
  181. package/package.json +50 -48
  182. package/src/DeckPlugin.test.ts +1 -1
  183. package/src/DeckPlugin.ts +2 -3
  184. package/src/capabilities/app-graph-builder.ts +7 -6
  185. package/src/capabilities/check-app-scheme.ts +3 -3
  186. package/src/capabilities/notification-tracker.ts +11 -9
  187. package/src/capabilities/react-root.tsx +1 -1
  188. package/src/capabilities/react-surface.tsx +4 -5
  189. package/src/capabilities/settings.ts +2 -2
  190. package/src/capabilities/state.ts +1 -1
  191. package/src/capabilities/url-handler.ts +7 -15
  192. package/src/components/Companion/Companion.tsx +91 -0
  193. package/src/components/Companion/index.ts +5 -0
  194. package/src/components/Matrix/Matrix.stories.tsx +15 -27
  195. package/src/components/Matrix/Matrix.tsx +3 -3
  196. package/src/components/Matrix/SPEC.md +1 -1
  197. package/src/components/Matrix/index.ts +1 -1
  198. package/src/components/Pane/Pane.stories.tsx +92 -0
  199. package/src/components/Pane/Pane.tsx +178 -0
  200. package/src/components/Pane/index.ts +5 -0
  201. package/src/components/Plank/Plank.stories.tsx +103 -0
  202. package/src/components/Plank/Plank.tsx +149 -0
  203. package/src/components/Plank/index.ts +5 -0
  204. package/src/components/index.ts +3 -4
  205. package/src/containers/Deck/Banner.tsx +5 -4
  206. package/src/containers/Deck/Deck.tsx +3 -3
  207. package/src/containers/Deck/DeckPlank.tsx +187 -0
  208. package/src/containers/Deck/DeckViewport.tsx +118 -246
  209. package/src/containers/{Plank → Deck}/PlankControls.tsx +21 -19
  210. package/src/containers/Deck/PlankFallback.tsx +42 -0
  211. package/src/containers/Deck/StatusBar.tsx +4 -2
  212. package/src/containers/Deck/useDeckPlank.ts +219 -0
  213. package/src/containers/DeckLayout/DeckLayout.stories.tsx +44 -60
  214. package/src/containers/DeckLayout/Dialog.tsx +1 -1
  215. package/src/containers/DeckLayout/Fallback.tsx +1 -1
  216. package/src/containers/DeckLayout/Popover.tsx +37 -20
  217. package/src/containers/DeckLayout/Toast.tsx +1 -1
  218. package/src/{components → containers}/DeckSettings/DeckSettings.stories.tsx +1 -1
  219. package/src/containers/DeckSettings/DeckSettings.tsx +42 -0
  220. package/src/containers/Sidebar/ComplementarySidebar.tsx +8 -7
  221. package/src/containers/Sidebar/Sidebar.tsx +1 -1
  222. package/src/containers/Sidebar/SidebarButton.tsx +3 -3
  223. package/src/containers/index.ts +1 -1
  224. package/src/hooks/index.ts +2 -0
  225. package/src/hooks/useCompanionSplit.ts +78 -0
  226. package/src/hooks/useCompanions.ts +39 -7
  227. package/src/hooks/useDeckCompanions.ts +3 -3
  228. package/src/hooks/useDeckState.ts +1 -1
  229. package/src/hooks/useSelectedCompanionVariant.ts +14 -0
  230. package/src/index.ts +7 -0
  231. package/src/meta.ts +2 -27
  232. package/src/operations/adjust.ts +23 -12
  233. package/src/operations/open.ts +12 -10
  234. package/src/operations/switch-workspace.ts +2 -2
  235. package/src/operations/update-companion.ts +10 -6
  236. package/src/translations.ts +3 -3
  237. package/src/types/DeckCapabilities.ts +6 -4
  238. package/src/types/DeckEvents.ts +1 -1
  239. package/src/types/DeckOperation.ts +3 -2
  240. package/src/types/index.ts +1 -0
  241. package/src/types/schema.ts +21 -16
  242. package/src/types/surface.ts +28 -0
  243. package/src/util/companion-view-state.ts +28 -0
  244. package/src/util/index.ts +1 -0
  245. package/src/util/plank-url-params.test.ts +5 -0
  246. package/src/util/plank-url-params.ts +5 -3
  247. package/src/util/sanitize-persisted-state.test.ts +1 -1
  248. package/src/util/set-active.test.ts +1 -1
  249. package/dist/lib/neutral/DeckLayout-DEURA3KR.mjs.map +0 -7
  250. package/dist/lib/neutral/DeckSettings-W5I57OXM.mjs +0 -27
  251. package/dist/lib/neutral/DeckSettings-W5I57OXM.mjs.map +0 -7
  252. package/dist/lib/neutral/adjust-HNU5CCRO.mjs.map +0 -7
  253. package/dist/lib/neutral/app-graph-builder-HMLT627T.mjs.map +0 -7
  254. package/dist/lib/neutral/chunk-324PPIZB.mjs.map +0 -7
  255. package/dist/lib/neutral/chunk-BS4EOYMK.mjs.map +0 -7
  256. package/dist/lib/neutral/chunk-GBIGQKYW.mjs.map +0 -7
  257. package/dist/lib/neutral/chunk-PYEY5SEC.mjs +0 -37
  258. package/dist/lib/neutral/chunk-PYEY5SEC.mjs.map +0 -7
  259. package/dist/lib/neutral/chunk-Q4W6B4IB.mjs +0 -8
  260. package/dist/lib/neutral/chunk-WAXJPQJI.mjs +0 -1306
  261. package/dist/lib/neutral/chunk-WAXJPQJI.mjs.map +0 -7
  262. package/dist/lib/neutral/notification-tracker-P36322BH.mjs.map +0 -7
  263. package/dist/lib/neutral/open-5OYNO3RT.mjs.map +0 -7
  264. package/dist/lib/neutral/react-root-HH5DEUOG.mjs.map +0 -7
  265. package/dist/lib/neutral/react-surface-3UVVCK3O.mjs.map +0 -7
  266. package/dist/lib/neutral/settings-EGNYUM4T.mjs.map +0 -7
  267. package/dist/lib/neutral/state-IIDXMQUO.mjs.map +0 -7
  268. package/dist/lib/neutral/switch-workspace-LZF4KZXH.mjs.map +0 -7
  269. package/dist/lib/neutral/update-companion-YUCZZVGY.mjs.map +0 -7
  270. package/dist/lib/neutral/url-handler-A6HLW4RB.mjs.map +0 -7
  271. package/dist/types/src/components/DeckSettings/DeckSettings.d.ts.map +0 -1
  272. package/dist/types/src/components/DeckSettings/index.d.ts.map +0 -1
  273. package/dist/types/src/containers/Plank/Plank.d.ts +0 -17
  274. package/dist/types/src/containers/Plank/Plank.d.ts.map +0 -1
  275. package/dist/types/src/containers/Plank/Plank.stories.d.ts +0 -67
  276. package/dist/types/src/containers/Plank/Plank.stories.d.ts.map +0 -1
  277. package/dist/types/src/containers/Plank/PlankComponent.d.ts +0 -17
  278. package/dist/types/src/containers/Plank/PlankComponent.d.ts.map +0 -1
  279. package/dist/types/src/containers/Plank/PlankContent.d.ts +0 -10
  280. package/dist/types/src/containers/Plank/PlankContent.d.ts.map +0 -1
  281. package/dist/types/src/containers/Plank/PlankControls.d.ts.map +0 -1
  282. package/dist/types/src/containers/Plank/PlankError.d.ts +0 -15
  283. package/dist/types/src/containers/Plank/PlankError.d.ts.map +0 -1
  284. package/dist/types/src/containers/Plank/PlankHeading.d.ts +0 -22
  285. package/dist/types/src/containers/Plank/PlankHeading.d.ts.map +0 -1
  286. package/dist/types/src/containers/Plank/PlankLoading.d.ts +0 -3
  287. package/dist/types/src/containers/Plank/PlankLoading.d.ts.map +0 -1
  288. package/dist/types/src/containers/Plank/PlankRoot.d.ts +0 -37
  289. package/dist/types/src/containers/Plank/PlankRoot.d.ts.map +0 -1
  290. package/dist/types/src/containers/Plank/index.d.ts +0 -4
  291. package/dist/types/src/containers/Plank/index.d.ts.map +0 -1
  292. package/src/components/DeckSettings/DeckSettings.tsx +0 -34
  293. package/src/containers/Plank/Plank.stories.tsx +0 -108
  294. package/src/containers/Plank/Plank.tsx +0 -22
  295. package/src/containers/Plank/PlankComponent.tsx +0 -223
  296. package/src/containers/Plank/PlankContent.tsx +0 -45
  297. package/src/containers/Plank/PlankError.tsx +0 -82
  298. package/src/containers/Plank/PlankHeading.tsx +0 -237
  299. package/src/containers/Plank/PlankLoading.tsx +0 -10
  300. package/src/containers/Plank/PlankRoot.tsx +0 -49
  301. package/src/containers/Plank/index.ts +0 -7
  302. /package/dist/lib/neutral/{add-toast-TNB6DXWU.mjs.map → add-toast-BHNEH5UY.mjs.map} +0 -0
  303. /package/dist/lib/neutral/{check-app-scheme-INSOF72J.mjs.map → check-app-scheme-HSAORHHX.mjs.map} +0 -0
  304. /package/dist/lib/neutral/{chunk-Q4W6B4IB.mjs.map → chunk-FKGYHBUB.mjs.map} +0 -0
  305. /package/dist/lib/neutral/{revert-workspace-B2QLT2C4.mjs.map → revert-workspace-RXWP2TEE.mjs.map} +0 -0
  306. /package/dist/lib/neutral/{scroll-into-view-B52C3PJO.mjs.map → scroll-into-view-HFV5EGYZ.mjs.map} +0 -0
  307. /package/dist/lib/neutral/{set-layout-mode-RPCCPQRB.mjs.map → set-layout-mode-XAGSZZGB.mjs.map} +0 -0
  308. /package/dist/lib/neutral/{update-complementary-7FZNB55J.mjs.map → update-complementary-K55I5EV5.mjs.map} +0 -0
  309. /package/dist/lib/neutral/{update-dialog-FNQTSSAP.mjs.map → update-dialog-Z3GEXMVV.mjs.map} +0 -0
  310. /package/dist/lib/neutral/{update-plank-size-3YW4NXEY.mjs.map → update-plank-size-BTESVV7N.mjs.map} +0 -0
  311. /package/dist/lib/neutral/{update-popover-G2VUD7E6.mjs.map → update-popover-QEVDSZSH.mjs.map} +0 -0
  312. /package/dist/lib/neutral/{update-sidebar-KRHPUHUB.mjs.map → update-sidebar-VSRCN47U.mjs.map} +0 -0
  313. /package/dist/types/src/{components → containers}/DeckSettings/index.d.ts +0 -0
  314. /package/src/{components → containers}/DeckSettings/index.ts +0 -0
package/PLUGIN.mdl CHANGED
@@ -44,8 +44,10 @@ type DeckState
44
44
  fullscreen: boolean # true when the solo plank is displayed without any chrome
45
45
  plankSizing: Record<string, number> # persisted plank widths in rem, keyed by item ID
46
46
  companionOpen: boolean # whether the companion pane is visible
47
- companionVariant?: string # which companion variant to display
47
+ companionOrientation?: 'horizontal' | 'vertical' # companion splitter orientation (side-by-side / stacked)
48
48
  companionFrameSizing: Record<string, number> # companion frame widths in rem
49
+ # NOTE: the selected companion variant and per-orientation split size now live in react-ui-attention
50
+ # view state (global, localStorage), not on DeckState — see util/companion-view-state.ts.
49
51
  ```
50
52
 
51
53
  ```mdl
@@ -1,8 +1,9 @@
1
1
  import {
2
2
  Deck,
3
3
  PlankErrorFallback
4
- } from "./chunk-WAXJPQJI.mjs";
5
- import "./chunk-GBIGQKYW.mjs";
4
+ } from "./chunk-Z2B5RSVI.mjs";
5
+ import "./chunk-PP32NEVY.mjs";
6
+ import "./chunk-SONATB35.mjs";
6
7
  import "./chunk-J5LGTIGS.mjs";
7
8
 
8
9
  // src/containers/DeckLayout/DeckLayout.tsx
@@ -86,7 +87,7 @@ import React3, { useCallback as useCallback2, useEffect, useRef, useState } from
86
87
  import { Surface as Surface3 } from "@dxos/app-framework/ui";
87
88
  import { AppSurface as AppSurface3, useObjectMenuItems } from "@dxos/app-toolkit/ui";
88
89
  import { Obj } from "@dxos/echo";
89
- import { Card, Popover, toLocalizedString, Toolbar, useTranslation } from "@dxos/react-ui";
90
+ import { Card, Icon, IconButton, Popover, toLocalizedString, useMediaQuery, useTranslation } from "@dxos/react-ui";
90
91
  import { Menu } from "@dxos/react-ui-menu";
91
92
  import { useDeckState as useDeckState2 } from "#hooks";
92
93
  import { meta } from "#meta";
@@ -116,10 +117,11 @@ var PopoverRoot = ({ children }) => {
116
117
  state.popoverAnchor,
117
118
  state.popoverContent
118
119
  ]);
120
+ const modal = state.popoverKind === "rename";
119
121
  return /* @__PURE__ */ React3.createElement(DeckPopoverProvider, {
120
122
  setOpen
121
123
  }, /* @__PURE__ */ React3.createElement(Popover.Root, {
122
- modal: false,
124
+ modal,
123
125
  open
124
126
  }, state.popoverAnchor && /* @__PURE__ */ React3.createElement(Popover.VirtualTrigger, {
125
127
  key: virtualIter,
@@ -127,7 +129,7 @@ var PopoverRoot = ({ children }) => {
127
129
  }), children));
128
130
  };
129
131
  var PopoverContent = () => {
130
- const { t } = useTranslation(meta.id);
132
+ const { t } = useTranslation(meta.profile.key);
131
133
  const { state, updateEphemeral } = useDeckState2();
132
134
  const { setOpen } = useDeckPopoverContext("PopoverContent");
133
135
  const popoverSubject = state.popoverContent && "subject" in state.popoverContent ? state.popoverContent.subject : void 0;
@@ -136,7 +138,14 @@ var PopoverContent = () => {
136
138
  const title = state.popoverTitle ? toLocalizedString(state.popoverTitle, t) : "Unknown";
137
139
  const icon = isObjectPopover ? Obj.getIcon(popoverSubject)?.icon ?? "ph--circle-dashed--regular" : void 0;
138
140
  const content = state.popoverContent;
139
- const isBasePopover = state.popoverKind === "base" && !!content && "component" in content;
141
+ const isComponentPopover = (state.popoverKind === "base" || state.popoverKind === "rename") && !!content && "component" in content;
142
+ const isRename = state.popoverKind === "rename";
143
+ const [isLg] = useMediaQuery("lg", {
144
+ fallback: [
145
+ true
146
+ ]
147
+ });
148
+ const side = isRename ? isLg ? "right" : "bottom" : state.popoverSide;
140
149
  const handleClose = useCallback2(() => {
141
150
  setOpen(false);
142
151
  updateEphemeral((state2) => ({
@@ -150,25 +159,28 @@ var PopoverContent = () => {
150
159
  updateEphemeral
151
160
  ]);
152
161
  const handleInteractOutside = useCallback2((event) => {
153
- if (
154
- // TODO(thure): CodeMirror should not focus itself when it updates.
155
- event.type === "dismissableLayer.focusOutside" && event.currentTarget?.classList.contains("cm-content")
156
- ) {
162
+ if (event.type === "dismissableLayer.focusOutside") {
157
163
  event.preventDefault();
158
- } else {
159
- handleClose();
164
+ return;
160
165
  }
166
+ handleClose();
161
167
  }, [
162
168
  handleClose
163
169
  ]);
164
170
  return /* @__PURE__ */ React3.createElement(Popover.Portal, null, /* @__PURE__ */ React3.createElement(Popover.Content, {
165
- side: state.popoverSide,
171
+ side,
166
172
  sticky: "always",
167
173
  hideWhenDetached: true,
168
- onOpenAutoFocus: (event) => event.preventDefault(),
174
+ // Rename focuses its input; other popovers keep focus where it was.
175
+ onOpenAutoFocus: isRename ? void 0 : (event) => event.preventDefault(),
169
176
  onInteractOutside: handleInteractOutside,
170
- onEscapeKeyDown: handleInteractOutside
171
- }, /* @__PURE__ */ React3.createElement(Popover.Viewport, null, isBasePopover && content && "component" in content ? (
177
+ onEscapeKeyDown: handleInteractOutside,
178
+ // Reuse the dialog's enter/exit motion so the rename popover does not flicker on open.
179
+ classNames: isRename ? [
180
+ "data-[state=open]:animate-slide-up-and-fade",
181
+ "data-[state=closed]:animate-slide-down-and-fade"
182
+ ] : void 0
183
+ }, /* @__PURE__ */ React3.createElement(Popover.Viewport, null, isComponentPopover && content && "component" in content ? (
172
184
  /* Base popover: a plugin-provided component (e.g. editor link preview). */
173
185
  /* @__PURE__ */ React3.createElement(Surface3.Surface, {
174
186
  type: AppSurface3.Popover,
@@ -186,12 +198,14 @@ var PopoverContent = () => {
186
198
  /* @__PURE__ */ React3.createElement(Menu.Root, null, /* @__PURE__ */ React3.createElement(Card.Root, {
187
199
  border: false,
188
200
  classNames: "dx-card-popover"
189
- }, /* @__PURE__ */ React3.createElement(Card.Header, null, /* @__PURE__ */ React3.createElement(Card.IconBlock, null, icon && /* @__PURE__ */ React3.createElement(Card.Icon, {
201
+ }, /* @__PURE__ */ React3.createElement(Card.Header, null, /* @__PURE__ */ React3.createElement(Card.Block, null, icon && /* @__PURE__ */ React3.createElement(Icon, {
190
202
  icon
191
- })), /* @__PURE__ */ React3.createElement(Card.Title, null, title), /* @__PURE__ */ React3.createElement(Card.IconBlock, null, /* @__PURE__ */ React3.createElement(Menu.Trigger, {
203
+ })), /* @__PURE__ */ React3.createElement(Card.Title, null, title), /* @__PURE__ */ React3.createElement(Card.Block, {
204
+ end: true
205
+ }, /* @__PURE__ */ React3.createElement(Menu.Trigger, {
192
206
  asChild: true,
193
207
  disabled: !objectMenuItems.length
194
- }, /* @__PURE__ */ React3.createElement(Toolbar.IconButton, {
208
+ }, /* @__PURE__ */ React3.createElement(IconButton, {
195
209
  variant: "ghost",
196
210
  density: "sm",
197
211
  icon: "ph--dots-three-vertical--regular",
@@ -200,7 +214,7 @@ var PopoverContent = () => {
200
214
  })), /* @__PURE__ */ React3.createElement(Menu.Content, {
201
215
  items: objectMenuItems
202
216
  }))), content && "subject" in content ? /* @__PURE__ */ React3.createElement(Surface3.Surface, {
203
- type: AppSurface3.Card,
217
+ type: AppSurface3.CardContent,
204
218
  data: content,
205
219
  limit: 1
206
220
  }) : /* @__PURE__ */ React3.createElement(Card.Body, {
@@ -216,7 +230,7 @@ import React4, { useState as useState2 } from "react";
216
230
  import { Button, Toast as NaturalToast, toLocalizedString as toLocalizedString2, useTranslation as useTranslation2 } from "@dxos/react-ui";
217
231
  import { meta as meta2 } from "#meta";
218
232
  var Toast = ({ id, title, description, icon, duration, actionLabel, actionAlt, onAction, onOpenChange }) => {
219
- const { t } = useTranslation2(meta2.id);
233
+ const { t } = useTranslation2(meta2.profile.key);
220
234
  const [open, setOpen] = useState2(true);
221
235
  const handleOpenChange = (next) => {
222
236
  setOpen(next);
@@ -288,4 +302,4 @@ export {
288
302
  NAV_ID,
289
303
  DeckLayout_default as default
290
304
  };
291
- //# sourceMappingURL=DeckLayout-DEURA3KR.mjs.map
305
+ //# sourceMappingURL=DeckLayout-B3RUUV5P.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/containers/DeckLayout/DeckLayout.tsx", "../../../src/containers/DeckLayout/ActiveNode.tsx", "../../../src/containers/DeckLayout/Dialog.tsx", "../../../src/containers/DeckLayout/Popover.tsx", "../../../src/containers/DeckLayout/Toast.tsx", "../../../src/containers/DeckLayout/constants.ts", "../../../src/containers/DeckLayout/index.ts"],
4
+ "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport React, { useCallback } from 'react';\n\nimport { useAtomCapability, useOperationInvoker, usePluginManager } from '@dxos/app-framework/ui';\nimport { LayoutOperation } from '@dxos/app-toolkit';\nimport { Mosaic } from '@dxos/react-ui-mosaic';\n\nimport { useDeckState } from '#hooks';\nimport { DeckCapabilities, getMode } from '#types';\n\nimport { Deck, type DeckLayoutChangeRequest } from '../Deck';\nimport { ActiveNode } from './ActiveNode';\nimport { Dialog } from './Dialog';\nimport { PopoverContent, PopoverRoot } from './Popover';\nimport { Toaster, type ToasterProps } from './Toast';\n\nexport type DeckLayoutProps = Pick<ToasterProps, 'onDismissToast'>;\n\nexport const DeckLayout = ({ onDismissToast }: DeckLayoutProps) => {\n const settings = useAtomCapability(DeckCapabilities.Settings);\n const pluginManager = usePluginManager();\n const { invokePromise } = useOperationInvoker();\n const { deck, state, updateState } = useDeckState();\n const layoutMode = getMode(deck);\n const { toasts } = state;\n\n const handleLayoutChange = useCallback(\n (request: DeckLayoutChangeRequest) => {\n void invokePromise(LayoutOperation.SetLayoutMode, request);\n },\n [invokePromise],\n );\n\n return (\n <Mosaic.Root>\n <PopoverRoot>\n <ActiveNode />\n <Deck.Root\n settings={settings}\n pluginManager={pluginManager}\n layoutMode={layoutMode}\n deck={deck}\n state={state}\n updateState={updateState}\n onLayoutChange={handleLayoutChange}\n >\n <Deck.Content>\n <Deck.Viewport>\n {deck.solo ? <Deck.SoloMode /> : deck.active.length === 0 ? <Deck.ContentEmpty /> : <Deck.MultiMode />}\n </Deck.Viewport>\n </Deck.Content>\n </Deck.Root>\n <PopoverContent />\n <Dialog />\n <Toaster toasts={toasts} onDismissToast={onDismissToast} />\n </PopoverRoot>\n </Mosaic.Root>\n );\n};\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport React from 'react';\n\nimport { Surface } from '@dxos/app-framework/ui';\nimport { AppSurface, useAppGraph } from '@dxos/app-toolkit/ui';\nimport { useNode } from '@dxos/plugin-graph';\nimport { useAttended } from '@dxos/react-ui-attention';\n\nimport { useNodeActionExpander } from '#hooks';\n\n// TODO(burdon): Factor out to effect in plugin set document title.\nexport const ActiveNode = () => {\n const [id] = useAttended();\n const { graph } = useAppGraph();\n const activeNode = useNode(graph, id);\n useNodeActionExpander(activeNode);\n\n return (\n <div className='sr-only'>\n {/* TODO(wittjosiah): Weird that this is a surface, feel like it's not really render logic.\n Probably this lives in React-land currently in order to access translations? */}\n <Surface.Surface\n type={AppSurface.DocumentTitle}\n data={{ subject: activeNode } satisfies AppSurface.DocumentTitleData}\n limit={1}\n />\n </div>\n );\n};\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport React, { useCallback } from 'react';\n\nimport { Surface } from '@dxos/app-framework/ui';\nimport { AppSurface } from '@dxos/app-toolkit/ui';\nimport { AlertDialog, Dialog as NaturalDialog } from '@dxos/react-ui';\n\nimport { useDeckState } from '#hooks';\n\nimport { PlankErrorFallback } from '../Deck/PlankFallback';\n\nexport const Dialog = () => {\n const { state, updateEphemeral } = useDeckState();\n const { dialogOpen, dialogType, dialogBlockAlign, dialogOverlayClasses, dialogOverlayStyle, dialogContent } = state;\n const Root = dialogType === 'alert' ? AlertDialog.Root : NaturalDialog.Root;\n const Overlay = dialogType === 'alert' ? AlertDialog.Overlay : NaturalDialog.Overlay;\n\n const handleOpenChange = useCallback(\n (nextOpen: boolean) => {\n updateEphemeral((s) => ({ ...s, dialogOpen: nextOpen }));\n },\n [updateEphemeral],\n );\n\n // TODO(thure): End block alignment affecting `modal` and whether the surface renders in an overlay is tailored to the needs of the ambient chat dialog. As the feature matures, consider separating concerns.\n return (\n <Root modal={dialogBlockAlign !== 'end'} open={dialogOpen} onOpenChange={handleOpenChange}>\n {dialogBlockAlign === 'end' ? (\n // TODO(burdon): Placeholder creates a suspense boundary; replace with defaults.\n <Surface.Surface\n type={AppSurface.Dialog}\n data={dialogContent ?? undefined}\n limit={1}\n fallback={PlankErrorFallback}\n placeholder={<div />}\n />\n ) : (\n <Overlay blockAlign={dialogBlockAlign} classNames={dialogOverlayClasses} style={dialogOverlayStyle}>\n <Surface.Surface\n type={AppSurface.Dialog}\n data={dialogContent ?? undefined}\n limit={1}\n fallback={PlankErrorFallback}\n />\n </Overlay>\n )}\n </Root>\n );\n};\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { createContext } from '@radix-ui/react-context';\nimport React, { type PropsWithChildren, useCallback, useEffect, useRef, useState } from 'react';\n\nimport { Surface } from '@dxos/app-framework/ui';\nimport { AppSurface, useObjectMenuItems } from '@dxos/app-toolkit/ui';\nimport { Obj } from '@dxos/echo';\nimport {\n Card,\n Icon,\n IconButton,\n Popover,\n type PopoverContentInteractOutsideEvent,\n toLocalizedString,\n useMediaQuery,\n useTranslation,\n} from '@dxos/react-ui';\nimport { Menu } from '@dxos/react-ui-menu';\n\nimport { useDeckState } from '#hooks';\nimport { meta } from '#meta';\n\nconst DEBOUNCE_DELAY = 40;\n\ntype DeckPopoverContextValue = {\n setOpen: (open: boolean) => void;\n};\n\nconst [DeckPopoverProvider, useDeckPopoverContext] = createContext<DeckPopoverContextValue>('DeckPopover');\n\nexport type PopoverRootProps = PropsWithChildren;\n\nexport const PopoverRoot = ({ children }: PopoverRootProps) => {\n const { state } = useDeckState();\n const virtualRef = useRef<HTMLButtonElement | null>(null);\n const [virtualIter, setVirtualIter] = useState(0);\n const [open, setOpen] = useState(false);\n const debounceRef = useRef<NodeJS.Timeout | null>(null);\n\n // TODO(thure): This is a workaround for the race condition between displaying a Popover and either rendering\n // the anchor further down the tree or measuring the virtual trigger's client rect.\n useEffect(() => {\n setOpen(false);\n if (state.popoverOpen) {\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n }\n if (state.popoverAnchor && virtualRef.current !== state.popoverAnchor) {\n virtualRef.current = state.popoverAnchor ?? null;\n setVirtualIter((iter) => iter + 1);\n }\n debounceRef.current = setTimeout(() => setOpen(true), DEBOUNCE_DELAY);\n }\n }, [state.popoverOpen, state.popoverAnchorId, state.popoverAnchor, state.popoverContent]);\n\n // The rename popover is modal so other navtree item menus are inert while it is open.\n const modal = state.popoverKind === 'rename';\n\n return (\n <DeckPopoverProvider setOpen={setOpen}>\n <Popover.Root modal={modal} open={open}>\n {state.popoverAnchor && <Popover.VirtualTrigger key={virtualIter} virtualRef={virtualRef} />}\n {children}\n </Popover.Root>\n </DeckPopoverProvider>\n );\n};\n\nexport const PopoverContent = () => {\n const { t } = useTranslation(meta.profile.key);\n const { state, updateEphemeral } = useDeckState();\n const { setOpen } = useDeckPopoverContext('PopoverContent');\n const popoverSubject =\n state.popoverContent && 'subject' in state.popoverContent ? state.popoverContent.subject : undefined;\n const isObjectPopover = Obj.isObject(popoverSubject);\n const objectMenuItems = useObjectMenuItems(popoverSubject);\n const title = state.popoverTitle ? toLocalizedString(state.popoverTitle, t) : 'Unknown';\n const icon = isObjectPopover ? (Obj.getIcon(popoverSubject)?.icon ?? 'ph--circle-dashed--regular') : undefined;\n const content = state.popoverContent;\n // Base and rename popovers render a plugin-provided component; everything else falls through to the card.\n const isComponentPopover =\n (state.popoverKind === 'base' || state.popoverKind === 'rename') && !!content && 'component' in content;\n const isRename = state.popoverKind === 'rename';\n\n // Anchor to the right of the row on wide displays; drop centered below on narrow ones.\n const [isLg] = useMediaQuery('lg', { fallback: [true] });\n const side = isRename ? (isLg ? 'right' : 'bottom') : state.popoverSide;\n\n const handleClose = useCallback(() => {\n setOpen(false);\n updateEphemeral((state) => ({\n ...state,\n popoverOpen: false,\n popoverAnchor: undefined,\n popoverAnchorId: undefined,\n popoverSide: undefined,\n }));\n }, [updateEphemeral]);\n\n const handleInteractOutside = useCallback(\n (event: KeyboardEvent | PopoverContentInteractOutsideEvent) => {\n // Focus leaving the popover (clicking into the card surfaces a portaled menu, or CodeMirror\n // re-focusing itself) must not dismiss it — only a pointer-down genuinely outside the card, or\n // Escape, closes. (Clicks inside the card never reach here; Radix scopes them to the content.)\n if (event.type === 'dismissableLayer.focusOutside') {\n event.preventDefault();\n return;\n }\n handleClose();\n },\n [handleClose],\n );\n\n return (\n <Popover.Portal>\n <Popover.Content\n side={side}\n sticky='always'\n hideWhenDetached\n // Rename focuses its input; other popovers keep focus where it was.\n onOpenAutoFocus={isRename ? undefined : (event) => event.preventDefault()}\n onInteractOutside={handleInteractOutside}\n onEscapeKeyDown={handleInteractOutside}\n // Reuse the dialog's enter/exit motion so the rename popover does not flicker on open.\n classNames={\n isRename\n ? ['data-[state=open]:animate-slide-up-and-fade', 'data-[state=closed]:animate-slide-down-and-fade']\n : undefined\n }\n >\n <Popover.Viewport>\n {isComponentPopover && content && 'component' in content ? (\n /* Base popover: a plugin-provided component (e.g. editor link preview). */\n <Surface.Surface type={AppSurface.Popover} data={content} limit={1} />\n ) : (\n /*\n * Card popover (default). Rendered for any open popover that isn't an explicit\n * base-component popover so the popover can never collapse to a bare 1px frame: the\n * header (icon + title + menu) always renders, and the body falls back to a fixed-\n * height \"no preview\" row when no subject resolves a card Surface (e.g. system-type\n * objects like a raw Feed that have no registered card and no renderable fields).\n */\n <Menu.Root>\n <Card.Root border={false} classNames='dx-card-popover'>\n <Card.Header>\n <Card.Block>{icon && <Icon icon={icon} />}</Card.Block>\n <Card.Title>{title}</Card.Title>\n {/* TODO(wittjosiah): Reconcile with Card.Menu. */}\n <Card.Block end>\n <Menu.Trigger asChild disabled={!objectMenuItems.length}>\n <IconButton\n variant='ghost'\n density='sm'\n icon='ph--dots-three-vertical--regular'\n iconOnly\n label='Actions'\n />\n </Menu.Trigger>\n <Menu.Content items={objectMenuItems} />\n </Card.Block>\n </Card.Header>\n\n {content && 'subject' in content ? (\n <Surface.Surface type={AppSurface.CardContent} data={content} limit={1} />\n ) : (\n <Card.Body classNames='min-bs-8'>\n <Card.Row>\n <Card.Text variant='description'>{t('popover-no-preview.message')}</Card.Text>\n </Card.Row>\n </Card.Body>\n )}\n </Card.Root>\n </Menu.Root>\n )}\n </Popover.Viewport>\n <Popover.Arrow />\n </Popover.Content>\n </Popover.Portal>\n );\n};\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport React, { useState } from 'react';\n\nimport { type LayoutOperation } from '@dxos/app-toolkit';\nimport { Button, Toast as NaturalToast, type ToastRootProps, toLocalizedString, useTranslation } from '@dxos/react-ui';\n\nimport { meta } from '#meta';\n\n// TODO(wittjosiah): Render remaining duration as a progress bar within the toast.\nexport const Toast = ({\n id,\n title,\n description,\n icon,\n duration,\n actionLabel,\n actionAlt,\n onAction,\n onOpenChange,\n}: LayoutOperation.Toast & Pick<ToastRootProps, 'onOpenChange'>) => {\n const { t } = useTranslation(meta.profile.key);\n\n // Control the open state so closing flips Radix's `open` (playing the exit animation) rather than\n // unmounting abruptly. Both the close button and Radix's own timeout/swipe route through here.\n const [open, setOpen] = useState(true);\n const handleOpenChange = (next: boolean) => {\n setOpen(next);\n onOpenChange?.(next);\n };\n\n return (\n <NaturalToast.Root data-testid={id} open={open} duration={duration} onOpenChange={handleOpenChange}>\n <NaturalToast.Title icon={icon} onClose={() => handleOpenChange(false)}>\n {title && <span>{toLocalizedString(title, t)}</span>}\n </NaturalToast.Title>\n {description && <NaturalToast.Description>{toLocalizedString(description, t)}</NaturalToast.Description>}\n {onAction && actionAlt && actionLabel && (\n <NaturalToast.Actions>\n <NaturalToast.Action altText={toLocalizedString(actionAlt, t)} asChild>\n <Button data-testid='toast.action' variant='primary' onClick={() => onAction?.()}>\n {toLocalizedString(actionLabel, t)}\n </Button>\n </NaturalToast.Action>\n </NaturalToast.Actions>\n )}\n </NaturalToast.Root>\n );\n};\n\nexport type ToasterProps = {\n toasts?: LayoutOperation.Toast[];\n onDismissToast?: (id: string) => void;\n};\n\nexport const Toaster = ({ toasts, onDismissToast }: ToasterProps) => {\n return (\n <>\n {toasts?.map((toast) => (\n <Toast\n {...toast}\n key={toast.id}\n onOpenChange={(open: boolean) => {\n if (!open) {\n onDismissToast?.(toast.id);\n }\n\n return open;\n }}\n />\n ))}\n </>\n );\n};\n", "//\n// Copyright 2024 DXOS.org\n//\n\nexport const NAV_ID = 'NavTree';\n\nexport const SURFACE_PREFIX = 'surface:';\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { DeckLayout } from './DeckLayout';\n\nexport { NAV_ID } from './constants';\n\nexport default DeckLayout;\n"],
5
+ "mappings": "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",
6
+ "names": ["React", "useCallback", "useAtomCapability", "useOperationInvoker", "usePluginManager", "LayoutOperation", "Mosaic", "useDeckState", "DeckCapabilities", "getMode", "React", "Surface", "AppSurface", "useAppGraph", "useNode", "useAttended", "useNodeActionExpander", "ActiveNode", "id", "graph", "activeNode", "div", "className", "type", "DocumentTitle", "data", "subject", "limit", "React", "useCallback", "Surface", "AppSurface", "AlertDialog", "Dialog", "NaturalDialog", "useDeckState", "Dialog", "state", "updateEphemeral", "useDeckState", "dialogOpen", "dialogType", "dialogBlockAlign", "dialogOverlayClasses", "dialogOverlayStyle", "dialogContent", "Root", "AlertDialog", "NaturalDialog", "Overlay", "handleOpenChange", "useCallback", "nextOpen", "s", "React", "modal", "open", "onOpenChange", "Surface", "type", "AppSurface", "data", "undefined", "limit", "fallback", "PlankErrorFallback", "placeholder", "div", "blockAlign", "classNames", "style", "createContext", "React", "useCallback", "useEffect", "useRef", "useState", "Surface", "AppSurface", "useObjectMenuItems", "Obj", "Card", "Icon", "IconButton", "Popover", "toLocalizedString", "useMediaQuery", "useTranslation", "Menu", "useDeckState", "meta", "DEBOUNCE_DELAY", "DeckPopoverProvider", "useDeckPopoverContext", "PopoverRoot", "children", "state", "virtualRef", "virtualIter", "setVirtualIter", "open", "setOpen", "debounceRef", "popoverOpen", "current", "clearTimeout", "popoverAnchor", "iter", "setTimeout", "popoverAnchorId", "popoverContent", "modal", "popoverKind", "Root", "VirtualTrigger", "key", "PopoverContent", "t", "profile", "updateEphemeral", "popoverSubject", "subject", "undefined", "isObjectPopover", "isObject", "objectMenuItems", "title", "popoverTitle", "icon", "getIcon", "content", "isComponentPopover", "isRename", "isLg", "fallback", "side", "popoverSide", "handleClose", "handleInteractOutside", "event", "type", "preventDefault", "Portal", "Content", "sticky", "hideWhenDetached", "onOpenAutoFocus", "onInteractOutside", "onEscapeKeyDown", "classNames", "Viewport", "data", "limit", "border", "Header", "Block", "Title", "end", "Trigger", "asChild", "disabled", "length", "variant", "density", "iconOnly", "label", "items", "CardContent", "Body", "Row", "Text", "Arrow", "React", "useState", "Button", "Toast", "NaturalToast", "toLocalizedString", "useTranslation", "meta", "id", "title", "description", "icon", "duration", "actionLabel", "actionAlt", "onAction", "onOpenChange", "t", "profile", "key", "open", "setOpen", "handleOpenChange", "next", "Root", "data-testid", "Title", "onClose", "span", "Description", "Actions", "Action", "altText", "asChild", "variant", "onClick", "Toaster", "toasts", "onDismissToast", "map", "toast", "DeckLayout", "onDismissToast", "settings", "useAtomCapability", "DeckCapabilities", "Settings", "pluginManager", "usePluginManager", "invokePromise", "useOperationInvoker", "deck", "state", "updateState", "useDeckState", "layoutMode", "getMode", "toasts", "handleLayoutChange", "useCallback", "request", "LayoutOperation", "SetLayoutMode", "React", "Mosaic", "Root", "PopoverRoot", "ActiveNode", "Deck", "onLayoutChange", "Content", "Viewport", "solo", "SoloMode", "active", "length", "ContentEmpty", "MultiMode", "PopoverContent", "Dialog", "Toaster", "NAV_ID", "DeckLayout"]
7
+ }
@@ -4,14 +4,13 @@ import "./chunk-J5LGTIGS.mjs";
4
4
  import { setAutoFreeze } from "immer";
5
5
  import { ActivationEvent, ActivationEvents, Plugin } from "@dxos/app-framework";
6
6
  import { AppActivationEvents, AppPlugin } from "@dxos/app-toolkit";
7
- import { translations as stackTranslations } from "@dxos/react-ui-stack/translations";
8
7
  import { AppGraphBuilder, CheckAppScheme, DeckSettings, DeckState, NotificationTracker, OperationHandler, ReactRoot, ReactSurface, UrlHandler } from "#capabilities";
9
8
  import { meta } from "#meta";
10
9
  import { translations } from "#translations";
11
10
  import { DeckEvents } from "#types";
12
11
 
13
- // raw-loader:/__w/dxos/dxos/packages/plugins/plugin-deck/PLUGIN.mdl?raw
14
- var PLUGIN_default = "---\nid: org.dxos.plugin.deck\nname: DeckPlugin\nversion: 0.1.0\n---\n\nThe Deck plugin is the core layout engine for DXOS Composer. It manages the multi-plank workspace\n(\"deck\"), the sidebar, the complementary sidebar, dialogs, popovers, and toast notifications.\nIt owns the persisted and ephemeral layout state, handles URL routing, and implements every\n`LayoutOperation` that changes what is visible on screen.\n\n## Extensions\n\nThe following extension dialects are used in this document.\nEach extension is defined in the Appendix or resolved via its URI.\n\n| Term | URI |\n|-------------|--------------------------------|\n| `type` | `org.dxos.mdl.type@1.0` |\n| `feat` | `org.dxos.mdl.feat@1.0` |\n| `test` | `org.dxos.mdl.test@1.0` |\n| `component` | `org.dxos.mdl.component@1.0` |\n| `op` | `org.dxos.mdl.op@1.0` |\n\n## Types\n\n```mdl\ntype LayoutMode\n literals: multi | solo | solo--fullscreen\n```\n\n```mdl\ntype PartAdjustment\n literals: close | companion | solo | solo--fullscreen | increment-start | increment-end\n```\n\n```mdl\ntype DeckState\n fields:\n initialized: boolean # false until the deck has left solo mode for the first time\n active: string[] # item IDs of planks displayed in multi mode\n inactive: string[] # item IDs that have been closed; persisted for reopening\n solo?: string # item ID of the single plank in solo or fullscreen mode\n fullscreen: boolean # true when the solo plank is displayed without any chrome\n plankSizing: Record<string, number> # persisted plank widths in rem, keyed by item ID\n companionOpen: boolean # whether the companion pane is visible\n companionVariant?: string # which companion variant to display\n companionFrameSizing: Record<string, number> # companion frame widths in rem\n```\n\n```mdl\ntype StoredDeckState\n desc: Persisted plugin state stored in KVS/localStorage.\n fields:\n sidebarState: closed | collapsed | expanded\n complementarySidebarState: closed | collapsed | expanded\n complementarySidebarPanel?: string\n activeDeck: string\n previousDeck: string\n decks: Record<string, DeckState>\n previousMode: Record<string, LayoutMode>\n```\n\n```mdl\ntype EphemeralDeckState\n desc: Transient plugin state that is NOT persisted across sessions.\n fields:\n dialogOpen: boolean\n dialogType?: default | alert\n dialogBlockAlign?: start | center | end\n dialogContent?: { component: string; props?: any }\n popoverOpen: boolean\n popoverSide?: top | right | bottom | left\n popoverKind?: base | card\n popoverTitle?: string\n popoverContentRef?: string\n popoverContent?: { component: string } | { subject: any }\n toasts: Toast[]\n currentUndoId?: string\n scrollIntoView?: string\n```\n\n```mdl\ntype DeckSettings\n fields:\n enableDeck?: boolean # display multiple panels side by side\n encapsulatedPlanks?: boolean # render each plank in an isolated container\n showHints?: boolean # show keyboard shortcut hints in the UI\n enableNativeRedirect?: boolean # redirect supported URLs to the native desktop app\n```\n\n## Components\n\n```mdl\ncomponent Matrix\n desc: |\n The primary workspace canvas. Renders the active planks side by side in multi mode,\n or a single plank in solo/fullscreen mode. Manages plank-resize handles and the\n companion pane alongside each plank.\n props:\n state: StoredDeckState # persisted layout state atom\n ephemeral: EphemeralDeckState # transient state atom\n state:\n mode: LayoutMode # current layout mode derived from state\n actions:\n openPlank(id: string)\n closePlank(id: string)\n adjustPlank(id: string, type: PartAdjustment)\n resizePlank(id: string, size: number)\n layout: |\n \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 sidebar \u2502 plank A \u2502 plank B \u2502\n \u2502 \u2502 \u2502 \u2502\n \u2502 \u2502 [content] \u2502 [content] \u2502\n \u2502 \u2502 \u2502 \u2502\n \u2502 \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502\n \u2502 \u2502 companion \u2502 \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n```mdl\ncomponent DeckSettings\n desc: Settings panel rendered in the Composer settings surface.\n props:\n settings: DeckSettings\n actions:\n updateSetting(key: string, value: any)\n```\n\n## Operations\n\n```mdl\nop Open\n desc: |\n Opens one or more subjects (item IDs or navigation paths) in the deck.\n In multi mode uses stack semantics: truncates after the pivot then appends.\n In solo or uninitialised mode, replaces the current subject entirely.\n Validates each target against the app graph and redirects to a 404 node if not found.\n Fires ScrollIntoView, Expose, and an observability event for each newly opened item.\n input:\n subject: string[] # item IDs or navigation paths to open\n pivotId?: string # truncate deck after this item before appending\n key?: string # replace an existing plank whose ID shares this key prefix\n workspace?: string # switch to this workspace before opening\n navigation?: immediate # skip validation; expand path only\n scrollIntoView?: boolean # default true; set false to suppress auto-scroll\n state?: any\n variant?: string\n output: string[]\n effects: [echo:read, layout:state]\n requires: [AppGraph, AttentionCapabilities, ClientCapabilities, DeckCapabilities]\n```\n\n```mdl\nop Close\n desc: |\n Removes one or more subjects from the active deck.\n Computes which plank (if any) should receive attention after removal and\n schedules ScrollIntoView for it.\n input:\n subject: string[]\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities, AttentionCapabilities]\n```\n\n```mdl\nop SetLayoutMode\n desc: |\n Transitions to a new layout mode (multi, solo, solo--fullscreen) or reverts\n to the previously recorded mode. Persists the previous mode so it can be\n restored later. Toggling solo--fullscreen flips the fullscreen flag rather\n than unconditionally setting it.\n input:\n mode?: LayoutMode # target mode; omit if reverting\n subject?: string # item to solo; used only for solo / solo--fullscreen\n revert?: boolean # revert to the previously persisted mode\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdateSidebar\n desc: Update the sidebar visibility state (closed / collapsed / expanded).\n input:\n state?: closed | collapsed | expanded\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdateComplementary\n desc: Update the complementary-sidebar visibility state and optional panel selection.\n input:\n state?: closed | collapsed | expanded\n panel?: string\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdateDialog\n desc: |\n Show or hide the global dialog overlay. Accepts a surface component identifier\n or a boolean state flag. Propagates to EphemeralDeckState.\n input:\n subject?: string # surface component identifier to display\n props?: any # props forwarded to the dialog surface\n state?: boolean # false to close the dialog\n type?: default | alert\n blockAlign?: start | center | end\n overlayClasses?: string\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdatePopover\n desc: |\n Show or hide the global popover. Accepts anchor, content reference, or\n an explicit subject reference.\n input:\n subject?: any # subject reference to pass to the popover surface\n contentRef?: string # surface component identifier for popover content\n anchorId?: string # DOM element ID for positioning the popover anchor\n side?: top | right | bottom | left\n kind?: base | card\n title?: string\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdateCompanion\n desc: Update the companion-pane open state and active variant.\n input:\n open?: boolean\n variant?: string\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop AdjustPlank\n desc: Apply a PartAdjustment to a specific plank (close, solo, move, companion).\n input:\n id: string\n type: PartAdjustment\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdatePlankSize\n desc: Persist the width of a plank in rem.\n input:\n id: string\n size: number\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop AddToast\n desc: Push a toast notification into the ephemeral queue.\n input:\n title?: string\n body?: string\n duration?: number\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop ScrollIntoView\n desc: Schedule a subject to be scrolled into view when its plank mounts.\n input:\n subject: string\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop SwitchWorkspace\n desc: Switch the active workspace deck, persisting the previous workspace ID.\n input:\n subject: string # workspace ID to switch to\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop RevertWorkspace\n desc: Revert to the previously active workspace.\n input: void\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n## Features\n\n```mdl\nfeat F-1: Multi-Plank Layout (Deck)\n\n req F-1.1:\n when: enableDeck setting is true\n then: deck renders active planks side by side in a scrollable row\n\n req F-1.2:\n when: user opens a new subject with a pivot\n then: deck is truncated after the pivot and the new subject is appended\n\n req F-1.3:\n when: subject is already present in the active deck\n then: deck is unchanged and the existing plank scrolls into view\n\n req F-1.4:\n when: user resizes a plank\n then: new width is persisted in plankSizing and restored on next load\n```\n\n```mdl\nfeat F-2: Solo / Fullscreen Mode\n\n req F-2.1:\n when: user triggers SetLayoutMode with mode=solo\n then: only the targeted plank is visible; other planks move to inactive\n\n req F-2.2:\n when: user triggers SetLayoutMode with mode=solo--fullscreen\n then: plank fills the viewport; sidebar and heading chrome are hidden\n\n req F-2.3:\n when: user triggers SetLayoutMode with revert=true\n then: layout returns to the previously persisted mode\n```\n\n```mdl\nfeat F-3: Sidebar & Complementary Sidebar\n\n req F-3.1:\n when: UpdateSidebar is dispatched with a new state\n then: sidebar transitions between closed / collapsed / expanded and state is persisted\n\n req F-3.2:\n when: UpdateComplementary is dispatched with a panel\n then: complementary sidebar opens to the specified panel\n```\n\n```mdl\nfeat F-4: Dialog & Popover Overlay\n\n req F-4.1:\n when: UpdateDialog is dispatched with a subject component\n then: the matching surface is rendered inside the global dialog overlay\n\n req F-4.2:\n when: UpdatePopover is dispatched with an anchorId\n then: popover is positioned relative to that DOM element\n\n req F-4.3:\n when: dialog or popover state is set to false\n then: overlay is closed and content is unmounted\n```\n\n```mdl\nfeat F-5: URL Navigation & Deep Linking\n\n req F-5.1:\n when: app loads with a URL referencing an item\n then: DeckPlugin resolves the path and opens the item in the deck\n\n req F-5.2:\n when: active deck changes\n then: browser URL is updated to reflect the current plank state\n\n req F-5.3:\n when: a navigation target is not found in the app graph\n then: Open operation redirects to a 404 node\n```\n\n```mdl\nfeat F-6: Toast Notifications\n\n req F-6.1:\n when: AddToast is dispatched\n then: a toast is appended to the queue and displayed for its configured duration\n\n req F-6.2:\n when: ShowUndo is dispatched\n then: a toast with an undo action is shown; invoking it executes the linked undo operation\n```\n\n## Acceptance\n\n```mdl\ntest T-1: Open subject in multi mode\n given: deck is in multi mode with planks A and B active\n when: Open({ subject: ['C'] }) is dispatched\n then:\n - plank C is appended to the right of plank B\n - browser URL reflects the new deck state\n - plank C is scrolled into view\n```\n\n```mdl\ntest T-2: Open with pivot truncation\n given: deck has planks [A, B, C] active\n when: Open({ subject: ['D'], pivotId: 'A' }) is dispatched\n then:\n - active deck becomes [A, D]\n - planks B and C are moved to inactive\n```\n\n```mdl\ntest T-3: Open already-visible subject\n given: deck has plank A active\n when: Open({ subject: ['A'] }) is dispatched\n then:\n - active deck is unchanged\n - plank A scrolls into view\n```\n\n```mdl\ntest T-4: Solo mode\n given: deck is in multi mode\n when: SetLayoutMode({ mode: 'solo', subject: 'A' }) is dispatched\n then:\n - only plank A is visible\n - mode is stored as solo in StoredDeckState\n - previousMode records 'multi' for the active deck\n```\n\n```mdl\ntest T-5: Revert from solo to multi\n given: layout mode is solo, previousMode is multi\n when: SetLayoutMode({ revert: true }) is dispatched\n then:\n - deck returns to multi mode\n - previously inactive planks are restored\n```\n\n```mdl\ntest T-6: Close plank\n given: deck has planks [A, B, C] active; B has attention\n when: Close({ subject: ['B'] }) is dispatched\n then:\n - active deck becomes [A, C]\n - attention moves to an adjacent plank\n - ScrollIntoView is scheduled for the newly attended plank\n```\n\n```mdl\ntest T-7: Toast is shown and dismissed\n given: no toasts are in the queue\n when: AddToast({ title: 'Saved', duration: 3000 }) is dispatched\n then:\n - toast appears in the UI\n - toast is removed after 3 seconds\n```\n\n---\n\n## Appendix: Extension Definitions\n\nExtension block types used in this document are defined below using\nthe core `ext` primitive \u2014 the only construct the base language provides.\n\n```mdl\next type\n uri: org.dxos.mdl.type@1.0\n desc: A named data structure with typed fields and optional literals.\n fields:\n desc?: Prose\n fields?: FieldMap # name[?]: TypeExpr (# inline comment)\n literals?: UnionList # a | b | c\n extends?: TypeRef[]\n```\n\n```mdl\next feat\n uri: org.dxos.mdl.feat@1.0\n desc: A named feature grouping one or more requirements.\n fields:\n desc?: Prose\n req: RequirementList\n nesting: self # feat blocks may contain feat blocks\n```\n\n```mdl\next test\n uri: org.dxos.mdl.test@1.0\n desc: An acceptance scenario expressed as given / when / then steps.\n fields:\n given?: Step | Step[]\n when?: Step | Step[]\n then: Step | Step[]\n tags?: TagList\n```\n\n```mdl\next component\n uri: org.dxos.mdl.component@1.0\n desc: A UI component with props, internal state, slots, actions, and events.\n fields:\n desc?: Prose\n props?: FieldMap # external inputs (immutable inside component)\n state?: FieldMap # internal reactive state\n slots?: FieldMap # named ReactNode injection points\n actions?: ActionMap # methods the component exposes or handles\n emits?: EventMap # events the component raises to its parent\n layout?: CodeBlock # ASCII sketch of visual structure (non-normative)\n```\n\n```mdl\next op\n uri: org.dxos.mdl.op@1.0\n desc: |\n A named operation with typed inputs, outputs, and declared errors.\n Pure ops have no effects or requires. Effectful ops declare both.\n fields:\n desc?: Prose\n input?: FieldMap # named input parameters\n output?: TypeExpr # return type\n errors?: ErrorMap # name: Prose (when this error occurs)\n effects?: EffectList # echo:read | echo:write | http | fs | ...\n requires?: ServiceList # injected service dependencies\n note?: Prose # implementation guidance (non-normative)\n```\n";
12
+ // raw-loader:/__w/dxos/dxos/packages/plugins/plugin-deck/PLUGIN.mdl
13
+ var PLUGIN_default = "---\nid: org.dxos.plugin.deck\nname: DeckPlugin\nversion: 0.1.0\n---\n\nThe Deck plugin is the core layout engine for DXOS Composer. It manages the multi-plank workspace\n(\"deck\"), the sidebar, the complementary sidebar, dialogs, popovers, and toast notifications.\nIt owns the persisted and ephemeral layout state, handles URL routing, and implements every\n`LayoutOperation` that changes what is visible on screen.\n\n## Extensions\n\nThe following extension dialects are used in this document.\nEach extension is defined in the Appendix or resolved via its URI.\n\n| Term | URI |\n|-------------|--------------------------------|\n| `type` | `org.dxos.mdl.type@1.0` |\n| `feat` | `org.dxos.mdl.feat@1.0` |\n| `test` | `org.dxos.mdl.test@1.0` |\n| `component` | `org.dxos.mdl.component@1.0` |\n| `op` | `org.dxos.mdl.op@1.0` |\n\n## Types\n\n```mdl\ntype LayoutMode\n literals: multi | solo | solo--fullscreen\n```\n\n```mdl\ntype PartAdjustment\n literals: close | companion | solo | solo--fullscreen | increment-start | increment-end\n```\n\n```mdl\ntype DeckState\n fields:\n initialized: boolean # false until the deck has left solo mode for the first time\n active: string[] # item IDs of planks displayed in multi mode\n inactive: string[] # item IDs that have been closed; persisted for reopening\n solo?: string # item ID of the single plank in solo or fullscreen mode\n fullscreen: boolean # true when the solo plank is displayed without any chrome\n plankSizing: Record<string, number> # persisted plank widths in rem, keyed by item ID\n companionOpen: boolean # whether the companion pane is visible\n companionOrientation?: 'horizontal' | 'vertical' # companion splitter orientation (side-by-side / stacked)\n companionFrameSizing: Record<string, number> # companion frame widths in rem\n # NOTE: the selected companion variant and per-orientation split size now live in react-ui-attention\n # view state (global, localStorage), not on DeckState \u2014 see util/companion-view-state.ts.\n```\n\n```mdl\ntype StoredDeckState\n desc: Persisted plugin state stored in KVS/localStorage.\n fields:\n sidebarState: closed | collapsed | expanded\n complementarySidebarState: closed | collapsed | expanded\n complementarySidebarPanel?: string\n activeDeck: string\n previousDeck: string\n decks: Record<string, DeckState>\n previousMode: Record<string, LayoutMode>\n```\n\n```mdl\ntype EphemeralDeckState\n desc: Transient plugin state that is NOT persisted across sessions.\n fields:\n dialogOpen: boolean\n dialogType?: default | alert\n dialogBlockAlign?: start | center | end\n dialogContent?: { component: string; props?: any }\n popoverOpen: boolean\n popoverSide?: top | right | bottom | left\n popoverKind?: base | card\n popoverTitle?: string\n popoverContentRef?: string\n popoverContent?: { component: string } | { subject: any }\n toasts: Toast[]\n currentUndoId?: string\n scrollIntoView?: string\n```\n\n```mdl\ntype DeckSettings\n fields:\n enableDeck?: boolean # display multiple panels side by side\n encapsulatedPlanks?: boolean # render each plank in an isolated container\n showHints?: boolean # show keyboard shortcut hints in the UI\n enableNativeRedirect?: boolean # redirect supported URLs to the native desktop app\n```\n\n## Components\n\n```mdl\ncomponent Matrix\n desc: |\n The primary workspace canvas. Renders the active planks side by side in multi mode,\n or a single plank in solo/fullscreen mode. Manages plank-resize handles and the\n companion pane alongside each plank.\n props:\n state: StoredDeckState # persisted layout state atom\n ephemeral: EphemeralDeckState # transient state atom\n state:\n mode: LayoutMode # current layout mode derived from state\n actions:\n openPlank(id: string)\n closePlank(id: string)\n adjustPlank(id: string, type: PartAdjustment)\n resizePlank(id: string, size: number)\n layout: |\n \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 sidebar \u2502 plank A \u2502 plank B \u2502\n \u2502 \u2502 \u2502 \u2502\n \u2502 \u2502 [content] \u2502 [content] \u2502\n \u2502 \u2502 \u2502 \u2502\n \u2502 \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502\n \u2502 \u2502 companion \u2502 \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n```mdl\ncomponent DeckSettings\n desc: Settings panel rendered in the Composer settings surface.\n props:\n settings: DeckSettings\n actions:\n updateSetting(key: string, value: any)\n```\n\n## Operations\n\n```mdl\nop Open\n desc: |\n Opens one or more subjects (item IDs or navigation paths) in the deck.\n In multi mode uses stack semantics: truncates after the pivot then appends.\n In solo or uninitialised mode, replaces the current subject entirely.\n Validates each target against the app graph and redirects to a 404 node if not found.\n Fires ScrollIntoView, Expose, and an observability event for each newly opened item.\n input:\n subject: string[] # item IDs or navigation paths to open\n pivotId?: string # truncate deck after this item before appending\n key?: string # replace an existing plank whose ID shares this key prefix\n workspace?: string # switch to this workspace before opening\n navigation?: immediate # skip validation; expand path only\n scrollIntoView?: boolean # default true; set false to suppress auto-scroll\n state?: any\n variant?: string\n output: string[]\n effects: [echo:read, layout:state]\n requires: [AppGraph, AttentionCapabilities, ClientCapabilities, DeckCapabilities]\n```\n\n```mdl\nop Close\n desc: |\n Removes one or more subjects from the active deck.\n Computes which plank (if any) should receive attention after removal and\n schedules ScrollIntoView for it.\n input:\n subject: string[]\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities, AttentionCapabilities]\n```\n\n```mdl\nop SetLayoutMode\n desc: |\n Transitions to a new layout mode (multi, solo, solo--fullscreen) or reverts\n to the previously recorded mode. Persists the previous mode so it can be\n restored later. Toggling solo--fullscreen flips the fullscreen flag rather\n than unconditionally setting it.\n input:\n mode?: LayoutMode # target mode; omit if reverting\n subject?: string # item to solo; used only for solo / solo--fullscreen\n revert?: boolean # revert to the previously persisted mode\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdateSidebar\n desc: Update the sidebar visibility state (closed / collapsed / expanded).\n input:\n state?: closed | collapsed | expanded\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdateComplementary\n desc: Update the complementary-sidebar visibility state and optional panel selection.\n input:\n state?: closed | collapsed | expanded\n panel?: string\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdateDialog\n desc: |\n Show or hide the global dialog overlay. Accepts a surface component identifier\n or a boolean state flag. Propagates to EphemeralDeckState.\n input:\n subject?: string # surface component identifier to display\n props?: any # props forwarded to the dialog surface\n state?: boolean # false to close the dialog\n type?: default | alert\n blockAlign?: start | center | end\n overlayClasses?: string\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdatePopover\n desc: |\n Show or hide the global popover. Accepts anchor, content reference, or\n an explicit subject reference.\n input:\n subject?: any # subject reference to pass to the popover surface\n contentRef?: string # surface component identifier for popover content\n anchorId?: string # DOM element ID for positioning the popover anchor\n side?: top | right | bottom | left\n kind?: base | card\n title?: string\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdateCompanion\n desc: Update the companion-pane open state and active variant.\n input:\n open?: boolean\n variant?: string\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop AdjustPlank\n desc: Apply a PartAdjustment to a specific plank (close, solo, move, companion).\n input:\n id: string\n type: PartAdjustment\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdatePlankSize\n desc: Persist the width of a plank in rem.\n input:\n id: string\n size: number\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop AddToast\n desc: Push a toast notification into the ephemeral queue.\n input:\n title?: string\n body?: string\n duration?: number\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop ScrollIntoView\n desc: Schedule a subject to be scrolled into view when its plank mounts.\n input:\n subject: string\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop SwitchWorkspace\n desc: Switch the active workspace deck, persisting the previous workspace ID.\n input:\n subject: string # workspace ID to switch to\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop RevertWorkspace\n desc: Revert to the previously active workspace.\n input: void\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n## Features\n\n```mdl\nfeat F-1: Multi-Plank Layout (Deck)\n\n req F-1.1:\n when: enableDeck setting is true\n then: deck renders active planks side by side in a scrollable row\n\n req F-1.2:\n when: user opens a new subject with a pivot\n then: deck is truncated after the pivot and the new subject is appended\n\n req F-1.3:\n when: subject is already present in the active deck\n then: deck is unchanged and the existing plank scrolls into view\n\n req F-1.4:\n when: user resizes a plank\n then: new width is persisted in plankSizing and restored on next load\n```\n\n```mdl\nfeat F-2: Solo / Fullscreen Mode\n\n req F-2.1:\n when: user triggers SetLayoutMode with mode=solo\n then: only the targeted plank is visible; other planks move to inactive\n\n req F-2.2:\n when: user triggers SetLayoutMode with mode=solo--fullscreen\n then: plank fills the viewport; sidebar and heading chrome are hidden\n\n req F-2.3:\n when: user triggers SetLayoutMode with revert=true\n then: layout returns to the previously persisted mode\n```\n\n```mdl\nfeat F-3: Sidebar & Complementary Sidebar\n\n req F-3.1:\n when: UpdateSidebar is dispatched with a new state\n then: sidebar transitions between closed / collapsed / expanded and state is persisted\n\n req F-3.2:\n when: UpdateComplementary is dispatched with a panel\n then: complementary sidebar opens to the specified panel\n```\n\n```mdl\nfeat F-4: Dialog & Popover Overlay\n\n req F-4.1:\n when: UpdateDialog is dispatched with a subject component\n then: the matching surface is rendered inside the global dialog overlay\n\n req F-4.2:\n when: UpdatePopover is dispatched with an anchorId\n then: popover is positioned relative to that DOM element\n\n req F-4.3:\n when: dialog or popover state is set to false\n then: overlay is closed and content is unmounted\n```\n\n```mdl\nfeat F-5: URL Navigation & Deep Linking\n\n req F-5.1:\n when: app loads with a URL referencing an item\n then: DeckPlugin resolves the path and opens the item in the deck\n\n req F-5.2:\n when: active deck changes\n then: browser URL is updated to reflect the current plank state\n\n req F-5.3:\n when: a navigation target is not found in the app graph\n then: Open operation redirects to a 404 node\n```\n\n```mdl\nfeat F-6: Toast Notifications\n\n req F-6.1:\n when: AddToast is dispatched\n then: a toast is appended to the queue and displayed for its configured duration\n\n req F-6.2:\n when: ShowUndo is dispatched\n then: a toast with an undo action is shown; invoking it executes the linked undo operation\n```\n\n## Acceptance\n\n```mdl\ntest T-1: Open subject in multi mode\n given: deck is in multi mode with planks A and B active\n when: Open({ subject: ['C'] }) is dispatched\n then:\n - plank C is appended to the right of plank B\n - browser URL reflects the new deck state\n - plank C is scrolled into view\n```\n\n```mdl\ntest T-2: Open with pivot truncation\n given: deck has planks [A, B, C] active\n when: Open({ subject: ['D'], pivotId: 'A' }) is dispatched\n then:\n - active deck becomes [A, D]\n - planks B and C are moved to inactive\n```\n\n```mdl\ntest T-3: Open already-visible subject\n given: deck has plank A active\n when: Open({ subject: ['A'] }) is dispatched\n then:\n - active deck is unchanged\n - plank A scrolls into view\n```\n\n```mdl\ntest T-4: Solo mode\n given: deck is in multi mode\n when: SetLayoutMode({ mode: 'solo', subject: 'A' }) is dispatched\n then:\n - only plank A is visible\n - mode is stored as solo in StoredDeckState\n - previousMode records 'multi' for the active deck\n```\n\n```mdl\ntest T-5: Revert from solo to multi\n given: layout mode is solo, previousMode is multi\n when: SetLayoutMode({ revert: true }) is dispatched\n then:\n - deck returns to multi mode\n - previously inactive planks are restored\n```\n\n```mdl\ntest T-6: Close plank\n given: deck has planks [A, B, C] active; B has attention\n when: Close({ subject: ['B'] }) is dispatched\n then:\n - active deck becomes [A, C]\n - attention moves to an adjacent plank\n - ScrollIntoView is scheduled for the newly attended plank\n```\n\n```mdl\ntest T-7: Toast is shown and dismissed\n given: no toasts are in the queue\n when: AddToast({ title: 'Saved', duration: 3000 }) is dispatched\n then:\n - toast appears in the UI\n - toast is removed after 3 seconds\n```\n\n---\n\n## Appendix: Extension Definitions\n\nExtension block types used in this document are defined below using\nthe core `ext` primitive \u2014 the only construct the base language provides.\n\n```mdl\next type\n uri: org.dxos.mdl.type@1.0\n desc: A named data structure with typed fields and optional literals.\n fields:\n desc?: Prose\n fields?: FieldMap # name[?]: TypeExpr (# inline comment)\n literals?: UnionList # a | b | c\n extends?: TypeRef[]\n```\n\n```mdl\next feat\n uri: org.dxos.mdl.feat@1.0\n desc: A named feature grouping one or more requirements.\n fields:\n desc?: Prose\n req: RequirementList\n nesting: self # feat blocks may contain feat blocks\n```\n\n```mdl\next test\n uri: org.dxos.mdl.test@1.0\n desc: An acceptance scenario expressed as given / when / then steps.\n fields:\n given?: Step | Step[]\n when?: Step | Step[]\n then: Step | Step[]\n tags?: TagList\n```\n\n```mdl\next component\n uri: org.dxos.mdl.component@1.0\n desc: A UI component with props, internal state, slots, actions, and events.\n fields:\n desc?: Prose\n props?: FieldMap # external inputs (immutable inside component)\n state?: FieldMap # internal reactive state\n slots?: FieldMap # named ReactNode injection points\n actions?: ActionMap # methods the component exposes or handles\n emits?: EventMap # events the component raises to its parent\n layout?: CodeBlock # ASCII sketch of visual structure (non-normative)\n```\n\n```mdl\next op\n uri: org.dxos.mdl.op@1.0\n desc: |\n A named operation with typed inputs, outputs, and declared errors.\n Pure ops have no effects or requires. Effectful ops declare both.\n fields:\n desc?: Prose\n input?: FieldMap # named input parameters\n output?: TypeExpr # return type\n errors?: ErrorMap # name: Prose (when this error occurs)\n effects?: EffectList # echo:read | echo:write | http | fs | ...\n requires?: ServiceList # injected service dependencies\n note?: Prose # implementation guidance (non-normative)\n```\n";
15
14
 
16
15
  // src/DeckPlugin.ts
17
16
  setAutoFreeze(false);
@@ -26,10 +25,7 @@ var DeckPlugin = Plugin.define(meta).pipe(
26
25
  activate: ReactSurface
27
26
  }),
28
27
  AppPlugin.addTranslationsModule({
29
- translations: [
30
- ...translations,
31
- ...stackTranslations
32
- ]
28
+ translations
33
29
  }),
34
30
  Plugin.addModule({
35
31
  activatesOn: AppActivationEvents.SetupSettings,
@@ -71,7 +67,7 @@ var DeckPlugin = Plugin.define(meta).pipe(
71
67
  }),
72
68
  AppPlugin.addPluginAssetModule({
73
69
  asset: {
74
- pluginId: meta.id,
70
+ pluginId: meta.profile.key,
75
71
  path: "PLUGIN.mdl",
76
72
  content: PLUGIN_default,
77
73
  mimeType: "application/x-mdl"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../../src/DeckPlugin.ts", "raw-loader:/__w/dxos/dxos/packages/plugins/plugin-deck/PLUGIN.mdl?raw"],
4
- "sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport { setAutoFreeze } from 'immer';\n\nimport { ActivationEvent, ActivationEvents, Plugin } from '@dxos/app-framework';\nimport { AppActivationEvents, AppPlugin } from '@dxos/app-toolkit';\nimport { translations as stackTranslations } from '@dxos/react-ui-stack/translations';\n\nimport {\n AppGraphBuilder,\n CheckAppScheme,\n DeckSettings,\n DeckState,\n NotificationTracker,\n OperationHandler,\n ReactRoot,\n ReactSurface,\n UrlHandler,\n} from '#capabilities';\nimport { meta } from '#meta';\nimport { translations } from '#translations';\nimport { DeckEvents } from '#types';\n\n// eslint-disable-next-line import/no-relative-packages\nimport pluginSpec from '../PLUGIN.mdl?raw';\n\n// NOTE(Zan): When producing values with immer, we shouldn't auto-freeze them because\n// our signal implementation needs to add some hidden properties to the produced values.\n// TODO(Zan): Move this to a more global location if we use immer more broadly.\nsetAutoFreeze(false);\n\nexport const DeckPlugin = Plugin.define(meta).pipe(\n AppPlugin.addAppGraphModule({ activate: AppGraphBuilder }),\n AppPlugin.addOperationHandlerModule({ activate: OperationHandler }),\n AppPlugin.addSurfaceModule({ activate: ReactSurface }),\n AppPlugin.addTranslationsModule({ translations: [...translations, ...stackTranslations] }),\n Plugin.addModule({\n activatesOn: AppActivationEvents.SetupSettings,\n firesAfterActivation: [DeckEvents.SettingsReady],\n activate: DeckSettings,\n }),\n Plugin.addModule({\n activatesOn: ActivationEvent.allOf(DeckEvents.SettingsReady, ActivationEvents.ProcessManagerReady),\n activate: CheckAppScheme,\n }),\n Plugin.addModule({\n // TODO(wittjosiah): Does not integrate with settings store.\n // Should this be a different event?\n // Should settings store be renamed to be more generic?\n activatesOn: ActivationEvent.oneOf(AppActivationEvents.SetupSettings, AppActivationEvents.SetupAppGraph),\n firesAfterActivation: [AppActivationEvents.LayoutReady, DeckEvents.StateReady],\n activate: DeckState,\n }),\n Plugin.addModule({\n activatesOn: ActivationEvents.Startup,\n activate: ReactRoot,\n }),\n // Plugin.addModule({\n // activatesOn: Events.SetupArtifactDefinition,\n // activate: Tools,\n // }),\n Plugin.addModule({\n activatesOn: ActivationEvent.allOf(ActivationEvents.ProcessManagerReady, DeckEvents.StateReady),\n activate: UrlHandler,\n }),\n Plugin.addModule({\n activatesOn: ActivationEvent.allOf(ActivationEvents.ProcessManagerReady, DeckEvents.StateReady),\n activate: NotificationTracker,\n }),\n AppPlugin.addPluginAssetModule({\n asset: { pluginId: meta.id, path: 'PLUGIN.mdl', content: pluginSpec, mimeType: 'application/x-mdl' },\n }),\n Plugin.make,\n);\n\nexport default DeckPlugin;\n", "---\nid: org.dxos.plugin.deck\nname: DeckPlugin\nversion: 0.1.0\n---\n\nThe Deck plugin is the core layout engine for DXOS Composer. It manages the multi-plank workspace\n(\"deck\"), the sidebar, the complementary sidebar, dialogs, popovers, and toast notifications.\nIt owns the persisted and ephemeral layout state, handles URL routing, and implements every\n`LayoutOperation` that changes what is visible on screen.\n\n## Extensions\n\nThe following extension dialects are used in this document.\nEach extension is defined in the Appendix or resolved via its URI.\n\n| Term | URI |\n|-------------|--------------------------------|\n| `type` | `org.dxos.mdl.type@1.0` |\n| `feat` | `org.dxos.mdl.feat@1.0` |\n| `test` | `org.dxos.mdl.test@1.0` |\n| `component` | `org.dxos.mdl.component@1.0` |\n| `op` | `org.dxos.mdl.op@1.0` |\n\n## Types\n\n```mdl\ntype LayoutMode\n literals: multi | solo | solo--fullscreen\n```\n\n```mdl\ntype PartAdjustment\n literals: close | companion | solo | solo--fullscreen | increment-start | increment-end\n```\n\n```mdl\ntype DeckState\n fields:\n initialized: boolean # false until the deck has left solo mode for the first time\n active: string[] # item IDs of planks displayed in multi mode\n inactive: string[] # item IDs that have been closed; persisted for reopening\n solo?: string # item ID of the single plank in solo or fullscreen mode\n fullscreen: boolean # true when the solo plank is displayed without any chrome\n plankSizing: Record<string, number> # persisted plank widths in rem, keyed by item ID\n companionOpen: boolean # whether the companion pane is visible\n companionVariant?: string # which companion variant to display\n companionFrameSizing: Record<string, number> # companion frame widths in rem\n```\n\n```mdl\ntype StoredDeckState\n desc: Persisted plugin state stored in KVS/localStorage.\n fields:\n sidebarState: closed | collapsed | expanded\n complementarySidebarState: closed | collapsed | expanded\n complementarySidebarPanel?: string\n activeDeck: string\n previousDeck: string\n decks: Record<string, DeckState>\n previousMode: Record<string, LayoutMode>\n```\n\n```mdl\ntype EphemeralDeckState\n desc: Transient plugin state that is NOT persisted across sessions.\n fields:\n dialogOpen: boolean\n dialogType?: default | alert\n dialogBlockAlign?: start | center | end\n dialogContent?: { component: string; props?: any }\n popoverOpen: boolean\n popoverSide?: top | right | bottom | left\n popoverKind?: base | card\n popoverTitle?: string\n popoverContentRef?: string\n popoverContent?: { component: string } | { subject: any }\n toasts: Toast[]\n currentUndoId?: string\n scrollIntoView?: string\n```\n\n```mdl\ntype DeckSettings\n fields:\n enableDeck?: boolean # display multiple panels side by side\n encapsulatedPlanks?: boolean # render each plank in an isolated container\n showHints?: boolean # show keyboard shortcut hints in the UI\n enableNativeRedirect?: boolean # redirect supported URLs to the native desktop app\n```\n\n## Components\n\n```mdl\ncomponent Matrix\n desc: |\n The primary workspace canvas. Renders the active planks side by side in multi mode,\n or a single plank in solo/fullscreen mode. Manages plank-resize handles and the\n companion pane alongside each plank.\n props:\n state: StoredDeckState # persisted layout state atom\n ephemeral: EphemeralDeckState # transient state atom\n state:\n mode: LayoutMode # current layout mode derived from state\n actions:\n openPlank(id: string)\n closePlank(id: string)\n adjustPlank(id: string, type: PartAdjustment)\n resizePlank(id: string, size: number)\n layout: |\n \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 sidebar \u2502 plank A \u2502 plank B \u2502\n \u2502 \u2502 \u2502 \u2502\n \u2502 \u2502 [content] \u2502 [content] \u2502\n \u2502 \u2502 \u2502 \u2502\n \u2502 \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502\n \u2502 \u2502 companion \u2502 \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n```mdl\ncomponent DeckSettings\n desc: Settings panel rendered in the Composer settings surface.\n props:\n settings: DeckSettings\n actions:\n updateSetting(key: string, value: any)\n```\n\n## Operations\n\n```mdl\nop Open\n desc: |\n Opens one or more subjects (item IDs or navigation paths) in the deck.\n In multi mode uses stack semantics: truncates after the pivot then appends.\n In solo or uninitialised mode, replaces the current subject entirely.\n Validates each target against the app graph and redirects to a 404 node if not found.\n Fires ScrollIntoView, Expose, and an observability event for each newly opened item.\n input:\n subject: string[] # item IDs or navigation paths to open\n pivotId?: string # truncate deck after this item before appending\n key?: string # replace an existing plank whose ID shares this key prefix\n workspace?: string # switch to this workspace before opening\n navigation?: immediate # skip validation; expand path only\n scrollIntoView?: boolean # default true; set false to suppress auto-scroll\n state?: any\n variant?: string\n output: string[]\n effects: [echo:read, layout:state]\n requires: [AppGraph, AttentionCapabilities, ClientCapabilities, DeckCapabilities]\n```\n\n```mdl\nop Close\n desc: |\n Removes one or more subjects from the active deck.\n Computes which plank (if any) should receive attention after removal and\n schedules ScrollIntoView for it.\n input:\n subject: string[]\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities, AttentionCapabilities]\n```\n\n```mdl\nop SetLayoutMode\n desc: |\n Transitions to a new layout mode (multi, solo, solo--fullscreen) or reverts\n to the previously recorded mode. Persists the previous mode so it can be\n restored later. Toggling solo--fullscreen flips the fullscreen flag rather\n than unconditionally setting it.\n input:\n mode?: LayoutMode # target mode; omit if reverting\n subject?: string # item to solo; used only for solo / solo--fullscreen\n revert?: boolean # revert to the previously persisted mode\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdateSidebar\n desc: Update the sidebar visibility state (closed / collapsed / expanded).\n input:\n state?: closed | collapsed | expanded\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdateComplementary\n desc: Update the complementary-sidebar visibility state and optional panel selection.\n input:\n state?: closed | collapsed | expanded\n panel?: string\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdateDialog\n desc: |\n Show or hide the global dialog overlay. Accepts a surface component identifier\n or a boolean state flag. Propagates to EphemeralDeckState.\n input:\n subject?: string # surface component identifier to display\n props?: any # props forwarded to the dialog surface\n state?: boolean # false to close the dialog\n type?: default | alert\n blockAlign?: start | center | end\n overlayClasses?: string\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdatePopover\n desc: |\n Show or hide the global popover. Accepts anchor, content reference, or\n an explicit subject reference.\n input:\n subject?: any # subject reference to pass to the popover surface\n contentRef?: string # surface component identifier for popover content\n anchorId?: string # DOM element ID for positioning the popover anchor\n side?: top | right | bottom | left\n kind?: base | card\n title?: string\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdateCompanion\n desc: Update the companion-pane open state and active variant.\n input:\n open?: boolean\n variant?: string\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop AdjustPlank\n desc: Apply a PartAdjustment to a specific plank (close, solo, move, companion).\n input:\n id: string\n type: PartAdjustment\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdatePlankSize\n desc: Persist the width of a plank in rem.\n input:\n id: string\n size: number\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop AddToast\n desc: Push a toast notification into the ephemeral queue.\n input:\n title?: string\n body?: string\n duration?: number\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop ScrollIntoView\n desc: Schedule a subject to be scrolled into view when its plank mounts.\n input:\n subject: string\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop SwitchWorkspace\n desc: Switch the active workspace deck, persisting the previous workspace ID.\n input:\n subject: string # workspace ID to switch to\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop RevertWorkspace\n desc: Revert to the previously active workspace.\n input: void\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n## Features\n\n```mdl\nfeat F-1: Multi-Plank Layout (Deck)\n\n req F-1.1:\n when: enableDeck setting is true\n then: deck renders active planks side by side in a scrollable row\n\n req F-1.2:\n when: user opens a new subject with a pivot\n then: deck is truncated after the pivot and the new subject is appended\n\n req F-1.3:\n when: subject is already present in the active deck\n then: deck is unchanged and the existing plank scrolls into view\n\n req F-1.4:\n when: user resizes a plank\n then: new width is persisted in plankSizing and restored on next load\n```\n\n```mdl\nfeat F-2: Solo / Fullscreen Mode\n\n req F-2.1:\n when: user triggers SetLayoutMode with mode=solo\n then: only the targeted plank is visible; other planks move to inactive\n\n req F-2.2:\n when: user triggers SetLayoutMode with mode=solo--fullscreen\n then: plank fills the viewport; sidebar and heading chrome are hidden\n\n req F-2.3:\n when: user triggers SetLayoutMode with revert=true\n then: layout returns to the previously persisted mode\n```\n\n```mdl\nfeat F-3: Sidebar & Complementary Sidebar\n\n req F-3.1:\n when: UpdateSidebar is dispatched with a new state\n then: sidebar transitions between closed / collapsed / expanded and state is persisted\n\n req F-3.2:\n when: UpdateComplementary is dispatched with a panel\n then: complementary sidebar opens to the specified panel\n```\n\n```mdl\nfeat F-4: Dialog & Popover Overlay\n\n req F-4.1:\n when: UpdateDialog is dispatched with a subject component\n then: the matching surface is rendered inside the global dialog overlay\n\n req F-4.2:\n when: UpdatePopover is dispatched with an anchorId\n then: popover is positioned relative to that DOM element\n\n req F-4.3:\n when: dialog or popover state is set to false\n then: overlay is closed and content is unmounted\n```\n\n```mdl\nfeat F-5: URL Navigation & Deep Linking\n\n req F-5.1:\n when: app loads with a URL referencing an item\n then: DeckPlugin resolves the path and opens the item in the deck\n\n req F-5.2:\n when: active deck changes\n then: browser URL is updated to reflect the current plank state\n\n req F-5.3:\n when: a navigation target is not found in the app graph\n then: Open operation redirects to a 404 node\n```\n\n```mdl\nfeat F-6: Toast Notifications\n\n req F-6.1:\n when: AddToast is dispatched\n then: a toast is appended to the queue and displayed for its configured duration\n\n req F-6.2:\n when: ShowUndo is dispatched\n then: a toast with an undo action is shown; invoking it executes the linked undo operation\n```\n\n## Acceptance\n\n```mdl\ntest T-1: Open subject in multi mode\n given: deck is in multi mode with planks A and B active\n when: Open({ subject: ['C'] }) is dispatched\n then:\n - plank C is appended to the right of plank B\n - browser URL reflects the new deck state\n - plank C is scrolled into view\n```\n\n```mdl\ntest T-2: Open with pivot truncation\n given: deck has planks [A, B, C] active\n when: Open({ subject: ['D'], pivotId: 'A' }) is dispatched\n then:\n - active deck becomes [A, D]\n - planks B and C are moved to inactive\n```\n\n```mdl\ntest T-3: Open already-visible subject\n given: deck has plank A active\n when: Open({ subject: ['A'] }) is dispatched\n then:\n - active deck is unchanged\n - plank A scrolls into view\n```\n\n```mdl\ntest T-4: Solo mode\n given: deck is in multi mode\n when: SetLayoutMode({ mode: 'solo', subject: 'A' }) is dispatched\n then:\n - only plank A is visible\n - mode is stored as solo in StoredDeckState\n - previousMode records 'multi' for the active deck\n```\n\n```mdl\ntest T-5: Revert from solo to multi\n given: layout mode is solo, previousMode is multi\n when: SetLayoutMode({ revert: true }) is dispatched\n then:\n - deck returns to multi mode\n - previously inactive planks are restored\n```\n\n```mdl\ntest T-6: Close plank\n given: deck has planks [A, B, C] active; B has attention\n when: Close({ subject: ['B'] }) is dispatched\n then:\n - active deck becomes [A, C]\n - attention moves to an adjacent plank\n - ScrollIntoView is scheduled for the newly attended plank\n```\n\n```mdl\ntest T-7: Toast is shown and dismissed\n given: no toasts are in the queue\n when: AddToast({ title: 'Saved', duration: 3000 }) is dispatched\n then:\n - toast appears in the UI\n - toast is removed after 3 seconds\n```\n\n---\n\n## Appendix: Extension Definitions\n\nExtension block types used in this document are defined below using\nthe core `ext` primitive \u2014 the only construct the base language provides.\n\n```mdl\next type\n uri: org.dxos.mdl.type@1.0\n desc: A named data structure with typed fields and optional literals.\n fields:\n desc?: Prose\n fields?: FieldMap # name[?]: TypeExpr (# inline comment)\n literals?: UnionList # a | b | c\n extends?: TypeRef[]\n```\n\n```mdl\next feat\n uri: org.dxos.mdl.feat@1.0\n desc: A named feature grouping one or more requirements.\n fields:\n desc?: Prose\n req: RequirementList\n nesting: self # feat blocks may contain feat blocks\n```\n\n```mdl\next test\n uri: org.dxos.mdl.test@1.0\n desc: An acceptance scenario expressed as given / when / then steps.\n fields:\n given?: Step | Step[]\n when?: Step | Step[]\n then: Step | Step[]\n tags?: TagList\n```\n\n```mdl\next component\n uri: org.dxos.mdl.component@1.0\n desc: A UI component with props, internal state, slots, actions, and events.\n fields:\n desc?: Prose\n props?: FieldMap # external inputs (immutable inside component)\n state?: FieldMap # internal reactive state\n slots?: FieldMap # named ReactNode injection points\n actions?: ActionMap # methods the component exposes or handles\n emits?: EventMap # events the component raises to its parent\n layout?: CodeBlock # ASCII sketch of visual structure (non-normative)\n```\n\n```mdl\next op\n uri: org.dxos.mdl.op@1.0\n desc: |\n A named operation with typed inputs, outputs, and declared errors.\n Pure ops have no effects or requires. Effectful ops declare both.\n fields:\n desc?: Prose\n input?: FieldMap # named input parameters\n output?: TypeExpr # return type\n errors?: ErrorMap # name: Prose (when this error occurs)\n effects?: EffectList # echo:read | echo:write | http | fs | ...\n requires?: ServiceList # injected service dependencies\n note?: Prose # implementation guidance (non-normative)\n```\n"],
5
- "mappings": ";;;AAIA,SAASA,qBAAqB;AAE9B,SAASC,iBAAiBC,kBAAkBC,cAAc;AAC1D,SAASC,qBAAqBC,iBAAiB;AAC/C,SAASC,gBAAgBC,yBAAyB;AAElD,SACEC,iBACAC,gBACAC,cACAC,WACAC,qBACAC,kBACAC,WACAC,cACAC,kBACK;AACP,SAASC,YAAY;AACrB,SAASX,oBAAoB;AAC7B,SAASY,kBAAkB;;;ACvB3B;;;AD+BAC,cAAc,KAAA;AAEP,IAAMC,aAAaC,OAAOC,OAAOC,IAAAA,EAAMC;EAC5CC,UAAUC,kBAAkB;IAAEC,UAAUC;EAAgB,CAAA;EACxDH,UAAUI,0BAA0B;IAAEF,UAAUG;EAAiB,CAAA;EACjEL,UAAUM,iBAAiB;IAAEJ,UAAUK;EAAa,CAAA;EACpDP,UAAUQ,sBAAsB;IAAEC,cAAc;SAAIA;SAAiBC;;EAAmB,CAAA;EACxFd,OAAOe,UAAU;IACfC,aAAaC,oBAAoBC;IACjCC,sBAAsB;MAACC,WAAWC;;IAClCf,UAAUgB;EACZ,CAAA;EACAtB,OAAOe,UAAU;IACfC,aAAaO,gBAAgBC,MAAMJ,WAAWC,eAAeI,iBAAiBC,mBAAmB;IACjGpB,UAAUqB;EACZ,CAAA;EACA3B,OAAOe,UAAU;;;;IAIfC,aAAaO,gBAAgBK,MAAMX,oBAAoBC,eAAeD,oBAAoBY,aAAa;IACvGV,sBAAsB;MAACF,oBAAoBa;MAAaV,WAAWW;;IACnEzB,UAAU0B;EACZ,CAAA;EACAhC,OAAOe,UAAU;IACfC,aAAaS,iBAAiBQ;IAC9B3B,UAAU4B;EACZ,CAAA;;;;;EAKAlC,OAAOe,UAAU;IACfC,aAAaO,gBAAgBC,MAAMC,iBAAiBC,qBAAqBN,WAAWW,UAAU;IAC9FzB,UAAU6B;EACZ,CAAA;EACAnC,OAAOe,UAAU;IACfC,aAAaO,gBAAgBC,MAAMC,iBAAiBC,qBAAqBN,WAAWW,UAAU;IAC9FzB,UAAU8B;EACZ,CAAA;EACAhC,UAAUiC,qBAAqB;IAC7BC,OAAO;MAAEC,UAAUrC,KAAKsC;MAAIC,MAAM;MAAcC,SAASC;MAAYC,UAAU;IAAoB;EACrG,CAAA;EACA5C,OAAO6C;AAAI;AAGb,IAAA,qBAAe9C;",
6
- "names": ["setAutoFreeze", "ActivationEvent", "ActivationEvents", "Plugin", "AppActivationEvents", "AppPlugin", "translations", "stackTranslations", "AppGraphBuilder", "CheckAppScheme", "DeckSettings", "DeckState", "NotificationTracker", "OperationHandler", "ReactRoot", "ReactSurface", "UrlHandler", "meta", "DeckEvents", "setAutoFreeze", "DeckPlugin", "Plugin", "define", "meta", "pipe", "AppPlugin", "addAppGraphModule", "activate", "AppGraphBuilder", "addOperationHandlerModule", "OperationHandler", "addSurfaceModule", "ReactSurface", "addTranslationsModule", "translations", "stackTranslations", "addModule", "activatesOn", "AppActivationEvents", "SetupSettings", "firesAfterActivation", "DeckEvents", "SettingsReady", "DeckSettings", "ActivationEvent", "allOf", "ActivationEvents", "ProcessManagerReady", "CheckAppScheme", "oneOf", "SetupAppGraph", "LayoutReady", "StateReady", "DeckState", "Startup", "ReactRoot", "UrlHandler", "NotificationTracker", "addPluginAssetModule", "asset", "pluginId", "id", "path", "content", "pluginSpec", "mimeType", "make"]
3
+ "sources": ["../../../src/DeckPlugin.ts", "raw-loader:/__w/dxos/dxos/packages/plugins/plugin-deck/PLUGIN.mdl"],
4
+ "sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport { setAutoFreeze } from 'immer';\n\nimport { ActivationEvent, ActivationEvents, Plugin } from '@dxos/app-framework';\nimport { AppActivationEvents, AppPlugin } from '@dxos/app-toolkit';\n\nimport {\n AppGraphBuilder,\n CheckAppScheme,\n DeckSettings,\n DeckState,\n NotificationTracker,\n OperationHandler,\n ReactRoot,\n ReactSurface,\n UrlHandler,\n} from '#capabilities';\nimport { meta } from '#meta';\nimport { translations } from '#translations';\nimport { DeckEvents } from '#types';\n\n// eslint-disable-next-line import/no-relative-packages\nimport pluginSpec from '../PLUGIN.mdl?raw';\n\n// NOTE(Zan): When producing values with immer, we shouldn't auto-freeze them because\n// our signal implementation needs to add some hidden properties to the produced values.\n// TODO(Zan): Move this to a more global location if we use immer more broadly.\nsetAutoFreeze(false);\n\nexport const DeckPlugin = Plugin.define(meta).pipe(\n AppPlugin.addAppGraphModule({ activate: AppGraphBuilder }),\n AppPlugin.addOperationHandlerModule({ activate: OperationHandler }),\n AppPlugin.addSurfaceModule({ activate: ReactSurface }),\n AppPlugin.addTranslationsModule({ translations }),\n Plugin.addModule({\n activatesOn: AppActivationEvents.SetupSettings,\n firesAfterActivation: [DeckEvents.SettingsReady],\n activate: DeckSettings,\n }),\n Plugin.addModule({\n activatesOn: ActivationEvent.allOf(DeckEvents.SettingsReady, ActivationEvents.ProcessManagerReady),\n activate: CheckAppScheme,\n }),\n Plugin.addModule({\n // TODO(wittjosiah): Does not integrate with settings store.\n // Should this be a different event?\n // Should settings store be renamed to be more generic?\n activatesOn: ActivationEvent.oneOf(AppActivationEvents.SetupSettings, AppActivationEvents.SetupAppGraph),\n firesAfterActivation: [AppActivationEvents.LayoutReady, DeckEvents.StateReady],\n activate: DeckState,\n }),\n Plugin.addModule({\n activatesOn: ActivationEvents.Startup,\n activate: ReactRoot,\n }),\n // Plugin.addModule({\n // activatesOn: Events.SetupArtifactDefinition,\n // activate: Tools,\n // }),\n Plugin.addModule({\n activatesOn: ActivationEvent.allOf(ActivationEvents.ProcessManagerReady, DeckEvents.StateReady),\n activate: UrlHandler,\n }),\n Plugin.addModule({\n activatesOn: ActivationEvent.allOf(ActivationEvents.ProcessManagerReady, DeckEvents.StateReady),\n activate: NotificationTracker,\n }),\n AppPlugin.addPluginAssetModule({\n asset: { pluginId: meta.profile.key, path: 'PLUGIN.mdl', content: pluginSpec, mimeType: 'application/x-mdl' },\n }),\n Plugin.make,\n);\n\nexport default DeckPlugin;\n", "---\nid: org.dxos.plugin.deck\nname: DeckPlugin\nversion: 0.1.0\n---\n\nThe Deck plugin is the core layout engine for DXOS Composer. It manages the multi-plank workspace\n(\"deck\"), the sidebar, the complementary sidebar, dialogs, popovers, and toast notifications.\nIt owns the persisted and ephemeral layout state, handles URL routing, and implements every\n`LayoutOperation` that changes what is visible on screen.\n\n## Extensions\n\nThe following extension dialects are used in this document.\nEach extension is defined in the Appendix or resolved via its URI.\n\n| Term | URI |\n|-------------|--------------------------------|\n| `type` | `org.dxos.mdl.type@1.0` |\n| `feat` | `org.dxos.mdl.feat@1.0` |\n| `test` | `org.dxos.mdl.test@1.0` |\n| `component` | `org.dxos.mdl.component@1.0` |\n| `op` | `org.dxos.mdl.op@1.0` |\n\n## Types\n\n```mdl\ntype LayoutMode\n literals: multi | solo | solo--fullscreen\n```\n\n```mdl\ntype PartAdjustment\n literals: close | companion | solo | solo--fullscreen | increment-start | increment-end\n```\n\n```mdl\ntype DeckState\n fields:\n initialized: boolean # false until the deck has left solo mode for the first time\n active: string[] # item IDs of planks displayed in multi mode\n inactive: string[] # item IDs that have been closed; persisted for reopening\n solo?: string # item ID of the single plank in solo or fullscreen mode\n fullscreen: boolean # true when the solo plank is displayed without any chrome\n plankSizing: Record<string, number> # persisted plank widths in rem, keyed by item ID\n companionOpen: boolean # whether the companion pane is visible\n companionOrientation?: 'horizontal' | 'vertical' # companion splitter orientation (side-by-side / stacked)\n companionFrameSizing: Record<string, number> # companion frame widths in rem\n # NOTE: the selected companion variant and per-orientation split size now live in react-ui-attention\n # view state (global, localStorage), not on DeckState \u2014 see util/companion-view-state.ts.\n```\n\n```mdl\ntype StoredDeckState\n desc: Persisted plugin state stored in KVS/localStorage.\n fields:\n sidebarState: closed | collapsed | expanded\n complementarySidebarState: closed | collapsed | expanded\n complementarySidebarPanel?: string\n activeDeck: string\n previousDeck: string\n decks: Record<string, DeckState>\n previousMode: Record<string, LayoutMode>\n```\n\n```mdl\ntype EphemeralDeckState\n desc: Transient plugin state that is NOT persisted across sessions.\n fields:\n dialogOpen: boolean\n dialogType?: default | alert\n dialogBlockAlign?: start | center | end\n dialogContent?: { component: string; props?: any }\n popoverOpen: boolean\n popoverSide?: top | right | bottom | left\n popoverKind?: base | card\n popoverTitle?: string\n popoverContentRef?: string\n popoverContent?: { component: string } | { subject: any }\n toasts: Toast[]\n currentUndoId?: string\n scrollIntoView?: string\n```\n\n```mdl\ntype DeckSettings\n fields:\n enableDeck?: boolean # display multiple panels side by side\n encapsulatedPlanks?: boolean # render each plank in an isolated container\n showHints?: boolean # show keyboard shortcut hints in the UI\n enableNativeRedirect?: boolean # redirect supported URLs to the native desktop app\n```\n\n## Components\n\n```mdl\ncomponent Matrix\n desc: |\n The primary workspace canvas. Renders the active planks side by side in multi mode,\n or a single plank in solo/fullscreen mode. Manages plank-resize handles and the\n companion pane alongside each plank.\n props:\n state: StoredDeckState # persisted layout state atom\n ephemeral: EphemeralDeckState # transient state atom\n state:\n mode: LayoutMode # current layout mode derived from state\n actions:\n openPlank(id: string)\n closePlank(id: string)\n adjustPlank(id: string, type: PartAdjustment)\n resizePlank(id: string, size: number)\n layout: |\n \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 sidebar \u2502 plank A \u2502 plank B \u2502\n \u2502 \u2502 \u2502 \u2502\n \u2502 \u2502 [content] \u2502 [content] \u2502\n \u2502 \u2502 \u2502 \u2502\n \u2502 \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502\n \u2502 \u2502 companion \u2502 \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n```mdl\ncomponent DeckSettings\n desc: Settings panel rendered in the Composer settings surface.\n props:\n settings: DeckSettings\n actions:\n updateSetting(key: string, value: any)\n```\n\n## Operations\n\n```mdl\nop Open\n desc: |\n Opens one or more subjects (item IDs or navigation paths) in the deck.\n In multi mode uses stack semantics: truncates after the pivot then appends.\n In solo or uninitialised mode, replaces the current subject entirely.\n Validates each target against the app graph and redirects to a 404 node if not found.\n Fires ScrollIntoView, Expose, and an observability event for each newly opened item.\n input:\n subject: string[] # item IDs or navigation paths to open\n pivotId?: string # truncate deck after this item before appending\n key?: string # replace an existing plank whose ID shares this key prefix\n workspace?: string # switch to this workspace before opening\n navigation?: immediate # skip validation; expand path only\n scrollIntoView?: boolean # default true; set false to suppress auto-scroll\n state?: any\n variant?: string\n output: string[]\n effects: [echo:read, layout:state]\n requires: [AppGraph, AttentionCapabilities, ClientCapabilities, DeckCapabilities]\n```\n\n```mdl\nop Close\n desc: |\n Removes one or more subjects from the active deck.\n Computes which plank (if any) should receive attention after removal and\n schedules ScrollIntoView for it.\n input:\n subject: string[]\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities, AttentionCapabilities]\n```\n\n```mdl\nop SetLayoutMode\n desc: |\n Transitions to a new layout mode (multi, solo, solo--fullscreen) or reverts\n to the previously recorded mode. Persists the previous mode so it can be\n restored later. Toggling solo--fullscreen flips the fullscreen flag rather\n than unconditionally setting it.\n input:\n mode?: LayoutMode # target mode; omit if reverting\n subject?: string # item to solo; used only for solo / solo--fullscreen\n revert?: boolean # revert to the previously persisted mode\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdateSidebar\n desc: Update the sidebar visibility state (closed / collapsed / expanded).\n input:\n state?: closed | collapsed | expanded\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdateComplementary\n desc: Update the complementary-sidebar visibility state and optional panel selection.\n input:\n state?: closed | collapsed | expanded\n panel?: string\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdateDialog\n desc: |\n Show or hide the global dialog overlay. Accepts a surface component identifier\n or a boolean state flag. Propagates to EphemeralDeckState.\n input:\n subject?: string # surface component identifier to display\n props?: any # props forwarded to the dialog surface\n state?: boolean # false to close the dialog\n type?: default | alert\n blockAlign?: start | center | end\n overlayClasses?: string\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdatePopover\n desc: |\n Show or hide the global popover. Accepts anchor, content reference, or\n an explicit subject reference.\n input:\n subject?: any # subject reference to pass to the popover surface\n contentRef?: string # surface component identifier for popover content\n anchorId?: string # DOM element ID for positioning the popover anchor\n side?: top | right | bottom | left\n kind?: base | card\n title?: string\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdateCompanion\n desc: Update the companion-pane open state and active variant.\n input:\n open?: boolean\n variant?: string\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop AdjustPlank\n desc: Apply a PartAdjustment to a specific plank (close, solo, move, companion).\n input:\n id: string\n type: PartAdjustment\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop UpdatePlankSize\n desc: Persist the width of a plank in rem.\n input:\n id: string\n size: number\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop AddToast\n desc: Push a toast notification into the ephemeral queue.\n input:\n title?: string\n body?: string\n duration?: number\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop ScrollIntoView\n desc: Schedule a subject to be scrolled into view when its plank mounts.\n input:\n subject: string\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop SwitchWorkspace\n desc: Switch the active workspace deck, persisting the previous workspace ID.\n input:\n subject: string # workspace ID to switch to\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n```mdl\nop RevertWorkspace\n desc: Revert to the previously active workspace.\n input: void\n output: void\n effects: [layout:state]\n requires: [DeckCapabilities]\n```\n\n## Features\n\n```mdl\nfeat F-1: Multi-Plank Layout (Deck)\n\n req F-1.1:\n when: enableDeck setting is true\n then: deck renders active planks side by side in a scrollable row\n\n req F-1.2:\n when: user opens a new subject with a pivot\n then: deck is truncated after the pivot and the new subject is appended\n\n req F-1.3:\n when: subject is already present in the active deck\n then: deck is unchanged and the existing plank scrolls into view\n\n req F-1.4:\n when: user resizes a plank\n then: new width is persisted in plankSizing and restored on next load\n```\n\n```mdl\nfeat F-2: Solo / Fullscreen Mode\n\n req F-2.1:\n when: user triggers SetLayoutMode with mode=solo\n then: only the targeted plank is visible; other planks move to inactive\n\n req F-2.2:\n when: user triggers SetLayoutMode with mode=solo--fullscreen\n then: plank fills the viewport; sidebar and heading chrome are hidden\n\n req F-2.3:\n when: user triggers SetLayoutMode with revert=true\n then: layout returns to the previously persisted mode\n```\n\n```mdl\nfeat F-3: Sidebar & Complementary Sidebar\n\n req F-3.1:\n when: UpdateSidebar is dispatched with a new state\n then: sidebar transitions between closed / collapsed / expanded and state is persisted\n\n req F-3.2:\n when: UpdateComplementary is dispatched with a panel\n then: complementary sidebar opens to the specified panel\n```\n\n```mdl\nfeat F-4: Dialog & Popover Overlay\n\n req F-4.1:\n when: UpdateDialog is dispatched with a subject component\n then: the matching surface is rendered inside the global dialog overlay\n\n req F-4.2:\n when: UpdatePopover is dispatched with an anchorId\n then: popover is positioned relative to that DOM element\n\n req F-4.3:\n when: dialog or popover state is set to false\n then: overlay is closed and content is unmounted\n```\n\n```mdl\nfeat F-5: URL Navigation & Deep Linking\n\n req F-5.1:\n when: app loads with a URL referencing an item\n then: DeckPlugin resolves the path and opens the item in the deck\n\n req F-5.2:\n when: active deck changes\n then: browser URL is updated to reflect the current plank state\n\n req F-5.3:\n when: a navigation target is not found in the app graph\n then: Open operation redirects to a 404 node\n```\n\n```mdl\nfeat F-6: Toast Notifications\n\n req F-6.1:\n when: AddToast is dispatched\n then: a toast is appended to the queue and displayed for its configured duration\n\n req F-6.2:\n when: ShowUndo is dispatched\n then: a toast with an undo action is shown; invoking it executes the linked undo operation\n```\n\n## Acceptance\n\n```mdl\ntest T-1: Open subject in multi mode\n given: deck is in multi mode with planks A and B active\n when: Open({ subject: ['C'] }) is dispatched\n then:\n - plank C is appended to the right of plank B\n - browser URL reflects the new deck state\n - plank C is scrolled into view\n```\n\n```mdl\ntest T-2: Open with pivot truncation\n given: deck has planks [A, B, C] active\n when: Open({ subject: ['D'], pivotId: 'A' }) is dispatched\n then:\n - active deck becomes [A, D]\n - planks B and C are moved to inactive\n```\n\n```mdl\ntest T-3: Open already-visible subject\n given: deck has plank A active\n when: Open({ subject: ['A'] }) is dispatched\n then:\n - active deck is unchanged\n - plank A scrolls into view\n```\n\n```mdl\ntest T-4: Solo mode\n given: deck is in multi mode\n when: SetLayoutMode({ mode: 'solo', subject: 'A' }) is dispatched\n then:\n - only plank A is visible\n - mode is stored as solo in StoredDeckState\n - previousMode records 'multi' for the active deck\n```\n\n```mdl\ntest T-5: Revert from solo to multi\n given: layout mode is solo, previousMode is multi\n when: SetLayoutMode({ revert: true }) is dispatched\n then:\n - deck returns to multi mode\n - previously inactive planks are restored\n```\n\n```mdl\ntest T-6: Close plank\n given: deck has planks [A, B, C] active; B has attention\n when: Close({ subject: ['B'] }) is dispatched\n then:\n - active deck becomes [A, C]\n - attention moves to an adjacent plank\n - ScrollIntoView is scheduled for the newly attended plank\n```\n\n```mdl\ntest T-7: Toast is shown and dismissed\n given: no toasts are in the queue\n when: AddToast({ title: 'Saved', duration: 3000 }) is dispatched\n then:\n - toast appears in the UI\n - toast is removed after 3 seconds\n```\n\n---\n\n## Appendix: Extension Definitions\n\nExtension block types used in this document are defined below using\nthe core `ext` primitive \u2014 the only construct the base language provides.\n\n```mdl\next type\n uri: org.dxos.mdl.type@1.0\n desc: A named data structure with typed fields and optional literals.\n fields:\n desc?: Prose\n fields?: FieldMap # name[?]: TypeExpr (# inline comment)\n literals?: UnionList # a | b | c\n extends?: TypeRef[]\n```\n\n```mdl\next feat\n uri: org.dxos.mdl.feat@1.0\n desc: A named feature grouping one or more requirements.\n fields:\n desc?: Prose\n req: RequirementList\n nesting: self # feat blocks may contain feat blocks\n```\n\n```mdl\next test\n uri: org.dxos.mdl.test@1.0\n desc: An acceptance scenario expressed as given / when / then steps.\n fields:\n given?: Step | Step[]\n when?: Step | Step[]\n then: Step | Step[]\n tags?: TagList\n```\n\n```mdl\next component\n uri: org.dxos.mdl.component@1.0\n desc: A UI component with props, internal state, slots, actions, and events.\n fields:\n desc?: Prose\n props?: FieldMap # external inputs (immutable inside component)\n state?: FieldMap # internal reactive state\n slots?: FieldMap # named ReactNode injection points\n actions?: ActionMap # methods the component exposes or handles\n emits?: EventMap # events the component raises to its parent\n layout?: CodeBlock # ASCII sketch of visual structure (non-normative)\n```\n\n```mdl\next op\n uri: org.dxos.mdl.op@1.0\n desc: |\n A named operation with typed inputs, outputs, and declared errors.\n Pure ops have no effects or requires. Effectful ops declare both.\n fields:\n desc?: Prose\n input?: FieldMap # named input parameters\n output?: TypeExpr # return type\n errors?: ErrorMap # name: Prose (when this error occurs)\n effects?: EffectList # echo:read | echo:write | http | fs | ...\n requires?: ServiceList # injected service dependencies\n note?: Prose # implementation guidance (non-normative)\n```\n"],
5
+ "mappings": ";;;AAIA,SAASA,qBAAqB;AAE9B,SAASC,iBAAiBC,kBAAkBC,cAAc;AAC1D,SAASC,qBAAqBC,iBAAiB;AAE/C,SACEC,iBACAC,gBACAC,cACAC,WACAC,qBACAC,kBACAC,WACAC,cACAC,kBACK;AACP,SAASC,YAAY;AACrB,SAASC,oBAAoB;AAC7B,SAASC,kBAAkB;;;ACtB3B;;;AD8BAC,cAAc,KAAA;AAEP,IAAMC,aAAaC,OAAOC,OAAOC,IAAAA,EAAMC;EAC5CC,UAAUC,kBAAkB;IAAEC,UAAUC;EAAgB,CAAA;EACxDH,UAAUI,0BAA0B;IAAEF,UAAUG;EAAiB,CAAA;EACjEL,UAAUM,iBAAiB;IAAEJ,UAAUK;EAAa,CAAA;EACpDP,UAAUQ,sBAAsB;IAAEC;EAAa,CAAA;EAC/Cb,OAAOc,UAAU;IACfC,aAAaC,oBAAoBC;IACjCC,sBAAsB;MAACC,WAAWC;;IAClCd,UAAUe;EACZ,CAAA;EACArB,OAAOc,UAAU;IACfC,aAAaO,gBAAgBC,MAAMJ,WAAWC,eAAeI,iBAAiBC,mBAAmB;IACjGnB,UAAUoB;EACZ,CAAA;EACA1B,OAAOc,UAAU;;;;IAIfC,aAAaO,gBAAgBK,MAAMX,oBAAoBC,eAAeD,oBAAoBY,aAAa;IACvGV,sBAAsB;MAACF,oBAAoBa;MAAaV,WAAWW;;IACnExB,UAAUyB;EACZ,CAAA;EACA/B,OAAOc,UAAU;IACfC,aAAaS,iBAAiBQ;IAC9B1B,UAAU2B;EACZ,CAAA;;;;;EAKAjC,OAAOc,UAAU;IACfC,aAAaO,gBAAgBC,MAAMC,iBAAiBC,qBAAqBN,WAAWW,UAAU;IAC9FxB,UAAU4B;EACZ,CAAA;EACAlC,OAAOc,UAAU;IACfC,aAAaO,gBAAgBC,MAAMC,iBAAiBC,qBAAqBN,WAAWW,UAAU;IAC9FxB,UAAU6B;EACZ,CAAA;EACA/B,UAAUgC,qBAAqB;IAC7BC,OAAO;MAAEC,UAAUpC,KAAKqC,QAAQC;MAAKC,MAAM;MAAcC,SAASC;MAAYC,UAAU;IAAoB;EAC9G,CAAA;EACA5C,OAAO6C;AAAI;AAGb,IAAA,qBAAe9C;",
6
+ "names": ["setAutoFreeze", "ActivationEvent", "ActivationEvents", "Plugin", "AppActivationEvents", "AppPlugin", "AppGraphBuilder", "CheckAppScheme", "DeckSettings", "DeckState", "NotificationTracker", "OperationHandler", "ReactRoot", "ReactSurface", "UrlHandler", "meta", "translations", "DeckEvents", "setAutoFreeze", "DeckPlugin", "Plugin", "define", "meta", "pipe", "AppPlugin", "addAppGraphModule", "activate", "AppGraphBuilder", "addOperationHandlerModule", "OperationHandler", "addSurfaceModule", "ReactSurface", "addTranslationsModule", "translations", "addModule", "activatesOn", "AppActivationEvents", "SetupSettings", "firesAfterActivation", "DeckEvents", "SettingsReady", "DeckSettings", "ActivationEvent", "allOf", "ActivationEvents", "ProcessManagerReady", "CheckAppScheme", "oneOf", "SetupAppGraph", "LayoutReady", "StateReady", "DeckState", "Startup", "ReactRoot", "UrlHandler", "NotificationTracker", "addPluginAssetModule", "asset", "pluginId", "profile", "key", "path", "content", "pluginSpec", "mimeType", "make"]
7
7
  }
@@ -0,0 +1,32 @@
1
+ import "./chunk-J5LGTIGS.mjs";
2
+
3
+ // src/containers/DeckSettings/DeckSettings.tsx
4
+ import React from "react";
5
+ import { useTranslation } from "@dxos/react-ui";
6
+ import { Form } from "@dxos/react-ui-form";
7
+ import { meta } from "#meta";
8
+ import { Settings } from "#types";
9
+ var isSocket = !!globalThis.__args;
10
+ var DeckSettings = ({ settings, onSettingsChange }) => {
11
+ const { t } = useTranslation(meta.profile.key);
12
+ return /* @__PURE__ */ React.createElement(Form.Root, {
13
+ variant: "settings",
14
+ schema: Settings.Settings,
15
+ values: settings,
16
+ readonly: !onSettingsChange,
17
+ onValuesChanged: (values) => onSettingsChange?.((current) => ({
18
+ ...current,
19
+ ...values
20
+ }))
21
+ }, /* @__PURE__ */ React.createElement(Form.Viewport, {
22
+ scroll: true
23
+ }, /* @__PURE__ */ React.createElement(Form.Content, null, /* @__PURE__ */ React.createElement(Form.Section, {
24
+ title: meta.profile.name ?? meta.profile.key
25
+ }, /* @__PURE__ */ React.createElement(Form.FieldSet, {
26
+ filter: (properties) => isSocket ? properties.filter((property) => property.name !== "enableNativeRedirect") : properties
27
+ })))));
28
+ };
29
+ export {
30
+ DeckSettings as default
31
+ };
32
+ //# sourceMappingURL=DeckSettings-Z7GMB7XJ.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/containers/DeckSettings/DeckSettings.tsx"],
4
+ "sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport React from 'react';\n\nimport { type AppSurface } from '@dxos/app-toolkit/ui';\nimport { useTranslation } from '@dxos/react-ui';\nimport { Form } from '@dxos/react-ui-form';\n\nimport { meta } from '#meta';\nimport { Settings } from '#types';\n\nconst isSocket = !!(globalThis as any).__args;\n\nexport type DeckSettingsProps = AppSurface.SettingsProps<Settings.Settings>;\n\nexport const DeckSettings = ({ settings, onSettingsChange }: DeckSettingsProps) => {\n const { t } = useTranslation(meta.profile.key);\n\n return (\n <Form.Root\n variant='settings'\n schema={Settings.Settings}\n values={settings}\n readonly={!onSettingsChange}\n onValuesChanged={(values) => onSettingsChange?.((current) => ({ ...current, ...values }))}\n >\n <Form.Viewport scroll>\n <Form.Content>\n <Form.Section title={meta.profile.name ?? meta.profile.key}>\n <Form.FieldSet\n filter={(properties) =>\n isSocket ? properties.filter((property) => property.name !== 'enableNativeRedirect') : properties\n }\n />\n </Form.Section>\n </Form.Content>\n </Form.Viewport>\n </Form.Root>\n );\n};\n"],
5
+ "mappings": ";;;AAIA,OAAOA,WAAW;AAGlB,SAASC,sBAAsB;AAC/B,SAASC,YAAY;AAErB,SAASC,YAAY;AACrB,SAASC,gBAAgB;AAEzB,IAAMC,WAAW,CAAC,CAAEC,WAAmBC;AAIhC,IAAMC,eAAe,CAAC,EAAEC,UAAUC,iBAAgB,MAAqB;AAC5E,QAAM,EAAEC,EAAC,IAAKV,eAAeE,KAAKS,QAAQC,GAAG;AAE7C,SACE,sBAAA,cAACX,KAAKY,MAAI;IACRC,SAAQ;IACRC,QAAQZ,SAASA;IACjBa,QAAQR;IACRS,UAAU,CAACR;IACXS,iBAAiB,CAACF,WAAWP,mBAAmB,CAACU,aAAa;MAAE,GAAGA;MAAS,GAAGH;IAAO,EAAA;KAEtF,sBAAA,cAACf,KAAKmB,UAAQ;IAACC,QAAAA;KACb,sBAAA,cAACpB,KAAKqB,SAAO,MACX,sBAAA,cAACrB,KAAKsB,SAAO;IAACC,OAAOtB,KAAKS,QAAQc,QAAQvB,KAAKS,QAAQC;KACrD,sBAAA,cAACX,KAAKyB,UAAQ;IACZC,QAAQ,CAACC,eACPxB,WAAWwB,WAAWD,OAAO,CAACE,aAAaA,SAASJ,SAAS,sBAAA,IAA0BG;;AAQvG;",
6
+ "names": ["React", "useTranslation", "Form", "meta", "Settings", "isSocket", "globalThis", "__args", "DeckSettings", "settings", "onSettingsChange", "t", "profile", "key", "Root", "variant", "schema", "values", "readonly", "onValuesChanged", "current", "Viewport", "scroll", "Content", "Section", "title", "name", "FieldSet", "filter", "properties", "property"]
7
+ }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  DeckCapabilities_exports
3
- } from "./chunk-BS4EOYMK.mjs";
3
+ } from "./chunk-ICOJ4GMB.mjs";
4
4
  import "./chunk-J5LGTIGS.mjs";
5
5
 
6
6
  // src/operations/add-toast.ts
@@ -21,4 +21,4 @@ var add_toast_default = handler;
21
21
  export {
22
22
  add_toast_default as default
23
23
  };
24
- //# sourceMappingURL=add-toast-TNB6DXWU.mjs.map
24
+ //# sourceMappingURL=add-toast-BHNEH5UY.mjs.map
@@ -8,10 +8,11 @@ import {
8
8
  DeckCapabilities_exports,
9
9
  DeckOperation_exports,
10
10
  PLANK_COMPANION_TYPE
11
- } from "./chunk-BS4EOYMK.mjs";
11
+ } from "./chunk-ICOJ4GMB.mjs";
12
12
  import {
13
13
  computeActiveUpdates
14
- } from "./chunk-GBIGQKYW.mjs";
14
+ } from "./chunk-PP32NEVY.mjs";
15
+ import "./chunk-SONATB35.mjs";
15
16
  import "./chunk-J5LGTIGS.mjs";
16
17
 
17
18
  // src/operations/adjust.ts
@@ -23,7 +24,6 @@ import { AppCapabilities, LayoutOperation } from "@dxos/app-toolkit";
23
24
  import { Operation } from "@dxos/compute";
24
25
  import { AttentionCapabilities } from "@dxos/plugin-attention";
25
26
  import { Graph } from "@dxos/plugin-graph";
26
- import { byPosition } from "@dxos/util";
27
27
  var handler = DeckOperation_exports.Adjust.pipe(Operation.withHandler(Effect.fnUntraced(function* (input) {
28
28
  const _state = yield* Capabilities.getAtomValue(DeckCapabilities_exports.State);
29
29
  const deck = yield* DeckCapabilities_exports.getDeck();
@@ -77,12 +77,18 @@ var handler = DeckOperation_exports.Adjust.pipe(Operation.withHandler(Effect.fnU
77
77
  ]
78
78
  });
79
79
  }
80
- if (input.type === "companion") {
81
- const companion = Function.pipe(Graph.getNode(graph, input.id), Option.map((node) => Graph.getConnections(graph, node.id, "child").filter((n) => n.type === PLANK_COMPANION_TYPE).toSorted((a, b) => byPosition(a.properties, b.properties))), Option.flatMap((companions) => companions.length > 0 ? Option.some(companions[0]) : Option.none()));
82
- if (Option.isSome(companion)) {
83
- yield* Operation.invoke(LayoutOperation.UpdateCompanion, {
84
- subject: companion.value.id
85
- });
80
+ if (input.type === "companion" || input.type === "companion-vertical") {
81
+ const orientation = input.type === "companion-vertical" ? "vertical" : "horizontal";
82
+ yield* Capabilities.updateAtomValue(DeckCapabilities_exports.State, (state) => updateActiveDeck(state, {
83
+ companionOrientation: orientation
84
+ }));
85
+ if (!deck.companionOpen) {
86
+ const hasCompanion = Function.pipe(Graph.getNode(graph, input.id), Option.map((node) => Graph.getConnections(graph, node.id, "child").some((n) => n.type === PLANK_COMPANION_TYPE)), Option.getOrElse(() => false));
87
+ if (hasCompanion) {
88
+ yield* Capabilities.updateAtomValue(DeckCapabilities_exports.State, (state) => updateActiveDeck(state, {
89
+ companionOpen: true
90
+ }));
91
+ }
86
92
  }
87
93
  }
88
94
  })));
@@ -90,4 +96,4 @@ var adjust_default = handler;
90
96
  export {
91
97
  adjust_default as default
92
98
  };
93
- //# sourceMappingURL=adjust-HNU5CCRO.mjs.map
99
+ //# sourceMappingURL=adjust-MJB6UMRQ.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/operations/adjust.ts"],
4
+ "sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport * as Function from 'effect/Function';\nimport * as Option from 'effect/Option';\n\nimport { Capabilities, Capability } from '@dxos/app-framework';\nimport { AppCapabilities, LayoutOperation } from '@dxos/app-toolkit';\nimport { Operation } from '@dxos/compute';\nimport { AttentionCapabilities } from '@dxos/plugin-attention';\nimport { Graph } from '@dxos/plugin-graph';\n\nimport { incrementPlank } from '../layout';\nimport { DeckCapabilities, DeckOperation, PLANK_COMPANION_TYPE } from '../types';\nimport { computeActiveUpdates } from '../util';\nimport { updateActiveDeck } from './helpers';\n\nconst handler: Operation.WithHandler<typeof DeckOperation.Adjust> = DeckOperation.Adjust.pipe(\n Operation.withHandler(\n Effect.fnUntraced(function* (input) {\n const _state = yield* Capabilities.getAtomValue(DeckCapabilities.State);\n const deck = yield* DeckCapabilities.getDeck();\n const attention = yield* Capability.get(AttentionCapabilities.Attention);\n const { graph } = yield* Capability.get(AppCapabilities.AppGraph);\n\n let soloOperation:\n | { type: 'solo'; entryId: string; mode: string }\n | { type: 'unsolo'; entryId: string }\n | undefined;\n\n if (input.type === 'increment-end' || input.type === 'increment-start') {\n const next = incrementPlank(deck.active, input);\n const { deckUpdates } = computeActiveUpdates({ next, deck, attention });\n yield* Capabilities.updateAtomValue(DeckCapabilities.State, (state) => updateActiveDeck(state, deckUpdates));\n }\n\n if (input.type.startsWith('solo')) {\n const entryId = input.id;\n if (!deck.solo) {\n soloOperation = { type: 'solo', entryId, mode: input.type };\n } else {\n if (input.type === 'solo--fullscreen') {\n soloOperation = { type: 'solo', entryId, mode: 'solo--fullscreen' };\n } else if (input.type === 'solo') {\n soloOperation = { type: 'unsolo', entryId };\n }\n }\n }\n\n if (soloOperation?.type === 'solo') {\n yield* Operation.invoke(LayoutOperation.SetLayoutMode, {\n subject: soloOperation.entryId,\n mode: soloOperation.mode,\n });\n } else if (soloOperation?.type === 'unsolo') {\n yield* Operation.invoke(LayoutOperation.SetLayoutMode, { mode: 'multi' });\n yield* Operation.invoke(LayoutOperation.Open, { subject: [soloOperation.entryId] });\n }\n\n if (input.type === 'companion' || input.type === 'companion-vertical') {\n const orientation = input.type === 'companion-vertical' ? 'vertical' : 'horizontal';\n\n // Set the orientation first so an already-open companion re-orients in place (the Splitter stays\n // mounted and only its `orientation` prop changes — panels are never unmounted).\n yield* Capabilities.updateAtomValue(DeckCapabilities.State, (state) =>\n updateActiveDeck(state, { companionOrientation: orientation }),\n );\n\n // Open the companion when one is available; the selected variant lives in global view state and is\n // resolved at render time, so opening restores the last-selected tab rather than forcing the first.\n if (!deck.companionOpen) {\n const hasCompanion = Function.pipe(\n Graph.getNode(graph, input.id),\n Option.map((node) =>\n Graph.getConnections(graph, node.id, 'child').some((n) => n.type === PLANK_COMPANION_TYPE),\n ),\n Option.getOrElse(() => false),\n );\n\n if (hasCompanion) {\n yield* Capabilities.updateAtomValue(DeckCapabilities.State, (state) =>\n updateActiveDeck(state, { companionOpen: true }),\n );\n }\n }\n }\n }),\n ),\n);\n\nexport default handler;\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAIA,YAAYA,YAAY;AACxB,YAAYC,cAAc;AAC1B,YAAYC,YAAY;AAExB,SAASC,cAAcC,kBAAkB;AACzC,SAASC,iBAAiBC,uBAAuB;AACjD,SAASC,iBAAiB;AAC1B,SAASC,6BAA6B;AACtC,SAASC,aAAa;AAOtB,IAAMC,UAA8DC,sBAAcC,OAAOC,KACvFC,UAAUC,YACDC,kBAAW,WAAWC,OAAK;AAChC,QAAMC,SAAS,OAAOC,aAAaC,aAAaC,yBAAiBC,KAAK;AACtE,QAAMC,OAAO,OAAOF,yBAAiBG,QAAO;AAC5C,QAAMC,YAAY,OAAOC,WAAWC,IAAIC,sBAAsBC,SAAS;AACvE,QAAM,EAAEC,MAAK,IAAK,OAAOJ,WAAWC,IAAII,gBAAgBC,QAAQ;AAEhE,MAAIC;AAKJ,MAAIhB,MAAMiB,SAAS,mBAAmBjB,MAAMiB,SAAS,mBAAmB;AACtE,UAAMC,OAAOC,eAAeb,KAAKc,QAAQpB,KAAAA;AACzC,UAAM,EAAEqB,YAAW,IAAKC,qBAAqB;MAAEJ;MAAMZ;MAAME;IAAU,CAAA;AACrE,WAAON,aAAaqB,gBAAgBnB,yBAAiBC,OAAO,CAACmB,UAAUC,iBAAiBD,OAAOH,WAAAA,CAAAA;EACjG;AAEA,MAAIrB,MAAMiB,KAAKS,WAAW,MAAA,GAAS;AACjC,UAAMC,UAAU3B,MAAM4B;AACtB,QAAI,CAACtB,KAAKuB,MAAM;AACdb,sBAAgB;QAAEC,MAAM;QAAQU;QAASG,MAAM9B,MAAMiB;MAAK;IAC5D,OAAO;AACL,UAAIjB,MAAMiB,SAAS,oBAAoB;AACrCD,wBAAgB;UAAEC,MAAM;UAAQU;UAASG,MAAM;QAAmB;MACpE,WAAW9B,MAAMiB,SAAS,QAAQ;AAChCD,wBAAgB;UAAEC,MAAM;UAAUU;QAAQ;MAC5C;IACF;EACF;AAEA,MAAIX,eAAeC,SAAS,QAAQ;AAClC,WAAOpB,UAAUkC,OAAOC,gBAAgBC,eAAe;MACrDC,SAASlB,cAAcW;MACvBG,MAAMd,cAAcc;IACtB,CAAA;EACF,WAAWd,eAAeC,SAAS,UAAU;AAC3C,WAAOpB,UAAUkC,OAAOC,gBAAgBC,eAAe;MAAEH,MAAM;IAAQ,CAAA;AACvE,WAAOjC,UAAUkC,OAAOC,gBAAgBG,MAAM;MAAED,SAAS;QAAClB,cAAcW;;IAAS,CAAA;EACnF;AAEA,MAAI3B,MAAMiB,SAAS,eAAejB,MAAMiB,SAAS,sBAAsB;AACrE,UAAMmB,cAAcpC,MAAMiB,SAAS,uBAAuB,aAAa;AAIvE,WAAOf,aAAaqB,gBAAgBnB,yBAAiBC,OAAO,CAACmB,UAC3DC,iBAAiBD,OAAO;MAAEa,sBAAsBD;IAAY,CAAA,CAAA;AAK9D,QAAI,CAAC9B,KAAKgC,eAAe;AACvB,YAAMC,eAAwB3C,cAC5B4C,MAAMC,QAAQ5B,OAAOb,MAAM4B,EAAE,GACtBc,WAAI,CAACC,SACVH,MAAMI,eAAe/B,OAAO8B,KAAKf,IAAI,OAAA,EAASiB,KAAK,CAACC,MAAMA,EAAE7B,SAAS8B,oBAAAA,CAAAA,GAEhEC,iBAAU,MAAM,KAAA,CAAA;AAGzB,UAAIT,cAAc;AAChB,eAAOrC,aAAaqB,gBAAgBnB,yBAAiBC,OAAO,CAACmB,UAC3DC,iBAAiBD,OAAO;UAAEc,eAAe;QAAK,CAAA,CAAA;MAElD;IACF;EACF;AACF,CAAA,CAAA,CAAA;AAIJ,IAAA,iBAAe7C;",
6
+ "names": ["Effect", "Function", "Option", "Capabilities", "Capability", "AppCapabilities", "LayoutOperation", "Operation", "AttentionCapabilities", "Graph", "handler", "DeckOperation", "Adjust", "pipe", "Operation", "withHandler", "fnUntraced", "input", "_state", "Capabilities", "getAtomValue", "DeckCapabilities", "State", "deck", "getDeck", "attention", "Capability", "get", "AttentionCapabilities", "Attention", "graph", "AppCapabilities", "AppGraph", "soloOperation", "type", "next", "incrementPlank", "active", "deckUpdates", "computeActiveUpdates", "updateAtomValue", "state", "updateActiveDeck", "startsWith", "entryId", "id", "solo", "mode", "invoke", "LayoutOperation", "SetLayoutMode", "subject", "Open", "orientation", "companionOrientation", "companionOpen", "hasCompanion", "Graph", "getNode", "map", "node", "getConnections", "some", "n", "PLANK_COMPANION_TYPE", "getOrElse"]
7
+ }