@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 +26 -0
- package/dist/{chunk-HSMVDKL3.js → chunk-EJ5NBW7V.js} +275 -0
- package/dist/chunk-HGML45QH.js +104 -0
- package/dist/index.d.ts +3 -43
- package/dist/index.js +16 -98
- package/dist/primitives.d.ts +171 -1
- package/dist/primitives.js +13 -1
- package/dist/redaction.d.ts +42 -0
- package/dist/redaction.js +7 -0
- package/package.json +7 -2
- package/src/primitives/index.ts +12 -0
- package/src/primitives/metric-strip.test.tsx +181 -0
- package/src/primitives/metric-strip.tsx +122 -0
- package/src/primitives/page-header.test.tsx +57 -0
- package/src/primitives/page-header.tsx +86 -0
- package/src/primitives/status-pill.test.tsx +104 -0
- package/src/primitives/status-pill.tsx +193 -0
- package/src/primitives/toolbar.test.tsx +89 -0
- package/src/primitives/toolbar.tsx +105 -0
- package/src/redaction/index.ts +12 -0
- package/src/run/run-row-shell.tsx +4 -1
- /package/src/primitives/{faint-text.test.tsx → faint-text-tokens.test.tsx} +0 -0
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
|
-
|
|
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-
|
|
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,
|