@dxos/plugin-deck 0.7.5-labs.e27f9b9 → 0.7.5-main.2567c87

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 (209) hide show
  1. package/dist/lib/browser/{app-graph-builder-CI6ZFMNL.mjs → app-graph-builder-67VRUD5K.mjs} +31 -57
  2. package/dist/lib/browser/app-graph-builder-67VRUD5K.mjs.map +7 -0
  3. package/dist/lib/browser/{check-app-scheme-S3EYUPMF.mjs → check-app-scheme-GEX6W2R5.mjs} +3 -3
  4. package/dist/lib/browser/{check-app-scheme-S3EYUPMF.mjs.map → check-app-scheme-GEX6W2R5.mjs.map} +2 -2
  5. package/dist/lib/browser/chunk-2M4PXYNB.mjs +1052 -0
  6. package/dist/lib/browser/chunk-2M4PXYNB.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-2PJNBVCY.mjs +39 -0
  8. package/dist/lib/browser/chunk-2PJNBVCY.mjs.map +7 -0
  9. package/dist/lib/browser/chunk-4C2AFTET.mjs +186 -0
  10. package/dist/lib/browser/chunk-4C2AFTET.mjs.map +7 -0
  11. package/dist/lib/browser/{chunk-YQ2GWTDU.mjs → chunk-5VFDMW5M.mjs} +2 -2
  12. package/dist/lib/browser/chunk-5VFDMW5M.mjs.map +7 -0
  13. package/dist/lib/browser/{chunk-N7TEPFVR.mjs → chunk-JQJ5UWVB.mjs} +2 -3
  14. package/dist/lib/browser/{chunk-N7TEPFVR.mjs.map → chunk-JQJ5UWVB.mjs.map} +3 -3
  15. package/dist/lib/browser/chunk-KY5WXIXY.mjs +44 -0
  16. package/dist/lib/browser/chunk-KY5WXIXY.mjs.map +7 -0
  17. package/dist/lib/browser/deck-PLCSKPGL.mjs +26 -0
  18. package/dist/lib/browser/deck-PLCSKPGL.mjs.map +7 -0
  19. package/dist/lib/browser/index.mjs +76 -36
  20. package/dist/lib/browser/index.mjs.map +4 -4
  21. package/dist/lib/browser/intent-resolver-FVOQSTBX.mjs +152 -0
  22. package/dist/lib/browser/intent-resolver-FVOQSTBX.mjs.map +7 -0
  23. package/dist/lib/browser/intent-resolver-K7GW4A2I.mjs +249 -0
  24. package/dist/lib/browser/intent-resolver-K7GW4A2I.mjs.map +7 -0
  25. package/dist/lib/browser/location-QHRBQBQN.mjs +35 -0
  26. package/dist/lib/browser/location-QHRBQBQN.mjs.map +7 -0
  27. package/dist/lib/browser/meta.json +1 -1
  28. package/dist/lib/browser/react-context-3BDW7W2N.mjs +32 -0
  29. package/dist/lib/browser/react-context-3BDW7W2N.mjs.map +7 -0
  30. package/dist/lib/browser/{react-root-HIHLRMCW.mjs → react-root-UL7ZDRVZ.mjs} +14 -10
  31. package/dist/lib/browser/react-root-UL7ZDRVZ.mjs.map +7 -0
  32. package/dist/lib/browser/react-surface-VPNOGGNN.mjs +28 -0
  33. package/dist/lib/browser/react-surface-VPNOGGNN.mjs.map +7 -0
  34. package/dist/lib/browser/{settings-WACNLCPB.mjs → settings-FNWW6WIJ.mjs} +7 -6
  35. package/dist/lib/browser/settings-FNWW6WIJ.mjs.map +7 -0
  36. package/dist/lib/browser/state-7I5BD7SE.mjs +34 -0
  37. package/dist/lib/browser/state-7I5BD7SE.mjs.map +7 -0
  38. package/dist/lib/browser/types.mjs +4 -16
  39. package/dist/lib/browser/url-handler-Z5B7LD3N.mjs +76 -0
  40. package/dist/lib/browser/url-handler-Z5B7LD3N.mjs.map +7 -0
  41. package/dist/types/src/DeckPlugin.d.ts.map +1 -1
  42. package/dist/types/src/capabilities/capabilities.d.ts +3 -132
  43. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  44. package/dist/types/src/capabilities/index.d.ts +3 -188
  45. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  46. package/dist/types/src/capabilities/{app-graph-builder.d.ts → layout/app-graph-builder.d.ts} +22 -22
  47. package/dist/types/src/capabilities/layout/app-graph-builder.d.ts.map +1 -0
  48. package/dist/types/src/capabilities/layout/deck.d.ts +4 -0
  49. package/dist/types/src/capabilities/layout/deck.d.ts.map +1 -0
  50. package/dist/types/src/capabilities/layout/index.d.ts +229 -0
  51. package/dist/types/src/capabilities/layout/index.d.ts.map +1 -0
  52. package/dist/types/src/capabilities/layout/intent-resolver.d.ts.map +1 -0
  53. package/dist/types/src/capabilities/layout/react-context.d.ts +8 -0
  54. package/dist/types/src/capabilities/layout/react-context.d.ts.map +1 -0
  55. package/dist/types/src/capabilities/layout/react-root.d.ts.map +1 -0
  56. package/dist/types/src/capabilities/layout/state.d.ts +42 -0
  57. package/dist/types/src/capabilities/layout/state.d.ts.map +1 -0
  58. package/dist/types/src/capabilities/navigation/check-app-scheme.d.ts.map +1 -0
  59. package/dist/types/src/capabilities/navigation/index.d.ts +5 -0
  60. package/dist/types/src/capabilities/navigation/index.d.ts.map +1 -0
  61. package/dist/types/src/capabilities/navigation/intent-resolver.d.ts +4 -0
  62. package/dist/types/src/capabilities/navigation/intent-resolver.d.ts.map +1 -0
  63. package/dist/types/src/capabilities/navigation/location.d.ts +4 -0
  64. package/dist/types/src/capabilities/navigation/location.d.ts.map +1 -0
  65. package/dist/types/src/capabilities/navigation/set-location.d.ts +10 -0
  66. package/dist/types/src/capabilities/navigation/set-location.d.ts.map +1 -0
  67. package/dist/types/src/capabilities/navigation/url-handler.d.ts.map +1 -0
  68. package/dist/types/src/capabilities/settings/index.d.ts +3 -0
  69. package/dist/types/src/capabilities/settings/index.d.ts.map +1 -0
  70. package/dist/types/src/capabilities/settings/react-surface.d.ts.map +1 -0
  71. package/dist/types/src/capabilities/settings/settings.d.ts.map +1 -0
  72. package/dist/types/src/components/DeckContext.d.ts +11 -0
  73. package/dist/types/src/components/DeckContext.d.ts.map +1 -0
  74. package/dist/types/src/components/DeckLayout/ActiveNode.d.ts.map +1 -1
  75. package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts.map +1 -1
  76. package/dist/types/src/components/DeckLayout/ContentEmpty.d.ts.map +1 -1
  77. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts +4 -1
  78. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
  79. package/dist/types/src/components/DeckLayout/Fullscreen.d.ts.map +1 -1
  80. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts +3 -3
  81. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts.map +1 -1
  82. package/dist/types/src/components/DeckLayout/Plank.d.ts +6 -8
  83. package/dist/types/src/components/DeckLayout/Plank.d.ts.map +1 -1
  84. package/dist/types/src/components/DeckLayout/PlankControls.d.ts +2 -2
  85. package/dist/types/src/components/DeckLayout/PlankControls.d.ts.map +1 -1
  86. package/dist/types/src/components/DeckLayout/PlankError.d.ts +3 -4
  87. package/dist/types/src/components/DeckLayout/PlankError.d.ts.map +1 -1
  88. package/dist/types/src/components/DeckLayout/Sidebar.d.ts.map +1 -1
  89. package/dist/types/src/components/DeckLayout/SidebarButton.d.ts +3 -5
  90. package/dist/types/src/components/DeckLayout/SidebarButton.d.ts.map +1 -1
  91. package/dist/types/src/components/DeckLayout/StatusBar.d.ts.map +1 -1
  92. package/dist/types/src/components/DeckLayout/Toast.d.ts +2 -2
  93. package/dist/types/src/components/DeckLayout/Toast.d.ts.map +1 -1
  94. package/dist/types/src/components/LayoutContext.d.ts +5 -0
  95. package/dist/types/src/components/LayoutContext.d.ts.map +1 -0
  96. package/dist/types/src/components/fragments.d.ts +0 -2
  97. package/dist/types/src/components/fragments.d.ts.map +1 -1
  98. package/dist/types/src/components/index.d.ts +2 -0
  99. package/dist/types/src/components/index.d.ts.map +1 -1
  100. package/dist/types/src/hooks/useMainSize.d.ts +2 -2
  101. package/dist/types/src/layout.d.ts +19 -5
  102. package/dist/types/src/layout.d.ts.map +1 -1
  103. package/dist/types/src/layout.test.d.ts +2 -0
  104. package/dist/types/src/layout.test.d.ts.map +1 -0
  105. package/dist/types/src/meta.d.ts +0 -1
  106. package/dist/types/src/meta.d.ts.map +1 -1
  107. package/dist/types/src/translations.d.ts +3 -3
  108. package/dist/types/src/types.d.ts +2 -107
  109. package/dist/types/src/types.d.ts.map +1 -1
  110. package/dist/types/src/util/index.d.ts +1 -2
  111. package/dist/types/src/util/index.d.ts.map +1 -1
  112. package/dist/types/src/util/layout-parts.d.ts +7 -0
  113. package/dist/types/src/util/layout-parts.d.ts.map +1 -0
  114. package/dist/types/src/util/useHoistStatusbar.d.ts.map +1 -1
  115. package/package.json +31 -32
  116. package/src/DeckPlugin.ts +57 -17
  117. package/src/capabilities/capabilities.ts +3 -4
  118. package/src/capabilities/index.ts +3 -12
  119. package/src/capabilities/{app-graph-builder.ts → layout/app-graph-builder.ts} +28 -36
  120. package/src/capabilities/layout/deck.ts +25 -0
  121. package/src/capabilities/layout/index.ts +12 -0
  122. package/src/capabilities/layout/intent-resolver.ts +128 -0
  123. package/src/capabilities/layout/react-context.tsx +26 -0
  124. package/src/capabilities/{react-root.tsx → layout/react-root.tsx} +11 -7
  125. package/src/capabilities/layout/state.ts +32 -0
  126. package/src/capabilities/{check-app-scheme.ts → navigation/check-app-scheme.ts} +2 -2
  127. package/src/capabilities/navigation/index.ts +10 -0
  128. package/src/capabilities/navigation/intent-resolver.ts +216 -0
  129. package/src/capabilities/navigation/location.ts +28 -0
  130. package/src/capabilities/navigation/set-location.ts +38 -0
  131. package/src/capabilities/navigation/url-handler.ts +67 -0
  132. package/src/capabilities/settings/index.ts +8 -0
  133. package/src/capabilities/settings/react-surface.tsx +23 -0
  134. package/src/capabilities/{settings.ts → settings/settings.ts} +5 -4
  135. package/src/components/DeckContext.ts +19 -0
  136. package/src/components/DeckLayout/ActiveNode.tsx +3 -2
  137. package/src/components/DeckLayout/ComplementarySidebar.tsx +69 -120
  138. package/src/components/DeckLayout/ContentEmpty.tsx +10 -7
  139. package/src/components/DeckLayout/DeckLayout.tsx +61 -103
  140. package/src/components/DeckLayout/Fullscreen.tsx +3 -2
  141. package/src/components/DeckLayout/NodePlankHeading.tsx +65 -57
  142. package/src/components/DeckLayout/Plank.tsx +41 -32
  143. package/src/components/DeckLayout/PlankControls.tsx +10 -11
  144. package/src/components/DeckLayout/PlankError.tsx +5 -6
  145. package/src/components/DeckLayout/Sidebar.tsx +20 -17
  146. package/src/components/DeckLayout/SidebarButton.tsx +31 -25
  147. package/src/components/DeckLayout/StatusBar.tsx +11 -5
  148. package/src/components/DeckLayout/Toast.tsx +2 -2
  149. package/src/components/LayoutContext.ts +12 -0
  150. package/src/components/LayoutSettings.tsx +8 -8
  151. package/src/components/fragments.ts +0 -8
  152. package/src/components/index.ts +2 -0
  153. package/src/hooks/useMainSize.ts +3 -3
  154. package/src/layout.test.ts +380 -0
  155. package/src/layout.ts +212 -43
  156. package/src/meta.ts +0 -1
  157. package/src/translations.ts +8 -8
  158. package/src/types.ts +2 -88
  159. package/src/util/index.ts +1 -2
  160. package/src/util/layout-parts.ts +12 -0
  161. package/src/util/useHoistStatusbar.ts +8 -17
  162. package/dist/lib/browser/app-graph-builder-CI6ZFMNL.mjs.map +0 -7
  163. package/dist/lib/browser/chunk-BTDY6SES.mjs +0 -1119
  164. package/dist/lib/browser/chunk-BTDY6SES.mjs.map +0 -7
  165. package/dist/lib/browser/chunk-FZOBKOA2.mjs +0 -24
  166. package/dist/lib/browser/chunk-FZOBKOA2.mjs.map +0 -7
  167. package/dist/lib/browser/chunk-M2L53AIH.mjs +0 -126
  168. package/dist/lib/browser/chunk-M2L53AIH.mjs.map +0 -7
  169. package/dist/lib/browser/chunk-YQ2GWTDU.mjs.map +0 -7
  170. package/dist/lib/browser/intent-resolver-CSXFDKTC.mjs +0 -494
  171. package/dist/lib/browser/intent-resolver-CSXFDKTC.mjs.map +0 -7
  172. package/dist/lib/browser/react-root-HIHLRMCW.mjs.map +0 -7
  173. package/dist/lib/browser/react-surface-4QVWKQYY.mjs +0 -38
  174. package/dist/lib/browser/react-surface-4QVWKQYY.mjs.map +0 -7
  175. package/dist/lib/browser/settings-WACNLCPB.mjs.map +0 -7
  176. package/dist/lib/browser/state-VPOYUKK6.mjs +0 -117
  177. package/dist/lib/browser/state-VPOYUKK6.mjs.map +0 -7
  178. package/dist/lib/browser/tools-5LDJNU56.mjs +0 -51
  179. package/dist/lib/browser/tools-5LDJNU56.mjs.map +0 -7
  180. package/dist/lib/browser/url-handler-HLF42IHP.mjs +0 -70
  181. package/dist/lib/browser/url-handler-HLF42IHP.mjs.map +0 -7
  182. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +0 -1
  183. package/dist/types/src/capabilities/check-app-scheme.d.ts.map +0 -1
  184. package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
  185. package/dist/types/src/capabilities/react-root.d.ts.map +0 -1
  186. package/dist/types/src/capabilities/react-surface.d.ts.map +0 -1
  187. package/dist/types/src/capabilities/set-active.d.ts +0 -9
  188. package/dist/types/src/capabilities/set-active.d.ts.map +0 -1
  189. package/dist/types/src/capabilities/settings.d.ts.map +0 -1
  190. package/dist/types/src/capabilities/state.d.ts +0 -76
  191. package/dist/types/src/capabilities/state.d.ts.map +0 -1
  192. package/dist/types/src/capabilities/tools.d.ts +0 -10
  193. package/dist/types/src/capabilities/tools.d.ts.map +0 -1
  194. package/dist/types/src/capabilities/url-handler.d.ts.map +0 -1
  195. package/dist/types/src/util/layoutAppliesTopbar.d.ts +0 -2
  196. package/dist/types/src/util/layoutAppliesTopbar.d.ts.map +0 -1
  197. package/src/capabilities/intent-resolver.ts +0 -350
  198. package/src/capabilities/react-surface.tsx +0 -31
  199. package/src/capabilities/set-active.ts +0 -43
  200. package/src/capabilities/state.ts +0 -102
  201. package/src/capabilities/tools.ts +0 -61
  202. package/src/capabilities/url-handler.ts +0 -63
  203. package/src/util/layoutAppliesTopbar.ts +0 -7
  204. /package/dist/types/src/capabilities/{intent-resolver.d.ts → layout/intent-resolver.d.ts} +0 -0
  205. /package/dist/types/src/capabilities/{react-root.d.ts → layout/react-root.d.ts} +0 -0
  206. /package/dist/types/src/capabilities/{check-app-scheme.d.ts → navigation/check-app-scheme.d.ts} +0 -0
  207. /package/dist/types/src/capabilities/{url-handler.d.ts → navigation/url-handler.d.ts} +0 -0
  208. /package/dist/types/src/capabilities/{react-surface.d.ts → settings/react-surface.d.ts} +0 -0
  209. /package/dist/types/src/capabilities/{settings.d.ts → settings/settings.d.ts} +0 -0
