@actuate-media/cms-admin 0.12.0 → 0.12.1
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/dist/__tests__/components/comment-mark.test.d.ts +2 -0
- package/dist/__tests__/components/comment-mark.test.d.ts.map +1 -0
- package/dist/__tests__/components/comment-mark.test.js +89 -0
- package/dist/__tests__/components/comment-mark.test.js.map +1 -0
- package/dist/__tests__/components/comment-side-panel.test.d.ts +2 -0
- package/dist/__tests__/components/comment-side-panel.test.d.ts.map +1 -0
- package/dist/__tests__/components/comment-side-panel.test.js +291 -0
- package/dist/__tests__/components/comment-side-panel.test.js.map +1 -0
- package/dist/__tests__/components/notification-bell.test.d.ts +2 -0
- package/dist/__tests__/components/notification-bell.test.d.ts.map +1 -0
- package/dist/__tests__/components/notification-bell.test.js +200 -0
- package/dist/__tests__/components/notification-bell.test.js.map +1 -0
- package/dist/__tests__/components/offline-status.test.d.ts +2 -0
- package/dist/__tests__/components/offline-status.test.d.ts.map +1 -0
- package/dist/__tests__/components/offline-status.test.js +56 -0
- package/dist/__tests__/components/offline-status.test.js.map +1 -0
- package/dist/__tests__/components/presence-chips-helpers.test.d.ts +2 -0
- package/dist/__tests__/components/presence-chips-helpers.test.d.ts.map +1 -0
- package/dist/__tests__/components/presence-chips-helpers.test.js +35 -0
- package/dist/__tests__/components/presence-chips-helpers.test.js.map +1 -0
- package/dist/__tests__/lib/active-editor.test.d.ts +2 -0
- package/dist/__tests__/lib/active-editor.test.d.ts.map +1 -0
- package/dist/__tests__/lib/active-editor.test.js +57 -0
- package/dist/__tests__/lib/active-editor.test.js.map +1 -0
- package/dist/__tests__/lib/coauthor-client.test.d.ts +2 -0
- package/dist/__tests__/lib/coauthor-client.test.d.ts.map +1 -0
- package/dist/__tests__/lib/coauthor-client.test.js +173 -0
- package/dist/__tests__/lib/coauthor-client.test.js.map +1 -0
- package/dist/__tests__/lib/collaboration-provider.test.d.ts +2 -0
- package/dist/__tests__/lib/collaboration-provider.test.d.ts.map +1 -0
- package/dist/__tests__/lib/collaboration-provider.test.js +335 -0
- package/dist/__tests__/lib/collaboration-provider.test.js.map +1 -0
- package/dist/__tests__/lib/comment-anchor.test.d.ts +2 -0
- package/dist/__tests__/lib/comment-anchor.test.d.ts.map +1 -0
- package/dist/__tests__/lib/comment-anchor.test.js +173 -0
- package/dist/__tests__/lib/comment-anchor.test.js.map +1 -0
- package/dist/__tests__/lib/comments-client.test.d.ts +2 -0
- package/dist/__tests__/lib/comments-client.test.d.ts.map +1 -0
- package/dist/__tests__/lib/comments-client.test.js +202 -0
- package/dist/__tests__/lib/comments-client.test.js.map +1 -0
- package/dist/__tests__/lib/notifications-client.test.d.ts +2 -0
- package/dist/__tests__/lib/notifications-client.test.d.ts.map +1 -0
- package/dist/__tests__/lib/notifications-client.test.js +155 -0
- package/dist/__tests__/lib/notifications-client.test.js.map +1 -0
- package/dist/__tests__/lib/slash-command.test.d.ts +2 -0
- package/dist/__tests__/lib/slash-command.test.d.ts.map +1 -0
- package/dist/__tests__/lib/slash-command.test.js +123 -0
- package/dist/__tests__/lib/slash-command.test.js.map +1 -0
- package/dist/actuate-admin.css +1 -1
- package/dist/components/AICoauthorBubbleMenu.d.ts +18 -0
- package/dist/components/AICoauthorBubbleMenu.d.ts.map +1 -0
- package/dist/components/AICoauthorBubbleMenu.js +113 -0
- package/dist/components/AICoauthorBubbleMenu.js.map +1 -0
- package/dist/components/CoauthorResultPopover.d.ts +28 -0
- package/dist/components/CoauthorResultPopover.d.ts.map +1 -0
- package/dist/components/CoauthorResultPopover.js +42 -0
- package/dist/components/CoauthorResultPopover.js.map +1 -0
- package/dist/components/CommandPalette.d.ts +1 -1
- package/dist/components/CommandPalette.d.ts.map +1 -1
- package/dist/components/CommandPalette.js +138 -40
- package/dist/components/CommandPalette.js.map +1 -1
- package/dist/components/CommentMark.d.ts +54 -0
- package/dist/components/CommentMark.d.ts.map +1 -0
- package/dist/components/CommentMark.js +104 -0
- package/dist/components/CommentMark.js.map +1 -0
- package/dist/components/CommentSidePanel.d.ts +86 -0
- package/dist/components/CommentSidePanel.d.ts.map +1 -0
- package/dist/components/CommentSidePanel.js +165 -0
- package/dist/components/CommentSidePanel.js.map +1 -0
- package/dist/components/LivePreview.d.ts.map +1 -1
- package/dist/components/LivePreview.js +80 -16
- package/dist/components/LivePreview.js.map +1 -1
- package/dist/components/NotificationBell.d.ts +45 -0
- package/dist/components/NotificationBell.d.ts.map +1 -0
- package/dist/components/NotificationBell.js +90 -0
- package/dist/components/NotificationBell.js.map +1 -0
- package/dist/components/OfflineStatus.d.ts +39 -0
- package/dist/components/OfflineStatus.d.ts.map +1 -0
- package/dist/components/OfflineStatus.js +47 -0
- package/dist/components/OfflineStatus.js.map +1 -0
- package/dist/components/PresenceChips.d.ts +31 -0
- package/dist/components/PresenceChips.d.ts.map +1 -0
- package/dist/components/PresenceChips.js +58 -0
- package/dist/components/PresenceChips.js.map +1 -0
- package/dist/components/TipTapEditor.d.ts +61 -1
- package/dist/components/TipTapEditor.d.ts.map +1 -1
- package/dist/components/TipTapEditor.js +211 -8
- package/dist/components/TipTapEditor.js.map +1 -1
- package/dist/components/ui/Badge.d.ts +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/active-editor.d.ts +6 -0
- package/dist/lib/active-editor.d.ts.map +1 -0
- package/dist/lib/active-editor.js +35 -0
- package/dist/lib/active-editor.js.map +1 -0
- package/dist/lib/coauthor-client.d.ts +72 -0
- package/dist/lib/coauthor-client.d.ts.map +1 -0
- package/dist/lib/coauthor-client.js +98 -0
- package/dist/lib/coauthor-client.js.map +1 -0
- package/dist/lib/collaboration-provider.d.ts +142 -0
- package/dist/lib/collaboration-provider.d.ts.map +1 -0
- package/dist/lib/collaboration-provider.js +168 -0
- package/dist/lib/collaboration-provider.js.map +1 -0
- package/dist/lib/comment-anchor.d.ts +86 -0
- package/dist/lib/comment-anchor.d.ts.map +1 -0
- package/dist/lib/comment-anchor.js +139 -0
- package/dist/lib/comment-anchor.js.map +1 -0
- package/dist/lib/comments-client.d.ts +106 -0
- package/dist/lib/comments-client.d.ts.map +1 -0
- package/dist/lib/comments-client.js +138 -0
- package/dist/lib/comments-client.js.map +1 -0
- package/dist/lib/notifications-client.d.ts +68 -0
- package/dist/lib/notifications-client.d.ts.map +1 -0
- package/dist/lib/notifications-client.js +85 -0
- package/dist/lib/notifications-client.js.map +1 -0
- package/dist/lib/slash-command.d.ts +39 -0
- package/dist/lib/slash-command.d.ts.map +1 -0
- package/dist/lib/slash-command.js +50 -0
- package/dist/lib/slash-command.js.map +1 -0
- package/package.json +12 -3
- package/src/__tests__/components/comment-mark.test.ts +99 -0
- package/src/__tests__/components/comment-side-panel.test.tsx +343 -0
- package/src/__tests__/components/notification-bell.test.tsx +213 -0
- package/src/__tests__/components/offline-status.test.tsx +66 -0
- package/src/__tests__/components/presence-chips-helpers.test.ts +44 -0
- package/src/__tests__/lib/active-editor.test.ts +70 -0
- package/src/__tests__/lib/coauthor-client.test.ts +213 -0
- package/src/__tests__/lib/collaboration-provider.test.ts +425 -0
- package/src/__tests__/lib/comment-anchor.test.ts +188 -0
- package/src/__tests__/lib/comments-client.test.ts +239 -0
- package/src/__tests__/lib/notifications-client.test.ts +195 -0
- package/src/__tests__/lib/slash-command.test.ts +146 -0
- package/src/components/AICoauthorBubbleMenu.tsx +187 -0
- package/src/components/CoauthorResultPopover.tsx +193 -0
- package/src/components/CommandPalette.tsx +397 -209
- package/src/components/CommentMark.ts +138 -0
- package/src/components/CommentSidePanel.tsx +521 -0
- package/src/components/LivePreview.tsx +117 -11
- package/src/components/NotificationBell.tsx +242 -0
- package/src/components/OfflineStatus.tsx +90 -0
- package/src/components/PresenceChips.tsx +150 -0
- package/src/components/TipTapEditor.tsx +310 -12
- package/src/index.ts +20 -0
- package/src/lib/active-editor.ts +39 -0
- package/src/lib/coauthor-client.ts +160 -0
- package/src/lib/collaboration-provider.ts +306 -0
- package/src/lib/comment-anchor.ts +179 -0
- package/src/lib/comments-client.ts +208 -0
- package/src/lib/notifications-client.ts +146 -0
- package/src/lib/slash-command.ts +67 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notifications REST client — typed wrapper around the slice-6 endpoints
|
|
3
|
+
* (`/notifications`, `/notifications/unread-count`,
|
|
4
|
+
* `/notifications/:id/read`, `/notifications/read-all`).
|
|
5
|
+
*
|
|
6
|
+
* Lives in `cms-admin` so the bell, the toast layer, and any future
|
|
7
|
+
* background sync agent share one request path, error model, and DTO
|
|
8
|
+
* shape. The wire format mirrors `NotificationDTO` in
|
|
9
|
+
* `@actuate-media/cms-core/realtime/notifications.ts`; we duplicate the
|
|
10
|
+
* shape here to avoid taking a hard dependency on the server module
|
|
11
|
+
* (which carries Prisma types that don't belong in the admin bundle).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { cmsApi } from './api.js'
|
|
15
|
+
|
|
16
|
+
export type NotificationKind = 'comment_reply' | 'comment_resolved' | 'comment_mention'
|
|
17
|
+
|
|
18
|
+
export interface CommentReplyPayload {
|
|
19
|
+
kind: 'comment_reply'
|
|
20
|
+
actorId: string | null
|
|
21
|
+
actorName: string | null
|
|
22
|
+
excerpt: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface CommentResolvedPayload {
|
|
26
|
+
kind: 'comment_resolved'
|
|
27
|
+
actorId: string | null
|
|
28
|
+
actorName: string | null
|
|
29
|
+
rootExcerpt: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface CommentMentionPayload {
|
|
33
|
+
kind: 'comment_mention'
|
|
34
|
+
actorId: string | null
|
|
35
|
+
actorName: string | null
|
|
36
|
+
excerpt: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type NotificationPayload =
|
|
40
|
+
| CommentReplyPayload
|
|
41
|
+
| CommentResolvedPayload
|
|
42
|
+
| CommentMentionPayload
|
|
43
|
+
|
|
44
|
+
export interface Notification {
|
|
45
|
+
id: string
|
|
46
|
+
userId: string
|
|
47
|
+
kind: NotificationKind
|
|
48
|
+
documentId: string | null
|
|
49
|
+
commentId: string | null
|
|
50
|
+
payload: NotificationPayload
|
|
51
|
+
readAt: string | null
|
|
52
|
+
createdAt: string
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type NotificationOutcome<T> =
|
|
56
|
+
| { ok: true; result: T }
|
|
57
|
+
| { ok: false; error: string; status: number }
|
|
58
|
+
|
|
59
|
+
export interface ListNotificationsOptions {
|
|
60
|
+
/** Default: false. When true the response includes already-read rows. */
|
|
61
|
+
includeRead?: boolean
|
|
62
|
+
/** Default: server-side cap. Hard upper bound is 200. */
|
|
63
|
+
pageSize?: number
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function failure(error: string, status: number): { ok: false; error: string; status: number } {
|
|
67
|
+
return { ok: false, error, status }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function envelope<T>(res: {
|
|
71
|
+
data?: unknown
|
|
72
|
+
error?: string
|
|
73
|
+
status: number
|
|
74
|
+
}): NotificationOutcome<T> {
|
|
75
|
+
if (res.error || res.data === undefined) {
|
|
76
|
+
return failure(res.error ?? `Request failed (${res.status})`, res.status)
|
|
77
|
+
}
|
|
78
|
+
return { ok: true, result: res.data as T }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function buildQuery(opts: ListNotificationsOptions = {}): string {
|
|
82
|
+
const params = new URLSearchParams()
|
|
83
|
+
if (opts.includeRead) params.set('includeRead', 'true')
|
|
84
|
+
if (opts.pageSize !== undefined) params.set('pageSize', String(opts.pageSize))
|
|
85
|
+
const qs = params.toString()
|
|
86
|
+
return qs ? `?${qs}` : ''
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export async function listNotifications(
|
|
90
|
+
opts: ListNotificationsOptions = {},
|
|
91
|
+
): Promise<NotificationOutcome<Notification[]>> {
|
|
92
|
+
const res = await cmsApi<Notification[]>(`/notifications${buildQuery(opts)}`, {
|
|
93
|
+
method: 'GET',
|
|
94
|
+
})
|
|
95
|
+
return envelope<Notification[]>(res)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function unreadCount(): Promise<NotificationOutcome<number>> {
|
|
99
|
+
const res = await cmsApi<{ count: number }>(`/notifications/unread-count`, {
|
|
100
|
+
method: 'GET',
|
|
101
|
+
})
|
|
102
|
+
if (res.error || !res.data) {
|
|
103
|
+
return failure(res.error ?? `Request failed (${res.status})`, res.status)
|
|
104
|
+
}
|
|
105
|
+
return { ok: true, result: res.data.count }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export async function markNotificationRead(id: string): Promise<NotificationOutcome<Notification>> {
|
|
109
|
+
if (!id) return failure('notification id is required', 0)
|
|
110
|
+
const res = await cmsApi<Notification>(`/notifications/${encodeURIComponent(id)}/read`, {
|
|
111
|
+
method: 'POST',
|
|
112
|
+
body: '{}',
|
|
113
|
+
})
|
|
114
|
+
return envelope<Notification>(res)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export async function markAllNotificationsRead(): Promise<NotificationOutcome<number>> {
|
|
118
|
+
const res = await cmsApi<{ count: number }>(`/notifications/read-all`, {
|
|
119
|
+
method: 'POST',
|
|
120
|
+
body: '{}',
|
|
121
|
+
})
|
|
122
|
+
if (res.error || !res.data) {
|
|
123
|
+
return failure(res.error ?? `Request failed (${res.status})`, res.status)
|
|
124
|
+
}
|
|
125
|
+
return { ok: true, result: res.data.count }
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Cheap single-line summary for the bell list — the server gives us a
|
|
130
|
+
* structured payload, but the dropdown wants one human-readable string
|
|
131
|
+
* per row. Falls back to `Notification` when an unknown kind sneaks in
|
|
132
|
+
* (defensive against future server-side additions).
|
|
133
|
+
*/
|
|
134
|
+
export function notificationSummary(notification: Notification): string {
|
|
135
|
+
const actor = notification.payload.actorName ?? 'Someone'
|
|
136
|
+
switch (notification.payload.kind) {
|
|
137
|
+
case 'comment_reply':
|
|
138
|
+
return `${actor} replied: "${notification.payload.excerpt}"`
|
|
139
|
+
case 'comment_resolved':
|
|
140
|
+
return `${actor} resolved your thread: "${notification.payload.rootExcerpt}"`
|
|
141
|
+
case 'comment_mention':
|
|
142
|
+
return `${actor} mentioned you: "${notification.payload.excerpt}"`
|
|
143
|
+
default:
|
|
144
|
+
return 'Notification'
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { COAUTHOR_ACTIONS, isCoauthorAction, type CoauthorAction } from './coauthor-client.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cmd+K slash-command parsing for the AI co-author.
|
|
5
|
+
*
|
|
6
|
+
* Recognized syntax (case-insensitive, leading whitespace tolerated):
|
|
7
|
+
*
|
|
8
|
+
* /ai <action> [text...]
|
|
9
|
+
*
|
|
10
|
+
* `<action>` is one of `COAUTHOR_ACTIONS`. `text` is the payload handed to
|
|
11
|
+
* `/ai/coauthor`; it may be empty while the user is still typing.
|
|
12
|
+
*
|
|
13
|
+
* The parser is pure so we can unit-test every edge case (case folding,
|
|
14
|
+
* trailing whitespace, partial input, unknown actions) without spinning up
|
|
15
|
+
* `cmdk` or the rest of the palette.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export interface AiSlashCommand {
|
|
19
|
+
action: CoauthorAction
|
|
20
|
+
text: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const SLASH_HEAD = /^\s*\/\s*ai(?:\s+|$)/i
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Returns the matched `{ action, text }` when `input` is a complete
|
|
27
|
+
* slash command (`/ai rewrite ...`), or `null` otherwise. `text` may be
|
|
28
|
+
* empty when the user has typed `/ai rewrite` with no payload yet.
|
|
29
|
+
*/
|
|
30
|
+
export function parseAiSlashCommand(input: string): AiSlashCommand | null {
|
|
31
|
+
if (typeof input !== 'string') return null
|
|
32
|
+
if (!SLASH_HEAD.test(input)) return null
|
|
33
|
+
const after = input.replace(SLASH_HEAD, '')
|
|
34
|
+
if (after.length === 0) return null
|
|
35
|
+
|
|
36
|
+
const firstWhitespace = after.search(/\s/)
|
|
37
|
+
const head = (firstWhitespace === -1 ? after : after.slice(0, firstWhitespace)).toLowerCase()
|
|
38
|
+
if (!isCoauthorAction(head)) return null
|
|
39
|
+
|
|
40
|
+
const tail = firstWhitespace === -1 ? '' : after.slice(firstWhitespace + 1)
|
|
41
|
+
return { action: head, text: tail.trim() }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Returns true when `input` looks like the start of an AI slash command
|
|
46
|
+
* — even when the action isn't typed yet (`/`, `/a`, `/ai`, `/ai re`).
|
|
47
|
+
* The palette uses this to switch into "AI mode" (hide nav results,
|
|
48
|
+
* show the four action suggestions).
|
|
49
|
+
*/
|
|
50
|
+
export function isAiSlashCommandInput(input: string): boolean {
|
|
51
|
+
if (typeof input !== 'string') return false
|
|
52
|
+
const trimmed = input.trim()
|
|
53
|
+
if (trimmed.length === 0) return false
|
|
54
|
+
if (trimmed === '/') return true
|
|
55
|
+
return /^\/\s*a(i\b|i?[a-z]*|)?(\s.*)?$/i.test(trimmed)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Ordered list of actions to show in AI mode. When the user has typed a
|
|
60
|
+
* valid command, that command's action is hoisted to the top so it gets
|
|
61
|
+
* the default keyboard focus (and Enter runs it).
|
|
62
|
+
*/
|
|
63
|
+
export function orderedAiActions(input: string): CoauthorAction[] {
|
|
64
|
+
const parsed = parseAiSlashCommand(input)
|
|
65
|
+
if (!parsed) return [...COAUTHOR_ACTIONS]
|
|
66
|
+
return [parsed.action, ...COAUTHOR_ACTIONS.filter((a) => a !== parsed.action)]
|
|
67
|
+
}
|