@anchrd/intel-ui 0.9.0 → 0.9.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anchrd/intel-ui",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -6,7 +6,7 @@ import {
6
6
  type Node,
7
7
  } from "@anchrd/intel-contract";
8
8
  import { useQuery } from "@tanstack/react-query";
9
- import { Check, Copy, MessageSquare, Plus, Trash2, Wrench } from "lucide-react";
9
+ import { Check, Copy, Info, MessageSquare, Plus, Trash2, Wrench } from "lucide-react";
10
10
  import { useState } from "react";
11
11
  import type { AgentDefinitionHandle } from "@/agent/agent-definition/agent-definition.ts";
12
12
  import { useEntryTitle } from "@/agent/agent-entry-title/agent-entry-title.ts";
@@ -18,11 +18,15 @@ import {
18
18
  SelectTrigger,
19
19
  SelectValue,
20
20
  } from "@/components/ui/select";
21
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
21
22
  import { agentMcpAddress } from "@/data/agent-runtime/agent-runtime.ts";
22
23
  import { EntryPicker, type PickerKind } from "@/entry-picker/entry-picker.tsx";
23
24
  import { Modal } from "@/modal/modal.tsx";
24
25
  import { useIntelRouterContext } from "@/router/router-context.ts";
25
26
 
27
+ // No dividers between sections — the space is the separation (#204). The explanation sits behind
28
+ // the icon rather than under the title, so the page shows what the agent IS and keeps the prose
29
+ // for whoever asks.
26
30
  function Section({
27
31
  title,
28
32
  hint,
@@ -33,14 +37,36 @@ function Section({
33
37
  children: React.ReactNode;
34
38
  }) {
35
39
  return (
36
- <section aria-label={title} className="border-b px-6 py-5 last:border-b-0">
37
- <h3 className="text-sm font-semibold">{title}</h3>
38
- {hint ? <p className="mt-1 max-w-2xl text-xs text-muted-foreground">{hint}</p> : null}
40
+ <section aria-label={title} className="px-6 py-7">
41
+ <div className="flex items-center gap-1.5">
42
+ <h3 className="text-sm font-semibold">{title}</h3>
43
+ {hint ? <SectionHint hint={hint} /> : null}
44
+ </div>
39
45
  <div className="mt-3">{children}</div>
40
46
  </section>
41
47
  );
42
48
  }
43
49
 
50
+ // ⚠️ The hint is the trigger's accessible name, not only the tooltip's content: Radix describes the
51
+ // trigger by the content only while it is open, and a listener who tabs past a nameless icon button
52
+ // would never learn there is anything behind it.
53
+ function SectionHint({ hint }: { hint: string }) {
54
+ return (
55
+ <TooltipProvider delayDuration={300}>
56
+ <Tooltip>
57
+ <TooltipTrigger
58
+ type="button"
59
+ aria-label={hint}
60
+ className="rounded-full text-muted-foreground outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring"
61
+ >
62
+ <Info aria-hidden="true" className="size-3.5" />
63
+ </TooltipTrigger>
64
+ <TooltipContent className="max-w-xs">{hint}</TooltipContent>
65
+ </Tooltip>
66
+ </TooltipProvider>
67
+ );
68
+ }
69
+
44
70
  /**
45
71
  * Everything an agent is, on one page: how to reach it, what it reads, what it may call, when it
46
72
  * acts on its own, and which model does the thinking.
@@ -151,20 +177,31 @@ function ContactSection({ node }: { node: Node }) {
151
177
  </button>
152
178
  </li>
153
179
  </ul>
154
- {/* ⚠️ Disabled, and the reason is written rather than hovered. A mail contact is deployment
155
- configuration (`AGENT_MAILBOXES`) and deliberately not part of the definition — ADR-0005
156
- §4 — so a working button here would write a field the runtime never reads. */}
180
+ {/* ⚠️ Disabled, with the reason behind a hover rather than on the page (#204). A mail contact
181
+ is deployment configuration (`AGENT_MAILBOXES`) and deliberately not part of the definition
182
+ — ADR-0005 §4 — so a working button here would write a field the runtime never reads. The
183
+ span carries the tooltip because a disabled button swallows the pointer events the trigger
184
+ listens for; the sr-only copy keeps the reason where `aria-describedby` points. */}
157
185
  <div className="mt-3">
158
- <button
159
- type="button"
160
- disabled
161
- aria-describedby="agent-contact-add-reason"
162
- className="inline-flex items-center gap-2 rounded-md border px-3 py-2 text-sm disabled:opacity-50"
163
- >
164
- <Plus aria-hidden="true" className="size-4" />
165
- {i18n.t("agent.contactAdd")}
166
- </button>
167
- <p id="agent-contact-add-reason" className="mt-1.5 text-xs text-muted-foreground">
186
+ <TooltipProvider delayDuration={300}>
187
+ <Tooltip>
188
+ <TooltipTrigger asChild>
189
+ <span className="inline-flex">
190
+ <button
191
+ type="button"
192
+ disabled
193
+ aria-describedby="agent-contact-add-reason"
194
+ className="inline-flex items-center gap-2 rounded-md border px-3 py-2 text-sm disabled:opacity-50"
195
+ >
196
+ <Plus aria-hidden="true" className="size-4" />
197
+ {i18n.t("agent.contactAdd")}
198
+ </button>
199
+ </span>
200
+ </TooltipTrigger>
201
+ <TooltipContent className="max-w-xs">{i18n.t("agent.contactAddReason")}</TooltipContent>
202
+ </Tooltip>
203
+ </TooltipProvider>
204
+ <p id="agent-contact-add-reason" className="sr-only">
168
205
  {i18n.t("agent.contactAddReason")}
169
206
  </p>
170
207
  </div>
@@ -196,9 +233,8 @@ function KnowledgeSection({
196
233
 
197
234
  return (
198
235
  <Section title={i18n.t("agent.knows")} hint={i18n.t("agent.knowsHint")}>
199
- {definition.references.length === 0 ? (
200
- <p className="text-sm text-muted-foreground">{i18n.t("agent.knowsEmpty")}</p>
201
- ) : (
236
+ {/* An empty list shows nothing at all — the add button says what belongs here (#204). */}
237
+ {definition.references.length === 0 ? null : (
202
238
  <ul className="space-y-2">
203
239
  {/* ⚠️ The position IS the identity here: `references` is an ordered array with no ids,
204
240
  the contract permits the same folder twice, and every edit below addresses a row by
@@ -331,9 +367,10 @@ function ToolsSection() {
331
367
  <p role="alert" className="text-sm text-destructive">
332
368
  {i18n.t("tools.unreachableHelp")}
333
369
  </p>
334
- ) : catalog.data && catalog.data.items.length === 0 ? (
335
- <p className="text-sm text-muted-foreground">{i18n.t("tools.emptyHelp")}</p>
336
- ) : (
370
+ ) : catalog.data &&
371
+ catalog.data.items.length ===
372
+ 0 ? // Nothing rather than a paragraph (#204); the Tools screen still explains the empty catalog.
373
+ null : (
337
374
  <ul className="flex flex-wrap gap-2">
338
375
  {catalog.data?.items.map((tool) => (
339
376
  <li
@@ -372,9 +409,7 @@ function ScheduleSection({
372
409
 
373
410
  return (
374
411
  <Section title={i18n.t("agent.schedule")} hint={i18n.t("agent.scheduleHint")}>
375
- {definition.schedules.length === 0 ? (
376
- <p className="text-sm text-muted-foreground">{i18n.t("agent.scheduleEmpty")}</p>
377
- ) : (
412
+ {definition.schedules.length === 0 ? null : (
378
413
  <ul className="space-y-2">
379
414
  {definition.schedules.map((schedule, index) => (
380
415
  <li
package/src/i18n/en.json CHANGED
@@ -308,7 +308,6 @@
308
308
  "agent.contactAddReason": "A mail address belongs to the installation, not to the agent's definition, so it is configured where the runtime is deployed.",
309
309
  "agent.knows": "What it knows",
310
310
  "agent.knowsHint": "The folders this agent reads, and what each one counts as. A system message instructs it, semantic context is searched, memory is written back to.",
311
- "agent.knowsEmpty": "This agent reads nothing yet.",
312
311
  "agent.addReference": "Add folder",
313
312
  "agent.removeReferenceOf": "Stop reading {title}",
314
313
  "agent.pickFolder": "Folder to read",
@@ -114,6 +114,7 @@ export function Nodes() {
114
114
  description={selected.description}
115
115
  target={{ type: "node", node: selected }}
116
116
  leading={selected.kind === "agent" ? <AgentAvatar title={selected.title} /> : null}
117
+ divider={selected.kind !== "agent"}
117
118
  >
118
119
  {selected.kind !== "folder" && (
119
120
  <TooltipProvider delayDuration={300}>
@@ -21,6 +21,7 @@ export function TitleRow({
21
21
  description,
22
22
  target,
23
23
  leading,
24
+ divider = true,
24
25
  children,
25
26
  }: {
26
27
  title: string;
@@ -30,10 +31,15 @@ export function TitleRow({
30
31
  // prop rather than another slot because it must sit BEFORE the title, and a portal can only
31
32
  // append — the same limitation that gave the shell two boxes instead of one list (#56).
32
33
  leading?: React.ReactNode;
34
+ // Off for the one kind whose next line is its own tab bar: two rules a few pixels apart read as
35
+ // clutter, so the agent page keeps only the tab bar's (#204).
36
+ divider?: boolean;
33
37
  children?: React.ReactNode;
34
38
  }) {
35
39
  return (
36
- <div className="flex items-start justify-between gap-5 border-b px-6 py-4">
40
+ <div
41
+ className={`flex items-start justify-between gap-5 px-6 py-4${divider ? " border-b" : ""}`}
42
+ >
37
43
  <div className="flex min-w-0 items-center gap-3">
38
44
  {leading}
39
45
  <div className="min-w-0">