@dxos/plugin-deck 0.8.4-main.fd6878d → 0.8.4-staging.60fe92afc8

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 (500) hide show
  1. package/LICENSE +102 -5
  2. package/PLUGIN.mdl +541 -0
  3. package/README.md +1 -1
  4. package/dist/lib/neutral/DeckLayout-DEURA3KR.mjs +291 -0
  5. package/dist/lib/neutral/DeckLayout-DEURA3KR.mjs.map +7 -0
  6. package/dist/lib/neutral/DeckPlugin.mjs +87 -0
  7. package/dist/lib/neutral/DeckPlugin.mjs.map +7 -0
  8. package/dist/lib/neutral/DeckPlugin.node.mjs +18 -0
  9. package/dist/lib/neutral/DeckPlugin.node.mjs.map +7 -0
  10. package/dist/lib/neutral/DeckPlugin.workerd.mjs +16 -0
  11. package/dist/lib/neutral/DeckPlugin.workerd.mjs.map +7 -0
  12. package/dist/lib/neutral/DeckSettings-W5I57OXM.mjs +27 -0
  13. package/dist/lib/neutral/DeckSettings-W5I57OXM.mjs.map +7 -0
  14. package/dist/lib/neutral/add-toast-TNB6DXWU.mjs +24 -0
  15. package/dist/lib/neutral/add-toast-TNB6DXWU.mjs.map +7 -0
  16. package/dist/lib/neutral/adjust-HNU5CCRO.mjs +93 -0
  17. package/dist/lib/neutral/adjust-HNU5CCRO.mjs.map +7 -0
  18. package/dist/lib/neutral/app-graph-builder-HMLT627T.mjs +129 -0
  19. package/dist/lib/neutral/app-graph-builder-HMLT627T.mjs.map +7 -0
  20. package/dist/lib/neutral/capabilities/index.mjs +25 -0
  21. package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
  22. package/dist/lib/neutral/check-app-scheme-INSOF72J.mjs +10 -0
  23. package/dist/lib/neutral/chunk-324PPIZB.mjs +101 -0
  24. package/dist/lib/neutral/chunk-324PPIZB.mjs.map +7 -0
  25. package/dist/lib/neutral/chunk-BS4EOYMK.mjs +282 -0
  26. package/dist/lib/neutral/chunk-BS4EOYMK.mjs.map +7 -0
  27. package/dist/lib/neutral/chunk-GBIGQKYW.mjs +112 -0
  28. package/dist/lib/neutral/chunk-GBIGQKYW.mjs.map +7 -0
  29. package/dist/lib/neutral/chunk-GLB73Q6U.mjs +22 -0
  30. package/dist/lib/neutral/chunk-GLB73Q6U.mjs.map +7 -0
  31. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
  32. package/dist/lib/neutral/chunk-PYEY5SEC.mjs +37 -0
  33. package/dist/lib/neutral/chunk-PYEY5SEC.mjs.map +7 -0
  34. package/dist/lib/neutral/chunk-Q4W6B4IB.mjs +8 -0
  35. package/dist/lib/neutral/chunk-Q4W6B4IB.mjs.map +7 -0
  36. package/dist/lib/neutral/chunk-WAXJPQJI.mjs +1306 -0
  37. package/dist/lib/neutral/chunk-WAXJPQJI.mjs.map +7 -0
  38. package/dist/lib/neutral/chunk-ZYYOSX5I.mjs +69 -0
  39. package/dist/lib/neutral/chunk-ZYYOSX5I.mjs.map +7 -0
  40. package/dist/lib/neutral/close-ASKR22A6.mjs +44 -0
  41. package/dist/lib/neutral/close-ASKR22A6.mjs.map +7 -0
  42. package/dist/lib/neutral/components/index.mjs +126 -0
  43. package/dist/lib/neutral/components/index.mjs.map +7 -0
  44. package/dist/lib/neutral/containers/index.mjs +32 -0
  45. package/dist/lib/neutral/containers/index.mjs.map +7 -0
  46. package/dist/lib/neutral/hooks/index.mjs +159 -0
  47. package/dist/lib/neutral/hooks/index.mjs.map +7 -0
  48. package/dist/lib/neutral/index.mjs +38 -0
  49. package/dist/lib/neutral/index.mjs.map +7 -0
  50. package/dist/lib/neutral/meta.json +1 -0
  51. package/dist/lib/neutral/meta.mjs +8 -0
  52. package/dist/lib/neutral/meta.mjs.map +7 -0
  53. package/dist/lib/neutral/notification-tracker-P36322BH.mjs +182 -0
  54. package/dist/lib/neutral/notification-tracker-P36322BH.mjs.map +7 -0
  55. package/dist/lib/neutral/open-5OYNO3RT.mjs +159 -0
  56. package/dist/lib/neutral/open-5OYNO3RT.mjs.map +7 -0
  57. package/dist/lib/neutral/operation-handler-266CVMTW.mjs +13 -0
  58. package/dist/lib/neutral/operation-handler-266CVMTW.mjs.map +7 -0
  59. package/dist/lib/neutral/operations/index.mjs +8 -0
  60. package/dist/lib/neutral/operations/index.mjs.map +7 -0
  61. package/dist/lib/neutral/plugin.mjs +16 -0
  62. package/dist/lib/neutral/plugin.mjs.map +7 -0
  63. package/dist/lib/neutral/react-root-HH5DEUOG.mjs +44 -0
  64. package/dist/lib/neutral/react-root-HH5DEUOG.mjs.map +7 -0
  65. package/dist/lib/neutral/react-surface-3UVVCK3O.mjs +34 -0
  66. package/dist/lib/neutral/react-surface-3UVVCK3O.mjs.map +7 -0
  67. package/dist/lib/neutral/revert-workspace-B2QLT2C4.mjs +21 -0
  68. package/dist/lib/neutral/revert-workspace-B2QLT2C4.mjs.map +7 -0
  69. package/dist/lib/neutral/scroll-into-view-B52C3PJO.mjs +21 -0
  70. package/dist/lib/neutral/scroll-into-view-B52C3PJO.mjs.map +7 -0
  71. package/dist/lib/neutral/set-PA35ONXO.mjs +37 -0
  72. package/dist/lib/neutral/set-PA35ONXO.mjs.map +7 -0
  73. package/dist/lib/neutral/set-layout-mode-RPCCPQRB.mjs +85 -0
  74. package/dist/lib/neutral/set-layout-mode-RPCCPQRB.mjs.map +7 -0
  75. package/dist/lib/neutral/settings-EGNYUM4T.mjs +33 -0
  76. package/dist/lib/neutral/settings-EGNYUM4T.mjs.map +7 -0
  77. package/dist/lib/neutral/state-IIDXMQUO.mjs +86 -0
  78. package/dist/lib/neutral/state-IIDXMQUO.mjs.map +7 -0
  79. package/dist/lib/neutral/switch-workspace-LZF4KZXH.mjs +60 -0
  80. package/dist/lib/neutral/switch-workspace-LZF4KZXH.mjs.map +7 -0
  81. package/dist/lib/neutral/translations.mjs +63 -0
  82. package/dist/lib/neutral/translations.mjs.map +7 -0
  83. package/dist/lib/neutral/types/index.mjs +34 -0
  84. package/dist/lib/neutral/types/index.mjs.map +7 -0
  85. package/dist/lib/neutral/update-companion-YUCZZVGY.mjs +32 -0
  86. package/dist/lib/neutral/update-companion-YUCZZVGY.mjs.map +7 -0
  87. package/dist/lib/neutral/update-complementary-7FZNB55J.mjs +28 -0
  88. package/dist/lib/neutral/update-complementary-7FZNB55J.mjs.map +7 -0
  89. package/dist/lib/neutral/update-dialog-FNQTSSAP.mjs +29 -0
  90. package/dist/lib/neutral/update-dialog-FNQTSSAP.mjs.map +7 -0
  91. package/dist/lib/neutral/update-plank-size-3YW4NXEY.mjs +26 -0
  92. package/dist/lib/neutral/update-plank-size-3YW4NXEY.mjs.map +7 -0
  93. package/dist/lib/neutral/update-popover-G2VUD7E6.mjs +33 -0
  94. package/dist/lib/neutral/update-popover-G2VUD7E6.mjs.map +7 -0
  95. package/dist/lib/neutral/update-sidebar-KRHPUHUB.mjs +25 -0
  96. package/dist/lib/neutral/update-sidebar-KRHPUHUB.mjs.map +7 -0
  97. package/dist/lib/neutral/url-handler-A6HLW4RB.mjs +229 -0
  98. package/dist/lib/neutral/url-handler-A6HLW4RB.mjs.map +7 -0
  99. package/dist/types/src/DeckPlugin.d.ts +3 -1
  100. package/dist/types/src/DeckPlugin.d.ts.map +1 -1
  101. package/dist/types/src/DeckPlugin.node.d.ts +4 -0
  102. package/dist/types/src/DeckPlugin.node.d.ts.map +1 -0
  103. package/dist/types/src/DeckPlugin.test.d.ts +2 -0
  104. package/dist/types/src/DeckPlugin.test.d.ts.map +1 -0
  105. package/dist/types/src/DeckPlugin.workerd.d.ts +4 -0
  106. package/dist/types/src/DeckPlugin.workerd.d.ts.map +1 -0
  107. package/dist/types/src/capabilities/app-graph-builder.d.ts +4 -2
  108. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  109. package/dist/types/src/capabilities/check-app-scheme.d.ts +17 -2
  110. package/dist/types/src/capabilities/check-app-scheme.d.ts.map +1 -1
  111. package/dist/types/src/capabilities/index.d.ts +197 -12
  112. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  113. package/dist/types/src/capabilities/notification-tracker.d.ts +13 -0
  114. package/dist/types/src/capabilities/notification-tracker.d.ts.map +1 -0
  115. package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
  116. package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
  117. package/dist/types/src/capabilities/react-root.d.ts +4 -2
  118. package/dist/types/src/capabilities/react-root.d.ts.map +1 -1
  119. package/dist/types/src/capabilities/react-surface.d.ts +3 -2
  120. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  121. package/dist/types/src/capabilities/settings.d.ts +5 -2
  122. package/dist/types/src/capabilities/settings.d.ts.map +1 -1
  123. package/dist/types/src/capabilities/state.d.ts +141 -51
  124. package/dist/types/src/capabilities/state.d.ts.map +1 -1
  125. package/dist/types/src/capabilities/tools.d.ts +4 -3
  126. package/dist/types/src/capabilities/tools.d.ts.map +1 -1
  127. package/dist/types/src/capabilities/url-handler.d.ts +3 -2
  128. package/dist/types/src/capabilities/url-handler.d.ts.map +1 -1
  129. package/dist/types/src/components/DeckSettings/DeckSettings.d.ts +4 -4
  130. package/dist/types/src/components/DeckSettings/DeckSettings.d.ts.map +1 -1
  131. package/dist/types/src/components/DeckSettings/DeckSettings.stories.d.ts +67 -0
  132. package/dist/types/src/components/DeckSettings/DeckSettings.stories.d.ts.map +1 -0
  133. package/dist/types/src/components/DeckSettings/index.d.ts +1 -1
  134. package/dist/types/src/components/DeckSettings/index.d.ts.map +1 -1
  135. package/dist/types/src/components/Matrix/Matrix.d.ts +41 -0
  136. package/dist/types/src/components/Matrix/Matrix.d.ts.map +1 -0
  137. package/dist/types/src/components/Matrix/Matrix.stories.d.ts +17 -0
  138. package/dist/types/src/components/Matrix/Matrix.stories.d.ts.map +1 -0
  139. package/dist/types/src/components/Matrix/index.d.ts +3 -0
  140. package/dist/types/src/components/Matrix/index.d.ts.map +1 -0
  141. package/dist/types/src/components/index.d.ts +3 -2
  142. package/dist/types/src/components/index.d.ts.map +1 -1
  143. package/dist/types/src/{components/DeckLayout → containers/Deck}/Banner.d.ts +1 -1
  144. package/dist/types/src/containers/Deck/Banner.d.ts.map +1 -0
  145. package/dist/types/src/containers/Deck/Deck.d.ts +25 -0
  146. package/dist/types/src/containers/Deck/Deck.d.ts.map +1 -0
  147. package/dist/types/src/containers/Deck/Deck.stories.d.ts +67 -0
  148. package/dist/types/src/containers/Deck/Deck.stories.d.ts.map +1 -0
  149. package/dist/types/src/containers/Deck/DeckContent.d.ts +7 -0
  150. package/dist/types/src/containers/Deck/DeckContent.d.ts.map +1 -0
  151. package/dist/types/src/containers/Deck/DeckRoot.d.ts +39 -0
  152. package/dist/types/src/containers/Deck/DeckRoot.d.ts.map +1 -0
  153. package/dist/types/src/containers/Deck/DeckViewport.d.ts +16 -0
  154. package/dist/types/src/containers/Deck/DeckViewport.d.ts.map +1 -0
  155. package/dist/types/src/containers/Deck/StatusBar.d.ts.map +1 -0
  156. package/dist/types/src/containers/Deck/index.d.ts +2 -0
  157. package/dist/types/src/containers/Deck/index.d.ts.map +1 -0
  158. package/dist/types/src/containers/DeckLayout/ActiveNode.d.ts.map +1 -0
  159. package/dist/types/src/containers/DeckLayout/DeckLayout.d.ts +5 -0
  160. package/dist/types/src/containers/DeckLayout/DeckLayout.d.ts.map +1 -0
  161. package/dist/types/src/containers/DeckLayout/DeckLayout.stories.d.ts +69 -0
  162. package/dist/types/src/containers/DeckLayout/DeckLayout.stories.d.ts.map +1 -0
  163. package/dist/types/src/containers/DeckLayout/Dialog.d.ts.map +1 -0
  164. package/dist/types/src/containers/DeckLayout/Fallback.d.ts +2 -0
  165. package/dist/types/src/containers/DeckLayout/Fallback.d.ts.map +1 -0
  166. package/dist/types/src/containers/DeckLayout/Popover.d.ts +5 -0
  167. package/dist/types/src/containers/DeckLayout/Popover.d.ts.map +1 -0
  168. package/dist/types/src/containers/DeckLayout/Toast.d.ts +10 -0
  169. package/dist/types/src/containers/DeckLayout/Toast.d.ts.map +1 -0
  170. package/dist/types/src/containers/DeckLayout/constants.d.ts.map +1 -0
  171. package/dist/types/src/containers/DeckLayout/index.d.ts +4 -0
  172. package/dist/types/src/containers/DeckLayout/index.d.ts.map +1 -0
  173. package/dist/types/src/containers/Plank/Plank.d.ts +17 -0
  174. package/dist/types/src/containers/Plank/Plank.d.ts.map +1 -0
  175. package/dist/types/src/containers/Plank/Plank.stories.d.ts +67 -0
  176. package/dist/types/src/containers/Plank/Plank.stories.d.ts.map +1 -0
  177. package/dist/types/src/containers/Plank/PlankComponent.d.ts +17 -0
  178. package/dist/types/src/containers/Plank/PlankComponent.d.ts.map +1 -0
  179. package/dist/types/src/containers/Plank/PlankContent.d.ts +10 -0
  180. package/dist/types/src/containers/Plank/PlankContent.d.ts.map +1 -0
  181. package/dist/types/src/{components → containers}/Plank/PlankControls.d.ts +5 -6
  182. package/dist/types/src/containers/Plank/PlankControls.d.ts.map +1 -0
  183. package/dist/types/src/containers/Plank/PlankError.d.ts +15 -0
  184. package/dist/types/src/containers/Plank/PlankError.d.ts.map +1 -0
  185. package/dist/types/src/{components → containers}/Plank/PlankHeading.d.ts +5 -4
  186. package/dist/types/src/containers/Plank/PlankHeading.d.ts.map +1 -0
  187. package/dist/types/src/{components → containers}/Plank/PlankLoading.d.ts.map +1 -1
  188. package/dist/types/src/containers/Plank/PlankRoot.d.ts +37 -0
  189. package/dist/types/src/containers/Plank/PlankRoot.d.ts.map +1 -0
  190. package/dist/types/src/containers/Plank/index.d.ts +4 -0
  191. package/dist/types/src/containers/Plank/index.d.ts.map +1 -0
  192. package/dist/types/src/containers/Sidebar/ComplementarySidebar.d.ts.map +1 -0
  193. package/dist/types/src/containers/Sidebar/Sidebar.d.ts.map +1 -0
  194. package/dist/types/src/{components → containers}/Sidebar/SidebarButton.d.ts +1 -1
  195. package/dist/types/src/containers/Sidebar/SidebarButton.d.ts.map +1 -0
  196. package/dist/types/src/{components → containers}/Sidebar/index.d.ts.map +1 -1
  197. package/dist/types/src/containers/index.d.ts +6 -0
  198. package/dist/types/src/containers/index.d.ts.map +1 -0
  199. package/dist/types/src/hooks/index.d.ts +2 -1
  200. package/dist/types/src/hooks/index.d.ts.map +1 -1
  201. package/dist/types/src/hooks/useBreakpoints.d.ts +1 -1
  202. package/dist/types/src/hooks/useCompanions.d.ts.map +1 -1
  203. package/dist/types/src/hooks/useDeckCompanions.d.ts +4 -4
  204. package/dist/types/src/hooks/useDeckCompanions.d.ts.map +1 -1
  205. package/dist/types/src/hooks/useDeckState.d.ts +17 -0
  206. package/dist/types/src/hooks/useDeckState.d.ts.map +1 -0
  207. package/dist/types/src/hooks/useMainSize.d.ts +2 -2
  208. package/dist/types/src/hooks/useMainSize.d.ts.map +1 -1
  209. package/dist/types/src/hooks/useNodeActionExpander.d.ts +1 -1
  210. package/dist/types/src/hooks/useNodeActionExpander.d.ts.map +1 -1
  211. package/dist/types/src/hooks/useSelectedCompanion.d.ts +13 -0
  212. package/dist/types/src/hooks/useSelectedCompanion.d.ts.map +1 -0
  213. package/dist/types/src/index.d.ts +1 -4
  214. package/dist/types/src/index.d.ts.map +1 -1
  215. package/dist/types/src/layout.d.ts +20 -9
  216. package/dist/types/src/layout.d.ts.map +1 -1
  217. package/dist/types/src/layout.test.d.ts +2 -0
  218. package/dist/types/src/layout.test.d.ts.map +1 -0
  219. package/dist/types/src/meta.d.ts +2 -3
  220. package/dist/types/src/meta.d.ts.map +1 -1
  221. package/dist/types/src/operations/add-toast.d.ts +5 -0
  222. package/dist/types/src/operations/add-toast.d.ts.map +1 -0
  223. package/dist/types/src/operations/adjust.d.ts +5 -0
  224. package/dist/types/src/operations/adjust.d.ts.map +1 -0
  225. package/dist/types/src/operations/close.d.ts +5 -0
  226. package/dist/types/src/operations/close.d.ts.map +1 -0
  227. package/dist/types/src/operations/helpers.d.ts +3 -0
  228. package/dist/types/src/operations/helpers.d.ts.map +1 -0
  229. package/dist/types/src/operations/index.d.ts +3 -0
  230. package/dist/types/src/operations/index.d.ts.map +1 -0
  231. package/dist/types/src/operations/open.d.ts +5 -0
  232. package/dist/types/src/operations/open.d.ts.map +1 -0
  233. package/dist/types/src/operations/revert-workspace.d.ts +5 -0
  234. package/dist/types/src/operations/revert-workspace.d.ts.map +1 -0
  235. package/dist/types/src/operations/scroll-into-view.d.ts +5 -0
  236. package/dist/types/src/operations/scroll-into-view.d.ts.map +1 -0
  237. package/dist/types/src/operations/set-layout-mode.d.ts +9 -0
  238. package/dist/types/src/operations/set-layout-mode.d.ts.map +1 -0
  239. package/dist/types/src/operations/set.d.ts +5 -0
  240. package/dist/types/src/operations/set.d.ts.map +1 -0
  241. package/dist/types/src/operations/switch-workspace.d.ts +5 -0
  242. package/dist/types/src/operations/switch-workspace.d.ts.map +1 -0
  243. package/dist/types/src/operations/update-companion.d.ts +5 -0
  244. package/dist/types/src/operations/update-companion.d.ts.map +1 -0
  245. package/dist/types/src/operations/update-complementary.d.ts +5 -0
  246. package/dist/types/src/operations/update-complementary.d.ts.map +1 -0
  247. package/dist/types/src/operations/update-dialog.d.ts +5 -0
  248. package/dist/types/src/operations/update-dialog.d.ts.map +1 -0
  249. package/dist/types/src/operations/update-plank-size.d.ts +5 -0
  250. package/dist/types/src/operations/update-plank-size.d.ts.map +1 -0
  251. package/dist/types/src/operations/update-popover.d.ts +5 -0
  252. package/dist/types/src/operations/update-popover.d.ts.map +1 -0
  253. package/dist/types/src/operations/update-sidebar.d.ts +5 -0
  254. package/dist/types/src/operations/update-sidebar.d.ts.map +1 -0
  255. package/dist/types/src/plugin.d.ts +4 -0
  256. package/dist/types/src/plugin.d.ts.map +1 -0
  257. package/dist/types/src/translations.d.ts +48 -53
  258. package/dist/types/src/translations.d.ts.map +1 -1
  259. package/dist/types/src/types/DeckCapabilities.d.ts +188 -0
  260. package/dist/types/src/types/DeckCapabilities.d.ts.map +1 -0
  261. package/dist/types/src/types/DeckEvents.d.ts +5 -0
  262. package/dist/types/src/types/DeckEvents.d.ts.map +1 -0
  263. package/dist/types/src/types/DeckOperation.d.ts +15 -0
  264. package/dist/types/src/types/DeckOperation.d.ts.map +1 -0
  265. package/dist/types/src/types/Settings.d.ts +10 -0
  266. package/dist/types/src/types/Settings.d.ts.map +1 -0
  267. package/dist/types/src/types/index.d.ts +4 -0
  268. package/dist/types/src/types/index.d.ts.map +1 -1
  269. package/dist/types/src/types/schema.d.ts +70 -69
  270. package/dist/types/src/types/schema.d.ts.map +1 -1
  271. package/dist/types/src/util/index.d.ts +2 -1
  272. package/dist/types/src/util/index.d.ts.map +1 -1
  273. package/dist/types/src/util/layoutAppliesTopbar.d.ts +1 -1
  274. package/dist/types/src/util/layoutAppliesTopbar.d.ts.map +1 -1
  275. package/dist/types/src/util/plank-url-params.d.ts +14 -0
  276. package/dist/types/src/util/plank-url-params.d.ts.map +1 -0
  277. package/dist/types/src/util/plank-url-params.test.d.ts +2 -0
  278. package/dist/types/src/util/plank-url-params.test.d.ts.map +1 -0
  279. package/dist/types/src/util/sanitize-persisted-state.d.ts +19 -0
  280. package/dist/types/src/util/sanitize-persisted-state.d.ts.map +1 -0
  281. package/dist/types/src/util/sanitize-persisted-state.test.d.ts +2 -0
  282. package/dist/types/src/util/sanitize-persisted-state.test.d.ts.map +1 -0
  283. package/dist/types/src/util/set-active.d.ts +19 -4
  284. package/dist/types/src/util/set-active.d.ts.map +1 -1
  285. package/dist/types/src/util/set-active.test.d.ts +2 -0
  286. package/dist/types/src/util/set-active.test.d.ts.map +1 -0
  287. package/dist/types/tsconfig.tsbuildinfo +1 -1
  288. package/package.json +126 -62
  289. package/src/DeckPlugin.node.ts +17 -0
  290. package/src/DeckPlugin.test.ts +27 -0
  291. package/src/DeckPlugin.ts +57 -71
  292. package/src/DeckPlugin.workerd.ts +16 -0
  293. package/src/capabilities/app-graph-builder.ts +111 -126
  294. package/src/capabilities/check-app-scheme.ts +123 -24
  295. package/src/capabilities/index.ts +14 -14
  296. package/src/capabilities/notification-tracker.ts +170 -0
  297. package/src/capabilities/operation-handler.ts +16 -0
  298. package/src/capabilities/react-root.tsx +40 -27
  299. package/src/capabilities/react-surface.tsx +28 -22
  300. package/src/capabilities/settings.ts +29 -19
  301. package/src/capabilities/state.ts +74 -88
  302. package/src/capabilities/tools.ts +58 -51
  303. package/src/capabilities/url-handler.ts +290 -48
  304. package/src/components/DeckSettings/DeckSettings.stories.tsx +37 -0
  305. package/src/components/DeckSettings/DeckSettings.tsx +19 -73
  306. package/src/components/DeckSettings/index.ts +2 -2
  307. package/src/components/Matrix/Matrix.stories.tsx +220 -0
  308. package/src/components/Matrix/Matrix.tsx +205 -0
  309. package/src/components/Matrix/SPEC.md +219 -0
  310. package/src/components/Matrix/index.ts +6 -0
  311. package/src/components/index.ts +6 -3
  312. package/src/containers/Deck/Banner.tsx +41 -0
  313. package/src/containers/Deck/Deck.stories.tsx +81 -0
  314. package/src/containers/Deck/Deck.tsx +21 -0
  315. package/src/containers/Deck/DeckContent.tsx +102 -0
  316. package/src/containers/Deck/DeckRoot.tsx +50 -0
  317. package/src/containers/Deck/DeckViewport.tsx +450 -0
  318. package/src/{components/DeckLayout → containers/Deck}/StatusBar.tsx +4 -4
  319. package/src/containers/Deck/index.ts +5 -0
  320. package/src/{components → containers}/DeckLayout/ActiveNode.tsx +9 -4
  321. package/src/containers/DeckLayout/DeckLayout.stories.tsx +395 -0
  322. package/src/containers/DeckLayout/DeckLayout.tsx +62 -0
  323. package/src/containers/DeckLayout/Dialog.tsx +52 -0
  324. package/src/containers/DeckLayout/Fallback.tsx +24 -0
  325. package/src/containers/DeckLayout/Popover.tsx +166 -0
  326. package/src/containers/DeckLayout/Toast.tsx +76 -0
  327. package/src/{components → containers}/DeckLayout/constants.ts +1 -0
  328. package/src/{components → containers}/DeckLayout/index.ts +3 -2
  329. package/src/containers/Plank/Plank.stories.tsx +108 -0
  330. package/src/containers/Plank/Plank.tsx +22 -0
  331. package/src/containers/Plank/PlankComponent.tsx +223 -0
  332. package/src/containers/Plank/PlankContent.tsx +45 -0
  333. package/src/{components → containers}/Plank/PlankControls.tsx +45 -38
  334. package/src/containers/Plank/PlankError.tsx +82 -0
  335. package/src/{components → containers}/Plank/PlankHeading.tsx +69 -65
  336. package/src/{components → containers}/Plank/PlankLoading.tsx +1 -1
  337. package/src/containers/Plank/PlankRoot.tsx +49 -0
  338. package/src/{components → containers}/Plank/index.ts +0 -2
  339. package/src/containers/Sidebar/ComplementarySidebar.tsx +189 -0
  340. package/src/containers/Sidebar/Sidebar.tsx +39 -0
  341. package/src/containers/Sidebar/SidebarButton.tsx +100 -0
  342. package/src/containers/index.ts +11 -0
  343. package/src/hooks/index.ts +2 -1
  344. package/src/hooks/useCompanions.ts +3 -3
  345. package/src/hooks/useDeckCompanions.ts +8 -10
  346. package/src/hooks/useDeckState.ts +73 -0
  347. package/src/hooks/useMainSize.ts +2 -2
  348. package/src/hooks/useNodeActionExpander.ts +4 -4
  349. package/src/hooks/useSelectedCompanion.ts +32 -0
  350. package/src/index.ts +1 -4
  351. package/src/layout.test.ts +59 -0
  352. package/src/layout.ts +38 -40
  353. package/src/meta.ts +27 -6
  354. package/src/operations/add-toast.ts +24 -0
  355. package/src/operations/adjust.ts +82 -0
  356. package/src/operations/close.ts +35 -0
  357. package/src/operations/helpers.ts +22 -0
  358. package/src/operations/index.ts +23 -0
  359. package/src/operations/open.ts +201 -0
  360. package/src/operations/revert-workspace.ts +22 -0
  361. package/src/operations/scroll-into-view.ts +24 -0
  362. package/src/operations/set-layout-mode.ts +84 -0
  363. package/src/operations/set.ts +36 -0
  364. package/src/operations/switch-workspace.ts +66 -0
  365. package/src/operations/update-companion.ts +35 -0
  366. package/src/operations/update-complementary.ts +33 -0
  367. package/src/operations/update-dialog.ts +34 -0
  368. package/src/operations/update-plank-size.ts +28 -0
  369. package/src/operations/update-popover.ts +36 -0
  370. package/src/operations/update-sidebar.ts +28 -0
  371. package/src/plugin.ts +11 -0
  372. package/src/translations.ts +48 -52
  373. package/src/types/DeckCapabilities.ts +34 -0
  374. package/src/types/DeckEvents.ts +21 -0
  375. package/src/types/DeckOperation.ts +56 -0
  376. package/src/types/Settings.ts +36 -0
  377. package/src/types/index.ts +5 -0
  378. package/src/types/schema.ts +50 -57
  379. package/src/util/index.ts +2 -1
  380. package/src/util/layoutAppliesTopbar.ts +2 -2
  381. package/src/util/plank-url-params.test.ts +85 -0
  382. package/src/util/plank-url-params.ts +36 -0
  383. package/src/util/sanitize-persisted-state.test.ts +79 -0
  384. package/src/util/sanitize-persisted-state.ts +52 -0
  385. package/src/util/set-active.test.ts +106 -0
  386. package/src/util/set-active.ts +50 -30
  387. package/src/vite-env.d.ts +10 -0
  388. package/dist/lib/browser/app-graph-builder-DVEKLXB4.mjs +0 -152
  389. package/dist/lib/browser/app-graph-builder-DVEKLXB4.mjs.map +0 -7
  390. package/dist/lib/browser/check-app-scheme-3BQJXWEY.mjs +0 -32
  391. package/dist/lib/browser/check-app-scheme-3BQJXWEY.mjs.map +0 -7
  392. package/dist/lib/browser/chunk-6MQIYIPY.mjs +0 -1494
  393. package/dist/lib/browser/chunk-6MQIYIPY.mjs.map +0 -7
  394. package/dist/lib/browser/chunk-CNTGBCMK.mjs +0 -145
  395. package/dist/lib/browser/chunk-CNTGBCMK.mjs.map +0 -7
  396. package/dist/lib/browser/chunk-F5BQOOEG.mjs +0 -160
  397. package/dist/lib/browser/chunk-F5BQOOEG.mjs.map +0 -7
  398. package/dist/lib/browser/chunk-M57WD3V6.mjs +0 -16
  399. package/dist/lib/browser/chunk-M57WD3V6.mjs.map +0 -7
  400. package/dist/lib/browser/chunk-MX7WRVX3.mjs +0 -127
  401. package/dist/lib/browser/chunk-MX7WRVX3.mjs.map +0 -7
  402. package/dist/lib/browser/chunk-NRCPV6AV.mjs +0 -129
  403. package/dist/lib/browser/chunk-NRCPV6AV.mjs.map +0 -7
  404. package/dist/lib/browser/chunk-Z5KITAZW.mjs +0 -13
  405. package/dist/lib/browser/chunk-Z5KITAZW.mjs.map +0 -7
  406. package/dist/lib/browser/index.mjs +0 -174
  407. package/dist/lib/browser/index.mjs.map +0 -7
  408. package/dist/lib/browser/intent-resolver-2SUIIV6N.mjs +0 -521
  409. package/dist/lib/browser/intent-resolver-2SUIIV6N.mjs.map +0 -7
  410. package/dist/lib/browser/meta.json +0 -1
  411. package/dist/lib/browser/react-root-GVZANZX7.mjs +0 -43
  412. package/dist/lib/browser/react-root-GVZANZX7.mjs.map +0 -7
  413. package/dist/lib/browser/react-surface-NXSSD2GW.mjs +0 -40
  414. package/dist/lib/browser/react-surface-NXSSD2GW.mjs.map +0 -7
  415. package/dist/lib/browser/settings-LUPQPZ27.mjs +0 -29
  416. package/dist/lib/browser/settings-LUPQPZ27.mjs.map +0 -7
  417. package/dist/lib/browser/state-CRXR7X63.mjs +0 -12
  418. package/dist/lib/browser/toolkit-OBKFXX23.mjs +0 -47
  419. package/dist/lib/browser/toolkit-OBKFXX23.mjs.map +0 -7
  420. package/dist/lib/browser/types/index.mjs +0 -32
  421. package/dist/lib/browser/url-handler-LROZYQ26.mjs +0 -70
  422. package/dist/lib/browser/url-handler-LROZYQ26.mjs.map +0 -7
  423. package/dist/types/src/capabilities/capabilities.d.ts +0 -184
  424. package/dist/types/src/capabilities/capabilities.d.ts.map +0 -1
  425. package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
  426. package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
  427. package/dist/types/src/capabilities/toolkit.d.ts +0 -5
  428. package/dist/types/src/capabilities/toolkit.d.ts.map +0 -1
  429. package/dist/types/src/components/DeckLayout/ActiveNode.d.ts.map +0 -1
  430. package/dist/types/src/components/DeckLayout/Banner.d.ts.map +0 -1
  431. package/dist/types/src/components/DeckLayout/ContentEmpty.d.ts +0 -3
  432. package/dist/types/src/components/DeckLayout/ContentEmpty.d.ts.map +0 -1
  433. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts +0 -6
  434. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +0 -1
  435. package/dist/types/src/components/DeckLayout/Dialog.d.ts.map +0 -1
  436. package/dist/types/src/components/DeckLayout/Fallback.d.ts +0 -3
  437. package/dist/types/src/components/DeckLayout/Fallback.d.ts.map +0 -1
  438. package/dist/types/src/components/DeckLayout/Popover.d.ts +0 -5
  439. package/dist/types/src/components/DeckLayout/Popover.d.ts.map +0 -1
  440. package/dist/types/src/components/DeckLayout/StatusBar.d.ts.map +0 -1
  441. package/dist/types/src/components/DeckLayout/Toast.d.ts +0 -5
  442. package/dist/types/src/components/DeckLayout/Toast.d.ts.map +0 -1
  443. package/dist/types/src/components/DeckLayout/Topbar.d.ts +0 -3
  444. package/dist/types/src/components/DeckLayout/Topbar.d.ts.map +0 -1
  445. package/dist/types/src/components/DeckLayout/constants.d.ts.map +0 -1
  446. package/dist/types/src/components/DeckLayout/index.d.ts +0 -4
  447. package/dist/types/src/components/DeckLayout/index.d.ts.map +0 -1
  448. package/dist/types/src/components/Plank/Plank.d.ts +0 -27
  449. package/dist/types/src/components/Plank/Plank.d.ts.map +0 -1
  450. package/dist/types/src/components/Plank/Plank.stories.d.ts +0 -8
  451. package/dist/types/src/components/Plank/Plank.stories.d.ts.map +0 -1
  452. package/dist/types/src/components/Plank/PlankControls.d.ts.map +0 -1
  453. package/dist/types/src/components/Plank/PlankError.d.ts +0 -13
  454. package/dist/types/src/components/Plank/PlankError.d.ts.map +0 -1
  455. package/dist/types/src/components/Plank/PlankHeading.d.ts.map +0 -1
  456. package/dist/types/src/components/Plank/index.d.ts +0 -6
  457. package/dist/types/src/components/Plank/index.d.ts.map +0 -1
  458. package/dist/types/src/components/Sidebar/ComplementarySidebar.d.ts.map +0 -1
  459. package/dist/types/src/components/Sidebar/Sidebar.d.ts.map +0 -1
  460. package/dist/types/src/components/Sidebar/SidebarButton.d.ts.map +0 -1
  461. package/dist/types/src/components/fragments.d.ts +0 -4
  462. package/dist/types/src/components/fragments.d.ts.map +0 -1
  463. package/dist/types/src/events.d.ts +0 -4
  464. package/dist/types/src/events.d.ts.map +0 -1
  465. package/dist/types/src/hooks/useHoistStatusbar.d.ts +0 -3
  466. package/dist/types/src/hooks/useHoistStatusbar.d.ts.map +0 -1
  467. package/dist/types/src/util/overscroll.d.ts +0 -47
  468. package/dist/types/src/util/overscroll.d.ts.map +0 -1
  469. package/src/capabilities/capabilities.ts +0 -14
  470. package/src/capabilities/intent-resolver.ts +0 -469
  471. package/src/capabilities/toolkit.ts +0 -55
  472. package/src/components/DeckLayout/Banner.tsx +0 -39
  473. package/src/components/DeckLayout/ContentEmpty.tsx +0 -31
  474. package/src/components/DeckLayout/DeckLayout.tsx +0 -302
  475. package/src/components/DeckLayout/Dialog.tsx +0 -36
  476. package/src/components/DeckLayout/Fallback.tsx +0 -28
  477. package/src/components/DeckLayout/Popover.tsx +0 -104
  478. package/src/components/DeckLayout/Toast.tsx +0 -61
  479. package/src/components/DeckLayout/Topbar.tsx +0 -11
  480. package/src/components/Plank/Plank.stories.tsx +0 -56
  481. package/src/components/Plank/Plank.tsx +0 -267
  482. package/src/components/Plank/PlankError.tsx +0 -49
  483. package/src/components/Sidebar/ComplementarySidebar.tsx +0 -194
  484. package/src/components/Sidebar/Sidebar.tsx +0 -42
  485. package/src/components/Sidebar/SidebarButton.tsx +0 -87
  486. package/src/components/fragments.ts +0 -14
  487. package/src/events.ts +0 -11
  488. package/src/hooks/useHoistStatusbar.ts +0 -25
  489. package/src/util/overscroll.ts +0 -69
  490. /package/dist/lib/{browser/state-CRXR7X63.mjs.map → neutral/check-app-scheme-INSOF72J.mjs.map} +0 -0
  491. /package/dist/lib/{browser/types/index.mjs.map → neutral/chunk-J5LGTIGS.mjs.map} +0 -0
  492. /package/dist/types/src/{components/DeckLayout → containers/Deck}/StatusBar.d.ts +0 -0
  493. /package/dist/types/src/{components → containers}/DeckLayout/ActiveNode.d.ts +0 -0
  494. /package/dist/types/src/{components → containers}/DeckLayout/Dialog.d.ts +0 -0
  495. /package/dist/types/src/{components → containers}/DeckLayout/constants.d.ts +0 -0
  496. /package/dist/types/src/{components → containers}/Plank/PlankLoading.d.ts +0 -0
  497. /package/dist/types/src/{components → containers}/Sidebar/ComplementarySidebar.d.ts +0 -0
  498. /package/dist/types/src/{components → containers}/Sidebar/Sidebar.d.ts +0 -0
  499. /package/dist/types/src/{components → containers}/Sidebar/index.d.ts +0 -0
  500. /package/src/{components → containers}/Sidebar/index.ts +0 -0
