Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions apps/web/src/bench/information-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ import { ApprovalRow } from "@/chat/approval-row";
import { IdentityAvatar } from "@/chat/avatar";
import type { WorkbenchParticipant } from "@/chat/threads-api";
import { libraryArtifactPath } from "@/library";
import { FileText, FlowArrow } from "@/lib/icons";
import { FileText, FlowArrow, Lightning } from "@/lib/icons";
import { Link } from "@/navigation";
import { ARTIFACTS_PATH_PREFIX, WORKFLOWS_PATH_PREFIX } from "@/path-ids";
import { ARTIFACTS_PATH_PREFIX, SKILLS_PATH_PREFIX, WORKFLOWS_PATH_PREFIX } from "@/path-ids";
import { tenantKeys } from "@/query-client";
import { routineDetailPath } from "@/global-routines";
import { scheduleSentence } from "@/pages/routines-page";
import { listScheduledWorkflows } from "@/routines-api";
import { skillDisplayName } from "@/skill-display-name";
import { listSkills } from "@/skills-api";
import { WorkbenchSchedulesPanel } from "../pages/workbench-schedules-panel";
import { usePendingApprovals } from "../pending-approvals";
import { benchLink } from "../shell/page-crumbs";
Expand Down Expand Up @@ -149,6 +151,12 @@ export function InformationTab({
.slice(0, 2)
: [];

const skills = useQuery({
queryKey: tenantKeys.skills(workbenchTenantId),
queryFn: () => listSkills(workbenchTenantId),
});
const shownSkills = (skills.data ?? []).slice(0, 2);

return (
<>
<AboutSection workbenchTenantId={workbenchTenantId} />
Expand Down Expand Up @@ -238,6 +246,30 @@ export function InformationTab({
))}
</div>
</Section>

<Section
title="Skills"
action={{ to: benchLink(SKILLS_PATH_PREFIX, workbenchTenantId), label: "View all" }}
>
{skills.isLoading ? <Skeleton className="h-10 w-full" /> : null}
{skills.isSuccess && shownSkills.length === 0 ? (
<p className="workbench-info-empty-note">No skills here yet.</p>
) : null}
<div className="drawer-list">
{shownSkills.map((skill) => (
<Link
key={skill.assetId}
to={`${SKILLS_PATH_PREFIX}/${encodeURIComponent(skill.name)}`}
className="drawer-li"
>
<Lightning size={16} aria-hidden="true" />
<span className="drawer-li-t">
<b>{skillDisplayName(skill)}</b>
</span>
</Link>
))}
</div>
</Section>
</>
);
}
1 change: 1 addition & 0 deletions apps/web/src/library/artifact-summary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const ArtifactSummary = type({
kind: "string",
createdAt: "string",
"updatedAt?": "string | null",
"from?": "string | null",
});

export type ArtifactSummary = typeof ArtifactSummary.infer;
2 changes: 1 addition & 1 deletion apps/web/src/library/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export {
libraryArtifactIdFromPath,
} from "./kind-filter";
export type { LibraryKindSegment } from "./kind-filter";
export { workflowRunIdFromSource } from "./provenance";
export { artifactFromLabel, workflowRunIdFromSource } from "./provenance";
export {
ARTIFACT_RENDERER_KINDS,
isTextDecodableMediaType,
Expand Down
10 changes: 10 additions & 0 deletions apps/web/src/library/provenance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,13 @@ export function workflowRunIdFromSource(
const runId = source.runId;
return typeof runId === "string" && runId !== "" ? runId : null;
}

/** Where an artifact came from, for the list's From column. Null when the
* `source` names no origin. */
export function artifactFromLabel(
source: Record<string, unknown> | null | undefined,
): string | null {
if (source === null || source === undefined) return null;
if (source.origin === "workflow") return "Workflow run";
return typeof source.origin === "string" && source.origin !== "" ? source.origin : null;
}
100 changes: 100 additions & 0 deletions apps/web/src/pages/library-list.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/* The raised list card and filter the Workflows, Artifacts and Skills
pages share (mockups: workflows.html, artifacts.html, skills.html). */
.lib-filter {
display: block;
height: 34px;
width: 240px;
max-width: 100%;
margin-bottom: 16px;
padding: 0 12px;
border: 0;
border-radius: var(--r-md);
background: var(--surface);
box-shadow: inset 0 0 0 1px var(--line-strong);
color: var(--ink);
font: inherit;
font-size: 13.5px;
}
.lib-filter:focus {
outline: none;
box-shadow:
inset 0 0 0 1px var(--ink-3),
0 0 0 3px var(--hover);
}

.lib-list {
margin: 0;
padding: 0;
list-style: none;
background: var(--surface);
border-radius: var(--r-lg);
box-shadow: var(--raised);
overflow: hidden;
font-size: 13.5px;
}

.lib-head,
.lib-row {
display: grid;
grid-template-columns: var(--lib-cols);
align-items: center;
gap: 16px;
padding: 12px 16px;
border-bottom: 1px solid var(--line);
}
.lib-head {
padding-block: 10px;
background: var(--surface-sunk);
color: var(--ink-3);
font-size: 12px;
font-weight: 700;
}
.lib-row:last-child {
border-bottom: 0;
}
.lib-row[data-link] {
cursor: pointer;
}
.lib-row[data-link]:hover,
.lib-row[data-state="selected"] {
background: var(--surface-sunk);
}

.lib-cell {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-variant-numeric: tabular-nums;
}
.lib-cell--soft {
color: var(--ink-2);
}
.lib-cell--end {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 8px;
}
.lib-name {
font-weight: 700;
color: var(--ink);
text-decoration: none;
}
.lib-name--mono {
font-family: var(--font-mono);
font-size: 13px;
}

@media (max-width: 720px) {
.lib-head {
display: none;
}
.lib-row {
grid-template-columns: minmax(0, 1fr);
gap: 4px;
}
.lib-cell--end {
justify-content: flex-start;
}
}
48 changes: 48 additions & 0 deletions apps/web/src/pages/library-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import type { CSSProperties, ReactNode } from "react";

import "./library-list.css";

export function ListFilter({
label,
value,
onChange,
}: {
readonly label: string;
readonly value: string;
readonly onChange: (value: string) => void;
}) {
return (
<input
className="lib-filter"
placeholder={label}
aria-label={label}
value={value}
onChange={(event) => onChange(event.target.value)}
/>
);
}

/** The raised card: a sunk header row, then the rows. `columns` is the
* grid-template-columns every row shares. */
export function ListCard({
label,
columns,
heads,
children,
}: {
readonly label: string;
readonly columns: string;
readonly heads: readonly string[];
readonly children: ReactNode;
}) {
return (
<ul className="lib-list" aria-label={label} style={{ "--lib-cols": columns } as CSSProperties}>
<li className="lib-head" aria-hidden="true">
{heads.map((head, index) => (
<span key={`${head}-${String(index)}`}>{head}</span>
))}
</li>
{children}
</ul>
);
}
16 changes: 0 additions & 16 deletions apps/web/src/pages/library-page.css

This file was deleted.

Loading
Loading