@adecore/agents-react 0.24.1 → 0.25.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 (113) hide show
  1. package/dist/chat/index.d.ts +4 -7
  2. package/dist/chat/index.js +4 -7
  3. package/dist/chat/logic/continue-after-stop.d.ts +3 -0
  4. package/dist/chat/logic/continue-after-stop.js +28 -0
  5. package/dist/chat/logic/thread-cards.d.ts +0 -1
  6. package/dist/chat/logic/thread-cards.js +0 -4
  7. package/dist/chat/preferences.d.ts +0 -1
  8. package/dist/chat/preferences.js +0 -3
  9. package/dist/chat/stash.d.ts +6 -0
  10. package/dist/chat/stash.js +13 -0
  11. package/dist/chat/ui/Composer.js +26 -5
  12. package/dist/chat/ui/EditDiff.js +4 -3
  13. package/dist/chat/ui/Pickers.js +1 -1
  14. package/dist/chat/ui/UnifiedDiff.js +4 -3
  15. package/dist/chat/ui/composer/ComposerControls.d.ts +1 -0
  16. package/dist/chat/ui/composer/ComposerControls.js +3 -3
  17. package/dist/chat/ui/composer/use-composer-submit.d.ts +3 -1
  18. package/dist/chat/ui/composer/use-composer-submit.js +22 -4
  19. package/dist/chat/ui/composer/use-continuable-turn.d.ts +2 -0
  20. package/dist/chat/ui/composer/use-continuable-turn.js +12 -0
  21. package/dist/chat/ui/composer/use-notice.d.ts +3 -1
  22. package/dist/chat/ui/composer/use-notice.js +8 -6
  23. package/dist/chat/ui/diff/ReviewDiff.js +2 -2
  24. package/dist/chat/ui/index.d.ts +9 -28
  25. package/dist/chat/ui/index.js +8 -27
  26. package/dist/chat/ui/intelligent-ui/UiReply.js +20 -2
  27. package/dist/chat/ui/intelligent-ui/node-text.d.ts +3 -1
  28. package/dist/chat/ui/intelligent-ui/node-text.js +32 -0
  29. package/dist/chat/ui/intelligent-ui/registry.js +15 -5
  30. package/dist/chat/ui/intelligent-ui/render-context.d.ts +6 -0
  31. package/dist/chat/ui/intelligent-ui/renderers/base-land.d.ts +1 -0
  32. package/dist/chat/ui/intelligent-ui/renderers/base-land.js +6 -0
  33. package/dist/chat/ui/intelligent-ui/renderers/binding.d.ts +6 -0
  34. package/dist/chat/ui/intelligent-ui/renderers/binding.js +10 -0
  35. package/dist/chat/ui/intelligent-ui/renderers/content.d.ts +2 -0
  36. package/dist/chat/ui/intelligent-ui/renderers/content.js +68 -6
  37. package/dist/chat/ui/intelligent-ui/renderers/inputs.d.ts +2 -0
  38. package/dist/chat/ui/intelligent-ui/renderers/inputs.js +40 -13
  39. package/dist/chat/ui/intelligent-ui/renderers/map-slots.d.ts +20 -0
  40. package/dist/chat/ui/intelligent-ui/renderers/map-slots.js +87 -0
  41. package/dist/chat/ui/intelligent-ui/renderers/map-style.d.ts +18 -0
  42. package/dist/chat/ui/intelligent-ui/renderers/map-style.js +72 -0
  43. package/dist/chat/ui/intelligent-ui/renderers/map-view.d.ts +15 -0
  44. package/dist/chat/ui/intelligent-ui/renderers/map-view.js +213 -0
  45. package/dist/chat/ui/intelligent-ui/renderers/map.d.ts +3 -0
  46. package/dist/chat/ui/intelligent-ui/renderers/map.js +113 -0
  47. package/dist/chat/ui/intelligent-ui/renderers/place-point.d.ts +3 -0
  48. package/dist/chat/ui/intelligent-ui/renderers/place-point.js +13 -0
  49. package/dist/chat/ui/intelligent-ui/renderers/text.d.ts +2 -0
  50. package/dist/chat/ui/intelligent-ui/renderers/text.js +14 -1
  51. package/dist/chat/ui/intelligent-ui/use-in-view.d.ts +1 -0
  52. package/dist/chat/ui/intelligent-ui/use-in-view.js +17 -0
  53. package/dist/chat/ui/rows/WorkRows.js +3 -3
  54. package/dist/host.d.ts +16 -0
  55. package/dist/index.d.ts +2 -2
  56. package/dist/index.js +1 -1
  57. package/dist/lazy.js +5 -7
  58. package/dist/locales/en/agent-chat.d.ts +47 -0
  59. package/dist/locales/en/agent-chat.js +48 -1
  60. package/dist/locales/nl/agent-chat.d.ts +47 -0
  61. package/dist/locales/nl/agent-chat.js +48 -1
  62. package/dist/prompts/index.d.ts +3 -5
  63. package/dist/prompts/index.js +3 -5
  64. package/dist/providers/index.d.ts +3 -9
  65. package/dist/providers/index.js +3 -9
  66. package/dist/state/index.d.ts +2 -2
  67. package/dist/state/index.js +2 -2
  68. package/dist/state/usage.d.ts +0 -1
  69. package/dist/state/usage.js +0 -3
  70. package/dist/theme.css +10 -0
  71. package/package.json +9 -8
  72. package/src/chat/index.ts +2 -23
  73. package/src/chat/logic/continue-after-stop.ts +32 -0
  74. package/src/chat/logic/thread-cards.ts +0 -5
  75. package/src/chat/preferences.ts +0 -4
  76. package/src/chat/stash.ts +17 -0
  77. package/src/chat/ui/Composer.tsx +33 -5
  78. package/src/chat/ui/EditDiff.tsx +6 -5
  79. package/src/chat/ui/Pickers.tsx +1 -0
  80. package/src/chat/ui/UnifiedDiff.tsx +6 -5
  81. package/src/chat/ui/composer/ComposerControls.tsx +13 -3
  82. package/src/chat/ui/composer/use-composer-submit.ts +25 -5
  83. package/src/chat/ui/composer/use-continuable-turn.ts +14 -0
  84. package/src/chat/ui/composer/use-notice.ts +14 -7
  85. package/src/chat/ui/diff/ReviewDiff.tsx +3 -3
  86. package/src/chat/ui/index.ts +9 -49
  87. package/src/chat/ui/intelligent-ui/UiReply.tsx +24 -1
  88. package/src/chat/ui/intelligent-ui/node-text.ts +34 -1
  89. package/src/chat/ui/intelligent-ui/registry.ts +33 -5
  90. package/src/chat/ui/intelligent-ui/render-context.ts +14 -0
  91. package/src/chat/ui/intelligent-ui/renderers/base-land.ts +7 -0
  92. package/src/chat/ui/intelligent-ui/renderers/binding.ts +14 -0
  93. package/src/chat/ui/intelligent-ui/renderers/content.tsx +144 -9
  94. package/src/chat/ui/intelligent-ui/renderers/inputs.tsx +79 -17
  95. package/src/chat/ui/intelligent-ui/renderers/map-slots.ts +105 -0
  96. package/src/chat/ui/intelligent-ui/renderers/map-style.ts +96 -0
  97. package/src/chat/ui/intelligent-ui/renderers/map-view.tsx +251 -0
  98. package/src/chat/ui/intelligent-ui/renderers/map.tsx +226 -0
  99. package/src/chat/ui/intelligent-ui/renderers/place-point.ts +16 -0
  100. package/src/chat/ui/intelligent-ui/renderers/text.tsx +28 -1
  101. package/src/chat/ui/intelligent-ui/use-in-view.ts +18 -0
  102. package/src/chat/ui/rows/WorkRows.tsx +3 -3
  103. package/src/host.ts +28 -0
  104. package/src/index.ts +2 -12
  105. package/src/lazy.tsx +5 -7
  106. package/src/locales/en/agent-chat.ts +48 -1
  107. package/src/locales/nl/agent-chat.ts +48 -1
  108. package/src/prompts/index.ts +3 -18
  109. package/src/providers/index.ts +3 -9
  110. package/src/state/index.ts +1 -16
  111. package/src/state/usage.ts +0 -4
  112. package/src/stylesheets.d.ts +2 -0
  113. package/src/theme.css +10 -0
