@agent-native/toolkit 0.16.16 → 0.17.0-nightly-20260826025507

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 (36) hide show
  1. package/dist/composer/MentionItemMedia.d.ts +9 -0
  2. package/dist/composer/MentionItemMedia.d.ts.map +1 -0
  3. package/dist/composer/MentionItemMedia.js +57 -0
  4. package/dist/composer/MentionItemMedia.js.map +1 -0
  5. package/dist/composer/MentionItemMedia.spec.d.ts +2 -0
  6. package/dist/composer/MentionItemMedia.spec.d.ts.map +1 -0
  7. package/dist/composer/MentionItemMedia.spec.js +72 -0
  8. package/dist/composer/MentionItemMedia.spec.js.map +1 -0
  9. package/dist/composer/MentionPopover.d.ts.map +1 -1
  10. package/dist/composer/MentionPopover.js +3 -24
  11. package/dist/composer/MentionPopover.js.map +1 -1
  12. package/dist/composer/TiptapComposer.d.ts.map +1 -1
  13. package/dist/composer/TiptapComposer.js +4 -1
  14. package/dist/composer/TiptapComposer.js.map +1 -1
  15. package/dist/composer/TiptapComposer.spec.js +54 -0
  16. package/dist/composer/TiptapComposer.spec.js.map +1 -1
  17. package/dist/composer/extensions/MentionReference.d.ts.map +1 -1
  18. package/dist/composer/extensions/MentionReference.js +43 -23
  19. package/dist/composer/extensions/MentionReference.js.map +1 -1
  20. package/dist/composer/index.d.ts +1 -1
  21. package/dist/composer/index.d.ts.map +1 -1
  22. package/dist/composer/runtime-adapters.d.ts +2 -0
  23. package/dist/composer/runtime-adapters.d.ts.map +1 -1
  24. package/dist/composer/runtime-adapters.js.map +1 -1
  25. package/dist/composer/types.d.ts +21 -0
  26. package/dist/composer/types.d.ts.map +1 -1
  27. package/package.json +1 -1
  28. package/src/composer/MentionItemMedia.spec.tsx +150 -0
  29. package/src/composer/MentionItemMedia.tsx +130 -0
  30. package/src/composer/MentionPopover.tsx +5 -32
  31. package/src/composer/TiptapComposer.spec.ts +63 -0
  32. package/src/composer/TiptapComposer.tsx +8 -1
  33. package/src/composer/extensions/MentionReference.tsx +54 -34
  34. package/src/composer/index.ts +2 -0
  35. package/src/composer/runtime-adapters.tsx +3 -0
  36. package/src/composer/types.ts +23 -0
@@ -188,6 +188,69 @@ describe("createTiptapComposerExtensions", () => {
188
188
  editor.destroy();
189
189
  });
190
190
 
