@dxos/plugin-thread 0.6.12 → 0.6.13-main.09887cd

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 (92) hide show
  1. package/dist/lib/browser/{ThreadArticle-O64ZECMF.mjs → ThreadArticle-GBPIH47W.mjs} +3 -3
  2. package/dist/lib/browser/{ThreadMain-ZDCRC6RG.mjs → ThreadMain-PGF7W5EV.mjs} +3 -3
  3. package/dist/lib/browser/chunk-5G3KL6TC.mjs +16 -0
  4. package/dist/lib/browser/chunk-5G3KL6TC.mjs.map +7 -0
  5. package/dist/lib/browser/{chunk-GKPZHUT3.mjs → chunk-JFQZ5W2I.mjs} +2 -2
  6. package/dist/lib/browser/{chunk-GKPZHUT3.mjs.map → chunk-JFQZ5W2I.mjs.map} +1 -1
  7. package/dist/lib/browser/{chunk-P7ZE5N5M.mjs → chunk-O4YRHIZC.mjs} +13 -11
  8. package/dist/lib/browser/chunk-O4YRHIZC.mjs.map +7 -0
  9. package/dist/lib/browser/index.mjs +411 -393
  10. package/dist/lib/browser/index.mjs.map +4 -4
  11. package/dist/lib/browser/meta.json +1 -1
  12. package/dist/lib/browser/meta.mjs +1 -1
  13. package/dist/lib/browser/types.mjs +2 -2
  14. package/dist/lib/node/{ThreadArticle-DO56JXBU.cjs → ThreadArticle-GH5UBWFU.cjs} +7 -7
  15. package/dist/lib/node/{ThreadMain-XJX75GHG.cjs → ThreadMain-CO6I3DNX.cjs} +7 -7
  16. package/dist/lib/node/{chunk-5PZNPPQF.cjs → chunk-FDYRG7HE.cjs} +65 -63
  17. package/dist/lib/node/chunk-FDYRG7HE.cjs.map +7 -0
  18. package/dist/lib/node/{chunk-D5GOVXHS.cjs → chunk-FOZUQZF4.cjs} +6 -6
  19. package/dist/lib/node/{chunk-D5GOVXHS.cjs.map → chunk-FOZUQZF4.cjs.map} +1 -1
  20. package/dist/lib/node/{chunk-BWBONZGU.cjs → chunk-JIJ7Z6NO.cjs} +7 -22
  21. package/dist/lib/node/chunk-JIJ7Z6NO.cjs.map +7 -0
  22. package/dist/lib/node/index.cjs +442 -428
  23. package/dist/lib/node/index.cjs.map +4 -4
  24. package/dist/lib/node/meta.cjs +4 -4
  25. package/dist/lib/node/meta.cjs.map +1 -1
  26. package/dist/lib/node/meta.json +1 -1
  27. package/dist/lib/node/types.cjs +3 -3
  28. package/dist/lib/node/types.cjs.map +1 -1
  29. package/dist/lib/node-esm/ThreadArticle-ZTYD52TR.mjs +19 -0
  30. package/dist/lib/node-esm/ThreadArticle-ZTYD52TR.mjs.map +7 -0
  31. package/dist/lib/node-esm/ThreadMain-LEHGOF7R.mjs +27 -0
  32. package/dist/lib/node-esm/ThreadMain-LEHGOF7R.mjs.map +7 -0
  33. package/dist/lib/node-esm/chunk-DC5ZA6H5.mjs +18 -0
  34. package/dist/lib/node-esm/chunk-DC5ZA6H5.mjs.map +7 -0
  35. package/dist/lib/node-esm/chunk-XOVJ65DG.mjs +413 -0
  36. package/dist/lib/node-esm/chunk-XOVJ65DG.mjs.map +7 -0
  37. package/dist/lib/node-esm/chunk-ZXIPFXSA.mjs +21 -0
  38. package/dist/lib/node-esm/chunk-ZXIPFXSA.mjs.map +7 -0
  39. package/dist/lib/node-esm/index.mjs +1121 -0
  40. package/dist/lib/node-esm/index.mjs.map +7 -0
  41. package/dist/lib/node-esm/meta.json +1 -0
  42. package/dist/lib/node-esm/meta.mjs +12 -0
  43. package/dist/lib/node-esm/meta.mjs.map +7 -0
  44. package/dist/lib/node-esm/types.mjs +9 -0
  45. package/dist/lib/node-esm/types.mjs.map +7 -0
  46. package/dist/types/src/ThreadPlugin.d.ts.map +1 -1
  47. package/dist/types/src/components/ChatContainer.d.ts.map +1 -1
  48. package/dist/types/src/components/CommentContainer.d.ts +1 -1
  49. package/dist/types/src/components/CommentContainer.d.ts.map +1 -1
  50. package/dist/types/src/components/CommentsContainer.d.ts +1 -5
  51. package/dist/types/src/components/CommentsContainer.d.ts.map +1 -1
  52. package/dist/types/src/components/MessageContainer.d.ts.map +1 -1
  53. package/dist/types/src/components/ThreadComplementary.d.ts +10 -0
  54. package/dist/types/src/components/ThreadComplementary.d.ts.map +1 -0
  55. package/dist/types/src/components/index.d.ts +3 -2
  56. package/dist/types/src/components/index.d.ts.map +1 -1
  57. package/dist/types/src/components/testing.d.ts +2 -0
  58. package/dist/types/src/components/testing.d.ts.map +1 -1
  59. package/dist/types/src/extensions/index.d.ts +2 -0
  60. package/dist/types/src/extensions/index.d.ts.map +1 -0
  61. package/dist/types/src/extensions/threads.d.ts +9 -0
  62. package/dist/types/src/extensions/threads.d.ts.map +1 -0
  63. package/dist/types/src/meta.d.ts +3 -9
  64. package/dist/types/src/meta.d.ts.map +1 -1
  65. package/dist/types/src/types.d.ts +6 -1
  66. package/dist/types/src/types.d.ts.map +1 -1
  67. package/package.json +40 -37
  68. package/src/ThreadPlugin.tsx +120 -310
  69. package/src/components/ChatContainer.tsx +7 -8
  70. package/src/components/CommentContainer.tsx +18 -19
  71. package/src/components/Comments.stories.tsx +2 -2
  72. package/src/components/CommentsContainer.tsx +18 -31
  73. package/src/components/MessageContainer.tsx +2 -1
  74. package/src/components/ThreadComplementary.tsx +113 -0
  75. package/src/components/index.ts +3 -2
  76. package/src/extensions/index.ts +5 -0
  77. package/src/extensions/threads.ts +117 -0
  78. package/src/meta.ts +15 -0
  79. package/src/types.ts +8 -3
  80. package/dist/lib/browser/chunk-5IBJO53M.mjs +0 -21
  81. package/dist/lib/browser/chunk-5IBJO53M.mjs.map +0 -7
  82. package/dist/lib/browser/chunk-P7ZE5N5M.mjs.map +0 -7
  83. package/dist/lib/node/chunk-5PZNPPQF.cjs.map +0 -7
  84. package/dist/lib/node/chunk-BWBONZGU.cjs.map +0 -7
  85. package/dist/types/src/types.test.d.ts +0 -2
  86. package/dist/types/src/types.test.d.ts.map +0 -1
  87. package/src/meta.tsx +0 -20
  88. package/src/types.test.ts +0 -13
  89. /package/dist/lib/browser/{ThreadArticle-O64ZECMF.mjs.map → ThreadArticle-GBPIH47W.mjs.map} +0 -0
  90. /package/dist/lib/browser/{ThreadMain-ZDCRC6RG.mjs.map → ThreadMain-PGF7W5EV.mjs.map} +0 -0
  91. /package/dist/lib/node/{ThreadArticle-DO56JXBU.cjs.map → ThreadArticle-GH5UBWFU.cjs.map} +0 -0
  92. /package/dist/lib/node/{ThreadMain-XJX75GHG.cjs.map → ThreadMain-CO6I3DNX.cjs.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ThreadAction
3
- } from "./chunk-GKPZHUT3.mjs";
3
+ } from "./chunk-JFQZ5W2I.mjs";
4
4
  import {
5
5
  ChatContainer,
6
6
  ChatHeading,
@@ -10,71 +10,49 @@ import {
10
10
  useAnalyticsCallback,
11
11
  useOnEditAnalytics,
12
12
  useStatus
13
- } from "./chunk-P7ZE5N5M.mjs";
13
+ } from "./chunk-O4YRHIZC.mjs";
14
14
  import {
15
15
  THREAD_ITEM,
16
16
  THREAD_PLUGIN,
17
17
  meta_default
18
- } from "./chunk-5IBJO53M.mjs";
18
+ } from "./chunk-5G3KL6TC.mjs";
19
19
 