@@ -0,0 +1,113 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useId, useState } from 'react';
3
+ import clsx from 'clsx';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { Button, LazyBoundary, Spinner } from '@adecore/ui';
6
+ import { useChatHost } from '../../../../host.js';
7
+ import { lazyNamed } from '../../../../lazy.js';
8
+ import { DiffFallbackBoundary } from '../../diff/DiffFallbackBoundary.js';
9
+ import { uiChildrenOf, uiNodeLabel } from '../node-text.js';
10
+ import { placePoint } from './place-point.js';
11
+ import { useInView } from '../use-in-view.js';
12
+ import { useLater } from '../use-later.js';
13
+ import { bindingOf, PENDING } from './binding.js';
14
+ import { useMapSlot } from './map-slots.js';
15
+ // MapLibre and its stylesheet load with the first map that is drawn, never with the chat.
16
+ const MapView = lazyNamed(() => import('./map-view.js'), 'MapView');
17
+ /* A map that is still loading shows a spinner only once the wait is noticeable. */
18
+ const LOADING_SPINNER_MS = 1000;
19
+ /* The number that ties a row to its marker. The list numbers itself for a screen reader, so the chip is only for the eye. */
20
+ function NumberChip({ number, selected }) {
21
+ return (_jsx("span", { "aria-hidden": true, className: clsx('mt-px flex size-5 shrink-0 items-center justify-center rounded-full text-xs tabular-nums', selected ? 'bg-accent text-accent-text' : 'bg-map-marker text-map-marker-text'), children: number }));
22
+ }
23
+ function PlaceRow({ listed, number, selected, onPick }) {
24
+ const { t } = useTranslation('agent-chat');
25
+ const { detail, address } = listed.node.props;
26
+ const body = (_jsxs(_Fragment, { children: [_jsx(NumberChip, { number: number, selected: selected }), _jsxs("span", { className: "flex min-w-0 grow flex-col gap-0.5", children: [_jsx("span", { className: "text-sm wrap-break-word text-text", children: listed.name }), detail !== undefined && detail !== '' && _jsx("span", { className: "text-xs wrap-break-word text-text-muted", children: detail }), address !== undefined && address !== '' && _jsx("span", { className: "text-xs wrap-break-word text-text-faint", children: address }), listed.point === undefined && _jsx("span", { className: "text-xs text-text-faint", children: t('blocks.map.lookingUp') }), listed.point === null && _jsx("span", { className: "text-xs text-text-faint", children: t('blocks.map.notFound') })] })] }));
27
+ const row = 'flex w-full min-h-8 items-start gap-2 rounded-md px-2 py-1.5 text-left';
28
+ return (_jsx("li", { children: onPick === null ? (_jsx("div", { className: clsx(row, selected && 'bg-surface-hover'), children: body })) : (_jsx("button", { type: "button", "aria-pressed": selected, className: clsx(row, 'hover:bg-surface-hover', selected && 'bg-surface-hover'), onClick: onPick, children: body })) }));
29
+ }
30
+ /* Why the map is not the host's: its own reason when the source has none, or what the view found out on the way. */
31
+ function noteFor(t, source, status) {
32
+ if (status === 'offline') {
33
+ return t('blocks.map.offline');
34
+ }
35
+ if (status === 'unavailable' || status === 'failed' || status === 'lost') {
36
+ return t('blocks.map.unavailable');
37
+ }
38
+ if (source?.state === 'off') {
39
+ return t(source.reason === 'disabled' ? 'blocks.map.disabled' : source.reason === 'signed-out' ? 'blocks.map.signedOut' : 'blocks.map.unavailable');
40
+ }
41
+ return source?.state === 'tiles' && status === 'tiles' ? (source.attribution ?? null) : null;
42
+ }
43
+ /*
44
+ * The picture of the places and the line under it that says whose tiles these are, or why there are none.
45
+ * The map is only drawn while the frame is near the viewport and holds one of the page's few map slots
46
+ * (`MAX_LIVE_MAPS`); one that waits for a slot, or whose graphics context the browser took back, offers a button.
47
+ */
48
+ function MapFrame({ markers, source, selected, onPick }) {
49
+ const { t } = useTranslation('agent-chat');
50
+ const id = useId();
51
+ const [element, setElement] = useState(null);
52
+ const [status, setStatus] = useState('loading');
53
+ const [attempt, setAttempt] = useState(0);
54
+ const inView = useInView(element);
55
+ const broken = status === 'failed' || status === 'lost';
56
+ const slot = useMapSlot(id, inView && !broken);
57
+ const drawn = slot.state === 'granted' && !broken;
58
+ const waiting = useLater(drawn && status === 'loading', LOADING_SPINNER_MS);
59
+ const note = noteFor(t, source, status);
60
+ const quiet = (text, action) => (_jsxs("div", { className: "grid h-full place-items-center content-center justify-items-center gap-2 p-3 text-center", children: [_jsx("p", { className: "text-xs text-text-faint", children: text }), action !== undefined && (_jsx(Button, { size: "sm", variant: "secondary", onClick: action.run, children: action.label }))] }));
61
+ const retry = () => {
62
+ setStatus('loading');
63
+ setAttempt((previous) => previous + 1);
64
+ };
65
+ let body = null;
66
+ if (status === 'failed') {
67
+ body = quiet(t('blocks.map.unavailable'));
68
+ }
69
+ else if (status === 'lost') {
70
+ body = quiet(t('blocks.map.contextLost'), { label: t('blocks.map.show'), run: retry });
71
+ }
72
+ else if (slot.state === 'queued') {
73
+ body = quiet(t('blocks.map.waiting'), { label: t('blocks.map.show'), run: slot.take });
74
+ }
75
+ else if (drawn) {
76
+ body = (_jsx(DiffFallbackBoundary, { fallback: quiet(t('blocks.map.unavailable')), resetKeys: [], children: _jsx(LazyBoundary, { children: _jsx(MapView, { markers: markers, source: source ?? { state: 'off', reason: 'unavailable' }, selected: selected, onPick: onPick, onStatus: setStatus }, attempt) }) }));
77
+ }
78
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { ref: setElement, className: "relative h-50 overflow-hidden rounded-lg border border-border bg-surface-sunken @min-[420px]:h-60", children: [body, waiting && (_jsx("div", { className: "pointer-events-none absolute inset-0 grid place-items-center", children: _jsx(Spinner, { size: 16 }) }))] }), note !== null && _jsx("p", { className: "px-0.5 text-xs text-text-faint", children: note })] }));
79
+ }
80
+ /* Asks the host for its tiles in a component of its own, since a host that offers none has no hook to call. */
81
+ function HostedFrame({ maps, scopeId, ...frame }) {
82
+ return _jsx(MapFrame, { ...frame, source: maps.useSource(scopeId) });
83
+ }
84
+ /*
85
+ * The places of a map as a numbered list, which is also the form a screen reader, an offline page and a
86
+ * map without tiles all rely on. A place that carries a `value` is a button when the map is bound, and
87
+ * picking it sets the binding. A place the lookup could not place stays in the list without a marker.
88
+ */
89
+ export function MapRenderer({ node, children, context }) {
90
+ const { t } = useTranslation('agent-chat');
91
+ const binding = bindingOf(node, context);
92
+ const current = binding?.value ?? node.props.value;
93
+ const maps = useChatHost().maps;
94
+ const places = uiChildrenOf(node, 'Place').map((place) => ({
95
+ node: place,
96
+ name: uiNodeLabel(place),
97
+ point: placePoint(place.props, place.id, context)
98
+ }));
99
+ const markers = places.flatMap((listed, index) => listed.point ? [{ id: listed.node.id, number: index + 1, lat: listed.point.lat, lon: listed.point.lon }] : []);
100
+ const selectedId = places.find((listed) => listed.node.props.value !== undefined && listed.node.props.value === current)?.node.id ?? null;
101
+ const pick = (id) => {
102
+ const value = places.find((listed) => listed.node.id === id)?.node.props.value;
103
+ if (value !== undefined) {
104
+ binding?.onValueChange(value);
105
+ }
106
+ };
107
+ const onPick = binding !== null ? pick : null;
108
+ const frame = maps ? (_jsx(HostedFrame, { maps: maps, scopeId: context.scopeId, markers: markers, selected: selectedId, onPick: onPick })) : (_jsx(MapFrame, { markers: markers, source: null, selected: selectedId, onPick: onPick }));
109
+ return (_jsxs("div", { className: clsx('@container flex flex-col gap-1.5 px-2', !node.complete && PENDING), children: [frame, _jsx("ol", { "aria-label": t('blocks.map.label', { count: places.length }), className: "flex flex-col", children: places.map((listed, index) => {
110
+ const { value } = listed.node.props;
111
+ return (_jsx(PlaceRow, { listed: listed, number: index + 1, selected: value !== undefined && value === current, onPick: binding !== null && value !== undefined ? () => binding.onValueChange(value) : null }, listed.node.id));
112
+ }) }), node.omitted !== undefined && node.omitted > 0 && (_jsx("p", { className: "px-2 text-xs text-text-faint", children: t('blocks.map.omitted', { count: node.omitted }) })), _jsx("div", { className: "chat-ui-nodes flex flex-col gap-1.5 empty:hidden", children: children })] }));
113
+ }
@@ -0,0 +1,3 @@
1
+ import type { UiProps } from '@adecore/intelligent-ui';
2
+ import type { UiPlacePoint, UiRenderContext } from '../render-context.js';
3
+ export declare function placePoint(props: UiProps<'Place'>, nodeId: string, context: Pick<UiRenderContext, 'place'>): UiPlacePoint | null | undefined;
@@ -0,0 +1,13 @@
1
+ /*
2
+ * What the host found for the address of a place wins over the coordinates the agent wrote, which stand
3
+ * while the lookup is on its way and when it finds nothing. An address alone waits for the host's reading.
4
+ */
5
+ export function placePoint(props, nodeId, context) {
6
+ const { lat, lon, address } = props;
7
+ const written = lat !== undefined && lon !== undefined ? { lat, lon } : undefined;
8
+ if (address === undefined || address.trim() === '') {
9
+ return written ?? null;
10
+ }
11
+ const found = context.place === undefined ? null : context.place(nodeId, address);
12
+ return found ?? written ?? found;
13
+ }
@@ -6,3 +6,5 @@ export declare function TagRenderer({ node, children }: UiRendererProps<UiProps<
6
6
  export declare function ProgressRenderer({ node, children, context }: UiRendererProps<UiProps<'Progress'>>): import("react").JSX.Element;
7
7
  export declare function StepsRenderer({ children }: UiRendererProps<UiProps<'Steps'>>): import("react").JSX.Element;
8
8
  export declare function StepRenderer({ node, children, context }: UiRendererProps<UiProps<'Step'>>): import("react").JSX.Element;
9
+ export declare function TimelineRenderer({ children }: UiRendererProps<UiProps<'Timeline'>>): import("react").JSX.Element;
10
+ export declare function EventRenderer({ node, children }: UiRendererProps<UiProps<'Event'>>): import("react").JSX.Element;
@@ -6,7 +6,7 @@ import { Circle, CircleCheck, CircleDot, CircleMinus, CircleX } from 'lucide-rea
6
6
  import { Icon, Meter, Pill, Spinner } from '@adecore/ui';
7
7
  import { formatNumber, formatPercent } from '@adecore/ui/format';
8
8
  import { UiHeadContext } from '../render-context.js';
9
- import { uiNodeLabel } from '../node-text.js';
9
+ import { uiEventTime, uiNodeLabel } from '../node-text.js';
10
10
  import { UI_TONE_ICONS, UI_TONE_PILL, UI_TONE_SURFACE, UI_TONE_TEXT } from '../tones.js';
11
11
  const STEP_ICONS = {
12
12
  done: CircleCheck,
@@ -62,3 +62,16 @@ export function StepRenderer({ node, children, context }) {
62
62
  const quiet = state === 'pending' || state === 'skipped';
63
63
  return (_jsxs("li", { className: "flex min-h-7 items-center gap-2 text-sm", children: [_jsx("span", { className: clsx('grid size-4 shrink-0 place-items-center', STEP_TONES[state]), children: state === 'running' ? (isLive(context) ? (_jsx(Spinner, { size: 14 })) : (_jsx(Icon, { icon: CircleDot, size: 14 }))) : (_jsx(Icon, { icon: STEP_ICONS[state], size: 14 })) }), _jsx("span", { className: "sr-only", children: t(`blocks.step.${state}`) }), _jsxs("span", { className: "flex min-w-0 grow flex-wrap items-baseline justify-between gap-x-2", children: [_jsx("span", { className: clsx('max-w-full min-w-0 grow basis-48 wrap-break-word', quiet ? 'text-text-faint' : 'text-text'), children: children }), detail !== undefined && detail !== '' && _jsx("span", { className: "max-w-full text-xs wrap-anywhere text-text-faint tabular-nums", children: detail })] })] }));
64
64
  }
65
+ export function TimelineRenderer({ children }) {
66
+ return _jsx("ol", { className: "chat-ui-nodes flex flex-col px-2", children: children });
67
+ }
68
+ /* Each row draws its own stretch of the line, down to the next dot, so a row that failed to draw leaves the others whole. */
69
+ export function EventRenderer({ node, children }) {
70
+ const { t } = useTranslation('agent-chat');
71
+ const { at, detail } = node.props;
72
+ const tone = node.props.tone ?? 'neutral';
73
+ const time = uiEventTime(at);
74
+ // A neutral dot says nothing the title does not, and a detail is part of what the row says.
75
+ const label = [time, uiNodeLabel(node), tone === 'neutral' ? '' : t(`blocks.event.tone.${tone}`), detail ?? ''].filter((part) => part !== '').join(', ');
76
+ return (_jsxs("li", { "aria-label": label, className: "group/event relative flex min-h-7 gap-3 pb-3 last:pb-0", children: [_jsx("span", { "aria-hidden": true, className: clsx('mt-1 size-2 shrink-0 rounded-full bg-current', UI_TONE_TEXT[tone]) }), _jsx("span", { "aria-hidden": true, className: "absolute top-3.5 bottom-0 left-0.75 w-px bg-border group-last/event:hidden" }), _jsxs("div", { className: "flex min-w-0 grow flex-col gap-0.5", children: [_jsx("time", { dateTime: at, className: "text-xs text-text-faint tabular-nums", children: time }), _jsx("div", { className: "text-sm wrap-break-word text-text", children: children }), detail !== undefined && detail !== '' && _jsx("div", { className: "text-xs wrap-anywhere text-text-faint", children: detail })] })] }));
77
+ }
@@ -0,0 +1 @@
1
+ export declare function useInView(element: Element | null, margin?: string): boolean;
@@ -0,0 +1,17 @@
1
+ import { useEffect, useState } from 'react';
2
+ /*
3
+ * Whether `element` is near the viewport, with `margin` to spare so it is ready by the time it scrolls in.
4
+ * Without an observer (a server render, a test) it counts as in view.
5
+ */
6
+ export function useInView(element, margin = '200px') {
7
+ const [inView, setInView] = useState(typeof IntersectionObserver === 'undefined');
8
+ useEffect(() => {
9
+ if (element === null || typeof IntersectionObserver === 'undefined') {
10
+ return;
11
+ }
12
+ const observer = new IntersectionObserver((entries) => setInView(entries.at(-1)?.isIntersecting ?? false), { rootMargin: margin });
13
+ observer.observe(element);
14
+ return () => observer.disconnect();
15
+ }, [element, margin]);
16
+ return inView;
17
+ }
@@ -1,5 +1,5 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Fragment, Suspense, useEffect, useState } from 'react';
2
+ import { Fragment, useEffect, useState } from 'react';
3
3
  import clsx from 'clsx';
4
4
  import i18next from 'i18next';
5
5
  import { useTranslation } from 'react-i18next';
@@ -13,7 +13,7 @@ import { useChatScope } from '../../../scope.js';
13
13
  import { useCurrentItem } from '../../../state/chats.js';
14
14
  import { useTimelineFlash } from '../../timeline-flash.js';
15
15
  import { formatClockDuration, formatElapsedShort } from '@adecore/ui/format';
16
- import { Icon, MiddleTruncate, Spinner, useTickingText } from '@adecore/ui';
16
+ import { Icon, LazyBoundary, MiddleTruncate, Spinner, useTickingText } from '@adecore/ui';
17
17
  import { ROW_GUTTER, toolIcon } from '../icons.js';
18
18
  import { AnsiOutput } from '../AnsiOutput.js';
19
19
  import { RowTime } from '../RowTime.js';
@@ -48,7 +48,7 @@ export function ToggleLine({ icon, label, detail, command, path, open = false, o
48
48
  /* The diff renderers load lazily, so a diff waits on them under a line that says so. */
49
49
  function DiffSuspense({ children }) {
50
50
  const { t } = useTranslation('agent-chat');
51
- return _jsx(Suspense, { fallback: _jsx("div", { className: "px-3 py-2 text-xs text-text-faint", children: t('work.loadingDiff') }), children: children });
51
+ return _jsx(LazyBoundary, { fallback: _jsx("div", { className: "px-3 py-2 text-xs text-text-faint", children: t('work.loadingDiff') }), children: children });
52
52
  }
53
53
  /* A file in a changed files card, which opens its diff under it. */
54
54
  function ChangedFileLine({ path, open, onToggle, trailing, children }) {
package/dist/host.d.ts CHANGED
@@ -125,6 +125,19 @@ export interface ReplyAuthor {
125
125
  name: string;
126
126
  mark?: ReactNode;
127
127
  }
128
+ export type UiMapSource = {
129
+ state: 'tiles';
130
+ styleUrl(theme: 'light' | 'dark'): string;
131
+ transformRequest?(url: string): {
132
+ url: string;
133
+ headers?: Record<string, string>;
134
+ };
135
+ labelField?(language: string): string;
136
+ attribution?: string;
137
+ } | {
138
+ state: 'off';
139
+ reason: 'disabled' | 'signed-out' | 'unavailable';
140
+ };
128
141
  export interface ChatHost {
129
142
  intelligentUi?: {
130
143
  subscribe?(scopeId: string, chatId: string, changed: () => void): () => void;
@@ -134,6 +147,9 @@ export interface ChatHost {
134
147
  query?(scopeId: string, payload: ChatUiQueryPayload): Promise<ChatUiQueryReading>;
135
148
  sendChoice(scopeId: string, payload: ChatUiChoicePayload): Promise<'sent' | 'queued'>;
136
149
  };
150
+ maps?: {
151
+ useSource(scopeId: string): UiMapSource;
152
+ };
137
153
  accents: {
138
154
  all: readonly AccentChoice[];
139
155
  featured: readonly string[];
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- export { type ChatActions, type FileRef, type ShellCodeBlockContext, type ChatPlace, type SubagentTask, type TimelineFindOptions, type TimelineFind, type ComposerDictationProps, type ComposerSlotProps, type SubagentSlotProps, type ThreadCard, type VisualHost, type ReplyAuthor, type ChatHost, ChatHostProvider, useChatHost, type AccentChoice, type ChatToast, type TextareaProps, type ResourceUrl } from './host.js';
1
+ export { type ChatActions, type FileRef, type ShellCodeBlockContext, type ChatPlace, type SubagentTask, type TimelineFindOptions, type TimelineFind, type ComposerDictationProps, type ComposerSlotProps, type SubagentSlotProps, type ThreadCard, type VisualHost, type ReplyAuthor, type ChatHost, type UiMapSource, ChatHostProvider, useChatHost, type AccentChoice, type ChatToast, type TextareaProps, type ResourceUrl } from './host.js';
2
2
  export { type ChatScope, ChatScopeContext, useChatScope } from './scope.js';
3
3
  export { type ChatRequestMap, type ChatEventMap, type ChatTransportStatus, type ChatTransport, ChatTransportError, errorCode, isConnectionError } from './transport.js';
4
4
  export { portTransport, type PortTransport } from './port-transport.js';
5
- export { onChatStorageConfiguration, configureChatStorage, chatStorageKey, chatStorageLegacyKeys, chatStorageAdapter, hydrateChatStorage, createChatStore, type ChatStorageRecord, type ChatStorageAdapter, type ChatStorageOptions } from './storage.js';
5
+ export { configureChatStorage, type ChatStorageRecord, type ChatStorageAdapter, type ChatStorageOptions } from './storage.js';
6
6
  export { setLazyPrefetch, onLazyOpenError } from './lazy.js';
7
7
  export { type MountedEntry, MountedRegistry } from './mounted-registry.js';
8
8
  export { type AgentsSettingsSection, PROVIDERS_SECTION, USAGE_SECTION, settingsSection } from './settings/sections.js';
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ export { ChatHostProvider, useChatHost } from './host.js';
3
3
  export { ChatScopeContext, useChatScope } from './scope.js';
4
4
  export { ChatTransportError, errorCode, isConnectionError } from './transport.js';
5
5
  export { portTransport } from './port-transport.js';
6
- export { onChatStorageConfiguration, configureChatStorage, chatStorageKey, chatStorageLegacyKeys, chatStorageAdapter, hydrateChatStorage, createChatStore } from './storage.js';
6
+ export { configureChatStorage } from './storage.js';
7
7
  export { setLazyPrefetch, onLazyOpenError } from './lazy.js';
8
8
  export { MountedRegistry } from './mounted-registry.js';
9
9
  export { PROVIDERS_SECTION, USAGE_SECTION, settingsSection } from './settings/sections.js';
package/dist/lazy.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { lazy } from 'react';
3
+ import { LoadedComponent } from '@adecore/ui';
3
4
  const loaders = [];
4
5
  let register = null;
5
6
  const openErrorListeners = new Set();
@@ -29,12 +30,9 @@ export function onLazyOpenError(listener) {
29
30
  * module the prefetcher already fetched.
30
31
  */
31
32
  export function lazyNamed(load, name, options) {
32
- let loaded = null;
33
- const open = async () => {
34
- const component = (await load())[name];
35
- loaded ??= component;
36
- return component;
37
- };
33
+ // Through `LoadedComponent`, so a `LazyBoundary` around it draws the moment the module lands.
34
+ const loaded = new LoadedComponent(async () => (await load())[name]);
35
+ const open = () => loaded.load();
38
36
  if (options?.prefetch !== false) {
39
37
  loaders.push(open);
40
38
  register?.(open);
@@ -52,7 +50,7 @@ export function lazyNamed(load, name, options) {
52
50
  }
53
51
  });
54
52
  const LazyNamed = (props) => {
55
- const Component = loaded ?? Lazy;
53
+ const Component = loaded.current ?? Lazy;
56
54
  return _jsx(Component, { ...props });
57
55
  };
58
56
  return LazyNamed;
@@ -301,6 +301,8 @@ declare const _default: {
301
301
  };
302
302
  notice: {
303
303
  clearFailed: string;
304
+ stashChatsDropped_one: string;
305
+ stashChatsDropped_other: string;
304
306
  compactFailed: string;
305
307
  noAttachments: string;
306
308
  thisAgent: string;
@@ -344,6 +346,8 @@ declare const _default: {
344
346
  tooLong: string;
345
347
  stop: string;
346
348
  queueButton: string;
349
+ continue: string;
350
+ continueHint: string;
347
351
  send: string;
348
352
  clear: {
349
353
  title: string;
@@ -438,6 +442,8 @@ declare const _default: {
438
442
  noText: string;
439
443
  files_one: string;
440
444
  files_other: string;
445
+ chats_one: string;
446
+ chats_other: string;
441
447
  };
442
448
  mode: {
443
449
  label: string;
@@ -549,6 +555,15 @@ declare const _default: {
549
555
  failed: string;
550
556
  skipped: string;
551
557
  };
558
+ event: {
559
+ tone: {
560
+ neutral: string;
561
+ info: string;
562
+ success: string;
563
+ warning: string;
564
+ danger: string;
565
+ };
566
+ };
552
567
  stat: {
553
568
  change: string;
554
569
  from: string;
@@ -568,6 +583,35 @@ declare const _default: {
568
583
  showValues: string;
569
584
  hideValues: string;
570
585
  };
586
+ gallery: {
587
+ label_one: string;
588
+ label_other: string;
589
+ };
590
+ map: {
591
+ label_one: string;
592
+ label_other: string;
593
+ omitted_one: string;
594
+ omitted_other: string;
595
+ show: string;
596
+ waiting: string;
597
+ contextLost: string;
598
+ lookingUp: string;
599
+ notFound: string;
600
+ zoomIn: string;
601
+ zoomOut: string;
602
+ scrollHint: string;
603
+ touchHint: string;
604
+ offline: string;
605
+ disabled: string;
606
+ signedOut: string;
607
+ unavailable: string;
608
+ };
609
+ compare: {
610
+ label: string;
611
+ before: string;
612
+ after: string;
613
+ missing: string;
614
+ };
571
615
  image: {
572
616
  label: string;
573
617
  unavailable: string;
@@ -575,6 +619,9 @@ declare const _default: {
575
619
  sources: string;
576
620
  citation: string;
577
621
  outsideProject: string;
622
+ textField: {
623
+ count: string;
624
+ };
578
625
  options: string;
579
626
  choices: string;
580
627
  checklist: string;
@@ -301,6 +301,8 @@ export default {
301
301
  },
302
302
  notice: {
303
303
  clearFailed: 'The thread could not be cleared',
304
+ stashChatsDropped_one: '1 attached chat is not in this project and was left out',
305
+ stashChatsDropped_other: '{{count}} attached chats are not in this project and were left out',
304
306
  compactFailed: 'The context could not be compacted',
305
307
  noAttachments: '{{agent}} takes no attachments',
306
308
  thisAgent: 'This agent',
@@ -344,6 +346,8 @@ export default {
344
346
  tooLong: 'characters, too long to send',
345
347
  stop: 'Stop, Shift-click to also stop its sub-agents',
346
348
  queueButton: 'Queue',
349
+ continue: 'Continue',
350
+ continueHint: 'Continue where the stopped turn left off',
347
351
  send: 'Send',
348
352
  clear: {
349
353
  title: 'Clear the thread?',
@@ -437,7 +441,9 @@ export default {
437
441
  title: 'Stashed prompts',
438
442
  noText: 'No text',
439
443
  files_one: '{{count}} file, not kept',
440
- files_other: '{{count}} files, not kept'
444
+ files_other: '{{count}} files, not kept',
445
+ chats_one: '{{count}} chat attached',
446
+ chats_other: '{{count}} chats attached'
441
447
  },
442
448
  mode: {
443
449
  label: 'Permissions',
@@ -549,6 +555,15 @@ export default {
549
555
  failed: 'Failed',
550
556
  skipped: 'Skipped'
551
557
  },
558
+ event: {
559
+ tone: {
560
+ neutral: 'Neutral',
561
+ info: 'Info',
562
+ success: 'Success',
563
+ warning: 'Warning',
564
+ danger: 'Danger'
565
+ }
566
+ },
552
567
  stat: {
553
568
  change: '{{change}} from {{previous}}',
554
569
  from: 'from {{previous}}'
@@ -568,6 +583,35 @@ export default {
568
583
  showValues: 'Show all values',
569
584
  hideValues: 'Hide values'
570
585
  },
586
+ gallery: {
587
+ label_one: '{{count}} image',
588
+ label_other: '{{count}} images'
589
+ },
590
+ map: {
591
+ label_one: 'Map of {{count}} place',
592
+ label_other: 'Map of {{count}} places',
593
+ omitted_one: '{{count}} more place is not shown',
594
+ omitted_other: '{{count}} more places are not shown',
595
+ show: 'Show map',
596
+ waiting: 'The map waits for a free spot: only a few maps are drawn at once.',
597
+ contextLost: 'The map lost its graphics. The places stay listed.',
598
+ lookingUp: 'Looking up',
599
+ notFound: 'Not on the map',
600
+ zoomIn: 'Zoom in',
601
+ zoomOut: 'Zoom out',
602
+ scrollHint: 'Hold ⌘ or Ctrl and scroll to zoom',
603
+ touchHint: 'Use two fingers to move the map',
604
+ offline: 'Map tiles are not available offline. The places stay listed.',
605
+ disabled: 'Map tiles are off.',
606
+ signedOut: 'Sign in to show map tiles.',
607
+ unavailable: 'Map tiles are not available right now.'
608
+ },
609
+ compare: {
610
+ label: 'Compare {{before}} and {{after}}',
611
+ before: 'Before',
612
+ after: 'After',
613
+ missing: 'One image is not available'
614
+ },
571
615
  image: {
572
616
  label: 'Image',
573
617
  unavailable: 'The image is not available'
@@ -575,6 +619,9 @@ export default {
575
619
  sources: 'Sources',
576
620
  citation: 'Source {{number}}: {{title}}',
577
621
  outsideProject: 'Outside the project, shown as text',
622
+ textField: {
623
+ count: '{{used}} of {{max}} characters'
624
+ },
578
625
  options: 'Options',
579
626
  choices: 'Choices',
580
627
  checklist: 'Checklist',
@@ -301,6 +301,8 @@ declare const _default: {
301
301
  };
302
302
  notice: {
303
303
  clearFailed: string;
304
+ stashChatsDropped_one: string;
305
+ stashChatsDropped_other: string;
304
306
  compactFailed: string;
305
307
  noAttachments: string;
306
308
  thisAgent: string;
@@ -344,6 +346,8 @@ declare const _default: {
344
346
  tooLong: string;
345
347
  stop: string;
346
348
  queueButton: string;
349
+ continue: string;
350
+ continueHint: string;
347
351
  send: string;
348
352
  clear: {
349
353
  title: string;
@@ -438,6 +442,8 @@ declare const _default: {
438
442
  noText: string;
439
443
  files_one: string;
440
444
  files_other: string;
445
+ chats_one: string;
446
+ chats_other: string;
441
447
  };
442
448
  mode: {
443
449
  label: string;
@@ -549,6 +555,15 @@ declare const _default: {
549
555
  failed: string;
550
556
  skipped: string;
551
557
  };
558
+ event: {
559
+ tone: {
560
+ neutral: string;
561
+ info: string;
562
+ success: string;
563
+ warning: string;
564
+ danger: string;
565
+ };
566
+ };
552
567
  stat: {
553
568
  change: string;
554
569
  from: string;
@@ -568,6 +583,35 @@ declare const _default: {
568
583
  showValues: string;
569
584
  hideValues: string;
570
585
  };
586
+ gallery: {
587
+ label_one: string;
588
+ label_other: string;
589
+ };
590
+ map: {
591
+ label_one: string;
592
+ label_other: string;
593
+ omitted_one: string;
594
+ omitted_other: string;
595
+ show: string;
596
+ waiting: string;
597
+ contextLost: string;
598
+ lookingUp: string;
599
+ notFound: string;
600
+ zoomIn: string;
601
+ zoomOut: string;
602
+ scrollHint: string;
603
+ touchHint: string;
604
+ offline: string;
605
+ disabled: string;
606
+ signedOut: string;
607
+ unavailable: string;
608
+ };
609
+ compare: {
610
+ label: string;
611
+ before: string;
612
+ after: string;
613
+ missing: string;
614
+ };
571
615
  image: {
572
616
  label: string;
573
617
  unavailable: string;
@@ -575,6 +619,9 @@ declare const _default: {
575
619
  sources: string;
576
620
  citation: string;
577
621
  outsideProject: string;
622
+ textField: {
623
+ count: string;
624
+ };
578
625
  options: string;
579
626
  choices: string;
580
627
  checklist: string;
@@ -301,6 +301,8 @@ export default {
301
301
  },
302
302
  notice: {
303
303
  clearFailed: 'De thread kon niet worden gewist',
304
+ stashChatsDropped_one: '1 bijgevoegde chat hoort niet bij dit project en is weggelaten',
305
+ stashChatsDropped_other: '{{count}} bijgevoegde chats horen niet bij dit project en zijn weggelaten',
304
306
  compactFailed: 'De context kon niet worden gecomprimeerd',
305
307
  noAttachments: '{{agent}} neemt geen bijlagen aan',
306
308
  thisAgent: 'Deze agent',
@@ -344,6 +346,8 @@ export default {
344
346
  tooLong: 'tekens, te lang om te versturen',
345
347
  stop: 'Stoppen, Shift-klik stopt ook de sub-agents',
346
348
  queueButton: 'In de wachtrij zetten',
349
+ continue: 'Doorgaan',
350
+ continueHint: 'Ga verder waar de gestopte beurt ophield',
347
351
  send: 'Versturen',
348
352
  clear: {
349
353
  title: 'Thread wissen?',
@@ -437,7 +441,9 @@ export default {
437
441
  title: 'Stashed prompts',
438
442
  noText: 'Geen tekst',
439
443
  files_one: '{{count}} bestand, niet bewaard',
440
- files_other: '{{count}} bestanden, niet bewaard'
444
+ files_other: '{{count}} bestanden, niet bewaard',
445
+ chats_one: '{{count}} chat bijgevoegd',
446
+ chats_other: '{{count}} chats bijgevoegd'
441
447
  },
442
448
  mode: {
443
449
  label: 'Rechten',
@@ -549,6 +555,15 @@ export default {
549
555
  failed: 'Mislukt',
550
556
  skipped: 'Overgeslagen'
551
557
  },
558
+ event: {
559
+ tone: {
560
+ neutral: 'Neutraal',
561
+ info: 'Info',
562
+ success: 'Gelukt',
563
+ warning: 'Waarschuwing',
564
+ danger: 'Gevaar'
565
+ }
566
+ },
552
567
  stat: {
553
568
  change: '{{change}} vanaf {{previous}}',
554
569
  from: 'vanaf {{previous}}'
@@ -568,6 +583,35 @@ export default {
568
583
  showValues: 'Alle waarden tonen',
569
584
  hideValues: 'Waarden verbergen'
570
585
  },
586
+ gallery: {
587
+ label_one: '{{count}} afbeelding',
588
+ label_other: '{{count}} afbeeldingen'
589
+ },
590
+ map: {
591
+ label_one: 'Kaart met {{count}} plek',
592
+ label_other: 'Kaart met {{count}} plekken',
593
+ omitted_one: '{{count}} plek meer wordt niet getoond',
594
+ omitted_other: '{{count}} plekken meer worden niet getoond',
595
+ show: 'Kaart tonen',
596
+ waiting: 'De kaart wacht op een vrije plek: er worden maar een paar kaarten tegelijk getekend.',
597
+ contextLost: 'De kaart is zijn beeldgeheugen kwijt. De plekken blijven in de lijst.',
598
+ lookingUp: 'Wordt opgezocht',
599
+ notFound: 'Niet op de kaart',
600
+ zoomIn: 'Inzoomen',
601
+ zoomOut: 'Uitzoomen',
602
+ scrollHint: 'Houd ⌘ of Ctrl ingedrukt en scrol om te zoomen',
603
+ touchHint: 'Gebruik twee vingers om de kaart te bewegen',
604
+ offline: 'Kaarttegels zijn offline niet beschikbaar. De plekken blijven in de lijst.',
605
+ disabled: 'Kaarttegels staan uit.',
606
+ signedOut: 'Log in om kaarttegels te tonen.',
607
+ unavailable: 'Kaarttegels zijn nu niet beschikbaar.'
608
+ },
609
+ compare: {
610
+ label: 'Vergelijk {{before}} en {{after}}',
611
+ before: 'Voor',
612
+ after: 'Na',
613
+ missing: 'Eén afbeelding is niet beschikbaar'
614
+ },
571
615
  image: {
572
616
  label: 'Afbeelding',
573
617
  unavailable: 'De afbeelding is niet beschikbaar'
@@ -575,6 +619,9 @@ export default {
575
619
  sources: 'Bronnen',
576
620
  citation: 'Bron {{number}}: {{title}}',
577
621
  outsideProject: 'Buiten het project, als tekst getoond',
622
+ textField: {
623
+ count: '{{used}} van {{max}} tekens'
624
+ },
578
625
  options: 'Opties',
579
626
  choices: 'Keuzes',
580
627
  checklist: 'Checklijst',