@open-mercato/ui 0.6.2-develop.3406.1.2b403f40da → 0.6.2-develop.3446.1.bd060c6017

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 (34) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/ai/AiAssistantLauncher.js +11 -10
  3. package/dist/ai/AiAssistantLauncher.js.map +2 -2
  4. package/dist/ai/AiChat.js +218 -83
  5. package/dist/ai/AiChat.js.map +2 -2
  6. package/dist/ai/AiIcon.js +1 -1
  7. package/dist/ai/AiIcon.js.map +2 -2
  8. package/dist/ai/parts/FieldDiffCard.js +4 -3
  9. package/dist/ai/parts/FieldDiffCard.js.map +2 -2
  10. package/dist/ai/useAiChat.js +98 -2
  11. package/dist/ai/useAiChat.js.map +2 -2
  12. package/dist/backend/DataTable.js +4 -13
  13. package/dist/backend/DataTable.js.map +2 -2
  14. package/dist/primitives/date-format.js +53 -0
  15. package/dist/primitives/date-format.js.map +7 -0
  16. package/dist/primitives/date-picker.js +3 -26
  17. package/dist/primitives/date-picker.js.map +2 -2
  18. package/dist/primitives/date-range-picker.js +3 -22
  19. package/dist/primitives/date-range-picker.js.map +2 -2
  20. package/package.json +4 -4
  21. package/src/ai/AiAssistantLauncher.tsx +11 -10
  22. package/src/ai/AiChat.tsx +200 -26
  23. package/src/ai/AiIcon.tsx +1 -1
  24. package/src/ai/__tests__/AiChat.task-plan.test.tsx +356 -0
  25. package/src/ai/__tests__/AiChat.test.tsx +44 -2
  26. package/src/ai/parts/FieldDiffCard.tsx +4 -3
  27. package/src/ai/parts/__tests__/FieldDiffCard.test.tsx +24 -0
  28. package/src/ai/parts/types.ts +3 -0
  29. package/src/ai/useAiChat.ts +154 -1
  30. package/src/backend/DataTable.tsx +12 -22
  31. package/src/primitives/__tests__/date-format.test.ts +43 -0
  32. package/src/primitives/date-format.ts +60 -0
  33. package/src/primitives/date-picker.tsx +5 -17
  34. package/src/primitives/date-range-picker.tsx +3 -13