@@ -0,0 +1,12 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { type Context, createContext, useContext } from 'react';
6
+
7
+ import type { Layout } from '@dxos/app-framework';
8
+ import { raise } from '@dxos/debug';
9
+
10
+ export const LayoutContext: Context<Layout | null> = createContext<Layout | null>(null);
11
+
12
+ export const useLayout = (): Layout => useContext(LayoutContext) ?? raise(new Error('Missing LayoutContext'));
@@ -5,7 +5,7 @@
5
5
  import React from 'react';
6
6
 
7
7
  import { Input, Select, useTranslation } from '@dxos/react-ui';
8
- import { DeprecatedFormContainer, DeprecatedFormInput } from '@dxos/react-ui-form';
8
+ import { DeprecatedFormInput } from '@dxos/react-ui-form';
9
9
 
10
10
  import { DECK_PLUGIN } from '../meta';
11
11
  import {
@@ -22,7 +22,7 @@ export const LayoutSettings = ({ settings }: { settings: DeckSettingsProps }) =>
22
22
  const { t } = useTranslation(DECK_PLUGIN);
23
23
 
24
24
  return (
25
- <DeprecatedFormContainer>
25
+ <>
26
26
  <DeprecatedFormInput label={t('select new plank positioning label')}>
27
27
  <Select.Root
28
28
  value={settings.newPlankPositioning ?? 'start'}
@@ -72,12 +72,12 @@ export const LayoutSettings = ({ settings }: { settings: DeckSettingsProps }) =>
72
72
  />
73
73
  </DeprecatedFormInput>
74
74
  )}
