@dxos/plugin-thread 0.6.13 → 0.6.14-main.1366248

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