@dxos/plugin-navtree 0.10.0 → 0.11.0

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 (187) hide show
  1. package/PLUGIN.mdl +8 -6
  2. package/dist/lib/NavTreePlugin.mjs +40 -0
  3. package/dist/lib/NavTreePlugin.mjs.map +1 -0
  4. package/dist/lib/capabilities.mjs +11 -0
  5. package/dist/lib/capabilities.mjs.map +1 -0
  6. package/dist/lib/chunk-CommandsDialogContent.mjs +100 -0
  7. package/dist/lib/chunk-CommandsDialogContent.mjs.map +1 -0
  8. package/dist/lib/chunk-CommandsTrigger.mjs +27 -0
  9. package/dist/lib/chunk-CommandsTrigger.mjs.map +1 -0
  10. package/dist/lib/chunk-NavTreeDocumentTitle.mjs +17 -0
  11. package/dist/lib/chunk-NavTreeDocumentTitle.mjs.map +1 -0
  12. package/dist/lib/chunk-app-graph-builder.mjs +33 -0
  13. package/dist/lib/chunk-app-graph-builder.mjs.map +1 -0
  14. package/dist/lib/chunk-expose.mjs +31 -0
  15. package/dist/lib/chunk-expose.mjs.map +1 -0
  16. package/dist/lib/chunk-keyboard.mjs +49 -0
  17. package/dist/lib/chunk-keyboard.mjs.map +1 -0
  18. package/dist/lib/{neutral/chunk-IRQMJLRY.mjs → chunk-meta.mjs} +14 -23
  19. package/dist/lib/chunk-meta.mjs.map +1 -0
  20. package/dist/lib/chunk-operation-handler.mjs +11 -0
  21. package/dist/lib/chunk-operation-handler.mjs.map +1 -0
  22. package/dist/lib/chunk-react-surface.mjs +46 -0
  23. package/dist/lib/chunk-react-surface.mjs.map +1 -0
  24. package/dist/lib/chunk-state.mjs +106 -0
  25. package/dist/lib/chunk-state.mjs.map +1 -0
  26. package/dist/lib/chunk-types.mjs +26 -0
  27. package/dist/lib/chunk-types.mjs.map +1 -0
  28. package/dist/lib/chunk-util.mjs +51 -0
  29. package/dist/lib/chunk-util.mjs.map +1 -0
  30. package/dist/lib/components.mjs +723 -0
  31. package/dist/lib/components.mjs.map +1 -0
  32. package/dist/lib/containers.mjs +231 -0
  33. package/dist/lib/containers.mjs.map +1 -0
  34. package/dist/lib/hooks.mjs +162 -0
  35. package/dist/lib/hooks.mjs.map +1 -0
  36. package/dist/lib/index.mjs +4 -0
  37. package/dist/lib/meta.mjs +2 -0
  38. package/dist/lib/operations.mjs +7 -0
  39. package/dist/lib/operations.mjs.map +1 -0
  40. package/dist/lib/plugin.mjs +9 -0
  41. package/dist/lib/plugin.mjs.map +1 -0
  42. package/dist/lib/testing.mjs +397 -0
  43. package/dist/lib/testing.mjs.map +1 -0
  44. package/dist/lib/translations.mjs +17 -0
  45. package/dist/lib/translations.mjs.map +1 -0
  46. package/dist/lib/types.mjs +2 -0
  47. package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
  48. package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
  49. package/dist/types/src/capabilities/keyboard.d.ts +1 -1
  50. package/dist/types/src/capabilities/nav-tree-view-state.d.ts +12 -0
  51. package/dist/types/src/capabilities/nav-tree-view-state.d.ts.map +1 -0
  52. package/dist/types/src/capabilities/nav-tree-view-state.test.d.ts +2 -0
  53. package/dist/types/src/capabilities/nav-tree-view-state.test.d.ts.map +1 -0
  54. package/dist/types/src/capabilities/operation-handler.d.ts +1 -1
  55. package/dist/types/src/capabilities/react-surface.d.ts +1 -1
  56. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  57. package/dist/types/src/capabilities/state.d.ts +1 -1
  58. package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
  59. package/dist/types/src/components/NavTree/NavTree.stories.d.ts +3 -0
  60. package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
  61. package/dist/types/src/components/NavTreeContext/NavTreeContext.d.ts.map +1 -1
  62. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts +1 -1
  63. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
  64. package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
  65. package/dist/types/src/components/Sidebar/L1Panel.d.ts +5 -2
  66. package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
  67. package/dist/types/src/components/Sidebar/L1Tabs.d.ts.map +1 -1
  68. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.d.ts.map +1 -1
  69. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts +2 -0
  70. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts.map +1 -1
  71. package/dist/types/src/containers/CommandsTrigger/CommandsTrigger.d.ts +4 -1
  72. package/dist/types/src/containers/CommandsTrigger/CommandsTrigger.d.ts.map +1 -1
  73. package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts.map +1 -1
  74. package/dist/types/src/containers/NavTreeDocumentTitle/NavTreeDocumentTitle.d.ts +5 -2
  75. package/dist/types/src/containers/NavTreeDocumentTitle/NavTreeDocumentTitle.d.ts.map +1 -1
  76. package/dist/types/src/experimental/Tree.d.ts +0 -1
  77. package/dist/types/src/experimental/Tree.d.ts.map +1 -1
  78. package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
  79. package/dist/types/src/experimental/visit-nodes.d.ts +3 -0
  80. package/dist/types/src/experimental/visit-nodes.d.ts.map +1 -0
  81. package/dist/types/src/hooks/useActions.d.ts +3 -1
  82. package/dist/types/src/hooks/useActions.d.ts.map +1 -1
  83. package/dist/types/src/translations.d.ts +2 -0
  84. package/dist/types/src/translations.d.ts.map +1 -1
  85. package/dist/types/src/types/NavTreeCapabilities.d.ts +1 -1
  86. package/dist/types/src/types/NavTreeCapabilities.d.ts.map +1 -1
  87. package/dist/types/src/types/schema.d.ts +1 -1
  88. package/dist/types/src/types/schema.d.ts.map +1 -1
  89. package/dist/types/src/util.d.ts.map +1 -1
  90. package/dist/types/tsconfig.tsbuildinfo +1 -1
  91. package/package.json +47 -52
  92. package/src/NavTreePlugin.tsx +3 -1
  93. package/src/capabilities/nav-tree-view-state.test.ts +55 -0
  94. package/src/capabilities/nav-tree-view-state.ts +19 -0
  95. package/src/capabilities/{react-surface.tsx → react-surface.ts} +8 -9
  96. package/src/capabilities/state.ts +47 -38
  97. package/src/components/NavTree/NavTree.stories.tsx +26 -5
  98. package/src/components/NavTree/NavTree.tsx +10 -13
  99. package/src/components/NavTreeItem/NavTreeItemAction.tsx +1 -1
  100. package/src/components/Sidebar/L0Menu.tsx +9 -3
  101. package/src/components/Sidebar/L1Panel.tsx +52 -20
  102. package/src/components/Sidebar/L1Tabs.tsx +6 -0
  103. package/src/containers/CommandsDialogContent/CommandsDialogContent.tsx +49 -44
  104. package/src/containers/CommandsTrigger/CommandsTrigger.tsx +2 -0
  105. package/src/containers/NavTreeContainer/NavTreeContainer.tsx +17 -18
  106. package/src/containers/NavTreeDocumentTitle/NavTreeDocumentTitle.tsx +2 -0
  107. package/src/experimental/Tree.stories.tsx +2 -1
  108. package/src/experimental/Tree.tsx +2 -18
  109. package/src/experimental/visit-nodes.ts +27 -0
  110. package/src/hooks/useActions.ts +15 -4
  111. package/src/hooks/useNavTreeModel.ts +1 -1
  112. package/src/operations/expose.ts +2 -2
  113. package/src/testing/app-graph-builder.ts +1 -1
  114. package/src/translations.ts +3 -0
  115. package/src/types/NavTreeCapabilities.ts +1 -1
  116. package/src/types/schema.ts +1 -1
  117. package/src/util.ts +7 -9
  118. package/dist/lib/neutral/CommandsDialogContent-TEKBCQ3U.mjs +0 -112
  119. package/dist/lib/neutral/CommandsDialogContent-TEKBCQ3U.mjs.map +0 -7
  120. package/dist/lib/neutral/CommandsTrigger-HGCI22SP.mjs +0 -27
  121. package/dist/lib/neutral/CommandsTrigger-HGCI22SP.mjs.map +0 -7
  122. package/dist/lib/neutral/NavTreeContainer-ICDKC2GI.mjs +0 -12
  123. package/dist/lib/neutral/NavTreeContainer-ICDKC2GI.mjs.map +0 -7
  124. package/dist/lib/neutral/NavTreeDocumentTitle-RMERXZGK.mjs +0 -22
  125. package/dist/lib/neutral/NavTreeDocumentTitle-RMERXZGK.mjs.map +0 -7
  126. package/dist/lib/neutral/NavTreePlugin.mjs +0 -68
  127. package/dist/lib/neutral/NavTreePlugin.mjs.map +0 -7
  128. package/dist/lib/neutral/NavTreePlugin.node.mjs +0 -18
  129. package/dist/lib/neutral/NavTreePlugin.node.mjs.map +0 -7
  130. package/dist/lib/neutral/NavTreePlugin.workerd.mjs +0 -16
  131. package/dist/lib/neutral/NavTreePlugin.workerd.mjs.map +0 -7
  132. package/dist/lib/neutral/app-graph-builder-2DYAIROU.mjs +0 -42
  133. package/dist/lib/neutral/app-graph-builder-2DYAIROU.mjs.map +0 -7
  134. package/dist/lib/neutral/capabilities/index.mjs +0 -17
  135. package/dist/lib/neutral/capabilities/index.mjs.map +0 -7
  136. package/dist/lib/neutral/chunk-4RN6TH36.mjs +0 -292
  137. package/dist/lib/neutral/chunk-4RN6TH36.mjs.map +0 -7
  138. package/dist/lib/neutral/chunk-BAVI2K7A.mjs +0 -27
  139. package/dist/lib/neutral/chunk-BAVI2K7A.mjs.map +0 -7
  140. package/dist/lib/neutral/chunk-EAN4K6OL.mjs +0 -8
  141. package/dist/lib/neutral/chunk-EAN4K6OL.mjs.map +0 -7
  142. package/dist/lib/neutral/chunk-IRQMJLRY.mjs.map +0 -7
  143. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +0 -10
  144. package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +0 -7
  145. package/dist/lib/neutral/chunk-JOSNCOV4.mjs +0 -79
  146. package/dist/lib/neutral/chunk-JOSNCOV4.mjs.map +0 -7
  147. package/dist/lib/neutral/components/index.mjs +0 -814
  148. package/dist/lib/neutral/components/index.mjs.map +0 -7
  149. package/dist/lib/neutral/containers/index.mjs +0 -20
  150. package/dist/lib/neutral/containers/index.mjs.map +0 -7
  151. package/dist/lib/neutral/expose-HIBA2NQG.mjs +0 -38
  152. package/dist/lib/neutral/expose-HIBA2NQG.mjs.map +0 -7
  153. package/dist/lib/neutral/hooks/index.mjs +0 -181
  154. package/dist/lib/neutral/hooks/index.mjs.map +0 -7
  155. package/dist/lib/neutral/index.mjs +0 -34
  156. package/dist/lib/neutral/index.mjs.map +0 -7
  157. package/dist/lib/neutral/keyboard-JM2HCF5N.mjs +0 -58
  158. package/dist/lib/neutral/keyboard-JM2HCF5N.mjs.map +0 -7
  159. package/dist/lib/neutral/meta.json +0 -1
  160. package/dist/lib/neutral/meta.mjs +0 -12
  161. package/dist/lib/neutral/meta.mjs.map +0 -7
  162. package/dist/lib/neutral/operation-handler-ORDOJUL2.mjs +0 -13
  163. package/dist/lib/neutral/operation-handler-ORDOJUL2.mjs.map +0 -7
  164. package/dist/lib/neutral/operations/index.mjs +0 -8
  165. package/dist/lib/neutral/operations/index.mjs.map +0 -7
  166. package/dist/lib/neutral/plugin.mjs +0 -16
  167. package/dist/lib/neutral/plugin.mjs.map +0 -7
  168. package/dist/lib/neutral/react-surface-QPMUEDZX.mjs +0 -48
  169. package/dist/lib/neutral/react-surface-QPMUEDZX.mjs.map +0 -7
  170. package/dist/lib/neutral/state-PDZU4PIP.mjs +0 -126
  171. package/dist/lib/neutral/state-PDZU4PIP.mjs.map +0 -7
  172. package/dist/lib/neutral/testing/index.mjs +0 -439
  173. package/dist/lib/neutral/testing/index.mjs.map +0 -7
  174. package/dist/lib/neutral/translations.mjs +0 -23
  175. package/dist/lib/neutral/translations.mjs.map +0 -7
  176. package/dist/lib/neutral/types/index.mjs +0 -10
  177. package/dist/lib/neutral/types/index.mjs.map +0 -7
  178. package/dist/types/src/NavTreePlugin.node.d.ts +0 -4
  179. package/dist/types/src/NavTreePlugin.node.d.ts.map +0 -1
  180. package/dist/types/src/NavTreePlugin.test.d.ts +0 -2
  181. package/dist/types/src/NavTreePlugin.test.d.ts.map +0 -1
  182. package/dist/types/src/NavTreePlugin.workerd.d.ts +0 -4
  183. package/dist/types/src/NavTreePlugin.workerd.d.ts.map +0 -1
  184. package/src/NavTreePlugin.node.ts +0 -17
  185. package/src/NavTreePlugin.test.ts +0 -27
  186. package/src/NavTreePlugin.workerd.ts +0 -16
  187. /package/src/components/NavTreeContext/{NavTreeContext.tsx → NavTreeContext.ts} +0 -0
