weifuwu 0.57.0 → 0.59.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.
@@ -0,0 +1,59 @@
1
+ /**
2
+ * weifuwu/client AI 解码器 — 消费 wf: 协议(docs/ai-contract.md)
3
+ *
4
+ * 协议的前端参考实现:POST → 解析 SSE → 按事件名分发回调。
5
+ *
6
+ * - 零依赖:fetch + ReadableStream + 自研 SSE 解析
7
+ * - 未知事件透传不抛错(协议 §6):x:* 走 onEvent,未订阅的跳过
8
+ * - 事件录制(副产品):record 开启时记录完整事件序列,可导出为测试 fixture
9
+ * - trace 桥(协议 §7):默认自动生成 X-Trace-Id 并随请求发送,
10
+ * 后端以之作为 wf:message_start.id → 整个 agent run 一次搜完
11
+ * - abort:handle.abort() 或外部 signal → 取消请求 → 后端断开 → provider 取消
12
+ */
13
+ import type { WfApprovalRequest, WfDone, WfError, WfStreamEvent, WfToolCall, WfToolProgress, WfToolResult, WfUsage } from '../ai/types.ts';
14
+ export interface AiStreamCallbacks {
15
+ /** wf:token — 增量文本,直接 append */
16
+ onToken?: (text: string) => void;
17
+ /** wf:tool_call — 完整工具调用(后端已聚合) */
18
+ onToolCall?: (call: WfToolCall) => void;
19
+ /** wf:tool_result — ok:false 不代表会话结束 */
20
+ onToolResult?: (result: WfToolResult) => void;
21
+ /** wf:tool_progress — 长任务进度 */
22
+ onToolProgress?: (p: WfToolProgress) => void;
23
+ /** wf:step — 步骤可视化(agent 扩展) */
24
+ onStep?: (s: {
25
+ type: 'llm' | 'tool';
26
+ content?: string;
27
+ toolCallId?: string;
28
+ name?: string;
29
+ }) => void;
30
+ /** wf:approval_request — 渲染审批卡片 */
31
+ onApproval?: (req: WfApprovalRequest) => void;
32
+ /** wf:usage — token 计数 */
33
+ onUsage?: (u: WfUsage) => void;
34
+ /** wf:done — 收尾 */
35
+ onDone?: (d: WfDone) => void;
36
+ /** wf:error — 错误即值,结构化降级 */
37
+ onError?: (e: WfError) => void;
38
+ /** x:* 自定义事件透传兜底(协议 §6:框架不解释) */
39
+ onEvent?: (name: string, data: unknown) => void;
40
+ }
41
+ export interface AiStreamOptions extends AiStreamCallbacks {
42
+ signal?: AbortSignal;
43
+ headers?: Record<string, string>;
44
+ /** 显式指定 traceId;默认自动生成(协议 §7) */
45
+ traceId?: string;
46
+ /** 录制事件序列(调试/导出 fixture),默认开启,环形上限 1000 */
47
+ record?: boolean;
48
+ }
49
+ export interface AiStreamHandle {
50
+ /** 取消请求(用户停止/组件卸载/导航跳走时调用) */
51
+ abort: () => void;
52
+ /** 流结束(正常 done / error / abort 都 resolve) */
53
+ done: Promise<void>;
54
+ /** 本次请求使用的 traceId(自动生成的也能拿到) */
55
+ traceId: string;
56
+ /** 录制的事件序列(record: true 时) */
57
+ events: WfStreamEvent[];
58
+ }
59
+ export declare function aiStream(url: string, body: unknown, options?: AiStreamOptions): AiStreamHandle;
@@ -31,6 +31,9 @@ export { ApiError } from './middleware/api.ts';
31
31
  export type { AuthClient, AuthOptions, AuthInjected } from './middleware/auth.ts';
32
32
  export { extendCtx } from './types.ts';
33
33
  export type { WfuiContext, AppMiddleware, RouteDef } from './types.ts';
