@springbrand/message-panel 0.1.0 → 0.1.2

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 (99) hide show
  1. package/dist/demo/camel-chat-showcase.d.ts +1 -0
  2. package/dist/demo/camel-chat-showcase.js +1007 -0
  3. package/dist/demo/fixtures.d.ts +32 -0
  4. package/dist/demo/fixtures.js +250 -0
  5. package/dist/demo/index.js +4 -0
  6. package/dist/demo/message-panel-chat-showcase.d.ts +1 -0
  7. package/dist/demo/message-panel-chat-showcase.js +114 -0
  8. package/dist/demo/message-panel-gallery.d.ts +1 -0
  9. package/dist/demo/message-panel-gallery.js +130 -0
  10. package/dist/src/adapters/ai-sdk.d.ts +4 -0
  11. package/dist/src/adapters/ai-sdk.js +25 -0
  12. package/dist/src/camel/camel-chat-messages.d.ts +44 -0
  13. package/dist/src/camel/camel-chat-messages.js +421 -0
  14. package/dist/src/camel/camel-model-select.d.ts +13 -0
  15. package/dist/src/camel/camel-model-select.js +7 -0
  16. package/dist/src/camel/camel-plan-bar.d.ts +5 -0
  17. package/dist/src/camel/camel-plan-bar.js +24 -0
  18. package/dist/src/camel/camel-prompt-input.d.ts +32 -0
  19. package/dist/src/camel/camel-prompt-input.js +182 -0
  20. package/dist/src/camel/camel-tool-presentation.d.ts +21 -0
  21. package/dist/src/camel/camel-tool-presentation.js +784 -0
  22. package/dist/src/camel/camel-turn.d.ts +57 -0
  23. package/dist/src/camel/camel-turn.js +203 -0
  24. package/dist/src/camel/index.js +7 -0
  25. package/dist/src/camel/turn-summary-bar.d.ts +8 -0
  26. package/dist/src/camel/turn-summary-bar.js +12 -0
  27. package/dist/src/chat-summary-panel.d.ts +22 -0
  28. package/dist/src/chat-summary-panel.js +26 -0
  29. package/dist/src/composer/chat-composer.d.ts +4 -0
  30. package/dist/src/composer/chat-composer.js +90 -0
  31. package/dist/src/composer/composer-attachments.d.ts +13 -0
  32. package/dist/src/composer/composer-attachments.js +47 -0
  33. package/dist/src/composer/composer-trigger-popover.d.ts +31 -0
  34. package/dist/src/composer/composer-trigger-popover.js +29 -0
  35. package/dist/src/composer/composer.d.ts +15 -0
  36. package/dist/src/composer/composer.js +180 -0
  37. package/dist/src/composer/drop-controller.d.ts +8 -0
  38. package/dist/src/composer/drop-controller.js +69 -0
  39. package/dist/src/composer/index.js +6 -0
  40. package/dist/src/composer/types.d.ts +62 -0
  41. package/dist/src/composer/types.js +1 -0
  42. package/dist/src/contracts.d.ts +94 -0
  43. package/dist/src/contracts.js +1 -0
  44. package/dist/src/conversation.d.ts +13 -0
  45. package/dist/src/conversation.js +18 -0
  46. package/dist/src/index.js +12 -0
  47. package/dist/src/internal/cn.d.ts +1 -0
  48. package/dist/src/internal/cn.js +3 -0
  49. package/dist/src/message-panel.d.ts +4 -0
  50. package/dist/src/message-panel.js +144 -0
  51. package/dist/src/message.d.ts +36 -0
  52. package/dist/src/message.js +83 -0
  53. package/dist/src/parts/index.d.ts +187 -0
  54. package/dist/src/parts/index.js +332 -0
  55. package/dist/src/parts/kind.d.ts +3 -0
  56. package/dist/src/parts/kind.js +69 -0
  57. package/dist/src/parts/plan.d.ts +10 -0
  58. package/dist/src/parts/plan.js +40 -0
  59. package/dist/src/parts/primitives.d.ts +70 -0
  60. package/dist/src/parts/primitives.js +153 -0
  61. package/dist/src/registry.d.ts +4 -0
  62. package/dist/src/registry.js +31 -0
  63. package/dist/src/status.d.ts +26 -0
  64. package/dist/src/status.js +74 -0
  65. package/package.json +17 -8
  66. package/demo/camel-chat-showcase.tsx +0 -1265
  67. package/demo/fixtures.ts +0 -292
  68. package/demo/message-panel-chat-showcase.tsx +0 -250
  69. package/demo/message-panel-gallery.tsx +0 -389
  70. package/src/adapters/ai-sdk.ts +0 -32
  71. package/src/camel/camel-chat-messages.tsx +0 -1420
  72. package/src/camel/camel-model-select.tsx +0 -88
  73. package/src/camel/camel-plan-bar.tsx +0 -115
  74. package/src/camel/camel-prompt-input.tsx +0 -540
  75. package/src/camel/camel-tool-presentation.tsx +0 -1270
  76. package/src/camel/camel-turn.ts +0 -315
  77. package/src/camel/turn-summary-bar.tsx +0 -81
  78. package/src/chat-summary-panel.tsx +0 -229
  79. package/src/composer/chat-composer.tsx +0 -216
  80. package/src/composer/composer-attachments.tsx +0 -138
  81. package/src/composer/composer-trigger-popover.tsx +0 -236
  82. package/src/composer/composer.tsx +0 -425
  83. package/src/composer/drop-controller.ts +0 -80
  84. package/src/composer/types.ts +0 -79
  85. package/src/contracts.ts +0 -139
  86. package/src/conversation.tsx +0 -76
  87. package/src/internal/cn.ts +0 -3
  88. package/src/message-panel.tsx +0 -368
  89. package/src/message.tsx +0 -254
  90. package/src/parts/index.tsx +0 -1149
  91. package/src/parts/kind.ts +0 -67
  92. package/src/parts/plan.ts +0 -63
  93. package/src/parts/primitives.tsx +0 -439
  94. package/src/registry.ts +0 -44
  95. package/src/status.tsx +0 -132
  96. /package/{demo/index.ts → dist/demo/index.d.ts} +0 -0
  97. /package/{src/camel/index.ts → dist/src/camel/index.d.ts} +0 -0
  98. /package/{src/composer/index.ts → dist/src/composer/index.d.ts} +0 -0
  99. /package/{src/index.ts → dist/src/index.d.ts} +0 -0
