@dxos/plugin-magazine 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 (242) hide show
  1. package/dist/lib/MagazinePlugin.mjs +36 -0
  2. package/dist/lib/MagazinePlugin.mjs.map +1 -0
  3. package/dist/lib/MagazinePlugin.workerd.mjs +26 -0
  4. package/dist/lib/MagazinePlugin.workerd.mjs.map +1 -0
  5. package/dist/lib/atoms.mjs +175 -0
  6. package/dist/lib/atoms.mjs.map +1 -0
  7. package/dist/lib/capabilities.mjs +12 -0
  8. package/dist/lib/capabilities.mjs.map +1 -0
  9. package/dist/lib/chunk-FeedArticle.mjs +70 -0
  10. package/dist/lib/chunk-FeedArticle.mjs.map +1 -0
  11. package/dist/lib/chunk-FeedProperties.mjs +78 -0
  12. package/dist/lib/chunk-FeedProperties.mjs.map +1 -0
  13. package/dist/lib/chunk-MagazineArticle.mjs +267 -0
  14. package/dist/lib/chunk-MagazineArticle.mjs.map +1 -0
  15. package/dist/lib/chunk-PostArticle.mjs +174 -0
  16. package/dist/lib/chunk-PostArticle.mjs.map +1 -0
  17. package/dist/lib/chunk-PostCard.mjs +62 -0
  18. package/dist/lib/chunk-PostCard.mjs.map +1 -0
  19. package/dist/lib/chunk-app-graph-builder.mjs +91 -0
  20. package/dist/lib/chunk-app-graph-builder.mjs.map +1 -0
  21. package/dist/lib/chunk-clear-magazine.mjs +37 -0
  22. package/dist/lib/chunk-clear-magazine.mjs.map +1 -0
  23. package/dist/lib/chunk-create-object.mjs +59 -0
  24. package/dist/lib/chunk-create-object.mjs.map +1 -0
  25. package/dist/lib/chunk-curate-magazine.mjs +164 -0
  26. package/dist/lib/chunk-curate-magazine.mjs.map +1 -0
  27. package/dist/lib/chunk-date.mjs +25 -0
  28. package/dist/lib/chunk-date.mjs.map +1 -0
  29. package/dist/lib/chunk-fetch-article-content.mjs +26 -0
  30. package/dist/lib/chunk-fetch-article-content.mjs.map +1 -0
  31. package/dist/lib/chunk-load-post-content.mjs +57 -0
  32. package/dist/lib/chunk-load-post-content.mjs.map +1 -0
  33. package/dist/lib/{neutral/chunk-JFPR6OV4.mjs → chunk-meta.mjs} +19 -26
  34. package/dist/lib/chunk-meta.mjs.map +1 -0
  35. package/dist/lib/chunk-operation-handler.mjs +13 -0
  36. package/dist/lib/chunk-operation-handler.mjs.map +1 -0
  37. package/dist/lib/chunk-paths.mjs +8 -0
  38. package/dist/lib/chunk-paths.mjs.map +1 -0
  39. package/dist/lib/chunk-post-content.mjs +14 -0
  40. package/dist/lib/chunk-post-content.mjs.map +1 -0
  41. package/dist/lib/chunk-react-surface.mjs +60 -0
  42. package/dist/lib/chunk-react-surface.mjs.map +1 -0
  43. package/dist/lib/chunk-routine-templates.mjs +52 -0
  44. package/dist/lib/chunk-routine-templates.mjs.map +1 -0
  45. package/dist/lib/chunk-skill-definition.mjs +12 -0
  46. package/dist/lib/chunk-skill-definition.mjs.map +1 -0
  47. package/dist/lib/chunk-sources.mjs +576 -0
  48. package/dist/lib/chunk-sources.mjs.map +1 -0
  49. package/dist/lib/chunk-sync-feed.mjs +86 -0
  50. package/dist/lib/chunk-sync-feed.mjs.map +1 -0
  51. package/dist/lib/chunk-text.mjs +52 -0
  52. package/dist/lib/chunk-text.mjs.map +1 -0
  53. package/dist/lib/chunk-types.mjs +607 -0
  54. package/dist/lib/chunk-types.mjs.map +1 -0
  55. package/dist/lib/components.mjs +323 -0
  56. package/dist/lib/components.mjs.map +1 -0
  57. package/dist/lib/containers.mjs +11 -0
  58. package/dist/lib/containers.mjs.map +1 -0
  59. package/dist/lib/index.mjs +3 -0
  60. package/dist/lib/meta.mjs +2 -0
  61. package/dist/lib/operations.mjs +7 -0
  62. package/dist/lib/operations.mjs.map +1 -0
  63. package/dist/lib/plugin.mjs +9 -0
  64. package/dist/lib/plugin.mjs.map +1 -0
  65. package/dist/lib/plugin.workerd.mjs +3 -0
  66. package/dist/lib/{neutral/skills/index.mjs → skills.mjs} +15 -28
  67. package/dist/lib/skills.mjs.map +1 -0
  68. package/dist/lib/testing.mjs +71 -0
  69. package/dist/lib/testing.mjs.map +1 -0
  70. package/dist/lib/translations.mjs +74 -0
  71. package/dist/lib/translations.mjs.map +1 -0
  72. package/dist/lib/types.mjs +2 -0
  73. package/dist/types/src/MagazinePlugin.d.ts.map +1 -1
  74. package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
  75. package/dist/types/src/capabilities/create-object.d.ts +1 -1
  76. package/dist/types/src/capabilities/index.d.ts +0 -1
  77. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  78. package/dist/types/src/capabilities/operation-handler.d.ts +1 -1
  79. package/dist/types/src/capabilities/react-surface.d.ts +1 -1
  80. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  81. package/dist/types/src/capabilities/routine-templates.d.ts +1 -1
  82. package/dist/types/src/components/PostContent/PostContent.d.ts +1 -13
  83. package/dist/types/src/components/PostContent/PostContent.d.ts.map +1 -1
  84. package/dist/types/src/components/PostContent/dedupe-images.d.ts +13 -0
  85. package/dist/types/src/components/PostContent/dedupe-images.d.ts.map +1 -0
  86. package/dist/types/src/components/PostContent/index.d.ts +1 -0
  87. package/dist/types/src/components/PostContent/index.d.ts.map +1 -1
  88. package/dist/types/src/components/PostStack/PostStack.d.ts +1 -1
  89. package/dist/types/src/components/PostStack/PostStack.d.ts.map +1 -1
  90. package/dist/types/src/components/SubscriptionStack/SubscriptionStack.d.ts +1 -1
  91. package/dist/types/src/components/SubscriptionStack/SubscriptionStack.d.ts.map +1 -1
  92. package/dist/types/src/containers/FeedArticle/FeedArticle.d.ts +4 -1
  93. package/dist/types/src/containers/FeedArticle/FeedArticle.d.ts.map +1 -1
  94. package/dist/types/src/containers/FeedProperties/FeedProperties.d.ts +4 -1
  95. package/dist/types/src/containers/FeedProperties/FeedProperties.d.ts.map +1 -1
  96. package/dist/types/src/containers/MagazineArticle/MagazineArticle.d.ts +4 -1
  97. package/dist/types/src/containers/MagazineArticle/MagazineArticle.d.ts.map +1 -1
  98. package/dist/types/src/containers/MagazineArticle/MagazineArticle.stories.d.ts.map +1 -1
  99. package/dist/types/src/containers/MagazineArticle/MagazineTile.d.ts +4 -1
  100. package/dist/types/src/containers/MagazineArticle/MagazineTile.d.ts.map +1 -1
  101. package/dist/types/src/containers/MagazineArticle/useToolbar.d.ts +4 -8
  102. package/dist/types/src/containers/MagazineArticle/useToolbar.d.ts.map +1 -1
  103. package/dist/types/src/containers/PostArticle/PostArticle.d.ts +4 -1
  104. package/dist/types/src/containers/PostArticle/PostArticle.d.ts.map +1 -1
  105. package/dist/types/src/containers/PostArticle/PostArticle.stories.d.ts.map +1 -1
  106. package/dist/types/src/containers/PostArticle/PostToolbar.d.ts +1 -1
  107. package/dist/types/src/containers/PostArticle/PostToolbar.d.ts.map +1 -1
  108. package/dist/types/src/containers/PostCard/PostCard.d.ts +4 -6
  109. package/dist/types/src/containers/PostCard/PostCard.d.ts.map +1 -1
  110. package/dist/types/src/containers/SubscriptionsArticle/SubscriptionsArticle.d.ts +4 -1
  111. package/dist/types/src/containers/SubscriptionsArticle/SubscriptionsArticle.d.ts.map +1 -1
  112. package/dist/types/src/containers/SubscriptionsArticle/SubscriptionsArticle.stories.d.ts.map +1 -1
  113. package/dist/types/src/stories/ArticleExtractor.stories.d.ts +1 -0
  114. package/dist/types/src/stories/ArticleExtractor.stories.d.ts.map +1 -1
  115. package/dist/types/src/stories/MagazineCurate.stories.d.ts.map +1 -1
  116. package/dist/types/src/templates/magazine-curation.d.ts.map +1 -1
  117. package/dist/types/src/translations.d.ts +1 -0
  118. package/dist/types/src/translations.d.ts.map +1 -1
  119. package/dist/types/tsconfig.tsbuildinfo +1 -1
  120. package/dx.config.ts +1 -1
  121. package/package.json +57 -60
  122. package/src/MagazinePlugin.tsx +0 -3
  123. package/src/atoms/atoms.test.ts +5 -4
  124. package/src/capabilities/app-graph-builder.ts +8 -6
  125. package/src/capabilities/index.ts +0 -1
  126. package/src/capabilities/{react-surface.tsx → react-surface.ts} +10 -12
  127. package/src/components/PostContent/PostContent.test.ts +1 -1
  128. package/src/components/PostContent/PostContent.tsx +1 -41
  129. package/src/components/PostContent/dedupe-images.ts +48 -0
  130. package/src/components/PostContent/index.ts +1 -0
  131. package/src/containers/FeedArticle/FeedArticle.stories.tsx +2 -2
  132. package/src/containers/FeedArticle/FeedArticle.tsx +4 -2
  133. package/src/containers/FeedProperties/FeedProperties.tsx +6 -4
  134. package/src/containers/MagazineArticle/MagazineArticle.stories.tsx +2 -1
  135. package/src/containers/MagazineArticle/MagazineArticle.tsx +8 -6
  136. package/src/containers/MagazineArticle/MagazineTile.tsx +2 -0
  137. package/src/containers/MagazineArticle/useToolbar.tsx +2 -0
  138. package/src/containers/PostArticle/PostArticle.stories.tsx +2 -1
  139. package/src/containers/PostArticle/PostArticle.tsx +6 -3
  140. package/src/containers/PostArticle/PostToolbar.tsx +1 -1
  141. package/src/containers/PostCard/PostCard.tsx +3 -1
  142. package/src/containers/SubscriptionsArticle/SubscriptionsArticle.stories.tsx +2 -1
  143. package/src/containers/SubscriptionsArticle/SubscriptionsArticle.tsx +5 -3
  144. package/src/operations/curate-magazine.test.ts +108 -4
  145. package/src/operations/sync-feed.test.ts +1 -1
  146. package/src/operations/sync-feed.ts +1 -1
  147. package/src/paths.ts +3 -3
  148. package/src/stories/ArticleExtractor.stories.tsx +1 -1
  149. package/src/stories/MagazineCurate.stories.tsx +3 -2
  150. package/src/templates/magazine-curation.ts +3 -2
  151. package/src/types/Magazine.test.ts +1 -1
  152. package/src/types/Subscription.ts +1 -1
  153. package/dist/lib/neutral/FeedArticle-PEWCFFSP.mjs +0 -90
  154. package/dist/lib/neutral/FeedArticle-PEWCFFSP.mjs.map +0 -7
  155. package/dist/lib/neutral/FeedProperties-EIO7IZOA.mjs +0 -97
  156. package/dist/lib/neutral/FeedProperties-EIO7IZOA.mjs.map +0 -7
  157. package/dist/lib/neutral/MagazineArticle-5T7RTIPS.mjs +0 -325
  158. package/dist/lib/neutral/MagazineArticle-5T7RTIPS.mjs.map +0 -7
  159. package/dist/lib/neutral/MagazinePlugin.mjs +0 -62
  160. package/dist/lib/neutral/MagazinePlugin.mjs.map +0 -7
  161. package/dist/lib/neutral/MagazinePlugin.workerd.mjs +0 -12
  162. package/dist/lib/neutral/MagazinePlugin.workerd.mjs.map +0 -7
  163. package/dist/lib/neutral/PostArticle-BGIPU7XA.mjs +0 -230
  164. package/dist/lib/neutral/PostArticle-BGIPU7XA.mjs.map +0 -7
  165. package/dist/lib/neutral/PostCard-VDGN7BBA.mjs +0 -60
  166. package/dist/lib/neutral/PostCard-VDGN7BBA.mjs.map +0 -7
  167. package/dist/lib/neutral/app-graph-builder-JDPRBVHL.mjs +0 -121
  168. package/dist/lib/neutral/app-graph-builder-JDPRBVHL.mjs.map +0 -7
  169. package/dist/lib/neutral/capabilities/index.mjs +0 -21
  170. package/dist/lib/neutral/capabilities/index.mjs.map +0 -7
  171. package/dist/lib/neutral/chunk-3K4AQKBZ.mjs +0 -12
  172. package/dist/lib/neutral/chunk-3K4AQKBZ.mjs.map +0 -7
  173. package/dist/lib/neutral/chunk-AFPBWBTK.mjs +0 -591
  174. package/dist/lib/neutral/chunk-AFPBWBTK.mjs.map +0 -7
  175. package/dist/lib/neutral/chunk-APQRTYT3.mjs +0 -57
  176. package/dist/lib/neutral/chunk-APQRTYT3.mjs.map +0 -7
  177. package/dist/lib/neutral/chunk-GU725ZF4.mjs +0 -11
  178. package/dist/lib/neutral/chunk-GU725ZF4.mjs.map +0 -7
  179. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +0 -10
  180. package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +0 -7
  181. package/dist/lib/neutral/chunk-JFPR6OV4.mjs.map +0 -7
  182. package/dist/lib/neutral/chunk-KTYLUOUG.mjs +0 -36
  183. package/dist/lib/neutral/chunk-KTYLUOUG.mjs.map +0 -7
  184. package/dist/lib/neutral/chunk-LFSQMLPK.mjs +0 -8
  185. package/dist/lib/neutral/chunk-LFSQMLPK.mjs.map +0 -7
  186. package/dist/lib/neutral/chunk-MBPX7RPI.mjs +0 -14
  187. package/dist/lib/neutral/chunk-MBPX7RPI.mjs.map +0 -7
  188. package/dist/lib/neutral/chunk-OYCDRSWB.mjs +0 -624
  189. package/dist/lib/neutral/chunk-OYCDRSWB.mjs.map +0 -7
  190. package/dist/lib/neutral/chunk-SAWKLGJU.mjs +0 -28
  191. package/dist/lib/neutral/chunk-SAWKLGJU.mjs.map +0 -7
  192. package/dist/lib/neutral/chunk-XCNCK47N.mjs +0 -15
  193. package/dist/lib/neutral/chunk-XCNCK47N.mjs.map +0 -7
  194. package/dist/lib/neutral/clear-magazine-KNY4OWY4.mjs +0 -59
  195. package/dist/lib/neutral/clear-magazine-KNY4OWY4.mjs.map +0 -7
  196. package/dist/lib/neutral/components/index.mjs +0 -338
  197. package/dist/lib/neutral/components/index.mjs.map +0 -7
  198. package/dist/lib/neutral/containers/index.mjs +0 -17
  199. package/dist/lib/neutral/containers/index.mjs.map +0 -7
  200. package/dist/lib/neutral/create-object-SYZMRR4H.mjs +0 -98
  201. package/dist/lib/neutral/create-object-SYZMRR4H.mjs.map +0 -7
  202. package/dist/lib/neutral/curate-magazine-7RMKSJC5.mjs +0 -184
  203. package/dist/lib/neutral/curate-magazine-7RMKSJC5.mjs.map +0 -7
  204. package/dist/lib/neutral/fetch-article-content-CXPKXIMR.mjs +0 -31
  205. package/dist/lib/neutral/fetch-article-content-CXPKXIMR.mjs.map +0 -7
  206. package/dist/lib/neutral/index.mjs +0 -18
  207. package/dist/lib/neutral/index.mjs.map +0 -7
  208. package/dist/lib/neutral/load-post-content-H3S4TKVM.mjs +0 -65
  209. package/dist/lib/neutral/load-post-content-H3S4TKVM.mjs.map +0 -7
  210. package/dist/lib/neutral/meta.json +0 -1
  211. package/dist/lib/neutral/meta.mjs +0 -8
  212. package/dist/lib/neutral/meta.mjs.map +0 -7
  213. package/dist/lib/neutral/navigation-resolver-IN4OMCDR.mjs +0 -18
  214. package/dist/lib/neutral/navigation-resolver-IN4OMCDR.mjs.map +0 -7
  215. package/dist/lib/neutral/operation-handler-JE3743BU.mjs +0 -8
  216. package/dist/lib/neutral/operation-handler-JE3743BU.mjs.map +0 -7
  217. package/dist/lib/neutral/operations/index.mjs +0 -8
  218. package/dist/lib/neutral/operations/index.mjs.map +0 -7
  219. package/dist/lib/neutral/plugin.mjs +0 -16
  220. package/dist/lib/neutral/plugin.mjs.map +0 -7
  221. package/dist/lib/neutral/plugin.workerd.mjs +0 -16
  222. package/dist/lib/neutral/plugin.workerd.mjs.map +0 -7
  223. package/dist/lib/neutral/react-surface-TZESAMG2.mjs +0 -60
  224. package/dist/lib/neutral/react-surface-TZESAMG2.mjs.map +0 -7
  225. package/dist/lib/neutral/routine-templates-D5A2L4KL.mjs +0 -52
  226. package/dist/lib/neutral/routine-templates-D5A2L4KL.mjs.map +0 -7
  227. package/dist/lib/neutral/skill-definition-3KLMUEC5.mjs +0 -8
  228. package/dist/lib/neutral/skill-definition-3KLMUEC5.mjs.map +0 -7
  229. package/dist/lib/neutral/skills/index.mjs.map +0 -7
  230. package/dist/lib/neutral/sync-feed-SDXYF7HU.mjs +0 -99
  231. package/dist/lib/neutral/sync-feed-SDXYF7HU.mjs.map +0 -7
  232. package/dist/lib/neutral/translations.mjs +0 -81
  233. package/dist/lib/neutral/translations.mjs.map +0 -7
  234. package/dist/lib/neutral/types/index.mjs +0 -14
  235. package/dist/lib/neutral/types/index.mjs.map +0 -7
  236. package/dist/types/src/capabilities/navigation-resolver.d.ts +0 -5
  237. package/dist/types/src/capabilities/navigation-resolver.d.ts.map +0 -1
  238. package/dist/types/src/operations/curate-magazine.skill.test.d.ts +0 -2
  239. package/dist/types/src/operations/curate-magazine.skill.test.d.ts.map +0 -1
  240. package/src/capabilities/navigation-resolver.ts +0 -21
  241. package/src/operations/curate-magazine.skill.conversations.json +0 -1
  242. package/src/operations/curate-magazine.skill.test.ts +0 -167
