@candor-ui/react 1.1.0 → 1.5.0

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 (37) hide show
  1. package/dist/adapters/ag-ui.d.ts +66 -1
  2. package/dist/adapters/ag-ui.js +308 -1
  3. package/dist/adapters/ai-sdk.d.ts +6 -1
  4. package/dist/adapters/ai-sdk.js +18 -1
  5. package/dist/adapters/claude-agent-sdk.d.ts +111 -2
  6. package/dist/adapters/claude-agent-sdk.js +163 -5
  7. package/dist/adapters/claude-agent-server.d.ts +195 -10
  8. package/dist/adapters/claude-agent-server.js +448 -49
  9. package/dist/adapters/use-ag-ui.d.ts +6 -0
  10. package/dist/adapters/use-ag-ui.js +10 -4
  11. package/dist/adapters/use-candor-chat.js +2 -2
  12. package/dist/adapters/use-claude-agent.d.ts +35 -6
  13. package/dist/adapters/use-claude-agent.js +96 -20
  14. package/dist/chat/ai-disclosure.d.ts +37 -0
  15. package/dist/chat/ai-disclosure.js +48 -0
  16. package/dist/chat/attachment-preview.js +1 -1
  17. package/dist/chat/card-registry.js +1 -1
  18. package/dist/chat/citation.js +5 -3
  19. package/dist/chat/composer.d.ts +5 -2
  20. package/dist/chat/composer.js +4 -3
  21. package/dist/chat/entity-chip.js +1 -1
  22. package/dist/chat/folded-turns.js +1 -1
  23. package/dist/chat/message.js +1 -1
  24. package/dist/chat/payload.js +5 -2
  25. package/dist/chat/permission-prompt.d.ts +129 -0
  26. package/dist/chat/permission-prompt.js +144 -0
  27. package/dist/chat/reasoning.js +1 -1
  28. package/dist/chat/thread.d.ts +7 -1
  29. package/dist/chat/thread.js +24 -3
  30. package/dist/chat/tool-call.d.ts +5 -0
  31. package/dist/chat/tool-call.js +7 -2
  32. package/dist/index.d.ts +3 -0
  33. package/dist/index.js +3 -0
  34. package/dist/lib/chat.d.ts +15 -0
  35. package/dist/lib/use-virtual-list.d.ts +39 -0
  36. package/dist/lib/use-virtual-list.js +124 -0
  37. package/package.json +1 -1
