@dxos/plugin-stack 0.12.0-next-2800d0376abcc62d5544afdd744b16b2b960434e → 0.13.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 (68) hide show
  1. package/PLUGIN.mdl +72 -9
  2. package/dist/lib/capabilities.browser.mjs +18 -0
  3. package/dist/lib/capabilities.browser.mjs.map +1 -0
  4. package/dist/lib/capabilities.mjs +2 -2
  5. package/dist/lib/capabilities.mjs.map +1 -1
  6. package/dist/lib/capabilities.node.mjs.map +1 -1
  7. package/dist/lib/capabilities.tauri.mjs +18 -0
  8. package/dist/lib/capabilities.tauri.mjs.map +1 -0
  9. package/dist/lib/capabilities.workerd.mjs.map +1 -1
  10. package/dist/lib/chunk-PLUGIN.mjs +6 -0
  11. package/dist/lib/chunk-PLUGIN.mjs.map +1 -0
  12. package/dist/lib/chunk-StackArticle.mjs +16 -17
  13. package/dist/lib/chunk-StackArticle.mjs.map +1 -1
  14. package/dist/lib/chunk-react-surface.mjs +2 -2
  15. package/dist/lib/chunk-react-surface.mjs.map +1 -1
  16. package/dist/lib/components.mjs +62 -52
  17. package/dist/lib/components.mjs.map +1 -1
  18. package/dist/lib/containers.mjs.map +1 -1
  19. package/dist/lib/index.mjs +0 -1
  20. package/dist/lib/meta.mjs.map +1 -1
  21. package/dist/lib/translations.mjs.map +1 -1
  22. package/dist/types/src/capabilities/gen/browser.d.ts +5 -0
  23. package/dist/types/src/capabilities/gen/browser.d.ts.map +1 -0
  24. package/dist/types/src/capabilities/gen/node.d.ts.map +1 -1
  25. package/dist/types/src/capabilities/gen/tauri.d.ts +5 -0
  26. package/dist/types/src/capabilities/gen/tauri.d.ts.map +1 -0
  27. package/dist/types/src/capabilities/gen/workerd.d.ts.map +1 -1
  28. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  29. package/dist/types/src/components/Stack/Stack.d.ts +4 -13
  30. package/dist/types/src/components/Stack/Stack.d.ts.map +1 -1
  31. package/dist/types/src/components/Stack/StackContext.d.ts +1 -1
  32. package/dist/types/src/components/Stack/StackContext.d.ts.map +1 -1
  33. package/dist/types/src/components/Stack/index.d.ts +2 -2
  34. package/dist/types/src/components/Stack/index.d.ts.map +1 -1
  35. package/dist/types/src/components/index.d.ts +1 -1
  36. package/dist/types/src/components/index.d.ts.map +1 -1
  37. package/dist/types/src/containers/StackArticle/StackArticle.d.ts +1 -1
  38. package/dist/types/src/containers/StackArticle/StackArticle.d.ts.map +1 -1
  39. package/dist/types/src/containers/StackArticle/StackArticle.stories.d.ts +1 -1
  40. package/dist/types/src/containers/StackArticle/StackArticle.stories.d.ts.map +1 -1
  41. package/dist/types/src/containers/StackArticle/index.d.ts +1 -1
  42. package/dist/types/src/containers/StackArticle/index.d.ts.map +1 -1
  43. package/dist/types/src/containers/index.d.ts.map +1 -1
  44. package/dist/types/src/index.d.ts +1 -2
  45. package/dist/types/src/index.d.ts.map +1 -1
  46. package/dist/types/src/translations.d.ts.map +1 -1
  47. package/dist/types/src/types/index.d.ts +2 -2
  48. package/dist/types/src/types/index.d.ts.map +1 -1
  49. package/dist/types/tsconfig.tsbuildinfo +1 -1
  50. package/package.json +34 -32
  51. package/src/capabilities/gen/browser.ts +27 -0
  52. package/src/capabilities/gen/node.ts +1 -1
  53. package/src/capabilities/gen/tauri.ts +27 -0
  54. package/src/capabilities/gen/workerd.ts +1 -1
  55. package/src/capabilities/index.ts +2 -2
  56. package/src/capabilities/react-surface.ts +2 -2
  57. package/src/components/Stack/Stack.tsx +67 -62
  58. package/src/components/Stack/StackContext.ts +1 -1
  59. package/src/components/Stack/index.ts +2 -2
  60. package/src/components/index.ts +1 -1
  61. package/src/containers/StackArticle/StackArticle.stories.tsx +4 -4
  62. package/src/containers/StackArticle/StackArticle.tsx +24 -26
  63. package/src/containers/StackArticle/index.ts +1 -1
  64. package/src/containers/index.ts +1 -1
  65. package/src/index.ts +1 -2
  66. package/src/meta.ts +1 -1
  67. package/src/translations.ts +2 -2
  68. package/src/types/index.ts +2 -2
package/PLUGIN.mdl CHANGED
@@ -15,9 +15,10 @@ Each extension is defined in the Appendix or resolved via its URI.
15
15
  |-------------|--------------------------------|
16
16
  | `type` | `org.dxos.mdl.type@1.0` |
17
17
  | `feat` | `org.dxos.mdl.feat@1.0` |
18
- | `test` | `org.dxos.mdl.test@1.0` |
18
+ | `scenario` | `org.dxos.mdl.scenario@1.0` |
19
19
  | `component` | `org.dxos.mdl.component@1.0` |
20
20
  | `op` | `org.dxos.mdl.op@1.0` |
21
+ | `test` | `org.dxos.mdl.test@1.0` |
21
22
 
22
23
  ## Types
23
24
 
@@ -212,7 +213,7 @@ feat F-5: Schema Registration
212
213
  ## Acceptance
213
214
 
214
215
  ```mdl
215
- test T-1: Collection renders as stack
216
+ scenario T-1: Collection renders as stack
216
217
  given: a Collection with three objects is opened in article role
217
218
  then:
218
219
  - StackArticle is displayed
@@ -221,14 +222,14 @@ test T-1: Collection renders as stack
221
222
  ```
222
223
 
223
224
  ```mdl
224
- test T-2: Add section via toolbar
225
+ scenario T-2: Add section via toolbar
225
226
  given: StackArticle is open
226
227
  when: user clicks the Add Section toolbar button
227
228
  then: SpaceOperation.OpenObjectForm is invoked with the collection as target
228
229
  ```
229
230
 
230
231
  ```mdl
231
- test T-3: Remove section
232
+ scenario T-3: Remove section
232
233
  given: StackArticle is open with at least one section
233
234
  when: user selects Remove from a section dropdown
234
235
  then:
@@ -237,7 +238,7 @@ test T-3: Remove section
237
238
  ```
238
239
 
239
240
  ```mdl
240
- test T-4: Collapse section
241
+ scenario T-4: Collapse section
241
242
  given: a section is expanded
242
243
  when: user selects Collapse from the section dropdown
243
244
  then:
@@ -247,20 +248,82 @@ test T-4: Collapse section
247
248
  ```
248
249
 
249
250
  ```mdl
250
- test T-5: Navigate to section
251
+ scenario T-5: Navigate to section
251
252
  given: StackArticle is open
252
253
  when: user clicks the navigate button on a section heading
253
254
  then: LayoutOperation.Open is invoked with the section's DXN id
254
255
  ```
255
256
 
256
257
  ```mdl
257
- test T-6: StackViewType registered
258
+ scenario T-6: StackViewType registered
258
259
  given: StackPlugin is active
259
260
  then: StackViewType (typename org.dxos.type.stackView) is registered with the ECHO schema module
260
261
  ```
261
262
 
262
263
  ---
263
264
 
265
+ ## QA
266
+
267
+ Executable tests. Each step carries a human instruction (`do`) and, where the step maps to an
268
+ invocable operation, an agent binding (`invoke`). Report failures by test and step number.
269
+
270
+ ```mdl
271
+ test QA-1: Render a collection as a stack, add, collapse, and remove a section
272
+ desc: |
273
+ StackPlugin declares no operations of its own — every mutation goes through
274
+ `SpaceOperation.AddObject`/`RemoveObjects` (F-2.1, F-2.3), which belong to `plugin-space`.
275
+ actors: both
276
+ covers: [F-1.1, F-1.2, F-2.3, F-3.1, T-1, T-3, T-4]
277
+ status: unverified
278
+ given:
279
+ - an identity exists
280
+ - space: a space is open
281
+ - plugin-stack is enabled — it is OFF by default for everyone (not listed in
282
+ `getDefaults`, unlike most content plugins in `plugin-defs.tsx`), so no `article` surface
283
+ resolves for a Collection's stack view until it is turned on
284
+ - no collection named "QA Stack" exists — ABORT rather than deleting one this test did not
285
+ create
286
+ steps:
287
+ - name: Create the collection and a section
288
+ do: In the navtree, click + on the space and choose Collection. Name it "QA Stack". Open
289
+ it, click "Add Section" in the toolbar, and choose Markdown, naming it "QA Section".
290
+ invoke: org.dxos.operation.space.createCollection { name: "QA Stack" }
291
+ capture: collection
292
+ expect: "QA Stack" opens as a StackArticle with one StackSection titled "QA Section"
293
+ assert: return $collection.object.name === 'QA Stack';
294
+ note: |
295
+ Coalesce the "Add Section" click with the object creation it triggers — `OpenObjectForm`
296
+ opens a dialog and the resulting object is added via `AddObject`, which is the same
297
+ pattern as `plugin-markdown`'s create+place split.
298
+ - name: Add the section object
299
+ do: (the Add Section dialog places it for you on confirm)
300
+ invoke: org.dxos.operation.markdown.create { name: "QA Section" }
301
+ capture: doc
302
+ expect: a Document named "QA Section" exists
303
+ - name: Place the section in the stack
304
+ do: (as above)
305
+ invoke: org.dxos.operation.space.addObject { object: $doc.object, target: $collection.object }
306
+ expect: a StackSection titled "QA Section" now renders below the toolbar (F-1.2)
307
+ assert: return $collection.object.objects.some((ref) => ref.target?.id === $doc.object.id);
308
+ - name: Collapse the section
309
+ do: Open the section's sigil dropdown and choose Collapse.
310
+ expect: the section shows only its title; the caret points to "expand" (F-3.3, T-4)
311
+ note: NO invoke — collapse state is local StackArticle component state (F-3.4), not an
312
+ ECHO write, so there is nothing to verify against the database.
313
+ - name: Remove the section
314
+ do: Open the dropdown again and choose Remove.
315
+ invoke: org.dxos.operation.space.removeObjects { objects: [$doc.object], target: $collection.object }
316
+ expect: the section no longer appears in the stack (T-3)
317
+ assert: return !$collection.object.objects.some((ref) => ref.target?.id === $doc.object.id);
318
+ after:
319
+ - name: Delete the collection
320
+ do: Right-click "QA Stack" in the navtree and choose Delete.
321
+ invoke: org.dxos.operation.space.removeObjects { objects: [$collection.object], target: $given.space }
322
+ expect: the row leaves the navtree and its plank closes
323
+ ```
324
+
325
+ ---
326
+
264
327
  ## Appendix: Extension Definitions
