@reforma/components 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/dist/ToggleButtonGroup/ToggleButtonGroup.module.css +1 -1
  2. package/dist/chat/ComposerBanner/ComposerBanner.d.ts.map +1 -1
  3. package/dist/chat/ComposerBanner/ComposerBanner.js +16 -15
  4. package/dist/chat/ComposerBanner/ComposerBanner.js.map +1 -1
  5. package/dist/chat/Message/turn/Message.module.css +1 -1
  6. package/dist/chat/Message/turn/Message.module.css.js +22 -16
  7. package/dist/chat/Message/turn/Message.module.css.js.map +1 -1
  8. package/dist/chat/Message/turn/MessageAssistantBody.d.ts +2 -1
  9. package/dist/chat/Message/turn/MessageAssistantBody.d.ts.map +1 -1
  10. package/dist/chat/Message/turn/MessageAssistantBody.js +148 -56
  11. package/dist/chat/Message/turn/MessageAssistantBody.js.map +1 -1
  12. package/dist/chat/Message/turn/MessageUserBubble.d.ts.map +1 -1
  13. package/dist/chat/Message/turn/MessageUserBubble.js +52 -53
  14. package/dist/chat/Message/turn/MessageUserBubble.js.map +1 -1
  15. package/dist/chat/Message/turn/workedCollapse.d.ts +16 -0
  16. package/dist/chat/Message/turn/workedCollapse.d.ts.map +1 -0
  17. package/dist/chat/Message/turn/workedCollapse.js +31 -0
  18. package/dist/chat/Message/turn/workedCollapse.js.map +1 -0
  19. package/dist/chat/Tool/agent/AgentTool.d.ts +10 -0
  20. package/dist/chat/Tool/agent/AgentTool.d.ts.map +1 -1
  21. package/dist/chat/Tool/agent/AgentTool.js +110 -99
  22. package/dist/chat/Tool/agent/AgentTool.js.map +1 -1
  23. package/dist/chat/Tool/index.d.ts +1 -1
  24. package/dist/chat/Tool/index.d.ts.map +1 -1
  25. package/dist/chat/Tool/index.js +45 -44
  26. package/dist/chat/index.js +62 -61
  27. package/dist/index.js +364 -363
  28. package/package.json +1 -1
@@ -1,30 +1,30 @@
1
- import { cn as b } from "@reforma/lib";
2
- import { resolveLayerCite as x, attachedLayerRefKey as g, formatLayerCite as S, codeEditorStateKey as v } from "@reforma/types";
3
- import { observer as M } from "mobx-react-lite";
4
- import { useState as N } from "react";
5
- import { For as E } from "../../../For/For.js";
6
- import { Scrollable as $ } from "../../../Scrollable/Scrollable.js";
7
- import { ScrollExpandChevronButton as w } from "../../../Scrollable/ScrollExpandChevronButton.js";
1
+ import { cn as x } from "@reforma/lib";
2
+ import { resolveLayerCite as g, attachedLayerRefKey as S, formatLayerCite as v, codeEditorStateKey as M } from "@reforma/types";
3
+ import { observer as N } from "mobx-react-lite";
4
+ import { useState as E } from "react";
5
+ import { For as $ } from "../../../For/For.js";
6
+ import { Scrollable as w } from "../../../Scrollable/Scrollable.js";
7
+ import { ScrollExpandChevronButton as B } from "../../../Scrollable/ScrollExpandChevronButton.js";
8
8
  import { Show as f } from "../../../Show/Show.js";
9
- import { Badge as B } from "../../../Tag/Badge.js";
10
- import { useChat as L } from "../../ChatContext.js";
11
- import { Chip as k } from "../../Chip/Chip.js";
12
- import { chipItemFromPath as A } from "../../Chip/helpers/chipPath.js";
13
- import { fileCite as U, fileChipDisplay as _ } from "../../Chip/helpers/cursorLines.js";
9
+ import { Badge as L } from "../../../Tag/Badge.js";
10
+ import { useChat as k } from "../../ChatContext.js";
11
+ import { Chip as A } from "../../Chip/Chip.js";
12
+ import { chipItemFromPath as U } from "../../Chip/helpers/chipPath.js";
13
+ import { fileCite as _, fileChipDisplay as I } from "../../Chip/helpers/cursorLines.js";
14
14
  import { MessageText as y } from "../streamdown/MessageText.js";
15
15
  import i from "./Message.module.css.js";
