@reforma/components 0.6.0 → 0.6.1

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.
@@ -1 +1 @@
1
- .MessagePhaseSection_root_Cqd0t1{display:flex;flex-direction:column;gap:var(--spacing-lg);margin-top:var(--spacing-md);width:100%;color:var(--color-text);font-weight:400;font-size:var(--text-md);line-height:var(--leading-md);font-family:Onest,sans-serif;letter-spacing:0}.MessagePhaseSection_root_Cqd0t1:first-child{margin-top:0}.MessagePhaseSection_header_a11Bwm{width:100%;--reforma-button-opacity--hover: 1}.MessagePhaseSection_headerContent_6gdB1H{justify-content:start;gap:var(--spacing-md);width:100%}.MessagePhaseSection_headerTitle_RT3fFZ{display:flex;flex-shrink:0;overflow:hidden}.MessagePhaseSection_headerSummary_HTSuia{display:flex;align-items:center;gap:var(--spacing-md);min-width:0;font-weight:400;font-size:var(--text-sm);line-height:var(--leading-sm);font-family:Onest,sans-serif;letter-spacing:0}.MessagePhaseSection_chevronIcon_7lOW0r{flex-shrink:0;opacity:0;transition:all .15s ease}.MessagePhaseSection_chevronIcon_7lOW0r[aria-expanded=true]{opacity:1}.MessagePhaseSection_header_a11Bwm:hover .MessagePhaseSection_chevronIcon_7lOW0r,.MessagePhaseSection_header_a11Bwm:focus-visible .MessagePhaseSection_chevronIcon_7lOW0r{opacity:1}.MessagePhaseSection_phasePanel_UxIl4o{position:relative;padding-right:var(--spacing-md);width:100%}.MessagePhaseSection_phasePanelContent_xFRBiK{display:flex;flex-direction:column;gap:var(--spacing-lg)}
1
+ .MessagePhaseSection_root_Cqd0t1{display:flex;flex-direction:column;gap:var(--spacing-lg);margin-top:var(--spacing-md);width:100%;color:var(--color-text);font-weight:400;font-size:var(--text-md);line-height:var(--leading-md);font-family:Onest,sans-serif;letter-spacing:0}.MessagePhaseSection_root_Cqd0t1:first-child{margin-top:0}.MessagePhaseSection_header_a11Bwm{width:100%;--reforma-button-opacity--hover: 1}.MessagePhaseSection_headerContent_6gdB1H{justify-content:start;gap:var(--spacing-md);width:100%}.MessagePhaseSection_headerTitle_RT3fFZ{display:flex;flex-shrink:0;overflow:hidden}.MessagePhaseSection_headerSummary_HTSuia{display:flex;align-items:center;gap:var(--spacing-md);min-width:0;font-weight:400;font-size:var(--text-sm);line-height:var(--leading-sm);font-family:Onest,sans-serif;letter-spacing:0}.MessagePhaseSection_chevronIcon_7lOW0r{flex-shrink:0;opacity:0;transition:all .15s ease}.MessagePhaseSection_chevronIcon_7lOW0r[aria-expanded=true]{opacity:1}.MessagePhaseSection_header_a11Bwm:hover .MessagePhaseSection_chevronIcon_7lOW0r,.MessagePhaseSection_header_a11Bwm:focus-visible .MessagePhaseSection_chevronIcon_7lOW0r{opacity:1}.MessagePhaseSection_phasePanel_UxIl4o{position:relative;width:100%}.MessagePhaseSection_phasePanelContent_xFRBiK{display:flex;flex-direction:column;gap:var(--spacing-lg)}
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Assistant turn segments and streaming pending loader.
3
3
  * A finished text answer folds the work into one "Worked for …" disclosure.
4
- * Cancelled turns and turns that do not end on text keep the phase rows open.
4
+ * The latest turn keeps its phase rows open when cancelled or not ending on text;
5
+ * an older one folds them anyway.
5
6
  */
