dishui 0.0.91 → 0.0.101

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 (156) hide show
  1. package/dist/.release-verified.json +6 -0
  2. package/dist/ai.js +24 -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/Ticket.d.ts +52 -0
  6. package/dist/components/Card/Ticket2.js +218 -0
  7. package/dist/components/Card/Ticket2.js.map +1 -0
  8. package/dist/components/Card/index.d.ts +32 -0
  9. package/dist/components/Card/index.js +211 -203
  10. package/dist/components/Card/index.js.map +1 -1
  11. package/dist/components/GroupButton/index.js +4 -4
  12. package/dist/components/GroupButton/index.js.map +1 -1
  13. package/dist/components/GroupedTable/index.js +10 -10
  14. package/dist/components/GroupedTable/index.js.map +1 -1
  15. package/dist/components/NavigationList/index.js +9 -9
  16. package/dist/components/NavigationList/index.js.map +1 -1
  17. package/dist/components/Section/index.js +11 -11
  18. package/dist/components/Section/index.js.map +1 -1
  19. package/dist/components/Skeleton/index.js +1 -1
  20. package/dist/components/Skeleton/index.js.map +1 -1
  21. package/dist/components/Table/index.js +6 -6
  22. package/dist/components/Table/index.js.map +1 -1
  23. package/dist/components/ai/AgentChat/index.d.ts +54 -0
  24. package/dist/components/ai/AgentChat/index.js +176 -0
  25. package/dist/components/ai/AgentChat/index.js.map +1 -0
  26. package/dist/components/ai/AgentRunCard/index.d.ts +38 -0
  27. package/dist/components/ai/AgentRunCard/index.js +100 -0
  28. package/dist/components/ai/AgentRunCard/index.js.map +1 -0
  29. package/dist/components/ai/AgentScreen/index.d.ts +50 -0
  30. package/dist/components/ai/AgentScreen/index.js +233 -0
  31. package/dist/components/ai/AgentScreen/index.js.map +1 -0
  32. package/dist/components/ai/ApprovalCard/index.d.ts +26 -0
  33. package/dist/components/ai/ApprovalCard/index.js +89 -0
  34. package/dist/components/ai/ApprovalCard/index.js.map +1 -0
  35. package/dist/components/ai/CodeView/index.d.ts +30 -0
  36. package/dist/components/ai/CodeView/index.js +149 -0
  37. package/dist/components/ai/CodeView/index.js.map +1 -0
  38. package/dist/components/ai/CommandSearch/index.d.ts +39 -0
  39. package/dist/components/ai/CommandSearch/index.js +217 -0
  40. package/dist/components/ai/CommandSearch/index.js.map +1 -0
  41. package/dist/components/ai/ContextCards/index.d.ts +42 -0
  42. package/dist/components/ai/ContextCards/index.js +106 -0
  43. package/dist/components/ai/ContextCards/index.js.map +1 -0
  44. package/dist/components/ai/DiffTable/index.d.ts +70 -0
  45. package/dist/components/ai/DiffTable/index.js +235 -0
  46. package/dist/components/ai/DiffTable/index.js.map +1 -0
  47. package/dist/components/ai/FilterTable/index.d.ts +35 -0
  48. package/dist/components/ai/FilterTable/index.js +86 -0
  49. package/dist/components/ai/FilterTable/index.js.map +1 -0
  50. package/dist/components/ai/FineTuneCard/index.d.ts +24 -0
  51. package/dist/components/ai/FineTuneCard/index.js +75 -0
  52. package/dist/components/ai/FineTuneCard/index.js.map +1 -0
  53. package/dist/components/ai/InsightCards/index.d.ts +23 -0
  54. package/dist/components/ai/InsightCards/index.js +64 -0
  55. package/dist/components/ai/InsightCards/index.js.map +1 -0
  56. package/dist/components/ai/IssueCard/index.d.ts +39 -0
  57. package/dist/components/ai/IssueCard/index.js +78 -0
  58. package/dist/components/ai/IssueCard/index.js.map +1 -0
  59. package/dist/components/ai/LoadingState/index.d.ts +22 -0
  60. package/dist/components/ai/LoadingState/index.js +80 -0
  61. package/dist/components/ai/LoadingState/index.js.map +1 -0
  62. package/dist/components/ai/PromptBar/index.d.ts +26 -0
  63. package/dist/components/ai/PromptBar/index.js +117 -0
  64. package/dist/components/ai/PromptBar/index.js.map +1 -0
  65. package/dist/components/ai/RecommendationCard/index.d.ts +44 -0
  66. package/dist/components/ai/RecommendationCard/index.js +142 -0
  67. package/dist/components/ai/RecommendationCard/index.js.map +1 -0
  68. package/dist/components/ai/RecordsTable/index.d.ts +65 -0
  69. package/dist/components/ai/RecordsTable/index.js +334 -0
  70. package/dist/components/ai/RecordsTable/index.js.map +1 -0
  71. package/dist/components/ai/SelectionActions/index.d.ts +19 -0
  72. package/dist/components/ai/SelectionActions/index.js +40 -0
  73. package/dist/components/ai/SelectionActions/index.js.map +1 -0
  74. package/dist/components/ai/StateFlow/index.d.ts +24 -0
  75. package/dist/components/ai/StateFlow/index.js +50 -0
  76. package/dist/components/ai/StateFlow/index.js.map +1 -0
  77. package/dist/components/ai/StreamingText/index.d.ts +44 -0
  78. package/dist/components/ai/StreamingText/index.js +211 -0
  79. package/dist/components/ai/StreamingText/index.js.map +1 -0
  80. package/dist/components/ai/TaskRows/index.d.ts +53 -0
  81. package/dist/components/ai/TaskRows/index.js +186 -0
  82. package/dist/components/ai/TaskRows/index.js.map +1 -0
  83. package/dist/components/ai/Thinking/index.d.ts +47 -0
  84. package/dist/components/ai/Thinking/index.js +197 -0
  85. package/dist/components/ai/Thinking/index.js.map +1 -0
  86. package/dist/components/ai/ToolChips/index.d.ts +21 -0
  87. package/dist/components/ai/ToolChips/index.js +63 -0
  88. package/dist/components/ai/ToolChips/index.js.map +1 -0
  89. package/dist/components/ai/index.d.ts +54 -0
  90. package/dist/components/ai/shared.d.ts +13 -0
  91. package/dist/components/ai/shared.js +25 -0
  92. package/dist/components/ai/shared.js.map +1 -0
  93. package/dist/components/pro/DataTable/rowDetailExtension.js +1 -1
  94. package/dist/components/pro/DataTable/rowDetailExtension.js.map +1 -1
  95. package/dist/components/pro/Flow/constants.js +3 -3
  96. package/dist/components/pro/Flow/constants.js.map +1 -1
  97. package/dist/components/pro/Flow/nodes/shapes.js +3 -3
  98. package/dist/components/pro/Flow/nodes/shapes.js.map +1 -1
  99. package/dist/components/pro/Flow/utils/themeColor.d.ts +2 -2
  100. package/dist/components/pro/Flow/utils/themeColor.js +2 -2
  101. package/dist/components/pro/Flow/utils/themeColor.js.map +1 -1
  102. package/dist/components/pro/Kanban/Kanban.d.ts +3 -0
  103. package/dist/components/pro/Kanban/Kanban.js +339 -0
  104. package/dist/components/pro/Kanban/Kanban.js.map +1 -0
  105. package/dist/components/pro/Kanban/Swimlanes.d.ts +3 -0
  106. package/dist/components/pro/Kanban/Swimlanes.js +60 -0
  107. package/dist/components/pro/Kanban/Swimlanes.js.map +1 -0
  108. package/dist/components/pro/Kanban/index.d.ts +5 -0
  109. package/dist/components/pro/Kanban/types.d.ts +103 -0
  110. package/dist/components/pro/Kanban/useKanbanDrag.d.ts +17 -0
  111. package/dist/components/pro/Kanban/useKanbanDrag.js +69 -0
  112. package/dist/components/pro/Kanban/useKanbanDrag.js.map +1 -0
  113. package/dist/components/pro/Kanban/utils.d.ts +13 -0
  114. package/dist/components/pro/Kanban/utils.js +48 -0
  115. package/dist/components/pro/Kanban/utils.js.map +1 -0
  116. package/dist/components/pro/RichTextEditor/AIRewrite.js +158 -127
  117. package/dist/components/pro/RichTextEditor/AIRewrite.js.map +1 -1
  118. package/dist/components/pro/RichTextEditor/BlockHandle.d.ts +2 -0
  119. package/dist/components/pro/RichTextEditor/BlockHandle.js +156 -115
  120. package/dist/components/pro/RichTextEditor/BlockHandle.js.map +1 -1
  121. package/dist/components/pro/RichTextEditor/RichTextEditor.js +136 -159
  122. package/dist/components/pro/RichTextEditor/RichTextEditor.js.map +1 -1
  123. package/dist/components/pro/RichTextEditor/Toolbar/ColorPicker.js +44 -41
  124. package/dist/components/pro/RichTextEditor/Toolbar/ColorPicker.js.map +1 -1
  125. package/dist/components/pro/RichTextEditor/Toolbar/FloatingDialogMenu.js +59 -53
  126. package/dist/components/pro/RichTextEditor/Toolbar/FloatingDialogMenu.js.map +1 -1
  127. package/dist/components/pro/RichTextEditor/Toolbar/MarkdownTemplates.js +48 -44
  128. package/dist/components/pro/RichTextEditor/Toolbar/MarkdownTemplates.js.map +1 -1
  129. package/dist/components/pro/RichTextEditor/Toolbar/TableOpsMenu.js +67 -63
  130. package/dist/components/pro/RichTextEditor/Toolbar/TableOpsMenu.js.map +1 -1
  131. package/dist/components/pro/RichTextEditor/Toolbar/Toolbar.d.ts +1 -0
  132. package/dist/components/pro/RichTextEditor/Toolbar/Toolbar.js +182 -129
  133. package/dist/components/pro/RichTextEditor/Toolbar/Toolbar.js.map +1 -1
  134. package/dist/components/pro/RichTextEditor/Toolbar/ToolbarButton.d.ts +1 -0
  135. package/dist/components/pro/RichTextEditor/Toolbar/ToolbarButton.js +3 -3
  136. package/dist/components/pro/RichTextEditor/Toolbar/ToolbarButton.js.map +1 -1
  137. package/dist/components/pro/RichTextEditor/Toolbar/useAnchoredDropdown.d.ts +3 -0
  138. package/dist/components/pro/RichTextEditor/Toolbar/useAnchoredDropdown.js +31 -0
  139. package/dist/components/pro/RichTextEditor/Toolbar/useAnchoredDropdown.js.map +1 -0
  140. package/dist/components/pro/RichTextEditor/index.d.ts +1 -1
  141. package/dist/components/pro/RichTextEditor/types.d.ts +78 -11
  142. package/dist/components/pro/RichTextEditor/types.js +11 -0
  143. package/dist/components/pro/RichTextEditor/types.js.map +1 -1
  144. package/dist/dishui.css +1 -1
  145. package/dist/index-nostyled.d.ts +1 -1
  146. package/dist/index.d.ts +6 -3
  147. package/dist/index.js +134 -107
  148. package/dist/kanban.js +5 -0
  149. package/dist/layouts/Sidebar.d.ts +16 -0
  150. package/dist/layouts/Sidebar.js +85 -52
  151. package/dist/layouts/Sidebar.js.map +1 -1
  152. package/dist/utils/i18n.js +20 -0
  153. package/dist/utils/i18n.js.map +1 -1
  154. package/dist/utils/menu.js +1 -1
  155. package/dist/utils/menu.js.map +1 -1
  156. package/package.json +14 -3