191
+ it.each([
192
+ [
193
+ "text",
194
+ {
195
+ type: "text",
196
+ text: "PK",
197
+ backgroundColor: "#4f46e5",
198
+ },
199
+ ],
200
+ [
201
+ "image",
202
+ {
203
+ type: "image",
204
+ src: "/agents/property.png",
205
+ fit: "cover",
206
+ backgroundColor: "#ffffff",
207
+ },
208
+ ],
209
+ ["none", { type: "none" }],
210
+ ] as const)(
211
+ "preserves %s mention media through HTML drafts",
212
+ (_type, media) => {
213
+ const first = new Editor({
214
+ element: document.createElement("div"),
215
+ extensions: createTiptapComposerExtensions(() => "Message agent..."),
216
+ content: {
217
+ type: "doc",
218
+ content: [
219
+ {
220
+ type: "paragraph",
221
+ content: [
222
+ {
223
+ type: "mentionReference",
224
+ attrs: {
225
+ label: "Property agent",
226
+ media,
227
+ },
228
+ },
229
+ ],
230
+ },
231
+ ],
232
+ },
233
+ });
234
+ const html = first.getHTML();
235
+ first.destroy();
236
+
237
+ expect(html).toContain("data-media=");
238
+ expect(html).not.toContain("[object Object]");
239
+
240
+ const restored = new Editor({
241
+ element: document.createElement("div"),
242
+ extensions: createTiptapComposerExtensions(() => "Message agent..."),
243
+ content: html,
244
+ });
245
+ const mentionNode = restored.getJSON().content?.[0]?.content?.[0] as
246
+ | { attrs?: Record<string, unknown> }
247
+ | undefined;
248
+ restored.destroy();
249
+
250
+ expect(mentionNode?.attrs?.media).toEqual(media);
251
+ },
252
+ );
253
+
191
254
  it("allows sending an attachment-only prompt", () => {
192
255
  expect(
193
256
  canSubmitComposerContent({
@@ -44,6 +44,7 @@ import { getComposerDraftKey } from "./draft-key.js";
44
44
  import { FileReference } from "./extensions/FileReference.js";
45
45
  import { MentionReference } from "./extensions/MentionReference.js";
46
46
  import { SkillReference } from "./extensions/SkillReference.js";
47
+ import { MentionItemMedia } from "./MentionItemMedia.js";
47
48
  import { MentionPopover, type MentionPopoverRef } from "./MentionPopover.js";
48
49
  import {
49
50
  isClaudeCodeAgentId,
@@ -179,6 +180,7 @@ function composerReferenceFromMentionItem(
179
180
  return {
180
181
  label: item.label,
181
182
  icon: item.icon || "file",
183
+ media: item.media,
182
184
  source: item.source,
183
185
  refType: item.refType,
184
186
  refId: item.refId || null,
@@ -195,6 +197,7 @@ function mentionReferenceAttrs(ref: AgentComposerReference) {
195
197
  return {
196
198
  label: ref.label,
197
199
  icon: ref.icon || "file",
200
+ media: ref.media || null,
198
201
  source: ref.source,
199
202
  refType: ref.refType,
200
203
  refId: ref.refId || null,
@@ -3693,7 +3696,11 @@ export function TiptapComposer({
3693
3696
  key={ref.slotKey}
3694
3697
  className="inline-flex max-w-full items-center gap-1.5 rounded-md border border-border bg-background px-2 py-1 text-[11px] font-medium text-foreground shadow-sm"
3695
3698
  >
3696
- <IconClipboardList className="h-3 w-3 shrink-0 text-muted-foreground" />
3699
+ <MentionItemMedia
3700
+ media={ref.media}
3701
+ size="sm"
3702
+ fallbackIcon="clipboard"
3703
+ />
3697
3704
  {ref.slotLabel && (
3698
3705
  <span className="shrink-0 text-muted-foreground">
3699
3706
  {ref.slotLabel}
@@ -1,40 +1,46 @@
1
- import {
2
- IconFile,
3
- IconFolder,
4
- IconFileText,
5
- IconCheckbox,
6
- IconMail,
7
- IconUser,
8
- IconPresentation,
9
- IconStack2,
10
- IconMessageChatbot,
11
- } from "@tabler/icons-react";
12
1
  import { mergeAttributes, Node } from "@tiptap/core";
13
2
  import { NodeViewWrapper, ReactNodeViewRenderer } from "@tiptap/react";
14
3
 
15
- const iconProps = { size: 14, className: "shrink-0 text-muted-foreground" };
4
+ import { MentionItemMedia } from "../MentionItemMedia.js";
5
+ import type { MentionItemMedia as MentionItemMediaValue } from "../types.js";
16
6
 
17
- function MentionIcon({ icon }: { icon?: string }) {
18
- switch (icon) {
19
- case "folder":
20
- return <IconFolder {...iconProps} />;
21
- case "document":
22
- return <IconFileText {...iconProps} />;
23
- case "form":
24
- return <IconCheckbox {...iconProps} />;
25
- case "email":
26
- return <IconMail {...iconProps} />;
27
- case "user":
28
- return <IconUser {...iconProps} />;
29
- case "deck":
30
- return <IconPresentation {...iconProps} />;
31
- case "agent":
32
- return <IconMessageChatbot {...iconProps} />;
33
- case "file":
34
- return <IconFile {...iconProps} />;
35
- default:
36
- return <IconStack2 {...iconProps} />;
37
- }
7
+ function parseMentionItemMedia(
8
+ value: string | null,
9
+ ): MentionItemMediaValue | null {
10
+ if (!value) return null;
11
+ try {
12
+ const parsed: unknown = JSON.parse(value);
13
+ if (
14
+ typeof parsed !== "object" ||
15
+ parsed === null ||
16
+ Array.isArray(parsed)
17
+ ) {
18
+ return null;
19
+ }
20
+ const candidate = parsed as Record<string, unknown>;
21
+ const backgroundColor =
22
+ typeof candidate.backgroundColor === "string"
23
+ ? candidate.backgroundColor
24
+ : undefined;
25
+ if (candidate.type === "none") return { type: "none" };
26
+ if (candidate.type === "text" && typeof candidate.text === "string") {
27
+ return {
28
+ type: "text",
29
+ text: candidate.text,
30
+ ...(backgroundColor ? { backgroundColor } : {}),
31
+ };
32
+ }
33
+ if (candidate.type === "image" && typeof candidate.src === "string") {
34
+ return {
35
+ type: "image",
36
+ src: candidate.src,
37
+ ...(candidate.fit === "cover" ? { fit: "cover" } : {}),
38
+ ...(backgroundColor ? { backgroundColor } : {}),
39
+ };
40
+ }
41
+ // coercion-ok: Malformed optional media must preserve legacy draft rendering.
42
+ } catch {}
43
+ return null;
38
44
  }
39
45
 
40
46
  const MentionReferenceComponent = ({ node }: { node: any }) => {
@@ -44,7 +50,12 @@ const MentionReferenceComponent = ({ node }: { node: any }) => {
44
50
  className="inline-flex items-center gap-1 rounded-md border border-input bg-muted/50 px-1.5 py-0.5 text-xs font-medium text-foreground align-middle mx-0.5 max-w-[200px] select-none"
45
51
  title={node.attrs.refPath || node.attrs.refId || node.attrs.label}
46
52
  >
47
- <MentionIcon icon={node.attrs.icon} />
53
+ <MentionItemMedia
54
+ icon={node.attrs.icon}
55
+ media={node.attrs.media}
56
+ size="sm"
57
+ fallbackIcon="stack"
58
+ />
48
59
  <span className="truncate">{node.attrs.label}</span>
49
60
  </span>
50
61
  </NodeViewWrapper>
@@ -62,6 +73,15 @@ export const MentionReference = Node.create({
62
73
  return {
63
74
  label: { default: null },
64
75
  icon: { default: "file" },
76
+ media: {
77
+ default: null,
78
+ parseHTML: (element) =>
79
+ parseMentionItemMedia(element.getAttribute("data-media")),
80
+ renderHTML: (attributes) =>
81
+ attributes.media
82
+ ? { "data-media": JSON.stringify(attributes.media) }
83
+ : {},
84
+ },
65
85
  source: { default: "" },
66
86
  refType: { default: "file" },
67
87
  refId: { default: null },
@@ -81,6 +81,8 @@ export type {
81
81
  FileResult,
82
82
  SkillResult,
83
83
  MentionItem,
84
+ MentionItemMedia,
85
+ MentionReferenceInsert,
84
86
  Reference,
85
87
  SlashCommand,
86
88
  } from "./types.js";
@@ -6,6 +6,8 @@ import {
6
6
  type ReactNode,
7
7
  } from "react";
8
8
 
9
+ import type { MentionItemMedia } from "./types.js";
10
+
9
11
  export type ComposerTranslate = (
10
12
  key: string,
11
13
  options?: Record<string, unknown>,
@@ -86,6 +88,7 @@ export interface ComposerBuilderConnectFlowOptions {
86
88
  export interface AgentComposerReference {
87
89
  label: string;
88
90
  icon?: string;
91
+ media?: MentionItemMedia;
89
92
  source?: string;
90
93
  refType: string;
91
94
  refId?: string | null;
@@ -12,11 +12,32 @@ export interface SkillResult {
12
12
  source: "codebase" | "resource";
13
13
  }
14
14
 
15
+ export type MentionItemMedia =
16
+ | {
17
+ type: "text";
18
+ /** Short text shown inside the media frame, such as an emoji or initials. */
19
+ text: string;
20
+ /** Optional CSS color used behind the text. */
21
+ backgroundColor?: string;
22
+ }
23
+ | {
24
+ type: "image";
25
+ /** Image URL shown inside the media frame. Relative URLs are supported. */
26
+ src: string;
27
+ /** How the image fits the frame. Defaults to contain. */
28
+ fit?: "contain" | "cover";
29
+ /** Optional CSS color visible behind contained images. */
30
+ backgroundColor?: string;
31
+ }
32
+ | { type: "none" };
33
+
15
34
  export interface MentionItem {
16
35
  id: string;
17
36
  label: string;
18
37
  description?: string;
19
38
  icon?: string;
39
+ /** Optional presentation that takes precedence over the legacy icon. */
40
+ media?: MentionItemMedia;
20
41
  source: string;
21
42
  refType: string;
22
43
  refPath?: string;
@@ -44,6 +65,8 @@ export interface Reference {
44
65
  export interface MentionReferenceInsert {
45
66
  label: string;
46
67
  icon?: string;
68
+ /** Optional presentation that takes precedence over the legacy icon. */
69
+ media?: MentionItemMedia;
47
70
  source?: string;
48
71
  refType: string;
49
72
  refId?: string | null;