@@ -0,0 +1,106 @@
1
+ import { Capabilities, Capability } from "@dxos/app-framework";
2
+ import { AppCapabilities } from "@dxos/app-toolkit";
3
+ import { Graph, Node } from "@dxos/plugin-graph";
4
+ import * as Effect from "effect/Effect";
5
+ import { AttentionCapabilities } from "@dxos/plugin-attention";
6
+ import { NavTreeCapabilities } from "#types";
7
+ import { Path } from "@dxos/react-ui-list";
8
+ import { Atom } from "@effect-atom/atom";
9
+ import * as Schema from "effect/Schema";
10
+ import { ViewState } from "@dxos/react-ui-attention/types";
11
+ //#region src/capabilities/nav-tree-view-state.ts
12
+ /**
13
+ * Per-path navtree expansion, persisted (localStorage) via the ViewState `local` backend keyed by
14
+ * tree path — replaces the hand-rolled single-blob localStorage store. Only `open` is durable; the
15
+ * `current` highlight is ephemeral (driven by the active layout) and never persisted.
16
+ */
17
+ var navTreeOpenAspect = ViewState.define({
18
+ key: "navtree-open",
19
+ backend: "local",
20
+ schema: Schema.Struct({ open: Schema.Boolean }),
21
+ defaultValue: () => ({ open: false })
22
+ });
23
+ //#endregion
24
+ //#region src/capabilities/state.ts
25
+ /** Default `open` value for new entries; `current` is derived from the layout when the entry is created. */
26
+ var defaultOpen = false;
27
+ /** L0 (top-level workspace) paths are direct children of root — not part of the expandable tree model. */
28
+ var isTopLevelPath = (path) => path.length === 2 && path[0] === Node.RootId;
29
+ /** Default state entries for initial tree structure. */
30
+ var defaultStateEntries = [["root", {
31
+ open: true,
32
+ current: false
33
+ }]];
34
+ var state_default = Capability.makeModule(Effect.fnUntraced(function* () {
35
+ const registry = yield* Capability.get(Capabilities.AtomRegistry);
36
+ const layoutAtom = yield* Capability.get(AppCapabilities.Layout);
37
+ const viewState = yield* Capability.get(AttentionCapabilities.ViewState);
38
+ let activeIds = registry.get(layoutAtom).active;
39
+ /** Item state for a path not seen before: `current` follows the layout, `open` starts closed. */
40
+ const initialItemState = (pathString) => ({
41
+ open: defaultOpen,
42
+ current: activeIds.includes(Path.last(pathString))
43
+ });
44
+ const persistedPaths = viewState.contexts(navTreeOpenAspect);
45
+ const backingState = persistedPaths.length === 0 ? new Map(defaultStateEntries) : new Map(persistedPaths.map((pathString) => [pathString, {
46
+ open: viewState.get(navTreeOpenAspect, pathString).open,
47
+ current: activeIds.includes(Path.last(pathString))
48
+ }]));
49
+ const itemAtomFamily = Atom.family((pathString) => Atom.make(backingState.get(pathString) ?? initialItemState(pathString)).pipe(Atom.keepAlive));
50
+ const getItemAtom = (path) => {
51
+ const pathString = Path.create(...path);
52
+ if (!backingState.has(pathString)) backingState.set(pathString, initialItemState(pathString));
53
+ return itemAtomFamily(pathString);
54
+ };
55
+ const getItem = (path) => {
56
+ return registry.get(getItemAtom(path));
57
+ };
58
+ const setItem = (path, key, next) => {
59
+ const pathString = Path.create(...path);
60
+ const atom = itemAtomFamily(pathString);
61
+ const newValue = {
62
+ ...registry.get(atom),
63
+ [key]: next
64
+ };
65
+ registry.set(atom, newValue);
66
+ backingState.set(pathString, newValue);
67
+ if (key === "open" && !isTopLevelPath(path)) viewState.set(navTreeOpenAspect, pathString, { open: next });
68
+ };
69
+ const unsubscribe = registry.subscribe(layoutAtom, (layout) => {
70
+ const removed = activeIds.filter((id) => !layout.active.includes(id));
71
+ activeIds = layout.active;
72
+ const handleUpdate = () => {
73
+ removed.forEach((id) => {
74
+ Array.from(backingState.keys()).filter((key) => Path.last(key) === id).forEach((key) => {
75
+ setItem(Path.parts(key), "current", false);
76
+ });
77
+ });
78
+ layout.active.forEach((id) => {
79
+ Array.from(/* @__PURE__ */ new Set([...backingState.keys(), id])).filter((key) => Path.last(key) === id).forEach((key) => {
80
+ setItem(Path.parts(key), "current", true);
81
+ });
82
+ });
83
+ };
84
+ queueMicrotask(handleUpdate);
85
+ });
86
+ yield* Effect.gen(function* () {
87
+ const { graph } = yield* Capability.waitFor(AppCapabilities.AppGraph);
88
+ const layout = registry.get(layoutAtom);
89
+ if (layout.workspace) Graph.expand(graph, layout.workspace, "child");
90
+ const openPaths = Array.from(backingState.entries()).filter(([, state]) => state.open).map(([pathString]) => Path.parts(pathString)).filter((path) => !isTopLevelPath(path));
91
+ for (const path of openPaths) {
92
+ const nodeId = path[path.length - 1];
93
+ if (!nodeId) continue;
94
+ Graph.expand(graph, nodeId, "child");
95
+ }
96
+ }).pipe(Effect.forkDaemon);
97
+ return Capability.contributes(NavTreeCapabilities.State, {
98
+ getItem,
99
+ getItemAtom,
100
+ setItem
101
+ }, () => Effect.sync(() => unsubscribe()));
102
+ }));
103
+ //#endregion
104
+ export { state_default as default };
105
+
106
+ //# sourceMappingURL=chunk-state.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-state.mjs","names":[],"sources":["../../src/capabilities/nav-tree-view-state.ts","../../src/capabilities/state.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Schema from 'effect/Schema';\n\nimport { ViewState } from '@dxos/react-ui-attention/types';\n\n/**\n * Per-path navtree expansion, persisted (localStorage) via the ViewState `local` backend keyed by\n * tree path — replaces the hand-rolled single-blob localStorage store. Only `open` is durable; the\n * `current` highlight is ephemeral (driven by the active layout) and never persisted.\n */\nexport const navTreeOpenAspect = ViewState.define({\n key: 'navtree-open',\n backend: 'local',\n schema: Schema.Struct({ open: Schema.Boolean }),\n defaultValue: () => ({ open: false }),\n});\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { Atom } from '@effect-atom/atom';\nimport * as Effect from 'effect/Effect';\n\nimport { Capabilities, Capability } from '@dxos/app-framework';\nimport { AppCapabilities } from '@dxos/app-toolkit';\nimport { AttentionCapabilities } from '@dxos/plugin-attention';\nimport { Graph, Node } from '@dxos/plugin-graph';\nimport { Path } from '@dxos/react-ui-list';\n\nimport { NavTreeCapabilities } from '#types';\n\nimport { navTreeOpenAspect } from './nav-tree-view-state';\n\n/** Default `open` value for new entries; `current` is derived from the layout when the entry is created. */\nconst defaultOpen = false;\n\n/** L0 (top-level workspace) paths are direct children of root — not part of the expandable tree model. */\nconst isTopLevelPath = (path: string[]): boolean => path.length === 2 && path[0] === Node.RootId;\n\n/** Default state entries for initial tree structure. */\n// TODO(thure): Initialize these dynamically.\nconst defaultStateEntries: [string, NavTreeCapabilities.NavTreeItemState][] = [\n ['root', { open: true, current: false }],\n];\n\nexport default Capability.makeModule(\n Effect.fnUntraced(function* () {\n const registry = yield* Capability.get(Capabilities.AtomRegistry);\n const layoutAtom = yield* Capability.get(AppCapabilities.Layout);\n // Persistence backend for per-path expansion (`open`); replaces the hand-rolled localStorage blob.\n const viewState = yield* Capability.get(AttentionCapabilities.ViewState);\n\n // Mirror of the layout's active planks. An item registers its path only on its first render, which\n // can happen long after the layout change that made it current, so entries derive `current` from\n // this at creation time rather than waiting for the next layout notification.\n let activeIds: readonly string[] = registry.get(layoutAtom).active;\n\n /** Item state for a path not seen before: `current` follows the layout, `open` starts closed. */\n const initialItemState = (pathString: string): NavTreeCapabilities.NavTreeItemState => ({\n open: defaultOpen,\n current: activeIds.includes(Path.last(pathString)),\n });\n\n // Backing state (not reactive), seeded from the persisted `open` values; `current` is ephemeral.\n const persistedPaths = viewState.contexts(navTreeOpenAspect);\n const backingState: Map<string, NavTreeCapabilities.NavTreeItemState> =\n persistedPaths.length === 0\n ? new Map(defaultStateEntries)\n : new Map(\n persistedPaths.map((pathString) => [\n pathString,\n {\n open: viewState.get(navTreeOpenAspect, pathString).open,\n current: activeIds.includes(Path.last(pathString)),\n },\n ]),\n );\n\n // Per-path atom family for fine-grained reactivity.\n // keepAlive prevents atoms from being garbage collected when components unmount,\n // ensuring state is preserved across deletion/restoration cycles.\n const itemAtomFamily = Atom.family((pathString: string) =>\n Atom.make<NavTreeCapabilities.NavTreeItemState>(\n backingState.get(pathString) ?? initialItemState(pathString),\n ).pipe(Atom.keepAlive),\n );\n\n const getItemAtom = (path: string[]): Atom.Atom<NavTreeCapabilities.NavTreeItemState> => {\n const pathString = Path.create(...path);\n if (!backingState.has(pathString)) {\n backingState.set(pathString, initialItemState(pathString));\n }\n return itemAtomFamily(pathString);\n };\n\n const getItem = (path: string[]): NavTreeCapabilities.NavTreeItemState => {\n return registry.get(getItemAtom(path));\n };\n\n const setItem = (path: string[], key: 'open' | 'current', next: boolean) => {\n const pathString = Path.create(...path);\n const atom = itemAtomFamily(pathString);\n const currentValue = registry.get(atom);\n const newValue = { ...currentValue, [key]: next };\n\n registry.set(atom, newValue);\n // Track every touched path so the layout subscription can find it; only `open` is durable, and\n // top-level workspace paths are excluded (their expansion is not part of the tree model).\n backingState.set(pathString, newValue);\n if (key === 'open' && !isTopLevelPath(path)) {\n viewState.set(navTreeOpenAspect, pathString, { open: next });\n }\n };\n\n // Subscribe to layout changes to update current state.\n const unsubscribe = registry.subscribe(layoutAtom, (layout) => {\n const removed = activeIds.filter((id) => !layout.active.includes(id));\n activeIds = layout.active;\n\n const handleUpdate = () => {\n // Mark removed items as not current.\n removed.forEach((id) => {\n const keys = Array.from(backingState.keys()).filter((key) => Path.last(key) === id);\n keys.forEach((key) => {\n setItem(Path.parts(key), 'current', false);\n });\n });\n\n // Mark active items as current.\n layout.active.forEach((id: string) => {\n const keys = Array.from(new Set([...backingState.keys(), id])).filter((key) => Path.last(key) === id);\n keys.forEach((key) => {\n setItem(Path.parts(key), 'current', true);\n });\n });\n };\n\n // Deferred only far enough to leave the layout notification (writing item atoms synchronously here\n // would set state during the tree's render pass). Items whose path is not registered yet no longer\n // need waiting out — they seed `current` from `activeIds` when they register.\n queueMicrotask(handleUpdate);\n });\n\n // Once graph is ready, expand every node marked open in state so the graph has children loaded for rendering.\n yield* Effect.gen(function* () {\n const { graph } = yield* Capability.waitFor(AppCapabilities.AppGraph);\n\n // Always expand the active workspace so its subtree is initialized.\n const layout = registry.get(layoutAtom);\n if (layout.workspace) {\n Graph.expand(graph, layout.workspace, 'child');\n }\n\n // Expand persisted open nodes, skipping inactive workspace tabs.\n const openPaths = Array.from(backingState.entries())\n .filter(([, state]) => state.open)\n .map(([pathString]) => Path.parts(pathString))\n .filter((path) => !isTopLevelPath(path));\n for (const path of openPaths) {\n const nodeId = path[path.length - 1];\n if (!nodeId) {\n continue;\n }\n Graph.expand(graph, nodeId, 'child');\n }\n }).pipe(Effect.forkDaemon);\n\n return Capability.contributes(\n NavTreeCapabilities.State,\n {\n getItem,\n getItemAtom,\n setItem,\n },\n () => Effect.sync(() => unsubscribe()),\n );\n }),\n);\n"],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { Capability } from "@dxos/app-framework";
2
+ import { meta } from "#meta";
3
+ import { AppActivationEvents } from "@dxos/app-toolkit";
4
+ //#region \0rolldown/runtime.js
5
+ var __defProp = Object.defineProperty;
6
+ var __exportAll = (all, no_symbols) => {
7
+ let target = {};
8
+ for (var name in all) __defProp(target, name, {
9
+ get: all[name],
10
+ enumerable: true
11
+ });
12
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
13
+ return target;
14
+ };
15
+ //#endregion
16
+ //#region src/types/NavTreeCapabilities.ts
17
+ var NavTreeCapabilities_exports = /* @__PURE__ */ __exportAll({ State: () => State });
18
+ var State = Capability.make(`${meta.profile.key}.capability.state`);
19
+ //#endregion
20
+ //#region src/types/NavTreeEvents.ts
21
+ var NavTreeEvents_exports = /* @__PURE__ */ __exportAll({ StateReady: () => StateReady });
22
+ var StateReady = AppActivationEvents.createStateEvent(meta.profile.key);
23
+ //#endregion
24
+ export { __exportAll as i, NavTreeCapabilities_exports as n, State as r, NavTreeEvents_exports as t };
25
+
26
+ //# sourceMappingURL=chunk-types.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-types.mjs","names":[],"sources":["../../src/types/NavTreeCapabilities.ts","../../src/types/NavTreeEvents.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\n// @import-as-namespace\n\nimport { type Atom } from '@effect-atom/atom';\n\nimport { Capability } from '@dxos/app-framework';\n\nimport { meta } from '#meta';\n\nexport type NavTreeItemState = { open: boolean; current: boolean };\n\nexport type NavTreeStateStore = {\n getItem: (path: string[]) => NavTreeItemState;\n getItemAtom: (path: string[]) => Atom.Atom<NavTreeItemState>;\n setItem: (path: string[], key: 'open' | 'current', next: boolean) => void;\n};\n\nexport const State = Capability.make<NavTreeStateStore>(`${meta.profile.key}.capability.state`);\n","//\n// Copyright 2025 DXOS.org\n//\n\n// @import-as-namespace\n\nimport { type ActivationEvent } from '@dxos/app-framework';\nimport { AppActivationEvents } from '@dxos/app-toolkit';\n\nimport { meta } from '#meta';\n\nexport const StateReady: ActivationEvent.ActivationEvent = AppActivationEvents.createStateEvent(meta.profile.key);\n"],"mappings":";;;;;;;;;;;;;;;;;AAoBA,IAAa,QAAQ,WAAW,KAAwB,GAAG,KAAK,QAAQ,IAAI,kBAAkB;;;;ACT9F,IAAa,aAA8C,oBAAoB,iBAAiB,KAAK,QAAQ,GAAG"}
@@ -0,0 +1,51 @@
1
+ import { isNonNullable } from "@dxos/util";
2
+ import { Graph, Node } from "@dxos/plugin-graph";
3
+ //#region src/util.ts
4
+ var getParent = (graph, node, path) => {
5
+ const parentId = path[path.length - 2];
6
+ return Graph.getConnections(graph, node.id, Node.childRelation("inbound")).find((n) => n.id === parentId);
7
+ };
8
+ var getPersistenceParent = (graph, node, path, persistenceClass) => {
9
+ if (node.properties.acceptPersistenceClass?.has(persistenceClass)) return node;
10
+ else {
11
+ const parent = getParent(graph, node, path);
12
+ return parent ? getPersistenceParent(graph, parent, path.slice(0, path.length - 1), persistenceClass) : null;
13
+ }
14
+ };
15
+ var resolveMigrationOperation = (graph, activeNode, destinationPath, destinationRelatedNode) => {
16
+ const activeClass = activeNode.properties.persistenceClass;
17
+ if (destinationRelatedNode && activeClass) {
18
+ const persistenceParent = getPersistenceParent(graph, destinationRelatedNode, destinationPath, activeClass);
19
+ if (persistenceParent) {
20
+ const activeKey = activeNode.properties.persistenceKey;
21
+ if (activeKey && persistenceParent?.properties.acceptPersistenceKey) return persistenceParent.properties.acceptPersistenceKey.has(activeKey) && persistenceParent.properties.onTransferStart ? "transfer" : persistenceParent.properties.onCopy ? "copy" : "reject";
22
+ else return "reject";
23
+ } else return "reject";
24
+ } else return "reject";
25
+ };
26
+ var sortActions = (actions) => actions.sort((a, b) => {
27
+ const aPrimary = Node.hasDisposition(a, "list-item-primary");
28
+ if (aPrimary === Node.hasDisposition(b, "list-item-primary")) return 0;
29
+ return aPrimary ? -1 : 1;
30
+ });
31
+ var getChildren = (graph, node, path = []) => {
32
+ return Graph.getConnections(graph, node.id, "child").map((n) => {
33
+ return [...path, node.id].includes(n.id) ? void 0 : n;
34
+ }).filter(isNonNullable);
35
+ };
36
+ /**
37
+ * Determines whether a node should be visible based on its disposition.
38
+ */
39
+ var filterItems = (node, disposition) => {
40
+ if (!disposition && Node.hasDisposition(node, "hidden")) return false;
41
+ else if (!disposition) return !Node.isAction(node) || Node.hasDisposition(node, "item");
42
+ else return Node.hasDisposition(node, disposition);
43
+ };
44
+ var l0ItemType = (item) => {
45
+ if (Node.isActionLike(item)) return "action";
46
+ else return "tab";
47
+ };
48
+ //#endregion
49
+ export { l0ItemType as a, getPersistenceParent as i, getChildren as n, resolveMigrationOperation as o, getParent as r, sortActions as s, filterItems as t };
50
+
51
+ //# sourceMappingURL=chunk-util.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-util.mjs","names":[],"sources":["../../src/util.ts"],"sourcesContent":["//\n// Copyright 2023 DXOS.org\n//\n\nimport { Graph, Node } from '@dxos/plugin-graph';\nimport { isNonNullable } from '@dxos/util';\n\nimport { type NavTreeItemGraphNode } from '#types';\n\nexport const getParent = (\n graph: Graph.ReadableGraph,\n node: NavTreeItemGraphNode,\n path: string[],\n): NavTreeItemGraphNode | undefined => {\n const parentId = path[path.length - 2];\n return Graph.getConnections(graph, node.id, Node.childRelation('inbound')).find(\n (n: Node.Node) => n.id === parentId,\n ) as NavTreeItemGraphNode | undefined;\n};\n\nexport const getPersistenceParent = (\n graph: Graph.ReadableGraph,\n node: NavTreeItemGraphNode,\n path: string[],\n persistenceClass: string,\n): NavTreeItemGraphNode | null => {\n if (node.properties.acceptPersistenceClass?.has(persistenceClass)) {\n return node;\n } else {\n const parent = getParent(graph, node, path);\n return parent ? getPersistenceParent(graph, parent, path.slice(0, path.length - 1), persistenceClass) : null;\n }\n};\n\nexport const resolveMigrationOperation = (\n graph: Graph.ReadableGraph,\n activeNode: NavTreeItemGraphNode,\n destinationPath: string[],\n destinationRelatedNode?: NavTreeItemGraphNode,\n): 'transfer' | 'copy' | 'reject' => {\n const activeClass = activeNode.properties.persistenceClass;\n if (destinationRelatedNode && activeClass) {\n const persistenceParent = getPersistenceParent(graph, destinationRelatedNode, destinationPath, activeClass);\n if (persistenceParent) {\n const activeKey = activeNode.properties.persistenceKey;\n if (activeKey && persistenceParent?.properties.acceptPersistenceKey) {\n return persistenceParent.properties.acceptPersistenceKey.has(activeKey) &&\n persistenceParent.properties.onTransferStart\n ? 'transfer'\n : persistenceParent.properties.onCopy\n ? 'copy'\n : 'reject';\n } else {\n return 'reject';\n }\n } else {\n return 'reject';\n }\n } else {\n return 'reject';\n }\n};\n\n// TODO(wittjosiah): Move into node implementation?\nexport const sortActions = (actions: Node.Action[]): Node.Action[] =>\n actions.sort((a, b) => {\n const aPrimary = Node.hasDisposition(a, 'list-item-primary');\n const bPrimary = Node.hasDisposition(b, 'list-item-primary');\n if (aPrimary === bPrimary) {\n return 0;\n }\n\n return aPrimary ? -1 : 1;\n });\n\nexport const getChildren = (\n graph: Graph.ReadableGraph,\n node: NavTreeItemGraphNode,\n path: readonly string[] = [],\n): NavTreeItemGraphNode[] => {\n return Graph.getConnections(graph, node.id, 'child')\n .map((n: Node.Node) => {\n // Break cycles.\n const nextPath = [...path, node.id];\n return nextPath.includes(n.id) ? undefined : (n as NavTreeItemGraphNode);\n })\n .filter(isNonNullable) as NavTreeItemGraphNode[];\n};\n\n/**\n * Determines whether a node should be visible based on its disposition.\n */\nexport const filterItems = (node: Node.Node, disposition?: string) => {\n if (!disposition && Node.hasDisposition(node, 'hidden')) {\n return false;\n } else if (!disposition) {\n const action = Node.isAction(node);\n return !action || Node.hasDisposition(node, 'item');\n } else {\n return Node.hasDisposition(node, disposition);\n }\n};\n\nexport const l0ItemType = (item: Node.Node) => {\n if (Node.isActionLike(item)) {\n return 'action';\n } else {\n return 'tab';\n }\n};\n"],"mappings":";;;AASA,IAAa,aACX,OACA,MACA,SACqC;CACrC,MAAM,WAAW,KAAK,KAAK,SAAS;CACpC,OAAO,MAAM,eAAe,OAAO,KAAK,IAAI,KAAK,cAAc,SAAS,CAAC,CAAC,CAAC,MACxE,MAAiB,EAAE,OAAO,QAC7B;AACF;AAEA,IAAa,wBACX,OACA,MACA,MACA,qBACgC;CAChC,IAAI,KAAK,WAAW,wBAAwB,IAAI,gBAAgB,GAC9D,OAAO;MACF;EACL,MAAM,SAAS,UAAU,OAAO,MAAM,IAAI;EAC1C,OAAO,SAAS,qBAAqB,OAAO,QAAQ,KAAK,MAAM,GAAG,KAAK,SAAS,CAAC,GAAG,gBAAgB,IAAI;CAC1G;AACF;AAEA,IAAa,6BACX,OACA,YACA,iBACA,2BACmC;CACnC,MAAM,cAAc,WAAW,WAAW;CAC1C,IAAI,0BAA0B,aAAa;EACzC,MAAM,oBAAoB,qBAAqB,OAAO,wBAAwB,iBAAiB,WAAW;EAC1G,IAAI,mBAAmB;GACrB,MAAM,YAAY,WAAW,WAAW;GACxC,IAAI,aAAa,mBAAmB,WAAW,sBAC7C,OAAO,kBAAkB,WAAW,qBAAqB,IAAI,SAAS,KACpE,kBAAkB,WAAW,kBAC3B,aACA,kBAAkB,WAAW,SAC3B,SACA;QAEN,OAAO;EAEX,OACE,OAAO;CAEX,OACE,OAAO;AAEX;AAGA,IAAa,eAAe,YAC1B,QAAQ,MAAM,GAAG,MAAM;CACrB,MAAM,WAAW,KAAK,eAAe,GAAG,mBAAmB;CAE3D,IAAI,aADa,KAAK,eAAe,GAAG,mBACvB,GACf,OAAO;CAGT,OAAO,WAAW,KAAK;AACzB,CAAC;AAEH,IAAa,eACX,OACA,MACA,OAA0B,CAAC,MACA;CAC3B,OAAO,MAAM,eAAe,OAAO,KAAK,IAAI,OAAO,CAAA,CAChD,KAAK,MAAiB;EAGrB,OAAO,CADW,GAAG,MAAM,KAAK,EACzB,CAAA,CAAS,SAAS,EAAE,EAAE,IAAI,KAAA,IAAa;CAChD,CAAC,CAAA,CACA,OAAO,aAAa;AACzB;;;;AAKA,IAAa,eAAe,MAAiB,gBAAyB;CACpE,IAAI,CAAC,eAAe,KAAK,eAAe,MAAM,QAAQ,GACpD,OAAO;MACF,IAAI,CAAC,aAEV,OAAO,CADQ,KAAK,SAAS,IACrB,KAAU,KAAK,eAAe,MAAM,MAAM;MAElD,OAAO,KAAK,eAAe,MAAM,WAAW;AAEhD;AAEA,IAAa,cAAc,SAAoB;CAC7C,IAAI,KAAK,aAAa,IAAI,GACxB,OAAO;MAEP,OAAO;AAEX"}