265
328
 
266
329
  ```mdl
@@ -285,8 +348,8 @@ ext feat
285
348
  ```
286
349
 
287
350
  ```mdl
288
- ext test
289
- uri: org.dxos.mdl.test@1.0
351
+ ext scenario
352
+ uri: org.dxos.mdl.scenario@1.0
290
353
  desc: An acceptance scenario expressed as given / when / then steps.
291
354
  fields:
292
355
  given?: Step | Step[]
@@ -0,0 +1,18 @@
1
+ import { t as PLUGIN_default } from "./chunk-PLUGIN.mjs";
2
+ import * as AppCapability from "@dxos/app-toolkit/AppCapability";
3
+ import { translations } from "#translations";
4
+ import { meta } from "#meta";
5
+ //#region src/capabilities/gen/browser.ts
6
+ var PluginAsset = AppCapability.pluginAsset({
7
+ pluginId: meta.profile.key,
8
+ path: "PLUGIN.mdl",
9
+ content: PLUGIN_default,
10
+ mimeType: "application/x-mdl"
11
+ });
12
+ var ReactSurface = AppCapability.surface(() => import("./chunk-react-surface.mjs"), { roles: ["org.dxos.role.article"] });
13
+ var Schema = AppCapability.schema(() => import("./chunk-schema.mjs"));
14
+ var Translations = AppCapability.translations(translations);
15
+ //#endregion
16
+ export { PluginAsset, ReactSurface, Schema, Translations };
17
+
18
+ //# sourceMappingURL=capabilities.browser.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.browser.mjs","names":[],"sources":["../../src/capabilities/gen/browser.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'browser' condition.\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\n\nimport { meta } from '#meta';\nimport { translations } from '#translations';\n\nimport pluginSpec from '../../../PLUGIN.mdl?raw';\n\nexport const PluginAsset = AppCapability.pluginAsset({\n pluginId: meta.profile.key,\n path: 'PLUGIN.mdl',\n content: pluginSpec,\n mimeType: 'application/x-mdl',\n});\n\nexport const ReactSurface = AppCapability.surface(() => import('../react-surface.ts'), {\n roles: ['org.dxos.role.article'],\n});\n\nexport const Schema = AppCapability.schema(() => import('../schema.ts'));\n\nexport const Translations = AppCapability.translations(translations);\n"],"mappings":";;;;;AAaA,IAAa,cAAc,cAAc,YAAY;CACnD,UAAU,KAAK,QAAQ;CACvB,MAAM;CACN,SAAS;CACT,UAAU;AACZ,CAAC;AAED,IAAa,eAAe,cAAc,cAAc,OAAO,8BAAwB,EACrF,OAAO,CAAC,uBAAuB,EACjC,CAAC;AAED,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAe;AAEvE,IAAa,eAAe,cAAc,aAAa,YAAY"}
@@ -1,12 +1,12 @@
1
+ import { t as PLUGIN_default } from "./chunk-PLUGIN.mjs";
1
2
  import * as AppCapability from "@dxos/app-toolkit/AppCapability";
2
3
  import { translations } from "#translations";
3
4
  import { meta } from "#meta";
4
- //#endregion
5
5
  //#region src/capabilities/index.ts
6
6
  var PluginAsset = AppCapability.pluginAsset({
7
7
  pluginId: meta.profile.key,
8
8
  path: "PLUGIN.mdl",
9
- content: "---\nid: org.dxos.plugin.stack\nname: StackPlugin\nversion: 0.1.0\n---\n\nCollections plugin for `DXOS` Composer — renders any `Collection` object as a vertically scrollable stack of collapsible sections, where each section displays an arbitrary ECHO object via the `section` surface role. Supports adding, removing, navigating, and collapsing individual sections.\n\n## Extensions\n\nThe following extension dialects are used in this document.\nEach extension is defined in the Appendix or resolved via its URI.\n\n| Term | URI |\n|-------------|--------------------------------|\n| `type` | `org.dxos.mdl.type@1.0` |\n| `feat` | `org.dxos.mdl.feat@1.0` |\n| `test` | `org.dxos.mdl.test@1.0` |\n| `component` | `org.dxos.mdl.component@1.0` |\n| `op` | `org.dxos.mdl.op@1.0` |\n\n## Types\n\n```mdl\ntype SectionSchema\n fields:\n height?: number # explicit height override for the section\n custom?: Record<string, any> # view-instance-specific data (e.g. cover mode for images)\n```\n\n```mdl\ntype StackViewType\n typename: org.dxos.type.stackView\n version: 0.1.0\n fields:\n sections: Record<string, SectionSchema>\n```\n\n```mdl\ntype StackSectionView\n fields:\n title?: string\n size?: StackItemSize\n height?: number\n collapsed?: boolean\n custom?: Record<string, any>\n```\n\n```mdl\ntype StackSectionMetadata\n fields:\n icon?: string\n placeholder?: Label\n viewActions?: (item: CollectionItem) => StackSectionAction\n```\n\n```mdl\ntype StackSectionItem\n fields:\n id: string # DXN string for the contained object\n object: Obj.Unknown\n view: StackSectionView\n metadata: StackSectionMetadata\n```\n\n```mdl\ntype AddSectionPosition\n literals: before | after | beforeAll | afterAll\n```\n\n```mdl\ntype StackSettingsProps\n fields:\n separation: boolean # whether visual separators are shown between sections\n```\n\n## Components\n\n```mdl\ncomponent StackArticle\n desc: Full-page article view of a Collection — renders items as a vertical stack of StackSection components with a toolbar for adding sections.\n props:\n subject: Collection\n role: AppSurface.Role\n attendableId?: string\n state:\n collapsedSections: Record<string, boolean> # collapsed state by DXN string\n items: StackSectionItem[] # derived from collection objects\n slots:\n (none)\n actions:\n handleAdd(id: string, position: AddSectionPosition)\n handleDelete(id: string)\n handleNavigate(id: string)\n handleCollapse(id: string, collapsed: boolean)\n handleAddSection()\n emits:\n (none)\n layout: |\n ┌──────────────────────────────┐\n │ Toolbar [+ Add Section] │\n ├──────────────────────────────┤\n │ Stack (vertical, intrinsic) │\n │ ┌────────────────────────┐ │\n │ │ StackSection │ │\n │ │ ▾ [icon] Title [→] │ │\n │ │ └─ Surface:section │ │\n │ └────────────────────────┘ │\n │ ┌────────────────────────┐ │\n │ │ StackSection (collapsed│ │\n │ │ ▸ [icon] Title [↕] │ │\n │ └────────────────────────┘ │\n │ … │\n └──────────────────────────────┘\n```\n\n```mdl\ncomponent StackSection\n desc: A single collapsible section in the stack — shows a sigil icon button with a dropdown menu and delegates content rendering to the section surface slot.\n props:\n id: string\n object: Obj.Unknown\n view: StackSectionView\n metadata: StackSectionMetadata\n state:\n optionsMenuOpen: boolean\n attendableId: string # composed as parentAttendableId/object.id\n slots:\n content: ReactNode # rendered by Surface(type=section, subject=object)\n actions:\n navigate(id: string)\n add(id: string, position: AddSectionPosition)\n collapse(id: string, collapsed: boolean)\n delete(id: string)\n layout: |\n ┌─────────────────────────────────┐\n │ StackItem.Heading │\n │ [sigil▾] (sticky) [→ or ↕] │\n ├─────────────────────────────────┤\n │ CollapsibleContent │\n │ Surface type=section │\n └─────────────────────────────────┘\n```\n\n```mdl\ncomponent StackSettings\n desc: Settings panel for configuring stack-level display preferences such as section separation.\n props:\n settings: StackSettingsProps\n layout: |\n ┌─────────────────────────────┐\n │ Settings.Section │\n │ Separation [toggle] │\n └─────────────────────────────┘\n```\n\n## Features\n\n```mdl\nfeat F-1: Stack Rendering\n\n req F-1.1: StackPlugin registers a surface for the article role filtered to Collection objects.\n req F-1.2: StackArticle renders each item in the collection as a StackSection in document order.\n req F-1.3: Each StackSection title is derived from the object's title property or graph node label.\n req F-1.4: The icon displayed on the section sigil is taken from the object schema's IconAnnotation.\n```\n\n```mdl\nfeat F-2: Section Management\n\n req F-2.1:\n when: user clicks the Add Section button in the toolbar\n then: SpaceOperation.OpenObjectForm dialog is invoked with the collection as target\n req F-2.2:\n when: user selects \"Add Before\" or \"Add After\" from a section dropdown\n then: LayoutOperation.UpdateDialog is invoked with the AddSectionDialog, position, and collection context\n req F-2.3:\n when: user selects \"Remove\" from a section dropdown\n then: SpaceOperation.RemoveObjects is invoked; section is removed from the collection\n req F-2.4:\n when: user selects \"Navigate to\" from a collapsed section dropdown or the navigate button\n then: LayoutOperation.Open is invoked with the section's id\n```\n\n```mdl\nfeat F-3: Collapse / Expand\n\n req F-3.1: Each section can be individually collapsed via the dropdown menu or caret trigger.\n req F-3.2:\n when: section is expanded\n then: full Surface:section content is visible; navigate button shown in heading\n req F-3.3:\n when: section is collapsed\n then: only the title is shown in StackItem.Content; expand toggle shown in heading\n req F-3.4: Collapsed state is managed locally in StackArticle component state (not persisted).\n```\n\n```mdl\nfeat F-4: Settings\n\n req F-4.1: StackPlugin exposes a settings panel via the StackSettings component.\n req F-4.2: The separation setting controls whether visual separators appear between sections.\n```\n\n```mdl\nfeat F-5: Schema Registration\n\n req F-5.1: StackPlugin registers StackViewType with the ECHO schema module on activation.\n req F-5.2: StackViewType stores per-section metadata (height, custom data) keyed by object id.\n```\n\n## Acceptance\n\n```mdl\ntest T-1: Collection renders as stack\n given: a Collection with three objects is opened in article role\n then:\n - StackArticle is displayed\n - three StackSection components are rendered in order\n - each section shows the object's title\n```\n\n```mdl\ntest T-2: Add section via toolbar\n given: StackArticle is open\n when: user clicks the Add Section toolbar button\n then: SpaceOperation.OpenObjectForm is invoked with the collection as target\n```\n\n```mdl\ntest T-3: Remove section\n given: StackArticle is open with at least one section\n when: user selects Remove from a section dropdown\n then:\n - SpaceOperation.RemoveObjects is invoked\n - section no longer appears in the stack\n```\n\n```mdl\ntest T-4: Collapse section\n given: a section is expanded\n when: user selects Collapse from the section dropdown\n then:\n - section content is hidden\n - section title is shown in collapsed state\n - expand toggle is visible in heading\n```\n\n```mdl\ntest T-5: Navigate to section\n given: StackArticle is open\n when: user clicks the navigate button on a section heading\n then: LayoutOperation.Open is invoked with the section's DXN id\n```\n\n```mdl\ntest T-6: StackViewType registered\n given: StackPlugin is active\n then: StackViewType (typename org.dxos.type.stackView) is registered with the ECHO schema module\n```\n\n---\n\n## Appendix: Extension Definitions\n\n```mdl\next type\n uri: org.dxos.mdl.type@1.0\n desc: A named data structure with typed fields and optional literals.\n fields:\n desc?: Prose\n fields?: FieldMap\n literals?: UnionList\n extends?: TypeRef[]\n```\n\n```mdl\next feat\n uri: org.dxos.mdl.feat@1.0\n desc: A named feature grouping one or more requirements.\n fields:\n desc?: Prose\n req: RequirementList\n nesting: self\n```\n\n```mdl\next test\n uri: org.dxos.mdl.test@1.0\n desc: An acceptance scenario expressed as given / when / then steps.\n fields:\n given?: Step | Step[]\n when?: Step | Step[]\n then: Step | Step[]\n tags?: TagList\n```\n\n```mdl\next component\n uri: org.dxos.mdl.component@1.0\n desc: A UI component with props, internal state, slots, actions, and events.\n fields:\n desc?: Prose\n props?: FieldMap\n state?: FieldMap\n slots?: FieldMap\n actions?: ActionMap\n emits?: EventMap\n layout?: CodeBlock\n```\n\n```mdl\next op\n uri: org.dxos.mdl.op@1.0\n desc: |\n A named operation with typed inputs, outputs, and declared errors.\n Pure ops have no effects or requires. Effectful ops declare both.\n fields:\n desc?: Prose\n input?: FieldMap\n output?: TypeExpr\n errors?: ErrorMap\n effects?: EffectList\n requires?: ServiceList\n note?: Prose\n```\n",
9
+ content: PLUGIN_default,
10
10
  mimeType: "application/x-mdl"