75
- <DeprecatedFormInput label={t('settings enable ide-style statusbar label')}>
76
- <Input.Switch
77
- checked={settings.enableIdeStyleStatusbar}
78
- onCheckedChange={(checked) => (settings.enableIdeStyleStatusbar = checked)}
79
- />
75
+ <DeprecatedFormInput label={t('settings custom slots')}>
76
+ <Input.Switch checked={settings.customSlots} onCheckedChange={(checked) => (settings.customSlots = checked)} />
80
77
  </DeprecatedFormInput>
81
- </DeprecatedFormContainer>
78
+ <DeprecatedFormInput label={t('settings flat deck')}>
79
+ <Input.Switch checked={settings.flatDeck} onCheckedChange={(checked) => (settings.flatDeck = checked)} />
80
+ </DeprecatedFormInput>
81
+ </>
82
82
  );
83
83
  };
@@ -2,13 +2,5 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { mx } from '@dxos/react-ui-theme';
6
-
7
5
  export const soloInlinePadding =
8
6
  'pis-[calc(env(safe-area-inset-left)+.25rem)] pie-[calc(env(safe-area-inset-left)+.25rem)]';
9
-
10
- const sidebarToggleStyles = 'bs-[--rail-item] is-[--rail-item] absolute block-end-2 z-[1] !bg-deck lg:hidden';
11
-
12
- export const fixedSidebarToggleStyles = mx(sidebarToggleStyles, 'inline-start-2');
13
-
14
- export const fixedComplementarySidebarToggleStyles = mx(sidebarToggleStyles, 'inline-end-2');
@@ -3,4 +3,6 @@
3
3
  //
