@springbrand/message-panel 0.1.3-alpha.33 → 0.1.3-alpha.36
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/cloud-os/README.md +2 -1
- package/cloud-os/assets/thinking-star.svg +9 -0
- package/cloud-os/chat/cloud-os-chat-messages.tsx +290 -239
- package/cloud-os/chat/code-runner.tsx +106 -0
- package/cloud-os/chat/range.ts +8 -0
- package/cloud-os/chat/rich-blocks.tsx +186 -256
- package/cloud-os/chat/surfaces.tsx +90 -0
- package/cloud-os/chat/tool-call.tsx +94 -0
- package/cloud-os/chat/tool-presentation.ts +3 -11
- package/cloud-os/chat/tool-rows.tsx +145 -378
- package/cloud-os/chat/tool-timeline.tsx +123 -0
- package/cloud-os/chat/transcript-model.ts +86 -116
- package/cloud-os/chat/web-search.tsx +135 -0
- package/cloud-os/composer/cloud-os-chat-input.tsx +1 -1
- package/cloud-os/index.ts +10 -5
- package/cloud-os/primitives/collapsible.tsx +21 -0
- package/cloud-os/styles/cloud-os.css +18 -12
- package/demo/chat-scenarios.ts +3 -9
- package/package.json +3 -1
- package/cloud-os/assets/thinking-spark.svg +0 -9
- package/cloud-os/chat/use-lifecycle-disclosure.ts +0 -65
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ComponentProps } from "react";
|
|
4
|
+
import { useLayoutEffect, useRef, useState } from "react";
|
|
5
|
+
import { cn } from "../internal/cn";
|
|
6
|
+
|
|
7
|
+
export const paper = "bg-background border border-border/60 dark:bg-popover";
|
|
8
|
+
|
|
9
|
+
export const field = "bg-foreground/[0.04] dark:bg-foreground/[0.06]";
|
|
10
|
+
|
|
11
|
+
export const ghostButton =
|
|
12
|
+
"flex items-center justify-center rounded-full text-foreground/45 outline-none transition-[background-color,color,scale] duration-150 hover:bg-foreground/[0.06] hover:text-foreground/90 active:scale-[0.96] focus-visible:ring-1 focus-visible:ring-foreground/20 motion-reduce:transition-none dark:hover:bg-foreground/[0.09]";
|
|
13
|
+
|
|
14
|
+
export const labelSwap =
|
|
15
|
+
"col-start-1 row-start-1 flex w-max items-center gap-1.5 leading-none transition-[opacity,filter] duration-300 ease-[cubic-bezier(0.23,1,0.32,1)] motion-reduce:transition-none";
|
|
16
|
+
|
|
17
|
+
export const labelSwapIn = "opacity-100 blur-none";
|
|
18
|
+
|
|
19
|
+
export const labelSwapOut =
|
|
20
|
+
"pointer-events-none select-none opacity-0 blur-[2px]";
|
|
21
|
+
|
|
22
|
+
export const collapsePanel =
|
|
23
|
+
"h-(--collapsible-panel-height) overflow-hidden transition-[height] duration-200 ease-[cubic-bezier(0.32,0.72,0,1)] data-[ending-style]:h-0 data-[starting-style]:h-0 motion-reduce:transition-none";
|
|
24
|
+
|
|
25
|
+
export const mono = "font-mono text-[11px] tracking-tight";
|
|
26
|
+
|
|
27
|
+
export const codeScroll = "max-h-48 overflow-auto";
|
|
28
|
+
|
|
29
|
+
export const codeSurface = "w-max min-w-full";
|
|
30
|
+
|
|
31
|
+
export function ShimmerLabel({
|
|
32
|
+
active = true,
|
|
33
|
+
className,
|
|
34
|
+
...props
|
|
35
|
+
}: ComponentProps<"span"> & { active?: boolean }) {
|
|
36
|
+
return (
|
|
37
|
+
<span
|
|
38
|
+
className={cn(active && "shimmer motion-reduce:animate-none", className)}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function SwapLabel({
|
|
45
|
+
active,
|
|
46
|
+
children,
|
|
47
|
+
className,
|
|
48
|
+
}: {
|
|
49
|
+
active: 0 | 1;
|
|
50
|
+
children: [React.ReactNode, React.ReactNode];
|
|
51
|
+
className?: string;
|
|
52
|
+
}) {
|
|
53
|
+
const layers = [useRef<HTMLSpanElement>(null), useRef<HTMLSpanElement>(null)];
|
|
54
|
+
const [width, setWidth] = useState<number | null>(null);
|
|
55
|
+
|
|
56
|
+
useLayoutEffect(() => {
|
|
57
|
+
const target = layers[active]?.current;
|
|
58
|
+
if (!target) return undefined;
|
|
59
|
+
const measure = () =>
|
|
60
|
+
setWidth(Math.ceil(target.getBoundingClientRect().width));
|
|
61
|
+
measure();
|
|
62
|
+
const observer = new ResizeObserver(measure);
|
|
63
|
+
observer.observe(target);
|
|
64
|
+
return () => observer.disconnect();
|
|
65
|
+
}, [active]);
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<span
|
|
69
|
+
style={width === null ? undefined : { width }}
|
|
70
|
+
className={cn(
|
|
71
|
+
"grid overflow-x-clip transition-[width] duration-300 ease-[cubic-bezier(0.23,1,0.32,1)] motion-reduce:transition-none",
|
|
72
|
+
className,
|
|
73
|
+
)}
|
|
74
|
+
>
|
|
75
|
+
{children.map((layer, index) => (
|
|
76
|
+
<span
|
|
77
|
+
key={index}
|
|
78
|
+
ref={layers[index]}
|
|
79
|
+
aria-hidden={active !== index}
|
|
80
|
+
className={cn(
|
|
81
|
+
labelSwap,
|
|
82
|
+
active === index ? labelSwapIn : labelSwapOut,
|
|
83
|
+
)}
|
|
84
|
+
>
|
|
85
|
+
{layer}
|
|
86
|
+
</span>
|
|
87
|
+
))}
|
|
88
|
+
</span>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { CheckIcon, ChevronRightIcon } from "lucide-react";
|
|
4
|
+
import {
|
|
5
|
+
Collapsible,
|
|
6
|
+
CollapsibleContent,
|
|
7
|
+
CollapsibleTrigger,
|
|
8
|
+
} from "../primitives/collapsible";
|
|
9
|
+
import { cn } from "../internal/cn";
|
|
10
|
+
import {
|
|
11
|
+
collapsePanel,
|
|
12
|
+
field,
|
|
13
|
+
mono,
|
|
14
|
+
ShimmerLabel,
|
|
15
|
+
SwapLabel,
|
|
16
|
+
} from "./surfaces";
|
|
17
|
+
|
|
18
|
+
export interface ToolCallProps {
|
|
19
|
+
label: string;
|
|
20
|
+
activeLabel: string;
|
|
21
|
+
query: string;
|
|
22
|
+
result: string;
|
|
23
|
+
running: boolean;
|
|
24
|
+
open: boolean;
|
|
25
|
+
onOpenChange: (open: boolean) => void;
|
|
26
|
+
className?: string;
|
|
27
|
+
children?: React.ReactNode;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function ToolCall({
|
|
31
|
+
label,
|
|
32
|
+
activeLabel,
|
|
33
|
+
query,
|
|
34
|
+
result,
|
|
35
|
+
running,
|
|
36
|
+
open,
|
|
37
|
+
onOpenChange,
|
|
38
|
+
className,
|
|
39
|
+
children,
|
|
40
|
+
}: ToolCallProps) {
|
|
41
|
+
return (
|
|
42
|
+
<Collapsible
|
|
43
|
+
data-slot="tool-call"
|
|
44
|
+
open={open}
|
|
45
|
+
onOpenChange={onOpenChange}
|
|
46
|
+
className={cn("w-full max-w-sm", className)}
|
|
47
|
+
>
|
|
48
|
+
<CollapsibleTrigger className="group/trigger text-foreground/55 hover:text-foreground/90 flex items-center gap-2 rounded-md py-1 text-[13.5px] transition-colors outline-none">
|
|
49
|
+
<ChevronRightIcon className="size-3.5 shrink-0 opacity-60 transition-transform duration-200 ease-[cubic-bezier(0.32,0.72,0,1)] group-data-open/trigger:rotate-90 group-data-panel-open/trigger:rotate-90 motion-reduce:transition-none" />
|
|
50
|
+
<SwapLabel active={running ? 0 : 1} className="text-start">
|
|
51
|
+
<ShimmerLabel
|
|
52
|
+
active={running}
|
|
53
|
+
className="relative inline-block leading-none"
|
|
54
|
+
>
|
|
55
|
+
{activeLabel}
|
|
56
|
+
</ShimmerLabel>
|
|
57
|
+
<>{label}</>
|
|
58
|
+
</SwapLabel>
|
|
59
|
+
{query && (
|
|
60
|
+
<span
|
|
61
|
+
className={cn(
|
|
62
|
+
mono,
|
|
63
|
+
"bg-foreground/[0.06] text-foreground/70 rounded-md px-1.5 py-0.5",
|
|
64
|
+
)}
|
|
65
|
+
>
|
|
66
|
+
{query}
|
|
67
|
+
</span>
|
|
68
|
+
)}
|
|
69
|
+
<span className="ms-auto flex w-4 items-center justify-end">
|
|
70
|
+
{!running && (
|
|
71
|
+
<CheckIcon className="fade-in zoom-in-90 animate-in size-3.5 text-emerald-500 duration-200" />
|
|
72
|
+
)}
|
|
73
|
+
</span>
|
|
74
|
+
</CollapsibleTrigger>
|
|
75
|
+
<CollapsibleContent className={cn(collapsePanel, "outline-none")}>
|
|
76
|
+
{children ? (
|
|
77
|
+
<div className="mt-2">{children}</div>
|
|
78
|
+
) : (
|
|
79
|
+
<div className={cn(field, "mt-2 overflow-hidden rounded-2xl text-xs")}>
|
|
80
|
+
<div className="px-3.5 py-2.5">
|
|
81
|
+
<p className={cn(mono, "text-foreground/35 mb-1")}>Result</p>
|
|
82
|
+
<p
|
|
83
|
+
className="text-foreground/90 overflow-auto leading-4"
|
|
84
|
+
style={{ maxHeight: "5rem" }}
|
|
85
|
+
>
|
|
86
|
+
{result}
|
|
87
|
+
</p>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
)}
|
|
91
|
+
</CollapsibleContent>
|
|
92
|
+
</Collapsible>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
@@ -213,7 +213,7 @@ export function canonicalToolKind(name: string): CloudOsToolKind {
|
|
|
213
213
|
return "web-fetch";
|
|
214
214
|
}
|
|
215
215
|
if (
|
|
216
|
-
["javascript", "js_exec", "node_repl", "execute_javascript", "execute_code"].includes(
|
|
216
|
+
["javascript", "js_exec", "node_repl", "execute", "execute_javascript", "execute_code"].includes(
|
|
217
217
|
normalized,
|
|
218
218
|
)
|
|
219
219
|
) {
|
|
@@ -329,16 +329,8 @@ export function getToolCallSummary(call: CloudOsToolCall): {
|
|
|
329
329
|
target: url ? hostnameOf(url) : undefined,
|
|
330
330
|
};
|
|
331
331
|
}
|
|
332
|
-
case "javascript":
|
|
333
|
-
|
|
334
|
-
.split("\n")
|
|
335
|
-
.map((line) => line.trim())
|
|
336
|
-
.find((line) => line.length > 0);
|
|
337
|
-
return {
|
|
338
|
-
verb: running ? "Running code" : "Ran code",
|
|
339
|
-
target: firstLine ? truncate(firstLine, 60) : undefined,
|
|
340
|
-
};
|
|
341
|
-
}
|
|
332
|
+
case "javascript":
|
|
333
|
+
return { verb: running ? "Running code" : "Ran code" };
|
|
342
334
|
case "tasks":
|
|
343
335
|
return { verb: running ? "Updating plan" : "Updated plan" };
|
|
344
336
|
case "agent": {
|