11
11
  });
12
12
  var ReactSurface = AppCapability.surface(() => import("./chunk-react-surface.mjs"), { roles: ["org.dxos.role.article"] });
@@ -1 +1 @@
1
- {"version":3,"file":"capabilities.mjs","names":[],"sources":["../../src/capabilities/index.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\n\nimport { meta } from '#meta';\nimport { translations } from '#translations';\n\n// eslint-disable-next-line import/no-relative-packages\nimport pluginSpec from '../../PLUGIN.mdl?raw';\n\nexport const PluginAsset = AppCapability.pluginAsset({\n pluginId: meta.profile.key,\n path: 'PLUGIN.mdl',\n content: pluginSpec,\n mimeType: 'application/x-mdl',\n});\nexport const ReactSurface = AppCapability.surface(() => import('./react-surface'), {\n roles: ['org.dxos.role.article'],\n});\nexport const Schema = AppCapability.schema(() => import('./schema'));\nexport const Translations = AppCapability.translations(translations);\n"],"mappings":";;;;;AAYA,IAAa,cAAc,cAAc,YAAY;CACnD,UAAU,KAAK,QAAQ;CACvB,MAAM;CACN,SAAS;CACT,UAAU;AACZ,CAAC;AACD,IAAa,eAAe,cAAc,cAAc,OAAO,8BAAoB,EACjF,OAAO,CAAC,uBAAuB,EACjC,CAAC;AACD,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAW;AACnE,IAAa,eAAe,cAAc,aAAa,YAAY"}
1
+ {"version":3,"file":"capabilities.mjs","names":[],"sources":["../../src/capabilities/index.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\n\nimport { meta } from '#meta';\nimport { translations } from '#translations';\n\n// eslint-disable-next-line import/no-relative-packages\nimport pluginSpec from '../../PLUGIN.mdl?raw';\n\nexport const PluginAsset = AppCapability.pluginAsset({\n pluginId: meta.profile.key,\n path: 'PLUGIN.mdl',\n content: pluginSpec,\n mimeType: 'application/x-mdl',\n});\nexport const ReactSurface = AppCapability.surface(() => import('./react-surface.ts'), {\n roles: ['org.dxos.role.article'],\n});\nexport const Schema = AppCapability.schema(() => import('./schema.ts'));\nexport const Translations = AppCapability.translations(translations);\n"],"mappings":";;;;;AAYA,IAAa,cAAc,cAAc,YAAY;CACnD,UAAU,KAAK,QAAQ;CACvB,MAAM;CACN,SAAS;CACT,UAAU;AACZ,CAAC;AACD,IAAa,eAAe,cAAc,cAAc,OAAO,8BAAuB,EACpF,OAAO,CAAC,uBAAuB,EACjC,CAAC;AACD,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAc;AACtE,IAAa,eAAe,cAAc,aAAa,YAAY"}
@@ -1 +1 @@
1
- {"version":3,"file":"capabilities.node.mjs","names":[],"sources":["../../src/capabilities/gen/node.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'node' condition.\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\n\nimport { translations } from '#translations';\n\nexport const Schema = AppCapability.schema(() => import('../schema'));\n\nexport const Translations = AppCapability.translations(translations);\n\nexport const PluginAsset = undefined;\nexport const ReactSurface = undefined;\n"],"mappings":";;;AAUA,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAY;AAEpE,IAAa,eAAe,cAAc,aAAa,YAAY;AAEnE,IAAa,cAAc,KAAA;AAC3B,IAAa,eAAe,KAAA"}
1
+ {"version":3,"file":"capabilities.node.mjs","names":[],"sources":["../../src/capabilities/gen/node.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'node' condition.\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\n\nimport { translations } from '#translations';\n\nexport const Schema = AppCapability.schema(() => import('../schema.ts'));\n\nexport const Translations = AppCapability.translations(translations);\n\nexport const PluginAsset = undefined;\nexport const ReactSurface = undefined;\n"],"mappings":";;;AAUA,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAe;AAEvE,IAAa,eAAe,cAAc,aAAa,YAAY;AAEnE,IAAa,cAAc,KAAA;AAC3B,IAAa,eAAe,KAAA"}
@@ -0,0 +1,18 @@
1
+ import { t as PLUGIN_default } from "./chunk-PLUGIN.mjs";
2
+ import * as AppCapability from "@dxos/app-toolkit/AppCapability";
3
+ import { translations } from "#translations";
4
+ import { meta } from "#meta";
5
+ //#region src/capabilities/gen/tauri.ts
6
+ var PluginAsset = AppCapability.pluginAsset({
7
+ pluginId: meta.profile.key,
8
+ path: "PLUGIN.mdl",
9
+ content: PLUGIN_default,
10
+ mimeType: "application/x-mdl"
11
+ });
12
+ var ReactSurface = AppCapability.surface(() => import("./chunk-react-surface.mjs"), { roles: ["org.dxos.role.article"] });
13
+ var Schema = AppCapability.schema(() => import("./chunk-schema.mjs"));
14
+ var Translations = AppCapability.translations(translations);
15
+ //#endregion
16
+ export { PluginAsset, ReactSurface, Schema, Translations };
17
+
18
+ //# sourceMappingURL=capabilities.tauri.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.tauri.mjs","names":[],"sources":["../../src/capabilities/gen/tauri.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'tauri' condition.\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\n\nimport { meta } from '#meta';\nimport { translations } from '#translations';\n\nimport pluginSpec from '../../../PLUGIN.mdl?raw';\n\nexport const PluginAsset = AppCapability.pluginAsset({\n pluginId: meta.profile.key,\n path: 'PLUGIN.mdl',\n content: pluginSpec,\n mimeType: 'application/x-mdl',\n});\n\nexport const ReactSurface = AppCapability.surface(() => import('../react-surface.ts'), {\n roles: ['org.dxos.role.article'],\n});\n\nexport const Schema = AppCapability.schema(() => import('../schema.ts'));\n\nexport const Translations = AppCapability.translations(translations);\n"],"mappings":";;;;;AAaA,IAAa,cAAc,cAAc,YAAY;CACnD,UAAU,KAAK,QAAQ;CACvB,MAAM;CACN,SAAS;CACT,UAAU;AACZ,CAAC;AAED,IAAa,eAAe,cAAc,cAAc,OAAO,8BAAwB,EACrF,OAAO,CAAC,uBAAuB,EACjC,CAAC;AAED,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAe;AAEvE,IAAa,eAAe,cAAc,aAAa,YAAY"}
@@ -1 +1 @@
1
- {"version":3,"file":"capabilities.workerd.mjs","names":[],"sources":["../../src/capabilities/gen/workerd.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'workerd' condition.\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\n\nimport { translations } from '#translations';\n\nexport const Schema = AppCapability.schema(() => import('../schema'));\n\nexport const Translations = AppCapability.translations(translations);\n\nexport const PluginAsset = undefined;\nexport const ReactSurface = undefined;\n"],"mappings":";;;AAUA,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAY;AAEpE,IAAa,eAAe,cAAc,aAAa,YAAY;AAEnE,IAAa,cAAc,KAAA;AAC3B,IAAa,eAAe,KAAA"}
1
+ {"version":3,"file":"capabilities.workerd.mjs","names":[],"sources":["../../src/capabilities/gen/workerd.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'workerd' condition.\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\n\nimport { translations } from '#translations';\n\nexport const Schema = AppCapability.schema(() => import('../schema.ts'));\n\nexport const Translations = AppCapability.translations(translations);\n\nexport const PluginAsset = undefined;\nexport const ReactSurface = undefined;\n"],"mappings":";;;AAUA,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAe;AAEvE,IAAa,eAAe,cAAc,aAAa,YAAY;AAEnE,IAAa,cAAc,KAAA;AAC3B,IAAa,eAAe,KAAA"}
@@ -0,0 +1,6 @@
1
+ //#region PLUGIN.mdl?raw
2
+ var PLUGIN_default = "---\nid: org.dxos.plugin.stack\nname: StackPlugin\nversion: 0.1.0\n---\n\nCollections plugin for `DXOS` Composer — renders any `Collection` object as a vertically scrollable stack of collapsible sections, where each section displays an arbitrary ECHO object via the `section` surface role. Supports adding, removing, navigating, and collapsing individual sections.\n\n## Extensions\n\nThe following extension dialects are used in this document.\nEach extension is defined in the Appendix or resolved via its URI.\n\n| Term | URI |\n|-------------|--------------------------------|\n| `type` | `org.dxos.mdl.type@1.0` |\n| `feat` | `org.dxos.mdl.feat@1.0` |\n| `scenario` | `org.dxos.mdl.scenario@1.0` |\n| `component` | `org.dxos.mdl.component@1.0` |\n| `op` | `org.dxos.mdl.op@1.0` |\n| `test` | `org.dxos.mdl.test@1.0` |\n\n## Types\n\n```mdl\ntype SectionSchema\n fields:\n height?: number # explicit height override for the section\n custom?: Record<string, any> # view-instance-specific data (e.g. cover mode for images)\n```\n\n```mdl\ntype StackViewType\n typename: org.dxos.type.stackView\n version: 0.1.0\n fields:\n sections: Record<string, SectionSchema>\n```\n\n```mdl\ntype StackSectionView\n fields:\n title?: string\n size?: StackItemSize\n height?: number\n collapsed?: boolean\n custom?: Record<string, any>\n```\n\n```mdl\ntype StackSectionMetadata\n fields:\n icon?: string\n placeholder?: Label\n viewActions?: (item: CollectionItem) => StackSectionAction\n```\n\n```mdl\ntype StackSectionItem\n fields:\n id: string # DXN string for the contained object\n object: Obj.Unknown\n view: StackSectionView\n metadata: StackSectionMetadata\n```\n\n```mdl\ntype AddSectionPosition\n literals: before | after | beforeAll | afterAll\n```\n\n```mdl\ntype StackSettingsProps\n fields:\n separation: boolean # whether visual separators are shown between sections\n```\n\n## Components\n\n```mdl\ncomponent StackArticle\n desc: Full-page article view of a Collection — renders items as a vertical stack of StackSection components with a toolbar for adding sections.\n props:\n subject: Collection\n role: AppSurface.Role\n attendableId?: string\n state:\n collapsedSections: Record<string, boolean> # collapsed state by DXN string\n items: StackSectionItem[] # derived from collection objects\n slots:\n (none)\n actions:\n handleAdd(id: string, position: AddSectionPosition)\n handleDelete(id: string)\n handleNavigate(id: string)\n handleCollapse(id: string, collapsed: boolean)\n handleAddSection()\n emits:\n (none)\n layout: |\n ┌──────────────────────────────┐\n │ Toolbar [+ Add Section] │\n ├──────────────────────────────┤\n │ Stack (vertical, intrinsic) │\n │ ┌────────────────────────┐ │\n │ │ StackSection │ │\n │ │ ▾ [icon] Title [→] │ │\n │ │ └─ Surface:section │ │\n │ └────────────────────────┘ │\n │ ┌────────────────────────┐ │\n │ │ StackSection (collapsed│ │\n │ │ ▸ [icon] Title [↕] │ │\n │ └────────────────────────┘ │\n │ … │\n └──────────────────────────────┘\n```\n\n```mdl\ncomponent StackSection\n desc: A single collapsible section in the stack — shows a sigil icon button with a dropdown menu and delegates content rendering to the section surface slot.\n props:\n id: string\n object: Obj.Unknown\n view: StackSectionView\n metadata: StackSectionMetadata\n state:\n optionsMenuOpen: boolean\n attendableId: string # composed as parentAttendableId/object.id\n slots:\n content: ReactNode # rendered by Surface(type=section, subject=object)\n actions:\n navigate(id: string)\n add(id: string, position: AddSectionPosition)\n collapse(id: string, collapsed: boolean)\n delete(id: string)\n layout: |\n ┌─────────────────────────────────┐\n │ StackItem.Heading │\n │ [sigil▾] (sticky) [→ or ↕] │\n ├─────────────────────────────────┤\n │ CollapsibleContent │\n │ Surface type=section │\n └─────────────────────────────────┘\n```\n\n```mdl\ncomponent StackSettings\n desc: Settings panel for configuring stack-level display preferences such as section separation.\n props:\n settings: StackSettingsProps\n layout: |\n ┌─────────────────────────────┐\n │ Settings.Section │\n │ Separation [toggle] │\n └─────────────────────────────┘\n```\n\n## Features\n\n```mdl\nfeat F-1: Stack Rendering\n\n req F-1.1: StackPlugin registers a surface for the article role filtered to Collection objects.\n req F-1.2: StackArticle renders each item in the collection as a StackSection in document order.\n req F-1.3: Each StackSection title is derived from the object's title property or graph node label.\n req F-1.4: The icon displayed on the section sigil is taken from the object schema's IconAnnotation.\n```\n\n```mdl\nfeat F-2: Section Management\n\n req F-2.1:\n when: user clicks the Add Section button in the toolbar\n then: SpaceOperation.OpenObjectForm dialog is invoked with the collection as target\n req F-2.2:\n when: user selects \"Add Before\" or \"Add After\" from a section dropdown\n then: LayoutOperation.UpdateDialog is invoked with the AddSectionDialog, position, and collection context\n req F-2.3:\n when: user selects \"Remove\" from a section dropdown\n then: SpaceOperation.RemoveObjects is invoked; section is removed from the collection\n req F-2.4:\n when: user selects \"Navigate to\" from a collapsed section dropdown or the navigate button\n then: LayoutOperation.Open is invoked with the section's id\n```\n\n```mdl\nfeat F-3: Collapse / Expand\n\n req F-3.1: Each section can be individually collapsed via the dropdown menu or caret trigger.\n req F-3.2:\n when: section is expanded\n then: full Surface:section content is visible; navigate button shown in heading\n req F-3.3:\n when: section is collapsed\n then: only the title is shown in StackItem.Content; expand toggle shown in heading\n req F-3.4: Collapsed state is managed locally in StackArticle component state (not persisted).\n```\n\n```mdl\nfeat F-4: Settings\n\n req F-4.1: StackPlugin exposes a settings panel via the StackSettings component.\n req F-4.2: The separation setting controls whether visual separators appear between sections.\n```\n\n```mdl\nfeat F-5: Schema Registration\n\n req F-5.1: StackPlugin registers StackViewType with the ECHO schema module on activation.\n req F-5.2: StackViewType stores per-section metadata (height, custom data) keyed by object id.\n```\n\n## Acceptance\n\n```mdl\nscenario T-1: Collection renders as stack\n given: a Collection with three objects is opened in article role\n then:\n - StackArticle is displayed\n - three StackSection components are rendered in order\n - each section shows the object's title\n```\n\n```mdl\nscenario T-2: Add section via toolbar\n given: StackArticle is open\n when: user clicks the Add Section toolbar button\n then: SpaceOperation.OpenObjectForm is invoked with the collection as target\n```\n\n```mdl\nscenario T-3: Remove section\n given: StackArticle is open with at least one section\n when: user selects Remove from a section dropdown\n then:\n - SpaceOperation.RemoveObjects is invoked\n - section no longer appears in the stack\n```\n\n```mdl\nscenario T-4: Collapse section\n given: a section is expanded\n when: user selects Collapse from the section dropdown\n then:\n - section content is hidden\n - section title is shown in collapsed state\n - expand toggle is visible in heading\n```\n\n```mdl\nscenario T-5: Navigate to section\n given: StackArticle is open\n when: user clicks the navigate button on a section heading\n then: LayoutOperation.Open is invoked with the section's DXN id\n```\n\n```mdl\nscenario T-6: StackViewType registered\n given: StackPlugin is active\n then: StackViewType (typename org.dxos.type.stackView) is registered with the ECHO schema module\n```\n\n---\n\n## QA\n\nExecutable tests. Each step carries a human instruction (`do`) and, where the step maps to an\ninvocable operation, an agent binding (`invoke`). Report failures by test and step number.\n\n```mdl\ntest QA-1: Render a collection as a stack, add, collapse, and remove a section\n desc: |\n StackPlugin declares no operations of its own — every mutation goes through\n `SpaceOperation.AddObject`/`RemoveObjects` (F-2.1, F-2.3), which belong to `plugin-space`.\n actors: both\n covers: [F-1.1, F-1.2, F-2.3, F-3.1, T-1, T-3, T-4]\n status: unverified\n given:\n - an identity exists\n - space: a space is open\n - plugin-stack is enabled — it is OFF by default for everyone (not listed in\n `getDefaults`, unlike most content plugins in `plugin-defs.tsx`), so no `article` surface\n resolves for a Collection's stack view until it is turned on\n - no collection named \"QA Stack\" exists — ABORT rather than deleting one this test did not\n create\n steps:\n - name: Create the collection and a section\n do: In the navtree, click + on the space and choose Collection. Name it \"QA Stack\". Open\n it, click \"Add Section\" in the toolbar, and choose Markdown, naming it \"QA Section\".\n invoke: org.dxos.operation.space.createCollection { name: \"QA Stack\" }\n capture: collection\n expect: \"QA Stack\" opens as a StackArticle with one StackSection titled \"QA Section\"\n assert: return $collection.object.name === 'QA Stack';\n note: |\n Coalesce the \"Add Section\" click with the object creation it triggers — `OpenObjectForm`\n opens a dialog and the resulting object is added via `AddObject`, which is the same\n pattern as `plugin-markdown`'s create+place split.\n - name: Add the section object\n do: (the Add Section dialog places it for you on confirm)\n invoke: org.dxos.operation.markdown.create { name: \"QA Section\" }\n capture: doc\n expect: a Document named \"QA Section\" exists\n - name: Place the section in the stack\n do: (as above)\n invoke: org.dxos.operation.space.addObject { object: $doc.object, target: $collection.object }\n expect: a StackSection titled \"QA Section\" now renders below the toolbar (F-1.2)\n assert: return $collection.object.objects.some((ref) => ref.target?.id === $doc.object.id);\n - name: Collapse the section\n do: Open the section's sigil dropdown and choose Collapse.\n expect: the section shows only its title; the caret points to \"expand\" (F-3.3, T-4)\n note: NO invoke — collapse state is local StackArticle component state (F-3.4), not an\n ECHO write, so there is nothing to verify against the database.\n - name: Remove the section\n do: Open the dropdown again and choose Remove.\n invoke: org.dxos.operation.space.removeObjects { objects: [$doc.object], target: $collection.object }\n expect: the section no longer appears in the stack (T-3)\n assert: return !$collection.object.objects.some((ref) => ref.target?.id === $doc.object.id);\n after:\n - name: Delete the collection\n do: Right-click \"QA Stack\" in the navtree and choose Delete.\n invoke: org.dxos.operation.space.removeObjects { objects: [$collection.object], target: $given.space }\n expect: the row leaves the navtree and its plank closes\n```\n\n---\n\n## Appendix: Extension Definitions\n\n```mdl\next type\n uri: org.dxos.mdl.type@1.0\n desc: A named data structure with typed fields and optional literals.\n fields:\n desc?: Prose\n fields?: FieldMap\n literals?: UnionList\n extends?: TypeRef[]\n```\n\n```mdl\next feat\n uri: org.dxos.mdl.feat@1.0\n desc: A named feature grouping one or more requirements.\n fields:\n desc?: Prose\n req: RequirementList\n nesting: self\n```\n\n```mdl\next scenario\n uri: org.dxos.mdl.scenario@1.0\n desc: An acceptance scenario expressed as given / when / then steps.\n fields:\n given?: Step | Step[]\n when?: Step | Step[]\n then: Step | Step[]\n tags?: TagList\n```\n\n```mdl\next component\n uri: org.dxos.mdl.component@1.0\n desc: A UI component with props, internal state, slots, actions, and events.\n fields:\n desc?: Prose\n props?: FieldMap\n state?: FieldMap\n slots?: FieldMap\n actions?: ActionMap\n emits?: EventMap\n layout?: CodeBlock\n```\n\n```mdl\next op\n uri: org.dxos.mdl.op@1.0\n desc: |\n A named operation with typed inputs, outputs, and declared errors.\n Pure ops have no effects or requires. Effectful ops declare both.\n fields:\n desc?: Prose\n input?: FieldMap\n output?: TypeExpr\n errors?: ErrorMap\n effects?: EffectList\n requires?: ServiceList\n note?: Prose\n```\n";
3
+ //#endregion
4
+ export { PLUGIN_default as t };
5
+
6
+ //# sourceMappingURL=chunk-PLUGIN.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-PLUGIN.mjs","names":[],"sources":[],"mappings":""}
@@ -1,20 +1,23 @@
1
1
  import { meta } from "#meta";