package/dist/ai/AiChat.js CHANGED
@@ -2,17 +2,32 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import {
5
+ Activity,
6
+ AlertTriangle,
7
+ BadgeQuestionMark,
5
8
  Bot,
9
+ ChartNoAxesCombined,
6
10
  Check,
7
11
  ChevronDown,
8
12
  ChevronRight,
13
+ Circle,
14
+ CircleAlert,
15
+ CircleCheck,
16
+ CircleDashed,
9
17
  Copy,
18
+ Database,
19
+ FileQuestion,
20
+ GitBranch,
21
+ Handshake,
10
22
  Lightbulb,
23
+ ListChecks,
11
24
  Loader2,
12
25
  Paperclip,
13
26
  Plus,
27
+ Search,
14
28
  Send,
15
29
  Square,
30
+ TrendingDown,
16
31
  User,
17
32
  Wrench,
18
33
  X
@@ -42,6 +57,7 @@ import {
42
57
  import { useAiChatUpload } from "./useAiChatUpload.js";
43
58
  import { useAiShortcuts } from "./useAiShortcuts.js";
44
59
  import { LoopTracePanel } from "./LoopTracePanel.js";
60
+ import { AiIcon } from "./AiIcon.js";
45
61
  const PREVIEW_DATA_URL_MAX_BYTES = 2 * 1024 * 1024;
46
62
  const COMPACT_FOOTER_MAX_WIDTH = 640;
47
63
  const MODEL_PICKER_STORAGE_PREFIX = "om-ai-model-picker:";
@@ -192,80 +208,153 @@ function safeStringify(value) {
192
208
  return String(value);
193
209
  }
194
210
  }
211
+ function titleWords(value) {
212
+ return value.replace(/\b[a-z]/g, (char) => char.toUpperCase());
213
+ }
214
+ function formatToolCaption(call) {
215
+ const rawCaption = call.caption?.trim();
216
+ if (!rawCaption) return call.toolName;
217
+ const caption = titleWords(rawCaption.replace(/\s*·\s*/g, " - "));
218
+ return `${caption} (${call.toolName})`;
219
+ }
220
+ function AiChatTaskPlan({ tasks }) {
221
+ const t = useT();
222
+ if (!tasks || tasks.length === 0) return null;
223
+ return /* @__PURE__ */ jsxs(
224
+ "div",
225
+ {
226
+ className: "rounded-md border border-border bg-muted/30",
227
+ "data-ai-chat-task-plan": "",
228
+ "data-ai-chat-task-plan-count": tasks.length,
229
+ children: [
230
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-border/60 px-2 py-1.5 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: [
231
+ /* @__PURE__ */ jsx(ListChecks, { className: "size-3.5 text-muted-foreground", "aria-hidden": true }),
232
+ /* @__PURE__ */ jsx("span", { children: t("ai_assistant.chat.taskPlanTitle", "Plan") }),
233
+ /* @__PURE__ */ jsx("span", { className: "ml-auto font-mono", children: tasks.length })
234
+ ] }),
235
+ /* @__PURE__ */ jsx("ul", { className: "divide-y divide-border/60", "data-ai-chat-task-plan-list": "", children: tasks.map((task) => /* @__PURE__ */ jsx(AiChatTaskRow, { task }, task.id)) })
236
+ ]
237
+ }
238
+ );
239
+ }
240
+ function AiChatTaskRow({ task }) {
241
+ const t = useT();
242
+ const statusLabel = task.state === "running" ? t("ai_assistant.chat.taskRunning", "running\u2026") : task.state === "done" ? t("ai_assistant.chat.taskDone", "done") : task.state === "failed" ? t("ai_assistant.chat.taskFailed", "failed") : task.state === "skipped" ? t("ai_assistant.chat.taskSkipped", "skipped") : t("ai_assistant.chat.taskPending", "pending");
243
+ const Icon = task.state === "running" ? Loader2 : task.state === "done" ? CircleCheck : task.state === "failed" ? CircleAlert : task.state === "skipped" ? CircleDashed : Circle;
244
+ const iconClassName = cn(
245
+ "size-3.5 shrink-0",
246
+ task.state === "running" && "animate-spin text-muted-foreground",
247
+ task.state === "done" && "text-status-success-icon",
248
+ task.state === "failed" && "text-destructive",
249
+ task.state === "skipped" && "text-muted-foreground",
250
+ task.state === "pending" && "text-muted-foreground"
251
+ );
252
+ const statusBadgeClassName = cn(
253
+ "ml-auto text-[10px] uppercase tracking-wide",
254
+ task.state === "done" && "text-status-success-text",
255
+ task.state === "failed" && "text-destructive",
256
+ task.state !== "done" && task.state !== "failed" && "text-muted-foreground"
257
+ );
258
+ return /* @__PURE__ */ jsxs(
259
+ "li",
260
+ {
261
+ className: "flex h-7 items-center gap-2 px-2 text-xs",
262
+ "data-ai-chat-task-id": task.id,
263
+ "data-ai-chat-task-state": task.state,
264
+ "data-ai-chat-task-source": task.source,
265
+ "data-ai-chat-task-tool-call-id": task.toolCallId,
266
+ children: [
267
+ /* @__PURE__ */ jsx(Icon, { className: iconClassName, "aria-hidden": true }),
268
+ /* @__PURE__ */ jsx("span", { className: "truncate", title: task.detail ?? task.label, children: task.label }),
269
+ /* @__PURE__ */ jsx("span", { className: statusBadgeClassName, children: statusLabel })
270
+ ]
271
+ }
272
+ );
273
+ }
195
274
  function ToolCallList({ toolCalls }) {
196
275
  const t = useT();
197
276
  const [openId, setOpenId] = React.useState(null);
198
277
  if (!toolCalls || toolCalls.length === 0) return null;
199
- return /* @__PURE__ */ jsxs("div", { className: "space-y-1", "data-ai-chat-tool-calls": "", children: [
200
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-0.5 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: [
201
- /* @__PURE__ */ jsx("span", { children: t("ai_assistant.chat.agentTasksTitle", "Agent tasks") }),
202
- /* @__PURE__ */ jsx("span", { children: toolCalls.length })
203
- ] }),
204
- toolCalls.map((call) => {
205
- const isOpen = openId === call.id;
206
- const isError = call.state === "error";
207
- const isPending = call.state === "pending";
208
- const isComplete = call.state === "complete";
209
- const statusLabel = isError ? t("ai_assistant.chat.toolError", "failed") : isPending ? t("ai_assistant.chat.toolRunning", "running\u2026") : t("ai_assistant.chat.toolDone", "done");
210
- return /* @__PURE__ */ jsxs(
211
- "div",
212
- {
213
- className: cn(
214
- "rounded-md border border-border bg-muted/30",
215
- isError ? "border-destructive/40 bg-destructive/5" : ""
216
- ),
217
- "data-ai-chat-tool-call": call.toolName,
218
- "data-ai-chat-tool-state": call.state,
219
- children: [
220
- /* @__PURE__ */ jsxs(
221
- "button",
222
- {
223
- type: "button",
224
- onClick: () => setOpenId(isOpen ? null : call.id),
225
- className: "flex w-full items-center gap-2 px-2 py-1.5 text-left text-xs font-medium hover:bg-muted/60",
226
- children: [
227
- isOpen ? /* @__PURE__ */ jsx(ChevronDown, { className: "size-3.5 text-muted-foreground", "aria-hidden": true }) : /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5 text-muted-foreground", "aria-hidden": true }),
228
- isPending ? /* @__PURE__ */ jsx(Loader2, { className: "size-3.5 animate-spin text-muted-foreground", "aria-hidden": true }) : /* @__PURE__ */ jsx(
229
- Wrench,
230
- {
231
- className: cn(
232
- "size-3.5",
233
- isError ? "text-destructive" : "text-muted-foreground"
234
- ),
235
- "aria-hidden": true
236
- }
237
- ),
238
- /* @__PURE__ */ jsx("span", { className: "font-mono", children: call.toolName }),
239
- /* @__PURE__ */ jsx(
240
- "span",
241
- {
242
- className: cn(
243
- "ml-auto text-[10px] uppercase tracking-wide",
244
- isError ? "text-destructive" : isComplete ? "text-status-success-text" : "text-muted-foreground"
278
+ return /* @__PURE__ */ jsxs(
279
+ "div",
280
+ {
281
+ className: "rounded-md border border-border bg-muted/30",
282
+ "data-ai-chat-tool-calls": "",
283
+ "data-ai-chat-tool-call-count": toolCalls.length,
284
+ children: [
285
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-border/60 px-2 py-1.5 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: [
286
+ /* @__PURE__ */ jsx(Wrench, { className: "size-3.5 text-muted-foreground", "aria-hidden": true }),
287
+ /* @__PURE__ */ jsx("span", { children: t("ai_assistant.chat.agentTasksTitle", "Tool calls") }),
288
+ /* @__PURE__ */ jsx("span", { className: "ml-auto font-mono", children: toolCalls.length })
289
+ ] }),
290
+ /* @__PURE__ */ jsx("ul", { className: "divide-y divide-border/60", children: toolCalls.map((call) => {
291
+ const isOpen = openId === call.id;
292
+ const isError = call.state === "error";
293
+ const isPending = call.state === "pending";
294
+ const isComplete = call.state === "complete";
295
+ const statusLabel = isError ? t("ai_assistant.chat.toolError", "failed") : isPending ? t("ai_assistant.chat.toolRunning", "running\u2026") : t("ai_assistant.chat.toolDone", "done");
296
+ return /* @__PURE__ */ jsxs(
297
+ "li",
298
+ {
299
+ className: cn(
300
+ isError ? "bg-destructive/5" : ""
301
+ ),
302
+ "data-ai-chat-tool-call": call.toolName,
303
+ "data-ai-chat-tool-state": call.state,
304
+ children: [
305
+ /* @__PURE__ */ jsxs(
306
+ Button,
307
+ {
308
+ variant: "ghost",
309
+ size: "2xs",
310
+ type: "button",
311
+ onClick: () => setOpenId(isOpen ? null : call.id),
312
+ className: "w-full justify-start rounded-none px-2 text-left hover:bg-muted/60",
313
+ children: [
314
+ isOpen ? /* @__PURE__ */ jsx(ChevronDown, { className: "size-3.5 text-muted-foreground", "aria-hidden": true }) : /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5 text-muted-foreground", "aria-hidden": true }),
315
+ isPending ? /* @__PURE__ */ jsx(Loader2, { className: "size-3.5 animate-spin text-muted-foreground", "aria-hidden": true }) : /* @__PURE__ */ jsx(
316
+ Wrench,
317
+ {
318
+ className: cn(
319
+ "size-3.5",
320
+ isError ? "text-destructive" : "text-muted-foreground"
321
+ ),
322
+ "aria-hidden": true
323
+ }
245
324
  ),
246
- children: statusLabel
247
- }
248
- )
249
- ]
250
- }
251
- ),
252
- isOpen ? /* @__PURE__ */ jsxs("div", { className: "space-y-1 border-t border-border/60 px-2 py-1.5 text-xs", children: [
253
- call.input !== void 0 ? /* @__PURE__ */ jsxs("div", { children: [
254
- /* @__PURE__ */ jsx("div", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: t("ai_assistant.chat.toolInput", "Input") }),
255
- /* @__PURE__ */ jsx("pre", { className: "mt-0.5 max-h-32 overflow-auto rounded bg-background p-1.5 font-mono text-[11px]", children: safeStringify(call.input) })
256
- ] }) : null,
257
- call.output !== void 0 && !isError ? /* @__PURE__ */ jsxs("div", { children: [
258
- /* @__PURE__ */ jsx("div", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: t("ai_assistant.chat.toolOutput", "Output") }),
259
- /* @__PURE__ */ jsx("pre", { className: "mt-0.5 max-h-32 overflow-auto rounded bg-background p-1.5 font-mono text-[11px]", children: safeStringify(call.output) })
260
- ] }) : null,
261
- call.errorMessage ? /* @__PURE__ */ jsx("div", { className: "text-destructive", children: call.errorMessage }) : null
262
- ] }) : null
263
- ]
264
- },
265
- call.id
266
- );
267
- })
268
- ] });
325
+ /* @__PURE__ */ jsx("span", { className: "truncate", title: formatToolCaption(call), children: formatToolCaption(call) }),
326
+ /* @__PURE__ */ jsx(
327
+ "span",
328
+ {
329
+ className: cn(
330
+ "ml-auto text-[10px] uppercase tracking-wide",
331
+ isError ? "text-destructive" : isComplete ? "text-status-success-text" : "text-muted-foreground"
332
+ ),
333
+ children: statusLabel
334
+ }
335
+ )
336
+ ]
337
+ }
338
+ ),
339
+ isOpen ? /* @__PURE__ */ jsxs("div", { className: "space-y-1 border-t border-border/60 px-2 py-1.5 text-xs", children: [
340
+ call.input !== void 0 ? /* @__PURE__ */ jsxs("div", { children: [
341
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: t("ai_assistant.chat.toolInput", "Input") }),
342
+ /* @__PURE__ */ jsx("pre", { className: "mt-0.5 max-h-32 overflow-auto rounded bg-background p-1.5 font-mono text-[11px]", children: safeStringify(call.input) })
343
+ ] }) : null,
344
+ call.output !== void 0 && !isError ? /* @__PURE__ */ jsxs("div", { children: [
345
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: t("ai_assistant.chat.toolOutput", "Output") }),
346
+ /* @__PURE__ */ jsx("pre", { className: "mt-0.5 max-h-32 overflow-auto rounded bg-background p-1.5 font-mono text-[11px]", children: safeStringify(call.output) })
347
+ ] }) : null,
348
+ call.errorMessage ? /* @__PURE__ */ jsx("div", { className: "text-destructive", children: call.errorMessage }) : null
349
+ ] }) : null
350
+ ]
351
+ },
352
+ call.id
353
+ );
354
+ }) })
355
+ ]
356
+ }
357
+ );
269
358
  }
