@adecore/agents-react 0.25.0 → 0.25.2

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.
@@ -1,5 +1,5 @@
1
1
  import { parseUiEventTime } from '@adecore/intelligent-ui';
2
- import { formatClock, formatDay, formatDayClock, formatDayClockWithYear, formatDayWithYear } from '@adecore/ui/format';
2
+ import { formatClock, formatDateTime, formatDay, formatDayClock, formatDayClockWithYear, formatDayWithYear } from '@adecore/ui/format';
3
3
  /* The text a node holds, every `$text` below it joined as written, such as the code of a CodeBlock. */
4
4
  export function uiNodeText(node) {
5
5
  return node.children.map((child) => (child.type === '$text' ? String(child.props.text ?? '') : uiNodeText(child))).join('');
@@ -129,7 +129,9 @@ export function uiEventTime(at, now = Date.now()) {
129
129
  const year = moment.written ? moment.date.getUTCFullYear() : moment.date.getFullYear();
130
130
  const withYear = year !== new Date(now).getFullYear();
131
131
  if (moment.kind === 'time') {
132
- return formatClock(moment.date, zone);
132
+ return moment.seconds
133
+ ? formatDateTime(moment.date, { hour: '2-digit', minute: '2-digit', second: '2-digit', timeZone: 'UTC' })
134
+ : formatClock(moment.date, zone);
133
135
  }
134
136
  if (moment.kind === 'date') {
135
137
  return withYear ? formatDayWithYear(moment.date, zone) : formatDay(moment.date, zone);
@@ -11,12 +11,17 @@ const MARKER = 'flex size-5 items-center justify-center rounded-full bg-map-mark
11
11
  const PICKED = 'z-1 bg-accent text-accent-text outline-2 outline-surface';
12
12
  // A tile that fails this many times with none loading in between leaves the map empty, so the outline stands in.
13
13
  const TILE_ERRORS_IN_A_ROW = 4;
14
- let workerReady = false;
15
- /* The worker is a file of the package, so the bundler of the app has to serve it: the default address beside the chunk would not exist. */
16
- function ensureBundledWorker() {
17
- if (!workerReady) {
18
- setWorkerUrl(new URL('maplibre-gl/dist/maplibre-gl-worker.mjs', import.meta.url).href);
19
- workerReady = true;
14
+ let workerUrlSet = null;
15
+ /*
16
+ * The worker is a file of the package, so the bundler of the app has to serve it: the default address beside the chunk
17
+ * would not exist. A dev server that pre-bundles this package rewrites the fallback to a folder that is gone, so the
18
+ * host hands the address from its own source.
19
+ */
20
+ function ensureBundledWorker(hosted) {
21
+ const url = hosted ?? new URL('maplibre-gl/dist/maplibre-gl-worker.mjs', import.meta.url).href;
22
+ if (url !== workerUrlSet) {
23
+ setWorkerUrl(url);
24
+ workerUrlSet = url;
20
25
  }
21
26
  }
22
27
  /* The grounds of the built-in map are theme tokens, which the page resolves; the style needs plain colors. */
@@ -31,7 +36,9 @@ function readColors(element) {
31
36
  */
32
37
  export function MapView({ markers, source, selected, onPick, onStatus }) {
33
38
  const { t, i18n } = useTranslation('agent-chat');
34
- const mode = useChatHost().code.useMode();
39
+ const host = useChatHost();
40
+ const mode = host.code.useMode();
41
+ const workerUrl = host.maps?.workerUrl;
35
42
  const element = useRef(null);
36
43
  const [map, setMap] = useState(null);
37
44
  const [outline, setOutline] = useState(false);
@@ -53,7 +60,7 @@ export function MapView({ markers, source, selected, onPick, onStatus }) {
53
60
  const tiles = current.state === 'tiles' && !offline;
54
61
  let created;
55
62
  try {
56
- ensureBundledWorker();
63
+ ensureBundledWorker(workerUrl);
57
64
  created = new MapLibreMap({
58
65
  container,
59
66
  style: current.state === 'tiles' && tiles ? current.styleUrl(mode) : baseMapStyle(colors),
@@ -1,4 +1,4 @@
1
- import type { UiProps } from '@adecore/intelligent-ui';
1
+ import { type UiProps } from '@adecore/intelligent-ui';
2
2
  import { type UiRendererProps } from '../render-context.js';
3
3
  export declare function SummaryRenderer({ node, children }: UiRendererProps<UiProps<'Summary'>>): import("react").JSX.Element;
4
4
  export declare function CalloutRenderer({ node, children }: UiRendererProps<UiProps<'Callout'>>): import("react").JSX.Element;
@@ -3,6 +3,7 @@ import { useContext } from 'react';
3
3
  import clsx from 'clsx';
4
4
  import { useTranslation } from 'react-i18next';
5
5
  import { Circle, CircleCheck, CircleDot, CircleMinus, CircleX } from 'lucide-react';
6
+ import { parseUiEventTime } from '@adecore/intelligent-ui';
6
7
  import { Icon, Meter, Pill, Spinner } from '@adecore/ui';
7
8
  import { formatNumber, formatPercent } from '@adecore/ui/format';
8
9
  import { UiHeadContext } from '../render-context.js';
@@ -73,5 +74,5 @@ export function EventRenderer({ node, children }) {
73
74
  const time = uiEventTime(at);
74
75
  // A neutral dot says nothing the title does not, and a detail is part of what the row says.
75
76
  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
+ 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: [parseUiEventTime(at) === null ? (_jsx("span", { className: "text-xs wrap-anywhere text-text-faint tabular-nums", children: time })) : (_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
78
  }
package/dist/host.d.ts CHANGED
@@ -149,6 +149,7 @@ export interface ChatHost {
149
149
  };
150
150
  maps?: {
151
151
  useSource(scopeId: string): UiMapSource;
152
+ workerUrl?: string;
152
153
  };
153
154
  accents: {
154
155
  all: readonly AccentChoice[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adecore/agents-react",
3
- "version": "0.25.0",
3
+ "version": "0.25.2",
4
4
  "description": "React agent chat threads, composers, approvals, provider accounts and usage views.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -67,11 +67,11 @@
67
67
  "test:visuals": "bun --config=integration.bunfig.toml --conditions=source test ./src/chat/visual-viewport.integration.test.ts"
68
68
  },
69
69
  "dependencies": {
70
- "@adecore/agent-contracts": "0.25.0",
71
- "@adecore/editor": "0.25.0",
72
- "@adecore/editor-react": "0.25.0",
73
- "@adecore/intelligent-ui": "0.25.0",
74
- "@adecore/merge": "0.25.0",
70
+ "@adecore/agent-contracts": "0.25.2",
71
+ "@adecore/editor": "0.25.2",
72
+ "@adecore/editor-react": "0.25.2",
73
+ "@adecore/intelligent-ui": "0.25.2",
74
+ "@adecore/merge": "0.25.2",
75
75
  "@codemirror/commands": "^6.11.1",
76
76
  "@codemirror/language": "^6.12.4",
77
77
  "@codemirror/state": "^6.7.6",
@@ -94,14 +94,14 @@
94
94
  "zustand": "^5.0.15"
95
95
  },
96
96
  "peerDependencies": {
97
- "@adecore/ui": "^0.25.0",
97
+ "@adecore/ui": "^0.25.2",
98
98
  "i18next": "^26.4.2",
99
99
  "react": "^19.3.0",
100
100
  "react-dom": "^19.3.0",
101
101
  "react-i18next": "^17.0.15"
102
102
  },
103
103
  "devDependencies": {
104
- "@adecore/ui": "0.25.0",
104
+ "@adecore/ui": "0.25.2",
105
105
  "@types/bun": "^1.4.2",
106
106
  "@types/react": "^19.3.0",
107
107
  "@types/react-dom": "^19.3.0",
@@ -1,6 +1,6 @@
1
1
  import type { TFunction } from 'i18next';
2
2
  import { parseUiEventTime, type UiComponentName, type UiProps, type UiViewNode } from '@adecore/intelligent-ui';
3
- import { formatClock, formatDay, formatDayClock, formatDayClockWithYear, formatDayWithYear } from '@adecore/ui/format';
3
+ import { formatClock, formatDateTime, formatDay, formatDayClock, formatDayClockWithYear, formatDayWithYear } from '@adecore/ui/format';
4
4
  import type { UiLiveStatus } from './render-context';
5
5
 
6
6
  /* The text a node holds, every `$text` below it joined as written, such as the code of a CodeBlock. */
@@ -143,7 +143,9 @@ export function uiEventTime(at: string, now: Date | number = Date.now()): string
143
143
  const year = moment.written ? moment.date.getUTCFullYear() : moment.date.getFullYear();
144
144
  const withYear = year !== new Date(now).getFullYear();
145
145
  if (moment.kind === 'time') {
146
- return formatClock(moment.date, zone);
146
+ return moment.seconds
147
+ ? formatDateTime(moment.date, { hour: '2-digit', minute: '2-digit', second: '2-digit', timeZone: 'UTC' })
148
+ : formatClock(moment.date, zone);
147
149
  }
148
150
  if (moment.kind === 'date') {
149
151
  return withYear ? formatDayWithYear(moment.date, zone) : formatDay(moment.date, zone);
@@ -33,13 +33,18 @@ const PICKED = 'z-1 bg-accent text-accent-text outline-2 outline-surface';
33
33
  // A tile that fails this many times with none loading in between leaves the map empty, so the outline stands in.
34
34
  const TILE_ERRORS_IN_A_ROW = 4;
35
35
 
36
- let workerReady = false;
36
+ let workerUrlSet: string | null = null;
37
37
 
38
- /* The worker is a file of the package, so the bundler of the app has to serve it: the default address beside the chunk would not exist. */
39
- function ensureBundledWorker(): void {
40
- if (!workerReady) {
41
- setWorkerUrl(new URL('maplibre-gl/dist/maplibre-gl-worker.mjs', import.meta.url).href);
42
- workerReady = true;
38
+ /*
39
+ * The worker is a file of the package, so the bundler of the app has to serve it: the default address beside the chunk
40
+ * would not exist. A dev server that pre-bundles this package rewrites the fallback to a folder that is gone, so the
41
+ * host hands the address from its own source.
42
+ */
43
+ function ensureBundledWorker(hosted: string | undefined): void {
44
+ const url = hosted ?? new URL('maplibre-gl/dist/maplibre-gl-worker.mjs', import.meta.url).href;
45
+ if (url !== workerUrlSet) {
46
+ setWorkerUrl(url);
47
+ workerUrlSet = url;
43
48
  }
44
49
  }
45
50
 
@@ -56,7 +61,9 @@ function readColors(element: HTMLElement): MapColors {
56
61
  */
57
62
  export function MapView({ markers, source, selected, onPick, onStatus }: MapViewProps) {
58
63
  const { t, i18n } = useTranslation('agent-chat');
59
- const mode = useChatHost().code.useMode();
64
+ const host = useChatHost();
65
+ const mode = host.code.useMode();
66
+ const workerUrl = host.maps?.workerUrl;
60
67
  const element = useRef<HTMLDivElement>(null);
61
68
  const [map, setMap] = useState<MapLibreMap | null>(null);
62
69
  const [outline, setOutline] = useState(false);
@@ -79,7 +86,7 @@ export function MapView({ markers, source, selected, onPick, onStatus }: MapView
79
86
  const tiles = current.state === 'tiles' && !offline;
80
87
  let created: MapLibreMap;
81
88
  try {
82
- ensureBundledWorker();
89
+ ensureBundledWorker(workerUrl);
83
90
  created = new MapLibreMap({
84
91
  container,
85
92
  style: current.state === 'tiles' && tiles ? current.styleUrl(mode) : baseMapStyle(colors),
@@ -2,7 +2,7 @@ import { useContext } from 'react';
2
2
  import clsx from 'clsx';
3
3
  import { useTranslation } from 'react-i18next';
4
4
  import { Circle, CircleCheck, CircleDot, CircleMinus, CircleX, type LucideIcon } from 'lucide-react';
5
- import type { UiProps } from '@adecore/intelligent-ui';
5
+ import { parseUiEventTime, type UiProps } from '@adecore/intelligent-ui';
6
6
  import { Icon, Meter, Pill, Spinner } from '@adecore/ui';
7
7
  import { formatNumber, formatPercent } from '@adecore/ui/format';
8
8
  import { UiHeadContext, type UiRenderContext, type UiRendererProps } from '../render-context';
@@ -150,9 +150,14 @@ export function EventRenderer({ node, children }: UiRendererProps<UiProps<'Event
150
150
  <span aria-hidden className={clsx('mt-1 size-2 shrink-0 rounded-full bg-current', UI_TONE_TEXT[tone])} />
151
151
  <span aria-hidden className="absolute top-3.5 bottom-0 left-0.75 w-px bg-border group-last/event:hidden" />
152
152
  <div className="flex min-w-0 grow flex-col gap-0.5">
153
- <time dateTime={at} className="text-xs text-text-faint tabular-nums">
154
- {time}
155
- </time>
153
+ {/* A time nobody could read stays as written and is no `dateTime` for a machine to read. */}
154
+ {parseUiEventTime(at) === null ? (
155
+ <span className="text-xs wrap-anywhere text-text-faint tabular-nums">{time}</span>
156
+ ) : (
157
+ <time dateTime={at} className="text-xs text-text-faint tabular-nums">
158
+ {time}
159
+ </time>
160
+ )}
156
161
  <div className="text-sm wrap-break-word text-text">{children}</div>
157
162
  {detail !== undefined && detail !== '' && <div className="text-xs wrap-anywhere text-text-faint">{detail}</div>}
158
163
  </div>
package/src/host.ts CHANGED
@@ -236,6 +236,11 @@ export interface ChatHost {
236
236
  /* The map tiles of a Map block; left out, a map draws its outline and its list of places only. */
237
237
  maps?: {
238
238
  useSource(scopeId: string): UiMapSource;
239
+ /*
240
+ * The address of MapLibre's worker file (`maplibre-gl/dist/maplibre-gl-worker.mjs`) as the app's bundler serves it,
241
+ * for a bundler that moves this package and breaks the address the map would otherwise work out.
242
+ */
243
+ workerUrl?: string;
239
244
  };
240
245
  /* The colors an account may wear in the order a picker offers them, the few it shows first, and their names. */
241
246
  accents: {