34
+ export { aiStream } from './ai.ts';
35
+ export type { AiStreamCallbacks, AiStreamOptions, AiStreamHandle } from './ai.ts';
36
+ export type { WfStreamEvent, WfMessageStart, WfToken, WfUsage, WfDone, WfError, WfErrorCode, WfToolCall, WfToolResult, WfToolProgress, WfStep, WfApprovalRequest, WfApprovalResponse, WfApprovalDecision, ChatMessage, ChatParams, MessageRole, ToolCall, ToolDefinition, } from '../ai/types.ts';
34
37
  export { ErrorBoundary } from './error-boundary.ts';
35
38
  export type { ErrorBoundaryProps } from './error-boundary.ts';
36
39
  export { i18n } from './i18n.ts';
@@ -1584,6 +1584,133 @@ function auth(options) {
1584
1584
  };
1585
1585
  }
1586
1586
 
1587
+ // src/client/ai.ts
1588
+ var RECORD_LIMIT = 1e3;
1589
+ function aiStream(url, body, options) {
1590
+ const controller = new AbortController();
1591
+ const external = options?.signal;
1592
+ if (external) {
1593
+ if (external.aborted) controller.abort();
1594
+ else external.addEventListener("abort", () => controller.abort(), { once: true });
1595
+ }
1596
+ const traceId = options?.traceId ?? randomId();
1597
+ const record = options?.record !== false;
1598
+ const events = [];
1599
+ const done = (async () => {
1600
+ let res;
1601
+ try {
1602
+ res = await fetch(url, {
1603
+ method: "POST",
1604
+ headers: {
1605
+ "Content-Type": "application/json",
1606
+ "X-Trace-Id": traceId,
1607
+ Accept: "text/event-stream",
1608
+ ...options?.headers
1609
+ },
1610
+ body: JSON.stringify(body),
1611
+ signal: controller.signal
1612
+ });
1613
+ } catch (err) {
1614
+ if (controller.signal.aborted) return;
1615
+ options?.onError?.({ code: "provider_error", message: err instanceof Error ? err.message : String(err) });
1616
+ return;
1617
+ }
1618
+ if (!res.ok) {
1619
+ options?.onError?.({
1620
+ code: httpErrorCode(res.status),
1621
+ message: `HTTP ${res.status} ${res.statusText}`
1622
+ });
1623
+ return;
1624
+ }
1625
+ try {
1626
+ for await (const { name, data } of parseWfEvents(res.body)) {
1627
+ if (controller.signal.aborted) return;
1628
+ pushEvent(events, { name, data }, record);
1629
+ dispatch(name, data, options ?? {});
1630
+ }
1631
+ } catch (err) {
1632
+ if (controller.signal.aborted) return;
1633
+ options?.onError?.({ code: "provider_error", message: err instanceof Error ? err.message : String(err) });
1634
+ }
1635
+ })();
1636
+ return {
1637
+ abort: () => controller.abort(),
1638
+ done,
1639
+ traceId,
1640
+ events
1641
+ };
1642
+ }
1643
+ function dispatch(name, data, o) {
1644
+ switch (name) {
1645
+ case "wf:token":
1646
+ return o.onToken?.(data.text);
1647
+ case "wf:tool_call":
1648
+ return o.onToolCall?.(data);
1649
+ case "wf:tool_result":
1650
+ return o.onToolResult?.(data);
1651
+ case "wf:tool_progress":
1652
+ return o.onToolProgress?.(data);
1653
+ case "wf:step":
1654
+ return o.onStep?.(data);
1655
+ case "wf:approval_request":
1656
+ return o.onApproval?.(data);
1657
+ case "wf:usage":
1658
+ return o.onUsage?.(data);
1659
+ case "wf:done":
1660
+ return o.onDone?.(data);
1661
+ case "wf:error":
1662
+ return o.onError?.(data);
1663
+ default:
1664
+ return o.onEvent?.(name, data);
1665
+ }
1666
+ }
1667
+ async function* parseWfEvents(stream) {
1668
+ const reader = stream.getReader();
1669
+ const decoder = new TextDecoder();
1670
+ let buffer = "";
1671
+ try {
1672
+ while (true) {
1673
+ const { done, value } = await reader.read();
1674
+ if (done) break;
1675
+ buffer += decoder.decode(value, { stream: true });
1676
+ const blocks = buffer.split("\n\n");
1677
+ buffer = blocks.pop() ?? "";
1678
+ for (const block of blocks) {
1679
+ let name = "message";
1680
+ let data = "";
1681
+ for (const line of block.split("\n")) {
1682
+ if (line.startsWith("event: ")) name = line.slice(7);
1683
+ else if (line.startsWith("data: ")) data += line.slice(6);
1684
+ }
1685
+ if (!data) continue;
1686
+ try {
1687
+ yield { name, data: JSON.parse(data) };
1688
+ } catch {
1689
+ }
1690
+ }
1691
+ }
1692
+ } finally {
1693
+ reader.releaseLock();
1694
+ }
1695
+ }
1696
+ function pushEvent(events, e, record) {
1697
+ if (!record) return;
1698
+ if (events.length >= RECORD_LIMIT) events.shift();
1699
+ events.push(e);
1700
+ }
1701
+ function httpErrorCode(status) {
1702
+ if (status === 401 || status === 403) return "auth_failed";
1703
+ if (status === 429) return "rate_limited";
1704
+ if (status >= 500) return "provider_error";
1705
+ return "invalid_request";
1706
+ }
1707
+ function randomId() {
1708
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
1709
+ return crypto.randomUUID();
1710
+ }
1711
+ return `trace-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
1712
+ }
1713
+
1587
1714
  // src/client/error-boundary.ts
1588
1715
  function ErrorBoundary(props, ctx) {
1589
1716
  let error = null;
@@ -1842,6 +1969,7 @@ export {
1842
1969
  Fragment,
1843
1970
  Portal,
1844
1971
  RouteView,
1972
+ aiStream,
1845
1973
  api,
1846
1974
  asyncComponent,
1847
1975
  auth,
@@ -0,0 +1,30 @@
1
+ import type { Component } from '../../client/vnode.ts';
2
+ import type { WfApprovalRequest } from '../../ai/types.ts';
3
+ /**
4
+ * ApprovalCard — 人工审批卡片(wf: 协议 §4.5 交互原语)
5
+ *
6
+ * 状态机:pending(待批,允许/拒绝 + 备注)→ approved / rejected / timeout(终态)。
7
+ * 纯受控组件:决策回传由 onApprove / onReject 上抛(app 负责 POST /approve)。
8
+ *
9
+ * ```tsx
10
+ * <ApprovalCard
11
+ * request={approvalReq}
12
+ * onApprove={() => respond('approved')}
13
+ * onReject={(note) => respond('rejected', note)}
14
+ * />
15
+ * ```
16
+ */
17
+ export type ApprovalStatus = 'pending' | 'approved' | 'rejected' | 'timeout';
18
+ export interface ApprovalCardProps {
19
+ /** wf:approval_request 数据 */
20
+ request: WfApprovalRequest;
21
+ /** 卡片状态(默认 pending) */
22
+ status?: ApprovalStatus;
23
+ /** 用户点"允许" */
24
+ onApprove?: () => void;
25
+ /** 用户点"拒绝",note 为可选备注(进 agent 上下文) */
26
+ onReject?: (note?: string) => void;
27
+ /** 自定义详情渲染(默认显示 name + args) */
28
+ renderDetail?: (request: WfApprovalRequest) => any;
29
+ }
30
+ export declare const ApprovalCard: Component<ApprovalCardProps>;
@@ -3,5 +3,7 @@ export interface AvatarProps {
3
3
  name?: string;
4
4
  src?: string;
5
5
  size?: 'sm' | 'md' | 'lg';
6
+ /** 指定背景色(覆盖按名字哈希的颜色),如按类型着色:user=蓝 / ai=紫 */
7
+ color?: string;
6
8
  }
7
9
  export declare const Avatar: Component<AvatarProps>;
@@ -3,6 +3,10 @@ export interface CardProps {
3
3
  variant?: 'default' | 'outlined';
4
4
  padding?: 'sm' | 'md' | 'lg';
5
5
  clickable?: boolean;
6
+ /** hover 抬升(阴影 + 上移),适合可点击/可悬停的卡片 */
7
+ hover?: boolean;
8
+ /** 选中态(边框高亮 + 品牌浅底),适合选择卡片 */
9
+ active?: boolean;
6
10
  onClick?: () => void;
7
11
  children?: any;
8
12
  }
@@ -9,6 +9,8 @@ export interface InputProps {
9
9
  disabled?: boolean;
10
10
  error?: string;
11
11
  hint?: string;
12
+ /** 边框变体:borderless 用于可编辑标题/内联编辑(hover/focus 才显边框) */
13
+ variant?: 'default' | 'borderless';
12
14
  onInput?: (e: Event) => void;
13
15
  onChange?: (e: Event) => void;
14
16
  }
@@ -0,0 +1,22 @@
1
+ import type { Component } from '../../client/vnode.ts';
2
+ export interface SegmentedOption {
3
+ value: string;
4
+ label: string;
5
+ disabled?: boolean;
6
+ }
7
+ export interface SegmentedControlProps {
8
+ /** 选项列表(label 可为字符串或任意 VNode) */
9
+ options: SegmentedOption[];
10
+ /** 当前选中值 */
11
+ value?: string;
12
+ onChange?: (value: string) => void;
13
+ size?: 'sm' | 'md';
14
+ /** 撑满父容器宽度(选项等分) */
15
+ block?: boolean;
16
+ ariaLabel?: string;
17
+ }
18
+ /**
19
+ * 分段控件 — 单选切换(模式切换 / 状态筛选 / 模板选择)
20
+ * 语义:toggle group(aria-pressed),键盘 focus-visible 可见
21
+ */
22
+ export declare const SegmentedControl: Component<SegmentedControlProps>;
@@ -5,5 +5,7 @@ export interface StatCardProps {
5
5
  trend?: 'up' | 'down';
6
6
  trendLabel?: string;
7
7
  icon?: string;
8
+ /** 点击跳转/交互(悬停抬升 + role=button) */
9
+ onClick?: () => void;
8
10
  }
9
11
  export declare const StatCard: Component<StatCardProps>;
@@ -8,6 +8,10 @@ export interface TextareaProps {
8
8
  error?: string;
9
9
  hint?: string;
10
10
  rows?: number;
11
+ /** 最大字符数(同时限制输入) */
12
+ maxLength?: number;
13
+ /** 显示字数统计(右下角;配合受控 value 实时更新) */
14
+ showCount?: boolean;
11
15
  onInput?: (e: Event) => void;
12
16
  }
13
17
  export declare const Textarea: Component<TextareaProps>;
@@ -0,0 +1,23 @@
1
+ import type { Component } from '../../client/vnode.ts';
2
+ import type { WfToolCall, WfToolProgress, WfToolResult } from '../../ai/types.ts';
3
+ /**
4
+ * ToolCallCard — 工具调用卡片(wf: 协议 §4 交互原语)
5
+ *
6
+ * 纯展示组件:接收协议事件数据 + 可选回调,无内部状态。
7
+ * 状态机:running(等待/执行中,带进度条)→ ok / error(有 result 时)。
8
+ *
9
+ * ```tsx
10
+ * <ToolCallCard call={toolCall} progress={progress} result={result} />
11
+ * ```
12
+ */
13
+ export interface ToolCallCardProps {
14
+ /** wf:tool_call 数据 */
15
+ call: WfToolCall;
16
+ /** wf:tool_progress 数据(可选,展示进度条) */
17
+ progress?: WfToolProgress;
18
+ /** wf:tool_result 数据(可选,ok/error 终态) */
19
+ result?: WfToolResult;
20
+ /** 自定义参数渲染(默认 JSON.stringify) */
21
+ renderArgs?: (args: Record<string, unknown>) => any;
22
+ }
23
+ export declare const ToolCallCard: Component<ToolCallCardProps>;
@@ -59,6 +59,8 @@ export { Slider } from './Slider/Slider.ts';
59
59
  export type { SliderProps } from './Slider/Slider.ts';
60
60
  export { SearchInput } from './SearchInput/SearchInput.ts';
61
61
  export type { SearchInputProps } from './SearchInput/SearchInput.ts';
62
+ export { SegmentedControl } from './SegmentedControl/SegmentedControl.ts';
63
+ export type { SegmentedControlProps, SegmentedOption } from './SegmentedControl/SegmentedControl.ts';
62
64
  export { ProgressBar } from './ProgressBar/ProgressBar.ts';
63
65
  export type { ProgressBarProps } from './ProgressBar/ProgressBar.ts';
64
66
  export { Accordion } from './Accordion/Accordion.ts';
@@ -92,3 +94,7 @@ export type { EditorProps, ToolbarItem } from './Editor/Editor.ts';
92
94
  export { ThemeSwitch } from './ThemeSwitch/ThemeSwitch.ts';
93
95
  export type { ThemeSwitchProps, ThemeMode } from './ThemeSwitch/ThemeSwitch.ts';
94
96
  export { applyTheme, getTheme } from './ThemeSwitch/ThemeSwitch.ts';
97
+ export { ToolCallCard } from './ToolCallCard/ToolCallCard.ts';
98
+ export type { ToolCallCardProps } from './ToolCallCard/ToolCallCard.ts';
99
+ export { ApprovalCard } from './ApprovalCard/ApprovalCard.ts';
100
+ export type { ApprovalCardProps, ApprovalStatus } from './ApprovalCard/ApprovalCard.ts';
@@ -52,9 +52,9 @@ var Button = (_init, ctx) => (props) => {
52
52
 
53
53
  // src/components/Input/Input.ts
54
54
  var Input = (_init) => (props) => {
55
- const { label, name, type = "text", value, placeholder, required, disabled, error, hint, onInput, onChange } = props;
55
+ const { label, name, type = "text", value, placeholder, required, disabled, error, hint, variant = "default", onInput, onChange } = props;
56
56
  const inputEl = h("input", {
57
- class: "wf-input",
57
+ class: `wf-input${variant === "borderless" ? " wf-input--borderless" : ""}`,
58
58
  name: name || void 0,
59
59
  type,
60
60
  value: value ?? "",
@@ -79,7 +79,7 @@ var Input = (_init) => (props) => {
79
79
 
80
80
  // src/components/Textarea/Textarea.ts
81
81
  var Textarea = (_init, _ctx) => (props) => {
82
- const { label, value, placeholder, required, disabled, error, hint, rows = 3, onInput } = props;
82
+ const { label, value, placeholder, required, disabled, error, hint, rows = 3, maxLength, showCount, onInput } = props;
83
83
  const textareaEl = h("textarea", {
84
84
  class: "wf-textarea",
85
85
  value: value ?? "",
@@ -87,6 +87,7 @@ var Textarea = (_init, _ctx) => (props) => {
87
87
  required: required || void 0,
88
88
  disabled: disabled || void 0,
89
89
  rows,
90
+ maxLength,
90
91
  onInput
91
92
  });
92
93
  const children = [];
@@ -96,6 +97,14 @@ var Textarea = (_init, _ctx) => (props) => {
96
97
  children.push(h("label", { class: "wf-textarea-label" }, labelContent));
97
98
  }
98
99
  children.push(textareaEl);
100
+ if (showCount) {
101
+ const len = (value ?? "").length;
102
+ const over = maxLength != null && len > maxLength;
103
+ children.push(h("div", {
104
+ class: `wf-textarea-count${over ? " wf-textarea-count--over" : ""}`,
105
+ "aria-live": "polite"
106
+ }, maxLength != null ? `${len}/${maxLength}` : String(len)));
107
+ }
99
108
  if (error) children.push(h("div", { class: "wf-textarea-err" }, error));
100
109
  if (hint && !error) children.push(h("div", { class: "wf-textarea-hint" }, hint));
101
110
  return h("div", { class: `wf-textarea-wrap${error ? " wf-textarea--err" : ""}` }, children);
@@ -1059,12 +1068,14 @@ function getPageRange(current, total) {
1059
1068
 
1060
1069
  // src/components/Card/Card.ts
1061
1070
  var Card = (_init, _ctx) => (props) => {
1062
- const { variant = "default", padding = "md", clickable, onClick, children } = props;
1071
+ const { variant = "default", padding = "md", clickable, hover, active, onClick, children } = props;
1063
1072
  const cls = [
1064
1073
  "wf-card",
1065
1074
  `wf-card--${variant}`,
1066
1075
  `wf-card--pad-${padding}`,
1067
- clickable && "wf-card--clickable"
1076
+ clickable && "wf-card--clickable",
1077
+ hover && "wf-card--hover",
1078
+ active && "wf-card--active"
1068
1079
  ].filter(Boolean).join(" ");
1069
1080
  return h("div", { class: cls, onClick, role: clickable ? "button" : void 0, tabindex: clickable ? 0 : void 0 }, children);
1070
1081
  };
@@ -1086,7 +1097,7 @@ function hashColor(name) {
1086
1097
  return COLORS[Math.abs(hash) % COLORS.length];
1087
1098
  }
1088
1099
  var Avatar = (_init, _ctx) => (props) => {
1089
- const { name = "", src, size = "md" } = props;
1100
+ const { name = "", src, size = "md", color } = props;
1090
1101
  const initial = name.trim()[0]?.toUpperCase() ?? "?";
1091
1102
  if (src) {
1092
1103
  return h("img", {
@@ -1097,7 +1108,7 @@ var Avatar = (_init, _ctx) => (props) => {
1097
1108
  }
1098
1109
  return h("div", {
1099
1110
  class: `wf-avatar wf-avatar--${size}`,
1100
- style: { background: hashColor(name) }
1111
+ style: { background: color ?? hashColor(name) }
1101
1112
  }, initial);
1102
1113
  };
1103
1114
 
@@ -1113,7 +1124,7 @@ var Tag = (_init, _ctx) => (props) => {
1113
1124
 
1114
1125
  // src/components/StatCard/StatCard.ts
1115
1126
  var StatCard = (_init, _ctx) => (props) => {
1116
- const { label, value, trend, trendLabel, icon } = props;
1127
+ const { label, value, trend, trendLabel, icon, onClick } = props;
1117
1128
  const children = [];
1118
1129
  if (icon) children.push(h("div", { class: "wf-stat-icon" }, icon));
1119
1130
  children.push(h("div", { class: "wf-stat-value" }, String(value)));
@@ -1126,7 +1137,12 @@ var StatCard = (_init, _ctx) => (props) => {
1126
1137
  trendLabel ? h("span", { class: "wf-stat-trend-label" }, trendLabel) : null
1127
1138
  ].filter(Boolean)));
1128
1139
  }
1129
- return h("div", { class: "wf-stat" }, children);
1140
+ return h("div", {
1141
+ class: `wf-stat${onClick ? " wf-stat--clickable" : ""}`,
1142
+ onClick,
1143
+ role: onClick ? "button" : void 0,
1144
+ tabindex: onClick ? 0 : void 0
1145
+ }, children);
1130
1146
  };
1131
1147
 
1132
1148
  // src/components/Steps/Steps.ts
@@ -1279,6 +1295,27 @@ var SearchInput = (_init, _ctx) => (props) => {
1279
1295
  ].filter(Boolean));
1280
1296
  };
1281
1297
 
1298
+ // src/components/SegmentedControl/SegmentedControl.ts
1299
+ var SegmentedControl = (_init, _ctx) => (props) => {
1300
+ const { options, value, onChange, size = "md", block, ariaLabel } = props;
1301
+ const cls = [
1302
+ "wf-segmented",
1303
+ size === "sm" && "wf-segmented--sm",
1304
+ block && "wf-segmented--block"
1305
+ ].filter(Boolean).join(" ");
1306
+ return h(
1307
+ "div",
1308
+ { class: cls, role: "group", "aria-label": ariaLabel },
1309
+ options.map((opt) => h("button", {
1310
+ type: "button",
1311
+ class: `wf-segmented-option${opt.value === value ? " wf-segmented-option--active" : ""}`,
1312
+ "aria-pressed": opt.value === value ? "true" : "false",
1313
+ disabled: opt.disabled || void 0,
1314
+ onClick: opt.disabled ? void 0 : () => onChange?.(opt.value)
1315
+ }, opt.label))
1316
+ );
1317
+ };
1318
+
1282
1319
  // src/components/ProgressBar/ProgressBar.ts
1283
1320
  var ProgressBar = (_init, ctx) => (props) => {
1284
1321
  const { value = 0, max = 100, label, showValue } = props;
@@ -3084,9 +3121,94 @@ var ThemeSwitch = (initProps, ctx) => {
3084
3121
  }, segments);
3085
3122
  };
3086
3123
  };
3124
+
3125
+ // src/components/ToolCallCard/ToolCallCard.ts
3126
+ var stateIcon = { running: "\u2699", ok: "\u2713", error: "\u2715" };
3127
+ var ToolCallCard = (_init, _ctx) => (props) => {
3128
+ const { call, progress, result, renderArgs } = props;
3129
+ const state = result ? result.ok ? "ok" : "error" : "running";
3130
+ const argsNode = renderArgs ? renderArgs(call.args) : h("code", { class: "wf-toolcall-args" }, JSON.stringify(call.args));
3131
+ const progressNode = progress ? [
3132
+ progress.message ? h("div", { class: "wf-toolcall-msg" }, `${progress.message} (${progress.step}/${progress.total})`) : null,
3133
+ h("div", { class: "wf-toolcall-bar", role: "progressbar", "aria-valuenow": progress.step, "aria-valuemax": progress.total }, [
3134
+ h("div", {
3135
+ class: "wf-toolcall-bar-fill",
3136
+ style: { width: `${Math.min(100, progress.step / Math.max(1, progress.total) * 100)}%` }
3137
+ })
3138
+ ])
3139
+ ] : null;
3140
+ const errorNode = result && !result.ok && result.error ? h("div", { class: "wf-toolcall-error" }, `${result.error.code}: ${result.error.message}`) : null;
3141
+ return h("div", { class: `wf-toolcall wf-toolcall--${state}` }, [
3142
+ h("div", { class: "wf-toolcall-header" }, [
3143
+ h("span", { class: `wf-toolcall-icon wf-toolcall-icon--${state}` }, stateIcon[state]),
3144
+ h("span", { class: "wf-toolcall-name" }, call.name)
3145
+ ]),
3146
+ h("div", { class: "wf-toolcall-body" }, [argsNode, progressNode, errorNode].filter(Boolean))
3147
+ ]);
3148
+ };
3149
+
3150
+ // src/components/ApprovalCard/ApprovalCard.ts
3151
+ var statusText = {
3152
+ pending: "\u7B49\u5F85\u5BA1\u6279",
3153
+ approved: "\u5DF2\u6279\u51C6",
3154
+ rejected: "\u5DF2\u62D2\u7EDD",
3155
+ timeout: "\u5BA1\u6279\u8D85\u65F6"
3156
+ };
3157
+ var ApprovalCard = (_init, ctx) => {
3158
+ let note = "";
3159
+ let showNote = false;
3160
+ return (props) => {
3161
+ const { request, status = "pending", onApprove, onReject, renderDetail } = props;
3162
+ const detail = renderDetail ? renderDetail(request) : [
3163
+ h("div", { class: "wf-approval-name" }, request.name),
3164
+ h("code", { class: "wf-approval-args" }, JSON.stringify(request.args))
3165
+ ];
3166
+ const actions = status === "pending" ? h("div", { class: "wf-approval-actions" }, [
3167
+ showNote ? h("div", { class: "wf-approval-note" }, [
3168
+ h("input", {
3169
+ class: "wf-approval-note-input",
3170
+ placeholder: "\u5907\u6CE8\uFF08\u62D2\u7EDD\u539F\u56E0\uFF0C\u5C06\u8FDB\u5165 agent \u4E0A\u4E0B\u6587\uFF09\u2026",
3171
+ value: note,
3172
+ onInput: (e) => {
3173
+ note = e.target.value;
3174
+ }
3175
+ })
3176
+ ]) : null,
3177
+ h("div", { class: "wf-approval-btns" }, [
3178
+ h("button", {
3179
+ class: "wf-btn wf-btn--primary wf-btn--sm",
3180
+ type: "button",
3181
+ onClick: () => onApprove?.()
3182
+ }, "\u5141\u8BB8"),
3183
+ h("button", {
3184
+ class: "wf-btn wf-btn--danger wf-btn--sm",
3185
+ type: "button",
3186
+ onClick: () => {
3187
+ if (!showNote) {
3188
+ showNote = true;
3189
+ ctx.ui.render();
3190
+ return;
3191
+ }
3192
+ onReject?.(note);
3193
+ }
3194
+ }, showNote ? "\u786E\u8BA4\u62D2\u7EDD" : "\u62D2\u7EDD")
3195
+ ])
3196
+ ].filter(Boolean)) : h("div", { class: `wf-approval-status wf-approval-status--${status}` }, statusText[status]);
3197
+ return h("div", { class: `wf-approval wf-approval--${status}` }, [
3198
+ h("div", { class: "wf-approval-header" }, [
3199
+ h("span", { class: "wf-approval-icon" }, status === "pending" ? "\u23F8" : status === "approved" ? "\u2713" : "\u2715"),
3200
+ h("span", { class: "wf-approval-title" }, "\u5DE5\u5177\u5BA1\u6279")
3201
+ ]),
3202
+ h("div", { class: "wf-approval-detail" }, detail),
3203
+ request.reason ? h("div", { class: "wf-approval-reason" }, request.reason) : null,
3204
+ actions
3205
+ ]);
3206
+ };
3207
+ };
3087
3208
  export {
3088
3209
  Accordion,
3089
3210
  Alert,
3211
+ ApprovalCard,
3090
3212
  Avatar,
3091
3213
  Badge,
3092
3214
  Breadcrumb,
@@ -3115,6 +3237,7 @@ export {
3115
3237
  ProgressBar,
3116
3238
  RadioGroup,
3117
3239
  SearchInput,
3240
+ SegmentedControl,
3118
3241
  Select,
3119
3242
  Skeleton,
3120
3243
  Slider,
@@ -3127,6 +3250,7 @@ export {
3127
3250
  Textarea,
3128
3251
  ThemeSwitch,
3129
3252
  Toast,
3253
+ ToolCallCard,
3130
3254
  Tooltip,
3131
3255
  applyTheme,
3132
3256
  confirm,