@@ -2,59 +2,301 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Capabilities, LayoutAction, type PluginContext, contributes, createIntent } from '@dxos/app-framework';
6
- import { scheduledEffect } from '@dxos/echo-signals/core';
7
-
8
- import { defaultDeck } from '../types';
9
-
10
- import { DeckCapabilities } from './capabilities';
11
-
12
- // TODO(wittjosiah): Cleanup the url handling. May justify introducing routing capabilities.
13
- export default async (context: PluginContext) => {
14
- const { dispatchPromise: dispatch } = context.getCapability(Capabilities.IntentDispatcher);
15
- const state = context.getCapability(DeckCapabilities.MutableDeckState);
16
-
17
- const handleNavigation = async () => {
18
- const pathname = window.location.pathname;
19
- if (pathname === '/reset') {
20
- state.activeDeck = 'default';
21
- state.decks = {
22
- default: { ...defaultDeck },
23
- };
24
- window.location.pathname = '/';
25
- return;
26
- }
5
+ import * as Effect from 'effect/Effect';
6
+
7
+ import { Capabilities, Capability } from '@dxos/app-framework';
8
+ import {
9
+ AppCapabilities,
10
+ LayoutOperation,
11
+ NOT_FOUND_PATH,
12
+ expandPath,
13
+ fromUrlPath,
14
+ getWorkspaceFromPath,
15
+ toUrlPath,
16
+ } from '@dxos/app-toolkit';
17
+ import { Operation } from '@dxos/compute';
18
+ import { EffectEx } from '@dxos/effect';
19
+ import { invariant } from '@dxos/invariant';
20
+ import { log } from '@dxos/log';
21
+ import { Node } from '@dxos/plugin-graph';
22
+ import { isTauri } from '@dxos/util';
23
+
24
+ import { DeckCapabilities, type StoredDeckState, defaultDeck } from '#types';
25
+
26
+ import { updateActiveDeck } from '../operations/helpers';
27
+ import { deserializePlanks, serializePlanks, stripPlanks } from '../util';
28
+ import { shouldDeferNavigationHandlers } from './check-app-scheme';
29
+
30
+ /** Dispatch all NavigationHandler contributions with a given URL. */
31
+ const dispatchNavigationHandlers = Effect.fn(function* (url: URL) {
32
+ const handlers = yield* Capability.getAll(AppCapabilities.NavigationHandler);
33
+ yield* Effect.all(
34
+ handlers.map((handler) => handler(url)),
35
+ { concurrency: 'unbounded' },
36
+ );
37
+ });
38
+
39
+ export default Capability.makeModule(
40
+ Effect.fnUntraced(function* () {
41
+ const operationService = yield* Capability.get(Capabilities.OperationInvoker);
42
+ const capabilities = yield* Capability.Service;
43
+ const registry = yield* Capability.get(Capabilities.AtomRegistry);
44
+ const stateAtom = yield* Capability.get(DeckCapabilities.State);
45
+ const settingsAtom = yield* Capability.get(DeckCapabilities.Settings);
46
+
47
+ const provideServices = <A, E>(effect: Effect.Effect<A, E, Capability.Service | Operation.Service>) =>
48
+ effect.pipe(
49
+ Effect.provideService(Capability.Service, capabilities),
50
+ Effect.provideService(Operation.Service, operationService),
51
+ );
52
+
53
+ // Helper to get state.
54
+ const getState = () => registry.get(stateAtom);
55
+
56
+ // Helper to get computed deck from state.
57
+ const getDeck = () => {
58
+ const state = getState();
59
+ const deck = state.decks[state.activeDeck];
60
+ invariant(deck, `Deck not found: ${state.activeDeck}`);
61
+ return deck;
62
+ };
63
+
64
+ // Helper to update state.
65
+ const updateState = (fn: (current: StoredDeckState) => StoredDeckState) => {
66
+ registry.set(stateAtom, fn(getState()));
67
+ };
68
+
69
+ const handleNavigation = Effect.fn(function* (url?: URL) {
70
+ const { graph } = yield* Capability.get(AppCapabilities.AppGraph);
71
+ const resolvedUrl = url ?? new URL(window.location.href);
72
+ // When native redirect is active, check-app-scheme owns the initial dispatch
73
+ // to prevent one-time tokens from being consumed before the native app can use them.
74
+ const settings = registry.get(settingsAtom);
75
+ const deferHandlers = settings?.enableNativeRedirect && shouldDeferNavigationHandlers();
76
+ if (!deferHandlers) {
77
+ yield* dispatchNavigationHandlers(resolvedUrl);
78
+ }
79
+
80
+ const pathname = resolvedUrl.pathname;
81
+ const state = getState();
82
+ if (pathname === '/reset') {
83
+ updateState((s) => ({
84
+ ...s,
85
+ activeDeck: 'default',
86
+ decks: {
87
+ default: { ...defaultDeck },
88
+ },
89
+ }));
90
+ window.location.pathname = '/';
91
+ return;
92
+ }
93
+
94
+ const qualifiedId = fromUrlPath(pathname);
95
+ const workspace = getWorkspaceFromPath(qualifiedId);
96
+ if (workspace !== Node.RootId && workspace !== state.activeDeck) {
97
+ yield* Operation.invoke(LayoutOperation.SwitchWorkspace, { subject: workspace });
98
+ }
99
+
100
+ const deck = getDeck();
101
+ const activeId = qualifiedId !== workspace ? qualifiedId : undefined;
102
+ if (activeId) {
103
+ // Ensure the object referenced by the URL is open in the deck.
104
+ // Open validates the target and may redirect to 404, returning the resolved IDs.
105
+ const resolvedIds = yield* Operation.invoke(LayoutOperation.Open, { subject: [activeId] });
106
+ // If not already in solo mode, switch to solo for the resolved target.
107
+ if (!deck.solo) {
108
+ yield* Operation.invoke(LayoutOperation.SetLayoutMode, {
109
+ subject: resolvedIds?.[0] ?? activeId,
110
+ mode: 'solo',
111
+ });
112
+ }
113
+ } else if (deck.solo && deck.solo !== NOT_FOUND_PATH) {
114
+ // Stay in solo mode; redirect URL to reflect the current solo item.
115
+ // Do not switch to deck mode here — only explicit user action should change layout mode.
116
+ const path = toUrlPath(deck.solo);
117
+ if (window.location.pathname !== path) {
118
+ history.replaceState(null, '', `${path}${stripPlanks(window.location.search)}`);
119
+ }
120
+ } else if (!activeId && !deck.solo) {
121
+ // Multi-mode: restore planks from query params.
122
+ const plankIds = deserializePlanks(resolvedUrl);
123
+ if (plankIds.length > 0) {
124
+ for (const plankId of plankIds) {
125
+ expandPath(graph, plankId);
126
+ }
127
+ updateState((state) => updateActiveDeck(state, { active: plankIds, initialized: true }));
128
+ }
129
+ }
130
+ });
131
+
132
+ const onPopState = () => void EffectEx.runAndForwardErrors(provideServices(handleNavigation()));
27
133
 
28
- const [_, nextDeck, nextSolo] = pathname.split('/');
29
- if (nextDeck && nextDeck !== state.activeDeck) {
30
- await dispatch(createIntent(LayoutAction.SwitchWorkspace, { part: 'workspace', subject: nextDeck }));
134
+ // Install before handleNavigation()/state-sync push entries on top of the sentinel.
135
+ const sentinelKey = installLeaveTrap();
136
+
137
+ // Landing on the sentinel means a Back is about to leave Composer; confirm and act on it.
138
+ // The guard stops our own back()/forward() from re-entering.
139
+ let handlingSentinel = false;
140
+ const onCurrentEntryChange = () => {
141
+ const current = window.navigation.currentEntry;
142
+ if (handlingSentinel || !current || current.key !== sentinelKey) {
143
+ return;
144
+ }
145
+ handlingSentinel = true;
146
+ queueMicrotask(() => {
147
+ if (window.confirm('Leave Composer?')) {
148
+ history.back(); // Past the sentinel to the prior page.
149
+ } else {
150
+ history.forward(); // Back to where the user was.
151
+ }
152
+ setTimeout(() => {
153
+ handlingSentinel = false;
154
+ });
155
+ });
156
+ };
157
+
158
+ yield* provideServices(handleNavigation());
159
+ window.addEventListener('popstate', onPopState);
160
+ if ('navigation' in window) {
161
+ window.navigation.addEventListener('currententrychange', onCurrentEntryChange);
31
162
  }
32
163
 
33
- if (nextSolo && nextSolo !== state.deck.solo) {
34
- await dispatch(
35
- createIntent(LayoutAction.SetLayoutMode, { part: 'mode', subject: nextSolo, options: { mode: 'solo' } }),
164
+ // Tauri deep link support.
165
+ let unlistenDeepLink: (() => void) | undefined;
166
+ if (isTauri()) {
167
+ yield* Effect.gen(function* () {
168
+ const { getCurrent, onOpenUrl } = yield* Effect.promise(() => import('@tauri-apps/plugin-deep-link'));
169
+
170
+ const launchUrls = yield* Effect.promise(() => getCurrent());
171
+ if (launchUrls && launchUrls.length > 0) {
172
+ log('app launched with deep links', { urls: launchUrls });
173
+ for (const urlString of launchUrls) {
174
+ yield* provideServices(handleDeepLink(urlString, handleNavigation));
175
+ }
176
+ }
177
+
178
+ unlistenDeepLink = yield* Effect.promise(() =>
179
+ onOpenUrl((urls) => {
180
+ for (const urlString of urls) {
181
+ void EffectEx.runAndForwardErrors(provideServices(handleDeepLink(urlString, handleNavigation)));
182
+ }
183
+ }),
184
+ );
185
+ }).pipe(
186
+ Effect.catchAll((error) => Effect.sync(() => log.warn('failed to initialize deep link listener', { error }))),
36
187
  );
37
- } else if (!nextSolo && state.deck.solo) {
38
- await dispatch(createIntent(LayoutAction.SetLayoutMode, { part: 'mode', options: { mode: 'deck' } }));
39
188
  }
40
- };
41
-
42
- await handleNavigation();
43
- window.addEventListener('popstate', handleNavigation);
44
-
45
- const unsubscribe = scheduledEffect(
46
- () => ({ solo: state.deck.solo, activeDeck: state.activeDeck }),
47
- ({ solo, activeDeck }) => {
48
- const path = solo ? `/${activeDeck}/${solo}` : `/${activeDeck}`;
49
- if (window.location.pathname !== path) {
50
- // TODO(thure): In some browsers, this only preserves the most recent state change, even though this is not `history.replace`…
51
- history.pushState(null, '', `${path}${window.location.search}`);
189
+
190
+ // Sync URL with layout state changes.
191
+ let lastSolo: string | undefined;
192
+ let lastActiveDeck: string | undefined;
193
+ let lastActiveKey: string | undefined;
194
+ const unsubscribe = registry.subscribe(stateAtom, () => {
195
+ const state = getState();
196
+ const deck = getDeck();
197
+ const solo = deck.solo;
198
+ const activeDeck = state.activeDeck;
199
+ const activeKey = solo ? undefined : JSON.stringify(deck.active);
200
+
201
+ if (solo !== lastSolo || activeDeck !== lastActiveDeck || activeKey !== lastActiveKey) {
202
+ lastSolo = solo;
203
+ lastActiveDeck = activeDeck;
204
+ lastActiveKey = activeKey;
205
+
206
+ const path = solo && solo !== NOT_FOUND_PATH ? toUrlPath(solo) : toUrlPath(activeDeck);
207
+ const search = !solo
208
+ ? serializePlanks(deck.active, window.location.search)
209
+ : stripPlanks(window.location.search);
210
+ const newUrl = `${path}${search}`;
211
+
212
+ if (`${window.location.pathname}${window.location.search}` !== newUrl) {
213
+ history.pushState(null, '', newUrl);
214
+ }
52
215
  }
53
- },
54
- );
216
+ });
55
217
 
56
- return contributes(Capabilities.Null, null, () => {
57
- window.removeEventListener('popstate', handleNavigation);
58
- unsubscribe();
59
- });
218
+ return Capability.contributes(Capabilities.Null, null, () =>
219
+ Effect.sync(() => {
220
+ window.removeEventListener('popstate', onPopState);
221
+ if ('navigation' in window) {
222
+ window.navigation.removeEventListener('currententrychange', onCurrentEntryChange);
223
+ }
224
+ unsubscribe();
225
+ unlistenDeepLink?.();
226
+ }),
227
+ );
228
+ }),
229
+ );
230
+
231
+ /**
232
+ * sessionStorage key holding the sentinel history entry's key. The entry is identified by its
233
+ * (reload-stable) Navigation API key rather than by entry state, because the deck overwrites
234
+ * history state via replaceState during URL sync — which would erase a state marker. sessionStorage
235
+ * survives reloads within the tab and the deck never touches it.
236
+ */
237
+ const SENTINEL_STORAGE_KEY = 'dxos.composer.deck.leaveTrap.sentinelKey';
238
+
239
+ /**
240
+ * Insert a "sentinel" history entry beneath the app's working entries, so a Back-press that would
241
+ * leave Composer instead lands on the sentinel — where `onCurrentEntryChange` confirms the exit. A
242
+ * cross-document back is uncancelable and `beforeunload` cannot distinguish reload from leave, so
243
+ * this same-document floor is required; reload fires no traversal and is never trapped. Requires the
244
+ * Navigation API (Chromium); no-op otherwise. Idempotent across reloads via the sessionStorage-held
245
+ * entry key, so the sentinel is not duplicated. Returns the sentinel entry's key, or undefined.
246
+ */
247
+ const installLeaveTrap = (): string | undefined => {
248
+ if (!('navigation' in window)) {
249
+ return undefined;
250
+ }
251
+ const saved = sessionStorage.getItem(SENTINEL_STORAGE_KEY);
252
+ if (saved && window.navigation.entries().some((entry) => entry.key === saved)) {
253
+ // The sentinel survived (reload, or the user returned to Composer after leaving). If we are
254
+ // sitting ON it — e.g. the user left via the sentinel then came back Forward onto it — push a
255
+ // working entry above so the user is above the floor again and the trap re-arms.
256
+ if (window.navigation.currentEntry?.key === saved) {
257
+ history.pushState(null, '', window.location.pathname + window.location.search);
258
+ }
259
+ return saved;
260
+ }
261
+ // history.length > 1 (not navigation.canGoBack, which is false for a cross-origin prior entry)
262
+ // means there is somewhere to leave to; otherwise Back can't exit and no sentinel is needed.
263
+ const key = window.navigation.currentEntry?.key;
264
+ if (key && window.history.length > 1) {
265
+ // Record the current (landing) entry as the sentinel, then push the working entry above it.
266
+ sessionStorage.setItem(SENTINEL_STORAGE_KEY, key);
267
+ history.pushState(null, '', window.location.pathname + window.location.search);
268
+ return key;
269
+ }
270
+ return undefined;
60
271
  };
272
+
273
+ /** Check if a path is a redirect path handled elsewhere (e.g., OAuth). */
274
+ const isRedirectPath = (pathname: string): boolean => pathname.startsWith('/redirect/');
275
+
276
+ /** Handle a deep link URL string. Merges query params into window.location and navigates. */
277
+ const handleDeepLink = Effect.fn(function* (urlString: string, navigate: (url?: URL) => Effect.Effect<void, any, any>) {
278
+ log('deep link received', { url: urlString });
279
+
280
+ const deepLinkUrl = new URL(urlString);
281
+
282
+ // For custom schemes (e.g., composer://a/b/c), new URL() treats the first segment as the
283
+ // hostname. Reconstruct the full path from hostname + pathname.
284
+ const fullPath =
285
+ deepLinkUrl.protocol !== 'https:' && deepLinkUrl.protocol !== 'http:' && deepLinkUrl.hostname
286
+ ? '/' + deepLinkUrl.hostname + deepLinkUrl.pathname
287
+ : deepLinkUrl.pathname;
288
+
289
+ if (isRedirectPath(fullPath)) {
290
+ return;
291
+ }
292
+
293
+ // Merge deep link query params into the current window URL so handlers can read them.
294
+ const current = new URL(window.location.href);
295
+ if (deepLinkUrl.search) {
296
+ deepLinkUrl.searchParams.forEach((value, key) => current.searchParams.set(key, value));
297
+ }
298
+ current.pathname = fullPath;
299
+ history.replaceState(null, '', current.pathname + current.search);
300
+
301
+ yield* navigate(current);
302
+ });
@@ -0,0 +1,37 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+
7
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
8
+
9
+ import { translations } from '#translations';
10
+
11
+ import { DeckSettings } from './DeckSettings';
12
+
13
+ const meta = {
14
+ title: 'plugins/plugin-deck/components/DeckSettings',
15
+ tags: ['settings'],
16
+ component: DeckSettings,
17
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
18
+ parameters: {
19
+ layout: 'fullscreen',
20
+ translations,
21
+ },
22
+ } satisfies Meta<typeof DeckSettings>;
23
+
24
+ export default meta;
25
+
26
+ type Story = StoryObj<typeof meta>;
27
+
28
+ export const Default: Story = {
29
+ args: {
30
+ settings: {
31
+ enableDeck: true,
32
+ encapsulatedPlanks: false,
33
+ showHints: true,
34
+ enableNativeRedirect: false,
35
+ },
36
+ },
37
+ };
@@ -4,85 +4,31 @@
4
4
 
5
5
  import React from 'react';
6
6
 
7
- import { Input, Select, useTranslation } from '@dxos/react-ui';
8
- import { DeprecatedFormContainer, DeprecatedFormInput } from '@dxos/react-ui-form';
7
+ import { type AppSurface } from '@dxos/app-toolkit/ui';
8
+ import { useTranslation } from '@dxos/react-ui';
9
+ import { Settings as SettingsForm } from '@dxos/react-ui-form';
9
10
 
10
- import { DECK_PLUGIN } from '../../meta';
11
- import {
12
- type DeckSettingsProps,
13
- type NewPlankPositioning,
14
- NewPlankPositions,
15
- type Overscroll,
16
- OverscrollOptions,
17
- } from '../../types';
11
+ import { meta } from '#meta';
12
+ import { Settings } from '#types';
18
13
 
19
14
  const isSocket = !!(globalThis as any).__args;
20
15
 
21
- export const DeckSettings = ({ settings }: { settings: DeckSettingsProps }) => {
22
- const { t } = useTranslation(DECK_PLUGIN);
16
+ export type DeckSettingsProps = AppSurface.SettingsArticleProps<Settings.Settings>;
17
+
18
+ export const DeckSettings = ({ settings, onSettingsChange }: DeckSettingsProps) => {
19
+ const { t } = useTranslation(meta.id);
23
20
 
24
21
  return (
25
- <DeprecatedFormContainer>
26
- <DeprecatedFormInput label={t('settings enable deck label')}>
27
- <Input.Switch checked={settings.enableDeck} onCheckedChange={(checked) => (settings.enableDeck = checked)} />
28
- </DeprecatedFormInput>
29
- <DeprecatedFormInput label={t('select new plank positioning label')}>
30
- <Select.Root
31
- disabled={!settings.enableDeck}
32
- value={settings.newPlankPositioning ?? 'start'}
33
- onValueChange={(value) => (settings.newPlankPositioning = value as NewPlankPositioning)}
34
- >
35
- <Select.TriggerButton placeholder={t('select new plank positioning placeholder')} />
36
- <Select.Portal>
37
- <Select.Content>
38
- <Select.Viewport>
39
- {NewPlankPositions.map((position) => (
40
- <Select.Option key={position} value={position}>
41
- {t(`settings new plank position ${position} label`)}
42
- </Select.Option>
43
- ))}
44
- </Select.Viewport>
45
- </Select.Content>
46
- </Select.Portal>
47
- </Select.Root>
48
- </DeprecatedFormInput>
49
- <DeprecatedFormInput label={t('settings overscroll label')}>
50
- <Select.Root
51
- disabled={!settings.enableDeck}
52
- value={settings.overscroll ?? 'none'}
53
- onValueChange={(value) => (settings.overscroll = value as Overscroll)}
54
- >
55
- <Select.TriggerButton placeholder={t('select overscroll placeholder')} />
56
- <Select.Portal>
57
- <Select.Content>
58
- <Select.Viewport>
59
- {OverscrollOptions.map((option) => (
60
- <Select.Option key={option} value={option}>
61
- {t(`settings overscroll ${option} label`)}
62
- </Select.Option>
63
- ))}
64
- </Select.Viewport>
65
- </Select.Content>
66
- </Select.Portal>
67
- </Select.Root>
68
- </DeprecatedFormInput>
69
- <DeprecatedFormInput label={t('settings enable statusbar label')}>
70
- <Input.Switch
71
- checked={settings.enableStatusbar}
72
- onCheckedChange={(checked) => (settings.enableStatusbar = checked)}
22
+ <SettingsForm.Viewport>
23
+ <SettingsForm.Section title={t('settings.title', { ns: meta.id })}>
24
+ <SettingsForm.FieldSet
25
+ readonly={!onSettingsChange}
26
+ schema={Settings.Settings}
27
+ visible={(path) => path !== 'enableNativeRedirect' || !isSocket}
28
+ values={settings}
29
+ onValuesChanged={(values) => onSettingsChange?.(() => values)}
73
30
  />
74
- </DeprecatedFormInput>
75
- <DeprecatedFormInput label={t('settings show hints label')}>
76
- <Input.Switch checked={settings.showHints} onCheckedChange={(checked) => (settings.showHints = checked)} />
77
- </DeprecatedFormInput>
78
- {!isSocket && (
79
- <DeprecatedFormInput label={t('settings native redirect label')}>
80
- <Input.Switch
81
- checked={settings.enableNativeRedirect}
82
- onCheckedChange={(checked) => (settings.enableNativeRedirect = checked)}
83
- />
84
- </DeprecatedFormInput>
85
- )}
86
- </DeprecatedFormContainer>
31
+ </SettingsForm.Section>
32
+ </SettingsForm.Viewport>
87
33
  );
88
34
  };
@@ -1,5 +1,5 @@
1
1
  //
2
- // Copyright 2025 DXOS.org
2
+ // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- export * from './DeckSettings';
5
+ export { DeckSettings as default } from './DeckSettings';