@@ -10,10 +10,11 @@ import { withPluginManager } from '@dxos/app-framework/testing';
10
10
  import { type Client } from '@dxos/client';
11
11
  import { type Space } from '@dxos/client/echo';
12
12
  import { Filter, Tag } from '@dxos/echo';
13
+ import { useQuery } from '@dxos/echo-react';
13
14
  import { ClientPlugin, initializeIdentity } from '@dxos/plugin-client/testing';
14
15
  import { SpacePlugin } from '@dxos/plugin-space/testing';
15
16
  import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
16
- import { useQuery, useSpaces } from '@dxos/react-client/echo';
17
+ import { useSpaces } from '@dxos/react-client/echo';
17
18
  import { Loading, withLayout } from '@dxos/react-ui/testing';
18
19
  import { Text } from '@dxos/schema';
19
20
 
@@ -8,10 +8,10 @@ import React, { useCallback, useContext, useEffect, useMemo, useRef } from 'reac
8
8
  import { useOperationInvoker } from '@dxos/app-framework/ui';
9
9
  import { type AppSurface } from '@dxos/app-toolkit/ui';
10
10
  import { Filter, Obj, Ref } from '@dxos/echo';
11
+ import { useObject, useQuery } from '@dxos/echo-react';
11
12
  import { log } from '@dxos/log';
