@dxos/plugin-thread 0.6.13 → 0.6.14-main.69511f5

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 (101) hide show
  1. package/dist/lib/browser/{ThreadArticle-O64ZECMF.mjs → ThreadArticle-2Y7UWZLA.mjs} +3 -3
  2. package/dist/lib/browser/{ThreadMain-ZDCRC6RG.mjs → ThreadMain-VK5UEBQ7.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-P7ZE5N5M.mjs → chunk-LHT42MTJ.mjs} +16 -16
  6. package/dist/lib/browser/chunk-LHT42MTJ.mjs.map +7 -0
  7. package/dist/lib/browser/{chunk-GKPZHUT3.mjs → chunk-WKCIKGGW.mjs} +2 -2
  8. package/dist/lib/browser/{chunk-GKPZHUT3.mjs.map → chunk-WKCIKGGW.mjs.map} +1 -1
  9. package/dist/lib/browser/index.mjs +431 -383
  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-CIYBYISR.cjs} +7 -7
  15. package/dist/lib/node/{ThreadMain-XJX75GHG.cjs → ThreadMain-V6KBUQLH.cjs} +7 -7
  16. package/dist/lib/node/{chunk-D5GOVXHS.cjs → chunk-A2IORKQK.cjs} +6 -6
  17. package/dist/lib/node/{chunk-D5GOVXHS.cjs.map → chunk-A2IORKQK.cjs.map} +1 -1
  18. package/dist/lib/node/{chunk-5PZNPPQF.cjs → chunk-HWN7Q5XK.cjs} +65 -65
  19. package/dist/lib/node/chunk-HWN7Q5XK.cjs.map +7 -0
  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 +480 -436
  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-PXO4ZJFU.mjs +19 -0
  30. package/dist/lib/node-esm/ThreadArticle-PXO4ZJFU.mjs.map +7 -0
  31. package/dist/lib/node-esm/ThreadMain-U7R24CN3.mjs +27 -0
  32. package/dist/lib/node-esm/ThreadMain-U7R24CN3.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-KWNXG6FG.mjs +21 -0
  36. package/dist/lib/node-esm/chunk-KWNXG6FG.mjs.map +7 -0
  37. package/dist/lib/node-esm/chunk-SXXOBRAX.mjs +411 -0
  38. package/dist/lib/node-esm/chunk-SXXOBRAX.mjs.map +7 -0
  39. package/dist/lib/node-esm/index.mjs +1151 -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/Chat.stories.d.ts +3 -42
  48. package/dist/types/src/components/Chat.stories.d.ts.map +1 -1
  49. package/dist/types/src/components/ChatContainer.d.ts.map +1 -1
  50. package/dist/types/src/components/CommentContainer.d.ts +1 -1
  51. package/dist/types/src/components/CommentContainer.d.ts.map +1 -1
  52. package/dist/types/src/components/Comments.stories.d.ts +3 -41
  53. package/dist/types/src/components/Comments.stories.d.ts.map +1 -1
  54. package/dist/types/src/components/CommentsContainer.d.ts +1 -5
  55. package/dist/types/src/components/CommentsContainer.d.ts.map +1 -1
  56. package/dist/types/src/components/MessageContainer.d.ts.map +1 -1
  57. package/dist/types/src/components/ThreadComplementary.d.ts +10 -0
  58. package/dist/types/src/components/ThreadComplementary.d.ts.map +1 -0
  59. package/dist/types/src/components/index.d.ts +6 -6
  60. package/dist/types/src/components/index.d.ts.map +1 -1
  61. package/dist/types/src/components/testing.d.ts +2 -0
  62. package/dist/types/src/components/testing.d.ts.map +1 -1
  63. package/dist/types/src/components/types.d.ts +2 -2
  64. package/dist/types/src/components/types.d.ts.map +1 -1
  65. package/dist/types/src/extensions/index.d.ts +2 -0
  66. package/dist/types/src/extensions/index.d.ts.map +1 -0
  67. package/dist/types/src/extensions/threads.d.ts +9 -0
  68. package/dist/types/src/extensions/threads.d.ts.map +1 -0
  69. package/dist/types/src/meta.d.ts +3 -9
  70. package/dist/types/src/meta.d.ts.map +1 -1
  71. package/dist/types/src/types.d.ts +7 -2
  72. package/dist/types/src/types.d.ts.map +1 -1
  73. package/package.json +41 -46
  74. package/src/ThreadPlugin.tsx +146 -307
  75. package/src/components/Chat.stories.tsx +7 -4
  76. package/src/components/ChatContainer.tsx +7 -8
  77. package/src/components/CommentContainer.tsx +18 -19
  78. package/src/components/Comments.stories.tsx +9 -6
  79. package/src/components/CommentsContainer.tsx +18 -31
  80. package/src/components/MessageContainer.tsx +13 -23
  81. package/src/components/ThreadComplementary.tsx +113 -0
  82. package/src/components/ThreadSettings.tsx +3 -3
  83. package/src/components/index.ts +6 -6
  84. package/src/components/types.ts +2 -2
  85. package/src/extensions/index.ts +5 -0
  86. package/src/extensions/threads.ts +117 -0
  87. package/src/meta.ts +15 -0
  88. package/src/types.ts +9 -4
  89. package/dist/lib/browser/chunk-5IBJO53M.mjs +0 -21
  90. package/dist/lib/browser/chunk-5IBJO53M.mjs.map +0 -7
  91. package/dist/lib/browser/chunk-P7ZE5N5M.mjs.map +0 -7
  92. package/dist/lib/node/chunk-5PZNPPQF.cjs.map +0 -7
  93. package/dist/lib/node/chunk-BWBONZGU.cjs.map +0 -7
  94. package/dist/types/src/types.test.d.ts +0 -2
  95. package/dist/types/src/types.test.d.ts.map +0 -1
  96. package/src/meta.tsx +0 -20
  97. package/src/types.test.ts +0 -13
  98. /package/dist/lib/browser/{ThreadArticle-O64ZECMF.mjs.map → ThreadArticle-2Y7UWZLA.mjs.map} +0 -0
  99. /package/dist/lib/browser/{ThreadMain-ZDCRC6RG.mjs.map → ThreadMain-VK5UEBQ7.mjs.map} +0 -0
  100. /package/dist/lib/node/{ThreadArticle-DO56JXBU.cjs.map → ThreadArticle-CIYBYISR.cjs.map} +0 -0
  101. /package/dist/lib/node/{ThreadMain-XJX75GHG.cjs.map → ThreadMain-V6KBUQLH.cjs.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ThreadAction
3
- } from "./chunk-GKPZHUT3.mjs";
3
+ } from "./chunk-WKCIKGGW.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-LHT42MTJ.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
21
  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";