@@ -0,0 +1,153 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { BookOpenIcon, BrainIcon, CheckCircle2Icon, ChevronDownIcon, ChevronRightIcon, CircleIcon, ClockIcon, WrenchIcon, XCircleIcon, } from "lucide-react";
3
+ import { createContext, forwardRef, useContext, useMemo, useState, } from "react";
4
+ import { cn } from "../internal/cn";
5
+ import { MessageResponse } from "../message";
6
+ const ReasoningContext = createContext(null);
7
+ export function Reasoning({ isStreaming = false, open, defaultOpen, onOpenChange, duration, className, children, ...props }) {
8
+ const [internalOpen, setInternalOpen] = useState(defaultOpen ?? isStreaming);
9
+ const resolvedOpen = open ?? internalOpen;
10
+ const toggle = () => {
11
+ const next = !resolvedOpen;
12
+ if (open === undefined)
13
+ setInternalOpen(next);
14
+ onOpenChange?.(next);
15
+ };
16
+ const value = useMemo(() => ({ open: resolvedOpen, toggle, isStreaming, duration }), [resolvedOpen, isStreaming, duration]);
17
+ return (_jsx(ReasoningContext.Provider, { value: value, children: _jsx("div", { className: cn("sb-reasoning", className), ...props, children: children }) }));
18
+ }
19
+ export function ReasoningTrigger({ children, className, ...props }) {
20
+ const context = useContext(ReasoningContext);
21
+ if (!context)
22
+ throw new Error("ReasoningTrigger must be used within Reasoning");
23
+ const label = context.isStreaming
24
+ ? "Thinking…"
25
+ : context.duration
26
+ ? `Thought for ${context.duration}s`
27
+ : "Reasoning";
28
+ return (_jsx("button", { type: "button", className: cn("sb-reasoning__trigger", className), onClick: context.toggle, "aria-expanded": context.open, ...props, children: children ?? (_jsxs(_Fragment, { children: [_jsx(BrainIcon, { size: 16 }), _jsx("span", { children: label }), _jsx(ChevronDownIcon, { size: 16, "aria-hidden": "true" })] })) }));
29
+ }
30
+ export function ReasoningContent({ children, className, ...props }) {
31
+ const context = useContext(ReasoningContext);
32
+ if (!context)
33
+ throw new Error("ReasoningContent must be used within Reasoning");
34
+ if (!context.open)
35
+ return null;
36
+ return (_jsx("div", { className: cn("sb-reasoning__content", className), ...props, children: _jsx(MessageResponse, { streaming: context.isStreaming, children: children }) }));
37
+ }
38
+ const toolStatus = {
39
+ "approval-requested": {
40
+ label: "Awaiting approval",
41
+ icon: _jsx(ClockIcon, { size: 14 }),
42
+ tone: "warning",
43
+ },
44
+ "approval-responded": {
45
+ label: "Responded",
46
+ icon: _jsx(CheckCircle2Icon, { size: 14 }),
47
+ tone: "info",
48
+ },
49
+ "input-available": {
50
+ label: "Running",
51
+ icon: _jsx(ClockIcon, { size: 14 }),
52
+ tone: "running",
53
+ },
54
+ "input-streaming": {
55
+ label: "Pending",
56
+ icon: _jsx(CircleIcon, { size: 14 }),
57
+ tone: "running",
58
+ },
59
+ "output-available": {
60
+ label: "Completed",
61
+ icon: _jsx(CheckCircle2Icon, { size: 14 }),
62
+ tone: "success",
63
+ },
64
+ "output-denied": {
65
+ label: "Denied",
66
+ icon: _jsx(XCircleIcon, { size: 14 }),
67
+ tone: "warning",
68
+ },
69
+ "output-error": {
70
+ label: "Error",
71
+ icon: _jsx(XCircleIcon, { size: 14 }),
72
+ tone: "error",
73
+ },
74
+ };
75
+ export function getStatusBadge(status) {
76
+ const value = toolStatus[status];
77
+ return (_jsxs("span", { className: "sb-tool-line__status", "data-tone": value.tone, children: [value.icon, value.label] }));
78
+ }
79
+ export const Tool = forwardRef(({ className, ...props }, ref) => (_jsx("details", { ref: ref, className: cn("sb-tool-line", className), ...props })));
80
+ Tool.displayName = "Tool";
81
+ export function ToolHeader({ className, title, type, state, toolName, ...props }) {
82
+ const derived = type === "dynamic-tool"
83
+ ? toolName ?? "tool"
84
+ : type.split("-").slice(1).join("-");
85
+ return (_jsxs("summary", { className: cn("sb-tool-line__trigger", className), ...props, children: [_jsx(WrenchIcon, { size: 16 }), _jsx("span", { children: title ?? derived }), state !== "output-available" && getStatusBadge(state), _jsx(ChevronRightIcon, { size: 16, className: "sb-tool-line__chevron", "aria-hidden": "true" })] }));
86
+ }
87
+ export const ToolContent = forwardRef(({ className, ...props }, ref) => (_jsx("div", { ref: ref, className: cn("sb-tool-line__details", className), ...props })));
88
+ ToolContent.displayName = "ToolContent";
89
+ export function ToolInput({ input, className, ...props }) {
90
+ return (_jsx("pre", { className: cn("sb-terminal", className), ...props, children: JSON.stringify(input, null, 2) }));
91
+ }
92
+ export function ToolOutput({ output, errorText, className, ...props }) {
93
+ if (output == null && !errorText)
94
+ return null;
95
+ const content = typeof output === "string"
96
+ ? output
97
+ : output == null
98
+ ? ""
99
+ : JSON.stringify(output, null, 2);
100
+ return (_jsx("pre", { className: cn("sb-terminal", className), "data-error": errorText ? "true" : undefined, ...props, children: errorText || content }));
101
+ }
102
+ const ConfirmationContext = createContext(null);
103
+ export function Confirmation({ state, approval, className, ...props }) {
104
+ const value = useMemo(() => ({ state, approval }), [state, approval]);
105
+ return (_jsx(ConfirmationContext.Provider, { value: value, children: _jsx("div", { className: cn("sb-confirmation", className), ...props }) }));
106
+ }
107
+ function useConfirmation() {
108
+ const context = useContext(ConfirmationContext);
109
+ if (!context)
110
+ throw new Error("Confirmation child must be used within Confirmation");
111
+ return context;
112
+ }
113
+ export const ConfirmationTitle = ({ className, ...props }) => (_jsx("div", { className: cn("sb-confirmation__title", className), ...props }));
114
+ export function ConfirmationRequest({ children }) {
115
+ const { state } = useConfirmation();
116
+ return state === "approval-requested" || state === "pending" ? children : null;
117
+ }
118
+ export function ConfirmationAccepted({ children }) {
119
+ const { state, approval } = useConfirmation();
120
+ return state === "confirmed" || approval?.approved === true ? children : null;
121
+ }
122
+ export function ConfirmationRejected({ children }) {
123
+ const { state, approval } = useConfirmation();
124
+ return state === "cancelled" ||
125
+ state === "output-denied" ||
126
+ approval?.approved === false
127
+ ? children
128
+ : null;
129
+ }
130
+ export function ConfirmationActions({ className, ...props }) {
131
+ const { state } = useConfirmation();
132
+ if (state !== "approval-requested" && state !== "pending")
133
+ return null;
134
+ return (_jsx("div", { className: cn("sb-confirmation__actions", className), ...props }));
135
+ }
136
+ export const ConfirmationAction = forwardRef(({ className, ...props }, ref) => (_jsx("button", { ref: ref, type: "button", className: cn("sb-button", className), ...props })));
137
+ ConfirmationAction.displayName = "ConfirmationAction";
138
+ export const Plan = forwardRef(({ className, isStreaming, ...props }, ref) => (_jsx("div", { ref: ref, className: cn("sb-plan", className), "data-streaming": isStreaming ? "true" : undefined, ...props })));
139
+ Plan.displayName = "Plan";
140
+ export const PlanHeader = ({ className, ...props }) => (_jsx("div", { className: cn("sb-plan__header", className), ...props }));
141
+ export const PlanTitle = ({ className, ...props }) => (_jsx("h3", { className: cn("sb-plan__title", className), ...props }));
142
+ export const PlanDescription = ({ className, ...props }) => (_jsx("p", { className: cn("sb-plan__description", className), ...props }));
143
+ export const PlanContent = ({ className, ...props }) => (_jsx("div", { className: cn("sb-plan__content", className), ...props }));
144
+ export const PlanFooter = ({ className, ...props }) => (_jsx("div", { className: cn("sb-plan__footer", className), ...props }));
145
+ export const Sources = forwardRef(({ className, ...props }, ref) => (_jsx("details", { ref: ref, className: cn("sb-sources", className), ...props })));
146
+ Sources.displayName = "Sources";
147
+ export function SourcesTrigger({ count, children, className, ...props }) {
148
+ return (_jsx("summary", { className: cn("sb-sources__trigger", className), ...props, children: children ?? (_jsxs(_Fragment, { children: [_jsxs("span", { children: ["\u6765\u6E90 ", count, " \u4E2A"] }), _jsx(ChevronDownIcon, { size: 15 })] })) }));
149
+ }
150
+ export const SourcesContent = ({ className, ...props }) => (_jsx("div", { className: cn("sb-sources__content", className), ...props }));
151
+ export function Source({ title, children, ...props }) {
152
+ return (_jsx("a", { target: "_blank", rel: "noreferrer", ...props, children: children ?? (_jsxs(_Fragment, { children: [_jsx(BookOpenIcon, { size: 14 }), _jsx("span", { children: title })] })) }));
153
+ }
@@ -0,0 +1,4 @@
1
+ import type { PartRendererDefinition, PartRendererRegistry } from "./contracts";
2
+ export declare function createPartRendererRegistry(definitions: readonly PartRendererDefinition[]): PartRendererRegistry;
3
+ export declare function mergePartRendererRegistries(...registries: Array<PartRendererRegistry | undefined>): PartRendererRegistry;
4
+ export declare const defaultPartRendererRegistry: PartRendererRegistry;
@@ -0,0 +1,31 @@
1
+ import { DefaultPartRenderer } from "./parts";
2
+ export function createPartRendererRegistry(definitions) {
3
+ const stable = [...definitions];
4
+ return {
5
+ names: stable.map((definition) => definition.name),
6
+ resolve(part) {
7
+ return stable.find((definition) => definition.match(part))?.component;
8
+ },
9
+ };
10
+ }
11
+ export function mergePartRendererRegistries(...registries) {
12
+ const resolved = registries.filter((registry) => registry !== undefined);
13
+ return {
14
+ names: resolved.flatMap((registry) => registry.names),
15
+ resolve(part) {
16
+ for (const registry of resolved) {
17
+ const component = registry.resolve(part);
18
+ if (component)
19
+ return component;
20
+ }
21
+ return undefined;
22
+ },
23
+ };
24
+ }
25
+ export const defaultPartRendererRegistry = createPartRendererRegistry([
26
+ {
27
+ name: "default",
28
+ match: () => true,
29
+ component: DefaultPartRenderer,
30
+ },
31
+ ]);
@@ -0,0 +1,26 @@
1
+ import { type OrbState } from "thinking-orbs";
2
+ import type { MessagePanelStatus } from "./contracts";
3
+ export type PanelVisualState = "empty" | "hydrating" | "thinking" | "streaming" | "ready" | "error" | "recovering" | "background";
4
+ export declare function derivePanelVisualState({ messageCount, status, isHydrating, isRecovering, isServerStreaming, }: {
5
+ messageCount: number;
6
+ status: MessagePanelStatus;
7
+ isHydrating?: boolean;
8
+ isRecovering?: boolean;
9
+ isServerStreaming?: boolean;
10
+ }): PanelVisualState;
11
+ export declare function ThinkingIndicator({ label, state, className, }: {
12
+ label?: string;
13
+ state?: OrbState;
14
+ className?: string;
15
+ }): import("react").JSX.Element;
16
+ export declare function deriveThinkingActivity(part?: unknown): {
17
+ state: OrbState;
18
+ label: string;
19
+ };
20
+ export declare function TopLoadingBar({ status }: {
21
+ status: MessagePanelStatus;
22
+ }): import("react").JSX.Element | null;
23
+ export declare function PanelStatus({ kind, children, }: {
24
+ kind: "hydrating" | "recovering" | "background" | "error";
25
+ children: React.ReactNode;
26
+ }): import("react").JSX.Element;
@@ -0,0 +1,74 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from "react";
3
+ import { ThinkingOrb } from "thinking-orbs";
4
+ import { cn } from "./internal/cn";
5
+ import { resolvePartKind, toolNameOfPart } from "./parts/kind";
6
+ export function derivePanelVisualState({ messageCount, status, isHydrating = false, isRecovering = false, isServerStreaming = false, }) {
7
+ if (isHydrating)
8
+ return "hydrating";
9
+ if (status === "error")
10
+ return "error";
11
+ if (isRecovering)
12
+ return "recovering";
13
+ if (isServerStreaming)
14
+ return "background";
15
+ if (status === "submitted")
16
+ return "thinking";
17
+ if (status === "streaming")
18
+ return "streaming";
19
+ return messageCount === 0 ? "empty" : "ready";
20
+ }
21
+ export function ThinkingIndicator({ label = "Thinking…", state = "solving", className, }) {
22
+ return (_jsxs("div", { className: cn("sb-thinking", className), role: "status", "aria-label": label, children: [_jsx(ThinkingOrb, { state: state, size: 20, "aria-hidden": "true" }), _jsx("span", { children: label })] }));
23
+ }
24
+ export function deriveThinkingActivity(part) {
25
+ const kind = resolvePartKind(part);
26
+ const toolName = toolNameOfPart(part);
27
+ if (kind === "reasoning")
28
+ return { state: "solving", label: "Reasoning…" };
29
+ if (kind === "text")
30
+ return { state: "composing", label: "Composing answer…" };
31
+ if (["plan", "image", "chart", "dashboard"].includes(kind ?? "") ||
32
+ (toolName != null &&
33
+ /(?:^|[_-])(?:image|chart|artifact|render|write_file)(?:$|[_-])/i.test(toolName))) {
34
+ return { state: "shaping", label: "Shaping result…" };
35
+ }
36
+ if (toolName != null &&
37
+ /(?:^|[_-])(?:search|fetch|browse|browser|web)(?:$|[_-])/i.test(toolName)) {
38
+ return { state: "searching", label: "Searching…" };
39
+ }
40
+ if (toolName != null) {
41
+ return {
42
+ state: "working",
43
+ label: `Running ${toolName.replaceAll("_", " ")}…`,
44
+ };
45
+ }
46
+ return part === undefined
47
+ ? { state: "solving", label: "Thinking…" }
48
+ : { state: "working", label: "Working…" };
49
+ }
50
+ export function TopLoadingBar({ status }) {
51
+ const [phase, setPhase] = useState("idle");
52
+ const previous = useRef(status);
53
+ useEffect(() => {
54
+ const before = previous.current;
55
+ previous.current = status;
56
+ if (status === "submitted") {
57
+ setPhase("loading");
58
+ return;
59
+ }
60
+ if (before === "submitted" && status === "streaming") {
61
+ setPhase("done");
62
+ const timeout = window.setTimeout(() => setPhase("idle"), 500);
63
+ return () => window.clearTimeout(timeout);
64
+ }
65
+ if (status !== "streaming")
66
+ setPhase("idle");
67
+ }, [status]);
68
+ if (phase === "idle")
69
+ return null;
70
+ return (_jsx("div", { className: "sb-top-loading", role: "progressbar", "aria-label": "\u7B49\u5F85\u54CD\u5E94", children: _jsx("span", { "data-phase": phase }) }));
71
+ }
72
+ export function PanelStatus({ kind, children, }) {
73
+ return (_jsxs("div", { className: "sb-panel-status", "data-kind": kind, role: kind === "error" ? "alert" : "status", children: [_jsx("span", { "aria-hidden": "true" }), children] }));
74
+ }
package/package.json CHANGED
@@ -1,19 +1,28 @@
1
1
  {
2
2
  "name": "@springbrand/message-panel",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "files": [
6
- "demo",
7
- "src"
6
+ "dist",
7
+ "src/styles"
8
8
  ],
9
9
  "publishConfig": {
10
10
  "access": "public"
11
11
  },
12
12
  "exports": {
13
- ".": "./src/index.ts",
14
- "./composer": "./src/composer/index.ts",
13
+ ".": {
14
+ "types": "./dist/src/index.d.ts",
15
+ "import": "./dist/src/index.js"
16
+ },
17
+ "./composer": {
18
+ "types": "./dist/src/composer/index.d.ts",
19
+ "import": "./dist/src/composer/index.js"
20
+ },
15
21
  "./styles.css": "./src/styles/index.css",
16
- "./demo": "./demo/index.ts"
22
+ "./demo": {
23
+ "types": "./dist/demo/index.d.ts",
24
+ "import": "./dist/demo/index.js"
25
+ }
17
26
  },
18
27
  "peerDependencies": {
19
28
  "ai": "^7.0.0",
@@ -30,8 +39,7 @@
30
39
  "radix-ui": "^1.6.2",
31
40
  "streamdown": "^2.5.0",
32
41
  "thinking-orbs": "0.1.1",
33
- "use-stick-to-bottom": "^1.1.6",
34
- "@springbrand/agent-runtime": "0.1.0"
42
+ "use-stick-to-bottom": "^1.1.6"
35
43
  },
36
44
  "devDependencies": {
37
45
  "@types/react": "^19.2.17",
@@ -39,6 +47,7 @@
39
47
  "typescript": "^7.0.2"
40
48
  },
41
49
  "scripts": {
50
+ "build": "tsc -p tsconfig.build.json",
42
51
  "typecheck": "tsc --noEmit"
43
52
  }
44
53
  }