dishui 0.0.89 → 0.0.97

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 (106) hide show
  1. package/dist/.release-verified.json +6 -0
  2. package/dist/ai.js +22 -0
  3. package/dist/components/Card/Metric2.js +11 -11
  4. package/dist/components/Card/Metric2.js.map +1 -1
  5. package/dist/components/Card/index.d.ts +20 -0
  6. package/dist/components/Card/index.js +184 -179
  7. package/dist/components/Card/index.js.map +1 -1
  8. package/dist/components/GroupButton/index.js +4 -4
  9. package/dist/components/GroupButton/index.js.map +1 -1
  10. package/dist/components/GroupedTable/index.js +10 -10
  11. package/dist/components/GroupedTable/index.js.map +1 -1
  12. package/dist/components/NavigationList/index.js +9 -9
  13. package/dist/components/NavigationList/index.js.map +1 -1
  14. package/dist/components/Section/index.js +11 -11
  15. package/dist/components/Section/index.js.map +1 -1
  16. package/dist/components/Skeleton/index.js +1 -1
  17. package/dist/components/Skeleton/index.js.map +1 -1
  18. package/dist/components/Table/index.d.ts +23 -2
  19. package/dist/components/Table/index.js +153 -101
  20. package/dist/components/Table/index.js.map +1 -1
  21. package/dist/components/ai/AgentChat/index.d.ts +54 -0
  22. package/dist/components/ai/AgentChat/index.js +176 -0
  23. package/dist/components/ai/AgentChat/index.js.map +1 -0
  24. package/dist/components/ai/AgentScreen/index.d.ts +50 -0
  25. package/dist/components/ai/AgentScreen/index.js +233 -0
  26. package/dist/components/ai/AgentScreen/index.js.map +1 -0
  27. package/dist/components/ai/ApprovalCard/index.d.ts +26 -0
  28. package/dist/components/ai/ApprovalCard/index.js +89 -0
  29. package/dist/components/ai/ApprovalCard/index.js.map +1 -0
  30. package/dist/components/ai/CodeView/index.d.ts +30 -0
  31. package/dist/components/ai/CodeView/index.js +149 -0
  32. package/dist/components/ai/CodeView/index.js.map +1 -0
  33. package/dist/components/ai/CommandSearch/index.d.ts +39 -0
  34. package/dist/components/ai/CommandSearch/index.js +217 -0
  35. package/dist/components/ai/CommandSearch/index.js.map +1 -0
  36. package/dist/components/ai/ContextCards/index.d.ts +42 -0
  37. package/dist/components/ai/ContextCards/index.js +106 -0
  38. package/dist/components/ai/ContextCards/index.js.map +1 -0
  39. package/dist/components/ai/DiffTable/index.d.ts +70 -0
  40. package/dist/components/ai/DiffTable/index.js +235 -0
  41. package/dist/components/ai/DiffTable/index.js.map +1 -0
  42. package/dist/components/ai/FilterTable/index.d.ts +35 -0
  43. package/dist/components/ai/FilterTable/index.js +86 -0
  44. package/dist/components/ai/FilterTable/index.js.map +1 -0
  45. package/dist/components/ai/FineTuneCard/index.d.ts +24 -0
  46. package/dist/components/ai/FineTuneCard/index.js +75 -0
  47. package/dist/components/ai/FineTuneCard/index.js.map +1 -0
  48. package/dist/components/ai/InsightCards/index.d.ts +23 -0
  49. package/dist/components/ai/InsightCards/index.js +64 -0
  50. package/dist/components/ai/InsightCards/index.js.map +1 -0
  51. package/dist/components/ai/LoadingState/index.d.ts +22 -0
  52. package/dist/components/ai/LoadingState/index.js +80 -0
  53. package/dist/components/ai/LoadingState/index.js.map +1 -0
  54. package/dist/components/ai/PromptBar/index.d.ts +26 -0
  55. package/dist/components/ai/PromptBar/index.js +117 -0
  56. package/dist/components/ai/PromptBar/index.js.map +1 -0
  57. package/dist/components/ai/RecommendationCard/index.d.ts +44 -0
  58. package/dist/components/ai/RecommendationCard/index.js +142 -0
  59. package/dist/components/ai/RecommendationCard/index.js.map +1 -0
  60. package/dist/components/ai/RecordsTable/index.d.ts +65 -0
  61. package/dist/components/ai/RecordsTable/index.js +334 -0
  62. package/dist/components/ai/RecordsTable/index.js.map +1 -0
  63. package/dist/components/ai/SelectionActions/index.d.ts +19 -0
  64. package/dist/components/ai/SelectionActions/index.js +40 -0
  65. package/dist/components/ai/SelectionActions/index.js.map +1 -0
  66. package/dist/components/ai/StateFlow/index.d.ts +24 -0
  67. package/dist/components/ai/StateFlow/index.js +50 -0
  68. package/dist/components/ai/StateFlow/index.js.map +1 -0
  69. package/dist/components/ai/StreamingText/index.d.ts +44 -0
  70. package/dist/components/ai/StreamingText/index.js +211 -0
  71. package/dist/components/ai/StreamingText/index.js.map +1 -0
  72. package/dist/components/ai/TaskRows/index.d.ts +53 -0
  73. package/dist/components/ai/TaskRows/index.js +186 -0
  74. package/dist/components/ai/TaskRows/index.js.map +1 -0
  75. package/dist/components/ai/Thinking/index.d.ts +47 -0
  76. package/dist/components/ai/Thinking/index.js +197 -0
  77. package/dist/components/ai/Thinking/index.js.map +1 -0
  78. package/dist/components/ai/ToolChips/index.d.ts +21 -0
  79. package/dist/components/ai/ToolChips/index.js +63 -0
  80. package/dist/components/ai/ToolChips/index.js.map +1 -0
  81. package/dist/components/ai/index.d.ts +50 -0
  82. package/dist/components/ai/shared.d.ts +13 -0
  83. package/dist/components/ai/shared.js +25 -0
  84. package/dist/components/ai/shared.js.map +1 -0
  85. package/dist/components/pro/DataTable/DataTable.js +70 -51
  86. package/dist/components/pro/DataTable/DataTable.js.map +1 -1
  87. package/dist/components/pro/DataTable/components/TableBody.d.ts +3 -1
  88. package/dist/components/pro/DataTable/components/TableBody.js +41 -39
  89. package/dist/components/pro/DataTable/components/TableBody.js.map +1 -1
  90. package/dist/components/pro/DataTable/components/TableToolbar.d.ts +2 -1
  91. package/dist/components/pro/DataTable/components/TableToolbar.js +25 -19
  92. package/dist/components/pro/DataTable/components/TableToolbar.js.map +1 -1
  93. package/dist/components/pro/DataTable/index.d.ts +1 -1
  94. package/dist/components/pro/DataTable/rowDetailExtension.js +1 -1
  95. package/dist/components/pro/DataTable/rowDetailExtension.js.map +1 -1
  96. package/dist/components/pro/DataTable/types.d.ts +19 -0
  97. package/dist/data-table.js.map +1 -1
  98. package/dist/dishui.css +1 -1
  99. package/dist/index.d.ts +5 -4
  100. package/dist/index.js +128 -107
  101. package/dist/layouts/Sidebar.d.ts +16 -0
  102. package/dist/layouts/Sidebar.js +85 -52
  103. package/dist/layouts/Sidebar.js.map +1 -1
  104. package/dist/lite.d.ts +2 -0
  105. package/dist/lite.js +22 -21
  106. package/package.json +10 -3