@@ -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 {};
@@ -0,0 +1,334 @@
1
+ import { cn as e } from "../../../utils/cn.js";
2
+ /* empty css */
3
+ import { useEffect as t, useMemo as n, useState as r } from "react";
4
+ import { Fragment as i, jsx as a, jsxs as o } from "react/jsx-runtime";
5
+ //#region src/components/ai/RecordsTable/index.tsx
6
+ var s = {
7
+ index: "#",
8
+ company: "Company",
9
+ categories: "Categories",
10
+ lastInteraction: "Last interaction",
11
+ strength: "Connection strength",
12
+ links: "Links",
13
+ noContact: "No contact",
14
+ count: "count",
15
+ average: "average",
16
+ linksCount: "links",
17
+ addCalculation: "Add calculation",
18
+ empty: "No records"
19
+ }, c = {
20
+ "very-strong": "Very strong",
21
+ strong: "Strong",
22
+ weak: "Weak",
23
+ "very-weak": "Very weak",
24
+ none: "No communication"
25
+ }, l = {
26
+ none: 0,
27
+ "very-weak": 1,
28
+ weak: 2,
29
+ strong: 3,
30
+ "very-strong": 4
31
+ };
32
+ function u(e) {
33
+ return l[e] * 25;
34
+ }
35
+ var d = 6e4, f = 60 * d, p = 24 * f, m = 7 * p, h = 30.44 * p, g = 365.25 * p, _ = (e) => `${e} year${e === 1 ? "" : "s"} ago`;
36
+ function v(e, t) {
37
+ let n = Math.max(0, t - e);
38
+ if (n < d) return "just now";
39
+ if (n < f) {
40
+ let e = Math.round(n / d);
41
+ return `${e} minute${e === 1 ? "" : "s"} ago`;
42
+ }
43
+ if (n < p) {
44
+ let e = Math.round(n / f);
45
+ return `${e} hour${e === 1 ? "" : "s"} ago`;
46
+ }
47
+ if (n < m) {
48
+ let e = Math.round(n / p);
49
+ return e <= 1 ? "yesterday" : `${e} days ago`;
50
+ }
51
+ if (n < 2 * h) {
52
+ let e = Math.round(n / m);
53
+ return `${e} week${e === 1 ? "" : "s"} ago`;
54
+ }
55
+ if (n < g) return `${Math.round(n / h)} months ago`;
56
+ let r = n / g, i = Math.floor(r), a = r - i;
57
+ return a < .25 ? `about ${_(i)}` : a < .75 ? `over ${_(i)}` : `almost ${_(i + 1)}`;
58
+ }
59
+ var y = (e) => {
60
+ if (e == null) return null;
61
+ let t = typeof e == "number" ? e : Date.parse(e);
62
+ return Number.isFinite(t) ? t : null;
63
+ }, b = (e) => new Date(e).toISOString().slice(0, 10), x = (e) => {
64
+ try {
65
+ return new URL(e.includes("://") ? e : `https://${e}`).hostname.replace(/^www\./, "");
66
+ } catch {
67
+ return e;
68
+ }
69
+ }, S = (e, t, n) => {
70
+ switch (n) {
71
+ case "name": return e.name.localeCompare(t.name);
72
+ case "strength": return l[e.strength] - l[t.strength];
73
+ case "tags": return e.tags.length - t.tags.length;
74
+ case "lastInteraction": return (y(e.lastInteractionAt) ?? -Infinity) - (y(t.lastInteractionAt) ?? -Infinity);
75
+ default: return 0;
76
+ }
77
+ }, C = ({ direction: e }) => /* @__PURE__ */ o("svg", {
78
+ width: "12",
79
+ height: "12",
80
+ viewBox: "0 0 24 24",
81
+ fill: "none",
82
+ stroke: "currentColor",
83
+ strokeWidth: "2",
84
+ strokeLinecap: "round",
85
+ strokeLinejoin: "round",
86
+ "aria-hidden": "true",
87
+ children: [e !== "desc" && /* @__PURE__ */ a("path", {
88
+ d: "m7 10 5-5 5 5",
89
+ opacity: e === "asc" ? 1 : .4
90
+ }), e !== "asc" && /* @__PURE__ */ a("path", {
91
+ d: "m7 14 5 5 5-5",
92
+ opacity: e === "desc" ? 1 : .4
93
+ })]
94
+ }), w = ({ strength: t }) => {
95
+ let n = l[t];
96
+ return /* @__PURE__ */ a("span", {
97
+ className: "dishui-ai-records-table__signal",
98
+ "aria-hidden": "true",
99
+ children: Array.from({ length: 4 }, (t, r) => /* @__PURE__ */ a("span", { className: e("dishui-ai-records-table__bar", r < n && "dishui-ai-records-table__bar--on") }, r))
100
+ });
101
+ }, T = ({ label: t, sortKey: n, sort: r, onToggle: i, className: s }) => {
102
+ let c = r?.key === n ? r.direction : void 0;
103
+ return /* @__PURE__ */ a("th", {
104
+ scope: "col",
105
+ className: e("dishui-ai-records-table__th", s),
106
+ "aria-sort": c ? c === "asc" ? "ascending" : "descending" : "none",
107
+ children: /* @__PURE__ */ o("button", {
108
+ type: "button",
109
+ className: e("dishui-ai-records-table__sort", c && "dishui-ai-records-table__sort--active"),
110
+ onClick: () => i(n),
111
+ children: [/* @__PURE__ */ a("span", { children: t }), /* @__PURE__ */ a(C, { direction: c })]
112
+ })
113
+ });
114
+ };
115
+ function E({ records: l, sort: d, defaultSort: f = null, onSortChange: p, maxTags: m = 3, showIndex: h = !0, footer: g = !0, onRowClick: _, selectedId: C, stickyHeader: E = !1, maxHeight: D, now: O, emptyText: k, labels: A, className: j, style: M, ...N }) {
116
+ let P = {
117
+ ...s,
118
+ ...A
119
+ }, [F, I] = r(f), L = d === void 0 ? F : d, R = (e) => {
120
+ let t = L?.key === e ? L.direction === "asc" ? {
121
+ key: e,
122
+ direction: "desc"
123
+ } : null : {
124
+ key: e,
125
+ direction: "asc"
126
+ };
127
+ d === void 0 && I(t), p?.(t);
128
+ }, [z, B] = r(null);
129
+ t(() => {
130
+ if (O !== void 0) return;
131
+ let e = window.setTimeout(() => B(Date.now()), 0);
132
+ return () => window.clearTimeout(e);
133
+ }, [O]);
134
+ let V = O ?? z, H = n(() => {
135
+ if (!L) return l;
136
+ let e = L.direction === "asc" ? 1 : -1, t = l.map((e, t) => ({
137
+ r: e,
138
+ i: t
139
+ }));
140
+ return t.sort((t, n) => S(t.r, n.r, L.key) * e || t.i - n.i), t.map((e) => e.r);
141
+ }, [l, L]), U = n(() => {
142
+ let e = l.length;
143
+ return {
144
+ count: e,
145
+ avg: e ? Math.round(l.reduce((e, t) => e + u(t.strength), 0) / e) : 0,
146
+ links: l.filter((e) => e.link).length
147
+ };
148
+ }, [l]), W = !!_, G = D === void 0 ? void 0 : { maxHeight: D };
149
+ return /* @__PURE__ */ a("div", {
150
+ ...N,
151
+ style: M,
152
+ className: e("dishui-ai-records-table", E && "dishui-ai-records-table--sticky", j),
153
+ children: /* @__PURE__ */ a("div", {
154
+ className: "dishui-ai-records-table__scroll",
155
+ style: G,
156
+ children: /* @__PURE__ */ o("table", {
157
+ className: "dishui-ai-records-table__table",
158
+ children: [
159
+ /* @__PURE__ */ a("thead", { children: /* @__PURE__ */ o("tr", { children: [
160
+ h && /* @__PURE__ */ a("th", {
161
+ scope: "col",
162
+ className: "dishui-ai-records-table__th dishui-ai-records-table__th--index",
163
+ children: P.index
164
+ }),
165
+ /* @__PURE__ */ a(T, {
166
+ label: P.company,
167
+ sortKey: "name",
168
+ sort: L,
169
+ onToggle: R
170
+ }),
171
+ /* @__PURE__ */ a(T, {
172
+ label: P.categories,
173
+ sortKey: "tags",
174
+ sort: L,
175
+ onToggle: R
176
+ }),
177
+ /* @__PURE__ */ a(T, {
178
+ label: P.lastInteraction,
179
+ sortKey: "lastInteraction",
180
+ sort: L,
181
+ onToggle: R
182
+ }),
183
+ /* @__PURE__ */ a(T, {
184
+ label: P.strength,
185
+ sortKey: "strength",
186
+ sort: L,
187
+ onToggle: R
188
+ }),
189
+ /* @__PURE__ */ a("th", {
190
+ scope: "col",
191
+ className: "dishui-ai-records-table__th",
192
+ children: P.links
193
+ })
194
+ ] }) }),
195
+ /* @__PURE__ */ o("tbody", { children: [H.length === 0 && /* @__PURE__ */ a("tr", { children: /* @__PURE__ */ a("td", {
196
+ className: "dishui-ai-records-table__td dishui-ai-records-table__td--empty",
197
+ colSpan: h ? 6 : 5,
198
+ children: k ?? P.empty
199
+ }) }), H.map((t, n) => {
200
+ let r = y(t.lastInteractionAt), i = t.tags.slice(0, m), s = t.tags.slice(m), l = C === t.id;
201
+ return /* @__PURE__ */ o("tr", {
202
+ className: e("dishui-ai-records-table__row", W && "dishui-ai-records-table__row--interactive", l && "dishui-ai-records-table__row--selected"),
203
+ "data-selected": l || void 0,
204
+ tabIndex: W ? 0 : void 0,
205
+ onClick: W ? () => _?.(t) : void 0,
206
+ onKeyDown: W ? (e) => {
207
+ (e.key === "Enter" || e.key === " ") && (e.preventDefault(), _?.(t));
208
+ } : void 0,
209
+ children: [
210
+ h && /* @__PURE__ */ a("td", {
211
+ className: "dishui-ai-records-table__td dishui-ai-records-table__td--index",
212
+ children: n + 1
213
+ }),
214
+ /* @__PURE__ */ o("td", {
215
+ className: "dishui-ai-records-table__td dishui-ai-records-table__td--company",
216
+ children: [/* @__PURE__ */ a("span", {
217
+ className: "dishui-ai-records-table__avatar",
218
+ "aria-hidden": "true",
219
+ children: t.avatar ?? t.name.trim().charAt(0).toUpperCase()
220
+ }), /* @__PURE__ */ o("span", {
221
+ className: "dishui-ai-records-table__name",
222
+ children: [t.name, t.location && /* @__PURE__ */ o("span", {
223
+ className: "dishui-ai-records-table__location",
224
+ children: [" — ", t.location]
225
+ })]
226
+ })]
227
+ }),
228
+ /* @__PURE__ */ a("td", {
229
+ className: "dishui-ai-records-table__td",
230
+ children: /* @__PURE__ */ o("span", {
231
+ className: "dishui-ai-records-table__tags",
232
+ children: [
233
+ i.map((e) => /* @__PURE__ */ a("span", {
234
+ className: "dishui-ai-records-table__tag",
235
+ children: e
236
+ }, e)),
237
+ s.length > 0 && /* @__PURE__ */ o("span", {
238
+ className: "dishui-ai-records-table__tag dishui-ai-records-table__tag--more",
239
+ title: s.join(", "),
240
+ children: ["+", s.length]
241
+ }),
242
+ t.tags.length === 0 && /* @__PURE__ */ a("span", {
243
+ className: "dishui-ai-records-table__muted",
244
+ children: "—"
245
+ })
246
+ ]
247
+ })
248
+ }),
249
+ /* @__PURE__ */ a("td", {
250
+ className: e("dishui-ai-records-table__td dishui-ai-records-table__td--time", r === null && "dishui-ai-records-table__td--muted"),
251
+ children: r === null ? P.noContact : V === null ? b(r) : /* @__PURE__ */ a("time", {
252
+ dateTime: new Date(r).toISOString(),
253
+ title: b(r),
254
+ children: v(r, V)
255
+ })
256
+ }),
257
+ /* @__PURE__ */ o("td", {
258
+ className: e("dishui-ai-records-table__td dishui-ai-records-table__td--strength", `dishui-ai-records-table__td--${t.strength}`),
259
+ children: [/* @__PURE__ */ a(w, { strength: t.strength }), /* @__PURE__ */ a("span", { children: c[t.strength] })]
260
+ }),
261
+ /* @__PURE__ */ a("td", {
262
+ className: "dishui-ai-records-table__td",
263
+ children: t.link ? /* @__PURE__ */ a("a", {
264
+ className: "dishui-ai-records-table__link",
265
+ href: t.link.includes("://") ? t.link : `https://${t.link}`,
266
+ target: "_blank",
267
+ rel: "noreferrer",
268
+ onClick: (e) => e.stopPropagation(),
269
+ children: x(t.link)
270
+ }) : /* @__PURE__ */ a("span", {
271
+ className: "dishui-ai-records-table__muted",
272
+ children: "—"
273
+ })
274
+ })
275
+ ]
276
+ }, t.id);
277
+ })] }),
278
+ g && /* @__PURE__ */ a("tfoot", { children: /* @__PURE__ */ a("tr", { children: g === !0 ? /* @__PURE__ */ o(i, { children: [
279
+ h && /* @__PURE__ */ a("td", { className: "dishui-ai-records-table__tf dishui-ai-records-table__td--index" }),
280
+ /* @__PURE__ */ o("td", {
281
+ className: "dishui-ai-records-table__tf",
282
+ children: [
283
+ /* @__PURE__ */ a("span", {
284
+ className: "dishui-ai-records-table__num",
285
+ children: U.count
286
+ }),
287
+ " ",
288
+ P.count
289
+ ]
290
+ }),
291
+ /* @__PURE__ */ a("td", {
292
+ className: "dishui-ai-records-table__tf dishui-ai-records-table__tf--muted",
293
+ children: P.addCalculation
294
+ }),
295
+ /* @__PURE__ */ a("td", {
296
+ className: "dishui-ai-records-table__tf dishui-ai-records-table__tf--muted",
297
+ children: "—"
298
+ }),
299
+ /* @__PURE__ */ o("td", {
300
+ className: "dishui-ai-records-table__tf",
301
+ children: [
302
+ /* @__PURE__ */ o("span", {
303
+ className: "dishui-ai-records-table__num",
304
+ children: [U.avg, "%"]
305
+ }),
306
+ " ",
307
+ P.average
308
+ ]
309
+ }),
310
+ /* @__PURE__ */ o("td", {
311
+ className: "dishui-ai-records-table__tf",
312
+ children: [
313
+ /* @__PURE__ */ a("span", {
314
+ className: "dishui-ai-records-table__num",
315
+ children: U.links
316
+ }),
317
+ " ",
318
+ P.linksCount
319
+ ]
320
+ })
321
+ ] }) : /* @__PURE__ */ a("td", {
322
+ className: "dishui-ai-records-table__tf",
323
+ colSpan: h ? 6 : 5,
324
+ children: g
325
+ }) }) })
326
+ ]
327
+ })
328
+ })
329
+ });
330
+ }
331
+ //#endregion
332
+ export { c as STRENGTH_LABEL, E as default, v as formatRelativeTime, u as strengthPercent };
333
+
334
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/ai/RecordsTable/index.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useState } from 'react';\nimport { cn } from '../../../utils/cn';\nimport './RecordsTable.css';\n\nexport type RecordStrength = 'very-strong' | 'strong' | 'weak' | 'very-weak' | 'none';\n\nexport type RecordRow = {\n id: string;\n name: string;\n location?: string;\n avatar?: React.ReactNode;\n tags: string[];\n /** Epoch ms or ISO string; `null`/undefined = no contact yet. */\n lastInteractionAt?: number | string | null;\n strength: RecordStrength;\n link?: string | null;\n};\n\nexport type RecordsSortKey = 'name' | 'lastInteraction' | 'strength' | 'tags';\nexport type RecordsSort = { key: RecordsSortKey; direction: 'asc' | 'desc' };\n\nexport type RecordsTableProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> & {\n records: RecordRow[];\n sort?: RecordsSort | null;\n defaultSort?: RecordsSort | null;\n onSortChange?: (sort: RecordsSort | null) => void;\n /** Tags shown before collapsing into a `+n` chip. */\n maxTags?: number;\n showIndex?: boolean;\n /** `true` computes count / average strength / link count; a node replaces it. */\n footer?: boolean | React.ReactNode;\n onRowClick?: (record: RecordRow) => void;\n selectedId?: string | null;\n stickyHeader?: boolean;\n maxHeight?: string | number;\n /** Reference time for relative dates. Defaults to mount time (set in an effect). */\n now?: number;\n emptyText?: React.ReactNode;\n labels?: Partial<typeof DEFAULT_LABELS>;\n};\n\nconst DEFAULT_LABELS = {\n index: '#',\n company: 'Company',\n categories: 'Categories',\n lastInteraction: 'Last interaction',\n strength: 'Connection strength',\n links: 'Links',\n noContact: 'No contact',\n count: 'count',\n average: 'average',\n linksCount: 'links',\n addCalculation: 'Add calculation',\n empty: 'No records',\n};\n\nexport const STRENGTH_LABEL: Record<RecordStrength, string> = {\n 'very-strong': 'Very strong',\n strong: 'Strong',\n weak: 'Weak',\n 'very-weak': 'Very weak',\n none: 'No communication',\n};\n\nconst STRENGTH_BARS: Record<RecordStrength, number> = { none: 0, 'very-weak': 1, weak: 2, strong: 3, 'very-strong': 4 };\n\n/** none 0 / very-weak 25 / weak 50 / strong 75 / very-strong 100. */\nexport function strengthPercent(strength: RecordStrength): number {\n return STRENGTH_BARS[strength] * 25;\n}\n\nconst MINUTE = 60_000;\nconst HOUR = 60 * MINUTE;\nconst DAY = 24 * HOUR;\nconst WEEK = 7 * DAY;\nconst MONTH = 30.44 * DAY;\nconst YEAR = 365.25 * DAY;\nconst yearLabel = (n: number) => `${n} year${n === 1 ? '' : 's'} ago`;\n\n/**\n * date-fns-like relative buckets: `just now`, `4 days ago`, `5 weeks ago`,\n * `11 months ago`, `about 1 year ago`, `over 1 year ago`, `almost 2 years ago`.\n */\nexport function formatRelativeTime(from: number, now: number): string {\n const diff = Math.max(0, now - from);\n if (diff < MINUTE) return 'just now';\n if (diff < HOUR) {\n const m = Math.round(diff / MINUTE);\n return `${m} minute${m === 1 ? '' : 's'} ago`;\n }\n if (diff < DAY) {\n const h = Math.round(diff / HOUR);\n return `${h} hour${h === 1 ? '' : 's'} ago`;\n }\n if (diff < WEEK) {\n const d = Math.round(diff / DAY);\n return d <= 1 ? 'yesterday' : `${d} days ago`;\n }\n if (diff < 2 * MONTH) {\n const w = Math.round(diff / WEEK);\n return `${w} week${w === 1 ? '' : 's'} ago`;\n }\n if (diff < YEAR) {\n const mo = Math.round(diff / MONTH);\n return `${mo} months ago`;\n }\n const years = diff / YEAR;\n const whole = Math.floor(years);\n const frac = years - whole;\n if (frac < 0.25) return `about ${yearLabel(whole)}`;\n if (frac < 0.75) return `over ${yearLabel(whole)}`;\n return `almost ${yearLabel(whole + 1)}`;\n}\n\nconst toEpoch = (value: number | string | null | undefined): number | null => {\n if (value === null || value === undefined) return null;\n const t = typeof value === 'number' ? value : Date.parse(value);\n return Number.isFinite(t) ? t : null;\n};\n\nconst formatAbsolute = (t: number) => new Date(t).toISOString().slice(0, 10);\n\nconst hostOf = (url: string) => {\n try {\n return new URL(url.includes('://') ? url : `https://${url}`).hostname.replace(/^www\\./, '');\n } catch {\n return url;\n }\n};\n\nconst compare = (a: RecordRow, b: RecordRow, key: RecordsSortKey): number => {\n switch (key) {\n case 'name':\n return a.name.localeCompare(b.name);\n case 'strength':\n return STRENGTH_BARS[a.strength] - STRENGTH_BARS[b.strength];\n case 'tags':\n return a.tags.length - b.tags.length;\n case 'lastInteraction': {\n const ta = toEpoch(a.lastInteractionAt) ?? Number.NEGATIVE_INFINITY;\n const tb = toEpoch(b.lastInteractionAt) ?? Number.NEGATIVE_INFINITY;\n return ta - tb;\n }\n default:\n return 0;\n }\n};\n\nconst SortIcon: React.FC<{ direction?: 'asc' | 'desc' }> = ({ direction }) => (\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n {direction !== 'desc' && <path d=\"m7 10 5-5 5 5\" opacity={direction === 'asc' ? 1 : 0.4} />}\n {direction !== 'asc' && <path d=\"m7 14 5 5 5-5\" opacity={direction === 'desc' ? 1 : 0.4} />}\n </svg>\n);\n\nconst StrengthGlyph: React.FC<{ strength: RecordStrength }> = ({ strength }) => {\n const bars = STRENGTH_BARS[strength];\n return (\n <span className=\"dishui-ai-records-table__signal\" aria-hidden=\"true\">\n {Array.from({ length: 4 }, (_, i) => (\n <span key={i} className={cn('dishui-ai-records-table__bar', i < bars && 'dishui-ai-records-table__bar--on')} />\n ))}\n </span>\n );\n};\n\ntype SortableHeaderProps = {\n label: React.ReactNode;\n sortKey: RecordsSortKey;\n sort: RecordsSort | null;\n onToggle: (key: RecordsSortKey) => void;\n className?: string;\n};\n\nconst SortableHeader: React.FC<SortableHeaderProps> = ({ label, sortKey, sort, onToggle, className }) => {\n const active = sort?.key === sortKey ? sort.direction : undefined;\n return (\n <th\n scope=\"col\"\n className={cn('dishui-ai-records-table__th', className)}\n aria-sort={active ? (active === 'asc' ? 'ascending' : 'descending') : 'none'}\n >\n <button\n type=\"button\"\n className={cn('dishui-ai-records-table__sort', active && 'dishui-ai-records-table__sort--active')}\n onClick={() => onToggle(sortKey)}\n >\n <span>{label}</span>\n <SortIcon direction={active} />\n </button>\n </th>\n );\n};\n\n/**\n * CRM-style grid with tag chips, sortable columns, relative interaction\n * dates and a 4-bar connection-strength glyph paired with its label.\n */\nexport default function RecordsTable({\n records,\n sort: sortProp,\n defaultSort = null,\n onSortChange,\n maxTags = 3,\n showIndex = true,\n footer = true,\n onRowClick,\n selectedId,\n stickyHeader = false,\n maxHeight,\n now: nowProp,\n emptyText,\n labels: labelOverrides,\n className,\n style,\n ...rest\n}: RecordsTableProps) {\n const labels = { ...DEFAULT_LABELS, ...labelOverrides };\n\n const [internalSort, setInternalSort] = useState<RecordsSort | null>(defaultSort);\n const sort = sortProp !== undefined ? sortProp : internalSort;\n const toggleSort = (key: RecordsSortKey) => {\n const next: RecordsSort | null =\n sort?.key !== key ? { key, direction: 'asc' } : sort.direction === 'asc' ? { key, direction: 'desc' } : null;\n if (sortProp === undefined) setInternalSort(next);\n onSortChange?.(next);\n };\n\n // Relative times need a reference \"now\"; never read the clock during render.\n const [mountNow, setMountNow] = useState<number | null>(null);\n useEffect(() => {\n if (nowProp !== undefined) return;\n const id = window.setTimeout(() => setMountNow(Date.now()), 0);\n return () => window.clearTimeout(id);\n }, [nowProp]);\n const now = nowProp ?? mountNow;\n\n const sorted = useMemo(() => {\n if (!sort) return records;\n const dir = sort.direction === 'asc' ? 1 : -1;\n const indexed = records.map((r, i) => ({ r, i }));\n // The copied array is intentionally sorted in place; `records` is never mutated.\n // eslint-disable-next-line unicorn/no-array-sort\n indexed.sort((a, b) => compare(a.r, b.r, sort.key) * dir || a.i - b.i);\n return indexed.map(x => x.r);\n }, [records, sort]);\n\n const stats = useMemo(() => {\n const count = records.length;\n const avg = count ? Math.round(records.reduce((n, r) => n + strengthPercent(r.strength), 0) / count) : 0;\n const links = records.filter(r => r.link).length;\n return { count, avg, links };\n }, [records]);\n\n const interactive = Boolean(onRowClick);\n const scrollStyle: React.CSSProperties | undefined = maxHeight !== undefined ? { maxHeight } : undefined;\n\n return (\n <div {...rest} style={style} className={cn('dishui-ai-records-table', stickyHeader && 'dishui-ai-records-table--sticky', className)}>\n <div className=\"dishui-ai-records-table__scroll\" style={scrollStyle}>\n <table className=\"dishui-ai-records-table__table\">\n <thead>\n <tr>\n {showIndex && <th scope=\"col\" className=\"dishui-ai-records-table__th dishui-ai-records-table__th--index\">{labels.index}</th>}\n <SortableHeader label={labels.company} sortKey=\"name\" sort={sort} onToggle={toggleSort} />\n <SortableHeader label={labels.categories} sortKey=\"tags\" sort={sort} onToggle={toggleSort} />\n <SortableHeader label={labels.lastInteraction} sortKey=\"lastInteraction\" sort={sort} onToggle={toggleSort} />\n <SortableHeader label={labels.strength} sortKey=\"strength\" sort={sort} onToggle={toggleSort} />\n <th scope=\"col\" className=\"dishui-ai-records-table__th\">{labels.links}</th>\n </tr>\n </thead>\n <tbody>\n {sorted.length === 0 && (\n <tr>\n <td className=\"dishui-ai-records-table__td dishui-ai-records-table__td--empty\" colSpan={showIndex ? 6 : 5}>\n {emptyText ?? labels.empty}\n </td>\n </tr>\n )}\n {sorted.map((record, i) => {\n const t = toEpoch(record.lastInteractionAt);\n const shown = record.tags.slice(0, maxTags);\n const hiddenTags = record.tags.slice(maxTags);\n const selected = selectedId === record.id;\n return (\n <tr\n key={record.id}\n className={cn(\n 'dishui-ai-records-table__row',\n interactive && 'dishui-ai-records-table__row--interactive',\n selected && 'dishui-ai-records-table__row--selected',\n )}\n data-selected={selected || undefined}\n tabIndex={interactive ? 0 : undefined}\n onClick={interactive ? () => onRowClick?.(record) : undefined}\n onKeyDown={\n interactive\n ? e => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onRowClick?.(record);\n }\n }\n : undefined\n }\n >\n {showIndex && <td className=\"dishui-ai-records-table__td dishui-ai-records-table__td--index\">{i + 1}</td>}\n <td className=\"dishui-ai-records-table__td dishui-ai-records-table__td--company\">\n <span className=\"dishui-ai-records-table__avatar\" aria-hidden=\"true\">\n {record.avatar ?? record.name.trim().charAt(0).toUpperCase()}\n </span>\n <span className=\"dishui-ai-records-table__name\">\n {record.name}\n {record.location && <span className=\"dishui-ai-records-table__location\"> — {record.location}</span>}\n </span>\n </td>\n <td className=\"dishui-ai-records-table__td\">\n <span className=\"dishui-ai-records-table__tags\">\n {shown.map(tag => (\n <span key={tag} className=\"dishui-ai-records-table__tag\">{tag}</span>\n ))}\n {hiddenTags.length > 0 && (\n <span className=\"dishui-ai-records-table__tag dishui-ai-records-table__tag--more\" title={hiddenTags.join(', ')}>\n +{hiddenTags.length}\n </span>\n )}\n {record.tags.length === 0 && <span className=\"dishui-ai-records-table__muted\">—</span>}\n </span>\n </td>\n <td className={cn('dishui-ai-records-table__td dishui-ai-records-table__td--time', t === null && 'dishui-ai-records-table__td--muted')}>\n {t === null ? labels.noContact : now === null ? formatAbsolute(t) : <time dateTime={new Date(t).toISOString()} title={formatAbsolute(t)}>{formatRelativeTime(t, now)}</time>}\n </td>\n <td className={cn('dishui-ai-records-table__td dishui-ai-records-table__td--strength', `dishui-ai-records-table__td--${record.strength}`)}>\n <StrengthGlyph strength={record.strength} />\n <span>{STRENGTH_LABEL[record.strength]}</span>\n </td>\n <td className=\"dishui-ai-records-table__td\">\n {record.link ? (\n <a\n className=\"dishui-ai-records-table__link\"\n href={record.link.includes('://') ? record.link : `https://${record.link}`}\n target=\"_blank\"\n rel=\"noreferrer\"\n onClick={e => e.stopPropagation()}\n >\n {hostOf(record.link)}\n </a>\n ) : (\n <span className=\"dishui-ai-records-table__muted\">—</span>\n )}\n </td>\n </tr>\n );\n })}\n </tbody>\n {footer && (\n <tfoot>\n <tr>\n {footer === true ? (\n <>\n {showIndex && <td className=\"dishui-ai-records-table__tf dishui-ai-records-table__td--index\" />}\n <td className=\"dishui-ai-records-table__tf\"><span className=\"dishui-ai-records-table__num\">{stats.count}</span> {labels.count}</td>\n <td className=\"dishui-ai-records-table__tf dishui-ai-records-table__tf--muted\">{labels.addCalculation}</td>\n <td className=\"dishui-ai-records-table__tf dishui-ai-records-table__tf--muted\">—</td>\n <td className=\"dishui-ai-records-table__tf\"><span className=\"dishui-ai-records-table__num\">{stats.avg}%</span> {labels.average}</td>\n <td className=\"dishui-ai-records-table__tf\"><span className=\"dishui-ai-records-table__num\">{stats.links}</span> {labels.linksCount}</td>\n </>\n ) : (\n <td className=\"dishui-ai-records-table__tf\" colSpan={showIndex ? 6 : 5}>{footer}</td>\n )}\n </tr>\n </tfoot>\n )}\n </table>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ export type SelectionAction = {
3
+ id: string;
4
+ label: React.ReactNode;
5
+ icon?: React.ReactNode;
6
+ onSelect: () => void;
7
+ disabled?: boolean;
8
+ disabledReason?: string;
9
+ destructive?: boolean;
10
+ };
11
+ export type SelectionActionsProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> & {
12
+ count: number;
13
+ actions: SelectionAction[];
14
+ onClear?: () => void;
15
+ selectionLabel?: (count: number) => React.ReactNode;
16
+ clearLabel?: string;
17
+ sticky?: boolean;
18
+ };
19
+ export default function SelectionActions({ count, actions, onClear, selectionLabel, clearLabel, sticky, className, ...rest }: SelectionActionsProps): React.JSX.Element | null;
@@ -0,0 +1,40 @@
1
+ import { cn as e } from "../../../utils/cn.js";
2
+ /* empty css */
3
+ import "react";
4
+ import { jsx as t, jsxs as n } from "react/jsx-runtime";
5
+ //#region src/components/ai/SelectionActions/index.tsx
6
+ var r = (e) => `${e} selected`;
7
+ function i({ count: i, actions: a, onClear: o, selectionLabel: s = r, clearLabel: c = "Clear selection", sticky: l = !1, className: u, ...d }) {
8
+ return i <= 0 ? null : /* @__PURE__ */ n("div", {
9
+ ...d,
10
+ className: e("dishui-ai-selection-actions", l && "dishui-ai-selection-actions--sticky", u),
11
+ role: "toolbar",
12
+ "aria-label": "Selection actions",
13
+ children: [
14
+ /* @__PURE__ */ t("strong", { children: s(i) }),
15
+ /* @__PURE__ */ t("span", {
16
+ className: "dishui-ai-selection-actions__divider",
17
+ "aria-hidden": "true"
18
+ }),
19
+ a.map((r) => /* @__PURE__ */ n("button", {
20
+ type: "button",
21
+ className: e(r.destructive && "dishui-ai-selection-actions__action--destructive"),
22
+ disabled: r.disabled,
23
+ title: r.disabledReason,
24
+ onClick: r.onSelect,
25
+ children: [r.icon, /* @__PURE__ */ t("span", { children: r.label })]
26
+ }, r.id)),
27
+ o && /* @__PURE__ */ t("button", {
28
+ type: "button",
29
+ className: "dishui-ai-selection-actions__clear",
30
+ onClick: o,
31
+ "aria-label": c,
32
+ children: "×"
33
+ })
34
+ ]
35
+ });
36
+ }
37
+ //#endregion
38
+ export { i as default };
39
+
40
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/ai/SelectionActions/index.tsx"],"sourcesContent":["import React from 'react';\nimport { cn } from '../../../utils/cn';\nimport './SelectionActions.css';\n\nexport type SelectionAction = { id: string; label: React.ReactNode; icon?: React.ReactNode; onSelect: () => void; disabled?: boolean; disabledReason?: string; destructive?: boolean };\nexport type SelectionActionsProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> & { count: number; actions: SelectionAction[]; onClear?: () => void; selectionLabel?: (count: number) => React.ReactNode; clearLabel?: string; sticky?: boolean };\nconst defaultSelectionLabel = (count: number) => `${count} selected`;\nexport default function SelectionActions({ count, actions, onClear, selectionLabel = defaultSelectionLabel, clearLabel = 'Clear selection', sticky = false, className, ...rest }: SelectionActionsProps) {\n if (count <= 0) return null;\n return <div {...rest} className={cn('dishui-ai-selection-actions', sticky && 'dishui-ai-selection-actions--sticky', className)} role=\"toolbar\" aria-label=\"Selection actions\"><strong>{selectionLabel(count)}</strong><span className=\"dishui-ai-selection-actions__divider\" aria-hidden=\"true\"/>{actions.map(action => <button key={action.id} type=\"button\" className={cn(action.destructive && 'dishui-ai-selection-actions__action--destructive')} disabled={action.disabled} title={action.disabledReason} onClick={action.onSelect}>{action.icon}<span>{action.label}</span></button>)}{onClear && <button type=\"button\" className=\"dishui-ai-selection-actions__clear\" onClick={onClear} aria-label={clearLabel}>×</button>}</div>;\n}\n"],"mappings":";;;;;AAMA,IAAM,KAAyB,MAAkB,GAAG,EAAM;AAC1D,SAAwB,EAAiB,EAAE,UAAO,YAAS,YAAS,oBAAiB,GAAuB,gBAAa,mBAAmB,YAAS,IAAO,cAAW,GAAG,KAA+B;CAEvM,OADI,KAAS,IAAU,OAChB,kBAAC,OAAD;EAAK,GAAI;EAAM,WAAW,EAAG,+BAA+B,KAAU,uCAAuC,CAAS;EAAG,MAAK;EAAU,cAAW;EAAnJ,UAAA;GAAuK,kBAAC,UAAD,EAAA,UAAS,EAAe,CAAK,EAAU,CAAA;GAAC,kBAAC,QAAD;IAAM,WAAU;IAAuC,eAAY;GAAO,CAAA;GAAE,EAAQ,KAAI,MAAU,kBAAC,UAAD;IAAwB,MAAK;IAAS,WAAW,EAAG,EAAO,eAAe,kDAAkD;IAAG,UAAU,EAAO;IAAU,OAAO,EAAO;IAAgB,SAAS,EAAO;IAAxM,UAAA,CAAmN,EAAO,MAAK,kBAAC,QAAD,EAAA,UAAO,EAAO,MAAY,CAAA,CAAS;GAArP,GAAA,EAAO,EAA8O,CAAC;GAAG,KAAW,kBAAC,UAAD;IAAQ,MAAK;IAAS,WAAU;IAAqC,SAAS;IAAS,cAAY;IAAY,UAAA;GAAS,CAAA;EAAO;;AAC1sB"}
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ export type StateFlowNode = {
3
+ id: string;
4
+ label: React.ReactNode;
5
+ description?: React.ReactNode;
6
+ status?: 'pending' | 'running' | 'completed' | 'failed';
7
+ shape?: 'process' | 'decision' | 'terminal';
8
+ disabled?: boolean;
9
+ };
10
+ export type StateFlowEdge = {
11
+ from: string;
12
+ to: string;
13
+ label?: React.ReactNode;
14
+ };
15
+ export type StateFlowProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children' | 'onSelect'> & {
16
+ nodes: StateFlowNode[];
17
+ edges: StateFlowEdge[];
18
+ selectedId?: string;
19
+ defaultSelectedId?: string;
20
+ onSelect?: (node: StateFlowNode) => void;
21
+ direction?: 'vertical' | 'horizontal';
22
+ emptyText?: React.ReactNode;
23
+ };
24
+ export default function StateFlow({ nodes, edges, selectedId, defaultSelectedId, onSelect, direction, emptyText, className, ...rest }: StateFlowProps): React.JSX.Element;
@@ -0,0 +1,50 @@
1
+ import { cn as e } from "../../../utils/cn.js";
2
+ /* empty css */
3
+ import t, { useId as n, useMemo as r, useState as i } from "react";
4
+ import { jsx as a, jsxs as o } from "react/jsx-runtime";
5
+ //#region src/components/ai/StateFlow/index.tsx
6
+ function s({ nodes: s, edges: c, selectedId: l, defaultSelectedId: u, onSelect: d, direction: f = "vertical", emptyText: p = "No steps in this flow", className: m, ...h }) {
7
+ let g = n(), [_, v] = i(u), y = l ?? _, b = r(() => {
8
+ let e = /* @__PURE__ */ new Map();
9
+ return c.forEach((t) => e.set(t.to, [...e.get(t.to) ?? [], t])), e;
10
+ }, [c]);
11
+ if (!s.length) return /* @__PURE__ */ a("div", {
12
+ ...h,
13
+ className: e("dishui-ai-state-flow dishui-ai-state-flow--empty", m),
14
+ children: p
15
+ });
16
+ let x = (e) => {
17
+ e.disabled || (l === void 0 && v(e.id), d?.(e));
18
+ };
19
+ return /* @__PURE__ */ a("div", {
20
+ ...h,
21
+ className: e("dishui-ai-state-flow", `dishui-ai-state-flow--${f}`, m),
22
+ role: "list",
23
+ "aria-label": "Workflow",
24
+ children: s.map((n, r) => /* @__PURE__ */ o(t.Fragment, { children: [r > 0 && /* @__PURE__ */ o("div", {
25
+ className: "dishui-ai-state-flow__connector",
26
+ "aria-hidden": "true",
27
+ children: [/* @__PURE__ */ a("span", { children: "↓" }), (b.get(n.id) ?? []).map((e) => e.label && /* @__PURE__ */ a("small", { children: e.label }, `${e.from}-${e.to}`))]
28
+ }), /* @__PURE__ */ o("button", {
29
+ type: "button",
30
+ id: `${g}-${n.id}`,
31
+ role: "listitem",
32
+ disabled: n.disabled,
33
+ "aria-current": y === n.id ? "step" : void 0,
34
+ className: e("dishui-ai-state-flow__node", `dishui-ai-state-flow__node--${n.shape ?? "process"}`, n.status && `dishui-ai-state-flow__node--${n.status}`, y === n.id && "dishui-ai-state-flow__node--selected"),
35
+ onClick: () => x(n),
36
+ children: [
37
+ /* @__PURE__ */ a("span", {
38
+ className: "dishui-ai-state-flow__status",
39
+ children: n.status ?? "step"
40
+ }),
41
+ /* @__PURE__ */ a("strong", { children: n.label }),
42
+ n.description && /* @__PURE__ */ a("small", { children: n.description })
43
+ ]
44
+ })] }, n.id))
45
+ });
46
+ }
47
+ //#endregion
48
+ export { s as default };
49
+
50
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/ai/StateFlow/index.tsx"],"sourcesContent":["import React, { useId, useMemo, useState } from 'react';\nimport { cn } from '../../../utils/cn';\nimport './StateFlow.css';\n\nexport type StateFlowNode = { id: string; label: React.ReactNode; description?: React.ReactNode; status?: 'pending' | 'running' | 'completed' | 'failed'; shape?: 'process' | 'decision' | 'terminal'; disabled?: boolean };\nexport type StateFlowEdge = { from: string; to: string; label?: React.ReactNode };\nexport type StateFlowProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children' | 'onSelect'> & { nodes: StateFlowNode[]; edges: StateFlowEdge[]; selectedId?: string; defaultSelectedId?: string; onSelect?: (node: StateFlowNode) => void; direction?: 'vertical' | 'horizontal'; emptyText?: React.ReactNode };\n\nexport default function StateFlow({ nodes, edges, selectedId, defaultSelectedId, onSelect, direction = 'vertical', emptyText = 'No steps in this flow', className, ...rest }: StateFlowProps) {\n const uid = useId(); const [internal, setInternal] = useState(defaultSelectedId); const selected = selectedId ?? internal;\n const incoming = useMemo(() => { const map = new Map<string, StateFlowEdge[]>(); edges.forEach(edge => map.set(edge.to, [...(map.get(edge.to) ?? []), edge])); return map; }, [edges]);\n if (!nodes.length) return <div {...rest} className={cn('dishui-ai-state-flow dishui-ai-state-flow--empty', className)}>{emptyText}</div>;\n const select = (node: StateFlowNode) => { if (node.disabled) return; if (selectedId === undefined) setInternal(node.id); onSelect?.(node); };\n return <div {...rest} className={cn('dishui-ai-state-flow', `dishui-ai-state-flow--${direction}`, className)} role=\"list\" aria-label=\"Workflow\">{nodes.map((node, index) => <React.Fragment key={node.id}>{index > 0 && <div className=\"dishui-ai-state-flow__connector\" aria-hidden=\"true\"><span>↓</span>{(incoming.get(node.id) ?? []).map(edge => edge.label && <small key={`${edge.from}-${edge.to}`}>{edge.label}</small>)}</div>}<button type=\"button\" id={`${uid}-${node.id}`} role=\"listitem\" disabled={node.disabled} aria-current={selected === node.id ? 'step' : undefined} className={cn('dishui-ai-state-flow__node', `dishui-ai-state-flow__node--${node.shape ?? 'process'}`, node.status && `dishui-ai-state-flow__node--${node.status}`, selected === node.id && 'dishui-ai-state-flow__node--selected')} onClick={() => select(node)}><span className=\"dishui-ai-state-flow__status\">{node.status ?? 'step'}</span><strong>{node.label}</strong>{node.description && <small>{node.description}</small>}</button></React.Fragment>)}</div>;\n}\n"],"mappings":";;;;;AAQA,SAAwB,EAAU,EAAE,UAAO,UAAO,eAAY,sBAAmB,aAAU,eAAY,YAAY,eAAY,yBAAyB,cAAW,GAAG,KAAwB;CAC5L,IAAM,IAAM,EAAM,GAAS,CAAC,GAAU,KAAe,EAAS,CAAiB,GAAS,IAAW,KAAc,GAC3G,IAAW,QAAc;EAAE,IAAM,oBAAM,IAAI,IAA6B;EAAiF,OAA9E,EAAM,SAAQ,MAAQ,EAAI,IAAI,EAAK,IAAI,CAAC,GAAI,EAAI,IAAI,EAAK,EAAE,KAAK,CAAC,GAAI,CAAI,CAAC,CAAC,GAAU;CAAK,GAAG,CAAC,CAAK,CAAC;CACrL,IAAI,CAAC,EAAM,QAAQ,OAAO,kBAAC,OAAD;EAAK,GAAI;EAAM,WAAW,EAAG,oDAAoD,CAAS;EAAI,UAAA;CAAe,CAAA;CACvI,IAAM,KAAU,MAAwB;EAAM,EAAK,aAAsB,MAAe,KAAA,KAAW,EAAY,EAAK,EAAE,GAAG,IAAW,CAAI;CAAG;CAC3I,OAAO,kBAAC,OAAD;EAAK,GAAI;EAAM,WAAW,EAAG,wBAAwB,yBAAyB,KAAa,CAAS;EAAG,MAAK;EAAO,cAAW;EAAY,UAAA,EAAM,KAAK,GAAM,MAAU,kBAAC,EAAM,UAAP,EAAA,UAAA,CAA+B,IAAQ,KAAK,kBAAC,OAAD;GAAK,WAAU;GAAkC,eAAY;GAA7D,UAAA,CAAoE,kBAAC,QAAD,EAAA,UAAM,IAAO,CAAA,IAAG,EAAS,IAAI,EAAK,EAAE,KAAK,CAAC,EAAA,CAAG,KAAI,MAAQ,EAAK,SAAS,kBAAC,SAAD,EAAA,UAAwC,EAAK,MAAa,GAA9C,GAAG,EAAK,KAAK,GAAG,EAAK,IAAyB,CAAC,CAAO;EAAE,CAAA,GAAA,kBAAC,UAAD;GAAQ,MAAK;GAAS,IAAI,GAAG,EAAI,GAAG,EAAK;GAAM,MAAK;GAAW,UAAU,EAAK;GAAU,gBAAc,MAAa,EAAK,KAAK,SAAS,KAAA;GAAW,WAAW,EAAG,8BAA8B,+BAA+B,EAAK,SAAS,aAAa,EAAK,UAAU,+BAA+B,EAAK,UAAU,MAAa,EAAK,MAAM,sCAAsC;GAAG,eAAe,EAAO,CAAI;GAA/Y,UAAA;IAAkZ,kBAAC,QAAD;KAAM,WAAU;KAAgC,UAAA,EAAK,UAAU;IAAa,CAAA;IAAC,kBAAC,UAAD,EAAA,UAAS,EAAK,MAAc,CAAA;IAAE,EAAK,eAAe,kBAAC,SAAD,EAAA,UAAQ,EAAK,YAAmB,CAAA;GAAU;EAAiB,CAAA,CAAA,EAAA,GAAlzB,EAAK,EAA6yB,CAAC;CAAO,CAAA;AAC7/B"}
@@ -0,0 +1,44 @@
1
+ import React from 'react';
2
+ export type StreamingSource = {
3
+ id: string;
4
+ title: string;
5
+ domain?: string;
6
+ url?: string;
7
+ favicon?: React.ReactNode;
8
+ };
9
+ export type StreamingAction = {
10
+ id: string;
11
+ label: string;
12
+ icon?: React.ReactNode;
13
+ onClick?: () => void;
14
+ };
15
+ export type StreamingFeedback = 'up' | 'down';
16
+ export type StreamingTextProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> & {
17
+ /** Full or partial answer text. `\n\n` starts a new paragraph. */
18
+ text: string;
19
+ /** Shows the caret and hides actions / follow-ups until the answer is complete. */
20
+ streaming?: boolean;
21
+ /** Reveal `text` progressively on the client. Remount (key) to replay. */
22
+ typewriter?: boolean;
23
+ /** Characters revealed per tick in typewriter mode. */
24
+ typewriterSpeed?: number;
25
+ sources?: StreamingSource[];
26
+ /** Render `[n]` markers in `text` as inline chips pointing at `sources[n - 1]`. */
27
+ citations?: boolean;
28
+ /** Source chips shown before the “+k” overflow toggle. */
29
+ maxVisibleSources?: number;
30
+ /** Extra actions appended after the built-in copy / regenerate / feedback buttons. */
31
+ actions?: StreamingAction[];
32
+ onCopy?: (text: string) => void;
33
+ onRegenerate?: () => void;
34
+ onFeedback?: (kind: StreamingFeedback) => void;
35
+ followUps?: string[];
36
+ onFollowUp?: (question: string) => void;
37
+ followUpsLabel?: React.ReactNode;
38
+ };
39
+ /**
40
+ * Streamed answer: paragraphs with optional inline citations, a source bar,
41
+ * an action row and follow-up questions. Fully controlled — it renders the
42
+ * `text` it is given; `typewriter` only adds a client-side reveal.
43
+ */
44
+ export default function StreamingText({ text, streaming, typewriter, typewriterSpeed, sources, citations, maxVisibleSources, actions, onCopy, onRegenerate, onFeedback, followUps, onFollowUp, followUpsLabel, className, ...rest }: StreamingTextProps): React.JSX.Element;