Files
fusion/packages/dashboard/app/components/WorkflowIcon.tsx
gsxdsm 5f67c85fea FN-7285: add workflow icons and trim built-in labels
Adds workflow icon metadata while preserving readable workflow names without built-in suffixes.

- Store and validate compact plain-text workflow icons across core APIs, imports, exports, tools, and analytics.
- Render Fusion marks for built-in workflows and custom text icons beside existing workflow labels on board, switcher, detail, and editor surfaces.
- Update workflow editor creation/copy flows, docs, release notes, and tests for icon metadata and shorter built-in names.

Files changed:
 .changeset/fn-7285-workflow-icons.md               |   7 ++
 docs/dashboard-guide.md                            |  12 +--
 .../cli/skill/fusion/references/extension-tools.md |   2 +
 .../core/src/__tests__/builtin-workflows.test.ts   |   8 +-
 packages/core/src/__tests__/db-migrate.test.ts     |   7 +-
 .../core/src/__tests__/workflow-analytics.test.ts  |  15 +--
 .../__tests__/workflow-definition-store.test.ts    |  31 ++++++
 packages/core/src/builtin-workflows.ts             |  20 ++--
 packages/core/src/db.ts                            |  11 +-
 packages/core/src/index.ts                         |   4 +
 packages/core/src/store.ts                         |  24 ++++-
 packages/core/src/workflow-analytics.ts            |   7 +-
 packages/core/src/workflow-definition-types.ts     |  31 ++++++
 packages/dashboard/app/api/legacy.ts               |   2 +
 packages/dashboard/app/components/Board.tsx        |  18 ++--
 packages/dashboard/app/components/Column.tsx       |   2 +-
 packages/dashboard/app/components/TaskCard.tsx     |   7 +-
 .../dashboard/app/components/TaskDetailModal.tsx   |  20 ++--
 packages/dashboard/app/components/WorkflowIcon.css |  42 ++++++++
 packages/dashboard/app/components/WorkflowIcon.tsx |  49 +++++++++
 .../app/components/WorkflowNodeEditor.css          |  32 ++++++
 .../app/components/WorkflowNodeEditor.tsx          | 113 ++++++++++++++++-----
 .../dashboard/app/components/WorkflowSwitcher.css  |   9 ++
 .../dashboard/app/components/WorkflowSwitcher.tsx  |  15 ++-
 .../dashboard/app/components/WorktreeGroup.tsx     |   2 +-
 .../app/components/__tests__/Board.test.tsx        |  12 +--
 .../app/components/__tests__/Lane.test.tsx         |   6 +-
 .../__tests__/WorkflowNodeEditor.test.tsx          |  57 +++++++++--
 .../components/__tests__/WorkflowSwitcher.test.tsx |  68 ++++++++-----
 .../__tests__/board-mobile-initial-render.test.tsx |   2 +-
 .../__tests__/CommandCenter.test.tsx               |   4 +-
 .../command-center/areas/WorkflowArea.tsx          |   2 +
 .../areas/__tests__/WorkflowArea.test.tsx          |   6 +-
 .../settings/sections/GeneralSection.tsx           |   6 +-
 .../register-command-center-routes.test.ts         |   2 +-
 .../__tests__/workflow-import-export.test.ts       |  32 +++++-
 packages/dashboard/src/routes/board-workflows.ts   |   6 +-
 .../src/routes/register-workflow-routes.ts         |  32 +++++-
 packages/engine/src/__tests__/agent-tools.test.ts  |   4 +-
 packages/engine/src/agent-tools.ts                 |   6 +-
 40 files changed, 590 insertions(+), 145 deletions(-)

Fusion-Task-Id: FN-7285
Fusion-Task-Lineage: 0fa689a2-f19d-4d1b-9a40-cc0b2181200a
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-06-30 12:21:25 -07:00

50 lines
1.8 KiB
TypeScript

import "./WorkflowIcon.css";
export interface WorkflowIconProps {
workflowId: string;
icon?: string;
className?: string;
decorative?: boolean;
}
function isBuiltinWorkflowId(workflowId: string): boolean {
return workflowId.startsWith("builtin:");
}
function classNames(...parts: Array<string | false | null | undefined>): string {
return parts.filter(Boolean).join(" ");
}
/**
* FNXC:WorkflowIcons 2026-06-30-12:12:
* Rich workflow identity surfaces render built-ins with the Fusion brand mark and custom workflows with compact text icons only when metadata exists.
* Return null for no-icon custom workflows so cards, badges, and switchers never leave empty icon shells.
*/
export function WorkflowIcon({ workflowId, icon, className, decorative = false }: WorkflowIconProps) {
const isBuiltin = isBuiltinWorkflowId(workflowId);
const label = isBuiltin ? "Fusion built-in workflow" : "Workflow icon";
const accessibilityProps = decorative
? { "aria-hidden": true as const }
: { role: "img" as const, "aria-label": label, title: label };
if (isBuiltin) {
return (
<span className={classNames("workflow-icon workflow-icon--builtin", className)} {...accessibilityProps}>
<svg className="workflow-icon-mark" viewBox="0 0 128 128" focusable="false" aria-hidden="true">
<circle cx="64" cy="64" r="52" />
<path d="M26 101C44 82 62 64 82 45C90 37 98 30 104 24C96 35 89 47 81 60C70 79 57 95 43 108C38 112 32 108 26 101Z" />
</svg>
</span>
);
}
const trimmedIcon = typeof icon === "string" ? icon.trim() : "";
if (!trimmedIcon) return null;
return (
<span className={classNames("workflow-icon workflow-icon--custom", className)} {...accessibilityProps}>
<span className="workflow-icon-custom-glyph" aria-hidden="true">{trimmedIcon}</span>
</span>
);
}