12
- import { useObject, useQuery } from '@dxos/react-client/echo';
13
13
  import { Panel } from '@dxos/react-ui';
14
- import { getParentId, isLinkedSegment } from '@dxos/react-ui-attention';
14
+ import { Attention } from '@dxos/react-ui-attention';
15
15
 
16
16
  import { usePostContentAtom } from '#atoms';
17
17
  import { PostContent } from '#components';
@@ -26,7 +26,8 @@ export type PostArticleProps = AppSurface.ObjectArticleProps<Subscription.Post>;
26
26
  export const PostArticle = ({ role, subject, attendableId }: PostArticleProps) => {
27
27
  // When shown as a companion the `attendableId` is a linked segment, but attention lives on the
28
28
  // parent plank — resolve to the parent so the toolbar reads as attended (active).
29
- const toolbarAttendableId = attendableId && isLinkedSegment(attendableId) ? getParentId(attendableId) : attendableId;
29
+ const toolbarAttendableId =
30
+ attendableId && Attention.isLinkedSegment(attendableId) ? Attention.getParentId(attendableId) : attendableId;
30
31
  const { invokePromise } = useOperationInvoker();
31
32
  const registry = useContext(RegistryContext);
32
33
  const [post] = useObject(subject);
@@ -138,3 +139,5 @@ export const PostArticle = ({ role, subject, attendableId }: PostArticleProps) =
138
139
  </Panel.Root>
139
140
  );
140
141
  };
142
+
143
+ PostArticle.displayName = 'PostArticle';
@@ -2,7 +2,7 @@
2
2
  // Copyright 2026 DXOS.org
3
3
  //
4
4
 
5
- import { type Atom } from '@effect-atom/atom-react';
5
+ import { type Atom } from '@effect-atom/atom';
6
6
  import React from 'react';
7
7
 
8
8
  import { Panel } from '@dxos/react-ui';
@@ -6,7 +6,7 @@ import React, { useMemo } from 'react';
6
6
 
