@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.
Files changed (152) hide show
  1. package/dist/__tests__/components/comment-mark.test.d.ts +2 -0
  2. package/dist/__tests__/components/comment-mark.test.d.ts.map +1 -0
  3. package/dist/__tests__/components/comment-mark.test.js +89 -0
  4. package/dist/__tests__/components/comment-mark.test.js.map +1 -0
  5. package/dist/__tests__/components/comment-side-panel.test.d.ts +2 -0
  6. package/dist/__tests__/components/comment-side-panel.test.d.ts.map +1 -0
  7. package/dist/__tests__/components/comment-side-panel.test.js +291 -0
  8. package/dist/__tests__/components/comment-side-panel.test.js.map +1 -0
  9. package/dist/__tests__/components/notification-bell.test.d.ts +2 -0
  10. package/dist/__tests__/components/notification-bell.test.d.ts.map +1 -0
  11. package/dist/__tests__/components/notification-bell.test.js +200 -0
  12. package/dist/__tests__/components/notification-bell.test.js.map +1 -0
  13. package/dist/__tests__/components/offline-status.test.d.ts +2 -0
  14. package/dist/__tests__/components/offline-status.test.d.ts.map +1 -0
  15. package/dist/__tests__/components/offline-status.test.js +56 -0
  16. package/dist/__tests__/components/offline-status.test.js.map +1 -0
  17. package/dist/__tests__/components/presence-chips-helpers.test.d.ts +2 -0
  18. package/dist/__tests__/components/presence-chips-helpers.test.d.ts.map +1 -0
  19. package/dist/__tests__/components/presence-chips-helpers.test.js +35 -0
  20. package/dist/__tests__/components/presence-chips-helpers.test.js.map +1 -0
  21. package/dist/__tests__/lib/active-editor.test.d.ts +2 -0
  22. package/dist/__tests__/lib/active-editor.test.d.ts.map +1 -0
  23. package/dist/__tests__/lib/active-editor.test.js +57 -0
  24. package/dist/__tests__/lib/active-editor.test.js.map +1 -0
  25. package/dist/__tests__/lib/coauthor-client.test.d.ts +2 -0
  26. package/dist/__tests__/lib/coauthor-client.test.d.ts.map +1 -0
  27. package/dist/__tests__/lib/coauthor-client.test.js +173 -0
  28. package/dist/__tests__/lib/coauthor-client.test.js.map +1 -0
  29. package/dist/__tests__/lib/collaboration-provider.test.d.ts +2 -0
  30. package/dist/__tests__/lib/collaboration-provider.test.d.ts.map +1 -0
  31. package/dist/__tests__/lib/collaboration-provider.test.js +335 -0
  32. package/dist/__tests__/lib/collaboration-provider.test.js.map +1 -0
  33. package/dist/__tests__/lib/comment-anchor.test.d.ts +2 -0
  34. package/dist/__tests__/lib/comment-anchor.test.d.ts.map +1 -0
  35. package/dist/__tests__/lib/comment-anchor.test.js +173 -0
  36. package/dist/__tests__/lib/comment-anchor.test.js.map +1 -0
  37. package/dist/__tests__/lib/comments-client.test.d.ts +2 -0
  38. package/dist/__tests__/lib/comments-client.test.d.ts.map +1 -0
  39. package/dist/__tests__/lib/comments-client.test.js +202 -0
  40. package/dist/__tests__/lib/comments-client.test.js.map +1 -0
  41. package/dist/__tests__/lib/notifications-client.test.d.ts +2 -0
  42. package/dist/__tests__/lib/notifications-client.test.d.ts.map +1 -0
  43. package/dist/__tests__/lib/notifications-client.test.js +155 -0
  44. package/dist/__tests__/lib/notifications-client.test.js.map +1 -0
  45. package/dist/__tests__/lib/slash-command.test.d.ts +2 -0
  46. package/dist/__tests__/lib/slash-command.test.d.ts.map +1 -0
  47. package/dist/__tests__/lib/slash-command.test.js +123 -0
  48. package/dist/__tests__/lib/slash-command.test.js.map +1 -0
  49. package/dist/actuate-admin.css +1 -1
  50. package/dist/components/AICoauthorBubbleMenu.d.ts +18 -0
  51. package/dist/components/AICoauthorBubbleMenu.d.ts.map +1 -0
  52. package/dist/components/AICoauthorBubbleMenu.js +113 -0
  53. package/dist/components/AICoauthorBubbleMenu.js.map +1 -0
  54. package/dist/components/CoauthorResultPopover.d.ts +28 -0
  55. package/dist/components/CoauthorResultPopover.d.ts.map +1 -0
  56. package/dist/components/CoauthorResultPopover.js +42 -0
  57. package/dist/components/CoauthorResultPopover.js.map +1 -0
  58. package/dist/components/CommandPalette.d.ts +1 -1
  59. package/dist/components/CommandPalette.d.ts.map +1 -1
  60. package/dist/components/CommandPalette.js +138 -40
  61. package/dist/components/CommandPalette.js.map +1 -1
  62. package/dist/components/CommentMark.d.ts +54 -0
  63. package/dist/components/CommentMark.d.ts.map +1 -0
  64. package/dist/components/CommentMark.js +104 -0
  65. package/dist/components/CommentMark.js.map +1 -0
  66. package/dist/components/CommentSidePanel.d.ts +86 -0
  67. package/dist/components/CommentSidePanel.d.ts.map +1 -0
  68. package/dist/components/CommentSidePanel.js +165 -0
  69. package/dist/components/CommentSidePanel.js.map +1 -0
  70. package/dist/components/LivePreview.d.ts.map +1 -1
  71. package/dist/components/LivePreview.js +80 -16
  72. package/dist/components/LivePreview.js.map +1 -1
  73. package/dist/components/NotificationBell.d.ts +45 -0
  74. package/dist/components/NotificationBell.d.ts.map +1 -0
  75. package/dist/components/NotificationBell.js +90 -0
  76. package/dist/components/NotificationBell.js.map +1 -0
  77. package/dist/components/OfflineStatus.d.ts +39 -0
  78. package/dist/components/OfflineStatus.d.ts.map +1 -0
  79. package/dist/components/OfflineStatus.js +47 -0
  80. package/dist/components/OfflineStatus.js.map +1 -0
  81. package/dist/components/PresenceChips.d.ts +31 -0
  82. package/dist/components/PresenceChips.d.ts.map +1 -0
  83. package/dist/components/PresenceChips.js +58 -0
  84. package/dist/components/PresenceChips.js.map +1 -0
  85. package/dist/components/TipTapEditor.d.ts +61 -1
  86. package/dist/components/TipTapEditor.d.ts.map +1 -1
  87. package/dist/components/TipTapEditor.js +211 -8
  88. package/dist/components/TipTapEditor.js.map +1 -1
  89. package/dist/components/ui/Badge.d.ts +1 -1
  90. package/dist/index.d.ts +8 -0
  91. package/dist/index.d.ts.map +1 -1
  92. package/dist/index.js +4 -0
  93. package/dist/index.js.map +1 -1
  94. package/dist/lib/active-editor.d.ts +6 -0
  95. package/dist/lib/active-editor.d.ts.map +1 -0
  96. package/dist/lib/active-editor.js +35 -0
  97. package/dist/lib/active-editor.js.map +1 -0
  98. package/dist/lib/coauthor-client.d.ts +72 -0
  99. package/dist/lib/coauthor-client.d.ts.map +1 -0
  100. package/dist/lib/coauthor-client.js +98 -0
  101. package/dist/lib/coauthor-client.js.map +1 -0
  102. package/dist/lib/collaboration-provider.d.ts +142 -0
  103. package/dist/lib/collaboration-provider.d.ts.map +1 -0
  104. package/dist/lib/collaboration-provider.js +168 -0
  105. package/dist/lib/collaboration-provider.js.map +1 -0
  106. package/dist/lib/comment-anchor.d.ts +86 -0
  107. package/dist/lib/comment-anchor.d.ts.map +1 -0
  108. package/dist/lib/comment-anchor.js +139 -0
  109. package/dist/lib/comment-anchor.js.map +1 -0
  110. package/dist/lib/comments-client.d.ts +106 -0
  111. package/dist/lib/comments-client.d.ts.map +1 -0
  112. package/dist/lib/comments-client.js +138 -0
  113. package/dist/lib/comments-client.js.map +1 -0
  114. package/dist/lib/notifications-client.d.ts +68 -0
  115. package/dist/lib/notifications-client.d.ts.map +1 -0
  116. package/dist/lib/notifications-client.js +85 -0
  117. package/dist/lib/notifications-client.js.map +1 -0
  118. package/dist/lib/slash-command.d.ts +39 -0
  119. package/dist/lib/slash-command.d.ts.map +1 -0
  120. package/dist/lib/slash-command.js +50 -0
  121. package/dist/lib/slash-command.js.map +1 -0
  122. package/package.json +12 -3
  123. package/src/__tests__/components/comment-mark.test.ts +99 -0
  124. package/src/__tests__/components/comment-side-panel.test.tsx +343 -0
  125. package/src/__tests__/components/notification-bell.test.tsx +213 -0
  126. package/src/__tests__/components/offline-status.test.tsx +66 -0
  127. package/src/__tests__/components/presence-chips-helpers.test.ts +44 -0
  128. package/src/__tests__/lib/active-editor.test.ts +70 -0
  129. package/src/__tests__/lib/coauthor-client.test.ts +213 -0
  130. package/src/__tests__/lib/collaboration-provider.test.ts +425 -0
  131. package/src/__tests__/lib/comment-anchor.test.ts +188 -0
  132. package/src/__tests__/lib/comments-client.test.ts +239 -0
  133. package/src/__tests__/lib/notifications-client.test.ts +195 -0
  134. package/src/__tests__/lib/slash-command.test.ts +146 -0
  135. package/src/components/AICoauthorBubbleMenu.tsx +187 -0
  136. package/src/components/CoauthorResultPopover.tsx +193 -0
  137. package/src/components/CommandPalette.tsx +397 -209
  138. package/src/components/CommentMark.ts +138 -0
  139. package/src/components/CommentSidePanel.tsx +521 -0
  140. package/src/components/LivePreview.tsx +117 -11
  141. package/src/components/NotificationBell.tsx +242 -0
  142. package/src/components/OfflineStatus.tsx +90 -0
  143. package/src/components/PresenceChips.tsx +150 -0
  144. package/src/components/TipTapEditor.tsx +310 -12
  145. package/src/index.ts +20 -0
  146. package/src/lib/active-editor.ts +39 -0
  147. package/src/lib/coauthor-client.ts +160 -0
  148. package/src/lib/collaboration-provider.ts +306 -0
  149. package/src/lib/comment-anchor.ts +179 -0
  150. package/src/lib/comments-client.ts +208 -0
  151. package/src/lib/notifications-client.ts +146 -0
  152. 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
+ }