2
2
  import { useCallback, useId, useMemo, useState } from "react";
3
- import { useOperationInvoker } from "@dxos/app-framework/ui";
4
3
  import { Obj } from "@dxos/echo";
5
- import { Panel, Toolbar, useTranslation } from "@dxos/react-ui";
6
4
  import { Mosaic } from "@dxos/react-ui-mosaic";
5
+ import * as UiHooks from "@dxos/react-ui/Hooks";
7
6
  import { jsx, jsxs } from "react/jsx-runtime";
8
7
  import { arrayMove, isNonNullable } from "@dxos/util";
9
8
  import { useAtomValue } from "@effect/atom-react/Hooks";
10
- import * as Atom from "effect/unstable/reactivity/Atom";
9
+ import * as Atom from "effect/reactivity/Atom";
10
+ import * as Hooks from "@dxos/app-framework/Hooks";
11
11
  import * as SpaceOperation from "@dxos/plugin-space/SpaceOperation";
12
- import { Menu, createMenuAction } from "@dxos/react-ui-menu";
12
+ import { ActionMenu, createMenuAction } from "@dxos/react-ui-menu";
13
+ import * as Button from "@dxos/react-ui/Button";
14
+ import * as Panel from "@dxos/react-ui/Panel";
15
+ import * as Toolbar from "@dxos/react-ui/Toolbar";
13
16
  import { Stack } from "#components";