4
4
 
5
5
  export * from './DeckLayout';
6
+ export * from './DeckContext';
7
+ export * from './LayoutContext';
6
8
  export * from './LayoutSettings';
@@ -5,9 +5,9 @@
5
5
  import { useMainContext } from '@dxos/react-ui';
6
6
 
7
7
  export const useMainSize = () => {
8
- const { navigationSidebarState, complementarySidebarState } = useMainContext('DeckPluginPlank');
8
+ const { navigationSidebarOpen, complementarySidebarOpen } = useMainContext('DeckPluginPlank');
9
9
  return {
10
- 'data-sidebar-inline-start-state': navigationSidebarState,
11
- 'data-sidebar-inline-end-state': complementarySidebarState,
10
+ 'data-sidebar-inline-start-state': navigationSidebarOpen ? 'open' : 'closed',
11
+ 'data-sidebar-inline-end-state': complementarySidebarOpen ? 'open' : 'closed',
12
12
  };
13
13
  };
@@ -0,0 +1,380 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { expect, describe, test } from 'vitest';
6
+
7
+ import { type LayoutParts, type LayoutAdjustment, type LayoutEntry } from '@dxos/app-framework';
8
+
9
+ import { uriToActiveParts, activePartsToUri, incrementPlank, mergeLayoutParts, openEntry } from './layout';
10
+
11
+ describe('Layout URI parsing and formatting', () => {
12
+ test('uriToActiveParts parses a simple URI correctly', () => {
13
+ const uri = 'https://composer.space/main-id1~path1+id2_sidebar-id3';
14
+ const result = uriToActiveParts(uri);
15
+ expect(result).to.deep.equal({
16
+ main: [{ id: 'id1', path: 'path1' }, { id: 'id2' }],
17
+ sidebar: [{ id: 'id3' }],
18
+ });
19
+ });
20
+
21
+ test('activePartsToUri formats a simple object correctly', () => {
22
+ const activeParts: LayoutParts = {
23
+ main: [{ id: 'id1', path: 'path1' }, { id: 'id2' }],
24
+ sidebar: [{ id: 'id3' }],
25
+ };
26
+ const result = activePartsToUri(activeParts);
27
+ expect(result).to.equal('main-id1~path1+id2_sidebar-id3');
28
+ });
29
+
30
+ test('activePartsToUri handles complex cases with multiple parts, and simple paths', () => {
31
+ const complexActiveParts: LayoutParts = {
32
+ main: [{ id: 'id1', path: 'path1' }, { id: 'id2' }, { id: 'id3', path: 'path3' }],
33
+ sidebar: [{ id: 'id4' }, { id: 'id5', path: 'path5' }],
34
+ complementary: [{ id: 'id6', path: 'path6' }, { id: 'id7' }],
35
+ };
36
+ const result = activePartsToUri(complexActiveParts);
37
+ expect(result).to.equal('main-id1~path1+id2+id3~path3_sidebar-id4+id5~path5_complementary-id6~path6+id7');
38
+ });
39
+
40
+ test('Round trip: URI to object and back to URI', () => {
41
+ const originalUri = 'main-id1~path1+id2_sidebar-id3_complementary-id4~path4';
42
+ const activeParts = uriToActiveParts(originalUri);
43
+ const resultUri = activePartsToUri(activeParts);
44
+ expect(resultUri).to.equal(originalUri);
45
+ });
46
+
47
+ test('Round trip: object to URI and back to object', () => {
48
+ const originalParts: LayoutParts = {
49
+ main: [{ id: 'id1', path: 'path1' }, { id: 'id2' }],
50
+ sidebar: [{ id: 'id3' }],
51
+ complementary: [{ id: 'id4', path: 'path4' }],
52
+ };
53
+ const uri = activePartsToUri(originalParts);
54
+ const resultParts = uriToActiveParts(`https://composer.space/${uri}`);
55
+ expect(resultParts).to.deep.equal(originalParts);
56
+ });
57
+
58
+ test('uriToActiveParts handles missing parts', () => {
59
+ const uri = 'https://composer.space/main-id1~path1_sidebar-id2';
60
+ const result = uriToActiveParts(uri);
61
+ expect(result).to.deep.equal({
62
+ main: [{ id: 'id1', path: 'path1' }],
63
+ sidebar: [{ id: 'id2' }],
64
+ });
65
+ });
66
+
67
+ test('activePartsToUri excludes empty parts', () => {
68
+ const activeParts: LayoutParts = {
69
+ main: [{ id: 'id1', path: 'path1' }],
70
+ };
71
+ const result = activePartsToUri(activeParts);
72
+ expect(result).to.equal('main-id1~path1');
73
+ });
74
+ });
75
+
76
+ describe('Layout adjustment', () => {
77
+ test('adjustLayout moves an item left in the main part', () => {
78
+ const layout: LayoutParts = {
79
+ main: [{ id: 'id1' }, { id: 'id2' }, { id: 'id3' }],
80
+ sidebar: [{ id: 'sid1' }],
81
+ };
82
+ const adjustment: LayoutAdjustment = {
83
+ layoutCoordinate: { part: 'main', entryId: 'id2' },
84
+ type: 'increment-start',
85
+ };
86
+ const result = incrementPlank(layout, adjustment);
87
+ expect(result.main).to.deep.equal([{ id: 'id2' }, { id: 'id1' }, { id: 'id3' }]);
88
+ expect(result.sidebar).to.deep.equal([{ id: 'sid1' }]);
89
+ });
90
+
91
+ test('adjustLayout moves an item right in the main part', () => {
92
+ const layout: LayoutParts = {
93
+ main: [{ id: 'id1' }, { id: 'id2' }, { id: 'id3' }],
94
+ sidebar: [{ id: 'sid1' }],
95
+ };
96
+ const adjustment: LayoutAdjustment = {
97
+ layoutCoordinate: { part: 'main', entryId: 'id2' },
98
+ type: 'increment-end',
99
+ };
100
+ const result = incrementPlank(layout, adjustment);
101
+ expect(result.main).to.deep.equal([{ id: 'id1' }, { id: 'id3' }, { id: 'id2' }]);
102
+ expect(result.sidebar).to.deep.equal([{ id: 'sid1' }]);
103
+ });
104
+
105
+ test('adjustLayout does not move items in non-main parts', () => {
106
+ const layout: LayoutParts = {
107
+ main: [{ id: 'id1' }],
108
+ sidebar: [{ id: 'sid1' }, { id: 'sid2' }, { id: 'sid3' }],
109
+ };
110
+ const adjustment: LayoutAdjustment = {
111
+ layoutCoordinate: { part: 'sidebar', entryId: 'sid2' },
112
+ type: 'increment-end',
113
+ };
114
+ const result = incrementPlank(layout, adjustment);
115
+ expect(result).to.deep.equal(layout);
116
+ });
117
+
118
+ test('adjustLayout does not move the first item left in main', () => {
119
+ const layout: LayoutParts = {
120
+ main: [{ id: 'id1' }, { id: 'id2' }],
121
+ };
122
+ const adjustment: LayoutAdjustment = {
123
+ layoutCoordinate: { part: 'main', entryId: 'id1' },
124
+ type: 'increment-start',
125
+ };
126
+ const result = incrementPlank(layout, adjustment);
127
+ expect(result).to.deep.equal(layout);
128
+ });
129
+
130
+ test('adjustLayout does not move the last item right in main', () => {
131
+ const layout: LayoutParts = {
132
+ main: [{ id: 'id1' }, { id: 'id2' }],
133
+ };
134
+ const adjustment: LayoutAdjustment = {
135
+ layoutCoordinate: { part: 'main', entryId: 'id2' },
136
+ type: 'increment-end',
137
+ };
138
+ const result = incrementPlank(layout, adjustment);
139
+ expect(result).to.deep.equal(layout);
140
+ });
141
+
142
+ test('adjustLayout handles non-existent slugId in main', () => {
143
+ const layout: LayoutParts = {
144
+ main: [{ id: 'id1' }, { id: 'id2' }],
145
+ };
146
+ const adjustment: LayoutAdjustment = {
147
+ layoutCoordinate: { part: 'main', entryId: 'id3' },
148
+ type: 'increment-start',
149
+ };
150
+ const result = incrementPlank(layout, adjustment);
151
+ expect(result).to.deep.equal(layout);
152
+ });
153
+
154
+ test('adjustLayout preserves other parts when adjusting main', () => {
155
+ const layout: LayoutParts = {
156
+ main: [{ id: 'id1' }, { id: 'id2' }],
157
+ sidebar: [{ id: 'sid1' }],
158
+ complementary: [{ id: 'cid1' }],
159
+ };
160
+ const adjustment: LayoutAdjustment = {
161
+ layoutCoordinate: { part: 'main', entryId: 'id2' },
162
+ type: 'increment-start',
163
+ };
164
+ const result = incrementPlank(layout, adjustment);
165
+ expect(result.main).to.deep.equal([{ id: 'id2' }, { id: 'id1' }]);
166
+ expect(result.sidebar).to.deep.equal([{ id: 'sid1' }]);
167
+ expect(result.complementary).to.deep.equal([{ id: 'cid1' }]);
168
+ });
169
+
170
+ test('adjustLayout handles empty main part', () => {
171
+ const layout: LayoutParts = {
172
+ main: [],
173
+ sidebar: [{ id: 'sid1' }],
174
+ };
175
+ const adjustment: LayoutAdjustment = {
176
+ layoutCoordinate: { part: 'main', entryId: 'id1' },
177
+ type: 'increment-start',
178
+ };
179
+ const result = incrementPlank(layout, adjustment);
180
+ expect(result).to.deep.equal(layout);
181
+ });
182
+
183
+ test('adjustLayout handles undefined main part', () => {
184
+ const layout: LayoutParts = {
185
+ sidebar: [{ id: 'sid1' }],
186
+ };
187
+ const adjustment: LayoutAdjustment = {
188
+ layoutCoordinate: { part: 'main', entryId: 'id1' },
189
+ type: 'increment-start',
190
+ };
191
+ const result = incrementPlank(layout, adjustment);
192
+ expect(result).to.deep.equal(layout);
193
+ });
194
+
195
+ test('adjustLayout handles main part with only one item', () => {
196
+ const layout: LayoutParts = {
197
+ main: [{ id: 'id1' }],
198
+ };
199
+ const adjustment: LayoutAdjustment = {
200
+ layoutCoordinate: { part: 'main', entryId: 'id1' },
201
+ type: 'increment-end',
202
+ };
203
+ const result = incrementPlank(layout, adjustment);
204
+ expect(result).to.deep.equal(layout);
205
+ });
206
+ });
207
+
208
+ describe('Layout parts merging', () => {
209
+ test('merges two simple layout parts', () => {
210
+ const part1: LayoutParts = { main: [{ id: 'id1' }] };
211
+ const part2: LayoutParts = { sidebar: [{ id: 'id2' }] };
212
+ const result = mergeLayoutParts(part1, part2);
213
+ expect(result).to.deep.equal({
214
+ main: [{ id: 'id1' }],
215
+ sidebar: [{ id: 'id2' }],
216
+ });
217
+ });
218
+
219
+ test('replaces entries with the same id in the same part', () => {
220
+ const part1: LayoutParts = { main: [{ id: 'id1', path: 'path1' }] };
221
+ const part2: LayoutParts = { main: [{ id: 'id1', path: 'path2' }] };
222
+ const result = mergeLayoutParts(part1, part2);
223
+ expect(result).to.deep.equal({
224
+ main: [{ id: 'id1', path: 'path2' }],
225
+ });
226
+ });
227
+
228
+ test('merges multiple layout parts', () => {
229
+ const part1: LayoutParts = { main: [{ id: 'id1' }] };
230
+ const part2: LayoutParts = { sidebar: [{ id: 'id2' }] };
231
+ const part3: LayoutParts = { complementary: [{ id: 'id3' }] };
232
+ const result = mergeLayoutParts(part1, part2, part3);
233
+ expect(result).to.deep.equal({
234
+ main: [{ id: 'id1' }],
235
+ sidebar: [{ id: 'id2' }],
236
+ complementary: [{ id: 'id3' }],
237
+ });
238
+ });
239
+
240
+ test('handles empty layout parts', () => {
241
+ const part1: LayoutParts = { main: [{ id: 'id1' }] };
242
+ const part2: LayoutParts = {};
243
+ const result = mergeLayoutParts(part1, part2);
244
+ expect(result).to.deep.equal({
245
+ main: [{ id: 'id1' }],
246
+ });
247
+ });
248
+
249
+ test('merges parts with multiple entries', () => {
250
+ const part1: LayoutParts = { main: [{ id: 'id1' }, { id: 'id2' }] };
251
+ const part2: LayoutParts = { main: [{ id: 'id3' }], sidebar: [{ id: 'id4' }] };
252
+ const result = mergeLayoutParts(part1, part2);
253
+ expect(result).to.deep.equal({
254
+ main: [{ id: 'id1' }, { id: 'id2' }, { id: 'id3' }],
255
+ sidebar: [{ id: 'id4' }],
256
+ });
257
+ });
258
+
259
+ test('replaces entries with the same id and keeps unique entries', () => {
260
+ const part1: LayoutParts = {
261
+ main: [{ id: 'id1', path: 'path1' }, { id: 'id2' }],
262
+ sidebar: [{ id: 'id3' }],
263
+ };
264
+ const part2: LayoutParts = {
265
+ main: [{ id: 'id1', path: 'path2' }, { id: 'id4' }],
266
+ complementary: [{ id: 'id5' }],
267
+ };
268
+ const result = mergeLayoutParts(part1, part2);
269
+ expect(result).to.deep.equal({
270
+ main: [{ id: 'id1', path: 'path2' }, { id: 'id2' }, { id: 'id4' }],
271
+ sidebar: [{ id: 'id3' }],
272
+ complementary: [{ id: 'id5' }],
273
+ });
274
+ });
275
+
276
+ test('merges complex layout parts', () => {
277
+ const part1: LayoutParts = {
278
+ main: [{ id: 'id1', path: 'path1' }, { id: 'id2' }],
279
+ sidebar: [{ id: 'id3' }],
280
+ };
281
+ const part2: LayoutParts = {
282
+ main: [{ id: 'id1', path: 'path2' }, { id: 'id4' }],
283
+ complementary: [{ id: 'id5' }],
284
+ };
285
+ const part3: LayoutParts = {
286
+ sidebar: [{ id: 'id6' }],
287
+ fullScreen: [{ id: 'id7' }],
288
+ };
289
+ const result = mergeLayoutParts(part1, part2, part3);
290
+ expect(result).to.deep.equal({
291
+ main: [{ id: 'id1', path: 'path2' }, { id: 'id2' }, { id: 'id4' }],
292
+ sidebar: [{ id: 'id3' }, { id: 'id6' }],
293
+ complementary: [{ id: 'id5' }],
294
+ fullScreen: [{ id: 'id7' }],
295
+ });
296
+ });
297
+
298
+ test('handles merging with duplicate entries in the same part', () => {
299
+ const part1: LayoutParts = {
300
+ main: [{ id: 'id1' }, { id: 'id1' }, { id: 'id2' }],
301
+ };
302
+ const result = mergeLayoutParts(part1);
303
+ expect(result).to.deep.equal({
304
+ main: [{ id: 'id1' }, { id: 'id2' }],
305
+ });
306
+ });
307
+
308
+ test('preserves order of entries when merging', () => {
309
+ const part1: LayoutParts = { main: [{ id: 'id1' }, { id: 'id2' }] };
310
+ const part2: LayoutParts = { main: [{ id: 'id3' }, { id: 'id1' }] };
311
+ const result = mergeLayoutParts(part1, part2);
312
+ expect(result).to.deep.equal({
313
+ main: [{ id: 'id1' }, { id: 'id2' }, { id: 'id3' }],
314
+ });
315
+ });
316
+ });
317
+
318
+ describe('openEntry', () => {
319
+ const initialLayout: LayoutParts = {
320
+ main: [
321
+ { id: 'id1', path: 'path1' },
322
+ { id: 'id2', path: 'path2' },
323
+ { id: 'id3', path: 'path3' },
324
+ ],
325
+ sidebar: [{ id: 'sid1', path: 'sidepath1' }],
326
+ };
327
+
328
+ const newEntry: LayoutEntry = { id: 'new', path: 'newpath' };
329
+
330
+ test('adds entry to start of main without pivot', () => {
331
+ const result = openEntry(initialLayout, 'main', newEntry, { positioning: 'start' });
332
+ expect(result.main?.[0]).to.deep.equal(newEntry);
333
+ expect(result.main?.length).to.equal(4);
334
+ });
335
+
336
+ test('adds entry to end of main without pivot', () => {
337
+ const result = openEntry(initialLayout, 'main', newEntry, { positioning: 'end' });
338
+ expect(result.main?.[result.main.length - 1]).to.deep.equal(newEntry);
339
+ expect(result.main?.length).to.equal(4);
340
+ });
341
+
342
+ test('adds entry before pivot in main', () => {
343
+ const result = openEntry(initialLayout, 'main', newEntry, { positioning: 'start', pivotId: 'id2' });
344
+ expect(result.main?.[1]).to.deep.equal(newEntry);
345
+ expect(result.main?.length).to.equal(4);
346
+ });
347
+
348
+ test('adds entry after pivot in main', () => {
349
+ const result = openEntry(initialLayout, 'main', newEntry, { positioning: 'end', pivotId: 'id2' });
350
+ expect(result.main?.[2]).to.deep.equal(newEntry);
351
+ expect(result.main?.length).to.equal(4);
352
+ });
353
+
354
+ test('adds entry to start when pivot is not found', () => {
355
+ const result = openEntry(initialLayout, 'main', newEntry, { positioning: 'start', pivotId: 'nonexistent' });
356
+ expect(result.main?.[0]).to.deep.equal(newEntry);
357
+ expect(result.main?.length).to.equal(4);
358
+ });
359
+
360
+ test('does not add duplicate entry to main', () => {
361
+ const result = openEntry(initialLayout, 'main', initialLayout.main![0], { positioning: 'start' });
362
+ expect(result.main).to.deep.equal(initialLayout.main);
363
+ });
364
+
365
+ test('replaces entry in non-main part', () => {
366
+ const result = openEntry(initialLayout, 'sidebar', newEntry);
367
+ expect(result.sidebar).to.deep.equal([newEntry]);
368
+ });
369
+
370
+ test('creates new part if it does not exist', () => {
371
+ const result = openEntry(initialLayout, 'complementary', newEntry);
372
+ expect(result.complementary).to.deep.equal([newEntry]);
373
+ });
374
+
375
+ test('handles undefined main part', () => {
376
+ const layoutWithoutMain: LayoutParts = { sidebar: [{ id: 'sid1', path: 'sidepath1' }] };
377
+ const result = openEntry(layoutWithoutMain, 'main', newEntry);
378
+ expect(result.main).to.deep.equal([newEntry]);
379
+ });
380
+ });