deepspace 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +247 -1
- package/README.md +18 -41
- package/dist/cli.js +1499 -1327
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +4 -157
- package/dist/index.js +248 -326
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +4 -137
- package/dist/server.js +3 -412
- package/dist/server.js.map +1 -1
- package/dist/worker.d.ts +5 -210
- package/dist/worker.js +3 -419
- package/dist/worker.js.map +1 -1
- package/features/documents/src/LibrarySidebar.tsx +6 -8
- package/features/documents/src/editor/useDocEditor.ts +3 -8
- package/features/landing/feature.json +1 -1
- package/features/messaging/src/components/chat/MessageItem.tsx +83 -34
- package/features/messaging/src/hooks/useChatChannel.ts +2 -8
- package/package.json +2 -2
|
@@ -216,16 +216,14 @@ export function LibrarySidebar({
|
|
|
216
216
|
const onCommitFolderRenameRef = useRef(onCommitRenameFolder)
|
|
217
217
|
onCommitFolderRenameRef.current = onCommitRenameFolder
|
|
218
218
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
// Intentionally read the ref's LATEST value at unmount to clear whatever
|
|
222
|
-
// blur timer is pending; capturing it at effect-setup time (null) would be
|
|
223
|
-
// wrong here since this is a timer ref, not a DOM node.
|
|
224
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps -- must clear the latest pending timer at unmount, not the setup-time value
|
|
225
|
-
if (folderRenameBlurTimerRef.current) clearTimeout(folderRenameBlurTimerRef.current)
|
|
226
|
-
}
|
|
219
|
+
const clearPendingFolderRename = useCallback(() => {
|
|
220
|
+
if (folderRenameBlurTimerRef.current) clearTimeout(folderRenameBlurTimerRef.current)
|
|
227
221
|
}, [])
|
|
228
222
|
|
|
223
|
+
useEffect(() => {
|
|
224
|
+
return clearPendingFolderRename
|
|
225
|
+
}, [clearPendingFolderRename])
|
|
226
|
+
|
|
229
227
|
const submitNewFolder = useCallback(async () => {
|
|
230
228
|
const name = newFolderName.trim()
|
|
231
229
|
if (!name) {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import { useEffect, useMemo, useRef } from 'react'
|
|
17
|
-
import { useEditor, type Editor } from '@tiptap/react'
|
|
17
|
+
import { useEditor, type AnyExtension, type Editor } from '@tiptap/react'
|
|
18
18
|
import StarterKit from '@tiptap/starter-kit'
|
|
19
19
|
import Collaboration from '@tiptap/extension-collaboration'
|
|
20
20
|
import { CollaborationCaret } from '@tiptap/extension-collaboration-caret'
|
|
@@ -127,7 +127,7 @@ export function useDocEditor({
|
|
|
127
127
|
const provider = useMemo(() => (awareness ? { awareness } : null), [awareness])
|
|
128
128
|
|
|
129
129
|
const extensions = useMemo(() => {
|
|
130
|
-
const exts:
|
|
130
|
+
const exts: AnyExtension[] = [
|
|
131
131
|
StarterKit.configure({
|
|
132
132
|
// Yjs has its own undo/redo history via the Collaboration extension; keeping
|
|
133
133
|
// both enabled corrupts the shared document on undo.
|
|
@@ -175,12 +175,7 @@ export function useDocEditor({
|
|
|
175
175
|
// in dev (the editor view is destroyed and re-created, and any subscriber
|
|
176
176
|
// that runs in between throws on `editor.view`). Defer to a useEffect.
|
|
177
177
|
immediatelyRender: false,
|
|
178
|
-
|
|
179
|
-
// here is a hard "unknown rule" error under the scaffolded app's
|
|
180
|
-
// minimal eslint config (which never registers that plugin), while the
|
|
181
|
-
// SDK monorepo's config does enable the rule and needs the suppression.
|
|
182
|
-
// eslint-disable-next-line
|
|
183
|
-
extensions: extensions as any,
|
|
178
|
+
extensions,
|
|
184
179
|
editorProps: {
|
|
185
180
|
transformPastedHTML: stripCollaborationArtifactsFromHTML,
|
|
186
181
|
attributes: {
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"framer-motion": "^12.38.0"
|
|
60
60
|
},
|
|
61
61
|
"instructions": [
|
|
62
|
-
"Configure APP_NAME, HERO_HEADLINE, HERO_SUBHEADLINE, FAQ_ITEMS etc. in
|
|
62
|
+
"Configure APP_NAME, HERO_HEADLINE, HERO_SUBHEADLINE, FAQ_ITEMS, etc. in src/pages/(app)/landing.tsx"
|
|
63
63
|
],
|
|
64
64
|
"patterns": []
|
|
65
65
|
}
|
|
@@ -14,7 +14,14 @@ import type { GroupedReaction } from 'deepspace'
|
|
|
14
14
|
import { UserProfilePopover } from './UserProfilePopover'
|
|
15
15
|
import { useLongPress } from '../hooks/useLongPress'
|
|
16
16
|
|
|
17
|
-
const QUICK_EMOJIS = [
|
|
17
|
+
const QUICK_EMOJIS = [
|
|
18
|
+
'\u{1F44D}',
|
|
19
|
+
'\u2764\uFE0F',
|
|
20
|
+
'\u{1F602}',
|
|
21
|
+
'\u{1F389}',
|
|
22
|
+
'\u{1F525}',
|
|
23
|
+
'\u{1F440}',
|
|
24
|
+
]
|
|
18
25
|
|
|
19
26
|
export interface MessageRect {
|
|
20
27
|
top: number
|
|
@@ -61,6 +68,8 @@ export function MessageItem({
|
|
|
61
68
|
const [editContent, setEditContent] = useState(message.data.content)
|
|
62
69
|
const editContainerRef = useRef<HTMLDivElement>(null)
|
|
63
70
|
const contentRef = useRef<HTMLDivElement>(null)
|
|
71
|
+
const latestMessageContentRef = useRef(message.data.content)
|
|
72
|
+
latestMessageContentRef.current = message.data.content
|
|
64
73
|
|
|
65
74
|
const authorId = message.data.authorId || message.createdBy
|
|
66
75
|
const author = getUser(authorId)
|
|
@@ -78,27 +87,25 @@ export function MessageItem({
|
|
|
78
87
|
height: rect.height,
|
|
79
88
|
})
|
|
80
89
|
},
|
|
81
|
-
{ delay: 500, threshold: 10 }
|
|
90
|
+
{ delay: 500, threshold: 10 },
|
|
82
91
|
)
|
|
83
92
|
|
|
84
93
|
useEffect(() => {
|
|
85
|
-
if (forceEdit
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// Deps intentionally limited to [forceEdit]: this reacts only to the
|
|
90
|
-
// forceEdit trigger. Including isEditing/message.data.content would re-fire
|
|
91
|
-
// on every keystroke and clobber the in-progress edit buffer.
|
|
92
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps -- one-shot on forceEdit; adding isEditing/message.data.content would re-fire on keystrokes and clobber the edit buffer
|
|
93
|
-
}, [forceEdit])
|
|
94
|
+
if (!forceEdit || isEditing) return
|
|
95
|
+
setEditContent(latestMessageContentRef.current)
|
|
96
|
+
setIsEditing(true)
|
|
97
|
+
}, [forceEdit, isEditing])
|
|
94
98
|
|
|
95
99
|
const timestamp = new Date(message.createdAt)
|
|
96
100
|
const timeStr = timestamp.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })
|
|
97
|
-
const dateTimeStr =
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
const dateTimeStr =
|
|
102
|
+
timestamp.toLocaleDateString(undefined, {
|
|
103
|
+
month: '2-digit',
|
|
104
|
+
day: '2-digit',
|
|
105
|
+
year: '2-digit',
|
|
106
|
+
}) +
|
|
107
|
+
', ' +
|
|
108
|
+
timeStr
|
|
102
109
|
|
|
103
110
|
const exitEdit = useCallback(() => {
|
|
104
111
|
setIsEditing(false)
|
|
@@ -173,10 +180,7 @@ export function MessageItem({
|
|
|
173
180
|
}`}
|
|
174
181
|
>
|
|
175
182
|
<div className="shrink-0 w-10" />
|
|
176
|
-
<p
|
|
177
|
-
data-testid="deleted-tombstone"
|
|
178
|
-
className="text-sm italic text-muted-foreground/70"
|
|
179
|
-
>
|
|
183
|
+
<p data-testid="deleted-tombstone" className="text-sm italic text-muted-foreground/70">
|
|
180
184
|
This message was deleted.
|
|
181
185
|
</p>
|
|
182
186
|
</div>
|
|
@@ -205,10 +209,7 @@ export function MessageItem({
|
|
|
205
209
|
</div>
|
|
206
210
|
|
|
207
211
|
{/* Content column */}
|
|
208
|
-
<div
|
|
209
|
-
ref={contentRef}
|
|
210
|
-
className={`flex-1 min-w-0 ${isHighlighted ? 'invisible' : ''}`}
|
|
211
|
-
>
|
|
212
|
+
<div ref={contentRef} className={`flex-1 min-w-0 ${isHighlighted ? 'invisible' : ''}`}>
|
|
212
213
|
{isFirstInGroup && (
|
|
213
214
|
<div className="flex items-baseline gap-2 mb-0.5">
|
|
214
215
|
<UserProfilePopover userId={authorId} onStartDM={onStartDM}>
|
|
@@ -239,7 +240,13 @@ export function MessageItem({
|
|
|
239
240
|
className="p-1.5 rounded-md bg-primary/10 text-primary hover:bg-primary/20 transition-colors"
|
|
240
241
|
title="Save"
|
|
241
242
|
>
|
|
242
|
-
<svg
|
|
243
|
+
<svg
|
|
244
|
+
className="w-4 h-4"
|
|
245
|
+
fill="none"
|
|
246
|
+
viewBox="0 0 24 24"
|
|
247
|
+
stroke="currentColor"
|
|
248
|
+
strokeWidth={2}
|
|
249
|
+
>
|
|
243
250
|
<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
|
|
244
251
|
</svg>
|
|
245
252
|
</button>
|
|
@@ -249,7 +256,13 @@ export function MessageItem({
|
|
|
249
256
|
className="p-1.5 rounded-md bg-muted text-muted-foreground hover:bg-destructive/10 hover:text-destructive transition-colors"
|
|
250
257
|
title="Cancel"
|
|
251
258
|
>
|
|
252
|
-
<svg
|
|
259
|
+
<svg
|
|
260
|
+
className="w-4 h-4"
|
|
261
|
+
fill="none"
|
|
262
|
+
viewBox="0 0 24 24"
|
|
263
|
+
stroke="currentColor"
|
|
264
|
+
strokeWidth={2}
|
|
265
|
+
>
|
|
253
266
|
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
254
267
|
</svg>
|
|
255
268
|
</button>
|
|
@@ -257,7 +270,10 @@ export function MessageItem({
|
|
|
257
270
|
</div>
|
|
258
271
|
</div>
|
|
259
272
|
) : (
|
|
260
|
-
<p
|
|
273
|
+
<p
|
|
274
|
+
className="text-sm text-foreground/90 whitespace-pre-wrap break-words leading-relaxed"
|
|
275
|
+
data-testid="message-content"
|
|
276
|
+
>
|
|
261
277
|
{message.data.content}
|
|
262
278
|
{message.data.edited ? (
|
|
263
279
|
<span className="text-[10px] ml-1.5 text-muted-foreground italic">(edited)</span>
|
|
@@ -300,7 +316,10 @@ export function MessageItem({
|
|
|
300
316
|
|
|
301
317
|
{/* Desktop hover toolbar */}
|
|
302
318
|
{!isEditing && (
|
|
303
|
-
<div
|
|
319
|
+
<div
|
|
320
|
+
data-testid={`hover-toolbar-${message.recordId}`}
|
|
321
|
+
className="absolute -top-3 right-5 flex items-center gap-0.5 bg-card border border-border/60 rounded-lg shadow-sm p-0.5 transition-all duration-100 opacity-0 pointer-events-none group-hover:opacity-100 group-hover:pointer-events-auto"
|
|
322
|
+
>
|
|
304
323
|
{QUICK_EMOJIS.map((emoji) => (
|
|
305
324
|
<button
|
|
306
325
|
key={emoji}
|
|
@@ -318,8 +337,18 @@ export function MessageItem({
|
|
|
318
337
|
className="p-1.5 rounded-md hover:bg-muted text-muted-foreground hover:text-foreground transition-colors"
|
|
319
338
|
title="Reply in thread"
|
|
320
339
|
>
|
|
321
|
-
<svg
|
|
322
|
-
|
|
340
|
+
<svg
|
|
341
|
+
className="w-3.5 h-3.5"
|
|
342
|
+
fill="none"
|
|
343
|
+
viewBox="0 0 24 24"
|
|
344
|
+
stroke="currentColor"
|
|
345
|
+
strokeWidth={1.5}
|
|
346
|
+
>
|
|
347
|
+
<path
|
|
348
|
+
strokeLinecap="round"
|
|
349
|
+
strokeLinejoin="round"
|
|
350
|
+
d="M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6"
|
|
351
|
+
/>
|
|
323
352
|
</svg>
|
|
324
353
|
</button>
|
|
325
354
|
{isOwn && (
|
|
@@ -333,8 +362,18 @@ export function MessageItem({
|
|
|
333
362
|
className="p-1.5 rounded-md hover:bg-muted text-muted-foreground hover:text-foreground transition-colors"
|
|
334
363
|
title="Edit message"
|
|
335
364
|
>
|
|
336
|
-
<svg
|
|
337
|
-
|
|
365
|
+
<svg
|
|
366
|
+
className="w-3.5 h-3.5"
|
|
367
|
+
fill="none"
|
|
368
|
+
viewBox="0 0 24 24"
|
|
369
|
+
stroke="currentColor"
|
|
370
|
+
strokeWidth={1.5}
|
|
371
|
+
>
|
|
372
|
+
<path
|
|
373
|
+
strokeLinecap="round"
|
|
374
|
+
strokeLinejoin="round"
|
|
375
|
+
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
|
|
376
|
+
/>
|
|
338
377
|
</svg>
|
|
339
378
|
</button>
|
|
340
379
|
<button
|
|
@@ -343,8 +382,18 @@ export function MessageItem({
|
|
|
343
382
|
className="p-1.5 rounded-md hover:bg-destructive/10 text-muted-foreground hover:text-destructive transition-colors"
|
|
344
383
|
title="Delete message"
|
|
345
384
|
>
|
|
346
|
-
<svg
|
|
347
|
-
|
|
385
|
+
<svg
|
|
386
|
+
className="w-3.5 h-3.5"
|
|
387
|
+
fill="none"
|
|
388
|
+
viewBox="0 0 24 24"
|
|
389
|
+
stroke="currentColor"
|
|
390
|
+
strokeWidth={1.5}
|
|
391
|
+
>
|
|
392
|
+
<path
|
|
393
|
+
strokeLinecap="round"
|
|
394
|
+
strokeLinejoin="round"
|
|
395
|
+
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
|
396
|
+
/>
|
|
348
397
|
</svg>
|
|
349
398
|
</button>
|
|
350
399
|
</>
|
|
@@ -36,9 +36,7 @@ export function useChatChannel(channelName: string = DEFAULT_CHANNEL_NAME) {
|
|
|
36
36
|
const defaultChannel = channels
|
|
37
37
|
.filter(
|
|
38
38
|
(c: RecordData<Channel>) =>
|
|
39
|
-
c.data.name === channelName &&
|
|
40
|
-
c.data.type === 'public' &&
|
|
41
|
-
!c.data.archived,
|
|
39
|
+
c.data.name === channelName && c.data.type === 'public' && !c.data.archived,
|
|
42
40
|
)
|
|
43
41
|
.sort(
|
|
44
42
|
(a: RecordData<Channel>, b: RecordData<Channel>) =>
|
|
@@ -59,11 +57,7 @@ export function useChatChannel(channelName: string = DEFAULT_CHANNEL_NAME) {
|
|
|
59
57
|
description: 'Default chat channel',
|
|
60
58
|
})
|
|
61
59
|
}
|
|
62
|
-
|
|
63
|
-
// guarded by hasInitialized. Including defaultChannel/create/channelName
|
|
64
|
-
// would re-run and defeat the create-once semantics.
|
|
65
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps -- one-shot init guarded by hasInitialized; adding defaultChannel/create/channelName would defeat create-once
|
|
66
|
-
}, [user, status])
|
|
60
|
+
}, [user, status, defaultChannel, create, channelName])
|
|
67
61
|
|
|
68
62
|
// Wait for the members query to be ready before auto-joining.
|
|
69
63
|
useEffect(() => {
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deepspace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"engines": {
|
|
5
|
-
"node": ">=22.15.0"
|
|
5
|
+
"node": ">=22.15.0 <23 || >=24 <25 || >=26 <27"
|
|
6
6
|
},
|
|
7
7
|
"description": "DeepSpace SDK — build real-time collaborative apps on Cloudflare Workers",
|
|
8
8
|
"type": "module",
|