@actuate-media/cms-admin 0.11.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/LICENSE +21 -21
- 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__/fields/component-block-helpers.test.d.ts +7 -0
- package/dist/__tests__/fields/component-block-helpers.test.d.ts.map +1 -0
- package/dist/__tests__/fields/component-block-helpers.test.js +592 -0
- package/dist/__tests__/fields/component-block-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/fields/ComponentBlockField.d.ts +25 -0
- package/dist/fields/ComponentBlockField.d.ts.map +1 -0
- package/dist/fields/ComponentBlockField.js +74 -0
- package/dist/fields/ComponentBlockField.js.map +1 -0
- package/dist/fields/FieldRenderer.d.ts +3 -0
- package/dist/fields/FieldRenderer.d.ts.map +1 -1
- package/dist/fields/FieldRenderer.js +3 -1
- package/dist/fields/FieldRenderer.js.map +1 -1
- package/dist/fields/PropInput.d.ts +14 -0
- package/dist/fields/PropInput.d.ts.map +1 -0
- package/dist/fields/PropInput.js +163 -0
- package/dist/fields/PropInput.js.map +1 -0
- package/dist/fields/component-block-helpers.d.ts +96 -0
- package/dist/fields/component-block-helpers.d.ts.map +1 -0
- package/dist/fields/component-block-helpers.js +323 -0
- package/dist/fields/component-block-helpers.js.map +1 -0
- package/dist/fields/index.d.ts +4 -0
- package/dist/fields/index.d.ts.map +1 -1
- package/dist/fields/index.js +2 -0
- package/dist/fields/index.js.map +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -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 +20 -4
- 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__/fields/component-block-helpers.test.ts +674 -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/fields/ComponentBlockField.tsx +179 -0
- package/src/fields/FieldRenderer.tsx +8 -0
- package/src/fields/PropInput.tsx +552 -0
- package/src/fields/component-block-helpers.ts +341 -0
- package/src/fields/index.ts +4 -0
- package/src/index.ts +27 -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,85 @@
|
|
|
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
|
+
import { cmsApi } from './api.js';
|
|
14
|
+
function failure(error, status) {
|
|
15
|
+
return { ok: false, error, status };
|
|
16
|
+
}
|
|
17
|
+
function envelope(res) {
|
|
18
|
+
if (res.error || res.data === undefined) {
|
|
19
|
+
return failure(res.error ?? `Request failed (${res.status})`, res.status);
|
|
20
|
+
}
|
|
21
|
+
return { ok: true, result: res.data };
|
|
22
|
+
}
|
|
23
|
+
function buildQuery(opts = {}) {
|
|
24
|
+
const params = new URLSearchParams();
|
|
25
|
+
if (opts.includeRead)
|
|
26
|
+
params.set('includeRead', 'true');
|
|
27
|
+
if (opts.pageSize !== undefined)
|
|
28
|
+
params.set('pageSize', String(opts.pageSize));
|
|
29
|
+
const qs = params.toString();
|
|
30
|
+
return qs ? `?${qs}` : '';
|
|
31
|
+
}
|
|
32
|
+
export async function listNotifications(opts = {}) {
|
|
33
|
+
const res = await cmsApi(`/notifications${buildQuery(opts)}`, {
|
|
34
|
+
method: 'GET',
|
|
35
|
+
});
|
|
36
|
+
return envelope(res);
|
|
37
|
+
}
|
|
38
|
+
export async function unreadCount() {
|
|
39
|
+
const res = await cmsApi(`/notifications/unread-count`, {
|
|
40
|
+
method: 'GET',
|
|
41
|
+
});
|
|
42
|
+
if (res.error || !res.data) {
|
|
43
|
+
return failure(res.error ?? `Request failed (${res.status})`, res.status);
|
|
44
|
+
}
|
|
45
|
+
return { ok: true, result: res.data.count };
|
|
46
|
+
}
|
|
47
|
+
export async function markNotificationRead(id) {
|
|
48
|
+
if (!id)
|
|
49
|
+
return failure('notification id is required', 0);
|
|
50
|
+
const res = await cmsApi(`/notifications/${encodeURIComponent(id)}/read`, {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
body: '{}',
|
|
53
|
+
});
|
|
54
|
+
return envelope(res);
|
|
55
|
+
}
|
|
56
|
+
export async function markAllNotificationsRead() {
|
|
57
|
+
const res = await cmsApi(`/notifications/read-all`, {
|
|
58
|
+
method: 'POST',
|
|
59
|
+
body: '{}',
|
|
60
|
+
});
|
|
61
|
+
if (res.error || !res.data) {
|
|
62
|
+
return failure(res.error ?? `Request failed (${res.status})`, res.status);
|
|
63
|
+
}
|
|
64
|
+
return { ok: true, result: res.data.count };
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Cheap single-line summary for the bell list — the server gives us a
|
|
68
|
+
* structured payload, but the dropdown wants one human-readable string
|
|
69
|
+
* per row. Falls back to `Notification` when an unknown kind sneaks in
|
|
70
|
+
* (defensive against future server-side additions).
|
|
71
|
+
*/
|
|
72
|
+
export function notificationSummary(notification) {
|
|
73
|
+
const actor = notification.payload.actorName ?? 'Someone';
|
|
74
|
+
switch (notification.payload.kind) {
|
|
75
|
+
case 'comment_reply':
|
|
76
|
+
return `${actor} replied: "${notification.payload.excerpt}"`;
|
|
77
|
+
case 'comment_resolved':
|
|
78
|
+
return `${actor} resolved your thread: "${notification.payload.rootExcerpt}"`;
|
|
79
|
+
case 'comment_mention':
|
|
80
|
+
return `${actor} mentioned you: "${notification.payload.excerpt}"`;
|
|
81
|
+
default:
|
|
82
|
+
return 'Notification';
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=notifications-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notifications-client.js","sourceRoot":"","sources":["../../src/lib/notifications-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAoDjC,SAAS,OAAO,CAAC,KAAa,EAAE,MAAc;IAC5C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;AACrC,CAAC;AAED,SAAS,QAAQ,CAAI,GAIpB;IACC,IAAI,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QACxC,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,mBAAmB,GAAG,CAAC,MAAM,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAC3E,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,IAAS,EAAE,CAAA;AAC5C,CAAC;AAED,SAAS,UAAU,CAAC,OAAiC,EAAE;IACrD,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAA;IACpC,IAAI,IAAI,CAAC,WAAW;QAAE,MAAM,CAAC,GAAG,CAAC,aAAa,EAAE,MAAM,CAAC,CAAA;IACvD,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS;QAAE,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC9E,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAA;IAC5B,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;AAC3B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAiC,EAAE;IAEnC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAiB,iBAAiB,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE;QAC5E,MAAM,EAAE,KAAK;KACd,CAAC,CAAA;IACF,OAAO,QAAQ,CAAiB,GAAG,CAAC,CAAA;AACtC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,GAAG,GAAG,MAAM,MAAM,CAAoB,6BAA6B,EAAE;QACzE,MAAM,EAAE,KAAK;KACd,CAAC,CAAA;IACF,IAAI,GAAG,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3B,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,mBAAmB,GAAG,CAAC,MAAM,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAC3E,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,CAAA;AAC7C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,EAAU;IACnD,IAAI,CAAC,EAAE;QAAE,OAAO,OAAO,CAAC,6BAA6B,EAAE,CAAC,CAAC,CAAA;IACzD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAe,kBAAkB,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE;QACtF,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,IAAI;KACX,CAAC,CAAA;IACF,OAAO,QAAQ,CAAe,GAAG,CAAC,CAAA;AACpC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB;IAC5C,MAAM,GAAG,GAAG,MAAM,MAAM,CAAoB,yBAAyB,EAAE;QACrE,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,IAAI;KACX,CAAC,CAAA;IACF,IAAI,GAAG,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3B,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,mBAAmB,GAAG,CAAC,MAAM,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAC3E,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,CAAA;AAC7C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,YAA0B;IAC5D,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,SAAS,IAAI,SAAS,CAAA;IACzD,QAAQ,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAClC,KAAK,eAAe;YAClB,OAAO,GAAG,KAAK,cAAc,YAAY,CAAC,OAAO,CAAC,OAAO,GAAG,CAAA;QAC9D,KAAK,kBAAkB;YACrB,OAAO,GAAG,KAAK,2BAA2B,YAAY,CAAC,OAAO,CAAC,WAAW,GAAG,CAAA;QAC/E,KAAK,iBAAiB;YACpB,OAAO,GAAG,KAAK,oBAAoB,YAAY,CAAC,OAAO,CAAC,OAAO,GAAG,CAAA;QACpE;YACE,OAAO,cAAc,CAAA;IACzB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type CoauthorAction } from './coauthor-client.js';
|
|
2
|
+
/**
|
|
3
|
+
* Cmd+K slash-command parsing for the AI co-author.
|
|
4
|
+
*
|
|
5
|
+
* Recognized syntax (case-insensitive, leading whitespace tolerated):
|
|
6
|
+
*
|
|
7
|
+
* /ai <action> [text...]
|
|
8
|
+
*
|
|
9
|
+
* `<action>` is one of `COAUTHOR_ACTIONS`. `text` is the payload handed to
|
|
10
|
+
* `/ai/coauthor`; it may be empty while the user is still typing.
|
|
11
|
+
*
|
|
12
|
+
* The parser is pure so we can unit-test every edge case (case folding,
|
|
13
|
+
* trailing whitespace, partial input, unknown actions) without spinning up
|
|
14
|
+
* `cmdk` or the rest of the palette.
|
|
15
|
+
*/
|
|
16
|
+
export interface AiSlashCommand {
|
|
17
|
+
action: CoauthorAction;
|
|
18
|
+
text: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Returns the matched `{ action, text }` when `input` is a complete
|
|
22
|
+
* slash command (`/ai rewrite ...`), or `null` otherwise. `text` may be
|
|
23
|
+
* empty when the user has typed `/ai rewrite` with no payload yet.
|
|
24
|
+
*/
|
|
25
|
+
export declare function parseAiSlashCommand(input: string): AiSlashCommand | null;
|
|
26
|
+
/**
|
|
27
|
+
* Returns true when `input` looks like the start of an AI slash command
|
|
28
|
+
* — even when the action isn't typed yet (`/`, `/a`, `/ai`, `/ai re`).
|
|
29
|
+
* The palette uses this to switch into "AI mode" (hide nav results,
|
|
30
|
+
* show the four action suggestions).
|
|
31
|
+
*/
|
|
32
|
+
export declare function isAiSlashCommandInput(input: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Ordered list of actions to show in AI mode. When the user has typed a
|
|
35
|
+
* valid command, that command's action is hoisted to the top so it gets
|
|
36
|
+
* the default keyboard focus (and Enter runs it).
|
|
37
|
+
*/
|
|
38
|
+
export declare function orderedAiActions(input: string): CoauthorAction[];
|
|
39
|
+
//# sourceMappingURL=slash-command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slash-command.d.ts","sourceRoot":"","sources":["../../src/lib/slash-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAE9F;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,cAAc,CAAA;IACtB,IAAI,EAAE,MAAM,CAAA;CACb;AAID;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAYxE;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAM5D;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,EAAE,CAIhE"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { COAUTHOR_ACTIONS, isCoauthorAction } from './coauthor-client.js';
|
|
2
|
+
const SLASH_HEAD = /^\s*\/\s*ai(?:\s+|$)/i;
|
|
3
|
+
/**
|
|
4
|
+
* Returns the matched `{ action, text }` when `input` is a complete
|
|
5
|
+
* slash command (`/ai rewrite ...`), or `null` otherwise. `text` may be
|
|
6
|
+
* empty when the user has typed `/ai rewrite` with no payload yet.
|
|
7
|
+
*/
|
|
8
|
+
export function parseAiSlashCommand(input) {
|
|
9
|
+
if (typeof input !== 'string')
|
|
10
|
+
return null;
|
|
11
|
+
if (!SLASH_HEAD.test(input))
|
|
12
|
+
return null;
|
|
13
|
+
const after = input.replace(SLASH_HEAD, '');
|
|
14
|
+
if (after.length === 0)
|
|
15
|
+
return null;
|
|
16
|
+
const firstWhitespace = after.search(/\s/);
|
|
17
|
+
const head = (firstWhitespace === -1 ? after : after.slice(0, firstWhitespace)).toLowerCase();
|
|
18
|
+
if (!isCoauthorAction(head))
|
|
19
|
+
return null;
|
|
20
|
+
const tail = firstWhitespace === -1 ? '' : after.slice(firstWhitespace + 1);
|
|
21
|
+
return { action: head, text: tail.trim() };
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Returns true when `input` looks like the start of an AI slash command
|
|
25
|
+
* — even when the action isn't typed yet (`/`, `/a`, `/ai`, `/ai re`).
|
|
26
|
+
* The palette uses this to switch into "AI mode" (hide nav results,
|
|
27
|
+
* show the four action suggestions).
|
|
28
|
+
*/
|
|
29
|
+
export function isAiSlashCommandInput(input) {
|
|
30
|
+
if (typeof input !== 'string')
|
|
31
|
+
return false;
|
|
32
|
+
const trimmed = input.trim();
|
|
33
|
+
if (trimmed.length === 0)
|
|
34
|
+
return false;
|
|
35
|
+
if (trimmed === '/')
|
|
36
|
+
return true;
|
|
37
|
+
return /^\/\s*a(i\b|i?[a-z]*|)?(\s.*)?$/i.test(trimmed);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Ordered list of actions to show in AI mode. When the user has typed a
|
|
41
|
+
* valid command, that command's action is hoisted to the top so it gets
|
|
42
|
+
* the default keyboard focus (and Enter runs it).
|
|
43
|
+
*/
|
|
44
|
+
export function orderedAiActions(input) {
|
|
45
|
+
const parsed = parseAiSlashCommand(input);
|
|
46
|
+
if (!parsed)
|
|
47
|
+
return [...COAUTHOR_ACTIONS];
|
|
48
|
+
return [parsed.action, ...COAUTHOR_ACTIONS.filter((a) => a !== parsed.action)];
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=slash-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slash-command.js","sourceRoot":"","sources":["../../src/lib/slash-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAuB,MAAM,sBAAsB,CAAA;AAsB9F,MAAM,UAAU,GAAG,uBAAuB,CAAA;AAE1C;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAC1C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACxC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;IAC3C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAEnC,MAAM,eAAe,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAC1C,MAAM,IAAI,GAAG,CAAC,eAAe,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,WAAW,EAAE,CAAA;IAC7F,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAExC,MAAM,IAAI,GAAG,eAAe,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,eAAe,GAAG,CAAC,CAAC,CAAA;IAC3E,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAA;AAC5C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAa;IACjD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;IAC5B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IACtC,IAAI,OAAO,KAAK,GAAG;QAAE,OAAO,IAAI,CAAA;IAChC,OAAO,kCAAkC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;AACzD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,MAAM,MAAM,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAA;IACzC,IAAI,CAAC,MAAM;QAAE,OAAO,CAAC,GAAG,gBAAgB,CAAC,CAAA;IACzC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;AAChF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@actuate-media/cms-admin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/actuate-media/actuatecms.git",
|
|
@@ -38,7 +38,10 @@
|
|
|
38
38
|
"@radix-ui/react-select": "^2.2.6",
|
|
39
39
|
"@radix-ui/react-switch": "^1.1.0",
|
|
40
40
|
"@radix-ui/react-tabs": "^1.1.0",
|
|
41
|
+
"@tiptap/core": "^2.11.0",
|
|
41
42
|
"@tiptap/extension-code-block-lowlight": "^2.11.0",
|
|
43
|
+
"@tiptap/extension-collaboration": "^2.11.0",
|
|
44
|
+
"@tiptap/extension-collaboration-cursor": "^2.11.0",
|
|
42
45
|
"@tiptap/extension-horizontal-rule": "^2.11.0",
|
|
43
46
|
"@tiptap/extension-image": "^2.11.0",
|
|
44
47
|
"@tiptap/extension-link": "^2.11.0",
|
|
@@ -59,20 +62,33 @@
|
|
|
59
62
|
"react-dom": "^19.2.0",
|
|
60
63
|
"recharts": "^3.8.1",
|
|
61
64
|
"sonner": "^2.0.7",
|
|
62
|
-
"tailwind-merge": "^3.5.0"
|
|
65
|
+
"tailwind-merge": "^3.5.0",
|
|
66
|
+
"y-indexeddb": "^9.0.12",
|
|
67
|
+
"y-protocols": "^1.0.6",
|
|
68
|
+
"y-websocket": "^2.0.4",
|
|
69
|
+
"yjs": "^13.6.0"
|
|
63
70
|
},
|
|
64
71
|
"peerDependencies": {
|
|
65
|
-
"@actuate-media/cms-core": ">=0.1.0"
|
|
72
|
+
"@actuate-media/cms-core": ">=0.1.0",
|
|
73
|
+
"@actuate-media/component-blocks": ">=0.1.0"
|
|
74
|
+
},
|
|
75
|
+
"peerDependenciesMeta": {
|
|
76
|
+
"@actuate-media/component-blocks": {
|
|
77
|
+
"optional": true
|
|
78
|
+
}
|
|
66
79
|
},
|
|
67
80
|
"devDependencies": {
|
|
68
81
|
"@tailwindcss/cli": "^4.0.0",
|
|
82
|
+
"@testing-library/react": "^16.1.0",
|
|
69
83
|
"@types/react": "^19.0.0",
|
|
70
84
|
"@types/react-dom": "^19.0.0",
|
|
71
85
|
"concurrently": "^9.2.1",
|
|
86
|
+
"happy-dom": "^20.9.0",
|
|
72
87
|
"tailwindcss": "^4.0.0",
|
|
73
88
|
"typescript": "^5.7.0",
|
|
74
89
|
"vitest": "^3.0.0",
|
|
75
|
-
"@actuate-media/cms-core": "0.
|
|
90
|
+
"@actuate-media/cms-core": "0.22.0",
|
|
91
|
+
"@actuate-media/component-blocks": "0.2.0"
|
|
76
92
|
},
|
|
77
93
|
"scripts": {
|
|
78
94
|
"build": "tsc --project tsconfig.json && npx @tailwindcss/cli -i src/styles/build-input.css -o dist/actuate-admin.css --minify",
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
import { Editor } from '@tiptap/core'
|
|
4
|
+
import StarterKit from '@tiptap/starter-kit'
|
|
5
|
+
|
|
6
|
+
import { CommentMark } from '../../components/CommentMark.js'
|
|
7
|
+
|
|
8
|
+
function newEditor(initialHtml = '<p>hello world</p>') {
|
|
9
|
+
return new Editor({
|
|
10
|
+
extensions: [StarterKit.configure({ history: false }), CommentMark],
|
|
11
|
+
content: initialHtml,
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe('CommentMark', () => {
|
|
16
|
+
it('registers under the name "comment"', () => {
|
|
17
|
+
const ed = newEditor()
|
|
18
|
+
expect(ed.extensionManager.extensions.some((e) => e.name === 'comment')).toBe(true)
|
|
19
|
+
ed.destroy()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('setComment applies the mark across the current selection', () => {
|
|
23
|
+
const ed = newEditor('<p>hello world</p>')
|
|
24
|
+
ed.commands.setTextSelection({ from: 1, to: 6 }) // "hello"
|
|
25
|
+
const ok = ed.chain().setComment({ commentId: 'c-1' }).run()
|
|
26
|
+
expect(ok).toBe(true)
|
|
27
|
+
expect(ed.isActive('comment', { commentId: 'c-1' })).toBe(true)
|
|
28
|
+
// The rendered HTML should now contain the data attribute.
|
|
29
|
+
const html = ed.getHTML()
|
|
30
|
+
expect(html).toContain('data-comment-id="c-1"')
|
|
31
|
+
expect(html).toContain('hello')
|
|
32
|
+
ed.destroy()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('setComment rejects a missing commentId', () => {
|
|
36
|
+
const ed = newEditor()
|
|
37
|
+
ed.commands.setTextSelection({ from: 1, to: 6 })
|
|
38
|
+
const ok = ed.chain().setComment({ commentId: '' }).run()
|
|
39
|
+
expect(ok).toBe(false)
|
|
40
|
+
expect(ed.getHTML()).not.toContain('data-comment-id')
|
|
41
|
+
ed.destroy()
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('updateComment swaps the commentId across the mark range', () => {
|
|
45
|
+
const ed = newEditor('<p>hello world</p>')
|
|
46
|
+
ed.commands.setTextSelection({ from: 1, to: 6 })
|
|
47
|
+
ed.chain().setComment({ commentId: 'c-1' }).run()
|
|
48
|
+
// Re-select the same range so updateAttributes can find the mark to
|
|
49
|
+
// mutate. TipTap's updateAttributes applies to the marks that fall
|
|
50
|
+
// within the selection — a collapsed cursor at the boundary would
|
|
51
|
+
// not match this mark.
|
|
52
|
+
ed.commands.setTextSelection({ from: 1, to: 6 })
|
|
53
|
+
const ok = ed.chain().updateComment({ commentId: 'c-2' }).run()
|
|
54
|
+
expect(ok).toBe(true)
|
|
55
|
+
expect(ed.getHTML()).toContain('data-comment-id="c-2"')
|
|
56
|
+
ed.destroy()
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('unsetComment removes the mark from the selection', () => {
|
|
60
|
+
const ed = newEditor('<p>hello world</p>')
|
|
61
|
+
ed.commands.setTextSelection({ from: 1, to: 6 })
|
|
62
|
+
ed.chain().setComment({ commentId: 'c-1' }).run()
|
|
63
|
+
expect(ed.getHTML()).toContain('data-comment-id')
|
|
64
|
+
ed.commands.setTextSelection({ from: 1, to: 6 })
|
|
65
|
+
ed.chain().unsetComment().run()
|
|
66
|
+
expect(ed.getHTML()).not.toContain('data-comment-id')
|
|
67
|
+
ed.destroy()
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('parses an existing comment span on load', () => {
|
|
71
|
+
const ed = newEditor('<p><span data-comment-id="c-7">hello</span> world</p>')
|
|
72
|
+
expect(ed.getHTML()).toContain('data-comment-id="c-7"')
|
|
73
|
+
ed.destroy()
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('applies the default CSS class on every rendered span', () => {
|
|
77
|
+
const ed = newEditor('<p>hello world</p>')
|
|
78
|
+
ed.commands.setTextSelection({ from: 1, to: 6 })
|
|
79
|
+
ed.chain().setComment({ commentId: 'c-9' }).run()
|
|
80
|
+
expect(ed.getHTML()).toMatch(/class="actuate-comment"/)
|
|
81
|
+
ed.destroy()
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('supports overlapping comments (non-spanning behaviour)', () => {
|
|
85
|
+
// Set a comment over chars 1..6 ("hello").
|
|
86
|
+
const ed = newEditor('<p>hello world</p>')
|
|
87
|
+
ed.commands.setTextSelection({ from: 1, to: 6 })
|
|
88
|
+
ed.chain().setComment({ commentId: 'c-a' }).run()
|
|
89
|
+
// Now apply a second comment over chars 4..8 ("o wo"). TipTap will
|
|
90
|
+
// split the marks — that's the standard mark-overlap behaviour.
|
|
91
|
+
ed.commands.setTextSelection({ from: 4, to: 8 })
|
|
92
|
+
ed.chain().setComment({ commentId: 'c-b' }).run()
|
|
93
|
+
|
|
94
|
+
const html = ed.getHTML()
|
|
95
|
+
expect(html).toContain('data-comment-id="c-a"')
|
|
96
|
+
expect(html).toContain('data-comment-id="c-b"')
|
|
97
|
+
ed.destroy()
|
|
98
|
+
})
|
|
99
|
+
})
|