devnonla-ui 0.1.0 → 0.3.0
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/README.md +22 -10
- package/package.json +2 -2
- package/src/alert/Alert.tsx +1 -1
- package/src/app/App.tsx +38 -18
- package/src/app/context.ts +32 -0
- package/src/button/Button.tsx +2 -2
- package/src/calendar/Calendar.tsx +4 -4
- package/src/chat/AgentPanel.tsx +15 -6
- package/src/chat/common/types.ts +27 -2
- package/src/chat/common/useAgentStream.ts +78 -12
- package/src/chat/common/utils.ts +13 -0
- package/src/chat/index.ts +5 -3
- package/src/chat/message-ui/ChatAgentMessage.tsx +1 -1
- package/src/chat/message-ui/ChatInput.tsx +1 -1
- package/src/chat/message-ui/ChatMarkdown.tsx +6 -6
- package/src/chat/message-ui/ChatMarkdownTable.tsx +1 -1
- package/src/chat/message-ui/ChatThinking.tsx +4 -3
- package/src/chat/message-ui/ChatUserMessage.tsx +0 -8
- package/src/chat/message-ui/MermaidBlock.tsx +11 -1
- package/src/chat/tool-ui/BackgroundTaskToolUI.tsx +2 -1
- package/src/chat/tool-ui/CallAgentToolUI.tsx +3 -2
- package/src/chat/tool-ui/ChatToolCall.tsx +3 -2
- package/src/chat/tool-ui/GetCurrentTimeToolUI.tsx +2 -1
- package/src/chat/tool-ui/ReadSkillToolUI.tsx +3 -2
- package/src/chat/tool-ui/RunJsToolUI.tsx +3 -2
- package/src/chat/tool-ui/WebFetchToolUI.tsx +3 -2
- package/src/chat/tool-ui/registry.ts +24 -12
- package/src/checkbox/Checkbox.tsx +5 -4
- package/src/codeblock/CodeBlock.tsx +1 -0
- package/src/colorpicker/ColorPicker.tsx +694 -30
- package/src/colorpicker/color.ts +246 -0
- package/src/datepicker/DatePicker.tsx +15 -9
- package/src/desktop/DesktopHeader.tsx +1 -1
- package/src/desktop/DesktopIcon.tsx +2 -4
- package/src/desktop/DesktopWindow.tsx +19 -5
- package/src/desktop/MeadowDesktop.tsx +2 -2
- package/src/drawer/Drawer.tsx +8 -6
- package/src/dropdown/ContextMenu.tsx +3 -1
- package/src/dropdown/Dropdown.tsx +8 -6
- package/src/form/Form.tsx +2 -2
- package/src/form/FormItem.tsx +2 -2
- package/src/form/partials/FormItemHelps.tsx +2 -2
- package/src/form/variants/FieldColor.tsx +16 -51
- package/src/form/variants/FieldObject.tsx +2 -2
- package/src/form/variants/FieldRepeaterItem.tsx +4 -4
- package/src/form-layout/FormLayout.tsx +6 -6
- package/src/index.ts +25 -8
- package/src/input/Input.tsx +11 -10
- package/src/lib/sizes.ts +13 -3
- package/src/lib/surface.ts +2 -1
- package/src/menu/Menu.tsx +2 -2
- package/src/message/message.tsx +35 -2
- package/src/modal/Modal.tsx +49 -3
- package/src/pagination/Pagination.tsx +6 -5
- package/src/popconfirm/Popconfirm.tsx +1 -1
- package/src/popover/Popover.tsx +6 -8
- package/src/scroll/OverlayScroll.tsx +5 -3
- package/src/segmented/Segmented.tsx +6 -5
- package/src/select/Select.tsx +9 -6
- package/src/shimmer/Shimmer.tsx +17 -0
- package/src/skeleton/Skeleton.tsx +3 -3
- package/src/spin/Spin.tsx +8 -5
- package/src/splitter/Splitter.tsx +330 -0
- package/src/splitter/sizes.ts +67 -0
- package/src/styles.css +96 -27
- package/src/switch/Switch.tsx +6 -5
- package/src/table/Table.tsx +6 -7
- package/src/tag/Tag.tsx +2 -2
- package/src/theme.ts +23 -3
- package/src/timepicker/TimePicker.tsx +13 -11
- package/src/tooltip/Tooltip.tsx +6 -7
|
@@ -24,11 +24,11 @@ export const chatMarkdownClass = cn(
|
|
|
24
24
|
"[&_strong]:font-semibold [&_em]:italic",
|
|
25
25
|
"[&_blockquote]:m-0 [&_blockquote]:mb-(--chat-p-mb) [&_blockquote]:border-0 [&_blockquote]:p-0 [&_blockquote]:not-italic [&_blockquote]:text-inherit [&_blockquote:last-child]:mb-0",
|
|
26
26
|
"[&_ul]:mt-2 [&_ul]:mb-(--chat-p-mb)",
|
|
27
|
-
"[&_ul:not(.contains-task-list)]:list-disc [&_ul:not(.contains-task-list)]:pl-
|
|
28
|
-
"[&_ul.contains-task-list]:list-none [&_ul.contains-task-list]:pl-
|
|
27
|
+
"[&_ul:not(.contains-task-list)]:list-disc [&_ul:not(.contains-task-list)]:pl-6.5",
|
|
28
|
+
"[&_ul.contains-task-list]:list-none [&_ul.contains-task-list]:pl-6.5",
|
|
29
29
|
"[&_li.task-list-item]:relative",
|
|
30
|
-
"[&_li.task-list-item>.nonla-md-task-check]:absolute [&_li.task-list-item>.nonla-md-task-check]:top-0 [&_li.task-list-item>.nonla-md-task-check]
|
|
31
|
-
"[&_ol]:mt-2 [&_ol]:mb-(--chat-p-mb) [&_ol]:list-decimal [&_ol]:pl-
|
|
30
|
+
"[&_li.task-list-item>.nonla-md-task-check]:absolute [&_li.task-list-item>.nonla-md-task-check]:top-0 [&_li.task-list-item>.nonla-md-task-check]:-left-5.5",
|
|
31
|
+
"[&_ol]:mt-2 [&_ol]:mb-(--chat-p-mb) [&_ol]:list-decimal [&_ol]:pl-6.5",
|
|
32
32
|
"[&_li]:my-1.5 [&_li]:leading-(--chat-body-leading)",
|
|
33
33
|
"[&_a]:text-link [&_a]:no-underline [&_a]:hover:underline [&_a]:hover:underline-offset-[3px]",
|
|
34
34
|
);
|
|
@@ -78,14 +78,14 @@ export function createChatMarkdownComponents(getState?: () => ChatMarkdownStream
|
|
|
78
78
|
th({ children }) {
|
|
79
79
|
return (
|
|
80
80
|
<th>
|
|
81
|
-
<div className="inline-block max-w-
|
|
81
|
+
<div className="inline-block max-w-75 wrap-break-word">{children}</div>
|
|
82
82
|
</th>
|
|
83
83
|
);
|
|
84
84
|
},
|
|
85
85
|
td({ children }) {
|
|
86
86
|
return (
|
|
87
87
|
<td>
|
|
88
|
-
<div className="inline-block max-w-
|
|
88
|
+
<div className="inline-block max-w-75 wrap-break-word">{children}</div>
|
|
89
89
|
</td>
|
|
90
90
|
);
|
|
91
91
|
},
|
|
@@ -35,7 +35,7 @@ export function ChatMarkdownTable({ children }: { children: ReactNode }) {
|
|
|
35
35
|
<div className="overflow-x-auto">
|
|
36
36
|
<table
|
|
37
37
|
ref={tableRef}
|
|
38
|
-
className="w-max min-w-full wrap-normal border-separate border-spacing-0 text-[14px] [&_th]:bg-foreground/
|
|
38
|
+
className="w-max min-w-full wrap-normal border-separate border-spacing-0 text-[14px] [&_th]:bg-foreground/4 [&_th]:text-foreground [&_th]:font-semibold [&_th]:px-2.5 [&_th]:py-1.5 [&_th]:text-left [&_th]:align-top [&_th]:border-r [&_th]:border-b [&_th]:border-border [&_th:last-child]:border-r-0 [&_td]:px-2.5 [&_td]:py-1.5 [&_td]:align-top [&_td]:text-foreground [&_td]:bg-card [&_td]:border-r [&_td]:border-b [&_td]:border-border [&_td:last-child]:border-r-0 [&_tbody_tr:last-child_td]:border-b-0"
|
|
39
39
|
>
|
|
40
40
|
{children}
|
|
41
41
|
</table>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
2
2
|
import { cn } from "../../lib/cn";
|
|
3
|
+
import { Shimmer } from "../../shimmer/Shimmer";
|
|
3
4
|
|
|
4
5
|
export type ChatThinkingProps = {
|
|
5
6
|
thinking: string;
|
|
@@ -46,9 +47,9 @@ export function ChatThinking({ thinking, duration = 0, streaming = false, classN
|
|
|
46
47
|
if (el) el.scrollTop = el.scrollHeight;
|
|
47
48
|
}}
|
|
48
49
|
>
|
|
49
|
-
<summary className="cursor-pointer select-none text-sm font-medium
|
|
50
|
-
<
|
|
51
|
-
<svg className="w-3 h-3 shrink-0 opacity-0 transition-[opacity,transform] duration-150 group-hover/thinking:opacity-100 group-open/thinking:opacity-100 group-open/thinking:rotate-90" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden>
|
|
50
|
+
<summary className="cursor-pointer select-none text-sm font-medium flex items-center gap-1 py-0.5 list-none [&::-webkit-details-marker]:hidden">
|
|
51
|
+
<Shimmer active={streaming} className="text-tertiary-foreground">{label}</Shimmer>
|
|
52
|
+
<svg className="w-3 h-3 shrink-0 opacity-0 text-tertiary-foreground transition-[opacity,transform] duration-150 group-hover/thinking:opacity-100 group-open/thinking:opacity-100 group-open/thinking:rotate-90" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden>
|
|
52
53
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
|
53
54
|
</svg>
|
|
54
55
|
</summary>
|
|
@@ -25,21 +25,13 @@ export function ChatUserMessage({ content, className }: ChatUserMessageProps) {
|
|
|
25
25
|
|
|
26
26
|
const collapsed = isOverflow && !isExpanded;
|
|
27
27
|
|
|
28
|
-
const toggle = () => {
|
|
29
|
-
const sel = window.getSelection();
|
|
30
|
-
if (sel?.toString()) return;
|
|
31
|
-
setIsExpanded((v) => !v);
|
|
32
|
-
};
|
|
33
|
-
|
|
34
28
|
return (
|
|
35
29
|
<div
|
|
36
30
|
className={cn(
|
|
37
31
|
"nonla-chat-user relative isolate mt-6 mb-3 mx-4 overflow-hidden rounded-xl px-3 py-2.5",
|
|
38
32
|
"border border-brand/40 bg-[color-mix(in_oklab,var(--brand)_14%,var(--glass))] backdrop-blur-xl backdrop-saturate-150",
|
|
39
|
-
isOverflow && "cursor-pointer",
|
|
40
33
|
className,
|
|
41
34
|
)}
|
|
42
|
-
onClick={isOverflow ? toggle : undefined}
|
|
43
35
|
>
|
|
44
36
|
<div
|
|
45
37
|
ref={contentRef}
|
|
@@ -257,7 +257,17 @@ export function MermaidBlock({ children, className }: MermaidBlockProps) {
|
|
|
257
257
|
) : null}
|
|
258
258
|
</div>
|
|
259
259
|
|
|
260
|
-
<div
|
|
260
|
+
<div
|
|
261
|
+
role="application"
|
|
262
|
+
aria-label="Pan and zoom diagram"
|
|
263
|
+
onWheel={handleWheel}
|
|
264
|
+
onMouseDown={handleMouseDown}
|
|
265
|
+
onMouseMove={handleMouseMove}
|
|
266
|
+
onMouseUp={handleMouseUp}
|
|
267
|
+
onMouseLeave={handleMouseLeave}
|
|
268
|
+
onDoubleClick={resetView}
|
|
269
|
+
className={cn("flex-1 overflow-hidden w-full h-full select-none", isDragging ? "cursor-grabbing" : "cursor-grab")}
|
|
270
|
+
>
|
|
261
271
|
<div ref={fullscreenRef} className="flex items-center justify-center w-full h-full origin-center [&_svg]:max-w-none [&_svg]:max-h-none pointer-events-none" style={{ transform: `translate(${pan.x}px, ${pan.y}px) scale(${zoom})` }} />
|
|
262
272
|
</div>
|
|
263
273
|
</dialog>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useEffect, useState } from "react";
|
|
2
|
+
import { Shimmer } from "../../shimmer/Shimmer";
|
|
2
3
|
import { formatBgElapsed, parseBgTaskRef } from "../common/bgTasks";
|
|
3
4
|
import { formatToolName, timestampMs } from "../common/utils";
|
|
4
5
|
import { ChatSpinner } from "../message-ui/ChatSpinner";
|
|
@@ -24,7 +25,7 @@ export function BackgroundTaskToolUI({ msg, assistantLabel = "Assistant", assist
|
|
|
24
25
|
<div className="min-w-0 flex-1 pl-2">
|
|
25
26
|
<div className="flex items-center gap-2 py-1">
|
|
26
27
|
<ChatSpinner />
|
|
27
|
-
<
|
|
28
|
+
<Shimmer className="min-w-0 flex-1 truncate text-[14px] font-medium text-foreground">{label}</Shimmer>
|
|
28
29
|
<span className="shrink-0 rounded px-1 py-px text-[9px] font-semibold uppercase tracking-wider text-brand-700">Background</span>
|
|
29
30
|
<span className="shrink-0 text-[14px] tabular-nums text-muted-foreground">{formatBgElapsed(timestampMs(msg.timestamp), now)}</span>
|
|
30
31
|
</div>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ReactNode, useEffect, useRef, useState } from "react";
|
|
2
2
|
import { FluentIcon } from "../../icon/FluentIcon";
|
|
3
3
|
import { cn } from "../../lib/cn";
|
|
4
|
+
import { Shimmer } from "../../shimmer/Shimmer";
|
|
4
5
|
import { formatBgElapsed, parseBgTaskRef } from "../common/bgTasks";
|
|
5
6
|
import { parseJsonObject, prettyJson, timestampMs } from "../common/utils";
|
|
6
7
|
import { ChatMarkdown } from "../message-ui/ChatMarkdown";
|
|
@@ -30,7 +31,7 @@ function parseCallAgentOutput(raw: unknown): CallAgentParsed | null {
|
|
|
30
31
|
|
|
31
32
|
function TypingDots() {
|
|
32
33
|
return (
|
|
33
|
-
<div className="flex items-center gap-1 px-0.5 py-0.5" aria-label="Typing">
|
|
34
|
+
<div className="flex items-center gap-1 px-0.5 py-0.5" role="status" aria-label="Typing">
|
|
34
35
|
{[0, 1, 2].map((i) => (
|
|
35
36
|
<span key={i} className="size-1.5 rounded-full bg-muted-foreground/50 animate-bounce" style={{ animationDelay: `${i * 150}ms` }} />
|
|
36
37
|
))}
|
|
@@ -121,7 +122,7 @@ export function CallAgentToolUI({ msg, assistantLabel = "Assistant", assistantCo
|
|
|
121
122
|
<div className={cn("overflow-hidden rounded-xl border bg-card", failed ? "border-destructive/35" : "border-border")}>
|
|
122
123
|
<div className="flex items-center gap-2 border-b border-border-subtle px-3 py-2">
|
|
123
124
|
<FluentIcon name="chat-24" size={13} className="shrink-0 text-muted-foreground" />
|
|
124
|
-
<
|
|
125
|
+
<Shimmer active={composing || awaitingReply} className="min-w-0 flex-1 truncate text-[14px] font-medium text-foreground">Calling {calleeName}</Shimmer>
|
|
125
126
|
{bgRunning ? <span className="text-[14px] tabular-nums text-muted-foreground">{formatBgElapsed(timestampMs(msg.timestamp), now)}</span> : null}
|
|
126
127
|
{statusLabel ? <span className="max-w-40 truncate text-[13px] italic text-destructive">{statusLabel}</span> : null}
|
|
127
128
|
<ToolUiTrailing running={composing || awaitingReply} failed={failed && !awaitingReply} />
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ReactNode, useState } from "react";
|
|
2
2
|
import { cn } from "../../lib/cn";
|
|
3
|
+
import { Shimmer } from "../../shimmer/Shimmer";
|
|
3
4
|
import { formatToolName, hasMeaningfulInput, prettyJson } from "../common/utils";
|
|
4
5
|
import { ChatSpinner } from "../message-ui/ChatSpinner";
|
|
5
6
|
import { ToolUiTrailing } from "./ToolUiTrailing";
|
|
@@ -37,7 +38,7 @@ export function ChatToolCall({ toolName = "Tool", label, toolInput, toolOutput,
|
|
|
37
38
|
const header = (
|
|
38
39
|
<>
|
|
39
40
|
{identityIcon}
|
|
40
|
-
<
|
|
41
|
+
<Shimmer active={running} className={cn("min-w-0 flex-1 truncate text-left text-[14px] font-medium", !running && "text-muted-foreground", expandable && !running && "transition-colors group-hover:text-foreground")}>{displayLabel}</Shimmer>
|
|
41
42
|
<ToolUiTrailing running={running} failed={hasError} chevron={expandable} chevronClassName={cn("group-hover:opacity-100", open && "opacity-100 rotate-90")} />
|
|
42
43
|
</>
|
|
43
44
|
);
|
|
@@ -60,7 +61,7 @@ export function ChatToolCall({ toolName = "Tool", label, toolInput, toolOutput,
|
|
|
60
61
|
{running ? (
|
|
61
62
|
<div className="flex items-center gap-2 border-t border-border bg-muted/40 px-3 py-2 text-muted-foreground">
|
|
62
63
|
<ChatSpinner />
|
|
63
|
-
<
|
|
64
|
+
<Shimmer className="italic">Running…</Shimmer>
|
|
64
65
|
</div>
|
|
65
66
|
) : null}
|
|
66
67
|
{!isPending ? (
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FluentIcon } from "../../icon/FluentIcon";
|
|
2
2
|
import { parseJsonObject } from "../common/utils";
|
|
3
|
+
import { Shimmer } from "../../shimmer/Shimmer";
|
|
3
4
|
import { ToolUiBadge } from "./ToolUiBadge";
|
|
4
5
|
import { ToolUiTrailing } from "./ToolUiTrailing";
|
|
5
6
|
import { isToolRunning, type ToolUIProps } from "./types";
|
|
@@ -40,7 +41,7 @@ export function GetCurrentTimeToolUI({ msg, assistantLabel = "Assistant", assist
|
|
|
40
41
|
<div className="px-4 pb-1">
|
|
41
42
|
<div className="flex items-center gap-2 py-1">
|
|
42
43
|
<FluentIcon name="clock-24" size={13} className="shrink-0 text-muted-foreground" />
|
|
43
|
-
<
|
|
44
|
+
<Shimmer active={running} className="truncate text-[14px] font-medium tabular-nums text-muted-foreground">{hasError ? "Failed to get time" : running ? "Getting time…" : label}</Shimmer>
|
|
44
45
|
<ToolUiTrailing running={running} failed={hasError} />
|
|
45
46
|
</div>
|
|
46
47
|
</div>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FluentIcon } from "../../icon/FluentIcon";
|
|
2
2
|
import { cn } from "../../lib/cn";
|
|
3
|
+
import { Shimmer } from "../../shimmer/Shimmer";
|
|
3
4
|
import { parseJsonObject } from "../common/utils";
|
|
4
5
|
import { ToolUiBadge } from "./ToolUiBadge";
|
|
5
6
|
import { ToolUiTrailing } from "./ToolUiTrailing";
|
|
@@ -55,10 +56,10 @@ export function ReadSkillToolUI({ msg, assistantLabel = "Assistant", assistantCo
|
|
|
55
56
|
<details className="group/readskill px-4 pb-2" style={{ overflowAnchor: "none" }}>
|
|
56
57
|
<summary className="flex cursor-pointer list-none items-center gap-2 py-0.5 text-[14px] leading-5.5 select-none [&::-webkit-details-marker]:hidden">
|
|
57
58
|
<FluentIcon name={reference ? "document-text-24" : "book-24"} size={13} className="shrink-0 text-muted-foreground" />
|
|
58
|
-
<
|
|
59
|
+
<Shimmer active={running} className="min-w-0 truncate font-medium text-muted-foreground">
|
|
59
60
|
{verb}
|
|
60
61
|
{targetLabel ? <span className="font-normal text-tertiary-foreground"> {targetLabel}</span> : null}
|
|
61
|
-
</
|
|
62
|
+
</Shimmer>
|
|
62
63
|
<ToolUiTrailing running={running} failed={failed} chevron chevronClassName="group-hover/readskill:opacity-100 group-open/readskill:opacity-100 group-open/readskill:rotate-90" />
|
|
63
64
|
</summary>
|
|
64
65
|
{body ? (
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FluentIcon } from "../../icon/FluentIcon";
|
|
2
2
|
import { cn } from "../../lib/cn";
|
|
3
3
|
import { CodeBlock } from "../../codeblock/CodeBlock";
|
|
4
|
+
import { Shimmer } from "../../shimmer/Shimmer";
|
|
4
5
|
import { parseBgTaskRef } from "../common/bgTasks";
|
|
5
6
|
import { parseJsonObject, prettyJson } from "../common/utils";
|
|
6
7
|
import { ChatSpinner } from "../message-ui/ChatSpinner";
|
|
@@ -46,7 +47,7 @@ function SandboxOutput({
|
|
|
46
47
|
return (
|
|
47
48
|
<div className="flex items-center gap-2 border-t border-border px-3 py-2 text-[13px] text-muted-foreground">
|
|
48
49
|
<ChatSpinner />
|
|
49
|
-
<
|
|
50
|
+
<Shimmer>Running…</Shimmer>
|
|
50
51
|
</div>
|
|
51
52
|
);
|
|
52
53
|
}
|
|
@@ -87,7 +88,7 @@ export function RunJsToolUI({ msg, assistantLabel = "Assistant", assistantColor,
|
|
|
87
88
|
<details className="group/runjs px-4 pb-2" style={{ overflowAnchor: "none" }}>
|
|
88
89
|
<summary className="flex cursor-pointer list-none items-center gap-2 py-0.5 text-[14px] leading-5.5 select-none [&::-webkit-details-marker]:hidden">
|
|
89
90
|
<FluentIcon name="code-24" size={13} className="shrink-0 text-muted-foreground" />
|
|
90
|
-
<
|
|
91
|
+
<Shimmer active={running} className="min-w-0 truncate font-medium text-muted-foreground">{verb}</Shimmer>
|
|
91
92
|
<ToolUiTrailing running={running} failed={failed} chevron chevronClassName="group-hover/runjs:opacity-100 group-open/runjs:opacity-100 group-open/runjs:rotate-90" />
|
|
92
93
|
</summary>
|
|
93
94
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FluentIcon } from "../../icon/FluentIcon";
|
|
2
2
|
import { cn } from "../../lib/cn";
|
|
3
|
+
import { Shimmer } from "../../shimmer/Shimmer";
|
|
3
4
|
import { parseJsonObject } from "../common/utils";
|
|
4
5
|
import { ToolUiBadge } from "./ToolUiBadge";
|
|
5
6
|
import { ToolUiTrailing } from "./ToolUiTrailing";
|
|
@@ -39,9 +40,9 @@ export function WebFetchToolUI({ msg, assistantLabel = "Assistant", assistantCol
|
|
|
39
40
|
<details className="group/webfetch px-4 pb-2" style={{ overflowAnchor: "none" }}>
|
|
40
41
|
<summary className="flex cursor-pointer list-none items-center gap-2 py-0.5 text-[14px] leading-5.5 select-none [&::-webkit-details-marker]:hidden">
|
|
41
42
|
<FluentIcon name="globe-24" size={13} className="shrink-0 text-muted-foreground" />
|
|
42
|
-
<
|
|
43
|
+
<Shimmer active={running} className="min-w-0 truncate font-medium text-muted-foreground">
|
|
43
44
|
{verb} <span className="font-normal text-tertiary-foreground">{url}</span>
|
|
44
|
-
</
|
|
45
|
+
</Shimmer>
|
|
45
46
|
<ToolUiTrailing running={running} failed={failed} chevron chevronClassName="group-hover/webfetch:opacity-100 group-open/webfetch:opacity-100 group-open/webfetch:rotate-90" />
|
|
46
47
|
</summary>
|
|
47
48
|
{body ? (
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ComponentType } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import type { AgentToolNameMatch } from "../common/types";
|
|
3
|
+
import { isCallAgentToolName, matchesToolName } from "../common/utils";
|
|
3
4
|
import { CallAgentToolUI } from "./CallAgentToolUI";
|
|
4
5
|
import { GetCurrentTimeToolUI } from "./GetCurrentTimeToolUI";
|
|
5
6
|
import { ReadSkillToolUI } from "./ReadSkillToolUI";
|
|
@@ -7,23 +8,34 @@ import { RunJsToolUI } from "./RunJsToolUI";
|
|
|
7
8
|
import type { ToolUIProps } from "./types";
|
|
8
9
|
import { WebFetchToolUI } from "./WebFetchToolUI";
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
/** Exact name, aliases, or a predicate (e.g. `call_agent__*`). */
|
|
12
|
+
export type AgentToolUIName = AgentToolNameMatch;
|
|
13
|
+
|
|
14
|
+
export type AgentToolUI = {
|
|
15
|
+
name: AgentToolUIName;
|
|
12
16
|
component: ComponentType<ToolUIProps>;
|
|
13
17
|
};
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
{
|
|
19
|
+
export function matchesToolUIName(name: AgentToolUIName, toolName: string): boolean {
|
|
20
|
+
return matchesToolName(name, toolName);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const builtinToolUis: AgentToolUI[] = [
|
|
24
|
+
{ name: isCallAgentToolName, component: CallAgentToolUI },
|
|
25
|
+
{ name: ["web_fetch", "fetch_url", "browser"], component: WebFetchToolUI },
|
|
26
|
+
{ name: "get_current_time", component: GetCurrentTimeToolUI },
|
|
27
|
+
{ name: "read_skill", component: ReadSkillToolUI },
|
|
28
|
+
{ name: "run_js", component: RunJsToolUI },
|
|
21
29
|
];
|
|
22
30
|
|
|
23
|
-
|
|
31
|
+
/** App `toolUis` win on the same name; builtins are the fallback. */
|
|
32
|
+
export function resolveToolUI(toolName: string | null | undefined, extras?: readonly AgentToolUI[] | null): ComponentType<ToolUIProps> | null {
|
|
24
33
|
if (!toolName) return null;
|
|
25
|
-
|
|
26
|
-
|
|
34
|
+
const lists = extras?.length ? [extras, builtinToolUis] : [builtinToolUis];
|
|
35
|
+
for (const list of lists) {
|
|
36
|
+
for (const entry of list) {
|
|
37
|
+
if (matchesToolUIName(entry.name, toolName)) return entry.component;
|
|
38
|
+
}
|
|
27
39
|
}
|
|
28
40
|
return null;
|
|
29
41
|
}
|
|
@@ -5,7 +5,7 @@ import { cn } from "../lib/cn";
|
|
|
5
5
|
export type CheckboxProps = Omit<ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>, "onCheckedChange" | "checked" | "onChange"> & {
|
|
6
6
|
checked?: boolean | "indeterminate";
|
|
7
7
|
defaultChecked?: boolean;
|
|
8
|
-
/**
|
|
8
|
+
/** Receives next checked boolean (not a DOM event). */
|
|
9
9
|
onChange?: (checked: boolean) => void;
|
|
10
10
|
children?: ReactNode;
|
|
11
11
|
indeterminate?: boolean;
|
|
@@ -25,6 +25,7 @@ export const Checkbox = forwardRef<HTMLButtonElement, CheckboxProps>(function Ch
|
|
|
25
25
|
) {
|
|
26
26
|
const resolved = indeterminate ? "indeterminate" : checked;
|
|
27
27
|
return (
|
|
28
|
+
// biome-ignore lint/a11y/noLabelWithoutControl: wraps Radix checkbox button, not a native input
|
|
28
29
|
<label className={cn("inline-flex items-center gap-2 text-sm text-foreground cursor-pointer", disabled && "opacity-45 cursor-not-allowed")}>
|
|
29
30
|
<CheckboxPrimitive.Root
|
|
30
31
|
ref={ref}
|
|
@@ -33,9 +34,9 @@ export const Checkbox = forwardRef<HTMLButtonElement, CheckboxProps>(function Ch
|
|
|
33
34
|
disabled={disabled}
|
|
34
35
|
onCheckedChange={(v) => onChange?.(v === true)}
|
|
35
36
|
className={cn(
|
|
36
|
-
"size-4 shrink-0 rounded-
|
|
37
|
-
"focus-visible:outline-none data-[state=checked]:bg-brand data-[state=checked]:border-brand data-[state=checked]:text-
|
|
38
|
-
"data-[state=indeterminate]:bg-brand data-[state=indeterminate]:border-brand data-[state=indeterminate]:text-
|
|
37
|
+
"size-4 shrink-0 rounded-sm border border-input bg-(--control-bg) transition-colors duration-150 ease-[cubic-bezier(0.16,1,0.3,1)]",
|
|
38
|
+
"focus-visible:outline-none data-[state=checked]:bg-brand data-[state=checked]:border-brand data-[state=checked]:text-(--nonla-solid-fg)",
|
|
39
|
+
"data-[state=indeterminate]:bg-brand data-[state=indeterminate]:border-brand data-[state=indeterminate]:text-(--nonla-solid-fg)",
|
|
39
40
|
className,
|
|
40
41
|
)}
|
|
41
42
|
{...rest}
|
|
@@ -65,6 +65,7 @@ export function CodeBlock({ code, language, title, lineNumbers = false, wordWrap
|
|
|
65
65
|
<div className={cn("nonla-codeblock-body nonla-codeblock-well relative min-w-0 overflow-y-auto bg-card font-mono text-[13px] leading-5", children ? "max-h-96" : "mx-0.5 mb-0.5 max-h-96 rounded-lg")}>
|
|
66
66
|
<div className={cn("w-full", !wordWrap && "overflow-x-auto")}>
|
|
67
67
|
<pre className={cn("nonla-codeblock-pre m-0 whitespace-pre break-normal", lineNumbers && "nonla-codeblock-lines", wordWrap && "whitespace-pre-wrap wrap-break-word")}>
|
|
68
|
+
{/* biome-ignore lint/security/noDangerouslySetInnerHtml: highlight.js emits escaped HTML */}
|
|
68
69
|
<code className={cn("block px-3 py-2.5", !wordWrap && "w-max min-w-full")} dangerouslySetInnerHTML={{ __html: html }} />
|
|
69
70
|
</pre>
|
|
70
71
|
</div>
|