@tangle-network/ui 11.2.4 → 11.4.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # @tangle-network/ui
2
2
 
3
+ ## 11.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - ef1be75: Publish the redaction renderer on its own `./redaction` subpath.
8
+
9
+ `RedactedDocument` is the client half of agent-app's reversible redaction: agent-app builds a document of masked spans and reveals one at a time through `revealSpan`, where authorization and the audit record happen, and this component renders that document and calls back for a reveal. It reached the package only through the root entry, and no consumer imports the bare package — every app takes ui through a subpath — so the component was unreachable in practice.
10
+
11
+ The module gains a header stating what it pairs with, including why its segment type omits the `cipher` that agent-app's carries: the ciphertext stays on the server, so the type a browser holds cannot name it.
12
+
13
+ - b7c12be: `RunRowShell`'s `title` prop widens from `string` to `ReactNode` — purely additive (strings render unchanged). Lets a row carry a treated title (e.g. an active-state shimmer sweep) so consumers composing the canonical run-row grammar don't have to re-fork the shell for it.
14
+
15
+ ## 11.3.0
16
+
17
+ ### Minor Changes
18
+
19
+ - e67a809: Raise the dark surface ladder to AA and add the page-level primitives.
20
+
21
+ In the dark spine, `--md3-surface-container-high` and `--md3-surface-container-highest` sat close enough to the ink ramp that `--text-dim` fell under 4.5:1 on them. The ladder is re-spaced and the ink ramp moves with it, so every ink tier clears AA on all five planes.
22
+
23
+ Dark status chips keep their hue, drop 14% chroma, and lift the fill to 1.50:1 from the card. A new `--run-mix-*` ramp carries proportional bars, spaced in relative luminance so adjacent segments clear the 3:1 floor for non-text contrast.
24
+
25
+ One light token moves: `--surface-warning-text` goes `#b45309` to `#ab4f09`. It is a contrast fix, not a hue change. The old value measured 4.16:1 on the light page canvas, under the 4.5:1 body floor, whenever the colour was used as text away from its own pill background; the new value measures 4.51:1 there and 5.25:1 on the pill. Every other light value is unchanged.
26
+
27
+ `@tangle-network/ui` gains four primitives: `PageHeader`, `StatusPill`, `MetricStrip`, and `Toolbar`/`FilterField`. All additive; no existing export changes.
28
+
3
29
  ## 11.2.4
4
30
 
5
31
  ### Patch Changes
@@ -1261,6 +1261,275 @@ function ThemeToggle() {
1261
1261
  );
1262
1262
  }
1263
1263
 