14
17
  //#region src/containers/StackArticle/StackArticle.tsx
15
18
  var StackArticle = ({ attendableId, subject: collection }) => {
16
- const { invokePromise } = useOperationInvoker();
17
- const { t } = useTranslation(meta.profile.key);
19
+ const { invokePromise } = Hooks.useOperationInvoker();
20
+ const { t } = UiHooks.useTranslation(meta.profile.key);
18
21
  const [collapsedSections, setCollapsedSections] = useState({});
19
22
  const collectionObjects = useAtomValue(createCollectionObjects(collection));
20
23
  const items = useMemo(() => collectionObjects.map((object) => ({
@@ -98,13 +101,12 @@ var StackArticle = ({ attendableId, subject: collection }) => {
98
101
  target: collection,
99
102
  navigable: false
100
103
  }), [collection, invokePromise]);
101
- return /* @__PURE__ */ jsxs(Panel.Root, { children: [/* @__PURE__ */ jsx(Panel.Toolbar, {
104
+ return /* @__PURE__ */ jsxs(Panel.Root, { children: [/* @__PURE__ */ jsx(Panel.Header, {
102
105
  classNames: "dx-toolbar-surface",
103
106
  children: /* @__PURE__ */ jsxs(Toolbar.Root, {
104
107
  classNames: "dx-document",
105
108
  children: [
106
- /* @__PURE__ */ jsx(Toolbar.IconButton, {
107
- square: true,
109
+ /* @__PURE__ */ jsx(Button.Root, {
108
110
  icon: "ph--plus--regular",
109
111
  iconOnly: true,
110
112
  label: t("add-section.label"),
@@ -112,19 +114,18 @@ var StackArticle = ({ attendableId, subject: collection }) => {
112
114
  onClick: handleAddSection
113
115
  }),
114
116
  /* @__PURE__ */ jsx(Toolbar.Separator, {}),
115
- /* @__PURE__ */ jsxs(Menu.Root, { children: [/* @__PURE__ */ jsx(Menu.Trigger, {
116
- asChild: true,
117
- children: /* @__PURE__ */ jsx(Toolbar.IconButton, {
118
- square: true,
117
+ /* @__PURE__ */ jsx(ActionMenu, {
118
+ actions: optionsMenu,
119
+ children: /* @__PURE__ */ jsx(Button.Root, {
119
120
  icon: "ph--dots-three-vertical--regular",
120
121
  iconOnly: true,
121
122
  label: t("options.label"),
122
123
  "data-testid": "stack.options"
123
124
  })
124
- }), /* @__PURE__ */ jsx(Menu.Content, { items: optionsMenu })] })
125
+ })
125
126
  ]
126
127
  })
127
- }), /* @__PURE__ */ jsx(Panel.Content, { children: /* @__PURE__ */ jsx(Stack.Root, {
128
+ }), /* @__PURE__ */ jsx(Panel.Body, { children: /* @__PURE__ */ jsx(Stack.Root, {
128
129
  id: Obj.getURI(collection),
129
130
  attendableId,
130
131
  collapsed,
@@ -135,8 +136,6 @@ var StackArticle = ({ attendableId, subject: collection }) => {
135
136
  onMoveDown: handleMoveDown,
136
137
  onDelete: handleDelete,
137
138
  children: /* @__PURE__ */ jsx(Stack.Content, {
138
- centered: true,
139
- padding: true,
140
139
  "data-testid": "main.stack",
141
140
  children: /* @__PURE__ */ jsx(Stack.Viewport, { children: /* @__PURE__ */ jsx(Mosaic.Stack, {
142
141
  classNames: "dx-document",
@@ -1 +1 @@
1
- {"version":3,"file":"chunk-StackArticle.mjs","names":[],"sources":["../../src/containers/StackArticle/StackArticle.tsx"],"sourcesContent":["//\n// Copyright 2023 DXOS.org\n//\n\nimport { useAtomValue } from '@effect/atom-react/Hooks';\nimport * as Atom from 'effect/unstable/reactivity/Atom';\nimport React, { useCallback, useId, useMemo, useState } from 'react';\n\nimport { useOperationInvoker } from '@dxos/app-framework/ui';\nimport { type AppSurface } from '@dxos/app-toolkit/ui';\nimport { type Collection, Obj } from '@dxos/echo';\nimport * as SpaceOperation from '@dxos/plugin-space/SpaceOperation';\nimport { Panel, Toolbar, useTranslation } from '@dxos/react-ui';\nimport { type DndContainerHandler } from '@dxos/react-ui-dnd';\nimport { Menu, createMenuAction } from '@dxos/react-ui-menu';\nimport { Mosaic } from '@dxos/react-ui-mosaic';\nimport { arrayMove, isNonNullable } from '@dxos/util';\n\nimport { Stack, type StackSectionItem } from '#components';\nimport { meta } from '#meta';\n\nexport type StackArticleProps = AppSurface.ObjectArticleProps<Collection.Collection>;\n\nexport const StackArticle = ({ attendableId, subject: collection }: StackArticleProps) => {\n const { invokePromise } = useOperationInvoker();\n const { t } = useTranslation(meta.profile.key);\n const [collapsedSections, setCollapsedSections] = useState<Record<string, boolean>>({});\n\n const collectionObjects = useAtomValue(createCollectionObjects(collection));\n const items = useMemo<StackSectionItem[]>(\n () => collectionObjects.map((object) => ({ id: Obj.getURI(object), object })),\n [collectionObjects],\n );\n\n const collapsed = useMemo<ReadonlySet<string>>(\n () => new Set(Object.keys(collapsedSections).filter((id) => collapsedSections[id])),\n [collapsedSections],\n );\n\n // Index of a section within the collection by its object URI.\n const findIndex = useCallback(\n (id: string) => collection.objects.findIndex((ref) => ref.target != null && Obj.getURI(ref.target) === id),\n [collection],\n );\n\n // Per-instance discriminator so the same collection opened in two planks doesn't collide in the Mosaic registry.\n const instanceId = useId();\n\n // Reorder sections in place. Placeholder/tile drop locations are 1-based with half-step placeholders\n // between tiles, so the floor of the drop location is the destination array index (see Mosaic.Stack).\n const eventHandler = useMemo<DndContainerHandler<StackSectionItem>>(\n () => ({\n id: `${Obj.getURI(collection)}:${instanceId}`,\n canDrop: ({ source }) =>\n collection.objects.some((ref) => ref.target != null && Obj.getURI(ref.target) === source.id),\n onDrop: ({ source, target }) => {\n const to =\n target?.type === 'tile' || target?.type === 'placeholder'\n ? target.location\n : target?.type === 'container'\n ? collection.objects.length\n : -1;\n const insert = typeof to === 'number' && to >= 0 ? Math.floor(to) : -1;\n if (insert < 0) {\n return;\n }\n\n Obj.update(collection, (collection) => {\n const from = findIndex(source.id);\n if (from !== -1) {\n arrayMove(collection.objects, from, insert);\n }\n });\n },\n }),\n [collection, findIndex, instanceId],\n );\n\n const handleMoveUp = useCallback(\n (id: string) => {\n const index = findIndex(id);\n if (index > 0) {\n Obj.update(collection, (collection) => {\n const [ref] = collection.objects.splice(index, 1);\n collection.objects.splice(index - 1, 0, ref);\n });\n }\n },\n [collection, findIndex],\n );\n\n const handleMoveDown = useCallback(\n (id: string) => {\n const index = findIndex(id);\n if (index >= 0 && index < collection.objects.length - 1) {\n Obj.update(collection, (collection) => {\n const [ref] = collection.objects.splice(index, 1);\n collection.objects.splice(index + 1, 0, ref);\n });\n }\n },\n [collection, findIndex],\n );\n\n const handleDelete = useCallback(\n (id: string) => {\n const index = findIndex(id);\n if (index >= 0) {\n Obj.update(collection, (collection) => {\n collection.objects.splice(index, 1);\n });\n }\n },\n [collection, findIndex],\n );\n\n const handleAdd = useCallback(\n async (id: string) => {\n const { data } = await invokePromise(SpaceOperation.OpenObjectForm, {\n target: collection,\n navigable: false,\n });\n const object = data?.target;\n if (!object) {\n return;\n }\n\n // The created object is appended; move it to immediately after the originating section.\n const from = findIndex(Obj.getURI(object));\n const anchor = findIndex(id);\n if (from >= 0 && anchor >= 0) {\n Obj.update(collection, (collection) => {\n const [ref] = collection.objects.splice(from, 1);\n collection.objects.splice(anchor < from ? anchor + 1 : anchor, 0, ref);\n });\n }\n },\n [collection, invokePromise, findIndex],\n );\n\n const handleCollapse = useCallback(\n (id: string, value: boolean) => setCollapsedSections((prev) => ({ ...prev, [id]: value })),\n [],\n );\n\n const handleCollapseAll = useCallback(\n () => setCollapsedSections(Object.fromEntries(collectionObjects.map((object) => [Obj.getURI(object), true]))),\n [collectionObjects],\n );\n\n const handleExpandAll = useCallback(() => setCollapsedSections({}), []);\n\n const optionsMenu = useMemo(\n () => [\n createMenuAction('collapse-all', handleCollapseAll, {\n label: ['collapse-all.label', { ns: meta.profile.key }],\n icon: 'ph--arrows-in-line-vertical--regular',\n testId: 'stack.collapse-all',\n }),\n createMenuAction('expand-all', handleExpandAll, {\n label: ['expand-all.label', { ns: meta.profile.key }],\n icon: 'ph--arrows-out-line-vertical--regular',\n testId: 'stack.expand-all',\n }),\n ],\n [handleCollapseAll, handleExpandAll],\n );\n\n const handleAddSection = useCallback(\n () =>\n invokePromise(SpaceOperation.OpenObjectForm, {\n target: collection,\n navigable: false,\n }),\n [collection, invokePromise],\n );\n\n return (\n <Panel.Root>\n <Panel.Toolbar classNames='dx-toolbar-surface'>\n <Toolbar.Root classNames='dx-document'>\n <Toolbar.IconButton\n square\n icon='ph--plus--regular'\n iconOnly\n label={t('add-section.label')}\n data-testid='stack.addSection'\n onClick={handleAddSection}\n />\n <Toolbar.Separator />\n <Menu.Root>\n <Menu.Trigger asChild>\n <Toolbar.IconButton\n square\n icon='ph--dots-three-vertical--regular'\n iconOnly\n label={t('options.label')}\n data-testid='stack.options'\n />\n </Menu.Trigger>\n <Menu.Content items={optionsMenu} />\n </Menu.Root>\n </Toolbar.Root>\n </Panel.Toolbar>\n <Panel.Content>\n <Stack.Root\n id={Obj.getURI(collection)}\n attendableId={attendableId}\n collapsed={collapsed}\n eventHandler={eventHandler}\n onCollapse={handleCollapse}\n onAdd={handleAdd}\n onMoveUp={handleMoveUp}\n onMoveDown={handleMoveDown}\n onDelete={handleDelete}\n >\n <Stack.Content centered padding data-testid='main.stack'>\n <Stack.Viewport>\n <Mosaic.Stack classNames='dx-document' items={items} getId={getId} Tile={Stack.Section} />\n </Stack.Viewport>\n </Stack.Content>\n </Stack.Root>\n </Panel.Content>\n </Panel.Root>\n );\n};\n\nconst getId = (item: StackSectionItem) => item.id;\n\nconst createCollectionObjects = Atom.family((collection: Collection.Collection) =>\n Atom.make((get) => {\n const snapshot = get(Obj.atom(collection));\n return snapshot.objects.map((ref) => get(Obj.atomReactive(ref))).filter(isNonNullable);\n }),\n);\n\nStackArticle.displayName = 'StackArticle';\n"],"mappings":"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"}
1
+ {"version":3,"file":"chunk-StackArticle.mjs","names":[],"sources":["../../src/containers/StackArticle/StackArticle.tsx"],"sourcesContent":["//\n// Copyright 2023 DXOS.org\n//\n\nimport { useAtomValue } from '@effect/atom-react/Hooks';\nimport * as Atom from 'effect/reactivity/Atom';\nimport React, { useCallback, useId, useMemo, useState } from 'react';\n\nimport * as Hooks from '@dxos/app-framework/Hooks';\nimport type * as AppSurface from '@dxos/app-toolkit/AppSurface';\nimport { type Collection, Obj } from '@dxos/echo';\nimport * as SpaceOperation from '@dxos/plugin-space/SpaceOperation';\nimport { type DndContainerHandler } from '@dxos/react-ui-dnd';\nimport { ActionMenu, createMenuAction } from '@dxos/react-ui-menu';\nimport { Mosaic } from '@dxos/react-ui-mosaic';\nimport * as Button from '@dxos/react-ui/Button';\nimport * as UiHooks from '@dxos/react-ui/Hooks';\nimport * as Panel from '@dxos/react-ui/Panel';\nimport * as Toolbar from '@dxos/react-ui/Toolbar';\nimport { arrayMove, isNonNullable } from '@dxos/util';\n\nimport { Stack, type StackSectionItem } from '#components';\nimport { meta } from '#meta';\n\nexport type StackArticleProps = AppSurface.ObjectArticleProps<Collection.Collection>;\n\nexport const StackArticle = ({ attendableId, subject: collection }: StackArticleProps) => {\n const { invokePromise } = Hooks.useOperationInvoker();\n const { t } = UiHooks.useTranslation(meta.profile.key);\n const [collapsedSections, setCollapsedSections] = useState<Record<string, boolean>>({});\n\n const collectionObjects = useAtomValue(createCollectionObjects(collection));\n const items = useMemo<StackSectionItem[]>(\n () => collectionObjects.map((object) => ({ id: Obj.getURI(object), object })),\n [collectionObjects],\n );\n\n const collapsed = useMemo<ReadonlySet<string>>(\n () => new Set(Object.keys(collapsedSections).filter((id) => collapsedSections[id])),\n [collapsedSections],\n );\n\n // Index of a section within the collection by its object URI.\n const findIndex = useCallback(\n (id: string) => collection.objects.findIndex((ref) => ref.target != null && Obj.getURI(ref.target) === id),\n [collection],\n );\n\n // Per-instance discriminator so the same collection opened in two planks doesn't collide in the Mosaic registry.\n const instanceId = useId();\n\n // Reorder sections in place. Placeholder/tile drop locations are 1-based with half-step placeholders\n // between tiles, so the floor of the drop location is the destination array index (see Mosaic.Stack).\n const eventHandler = useMemo<DndContainerHandler<StackSectionItem>>(\n () => ({\n id: `${Obj.getURI(collection)}:${instanceId}`,\n canDrop: ({ source }) =>\n collection.objects.some((ref) => ref.target != null && Obj.getURI(ref.target) === source.id),\n onDrop: ({ source, target }) => {\n const to =\n target?.type === 'tile' || target?.type === 'placeholder'\n ? target.location\n : target?.type === 'container'\n ? collection.objects.length\n : -1;\n const insert = typeof to === 'number' && to >= 0 ? Math.floor(to) : -1;\n if (insert < 0) {\n return;\n }\n\n Obj.update(collection, (collection) => {\n const from = findIndex(source.id);\n if (from !== -1) {\n arrayMove(collection.objects, from, insert);\n }\n });\n },\n }),\n [collection, findIndex, instanceId],\n );\n\n const handleMoveUp = useCallback(\n (id: string) => {\n const index = findIndex(id);\n if (index > 0) {\n Obj.update(collection, (collection) => {\n const [ref] = collection.objects.splice(index, 1);\n collection.objects.splice(index - 1, 0, ref);\n });\n }\n },\n [collection, findIndex],\n );\n\n const handleMoveDown = useCallback(\n (id: string) => {\n const index = findIndex(id);\n if (index >= 0 && index < collection.objects.length - 1) {\n Obj.update(collection, (collection) => {\n const [ref] = collection.objects.splice(index, 1);\n collection.objects.splice(index + 1, 0, ref);\n });\n }\n },\n [collection, findIndex],\n );\n\n const handleDelete = useCallback(\n (id: string) => {\n const index = findIndex(id);\n if (index >= 0) {\n Obj.update(collection, (collection) => {\n collection.objects.splice(index, 1);\n });\n }\n },\n [collection, findIndex],\n );\n\n const handleAdd = useCallback(\n async (id: string) => {\n const { data } = await invokePromise(SpaceOperation.OpenObjectForm, {\n target: collection,\n navigable: false,\n });\n const object = data?.target;\n if (!object) {\n return;\n }\n\n // The created object is appended; move it to immediately after the originating section.\n const from = findIndex(Obj.getURI(object));\n const anchor = findIndex(id);\n if (from >= 0 && anchor >= 0) {\n Obj.update(collection, (collection) => {\n const [ref] = collection.objects.splice(from, 1);\n collection.objects.splice(anchor < from ? anchor + 1 : anchor, 0, ref);\n });\n }\n },\n [collection, invokePromise, findIndex],\n );\n\n const handleCollapse = useCallback(\n (id: string, value: boolean) => setCollapsedSections((prev) => ({ ...prev, [id]: value })),\n [],\n );\n\n const handleCollapseAll = useCallback(\n () => setCollapsedSections(Object.fromEntries(collectionObjects.map((object) => [Obj.getURI(object), true]))),\n [collectionObjects],\n );\n\n const handleExpandAll = useCallback(() => setCollapsedSections({}), []);\n\n const optionsMenu = useMemo(\n () => [\n createMenuAction('collapse-all', handleCollapseAll, {\n label: ['collapse-all.label', { ns: meta.profile.key }],\n icon: 'ph--arrows-in-line-vertical--regular',\n testId: 'stack.collapse-all',\n }),\n createMenuAction('expand-all', handleExpandAll, {\n label: ['expand-all.label', { ns: meta.profile.key }],\n icon: 'ph--arrows-out-line-vertical--regular',\n testId: 'stack.expand-all',\n }),\n ],\n [handleCollapseAll, handleExpandAll],\n );\n\n const handleAddSection = useCallback(\n () =>\n invokePromise(SpaceOperation.OpenObjectForm, {\n target: collection,\n navigable: false,\n }),\n [collection, invokePromise],\n );\n\n return (\n <Panel.Root>\n <Panel.Header classNames='dx-toolbar-surface'>\n <Toolbar.Root classNames='dx-document'>\n <Button.Root\n icon='ph--plus--regular'\n iconOnly\n label={t('add-section.label')}\n data-testid='stack.addSection'\n onClick={handleAddSection}\n />\n <Toolbar.Separator />\n <ActionMenu actions={optionsMenu}>\n <Button.Root\n icon='ph--dots-three-vertical--regular'\n iconOnly\n label={t('options.label')}\n data-testid='stack.options'\n />\n </ActionMenu>\n </Toolbar.Root>\n </Panel.Header>\n <Panel.Body>\n <Stack.Root\n id={Obj.getURI(collection)}\n attendableId={attendableId}\n collapsed={collapsed}\n eventHandler={eventHandler}\n onCollapse={handleCollapse}\n onAdd={handleAdd}\n onMoveUp={handleMoveUp}\n onMoveDown={handleMoveDown}\n onDelete={handleDelete}\n >\n <Stack.Content data-testid='main.stack'>\n <Stack.Viewport>\n <Mosaic.Stack classNames='dx-document' items={items} getId={getId} Tile={Stack.Section} />\n </Stack.Viewport>\n </Stack.Content>\n </Stack.Root>\n </Panel.Body>\n </Panel.Root>\n );\n};\n\nconst getId = (item: StackSectionItem) => item.id;\n\nconst createCollectionObjects = Atom.family((collection: Collection.Collection) =>\n Atom.make((get) => {\n const snapshot = get(Obj.atom(collection));\n return snapshot.objects.map((ref) => get(Obj.atomReactive(ref))).filter(isNonNullable);\n }),\n);\n\nStackArticle.displayName = 'StackArticle';\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { Surface } from "@dxos/app-framework/ui";
2
- import { AppSurface } from "@dxos/app-toolkit/ui";
1
+ import * as Surface from "@dxos/app-framework/Surface";
2
+ import * as AppSurface from "@dxos/app-toolkit/AppSurface";
3
3
  import { Collection } from "@dxos/echo";
4
4
  import * as Effect from "effect/Effect";
5
5
  import * as Capabilities from "@dxos/app-framework/Capabilities";
@@ -1 +1 @@
1
- {"version":3,"file":"chunk-react-surface.mjs","names":[],"sources":["../../src/capabilities/react-surface.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Capability from '@dxos/app-framework/Capability';\nimport { Surface } from '@dxos/app-framework/ui';\nimport { AppSurface } from '@dxos/app-toolkit/ui';\nimport { Collection } from '@dxos/echo';\n\nimport { StackArticle } from '#containers';\n\nexport default Capability.makeModule(() =>\n Effect.succeed(\n Capability.contribute(\n Capabilities.ReactSurface,\n Surface.create({\n id: 'article',\n filter: AppSurface.object(AppSurface.Article, Collection.Collection),\n component: StackArticle,\n props: ({ role, data: { subject, attendableId } }) => ({ role, subject, attendableId }),\n }),\n ),\n ),\n);\n"],"mappings":";;;;;;;;AAcA,IAAA,wBAAe,WAAW,iBACxB,OAAO,QACL,WAAW,WACT,aAAa,cACb,QAAQ,OAAO;CACb,IAAI;CACJ,QAAQ,WAAW,OAAO,WAAW,SAAS,WAAW,UAAU;CACnE,WAAW;CACX,QAAQ,EAAE,MAAM,MAAM,EAAE,SAAS,sBAAsB;EAAE;EAAM;EAAS;CAAa;AACvF,CAAC,CACH,CACF,CACF"}
1
+ {"version":3,"file":"chunk-react-surface.mjs","names":[],"sources":["../../src/capabilities/react-surface.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as Surface from '@dxos/app-framework/Surface';\nimport * as AppSurface from '@dxos/app-toolkit/AppSurface';\nimport { Collection } from '@dxos/echo';\n\nimport { StackArticle } from '#containers';\n\nexport default Capability.makeModule(() =>\n Effect.succeed(\n Capability.contribute(\n Capabilities.ReactSurface,\n Surface.create({\n id: 'article',\n filter: AppSurface.object(AppSurface.Article, Collection.Collection),\n component: StackArticle,\n props: ({ role, data: { subject, attendableId } }) => ({ role, subject, attendableId }),\n }),\n ),\n ),\n);\n"],"mappings":";;;;;;;;AAcA,IAAA,wBAAe,WAAW,iBACxB,OAAO,QACL,WAAW,WACT,aAAa,cACb,QAAQ,OAAO;CACb,IAAI;CACJ,QAAQ,WAAW,OAAO,WAAW,SAAS,WAAW,UAAU;CACnE,WAAW;CACX,QAAQ,EAAE,MAAM,MAAM,EAAE,SAAS,sBAAsB;EAAE;EAAM;EAAS;CAAa;AACvF,CAAC,CACH,CACF,CACF"}
@@ -1,12 +1,16 @@
1
1
  import { meta } from "#meta";
2
2
  import { createContext, forwardRef, useCallback, useContext, useMemo, useState } from "react";
3
- import { Surface } from "@dxos/app-framework/ui";
3
+ import * as Surface from "@dxos/app-framework/Surface";
4
+ import * as AppSurface from "@dxos/app-toolkit/AppSurface";
5
+ import * as AttentionSigil from "@dxos/app-toolkit/AttentionSigil";
4
6
  import * as GraphPath from "@dxos/app-toolkit/GraphPath";
5
- import { AppSurface, AttentionSigilButton } from "@dxos/app-toolkit/ui";
6
7
  import { Obj } from "@dxos/echo";
7
- import { DropdownMenu, Icon, ScrollArea, useTranslation } from "@dxos/react-ui";
8
8
  import { useAttentionAttributes } from "@dxos/react-ui-attention";
9
9
  import { Mosaic } from "@dxos/react-ui-mosaic";
10
+ import * as Hooks from "@dxos/react-ui/Hooks";
11
+ import * as Icon from "@dxos/react-ui/Icon";
12
+ import * as Menu from "@dxos/react-ui/Menu";
13
+ import * as ScrollArea from "@dxos/react-ui/ScrollArea";
10
14
  import { jsx, jsxs } from "react/jsx-runtime";
11
15
  //#region src/components/Stack/StackContext.ts
12
16
  var StackContext = createContext(void 0);
@@ -94,7 +98,7 @@ var DragHandleGlyph = () => /* @__PURE__ */ jsx("svg", {
94
98
  height: 16,
95
99
  viewBox: "0 0 10 16",
96
100
  "aria-hidden": true,
97
- className: "shrink-0 text-description",
101
+ className: "shrink-0 text-fg-muted",
98
102
  children: [
99
103
  3,
100
104
  8,
@@ -108,7 +112,7 @@ var DragHandleGlyph = () => /* @__PURE__ */ jsx("svg", {
108
112
  });
109
113
  var StackSection = ({ data, ...tileProps }) => {
110
114
  const { id, object } = data;
111
- const { t } = useTranslation(meta.profile.key);
115
+ const { t } = Hooks.useTranslation(meta.profile.key);
112
116
  const { attendableId: parentAttendableId, collapsed, onAdd, onMoveUp, onMoveDown, onCollapse, onDelete } = useStack();
113
117
  const [optionsMenuOpen, setOptionsMenuOpen] = useState(false);
114
118
  const attendableId = GraphPath.getCollectionObjectPath(parentAttendableId, object.id);
@@ -124,70 +128,76 @@ var StackSection = ({ data, ...tileProps }) => {
124
128
  className: "grid grid-rows-[min-content_1fr]",
125
129
  children: [/* @__PURE__ */ jsx("div", {
126
130
  className: "p-1 dx-toolbar-surface",
127
- children: /* @__PURE__ */ jsxs(DropdownMenu.Root, {
131
+ children: /* @__PURE__ */ jsxs(Menu.Root, {
128
132
  open: optionsMenuOpen,
129
- onOpenChange: setOptionsMenuOpen,
130
- children: [/* @__PURE__ */ jsx(DropdownMenu.Trigger, {
133
+ onOpenChange: ({ open }) => setOptionsMenuOpen(open),
134
+ children: [/* @__PURE__ */ jsx(Menu.Trigger, {
131
135
  asChild: true,
132
- children: /* @__PURE__ */ jsx(AttentionSigilButton, {
136
+ children: /* @__PURE__ */ jsx(AttentionSigil.Button, {
133
137
  size: "md",
134
138
  attendableId,
135
- children: /* @__PURE__ */ jsx(Icon, {
139
+ children: /* @__PURE__ */ jsx(Icon.Icon, {
136
140
  icon,
137
141
  classNames: "transition-opacity"
138
142
  })
139
143
  })
140
- }), /* @__PURE__ */ jsx(DropdownMenu.Portal, { children: /* @__PURE__ */ jsxs(DropdownMenu.Content, { children: [/* @__PURE__ */ jsxs(DropdownMenu.Viewport, { children: [
141
- isCollapsed ? /* @__PURE__ */ jsxs(DropdownMenu.Item, {
144
+ }), /* @__PURE__ */ jsxs(Menu.Content, { children: [
145
+ isCollapsed ? /* @__PURE__ */ jsx(Menu.Item, {
146
+ item: {
147
+ value: "section.expand",
148
+ label: t("expand.label"),
149
+ icon: "ph--arrows-out-line-vertical--regular"
150
+ },
142
151
  onClick: () => onCollapse(id, false),
143
- "data-testid": "section.expand",
144
- children: [/* @__PURE__ */ jsx(Icon, { icon: "ph--arrows-out-line-vertical--regular" }), /* @__PURE__ */ jsx("span", {
145
- className: "ms-2 grow",
146
- children: t("expand.label")
147
- })]
148
- }) : /* @__PURE__ */ jsxs(DropdownMenu.Item, {
152
+ "data-testid": "section.expand"
153
+ }) : /* @__PURE__ */ jsx(Menu.Item, {
154
+ item: {
155
+ value: "section.collapse",
156
+ label: t("collapse.label"),
157
+ icon: "ph--arrows-in-line-vertical--regular"
158
+ },
149
159
  onClick: () => onCollapse(id, true),
150
- "data-testid": "section.collapse",
151
- children: [/* @__PURE__ */ jsx(Icon, { icon: "ph--arrows-in-line-vertical--regular" }), /* @__PURE__ */ jsx("span", {
152
- className: "ms-2 grow",
153
- children: t("collapse.label")
154
- })]
160
+ "data-testid": "section.collapse"
155
161
  }),
156
- /* @__PURE__ */ jsx(DropdownMenu.Separator, {}),
157
- /* @__PURE__ */ jsxs(DropdownMenu.Item, {
162
+ /* @__PURE__ */ jsx(Menu.Separator, {}),
163
+ /* @__PURE__ */ jsx(Menu.Item, {
164
+ item: {
165
+ value: "section.add",
166
+ label: t("add-section.label"),
167
+ icon: "ph--plus--regular"
168
+ },
158
169
  onClick: () => onAdd(id),
159
- "data-testid": "section.add",
160
- children: [/* @__PURE__ */ jsx(Icon, { icon: "ph--plus--regular" }), /* @__PURE__ */ jsx("span", {
161
- className: "ms-2 grow",
162
- children: t("add-section.label")
163
- })]
170
+ "data-testid": "section.add"
164
171
  }),
165
- /* @__PURE__ */ jsxs(DropdownMenu.Item, {
172
+ /* @__PURE__ */ jsx(Menu.Item, {
173
+ item: {
174
+ value: "section.move-up",
175
+ label: t("move-up.label"),
176
+ icon: "ph--arrow-line-up--regular"
177
+ },
166
178
  onClick: () => onMoveUp(id),
167
- "data-testid": "section.move-up",
168
- children: [/* @__PURE__ */ jsx(Icon, { icon: "ph--arrow-line-up--regular" }), /* @__PURE__ */ jsx("span", {
169
- className: "ms-2 grow",
170
- children: t("move-up.label")
171
- })]
179
+ "data-testid": "section.move-up"
172
180
  }),
173
- /* @__PURE__ */ jsxs(DropdownMenu.Item, {
181
+ /* @__PURE__ */ jsx(Menu.Item, {
182
+ item: {
183
+ value: "section.move-down",
184
+ label: t("move-down.label"),
185
+ icon: "ph--arrow-line-down--regular"
186
+ },
174
187
  onClick: () => onMoveDown(id),
175
- "data-testid": "section.move-down",
176
- children: [/* @__PURE__ */ jsx(Icon, { icon: "ph--arrow-line-down--regular" }), /* @__PURE__ */ jsx("span", {
177
- className: "ms-2 grow",
178
- children: t("move-down.label")
179
- })]
188
+ "data-testid": "section.move-down"
180
189
  }),
181
- /* @__PURE__ */ jsx(DropdownMenu.Separator, {}),
182
- /* @__PURE__ */ jsxs(DropdownMenu.Item, {
190
+ /* @__PURE__ */ jsx(Menu.Separator, {}),
191
+ /* @__PURE__ */ jsx(Menu.Item, {
192
+ item: {
193
+ value: "section.remove",
194
+ label: t("remove-section.label"),
195
+ icon: "ph--trash--regular"
196
+ },
183
197
  onClick: () => onDelete(id),
184
- "data-testid": "section.remove",
185
- children: [/* @__PURE__ */ jsx(Icon, { icon: "ph--trash--regular" }), /* @__PURE__ */ jsx("span", {
186
- className: "ms-2 grow",
187
- children: t("remove-section.label")
188
- })]
198
+ "data-testid": "section.remove"
189
199
  })
190
- ] }), /* @__PURE__ */ jsx(DropdownMenu.Arrow, {})] }) })]
200
+ ] })]
191
201
  })
192
202
  }), /* @__PURE__ */ jsx("div", {
193
203
  className: "p-1",
@@ -202,9 +212,9 @@ var StackSection = ({ data, ...tileProps }) => {
202
212
  return /* @__PURE__ */ jsxs(Mosaic.Tile, {
203
213
  ...tileProps,
204
214
  data,
205
- classNames: "grid grid-cols-[var(--dx-rail-action)_1fr] dx-attention-surface border border-subdued-separator",
215
+ classNames: "grid grid-cols-[var(--dx-rail-action)_1fr] dx-attention-surface border border-separator-subtle",
206
216
  children: [/* @__PURE__ */ jsx("div", {
207
- className: "border-e border-subdued-separator",
217
+ className: "border-e border-separator-subtle",
208
218
  children: /* @__PURE__ */ jsx("div", {
209
219
  className: "sticky top-0 flex flex-col items-center",
210
220
  children: rail