Date: 2026-05-13 Area: agen-for-work/connectors/, @theme/, scripts/ Type: Documentation IA + tooling addition
agen-for-work/connectors/marketplace/ contains 96 integration guides. They are all listed alphabetically as a flat list inside the Marketplace group in agen-for-work/sidebars.yaml (lines 53–268), producing a 96-item "wall of text" in the sidebar.
For a user who knows the integration they need ("find Slack"), this is slow: scroll an unsegmented list of 96 items, or use the global Redocly search and hope it ranks the connector page first. For a user evaluating coverage ("do you support any CRM?"), there is no overview by domain — they cannot scan categories.
The product UI already groups connectors by category (see agen-for-work/sources/overview.md and the Marketplace tab → Category dropdown in-product), but no such structure exists in documentation.
- Reorganize the sidebar so the 96-item flat list becomes 22 collapsible category groups, collapsed by default; Redocly auto-expands the group of the active page.
- Introduce a single landing page
agen-for-work/connectors/marketplace.mdthat renders a searchable, category-sectioned catalog of all connectors. - Reuse the authoritative category taxonomy that exists in the dashboard codebase (
ConnectorCategoryenum andIntegrationCategoryMapindashboard/app/v2/src/tools/stores/envStore/workforceApp/workforceApp.types.ts), so docs and product agree on category names without ad-hoc curation. - Make the catalog data derive from per-file frontmatter (single source of truth in each connector's markdown), regenerated to a TypeScript data file by an explicit
npm run generate:connectorsstep. - Add CI validation so a connector cannot be added without a valid category.
- No icon or logo assets per integration. The docs repo has none today (
static/contains only Frontegg logos and one strayslack.svg); sourcing and licensing 96 third-party brand assets is a separate project. - No per-connector
description:on tiles. Names alone suffice for the "knows the name" path; descriptions can be added later as another optional frontmatter field. - No equivalent catalog for
ciam/oragen-for-saas/. If this pattern works, replicate it there afterwards. - No localization. Catalog UI strings ("Search 96 connectors…", "All categories", "No connectors match your search") are English.
- No live sync with the dashboard repo. We copy the taxonomy into docs once (via bootstrap) and treat docs frontmatter as the source of truth thereafter.
- No e2e or visual-regression tests. Smoke verification via
npm run devpreview is enough for v1.
Today none of the 96 marketplace markdown files have any frontmatter — they start straight with ## Connector_name integration. The bootstrap (Phase 2) adds a frontmatter block to every file:
---
category: Communication # NEW — required, string from enum
displayName: Slack # NEW — optional, overrides humanized filename
---categorymust be one of the 30 strings inConnectorCategory(in dashboard enum) — values likeProductivity,Communication,Project Management,CRM,Development,Storage,Finance,Customer Support,Marketing,E-commerce,Analytics,Monitoring,DevOps,HR,Video Conferencing,Scheduling,Identity,Design,Forms,IT Service Management,Documents,Automation,Data,Social Media,Feature Flags,Content Management,Sales Intelligence,Product Management,Infrastructure,AI. (Only 22 of these are populated by current docs.)displayNameoverrides the filename-derived label (e.g.monday→monday.com,bamboohr→BambooHR,microsoft-todo→Microsoft To Do).
A one-time bootstrap script reads the dashboard's IntegrationCategoryMap and inserts category into all 96 files. After that, frontmatter is hand-maintained.
The 30 categories live in dashboard/app/v2/src/tools/stores/envStore/workforceApp/workforceApp.types.ts as the ConnectorCategory enum. We copy the enum verbatim (string values only) into a small file in this repo so the validator and component can reference it without depending on the dashboard package:
@theme/markdoc/components/ConnectorCatalog/categories.ts:
export const CONNECTOR_CATEGORIES = [
'Productivity',
'Communication',
'Project Management',
// ... 27 more, in the same order as the dashboard enum
] as const
export type ConnectorCategory = (typeof CONNECTOR_CATEGORIES)[number]Order is the enum's source order; this is also the order used by sidebar groups and on the landing page.
Inputs: agen-for-work/connectors/marketplace/*.md.
Outputs: @theme/markdoc/components/ConnectorCatalog/connectors.generated.ts, shape:
import type { ConnectorCategory } from './categories'
export interface ConnectorEntry {
id: string // filename without .md, e.g. 'slack'
name: string // displayName from frontmatter, or humanized id
category: ConnectorCategory
href: string // 'connectors/marketplace/slack.md'
}
export const CONNECTORS: ConnectorEntry[] = [/* generated */]Behavior:
- Reads each markdown, extracts frontmatter via
gray-matter(already available via Redocly toolchain — confirm during phase 0, otherwise add the dep). - Validates each file has
categoryand that it is inCONNECTOR_CATEGORIES; fails the run otherwise. - Sorts entries by
(category index in CONNECTOR_CATEGORIES, name). - Writes the data file with a
// AUTO-GENERATED — do not editheader. - Idempotent: running twice produces identical output.
The script is exposed as npm run generate:connectors in package.json.
Exposed as npm run check:marketplace-frontmatter. Pattern matches the existing check:content-style script: a standalone Node script invoked separately (not bundled into npm run test, which is reserved for redocly lint). CI runs all three checks (test, check:content-style, check:marketplace-frontmatter) and fails on any.
Responsibilities:
- Re-run the generator into an in-memory string.
- Compare against the committed
connectors.generated.ts. Fail with a clear message if they differ: "connectors.generated.ts is stale — runnpm run generate:connectorsand commit the result." - Validate that every file in
agen-for-work/connectors/marketplace/*.md:- has
categoryin frontmatter; categoryis inCONNECTOR_CATEGORIES;- is referenced exactly once in
agen-for-work/sidebars.yaml.
- has
- Validate that every entry under the
Marketplacegroup insidebars.yamlpoints to a file that exists on disk.
No new tooling beyond gray-matter and js-yaml (the latter likely already present for Redocly config parsing — verify in phase 0).
Files:
ConnectorCatalog/
├── ConnectorCatalog.tsx # React component (styled-components)
├── ConnectorCatalog.markdoc.ts # Markdoc schema (no attributes)
├── categories.ts # CONNECTOR_CATEGORIES, ConnectorCategory type
├── connectors.generated.ts # generated, committed
└── index.ts # barrel exportRegistered in @theme/markdoc/components.tsx and @theme/markdoc/schema.ts following the existing ProductCard pattern.
Component behavior:
- Imports
CONNECTORSfromconnectors.generated.tsandCONNECTOR_CATEGORIESfromcategories.ts. No props. - Internal state:
query: string,category: 'all' | ConnectorCategory. - Top bar (sticky? no — keep simple): search input
aria-label="Search connectors"and a<select>populated from the categories that are actually present inCONNECTORS(skip empty ones), in enum order, prefixed by anAll categoriesoption. - Body:
- No filters active (default): render an
<h3>section per category that has entries, followed by a grid of tiles. Tiles are plain<a href={connector.href}>with the connector name centered. - Search input non-empty: collapse all sections into one alphabetically sorted flat grid of matches. Match is case-insensitive substring on
name. - Category dropdown set: render only that one category's section (still show the H3 for context).
- Both active: filter by category, then by query, render as a flat grid under one H3.
- Empty result: show
<p>No connectors match your search.</p>followed by aClear filtersbutton that resets both state values.
- No filters active (default): render an
- Styling matches
ProductCardconventions (styled-components,var(--icon-card-*)theme variables). Tile is a bordered rounded box with hover state; grid uses CSSgrid-template-columns: repeat(auto-fill, minmax(160px, 1fr)). - No animations beyond the standard 0.2s border-color transition on hover.
The Markdoc tag has zero attributes:
export const ConnectorCatalog: Schema & { tagName: string } = {
attributes: {},
render: 'ConnectorCatalog',
tagName: 'ConnectorCatalog',
}Usage in markdown: {% ConnectorCatalog /%}.
---
title: Marketplace
---
## Marketplace
Browse all available connectors. Use the search field to find a connector by
name, or filter by category.
Heading rules from scripts/check-content-style.mjs are respected (first H2, no H1, no emojis).
The current flat Marketplace group (one items: block with 96 entries) is replaced by a Marketplace group whose page: is the new landing and whose items: is a list of 22 category subgroups, in enum order:
- group: Marketplace
page: connectors/marketplace.md
expanded: false
items:
- group: Productivity
expanded: false
items:
- label: Airtable
page: connectors/marketplace/airtable.md
- label: Confluence
page: connectors/marketplace/confluence.md
# … alphabetical within group
- group: Communication
expanded: false
items:
- label: Discord
page: connectors/marketplace/discord.md
# …
# … 20 more category subgroups in enum orderWithin each category, items are alphabetical by label. Category groups appear in CONNECTOR_CATEGORIES order. Categories with no docs entries are omitted.
Redocly Realm auto-expands the group containing the active page, so no custom behaviour is needed for the "collapsed-but-active-group-open" UX.
agen-for-work/sources/overview.md— keep as-is. The Marketplace section there describes the product UI; the new landing page describes the docs catalog. They co-exist without overlap.- No changes to the 96 connector markdown files beyond the new frontmatter fields. Body content is untouched.
- Phase 0 — verify deps. Confirm
gray-matterandjs-yamlare available in the toolchain; add them as devDependencies if not. - Phase 1 — taxonomy + generator + validator. Add
categories.ts,scripts/generate-connectors.mjs,scripts/check-marketplace-frontmatter.mjs. Addgenerate:connectorsandcheck:marketplace-frontmattertopackage.jsonscripts (each standalone, matching the existingcheck:content-stylepattern). - Phase 2 — bootstrap frontmatter. One-shot script that reads
IntegrationCategoryMapfrom the dashboard repo (path supplied via env var) and writescategory:into all 96 markdown files. The script lives inscripts/bootstrap-connector-categories.mjsand is deleted after the PR merges (one-time use). - Phase 3 — component. Add
ConnectorCatalog.tsx,ConnectorCatalog.markdoc.ts,index.ts; register in@theme/markdoc/components.tsxandschema.ts. - Phase 4 — landing page. Add
agen-for-work/connectors/marketplace.md. - Phase 5 — sidebar restructure. Replace the flat list in
agen-for-work/sidebars.yamlwith category subgroups. - Phase 6 — generate + commit data file. Run
npm run generate:connectors, commitconnectors.generated.ts. - Phase 7 — validation.
npm run test,npm run check:content-style,npm run devsmoke-check.
npm run test— Redocly lint. Must pass.npm run check:content-style— unchanged rules; newmarketplace.mdand any other touched markdown must pass.npm run check:marketplace-frontmatter— new validator. Must pass.- Local smoke (
npm run dev):/agen-for-work/connectors/marketplacerenders the catalog with 96 tiles distributed across 22 H3 sections, in enum order.- Typing in the search input filters tiles live, with no flicker.
- Selecting a category in the dropdown narrows to that section.
- Empty result state appears for nonsense queries.
- Sidebar shows
Marketplacecollapsed; expanding shows 22 collapsed subgroups; clicking any connector auto-expands its category group.
gray-matter/js-yamlavailability. Either is a small dep, but if Redocly's Markdoc pipeline strips them at runtime we may need to bundle frontmatter parsing into the generator only (which runs at dev time, so this is fine). Resolve in Phase 0.- Bootstrap reliability. The bootstrap script depends on a sibling
dashboard/checkout at a known relative path. Document the env var (DASHBOARD_REPO=/path/to/dashboard) in the script. If the path is wrong, fail loud. - Category drift. Once docs frontmatter is the source of truth, future dashboard renames are not auto-propagated. The validator only checks
CONNECTOR_CATEGORIES, so an out-of-datecategories.tswill silently diverge from product. Mitigation: include a comment incategories.tspointing at the dashboard file and a date stamp; refresh manually when product changes are announced. - Sidebar diff size. Replacing 216 lines of flat YAML with a nested structure makes the PR diff noisy. Acceptable cost; review by reading the rendered preview rather than the YAML diff.
- Search performance. 96 entries × substring match is trivial; no debouncing or indexing needed. If the catalog grows past ~500, revisit.