1264
+ // src/primitives/page-header.tsx
1265
+ import * as React12 from "react";
1266
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
1267
+ var PageHeader = React12.forwardRef(
1268
+ ({
1269
+ className,
1270
+ title,
1271
+ description,
1272
+ actions,
1273
+ meta,
1274
+ titleId,
1275
+ level = 1,
1276
+ ...props
1277
+ }, ref) => {
1278
+ const Heading = level === 1 ? "h1" : "h2";
1279
+ return /* @__PURE__ */ jsxs15("header", { ref, className: cn("mb-6", className), ...props, children: [
1280
+ /* @__PURE__ */ jsxs15("div", { className: "flex flex-wrap items-start justify-between gap-x-6 gap-y-3", children: [
1281
+ /* @__PURE__ */ jsxs15("div", { className: "min-w-0", children: [
1282
+ /* @__PURE__ */ jsx17(
1283
+ Heading,
1284
+ {
1285
+ id: titleId,
1286
+ className: cn(
1287
+ "text-balance font-semibold tracking-tight",
1288
+ level === 1 ? "text-2xl" : "text-lg"
1289
+ ),
1290
+ children: title
1291
+ }
1292
+ ),
1293
+ description && /* @__PURE__ */ jsx17("p", { className: "mt-1 max-w-prose text-muted-foreground text-sm", children: description })
1294
+ ] }),
1295
+ actions && /* @__PURE__ */ jsx17("div", { className: "flex flex-wrap items-center gap-2", children: actions })
1296
+ ] }),
1297
+ meta && /* @__PURE__ */ jsx17("div", { className: "mt-3 flex flex-wrap items-center gap-x-4 gap-y-2 text-[var(--text-dim)] text-xs", children: meta })
1298
+ ] });
1299
+ }
1300
+ );
1301
+ PageHeader.displayName = "PageHeader";
1302
+
1303
+ // src/primitives/status-pill.tsx
1304
+ import * as React13 from "react";
1305
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
1306
+ var TONE_SURFACE = {
1307
+ success: "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)] border-[var(--surface-success-border)]",
1308
+ warning: "bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)] border-[var(--surface-warning-border)]",
1309
+ danger: "bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)] border-[var(--surface-danger-border)]",
1310
+ info: "bg-[var(--surface-info-bg)] text-[var(--surface-info-text)] border-[var(--surface-info-border)]",
1311
+ // `running` shares the info triple on purpose. `--status-running` exists but
1312
+ // is the green dot on a sandbox — a machine's power state, not a run's
1313
+ // outcome — and it ships as one colour with no paired background or text
1314
+ // tier, which is the pairing this component is built to guarantee. Green also
1315
+ // already means SUCCEEDED here, so a green "running" pill would say a run had
1316
+ // finished well while it was still going.
1317
+ running: "bg-[var(--surface-info-bg)] text-[var(--surface-info-text)] border-[var(--surface-info-border)]",
1318
+ neutral: "bg-[var(--surface-neutral-bg)] text-[var(--surface-neutral-text)] border-[var(--surface-neutral-border)]"
1319
+ };
1320
+ var TONE_TEXT = {
1321
+ success: "text-[var(--surface-success-text)]",
1322
+ warning: "text-[var(--surface-warning-text)]",
1323
+ danger: "text-[var(--surface-danger-text)]",
1324
+ info: "text-[var(--surface-info-text)]",
1325
+ running: "text-[var(--surface-info-text)]",
1326
+ neutral: "text-[var(--surface-neutral-text)]"
1327
+ };
1328
+ function ToneGlyph({ tone }) {
1329
+ const common = {
1330
+ viewBox: "0 0 8 8",
1331
+ className: "size-2 shrink-0"
1332
+ };
1333
+ switch (tone) {
1334
+ // Settled and good: a solid disc.
1335
+ case "success":
1336
+ return /* @__PURE__ */ jsx18("svg", { ...common, "aria-hidden": "true", fill: "currentColor", children: /* @__PURE__ */ jsx18("circle", { cx: "4", cy: "4", r: "3.25" }) });
1337
+ // Settled and bad: a disc with a bar through it, so it differs from
1338
+ // success in OUTLINE and not only in hue.
1339
+ case "danger":
1340
+ return /* @__PURE__ */ jsxs16(
1341
+ "svg",
1342
+ {
1343
+ ...common,
1344
+ "aria-hidden": "true",
1345
+ fill: "none",
1346
+ stroke: "currentColor",
1347
+ strokeWidth: "1.5",
1348
+ children: [
1349
+ /* @__PURE__ */ jsx18("circle", { cx: "4", cy: "4", r: "3.1", fill: "currentColor", opacity: "0.35" }),
1350
+ /* @__PURE__ */ jsx18("path", { d: "M1.9 6.1 6.1 1.9", strokeLinecap: "round" })
1351
+ ]
1352
+ }
1353
+ );
1354
+ // Needs a person: a triangle.
1355
+ case "warning":
1356
+ return /* @__PURE__ */ jsx18("svg", { ...common, "aria-hidden": "true", fill: "currentColor", children: /* @__PURE__ */ jsx18("path", { d: "M4 0.6 7.7 7.1H0.3z" }) });
1357
+ // Still moving: an open ring, visibly hollow at 8px.
1358
+ case "running":
1359
+ return /* @__PURE__ */ jsx18(
1360
+ "svg",
1361
+ {
1362
+ ...common,
1363
+ "aria-hidden": "true",
1364
+ fill: "none",
1365
+ stroke: "currentColor",
1366
+ strokeWidth: "1.75",
1367
+ children: /* @__PURE__ */ jsx18("circle", { cx: "4", cy: "4", r: "2.9" })
1368
+ }
1369
+ );
1370
+ // Informational: a square, the only right-angled mark in the set.
1371
+ case "info":
1372
+ return /* @__PURE__ */ jsx18("svg", { ...common, "aria-hidden": "true", fill: "currentColor", children: /* @__PURE__ */ jsx18("rect", { x: "0.9", y: "0.9", width: "6.2", height: "6.2", rx: "1.2" }) });
1373
+ // Nothing has happened: a dash. No enclosed area at all.
1374
+ default:
1375
+ return /* @__PURE__ */ jsx18(
1376
+ "svg",
1377
+ {
1378
+ ...common,
1379
+ "aria-hidden": "true",
1380
+ fill: "none",
1381
+ stroke: "currentColor",
1382
+ strokeWidth: "1.75",
1383
+ children: /* @__PURE__ */ jsx18("path", { d: "M1 4h6", strokeLinecap: "round" })
1384
+ }
1385
+ );
1386
+ }
1387
+ }
1388
+ var StatusPill = React13.forwardRef(
1389
+ ({ className, tone, children, bare = false, size = "sm", ...props }, ref) => /* @__PURE__ */ jsxs16(
1390
+ "span",
1391
+ {
1392
+ ref,
1393
+ className: cn(
1394
+ "inline-flex items-center gap-1.5 whitespace-nowrap rounded-full font-medium",
1395
+ size === "sm" ? "px-2 py-0.5 text-xs" : "px-2.5 py-1 text-sm",
1396
+ bare ? "border border-transparent bg-transparent px-0" : cn("border", TONE_SURFACE[tone]),
1397
+ className
1398
+ ),
1399
+ ...props,
1400
+ children: [
1401
+ /* @__PURE__ */ jsx18("span", { className: cn("inline-flex", bare && TONE_TEXT[tone]), children: /* @__PURE__ */ jsx18(ToneGlyph, { tone }) }),
1402
+ children
1403
+ ]
1404
+ }
1405
+ )
1406
+ );
1407
+ StatusPill.displayName = "StatusPill";
1408
+
1409
+ // src/primitives/metric-strip.tsx
1410
+ import * as React14 from "react";
1411
+ import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
1412
+ var MetricStrip = React14.forwardRef(
1413
+ ({ className, children, ...props }, ref) => /* @__PURE__ */ jsx19(
1414
+ "dl",
1415
+ {
1416
+ ref,
1417
+ className: cn(
1418
+ "grid grid-cols-2 rounded-[var(--radius-lg)] border border-border bg-card shadow-[var(--shadow-card)] sm:grid-cols-4",
1419
+ className
1420
+ ),
1421
+ ...props,
1422
+ children
1423
+ }
1424
+ )
1425
+ );
1426
+ MetricStrip.displayName = "MetricStrip";
1427
+ var Metric = React14.forwardRef(
1428
+ ({ className, label, value, hint, attention, ...props }, ref) => /* @__PURE__ */ jsxs17(
1429
+ "div",
1430
+ {
1431
+ ref,
1432
+ className: cn(
1433
+ // Leading hairline, DRAWN only where a divider belongs — never drawn
1434
+ // and then suppressed. Stating it as `border-l` plus `border-l-0`
1435
+ // variants puts both in one conflict group, where the winner is decided
1436
+ // by emitted-rule order rather than by intent; that resolved the wrong
1437
+ // way at every breakpoint and left a rule down the start of each row.
1438
+ // `:not(:nth-child(Nn+1))` is the row-start test for the column count
1439
+ // at that breakpoint, so no rule ever contradicts another.
1440
+ //
1441
+ // N is COUPLED to the grid template on `MetricStrip`: 2 columns below
1442
+ // `sm`, 4 from `sm`. A new breakpoint there — `md:grid-cols-3`, say —
1443
+ // needs its `md:[&:not(:nth-child(3n+1))]:border-l` here in the same
1444
+ // change, or the dividers land mid-row at that width.
1445
+ "border-border p-4 sm:p-5",
1446
+ "max-sm:[&:not(:nth-child(2n+1))]:border-l",
1447
+ "sm:[&:not(:nth-child(4n+1))]:border-l",
1448
+ className
1449
+ ),
1450
+ ...props,
1451
+ children: [
1452
+ /* @__PURE__ */ jsxs17("dt", { className: "flex items-center gap-2 text-muted-foreground text-sm", children: [
1453
+ /* @__PURE__ */ jsx19("span", { className: "truncate", children: label }),
1454
+ attention && /* @__PURE__ */ jsx19(StatusPill, { tone: attention.tone, children: attention.label })
1455
+ ] }),
1456
+ /* @__PURE__ */ jsx19(
1457
+ "dd",
1458
+ {
1459
+ className: cn(
1460
+ "mt-1 truncate font-semibold text-xl tabular-nums tracking-tight lg:text-2xl",
1461
+ attention?.tone === "danger" && "text-[var(--surface-danger-text)]"
1462
+ ),
1463
+ title: typeof value === "string" ? value : void 0,
1464
+ children: value
1465
+ }
1466
+ ),
1467
+ hint && // Titled for the same reason the value is: the line truncates, and a
1468
+ // hint is where the qualifying detail lives — which wallet, which
1469
+ // window — so a clipped one is the case most worth recovering.
1470
+ /* @__PURE__ */ jsx19(
1471
+ "dd",
1472
+ {
1473
+ className: "mt-0.5 truncate text-[var(--text-dim)] text-xs",
1474
+ title: typeof hint === "string" ? hint : void 0,
1475
+ children: hint
1476
+ }
1477
+ )
1478
+ ]
1479
+ }
1480
+ )
1481
+ );
1482
+ Metric.displayName = "Metric";
1483
+
1484
+ // src/primitives/toolbar.tsx
1485
+ import * as React15 from "react";
1486
+ import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
1487
+ var Toolbar = React15.forwardRef(
1488
+ ({ className, search, filters, actions, ...props }, ref) => /* @__PURE__ */ jsxs18(
1489
+ "div",
1490
+ {
1491
+ ref,
1492
+ className: cn(
1493
+ "mb-4 flex flex-col gap-3 lg:flex-row lg:items-center",
1494
+ className
1495
+ ),
1496
+ ...props,
1497
+ children: [
1498
+ search && /* @__PURE__ */ jsx20("div", { className: "min-w-0 lg:max-w-sm lg:flex-1", children: search }),
1499
+ filters && // `py-1` leaves room for a focus ring above and below; horizontally the
1500
+ // scroll container clips it, which is the trade a single-line filter row
1501
+ // makes. No negative margin — pulling the row wider than its parent is
1502
+ // what made every page report 4px of horizontal overflow.
1503
+ /* @__PURE__ */ jsx20("div", { className: "flex min-w-0 items-center gap-2 overflow-x-auto py-1 lg:flex-1", children: filters }),
1504
+ actions && /* @__PURE__ */ jsx20("div", { className: "flex shrink-0 items-center gap-2 lg:ml-auto", children: actions })
1505
+ ]
1506
+ }
1507
+ )
1508
+ );
1509
+ Toolbar.displayName = "Toolbar";
1510
+ var FilterField = React15.forwardRef(
1511
+ ({ className, label, htmlFor, children, ...props }, ref) => /* @__PURE__ */ jsxs18(
1512
+ "div",
1513
+ {
1514
+ ref,
1515
+ className: cn("flex shrink-0 items-center gap-2", className),
1516
+ ...props,
1517
+ children: [
1518
+ /* @__PURE__ */ jsx20(
1519
+ "label",
1520
+ {
1521
+ htmlFor,
1522
+ className: "whitespace-nowrap text-muted-foreground text-xs",
1523
+ children: label
1524
+ }
1525
+ ),
1526
+ children
1527
+ ]
1528
+ }
1529
+ )
1530
+ );
1531
+ FilterField.displayName = "FilterField";
1532
+
1264
1533
  export {
1265
1534
  Dialog,
1266
1535
  DialogTrigger,
@@ -1305,6 +1574,12 @@ export {
1305
1574
  SidebarDropZone,
1306
1575
  useTheme,
1307
1576
  ThemeToggle,
1577
+ PageHeader,
1578
+ StatusPill,
1579
+ MetricStrip,
1580
+ Metric,
1581
+ Toolbar,
1582
+ FilterField,
1308
1583
  Logo,
1309
1584
  TangleKnot
1310
1585
  };
@@ -0,0 +1,104 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-RQHJBTEU.js";
4
+
5
+ // src/redaction/redacted-document.tsx
6
+ import { useCallback, useState } from "react";
7
+ import { Eye, EyeOff, Loader2, ShieldAlert } from "lucide-react";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ var defaultLabel = (kind) => kind.replace(/[-_]/g, " ").toUpperCase();
10
+ function RedactedChip({
11
+ kind,
12
+ label,
13
+ onReveal
14
+ }) {
15
+ const [state, setState] = useState({ status: "masked" });
16
+ const reveal = useCallback(async () => {
17
+ setState({ status: "loading" });
18
+ try {
19
+ const r = await onReveal();
20
+ setState(
21
+ r.ok && r.value !== void 0 ? { status: "revealed", value: r.value } : { status: "denied", reason: r.reason }
22
+ );
23
+ } catch {
24
+ setState({ status: "denied", reason: "error" });
25
+ }
26
+ }, [onReveal]);
27
+ if (state.status === "revealed") {
28
+ return /* @__PURE__ */ jsxs(
29
+ "button",
30
+ {
31
+ type: "button",
32
+ onClick: () => setState({ status: "masked" }),
33
+ title: "Revealed \u2014 click to hide",
34
+ "aria-label": `${label}: revealed, click to hide`,
35
+ className: cn(
36
+ "inline-flex items-center gap-1 rounded-[var(--radius-sm)] px-1 font-medium",
37
+ "bg-[color-mix(in_oklch,var(--color-warning,orange)_18%,transparent)] text-foreground ring-1 ring-warning/40"
38
+ ),
39
+ children: [
40
+ state.value,
41
+ /* @__PURE__ */ jsx(EyeOff, { className: "size-3 opacity-60" })
42
+ ]
43
+ }
44
+ );
45
+ }
46
+ if (state.status === "denied") {
47
+ return /* @__PURE__ */ jsxs(
48
+ "span",
49
+ {
50
+ title: `Restricted${state.reason ? ` (${state.reason})` : ""}`,
51
+ "aria-label": `${label}: restricted`,
52
+ className: "inline-flex items-center gap-1 rounded-[var(--radius-sm)] bg-muted px-1 text-muted-foreground",
53
+ children: [
54
+ /* @__PURE__ */ jsx(ShieldAlert, { className: "size-3" }),
55
+ " ",
56
+ label
57
+ ]
58
+ }
59
+ );
60
+ }
61
+ return /* @__PURE__ */ jsxs(
62
+ "button",
63
+ {
64
+ type: "button",
65
+ disabled: state.status === "loading",
66
+ onClick: reveal,
67
+ title: `${label} \u2014 click to reveal`,
68
+ "aria-label": `${label} redacted, click to reveal`,
69
+ className: cn(
70
+ "inline-flex items-center gap-1 rounded-[var(--radius-sm)] px-1 font-medium tracking-wide",
71
+ "bg-muted text-muted-foreground hover:bg-muted/80 hover:text-foreground transition-colors",
72
+ "cursor-pointer select-none"
73
+ ),
74
+ children: [
75
+ state.status === "loading" ? /* @__PURE__ */ jsx(Loader2, { className: "size-3 animate-spin" }) : /* @__PURE__ */ jsx(Eye, { className: "size-3 opacity-60" }),
76
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "\u2588\u2588\u2588" }),
77
+ " ",
78
+ label
79
+ ]
80
+ }
81
+ );
82
+ }
83
+ function RedactedDocument({
84
+ document,
85
+ onReveal,
86
+ labelForKind = defaultLabel,
87
+ className
88
+ }) {
89
+ return /* @__PURE__ */ jsx("div", { className: cn("whitespace-pre-wrap break-words leading-relaxed", className), children: document.segments.map(
90
+ (seg, i) => seg.type === "text" ? /* @__PURE__ */ jsx("span", { children: seg.text }, i) : /* @__PURE__ */ jsx(
91
+ RedactedChip,
92
+ {
93
+ kind: seg.kind,
94
+ label: labelForKind(seg.kind),
95
+ onReveal: () => onReveal(seg.id)
96
+ },
97
+ seg.id
98
+ )
99
+ ) });
100
+ }
101
+
102
+ export {
103
+ RedactedDocument
104
+ };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { B as Button, a as ButtonProps, b as buttonVariants } from './button-CMQuQEW_.js';
2
- export { Avatar, AvatarFallback, AvatarImage, Badge, BadgeProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropZone, DropZoneProps, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, EmptyStateProps, InlineCode, InlineCodeProps, Input, InputProps, Label, Progress, SegmentedControl, SegmentedControlOption, SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SidebarDropZone, SidebarDropZoneProps, Skeleton, SkeletonCard, SkeletonTable, StatCard, StatCardProps, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, Textarea, TextareaProps, ThemeToggle, Toast, ToastContainer, ToastProvider, UploadFile, UploadProgress, UploadProgressProps, badgeVariants, useTheme, useToast } from './primitives.js';
2
+ export { Avatar, AvatarFallback, AvatarImage, Badge, BadgeProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropZone, DropZoneProps, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, EmptyStateProps, FilterField, FilterFieldProps, InlineCode, InlineCodeProps, Input, InputProps, Label, Metric, MetricProps, MetricStrip, MetricStripProps, PageHeader, PageHeaderProps, Progress, SegmentedControl, SegmentedControlOption, SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SidebarDropZone, SidebarDropZoneProps, Skeleton, SkeletonCard, SkeletonTable, StatCard, StatCardProps, StatusPill, StatusPillProps, StatusTone, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, Textarea, TextareaProps, ThemeToggle, Toast, ToastContainer, ToastProvider, Toolbar, ToolbarProps, UploadFile, UploadProgress, UploadProgressProps, badgeVariants, useTheme, useToast } from './primitives.js';
3
3
  export { Logo, LogoProps, TangleKnot } from '@tangle-network/brand';