7
7
  import { type AppSurface } from '@dxos/app-toolkit/ui';
8
8
  import { Filter, Obj } from '@dxos/echo';
9
- import { useObject, useQuery } from '@dxos/react-client/echo';
9
+ import { useObject, useQuery } from '@dxos/echo-react';
10
10
  import { Card } from '@dxos/react-ui';
11
11
 
12
12
  import { Subscription } from '#types';
@@ -68,3 +68,5 @@ export const PostCard = ({ subject }: PostCardProps) => {
68
68
  </Card.Body>
69
69
  );
70
70
  };
71
+
72
+ PostCard.displayName = 'PostCard';
@@ -10,12 +10,13 @@ import { withPluginManager } from '@dxos/app-framework/testing';
10
10
  import { type Client } from '@dxos/client';
11
11
  import { type Space } from '@dxos/client/echo';
12
12
  import { Filter } from '@dxos/echo';
13
+ import { useQuery } from '@dxos/echo-react';
13
14
  import { ClientPlugin } from '@dxos/plugin-client/testing';
14
15
  import { initializeIdentity } from '@dxos/plugin-client/testing';
15
16
  import { SpacePlugin } from '@dxos/plugin-space/testing';
16
17
  import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
17
18
  import { random } from '@dxos/random';
18
- import { useQuery, useSpaces } from '@dxos/react-client/echo';
19
+ import { useSpaces } from '@dxos/react-client/echo';
19
20
  import { Loading, withLayout } from '@dxos/react-ui/testing';
20
21
 
21
22
  import { translations } from '#translations';
@@ -8,11 +8,11 @@ import { useOperationInvoker } from '@dxos/app-framework/ui';
8
8
  import { LayoutOperation } from '@dxos/app-toolkit';
9
9
  import { type AppSurface, useLayout } from '@dxos/app-toolkit/ui';
10
10
  import { Filter, Obj, Ref, Type } from '@dxos/echo';
11
+ import { useQuery } from '@dxos/echo-react';
11
12
  import { invariant } from '@dxos/invariant';
12
13
  import { SpaceOperation } from '@dxos/plugin-space';
13
- import { useQuery } from '@dxos/react-client/echo';
14
14
  import { Panel, Toolbar, useTranslation } from '@dxos/react-ui';
15
- import { linkedSegment, useSelection } from '@dxos/react-ui-attention';
15
+ import { Attention, useSelection } from '@dxos/react-ui-attention';
16
16
 
17
17
  import { SubscriptionStack, type SubscriptionStackAction } from '#components';
18
18
  import { meta } from '#meta';
@@ -39,7 +39,7 @@ export const SubscriptionsArticle = ({ role, space, attendableId }: Subscription
39
39
  subject: { mode: 'single', id: action.feedId },
40
40
  });
41
41
 