270
359
  function ReasoningPanel({ text, streaming }) {
271
360
  const t = useT();
@@ -313,6 +402,7 @@ function MessageRow({
313
402
  const Icon = isAssistant ? Bot : User;
314
403
  const [copied, setCopied] = React.useState(false);
315
404
  const copyTimerRef = React.useRef(null);
405
+ const visibleTaskPlan = isAssistant && message.taskPlan ? message.taskPlan.filter((task) => task.source === "agent") : [];
316
406
  React.useEffect(() => {
317
407
  return () => {
318
408
  if (copyTimerRef.current) clearTimeout(copyTimerRef.current);
@@ -406,6 +496,7 @@ function MessageRow({
406
496
  streaming: message.reasoningStreaming === true
407
497
  }
408
498
  ) : null,
499
+ visibleTaskPlan.length > 0 ? /* @__PURE__ */ jsx(AiChatTaskPlan, { tasks: visibleTaskPlan }) : null,
409
500
  isAssistant && message.toolCalls && message.toolCalls.length > 0 ? /* @__PURE__ */ jsx(ToolCallList, { toolCalls: message.toolCalls }) : null,
410
501
  /* @__PURE__ */ jsx(MessageContent, { content: message.content, isAssistant }),
411
502
  isAssistant && registry && message.uiParts && message.uiParts.length > 0 ? /* @__PURE__ */ jsx(
@@ -489,6 +580,45 @@ function AiUiPartRenderer({
489
580
  }
490
581
  );
491
582
  }
583
+ function getSuggestionIcon(suggestion) {
584
+ if (suggestion.icon) return { icon: suggestion.icon, name: "custom" };
585
+ const text = `${suggestion.label} ${suggestion.prompt}`.toLowerCase();
586
+ const iconClassName = "size-4 text-foreground";
587
+ if (/\b(analy[sz]e|analysis|health|score|summary|summari[sz]e)\b/.test(text)) {
588
+ return { icon: /* @__PURE__ */ jsx(ChartNoAxesCombined, { className: iconClassName }), name: "analysis" };
589
+ }
590
+ if (/\b(at[-\s]?risk|risk|stalled|stale|overdue|danger)\b/.test(text)) {
591
+ return { icon: /* @__PURE__ */ jsx(AlertTriangle, { className: iconClassName }), name: "risk" };
592
+ }
593
+ if (/\bpipeline|stage|funnel\b/.test(text)) {
594
+ return { icon: /* @__PURE__ */ jsx(GitBranch, { className: iconClassName }), name: "pipeline" };
595
+ }
596
+ if (/\bdeal|opportunit(?:y|ies)\b/.test(text)) {
597
+ return { icon: /* @__PURE__ */ jsx(Handshake, { className: iconClassName }), name: "deal" };
598
+ }
599
+ if (/\bdata|access|record|records|schema\b/.test(text)) {
600
+ return { icon: /* @__PURE__ */ jsx(Database, { className: iconClassName }), name: "data" };
601
+ }
602
+ if (/\bwhat can you help|help me|capabilit(?:y|ies)\b/.test(text)) {
603
+ return { icon: /* @__PURE__ */ jsx(BadgeQuestionMark, { className: iconClassName }), name: "capabilities" };
604
+ }
605
+ if (/\bsuggest|try|idea|ideas|recommend\b/.test(text)) {
606
+ return { icon: /* @__PURE__ */ jsx(Lightbulb, { className: iconClassName }), name: "ideas" };
607
+ }
608
+ if (/\bhow do i|how to|use this|instructions?|guide\b/.test(text)) {
609
+ return { icon: /* @__PURE__ */ jsx(FileQuestion, { className: iconClassName }), name: "guide" };
610
+ }
611
+ if (/\bfind|search|show|list|lookup\b/.test(text)) {
612
+ return { icon: /* @__PURE__ */ jsx(Search, { className: iconClassName }), name: "search" };
613
+ }
614
+ if (/\bactivity|activities|timeline|recent\b/.test(text)) {
615
+ return { icon: /* @__PURE__ */ jsx(Activity, { className: iconClassName }), name: "activity" };
616
+ }
617
+ if (/\bdown|drop|declin|loss|lost\b/.test(text)) {
618
+ return { icon: /* @__PURE__ */ jsx(TrendingDown, { className: iconClassName }), name: "trend-down" };
619
+ }
620
+ return { icon: /* @__PURE__ */ jsx(AiIcon, { className: iconClassName }), name: "ai" };
621
+ }
492
622
  function WelcomeState({
493
623
  title,
494
624
  description,
@@ -514,20 +644,25 @@ function WelcomeState({
514
644
  /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: heading }),
515
645
  /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: desc })
516
646
  ] }),
517
- suggestions && suggestions.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex w-full max-w-md flex-col gap-2", "data-ai-chat-suggestions": "", children: suggestions.map((suggestion, index) => /* @__PURE__ */ jsxs(
518
- "button",
519
- {
520
- type: "button",
521
- className: "flex items-center gap-2 rounded-lg border border-border bg-card px-3 py-2.5 text-left text-sm transition-colors hover:bg-accent hover:text-accent-foreground",
522
- onClick: () => onSuggestionClick(suggestion.prompt),
523
- "data-ai-chat-suggestion": index,
524
- children: [
525
- suggestion.icon ? /* @__PURE__ */ jsx("span", { className: "shrink-0 text-muted-foreground", "aria-hidden": true, children: suggestion.icon }) : null,
526
- /* @__PURE__ */ jsx("span", { children: suggestion.label })
527
- ]
528
- },
529
- index
530
- )) }) : null
647
+ suggestions && suggestions.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex w-full max-w-md flex-col gap-2", "data-ai-chat-suggestions": "", children: suggestions.map((suggestion, index) => {
648
+ const suggestionIcon = getSuggestionIcon(suggestion);
649
+ return /* @__PURE__ */ jsxs(
650
+ Button,
651
+ {
652
+ variant: "outline",
653
+ type: "button",
654
+ className: "h-auto w-full justify-start whitespace-normal rounded-lg bg-card px-3 py-2.5 text-left text-sm",
655
+ onClick: () => onSuggestionClick(suggestion.prompt),
656
+ "data-ai-chat-suggestion": index,
657
+ "data-ai-chat-suggestion-icon": suggestionIcon.name,
658
+ children: [
659
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-muted-foreground", "aria-hidden": true, children: suggestionIcon.icon }),
660
+ /* @__PURE__ */ jsx("span", { children: suggestion.label })
661
+ ]
662
+ },
663
+ index
664
+ );
665
+ }) }) : null
531
666
  ] });
532
667
  }
533
668
  function ContextItemsPill({ items }) {