4
4
  export { A as ArtifactPane, a as ArtifactPaneProps } from './artifact-pane-DpuWcAiF.js';
5
5
  export { AgentTimeline, AgentTimelineArtifactItem, AgentTimelineCustomItem, AgentTimelineItem, AgentTimelineMessageItem, AgentTimelineProps, AgentTimelineStatusItem, AgentTimelineTone, AgentTimelineToolGroupItem, AgentTimelineToolItem, ChatContainer, ChatContainerProps, ChatMessage, ChatMessageProps, MessageList, MessageListProps, MessageRole, ThinkingIndicator, ThinkingIndicatorProps, UserMessage, UserMessageProps } from './chat.js';
@@ -21,8 +21,9 @@ export { F as FinalTextPart, G as GroupedMessage, M as MessageRun, a as MessageU
21
21
  export { C as CustomToolRenderer, D as DisplayVariant, T as ToolDisplayMetadata } from './tool-display-z4JcDmMQ.js';
22
22
  export { TOOL_CATEGORY_ICONS, cn, copyText, formatBytes, formatDuration, formatUptime, getToolCategory, getToolDisplayMetadata, getToolErrorText, timeAgo, truncateText } from './utils.js';
23
23
  export { CommandPreview, CommandPreviewProps, DiffPreview, DiffPreviewProps, GlobResultsPreview, GlobResultsPreviewProps, GrepResultsPreview, GrepResultsPreviewProps, QuestionPreview, QuestionPreviewProps, WebSearchPreview, WebSearchPreviewProps, WriteFilePreview, WriteFilePreviewProps } from './tool-previews.js';
24
- import { ReactNode } from 'react';
24
+ export { RedactedDocSegment, RedactedDocument, RedactedDocumentData, RedactedDocumentProps, RevealResult } from './redaction.js';
25
25
  import 'class-variance-authority/types';
26
+ import 'react';
26
27
  import 'react/jsx-runtime';
27
28
  import '@radix-ui/react-dialog';
28
29
  import 'class-variance-authority';
@@ -39,44 +40,3 @@ import '@hocuspocus/provider';
39
40
  import 'yjs';
40
41
  import 'nanostores';
41
42
  import 'clsx';
42
-
43
- /**
44
- * Viewer for a server-produced redacted document. Renders text inline and each
45
- * redacted span as a masked chip; clicking a chip asks the server to reveal that
46
- * one span. The original plaintext is NEVER in the document the client holds —
47
- * the chip carries only an id + kind; `onReveal` round-trips to the server, where
48
- * `@tangle-network/agent-app/redact`'s `revealSpan` runs the authorization check
49
- * and writes the audit trail. So authz + audit are server-truth; this is display.
50
- *
51
- * Structural types (no `@tangle-network/agent-app` dependency) — the viewer needs
52
- * only `{ id, kind }` per span; the cipher stays server-side.
53
- */
54
- type RedactedDocSegment = {
55
- type: "text";
56
- text: string;
57
- } | {
58
- type: "redacted";
59
- id: string;
60
- kind: string;
61
- };
62
- interface RedactedDocumentData {
63
- segments: RedactedDocSegment[];
64
- }
65
- interface RevealResult {
66
- ok: boolean;
67
- value?: string;
68
- /** e.g. `forbidden` | `not_found` when `ok` is false. */
69
- reason?: string;
70
- }
71
- interface RedactedDocumentProps {
72
- document: RedactedDocumentData;
73
- /** Reveal one span by id. Wire to a server route that calls agent-app's
74
- * `revealSpan` (authz + audit happen there). Resolves with the original. */
75
- onReveal: (spanId: string) => Promise<RevealResult>;
76
- /** Display label for a redaction kind (default: the kind, upper-cased). */
77
- labelForKind?: (kind: string) => string;
78
- className?: string;
79
- }
80
- declare function RedactedDocument({ document, onReveal, labelForKind, className, }: RedactedDocumentProps): ReactNode;
81
-
82
- export { type RedactedDocSegment, RedactedDocument, type RedactedDocumentData, type RedactedDocumentProps, type RevealResult };
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ import {
2
+ RedactedDocument
3
+ } from "./chunk-HGML45QH.js";
1
4
  import {
2
5
  AuthHeader,
3
6
  GitHubLoginButton,
@@ -76,9 +79,13 @@ import {
76
79
  DialogTrigger,
77
80
  DropZone,
78
81
  EmptyState,
82
+ FilterField,
79
83
  Input,
80
84
  Label,
81
85
  Logo,
86
+ Metric,
87
+ MetricStrip,
88
+ PageHeader,
82
89
  Progress,
83
90
  SegmentedControl,
84
91
  Select,
@@ -96,6 +103,7 @@ import {
96
103
  SkeletonCard,
97
104
  SkeletonTable,
98
105
  StatCard,
106
+ StatusPill,
99
107
  Switch,
100
108
  TangleKnot,
101
109
  TerminalCursor,
@@ -106,10 +114,11 @@ import {
106
114
  ThemeToggle,
107
115
  ToastContainer,
108
116
  ToastProvider,
117
+ Toolbar,
109
118
  UploadProgress,
110
119
  useTheme,
111
120
  useToast
112
- } from "./chunk-HSMVDKL3.js";
121
+ } from "./chunk-EJ5NBW7V.js";
113
122
  import {
114
123
  Avatar,
115
124
  AvatarFallback,
@@ -238,103 +247,6 @@ import {
238
247
  import {
239
248
  cn
240
249
  } from "./chunk-RQHJBTEU.js";
241
-
242
- // src/redaction/redacted-document.tsx
243
- import { useCallback, useState } from "react";
244
- import { Eye, EyeOff, Loader2, ShieldAlert } from "lucide-react";
245
- import { jsx, jsxs } from "react/jsx-runtime";
246
- var defaultLabel = (kind) => kind.replace(/[-_]/g, " ").toUpperCase();
247
- function RedactedChip({
248
- kind,
249
- label,
250
- onReveal
251
- }) {
252
- const [state, setState] = useState({ status: "masked" });
253
- const reveal = useCallback(async () => {
254
- setState({ status: "loading" });
255
- try {
256
- const r = await onReveal();
257
- setState(
258
- r.ok && r.value !== void 0 ? { status: "revealed", value: r.value } : { status: "denied", reason: r.reason }
259
- );
260
- } catch {
261
- setState({ status: "denied", reason: "error" });
262
- }
263
- }, [onReveal]);
264
- if (state.status === "revealed") {
265
- return /* @__PURE__ */ jsxs(
266
- "button",
267
- {
268
- type: "button",
269
- onClick: () => setState({ status: "masked" }),
270
- title: "Revealed \u2014 click to hide",
271
- "aria-label": `${label}: revealed, click to hide`,
272
- className: cn(
273
- "inline-flex items-center gap-1 rounded-[var(--radius-sm)] px-1 font-medium",
274
- "bg-[color-mix(in_oklch,var(--color-warning,orange)_18%,transparent)] text-foreground ring-1 ring-warning/40"
275
- ),
276
- children: [
277
- state.value,
278
- /* @__PURE__ */ jsx(EyeOff, { className: "size-3 opacity-60" })
279
- ]
280
- }
281
- );
282
- }
283
- if (state.status === "denied") {
284
- return /* @__PURE__ */ jsxs(
285
- "span",
286
- {
287
- title: `Restricted${state.reason ? ` (${state.reason})` : ""}`,
288
- "aria-label": `${label}: restricted`,
289
- className: "inline-flex items-center gap-1 rounded-[var(--radius-sm)] bg-muted px-1 text-muted-foreground",
290
- children: [
291
- /* @__PURE__ */ jsx(ShieldAlert, { className: "size-3" }),
292
- " ",
293
- label
294
- ]
295
- }
296
- );
297
- }
298
- return /* @__PURE__ */ jsxs(
299
- "button",
300
- {
301
- type: "button",
302
- disabled: state.status === "loading",
303
- onClick: reveal,
304
- title: `${label} \u2014 click to reveal`,
305
- "aria-label": `${label} redacted, click to reveal`,
306
- className: cn(
307
- "inline-flex items-center gap-1 rounded-[var(--radius-sm)] px-1 font-medium tracking-wide",
308
- "bg-muted text-muted-foreground hover:bg-muted/80 hover:text-foreground transition-colors",
309
- "cursor-pointer select-none"
310
- ),
311
- children: [
312
- state.status === "loading" ? /* @__PURE__ */ jsx(Loader2, { className: "size-3 animate-spin" }) : /* @__PURE__ */ jsx(Eye, { className: "size-3 opacity-60" }),
313
- /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "\u2588\u2588\u2588" }),
314
- " ",
315
- label
316
- ]
317
- }
318
- );
319
- }
320
- function RedactedDocument({
321
- document,
322
- onReveal,
323
- labelForKind = defaultLabel,
324
- className
325
- }) {
326
- return /* @__PURE__ */ jsx("div", { className: cn("whitespace-pre-wrap break-words leading-relaxed", className), children: document.segments.map(
327
- (seg, i) => seg.type === "text" ? /* @__PURE__ */ jsx("span", { children: seg.text }, i) : /* @__PURE__ */ jsx(
328
- RedactedChip,
329
- {
330
- kind: seg.kind,
331
- label: labelForKind(seg.kind),
332
- onReveal: () => onReveal(seg.id)
333
- },
334
- seg.id
335
- )
336
- ) });
337
- }
338
250
  export {
339
251
  AgentTimeline,
340
252
  ArtifactPane,
@@ -388,6 +300,7 @@ export {
388
300
  FilePreview,
389
301
  FileTabs,
390
302
  FileTree,
303
+ FilterField,
391
304
  GitHubLoginButton,
392
305
  GlobResultsPreview,
393
306
  GrepResultsPreview,
@@ -401,7 +314,10 @@ export {
401
314
  Logo,
402
315
  Markdown,
403
316
  MessageList,
317
+ Metric,
318
+ MetricStrip,
404
319
  OpenUIArtifactRenderer,
320
+ PageHeader,
405
321
  Progress,
406
322
  QuestionPreview,
407
323
  RealtimeSessionRegistry,
@@ -424,6 +340,7 @@ export {
424
340
  SkeletonCard,
425
341
  SkeletonTable,
426
342
  StatCard,
343
+ StatusPill,
427
344
  Switch,
428
345
  TOOL_CATEGORY_ICONS,
429
346
  Table,
@@ -449,6 +366,7 @@ export {
449
366
  ToastContainer,
450
367
  ToastProvider,
451
368
  ToolCallFeed,
369
+ Toolbar,
452
370
  UploadProgress,
453
371
  UserMenu,
454
372
  UserMessage,