20
20
  // packages/plugins/plugin-thread/src/ThreadPlugin.tsx
21
- import { Chat } from "@phosphor-icons/react";
22
- import { computed, effect } from "@preact/signals-core";
23
- import React5 from "react";
24
- import { LayoutAction, NavigationAction, parseIntentPlugin, parseNavigationPlugin, resolvePlugin, parseGraphPlugin, SLUG_PATH_SEPARATOR, SLUG_COLLECTION_INDICATOR, isLayoutParts, parseMetadataResolverPlugin, useResolvePlugins } from "@dxos/app-framework";
25
- import { getTypename } from "@dxos/echo-schema";
26
- import { create as create2 } from "@dxos/echo-schema";
21
+ import React6 from "react";
22
+ import { NavigationAction, isLayoutParts, parseIntentPlugin, parseNavigationPlugin, parseMetadataResolverPlugin, resolvePlugin } from "@dxos/app-framework";
23
+ import { create as create2, getTypename } from "@dxos/echo-schema";
27
24
  import { LocalStorageStore } from "@dxos/local-storage";
28
- import { parseAttentionPlugin } from "@dxos/plugin-attention";
25
+ import { log } from "@dxos/log";
29
26
  import { parseClientPlugin } from "@dxos/plugin-client";
30
- import { createExtension, isActionGroup } from "@dxos/plugin-graph";
31
- import { DocumentType } from "@dxos/plugin-markdown/types";
27
+ import { createExtension, isActionGroup, toSignal } from "@dxos/plugin-graph";
32
28
  import { ObservabilityAction } from "@dxos/plugin-observability/meta";
33
29
  import { SpaceAction } from "@dxos/plugin-space";
34
- import { ThreadType, MessageType as MessageType2, ChannelType } from "@dxos/plugin-space/types";
35
- import { getSpace as getSpace2, getTextInRange, Filter, createDocAccessor, fullyQualifiedId, loadObjectReferences } from "@dxos/react-client/echo";
36
- import { ScrollArea } from "@dxos/react-ui";
37
- import { createAttendableAttributes } from "@dxos/react-ui-attention";
38
- import { comments, createExternalCommentSync, listener as listener2 } from "@dxos/react-ui-editor";
30
+ import { ThreadType as ThreadType2, MessageType as MessageType2, ChannelType } from "@dxos/plugin-space/types";
31
+ import { getSpace as getSpace3, fullyQualifiedId as fullyQualifiedId5, loadObjectReferences, parseFullyQualifiedId } from "@dxos/react-client/echo";
39
32
  import { translations as threadTranslations } from "@dxos/react-ui-thread";
40
- import { nonNullable as nonNullable2 } from "@dxos/util";
41
33
 
42
34
  // packages/plugins/plugin-thread/src/components/index.ts
43
- import React4 from "react";
44
-
45
- // packages/plugins/plugin-thread/src/components/ThreadSettings.tsx
46
- import React from "react";
47
- import { SettingsValue } from "@dxos/plugin-settings";
48
- import { Input, useTranslation } from "@dxos/react-ui";
49
- var ThreadSettings = ({ settings }) => {
50
- const { t } = useTranslation(THREAD_PLUGIN);
51
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(SettingsValue, {
52
- label: t("settings standalone label")
53
- }, /* @__PURE__ */ React.createElement(Input.Switch, {
54
- checked: settings.standalone,
55
- onCheckedChange: (checked) => settings.standalone = !!checked
56
- })));
57
- };
35
+ import React5 from "react";
58
36
 
59
37
  // packages/plugins/plugin-thread/src/components/CommentContainer.tsx
60
38
  import { CheckCircle, X } from "@phosphor-icons/react";
61
- import React2, { useCallback, useEffect, useMemo, useRef, useState } from "react";
39
+ import React, { useCallback, useMemo, useRef, useState } from "react";
62
40
  import { create } from "@dxos/echo-schema";
63
41
  import { MessageType } from "@dxos/plugin-space/types";
64
- import { getSpace, useMembers } from "@dxos/react-client/echo";
42
+ import { fullyQualifiedId, getSpace, useMembers } from "@dxos/react-client/echo";
65
43
  import { useIdentity } from "@dxos/react-client/halo";
66
- import { Button, Tag, Tooltip, useThemeContext, useTranslation as useTranslation2 } from "@dxos/react-ui";
44
+ import { Button, Tag, Tooltip, useThemeContext, useTranslation } from "@dxos/react-ui";
67
45
  import { createBasicExtensions, createThemeExtensions, listener } from "@dxos/react-ui-editor";
68
46
  import { getSize, hoverableControlItem, hoverableControls, hoverableFocusedWithinControls, mx } from "@dxos/react-ui-theme";
69
47
  import { MessageTextbox, Thread, ThreadFooter, ThreadHeading } from "@dxos/react-ui-thread";
70
48
  import { nonNullable } from "@dxos/util";
71
49
  var sizeClass = getSize(4);
72
50
  var ToggleResolvedButton = ({ isResolved, onResolve }) => {
73
- const { t } = useTranslation2(THREAD_PLUGIN);
51
+ const { t } = useTranslation(THREAD_PLUGIN);
74
52
  const label = t(isResolved ? "mark as unresolved label" : "mark as resolved label");
75
- return /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
53
+ return /* @__PURE__ */ React.createElement(Tooltip.Root, null, /* @__PURE__ */ React.createElement(Tooltip.Trigger, {
76
54
  asChild: true
77
- }, /* @__PURE__ */ React2.createElement(Button, {
55
+ }, /* @__PURE__ */ React.createElement(Button, {
78
56
  variant: "ghost",
79
57
  "data-testid": "thread.toggle-resolved",
80
58
  onClick: onResolve,
@@ -82,23 +60,23 @@ var ToggleResolvedButton = ({ isResolved, onResolve }) => {
82
60
  "min-bs-0 p-1",
83
61
  !isResolved && hoverableControlItem
84
62
  ]
85
- }, /* @__PURE__ */ React2.createElement("span", {
63
+ }, /* @__PURE__ */ React.createElement("span", {
86
64
  className: "sr-only"
87
- }, label), isResolved ? /* @__PURE__ */ React2.createElement(CheckCircle, {
65
+ }, label), isResolved ? /* @__PURE__ */ React.createElement(CheckCircle, {
88
66
  className: sizeClass,
89
67
  weight: "fill"
90
- }) : /* @__PURE__ */ React2.createElement(CheckCircle, {
68
+ }) : /* @__PURE__ */ React.createElement(CheckCircle, {
91
69
  className: sizeClass
92
- }))), /* @__PURE__ */ React2.createElement(Tooltip.Portal, null, /* @__PURE__ */ React2.createElement(Tooltip.Content, {
70
+ }))), /* @__PURE__ */ React.createElement(Tooltip.Portal, null, /* @__PURE__ */ React.createElement(Tooltip.Content, {
93
71
  classNames: "z-[21]"
94
- }, label, /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null))));
72
+ }, label, /* @__PURE__ */ React.createElement(Tooltip.Arrow, null))));
95
73
  };
96
74
  var DeleteThreadButton = ({ onDelete }) => {
97
- const { t } = useTranslation2(THREAD_PLUGIN);
75
+ const { t } = useTranslation(THREAD_PLUGIN);
98
76
  const label = t("delete thread label");
99
- return /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
77
+ return /* @__PURE__ */ React.createElement(Tooltip.Root, null, /* @__PURE__ */ React.createElement(Tooltip.Trigger, {
100
78
  asChild: true
101
- }, /* @__PURE__ */ React2.createElement(Button, {
79
+ }, /* @__PURE__ */ React.createElement(Button, {
102
80
  variant: "ghost",
103
81
  "data-testid": "thread.delete",
104
82
  onClick: onDelete,
@@ -106,24 +84,23 @@ var DeleteThreadButton = ({ onDelete }) => {
106
84
  "min-bs-0 p-1",
107
85
  hoverableControlItem
108
86
  ]
109
- }, /* @__PURE__ */ React2.createElement("span", {
87
+ }, /* @__PURE__ */ React.createElement("span", {
110
88
  className: "sr-only"
111
- }, label), /* @__PURE__ */ React2.createElement(X, {
89
+ }, label), /* @__PURE__ */ React.createElement(X, {
112
90
  className: sizeClass
113
- }))), /* @__PURE__ */ React2.createElement(Tooltip.Portal, null, /* @__PURE__ */ React2.createElement(Tooltip.Content, {
91
+ }))), /* @__PURE__ */ React.createElement(Tooltip.Portal, null, /* @__PURE__ */ React.createElement(Tooltip.Content, {
114
92
  classNames: "z-[21]"
115
- }, label, /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null))));
93
+ }, label, /* @__PURE__ */ React.createElement(Tooltip.Arrow, null))));
116
94
  };