22
+ import { NavigationAction, isLayoutParts, parseIntentPlugin, parseNavigationPlugin, parseMetadataResolverPlugin, resolvePlugin, LayoutAction as LayoutAction2 } from "@dxos/app-framework";
27
23
  import { LocalStorageStore } from "@dxos/local-storage";
28
- import { parseAttentionPlugin } from "@dxos/plugin-attention";
24
+ import { log } from "@dxos/log";
29
25
  import { parseClientPlugin } from "@dxos/plugin-client";
30
- import { createExtension, isActionGroup } from "@dxos/plugin-graph";
31
- import { DocumentType } from "@dxos/plugin-markdown/types";
26
+ import { createExtension, isActionGroup, toSignal } from "@dxos/plugin-graph";
32
27
  import { ObservabilityAction } from "@dxos/plugin-observability/meta";
33
28
  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";
29
+ import { ThreadType as ThreadType2, MessageType as MessageType2, ChannelType } from "@dxos/plugin-space/types";
30
+ import { create as create2, getTypename } from "@dxos/react-client/echo";
31
+ import { getSpace as getSpace3, fullyQualifiedId as fullyQualifiedId5, loadObjectReferences, parseId } 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 { lazy } 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 { Input, useTranslation as useTranslation3 } from "@dxos/react-ui";
358
+ import { FormInput } from "@dxos/react-ui-data";
359
+ var ThreadSettings = ({ settings }) => {
360
+ const { t } = useTranslation3(THREAD_PLUGIN);
361
+ return /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(FormInput, {
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 = lazy(() => import("./ThreadMain-VK5UEBQ7.mjs"));
371
+ var ThreadArticle = lazy(() => import("./ThreadArticle-2Y7UWZLA.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,18 +583,20 @@ 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
  }
598
+ const type = "orphan-comments-for-subject";
599
+ const icon = "ph--chat-text--regular";
455
600
  return [
456
601
  createExtension({
457
602
  id: `${THREAD_PLUGIN}/comments-for-subject`,
@@ -459,29 +604,55 @@ var ThreadPlugin = () => {
459
604
  if (!id.endsWith("~comments")) {
460
605
  return;
461
606
  }
462
- const docId = id.split("~").at(0);
463
- const [spaceId, objectId] = docId?.split(":") ?? [];
607
+ const [subjectId] = id.split("~");
608
+ const { spaceId, objectId } = parseId(subjectId);
464
609
  const space = client.spaces.get().find((space2) => space2.id === spaceId);
465
- const doc = space?.db.getObjectById(objectId);
466
- if (!doc || !docId) {
610
+ if (!objectId) {
611
+ return {
612
+ id,
613
+ type,
614
+ data: null,
615
+ properties: {
616
+ icon,
617
+ label: [
618
+ "unnamed object threads label",
619
+ {
620
+ ns: THREAD_PLUGIN
621
+ }
622
+ ],
623
+ showResolvedThreads: false,
624
+ object: null,
625
+ space
626
+ }
627
+ };
628
+ }
629
+ const object = toSignal((onChange) => {
630
+ const timeout = setTimeout(async () => {
631
+ await space?.db.loadObjectById(objectId);
632
+ onChange();
633
+ });
634
+ return () => clearTimeout(timeout);
635
+ }, () => space?.db.getObjectById(objectId), subjectId);
636
+ if (!object || !subjectId) {
467
637
  return;
468
638
  }
469
- const docMeta = metadataResolver(getTypename(doc) ?? "");
470
- const label = docMeta.label?.(doc) || doc.name || docMeta.placeholder || [
639
+ const meta = metadataResolver(getTypename(object) ?? "");
640
+ const label = meta.label?.(object) || object.name || meta.placeholder || [
471
641
  "unnamed object threads label",
472
642
  {
473
643
  ns: THREAD_PLUGIN
474
644
  }
475
645
  ];
476
- const viewState = getViewState(docId);
646
+ const viewState = getViewState(subjectId);
477
647
  return {
478
648
  id,
479
- type: "orphan-comments-for-subject",
480
- data: doc,
649
+ type,
650
+ data: null,
481
651
  properties: {
482
- icon: meta_default.iconComponent,
652
+ icon,
483
653
  label,
484
- showResolvedThreads: viewState.showResolvedThreads
654
+ showResolvedThreads: viewState.showResolvedThreads,
655
+ object
485
656
  }
486
657
  };
487
658
  },
@@ -495,7 +666,7 @@ var ThreadPlugin = () => {
495
666
  const toggle = () => {
496
667
  const newToggleState = !viewState.showResolvedThreads;
497
668
  viewState.showResolvedThreads = newToggleState;
498
- void dispatch2({
669
+ void dispatch({
499
670
  action: ObservabilityAction.SEND_EVENT,
500
671
  data: {
501
672
  name: "threads.toggle-show-resolved",
@@ -520,7 +691,8 @@ var ThreadPlugin = () => {
520
691
  ],
521
692
  menuItemType: "toggle",
522
693
  isChecked: viewState.showResolvedThreads,
523
- testId: "threadPlugin.toggleShowResolved"
694
+ testId: "threadPlugin.toggleShowResolved",
695
+ icon: viewState.showResolvedThreads ? "ph--eye-slash--regular" : "ph--eye--regular"
524
696
  }
525
697
  }
526
698
  ];
@@ -542,7 +714,7 @@ var ThreadPlugin = () => {
542
714
  {
543
715
  id: `${THREAD_PLUGIN}/create/${node.id}`,
544
716
  data: async () => {
545
- await dispatch2([
717
+ await dispatch([
546
718
  {
547
719
  plugin: THREAD_PLUGIN,
548
720
  action: ThreadAction.CREATE
@@ -565,8 +737,7 @@ var ThreadPlugin = () => {
565
737
  ns: THREAD_PLUGIN
566
738
  }
567
739
  ],
568
- icon: (props) => /* @__PURE__ */ React5.createElement(Chat, props),
569
- iconSymbol: "ph--chat--regular",
740
+ icon: "ph--chat--regular",
570
741
  testId: "threadPlugin.createObject"
571
742
  }
572
743
  }
@@ -589,11 +760,8 @@ var ThreadPlugin = () => {
589
760
  settings: settings.values
590
761
  }) : null;
591
762
  }
592
- case "article":
593
- case "complementary": {
763
+ case "article": {
594
764
  const location = navigationPlugin?.provides.location;
595
- const threadsIntegrators = useResolvePlugins(providesThreadsConfig);
596
- const threadProvides = threadsIntegrators.map((p) => p.provides.thread);
597
765
  if (data.object instanceof ChannelType && data.object.threads[0]) {
598
766
  const channel = data.object;
599
767
  if (isLayoutParts(location?.active) && location.active.main) {
@@ -601,7 +769,7 @@ var ThreadPlugin = () => {
601
769
  const currentPosition = layoutEntries.findIndex((entry) => channel.id === entry.id);
602
770
  if (currentPosition > 0) {
603
771
  const objectToTheLeft = layoutEntries[currentPosition - 1];
604
- const context = getSpace2(data.object)?.db.getObjectById(objectToTheLeft.id);
772
+ const context = getSpace3(data.object)?.db.getObjectById(objectToTheLeft.id);
605
773
  return /* @__PURE__ */ React5.createElement(ThreadArticle, {
606
774
  thread: data.object.threads[0],
607
775
  context
@@ -612,92 +780,27 @@ var ThreadPlugin = () => {
612
780
  thread: data.object.threads[0]
613
781
  });
614
782
  }
615
- if (data.subject instanceof ThreadType) {
783
+ if (data.subject instanceof ThreadType2) {
616
784
  return /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(ChatHeading, {
617
785
  attendableId: data.subject.id
618
786
  }), /* @__PURE__ */ React5.createElement(ChatContainer, {
619
787
  thread: data.subject
620
788
  }));
621
789
  }
790
+ break;
791
+ }
792
+ case "complementary--comments": {
622
793
  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)))));
794
+ const { showResolvedThreads } = getViewState(fullyQualifiedId5(data.subject));
795
+ return /* @__PURE__ */ React5.createElement(ThreadComplementary, {
796
+ role,
797
+ subject: data.subject,
798
+ drafts: state.drafts[fullyQualifiedId5(data.subject)],
799
+ current: state.current,
800
+ showResolvedThreads
801
+ });
700
802
  }
803
+ break;
701
804
  }
702
805
  }
703
806
  return null;
@@ -709,18 +812,31 @@ var ThreadPlugin = () => {
709
812
  case ThreadAction.CREATE: {
710
813
  if (intent.data && intent.data.cursor !== void 0) {
711
814
  const { cursor, name, subject } = intent.data;
712
- const subjectId = fullyQualifiedId(subject);
713
- const thread = create2(ThreadType, {
815
+ if (subject?.threads === void 0) {
816
+ try {
817
+ subject.threads = [];
818
+ } catch (err) {
819
+ log.error("Subject does not support threads array", subject?.typename, {
820
+ F: __dxlog_file,
821
+ L: 347,
822
+ S: void 0,
823
+ C: (f, a) => f(...a)
824
+ });
825
+ return;
826
+ }
827
+ }
828
+ const subjectId = fullyQualifiedId5(subject);
829
+ const thread = create2(ThreadType2, {
714
830
  name,
715
831
  anchor: cursor,
716
832
  messages: [],
717
833
  status: "staged"
718
834
  });
719
- const stagingArea = state.staging[subjectId];
720
- if (stagingArea) {
721
- stagingArea.push(thread);
835
+ const draft = state.drafts[subjectId];
836
+ if (draft) {
837
+ draft.push(thread);
722
838
  } else {
723
- state.staging[subjectId] = [
839
+ state.drafts[subjectId] = [
724
840
  thread
725
841
  ];
726
842
  }
@@ -729,25 +845,27 @@ var ThreadPlugin = () => {
729
845
  intents: [
730
846
  [
731
847
  {
732
- action: NavigationAction.OPEN,
848
+ action: ThreadAction.SELECT,
733
849
  data: {
734
- activeParts: {
735
- complementary: `${subjectId}${SLUG_PATH_SEPARATOR}comments${SLUG_COLLECTION_INDICATOR}`
736
- }
850
+ current: fullyQualifiedId5(thread)
737
851
  }
738
- },
852
+ }
853
+ ],
854
+ [
739
855
  {
740
- action: ThreadAction.SELECT,
856
+ action: NavigationAction.OPEN,
741
857
  data: {
742
- current: thread.id,
743
- focus: true
858
+ activeParts: {
859
+ complementary: "comments"
860
+ }
744
861
  }
745
- },
862
+ }
863
+ ],
864
+ [
746
865
  {
747
- action: LayoutAction.SET_LAYOUT,
866
+ action: LayoutAction2.SET_LAYOUT,
748
867
  data: {
749
868
  element: "complementary",
750
- subject: subjectId,
751
869
  state: true
752
870
  }
753
871
  }
@@ -758,7 +876,7 @@ var ThreadPlugin = () => {
758
876
  return {
759
877
  data: create2(ChannelType, {
760
878
  threads: [
761
- create2(ThreadType, {
879
+ create2(ThreadType2, {
762
880
  messages: []
763
881
  })
764
882
  ]
@@ -767,15 +885,26 @@ var ThreadPlugin = () => {
767
885
  }
768
886
  }
769
887
  case ThreadAction.SELECT: {
770
- state.focus = intent.data?.current === state.current ? state.focus : intent.data?.focus;
771
888
  state.current = intent.data?.current;
772
889
  return {
773
- data: true
890
+ data: true,
891
+ intents: !intent.data?.skipOpen ? [
892
+ [
893
+ {
894
+ action: NavigationAction.OPEN,
895
+ data: {
896
+ activeParts: {
897
+ complementary: "comments"
898
+ }
899
+ }
900
+ }
901
+ ]
902
+ ] : []
774
903
  };
775
904
  }
776
905
  case ThreadAction.TOGGLE_RESOLVED: {
777
906
  const { thread } = intent.data ?? {};
778
- if (!(thread instanceof ThreadType)) {
907
+ if (!(thread instanceof ThreadType2)) {
779
908
  return;
780
909
  }
781
910
  if (thread.status === "active" || thread.status === void 0) {
@@ -783,9 +912,10 @@ var ThreadPlugin = () => {
783
912
  } else if (thread.status === "resolved") {
784
913
  thread.status = "active";
785
914
  }
786
- const space = getSpace2(thread);
915
+ const space = getSpace3(thread);
787
916
  const spaceId = space?.id;
788
917
  return {
918
+ data: true,
789
919
  intents: [
790
920
  [
791
921
  {
@@ -804,19 +934,21 @@ var ThreadPlugin = () => {
804
934
  }
805
935
  case ThreadAction.DELETE: {
806
936
  const { subject, thread } = intent.data ?? {};
807
- if (!(subject instanceof DocumentType) || !(thread instanceof ThreadType)) {
937
+ if (!(thread instanceof ThreadType2) || !(subject && typeof subject === "object" && "threads" in subject)) {
808
938
  return;
809
939
  }
810
- const subjectId = fullyQualifiedId(subject);
811
- const stagingArea = state.staging[subjectId];
812
- if (stagingArea) {
813
- const index = stagingArea.findIndex((t) => t.id === thread.id);
940
+ const subjectId = fullyQualifiedId5(subject);
941
+ const draft = state.drafts[subjectId];
942
+ if (draft) {
943
+ const index = draft.findIndex((t) => t.id === thread.id);
814
944
  if (index !== -1) {
815
- stagingArea.splice(index, 1);
816
- return;
945
+ draft.splice(index, 1);
946
+ return {
947
+ data: true
948
+ };
817
949
  }
818
950
  }
819
- const space = getSpace2(thread);
951
+ const space = getSpace3(thread);
820
952
  if (!space || !subject.threads) {
821
953
  return;
822
954
  }
@@ -874,22 +1006,22 @@ var ThreadPlugin = () => {
874
1006
  }
875
1007
  case ThreadAction.ON_MESSAGE_ADD: {
876
1008
  const { thread, subject } = intent.data ?? {};
877
- if (!(thread instanceof ThreadType) || !(subject && typeof subject === "object" && "threads" in subject)) {
1009
+ if (!(thread instanceof ThreadType2) || !(subject && typeof subject === "object" && "threads" in subject && Array.isArray(subject.threads))) {
878
1010
  return;
879
1011
  }
880
- const subjectId = fullyQualifiedId(subject);
881
- const space = getSpace2(subject);
1012
+ const subjectId = fullyQualifiedId5(subject);
1013
+ const space = getSpace3(subject);
882
1014
  const intents = [];
883
1015
  const analyticsProperties = {
884
1016
  threadId: thread.id,
885
1017
  spaceId: space?.id
886
1018
  };
887
- if (state.staging[subjectId]?.find((t) => t === thread)) {
1019
+ if (state.drafts[subjectId]?.find((t) => t === thread)) {
888
1020
  thread.status = "active";
889
1021
  subject.threads ? subject.threads.push(thread) : subject.threads = [
890
1022
  thread
891
1023
  ];
892
- state.staging[subjectId] = state.staging[subjectId]?.filter((t) => t.id !== thread.id);
1024
+ state.drafts[subjectId] = state.drafts[subjectId]?.filter(({ id }) => id !== thread.id);
893
1025
  intents.push({
894
1026
  action: ObservabilityAction.SEND_EVENT,
895
1027
  data: {
@@ -917,8 +1049,8 @@ var ThreadPlugin = () => {
917
1049
  }
918
1050
  case ThreadAction.DELETE_MESSAGE: {
919
1051
  const { subject, thread, messageId } = intent.data ?? {};
920
- const space = getSpace2(subject);
921
- if (!(subject instanceof DocumentType) || !(thread instanceof ThreadType) || !space) {
1052
+ const space = getSpace3(subject);
1053
+ if (!(thread instanceof ThreadType2) || !(subject && typeof subject === "object" && "threads" in subject)) {
922
1054
  return;
923
1055
  }
924
1056
  if (!intent.undo) {
@@ -960,7 +1092,7 @@ var ThreadPlugin = () => {
960
1092
  name: "threads.message.delete",
961
1093
  properties: {
962
1094
  threadId: thread.id,
963
- spaceId: space.id
1095
+ spaceId: space?.id
964
1096
  }
965
1097
  }
966
1098
  }
@@ -984,7 +1116,7 @@ var ThreadPlugin = () => {
984
1116
  name: "threads.message.undo-delete",
985
1117
  properties: {
986
1118
  threadId: thread.id,
987
- spaceId: space.id
1119
+ spaceId: space?.id
988
1120
  }
989
1121
  }
990
1122
  }
@@ -997,92 +1129,8 @@ var ThreadPlugin = () => {
997
1129
  }
998
1130
  },
999
1131
  markdown: {
1000
- // TODO(burdon): Factor out extension factory into separate file (for simplicity).
1001
1132
  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
- ];
1133
+ return threads(state, doc, intentPlugin?.provides.intent.dispatch);
1086
1134
  }
1087
1135
  }
1088
1136
  }