42
- const companion = linkedSegment('feed');
42
+ const companion = Attention.linkedSegment('feed');
43
43
  if (layout.mode === 'simple') {
44
44
  void invokePromise(LayoutOperation.UpdateComplementary, {
45
45
  subject: companion,
@@ -97,3 +97,5 @@ export const SubscriptionsArticle = ({ role, space, attendableId }: Subscription
97
97
  </Panel.Root>
98
98
  );
99
99
  };
100
+
101
+ SubscriptionsArticle.displayName = 'SubscriptionsArticle';
@@ -2,14 +2,70 @@
2
2
  // Copyright 2026 DXOS.org
3
3
  //
4
4
 
5
- import { afterEach, beforeEach, describe, expect, test } from 'vitest';
6
-
7
- import { Feed, Obj, Ref, Tag } from '@dxos/echo';
5
+ import { describe, it } from '@effect/vitest';
6
+ import * as Effect from 'effect/Effect';
7
+ import { afterEach, beforeEach, expect, test } from 'vitest';
8
+
9
+ import { AssistantTestLayer } from '@dxos/agent-runtime/testing';
10
+ import { ScriptedLanguageModel } from '@dxos/ai/testing';
11
+ import { AgentHandlers } from '@dxos/assistant-toolkit';
12
+ import { Operation } from '@dxos/compute';
13
+ import { Database, Feed, Obj, Ref, Tag } from '@dxos/echo';
8
14
  import { EchoTestBuilder } from '@dxos/echo-client/testing';
15
+ import { TestHelpers } from '@dxos/effect/testing';
9
16
  import { StateMap, TagIndex, Text } from '@dxos/schema';
10
17
 
11
- import { Magazine, Subscription } from '../types';
18
+ import { MagazineSkill } from '../skills';
19
+ import { FeedOperation, Magazine, Subscription } from '../types';
12
20
  import { applyKeep, resolveSelected } from './curate-magazine';
21
+ import { MagazineOperationHandlerSet } from './index';
22
+
23
+ /**
24
+ * Whether the model picks the right articles is a judgement question, graded out-of-band; scripting
25
+ * its selection keeps this on the operation's own behaviour — that the chosen ids are resolved,
26
+ * recorded on the magazine, and counted.
27
+ *
28
+ * The script is per-test state, but the layer captures the turns array once at module load and the
29
+ * model indexes it lazily per call. Holding it in a closure gives the layer a stable reference while
30
+ * `reset` clears the script between tests, so no turn leaks from one test into the next.
31
+ */
32
+ const createScriptedSelection = () => {
33
+ const turns: ScriptedLanguageModel.ScriptedTurn[] = [];
34
+ return {
35
+ aiService: ScriptedLanguageModel.scriptedAiService(turns),
36
+
37
+ /** Scripts the agent to select `ids` and then finish. Call once the posts exist. */
38
+ select: (ids: readonly string[]) => {
39
+ turns.push(
40
+ { parts: [ScriptedLanguageModel.toolCall('completeJob', { success: { posts: ids.map((id) => ({ id })) } })] },
41
+ // The loop asks again once the tool result is fed back; a text-only turn stops it.
42
+ { parts: [ScriptedLanguageModel.text('Done.')] },
43
+ );
44
+ },
45
+
46
+ reset: () => {
47
+ turns.length = 0;
48
+ },
49
+ };
50
+ };
51
+
52
+ const scripted = createScriptedSelection();
53
+
54
+ const TestLayer = AssistantTestLayer({
55
+ operationHandlers: [MagazineOperationHandlerSet, AgentHandlers],
56
+ types: [
57
+ Feed.Feed,
58
+ Subscription.Subscription,
59
+ Subscription.Post,
60
+ Magazine.Magazine,
61
+ Tag.Tag,
62
+ Text.Text,
63
+ StateMap.StateMap,
64
+ TagIndex.TagIndex,
65
+ ],
66
+ skills: [MagazineSkill.make()],
67
+ aiService: scripted.aiService,
68
+ });
13
69
 
14
70
  describe('applyKeep', () => {
15
71
  let builder: EchoTestBuilder;
@@ -89,3 +145,51 @@ describe('resolveSelected', () => {
89
145
  expect(selected.map(({ post }) => post.id)).toEqual([posts[2].id, posts[0].id]);
90
146
  });
91
147
  });
148
+
149
+ describe('CurateMagazine', () => {
150
+ beforeEach(() => scripted.reset());
151
+ afterEach(() => scripted.reset());
152
+
153
+ it.effect(
154
+ 'records the selected posts on the magazine and counts them',
155
+ Effect.fnUntraced(
156
+ function* (_) {
157
+ const subscription = yield* Database.add(
158
+ Subscription.makeSubscription({ name: 'Tech & Science Daily', type: 'rss' }),
159
+ );
160
+ yield* Database.flush();
161
+
162
+ const postFeed = yield* Database.load(subscription.feed);
163
+ const posts = ['Mars lander touches down', 'Webb images a nebula', 'Quarterly earnings'].map((title) =>
164
+ Obj.make(Subscription.Post, {
165
+ title,
166
+ link: `https://example.com/${title}`,
167
+ published: '2026-05-01T00:00:00Z',
168
+ source: Ref.make(subscription),
169
+ }),
170
+ );
171
+ yield* Feed.append(postFeed, posts);
172
+ yield* Database.flush();
173
+
174
+ // `Magazine.make` composes the Instructions (with the CurationOutput contract) from the topic.
175
+ const magazine = yield* Database.add(
176
+ Magazine.make({
177
+ name: 'The Cosmos',
178
+ feeds: [Ref.make(subscription)],
179
+ instructions: 'Curate articles about space exploration and astronomy.',
180
+ }),
181
+ );
182
+ yield* Database.flush();
183
+
184
+ scripted.select([posts[0].id, posts[1].id]);
185
+ const result = yield* Operation.invoke(FeedOperation.CurateMagazine, { magazine: Ref.make(magazine) });
186
+
187
+ const curated = yield* Effect.forEach(magazine.posts, Database.load);
188
+ expect(curated.map((post) => post.title)).toEqual([posts[0].title, posts[1].title]);
189
+ expect(result.curated).toBe(2);
190
+ },
191
+ Effect.provide(TestLayer),
192
+ TestHelpers.provideTestContext,
193
+ ),
194
+ );
195
+ });
@@ -6,10 +6,10 @@ import { describe, it } from '@effect/vitest';
6
6
  import * as Effect from 'effect/Effect';
7
7
  import { afterEach, beforeEach, vi } from 'vitest';
8
8
 
9
+ import { AssistantTestLayer } from '@dxos/agent-runtime/testing';
9
10
  import { Operation } from '@dxos/compute';
10
11
  import { Database, Feed, Filter, Ref, Tag } from '@dxos/echo';
11
12
  import { TestHelpers } from '@dxos/effect/testing';
12
- import { AssistantTestLayer } from '@dxos/functions-runtime/testing';
13
13
  import { EntityId } from '@dxos/keys';
14
14
 
15
15
  import { FeedOperation, Subscription } from '../types';
@@ -33,7 +33,7 @@ const handler: Operation.WithHandler<typeof FeedOperation.SyncFeed> = FeedOperat
33
33
  invariant(url, 'Feed URL is required.');
34
34
  const echoFeed = yield* Database.load(subscriptionFeed.feed);
35
35
  invariant(echoFeed, 'Backing ECHO feed not found.');
36
- invariant(Feed.getQueueUri(echoFeed), 'Feed not stored in a space.');
36
+ invariant(Feed.getFeedUri(echoFeed), 'Feed not stored in a space.');
37
37
 
38
38
  const fetcher = getFetcher(subscriptionFeed.type);
39
39
  const { feed: feedMeta, posts } = yield* fetcher(url, { corsProxy: browserCorsProxy() });
package/src/paths.ts CHANGED
@@ -2,13 +2,13 @@
2
2
  // Copyright 2026 DXOS.org
3
3
  //
4
4
 
5
- import { Paths } from '@dxos/app-toolkit';
5
+ import { GraphPath } from '@dxos/app-toolkit';
6
6
 
7
7
  import { Magazine } from '#types';
8
8
 
9
- const { getSectionPath: getMagazinesPath, getObjectPath: getMagazinePath } = Paths.createTypeSectionPaths(
9
+ const { getSectionPath: getMagazinesPath, getObjectPath: getMagazinePath } = GraphPath.createTypeSectionPaths(
10
10
  Magazine.Magazine,
11
- { groupId: Paths.GroupSegments.content },
11
+ { groupId: GraphPath.GroupSegments.content },
12
12
  );
13
13
 
14
14
  export { getMagazinePath, getMagazinesPath };
@@ -70,7 +70,7 @@ const DefaultStory = () => {
70
70
  void handleFetch();
71
71
  }
72
72
  }}
73
- classNames='is-full min-is-[24rem]'
73
+ classNames='w-full min-w-[24rem]'
74
74
  />
75
75
  </Input.Root>
76
76
  <Select.Root
@@ -20,11 +20,12 @@ import { type Client } from '@dxos/client';
20
20
  import { type Space } from '@dxos/client/echo';
21
21
  import { LayerSpec } from '@dxos/compute';
22
22
  import { DXN, Feed, Filter, Ref } from '@dxos/echo';
23
+ import { useQuery } from '@dxos/echo-react';
23
24
  import { ClientPlugin, initializeIdentity } from '@dxos/plugin-client/testing';
24
25
  import { RoutinePlugin } from '@dxos/plugin-routine/plugin';
25
26
  import { SpacePlugin } from '@dxos/plugin-space/testing';
26
27
  import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
27
- import { useQuery, useSpaces } from '@dxos/react-client/echo';
28
+ import { useSpaces } from '@dxos/react-client/echo';
28
29
  import { Panel } from '@dxos/react-ui';
29
30
  import { Loading, withLayout } from '@dxos/react-ui/testing';
30
31
  import { Text } from '@dxos/schema';
@@ -106,7 +107,7 @@ const DefaultStory = () => {
106
107
 
107
108
  return (
108
109
  <Panel.Root classNames='border-is border-separator'>
109
- <Panel.Content className='px-3 grid grid-cols-2 gap-3'>
110
+ <Panel.Content classNames='px-3 grid grid-cols-2 gap-3'>
110
111
  {/* Rendered directly: the `article` role is shared with plugin-space's catch-all RecordArticle
111
112
  (position:last), so a raw `Surface type={Article}` here is ambiguous and resolves to the
112
113
  fallback — the deck disambiguates via the app-graph node, which a story has no equivalent of. */}
@@ -7,7 +7,8 @@ import * as Effect from 'effect/Effect';
7
7
  import { Trigger } from '@dxos/compute';
8
8
  import { Ref } from '@dxos/echo';
9
9
  import { invariant } from '@dxos/invariant';
10
- import { Routine, type RoutineCapabilities } from '@dxos/plugin-routine/types';
10
+ import { makeRoutine } from '@dxos/plugin-routine';
11
+ import { type RoutineCapabilities } from '@dxos/plugin-routine/types';
11
12
 
12
13
  import { FeedOperation, Magazine } from '#types';
13
14
 
@@ -33,7 +34,7 @@ export const magazineCuration: RoutineCapabilities.Template = {
33
34
  const magazine = subject;
34
35
 
35
36
  // Pre-populate the trigger's input so the magazine binding is preserved through the save flow.
36
- return Routine.make({
37
+ return makeRoutine({
37
38
  name: name ?? magazine.name ?? 'Curate Magazine',
38
39
  // Bind the CurateMagazine operation directly as the action (an operation action, not instructions-based).
39
40
  spec: { kind: 'runnable', runnable: Ref.fromURI(FeedOperation.CurateMagazine.meta.key) },
@@ -6,9 +6,9 @@ import { describe, it } from '@effect/vitest';
6
6
  import * as Effect from 'effect/Effect';
7
7
  import { test } from 'vitest';
8
8
 
9
+ import { AssistantTestLayer } from '@dxos/agent-runtime/testing';
9
10
  import { Database, Feed, Tag } from '@dxos/echo';
10
11
  import { TestHelpers } from '@dxos/effect/testing';
11
- import { AssistantTestLayer } from '@dxos/functions-runtime/testing';
12
12
  import { EntityId } from '@dxos/keys';
13
13
  import { StateMap, TagIndex, Text } from '@dxos/schema';
14
14
 
@@ -171,7 +171,7 @@ export class Post extends Type.makeObject<Post>(DXN.make('org.dxos.type.subscrip
171
171
  guid: Schema.String.pipe(Schema.optional),
172
172
  }).pipe(
173
173
  LabelAnnotation.set(['title']),
174
- Annotation.IconAnnotation.set({ icon: 'ph--article--regular', hue: 'orange' }),
174
+ Annotation.IconAnnotation.set({ icon: 'ph--article--regular', hue: 'indigo' }),
175
175
  ),
176
176
  ) {}
177
177
 
@@ -1,90 +0,0 @@
1
- import "./chunk-J5LGTIGS.mjs";
2
-
3
- // src/containers/FeedArticle/FeedArticle.tsx
4
- import React2, { useCallback, useState } from "react";
5
- import { useOperationInvoker } from "@dxos/app-framework/ui";
6
- import { Filter, Obj, Query, Ref, Scope } from "@dxos/echo";
7
- import { useObject, useQuery } from "@dxos/react-client/echo";
8
- import { Panel as Panel2 } from "@dxos/react-ui";
9
- import { PostStack } from "#components";
10
- import { meta as meta2 } from "#meta";
11
- import { FeedOperation } from "#types";
12
- import { Subscription } from "#types";
13
-
14
- // src/containers/FeedArticle/FeedToolbar.tsx
15
- import React from "react";
16
- import { Panel } from "@dxos/react-ui";
17
- import { Menu, MenuBuilder, useMenuBuilder } from "@dxos/react-ui-menu";
18
- import { meta } from "#meta";
19
- var FeedToolbar = ({ attendableId, onSync }) => {
20
- const menuActions = useMenuBuilder(() => MenuBuilder.make().action("sync", {
21
- label: [
22
- "sync-feed.label",
23
- {
24
- ns: meta.profile.key
25
- }
26
- ],
27
- icon: "ph--arrows-clockwise--regular",
28
- iconOnly: true
29
- }, onSync).build(), [
30
- onSync
31
- ]);
32
- return /* @__PURE__ */ React.createElement(Menu.Root, {
33
- ...menuActions,
34
- attendableId
35
- }, /* @__PURE__ */ React.createElement(Panel.Toolbar, {
36
- asChild: true
37
- }, /* @__PURE__ */ React.createElement(Menu.Toolbar, null)));
38
- };
39
- FeedToolbar.displayName = "FeedToolbar";
40
-
41
- // src/containers/FeedArticle/FeedArticle.tsx
42
- var FeedArticle = ({ role, subject, attendableId }) => {
43
- const { invokePromise } = useOperationInvoker();
44
- const [currentPostId, setCurrentPostId] = useState();
45
- const [subscription] = useObject(subject);
46
- const [postFeed] = useObject(subscription?.feed);
47
- const db = Obj.getDatabase(subscription);
48
- const posts = useQuery(db, postFeed ? Query.select(Filter.type(Subscription.Post)).from(Scope.feed(Obj.getURI(postFeed))) : Query.select(Filter.nothing()));
49
- const handleAction = useCallback((action) => {
50
- if (action.type === "current") {
51
- setCurrentPostId(action.postId);
52
- }
53
- }, []);
54
- const handleSync = useCallback(() => {
55
- void invokePromise(FeedOperation.SyncFeed, {
56
- feed: Ref.make(subject)
57
- }, {
58
- spaceId: Obj.getDatabase(subject)?.spaceId,
59
- notify: {
60
- error: [
61
- "sync-feed-error.title",
62
- {
63
- ns: meta2.profile.key
64
- }
65
- ]
66
- }
67
- });
68
- }, [
69
- subject,
70
- invokePromise
71
- ]);
72
- return /* @__PURE__ */ React2.createElement(Panel2.Root, {
73
- role,
74
- className: "dx-document"
75
- }, /* @__PURE__ */ React2.createElement(FeedToolbar, {
76
- attendableId,
77
- onSync: handleSync
78
- }), /* @__PURE__ */ React2.createElement(Panel2.Content, {
79
- asChild: true
80
- }, /* @__PURE__ */ React2.createElement(PostStack, {
81
- id: subscription?.id ?? subject.id,
82
- posts,
83
- currentId: currentPostId,
84
- onAction: handleAction
85
- })));
86
- };
87
- export {
88
- FeedArticle as default
89
- };
90
- //# sourceMappingURL=FeedArticle-PEWCFFSP.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/containers/FeedArticle/FeedArticle.tsx", "../../../src/containers/FeedArticle/FeedToolbar.tsx"],
4
- "sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport React, { useCallback, useState } from 'react';\n\nimport { useOperationInvoker } from '@dxos/app-framework/ui';\nimport { type AppSurface } from '@dxos/app-toolkit/ui';\nimport { Filter, Obj, Query, Ref, Scope } from '@dxos/echo';\nimport { useObject, useQuery } from '@dxos/react-client/echo';\nimport { Panel } from '@dxos/react-ui';\n\nimport { PostStack, type PostStackAction } from '#components';\nimport { meta } from '#meta';\nimport { FeedOperation } from '#types';\nimport { Subscription } from '#types';\n\nimport { FeedToolbar } from './FeedToolbar';\n\nexport type FeedArticleProps = AppSurface.ObjectArticleProps<Subscription.Subscription>;\n\nexport const FeedArticle = ({ role, subject, attendableId }: FeedArticleProps) => {\n const { invokePromise } = useOperationInvoker();\n const [currentPostId, setCurrentPostId] = useState<string>();\n const [subscription] = useObject(subject);\n // Subscribe to the backing queue via its Ref — `.target` alone does not re-render when the\n // feed loads after navigation (same pitfall as plugin-inbox MailboxArticle).\n const [postFeed] = useObject(subscription?.feed);\n const db = Obj.getDatabase(subscription);\n const posts = useQuery(\n db,\n postFeed\n ? Query.select(Filter.type(Subscription.Post)).from(Scope.feed(Obj.getURI(postFeed)))\n : Query.select(Filter.nothing()),\n );\n\n const handleAction = useCallback((action: PostStackAction) => {\n if (action.type === 'current') {\n setCurrentPostId(action.postId);\n }\n }, []);\n\n const handleSync = useCallback(() => {\n // Failures surface as a toast via `notify`; invokePromise resolves with `{ error }`, never throws.\n void invokePromise(\n FeedOperation.SyncFeed,\n { feed: Ref.make(subject) },\n {\n spaceId: Obj.getDatabase(subject)?.spaceId,\n notify: { error: ['sync-feed-error.title', { ns: meta.profile.key }] },\n },\n );\n }, [subject, invokePromise]);\n\n return (\n <Panel.Root role={role} className='dx-document'>\n <FeedToolbar attendableId={attendableId} onSync={handleSync} />\n <Panel.Content asChild>\n <PostStack\n id={subscription?.id ?? subject.id}\n posts={posts}\n currentId={currentPostId}\n onAction={handleAction}\n />\n </Panel.Content>\n </Panel.Root>\n );\n};\n", "//\n// Copyright 2026 DXOS.org\n//\n\nimport React from 'react';\n\nimport { Panel } from '@dxos/react-ui';\nimport { Menu, MenuBuilder, useMenuBuilder } from '@dxos/react-ui-menu';\n\nimport { meta } from '#meta';\n\nexport type FeedToolbarProps = {\n attendableId?: string;\n onSync: () => void;\n};\n\n/**\n * Reactive feed toolbar built from the menu action-graph idiom: a single Sync action. Matches the\n * magazine/post toolbars; the feed's name is surfaced by the plank chrome, not duplicated here.\n */\nexport const FeedToolbar = ({ attendableId, onSync }: FeedToolbarProps) => {\n const menuActions = useMenuBuilder(\n () =>\n MenuBuilder.make()\n .action(\n 'sync',\n {\n label: ['sync-feed.label', { ns: meta.profile.key }],\n icon: 'ph--arrows-clockwise--regular',\n iconOnly: true,\n },\n onSync,\n )\n .build(),\n [onSync],\n );\n\n return (\n <Menu.Root {...menuActions} attendableId={attendableId}>\n <Panel.Toolbar asChild>\n <Menu.Toolbar />\n </Panel.Toolbar>\n </Menu.Root>\n );\n};\n\nFeedToolbar.displayName = 'FeedToolbar';\n"],
5
- "mappings": ";;;AAIA,OAAOA,UAASC,aAAaC,gBAAgB;AAE7C,SAASC,2BAA2B;AAEpC,SAASC,QAAQC,KAAKC,OAAOC,KAAKC,aAAa;AAC/C,SAASC,WAAWC,gBAAgB;AACpC,SAASC,SAAAA,cAAa;AAEtB,SAASC,iBAAuC;AAChD,SAASC,QAAAA,aAAY;AACrB,SAASC,qBAAqB;AAC9B,SAASC,oBAAoB;;;ACX7B,OAAOC,WAAW;AAElB,SAASC,aAAa;AACtB,SAASC,MAAMC,aAAaC,sBAAsB;AAElD,SAASC,YAAY;AAWd,IAAMC,cAAc,CAAC,EAAEC,cAAcC,OAAM,MAAoB;AACpE,QAAMC,cAAcL,eAClB,MACED,YAAYO,KAAI,EACbC,OACC,QACA;IACEC,OAAO;MAAC;MAAmB;QAAEC,IAAIR,KAAKS,QAAQC;MAAI;;IAClDC,MAAM;IACNC,UAAU;EACZ,GACAT,MAAAA,EAEDU,MAAK,GACV;IAACV;GAAO;AAGV,SACE,sBAAA,cAACN,KAAKiB,MAAI;IAAE,GAAGV;IAAaF;KAC1B,sBAAA,cAACN,MAAMmB,SAAO;IAACC,SAAAA;KACb,sBAAA,cAACnB,KAAKkB,SAAO,IAAA,CAAA,CAAA;AAIrB;AAEAd,YAAYgB,cAAc;;;ADzBnB,IAAMC,cAAc,CAAC,EAAEC,MAAMC,SAASC,aAAY,MAAoB;AAC3E,QAAM,EAAEC,cAAa,IAAKC,oBAAAA;AAC1B,QAAM,CAACC,eAAeC,gBAAAA,IAAoBC,SAAAA;AAC1C,QAAM,CAACC,YAAAA,IAAgBC,UAAUR,OAAAA;AAGjC,QAAM,CAACS,QAAAA,IAAYD,UAAUD,cAAcG,IAAAA;AAC3C,QAAMC,KAAKC,IAAIC,YAAYN,YAAAA;AAC3B,QAAMO,QAAQC,SACZJ,IACAF,WACIO,MAAMC,OAAOC,OAAOC,KAAKC,aAAaC,IAAI,CAAA,EAAGC,KAAKC,MAAMb,KAAKE,IAAIY,OAAOf,QAAAA,CAAAA,CAAAA,IACxEO,MAAMC,OAAOC,OAAOO,QAAO,CAAA,CAAA;AAGjC,QAAMC,eAAeC,YAAY,CAACC,WAAAA;AAChC,QAAIA,OAAOT,SAAS,WAAW;AAC7Bd,uBAAiBuB,OAAOC,MAAM;IAChC;EACF,GAAG,CAAA,CAAE;AAEL,QAAMC,aAAaH,YAAY,MAAA;AAE7B,SAAKzB,cACH6B,cAAcC,UACd;MAAEtB,MAAMuB,IAAIC,KAAKlC,OAAAA;IAAS,GAC1B;MACEmC,SAASvB,IAAIC,YAAYb,OAAAA,GAAUmC;MACnCC,QAAQ;QAAEC,OAAO;UAAC;UAAyB;YAAEC,IAAIC,MAAKC,QAAQC;UAAI;;MAAG;IACvE,CAAA;EAEJ,GAAG;IAACzC;IAASE;GAAc;AAE3B,SACE,gBAAAwC,OAAA,cAACC,OAAMC,MAAI;IAAC7C;IAAY8C,WAAU;KAChC,gBAAAH,OAAA,cAACI,aAAAA;IAAY7C;IAA4B8C,QAAQjB;MACjD,gBAAAY,OAAA,cAACC,OAAMK,SAAO;IAACC,SAAAA;KACb,gBAAAP,OAAA,cAACQ,WAAAA;IACCC,IAAI5C,cAAc4C,MAAMnD,QAAQmD;IAChCrC;IACAsC,WAAWhD;IACXiD,UAAU3B;;AAKpB;",
6
- "names": ["React", "useCallback", "useState", "useOperationInvoker", "Filter", "Obj", "Query", "Ref", "Scope", "useObject", "useQuery", "Panel", "PostStack", "meta", "FeedOperation", "Subscription", "React", "Panel", "Menu", "MenuBuilder", "useMenuBuilder", "meta", "FeedToolbar", "attendableId", "onSync", "menuActions", "make", "action", "label", "ns", "profile", "key", "icon", "iconOnly", "build", "Root", "Toolbar", "asChild", "displayName", "FeedArticle", "role", "subject", "attendableId", "invokePromise", "useOperationInvoker", "currentPostId", "setCurrentPostId", "useState", "subscription", "useObject", "postFeed", "feed", "db", "Obj", "getDatabase", "posts", "useQuery", "Query", "select", "Filter", "type", "Subscription", "Post", "from", "Scope", "getURI", "nothing", "handleAction", "useCallback", "action", "postId", "handleSync", "FeedOperation", "SyncFeed", "Ref", "make", "spaceId", "notify", "error", "ns", "meta", "profile", "key", "React", "Panel", "Root", "className", "FeedToolbar", "onSync", "Content", "asChild", "PostStack", "id", "currentId", "onAction"]
7
- }
@@ -1,97 +0,0 @@
1
- import "./chunk-J5LGTIGS.mjs";
2
-
3
- // src/containers/FeedProperties/FeedProperties.tsx
4
- import React, { useCallback, useMemo, useState } from "react";
5
- import { useOperationInvoker } from "@dxos/app-framework/ui";
6
- import { LayoutOperation, Paths } from "@dxos/app-toolkit";
7
- import { Operation, Trigger } from "@dxos/compute";
8
- import { Filter, Obj, Query, Ref } from "@dxos/echo";
9
- import { useObject, useQuery } from "@dxos/react-client/echo";
10
- import { IconButton, Input, useTranslation } from "@dxos/react-ui";
11
- import { Form } from "@dxos/react-ui-form";
12
- import { meta } from "#meta";
13
- import { FeedOperation } from "#types";
14
- var FeedProperties = ({ subject }) => {
15
- const { t } = useTranslation(meta.profile.key);
16
- const { invokePromise } = useOperationInvoker();
17
- const db = useMemo(() => Obj.getDatabase(subject), [
18
- subject
19
- ]);
20
- const [pending, setPending] = useState(false);
21
- const childTriggers = useQuery(db, Query.select(Filter.and(Filter.type(Trigger.Trigger), Filter.childOf(subject, {
22
- transitive: false
23
- }))).debugLabel("plugin-magazine.FeedProperties.syncTrigger"));
24
- const syncTrigger = useMemo(() => childTriggers.find((trigger) => trigger.spec?.kind === "timer"), [
25
- childTriggers
26
- ]);
27
- const [syncEnabled, setSyncEnabled] = useObject(syncTrigger, "enabled");
28
- const handleToggleSync = useCallback(async () => {
29
- if (!db) {
30
- return;
31
- }
32
- if (syncTrigger) {
33
- setSyncEnabled((enabled) => !enabled);
34
- return;
35
- }
36
- setPending(true);
37
- try {
38
- const operation = await ensureSyncFeedOperation(db);
39
- db.add(Trigger.make({
40
- [Obj.Parent]: subject,
41
- enabled: true,
42
- spec: Trigger.specTimer("*/5 * * * *"),
43
- runnable: Ref.make(operation),
44
- input: {
45
- feed: db.makeRef(Obj.getURI(subject))
46
- }
47
- }));
48
- } finally {
49
- setPending(false);
50
- }
51
- }, [
52
- syncTrigger,
53
- db,
54
- subject,
55
- setSyncEnabled
56
- ]);
57
- const handleViewTrigger = useCallback(() => {
58
- if (!db) {
59
- return;
60
- }
61
- void invokePromise(LayoutOperation.Open, {
62
- subject: [
63
- Paths.getSpacePath(db.spaceId, "settings", "org.dxos.plugin.routine.routines")
64
- ],
65
- workspace: Paths.getSpacePath(db.spaceId)
66
- });
67
- }, [
68
- invokePromise,
69
- db
70
- ]);
71
- return /* @__PURE__ */ React.createElement(Form.Section, null, /* @__PURE__ */ React.createElement(Input.Root, null, /* @__PURE__ */ React.createElement(Input.Label, null, t("feed-sync.label")), /* @__PURE__ */ React.createElement("div", {
72
- className: "flex flex-row items-center"
73
- }, /* @__PURE__ */ React.createElement(Input.Switch, {
74
- checked: syncEnabled ?? false,
75
- disabled: pending,
76
- onCheckedChange: () => {
77
- void handleToggleSync();
78
- }
79
- }), syncTrigger && /* @__PURE__ */ React.createElement(IconButton, {
80
- iconOnly: true,
81
- icon: "ph--gear--regular",
82
- label: t("view-trigger.label"),
83
- onClick: handleViewTrigger
84
- }))));
85
- };
86
- var ensureSyncFeedOperation = async (db) => {
87
- const existing = await db.query(Filter.and(Filter.type(Operation.PersistentOperation), Filter.key(FeedOperation.SyncFeed.meta.key))).run();
88
- const [existingOperation] = existing;
89
- if (existingOperation) {
90
- return existingOperation;
91
- }
92
- return db.add(Operation.serialize(FeedOperation.SyncFeed));
93
- };
94
- export {
95
- FeedProperties as default
96
- };
97
- //# sourceMappingURL=FeedProperties-EIO7IZOA.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/containers/FeedProperties/FeedProperties.tsx"],
4
- "sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport React, { useCallback, useMemo, useState } from 'react';\n\nimport { useOperationInvoker } from '@dxos/app-framework/ui';\nimport { LayoutOperation, Paths } from '@dxos/app-toolkit';\nimport { type AppSurface } from '@dxos/app-toolkit/ui';\nimport { Operation, Trigger } from '@dxos/compute';\nimport { Filter, Obj, Query, Ref } from '@dxos/echo';\nimport { useObject, useQuery } from '@dxos/react-client/echo';\nimport { IconButton, Input, useTranslation } from '@dxos/react-ui';\nimport { Form } from '@dxos/react-ui-form';\n\nimport { meta } from '#meta';\nimport { FeedOperation, Subscription } from '#types';\n\nexport type FeedPropertiesProps = AppSurface.ObjectPropertiesProps<Subscription.Subscription>;\n\nexport const FeedProperties = ({ subject }: FeedPropertiesProps) => {\n const { t } = useTranslation(meta.profile.key);\n const { invokePromise } = useOperationInvoker();\n const db = useMemo(() => Obj.getDatabase(subject), [subject]);\n const [pending, setPending] = useState(false);\n\n const childTriggers = useQuery(\n db,\n Query.select(Filter.and(Filter.type(Trigger.Trigger), Filter.childOf(subject, { transitive: false }))).debugLabel(\n 'plugin-magazine.FeedProperties.syncTrigger',\n ),\n );\n\n const syncTrigger = useMemo(() => childTriggers.find((trigger) => trigger.spec?.kind === 'timer'), [childTriggers]);\n\n const [syncEnabled, setSyncEnabled] = useObject(syncTrigger, 'enabled');\n\n const handleToggleSync = useCallback(async () => {\n if (!db) {\n return;\n }\n\n if (syncTrigger) {\n setSyncEnabled((enabled) => !enabled);\n return;\n }\n\n setPending(true);\n try {\n const operation = await ensureSyncFeedOperation(db);\n db.add(\n Trigger.make({\n [Obj.Parent]: subject,\n enabled: true,\n spec: Trigger.specTimer('*/5 * * * *'),\n runnable: Ref.make(operation),\n input: { feed: db.makeRef(Obj.getURI(subject)) },\n }),\n );\n } finally {\n setPending(false);\n }\n }, [syncTrigger, db, subject, setSyncEnabled]);\n\n const handleViewTrigger = useCallback(() => {\n if (!db) {\n return;\n }\n\n void invokePromise(LayoutOperation.Open, {\n subject: [Paths.getSpacePath(db.spaceId, 'settings', 'org.dxos.plugin.routine.routines')],\n workspace: Paths.getSpacePath(db.spaceId),\n });\n }, [invokePromise, db]);\n\n return (\n <Form.Section>\n <Input.Root>\n <Input.Label>{t('feed-sync.label')}</Input.Label>\n <div className='flex flex-row items-center'>\n <Input.Switch\n checked={syncEnabled ?? false}\n disabled={pending}\n onCheckedChange={() => {\n void handleToggleSync();\n }}\n />\n {syncTrigger && (\n <IconButton iconOnly icon='ph--gear--regular' label={t('view-trigger.label')} onClick={handleViewTrigger} />\n )}\n </div>\n </Input.Root>\n </Form.Section>\n );\n};\n\nconst ensureSyncFeedOperation = async (db: NonNullable<ReturnType<typeof Obj.getDatabase>>) => {\n const existing = await db\n .query(Filter.and(Filter.type(Operation.PersistentOperation), Filter.key(FeedOperation.SyncFeed.meta.key)))\n .run();\n const [existingOperation] = existing;\n if (existingOperation) {\n return existingOperation;\n }\n\n return db.add(Operation.serialize(FeedOperation.SyncFeed));\n};\n"],
5
- "mappings": "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",
6
- "names": ["React", "useCallback", "useMemo", "useState", "useOperationInvoker", "LayoutOperation", "Paths", "Operation", "Trigger", "Filter", "Obj", "Query", "Ref", "useObject", "useQuery", "IconButton", "Input", "useTranslation", "Form", "meta", "FeedOperation", "FeedProperties", "subject", "t", "profile", "key", "invokePromise", "db", "getDatabase", "pending", "setPending", "childTriggers", "select", "and", "type", "childOf", "transitive", "debugLabel", "syncTrigger", "find", "trigger", "spec", "kind", "syncEnabled", "setSyncEnabled", "handleToggleSync", "enabled", "operation", "ensureSyncFeedOperation", "add", "make", "Parent", "specTimer", "runnable", "input", "feed", "makeRef", "getURI", "handleViewTrigger", "Open", "getSpacePath", "spaceId", "workspace", "Section", "Root", "Label", "div", "className", "Switch", "checked", "disabled", "onCheckedChange", "iconOnly", "icon", "label", "onClick", "existing", "query", "PersistentOperation", "SyncFeed", "run", "existingOperation", "serialize"]
7
- }