117
- var CommentContainer = ({ thread, detached, context, current, autoFocusTextbox, onAttend, onThreadDelete, onMessageDelete, onResolve, onComment }) => {
95
+ var CommentContainer = ({ thread, detached, context, current, onAttend, onThreadDelete, onMessageDelete, onResolve, onComment }) => {
118
96
  const identity = useIdentity();
119
97
  const space = getSpace(thread);
120
98
  const members = useMembers(space?.key);
121
- const activity = useStatus(space, thread.id);
122
- const { t } = useTranslation2(THREAD_PLUGIN);
99
+ const activity = useStatus(space, fullyQualifiedId(thread));
100
+ const { t } = useTranslation(THREAD_PLUGIN);
123
101
  const threadScrollRef = useRef(null);
124
- const [autoFocus, setAutoFocus] = useState(!!autoFocusTextbox);
125
102
  const { themeMode } = useThemeContext();
126
- const textboxMetadata = getMessageMetadata(thread.id, identity);
103
+ const textboxMetadata = getMessageMetadata(fullyQualifiedId(thread), identity);
127
104
  const [_count, _setCount] = useState(0);
128
105
  const rerenderEditor = () => _setCount((count) => count + 1);
129
106
  const messageRef = useRef("");
@@ -141,11 +118,6 @@ var CommentContainer = ({ thread, detached, context, current, autoFocusTextbox,
141
118
  ], [
142
119
  _count
143
120
  ]);
144
- useEffect(() => {
145
- setAutoFocus(!!autoFocusTextbox);
146
- }, [
147
- autoFocusTextbox
148
- ]);
149
121
  const scrollToEnd = (behavior) => setTimeout(() => threadScrollRef.current?.scrollIntoView({
150
122
  behavior,
151
123
  block: "end"
@@ -164,7 +136,6 @@ var CommentContainer = ({ thread, detached, context, current, autoFocusTextbox,
164
136
  }));
165
137
  onComment?.(thread);
166
138
  messageRef.current = "";
167
- setAutoFocus(true);
168
139
  scrollToEnd("instant");
169
140
  rerenderEditor();
170
141
  return true;
@@ -172,44 +143,42 @@ var CommentContainer = ({ thread, detached, context, current, autoFocusTextbox,
172
143
  thread,
173
144
  identity
174
145
  ]);
175
- const handleDeleteMessage = (id) => onMessageDelete?.(id);
176
- return /* @__PURE__ */ React2.createElement(Thread, {
146
+ return /* @__PURE__ */ React.createElement(Thread, {
177
147
  onClickCapture: onAttend,
178
148
  onFocusCapture: onAttend,
179
149
  current,
180
- id: thread.id
181
- }, /* @__PURE__ */ React2.createElement("div", {
150
+ id: fullyQualifiedId(thread)
151
+ }, /* @__PURE__ */ React.createElement("div", {
182
152
  role: "none",
183
153
  className: mx("col-span-2 grid grid-cols-[var(--rail-size)_1fr_min-content]", hoverableControls, hoverableFocusedWithinControls)
184
- }, detached ? /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
154
+ }, detached ? /* @__PURE__ */ React.createElement(Tooltip.Root, null, /* @__PURE__ */ React.createElement(Tooltip.Trigger, {
185
155
  asChild: true
186
- }, /* @__PURE__ */ React2.createElement(ThreadHeading, {
156
+ }, /* @__PURE__ */ React.createElement(ThreadHeading, {
187
157
  detached: true
188
- }, thread.name ?? t("thread title placeholder"))), /* @__PURE__ */ React2.createElement(Tooltip.Portal, null, /* @__PURE__ */ React2.createElement(Tooltip.Content, {
158
+ }, thread.name ?? t("thread title placeholder"))), /* @__PURE__ */ React.createElement(Tooltip.Portal, null, /* @__PURE__ */ React.createElement(Tooltip.Content, {
189
159
  classNames: "z-[21]",
190
160
  side: "top"
191
- }, t("detached thread label"), /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null)))) : /* @__PURE__ */ React2.createElement(ThreadHeading, null, thread.name ?? t("thread title placeholder")), /* @__PURE__ */ React2.createElement("div", {
161
+ }, t("detached thread label"), /* @__PURE__ */ React.createElement(Tooltip.Arrow, null)))) : /* @__PURE__ */ React.createElement(ThreadHeading, null, thread.name ?? t("thread title placeholder")), /* @__PURE__ */ React.createElement("div", {
192
162
  className: "flex flex-row items-center pli-1"
193
- }, thread.status === "staged" && /* @__PURE__ */ React2.createElement(Tag, {
163
+ }, thread.status === "staged" && /* @__PURE__ */ React.createElement(Tag, {
194
164
  palette: "neutral"
195
- }, t("draft button")), onResolve && !(thread?.status === "staged") && /* @__PURE__ */ React2.createElement(ToggleResolvedButton, {
165
+ }, t("draft button")), onResolve && !(thread?.status === "staged") && /* @__PURE__ */ React.createElement(ToggleResolvedButton, {
196
166
  isResolved: thread?.status === "resolved",
197
167
  onResolve
198
- }), onThreadDelete && /* @__PURE__ */ React2.createElement(DeleteThreadButton, {
168
+ }), onThreadDelete && /* @__PURE__ */ React.createElement(DeleteThreadButton, {
199
169
  onDelete: onThreadDelete
200
- }))), thread.messages.filter(nonNullable).map((message) => /* @__PURE__ */ React2.createElement(MessageContainer, {
170
+ }))), thread.messages.filter(nonNullable).map((message) => /* @__PURE__ */ React.createElement(MessageContainer, {
201
171
  key: message.id,
202
172
  message,
203
173
  members,
204
- onDelete: handleDeleteMessage
205
- })), /* @__PURE__ */ React2.createElement(MessageTextbox, {
174
+ onDelete: (id) => onMessageDelete?.(id)
175
+ })), /* @__PURE__ */ React.createElement(MessageTextbox, {
206
176
  extensions,
207
- autoFocus,
208
177
  onSend: handleCreate,
209
178
  ...textboxMetadata
210
- }), /* @__PURE__ */ React2.createElement(ThreadFooter, {
179
+ }), /* @__PURE__ */ React.createElement(ThreadFooter, {
211
180
  activity
212
- }, t("activity message")), /* @__PURE__ */ React2.createElement("div", {
181
+ }, t("activity message")), /* @__PURE__ */ React.createElement("div", {
213
182
  role: "none",
214
183
  className: "bs-px -mbs-px",
215
184
  ref: threadScrollRef
@@ -217,26 +186,15 @@ var CommentContainer = ({ thread, detached, context, current, autoFocusTextbox,
217
186
  };
218
187
 
219
188
  // packages/plugins/plugin-thread/src/components/CommentsContainer.tsx
220
- import { ChatText, Quotes } from "@phosphor-icons/react";
221
- import React3, { useEffect as useEffect2 } from "react";
222
- import { useTranslation as useTranslation3, Trans } from "@dxos/react-ui";
223
- import { PlankHeading, plankHeadingIconProps } from "@dxos/react-ui-deck";
189
+ import { ChatText } from "@phosphor-icons/react";
190
+ import React2, { useEffect } from "react";
191
+ import { fullyQualifiedId as fullyQualifiedId2 } from "@dxos/react-client/echo";
192
+ import { useTranslation as useTranslation2, Trans } from "@dxos/react-ui";
224
193
  import { descriptionText, mx as mx2 } from "@dxos/react-ui-theme";
225
- var CommentsHeading = ({ attendableId }) => {
226
- const { t } = useTranslation3(THREAD_PLUGIN);
227
- return /* @__PURE__ */ React3.createElement("div", {
228
- role: "none",
229
- className: "flex items-center"
230
- }, /* @__PURE__ */ React3.createElement(PlankHeading.Button, {
231
- attendableId
232
- }, /* @__PURE__ */ React3.createElement(Quotes, plankHeadingIconProps)), /* @__PURE__ */ React3.createElement(PlankHeading.Label, {
233
- attendableId
234
- }, t("comments heading")));
235
- };
236
- var CommentsContainer = ({ threads, detached = [], currentId, autoFocusCurrentTextbox, showResolvedThreads, onThreadAttend, onThreadDelete, onMessageDelete, onThreadToggleResolved, ...props }) => {
237
- const { t } = useTranslation3(THREAD_PLUGIN);
238
- const filteredThreads = showResolvedThreads ? threads : threads.filter((thread) => !(thread?.status === "resolved"));
239
- useEffect2(() => {
194
+ var CommentsContainer = ({ threads: threads2, detached = [], currentId, showResolvedThreads, onThreadAttend, onThreadDelete, onMessageDelete, onThreadToggleResolved, ...props }) => {
195
+ const { t } = useTranslation2(THREAD_PLUGIN);
196
+ const filteredThreads = showResolvedThreads ? threads2 : threads2.filter((thread) => !(thread?.status === "resolved"));
197
+ useEffect(() => {
240
198
  if (currentId) {
241
199
  document.getElementById(currentId)?.scrollIntoView({
242
200
  behavior: "smooth",
@@ -246,44 +204,260 @@ var CommentsContainer = ({ threads, detached = [], currentId, autoFocusCurrentTe
246
204
  }, [
247
205
  currentId
248
206
  ]);
249
- return /* @__PURE__ */ React3.createElement(React3.Fragment, null, filteredThreads.length > 0 ? filteredThreads.map((thread) => /* @__PURE__ */ React3.createElement(CommentContainer, {
250
- key: thread.id,
251
- thread,
252
- current: currentId === thread.id,
253
- detached: detached.includes(thread.id),
254
- autoFocusTextbox: autoFocusCurrentTextbox && currentId === thread.id,
255
- ...onThreadAttend && {
256
- onAttend: () => onThreadAttend(thread)
257
- },
258
- ...onThreadDelete && {
259
- onThreadDelete: () => onThreadDelete(thread)
260
- },
261
- ...onMessageDelete && {
262
- onMessageDelete: (messageId) => onMessageDelete(thread, messageId)
263
- },
264
- ...onThreadToggleResolved && {
265
- onResolve: () => onThreadToggleResolved(thread)
266
- },
267
- ...props
268
- })) : /* @__PURE__ */ React3.createElement("div", {
207
+ return /* @__PURE__ */ React2.createElement(React2.Fragment, null, filteredThreads.length > 0 ? filteredThreads.map((thread) => {
208
+ const threadId = fullyQualifiedId2(thread);
209
+ return /* @__PURE__ */ React2.createElement(CommentContainer, {
210
+ key: threadId,
211
+ thread,
212
+ current: currentId === threadId,
213
+ detached: detached.includes(threadId),
214
+ ...onThreadAttend && {
215
+ onAttend: () => onThreadAttend(thread)
216
+ },
217
+ ...onThreadDelete && {
218
+ onThreadDelete: () => onThreadDelete(thread)
219
+ },
220
+ ...onMessageDelete && {
221
+ onMessageDelete: (messageId) => onMessageDelete(thread, messageId)
222
+ },
223
+ ...onThreadToggleResolved && {
224
+ onResolve: () => onThreadToggleResolved(thread)
225
+ },
226
+ ...props
227
+ });
228
+ }) : /* @__PURE__ */ React2.createElement("div", {
269
229
  role: "alert",
270
230
  className: mx2(descriptionText, "place-self-center border border-dashed border-neutral-400/50 rounded-lg text-center p-4 m-4")
271
- }, /* @__PURE__ */ React3.createElement("h2", {
231
+ }, /* @__PURE__ */ React2.createElement("h2", {
272
232
  className: "mbe-2 font-medium text-baseText"
273
- }, t("no comments title")), /* @__PURE__ */ React3.createElement("p", null, /* @__PURE__ */ React3.createElement(Trans, {
233
+ }, t("no comments title")), /* @__PURE__ */ React2.createElement("p", null, /* @__PURE__ */ React2.createElement(Trans, {
274
234
  t,
275
235
  i18nKey: "no comments message",
276
236
  components: {
277
- commentIcon: /* @__PURE__ */ React3.createElement(ChatText, {
237
+ commentIcon: /* @__PURE__ */ React2.createElement(ChatText, {
278
238
  className: "inline-block"
279
239
  })
280
240
  }
281
241
  }))));
282
242
  };
283
243
 
244
+ // packages/plugins/plugin-thread/src/components/ThreadComplementary.tsx
245
+ import React3, { useEffect as useEffect2, useMemo as useMemo2 } from "react";
246
+ import { LayoutAction, useIntentDispatcher, useResolvePlugins } from "@dxos/app-framework";
247
+ import { fullyQualifiedId as fullyQualifiedId3 } from "@dxos/react-client/echo";
248
+ import { ScrollArea } from "@dxos/react-ui";
249
+ import { useAttended } from "@dxos/react-ui-attention";
250
+ import { nonNullable as nonNullable2 } from "@dxos/util";
251
+ var providesThreadsConfig = (plugin) => "thread" in plugin.provides ? plugin : void 0;
252
+ var ThreadComplementary = ({ role, subject, drafts, current, showResolvedThreads }) => {
253
+ const dispatch = useIntentDispatcher();
254
+ const threadsIntegrators = useResolvePlugins(providesThreadsConfig);
255
+ const threadProvides = useMemo2(() => threadsIntegrators.map((p) => p.provides.thread), [
256
+ threadsIntegrators
257
+ ]);
258
+ const createSort = useMemo2(() => threadProvides.find((p) => p.predicate(subject))?.createSort, [
259
+ threadProvides,
260
+ subject
261
+ ]);
262
+ const sort = useMemo2(() => createSort?.(subject), [
263
+ createSort,
264
+ subject
265
+ ]);
266
+ const threads2 = useMemo2(() => {
267
+ return subject.threads.concat(drafts ?? []).filter(nonNullable2);
268
+ }, [
269
+ JSON.stringify(subject.threads),
270
+ JSON.stringify(drafts)
271
+ ]);
272
+ const detachedIds = useMemo2(() => {
273
+ return threads2.filter(({ anchor }) => !anchor).map((thread) => fullyQualifiedId3(thread));
274
+ }, [
275
+ threads2
276
+ ]);
277
+ useEffect2(() => {
278
+ if (!sort) {
279
+ return;
280
+ }
281
+ threads2.sort((a, b) => sort(a?.anchor, b?.anchor));
282
+ }, [
283
+ sort,
284
+ threads2
285
+ ]);
286
+ const attended = useAttended();
287
+ const qualifiedSubjectId = fullyQualifiedId3(subject);
288
+ return /* @__PURE__ */ React3.createElement("div", {
289
+ role: "none",
290
+ className: "contents"
291
+ }, /* @__PURE__ */ React3.createElement(ScrollArea.Root, {
292
+ classNames: "row-span-2"
293
+ }, /* @__PURE__ */ React3.createElement(ScrollArea.Viewport, null, /* @__PURE__ */ React3.createElement(CommentsContainer, {
294
+ threads: threads2,
295
+ detached: detachedIds,
296
+ currentId: attended.includes(qualifiedSubjectId) ? current : void 0,
297
+ showResolvedThreads,
298
+ onThreadAttend: (thread) => {
299
+ const threadId = fullyQualifiedId3(thread);
300
+ if (current !== threadId) {
301
+ current = threadId;
302
+ void dispatch?.([
303
+ {
304
+ action: LayoutAction.SCROLL_INTO_VIEW,
305
+ data: {
306
+ id: fullyQualifiedId3(subject),
307
+ thread: threadId,
308
+ cursor: thread.anchor
309
+ }
310
+ }
311
+ ]);
312
+ }
313
+ },
314
+ onThreadDelete: (thread) => {
315
+ return dispatch({
316
+ plugin: THREAD_PLUGIN,
317
+ action: ThreadAction.DELETE,
318
+ data: {
319
+ subject,
320
+ thread
321
+ }
322
+ });
323
+ },
324
+ onMessageDelete: (thread, messageId) => dispatch({
325
+ plugin: THREAD_PLUGIN,
326
+ action: ThreadAction.DELETE_MESSAGE,
327
+ data: {
328
+ subject,
329
+ thread,
330
+ messageId
331
+ }
332
+ }),
333
+ onThreadToggleResolved: (thread) => dispatch({
334
+ plugin: THREAD_PLUGIN,
335
+ action: ThreadAction.TOGGLE_RESOLVED,
336
+ data: {
337
+ thread
338
+ }
339
+ }),
340
+ onComment: (thread) => {
341
+ void dispatch({
342
+ action: ThreadAction.ON_MESSAGE_ADD,
343
+ data: {
344
+ thread,
345
+ subject
346
+ }
347
+ });
348
+ }
349
+ }), /* @__PURE__ */ React3.createElement("div", {
350
+ role: "none",
351
+ className: "bs-10"
352
+ }), /* @__PURE__ */ React3.createElement(ScrollArea.Scrollbar, null, /* @__PURE__ */ React3.createElement(ScrollArea.Thumb, null)))));
353
+ };
354
+
355
+ // packages/plugins/plugin-thread/src/components/ThreadSettings.tsx
356
+ import React4 from "react";
357
+ import { SettingsValue } from "@dxos/plugin-settings";
358
+ import { Input, useTranslation as useTranslation3 } from "@dxos/react-ui";
359
+ var ThreadSettings = ({ settings }) => {
360
+ const { t } = useTranslation3(THREAD_PLUGIN);
361
+ return /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(SettingsValue, {
362
+ label: t("settings standalone label")
363
+ }, /* @__PURE__ */ React4.createElement(Input.Switch, {
364
+ checked: settings.standalone,
365
+ onCheckedChange: (checked) => settings.standalone = !!checked
366
+ })));
367
+ };
368
+
284
369
  // packages/plugins/plugin-thread/src/components/index.ts
285
- var ThreadMain = React4.lazy(() => import("./ThreadMain-ZDCRC6RG.mjs"));
286
- var ThreadArticle = React4.lazy(() => import("./ThreadArticle-O64ZECMF.mjs"));
370
+ var ThreadMain = React5.lazy(() => import("./ThreadMain-PGF7W5EV.mjs"));
371
+ var ThreadArticle = React5.lazy(() => import("./ThreadArticle-GBPIH47W.mjs"));
372
+
373
+ // packages/plugins/plugin-thread/src/extensions/threads.ts
374
+ import { EditorView } from "@codemirror/view";
375
+ import { computed, effect } from "@preact/signals-core";
376
+ import { ThreadType } from "@dxos/plugin-space/types";
377
+ import { getSpace as getSpace2, getTextInRange, createDocAccessor, fullyQualifiedId as fullyQualifiedId4 } from "@dxos/react-client/echo";
378
+ import { comments, createExternalCommentSync } from "@dxos/react-ui-editor";
379
+ import { nonNullable as nonNullable3 } from "@dxos/util";
380
+ var getName = (doc, anchor) => {
381
+ if (doc.content) {
382
+ const [start, end] = anchor.split(":");
383
+ return getTextInRange(createDocAccessor(doc.content, [
384
+ "content"
385
+ ]), start, end);
386
+ }
387
+ };
388
+ var threads = (state, doc, dispatch) => {
389
+ const space = doc && getSpace2(doc);
390
+ if (!doc || !space || !dispatch) {
391
+ return [
392
+ comments()
393
+ ];
394
+ }
395
+ const threads2 = computed(() => [
396
+ ...doc.threads.filter(nonNullable3),
397
+ ...state.drafts[fullyQualifiedId4(doc)] ?? []
398
+ ].filter((thread) => !(thread?.status === "resolved")));
399
+ return [
400
+ EditorView.updateListener.of((update) => {
401
+ if (update.docChanged) {
402
+ doc.threads.forEach((thread) => {
403
+ if (thread?.anchor) {
404
+ const name = getName(doc, thread.anchor);
405
+ if (name && name !== thread.name) {
406
+ thread.name = name;
407
+ }
408
+ }
409
+ });
410
+ }
411
+ }),
412
+ createExternalCommentSync(fullyQualifiedId4(doc), (sink) => effect(() => sink()), () => threads2.value.filter((thread) => thread?.anchor).map((thread) => ({
413
+ id: fullyQualifiedId4(thread),
414
+ cursor: thread.anchor
415
+ }))),
416
+ comments({
417
+ id: fullyQualifiedId4(doc),
418
+ onCreate: ({ cursor }) => {
419
+ const name = getName(doc, cursor);
420
+ void dispatch({
421
+ action: ThreadAction.CREATE,
422
+ data: {
423
+ cursor,
424
+ name,
425
+ subject: doc
426
+ }
427
+ });
428
+ },
429
+ onDelete: ({ id }) => {
430
+ const draft = state.drafts[fullyQualifiedId4(doc)];
431
+ if (draft) {
432
+ const index = draft.findIndex((thread2) => fullyQualifiedId4(thread2) === id);
433
+ if (index !== -1) {
434
+ draft.splice(index, 1);
435
+ }
436
+ }
437
+ const thread = doc.threads.find((thread2) => thread2?.id === id);
438
+ if (thread) {
439
+ thread.anchor = void 0;
440
+ }
441
+ },
442
+ onUpdate: ({ id, cursor }) => {
443
+ const thread = state.drafts[fullyQualifiedId4(doc)]?.find((thread2) => fullyQualifiedId4(thread2) === id) ?? doc.threads.find((thread2) => thread2?.id === id);
444
+ if (thread instanceof ThreadType && thread.anchor) {
445
+ thread.name = getName(doc, thread.anchor);
446
+ thread.anchor = cursor;
447
+ }
448
+ },
449
+ onSelect: ({ selection: { current, closest } }) => {
450
+ void dispatch({
451
+ action: ThreadAction.SELECT,
452
+ data: {
453
+ current: current ?? closest,
454
+ skipOpen: true
455
+ }
456
+ });
457
+ }
458
+ })
459
+ ];
460
+ };
287
461
 
288
462
  // packages/plugins/plugin-thread/src/translations.ts
289
463
  var translations_default = [
@@ -322,14 +496,14 @@ var translations_default = [
322
496
  ];
323
497
 
324
498
  // packages/plugins/plugin-thread/src/ThreadPlugin.tsx
499
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/plugins/plugin-thread/src/ThreadPlugin.tsx";
325
500
  var initialViewState = {
326
501
  showResolvedThreads: false
327
502
  };
328
- var providesThreadsConfig = (plugin) => "thread" in plugin.provides ? plugin : void 0;
329
503
  var ThreadPlugin = () => {
330
504
  const settings = new LocalStorageStore(THREAD_PLUGIN);
331
505
  const state = create2({
332
- staging: {}
506
+ drafts: {}
333
507
  });
334
508
  const viewStore = create2({});
335
509
  const getViewState = (subjectId) => {
@@ -340,10 +514,8 @@ var ThreadPlugin = () => {
340
514
  }
341
515
  return viewStore[subjectId];
342
516
  };
343
- let attentionPlugin;
344
517
  let navigationPlugin;
345
518
  let intentPlugin;
346
- let dispatch;
347
519
  const unsubscribeCallbacks = [];
348
520
  return {
349
521
  meta: meta_default,
@@ -354,36 +526,8 @@ var ThreadPlugin = () => {
354
526
  allowUndefined: true
355
527
  })
356
528
  });
357
- attentionPlugin = resolvePlugin(plugins, parseAttentionPlugin);
358
529
  navigationPlugin = resolvePlugin(plugins, parseNavigationPlugin);
359
530
  intentPlugin = resolvePlugin(plugins, parseIntentPlugin);
360
- dispatch = intentPlugin?.provides.intent.dispatch;
361
- const graphPlugin = resolvePlugin(plugins, parseGraphPlugin);
362
- const client = resolvePlugin(plugins, parseClientPlugin)?.provides.client;
363
- if (!client) {
364
- return;
365
- }
366
- const channelsQuery = client.spaces.query(Filter.schema(ChannelType));
367
- const queryUnsubscribe = channelsQuery.subscribe();
368
- const unsubscribe = effect(() => {
369
- const attention = attentionPlugin?.provides.attention;
370
- if (!attention?.attended) {
371
- return;
372
- }
373
- const firstAttendedNodeWithComments = Array.from(attention.attended).map((id) => graphPlugin?.provides.graph.findNode(id)).find((node) => node?.data instanceof DocumentType && (node.data.threads?.length ?? 0) > 0);
374
- if (firstAttendedNodeWithComments) {
375
- void intentPlugin?.provides.intent.dispatch({
376
- action: NavigationAction.OPEN,
377
- data: {
378
- activeParts: {
379
- complementary: `${firstAttendedNodeWithComments.id}${SLUG_PATH_SEPARATOR}comments`
380
- }
381
- }
382
- });
383
- }
384
- });
385
- unsubscribeCallbacks.push(queryUnsubscribe);
386
- unsubscribeCallbacks.push(unsubscribe);
387
531
  },
388
532
  unload: async () => {
389
533
  unsubscribeCallbacks.forEach((unsubscribe) => unsubscribe());
@@ -399,12 +543,11 @@ var ThreadPlugin = () => {
399
543
  ns: THREAD_PLUGIN
400
544
  }
401
545
  ],
402
- icon: (props) => /* @__PURE__ */ React5.createElement(Chat, props),
403
- iconSymbol: "ph--chat--regular",
546
+ icon: "ph--chat--regular",
404
547
  // TODO(wittjosiah): Move out of metadata.
405
548
  loadReferences: (channel) => loadObjectReferences(channel, (channel2) => channel2.threads)
406
549
  },
407
- [ThreadType.typename]: {
550
+ [ThreadType2.typename]: {
408
551
  // TODO(wittjosiah): Move out of metadata.
409
552
  loadReferences: (thread) => loadObjectReferences(thread, (thread2) => thread2.messages)
410
553
  },
@@ -440,16 +583,16 @@ var ThreadPlugin = () => {
440
583
  echo: {
441
584
  schema: [
442
585
  ChannelType,
443
- ThreadType,
586
+ ThreadType2,
444
587
  MessageType2
445
588
  ]
446
589
  },
447
590
  graph: {
448
591
  builder: (plugins) => {
449
592
  const client = resolvePlugin(plugins, parseClientPlugin)?.provides.client;
450
- const dispatch2 = resolvePlugin(plugins, parseIntentPlugin)?.provides.intent.dispatch;
593
+ const dispatch = resolvePlugin(plugins, parseIntentPlugin)?.provides.intent.dispatch;
451
594
  const metadataResolver = resolvePlugin(plugins, parseMetadataResolverPlugin)?.provides.metadata.resolver;
452
- if (!client || !dispatch2 || !metadataResolver) {
595
+ if (!client || !dispatch || !metadataResolver) {
453
596
  return [];
454
597
  }
455
598
  return [
@@ -459,29 +602,36 @@ var ThreadPlugin = () => {
459
602
  if (!id.endsWith("~comments")) {
460
603
  return;
461
604
  }
462
- const docId = id.split("~").at(0);
463
- const [spaceId, objectId] = docId?.split(":") ?? [];
605
+ const [subjectId] = id.split("~");
606
+ const [spaceId, objectId] = parseFullyQualifiedId(subjectId);
464
607
  const space = client.spaces.get().find((space2) => space2.id === spaceId);
465
- const doc = space?.db.getObjectById(objectId);
466
- if (!doc || !docId) {
608
+ const object = toSignal((onChange) => {
609
+ const timeout = setTimeout(async () => {
610
+ await space?.db.loadObjectById(objectId);
611
+ onChange();
612
+ });
613
+ return () => clearTimeout(timeout);
614
+ }, () => space?.db.getObjectById(objectId), subjectId);
615
+ if (!object || !subjectId) {
467
616
  return;
468
617
  }
469
- const docMeta = metadataResolver(getTypename(doc) ?? "");
470
- const label = docMeta.label?.(doc) || doc.name || docMeta.placeholder || [
618
+ const meta = metadataResolver(getTypename(object) ?? "");
619
+ const label = meta.label?.(object) || object.name || meta.placeholder || [
471
620
  "unnamed object threads label",
472
621
  {
473
622
  ns: THREAD_PLUGIN
474
623
  }
475
624
  ];
476
- const viewState = getViewState(docId);
625
+ const viewState = getViewState(subjectId);
477
626
  return {
478
627
  id,
479
628
  type: "orphan-comments-for-subject",
480
- data: doc,
629
+ data: null,
481
630
  properties: {
482
- icon: meta_default.iconComponent,
631
+ icon: "ph--chat-text--regular",
483
632
  label,
484
- showResolvedThreads: viewState.showResolvedThreads
633
+ showResolvedThreads: viewState.showResolvedThreads,
634
+ object
485
635
  }
486
636
  };
487
637
  },
@@ -495,7 +645,7 @@ var ThreadPlugin = () => {
495
645
  const toggle = () => {
496
646
  const newToggleState = !viewState.showResolvedThreads;
497
647
  viewState.showResolvedThreads = newToggleState;
498
- void dispatch2({
648
+ void dispatch({
499
649
  action: ObservabilityAction.SEND_EVENT,
500
650
  data: {
501
651
  name: "threads.toggle-show-resolved",
@@ -520,7 +670,8 @@ var ThreadPlugin = () => {
520
670
  ],
521
671
  menuItemType: "toggle",
522
672
  isChecked: viewState.showResolvedThreads,
523
- testId: "threadPlugin.toggleShowResolved"
673
+ testId: "threadPlugin.toggleShowResolved",
674
+ icon: viewState.showResolvedThreads ? "ph--eye-slash--regular" : "ph--eye--regular"
524
675
  }
525
676
  }
526
677
  ];
@@ -542,7 +693,7 @@ var ThreadPlugin = () => {
542
693
  {
543
694
  id: `${THREAD_PLUGIN}/create/${node.id}`,
544
695
  data: async () => {
545
- await dispatch2([
696
+ await dispatch([
546
697
  {
547
698
  plugin: THREAD_PLUGIN,
548
699
  action: ThreadAction.CREATE
@@ -565,8 +716,7 @@ var ThreadPlugin = () => {
565
716
  ns: THREAD_PLUGIN
566
717
  }
567
718
  ],
568
- icon: (props) => /* @__PURE__ */ React5.createElement(Chat, props),
569
- iconSymbol: "ph--chat--regular",
719
+ icon: "ph--chat--regular",
570
720
  testId: "threadPlugin.createObject"
571
721
  }
572
722
  }
@@ -580,20 +730,17 @@ var ThreadPlugin = () => {
580
730
  component: ({ data, role }) => {
581
731
  switch (role) {
582
732
  case "main": {
583
- return data.active instanceof ChannelType && data.active.threads[0] ? /* @__PURE__ */ React5.createElement(ThreadMain, {
733
+ return data.active instanceof ChannelType && data.active.threads[0] ? /* @__PURE__ */ React6.createElement(ThreadMain, {
584
734
  thread: data.active.threads[0]
585
735
  }) : null;
586
736
  }
587
737
  case "settings": {
588
- return data.plugin === meta_default.id ? /* @__PURE__ */ React5.createElement(ThreadSettings, {
738
+ return data.plugin === meta_default.id ? /* @__PURE__ */ React6.createElement(ThreadSettings, {
589
739
  settings: settings.values
590
740
  }) : null;
591
741
  }
592
- case "article":
593
- case "complementary": {
742
+ case "article": {
594
743
  const location = navigationPlugin?.provides.location;
595
- const threadsIntegrators = useResolvePlugins(providesThreadsConfig);
596
- const threadProvides = threadsIntegrators.map((p) => p.provides.thread);
597
744
  if (data.object instanceof ChannelType && data.object.threads[0]) {
598
745
  const channel = data.object;
599
746
  if (isLayoutParts(location?.active) && location.active.main) {
@@ -601,103 +748,38 @@ var ThreadPlugin = () => {
601
748
  const currentPosition = layoutEntries.findIndex((entry) => channel.id === entry.id);
602
749
  if (currentPosition > 0) {
603
750
  const objectToTheLeft = layoutEntries[currentPosition - 1];
604
- const context = getSpace2(data.object)?.db.getObjectById(objectToTheLeft.id);
605
- return /* @__PURE__ */ React5.createElement(ThreadArticle, {
751
+ const context = getSpace3(data.object)?.db.getObjectById(objectToTheLeft.id);
752
+ return /* @__PURE__ */ React6.createElement(ThreadArticle, {
606
753
  thread: data.object.threads[0],
607
754
  context
608
755
  });
609
756
  }
610
757
  }
611
- return /* @__PURE__ */ React5.createElement(ThreadArticle, {
758
+ return /* @__PURE__ */ React6.createElement(ThreadArticle, {
612
759
  thread: data.object.threads[0]
613
760
  });
614
761
  }
615
- if (data.subject instanceof ThreadType) {
616
- return /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(ChatHeading, {
762
+ if (data.subject instanceof ThreadType2) {
763
+ return /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(ChatHeading, {
617
764
  attendableId: data.subject.id
618
- }), /* @__PURE__ */ React5.createElement(ChatContainer, {
765
+ }), /* @__PURE__ */ React6.createElement(ChatContainer, {
619
766
  thread: data.subject
620
767
  }));
621
768
  }
769
+ break;
770
+ }
771
+ case "complementary--comments": {
622
772
  if (data.subject && typeof data.subject === "object" && "threads" in data.subject && Array.isArray(data.subject.threads)) {
623
- const threads = data.subject.threads.concat(state.staging[fullyQualifiedId(data.subject)]).filter(nonNullable2);
624
- const createSort = threadProvides.find((p) => p.predicate(data.subject))?.createSort;
625
- if (createSort) {
626
- const sort = createSort(data.subject);
627
- threads.sort((a, b) => sort(a.anchor, b.anchor));
628
- }
629
- const detached = data.subject.threads.filter(nonNullable2).filter(({ anchor }) => !anchor).map((thread) => thread.id);
630
- const qualifiedSubjectId = fullyQualifiedId(data.subject);
631
- const attention = attentionPlugin?.provides.attention?.attended ?? /* @__PURE__ */ new Set([
632
- qualifiedSubjectId
633
- ]);
634
- const attendableAttrs = createAttendableAttributes(qualifiedSubjectId);
635
- const { showResolvedThreads } = getViewState(qualifiedSubjectId);
636
- return /* @__PURE__ */ React5.createElement("div", {
637
- role: "none",
638
- className: "contents group/attention",
639
- ...attendableAttrs
640
- }, role === "complementary" && /* @__PURE__ */ React5.createElement(CommentsHeading, {
641
- attendableId: qualifiedSubjectId
642
- }), /* @__PURE__ */ React5.createElement(ScrollArea.Root, {
643
- classNames: "row-span-2"
644
- }, /* @__PURE__ */ React5.createElement(ScrollArea.Viewport, null, /* @__PURE__ */ React5.createElement(CommentsContainer, {
645
- threads,
646
- detached,
647
- currentId: attention.has(qualifiedSubjectId) ? state.current : void 0,
648
- autoFocusCurrentTextbox: state.focus,
649
- showResolvedThreads,
650
- onThreadAttend: (thread) => {
651
- if (state.current !== thread.id) {
652
- state.current = thread.id;
653
- void dispatch?.({
654
- action: LayoutAction.SCROLL_INTO_VIEW,
655
- data: {
656
- id: fullyQualifiedId(data.subject),
657
- thread: fullyQualifiedId(thread),
658
- cursor: thread.anchor
659
- }
660
- });
661
- }
662
- },
663
- onThreadDelete: (thread) => dispatch?.({
664
- plugin: THREAD_PLUGIN,
665
- action: ThreadAction.DELETE,
666
- data: {
667
- subject: data.subject,
668
- thread
669
- }
670
- }),
671
- onMessageDelete: (thread, messageId) => dispatch?.({
672
- plugin: THREAD_PLUGIN,
673
- action: ThreadAction.DELETE_MESSAGE,
674
- data: {
675
- subject: data.subject,
676
- thread,
677
- messageId
678
- }
679
- }),
680
- onThreadToggleResolved: (thread) => dispatch?.({
681
- plugin: THREAD_PLUGIN,
682
- action: ThreadAction.TOGGLE_RESOLVED,
683
- data: {
684
- thread
685
- }
686
- }),
687
- onComment: (thread) => {
688
- void intentPlugin?.provides.intent.dispatch({
689
- action: ThreadAction.ON_MESSAGE_ADD,
690
- data: {
691
- thread,
692
- subject: data.subject
693
- }
694
- });
695
- }
696
- }), /* @__PURE__ */ React5.createElement("div", {
697
- role: "none",
698
- className: "bs-10"
699
- }), /* @__PURE__ */ React5.createElement(ScrollArea.Scrollbar, null, /* @__PURE__ */ React5.createElement(ScrollArea.Thumb, null)))));
773
+ const { showResolvedThreads } = getViewState(fullyQualifiedId5(data.subject));
774
+ return /* @__PURE__ */ React6.createElement(ThreadComplementary, {
775
+ role,
776
+ subject: data.subject,
777
+ drafts: state.drafts[fullyQualifiedId5(data.subject)],
778
+ current: state.current,
779
+ showResolvedThreads
780
+ });
700
781
  }
782
+ break;
701
783
  }
702
784
  }
703
785
  return null;
@@ -709,18 +791,31 @@ var ThreadPlugin = () => {
709
791
  case ThreadAction.CREATE: {
710
792
  if (intent.data && intent.data.cursor !== void 0) {
711
793
  const { cursor, name, subject } = intent.data;
712
- const subjectId = fullyQualifiedId(subject);
713
- const thread = create2(ThreadType, {
794
+ if (subject?.threads === void 0) {
795
+ try {
796
+ subject.threads = [];
797
+ } catch (err) {
798
+ log.error("Subject does not support threads array", subject?.typename, {
799
+ F: __dxlog_file,
800
+ L: 327,
801
+ S: void 0,
802
+ C: (f, a) => f(...a)
803
+ });
804
+ return;
805
+ }
806
+ }
807
+ const subjectId = fullyQualifiedId5(subject);
808
+ const thread = create2(ThreadType2, {
714
809
  name,
715
810
  anchor: cursor,
716
811
  messages: [],
717
812
  status: "staged"
718
813
  });
719
- const stagingArea = state.staging[subjectId];
720
- if (stagingArea) {
721
- stagingArea.push(thread);
814
+ const draft = state.drafts[subjectId];
815
+ if (draft) {
816
+ draft.push(thread);
722
817
  } else {
723
- state.staging[subjectId] = [
818
+ state.drafts[subjectId] = [
724
819
  thread
725
820
  ];
726
821
  }
@@ -728,27 +823,20 @@ var ThreadPlugin = () => {
728
823
  data: thread,
729
824
  intents: [
730
825
  [
731
- {
732
- action: NavigationAction.OPEN,
733
- data: {
734
- activeParts: {
735
- complementary: `${subjectId}${SLUG_PATH_SEPARATOR}comments${SLUG_COLLECTION_INDICATOR}`
736
- }
737
- }
738
- },
739
826
  {
740
827
  action: ThreadAction.SELECT,
741
828
  data: {
742
- current: thread.id,
743
- focus: true
829
+ current: fullyQualifiedId5(thread)
744
830
  }
745
- },
831
+ }
832
+ ],
833
+ [
746
834
  {
747
- action: LayoutAction.SET_LAYOUT,
835
+ action: NavigationAction.OPEN,
748
836
  data: {
749
- element: "complementary",
750
- subject: subjectId,
751
- state: true
837
+ activeParts: {
838
+ complementary: "comments"
839
+ }
752
840
  }
753
841
  }
754
842
  ]
@@ -758,7 +846,7 @@ var ThreadPlugin = () => {
758
846
  return {
759
847
  data: create2(ChannelType, {
760
848
  threads: [
761
- create2(ThreadType, {
849
+ create2(ThreadType2, {
762
850
  messages: []
763
851
  })
764
852
  ]
@@ -767,15 +855,26 @@ var ThreadPlugin = () => {
767
855
  }
768
856
  }
769
857
  case ThreadAction.SELECT: {
770
- state.focus = intent.data?.current === state.current ? state.focus : intent.data?.focus;
771
858
  state.current = intent.data?.current;
772
859
  return {
773
- data: true
860
+ data: true,
861
+ intents: !intent.data?.skipOpen ? [
862
+ [
863
+ {
864
+ action: NavigationAction.OPEN,
865
+ data: {
866
+ activeParts: {
867
+ complementary: "comments"
868
+ }
869
+ }
870
+ }
871
+ ]
872
+ ] : []
774
873
  };
775
874
  }
776
875
  case ThreadAction.TOGGLE_RESOLVED: {
777
876
  const { thread } = intent.data ?? {};
778
- if (!(thread instanceof ThreadType)) {
877
+ if (!(thread instanceof ThreadType2)) {
779
878
  return;
780
879
  }
781
880
  if (thread.status === "active" || thread.status === void 0) {
@@ -783,9 +882,10 @@ var ThreadPlugin = () => {
783
882
  } else if (thread.status === "resolved") {
784
883
  thread.status = "active";
785
884
  }
786
- const space = getSpace2(thread);
885
+ const space = getSpace3(thread);
787
886
  const spaceId = space?.id;
788
887
  return {
888
+ data: true,
789
889
  intents: [
790
890
  [
791
891
  {
@@ -804,19 +904,21 @@ var ThreadPlugin = () => {
804
904
  }
805
905
  case ThreadAction.DELETE: {
806
906
  const { subject, thread } = intent.data ?? {};
807
- if (!(subject instanceof DocumentType) || !(thread instanceof ThreadType)) {
907
+ if (!(thread instanceof ThreadType2) || !(subject && typeof subject === "object" && "threads" in subject)) {
808
908
  return;
809
909
  }
810
- const subjectId = fullyQualifiedId(subject);
811
- const stagingArea = state.staging[subjectId];
812
- if (stagingArea) {
813
- const index = stagingArea.findIndex((t) => t.id === thread.id);
910
+ const subjectId = fullyQualifiedId5(subject);
911
+ const draft = state.drafts[subjectId];
912
+ if (draft) {
913
+ const index = draft.findIndex((t) => t.id === thread.id);
814
914
  if (index !== -1) {
815
- stagingArea.splice(index, 1);
816
- return;
915
+ draft.splice(index, 1);
916
+ return {
917
+ data: true
918
+ };
817
919
  }
818
920
  }
819
- const space = getSpace2(thread);
921
+ const space = getSpace3(thread);
820
922
  if (!space || !subject.threads) {
821
923
  return;
822
924
  }
@@ -874,22 +976,22 @@ var ThreadPlugin = () => {
874
976
  }
875
977
  case ThreadAction.ON_MESSAGE_ADD: {
876
978
  const { thread, subject } = intent.data ?? {};
877
- if (!(thread instanceof ThreadType) || !(subject && typeof subject === "object" && "threads" in subject)) {
979
+ if (!(thread instanceof ThreadType2) || !(subject && typeof subject === "object" && "threads" in subject && Array.isArray(subject.threads))) {
878
980
  return;
879
981
  }
880
- const subjectId = fullyQualifiedId(subject);
881
- const space = getSpace2(subject);
982
+ const subjectId = fullyQualifiedId5(subject);
983
+ const space = getSpace3(subject);
882
984
  const intents = [];
883
985
  const analyticsProperties = {
884
986
  threadId: thread.id,
885
987
  spaceId: space?.id
886
988
  };
887
- if (state.staging[subjectId]?.find((t) => t === thread)) {
989
+ if (state.drafts[subjectId]?.find((t) => t === thread)) {
888
990
  thread.status = "active";
889
991
  subject.threads ? subject.threads.push(thread) : subject.threads = [
890
992
  thread
891
993
  ];
892
- state.staging[subjectId] = state.staging[subjectId]?.filter((t) => t.id !== thread.id);
994
+ state.drafts[subjectId] = state.drafts[subjectId]?.filter(({ id }) => id !== thread.id);
893
995
  intents.push({
894
996
  action: ObservabilityAction.SEND_EVENT,
895
997
  data: {
@@ -917,8 +1019,8 @@ var ThreadPlugin = () => {
917
1019
  }
918
1020
  case ThreadAction.DELETE_MESSAGE: {
919
1021
  const { subject, thread, messageId } = intent.data ?? {};
920
- const space = getSpace2(subject);
921
- if (!(subject instanceof DocumentType) || !(thread instanceof ThreadType) || !space) {
1022
+ const space = getSpace3(subject);
1023
+ if (!(thread instanceof ThreadType2) || !(subject && typeof subject === "object" && "threads" in subject)) {
922
1024
  return;
923
1025
  }
924
1026
  if (!intent.undo) {
@@ -960,7 +1062,7 @@ var ThreadPlugin = () => {
960
1062
  name: "threads.message.delete",
961
1063
  properties: {
962
1064
  threadId: thread.id,
963
- spaceId: space.id
1065
+ spaceId: space?.id
964
1066
  }
965
1067
  }
966
1068
  }
@@ -984,7 +1086,7 @@ var ThreadPlugin = () => {
984
1086
  name: "threads.message.undo-delete",
985
1087
  properties: {
986
1088
  threadId: thread.id,
987
- spaceId: space.id
1089
+ spaceId: space?.id
988
1090
  }
989
1091
  }
990
1092
  }
@@ -997,92 +1099,8 @@ var ThreadPlugin = () => {
997
1099
  }
998
1100
  },
999
1101
  markdown: {
1000
- // TODO(burdon): Factor out extension factory into separate file (for simplicity).
1001
1102
  extensions: ({ document: doc }) => {
1002
- const space = doc && getSpace2(doc);
1003
- if (!doc || !space) {
1004
- return [
1005
- comments()
1006
- ];
1007
- }
1008
- const threads = computed(() => [
1009
- ...doc.threads.filter(nonNullable2),
1010
- ...state.staging[fullyQualifiedId(doc)] ?? []
1011
- ].filter((thread) => !(thread?.status === "resolved")));
1012
- return [
1013
- listener2({
1014
- onChange: () => {
1015
- doc.threads.forEach((thread) => {
1016
- if (thread?.anchor) {
1017
- const [start, end] = thread.anchor.split(":");
1018
- const name = doc.content && getTextInRange(createDocAccessor(doc.content, [
1019
- "content"
1020
- ]), start, end);
1021
- if (name && name !== thread.name) {
1022
- thread.name = name;
1023
- }
1024
- }
1025
- });
1026
- }
1027
- }),
1028
- createExternalCommentSync(doc.id, (sink) => effect(() => sink()), () => threads.value.filter((thread) => thread?.anchor).map((thread) => ({
1029
- id: thread.id,
1030
- cursor: thread.anchor
1031
- }))),
1032
- comments({
1033
- id: doc.id,
1034
- onCreate: ({ cursor }) => {
1035
- const [start, end] = cursor.split(":");
1036
- const name = doc.content && getTextInRange(createDocAccessor(doc.content, [
1037
- "content"
1038
- ]), start, end);
1039
- void intentPlugin?.provides.intent.dispatch({
1040
- action: ThreadAction.CREATE,
1041
- data: {
1042
- cursor,
1043
- name,
1044
- subject: doc
1045
- }
1046
- });
1047
- },
1048
- onDelete: ({ id }) => {
1049
- const stagingArea = state.staging[fullyQualifiedId(doc)];
1050
- if (stagingArea) {
1051
- const index = stagingArea.findIndex((thread2) => thread2.id === id);
1052
- if (index !== -1) {
1053
- stagingArea.splice(index, 1);
1054
- }
1055
- }
1056
- const thread = doc.threads.find((thread2) => thread2?.id === id);
1057
- if (thread) {
1058
- thread.anchor = void 0;
1059
- }
1060
- },
1061
- onUpdate: ({ id, cursor }) => {
1062
- const thread = state.staging[fullyQualifiedId(doc)]?.find((thread2) => thread2.id === id) ?? doc.threads.find((thread2) => thread2?.id === id);
1063
- if (thread instanceof ThreadType && thread.anchor) {
1064
- const [start, end] = thread.anchor.split(":");
1065
- thread.name = doc.content && getTextInRange(createDocAccessor(doc.content, [
1066
- "content"
1067
- ]), start, end);
1068
- thread.anchor = cursor;
1069
- }
1070
- },
1071
- onSelect: ({ selection: { current, closest } }) => {
1072
- const dispatch2 = intentPlugin?.provides.intent.dispatch;
1073
- if (dispatch2) {
1074
- void dispatch2([
1075
- {
1076
- action: ThreadAction.SELECT,
1077
- data: {
1078
- current: current ?? closest
1079
- }
1080
- }
1081
- ]);
1082
- }
1083
- }
1084
- })
1085
- ];
1103
+ return threads(state, doc, intentPlugin?.provides.intent.dispatch);
1086
1104
  }
1087
1105
  }
1088
1106
  }