@@ -0,0 +1,64 @@
1
+ import { cn as e } from "../../../utils/cn.js";
2
+ /* empty css */
3
+ import { useState as t } from "react";
4
+ import { Fragment as n, jsx as r, jsxs as i } from "react/jsx-runtime";
5
+ //#region src/components/ai/InsightCards/index.tsx
6
+ function a({ items: a, columns: o = 3, selectedId: s, defaultSelectedId: c, onSelect: l, loading: u = !1, emptyText: d = "No insights available", className: f, ...p }) {
7
+ let [m, h] = t(c), g = s ?? m, _ = (e) => {
8
+ s === void 0 && h(e.id), l?.(e);
9
+ };
10
+ if (!u && !a.length) return /* @__PURE__ */ r("div", {
11
+ ...p,
12
+ className: e("dishui-ai-insight-cards dishui-ai-insight-cards--empty", f),
13
+ children: d
14
+ });
15
+ let v = u ? Array.from({ length: o }, (e, t) => ({
16
+ id: `loading-${t}`,
17
+ label: "",
18
+ value: ""
19
+ })) : a;
20
+ return /* @__PURE__ */ r("div", {
21
+ ...p,
22
+ className: e("dishui-ai-insight-cards", `dishui-ai-insight-cards--${o}`, f),
23
+ children: v.map((t) => {
24
+ let a = /* @__PURE__ */ i(n, { children: [
25
+ /* @__PURE__ */ r("span", {
26
+ className: "dishui-ai-insight-cards__label",
27
+ children: t.label
28
+ }),
29
+ /* @__PURE__ */ r("strong", {
30
+ className: "dishui-ai-insight-cards__value",
31
+ children: t.value
32
+ }),
33
+ t.trend && /* @__PURE__ */ i("span", {
34
+ className: e("dishui-ai-insight-cards__trend", `dishui-ai-insight-cards__trend--${t.trend.direction}`),
35
+ children: [
36
+ t.trend.direction === "up" ? "↑" : t.trend.direction === "down" ? "↓" : "→",
37
+ " ",
38
+ t.trend.value,
39
+ t.trend.label && /* @__PURE__ */ i("span", { children: [" ", t.trend.label] })
40
+ ]
41
+ }),
42
+ t.detail && /* @__PURE__ */ r("span", {
43
+ className: "dishui-ai-insight-cards__detail",
44
+ children: t.detail
45
+ }),
46
+ u && /* @__PURE__ */ i(n, { children: [/* @__PURE__ */ r("span", { className: "dishui-ai-insight-cards__skeleton" }), /* @__PURE__ */ r("span", { className: "dishui-ai-insight-cards__skeleton dishui-ai-insight-cards__skeleton--short" })] })
47
+ ] });
48
+ return l ? /* @__PURE__ */ r("button", {
49
+ type: "button",
50
+ className: e("dishui-ai-insight-cards__card", t.status && `dishui-ai-insight-cards__card--${t.status}`, g === t.id && "dishui-ai-insight-cards__card--selected"),
51
+ "aria-pressed": g === t.id,
52
+ onClick: () => _(t),
53
+ children: a
54
+ }, t.id) : /* @__PURE__ */ r("article", {
55
+ className: e("dishui-ai-insight-cards__card", t.status && `dishui-ai-insight-cards__card--${t.status}`),
56
+ children: a
57
+ }, t.id);
58
+ })
59
+ });
60
+ }
61
+ //#endregion
62
+ export { a as default };
63
+
64
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/ai/InsightCards/index.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport { cn } from '../../../utils/cn';\nimport './InsightCards.css';\n\nexport type InsightCardItem = { id: string; label: React.ReactNode; value: React.ReactNode; detail?: React.ReactNode; trend?: { value: string; direction: 'up' | 'down' | 'flat'; label?: string }; status?: 'neutral' | 'success' | 'warning' | 'danger' | 'info' };\nexport type InsightCardsProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children' | 'onSelect'> & { items: InsightCardItem[]; columns?: 2 | 3 | 4; selectedId?: string; defaultSelectedId?: string; onSelect?: (item: InsightCardItem) => void; loading?: boolean; emptyText?: React.ReactNode };\n\nexport default function InsightCards({ items, columns = 3, selectedId, defaultSelectedId, onSelect, loading = false, emptyText = 'No insights available', className, ...rest }: InsightCardsProps) {\n const [internal, setInternal] = useState(defaultSelectedId); const selected = selectedId ?? internal;\n const select = (item: InsightCardItem) => { if (selectedId === undefined) setInternal(item.id); onSelect?.(item); };\n if (!loading && !items.length) return <div {...rest} className={cn('dishui-ai-insight-cards dishui-ai-insight-cards--empty', className)}>{emptyText}</div>;\n const content = loading ? Array.from({ length: columns }, (_, i) => ({ id: `loading-${i}`, label: '', value: '' } as InsightCardItem)) : items;\n return <div {...rest} className={cn('dishui-ai-insight-cards', `dishui-ai-insight-cards--${columns}`, className)}>{content.map(item => { const body = <><span className=\"dishui-ai-insight-cards__label\">{item.label}</span><strong className=\"dishui-ai-insight-cards__value\">{item.value}</strong>{item.trend && <span className={cn('dishui-ai-insight-cards__trend', `dishui-ai-insight-cards__trend--${item.trend.direction}`)}>{item.trend.direction === 'up' ? '↑' : item.trend.direction === 'down' ? '↓' : '→'} {item.trend.value}{item.trend.label && <span> {item.trend.label}</span>}</span>}{item.detail && <span className=\"dishui-ai-insight-cards__detail\">{item.detail}</span>}{loading && <><span className=\"dishui-ai-insight-cards__skeleton\"/><span className=\"dishui-ai-insight-cards__skeleton dishui-ai-insight-cards__skeleton--short\"/></>}</>; return onSelect ? <button type=\"button\" key={item.id} className={cn('dishui-ai-insight-cards__card', item.status && `dishui-ai-insight-cards__card--${item.status}`, selected === item.id && 'dishui-ai-insight-cards__card--selected')} aria-pressed={selected === item.id} onClick={() => select(item)}>{body}</button> : <article key={item.id} className={cn('dishui-ai-insight-cards__card', item.status && `dishui-ai-insight-cards__card--${item.status}`)}>{body}</article>; })}</div>;\n}\n"],"mappings":";;;;;AAOA,SAAwB,EAAa,EAAE,UAAO,aAAU,GAAG,eAAY,sBAAmB,aAAU,aAAU,IAAO,eAAY,yBAAyB,cAAW,GAAG,KAA2B;CACjM,IAAM,CAAC,GAAU,KAAe,EAAS,CAAiB,GAAS,IAAW,KAAc,GACtF,KAAU,MAA0B;EAAsD,AAAhD,MAAe,KAAA,KAAW,EAAY,EAAK,EAAE,GAAG,IAAW,CAAI;CAAG;CAClH,IAAI,CAAC,KAAW,CAAC,EAAM,QAAQ,OAAO,kBAAC,OAAD;EAAK,GAAI;EAAM,WAAW,EAAG,0DAA0D,CAAS;EAAI,UAAA;CAAe,CAAA;CACzJ,IAAM,IAAU,IAAU,MAAM,KAAK,EAAE,QAAQ,EAAQ,IAAI,GAAG,OAAO;EAAE,IAAI,WAAW;EAAK,OAAO;EAAI,OAAO;CAAG,EAAqB,IAAI;CACzI,OAAO,kBAAC,OAAD;EAAK,GAAI;EAAM,WAAW,EAAG,2BAA2B,4BAA4B,KAAW,CAAS;EAAI,UAAA,EAAQ,KAAI,MAAQ;GAAE,IAAM,IAAO,kBAAA,GAAA,EAAA,UAAA;IAAE,kBAAC,QAAD;KAAM,WAAU;KAAkC,UAAA,EAAK;IAAY,CAAA;IAAC,kBAAC,UAAD;KAAQ,WAAU;KAAkC,UAAA,EAAK;IAAc,CAAA;IAAE,EAAK,SAAS,kBAAC,QAAD;KAAM,WAAW,EAAG,kCAAkC,mCAAmC,EAAK,MAAM,WAAW;KAA/G,UAAA;MAAmH,EAAK,MAAM,cAAc,OAAO,MAAM,EAAK,MAAM,cAAc,SAAS,MAAM;MAAI;MAAE,EAAK,MAAM;MAAO,EAAK,MAAM,SAAS,kBAAC,QAAD,EAAA,UAAA,CAAM,KAAE,EAAK,MAAM,KAAY,EAAA,CAAA;KAAQ;;IAAG,EAAK,UAAU,kBAAC,QAAD;KAAM,WAAU;KAAmC,UAAA,EAAK;IAAa,CAAA;IAAG,KAAW,kBAAA,GAAA,EAAA,UAAA,CAAE,kBAAC,QAAD,EAAM,WAAU,oCAAoC,CAAA,GAAC,kBAAC,QAAD,EAAM,WAAU,6EAA6E,CAAA,CAAG,EAAA,CAAA;GAAI,EAAA,CAAA;GAAG,OAAO,IAAW,kBAAC,UAAD;IAAQ,MAAK;IAAuB,WAAW,EAAG,iCAAiC,EAAK,UAAU,kCAAkC,EAAK,UAAU,MAAa,EAAK,MAAM,yCAAyC;IAAG,gBAAc,MAAa,EAAK;IAAI,eAAe,EAAO,CAAI;IAAI,UAAA;GAAa,GAA3Q,EAAK,EAAsQ,IAAI,kBAAC,WAAD;IAAuB,WAAW,EAAG,iCAAiC,EAAK,UAAU,kCAAkC,EAAK,QAAQ;IAAI,UAAA;GAAc,GAAxI,EAAK,EAAmI;EAAG,CAAC;CAAO,CAAA;AACzyC"}
@@ -0,0 +1,22 @@
1
+ import React from 'react';
2
+ export type LoadingStateVariant = 'grid' | 'drive' | 'dots' | 'orbit' | 'surfer';
3
+ export type LoadingStateProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> & {
4
+ /** Short present-tense verb shown next to the glyph, e.g. `Churning`, `Searching`. */
5
+ label?: React.ReactNode;
6
+ /** Glyph animation. All variants are indeterminate and respect reduced motion. */
7
+ variant?: LoadingStateVariant;
8
+ /** Show the elapsed time next to the label. */
9
+ showElapsed?: boolean;
10
+ /** Epoch milliseconds the work started; defaults to mount time. */
11
+ startedAt?: number;
12
+ /** Controlled elapsed time in milliseconds. When set, the internal timer is disabled. */
13
+ elapsedMs?: number;
14
+ /** `inline` sits in a sentence; `block` renders a bordered row. */
15
+ layout?: 'inline' | 'block';
16
+ size?: 'sm' | 'md';
17
+ };
18
+ /**
19
+ * Indeterminate agent progress: pixel-grid (or sibling) glyph, a short label
20
+ * and the elapsed time. Announces as a polite status region.
21
+ */
22
+ export default function LoadingState({ label, variant, showElapsed, startedAt, elapsedMs, layout, size, className, ...rest }: LoadingStateProps): React.JSX.Element;
@@ -0,0 +1,80 @@
1
+ import { cn as e } from "../../../utils/cn.js";
2
+ import { formatElapsed as t } from "../shared.js";
3
+ /* empty css */
4
+ import { useEffect as n, useState as r } from "react";
5
+ import { Fragment as i, jsx as a, jsxs as o } from "react/jsx-runtime";
6
+ //#region src/components/ai/LoadingState/index.tsx
7
+ var s = (e) => ({ "--i": e }), c = ({ variant: e }) => {
8
+ switch (e) {
9
+ case "drive": return /* @__PURE__ */ a("span", {
10
+ className: "dishui-ai-loading__drive",
11
+ children: Array.from({ length: 4 }, (e, t) => /* @__PURE__ */ a("span", {
12
+ className: "dishui-ai-loading__cell",
13
+ style: s(t)
14
+ }, t))
15
+ });
16
+ case "dots": return /* @__PURE__ */ a("span", {
17
+ className: "dishui-ai-loading__dots",
18
+ children: Array.from({ length: 3 }, (e, t) => /* @__PURE__ */ a("span", {
19
+ className: "dishui-ai-loading__dot",
20
+ style: s(t)
21
+ }, t))
22
+ });
23
+ case "orbit": return /* @__PURE__ */ o(i, { children: [/* @__PURE__ */ a("span", { className: "dishui-ai-loading__orbit" }), /* @__PURE__ */ a("span", { className: "dishui-ai-loading__satellite" })] });
24
+ case "surfer": return /* @__PURE__ */ a("span", {
25
+ className: "dishui-ai-loading__surfer",
26
+ children: Array.from({ length: 5 }, (e, t) => /* @__PURE__ */ a("span", {
27
+ className: "dishui-ai-loading__bar",
28
+ style: s(t)
29
+ }, t))
30
+ });
31
+ default: return /* @__PURE__ */ a("span", {
32
+ className: "dishui-ai-loading__grid",
33
+ children: Array.from({ length: 16 }, (e, t) => {
34
+ let n = Math.floor(t / 4) + t % 4;
35
+ return /* @__PURE__ */ a("span", {
36
+ className: "dishui-ai-loading__cell",
37
+ style: s(n)
38
+ }, t);
39
+ })
40
+ });
41
+ }
42
+ };
43
+ function l({ label: i = "Thinking", variant: s = "grid", showElapsed: l = !0, startedAt: u, elapsedMs: d, layout: f = "inline", size: p = "md", className: m, ...h }) {
44
+ let [g, _] = r(0), v = d !== void 0;
45
+ n(() => {
46
+ if (v || !l) return;
47
+ let e = u ?? Date.now(), t = window.setInterval(() => _(Date.now() - e), 100);
48
+ return () => window.clearInterval(t);
49
+ }, [
50
+ v,
51
+ l,
52
+ u
53
+ ]);
54
+ let y = v ? d : g;
55
+ return /* @__PURE__ */ o("div", {
56
+ role: "status",
57
+ "aria-live": "polite",
58
+ ...h,
59
+ className: e("dishui-ai-loading", `dishui-ai-loading--${s}`, p === "sm" && "dishui-ai-loading--sm", f === "block" && "dishui-ai-loading--block", m),
60
+ children: [
61
+ /* @__PURE__ */ a("span", {
62
+ className: "dishui-ai-loading__glyph",
63
+ "aria-hidden": "true",
64
+ children: /* @__PURE__ */ a(c, { variant: s })
65
+ }),
66
+ /* @__PURE__ */ a("span", {
67
+ className: "dishui-ai-loading__label",
68
+ children: i
69
+ }),
70
+ l && /* @__PURE__ */ a("span", {
71
+ className: "dishui-ai-loading__elapsed",
72
+ children: t(y)
73
+ })
74
+ ]
75
+ });
76
+ }
77
+ //#endregion
78
+ export { l as default };
79
+
80
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/ai/LoadingState/index.tsx"],"sourcesContent":["import React, { useEffect, useState } from 'react';\nimport { cn } from '../../../utils/cn';\nimport { formatElapsed } from '../shared';\nimport './LoadingState.css';\n\nexport type LoadingStateVariant = 'grid' | 'drive' | 'dots' | 'orbit' | 'surfer';\n\nexport type LoadingStateProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> & {\n /** Short present-tense verb shown next to the glyph, e.g. `Churning`, `Searching`. */\n label?: React.ReactNode;\n /** Glyph animation. All variants are indeterminate and respect reduced motion. */\n variant?: LoadingStateVariant;\n /** Show the elapsed time next to the label. */\n showElapsed?: boolean;\n /** Epoch milliseconds the work started; defaults to mount time. */\n startedAt?: number;\n /** Controlled elapsed time in milliseconds. When set, the internal timer is disabled. */\n elapsedMs?: number;\n /** `inline` sits in a sentence; `block` renders a bordered row. */\n layout?: 'inline' | 'block';\n size?: 'sm' | 'md';\n};\n\nconst index = (i: number) => ({ '--i': i } as React.CSSProperties);\n\nconst Glyph: React.FC<{ variant: LoadingStateVariant }> = ({ variant }) => {\n switch (variant) {\n case 'drive':\n return (\n <span className=\"dishui-ai-loading__drive\">\n {Array.from({ length: 4 }, (_, i) => <span key={i} className=\"dishui-ai-loading__cell\" style={index(i)} />)}\n </span>\n );\n case 'dots':\n return (\n <span className=\"dishui-ai-loading__dots\">\n {Array.from({ length: 3 }, (_, i) => <span key={i} className=\"dishui-ai-loading__dot\" style={index(i)} />)}\n </span>\n );\n case 'orbit':\n return (\n <>\n <span className=\"dishui-ai-loading__orbit\" />\n <span className=\"dishui-ai-loading__satellite\" />\n </>\n );\n case 'surfer':\n return (\n <span className=\"dishui-ai-loading__surfer\">\n {Array.from({ length: 5 }, (_, i) => <span key={i} className=\"dishui-ai-loading__bar\" style={index(i)} />)}\n </span>\n );\n default:\n return (\n <span className=\"dishui-ai-loading__grid\">\n {Array.from({ length: 16 }, (_, i) => {\n // Diagonal sweep: delay grows with row + column.\n const delay = Math.floor(i / 4) + (i % 4);\n return <span key={i} className=\"dishui-ai-loading__cell\" style={index(delay)} />;\n })}\n </span>\n );\n }\n};\n\n/**\n * Indeterminate agent progress: pixel-grid (or sibling) glyph, a short label\n * and the elapsed time. Announces as a polite status region.\n */\nexport default function LoadingState({\n label = 'Thinking',\n variant = 'grid',\n showElapsed = true,\n startedAt,\n elapsedMs,\n layout = 'inline',\n size = 'md',\n className,\n ...rest\n}: LoadingStateProps) {\n const [internalElapsed, setInternalElapsed] = useState(0);\n const controlled = elapsedMs !== undefined;\n\n useEffect(() => {\n if (controlled || !showElapsed) return;\n const start = startedAt ?? Date.now();\n const id = window.setInterval(() => setInternalElapsed(Date.now() - start), 100);\n return () => window.clearInterval(id);\n }, [controlled, showElapsed, startedAt]);\n\n const elapsed = controlled ? elapsedMs : internalElapsed;\n\n return (\n <div\n role=\"status\"\n aria-live=\"polite\"\n {...rest}\n className={cn(\n 'dishui-ai-loading',\n `dishui-ai-loading--${variant}`,\n size === 'sm' && 'dishui-ai-loading--sm',\n layout === 'block' && 'dishui-ai-loading--block',\n className,\n )}\n >\n <span className=\"dishui-ai-loading__glyph\" aria-hidden=\"true\">\n <Glyph variant={variant} />\n </span>\n <span className=\"dishui-ai-loading__label\">{label}</span>\n {showElapsed && <span className=\"dishui-ai-loading__elapsed\">{formatElapsed(elapsed)}</span>}\n </div>\n );\n}\n"],"mappings":";;;;;;AAuBA,IAAM,KAAS,OAAe,EAAE,OAAO,EAAE,IAEnC,KAAqD,EAAE,iBAAc;CACzE,QAAQ,GAAR;EACE,KAAK,SACH,OACE,kBAAC,QAAD;GAAM,WAAU;GACb,UAAA,MAAM,KAAK,EAAE,QAAQ,EAAE,IAAI,GAAG,MAAM,kBAAC,QAAD;IAAc,WAAU;IAA0B,OAAO,EAAM,CAAC;GAAI,GAAzD,CAAyD,CAAC;EACtG,CAAA;EAEV,KAAK,QACH,OACE,kBAAC,QAAD;GAAM,WAAU;GACb,UAAA,MAAM,KAAK,EAAE,QAAQ,EAAE,IAAI,GAAG,MAAM,kBAAC,QAAD;IAAc,WAAU;IAAyB,OAAO,EAAM,CAAC;GAAI,GAAxD,CAAwD,CAAC;EACrG,CAAA;EAEV,KAAK,SACH,OACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,QAAD,EAAM,WAAU,2BAA4B,CAAA,GAC5C,kBAAC,QAAD,EAAM,WAAU,+BAAgC,CAAA,CAChD,EAAA,CAAA;EAEN,KAAK,UACH,OACE,kBAAC,QAAD;GAAM,WAAU;GACb,UAAA,MAAM,KAAK,EAAE,QAAQ,EAAE,IAAI,GAAG,MAAM,kBAAC,QAAD;IAAc,WAAU;IAAyB,OAAO,EAAM,CAAC;GAAI,GAAxD,CAAwD,CAAC;EACrG,CAAA;EAEV,SACE,OACE,kBAAC,QAAD;GAAM,WAAU;GACb,UAAA,MAAM,KAAK,EAAE,QAAQ,GAAG,IAAI,GAAG,MAAM;IAEpC,IAAM,IAAQ,KAAK,MAAM,IAAI,CAAC,IAAK,IAAI;IACvC,OAAO,kBAAC,QAAD;KAAc,WAAU;KAA0B,OAAO,EAAM,CAAK;IAAI,GAA7D,CAA6D;GACjF,CAAC;EACG,CAAA;CAEZ;AACF;AAMA,SAAwB,EAAa,EACnC,WAAQ,YACR,aAAU,QACV,iBAAc,IACd,cACA,cACA,YAAS,UACT,UAAO,MACP,cACA,GAAG,KACiB;CACpB,IAAM,CAAC,GAAiB,KAAsB,EAAS,CAAC,GAClD,IAAa,MAAc,KAAA;CAEjC,QAAgB;EACd,IAAI,KAAc,CAAC,GAAa;EAChC,IAAM,IAAQ,KAAa,KAAK,IAAI,GAC9B,IAAK,OAAO,kBAAkB,EAAmB,KAAK,IAAI,IAAI,CAAK,GAAG,GAAG;EAC/E,aAAa,OAAO,cAAc,CAAE;CACtC,GAAG;EAAC;EAAY;EAAa;CAAS,CAAC;CAEvC,IAAM,IAAU,IAAa,IAAY;CAEzC,OACE,kBAAC,OAAD;EACE,MAAK;EACL,aAAU;EACV,GAAI;EACJ,WAAW,EACT,qBACA,sBAAsB,KACtB,MAAS,QAAQ,yBACjB,MAAW,WAAW,4BACtB,CACF;EAVF,UAAA;GAYE,kBAAC,QAAD;IAAM,WAAU;IAA2B,eAAY;IACrD,UAAA,kBAAC,GAAD,EAAgB,WAAU,CAAA;GACtB,CAAA;GACN,kBAAC,QAAD;IAAM,WAAU;IAA4B,UAAA;GAAY,CAAA;GACvD,KAAe,kBAAC,QAAD;IAAM,WAAU;IAA8B,UAAA,EAAc,CAAO;GAAQ,CAAA;EACxF;;AAET"}
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+ export type PromptAttachment = {
3
+ id: string;
4
+ name: string;
5
+ meta?: string;
6
+ };
7
+ export type PromptBarProps = Omit<React.FormHTMLAttributes<HTMLFormElement>, 'onSubmit' | 'onChange'> & {
8
+ value?: string;
9
+ defaultValue?: string;
10
+ onChange?: (value: string) => void;
11
+ onSubmit?: (value: string) => void;
12
+ placeholder?: string;
13
+ label?: string;
14
+ busy?: boolean;
15
+ disabled?: boolean;
16
+ maxLength?: number;
17
+ attachments?: PromptAttachment[];
18
+ onRemoveAttachment?: (attachment: PromptAttachment) => void;
19
+ /** Controls rendered at the left side of the bottom toolbar. */
20
+ leading?: React.ReactNode;
21
+ /** Controls rendered before the character count and send button. */
22
+ actions?: React.ReactNode;
23
+ submitLabel?: string;
24
+ };
25
+ /** Composer assembled from DishUI Textarea and Button primitives. */
26
+ export default function PromptBar({ value, defaultValue, onChange, onSubmit, placeholder, label, busy, disabled, maxLength, attachments, onRemoveAttachment, leading, actions, submitLabel, className, ...rest }: PromptBarProps): React.JSX.Element;
@@ -0,0 +1,117 @@
1
+ import { cn as e } from "../../../utils/cn.js";
2
+ import t from "../../Button/index.js";
3
+ import n from "../../Textarea/index.js";
4
+ /* empty css */
5
+ import { useId as r, useRef as i, useState as a } from "react";
6
+ import { jsx as o, jsxs as s } from "react/jsx-runtime";
7
+ //#region src/components/ai/PromptBar/index.tsx
8
+ var c = [], l = ({ busy: e }) => e ? /* @__PURE__ */ o("span", {
9
+ className: "dishui-ai-prompt-bar__busy",
10
+ "aria-hidden": "true",
11
+ children: "•••"
12
+ }) : /* @__PURE__ */ s("svg", {
13
+ "aria-hidden": "true",
14
+ width: "16",
15
+ height: "16",
16
+ viewBox: "0 0 24 24",
17
+ fill: "none",
18
+ stroke: "currentColor",
19
+ strokeWidth: "2",
20
+ strokeLinecap: "round",
21
+ strokeLinejoin: "round",
22
+ children: [/* @__PURE__ */ o("path", { d: "m5 12 7-7 7 7" }), /* @__PURE__ */ o("path", { d: "M12 19V5" })]
23
+ });
24
+ function u({ value: u, defaultValue: d = "", onChange: f, onSubmit: p, placeholder: m = "Ask the agent…", label: h = "Prompt", busy: g = !1, disabled: _ = !1, maxLength: v, attachments: y = c, onRemoveAttachment: b, leading: x, actions: S, submitLabel: C = "Send", className: w, ...T }) {
25
+ let E = r(), D = i(null), [O, k] = a(d), A = u ?? O, j = (e) => {
26
+ u === void 0 && k(e), f?.(e);
27
+ let t = D.current;
28
+ t && (t.style.height = "auto", t.style.height = `${Math.min(t.scrollHeight, 160)}px`);
29
+ }, M = () => {
30
+ let e = A.trim();
31
+ e && !g && !_ && p?.(e);
32
+ };
33
+ return /* @__PURE__ */ s("form", {
34
+ ...T,
35
+ className: e("dishui-ai-prompt-bar", w),
36
+ onSubmit: (e) => {
37
+ e.preventDefault(), M();
38
+ },
39
+ "aria-busy": g || void 0,
40
+ children: [y.length > 0 && /* @__PURE__ */ o("ul", {
41
+ className: "dishui-ai-prompt-bar__attachments",
42
+ "aria-label": "Attachments",
43
+ children: y.map((e) => /* @__PURE__ */ s("li", { children: [
44
+ /* @__PURE__ */ o("span", {
45
+ title: e.name,
46
+ children: e.name
47
+ }),
48
+ e.meta && /* @__PURE__ */ o("small", { children: e.meta }),
49
+ b && /* @__PURE__ */ o(t, {
50
+ type: "button",
51
+ variant: "ghost",
52
+ size: "xs",
53
+ square: !0,
54
+ "aria-label": `Remove ${e.name}`,
55
+ disabled: _ || g,
56
+ onClick: () => b(e),
57
+ children: "×"
58
+ })
59
+ ] }, e.id))
60
+ }), /* @__PURE__ */ s("div", {
61
+ className: "dishui-ai-prompt-bar__main",
62
+ children: [
63
+ x && /* @__PURE__ */ o("div", {
64
+ className: "dishui-ai-prompt-bar__leading",
65
+ children: x
66
+ }),
67
+ /* @__PURE__ */ o("div", {
68
+ className: "dishui-ai-prompt-bar__editor",
69
+ children: /* @__PURE__ */ o(n, {
70
+ ref: D,
71
+ id: E,
72
+ "aria-label": h,
73
+ rows: 1,
74
+ value: A,
75
+ maxLength: v,
76
+ disabled: _,
77
+ placeholder: m,
78
+ wrapperClassName: "dishui-ai-prompt-bar__textarea-wrap",
79
+ className: "dishui-ai-prompt-bar__textarea",
80
+ onChange: (e) => j(e.target.value),
81
+ onKeyDown: (e) => {
82
+ e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing && (e.preventDefault(), M());
83
+ }
84
+ })
85
+ }),
86
+ /* @__PURE__ */ s("div", {
87
+ className: "dishui-ai-prompt-bar__actions",
88
+ children: [
89
+ S,
90
+ v !== void 0 && /* @__PURE__ */ s("span", {
91
+ className: "dishui-ai-prompt-bar__count",
92
+ children: [
93
+ A.length,
94
+ "/",
95
+ v
96
+ ]
97
+ }),
98
+ /* @__PURE__ */ o(t, {
99
+ type: "submit",
100
+ variant: "primary",
101
+ size: "sm",
102
+ square: !0,
103
+ className: "dishui-ai-prompt-bar__submit",
104
+ disabled: _ || g || !A.trim(),
105
+ "aria-label": C,
106
+ children: /* @__PURE__ */ o(l, { busy: g })
107
+ })
108
+ ]
109
+ })
110
+ ]
111
+ })]
112
+ });
113
+ }
114
+ //#endregion
115
+ export { u as default };
116
+
117
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/ai/PromptBar/index.tsx"],"sourcesContent":["import React, { useId, useRef, useState } from 'react';\nimport Button from '../../Button';\nimport Textarea from '../../Textarea';\nimport { cn } from '../../../utils/cn';\nimport './PromptBar.css';\n\nconst EMPTY_ATTACHMENTS: PromptAttachment[] = [];\n\nexport type PromptAttachment = { id: string; name: string; meta?: string };\nexport type PromptBarProps = Omit<React.FormHTMLAttributes<HTMLFormElement>, 'onSubmit' | 'onChange'> & {\n value?: string;\n defaultValue?: string;\n onChange?: (value: string) => void;\n onSubmit?: (value: string) => void;\n placeholder?: string;\n label?: string;\n busy?: boolean;\n disabled?: boolean;\n maxLength?: number;\n attachments?: PromptAttachment[];\n onRemoveAttachment?: (attachment: PromptAttachment) => void;\n /** Controls rendered at the left side of the bottom toolbar. */\n leading?: React.ReactNode;\n /** Controls rendered before the character count and send button. */\n actions?: React.ReactNode;\n submitLabel?: string;\n};\n\nconst SendIcon = ({ busy }: { busy: boolean }) => busy ? (\n <span className=\"dishui-ai-prompt-bar__busy\" aria-hidden=\"true\">•••</span>\n) : (\n <svg aria-hidden=\"true\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"m5 12 7-7 7 7\" /><path d=\"M12 19V5\" />\n </svg>\n);\n\n/** Composer assembled from DishUI Textarea and Button primitives. */\nexport default function PromptBar({\n value,\n defaultValue = '',\n onChange,\n onSubmit,\n placeholder = 'Ask the agent…',\n label = 'Prompt',\n busy = false,\n disabled = false,\n maxLength,\n attachments = EMPTY_ATTACHMENTS,\n onRemoveAttachment,\n leading,\n actions,\n submitLabel = 'Send',\n className,\n ...rest\n}: PromptBarProps) {\n const id = useId();\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const [internal, setInternal] = useState(defaultValue);\n const text = value ?? internal;\n\n const update = (next: string) => {\n if (value === undefined) setInternal(next);\n onChange?.(next);\n const textarea = textareaRef.current;\n if (textarea) {\n textarea.style.height = 'auto';\n textarea.style.height = `${Math.min(textarea.scrollHeight, 160)}px`;\n }\n };\n const submit = () => {\n const next = text.trim();\n if (next && !busy && !disabled) onSubmit?.(next);\n };\n\n return (\n <form {...rest} className={cn('dishui-ai-prompt-bar', className)} onSubmit={event => { event.preventDefault(); submit(); }} aria-busy={busy || undefined}>\n {attachments.length > 0 && (\n <ul className=\"dishui-ai-prompt-bar__attachments\" aria-label=\"Attachments\">\n {attachments.map(file => (\n <li key={file.id}>\n <span title={file.name}>{file.name}</span>\n {file.meta && <small>{file.meta}</small>}\n {onRemoveAttachment && (\n <Button type=\"button\" variant=\"ghost\" size=\"xs\" square aria-label={`Remove ${file.name}`} disabled={disabled || busy} onClick={() => onRemoveAttachment(file)}>×</Button>\n )}\n </li>\n ))}\n </ul>\n )}\n <div className=\"dishui-ai-prompt-bar__main\">\n {leading && <div className=\"dishui-ai-prompt-bar__leading\">{leading}</div>}\n <div className=\"dishui-ai-prompt-bar__editor\">\n <Textarea\n ref={textareaRef}\n id={id}\n aria-label={label}\n rows={1}\n value={text}\n maxLength={maxLength}\n disabled={disabled}\n placeholder={placeholder}\n wrapperClassName=\"dishui-ai-prompt-bar__textarea-wrap\"\n className=\"dishui-ai-prompt-bar__textarea\"\n onChange={event => update(event.target.value)}\n onKeyDown={event => {\n if (event.key === 'Enter' && !event.shiftKey && !event.nativeEvent.isComposing) {\n event.preventDefault();\n submit();\n }\n }}\n />\n </div>\n <div className=\"dishui-ai-prompt-bar__actions\">\n {actions}\n {maxLength !== undefined && <span className=\"dishui-ai-prompt-bar__count\">{text.length}/{maxLength}</span>}\n <Button\n type=\"submit\"\n variant=\"primary\"\n size=\"sm\"\n square\n className=\"dishui-ai-prompt-bar__submit\"\n disabled={disabled || busy || !text.trim()}\n aria-label={submitLabel}\n >\n <SendIcon busy={busy} />\n </Button>\n </div>\n </div>\n </form>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import React from 'react';
2
+ export type ConfidenceLevel = 'high' | 'medium' | 'low';
3
+ export type Recommendation = {
4
+ title: React.ReactNode;
5
+ detail?: React.ReactNode;
6
+ /** 0–1. */
7
+ confidence: number;
8
+ };
9
+ export type RecommendationAlternative = {
10
+ id: string;
11
+ title: React.ReactNode;
12
+ /** Short judgement shown on the right, e.g. `Needs review`, `No signal`. */
13
+ verdict?: React.ReactNode;
14
+ /** 0–1; when present a small meter is shown next to the verdict. */
15
+ confidence?: number;
16
+ };
17
+ export type RecommendationCardLabels = {
18
+ accept?: string;
19
+ alternatives?: string;
20
+ otherOptions?: string;
21
+ };
22
+ export type RecommendationCardProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> & {
23
+ /** The agent's question, e.g. `Want me to place this restock order?` */
24
+ question: React.ReactNode;
25
+ recommendation: Recommendation;
26
+ alternatives?: RecommendationAlternative[];
27
+ /** Map a 0–1 confidence to its label. */
28
+ confidenceLabel?: (value: number) => string;
29
+ onAccept?: () => void;
30
+ /** When omitted, the button toggles the built-in alternatives list. */
31
+ onAlternatives?: () => void;
32
+ onSelectAlternative?: (alternative: RecommendationAlternative) => void;
33
+ labels?: RecommendationCardLabels;
34
+ /** Accept is in flight: both buttons disabled, Accept marked `aria-busy`. */
35
+ busy?: boolean;
36
+ disabled?: boolean;
37
+ };
38
+ export declare function confidenceLevel(value: number): ConfidenceLevel;
39
+ /**
40
+ * An agent suggestion: the question it is answering, the recommended
41
+ * action with a segmented confidence meter, optional alternatives and a
42
+ * secondary/primary action pair.
43
+ */
44
+ export default function RecommendationCard({ question, recommendation, alternatives, confidenceLabel, onAccept, onAlternatives, onSelectAlternative, labels, busy, disabled, className, ...rest }: RecommendationCardProps): React.JSX.Element;
@@ -0,0 +1,142 @@
1
+ import { cn as e } from "../../../utils/cn.js";
2
+ import t from "../../Button/index.js";
3
+ import { clamp as n } from "../shared.js";
4
+ /* empty css */
5
+ import { useId as r, useState as i } from "react";
6
+ import { Fragment as a, jsx as o, jsxs as s } from "react/jsx-runtime";
7
+ //#region src/components/ai/RecommendationCard/index.tsx
8
+ var c = {
9
+ accept: "Accept",
10
+ alternatives: "Alternatives",
11
+ otherOptions: "Other options"
12
+ }, l = 5;
13
+ function u(e) {
14
+ return e >= .75 ? "high" : e >= .45 ? "medium" : "low";
15
+ }
16
+ var d = (e) => {
17
+ switch (u(e)) {
18
+ case "high": return "High confidence";
19
+ case "medium": return "Medium confidence";
20
+ default: return "Low confidence";
21
+ }
22
+ }, f = ({ value: t, label: r, compact: i }) => {
23
+ let a = n(Number.isFinite(t) ? t : 0, 0, 1), c = a === 0 ? 0 : Math.max(1, Math.round(a * l)), d = u(a);
24
+ return /* @__PURE__ */ s("div", {
25
+ className: e("dishui-ai-recommendation-card__meter", `dishui-ai-recommendation-card__meter--${d}`, i && "dishui-ai-recommendation-card__meter--compact"),
26
+ role: "meter",
27
+ "aria-valuemin": 0,
28
+ "aria-valuemax": 1,
29
+ "aria-valuenow": a,
30
+ "aria-valuetext": r,
31
+ "aria-label": "Confidence",
32
+ children: [/* @__PURE__ */ o("span", {
33
+ className: "dishui-ai-recommendation-card__segments",
34
+ "aria-hidden": "true",
35
+ children: Array.from({ length: l }, (t, n) => /* @__PURE__ */ o("span", { className: e("dishui-ai-recommendation-card__segment", n < c && "dishui-ai-recommendation-card__segment--on") }, n))
36
+ }), !i && /* @__PURE__ */ o("span", {
37
+ className: "dishui-ai-recommendation-card__meter-label",
38
+ children: r
39
+ })]
40
+ });
41
+ };
42
+ function p({ question: n, recommendation: l, alternatives: u, confidenceLabel: p = d, onAccept: m, onAlternatives: h, onSelectAlternative: g, labels: _, busy: v = !1, disabled: y = !1, className: b, ...x }) {
43
+ let S = r(), C = {
44
+ ...c,
45
+ ..._
46
+ }, [w, T] = i(!1), E = !!u?.length, D = `${S}-alternatives`, O = v || y, k = () => {
47
+ h ? h() : T((e) => !e);
48
+ };
49
+ return /* @__PURE__ */ s("div", {
50
+ ...x,
51
+ className: e("dishui-ai-recommendation-card", b),
52
+ children: [
53
+ /* @__PURE__ */ o("div", {
54
+ className: "dishui-ai-recommendation-card__question",
55
+ children: n
56
+ }),
57
+ /* @__PURE__ */ s("div", {
58
+ className: "dishui-ai-recommendation-card__body",
59
+ children: [
60
+ /* @__PURE__ */ o("div", {
61
+ className: "dishui-ai-recommendation-card__title",
62
+ children: l.title
63
+ }),
64
+ l.detail !== void 0 && /* @__PURE__ */ o("div", {
65
+ className: "dishui-ai-recommendation-card__detail",
66
+ children: l.detail
67
+ }),
68
+ /* @__PURE__ */ o(f, {
69
+ value: l.confidence,
70
+ label: p(l.confidence)
71
+ })
72
+ ]
73
+ }),
74
+ E && /* @__PURE__ */ s("div", {
75
+ id: D,
76
+ className: "dishui-ai-recommendation-card__alternatives",
77
+ hidden: !w,
78
+ children: [/* @__PURE__ */ o("div", {
79
+ className: "dishui-ai-recommendation-card__alternatives-title",
80
+ children: C.otherOptions
81
+ }), /* @__PURE__ */ o("ul", {
82
+ className: "dishui-ai-recommendation-card__list",
83
+ children: u.map((e) => {
84
+ let t = /* @__PURE__ */ s(a, { children: [
85
+ /* @__PURE__ */ o("span", {
86
+ className: "dishui-ai-recommendation-card__alt-title",
87
+ children: e.title
88
+ }),
89
+ e.confidence !== void 0 && /* @__PURE__ */ o(f, {
90
+ value: e.confidence,
91
+ label: p(e.confidence),
92
+ compact: !0
93
+ }),
94
+ e.verdict !== void 0 && /* @__PURE__ */ o("span", {
95
+ className: "dishui-ai-recommendation-card__verdict",
96
+ children: e.verdict
97
+ })
98
+ ] });
99
+ return /* @__PURE__ */ o("li", {
100
+ className: "dishui-ai-recommendation-card__alt",
101
+ children: g ? /* @__PURE__ */ o("button", {
102
+ type: "button",
103
+ className: "dishui-ai-recommendation-card__alt-button",
104
+ disabled: O,
105
+ onClick: () => g(e),
106
+ children: t
107
+ }) : /* @__PURE__ */ o("div", {
108
+ className: "dishui-ai-recommendation-card__alt-button dishui-ai-recommendation-card__alt-button--static",
109
+ children: t
110
+ })
111
+ }, e.id);
112
+ })
113
+ })]
114
+ }),
115
+ (m || h || E) && /* @__PURE__ */ s("div", {
116
+ className: "dishui-ai-recommendation-card__actions",
117
+ children: [(h || E) && /* @__PURE__ */ o(t, {
118
+ type: "button",
119
+ variant: "secondary",
120
+ size: "sm",
121
+ disabled: O,
122
+ "aria-expanded": h ? void 0 : w,
123
+ "aria-controls": h || !E ? void 0 : D,
124
+ onClick: k,
125
+ children: C.alternatives
126
+ }), m && /* @__PURE__ */ o(t, {
127
+ type: "button",
128
+ variant: "primary",
129
+ size: "sm",
130
+ disabled: O,
131
+ "aria-busy": v || void 0,
132
+ onClick: m,
133
+ children: C.accept
134
+ })]
135
+ })
136
+ ]
137
+ });
138
+ }
139
+ //#endregion
140
+ export { u as confidenceLevel, p as default };
141
+
142
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/ai/RecommendationCard/index.tsx"],"sourcesContent":["import React, { useId, useState } from 'react';\nimport { cn } from '../../../utils/cn';\nimport Button from '../../Button';\nimport { clamp } from '../shared';\nimport './RecommendationCard.css';\n\nexport type ConfidenceLevel = 'high' | 'medium' | 'low';\n\nexport type Recommendation = {\n title: React.ReactNode;\n detail?: React.ReactNode;\n /** 0–1. */\n confidence: number;\n};\n\nexport type RecommendationAlternative = {\n id: string;\n title: React.ReactNode;\n /** Short judgement shown on the right, e.g. `Needs review`, `No signal`. */\n verdict?: React.ReactNode;\n /** 0–1; when present a small meter is shown next to the verdict. */\n confidence?: number;\n};\n\nexport type RecommendationCardLabels = {\n accept?: string;\n alternatives?: string;\n otherOptions?: string;\n};\n\nexport type RecommendationCardProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> & {\n /** The agent's question, e.g. `Want me to place this restock order?` */\n question: React.ReactNode;\n recommendation: Recommendation;\n alternatives?: RecommendationAlternative[];\n /** Map a 0–1 confidence to its label. */\n confidenceLabel?: (value: number) => string;\n onAccept?: () => void;\n /** When omitted, the button toggles the built-in alternatives list. */\n onAlternatives?: () => void;\n onSelectAlternative?: (alternative: RecommendationAlternative) => void;\n labels?: RecommendationCardLabels;\n /** Accept is in flight: both buttons disabled, Accept marked `aria-busy`. */\n busy?: boolean;\n disabled?: boolean;\n};\n\nconst DEFAULT_LABELS: Required<RecommendationCardLabels> = {\n accept: 'Accept',\n alternatives: 'Alternatives',\n otherOptions: 'Other options',\n};\n\nconst SEGMENTS = 5;\n\nexport function confidenceLevel(value: number): ConfidenceLevel {\n if (value >= 0.75) return 'high';\n if (value >= 0.45) return 'medium';\n return 'low';\n}\n\nconst defaultConfidenceLabel = (value: number) => {\n switch (confidenceLevel(value)) {\n case 'high': return 'High confidence';\n case 'medium': return 'Medium confidence';\n default: return 'Low confidence';\n }\n};\n\nconst ConfidenceMeter: React.FC<{ value: number; label: string; compact?: boolean }> = ({ value, label, compact }) => {\n const v = clamp(Number.isFinite(value) ? value : 0, 0, 1);\n const filled = v === 0 ? 0 : Math.max(1, Math.round(v * SEGMENTS));\n const level = confidenceLevel(v);\n return (\n <div\n className={cn('dishui-ai-recommendation-card__meter', `dishui-ai-recommendation-card__meter--${level}`, compact && 'dishui-ai-recommendation-card__meter--compact')}\n role=\"meter\"\n aria-valuemin={0}\n aria-valuemax={1}\n aria-valuenow={v}\n aria-valuetext={label}\n aria-label=\"Confidence\"\n >\n <span className=\"dishui-ai-recommendation-card__segments\" aria-hidden=\"true\">\n {Array.from({ length: SEGMENTS }, (_, i) => (\n <span key={i} className={cn('dishui-ai-recommendation-card__segment', i < filled && 'dishui-ai-recommendation-card__segment--on')} />\n ))}\n </span>\n {!compact && <span className=\"dishui-ai-recommendation-card__meter-label\">{label}</span>}\n </div>\n );\n};\n\n/**\n * An agent suggestion: the question it is answering, the recommended\n * action with a segmented confidence meter, optional alternatives and a\n * secondary/primary action pair.\n */\nexport default function RecommendationCard({\n question,\n recommendation,\n alternatives,\n confidenceLabel = defaultConfidenceLabel,\n onAccept,\n onAlternatives,\n onSelectAlternative,\n labels,\n busy = false,\n disabled = false,\n className,\n ...rest\n}: RecommendationCardProps) {\n const baseId = useId();\n const text = { ...DEFAULT_LABELS, ...labels };\n const [showAlternatives, setShowAlternatives] = useState(false);\n const hasAlternatives = !!alternatives?.length;\n const listId = `${baseId}-alternatives`;\n const inert = busy || disabled;\n\n const handleAlternatives = () => {\n if (onAlternatives) onAlternatives();\n else setShowAlternatives(v => !v);\n };\n\n return (\n <div {...rest} className={cn('dishui-ai-recommendation-card', className)}>\n <div className=\"dishui-ai-recommendation-card__question\">{question}</div>\n\n <div className=\"dishui-ai-recommendation-card__body\">\n <div className=\"dishui-ai-recommendation-card__title\">{recommendation.title}</div>\n {recommendation.detail !== undefined && (\n <div className=\"dishui-ai-recommendation-card__detail\">{recommendation.detail}</div>\n )}\n <ConfidenceMeter value={recommendation.confidence} label={confidenceLabel(recommendation.confidence)} />\n </div>\n\n {hasAlternatives && (\n <div\n id={listId}\n className=\"dishui-ai-recommendation-card__alternatives\"\n hidden={!showAlternatives}\n >\n <div className=\"dishui-ai-recommendation-card__alternatives-title\">{text.otherOptions}</div>\n <ul className=\"dishui-ai-recommendation-card__list\">\n {alternatives!.map(alt => {\n const inner = (\n <>\n <span className=\"dishui-ai-recommendation-card__alt-title\">{alt.title}</span>\n {alt.confidence !== undefined && (\n <ConfidenceMeter value={alt.confidence} label={confidenceLabel(alt.confidence)} compact />\n )}\n {alt.verdict !== undefined && <span className=\"dishui-ai-recommendation-card__verdict\">{alt.verdict}</span>}\n </>\n );\n return (\n <li key={alt.id} className=\"dishui-ai-recommendation-card__alt\">\n {onSelectAlternative ? (\n <button\n type=\"button\"\n className=\"dishui-ai-recommendation-card__alt-button\"\n disabled={inert}\n onClick={() => onSelectAlternative(alt)}\n >\n {inner}\n </button>\n ) : (\n <div className=\"dishui-ai-recommendation-card__alt-button dishui-ai-recommendation-card__alt-button--static\">{inner}</div>\n )}\n </li>\n );\n })}\n </ul>\n </div>\n )}\n\n {(onAccept || onAlternatives || hasAlternatives) && (\n <div className=\"dishui-ai-recommendation-card__actions\">\n {(onAlternatives || hasAlternatives) && (\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n disabled={inert}\n aria-expanded={onAlternatives ? undefined : showAlternatives}\n aria-controls={onAlternatives || !hasAlternatives ? undefined : listId}\n onClick={handleAlternatives}\n >\n {text.alternatives}\n </Button>\n )}\n {onAccept && (\n <Button type=\"button\" variant=\"primary\" size=\"sm\" disabled={inert} aria-busy={busy || undefined} onClick={onAccept}>\n {text.accept}\n </Button>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import React from 'react';
2
+ export type RecordStrength = 'very-strong' | 'strong' | 'weak' | 'very-weak' | 'none';
3
+ export type RecordRow = {
4
+ id: string;
5
+ name: string;
6
+ location?: string;
7
+ avatar?: React.ReactNode;
8
+ tags: string[];
9
+ /** Epoch ms or ISO string; `null`/undefined = no contact yet. */
10
+ lastInteractionAt?: number | string | null;
11
+ strength: RecordStrength;
12
+ link?: string | null;
13
+ };
14
+ export type RecordsSortKey = 'name' | 'lastInteraction' | 'strength' | 'tags';
15
+ export type RecordsSort = {
16
+ key: RecordsSortKey;
17
+ direction: 'asc' | 'desc';
18
+ };
19
+ export type RecordsTableProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> & {
20
+ records: RecordRow[];
21
+ sort?: RecordsSort | null;
22
+ defaultSort?: RecordsSort | null;
23
+ onSortChange?: (sort: RecordsSort | null) => void;
24
+ /** Tags shown before collapsing into a `+n` chip. */
25
+ maxTags?: number;
26
+ showIndex?: boolean;
27
+ /** `true` computes count / average strength / link count; a node replaces it. */
28
+ footer?: boolean | React.ReactNode;
29
+ onRowClick?: (record: RecordRow) => void;
30
+ selectedId?: string | null;
31
+ stickyHeader?: boolean;
32
+ maxHeight?: string | number;
33
+ /** Reference time for relative dates. Defaults to mount time (set in an effect). */
34
+ now?: number;
35
+ emptyText?: React.ReactNode;
36
+ labels?: Partial<typeof DEFAULT_LABELS>;
37
+ };
38
+ declare const DEFAULT_LABELS: {
39
+ index: string;
40
+ company: string;
41
+ categories: string;
42
+ lastInteraction: string;
43
+ strength: string;
44
+ links: string;
45
+ noContact: string;
46
+ count: string;
47
+ average: string;
48
+ linksCount: string;
49
+ addCalculation: string;
50
+ empty: string;
51
+ };
52
+ export declare const STRENGTH_LABEL: Record<RecordStrength, string>;
53
+ /** none 0 / very-weak 25 / weak 50 / strong 75 / very-strong 100. */
54
+ export declare function strengthPercent(strength: RecordStrength): number;
55
+ /**
56
+ * date-fns-like relative buckets: `just now`, `4 days ago`, `5 weeks ago`,
57
+ * `11 months ago`, `about 1 year ago`, `over 1 year ago`, `almost 2 years ago`.
58
+ */
59
+ export declare function formatRelativeTime(from: number, now: number): string;
60
+ /**
61
+ * CRM-style grid with tag chips, sortable columns, relative interaction
62
+ * dates and a 4-bar connection-strength glyph paired with its label.
63
+ */
64
+ export default function RecordsTable({ records, sort: sortProp, defaultSort, onSortChange, maxTags, showIndex, footer, onRowClick, selectedId, stickyHeader, maxHeight, now: nowProp, emptyText, labels: labelOverrides, className, style, ...rest }: RecordsTableProps): React.JSX.Element;
65
+ export {};