16
- import { useMessageTurn as I } from "./messageTurnContext.js";
17
- import { jsx as l, jsxs as P } from "react/jsx-runtime";
18
- const R = 500, C = {
16
+ import { useMessageTurn as P } from "./messageTurnContext.js";
17
+ import { jsx as l, jsxs as C } from "react/jsx-runtime";
18
+ const R = 500, b = {
19
19
  expand: "Show full message",
20
20
  collapse: "Show less"
21
21
  };
22
22
  function F(s) {
23
- const a = A(s.path);
23
+ const a = U(s.path);
24
24
  return {
25
- id: v(s),
25
+ id: M(s),
26
26
  path: a?.path ?? s.path,
27
- label: _(s),
27
+ label: I(s),
28
28
  tooltip: a?.tooltip,
29
29
  ...a?.icon ? {
30
30
  icon: a.icon
@@ -35,8 +35,8 @@ function F(s) {
35
35
  };
36
36
  }
37
37
  function T(s, a) {
38
- const p = a?.attached?.files ?? [], n = a?.attached?.layers ?? [], m = [.../* @__PURE__ */ new Set([...a?.skills ?? [], ...a?.commands ?? []])], e = [...p.map((t) => {
39
- const r = U(t);
38
+ const n = a?.attached?.files ?? [], p = a?.attached?.layers ?? [], m = [.../* @__PURE__ */ new Set([...a?.skills ?? [], ...a?.commands ?? []])], e = [...n.map((t) => {
39
+ const r = _(t);
40
40
  return {
41
41
  cite: r,
42
42
  segment: {
@@ -45,8 +45,8 @@ function T(s, a) {
45
45
  file: t
46
46
  }
47
47
  };
48
- }), ...n.map((t) => {
49
- const r = S(t);
48
+ }), ...p.map((t) => {
49
+ const r = v(t);
50
50
  return {
51
51
  cite: r,
52
52
  segment: {
@@ -113,7 +113,7 @@ function d({
113
113
  item: s,
114
114
  onOpenPath: a
115
115
  }) {
116
- return /* @__PURE__ */ l(k, {
116
+ return /* @__PURE__ */ l(A, {
117
117
  className: i.userCite,
118
118
  item: s,
119
119
  onAction: s.href ? void 0 : a
@@ -122,22 +122,22 @@ function d({
122
122
  function j({
123
123
  text: s,
124
124
  metadata: a,
125
- onOpenPath: p
125
+ onOpenPath: n
126
126
  }) {
127
- const n = T(s, a);
128
- return n.length === 0 ? null : !n.some((e) => e.type !== "text") && n[0]?.type === "text" ? /* @__PURE__ */ l(y, {
127
+ const p = T(s, a);
128
+ return p.length === 0 ? null : !p.some((e) => e.type !== "text") && p[0]?.type === "text" ? /* @__PURE__ */ l(y, {
129
129
  className: i.userMessageBody,
130
130
  mode: "static",
131
- children: n[0].value
131
+ children: p[0].value
132
132
  }) : /* @__PURE__ */ l("div", {
133
- className: b(i.userMessageBody, i.userMessageWithCites),
134
- children: /* @__PURE__ */ l(E, {
135
- each: n,
133
+ className: x(i.userMessageBody, i.userMessageWithCites),
134
+ children: /* @__PURE__ */ l($, {
135
+ each: p,
136
136
  children: (e, c) => e.type === "text" ? /* @__PURE__ */ l(y, {
137
137
  className: i.userMessageFragment,
138
138
  mode: "static",
139
139
  children: e.value
140
- }, `t-${c}`) : e.type === "command" ? /* @__PURE__ */ l(B, {
140
+ }, `t-${c}`) : e.type === "command" ? /* @__PURE__ */ l(L, {
141
141
  size: "xs",
142
142
  color: "success",
143
143
  value: e.skill,
@@ -147,7 +147,7 @@ function j({
147
147
  children: e.cite
148
148
  }, `c-${e.cite}-${c}`) : e.type === "file" ? /* @__PURE__ */ l(d, {
149
149
  item: F(e.file),
150
- onOpenPath: p
150
+ onOpenPath: n
151
151
  }, `f-${e.cite}-${c}`) : e.type === "link" ? /* @__PURE__ */ l(d, {
152
152
  item: {
153
153
  id: e.url,
@@ -156,25 +156,25 @@ function j({
156
156
  tooltip: e.url,
157
157
  href: e.url
158
158
  },
159
- onOpenPath: p
159
+ onOpenPath: n
160
160
  }, `u-${e.cite}-${c}`) : /* @__PURE__ */ l(d, {
161
161
  item: {
162
- id: g(e.layer),
162
+ id: S(e.layer),
163
163
  type: "element",
164
- label: e.layer.propName ? `${x(e.layer)}.${e.layer.propName}` : e.layer.text || e.layer.label || e.layer.layerId,
164
+ label: e.layer.propName ? `${g(e.layer)}.${e.layer.propName}` : e.layer.text || e.layer.label || e.layer.layerId,
165
165
  ...e.layer.icon ? {
166
166
  icon: e.layer.icon
167
167
  } : {}
168
168
  },
169
- onOpenPath: p
169
+ onOpenPath: n
170
170
  }, `l-${e.cite}-${c}`)
171
171
  })
172
172
  });
173
173
  }
174
- const se = M(() => {
175
- const s = I(), {
174
+ const se = N(() => {
175
+ const s = P(), {
176
176
  onOpenPath: a
177
- } = L(), [p, n] = N(!1);
177
+ } = k(), [n, p] = E(!1);
178
178
  if (s?.role !== "user" || s.message.role !== "user")
179
179
  return null;
180
180
  const {
@@ -188,32 +188,31 @@ const se = M(() => {
188
188
  metadata: e.metadata,
189
189
  onOpenPath: a
190
190
  }), r = m.length > R;
191
- return /* @__PURE__ */ P("div", {
192
- className: b(i.messageBubble, i.userBubble),
191
+ return /* @__PURE__ */ C("div", {
192
+ className: x(i.messageBubble, i.userBubble),
193
193
  children: [/* @__PURE__ */ l(f, {
194
194
  when: !r,
195
195
  children: /* @__PURE__ */ l("div", {
196
196
  className: i.parts,
197
197
  children: t
198
198
  })
199
- }), /* @__PURE__ */ l(f, {
199
+ }), /* @__PURE__ */ C(f, {
200
200
  when: r,
201
- children: /* @__PURE__ */ l($, {
201
+ children: [/* @__PURE__ */ l(w, {
202
202
  vertical: !0,
203
- edgeFade: !0,
203
+ edgeFade: !n,
204
204
  className: i.userMessageScroll,
205
205
  containerClass: i.userMessageScrollPort,
206
206
  contentClass: i.parts,
207
- overlay: /* @__PURE__ */ l(w, {
208
- expanded: p,
209
- onPress: () => n((o) => !o),
210
- labelExpand: C.expand,
211
- labelCollapse: C.collapse,
212
- className: i.userMessageExpandToggle
213
- }),
214
- "aria-expanded": p,
207
+ "aria-expanded": n,
215
208
  children: t
216
- })
209
+ }), /* @__PURE__ */ l(B, {
210
+ expanded: n,
211
+ onPress: () => p((o) => !o),
212
+ labelExpand: b.expand,
213
+ labelCollapse: b.collapse,
214
+ className: i.userMessageExpandToggle
215
+ })]
217
216
  })]
218
217
  });
219
218
  });
@@ -1 +1 @@
1
- {"version":3,"file":"MessageUserBubble.js","sources":["../../../../src/chat/Message/turn/MessageUserBubble.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@reforma/lib';\nimport {\n attachedLayerRefKey,\n codeEditorStateKey,\n formatLayerCite,\n resolveLayerCite,\n type CodeEditorState,\n type SelectedLayerContext,\n} from '@reforma/types';\nimport { observer } from 'mobx-react-lite';\nimport { useState, type ReactNode } from 'react';\n\nimport { For } from '../../../For';\nimport { Scrollable, ScrollExpandChevronButton } from '../../../Scrollable';\nimport { Show } from '../../../Show';\nimport { Badge } from '../../../Tag/Badge';\nimport { useChat } from '../../ChatContext';\nimport { Chip, type ChipItem } from '../../Chip/Chip';\nimport { chipItemFromPath } from '../../Chip/helpers/chipPath';\nimport { fileChipDisplay, fileCite } from '../../Chip/helpers/cursorLines';\nimport { MessageText } from '../streamdown/MessageText';\nimport styles from './Message.module.css';\nimport { useMessageTurn } from './messageTurnContext';\n\nimport type { ReformaUserMessageMetadata } from '@reforma/types/sandbox';\n\nexport const MESSAGE_USER_COLLAPSE_MIN_CHARS = 500;\n\nconst USER_COLLAPSE_LABELS = {\n expand: 'Show full message',\n collapse: 'Show less',\n} as const;\n\ntype TextSegment = { type: 'text'; value: string };\ntype FileSegment = { type: 'file'; cite: string; file: CodeEditorState };\ntype LayerSegment = {\n type: 'layer';\n cite: string;\n layer: SelectedLayerContext;\n};\ntype CommandSegment = { type: 'command'; cite: string; skill: string };\ntype LinkSegment = { type: 'link'; cite: string; url: string; host: string };\ntype UserMessageSegment = TextSegment | FileSegment | LayerSegment | CommandSegment | LinkSegment;\n\nfunction fileCiteChipItem(file: CodeEditorState): ChipItem {\n const fromPath = chipItemFromPath(file.path);\n\n return {\n id: codeEditorStateKey(file),\n path: fromPath?.path ?? file.path,\n label: fileChipDisplay(file),\n tooltip: fromPath?.tooltip,\n ...(fromPath?.icon ? { icon: fromPath.icon } : {}),\n ...(file.cursor ? { cursor: file.cursor } : {}),\n };\n}\n\n/**\n * Split user prompt into text + chips for cites that appear in the prompt.\n * Forced meta pins (comment / ask-to-apply) stay in metadata for the agent — not rendered here.\n */\nfunction splitUserMessageSegments(\n text: string,\n metadata: ReformaUserMessageMetadata | undefined\n): UserMessageSegment[] {\n const files = metadata?.attached?.files ?? [];\n const layers = metadata?.attached?.layers ?? [];\n const slashNames = [...new Set([\n ...(metadata?.skills ?? []),\n ...(metadata?.commands ?? []),\n ])];\n\n const cites: Array<{ cite: string; segment: Exclude<UserMessageSegment, TextSegment> }> = [\n ...files.map((file) => {\n const cite = fileCite(file);\n\n return { cite, segment: { type: 'file' as const, cite, file } };\n }),\n ...layers.map((layer) => {\n const cite = formatLayerCite(layer);\n\n return { cite, segment: { type: 'layer' as const, cite, layer } };\n }),\n ...slashNames.map((raw) => {\n const name = raw.trim().replace(/^\\//, '');\n const cite = `/${ name }`;\n\n return { cite, segment: { type: 'command' as const, cite, skill: name } };\n }),\n ...[...text.matchAll(/@https?:\\/\\/[^\\s]+/gi)].map((match) => {\n const cite = match[0] ?? '';\n const url = cite.slice(1);\n let host = url;\n\n try {\n host = new URL(url).hostname.replace(/^www\\./, '') || url;\n }\n catch {\n // keep `url` as the chip label\n }\n\n return { cite, segment: { type: 'link' as const, cite, url, host } };\n }),\n ].sort((a, b) => b.cite.length - a.cite.length);\n\n if (cites.length === 0) {\n return text ? [{ type: 'text', value: text }] : [];\n }\n\n const segments: UserMessageSegment[] = [];\n let remaining = text;\n\n while (remaining.length > 0) {\n let bestIdx = -1;\n let best: (typeof cites)[number] | undefined;\n\n for (const entry of cites) {\n const idx = remaining.indexOf(entry.cite);\n\n if (idx < 0) {\n continue;\n }\n\n if (\n bestIdx < 0\n || idx < bestIdx\n || (idx === bestIdx && entry.cite.length > (best?.cite.length ?? 0))\n ) {\n bestIdx = idx;\n best = entry;\n }\n }\n\n if (bestIdx < 0 || !best) {\n segments.push({ type: 'text', value: remaining });\n break;\n }\n\n if (bestIdx > 0) {\n segments.push({ type: 'text', value: remaining.slice(0, bestIdx) });\n }\n\n segments.push(best.segment);\n remaining = remaining.slice(bestIdx + best.cite.length);\n }\n\n return segments.filter(segment => segment.type !== 'text' || segment.value.length > 0);\n}\n\nfunction UserCiteChip({\n item,\n onOpenPath,\n}: {\n item: ChipItem;\n onOpenPath?: (item: ChipItem) => void;\n}): ReactNode {\n return (\n <Chip\n className={ styles.userCite }\n item={ item }\n onAction={ item.href ? undefined : onOpenPath }\n />\n );\n}\n\nfunction UserMessageBody({\n text,\n metadata,\n onOpenPath,\n}: {\n text: string;\n metadata: ReformaUserMessageMetadata | undefined;\n onOpenPath?: (item: ChipItem) => void;\n}): ReactNode {\n const segments = splitUserMessageSegments(text, metadata);\n\n if (segments.length === 0) {\n return null;\n }\n\n const hasCites = segments.some(segment => segment.type !== 'text');\n\n if (!hasCites && segments[0]?.type === 'text') {\n return (\n <MessageText\n className={ styles.userMessageBody }\n mode=\"static\"\n >\n { segments[0].value }\n </MessageText>\n );\n }\n\n return (\n <div className={ cn(styles.userMessageBody, styles.userMessageWithCites) }>\n <For each={ segments }>\n { (segment, index) => {\n if (segment.type === 'text') {\n return (\n <MessageText\n key={ `t-${ index }` }\n className={ styles.userMessageFragment }\n mode=\"static\"\n >\n { segment.value }\n </MessageText>\n );\n }\n\n if (segment.type === 'command') {\n return (\n <Badge\n key={ `c-${ segment.cite }-${ index }` }\n size=\"xs\"\n color=\"success\"\n value={ segment.skill }\n textValue={ segment.cite }\n aria-label={ segment.cite }\n className={ styles.userCite }\n >\n { segment.cite }\n </Badge>\n );\n }\n\n if (segment.type === 'file') {\n return (\n <UserCiteChip\n key={ `f-${ segment.cite }-${ index }` }\n item={ fileCiteChipItem(segment.file) }\n onOpenPath={ onOpenPath }\n />\n );\n }\n\n if (segment.type === 'link') {\n return (\n <UserCiteChip\n key={ `u-${ segment.cite }-${ index }` }\n item={ {\n id: segment.url,\n type: 'link',\n label: segment.host,\n tooltip: segment.url,\n href: segment.url,\n } }\n onOpenPath={ onOpenPath }\n />\n );\n }\n\n return (\n <UserCiteChip\n key={ `l-${ segment.cite }-${ index }` }\n item={ {\n id: attachedLayerRefKey(segment.layer),\n type: 'element',\n label: segment.layer.propName\n ? `${ resolveLayerCite(segment.layer) }.${ segment.layer.propName }`\n : (segment.layer.text || segment.layer.label || segment.layer.layerId),\n ...(segment.layer.icon ? { icon: segment.layer.icon } : {}),\n } }\n onOpenPath={ onOpenPath }\n />\n );\n } }\n </For>\n </div>\n );\n}\n\n/** User turn text bubble with collapsible long messages. Renders nothing when empty. */\nexport const MessageUserBubble = observer(() => {\n const turn = useMessageTurn();\n const { onOpenPath } = useChat();\n const [expanded, setExpanded] = useState(false);\n\n if (turn?.role !== 'user' || turn.message.role !== 'user') {\n return null;\n }\n\n const { text, message } = turn;\n const hasText = text.trim().length > 0;\n const hasAttached = Boolean(\n message.metadata?.attached?.files?.length\n || message.metadata?.attached?.layers?.length\n || message.metadata?.skills?.length\n || message.metadata?.commands?.length\n );\n\n if (!hasText && !hasAttached) {\n return null;\n }\n\n const body = (\n <UserMessageBody\n text={ text }\n metadata={ message.metadata }\n onOpenPath={ onOpenPath }\n />\n );\n const collapsible = text.length > MESSAGE_USER_COLLAPSE_MIN_CHARS;\n\n return (\n <div className={ cn(styles.messageBubble, styles.userBubble) }>\n <Show when={ !collapsible }>\n <div className={ styles.parts }>\n { body }\n </div>\n </Show>\n\n <Show when={ collapsible }>\n <Scrollable\n vertical\n edgeFade\n className={ styles.userMessageScroll }\n containerClass={ styles.userMessageScrollPort }\n contentClass={ styles.parts }\n overlay={ (\n <ScrollExpandChevronButton\n expanded={ expanded }\n onPress={ () => setExpanded(v => !v) }\n labelExpand={ USER_COLLAPSE_LABELS.expand }\n labelCollapse={ USER_COLLAPSE_LABELS.collapse }\n className={ styles.userMessageExpandToggle }\n />\n ) }\n aria-expanded={ expanded }\n >\n { body }\n </Scrollable>\n </Show>\n </div>\n );\n});\n"],"names":["MESSAGE_USER_COLLAPSE_MIN_CHARS","USER_COLLAPSE_LABELS","fileCiteChipItem","file","fromPath","chipItemFromPath","codeEditorStateKey","fileChipDisplay","splitUserMessageSegments","text","metadata","files","layers","slashNames","cites","cite","fileCite","layer","formatLayerCite","raw","name","match","url","host","a","b","segments","remaining","bestIdx","best","entry","idx","segment","UserCiteChip","item","onOpenPath","Chip","styles","UserMessageBody","MessageText","cn","For","index","Badge","attachedLayerRefKey","resolveLayerCite","MessageUserBubble","observer","turn","useMessageTurn","useChat","expanded","setExpanded","useState","message","hasText","hasAttached","body","collapsible","_jsx","Show","Scrollable","ScrollExpandChevronButton","v"],"mappings":"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"}
1
+ {"version":3,"file":"MessageUserBubble.js","sources":["../../../../src/chat/Message/turn/MessageUserBubble.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@reforma/lib';\nimport {\n attachedLayerRefKey,\n codeEditorStateKey,\n formatLayerCite,\n resolveLayerCite,\n type CodeEditorState,\n type SelectedLayerContext,\n} from '@reforma/types';\nimport { observer } from 'mobx-react-lite';\nimport { useState, type ReactNode } from 'react';\n\nimport { For } from '../../../For';\nimport { Scrollable, ScrollExpandChevronButton } from '../../../Scrollable';\nimport { Show } from '../../../Show';\nimport { Badge } from '../../../Tag/Badge';\nimport { useChat } from '../../ChatContext';\nimport { Chip, type ChipItem } from '../../Chip/Chip';\nimport { chipItemFromPath } from '../../Chip/helpers/chipPath';\nimport { fileChipDisplay, fileCite } from '../../Chip/helpers/cursorLines';\nimport { MessageText } from '../streamdown/MessageText';\nimport styles from './Message.module.css';\nimport { useMessageTurn } from './messageTurnContext';\n\nimport type { ReformaUserMessageMetadata } from '@reforma/types/sandbox';\n\nexport const MESSAGE_USER_COLLAPSE_MIN_CHARS = 500;\n\nconst USER_COLLAPSE_LABELS = {\n expand: 'Show full message',\n collapse: 'Show less',\n} as const;\n\ntype TextSegment = { type: 'text'; value: string };\ntype FileSegment = { type: 'file'; cite: string; file: CodeEditorState };\ntype LayerSegment = {\n type: 'layer';\n cite: string;\n layer: SelectedLayerContext;\n};\ntype CommandSegment = { type: 'command'; cite: string; skill: string };\ntype LinkSegment = { type: 'link'; cite: string; url: string; host: string };\ntype UserMessageSegment = TextSegment | FileSegment | LayerSegment | CommandSegment | LinkSegment;\n\nfunction fileCiteChipItem(file: CodeEditorState): ChipItem {\n const fromPath = chipItemFromPath(file.path);\n\n return {\n id: codeEditorStateKey(file),\n path: fromPath?.path ?? file.path,\n label: fileChipDisplay(file),\n tooltip: fromPath?.tooltip,\n ...(fromPath?.icon ? { icon: fromPath.icon } : {}),\n ...(file.cursor ? { cursor: file.cursor } : {}),\n };\n}\n\n/**\n * Split user prompt into text + chips for cites that appear in the prompt.\n * Forced meta pins (comment / ask-to-apply) stay in metadata for the agent — not rendered here.\n */\nfunction splitUserMessageSegments(\n text: string,\n metadata: ReformaUserMessageMetadata | undefined\n): UserMessageSegment[] {\n const files = metadata?.attached?.files ?? [];\n const layers = metadata?.attached?.layers ?? [];\n const slashNames = [...new Set([\n ...(metadata?.skills ?? []),\n ...(metadata?.commands ?? []),\n ])];\n\n const cites: Array<{ cite: string; segment: Exclude<UserMessageSegment, TextSegment> }> = [\n ...files.map((file) => {\n const cite = fileCite(file);\n\n return { cite, segment: { type: 'file' as const, cite, file } };\n }),\n ...layers.map((layer) => {\n const cite = formatLayerCite(layer);\n\n return { cite, segment: { type: 'layer' as const, cite, layer } };\n }),\n ...slashNames.map((raw) => {\n const name = raw.trim().replace(/^\\//, '');\n const cite = `/${ name }`;\n\n return { cite, segment: { type: 'command' as const, cite, skill: name } };\n }),\n ...[...text.matchAll(/@https?:\\/\\/[^\\s]+/gi)].map((match) => {\n const cite = match[0] ?? '';\n const url = cite.slice(1);\n let host = url;\n\n try {\n host = new URL(url).hostname.replace(/^www\\./, '') || url;\n }\n catch {\n // keep `url` as the chip label\n }\n\n return { cite, segment: { type: 'link' as const, cite, url, host } };\n }),\n ].sort((a, b) => b.cite.length - a.cite.length);\n\n if (cites.length === 0) {\n return text ? [{ type: 'text', value: text }] : [];\n }\n\n const segments: UserMessageSegment[] = [];\n let remaining = text;\n\n while (remaining.length > 0) {\n let bestIdx = -1;\n let best: (typeof cites)[number] | undefined;\n\n for (const entry of cites) {\n const idx = remaining.indexOf(entry.cite);\n\n if (idx < 0) {\n continue;\n }\n\n if (\n bestIdx < 0\n || idx < bestIdx\n || (idx === bestIdx && entry.cite.length > (best?.cite.length ?? 0))\n ) {\n bestIdx = idx;\n best = entry;\n }\n }\n\n if (bestIdx < 0 || !best) {\n segments.push({ type: 'text', value: remaining });\n break;\n }\n\n if (bestIdx > 0) {\n segments.push({ type: 'text', value: remaining.slice(0, bestIdx) });\n }\n\n segments.push(best.segment);\n remaining = remaining.slice(bestIdx + best.cite.length);\n }\n\n return segments.filter(segment => segment.type !== 'text' || segment.value.length > 0);\n}\n\nfunction UserCiteChip({\n item,\n onOpenPath,\n}: {\n item: ChipItem;\n onOpenPath?: (item: ChipItem) => void;\n}): ReactNode {\n return (\n <Chip\n className={ styles.userCite }\n item={ item }\n onAction={ item.href ? undefined : onOpenPath }\n />\n );\n}\n\nfunction UserMessageBody({\n text,\n metadata,\n onOpenPath,\n}: {\n text: string;\n metadata: ReformaUserMessageMetadata | undefined;\n onOpenPath?: (item: ChipItem) => void;\n}): ReactNode {\n const segments = splitUserMessageSegments(text, metadata);\n\n if (segments.length === 0) {\n return null;\n }\n\n const hasCites = segments.some(segment => segment.type !== 'text');\n\n if (!hasCites && segments[0]?.type === 'text') {\n return (\n <MessageText\n className={ styles.userMessageBody }\n mode=\"static\"\n >\n { segments[0].value }\n </MessageText>\n );\n }\n\n return (\n <div className={ cn(styles.userMessageBody, styles.userMessageWithCites) }>\n <For each={ segments }>\n { (segment, index) => {\n if (segment.type === 'text') {\n return (\n <MessageText\n key={ `t-${ index }` }\n className={ styles.userMessageFragment }\n mode=\"static\"\n >\n { segment.value }\n </MessageText>\n );\n }\n\n if (segment.type === 'command') {\n return (\n <Badge\n key={ `c-${ segment.cite }-${ index }` }\n size=\"xs\"\n color=\"success\"\n value={ segment.skill }\n textValue={ segment.cite }\n aria-label={ segment.cite }\n className={ styles.userCite }\n >\n { segment.cite }\n </Badge>\n );\n }\n\n if (segment.type === 'file') {\n return (\n <UserCiteChip\n key={ `f-${ segment.cite }-${ index }` }\n item={ fileCiteChipItem(segment.file) }\n onOpenPath={ onOpenPath }\n />\n );\n }\n\n if (segment.type === 'link') {\n return (\n <UserCiteChip\n key={ `u-${ segment.cite }-${ index }` }\n item={ {\n id: segment.url,\n type: 'link',\n label: segment.host,\n tooltip: segment.url,\n href: segment.url,\n } }\n onOpenPath={ onOpenPath }\n />\n );\n }\n\n return (\n <UserCiteChip\n key={ `l-${ segment.cite }-${ index }` }\n item={ {\n id: attachedLayerRefKey(segment.layer),\n type: 'element',\n label: segment.layer.propName\n ? `${ resolveLayerCite(segment.layer) }.${ segment.layer.propName }`\n : (segment.layer.text || segment.layer.label || segment.layer.layerId),\n ...(segment.layer.icon ? { icon: segment.layer.icon } : {}),\n } }\n onOpenPath={ onOpenPath }\n />\n );\n } }\n </For>\n </div>\n );\n}\n\n/** User turn text bubble with collapsible long messages. Renders nothing when empty. */\nexport const MessageUserBubble = observer(() => {\n const turn = useMessageTurn();\n const { onOpenPath } = useChat();\n const [expanded, setExpanded] = useState(false);\n\n if (turn?.role !== 'user' || turn.message.role !== 'user') {\n return null;\n }\n\n const { text, message } = turn;\n const hasText = text.trim().length > 0;\n const hasAttached = Boolean(\n message.metadata?.attached?.files?.length\n || message.metadata?.attached?.layers?.length\n || message.metadata?.skills?.length\n || message.metadata?.commands?.length\n );\n\n if (!hasText && !hasAttached) {\n return null;\n }\n\n const body = (\n <UserMessageBody\n text={ text }\n metadata={ message.metadata }\n onOpenPath={ onOpenPath }\n />\n );\n const collapsible = text.length > MESSAGE_USER_COLLAPSE_MIN_CHARS;\n\n return (\n <div className={ cn(styles.messageBubble, styles.userBubble) }>\n <Show when={ !collapsible }>\n <div className={ styles.parts }>\n { body }\n </div>\n </Show>\n\n <Show when={ collapsible }>\n <Scrollable\n vertical\n edgeFade={ !expanded }\n className={ styles.userMessageScroll }\n containerClass={ styles.userMessageScrollPort }\n contentClass={ styles.parts }\n aria-expanded={ expanded }\n >\n { body }\n </Scrollable>\n <ScrollExpandChevronButton\n expanded={ expanded }\n onPress={ () => setExpanded(v => !v) }\n labelExpand={ USER_COLLAPSE_LABELS.expand }\n labelCollapse={ USER_COLLAPSE_LABELS.collapse }\n className={ styles.userMessageExpandToggle }\n />\n </Show>\n </div>\n );\n});\n"],"names":["MESSAGE_USER_COLLAPSE_MIN_CHARS","USER_COLLAPSE_LABELS","fileCiteChipItem","file","fromPath","chipItemFromPath","codeEditorStateKey","fileChipDisplay","splitUserMessageSegments","text","metadata","files","layers","slashNames","cites","cite","fileCite","layer","formatLayerCite","raw","name","match","url","host","a","b","segments","remaining","bestIdx","best","entry","idx","segment","UserCiteChip","item","onOpenPath","Chip","styles","UserMessageBody","MessageText","cn","For","index","Badge","attachedLayerRefKey","resolveLayerCite","MessageUserBubble","observer","turn","useMessageTurn","useChat","expanded","setExpanded","useState","message","hasText","hasAttached","body","collapsible","_jsx","Show","_jsxs","Scrollable","ScrollExpandChevronButton","v"],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { MessageSegment } from '../normalize/normalizeMessageParts';
2
+ export type FinishedWorkSplit = {
3
+ work: readonly MessageSegment[];
4
+ answer: MessageSegment;
5
+ };
6
+ /**
7
+ * 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
+ */
10
+ export declare function splitFinishedWork(segments: readonly MessageSegment[], flags: {
11
+ streaming: boolean;
12
+ cancelled: boolean;
13
+ }): FinishedWorkSplit | null;
14
+ /** "Worked for 25s" / "Worked for 2m". "Worked" when the turn has no timestamps. */
15
+ export declare function workedForLabel(startedAt: number | undefined, endedAt: number | undefined): string;
16
+ //# sourceMappingURL=workedCollapse.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,31 @@
1
+ import { isTextUIPart as l } from "ai";
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")
8
+ return null;
9
+ const o = f(n.groups);
10
+ return !o || !l(o) || o.state === "streaming" || !o.text.trim() ? null : {
11
+ work: r.slice(0, -1),
12
+ answer: n
13
+ };
14
+ }
15
+ function h(r, e) {
16
+ if (r == null || e == null || !Number.isFinite(r) || !Number.isFinite(e) || e < r)
17
+ return "Worked";
18
+ const n = Math.max(1, Math.round((e - r) / 1e3));
19
+ if (n < 60)
20
+ 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`;
26
+ }
27
+ export {
28
+ a as splitFinishedWork,
29
+ h as workedForLabel
30
+ };
31
+ //# sourceMappingURL=workedCollapse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workedCollapse.js","sources":["../../../../src/chat/Message/turn/workedCollapse.ts"],"sourcesContent":["import { isTextUIPart } from 'ai';\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,4 +1,14 @@
1
1
  import type { ToolRendererProps } from '../types';
2
+ /** One composer strip: who is running, and the child thread Show opens. */
3
+ export declare function subagentComposerCopy(part: {
4
+ input?: unknown;
5
+ output?: unknown;
6
+ }, patch: Record<string, unknown> | undefined): {
7
+ text: string;
8
+ label: string;
9
+ childThreadId?: string;
10
+ waiting: boolean;
11
+ };
2
12
  /** Live subtitle for a tool — same copy for Agent rows and the compact rail task. */
3
13
  export declare function subagentActivityLabel(patch: Record<string, unknown> | undefined): string | undefined;
4
14
  /** Live/waiting subtitle. Done rows stay quiet — no empty "Completed". */
@@ -1 +1 @@
1
- {"version":3,"file":"AgentTool.d.ts","sourceRoot":"","sources":["../../../../src/chat/Tool/agent/AgentTool.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AA6IlD,qFAAqF;AACrF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CA0BpG;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,MAAM,GAAG,SAAS,CAcrB;AAiFD,mDAAmD;AACnD,eAAO,MAAM,SAAS,cAAuB,iBAAiB;8BAAjB,iBAAiB;CA6C5D,CAAC"}
1
+ {"version":3,"file":"AgentTool.d.ts","sourceRoot":"","sources":["../../../../src/chat/Tool/agent/AgentTool.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AA6IlD,2EAA2E;AAC3E,wBAAgB,oBAAoB,CAChC,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,EAC3C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAC3C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAmB3E;AAED,qFAAqF;AACrF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CA0BpG;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,MAAM,GAAG,SAAS,CAcrB;AAiFD,mDAAmD;AACnD,eAAO,MAAM,SAAS,cAAuB,iBAAiB;8BAAjB,iBAAiB;CA6C5D,CAAC"}