6
7
  export declare const MessageAssistantBody: (({ hideSources, }?: {
7
8
  /** Editor footer owns citations — drop in-body Sources groups. */
@@ -1 +1 @@
1
- {"version":3,"file":"MessageAssistantBody.d.ts","sourceRoot":"","sources":["../../../../src/chat/Message/turn/MessageAssistantBody.tsx"],"names":[],"mappings":"AAmJA;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,uBAE9B;IACC,kEAAkE;IAClE,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;IAFG,kEAAkE;kBACpD,OAAO;;uCAFtB;QACC,kEAAkE;QAClE,WAAW,CAAC,EAAE,OAAO,CAAC;KACzB;CA8EC,CAAC"}
1
+ {"version":3,"file":"MessageAssistantBody.d.ts","sourceRoot":"","sources":["../../../../src/chat/Message/turn/MessageAssistantBody.tsx"],"names":[],"mappings":"AAmJA;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,uBAE9B;IACC,kEAAkE;IAClE,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;IAFG,kEAAkE;kBACpD,OAAO;;uCAFtB;QACC,kEAAkE;QAClE,WAAW,CAAC,EAAE,OAAO,CAAC;KACzB;CAmFC,CAAC"}
@@ -1,18 +1,18 @@
1
- import { ChevronDown as A } from "@reforma/icons/8";
2
- import { cn as C } from "@reforma/lib";
3
- import { observer as T } from "mobx-react-lite";
4
- import { Button as F } from "../../../Button/Button.js";
5
- import { Collapsible as y, useCollapsible as P, CollapsibleTrigger as L, CollapsibleContent as M } from "../../../Collapsible/Collapsible.js";
6
- import { For as W } from "../../../For/For.js";
7
- import { Show as w } from "../../../Show/Show.js";
1
+ import { ChevronDown as C } from "@reforma/icons/8";
2
+ import { cn as T } from "@reforma/lib";
3
+ import { observer as F } from "mobx-react-lite";
4
+ import { Button as y } from "../../../Button/Button.js";
5
+ import { Collapsible as L, useCollapsible as P, CollapsibleTrigger as M, CollapsibleContent as W } from "../../../Collapsible/Collapsible.js";
6
+ import { For as N } from "../../../For/For.js";
7
+ import { Show as u } from "../../../Show/Show.js";
8
8
  import { ConversationPendingLoader as S } from "../../Conversation/ConversationPendingLoader.js";
9
- import { streamingPhaseActiveIndex as N, partsFromSegmentGroups as j } from "../normalize/normalizeMessageParts.js";
10
- import { MessagePhaseSection as B } from "../segments/MessagePhaseSection.js";
9
+ import { streamingPhaseActiveIndex as j, partsFromSegmentGroups as B } from "../normalize/normalizeMessageParts.js";
10
+ import { MessagePhaseSection as E } from "../segments/MessagePhaseSection.js";
11
11
  import v from "./Message.module.css.js";
12
- import { useMessageTurn as E } from "./messageTurnContext.js";
13
- import { splitFinishedWork as $, workedForLabel as D } from "./workedCollapse.js";
14
- import { jsx as r, jsxs as d, Fragment as b } from "react/jsx-runtime";
15
- function G(t) {
12
+ import { useMessageTurn as $ } from "./messageTurnContext.js";
13
+ import { splitFinishedWork as D, workedForLabel as G } from "./workedCollapse.js";
14
+ import { jsx as r, jsxs as p, Fragment as b } from "react/jsx-runtime";
15
+ function I(t) {
16
16
  const e = [];
17
17
  for (const s of t) {
18
18
  const n = s.groups.filter((l) => l.kind !== "sources");
@@ -23,34 +23,34 @@ function G(t) {
23
23
  }
24
24
  return e;
25
25
  }
26
- function k({
26
+ function w({
27
27
  segments: t,
28
28
  messageId: e,
29
29
  isStreaming: s,
30
30
  isLiveMessage: n,
31
31
  turnActive: l,
32
- phaseActiveIndex: u,
32
+ phaseActiveIndex: h,
33
33
  getPhaseStats: o,
34
- lastPart: p,
34
+ lastPart: d,
35
35
  loaderParts: f,
36
36
  markTail: a
37
37
  }) {
38
- return /* @__PURE__ */ r(W, {
38
+ return /* @__PURE__ */ r(N, {
39
39
  each: t,
40
40
  children: (i, c, {
41
41
  isLast: g
42
42
  }) => {
43
43
  const m = a && g;
44
- return /* @__PURE__ */ r(B, {
44
+ return /* @__PURE__ */ r(E, {
45
45
  segment: i,
46
46
  messageId: e,
47
47
  isStreaming: s,
48
48
  isLiveMessage: n,
49
49
  isLastSegment: m,
50
- streamActive: l && (m || c === u),
51
- fileStats: i.layout === "grouped" ? o?.(j(i.groups)) : void 0,
50
+ streamActive: l && (m || c === h),
51
+ fileStats: i.layout === "grouped" ? o?.(B(i.groups)) : void 0,
52
52
  children: m && s ? /* @__PURE__ */ r(S, {
53
- lastPart: p,
53
+ lastPart: d,
54
54
  messageId: e,
55
55
  parts: f
56
56
  }) : null
@@ -58,93 +58,94 @@ function k({
58
58
  }
59
59
  });
60
60
  }
61
- function I({
61
+ function O({
62
62
  label: t
63
63
  }) {
64
64
  const {
65
65
  state: e
66
66
  } = P();
67
- return /* @__PURE__ */ r(L, {
67
+ return /* @__PURE__ */ r(M, {
68
68
  asChild: !0,
69
- children: /* @__PURE__ */ d(F, {
69
+ children: /* @__PURE__ */ p(y, {
70
70
  type: "button",
71
71
  variant: "inline",
72
72
  color: "secondary",
73
73
  className: v.workedTrigger,
74
- children: [t, /* @__PURE__ */ r(A, {
75
- className: C("chevron", "next"),
74
+ children: [t, /* @__PURE__ */ r(C, {
75
+ className: T("chevron", "next"),
76
76
  "aria-expanded": e.isExpanded,
77
77
  "aria-hidden": !0
78
78
  })]
79
79
  })
80
80
  });
81
81
  }
82
- function O({
82
+ function R({
83
83
  children: t
84
84
  }) {
85
85
  const {
86
86
  state: e
87
87
  } = P();
88
- return /* @__PURE__ */ r(w, {
88
+ return /* @__PURE__ */ r(u, {
89
89
  when: e.isExpanded,
90
- children: /* @__PURE__ */ r(M, {
90
+ children: /* @__PURE__ */ r(W, {
91
91
  className: v.workedPanel,
92
92
  children: t
93
93
  })
94
94
  });
95
95
  }
96
- function R({
96
+ function q({
97
97
  label: t,
98
98
  children: e
99
99
  }) {
100
- return /* @__PURE__ */ d(y, {
100
+ return /* @__PURE__ */ p(L, {
101
101
  className: v.worked,
102
102
  defaultOpen: !1,
103
- children: [/* @__PURE__ */ r(I, {
103
+ children: [/* @__PURE__ */ r(O, {
104
104
  label: t
105
- }), /* @__PURE__ */ r(O, {
105
+ }), /* @__PURE__ */ r(R, {
106
106
  children: e
107
107
  })]
108
108
  });
109
109
  }
110
- const te = T(({
110
+ const se = F(({
111
111
  hideSources: t = !1
112
112
  } = {}) => {
113
- const e = E();
113
+ const e = $();
114
114
  if (e?.role !== "assistant" || e.message.role !== "assistant")
115
115
  return null;
116
116
  const {
117
117
  messageId: s,
118
118
  segments: n,
119
119
  lastPart: l,
120
- turnActive: u,
120
+ turnActive: h,
121
121
  isStreaming: o,
122
- isLastMessage: p,
122
+ isLastMessage: d,
123
123
  getPhaseStats: f
124
- } = e, a = t ? G(n) : n, i = e.message.parts, c = e.message.metadata;
124
+ } = e, a = t ? I(n) : n, i = e.message.parts, c = e.message.metadata;
125
125
  if (a.length === 0 && !o)
126
126
  return null;
127
- const g = N(a, o), m = $(a, {
127
+ const g = j(a, o), m = D(a, {
128
128
  streaming: o,
129
- cancelled: c?.stopReason === "cancelled"
130
- }), h = {
129
+ cancelled: c?.stopReason === "cancelled",
130
+ superseded: !d
131
+ }), k = {
131
132
  messageId: s,
132
133
  isStreaming: o,
133
- isLiveMessage: p,
134
- turnActive: u,
134
+ isLiveMessage: d,
135
+ turnActive: h,
135
136
  phaseActiveIndex: g,
136
137
  getPhaseStats: f,
137
138
  lastPart: l,
138
139
  loaderParts: i
139
140
  };
140
- return /* @__PURE__ */ r(w, {
141
+ return /* @__PURE__ */ r(u, {
141
142
  when: m,
142
- fallback: /* @__PURE__ */ d(b, {
143
- children: [/* @__PURE__ */ r(k, {
144
- ...h,
143
+ fallback: /* @__PURE__ */ p(b, {
144
+ children: [/* @__PURE__ */ r(w, {
145
+ ...k,
145
146
  segments: a,
146
147
  markTail: !0
147
- }), /* @__PURE__ */ r(w, {
148
+ }), /* @__PURE__ */ r(u, {
148
149
  when: o && a.length === 0,
149
150
  children: /* @__PURE__ */ r(S, {
150
151
  lastPart: l,
@@ -153,23 +154,26 @@ const te = T(({
153
154
  })
154
155
  })]
155
156
  }),
156
- children: (x) => /* @__PURE__ */ d(b, {
157
- children: [/* @__PURE__ */ r(R, {
158
- label: D(c?.startedAt, c?.endedAt),
159
- children: /* @__PURE__ */ r(k, {
160
- ...h,
157
+ children: (x) => /* @__PURE__ */ p(b, {
158
+ children: [/* @__PURE__ */ r(q, {
159
+ label: G(c?.startedAt, c?.endedAt),
160
+ children: /* @__PURE__ */ r(w, {
161
+ ...k,
161
162
  segments: x.work,
162
163
  markTail: !1
163
164
  })
164
- }), /* @__PURE__ */ r(k, {
165
- ...h,
166
- segments: [x.answer],
167
- markTail: !0
165
+ }), /* @__PURE__ */ r(u, {
166
+ when: x.answer,
167
+ children: (A) => /* @__PURE__ */ r(w, {
168
+ ...k,
169
+ segments: [A],
170
+ markTail: !0
171
+ })
168
172
  })]
169
173
  })
170
174
  });
171
175
  });
172
176
  export {
173
- te as MessageAssistantBody
177
+ se as MessageAssistantBody
174
178
  };
175
179
  //# sourceMappingURL=MessageAssistantBody.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MessageAssistantBody.js","sources":["../../../../src/chat/Message/turn/MessageAssistantBody.tsx"],"sourcesContent":["'use client';\n\nimport { ChevronDown } from '@reforma/icons/8';\nimport { cn } from '@reforma/lib';\nimport { observer } from 'mobx-react-lite';\n\nimport { Button } from '../../../Button';\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n useCollapsible,\n} from '../../../Collapsible';\nimport { For } from '../../../For';\nimport { Show } from '../../../Show';\nimport { ConversationPendingLoader } from '../../Conversation/ConversationPendingLoader';\nimport { partsFromSegmentGroups, streamingPhaseActiveIndex } from '../normalize/normalizeMessageParts';\nimport { MessagePhaseSection } from '../segments';\nimport styles from './Message.module.css';\nimport { useMessageTurn } from './messageTurnContext';\nimport { splitFinishedWork, workedForLabel } from './workedCollapse';\n\nimport type { ReactNode } from 'react';\nimport type { MessageSegment } from '../normalize/normalizeMessageParts';\nimport type { ReformaUIMessagePart } from '../types';\nimport type { GetPhaseStats } from './messageTurnContext';\n\nfunction segmentsWithoutSources(segments: readonly MessageSegment[]): MessageSegment[] {\n const next: MessageSegment[] = [];\n\n for (const segment of segments) {\n const groups = segment.groups.filter(group => group.kind !== 'sources');\n\n if (groups.length > 0) {\n next.push({ ...segment, groups });\n }\n }\n\n return next;\n}\n\ntype SegmentListProps = {\n segments: readonly MessageSegment[];\n messageId: string;\n isStreaming: boolean;\n isLiveMessage: boolean;\n turnActive: boolean;\n phaseActiveIndex: number;\n getPhaseStats: GetPhaseStats | undefined;\n lastPart: ReformaUIMessagePart | undefined;\n loaderParts: readonly ReformaUIMessagePart[];\n /** Work under the spoiler is not the message tail, so its phases stay closed. */\n markTail: boolean;\n};\n\nfunction AssistantSegments({\n segments,\n messageId,\n isStreaming,\n isLiveMessage,\n turnActive,\n phaseActiveIndex,\n getPhaseStats,\n lastPart,\n loaderParts,\n markTail,\n}: SegmentListProps) {\n return (\n <For each={ segments }>\n { (segment, segIndex, { isLast }) => {\n const isLastSegment = markTail && isLast;\n\n return (\n <MessagePhaseSection\n key={ `${ messageId }-${ segment.index }` }\n segment={ segment }\n messageId={ messageId }\n isStreaming={ isStreaming }\n isLiveMessage={ isLiveMessage }\n isLastSegment={ isLastSegment }\n streamActive={ turnActive && (isLastSegment || segIndex === phaseActiveIndex) }\n fileStats={\n segment.layout === 'grouped'\n ? getPhaseStats?.(partsFromSegmentGroups(segment.groups))\n : undefined\n }\n >\n { isLastSegment && isStreaming\n ? (\n <ConversationPendingLoader\n lastPart={ lastPart }\n messageId={ messageId }\n parts={ loaderParts }\n />\n )\n : null }\n </MessagePhaseSection>\n );\n } }\n </For>\n );\n}\n\nfunction WorkedTrigger({ label }: { label: string }) {\n const { state } = useCollapsible();\n\n return (\n <CollapsibleTrigger asChild>\n <Button\n type=\"button\"\n variant=\"inline\"\n color=\"secondary\"\n className={ styles.workedTrigger }\n >\n { label }\n <ChevronDown\n className={ cn('chevron', 'next') }\n aria-expanded={ state.isExpanded }\n aria-hidden\n />\n </Button>\n </CollapsibleTrigger>\n );\n}\n\n/** Closed panel stays out of the DOM. `hidden=until-found` is still a flex item and keeps `.worked`'s gap. */\nfunction WorkedPanel({ children }: { children: ReactNode }) {\n const { state } = useCollapsible();\n\n return (\n <Show when={ state.isExpanded }>\n <CollapsibleContent className={ styles.workedPanel }>\n { children }\n </CollapsibleContent>\n </Show>\n );\n}\n\nfunction WorkedFor({ label, children }: { label: string; children: ReactNode }) {\n return (\n <Collapsible className={ styles.worked } defaultOpen={ false }>\n <WorkedTrigger label={ label } />\n <WorkedPanel>{ children }</WorkedPanel>\n </Collapsible>\n );\n}\n\n/**\n * Assistant turn segments and streaming pending loader.\n * A finished text answer folds the work into one \"Worked for …\" disclosure.\n * Cancelled turns and turns that do not end on text keep the phase rows open.\n */\nexport const MessageAssistantBody = observer(({\n hideSources = false,\n}: {\n /** Editor footer owns citations — drop in-body Sources groups. */\n hideSources?: boolean;\n} = {}) => {\n const turn = useMessageTurn();\n\n if (turn?.role !== 'assistant' || turn.message.role !== 'assistant') {\n return null;\n }\n\n const {\n messageId,\n segments: rawSegments,\n lastPart,\n turnActive,\n isStreaming,\n isLastMessage,\n getPhaseStats,\n } = turn;\n const segments = hideSources ? segmentsWithoutSources(rawSegments) : rawSegments;\n const loaderParts = turn.message.parts;\n const metadata = turn.message.metadata;\n\n if (segments.length === 0 && !isStreaming) {\n return null;\n }\n\n const phaseActiveIndex = streamingPhaseActiveIndex(segments, isStreaming);\n const finished = splitFinishedWork(segments, {\n streaming: isStreaming,\n cancelled: metadata?.stopReason === 'cancelled',\n });\n const listProps = {\n messageId,\n isStreaming,\n isLiveMessage: isLastMessage,\n turnActive,\n phaseActiveIndex,\n getPhaseStats,\n lastPart,\n loaderParts,\n };\n\n return (\n <Show\n when={ finished }\n fallback={ (\n <>\n <AssistantSegments\n { ...listProps }\n segments={ segments }\n markTail\n />\n <Show when={ isStreaming && segments.length === 0 }>\n <ConversationPendingLoader\n lastPart={ lastPart }\n messageId={ messageId }\n parts={ loaderParts }\n />\n </Show>\n </>\n ) }\n >\n { split => (\n <>\n <WorkedFor label={ workedForLabel(metadata?.startedAt, metadata?.endedAt) }>\n <AssistantSegments\n { ...listProps }\n segments={ split.work }\n markTail={ false }\n />\n </WorkedFor>\n <AssistantSegments\n { ...listProps }\n segments={ [split.answer] }\n markTail\n />\n </>\n ) }\n </Show>\n );\n});\n"],"names":["segmentsWithoutSources","segments","next","segment","groups","group","AssistantSegments","messageId","isStreaming","isLiveMessage","turnActive","phaseActiveIndex","getPhaseStats","lastPart","loaderParts","markTail","For","segIndex","isLast","isLastSegment","MessagePhaseSection","partsFromSegmentGroups","_jsx","ConversationPendingLoader","WorkedTrigger","label","state","useCollapsible","CollapsibleTrigger","Button","styles","ChevronDown","cn","WorkedPanel","children","Show","CollapsibleContent","WorkedFor","Collapsible","MessageAssistantBody","observer","hideSources","turn","useMessageTurn","rawSegments","isLastMessage","metadata","streamingPhaseActiveIndex","finished","splitFinishedWork","listProps","_Fragment","split","_jsxs","workedForLabel"],"mappings":"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"}
1
+ {"version":3,"file":"MessageAssistantBody.js","sources":["../../../../src/chat/Message/turn/MessageAssistantBody.tsx"],"sourcesContent":["'use client';\n\nimport { ChevronDown } from '@reforma/icons/8';\nimport { cn } from '@reforma/lib';\nimport { observer } from 'mobx-react-lite';\n\nimport { Button } from '../../../Button';\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n useCollapsible,\n} from '../../../Collapsible';\nimport { For } from '../../../For';\nimport { Show } from '../../../Show';\nimport { ConversationPendingLoader } from '../../Conversation/ConversationPendingLoader';\nimport { partsFromSegmentGroups, streamingPhaseActiveIndex } from '../normalize/normalizeMessageParts';\nimport { MessagePhaseSection } from '../segments';\nimport styles from './Message.module.css';\nimport { useMessageTurn } from './messageTurnContext';\nimport { splitFinishedWork, workedForLabel } from './workedCollapse';\n\nimport type { ReactNode } from 'react';\nimport type { MessageSegment } from '../normalize/normalizeMessageParts';\nimport type { ReformaUIMessagePart } from '../types';\nimport type { GetPhaseStats } from './messageTurnContext';\n\nfunction segmentsWithoutSources(segments: readonly MessageSegment[]): MessageSegment[] {\n const next: MessageSegment[] = [];\n\n for (const segment of segments) {\n const groups = segment.groups.filter(group => group.kind !== 'sources');\n\n if (groups.length > 0) {\n next.push({ ...segment, groups });\n }\n }\n\n return next;\n}\n\ntype SegmentListProps = {\n segments: readonly MessageSegment[];\n messageId: string;\n isStreaming: boolean;\n isLiveMessage: boolean;\n turnActive: boolean;\n phaseActiveIndex: number;\n getPhaseStats: GetPhaseStats | undefined;\n lastPart: ReformaUIMessagePart | undefined;\n loaderParts: readonly ReformaUIMessagePart[];\n /** Work under the spoiler is not the message tail, so its phases stay closed. */\n markTail: boolean;\n};\n\nfunction AssistantSegments({\n segments,\n messageId,\n isStreaming,\n isLiveMessage,\n turnActive,\n phaseActiveIndex,\n getPhaseStats,\n lastPart,\n loaderParts,\n markTail,\n}: SegmentListProps) {\n return (\n <For each={ segments }>\n { (segment, segIndex, { isLast }) => {\n const isLastSegment = markTail && isLast;\n\n return (\n <MessagePhaseSection\n key={ `${ messageId }-${ segment.index }` }\n segment={ segment }\n messageId={ messageId }\n isStreaming={ isStreaming }\n isLiveMessage={ isLiveMessage }\n isLastSegment={ isLastSegment }\n streamActive={ turnActive && (isLastSegment || segIndex === phaseActiveIndex) }\n fileStats={\n segment.layout === 'grouped'\n ? getPhaseStats?.(partsFromSegmentGroups(segment.groups))\n : undefined\n }\n >\n { isLastSegment && isStreaming\n ? (\n <ConversationPendingLoader\n lastPart={ lastPart }\n messageId={ messageId }\n parts={ loaderParts }\n />\n )\n : null }\n </MessagePhaseSection>\n );\n } }\n </For>\n );\n}\n\nfunction WorkedTrigger({ label }: { label: string }) {\n const { state } = useCollapsible();\n\n return (\n <CollapsibleTrigger asChild>\n <Button\n type=\"button\"\n variant=\"inline\"\n color=\"secondary\"\n className={ styles.workedTrigger }\n >\n { label }\n <ChevronDown\n className={ cn('chevron', 'next') }\n aria-expanded={ state.isExpanded }\n aria-hidden\n />\n </Button>\n </CollapsibleTrigger>\n );\n}\n\n/** Closed panel stays out of the DOM. `hidden=until-found` is still a flex item and keeps `.worked`'s gap. */\nfunction WorkedPanel({ children }: { children: ReactNode }) {\n const { state } = useCollapsible();\n\n return (\n <Show when={ state.isExpanded }>\n <CollapsibleContent className={ styles.workedPanel }>\n { children }\n </CollapsibleContent>\n </Show>\n );\n}\n\nfunction WorkedFor({ label, children }: { label: string; children: ReactNode }) {\n return (\n <Collapsible className={ styles.worked } defaultOpen={ false }>\n <WorkedTrigger label={ label } />\n <WorkedPanel>{ children }</WorkedPanel>\n </Collapsible>\n );\n}\n\n/**\n * Assistant turn segments and streaming pending loader.\n * A finished text answer folds the work into one \"Worked for …\" disclosure.\n * The latest turn keeps its phase rows open when cancelled or not ending on text;\n * an older one folds them anyway.\n */\nexport const MessageAssistantBody = observer(({\n hideSources = false,\n}: {\n /** Editor footer owns citations — drop in-body Sources groups. */\n hideSources?: boolean;\n} = {}) => {\n const turn = useMessageTurn();\n\n if (turn?.role !== 'assistant' || turn.message.role !== 'assistant') {\n return null;\n }\n\n const {\n messageId,\n segments: rawSegments,\n lastPart,\n turnActive,\n isStreaming,\n isLastMessage,\n getPhaseStats,\n } = turn;\n const segments = hideSources ? segmentsWithoutSources(rawSegments) : rawSegments;\n const loaderParts = turn.message.parts;\n const metadata = turn.message.metadata;\n\n if (segments.length === 0 && !isStreaming) {\n return null;\n }\n\n const phaseActiveIndex = streamingPhaseActiveIndex(segments, isStreaming);\n const finished = splitFinishedWork(segments, {\n streaming: isStreaming,\n cancelled: metadata?.stopReason === 'cancelled',\n superseded: !isLastMessage,\n });\n const listProps = {\n messageId,\n isStreaming,\n isLiveMessage: isLastMessage,\n turnActive,\n phaseActiveIndex,\n getPhaseStats,\n lastPart,\n loaderParts,\n };\n\n return (\n <Show\n when={ finished }\n fallback={ (\n <>\n <AssistantSegments\n { ...listProps }\n segments={ segments }\n markTail\n />\n <Show when={ isStreaming && segments.length === 0 }>\n <ConversationPendingLoader\n lastPart={ lastPart }\n messageId={ messageId }\n parts={ loaderParts }\n />\n </Show>\n </>\n ) }\n >\n { split => (\n <>\n <WorkedFor label={ workedForLabel(metadata?.startedAt, metadata?.endedAt) }>\n <AssistantSegments\n { ...listProps }\n segments={ split.work }\n markTail={ false }\n />\n </WorkedFor>\n <Show when={ split.answer }>\n { answer => (\n <AssistantSegments\n { ...listProps }\n segments={ [answer] }\n markTail\n />\n ) }\n </Show>\n </>\n ) }\n </Show>\n );\n});\n"],"names":["segmentsWithoutSources","segments","next","segment","groups","group","AssistantSegments","messageId","isStreaming","isLiveMessage","turnActive","phaseActiveIndex","getPhaseStats","lastPart","loaderParts","markTail","For","segIndex","isLast","isLastSegment","MessagePhaseSection","partsFromSegmentGroups","_jsx","ConversationPendingLoader","WorkedTrigger","label","state","useCollapsible","CollapsibleTrigger","Button","styles","ChevronDown","cn","WorkedPanel","children","Show","CollapsibleContent","WorkedFor","Collapsible","MessageAssistantBody","observer","hideSources","turn","useMessageTurn","rawSegments","isLastMessage","metadata","streamingPhaseActiveIndex","finished","splitFinishedWork","listProps","_Fragment","split","_jsxs","workedForLabel","answer"],"mappings":"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"}
@@ -1,15 +1,19 @@
1
1
  import type { MessageSegment } from '../normalize/normalizeMessageParts';
2
2
  export type FinishedWorkSplit = {
3
3
  work: readonly MessageSegment[];
4
- answer: MessageSegment;
4
+ /** Null when a superseded turn has no text answer: everything folds. */
5
+ answer: MessageSegment | null;
5
6
  };
6
7
  /**
7
8
  * Finished turn whose last visible part is text: tuck every segment before that
8
- * answer under one disclosure. Streaming, cancelled, and "still waiting" turns stay open.
9
+ * answer under one disclosure. Streaming and "still waiting" turns stay open.
10
+ * The latest turn also stays open when cancelled or not ending on text; once a
11
+ * later message supersedes it, its work folds anyway.
9
12
  */
10
13
  export declare function splitFinishedWork(segments: readonly MessageSegment[], flags: {
11
14
  streaming: boolean;
12
15
  cancelled: boolean;
16
+ superseded: boolean;
13
17
  }): FinishedWorkSplit | null;
14
18
  /** "Worked for 25s" / "Worked for 2m". "Worked" when the turn has no timestamps. */
15
19
  export declare function workedForLabel(startedAt: number | undefined, endedAt: number | undefined): string;
@@ -1 +1 @@
1
- {"version":3,"file":"workedCollapse.d.ts","sourceRoot":"","sources":["../../../../src/chat/Message/turn/workedCollapse.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE,MAAM,MAAM,iBAAiB,GAAG;IAC5B,IAAI,EAAE,SAAS,cAAc,EAAE,CAAC;IAChC,MAAM,EAAE,cAAc,CAAC;CAC1B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,iBAAiB,CAC7B,QAAQ,EAAE,SAAS,cAAc,EAAE,EACnC,KAAK,EAAE;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,GAClD,iBAAiB,GAAG,IAAI,CAyB1B;AAED,oFAAoF;AACpF,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAgCjG"}
1
+ {"version":3,"file":"workedCollapse.d.ts","sourceRoot":"","sources":["../../../../src/chat/Message/turn/workedCollapse.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE,MAAM,MAAM,iBAAiB,GAAG;IAC5B,IAAI,EAAE,SAAS,cAAc,EAAE,CAAC;IAChC,wEAAwE;IACxE,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;CACjC,CAAC;AAYF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC7B,QAAQ,EAAE,SAAS,cAAc,EAAE,EACnC,KAAK,EAAE;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,GACvE,iBAAiB,GAAG,IAAI,CAgB1B;AAED,oFAAoF;AACpF,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAgCjG"}
@@ -1,31 +1,37 @@
1
1
  import { isTextUIPart as l } from "@reforma/types";
2
2
  import { segmentRequiresUserAction as s, lastVisiblePartFromGroups as f } from "../normalize/normalizeMessageParts.js";
3
- function a(r, e) {
4
- if (e.streaming || e.cancelled || r.length < 2 || r.some((i) => s(i.groups)))
5
- return null;
6
- const n = r[r.length - 1];
7
- if (!n || n.layout !== "ungrouped")
3
+ function c(r) {
4
+ if (!r || r.layout !== "ungrouped")
5
+ return !1;
6
+ const o = f(r.groups);
7
+ return !!o && l(o) && o.state !== "streaming" && !!o.text.trim();
8
+ }
9
+ function h(r, o) {
10
+ if (o.streaming || o.cancelled && !o.superseded || r.some((e) => s(e.groups)))
8
11
  return null;
9
- const o = f(n.groups);
10
- return !o || !l(o) || o.state === "streaming" || !o.text.trim() ? null : {
12
+ const n = r.at(-1);
13
+ return c(n) ? r.length < 2 ? null : {
11
14
  work: r.slice(0, -1),
12
15
  answer: n
13
- };
16
+ } : o.superseded && r.length > 0 ? {
17
+ work: r,
18
+ answer: null
19
+ } : null;
14
20
  }
15
- function h(r, e) {
16
- if (r == null || e == null || !Number.isFinite(r) || !Number.isFinite(e) || e < r)
21
+ function k(r, o) {
22
+ if (r == null || o == null || !Number.isFinite(r) || !Number.isFinite(o) || o < r)
17
23
  return "Worked";
18
- const n = Math.max(1, Math.round((e - r) / 1e3));
24
+ const n = Math.max(1, Math.round((o - r) / 1e3));
19
25
  if (n < 60)
20
26
  return `Worked for ${n}s`;
21
- const o = Math.floor(n / 60), i = n % 60;
22
- if (o < 60)
23
- return i === 0 ? `Worked for ${o}m` : `Worked for ${o}m ${i}s`;
24
- const t = Math.floor(o / 60), u = o % 60;
25
- return u === 0 ? `Worked for ${t}h` : `Worked for ${t}h ${u}m`;
27
+ const e = Math.floor(n / 60), u = n % 60;
28
+ if (e < 60)
29
+ return u === 0 ? `Worked for ${e}m` : `Worked for ${e}m ${u}s`;
30
+ const i = Math.floor(e / 60), t = e % 60;
31
+ return t === 0 ? `Worked for ${i}h` : `Worked for ${i}h ${t}m`;
26
32
  }
27
33
  export {
28
- a as splitFinishedWork,
29
- h as workedForLabel
34
+ h as splitFinishedWork,
35
+ k as workedForLabel
30
36
  };
31
37
  //# sourceMappingURL=workedCollapse.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"workedCollapse.js","sources":["../../../../src/chat/Message/turn/workedCollapse.ts"],"sourcesContent":["import { isTextUIPart } from '@reforma/types';\n\nimport {\n lastVisiblePartFromGroups,\n segmentRequiresUserAction,\n} from '../normalize/normalizeMessageParts';\n\nimport type { MessageSegment } from '../normalize/normalizeMessageParts';\n\nexport type FinishedWorkSplit = {\n work: readonly MessageSegment[];\n answer: MessageSegment;\n};\n\n/**\n * Finished turn whose last visible part is text: tuck every segment before that\n * answer under one disclosure. Streaming, cancelled, and \"still waiting\" turns stay open.\n */\nexport function splitFinishedWork(\n segments: readonly MessageSegment[],\n flags: { streaming: boolean; cancelled: boolean }\n): FinishedWorkSplit | null {\n if (flags.streaming || flags.cancelled || segments.length < 2) {\n return null;\n }\n\n if (segments.some(segment => segmentRequiresUserAction(segment.groups))) {\n return null;\n }\n\n const answer = segments[segments.length - 1];\n\n if (!answer || answer.layout !== 'ungrouped') {\n return null;\n }\n\n const tail = lastVisiblePartFromGroups(answer.groups);\n\n if (!tail || !isTextUIPart(tail) || tail.state === 'streaming' || !tail.text.trim()) {\n return null;\n }\n\n return {\n work: segments.slice(0, -1),\n answer,\n };\n}\n\n/** \"Worked for 25s\" / \"Worked for 2m\". \"Worked\" when the turn has no timestamps. */\nexport function workedForLabel(startedAt: number | undefined, endedAt: number | undefined): string {\n if (\n startedAt == null\n || endedAt == null\n || !Number.isFinite(startedAt)\n || !Number.isFinite(endedAt)\n || endedAt < startedAt\n ) {\n return 'Worked';\n }\n\n const seconds = Math.max(1, Math.round((endedAt - startedAt) / 1000));\n\n if (seconds < 60) {\n return `Worked for ${ seconds }s`;\n }\n\n const minutes = Math.floor(seconds / 60);\n const remSeconds = seconds % 60;\n\n if (minutes < 60) {\n return remSeconds === 0\n ? `Worked for ${ minutes }m`\n : `Worked for ${ minutes }m ${ remSeconds }s`;\n }\n\n const hours = Math.floor(minutes / 60);\n const remMinutes = minutes % 60;\n\n return remMinutes === 0\n ? `Worked for ${ hours }h`\n : `Worked for ${ hours }h ${ remMinutes }m`;\n}\n"],"names":["splitFinishedWork","segments","flags","segment","segmentRequiresUserAction","answer","tail","lastVisiblePartFromGroups","isTextUIPart","workedForLabel","startedAt","endedAt","seconds","minutes","remSeconds","hours","remMinutes"],"mappings":";;AAMO,SAASA,EAAkBC,GAAUC,GAAO;AAIjD,MAHIA,EAAM,aAAaA,EAAM,aAAaD,EAAS,SAAS,KAGxDA,EAAS,KAAK,CAAAE,MAAWC,EAA0BD,EAAQ,MAAM,CAAC;AACpE,WAAO;AAET,QAAME,IAASJ,EAASA,EAAS,SAAS,CAAC;AAC3C,MAAI,CAACI,KAAUA,EAAO,WAAW;AAC/B,WAAO;AAET,QAAMC,IAAOC,EAA0BF,EAAO,MAAM;AACpD,SAAI,CAACC,KAAQ,CAACE,EAAaF,CAAI,KAAKA,EAAK,UAAU,eAAe,CAACA,EAAK,KAAK,SACpE,OAEF;AAAA,IACL,MAAML,EAAS,MAAM,GAAG,EAAE;AAAA,IAC1B,QAAAI;AAAA,EAAA;AAEJ;AAGO,SAASI,EAAeC,GAAWC,GAAS;AACjD,MAAID,KAAa,QAAQC,KAAW,QAAQ,CAAC,OAAO,SAASD,CAAS,KAAK,CAAC,OAAO,SAASC,CAAO,KAAKA,IAAUD;AAChH,WAAO;AAET,QAAME,IAAU,KAAK,IAAI,GAAG,KAAK,OAAOD,IAAUD,KAAa,GAAI,CAAC;AACpE,MAAIE,IAAU;AACZ,WAAO,cAAcA,CAAO;AAE9B,QAAMC,IAAU,KAAK,MAAMD,IAAU,EAAE,GACjCE,IAAaF,IAAU;AAC7B,MAAIC,IAAU;AACZ,WAAOC,MAAe,IAAI,cAAcD,CAAO,MAAM,cAAcA,CAAO,KAAKC,CAAU;AAE3F,QAAMC,IAAQ,KAAK,MAAMF,IAAU,EAAE,GAC/BG,IAAaH,IAAU;AAC7B,SAAOG,MAAe,IAAI,cAAcD,CAAK,MAAM,cAAcA,CAAK,KAAKC,CAAU;AACvF;"}
1
+ {"version":3,"file":"workedCollapse.js","sources":["../../../../src/chat/Message/turn/workedCollapse.ts"],"sourcesContent":["import { isTextUIPart } from '@reforma/types';\n\nimport {\n lastVisiblePartFromGroups,\n segmentRequiresUserAction,\n} from '../normalize/normalizeMessageParts';\n\nimport type { MessageSegment } from '../normalize/normalizeMessageParts';\n\nexport type FinishedWorkSplit = {\n work: readonly MessageSegment[];\n /** Null when a superseded turn has no text answer: everything folds. */\n answer: MessageSegment | null;\n};\n\nfunction isTextAnswer(segment: MessageSegment | undefined): segment is MessageSegment {\n if (!segment || segment.layout !== 'ungrouped') {\n return false;\n }\n\n const tail = lastVisiblePartFromGroups(segment.groups);\n\n return !!tail && isTextUIPart(tail) && tail.state !== 'streaming' && !!tail.text.trim();\n}\n\n/**\n * Finished turn whose last visible part is text: tuck every segment before that\n * answer under one disclosure. Streaming and \"still waiting\" turns stay open.\n * The latest turn also stays open when cancelled or not ending on text; once a\n * later message supersedes it, its work folds anyway.\n */\nexport function splitFinishedWork(\n segments: readonly MessageSegment[],\n flags: { streaming: boolean; cancelled: boolean; superseded: boolean }\n): FinishedWorkSplit | null {\n if (flags.streaming || (flags.cancelled && !flags.superseded)) {\n return null;\n }\n\n if (segments.some(segment => segmentRequiresUserAction(segment.groups))) {\n return null;\n }\n\n const answer = segments.at(-1);\n\n if (isTextAnswer(answer)) {\n return segments.length < 2 ? null : { work: segments.slice(0, -1), answer };\n }\n\n return flags.superseded && segments.length > 0 ? { work: segments, answer: null } : null;\n}\n\n/** \"Worked for 25s\" / \"Worked for 2m\". \"Worked\" when the turn has no timestamps. */\nexport function workedForLabel(startedAt: number | undefined, endedAt: number | undefined): string {\n if (\n startedAt == null\n || endedAt == null\n || !Number.isFinite(startedAt)\n || !Number.isFinite(endedAt)\n || endedAt < startedAt\n ) {\n return 'Worked';\n }\n\n const seconds = Math.max(1, Math.round((endedAt - startedAt) / 1000));\n\n if (seconds < 60) {\n return `Worked for ${ seconds }s`;\n }\n\n const minutes = Math.floor(seconds / 60);\n const remSeconds = seconds % 60;\n\n if (minutes < 60) {\n return remSeconds === 0\n ? `Worked for ${ minutes }m`\n : `Worked for ${ minutes }m ${ remSeconds }s`;\n }\n\n const hours = Math.floor(minutes / 60);\n const remMinutes = minutes % 60;\n\n return remMinutes === 0\n ? `Worked for ${ hours }h`\n : `Worked for ${ hours }h ${ remMinutes }m`;\n}\n"],"names":["isTextAnswer","segment","tail","lastVisiblePartFromGroups","isTextUIPart","splitFinishedWork","segments","flags","segmentRequiresUserAction","answer","workedForLabel","startedAt","endedAt","seconds","minutes","remSeconds","hours","remMinutes"],"mappings":";;AAEA,SAASA,EAAaC,GAAS;AAC7B,MAAI,CAACA,KAAWA,EAAQ,WAAW;AACjC,WAAO;AAET,QAAMC,IAAOC,EAA0BF,EAAQ,MAAM;AACrD,SAAO,CAAC,CAACC,KAAQE,EAAaF,CAAI,KAAKA,EAAK,UAAU,eAAe,CAAC,CAACA,EAAK,KAAK,KAAA;AACnF;AAQO,SAASG,EAAkBC,GAAUC,GAAO;AAIjD,MAHIA,EAAM,aAAaA,EAAM,aAAa,CAACA,EAAM,cAG7CD,EAAS,KAAK,CAAAL,MAAWO,EAA0BP,EAAQ,MAAM,CAAC;AACpE,WAAO;AAET,QAAMQ,IAASH,EAAS,GAAG,EAAE;AAC7B,SAAIN,EAAaS,CAAM,IACdH,EAAS,SAAS,IAAI,OAAO;AAAA,IAClC,MAAMA,EAAS,MAAM,GAAG,EAAE;AAAA,IAC1B,QAAAG;AAAA,EAAA,IAGGF,EAAM,cAAcD,EAAS,SAAS,IAAI;AAAA,IAC/C,MAAMA;AAAA,IACN,QAAQ;AAAA,EAAA,IACN;AACN;AAGO,SAASI,EAAeC,GAAWC,GAAS;AACjD,MAAID,KAAa,QAAQC,KAAW,QAAQ,CAAC,OAAO,SAASD,CAAS,KAAK,CAAC,OAAO,SAASC,CAAO,KAAKA,IAAUD;AAChH,WAAO;AAET,QAAME,IAAU,KAAK,IAAI,GAAG,KAAK,OAAOD,IAAUD,KAAa,GAAI,CAAC;AACpE,MAAIE,IAAU;AACZ,WAAO,cAAcA,CAAO;AAE9B,QAAMC,IAAU,KAAK,MAAMD,IAAU,EAAE,GACjCE,IAAaF,IAAU;AAC7B,MAAIC,IAAU;AACZ,WAAOC,MAAe,IAAI,cAAcD,CAAO,MAAM,cAAcA,CAAO,KAAKC,CAAU;AAE3F,QAAMC,IAAQ,KAAK,MAAMF,IAAU,EAAE,GAC/BG,IAAaH,IAAU;AAC7B,SAAOG,MAAe,IAAI,cAAcD,CAAK,MAAM,cAAcA,CAAK,KAAKC,CAAU;AACvF;"}
@@ -165,7 +165,7 @@ function ee({
165
165
  when: r,
166
166
  fallback: /* @__PURE__ */ o(m, {
167
167
  className: h.title,
168
- color: a ? "danger" : void 0,
168
+ color: a ? "secondary" : void 0,
169
169
  children: l
170
170
  }),
171
171
  children: /* @__PURE__ */ o(M, {
@@ -220,20 +220,20 @@ const Ce = U(({
220
220
  } = V(e.state, a);
221
221
  if (!B(e) || F(e) !== "Agent")
222
222
  return null;
223
- const u = W(e), s = e.state === "output-available" ? H(e.output) : void 0, f = u.useFailureLabel || !!s, v = R(e), A = p(v), L = e.state === "output-available" && !!e.output?.jobId, b = J(v, L), P = X(r), $ = Z({
223
+ const u = W(e), s = e.state === "output-available" ? H(e.output) : void 0, f = u.useFailureLabel || !!s, v = R(e), A = p(v), L = e.state === "output-available" && !!e.output?.jobId, y = J(v, L), P = X(r), $ = Z({
224
224
  live: c,
225
225
  failed: f,
226
226
  stalled: l,
227
227
  needsUser: a,
228
228
  activity: P
229
- }), y = i(e.output?.childThreadId) ?? i(r?.childThreadId), k = !!(y && n && !f), E = s ?? u.problemText ?? u.tooltip;
229
+ }), b = i(e.output?.childThreadId) ?? i(r?.childThreadId), k = !!(b && n && !f), E = s ?? u.problemText ?? u.tooltip;
230
230
  return /* @__PURE__ */ o(ee, {
231
231
  activity: $,
232
232
  errorText: E,
233
- onOpen: k ? () => n?.(y, A) : void 0,
233
+ onOpen: k ? () => n?.(b, A) : void 0,
234
234
  running: c && !f && !l,
235
235
  stalled: l,
236
- title: c && !f && !l ? b.live : b.done
236
+ title: c && !f && !l ? y.live : y.done
237
237
  });
238
238
  });
239
239
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"AgentTool.js","sources":["../../../../src/chat/Tool/agent/AgentTool.tsx"],"sourcesContent":["import { Message } from '@reforma/icons/14';\nimport { ProgressLoader } from '@reforma/icons/assets';\nimport { basename, humanizeString, plural } from '@reforma/lib';\nimport { getToolName, isToolUIPart } from '@reforma/types';\nimport { observer } from 'mobx-react-lite';\n\nimport { Box } from '../../../Box';\nimport { Pressable } from '../../../Pressable';\nimport { Shimmer } from '../../../Shimmer';\nimport { Show } from '../../../Show';\nimport { Text } from '../../../Text';\nimport { useChat } from '../../ChatContext';\nimport { useMessageTurn } from '../../Message/turn/messageTurnContext';\nimport { getToolFailureRowState, TOOL_INTERRUPTED_MESSAGE } from '../helpers';\nimport { ToolShellCard } from '../shell';\nimport { useToolProgress } from '../shell/toolProgress';\nimport shellStyles from '../shell/ToolShellCard.module.css';\nimport styles from './AgentTool.module.css';\n\nimport type { AgentToolInput, AgentToolOutput } from '@reforma/types/sandbox';\nimport type { ToolRendererProps } from '../types';\n\n/** User-facing labels for known subagent ids (fallback: capitalized id). */\nconst SUBAGENT_LABELS: Record<string, string> = {\n research: 'Explore',\n shell: 'Shell',\n verify: 'Verify',\n plan: 'Plan',\n};\n\nconst SUBAGENT_LIVE: Record<string, string> = {\n research: 'Exploring',\n shell: 'Running',\n verify: 'Verifying',\n plan: 'Planning',\n};\n\nfunction agentErrorFromOutput(output: unknown): string | undefined {\n if (output == null || typeof output !== 'object') {\n return undefined;\n }\n\n const error = (output as AgentToolOutput).error;\n\n return (typeof error === 'string' && error.trim().length > 0) ? error.trim() : undefined;\n}\n\nfunction agentNameFromPart(part: { input?: unknown; output?: unknown }): string | undefined {\n for (const source of [part.output, part.input]) {\n if (source == null || typeof source !== 'object') {\n continue;\n }\n\n const name = (source as AgentToolInput | AgentToolOutput).name;\n\n if (typeof name === 'string' && name.trim().length > 0) {\n return name.trim();\n }\n }\n\n return undefined;\n}\n\nfunction subagentLabel(name: string | undefined): string {\n const id = name?.trim() || 'research';\n const mapped = SUBAGENT_LABELS[id];\n\n if (mapped) {\n return mapped;\n }\n\n return id.charAt(0).toUpperCase() + id.slice(1);\n}\n\nfunction agentPhaseCopy(name: string | undefined, background: boolean): { live: string; done: string } {\n const id = name?.trim() || 'research';\n const label = subagentLabel(name);\n\n return {\n live: SUBAGENT_LIVE[id] ?? `Running ${ label }`,\n done: background ? `Started ${ label } agent in background` : `Ran ${ label } agent`,\n };\n}\n\nfunction fieldString(value: unknown): string | undefined {\n if (typeof value !== 'string') {\n return undefined;\n }\n\n const trimmed = value.trim();\n\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction fileLabel(input: Record<string, unknown>): string | undefined {\n const path = fieldString(input.path);\n\n if (path) {\n return basename(path);\n }\n\n if (!Array.isArray(input.paths)) {\n return undefined;\n }\n\n const files = input.paths.filter((item): item is string => typeof item === 'string' && item.trim().length > 0);\n\n if (files.length === 1) {\n return basename(files[0]);\n }\n\n if (files.length > 1) {\n return plural(files.length, 'file', 'files');\n }\n\n return undefined;\n}\n\nfunction fileActivity(verb: string, fallback: string) {\n return (input: Record<string, unknown>) => `${ verb } ${ fileLabel(input) ?? fallback }`;\n}\n\nconst TOOL_ACTIVITY: Record<string, (input: Record<string, unknown>) => string | undefined> = {\n Read: fileActivity('Reading', 'file'),\n Write: fileActivity('Writing', 'file'),\n Edit: fileActivity('Editing', 'file'),\n ListDirectory: fileActivity('Listing', 'files'),\n CreateDirectory: fileActivity('Creating', 'folder'),\n MoveFile: fileActivity('Moving', 'file'),\n RemoveFile: fileActivity('Removing', 'file'),\n Grep: input => `Grepping ${ fieldString(input.pattern) ?? 'code' }`,\n Glob: input => `Finding ${ fieldString(input.glob) ?? 'files' }`,\n GetBackgroundResult: () => 'Background task',\n Bash: (input) => {\n const command = fieldString(input.command);\n\n return command ? `Running ${ command }` : 'Running command';\n },\n Fetch: input => `Fetching ${ fieldString(input.url) ?? 'page' }`,\n Skill: input => `Loading ${ fieldString(input.name) ?? 'skill' }`,\n GetMcpResources: (input) => {\n const uri = fieldString(input.uri);\n\n if (uri) {\n const tail = uri.replace(/\\/+$/, '').split('/').pop()?.trim();\n\n return `Reading ${ tail || uri }`;\n }\n\n return 'Listing MCP resources';\n },\n Preamble: input => fieldString(input.content),\n ReadLints: () => 'Checking lints',\n PageScreenshot: () => 'Capturing screenshot',\n CheckPage: () => 'Checking pages',\n DevServerStatus: () => 'Checking project',\n ImportAsset: () => 'Importing asset',\n CreatePlan: () => 'Creating plan',\n EditPlan: () => 'Editing plan',\n ReadPlan: () => 'Reading plan',\n};\n\n/** One composer strip: who is running, and the child thread Show opens. */\nexport function subagentComposerCopy(\n part: { input?: unknown; output?: unknown },\n patch: Record<string, unknown> | undefined\n): { text: string; label: string; childThreadId?: string; waiting: boolean } {\n const name = agentNameFromPart(part);\n const label = subagentLabel(name);\n const waiting = patch?.needsUser === true;\n const childThreadId = fieldString((part.output as AgentToolOutput | undefined)?.childThreadId)\n ?? fieldString(patch?.childThreadId);\n const text = waiting\n ? `${ label } · Waiting for you`\n : `${ label } Agent working`;\n\n return { text, label, childThreadId, waiting };\n}\n\n/** Live subtitle for a tool — same copy for Agent rows and the compact rail task. */\nexport function subagentActivityLabel(patch: Record<string, unknown> | undefined): string | undefined {\n if (!patch) {\n return undefined;\n }\n\n if (patch.type === 'text') {\n return 'Finishing reply';\n }\n\n if (patch.type === 'reasoning') {\n return 'Thinking…';\n }\n\n const toolName = fieldString(patch.toolName);\n\n if (!toolName) {\n return undefined;\n }\n\n const rawInput = patch.input;\n const input = (rawInput != null && typeof rawInput === 'object' && !Array.isArray(rawInput))\n ? rawInput as Record<string, unknown>\n : {};\n\n return TOOL_ACTIVITY[toolName]?.(input)\n ?? `Using ${ fieldString(patch.title) ?? humanizeString(toolName) }`;\n}\n\n/** Live/waiting subtitle. Done rows stay quiet — no empty \"Completed\". */\nexport function agentStatusLine(input: {\n live: boolean;\n failed: boolean;\n stalled?: boolean;\n needsUser?: boolean;\n activity?: string;\n}): string | undefined {\n if (input.failed || input.stalled) {\n return undefined;\n }\n\n if (input.needsUser) {\n return 'Waiting for you';\n }\n\n if (!input.live) {\n return undefined;\n }\n\n return input.activity ?? 'Starting…';\n}\n\n/** Subagent card. Running: loader, shimmer title, gray activity. Finished: same frame, quiet title. */\nfunction AgentCard({\n title,\n activity,\n errorText,\n running,\n stalled,\n onOpen,\n}: {\n title: string;\n activity?: string;\n errorText?: string;\n running: boolean;\n stalled: boolean;\n onOpen?: () => void;\n}) {\n const heading = stalled ? TOOL_INTERRUPTED_MESSAGE : title;\n const copy = (\n <Box align=\"center\" direction=\"row\" fullWidth gap=\"md\" padding=\"md\">\n <Show\n when={ running }\n fallback={ <Message aria-hidden /> }\n >\n <ProgressLoader aria-hidden size={ 14 } />\n </Show>\n <Box gap=\"xxs\" grow>\n <Show\n when={ running }\n fallback={ (\n <Text className={ styles.title } color={ stalled ? 'danger' : undefined }>\n { heading }\n </Text>\n ) }\n >\n <Shimmer as=\"span\" className={ styles.title } duration={ 1 }>\n { heading }\n </Shimmer>\n </Show>\n <Show when={ running ? activity : undefined }>\n { line => (\n <Text color=\"secondary\" truncate variant=\"sm\">\n { line }\n </Text>\n ) }\n </Show>\n <Show when={ errorText }>\n { line => (\n <Text color=\"danger\" variant=\"xs\">{ line }</Text>\n ) }\n </Show>\n </Box>\n </Box>\n );\n\n return (\n <ToolShellCard\n aria-busy={ running || undefined }\n className={ shellStyles.tool }\n collapsible={ false }\n data-slot=\"agent-tool\"\n >\n <Show\n when={ onOpen }\n fallback={ copy }\n >\n { open => (\n <Pressable\n aria-label={ activity ? `${ heading }. ${ activity }` : heading }\n className={ styles.hit }\n onPress={ open }\n >\n { copy }\n </Pressable>\n ) }\n </Show>\n </ToolShellCard>\n );\n}\n\n/** Agent subagent card. Opens the child thread. */\nexport const AgentTool = observer(({ part }: ToolRendererProps) => {\n const turn = useMessageTurn();\n const { onOpenSubagent } = useChat();\n const patch = turn?.getToolMeta?.(part.toolCallId)?.patch;\n const needsUser = patch?.needsUser === true;\n const { live, stalled } = useToolProgress(part.state, needsUser);\n\n if (!isToolUIPart(part) || getToolName(part) !== 'Agent') {\n return null;\n }\n\n const failure = getToolFailureRowState(part);\n const logicalError = part.state === 'output-available'\n ? agentErrorFromOutput(part.output)\n : undefined;\n const failed = failure.useFailureLabel || Boolean(logicalError);\n const agentName = agentNameFromPart(part);\n const label = subagentLabel(agentName);\n const background = part.state === 'output-available' && Boolean((part.output as AgentToolOutput | undefined)?.jobId);\n const phase = agentPhaseCopy(agentName, background);\n const activity = subagentActivityLabel(patch);\n const statusLine = agentStatusLine({\n live,\n failed,\n stalled,\n needsUser,\n activity,\n });\n const childThreadId = fieldString((part.output as AgentToolOutput | undefined)?.childThreadId)\n ?? fieldString(patch?.childThreadId);\n const canOpen = Boolean(childThreadId && onOpenSubagent && !failed);\n const errorText = logicalError ?? failure.problemText ?? failure.tooltip;\n const openChild = canOpen\n ? () => onOpenSubagent?.(childThreadId!, label)\n : undefined;\n\n return (\n <AgentCard\n activity={ statusLine }\n errorText={ errorText }\n onOpen={ openChild }\n running={ live && !failed && !stalled }\n stalled={ stalled }\n title={ live && !failed && !stalled ? phase.live : phase.done }\n />\n );\n});\n"],"names":["SUBAGENT_LABELS","SUBAGENT_LIVE","agentErrorFromOutput","output","error","agentNameFromPart","part","source","name","subagentLabel","id","mapped","agentPhaseCopy","background","label","fieldString","value","trimmed","fileLabel","input","path","basename","files","item","plural","fileActivity","verb","fallback","TOOL_ACTIVITY","command","uri","subagentComposerCopy","patch","waiting","childThreadId","subagentActivityLabel","toolName","rawInput","humanizeString","agentStatusLine","AgentCard","title","activity","errorText","running","stalled","onOpen","heading","TOOL_INTERRUPTED_MESSAGE","copy","Box","_jsx","Show","Message","ProgressLoader","_jsxs","Text","styles","Shimmer","line","ToolShellCard","shellStyles","open","Pressable","AgentTool","observer","turn","useMessageTurn","onOpenSubagent","useChat","needsUser","live","useToolProgress","isToolUIPart","getToolName","failure","getToolFailureRowState","logicalError","failed","agentName","phase","statusLine","canOpen"],"mappings":"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"}
1
+ {"version":3,"file":"AgentTool.js","sources":["../../../../src/chat/Tool/agent/AgentTool.tsx"],"sourcesContent":["import { Message } from '@reforma/icons/14';\nimport { ProgressLoader } from '@reforma/icons/assets';\nimport { basename, humanizeString, plural } from '@reforma/lib';\nimport { getToolName, isToolUIPart } from '@reforma/types';\nimport { observer } from 'mobx-react-lite';\n\nimport { Box } from '../../../Box';\nimport { Pressable } from '../../../Pressable';\nimport { Shimmer } from '../../../Shimmer';\nimport { Show } from '../../../Show';\nimport { Text } from '../../../Text';\nimport { useChat } from '../../ChatContext';\nimport { useMessageTurn } from '../../Message/turn/messageTurnContext';\nimport { getToolFailureRowState, TOOL_INTERRUPTED_MESSAGE } from '../helpers';\nimport { ToolShellCard } from '../shell';\nimport { useToolProgress } from '../shell/toolProgress';\nimport shellStyles from '../shell/ToolShellCard.module.css';\nimport styles from './AgentTool.module.css';\n\nimport type { AgentToolInput, AgentToolOutput } from '@reforma/types/sandbox';\nimport type { ToolRendererProps } from '../types';\n\n/** User-facing labels for known subagent ids (fallback: capitalized id). */\nconst SUBAGENT_LABELS: Record<string, string> = {\n research: 'Explore',\n shell: 'Shell',\n verify: 'Verify',\n plan: 'Plan',\n};\n\nconst SUBAGENT_LIVE: Record<string, string> = {\n research: 'Exploring',\n shell: 'Running',\n verify: 'Verifying',\n plan: 'Planning',\n};\n\nfunction agentErrorFromOutput(output: unknown): string | undefined {\n if (output == null || typeof output !== 'object') {\n return undefined;\n }\n\n const error = (output as AgentToolOutput).error;\n\n return (typeof error === 'string' && error.trim().length > 0) ? error.trim() : undefined;\n}\n\nfunction agentNameFromPart(part: { input?: unknown; output?: unknown }): string | undefined {\n for (const source of [part.output, part.input]) {\n if (source == null || typeof source !== 'object') {\n continue;\n }\n\n const name = (source as AgentToolInput | AgentToolOutput).name;\n\n if (typeof name === 'string' && name.trim().length > 0) {\n return name.trim();\n }\n }\n\n return undefined;\n}\n\nfunction subagentLabel(name: string | undefined): string {\n const id = name?.trim() || 'research';\n const mapped = SUBAGENT_LABELS[id];\n\n if (mapped) {\n return mapped;\n }\n\n return id.charAt(0).toUpperCase() + id.slice(1);\n}\n\nfunction agentPhaseCopy(name: string | undefined, background: boolean): { live: string; done: string } {\n const id = name?.trim() || 'research';\n const label = subagentLabel(name);\n\n return {\n live: SUBAGENT_LIVE[id] ?? `Running ${ label }`,\n done: background ? `Started ${ label } agent in background` : `Ran ${ label } agent`,\n };\n}\n\nfunction fieldString(value: unknown): string | undefined {\n if (typeof value !== 'string') {\n return undefined;\n }\n\n const trimmed = value.trim();\n\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction fileLabel(input: Record<string, unknown>): string | undefined {\n const path = fieldString(input.path);\n\n if (path) {\n return basename(path);\n }\n\n if (!Array.isArray(input.paths)) {\n return undefined;\n }\n\n const files = input.paths.filter((item): item is string => typeof item === 'string' && item.trim().length > 0);\n\n if (files.length === 1) {\n return basename(files[0]);\n }\n\n if (files.length > 1) {\n return plural(files.length, 'file', 'files');\n }\n\n return undefined;\n}\n\nfunction fileActivity(verb: string, fallback: string) {\n return (input: Record<string, unknown>) => `${ verb } ${ fileLabel(input) ?? fallback }`;\n}\n\nconst TOOL_ACTIVITY: Record<string, (input: Record<string, unknown>) => string | undefined> = {\n Read: fileActivity('Reading', 'file'),\n Write: fileActivity('Writing', 'file'),\n Edit: fileActivity('Editing', 'file'),\n ListDirectory: fileActivity('Listing', 'files'),\n CreateDirectory: fileActivity('Creating', 'folder'),\n MoveFile: fileActivity('Moving', 'file'),\n RemoveFile: fileActivity('Removing', 'file'),\n Grep: input => `Grepping ${ fieldString(input.pattern) ?? 'code' }`,\n Glob: input => `Finding ${ fieldString(input.glob) ?? 'files' }`,\n GetBackgroundResult: () => 'Background task',\n Bash: (input) => {\n const command = fieldString(input.command);\n\n return command ? `Running ${ command }` : 'Running command';\n },\n Fetch: input => `Fetching ${ fieldString(input.url) ?? 'page' }`,\n Skill: input => `Loading ${ fieldString(input.name) ?? 'skill' }`,\n GetMcpResources: (input) => {\n const uri = fieldString(input.uri);\n\n if (uri) {\n const tail = uri.replace(/\\/+$/, '').split('/').pop()?.trim();\n\n return `Reading ${ tail || uri }`;\n }\n\n return 'Listing MCP resources';\n },\n Preamble: input => fieldString(input.content),\n ReadLints: () => 'Checking lints',\n PageScreenshot: () => 'Capturing screenshot',\n CheckPage: () => 'Checking pages',\n DevServerStatus: () => 'Checking project',\n ImportAsset: () => 'Importing asset',\n CreatePlan: () => 'Creating plan',\n EditPlan: () => 'Editing plan',\n ReadPlan: () => 'Reading plan',\n};\n\n/** One composer strip: who is running, and the child thread Show opens. */\nexport function subagentComposerCopy(\n part: { input?: unknown; output?: unknown },\n patch: Record<string, unknown> | undefined\n): { text: string; label: string; childThreadId?: string; waiting: boolean } {\n const name = agentNameFromPart(part);\n const label = subagentLabel(name);\n const waiting = patch?.needsUser === true;\n const childThreadId = fieldString((part.output as AgentToolOutput | undefined)?.childThreadId)\n ?? fieldString(patch?.childThreadId);\n const text = waiting\n ? `${ label } · Waiting for you`\n : `${ label } Agent working`;\n\n return { text, label, childThreadId, waiting };\n}\n\n/** Live subtitle for a tool — same copy for Agent rows and the compact rail task. */\nexport function subagentActivityLabel(patch: Record<string, unknown> | undefined): string | undefined {\n if (!patch) {\n return undefined;\n }\n\n if (patch.type === 'text') {\n return 'Finishing reply';\n }\n\n if (patch.type === 'reasoning') {\n return 'Thinking…';\n }\n\n const toolName = fieldString(patch.toolName);\n\n if (!toolName) {\n return undefined;\n }\n\n const rawInput = patch.input;\n const input = (rawInput != null && typeof rawInput === 'object' && !Array.isArray(rawInput))\n ? rawInput as Record<string, unknown>\n : {};\n\n return TOOL_ACTIVITY[toolName]?.(input)\n ?? `Using ${ fieldString(patch.title) ?? humanizeString(toolName) }`;\n}\n\n/** Live/waiting subtitle. Done rows stay quiet — no empty \"Completed\". */\nexport function agentStatusLine(input: {\n live: boolean;\n failed: boolean;\n stalled?: boolean;\n needsUser?: boolean;\n activity?: string;\n}): string | undefined {\n if (input.failed || input.stalled) {\n return undefined;\n }\n\n if (input.needsUser) {\n return 'Waiting for you';\n }\n\n if (!input.live) {\n return undefined;\n }\n\n return input.activity ?? 'Starting…';\n}\n\n/** Subagent card. Running: loader, shimmer title, gray activity. Finished: same frame, quiet title. */\nfunction AgentCard({\n title,\n activity,\n errorText,\n running,\n stalled,\n onOpen,\n}: {\n title: string;\n activity?: string;\n errorText?: string;\n running: boolean;\n stalled: boolean;\n onOpen?: () => void;\n}) {\n const heading = stalled ? TOOL_INTERRUPTED_MESSAGE : title;\n const copy = (\n <Box align=\"center\" direction=\"row\" fullWidth gap=\"md\" padding=\"md\">\n <Show\n when={ running }\n fallback={ <Message aria-hidden /> }\n >\n <ProgressLoader aria-hidden size={ 14 } />\n </Show>\n <Box gap=\"xxs\" grow>\n <Show\n when={ running }\n fallback={ (\n <Text className={ styles.title } color={ stalled ? 'secondary' : undefined }>\n { heading }\n </Text>\n ) }\n >\n <Shimmer as=\"span\" className={ styles.title } duration={ 1 }>\n { heading }\n </Shimmer>\n </Show>\n <Show when={ running ? activity : undefined }>\n { line => (\n <Text color=\"secondary\" truncate variant=\"sm\">\n { line }\n </Text>\n ) }\n </Show>\n <Show when={ errorText }>\n { line => (\n <Text color=\"danger\" variant=\"xs\">{ line }</Text>\n ) }\n </Show>\n </Box>\n </Box>\n );\n\n return (\n <ToolShellCard\n aria-busy={ running || undefined }\n className={ shellStyles.tool }\n collapsible={ false }\n data-slot=\"agent-tool\"\n >\n <Show\n when={ onOpen }\n fallback={ copy }\n >\n { open => (\n <Pressable\n aria-label={ activity ? `${ heading }. ${ activity }` : heading }\n className={ styles.hit }\n onPress={ open }\n >\n { copy }\n </Pressable>\n ) }\n </Show>\n </ToolShellCard>\n );\n}\n\n/** Agent subagent card. Opens the child thread. */\nexport const AgentTool = observer(({ part }: ToolRendererProps) => {\n const turn = useMessageTurn();\n const { onOpenSubagent } = useChat();\n const patch = turn?.getToolMeta?.(part.toolCallId)?.patch;\n const needsUser = patch?.needsUser === true;\n const { live, stalled } = useToolProgress(part.state, needsUser);\n\n if (!isToolUIPart(part) || getToolName(part) !== 'Agent') {\n return null;\n }\n\n const failure = getToolFailureRowState(part);\n const logicalError = part.state === 'output-available'\n ? agentErrorFromOutput(part.output)\n : undefined;\n const failed = failure.useFailureLabel || Boolean(logicalError);\n const agentName = agentNameFromPart(part);\n const label = subagentLabel(agentName);\n const background = part.state === 'output-available' && Boolean((part.output as AgentToolOutput | undefined)?.jobId);\n const phase = agentPhaseCopy(agentName, background);\n const activity = subagentActivityLabel(patch);\n const statusLine = agentStatusLine({\n live,\n failed,\n stalled,\n needsUser,\n activity,\n });\n const childThreadId = fieldString((part.output as AgentToolOutput | undefined)?.childThreadId)\n ?? fieldString(patch?.childThreadId);\n const canOpen = Boolean(childThreadId && onOpenSubagent && !failed);\n const errorText = logicalError ?? failure.problemText ?? failure.tooltip;\n const openChild = canOpen\n ? () => onOpenSubagent?.(childThreadId!, label)\n : undefined;\n\n return (\n <AgentCard\n activity={ statusLine }\n errorText={ errorText }\n onOpen={ openChild }\n running={ live && !failed && !stalled }\n stalled={ stalled }\n title={ live && !failed && !stalled ? phase.live : phase.done }\n />\n );\n});\n"],"names":["SUBAGENT_LABELS","SUBAGENT_LIVE","agentErrorFromOutput","output","error","agentNameFromPart","part","source","name","subagentLabel","id","mapped","agentPhaseCopy","background","label","fieldString","value","trimmed","fileLabel","input","path","basename","files","item","plural","fileActivity","verb","fallback","TOOL_ACTIVITY","command","uri","subagentComposerCopy","patch","waiting","childThreadId","subagentActivityLabel","toolName","rawInput","humanizeString","agentStatusLine","AgentCard","title","activity","errorText","running","stalled","onOpen","heading","TOOL_INTERRUPTED_MESSAGE","copy","Box","_jsx","Show","Message","ProgressLoader","_jsxs","Text","styles","Shimmer","line","ToolShellCard","shellStyles","open","Pressable","AgentTool","observer","turn","useMessageTurn","onOpenSubagent","useChat","needsUser","live","useToolProgress","isToolUIPart","getToolName","failure","getToolFailureRowState","logicalError","failed","agentName","phase","statusLine","canOpen"],"mappings":"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"}