@@ -0,0 +1,124 @@
1
+ import * as React from "react";
2
+ const keyAttr = "data-virtual-key";
3
+ /**
4
+ * Render only the items of a long list that are in or near view, for Thread's and SessionList's `virtualize`.
5
+ * Items keep their measured heights by key, so a list that grows or reorders keeps its place; unmeasured items count
6
+ * as `estimate`. Spacers stand in for the rest. `pin` (an item's key) keeps that item rendered (the one holding
7
+ * focus), on its own if it's far from view, so focus is never lost to an item scrolling away. Until the scroller is
8
+ * measured (on the server, on the first render in the browser, and while it's hidden) the first or last `initial`
9
+ * items render, by `anchor`, so server and browser agree.
10
+ *
11
+ * Give each rendered item `ref={list.measure}` and `data-virtual-key={key}`.
12
+ */
13
+ export function useVirtualList({ enabled, count, keyOf, scroller, estimate = 96, overscan = 800, initial = 20, anchor = "end", gap = () => 0, pin, }) {
14
+ const [heights, setHeights] = React.useState(() => new Map());
15
+ const [viewport, setViewport] = React.useState(null);
16
+ const gapOf = React.useRef(gap);
17
+ React.useLayoutEffect(() => {
18
+ gapOf.current = gap;
19
+ });
20
+ // One observer for the rendered items, made when the first one mounts; a changed height re-renders.
21
+ const observer = React.useRef(null);
22
+ React.useEffect(() => () => {
23
+ observer.current?.disconnect();
24
+ observer.current = null;
25
+ }, []);
26
+ const measure = React.useCallback((el) => {
27
+ if (!el)
28
+ return;
29
+ if (typeof ResizeObserver !== "undefined")
30
+ observer.current ??= new ResizeObserver((entries) => {
31
+ const found = entries.flatMap((e) => {
32
+ const key = e.target.getAttribute(keyAttr);
33
+ const h = e.target.offsetHeight;
34
+ return key !== null && h > 0 ? [[key, h]] : [];
35
+ });
36
+ if (found.length)
37
+ setHeights((prev) => (found.every(([k, h]) => prev.get(k) === h) ? prev : new Map([...prev, ...found])));
38
+ });
39
+ const ro = observer.current;
40
+ ro?.observe(el);
41
+ const key = el.getAttribute(keyAttr);
42
+ const h = el.offsetHeight;
43
+ if (key !== null && h > 0)
44
+ setHeights((prev) => (prev.get(key) === h ? prev : new Map(prev).set(key, h)));
45
+ // An item that unmounts stops being observed, so it can be collected.
46
+ return () => ro?.unobserve(el);
47
+ }, []);
48
+ React.useLayoutEffect(() => {
49
+ const el = scroller.current;
50
+ if (!enabled || !el)
51
+ return;
52
+ const pending = { frame: 0 };
53
+ const read = () => {
54
+ pending.frame = 0;
55
+ const g = gapOf.current();
56
+ setViewport((v) => (v && v.top === el.scrollTop && v.height === el.clientHeight && v.gap === g ? v : { top: el.scrollTop, height: el.clientHeight, gap: g }));
57
+ };
58
+ const later = () => {
59
+ if (!pending.frame)
60
+ pending.frame = requestAnimationFrame(read);
61
+ };
62
+ read();
63
+ el.addEventListener("scroll", later, { passive: true });
64
+ const ro = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(later);
65
+ ro?.observe(el);
66
+ return () => {
67
+ el.removeEventListener("scroll", later);
68
+ ro?.disconnect();
69
+ cancelAnimationFrame(pending.frame);
70
+ };
71
+ }, [enabled, scroller]);
72
+ if (!enabled)
73
+ return { rows: Array.from({ length: count }, (_, index) => ({ index })), measure };
74
+ const g = viewport?.gap ?? 0;
75
+ const size = (i) => heights.get(String(keyOf(i))) ?? estimate;
76
+ const tops = [];
77
+ let y = 0;
78
+ for (let i = 0; i < count; i++) {
79
+ tops.push(y);
80
+ y += size(i) + g;
81
+ }
82
+ const total = Math.max(0, y - g);
83
+ let start;
84
+ let end;
85
+ if (!viewport || viewport.height === 0) {
86
+ start = anchor === "end" ? Math.max(0, count - initial) : 0;
87
+ end = anchor === "end" ? count : Math.min(count, initial);
88
+ }
89
+ else {
90
+ const lo = viewport.top - overscan;
91
+ const hi = viewport.top + viewport.height + overscan;
92
+ start = 0;
93
+ while (start < count - 1 && tops[start] + size(start) < lo)
94
+ start++;
95
+ end = start;
96
+ while (end < count && tops[end] <= hi)
97
+ end++;
98
+ end = Math.max(end, Math.min(count, start + 1));
99
+ }
100
+ // The pinned item and its neighbours (so Tab and Shift+Tab reach the next and previous item) render with the window,
101
+ // or on their own between spacers when they're far from it.
102
+ let ranges = [[start, end]];
103
+ const p = pin == null ? -1 : Array.from({ length: count }, (_, i) => i).find((i) => String(keyOf(i)) === pin) ?? -1;
104
+ if (p >= 0) {
105
+ const island = [Math.max(0, p - 1), Math.min(count, p + 2)];
106
+ ranges = island[1] < start ? [island, [start, end]] : island[0] > end ? [[start, end], island] : [[Math.min(start, island[0]), Math.max(end, island[1])]];
107
+ }
108
+ // A spacer stands where its items were; the gaps the layout puts beside it account for theirs.
109
+ const rows = [];
110
+ let prevEnd = 0;
111
+ for (const [a, b] of ranges) {
112
+ if (a > 0) {
113
+ const height = prevEnd === 0 ? tops[a] - g : tops[a] - (tops[prevEnd - 1] + size(prevEnd - 1)) - 2 * g;
114
+ if (a > prevEnd)
115
+ rows.push({ spacer: Math.max(0, height), key: `spacer-${prevEnd}` });
116
+ }
117
+ for (let i = a; i < b; i++)
118
+ rows.push({ index: i });
119
+ prevEnd = b;
120
+ }
121
+ if (prevEnd < count)
122
+ rows.push({ spacer: Math.max(0, total - tops[prevEnd]), key: `spacer-${prevEnd}` });
123
+ return { rows, measure };
124
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@candor-ui/react",
3
- "version": "1.1.0",
3
+ "version": "1.5.0",
4
4
  "description": "Candor UI's free tier as an npm package: React components for agents that do real work, on shadcn/ui and Tailwind 4.",
5
5
  "license": "MIT",
6
6
  "author": "Methodic LLC",