Rebuild website on Primer Brand design (#2703)

* feat(website): rebuild site on Primer Brand design prototype

Replace the Astro + Starlight site with the Brand Engineering design
prototype, ported component-for-component onto plain Astro + React
islands. The prototype is treated as the authority on markup and
styling; dynamic data is injected into its components rather than the
components being reinterpreted.

Framework:
- Remove @astrojs/starlight entirely, along with its document shell,
  search, footer and language selector. BaseLayout.astro now owns the
  document head, CSP, social meta and analytics.
- Add @primer/react-brand and @astrojs/react. Alias the package to its
  ESM build in vite.resolve, since the default CJS entrypoint breaks
  named-export detection during SSR, and mark it noExternal so its
  stylesheet imports resolve.
- Promote pagefind to an explicit devDependency; it was previously
  pulled in transitively by Starlight.

Pages: home, the five catalogs, the five detail routes, contributors,
Playbook index and articles, and the cookbook are all rendered by
ported prototype components inside a shared PageShell.

Detail pages share a DetailChassis (hero, breadcrumbs, sticky TOC with
scroll-spy, prev/next) while keeping what makes each type distinct: a
file switcher over bundled skill assets, an included-items grid and
external provenance for plugins, and a hero-scale preview for
extensions.

i18n: resolve translated Playbook entries via Astro.currentLocale
inside the shared article route instead of separate [locale] routes.
The explicit routes collided with the i18n fallback routes, so
translated articles were being shadowed by their English originals and
900 nonsensical double-locale pages were emitted.

Search: TopNavSearch now queries the Pagefind index client-side in
addition to the static resource index, merging on href and degrading
to the static index in dev, where no index has been built.

Also delete the superseded vanilla-TS page renderers and Astro
partials, which the ported React components fully replace.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* fix(website): resolve accessibility violations in ported detail pages

Fixes the three axe violations surfaced by the a11y audit after the
Primer Brand redesign:

- aria-prohibited-attr: `aria-label` was set on roleless `div`s in
  InstructionDetail and PluginDetail. The "Applies to" list now uses
  list/listitem roles; plugin provenance uses a group role.
- scrollable-region-focusable: the install command `code` element
  overflows horizontally but was not keyboard reachable. Added
  tabIndex to all three render sites.
- color-contrast: the Playbook "New" label used the brand's
  success-fg on success-subtle, reaching only 4.09:1 in light mode.
  Stepped one down the same green ramp for 6.14:1. Scoped to light
  mode; dark mode already passed and its green-7 is near-black.

Also corrects the stale route list in the audit script: /hooks/,
/workflows/ and /tools/ have never existed as pages.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* fix(website): collapse long filter facets and unpin detail-page footer

Two issues surfaced by review of the redesigned site:

Catalog facets rendered every option. The prototype's filter groups were
built from small hardcoded arrays, but real data produces 193 tool options
on /agents/ and 245 "Applies to" values on /instructions/. The sidebar grew
to ~10,000px and stretched the whole catalog row, pushing the (already
present) pagination control far below the fold so it read as missing.

Adopt the prototype's own solution for this, which it had already applied to
the extensions page: collapse groups past 10 options behind a "Show N more"
toggle, and cap .filterOptions with an internal scroll area. Ported verbatim
to the agents, instructions, skills, and plugins catalogs.

Detail pages scroll inside .scrollHost rather than the document, but the
footer came from PageShell, outside that element, so it stayed pinned over
the content instead of appearing at the end. The prototype renders its
footer inside the scroll host; PageShell now takes a renderFooter flag so
DetailChassis can do the same. LearningArticleLayout already did this.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* fix(website): source contributor count from .all-contributorsrc

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* Removing playwright-mcp files

* fix(website): resolve article text colour inside themed scope

Injected markdown inherited color from body, which sits outside the
ThemeProvider and always resolved the light-mode token, making body
copy unreadable in dark mode.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* fix(website): drop 'In this article' TOC from resource detail pages

Resource detail pages (agent, instruction, skill, plugin, extension) are
not articles, and their markdown headings do not form a meaningful
outline. Playbook and cookbook articles keep their TOC.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* style(website): thin the sidebar scrollbar on detail pages

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* fix(website): use themed link colour for markdown links in dark mode

Raw markdown-injected <a> elements previously fell back to the browser's
default blue/purple link colours, which are harsh against the dark-mode
background. Route them through --brand-color-text-link-rest (and the
pressed/hover token) instead.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* fix(website): apply ThemeProvider to Playbook article layout so dark mode works

LearningArticleLayout replaces PageShell for Playbook articles but never
wrapped itself in a ThemeProvider, so its useTheme() call always fell back
to Primer's light default regardless of the site's actual theme preference.
Split the component into a thin ThemeProvider wrapper plus the existing
implementation (now LearningArticleLayoutBody), matching the pattern already
used by PageShell.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* fix(website): align raw markdown code blocks with prototype styling

Astro's default Shiki config bakes in the fixed 'github-dark' theme's
literal colors, ignoring the site's actual light/dark mode - this made
plain markdown-fenced code blocks (as opposed to the prototype's own
SyntaxHighlightedCode component) always render a hardcoded dark box
regardless of theme.

Switch shikiConfig to the 'css-variables' theme so highlighted tokens
resolve through --astro-code-* custom properties instead, then map
those to the same brand color tokens the prototype's codeBlock uses
(canvas-subtle background, border-muted border, brand text/link/accent
colors for tokens). Raw markdown code blocks now match the prototype's
bordered, canvas-subtle surface in both color modes.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* fix(website): show real contributor count on every page shell

The contributor badge rendered 0 on Playbook, Cookbook, home and custom
pages, and reverted to 0 on hydration everywhere else.

Two causes:
- Shells that bypass PageShell (LearningArticleLayout, PlaybookIndex,
  PlaybookArticleBody, CookbookIndex, HomePage, TopNav, Custom) defaulted
  contributorsTotal to 0 instead of the site-data value.
- site-data read .all-contributorsrc with node:fs at module scope. Those
  shells are client:load hydrated, so the read threw in the browser and
  the count reset to 0 after hydration.

The count is now read once in astro.config.mjs and inlined through
vite.define as __CONTRIBUTORS_TOTAL__, so it is a literal in both the
server render and the client bundle.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* style(website): soften catalog filter list scrollbars

The filter option lists only set scrollbar-width: thin, so they rendered
the platform default scrollbar. They now use the same muted, transparent
track treatment as the article sidebar, which resolves through
--brand-color-border-muted in both colour modes.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* refactor(website): remove Access from tools panel from plugins catalog

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* feat(website): make Copilot app deep link the default plugin install

Plugin detail pages exposed only a copyable CLI command. They now lead
with a ghapp://plugins/install deep link in the same split-button
ActionMenu the other detail pages use, keeping the CLI command available
as a Copy action in the menu.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* fix(website): migrate markdown remark plugins to unified processor

Astro 7 deprecates markdown.remarkPlugins in favour of passing a
unified() processor from @astrojs/markdown-remark. Moves the GitHub
admonitions plugin into markdown.processor, clearing the startup
deprecation warning. shikiConfig stays at the markdown level as it is
not part of UnifiedProcessorOptions.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* fix(website): fail the build when the contributor manifest is unreadable

Silently falling back to 0 is how the contributor badge regressed before,
so a missing or malformed .all-contributorsrc now throws in production
builds and warns in dev instead of shipping a wrong count.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* Fix codespell and CodeQL findings in website components

- Fix real typos flagged by codespell: 'Couldn&apos;t' -> 'Couldn't'
  (plain apostrophe, matching convention elsewhere in JSX) and
  'Unparseable' -> 'Unparsable' in catalogFilters.ts
- DetailChassis.tsx: replace sequential HTML entity unescaping with a
  single-pass replace to avoid double-unescape/injection risk flagged
  by CodeQL
- SyntaxHighlightedCode.tsx: make the markup HTML comment regex match
  newlines so multi-line comments cannot break out of the token
  (Bad HTML filtering regexp)
- pagefindSearch.ts: strip HTML tags in a loop until stable so nested/
  malformed markup can't survive a single-pass strip (Incomplete
  multi-character sanitization)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* fix(website): harden detail heading sanitization

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* github-app settings

* Fix light-theme flash before dark mode applies on page load

ThemeProvider colorMode="auto" from @primer/react-brand only resolves
the real OS colour-scheme preference inside a useEffect, so its first
render is always light. Add a synchronous inline script in <head> that
reads prefers-color-scheme and stamps data-color-mode onto <html>
before first paint, using the same attribute Primer Brand's CSS
already keys off. React's own data-color-mode on the inner element
takes over once it hydrates.

Fixes #2820

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* Fix light-mode scrollbar rendering in dark mode on Playbook articles

.scrollHost (the article scroll region shared by Playbook articles,
cookbook recipes, and extension detail pages) never declared a
color-scheme, so the browser always painted its native scrollbar using
light-mode chrome regardless of the site's active theme. Set
color-scheme: light dark as a baseline and pin it explicitly to the
resolved data-mode, and add scrollbar-color so Firefox picks up the
themed thumb colour too.

Fixes #2822

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* Widen and center extension card preview image

The Canvas Extensions overview card thumbnail was fixed at 360px, leaving
uneven left/right margins within the card. Let it grow up to 440px and
center it with margin-inline: auto so the image is balanced within the
card.

Fixes #2823

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* Link Submit CTAs to each resource's contribution section

The bottom "Submit" CTA on every catalog page pointed at the top of
CONTRIBUTING.md, forcing readers to scroll and hunt for the relevant
"how to contribute" instructions for that specific resource type.
Point each catalog's CTA directly at the section that documents how to
contribute that resource: agents, instructions, plugins, and skills
link to their dedicated docs/README.*.md "How to Contribute" section,
and canvas extensions link to CONTRIBUTING.md's "Adding Canvas
Extensions" section (extensions have no dedicated README doc).

Fixes #2824

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* Center-align "View on GitHub" source link with its icon

The source-link label sat on the default inline text baseline instead
of being vertically centered against the GitHub mark icon next to it,
so the two visually drifted apart. Wrap the icon and label in an
inline-flex container with align-items: center so they share the same
vertical center.

Fixes #2825

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* Fix mobile resource and Playbook article layout issues

Clean up the shared resource detail and Playbook article styling so mobile pages no longer show unintended divider lines or overflow horizontally.

- remove article-section divider borders so content separates with spacing instead of white/muted horizontal rules
- drop the detail-page breadcrumb divider on mobile to match the Playbook mobile treatment
- constrain raw markdown pre/code blocks to their column and let long code scroll horizontally
- allow metadata chips to wrap inside the mobile sidebar instead of widening the page

Fixes #2827
Fixes #2828
Fixes #2829

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* Only show language selector on pages with real translations

Most of the site is English-only, but the language selector rendered
unconditionally on every page even though only the
learning-hub/copilot-workshops/app track has mirrored translations.

Add hasTranslations() to playbook-routes.ts, thread a showLanguageSelect
prop through PageShell, TopNav and LearningArticleLayout, and compute
it from the article's englishId in learning-hub/[...slug].astro so only
translated Playbook articles show the selector.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* Add Workshop recommended card to Playbook index

The copilot-workshops hands-on tracks (VS Code, CLI, App, Cloud harnesses) existed and rendered correctly at direct URLs, but had no entry point in the new Playbook index UI -- the Articles grid intentionally excludes nested multi-page tracks by design, and the only prior link was buried in body prose.

Adds a 'Workshop' recommended card (following the existing cli-for-beginners precedent) linking to /learning-hub/copilot-workshops/, and registers the route in pageHref.ts.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* Add a Workshop content-type filter to the Playbook

The hands-on workshop tracks (CLI for Beginners, and the copilot-workshops harnesses) had no shared way to discover them from the Articles grid -- each had a recommended card, but no consistent categorization.

Tags both workshop landing pages 'workshop', adds a new 'Workshop' Kind/content-type facet (derived the same way as the existing Terminology/Tutorial/Example kinds), and includes both landing pages in the Articles grid dataset so filtering by Content type > Workshop surfaces exactly the two tracks.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* Fix P0/P1 items from Brand Engineering audit (brand-experience#458)

- P0: html lang now reflects the actually-rendered locale for a page,
  not the requested URL locale (BaseLayout.astro, playbook-routes.ts,
  PlaybookArticle.astro)
- P0: markdown tables get tabindex=0 via enhanceMarkdownA11y, which
  Playbook articles were never running (PlaybookArticle.astro)
- P0: long inline content and breadcrumbs no longer clip on narrow
  viewports (dotnet-upgrade.module.css, github-copilot-app.module.css)
- P1: strip the markdown document's own leading H1 so detail pages
  don't render the title twice (detail-page.ts)
- P1: document the hero secondary-CTA hex and InstructionsCatalog
  illustration colors as intentional prototype-fidelity values rather
  than defects (styles.module.css)
- P1: add explicit Heading size props in PlaybookIndex.tsx

Ref: github/brand-experience#458

* Rename Playbook to Learning Hub across UI and code

Renames PlaybookIndex.tsx -> LearningHubIndex.tsx, PlaybookArticle.astro
-> LearningHubArticle.astro, PlaybookArticleBody.tsx ->
LearningHubArticleBody.tsx, PlaybookIcon.tsx -> LearningHubIcon.tsx,
lib/playbook-routes.ts -> lib/learning-hub-routes.ts, and
lib/playbook-article.ts -> lib/learning-hub-article.ts. Updates all
call sites, UI copy, TopNav's playbookLabel prop, nav/search entries,
and home page CTA data to use Learning Hub terminology. URLs under
/learning-hub/* are unchanged.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* Port github-copilot-app as bespoke Learning Hub article

Adopts the prototype's github-copilot-app.tsx directly as a dedicated React
component instead of rendering it through the generic markdown pipeline,
preserving its video carousel, comparison list, learn-more band, and other
bespoke JSX exactly as designed.

- New GithubCopilotApp.tsx under components/brand/learning-hub/, ported
  near-verbatim from the prototype (data-wiring only: pageHref import swap,
  media paths).
- The two large source videos (~23MB, ~32MB) are not committed; the article
  falls back to their poster/webp images with a TODO to host them externally.
- learning-hub-bespoke-articles.ts lists slugs that bypass the generic
  LearningHubArticle.astro pipeline.
- [...slug].astro branches to the bespoke component for "github-copilot-app"
  (English only for now); all other slugs and locales keep the generic path.

* Port bespoke Learning Hub articles

Add bespoke Learning Hub article components for the remaining prototype-backed
article pages, wire the cookbook index to the data-driven port, and route the
CLI overview page through the new bespoke component.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* Resolve merge validation blockers in astro config

* Regenerate README docs after merging origin/main

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* Fix Learning Hub Get started pages disappearing after opening (#2960)

The bespoke Learning Hub article components (GithubCopilotApp,
AgentsAndSubagents, CopilotConfigurationBasics,
GithubCopilotTerminologyGlossary, UsingAutomationsInCopilotApp,
WhatAreAgentsSkillsInstructions, WorkingWithCanvasExtensions,
CliForBeginnersOverview, CookbookIndex) received the `pageHref` helper
as a function prop passed into a `client:load` React island. Functions
cannot survive Astro's client-hydration prop serialization, so the
prop arrived as `undefined` on the client. The page rendered correctly
server-side (visible briefly), then React threw
`TypeError: pageHref is not a function` during hydration and unmounted
the tree, making the page appear to disappear.

Fixed by having each component import the `pageHref` singleton
directly (as AgentDetail, HomePage, and the other catalog components
already do) instead of receiving it as a prop, and removed the now
unnecessary `pageHref={pageHref}` prop and its import from the two
Astro routes that render these islands.

Verified in the dev server: all 9 previously affected routes now
hydrate without error, and `npm run build` completes successfully
(1097 pages).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* Strip stray trailing divider from Learning Hub articles (#2961)

Several Learning Hub markdown articles end their body with a trailing
--- thematic break, left over from an authoring template. Rendered
as-is it produced an unlabeled <hr> sitting directly above the page
footer, with no heading/section boundary to justify it.

Fix this generically in buildArticleSections() by stripping a single
trailing <hr> from the rendered HTML before it's split into sections,
rather than hand-editing every affected content file. This also
protects future articles authored with the same trailing-rule habit.
Legitimate mid-content dividers (e.g. between CLI lesson steps) are
left untouched.

* Remove emojis from Learning Hub article nav (#2962)

CLI for Beginners lessons decorate their ## headings with emoji
(e.g. "🎯 Learning Objectives"), which is fine inline in the article
body but read as stray glyphs once surfaced as plain-text labels in
the "In this article" nav — other Learning Hub pages don't
emoji-decorate their nav, so this was inconsistent.

Add a shared stripEmoji() helper and apply it to the nav labels built
from markdown headings in LearningHubArticle.astro. The article body
headings themselves are untouched.

* Fix hero code blocks blending into background (#2963)

Detail page hero install-command code blocks (SyntaxHighlightedCode via
heroExtras) used the same background token as the hero surface itself
(--brand-color-canvas-subtle), making them visually indistinguishable
except for a thin border.

Add a scoped .heroContent .codeBlock override in both shared style
modules (dotnet-upgrade.module.css for catalog Detail pages/Cookbook,
github-copilot-app.module.css for Learning Hub) that sets the
background to --brand-color-canvas-default instead, so hero code
blocks stand apart from the hero background.

The base .codeBlock rule is left untouched since body-content code
blocks (e.g. Cookbook recipe steps) correctly rely on canvas-subtle
being distinct from the page's canvas-default background.

* Tie color-scheme to dark mode for native scrollbars (#2964)

Native scrollbars (the page scrollbar, and any overflow container
without custom scrollbar styling, e.g. code block <pre> elements) are
painted by the browser based on the color-scheme CSS property, not our
design tokens. Only a handful of scoped containers (.scrollHost,
.filterOptions, .sidebarSticky) had custom scrollbar-color rules; the
page scrollbar and plain code-block overflow scrollbars had no
color-scheme set at all, so they rendered with light OS-default
scrollbars even when data-color-mode="dark" was set on <html>.

Add a global rule tying html's color-scheme to the same
data-color-mode attribute BaseLayout.astro already stamps on <html>,
so every native scrollbar (and other UA-drawn form controls) follows
the site's theme.

* Unify article/detail page typography, tables, and dividers (#2965)

Raw markdown injected into .articleSection (Skill/Agent/Instruction/
Plugin/Extension Detail pages via dotnet-upgrade.module.css, and
Learning Hub articles via github-copilot-app.module.css) had no styling
for headings, tables, horizontal rules, blockquotes, or inline code, so
it fell back to inconsistent browser UA defaults -- most visibly,
tables rendered with no borders or header emphasis at all.

Add a shared set of rules to both CSS modules, scoped under
.articleSection, that:
- map raw h1-h6 onto the brand type scale (font, weight, line-height)
- give hr a full-width divider matching the border-muted token
- style blockquote with a left accent bar
- render inline `code` (outside <pre>) as a small chip, matching
  .inlineCode used elsewhere for hand-authored tokens
- style table/th/td with bordered cells and a canvas-subtle header row,
  scrolling horizontally on narrow viewports

Also fix skills/acquire-codebase-knowledge/SKILL.md: a stray blank
line inside the "Bundled Assets" table split it into a lone header row
and a second block with no header, so GFM table parsing stopped after
the first row and the remaining rows rendered as literal pipe-delimited
text instead of a table -- this was the exact bug shown in the issue
screenshot.

* fix: align previous/next navigation links horizontally on Detail pages

Detail page Previous/Up next links were stacked vertically. Change
.nextUp to a row layout (space-between) so Previous sits left-aligned
and Up next right-aligned on the same row, with a max-width: 40rem
media query reverting to a stacked column layout on small screens.

Use an explicit .nextUpNext modifier class (applied to the "Up next"
link specifically) with margin-inline-start: auto instead of a
positional :last-child selector, so the link right-aligns correctly
even when it is the only link present (e.g. the first item in a
catalog, which has no Previous link).

Fixes #2966

* fix: align callout blocks with article content width

The .proTip callout (Note/Tip/Caution admonitions in Learning Hub
articles, and the "Maintained outside this repository" notice on
external Plugin Detail pages) had its own 32px horizontal margin.

The prototype places this callout as a sibling of .articleSection
(which has no horizontal padding of its own), using that margin to
align its edges with the section's own 32px padding. Our port always
renders it nested *inside* an already-padded .articleSection, so the
extra margin doubled up with that padding, making the callout visibly
narrower than the surrounding paragraph/table text -- most obvious on
narrow viewports.

Remove the horizontal margin (keep only the bottom spacing) in both
dotnet-upgrade.module.css and github-copilot-app.module.css so the
callout's internal padding lines up with the article text on both
edges, at all viewport widths.

Fixes #2967

* fix: standardize mobile card padding across resource pages

Agents, Instructions, Skills, and Plugins catalog cards (.item) used
the desktop 60px padding at every viewport, including phone widths,
because their @media (max-width: 47.99rem) block never reduced it --
unlike the Extension catalog, which drops to a 32px inset at that
breakpoint. This made cards on those four catalogs look inconsistently
over-indented on mobile compared to Extension cards.

Add the same `.item { padding: var(--base-size-32); }` override to the
mobile media query in agents.module.css, instructions.module.css, and
skills.module.css. plugins.module.css already had an override, but
with an asymmetric 32px/24px padding -- normalized it to the same 32px
on all sides used everywhere else.

Fixes #2968

* fix(website): address PR review feedback

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* fix(website): preserve legacy resource links

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* fix(website): bump transitive js-yaml to patched versions

Force astro's js-yaml dependency to 4.3.2 and front-matter/gray-matter's to 3.15.2 via npm overrides, fixing GHSA-52cp-r559-cp3m and GHSA-5p4m-2wfm-xmqj (quadratic-complexity DoS via YAML merge keys and !!omap resolution). Addresses github/vuln-mgmt#209786.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* fix(website): address redesign follow-up issues

Fix light-mode favicon contrast with an adaptive SVG mark, allow YouTube embeds through the site CSP, and align resource catalog sort controls with the Playbook article sort treatment across Agents, Instructions, Skills, Plugins, and Extensions.

Addresses #3006, #3007, #3008.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* fix(website): address PR review feedback

Respect reduced-motion preferences in detail TOC scrolling, restore default search indexes for bespoke Playbook pages, add pagination scroll/focus handling to the Agents catalog, sanitize contributor and cookbook URLs before rendering external links, index extension records in Pagefind, and restore install affordances for marketplace-backed external extensions.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* fix(website): revert Learning Hub naming from Playbook rename

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* chore(website): update stale Playbook comments to Learning Hub

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* fix(website): align source label in detail sidebar

Fixes #3207

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

* fix(website): address security review feedback

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6
This commit is contained in:
Aaron Powell
2026-09-16 10:50:43 +10:00
committed by GitHub
co-authored by Copilot App
parent fb4eb04fcb
commit 9ce814859e
194 changed files with 31234 additions and 13694 deletions
+55 -97
View File
@@ -1,116 +1,74 @@
---
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
import BackToTop from "../../components/BackToTop.astro";
import agentsData from "../../../public/data/agents.json";
import RawMarkdown from "../../components/pages/RawMarkdown.astro";
import type { HeaderItem } from "../../components/pages/Header.astro";
import DetailHeader from "../../components/pages/Header.astro";
import Breadcrumb from "../../components/pages/Breadcrumb.astro";
import Main from "../../components/pages/Main.astro";
import InstallButtons from "../../components/pages/InstallButtons.astro";
import Sidebar from "../../components/pages/Sidebar.astro";
import BaseLayout from "../../layouts/BaseLayout.astro";
import { AgentDetail } from "../../components/brand/AgentDetail";
import { buildDetailToc } from "../../components/brand/DetailChassis";
import { agents, searchIndex } from "../../lib/site-data";
import { loadDetailPage } from "../../lib/detail-page";
interface AgentItem extends HeaderItem {
const RAW_BASE = "https://raw.githubusercontent.com/github/awesome-copilot/main";
interface Item {
id: string;
title: string;
title?: string;
name?: string;
description?: string;
model?: string | string[];
tools?: string[];
hasHandoffs?: boolean;
handoffs?: string[];
mcpServers?: string[];
path: string;
filename?: string;
lastUpdated?: string | null;
}
const labelOf = (entry: Item) => entry.title ?? entry.name ?? entry.id;
export function getStaticPaths() {
return (agentsData.items as AgentItem[]).map((item) => ({
const name = (entry: Item) => entry.title ?? entry.name ?? entry.id;
const list = [...(agents as unknown as Item[])].sort((a, b) =>
name(a).localeCompare(name(b)),
);
return list.map((item, index) => ({
params: { id: item.id },
props: { item },
props: {
item,
previous: index > 0 ? list[index - 1] : undefined,
next: index < list.length - 1 ? list[index + 1] : undefined,
},
}));
}
const { item } = Astro.props as { item: AgentItem };
const { item, previous, next } = Astro.props as {
item: Item;
previous?: Item;
next?: Item;
};
const {
vscodeUrl,
insidersUrl,
githubUrl,
markdownHtml,
frontmatterText,
rawMarkdown,
lastUpdated,
} = loadDetailPage(item, "agent");
const detail = loadDetailPage(item, "agent");
const { html } = buildDetailToc(detail.markdownHtml);
const models = Array.isArray(item.model)
? item.model
: item.model
? [item.model]
: [];
const tools = item.tools ?? [];
const handoffs = item.handoffs ?? [];
const mcpServers = item.mcpServers ?? [];
const base = import.meta.env.BASE_URL ?? "/";
const prefix = base.endsWith("/") ? base : base + "/";
const detailHref = (id: string) =>
(prefix + "agent/" + id + "/").replace(/\/{2,}/g, "/");
const siblingOf = (sibling?: Item) =>
sibling ? { label: labelOf(sibling), href: detailHref(sibling.id) } : undefined;
const downloadUrl = RAW_BASE + "/" + item.path;
---
<StarlightPage
frontmatter={{
title: item.title,
description: item.description,
template: "splash",
prev: false,
next: false,
editUrl: false,
}}
<BaseLayout
title={labelOf(item) + " | Awesome GitHub Copilot"}
description={item.description}
>
<div
id="main-content"
class="resource-detail-page"
data-resource-detail
data-path={item.path}
>
<RawMarkdown markdown={rawMarkdown} />
<div class="container">
<Breadcrumb
paths={[
{ name: "Home", href: "/" },
{ name: "Agents", href: "/agents/" },
]}
title={item.title}
/>
<DetailHeader item={item} />
<div class="detail-layout">
<Main markdownHtml={markdownHtml} githubUrl={githubUrl} />
<Sidebar
item={item}
lastUpdated={lastUpdated ?? undefined}
frontmatterText={frontmatterText || undefined}
githubUrl={githubUrl}
chips={[
{ title: "Models", items: models },
{ title: "Handoffs", items: handoffs },
{ title: "MCP Servers", items: mcpServers },
{ title: "Tools", items: tools },
]}
>
<InstallButtons
vscodeUrl={vscodeUrl}
insidersUrl={insidersUrl}
rawMarkdown={rawMarkdown}
slot="install"
/>
</Sidebar>
</div>
</div>
</div>
<BackToTop />
<script>
import "../../scripts/pages/resource-detail";
</script>
</StarlightPage>
<AgentDetail
client:load
item={item as any}
markdownHtml={html}
vscodeUrl={detail.vscodeUrl}
insidersUrl={detail.insidersUrl}
githubUrl={detail.githubUrl}
downloadUrl={downloadUrl}
rawMarkdown={detail.rawMarkdown}
lastUpdated={detail.lastUpdated}
previous={siblingOf(previous)}
next={siblingOf(next)}
searchIndex={searchIndex}
/>
</BaseLayout>
+15 -48
View File
@@ -1,51 +1,18 @@
---
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
import agentsData from '../../public/data/agents.json';
import ContributeCTA from '../components/ContributeCTA.astro';
import EmbeddedPageData from '../components/EmbeddedPageData.astro';
import PageHeader from '../components/PageHeader.astro';
import BackToTop from '../components/BackToTop.astro';
import { renderAgentsHtml, sortAgents } from '../scripts/pages/agents-render';
const initialItems = sortAgents(agentsData.items, 'title');
import BaseLayout from "../layouts/BaseLayout.astro";
import { AgentsCatalog } from "../components/brand/AgentsCatalog";
import { agents, searchIndex } from "../lib/site-data";
import data from "../../public/data/agents.json";
---
<StarlightPage frontmatter={{ title: 'Custom Agents', description: 'Specialized agents that enhance GitHub Copilot for specific technologies, workflows, and domains', template: 'splash', prev: false, next: false, editUrl: false }}>
<div id="main-content" class="agents-page listing-cards-page">
<PageHeader title="Custom Agents" description="Specialized agents that enhance GitHub Copilot for specific technologies, workflows, and domains" icon="robot" />
<div class="page-content">
<div class="container">
<div class="listing-toolbar">
<div class="listing-toolbar-row">
<div class="results-count" id="results-count" aria-live="polite">{initialItems.length} agents</div>
<details class="listing-controls">
<summary class="listing-controls-trigger btn btn-secondary btn-small">Sort</summary>
<div class="listing-controls-panel">
<div class="filters-bar" id="filters-bar">
<div class="filter-group">
<label for="sort-select">Sort:</label>
<select id="sort-select" aria-label="Sort by">
<option value="title">Name (A-Z)</option>
<option value="lastUpdated">Recently Updated</option>
</select>
</div>
</div>
</div>
</details>
</div>
</div>
<div class="resource-list" id="resource-list" role="list" set:html={renderAgentsHtml(initialItems)}></div>
<ContributeCTA resourceType="agents" />
</div>
</div>
</div>
<BackToTop />
<EmbeddedPageData filename="agents.json" data={agentsData} />
<script>
import '../scripts/listing-flyouts';
import '../scripts/pages/agents';
</script>
</StarlightPage>
<BaseLayout
title="Agents | Awesome GitHub Copilot"
description="Browse community-contributed custom agents for GitHub Copilot."
>
<AgentsCatalog
client:load
agents={agents as any}
filters={data.filters as any}
searchIndex={searchIndex}
/>
</BaseLayout>
+42 -694
View File
@@ -1,697 +1,45 @@
---
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
import PageHeader from '../components/PageHeader.astro';
import { readFileSync } from "node:fs";
import path from "node:path";
import BaseLayout from "../layouts/BaseLayout.astro";
import {
ContributorsPage,
type Contributor,
} from "../components/brand/ContributorsPage";
import { searchIndex } from "../lib/site-data";
// `.all-contributorsrc` at the repository root is the same source the website
// data generator uses, so the page never drifts from the generated data.
// Resolved from the working directory rather than `import.meta.url`, which is
// unreliable once this module is bundled during a production build.
const rcPath = path.resolve(process.cwd(), "..", ".all-contributorsrc");
const rc = JSON.parse(readFileSync(rcPath, "utf8")) as {
contributors: {
login: string;
name?: string;
avatar_url: string;
profile: string;
}[];
};
const contributors: Contributor[] = rc.contributors
.map((entry) => ({
login: entry.login,
name: entry.name?.trim() || entry.login,
avatarUrl: entry.avatar_url,
profileUrl: entry.profile,
}))
.sort((a, b) => a.login.localeCompare(b.login, "en", { sensitivity: "base" }));
---
<StarlightPage frontmatter={{ title: 'Contributors', description: 'The wonderful people who have contributed to Awesome GitHub Copilot', template: 'splash', prev: false, next: false, editUrl: false }}>
<div id="main-content">
<PageHeader title="Contributors" description="The wonderful people who have contributed to Awesome GitHub Copilot" icon="book" />
<div class="page-content">
<div class="container">
<div class="contributor-grid" id="contributor-grid">
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tbody>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://www.aaron-powell.com/"><img src="https://avatars.githubusercontent.com/u/434140?v=4" width="100px;" alt=""/><br /><sub><b>Aaron Powell</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://codemilltech.com/"><img src="https://avatars.githubusercontent.com/u/2053639?v=4" width="100px;" alt=""/><br /><sub><b>Matt Soucoup</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.buymeacoffee.com/troystaylor"><img src="https://avatars.githubusercontent.com/u/44444967?v=4" width="100px;" alt=""/><br /><sub><b>Troy Simeon Taylor</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/abbas133"><img src="https://avatars.githubusercontent.com/u/7757139?v=4" width="100px;" alt=""/><br /><sub><b>Abbas</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://calva.io/"><img src="https://avatars.githubusercontent.com/u/30010?v=4" width="100px;" alt=""/><br /><sub><b>Peter Strömberg</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://danielscottraynsford.com/"><img src="https://avatars.githubusercontent.com/u/7589164?v=4" width="100px;" alt=""/><br /><sub><b>Daniel Scott-Raynsford</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jhauga"><img src="https://avatars.githubusercontent.com/u/10998676?v=4" width="100px;" alt=""/><br /><sub><b>John Haugabook</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://witter.cz/@pavel"><img src="https://avatars.githubusercontent.com/u/7853836?v=4" width="100px;" alt=""/><br /><sub><b>Pavel Simsa</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://digitarald.de/"><img src="https://avatars.githubusercontent.com/u/8599?v=4" width="100px;" alt=""/><br /><sub><b>Harald Kirschner</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://mubaidr.js.org/"><img src="https://avatars.githubusercontent.com/u/2222702?v=4" width="100px;" alt=""/><br /><sub><b>Muhammad Ubaid Raza</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tmeschter"><img src="https://avatars.githubusercontent.com/u/10506730?v=4" width="100px;" alt=""/><br /><sub><b>Tom Meschter</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.aungmyokyaw.com/"><img src="https://avatars.githubusercontent.com/u/9404824?v=4" width="100px;" alt=""/><br /><sub><b>Aung Myo Kyaw</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JasonYeMSFT"><img src="https://avatars.githubusercontent.com/u/39359541?v=4" width="100px;" alt=""/><br /><sub><b>JasonYeMSFT</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/jrc356/"><img src="https://avatars.githubusercontent.com/u/37387479?v=4" width="100px;" alt=""/><br /><sub><b>Jon Corbin</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/troytaylor-msft"><img src="https://avatars.githubusercontent.com/u/248058374?v=4" width="100px;" alt=""/><br /><sub><b>troytaylor-msft</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://delatorre.dev/"><img src="https://avatars.githubusercontent.com/u/38289677?v=4" width="100px;" alt=""/><br /><sub><b>Emerson Delatorre</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/burkeholland"><img src="https://avatars.githubusercontent.com/u/686963?v=4" width="100px;" alt=""/><br /><sub><b>Burke Holland</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://yaooqinn.github.io/"><img src="https://avatars.githubusercontent.com/u/8326978?v=4" width="100px;" alt=""/><br /><sub><b>Kent Yao</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://theainativemind.com/"><img src="https://avatars.githubusercontent.com/u/51440732?v=4" width="100px;" alt=""/><br /><sub><b>Daniel Meppiel</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yeelam-gordon"><img src="https://avatars.githubusercontent.com/u/73506701?v=4" width="100px;" alt=""/><br /><sub><b>Gordon Lam</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.madskristensen.net/"><img src="https://avatars.githubusercontent.com/u/1258877?v=4" width="100px;" alt=""/><br /><sub><b>Mads Kristensen</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://ks6088ts.github.io/"><img src="https://avatars.githubusercontent.com/u/1254960?v=4" width="100px;" alt=""/><br /><sub><b>Shinji Takenaka</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/spectatora"><img src="https://avatars.githubusercontent.com/u/1385755?v=4" width="100px;" alt=""/><br /><sub><b>spectatora</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sinedied"><img src="https://avatars.githubusercontent.com/u/593151?v=4" width="100px;" alt=""/><br /><sub><b>Yohan Lasorsa</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/VamshiVerma"><img src="https://avatars.githubusercontent.com/u/21999324?v=4" width="100px;" alt=""/><br /><sub><b>Vamshi Verma</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://montemagno.com/"><img src="https://avatars.githubusercontent.com/u/1676321?v=4" width="100px;" alt=""/><br /><sub><b>James Montemagno</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://twitter.com/alefragnani"><img src="https://avatars.githubusercontent.com/u/3781424?v=4" width="100px;" alt=""/><br /><sub><b>Alessandro Fragnani</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/ambilykk/"><img src="https://avatars.githubusercontent.com/u/10282550?v=4" width="100px;" alt=""/><br /><sub><b>Ambily</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/krushideep"><img src="https://avatars.githubusercontent.com/u/174652083?v=4" width="100px;" alt=""/><br /><sub><b>krushideep</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mihsoft"><img src="https://avatars.githubusercontent.com/u/53946345?v=4" width="100px;" alt=""/><br /><sub><b>devopsfan</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://tgrall.github.io/"><img src="https://avatars.githubusercontent.com/u/541250?v=4" width="100px;" alt=""/><br /><sub><b>Tugdual Grall</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/OrenMe"><img src="https://avatars.githubusercontent.com/u/5461862?v=4" width="100px;" alt=""/><br /><sub><b>Oren Me</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mjrousos"><img src="https://avatars.githubusercontent.com/u/10077254?v=4" width="100px;" alt=""/><br /><sub><b>Mike Rousos</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://devkimchi.com/"><img src="https://avatars.githubusercontent.com/u/1538528?v=4" width="100px;" alt=""/><br /><sub><b>Justin Yoo</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/guiopen"><img src="https://avatars.githubusercontent.com/u/94094527?v=4" width="100px;" alt=""/><br /><sub><b>Guilherme do Amaral Alves </b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/griffinashe/"><img src="https://avatars.githubusercontent.com/u/6391612?v=4" width="100px;" alt=""/><br /><sub><b>Griffin Ashe</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/anchildress1"><img src="https://avatars.githubusercontent.com/u/6563688?v=4" width="100px;" alt=""/><br /><sub><b>Ashley Childress</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://www.senseof.tech/"><img src="https://avatars.githubusercontent.com/u/50712277?v=4" width="100px;" alt=""/><br /><sub><b>Adrien Clerbois</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Vhivi"><img src="https://avatars.githubusercontent.com/u/38220028?v=4" width="100px;" alt=""/><br /><sub><b>ANGELELLI David</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://markdav.is/"><img src="https://avatars.githubusercontent.com/u/311063?v=4" width="100px;" alt=""/><br /><sub><b>Mark Davis</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MattVevang"><img src="https://avatars.githubusercontent.com/u/20714898?v=4" width="100px;" alt=""/><br /><sub><b>Matt Vevang</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://max.irro.at/"><img src="https://avatars.githubusercontent.com/u/589073?v=4" width="100px;" alt=""/><br /><sub><b>Maximilian Irro</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/nullchimp"><img src="https://avatars.githubusercontent.com/u/58362593?v=4" width="100px;" alt=""/><br /><sub><b>NULLchimp</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pkarda"><img src="https://avatars.githubusercontent.com/u/12649718?v=4" width="100px;" alt=""/><br /><sub><b>Peter Karda</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sdolgin"><img src="https://avatars.githubusercontent.com/u/576449?v=4" width="100px;" alt=""/><br /><sub><b>Saul Dolgin</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/shubham070"><img src="https://avatars.githubusercontent.com/u/5480589?v=4" width="100px;" alt=""/><br /><sub><b>Shubham Gaikwad</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/TheovanKraay"><img src="https://avatars.githubusercontent.com/u/24420698?v=4" width="100px;" alt=""/><br /><sub><b>Theo van Kraay</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/TianqiZhang"><img src="https://avatars.githubusercontent.com/u/5326582?v=4" width="100px;" alt=""/><br /><sub><b>Tianqi Zhang</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://blog.miniasp.com/"><img src="https://avatars.githubusercontent.com/u/88981?v=4" width="100px;" alt=""/><br /><sub><b>Will 保哥</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://tsubalog.hatenablog.com/"><img src="https://avatars.githubusercontent.com/u/1592808?v=4" width="100px;" alt=""/><br /><sub><b>Yuta Matsumura</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/anschnapp"><img src="https://avatars.githubusercontent.com/u/17565996?v=4" width="100px;" alt=""/><br /><sub><b>anschnapp</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hizahizi-hizumi"><img src="https://avatars.githubusercontent.com/u/163728895?v=4" width="100px;" alt=""/><br /><sub><b>hizahizi-hizumi</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://jianminhuang.cc/"><img src="https://avatars.githubusercontent.com/u/6296280?v=4" width="100px;" alt=""/><br /><sub><b>黃健旻 Vincent Huang</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/brunoborges"><img src="https://avatars.githubusercontent.com/u/129743?v=4" width="100px;" alt=""/><br /><sub><b>Bruno Borges</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.movinglive.ca/"><img src="https://avatars.githubusercontent.com/u/14792628?v=4" width="100px;" alt=""/><br /><sub><b>Steve Magne</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://shaneneuville.com/"><img src="https://avatars.githubusercontent.com/u/5375137?v=4" width="100px;" alt=""/><br /><sub><b>Shane Neuville</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://asilva.dev/"><img src="https://avatars.githubusercontent.com/u/2493377?v=4" width="100px;" alt=""/><br /><sub><b>André Silva</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/agreaves-ms"><img src="https://avatars.githubusercontent.com/u/111466195?v=4" width="100px;" alt=""/><br /><sub><b>Allen Greaves</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/AmeliaRose802"><img src="https://avatars.githubusercontent.com/u/26167931?v=4" width="100px;" alt=""/><br /><sub><b>Amelia Payne</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/BBoyBen"><img src="https://avatars.githubusercontent.com/u/34445365?v=4" width="100px;" alt=""/><br /><sub><b>BBoyBen</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://azureincubations.io/"><img src="https://avatars.githubusercontent.com/u/45323234?v=4" width="100px;" alt=""/><br /><sub><b>Brooke Hamilton</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/GeekTrainer"><img src="https://avatars.githubusercontent.com/u/6109729?v=4" width="100px;" alt=""/><br /><sub><b>Christopher Harrison</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/breakid"><img src="https://avatars.githubusercontent.com/u/1446918?v=4" width="100px;" alt=""/><br /><sub><b>Dan</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://blog.codewithdan.com/"><img src="https://avatars.githubusercontent.com/u/1767249?v=4" width="100px;" alt=""/><br /><sub><b>Dan Wahlin</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://debbie.codes/"><img src="https://avatars.githubusercontent.com/u/13063165?v=4" width="100px;" alt=""/><br /><sub><b>Debbie O'Brien</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/echarrod"><img src="https://avatars.githubusercontent.com/u/1381991?v=4" width="100px;" alt=""/><br /><sub><b>Ed Harrod</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://learn.microsoft.com/dotnet"><img src="https://avatars.githubusercontent.com/u/24882762?v=4" width="100px;" alt=""/><br /><sub><b>Genevieve Warren</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/guigui42"><img src="https://avatars.githubusercontent.com/u/2376010?v=4" width="100px;" alt=""/><br /><sub><b>Guillaume</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/riqueufmg"><img src="https://avatars.githubusercontent.com/u/108551585?v=4" width="100px;" alt=""/><br /><sub><b>Henrique Nunes</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jeremiah-snee-openx"><img src="https://avatars.githubusercontent.com/u/113928685?v=4" width="100px;" alt=""/><br /><sub><b>Jeremiah Snee</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kartikdhiman"><img src="https://avatars.githubusercontent.com/u/59189590?v=4" width="100px;" alt=""/><br /><sub><b>Kartik Dhiman</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://kristiyanvelkov.com/"><img src="https://avatars.githubusercontent.com/u/40764277?v=4" width="100px;" alt=""/><br /><sub><b>Kristiyan Velkov</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/msalaman"><img src="https://avatars.githubusercontent.com/u/28122166?v=4" width="100px;" alt=""/><br /><sub><b>msalaman</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://soderlind.no/"><img src="https://avatars.githubusercontent.com/u/1649452?v=4" width="100px;" alt=""/><br /><sub><b>Per Søderlind</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://dotneteers.net/"><img src="https://avatars.githubusercontent.com/u/28162552?v=4" width="100px;" alt=""/><br /><sub><b>Peter Smulovics</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/madvimer"><img src="https://avatars.githubusercontent.com/u/3188898?v=4" width="100px;" alt=""/><br /><sub><b>Ravish Rathod</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ricksmit3000"><img src="https://avatars.githubusercontent.com/u/7207783?v=4" width="100px;" alt=""/><br /><sub><b>Rick Smit</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pertrai1"><img src="https://avatars.githubusercontent.com/u/442374?v=4" width="100px;" alt=""/><br /><sub><b>Rob Simpson</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/inquinity"><img src="https://avatars.githubusercontent.com/u/406234?v=4" width="100px;" alt=""/><br /><sub><b>Robert Altman</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://salih.guru/"><img src="https://avatars.githubusercontent.com/u/76786120?v=4" width="100px;" alt=""/><br /><sub><b>Salih</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://graef.io/"><img src="https://avatars.githubusercontent.com/u/19261257?v=4" width="100px;" alt=""/><br /><sub><b>Sebastian Gräf</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/SebastienDegodez"><img src="https://avatars.githubusercontent.com/u/2349146?v=4" width="100px;" alt=""/><br /><sub><b>Sebastien DEGODEZ</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sesmyrnov"><img src="https://avatars.githubusercontent.com/u/59627981?v=4" width="100px;" alt=""/><br /><sub><b>Sergiy Smyrnov</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/SomeSolutionsArchitect"><img src="https://avatars.githubusercontent.com/u/139817767?v=4" width="100px;" alt=""/><br /><sub><b>SomeSolutionsArchitect</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kewalaka"><img src="https://avatars.githubusercontent.com/u/3146590?v=4" width="100px;" alt=""/><br /><sub><b>Stu Mace</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/STRUDSO"><img src="https://avatars.githubusercontent.com/u/1543732?v=4" width="100px;" alt=""/><br /><sub><b>Søren Trudsø Mahon</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://enakdesign.com/"><img src="https://avatars.githubusercontent.com/u/14024037?v=4" width="100px;" alt=""/><br /><sub><b>Tj Vita</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pelikhan"><img src="https://avatars.githubusercontent.com/u/4175913?v=4" width="100px;" alt=""/><br /><sub><b>Peli de Halleux</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.paulomorgado.net/"><img src="https://avatars.githubusercontent.com/u/470455?v=4" width="100px;" alt=""/><br /><sub><b>Paulo Morgado</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://paul.crane.net.nz/"><img src="https://avatars.githubusercontent.com/u/808676?v=4" width="100px;" alt=""/><br /><sub><b>Paul Crane</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.pamelafox.org/"><img src="https://avatars.githubusercontent.com/u/297042?v=4" width="100px;" alt=""/><br /><sub><b>Pamela Fox</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://oskarthornblad.se/"><img src="https://avatars.githubusercontent.com/u/640102?v=4" width="100px;" alt=""/><br /><sub><b>Oskar Thornblad</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/nischays"><img src="https://avatars.githubusercontent.com/u/54121853?v=4" width="100px;" alt=""/><br /><sub><b>Nischay Sharma</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Naikabg"><img src="https://avatars.githubusercontent.com/u/19915620?v=4" width="100px;" alt=""/><br /><sub><b>Nikolay Marinov</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/niksac"><img src="https://avatars.githubusercontent.com/u/20246918?v=4" width="100px;" alt=""/><br /><sub><b>Nik Sachdeva</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://onetipaweek.com/"><img src="https://avatars.githubusercontent.com/u/833231?v=4" width="100px;" alt=""/><br /><sub><b>Nick Taylor</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://nicholasdbrady.github.io/cookbook/"><img src="https://avatars.githubusercontent.com/u/18353756?v=4" width="100px;" alt=""/><br /><sub><b>Nick Brady</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/nastanford"><img src="https://avatars.githubusercontent.com/u/1755947?v=4" width="100px;" alt=""/><br /><sub><b>Nathan Stanford Sr</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/matebarabas"><img src="https://avatars.githubusercontent.com/u/22733424?v=4" width="100px;" alt=""/><br /><sub><b>Máté Barabás</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mikeparker104"><img src="https://avatars.githubusercontent.com/u/12763221?v=4" width="100px;" alt=""/><br /><sub><b>Mike Parker</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mikekistler"><img src="https://avatars.githubusercontent.com/u/85643503?v=4" width="100px;" alt=""/><br /><sub><b>Mike Kistler</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/giomartinsdev"><img src="https://avatars.githubusercontent.com/u/125399281?v=4" width="100px;" alt=""/><br /><sub><b>Giovanni de Almeida Martins</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dgh06175"><img src="https://avatars.githubusercontent.com/u/77305722?v=4" width="100px;" alt=""/><br /><sub><b>이상현</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zooav"><img src="https://avatars.githubusercontent.com/u/12625412?v=4" width="100px;" alt=""/><br /><sub><b>Ankur Sharma</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/webreidi"><img src="https://avatars.githubusercontent.com/u/55603905?v=4" width="100px;" alt=""/><br /><sub><b>Wendy Breiding</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/voidfnc"><img src="https://avatars.githubusercontent.com/u/194750710?v=4" width="100px;" alt=""/><br /><sub><b>voidfnc</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://about.me/shane-lee"><img src="https://avatars.githubusercontent.com/u/5466825?v=4" width="100px;" alt=""/><br /><sub><b>shane lee</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sdanzo-hrb"><img src="https://avatars.githubusercontent.com/u/136493100?v=4" width="100px;" alt=""/><br /><sub><b>sdanzo-hrb</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/nativebpm"><img src="https://avatars.githubusercontent.com/u/33398121?v=4" width="100px;" alt=""/><br /><sub><b>sauran</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/samqbush"><img src="https://avatars.githubusercontent.com/u/74389839?v=4" width="100px;" alt=""/><br /><sub><b>samqbush</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pareenaverma"><img src="https://avatars.githubusercontent.com/u/59843121?v=4" width="100px;" alt=""/><br /><sub><b>pareenaverma</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/oleksiyyurchyna"><img src="https://avatars.githubusercontent.com/u/10256765?v=4" width="100px;" alt=""/><br /><sub><b>oleksiyyurchyna</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/time-by-waves"><img src="https://avatars.githubusercontent.com/u/34587654?v=4" width="100px;" alt=""/><br /><sub><b>oceans-of-time</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kshashank57"><img src="https://avatars.githubusercontent.com/u/57212456?v=4" width="100px;" alt=""/><br /><sub><b>kshashank57</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hueanmy"><img src="https://avatars.githubusercontent.com/u/20430626?v=4" width="100px;" alt=""/><br /><sub><b>Meii</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/factory-davidgu"><img src="https://avatars.githubusercontent.com/u/229352262?v=4" width="100px;" alt=""/><br /><sub><b>factory-davidgu</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dangelov-qa"><img src="https://avatars.githubusercontent.com/u/92313553?v=4" width="100px;" alt=""/><br /><sub><b>dangelov-qa</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/BenoitMaucotel"><img src="https://avatars.githubusercontent.com/u/54392431?v=4" width="100px;" alt=""/><br /><sub><b>BenoitMaucotel</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/benjisho-aidome"><img src="https://avatars.githubusercontent.com/u/218995725?v=4" width="100px;" alt=""/><br /><sub><b>benjisho-aidome</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yukiomoto"><img src="https://avatars.githubusercontent.com/u/38450410?v=4" width="100px;" alt=""/><br /><sub><b>Yuki Omoto</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wschultz-boxboat"><img src="https://avatars.githubusercontent.com/u/110492948?v=4" width="100px;" alt=""/><br /><sub><b>Will Schultz</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://waren.build"><img src="https://avatars.githubusercontent.com/u/15052701?v=4" width="100px;" alt=""/><br /><sub><b>Waren Gonzaga</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://linktr.ee/vincentkoc"><img src="https://avatars.githubusercontent.com/u/25068?v=4" width="100px;" alt=""/><br /><sub><b>Vincent Koc</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Vaporjawn"><img src="https://avatars.githubusercontent.com/u/15694665?v=4" width="100px;" alt=""/><br /><sub><b>Victor Williams</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://vesharma.dev/"><img src="https://avatars.githubusercontent.com/u/62218708?v=4" width="100px;" alt=""/><br /><sub><b>Ve Sharma</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.ferryhopper.com/"><img src="https://avatars.githubusercontent.com/u/19361558?v=4" width="100px;" alt=""/><br /><sub><b>Vasileios Lahanas</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://tinyurl.com/3p5j9mwe"><img src="https://avatars.githubusercontent.com/u/9591887?v=4" width="100px;" alt=""/><br /><sub><b>Udaya Veeramreddygari</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/iletai"><img src="https://avatars.githubusercontent.com/u/26614687?v=4" width="100px;" alt=""/><br /><sub><b>Tài Lê</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://tsubasaogawa.me/"><img src="https://avatars.githubusercontent.com/u/7788821?v=4" width="100px;" alt=""/><br /><sub><b>Tsubasa Ogawa</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://glsauto.com/"><img src="https://avatars.githubusercontent.com/u/132710946?v=4" width="100px;" alt=""/><br /><sub><b>Troy Witthoeft (glsauto)</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://jfversluis.dev/"><img src="https://avatars.githubusercontent.com/u/939291?v=4" width="100px;" alt=""/><br /><sub><b>Gerald Versluis</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/geoder101"><img src="https://avatars.githubusercontent.com/u/145904?v=4" width="100px;" alt=""/><br /><sub><b>George Dernikos</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/gautambaghel"><img src="https://avatars.githubusercontent.com/u/22324290?v=4" width="100px;" alt=""/><br /><sub><b>Gautam</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/feapaydin"><img src="https://avatars.githubusercontent.com/u/19946639?v=4" width="100px;" alt=""/><br /><sub><b>Furkan Enes</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fmuecke"><img src="https://avatars.githubusercontent.com/u/7921024?v=4" width="100px;" alt=""/><br /><sub><b>Florian Mücke</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.felixarjuna.dev/"><img src="https://avatars.githubusercontent.com/u/79026094?v=4" width="100px;" alt=""/><br /><sub><b>Felix Arjuna</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ewega"><img src="https://avatars.githubusercontent.com/u/26189114?v=4" width="100px;" alt=""/><br /><sub><b>Eldrick Wega</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/danchev"><img src="https://avatars.githubusercontent.com/u/12420863?v=4" width="100px;" alt=""/><br /><sub><b>Dobri Danchev</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://dgamboa.com/"><img src="https://avatars.githubusercontent.com/u/7052267?v=4" width="100px;" alt=""/><br /><sub><b>Diego Gamboa</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/derekclair"><img src="https://avatars.githubusercontent.com/u/5247629?v=4" width="100px;" alt=""/><br /><sub><b>Derek Clair</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://dev.to/davidortinau"><img src="https://avatars.githubusercontent.com/u/41873?v=4" width="100px;" alt=""/><br /><sub><b>David Ortinau</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/danielabbatt"><img src="https://avatars.githubusercontent.com/u/8926756?v=4" width="100px;" alt=""/><br /><sub><b>Daniel Abbatt</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/CypherHK"><img src="https://avatars.githubusercontent.com/u/230935834?v=4" width="100px;" alt=""/><br /><sub><b>CypherHK</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/craigbekker"><img src="https://avatars.githubusercontent.com/u/1115912?v=4" width="100px;" alt=""/><br /><sub><b>Craig Bekker</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.peug.net/"><img src="https://avatars.githubusercontent.com/u/3845786?v=4" width="100px;" alt=""/><br /><sub><b>Christophe Peugnet</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lechnerc77"><img src="https://avatars.githubusercontent.com/u/22294087?v=4" width="100px;" alt=""/><br /><sub><b>Christian Lechner</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/charris-msft"><img src="https://avatars.githubusercontent.com/u/74415662?v=4" width="100px;" alt=""/><br /><sub><b>Chris Harris</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/artemsaveliev"><img src="https://avatars.githubusercontent.com/u/15679218?v=4" width="100px;" alt=""/><br /><sub><b>Artem Saveliev</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://javaetmoi.com/"><img src="https://avatars.githubusercontent.com/u/838318?v=4" width="100px;" alt=""/><br /><sub><b>Antoine Rey</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/PiKa919"><img src="https://avatars.githubusercontent.com/u/96786190?v=4" width="100px;" alt=""/><br /><sub><b>Ankit Das</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/alineavila"><img src="https://avatars.githubusercontent.com/u/24813256?v=4" width="100px;" alt=""/><br /><sub><b>Aline Ávila</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/martin-cod"><img src="https://avatars.githubusercontent.com/u/33550246?v=4" width="100px;" alt=""/><br /><sub><b>Alexander Martinkevich</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aldunchev"><img src="https://avatars.githubusercontent.com/u/4631021?v=4" width="100px;" alt=""/><br /><sub><b>Aleksandar Dunchev</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://www.qreate.it/"><img src="https://avatars.githubusercontent.com/u/1868590?v=4" width="100px;" alt=""/><br /><sub><b>Alan Sprecacenere</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/akashxlr8"><img src="https://avatars.githubusercontent.com/u/58072860?v=4" width="100px;" alt=""/><br /><sub><b>Akash Kumar Shaw</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/abdidaudpropel"><img src="https://avatars.githubusercontent.com/u/51310019?v=4" width="100px;" alt=""/><br /><sub><b>Abdi Daud</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/AIAlchemyForge"><img src="https://avatars.githubusercontent.com/u/253636689?v=4" width="100px;" alt=""/><br /><sub><b>AIAlchemyForge</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/4regab"><img src="https://avatars.githubusercontent.com/u/178603515?v=4" width="100px;" alt=""/><br /><sub><b>4regab</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MiguelElGallo"><img src="https://avatars.githubusercontent.com/u/60221874?v=4" width="100px;" alt=""/><br /><sub><b>Miguel P Z</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://a11ysupport.io/"><img src="https://avatars.githubusercontent.com/u/498678?v=4" width="100px;" alt=""/><br /><sub><b>Michael Fairchild</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/michael-volz/"><img src="https://avatars.githubusercontent.com/u/129928?v=4" width="100px;" alt=""/><br /><sub><b>Michael A. Volz (Flynn)</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Mike-Hanna"><img src="https://avatars.githubusercontent.com/u/50142889?v=4" width="100px;" alt=""/><br /><sub><b>Michael</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mehmetalierol"><img src="https://avatars.githubusercontent.com/u/16721723?v=4" width="100px;" alt=""/><br /><sub><b>Mehmet Ali EROL</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://maxprilutskiy.com/"><img src="https://avatars.githubusercontent.com/u/5614659?v=4" width="100px;" alt=""/><br /><sub><b>Max Prilutskiy</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mbianchidev"><img src="https://avatars.githubusercontent.com/u/37507190?v=4" width="100px;" alt=""/><br /><sub><b>Matteo Bianchi</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://marknoble.com/"><img src="https://avatars.githubusercontent.com/u/3819700?v=4" width="100px;" alt=""/><br /><sub><b>Mark Noble</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ManishJayaswal"><img src="https://avatars.githubusercontent.com/u/9527491?v=4" width="100px;" alt=""/><br /><sub><b>Manish Jayaswal</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://linktr.ee/lukemurray"><img src="https://avatars.githubusercontent.com/u/24467442?v=4" width="100px;" alt=""/><br /><sub><b>Luke Murray</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LouellaCreemers"><img src="https://avatars.githubusercontent.com/u/46204894?v=4" width="100px;" alt=""/><br /><sub><b>Louella Creemers</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/saikoumudi"><img src="https://avatars.githubusercontent.com/u/22682497?v=4" width="100px;" alt=""/><br /><sub><b>Sai Koumudi Kaluvakolanu</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/whiteken"><img src="https://avatars.githubusercontent.com/u/20211937?v=4" width="100px;" alt=""/><br /><sub><b>Kenny White</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/KaloyanGenev"><img src="https://avatars.githubusercontent.com/u/42644424?v=4" width="100px;" alt=""/><br /><sub><b>KaloyanGenev</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Ranrar"><img src="https://avatars.githubusercontent.com/u/95967772?v=4" width="100px;" alt=""/><br /><sub><b>Kim Skov Rasmussen</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.julien-dubois.com/"><img src="https://avatars.githubusercontent.com/u/316835?v=4" width="100px;" alt=""/><br /><sub><b>Julien Dubois</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://digio.es/"><img src="https://avatars.githubusercontent.com/u/173672918?v=4" width="100px;" alt=""/><br /><sub><b>José Antonio Garrido</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/josephgonzales01"><img src="https://avatars.githubusercontent.com/u/15100839?v=4" width="100px;" alt=""/><br /><sub><b>Joseph Gonzales</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yortch"><img src="https://avatars.githubusercontent.com/u/4576246?v=4" width="100px;" alt=""/><br /><sub><b>Jorge Balderas</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://johnpapa.net/"><img src="https://avatars.githubusercontent.com/u/1202528?v=4" width="100px;" alt=""/><br /><sub><b>John Papa</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.johnlokerse.dev/"><img src="https://avatars.githubusercontent.com/u/3514513?v=4" width="100px;" alt=""/><br /><sub><b>John</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/joe-watkins"><img src="https://avatars.githubusercontent.com/u/3695795?v=4" width="100px;" alt=""/><br /><sub><b>Joe Watkins</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://jan-v.nl/"><img src="https://avatars.githubusercontent.com/u/462356?v=4" width="100px;" alt=""/><br /><sub><b>Jan de Vries</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/nohwnd"><img src="https://avatars.githubusercontent.com/u/5735905?v=4" width="100px;" alt=""/><br /><sub><b>Jakub Jareš</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jaxn"><img src="https://avatars.githubusercontent.com/u/29095?v=4" width="100px;" alt=""/><br /><sub><b>Jackson Miller</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Ioana37"><img src="https://avatars.githubusercontent.com/u/69301842?v=4" width="100px;" alt=""/><br /><sub><b>Ioana A</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hunterhogan"><img src="https://avatars.githubusercontent.com/u/2958419?v=4" width="100px;" alt=""/><br /><sub><b>Hunter Hogan</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hashimwarren"><img src="https://avatars.githubusercontent.com/u/6027587?v=4" width="100px;" alt=""/><br /><sub><b>Hashim Warren</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Arggon"><img src="https://avatars.githubusercontent.com/u/20962238?v=4" width="100px;" alt=""/><br /><sub><b>Gonzalo</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://hachyderm.io/@0gis0"><img src="https://avatars.githubusercontent.com/u/175379?v=4" width="100px;" alt=""/><br /><sub><b>Gisela Torres</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/shibicr93"><img src="https://avatars.githubusercontent.com/u/6803434?v=4" width="100px;" alt=""/><br /><sub><b>Shibi Ramachandran</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lupritz"><img src="https://avatars.githubusercontent.com/u/145381941?v=4" width="100px;" alt=""/><br /><sub><b>lupritz</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/bhect0"><img src="https://avatars.githubusercontent.com/u/96436904?v=4" width="100px;" alt=""/><br /><sub><b>Héctor Benedicte</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tedvilutis"><img src="https://avatars.githubusercontent.com/u/69260340?v=4" width="100px;" alt=""/><br /><sub><b>Ted Vilutis</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://tonybaloney.github.io/"><img src="https://avatars.githubusercontent.com/u/1532417?v=4" width="100px;" alt=""/><br /><sub><b>Anthony Shaw</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ChrisMcKee1"><img src="https://avatars.githubusercontent.com/u/25754153?v=4" width="100px;" alt=""/><br /><sub><b>Chris McKee</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/CASTResearchLabs"><img src="https://avatars.githubusercontent.com/u/23238546?v=4" width="100px;" alt=""/><br /><sub><b>CASTResearchLabs</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jun-shiromizu"><img src="https://avatars.githubusercontent.com/u/211425548?v=4" width="100px;" alt=""/><br /><sub><b>白水淳</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://imransiddique.com/"><img src="https://avatars.githubusercontent.com/u/45405841?v=4" width="100px;" alt=""/><br /><sub><b>Imran Siddique</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/nblog"><img src="https://avatars.githubusercontent.com/u/10218627?v=4" width="100px;" alt=""/><br /><sub><b>共产主义接班人</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/av"><img src="https://avatars.githubusercontent.com/u/38184623?v=4" width="100px;" alt=""/><br /><sub><b>Ivan Charapanau</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/labudis"><img src="https://avatars.githubusercontent.com/u/2659733?v=4" width="100px;" alt=""/><br /><sub><b>Tadas Labudis</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.alvinashcraft.com/"><img src="https://avatars.githubusercontent.com/u/73072?v=4" width="100px;" alt=""/><br /><sub><b>Alvin Ashcraft</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://docs.microsoft.com/en-us/archive/blogs/jankrivanek/"><img src="https://avatars.githubusercontent.com/u/3809076?v=4" width="100px;" alt=""/><br /><sub><b>Jan Krivanek</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/DUBSOpenHub"><img src="https://avatars.githubusercontent.com/u/158339470?v=4" width="100px;" alt=""/><br /><sub><b>Gregg Cochran</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Jcardif"><img src="https://avatars.githubusercontent.com/u/29174946?v=4" width="100px;" alt=""/><br /><sub><b>Josh N</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.ianzhang.cn/"><img src="https://avatars.githubusercontent.com/u/3264250?v=4" width="100px;" alt=""/><br /><sub><b>ian zhang</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.garrettsiegel.com/"><img src="https://avatars.githubusercontent.com/u/46652519?v=4" width="100px;" alt=""/><br /><sub><b>Garrett Siegel</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/v-rperez030"><img src="https://avatars.githubusercontent.com/u/248766827?v=4" width="100px;" alt=""/><br /><sub><b>Roberto Perez</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dvelton"><img src="https://avatars.githubusercontent.com/u/48307985?v=4" width="100px;" alt=""/><br /><sub><b>Dan Velton</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://leereilly.net/"><img src="https://avatars.githubusercontent.com/u/121322?v=4" width="100px;" alt=""/><br /><sub><b>Lee Reilly</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://bunnybox.info/"><img src="https://avatars.githubusercontent.com/u/743743?v=4" width="100px;" alt=""/><br /><sub><b>Daniel Coelho</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/vfaraji89"><img src="https://avatars.githubusercontent.com/u/62544375?v=4" width="100px;" alt=""/><br /><sub><b>Vahid Faraji</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/ashleywolf/"><img src="https://avatars.githubusercontent.com/u/10735907?v=4" width="100px;" alt=""/><br /><sub><b>Ashley Wolf</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://noahjenkins.com/"><img src="https://avatars.githubusercontent.com/u/41129202?v=4" width="100px;" alt=""/><br /><sub><b>Noah Jenkins</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jeremykohn"><img src="https://avatars.githubusercontent.com/u/5316595?v=4" width="100px;" alt=""/><br /><sub><b>Jeremy Kohn</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Hakku"><img src="https://avatars.githubusercontent.com/u/5256151?v=4" width="100px;" alt=""/><br /><sub><b>Harri Sipola</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://torumakabe.github.io/"><img src="https://avatars.githubusercontent.com/u/993850?v=4" width="100px;" alt=""/><br /><sub><b>Toru Makabe</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/delee03"><img src="https://avatars.githubusercontent.com/u/202738606?v=4" width="100px;" alt=""/><br /><sub><b>Pham Tien Thuan Phat</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/benjisho"><img src="https://avatars.githubusercontent.com/u/97973081?v=4" width="100px;" alt=""/><br /><sub><b>Benji Shohet</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://about.me/amauryleve"><img src="https://avatars.githubusercontent.com/u/11340282?v=4" width="100px;" alt=""/><br /><sub><b>Amaury Levé</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://timdeschryver.dev/"><img src="https://avatars.githubusercontent.com/u/28659384?v=4" width="100px;" alt=""/><br /><sub><b>Tim Deschryver</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/AlahmadiQ8"><img src="https://avatars.githubusercontent.com/u/3461501?v=4" width="100px;" alt=""/><br /><sub><b>Mohammad Asad Alahmadi</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://aka.readspeak.cn/app"><img src="https://avatars.githubusercontent.com/u/22270677?v=4" width="100px;" alt=""/><br /><sub><b>fondoger</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://linktr.ee/yuvai"><img src="https://avatars.githubusercontent.com/u/48050809?v=4" width="100px;" alt=""/><br /><sub><b>Yuval Avidani</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://querypanel.io/"><img src="https://avatars.githubusercontent.com/u/7916051?v=4" width="100px;" alt=""/><br /><sub><b>Csaba Iváncza</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://timheuer.com/blog/"><img src="https://avatars.githubusercontent.com/u/4821?v=4" width="100px;" alt=""/><br /><sub><b>Tim Heuer</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lance2k"><img src="https://avatars.githubusercontent.com/u/38002304?v=4" width="100px;" alt=""/><br /><sub><b>lance2k</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://ag11.dev/"><img src="https://avatars.githubusercontent.com/u/20666190?v=4" width="100px;" alt=""/><br /><sub><b>Andrea Liliana Griffiths</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ajithraghavan"><img src="https://avatars.githubusercontent.com/u/37246967?v=4" width="100px;" alt=""/><br /><sub><b>Ajith Raghavan</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ninihen1"><img src="https://avatars.githubusercontent.com/u/123369259?v=4" width="100px;" alt=""/><br /><sub><b>Catherine Han</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://twitter.com/specialforest"><img src="https://avatars.githubusercontent.com/u/581410?v=4" width="100px;" alt=""/><br /><sub><b>Igor Shishkin</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/verdantburrito"><img src="https://avatars.githubusercontent.com/u/130576273?v=4" width="100px;" alt=""/><br /><sub><b>Burrito Verde</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jvanderwee"><img src="https://avatars.githubusercontent.com/u/3587922?v=4" width="100px;" alt=""/><br /><sub><b>Joseph Van der Wee</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://luizbon.com/"><img src="https://avatars.githubusercontent.com/u/292532?v=4" width="100px;" alt=""/><br /><sub><b>Luiz Bon</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://sanjay-rb.github.io/"><img src="https://avatars.githubusercontent.com/u/25894304?v=4" width="100px;" alt=""/><br /><sub><b>Sanjay Ramassery Babu</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/russrimm"><img src="https://avatars.githubusercontent.com/u/10841574?v=4" width="100px;" alt=""/><br /><sub><b>Russ Rimmerman [MSFT]</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rperez030"><img src="https://avatars.githubusercontent.com/u/38786330?v=4" width="100px;" alt=""/><br /><sub><b>Roberto Perez</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ShehabSherif0"><img src="https://avatars.githubusercontent.com/u/210266853?v=4" width="100px;" alt=""/><br /><sub><b>Shehab Sherif</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/beingsmit"><img src="https://avatars.githubusercontent.com/u/1781956?v=4" width="100px;" alt=""/><br /><sub><b>Smit Patel</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/StevenJV"><img src="https://avatars.githubusercontent.com/u/4377447?v=4" width="100px;" alt=""/><br /><sub><b>Steven Vore</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/subhashisbhowmikicpes"><img src="https://avatars.githubusercontent.com/u/233422801?v=4" width="100px;" alt=""/><br /><sub><b>Subhashis Bhowmik</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tlmii"><img src="https://avatars.githubusercontent.com/u/9613109?v=4" width="100px;" alt=""/><br /><sub><b>Tim Mulholland</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/niels9001"><img src="https://avatars.githubusercontent.com/u/9866362?v=4" width="100px;" alt=""/><br /><sub><b>Niels Laute</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://pasul.medium.com/"><img src="https://avatars.githubusercontent.com/u/8143332?v=4" width="100px;" alt=""/><br /><sub><b>Pavel Sulimau</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/PrimedPaul"><img src="https://avatars.githubusercontent.com/u/29710834?v=4" width="100px;" alt=""/><br /><sub><b>PrimedPaul</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/REAL-Madrid01"><img src="https://avatars.githubusercontent.com/u/65749290?v=4" width="100px;" alt=""/><br /><sub><b>Zhiqi Pu</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ramyashreeradix"><img src="https://avatars.githubusercontent.com/u/202798545?v=4" width="100px;" alt=""/><br /><sub><b>Ramyashree Shetty</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ZdaPhp"><img src="https://avatars.githubusercontent.com/u/15830419?v=4" width="100px;" alt=""/><br /><sub><b>ZdaPhp</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pigd0g"><img src="https://avatars.githubusercontent.com/u/16750317?v=4" width="100px;" alt=""/><br /><sub><b>pigd0g</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rahulbats"><img src="https://avatars.githubusercontent.com/u/627905?v=4" width="100px;" alt=""/><br /><sub><b>rahulbats</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/suyask-msft"><img src="https://avatars.githubusercontent.com/u/158708948?v=4" width="100px;" alt=""/><br /><sub><b>suyask-msft</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tagedeep"><img src="https://avatars.githubusercontent.com/u/43116939?v=4" width="100px;" alt=""/><br /><sub><b>tagedeep</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tinkeringDev"><img src="https://avatars.githubusercontent.com/u/31189972?v=4" width="100px;" alt=""/><br /><sub><b>tinkeringDev</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/travish"><img src="https://avatars.githubusercontent.com/u/169255?v=4" width="100px;" alt=""/><br /><sub><b>Travis Hill</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/utkarsh232005"><img src="https://avatars.githubusercontent.com/u/137105846?v=4" width="100px;" alt=""/><br /><sub><b>Utkarsh patrikar</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rbgmulmb"><img src="https://avatars.githubusercontent.com/u/27664402?v=4" width="100px;" alt=""/><br /><sub><b>Yauhen</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yiouli"><img src="https://avatars.githubusercontent.com/u/3508494?v=4" width="100px;" alt=""/><br /><sub><b>Yiou Li</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yukidukie"><img src="https://avatars.githubusercontent.com/u/38450410?v=4" width="100px;" alt=""/><br /><sub><b>Yuki Omoto</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/abhibavishi"><img src="https://avatars.githubusercontent.com/u/7823146?v=4" width="100px;" alt=""/><br /><sub><b>Abhi Bavishi</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/augustus-0"><img src="https://avatars.githubusercontent.com/u/113288678?v=4" width="100px;" alt=""/><br /><sub><b>augustus-0</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/codeHysteria28"><img src="https://avatars.githubusercontent.com/u/46035047?v=4" width="100px;" alt=""/><br /><sub><b>Branislav Buna</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/connerlambden"><img src="https://avatars.githubusercontent.com/u/9061871?v=4" width="100px;" alt=""/><br /><sub><b>connerlambden</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/DavidARaygoza"><img src="https://avatars.githubusercontent.com/u/100718117?v=4" width="100px;" alt=""/><br /><sub><b>David Raygoza</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dipievil"><img src="https://avatars.githubusercontent.com/u/5294742?v=4" width="100px;" alt=""/><br /><sub><b>Diego Porto Ritzel</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ericsche"><img src="https://avatars.githubusercontent.com/u/35633680?v=4" width="100px;" alt=""/><br /><sub><b>Eric Scherlinger</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fatihdurgut"><img src="https://avatars.githubusercontent.com/u/4159116?v=4" width="100px;" alt=""/><br /><sub><b>Fatih</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://blog.fujiy.net/"><img src="https://avatars.githubusercontent.com/u/1336227?v=4" width="100px;" alt=""/><br /><sub><b>Felipe Pessoto</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://medium.com/just-tech-it-now"><img src="https://avatars.githubusercontent.com/u/1039390?v=4" width="100px;" alt=""/><br /><sub><b>François</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/GeoffreyCasaubon"><img src="https://avatars.githubusercontent.com/u/790606?v=4" width="100px;" alt=""/><br /><sub><b>Geoffrey Casaubon</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Anddd7"><img src="https://avatars.githubusercontent.com/u/24785373?v=4" width="100px;" alt=""/><br /><sub><b>Anddd7</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/anderseide"><img src="https://avatars.githubusercontent.com/u/13043472?v=4" width="100px;" alt=""/><br /><sub><b>Anders Eide</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://aymenfurter.ch/"><img src="https://avatars.githubusercontent.com/u/20464460?v=4" width="100px;" alt=""/><br /><sub><b>Aymen</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://kvz.io/"><img src="https://avatars.githubusercontent.com/u/26752?v=4" width="100px;" alt=""/><br /><sub><b>Kevin van Zonneveld</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/luiscantero"><img src="https://avatars.githubusercontent.com/u/1353540?v=4" width="100px;" alt=""/><br /><sub><b>Luis Cantero</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mvkaran"><img src="https://avatars.githubusercontent.com/u/8726608?v=4" width="100px;" alt=""/><br /><sub><b>MV Karan</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Jugger23"><img src="https://avatars.githubusercontent.com/u/144260728?v=4" width="100px;" alt=""/><br /><sub><b>Marcel Deutzer</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://weblogs.asp.net/jongalloway"><img src="https://avatars.githubusercontent.com/u/68539?v=4" width="100px;" alt=""/><br /><sub><b>Jon Galloway</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://jlbeard.com/"><img src="https://avatars.githubusercontent.com/u/4313198?v=4" width="100px;" alt=""/><br /><sub><b>Josh Beard</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://jpinzer.me/"><img src="https://avatars.githubusercontent.com/u/8357054?v=4" width="100px;" alt=""/><br /><sub><b>Julian</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/simonkurtz-MSFT"><img src="https://avatars.githubusercontent.com/u/84809797?v=4" width="100px;" alt=""/><br /><sub><b>Simon Kurtz</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/TemitayoAfolabi"><img src="https://avatars.githubusercontent.com/u/108681158?v=4" width="100px;" alt=""/><br /><sub><b>Temitayo Afolabi</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/joeVenner"><img src="https://avatars.githubusercontent.com/u/61122897?v=4" width="100px;" alt=""/><br /><sub><b>JoeVenner</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pasindudilshan1"><img src="https://avatars.githubusercontent.com/u/146967638?v=4" width="100px;" alt=""/><br /><sub><b>Pasindu Premarathna</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ecosystem"><img src="https://avatars.githubusercontent.com/u/2956973?v=4" width="100px;" alt=""/><br /><sub><b>ecosystem</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/punit-fastah"><img src="https://avatars.githubusercontent.com/u/257566774?v=4" width="100px;" alt=""/><br /><sub><b>Punit</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.youtube.com/@cloudarch"><img src="https://avatars.githubusercontent.com/u/44020466?v=4" width="100px;" alt=""/><br /><sub><b>Onur Senturk</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://mastodon.social/@andrewstellman"><img src="https://avatars.githubusercontent.com/u/7516297?v=4" width="100px;" alt=""/><br /><sub><b>Andrew Stellman</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/jeonghlee8024"><img src="https://avatars.githubusercontent.com/u/18215249?v=4" width="100px;" alt=""/><br /><sub><b>Jeonghoon Lee</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://srisatyalokesh.is-a.dev/"><img src="https://avatars.githubusercontent.com/u/43106076?v=4" width="100px;" alt=""/><br /><sub><b>Satya K</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/samikroy"><img src="https://avatars.githubusercontent.com/u/20562985?v=4" width="100px;" alt=""/><br /><sub><b>Samik Roy</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/siminapasat"><img src="https://avatars.githubusercontent.com/u/16675781?v=4" width="100px;" alt=""/><br /><sub><b>Simina Pasat</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/garnertb"><img src="https://avatars.githubusercontent.com/u/1141646?v=4" width="100px;" alt=""/><br /><sub><b>Tyler Garner</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/cheguv"><img src="https://avatars.githubusercontent.com/u/21251550?v=4" width="100px;" alt=""/><br /><sub><b>Vijay Chegu</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/DTIBeograd"><img src="https://avatars.githubusercontent.com/u/30282550?v=4" width="100px;" alt=""/><br /><sub><b>DTIBeograd</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/behl1anmol"><img src="https://avatars.githubusercontent.com/u/37472462?v=4" width="100px;" alt=""/><br /><sub><b>Anmol Behl</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://aftermathtech.com/"><img src="https://avatars.githubusercontent.com/u/125813226?v=4" width="100px;" alt=""/><br /><sub><b>Brad Kinnard</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://felickz.github.io/"><img src="https://avatars.githubusercontent.com/u/1760475?v=4" width="100px;" alt=""/><br /><sub><b>Chad Bentz</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MarcelloCuoghi"><img src="https://avatars.githubusercontent.com/u/10816095?v=4" width="100px;" alt=""/><br /><sub><b>Marcello Cuoghi</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://josh-ops.com/"><img src="https://avatars.githubusercontent.com/u/19912012?v=4" width="100px;" alt=""/><br /><sub><b>Josh Johanning</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jennyf19"><img src="https://avatars.githubusercontent.com/u/19942418?v=4" width="100px;" alt=""/><br /><sub><b>jennyf19</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/SaravananRajaraman"><img src="https://avatars.githubusercontent.com/u/5166323?v=4" width="100px;" alt=""/><br /><sub><b>Saravanan Rajaraman</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Dhruvpatel004"><img src="https://avatars.githubusercontent.com/u/109230666?v=4" width="100px;" alt=""/><br /><sub><b>Patel Dhruv </b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/reneenoble"><img src="https://avatars.githubusercontent.com/u/7269759?v=4" width="100px;" alt=""/><br /><sub><b>Renee Noble</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jjpinto"><img src="https://avatars.githubusercontent.com/u/16046674?v=4" width="100px;" alt=""/><br /><sub><b>jjpinto</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://moeyui1.github.io/"><img src="https://avatars.githubusercontent.com/u/11503525?v=4" width="100px;" alt=""/><br /><sub><b>moeyui1</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mohammadali2549"><img src="https://avatars.githubusercontent.com/u/67632698?v=4" width="100px;" alt=""/><br /><sub><b>mohammadali2549</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/proflead"><img src="https://avatars.githubusercontent.com/u/59716480?v=4" width="100px;" alt=""/><br /><sub><b>Vladislav Guzey</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aparna198809"><img src="https://avatars.githubusercontent.com/u/99466930?v=4" width="100px;" alt=""/><br /><sub><b>aparna198809</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/TeddMcAdams"><img src="https://avatars.githubusercontent.com/u/15876990?v=4" width="100px;" alt=""/><br /><sub><b>Ed McAdams</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/eanders-tdy"><img src="https://avatars.githubusercontent.com/u/271782413?v=4" width="100px;" alt=""/><br /><sub><b>Emil Andersson</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://www.mikaelkrief.com/"><img src="https://avatars.githubusercontent.com/u/2725302?v=4" width="100px;" alt=""/><br /><sub><b>Mikael</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Mrigank005"><img src="https://avatars.githubusercontent.com/u/179711954?v=4" width="100px;" alt=""/><br /><sub><b>Mrigank Singh</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.jimbobbennett.dev/"><img src="https://avatars.githubusercontent.com/u/1710385?v=4" width="100px;" alt=""/><br /><sub><b>Jim Bennett</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Alishahzad1903"><img src="https://avatars.githubusercontent.com/u/94849277?v=4" width="100px;" alt=""/><br /><sub><b>Alishahzad1903</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/anvillan"><img src="https://avatars.githubusercontent.com/u/51379759?v=4" width="100px;" alt=""/><br /><sub><b>Antonio Villanueva</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://timhanewich.github.io/"><img src="https://avatars.githubusercontent.com/u/57418795?v=4" width="100px;" alt=""/><br /><sub><b>Tim Hanewich</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hddevteam"><img src="https://avatars.githubusercontent.com/u/14231255?v=4" width="100px;" alt=""/><br /><sub><b>ming</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.scottohara.me/"><img src="https://avatars.githubusercontent.com/u/4152514?v=4" width="100px;" alt=""/><br /><sub><b>Scott O'Hara</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://salih.guru/"><img src="https://avatars.githubusercontent.com/u/76786120?v=4" width="100px;" alt=""/><br /><sub><b>Salih</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/shaileshmishra1/"><img src="https://avatars.githubusercontent.com/u/50418172?v=4" width="100px;" alt=""/><br /><sub><b>Shailesh</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sjiyani"><img src="https://avatars.githubusercontent.com/u/89791048?v=4" width="100px;" alt=""/><br /><sub><b>Shubham Jiyani</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.thetechcruise.com/"><img src="https://avatars.githubusercontent.com/u/38348871?v=4" width="100px;" alt=""/><br /><sub><b>Srinivas Vaddi</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Philess"><img src="https://avatars.githubusercontent.com/u/10655866?v=4" width="100px;" alt=""/><br /><sub><b>Philippe D</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/goldirana"><img src="https://avatars.githubusercontent.com/u/43932117?v=4" width="100px;" alt=""/><br /><sub><b>Rajesh Goldy</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dstrupl"><img src="https://avatars.githubusercontent.com/u/4134230?v=4" width="100px;" alt=""/><br /><sub><b>dstrupl</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wuwen5"><img src="https://avatars.githubusercontent.com/u/5037807?v=4" width="100px;" alt=""/><br /><sub><b>wuwen</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tilakpatell"><img src="https://avatars.githubusercontent.com/u/108555753?v=4" width="100px;" alt=""/><br /><sub><b>Tilak Patel</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/vijay-kr-bandi"><img src="https://avatars.githubusercontent.com/u/7876511?v=4" width="100px;" alt=""/><br /><sub><b>Vijay Bandi</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.ahnu.edu.cn/"><img src="https://avatars.githubusercontent.com/u/219058658?v=4" width="100px;" alt=""/><br /><sub><b>Zixuan Jiang</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://www.dennislembree.com/"><img src="https://avatars.githubusercontent.com/u/473400?v=4" width="100px;" alt=""/><br /><sub><b>Dennis Lembree</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://omnificence.xyz/"><img src="https://avatars.githubusercontent.com/u/49101333?v=4" width="100px;" alt=""/><br /><sub><b>Dev Shah</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rs38"><img src="https://avatars.githubusercontent.com/u/12622612?v=4" width="100px;" alt=""/><br /><sub><b>Falco</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://ajenns.com/"><img src="https://avatars.githubusercontent.com/u/6963265?v=4" width="100px;" alt=""/><br /><sub><b>AJ</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Anush008"><img src="https://avatars.githubusercontent.com/u/46051506?v=4" width="100px;" alt=""/><br /><sub><b>Anush</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ayushsaklani-min"><img src="https://avatars.githubusercontent.com/u/221412911?v=4" width="100px;" alt=""/><br /><sub><b>Ayush Saklani</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://caarlos0.dev/"><img src="https://avatars.githubusercontent.com/u/245435?v=4" width="100px;" alt=""/><br /><sub><b>Carlos Alexandro Becker</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/shailendrahegde"><img src="https://avatars.githubusercontent.com/u/74619064?v=4" width="100px;" alt=""/><br /><sub><b>Mangokernel</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MarioCodes"><img src="https://avatars.githubusercontent.com/u/17473450?v=4" width="100px;" alt=""/><br /><sub><b>Mario Codes</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.flemingtechnologies.cl/"><img src="https://avatars.githubusercontent.com/u/5702027?v=4" width="100px;" alt=""/><br /><sub><b>Gonzalo Fleming</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.movinglive.ca/"><img src="https://avatars.githubusercontent.com/u/14792628?v=4" width="100px;" alt=""/><br /><sub><b>Steve Magne</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Sertxito"><img src="https://avatars.githubusercontent.com/u/25170262?v=4" width="100px;" alt=""/><br /><sub><b>Sertxito</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.zengliangyi.online/"><img src="https://avatars.githubusercontent.com/u/104827876?v=4" width="100px;" alt=""/><br /><sub><b>Rayner Zeng</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ilderaj"><img src="https://avatars.githubusercontent.com/u/6321440?v=4" width="100px;" alt=""/><br /><sub><b>ilderaj</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mvanderbend-msoft"><img src="https://avatars.githubusercontent.com/u/259659559?v=4" width="100px;" alt=""/><br /><sub><b>mvanderbend-msoft</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/parveen-dotnet"><img src="https://avatars.githubusercontent.com/u/226729782?v=4" width="100px;" alt=""/><br /><sub><b>Parveen Sharma</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pmorong"><img src="https://avatars.githubusercontent.com/u/10659855?v=4" width="100px;" alt=""/><br /><sub><b>pmorong</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/thevinodkumar"><img src="https://avatars.githubusercontent.com/u/42086653?v=4" width="100px;" alt=""/><br /><sub><b>vinod kumar</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://vidhartbhatia.com/"><img src="https://avatars.githubusercontent.com/u/23387006?v=4" width="100px;" alt=""/><br /><sub><b>Vidhart Bhatia</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/leonard520"><img src="https://avatars.githubusercontent.com/u/5118845?v=4" width="100px;" alt=""/><br /><sub><b>Xiaoyun Ding</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/denis-a-evdokimov"><img src="https://avatars.githubusercontent.com/u/19668152?v=4" width="100px;" alt=""/><br /><sub><b>denis-a-evdokimov</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://connexio.digital/"><img src="https://avatars.githubusercontent.com/u/17970602?v=4" width="100px;" alt=""/><br /><sub><b>Adriano Nogueira</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/AezanPathan"><img src="https://avatars.githubusercontent.com/u/110289332?v=4" width="100px;" alt=""/><br /><sub><b>Aezan</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/clubanderson"><img src="https://avatars.githubusercontent.com/u/407614?v=4" width="100px;" alt=""/><br /><sub><b>Andy Anderson</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://kwekudzata.vercel.app/dev"><img src="https://avatars.githubusercontent.com/u/148214043?v=4" width="100px;" alt=""/><br /><sub><b>Kweku Dzata</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/goodguy1963"><img src="https://avatars.githubusercontent.com/u/49859266?v=4" width="100px;" alt=""/><br /><sub><b>Marcel</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/navaneethreddydevops"><img src="https://avatars.githubusercontent.com/u/42119880?v=4" width="100px;" alt=""/><br /><sub><b>Navaneeth Reddy</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://jamesmcgrath.net/"><img src="https://avatars.githubusercontent.com/u/1762902?v=4" width="100px;" alt=""/><br /><sub><b>James</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jcountsNR"><img src="https://avatars.githubusercontent.com/u/94138069?v=4" width="100px;" alt=""/><br /><sub><b>Joseph Counts</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/NehaGitHubAcc"><img src="https://avatars.githubusercontent.com/u/60216366?v=4" width="100px;" alt=""/><br /><sub><b>Neha Mandge</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/srpatcha"><img src="https://avatars.githubusercontent.com/u/20883509?v=4" width="100px;" alt=""/><br /><sub><b>Srikanth Patchava</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://thomas-f-ray.art/"><img src="https://avatars.githubusercontent.com/u/74461863?v=4" width="100px;" alt=""/><br /><sub><b>Thomas Ray</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fizznix"><img src="https://avatars.githubusercontent.com/u/58569464?v=4" width="100px;" alt=""/><br /><sub><b>Nixon Kurian</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/petrsx"><img src="https://avatars.githubusercontent.com/u/18548253?v=4" width="100px;" alt=""/><br /><sub><b>Petr Stupka</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.pdebruin.org/"><img src="https://avatars.githubusercontent.com/u/4709852?v=4" width="100px;" alt=""/><br /><sub><b>Pieter de Bruin</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sudeepghatak"><img src="https://avatars.githubusercontent.com/u/12538280?v=4" width="100px;" alt=""/><br /><sub><b>sudeepghatak</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tlietz"><img src="https://avatars.githubusercontent.com/u/25965706?v=4" width="100px;" alt=""/><br /><sub><b>tlietz</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dawright22"><img src="https://avatars.githubusercontent.com/u/53329137?v=4" width="100px;" alt=""/><br /><sub><b>dawright22</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/alfersugo"><img src="https://avatars.githubusercontent.com/u/49598311?v=4" width="100px;" alt=""/><br /><sub><b>Alejandro Fernando Suarez Gomez</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://xquik.com/"><img src="https://avatars.githubusercontent.com/u/8755484?v=4" width="100px;" alt=""/><br /><sub><b>Burak Bayır</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://msamiullah.netlify.app/"><img src="https://avatars.githubusercontent.com/u/52650290?v=4" width="100px;" alt=""/><br /><sub><b>MUHAMMAD SAMIULLAH</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://metulev.com/"><img src="https://avatars.githubusercontent.com/u/711864?v=4" width="100px;" alt=""/><br /><sub><b>Nikola Metulev</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.colorado.edu/lab/krg"><img src="https://avatars.githubusercontent.com/u/4161712?v=4" width="100px;" alt=""/><br /><sub><b>Joseph Kasprzyk</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lovyjain"><img src="https://avatars.githubusercontent.com/u/54174168?v=4" width="100px;" alt=""/><br /><sub><b>Lovy Jain</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kimtth"><img src="https://avatars.githubusercontent.com/u/13846660?v=4" width="100px;" alt=""/><br /><sub><b>kimtth</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/AkashAi7"><img src="https://avatars.githubusercontent.com/u/46550108?v=4" width="100px;" alt=""/><br /><sub><b>Akash Dwivedi</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://surenk.com"><img src="https://avatars.githubusercontent.com/u/902972?v=4" width="100px;" alt=""/><br /><sub><b>Suren K</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.cloudblogger.eu"><img src="https://avatars.githubusercontent.com/u/53148138?v=4" width="100px;" alt=""/><br /><sub><b>Konstantinos Passadis | Azure MVP | MCT</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/alx-so"><img src="https://avatars.githubusercontent.com/u/8975621?v=4" width="100px;" alt=""/><br /><sub><b>Alex Sokol</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="http://blogs.gnome.org/jmatsuzawa/"><img src="https://avatars.githubusercontent.com/u/545426?v=4" width="100px;" alt=""/><br /><sub><b>Jiro Matsuzawa</b></sub></a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/GuoCheng24"><img src="https://avatars.githubusercontent.com/u/224264187?v=4" width="100px;" alt=""/><br /><sub><b>Guo Cheng</b></sub></a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/specialone0007"><img src="https://avatars.githubusercontent.com/u/105648710?v=4" width="100px;" alt=""/><br /><sub><b>Furkan Reha</b></sub></a></td>
</tr>
</tbody>
</table>
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
<p>
<img src="https://raw.githubusercontent.com/all-contributors/all-contributors-cli/1b8533af435da9854653492b1327a23a4dbd0a10/assets/logo-small.svg" alt="" />
<a href="https://all-contributors.js.org/docs/en/bot/usage">Add your contributions</a>
</p>
</div>
</div>
</div>
</div>
<style>
.page-content {
max-width: 72rem;
margin: 0 auto;
padding: 0 1rem 2rem;
}
.contributor-grid {
margin-top: 1rem;
}
/* Override Starlight table defaults and reshape as card grid */
.contributor-grid table {
display: block;
border: none;
width: 100% !important;
overflow: visible !important;
margin: 0;
}
.contributor-grid thead {
display: none;
}
.contributor-grid tbody {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
gap: 1rem;
}
.contributor-grid tr {
display: contents;
}
/* Each td becomes a card */
.contributor-grid td {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
text-align: center;
width: 100% !important;
padding: 1rem 0.5rem 0.75rem;
border-radius: 12px;
border: 1px solid var(--sl-color-gray-4);
background: var(--sl-color-bg);
transition: border-color 0.2s ease, box-shadow 0.2s ease;
gap: 0.15rem;
margin-top: 0;
}
.contributor-grid td:hover {
border-color: var(--sl-color-accent);
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
}
/* Hide <br> tags used by all-contributors for layout */
.contributor-grid td br {
display: none;
}
/* Profile link takes full width so emojis wrap to next row */
.contributor-grid td > a:first-child {
flex: 0 0 100%;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.4rem;
text-decoration: none;
color: inherit;
margin-bottom: 0.25rem;
}
/* Avatar images */
.contributor-grid td img {
border-radius: 50%;
width: 80px;
height: 80px;
object-fit: cover;
}
/* Name text */
.contributor-grid td sub {
font-size: 0.85rem;
line-height: 1.3;
}
.contributor-grid td sub b {
font-weight: 600;
color: var(--sl-color-text);
}
/* Contribution emoji links — flow inline */
.contributor-grid td > a:not(:first-child) {
display: inline !important;
text-decoration: none;
font-size: 0.95rem;
padding: 0.05rem;
opacity: 0.85;
transition: opacity 0.15s ease;
}
.contributor-grid td > a:not(:first-child):hover {
opacity: 1;
transform: scale(1.15);
}
/* Footer styling */
.contributor-grid tfoot {
display: block;
margin-top: 2rem;
}
.contributor-grid tfoot tr {
display: block;
}
.contributor-grid tfoot td {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 0.5rem;
border: none;
background: transparent;
font-size: 0.8rem;
color: var(--sl-color-gray-3);
padding: 0.75rem;
}
.contributor-grid tfoot td:hover {
border-color: transparent;
box-shadow: none;
}
.contributor-grid tfoot td img {
width: 20px;
height: 20px;
border-radius: 0;
}
.contributor-grid tfoot td a {
color: var(--sl-color-gray-2);
text-decoration: none;
font-size: 0.8rem;
}
.contributor-grid tfoot td a:hover {
color: var(--sl-color-accent);
}
/* Responsive: smaller screens */
@media (max-width: 480px) {
.contributor-grid tbody {
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
gap: 0.75rem;
}
.contributor-grid td {
padding: 0.75rem 0.5rem;
}
.contributor-grid td img {
width: 64px;
height: 64px;
}
.contributor-grid td sub b {
font-size: 0.78rem;
}
}
</style>
</StarlightPage>
<BaseLayout
title="Contributors | Awesome GitHub Copilot"
description="The people who build the Awesome GitHub Copilot community library."
>
<ContributorsPage
client:load
contributors={contributors}
searchIndex={searchIndex}
/>
</BaseLayout>
+111 -252
View File
@@ -1,292 +1,151 @@
---
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
import BackToTop from "../../components/BackToTop.astro";
import BaseLayout from "../../layouts/BaseLayout.astro";
import { ExtensionDetail } from "../../components/brand/ExtensionDetail";
import { buildDetailToc } from "../../components/brand/DetailChassis";
import extensionsData from "../../../public/data/extensions.json";
import RawMarkdown from "../../components/pages/RawMarkdown.astro";
import type { HeaderItem } from "../../components/pages/Header.astro";
import DetailHeader from "../../components/pages/Header.astro";
import Breadcrumb from "../../components/pages/Breadcrumb.astro";
import Sidebar from "../../components/pages/Sidebar.astro";
import PluginInstall from "../../components/pages/PluginInstall.astro";
import { readResourceMarkdown, formatLastUpdated } from "../../lib/detail-page";
import { searchIndex } from "../../lib/site-data";
import { formatLastUpdated, readResourceMarkdown } from "../../lib/detail-page";
import { sanitizeHttpUrl } from "../../lib/external-source";
const GITHUB_TREE = "https://github.com/github/awesome-copilot/tree/main";
const RAW_BASE = "https://raw.githubusercontent.com/github/awesome-copilot";
interface ExtensionScreenshot {
path?: string | null;
type?: string | null;
}
type Screenshot = { path?: string | null; type?: string | null } | null;
interface ExtensionAuthor {
name: string;
url?: string;
}
interface ExtensionEntry extends HeaderItem {
interface ExtensionItem {
id: string;
canvasId?: string;
extensionId?: string;
extensionName?: string;
pluginName?: string | null;
name: string;
title?: string;
version?: string | null;
readmeFile?: string | null;
description?: string;
version?: string | null;
pluginName?: string | null;
readmeFile?: string | null;
path?: string | null;
ref?: string | null;
lastUpdated?: string | null;
screenshots?: {
icon?: ExtensionScreenshot | null;
gallery?: ExtensionScreenshot | ExtensionScreenshot[] | null;
icon?: Screenshot;
gallery?: Screenshot | Screenshot[];
} | null;
imageUrl?: string | null;
assetPath?: string | null;
installUrl?: string | null;
installCommand?: string | null;
sourceUrl?: string | null;
externalSource?: string | null;
external?: boolean;
author?: ExtensionAuthor | null;
author?: { name?: string; url?: string } | null;
keywords?: string[];
}
// Sorted by display name so prev/next follows the catalog's ordering.
export function getStaticPaths() {
return (extensionsData.items as ExtensionEntry[]).map((item) => ({
const items = (extensionsData.items as ExtensionItem[])
.slice()
.sort((a, b) => a.name.localeCompare(b.name));
return items.map((item, index) => ({
params: { id: item.id },
props: { item },
props: {
item,
previous: index > 0 ? items[index - 1] : undefined,
next: index < items.length - 1 ? items[index + 1] : undefined,
},
}));
}
const { item } = Astro.props as { item: ExtensionEntry };
const { item, previous, next } = Astro.props as {
item: ExtensionItem;
previous?: ExtensionItem;
next?: ExtensionItem;
};
const headerItem = { ...item, title: item.title ?? item.name };
const isExternal = item.external === true;
// Allow only http(s) URLs from external/generated data in href/src contexts;
// unsafe values collapse to "" so downstream truthiness guards still hold.
// Only http(s) URLs may reach an href/src; anything else collapses to "" so the
// truthiness guards below drop it.
const safeUrl = (value?: string | null): string => {
const sanitized = sanitizeHttpUrl(value);
return sanitized === "#" ? "" : sanitized;
};
const readmePath = item.readmeFile ?? null;
const { markdownHtml, frontmatterText, rawMarkdown } = readmePath
? readResourceMarkdown(readmePath)
: { markdownHtml: "", frontmatterText: "", rawMarkdown: "" };
/**
* Preview assets are not copied into the site bundle; they are served from
* raw.githubusercontent.com. In-repo extensions record a repo-relative path
* pinned to the generated commit `ref`, external ones already record an
* absolute URL.
*/
const toAssetUrl = (assetPath?: string | null): string => {
if (!assetPath) return "";
const normalized = assetPath.replace(/\\/g, "/");
if (/^https?:\/\//i.test(normalized)) return safeUrl(normalized);
if (!item.ref) return "";
const encoded = normalized.split("/").map(encodeURIComponent).join("/");
return safeUrl(`${RAW_BASE}/${item.ref}/${encoded}`);
};
const screenshotList = (value?: Screenshot | Screenshot[]): Screenshot[] =>
!value ? [] : Array.isArray(value) ? value.filter(Boolean) : [value];
const images = Array.from(
new Set(
[
...screenshotList(item.screenshots?.gallery).map((s) =>
toAssetUrl(s?.path),
),
toAssetUrl(item.screenshots?.icon?.path),
safeUrl(item.imageUrl),
].filter(Boolean),
),
).map((url, index) => ({
url,
alt:
index === 0
? `${item.name} preview`
: `${item.name} screenshot ${index + 1}`,
}));
// `loadDetailPage` only covers the VS Code-installable resource types; an
// extension body is its README, read straight off disk when one exists.
const { markdownHtml } = item.readmeFile
? readResourceMarkdown(item.readmeFile)
: { markdownHtml: "" };
const { html } = buildDetailToc(markdownHtml);
const lastUpdated = formatLastUpdated(item.lastUpdated ?? null);
// Resolve preview images (icon + gallery), converting repo paths to raw URLs.
function toRawAssetUrl(assetPath?: string | null): string {
if (!assetPath) return "";
if (/^https?:\/\//i.test(assetPath)) return assetPath;
if (!item.ref) return "";
return `https://raw.githubusercontent.com/github/awesome-copilot/${item.ref}/${assetPath.replace(
/\\/g,
"/"
)}`;
}
// The catalog data's `installUrl` is a GitHub tree URL; the Copilot app deep
// link is the `ghapp://` scheme the prototype installs with. Only extensions
// with no marketplace plugin entry (e.g. plain-repo external canvases) omit
// `pluginName`, so gate on that rather than on `external` -- external plugins
// registered in the awesome-copilot marketplace remain installable by id.
const pluginSource = item.pluginName
? `${item.pluginName}@awesome-copilot`
: null;
const appInstallUrl = pluginSource
? `ghapp://plugins/install?source=${encodeURIComponent(pluginSource)}`
: null;
const sourceUrl = safeUrl(item.sourceUrl) || safeUrl(item.installUrl) || null;
function normalizeScreenshotEntries(
value: ExtensionScreenshot | ExtensionScreenshot[] | null | undefined
): ExtensionScreenshot[] {
if (!value) return [];
return Array.isArray(value) ? value.filter(Boolean) : [value];
}
const galleryImages: string[] = (() => {
const images: string[] = [];
if (item.imageUrl) images.push(item.imageUrl);
const iconUrl = toRawAssetUrl(item.screenshots?.icon?.path);
if (iconUrl) images.push(iconUrl);
for (const entry of normalizeScreenshotEntries(item.screenshots?.gallery)) {
const url = toRawAssetUrl(entry.path);
if (url) images.push(url);
}
return Array.from(new Set(images.map(safeUrl).filter(Boolean)));
})();
const installUrl = safeUrl(
item.installUrl ||
(item.path && item.ref
? `https://github.com/github/awesome-copilot/tree/${item.ref}/${item.path.replace(
/\\/g,
"/"
)}`
: "")
);
const sourceUrl = safeUrl(item.sourceUrl);
const externalSource = (item.externalSource || "").trim();
const installCommand =
item.installCommand ||
(item.pluginName
? `copilot plugin install ${item.pluginName}@awesome-copilot`
: "");
const githubUrl = isExternal
? sourceUrl || GITHUB_TREE
: item.path
? `${GITHUB_TREE}/${item.path}`
: installUrl || GITHUB_TREE;
// Sidebar "Source" shows item.path; external extensions have no repo path.
const sidebarItem = isExternal && sourceUrl ? { ...item, path: sourceUrl } : item;
const shortHash = (value: string) =>
/^[0-9a-f]{40}$/i.test(value) ? value.slice(0, 7) : value;
const chips: {
title: string;
items: string[];
filterBase?: string;
filterParam?: string;
}[] = [];
if (item.version) {
chips.push({ title: "Version", items: [item.version] });
}
if (item.canvasId) {
chips.push({ title: "Canvas ID", items: [item.canvasId] });
}
if (item.keywords && item.keywords.length > 0) {
chips.push({
title: "Keywords",
items: item.keywords,
filterBase: "/extensions/",
filterParam: "keyword",
});
}
if (item.author?.name) {
chips.push({ title: "Author", items: [item.author.name] });
}
if (!isExternal && item.ref) {
chips.push({ title: "Commit", items: [shortHash(item.ref)] });
}
const base = import.meta.env.BASE_URL ?? "/";
const detailHref = (id: string) =>
`${base.endsWith("/") ? base : `${base}/`}extension/${id}/`.replace(
/\/{2,}/g,
"/",
);
const siblingOf = (sibling?: ExtensionItem) =>
sibling ? { label: sibling.name, href: detailHref(sibling.id) } : undefined;
---
<StarlightPage
frontmatter={{
title: item.title ?? item.name,
description: item.description,
template: "splash",
prev: false,
next: false,
editUrl: false,
}}
<BaseLayout
title={`${item.name} | Awesome GitHub Copilot`}
description={item.description}
>
<div
id="main-content"
class="resource-detail-page extension-detail-page"
data-resource-detail
data-path={item.readmeFile ?? item.path ?? ""}
>
<RawMarkdown markdown={rawMarkdown} />
<div class="container">
<Breadcrumb
paths={[
{ name: "Home", href: "/" },
{ name: "Canvas Extensions", href: "/extensions/" },
]}
title={item.title ?? item.name}
/>
<DetailHeader item={headerItem} />
<div class="detail-layout">
<div class="detail-main">
{
galleryImages.length > 0 && (
<section class="detail-section extension-gallery">
<h2>Preview</h2>
<div class="extension-gallery-main">
<img
id="extension-gallery-image"
src={galleryImages[0]}
alt={`${item.name} preview`}
loading="eager"
/>
</div>
{galleryImages.length > 1 && (
<div class="extension-gallery-thumbs" role="list">
{galleryImages.map((url, index) => (
<button
type="button"
class={`extension-gallery-thumb${index === 0 ? " active" : ""}`}
data-gallery-url={url}
role="listitem"
aria-label={`Show image ${index + 1}`}
aria-current={index === 0 ? "true" : undefined}
>
<img src={url} alt={`Gallery image ${index + 1}`} loading="lazy" />
</button>
))}
</div>
)}
</section>
)
}
{
markdownHtml ? (
<section class="detail-section" aria-labelledby="doc-heading">
<h2 id="doc-heading">Documentation</h2>
<div class="article-content" set:html={markdownHtml} />
</section>
) : (
<section class="detail-section">
<h2>About</h2>
<p>{item.description}</p>
<p class="detail-empty">
{isExternal
? "This is a third-party canvas extension hosted outside this repository."
: "No README is bundled with this extension."}{" "}
View it{" "}
<a href={githubUrl} target="_blank" rel="noopener">
on GitHub
</a>
.
</p>
</section>
)
}
</div>
<Sidebar
item={sidebarItem}
lastUpdated={lastUpdated ?? undefined}
frontmatterText={frontmatterText || undefined}
githubUrl={githubUrl}
chips={chips}
>
{
isExternal ? (
<PluginInstall
slot="install"
isExternal={true}
pluginId={item.pluginName ?? item.id}
externalSource={externalSource || null}
label="Install this extension"
note="This extension is maintained in an external repository."
/>
) : (
<PluginInstall
slot="install"
pluginId={item.pluginName ?? item.id}
installCommand={installCommand || null}
label="Install"
/>
)
}
</Sidebar>
</div>
</div>
</div>
<BackToTop />
<script>
import "../../scripts/pages/resource-detail";
import "../../scripts/pages/extension-gallery";
</script>
</StarlightPage>
<ExtensionDetail
client:load
item={item}
images={images}
markdownHtml={html}
installUrl={appInstallUrl}
installCommand={item.installCommand}
sourceUrl={sourceUrl}
lastUpdated={lastUpdated}
previous={siblingOf(previous)}
next={siblingOf(next)}
searchIndex={searchIndex}
/>
</BaseLayout>
+13 -84
View File
@@ -1,88 +1,17 @@
---
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
import extensionsData from '../../public/data/extensions.json';
import ContributeCTA from '../components/ContributeCTA.astro';
import EmbeddedPageData from '../components/EmbeddedPageData.astro';
import PageHeader from '../components/PageHeader.astro';
import BackToTop from '../components/BackToTop.astro';
import { renderExtensionsHtml, sortExtensions } from '../scripts/pages/extensions-render';
import BaseLayout from "../layouts/BaseLayout.astro";
import { ExtensionsCatalog } from "../components/brand/ExtensionsCatalog";
import { extensions, searchIndex } from "../lib/site-data";
const initialItems = sortExtensions(extensionsData.items, 'title');
---
<StarlightPage frontmatter={{ title: 'Canvas Extensions', description: 'Canvas extensions for GitHub Copilot app experiences', template: 'splash', prev: false, next: false, editUrl: false }}>
<div id="main-content" class="extensions-page">
<PageHeader title="Canvas Extensions" description="Canvas extensions for GitHub Copilot app experiences" icon="plug" />
<div class="page-content">
<div class="container">
<div class="how-to-install">
<p><strong>Installing in the GitHub Copilot app:</strong> Click <strong>Copy Install</strong>, then run that plugin install command in Copilot.</p>
<p><strong>Example:</strong> <code>copilot plugin install accessibility-kanban@awesome-copilot</code></p>
</div>
<div class="listing-toolbar">
<div class="listing-toolbar-row">
<div class="results-count" id="results-count" aria-live="polite">{initialItems.length} extensions</div>
<details class="listing-controls">
<summary class="listing-controls-trigger btn btn-secondary btn-small">Sort &amp; Filter</summary>
<div class="listing-controls-panel">
<div class="filters-bar" id="filters-bar">
<div class="filter-group">
<label for="filter-keyword">Keyword:</label>
<select id="filter-keyword" multiple aria-label="Filter by keyword"></select>
</div>
<div class="filter-group">
<label for="sort-select">Sort:</label>
<select id="sort-select" aria-label="Sort extensions">
<option value="title">Name (A-Z)</option>
<option value="lastUpdated">Recently Updated</option>
</select>
</div>
<button id="clear-filters" class="btn btn-secondary btn-small">Clear Filters</button>
</div>
</div>
</details>
</div>
</div>
<div class="resource-list" id="resource-list" role="list" set:html={renderExtensionsHtml(initialItems)}></div>
<ContributeCTA resourceType="extensions" />
</div>
</div>
</div>
<BackToTop />
<EmbeddedPageData filename="extensions.json" data={extensionsData} />
<script>
import '../scripts/listing-flyouts';
import '../scripts/pages/extensions';
</script>
<style>
.how-to-install {
background: var(--sl-color-bg-nav);
border: 1px solid var(--sl-color-hairline);
border-radius: 0.5rem;
padding: 1rem 1.25rem;
margin-bottom: 1.5rem;
font-size: 0.9rem;
line-height: 1.6;
}
.how-to-install p {
margin: 0 0 0.5rem;
}
.how-to-install p:last-child {
margin-bottom: 0;
}
.how-to-install code {
background: var(--sl-color-bg-inline-code);
padding: 0.1em 0.35em;
border-radius: 0.2rem;
font-size: 0.85em;
}
</style>
</StarlightPage>
<BaseLayout
title="Extensions | Awesome GitHub Copilot"
description="Browse community-contributed canvas extensions for GitHub Copilot."
>
<ExtensionsCatalog
client:load
extensions={extensions as any}
searchIndex={searchIndex}
/>
</BaseLayout>
+20
View File
@@ -0,0 +1,20 @@
---
export function getStaticPaths() {
return [
"attester-import-check",
"dependency-license-checker",
"fix-broken-links",
"governance-audit",
"secrets-scanner",
"session-auto-commit",
"session-logger",
"tool-guardian",
].map((slug) => ({
params: { slug },
props: { target: `https://github.com/github/awesome-copilot/tree/main/hooks/${slug}` },
}));
}
const { target } = Astro.props;
return Astro.redirect(target);
---
+23 -180
View File
@@ -1,185 +1,28 @@
---
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
import Icon from "../components/Icon.astro";
import BackToTop from "../components/BackToTop.astro";
import { getCollection } from "astro:content";
const base = import.meta.env.BASE_URL;
import BaseLayout from "../layouts/BaseLayout.astro";
import { HomePage } from "../components/brand/HomePage";
import { counts, searchIndex } from "../lib/site-data";
// Learning Hub articles live in the docs collection rather than in generated data,
// so the count is derived here to keep every figure on the page live.
const docs = await getCollection("docs");
const learningHub = docs.filter(
(entry) =>
entry.id.includes("learning-hub/") &&
!entry.id.endsWith("/index") &&
!entry.id.endsWith("learning-hub"),
).length;
---
<StarlightPage
frontmatter={{
title: "Awesome GitHub Copilot",
description:
"Community-contributed agents, instructions, and skills to enhance your GitHub Copilot experience",
template: "splash",
pagefind: false,
prev: false,
next: false,
editUrl: false,
head: [
{
tag: "meta",
attrs: {
property: "og:type",
content: "website",
},
},
],
}}
hasSidebar={false}
<BaseLayout
title="Awesome GitHub Copilot"
description="The community library for GitHub Copilot: agents, instructions, skills, plugins, and extensions."
>
<div id="main-content">
<!-- Hero Section -->
<section class="hero" aria-labelledby="hero-heading">
<div class="container">
<h1 id="hero-heading">
<span class="gradient-text">Awesome GitHub Copilot</span>
</h1>
<p class="hero-subtitle">
Community-contributed agents, instructions, and skills to enhance your
GitHub Copilot experience
</p>
<p>
<a href="https://github.com/github/awesome-copilot/" target="_blank" rel="noopener noreferrer">
View the Awesome Copilot repository on GitHub
</a>
</p>
</div>
</section>
<!-- Quick Links -->
<section class="quick-links" aria-labelledby="quick-links-heading">
<h2 id="quick-links-heading" class="sr-only">Browse Resources</h2>
<div class="container">
<div class="cards-grid">
<a
href={`${base}agents/`}
class="card card-with-count card-category-ai"
id="card-agents"
style="--animation-delay: 0ms;"
>
<div class="card-icon" aria-hidden="true">
<Icon name="robot" size={40} />
</div>
<div class="card-content">
<h3>Agents</h3>
<p>Custom agents for specialized Copilot experiences</p>
</div>
<div
class="card-count"
data-count="agents"
aria-label="Agent count"
>
-
</div>
</a>
<a
href={`${base}instructions/`}
class="card card-with-count card-category-docs"
id="card-instructions"
style="--animation-delay: 50ms;"
>
<div class="card-icon" aria-hidden="true">
<Icon name="document" size={40} />
</div>
<div class="card-content">
<h3>Instructions</h3>
<p>Coding standards and best practices for Copilot</p>
</div>
<div
class="card-count"
data-count="instructions"
aria-label="Instruction count"
>
-
</div>
</a>
<a
href={`${base}skills/`}
class="card card-with-count card-category-power"
id="card-skills"
style="--animation-delay: 100ms;"
>
<div class="card-icon" aria-hidden="true">
<Icon name="lightning" size={40} />
</div>
<div class="card-content">
<h3>Skills</h3>
<p>Self-contained folders with instructions and resources</p>
</div>
<div
class="card-count"
data-count="skills"
aria-label="Skill count"
>
-
</div>
</a>
<a
href={`${base}plugins/`}
class="card card-with-count card-category-extension"
id="card-plugins"
style="--animation-delay: 250ms;"
>
<div class="card-icon" aria-hidden="true">
<Icon name="plug" size={40} />
</div>
<div class="card-content">
<h3>Plugins</h3>
<p>Curated plugins organized by themes</p>
</div>
<div
class="card-count"
data-count="plugins"
aria-label="Plugin count"
>
-
</div>
</a>
<a
href={`${base}extensions/`}
class="card card-with-count card-category-extension"
id="card-extensions"
style="--animation-delay: 300ms;"
>
<div class="card-icon" aria-hidden="true">
<Icon name="plug" size={40} />
</div>
<div class="card-content">
<h3>Canvas Extensions</h3>
<p>Interactive canvas extensions for Copilot app experiences</p>
</div>
<div
class="card-count"
data-count="extensions"
aria-label="Extension count"
>
-
</div>
</a>
<a
href={`${base}learning-hub/`}
class="card card-with-count card-category-learn"
id="card-learning-hub"
style="--animation-delay: 400ms;"
>
<div class="card-icon" aria-hidden="true">
<Icon name="book" size={40} />
</div>
<div class="card-content">
<h3>Learning Hub</h3>
<p>Articles and guides to master GitHub Copilot</p>
</div>
</a>
</div>
</div>
</section>
</div>
<!-- Back to Top Button -->
<BackToTop />
<script>
import "../scripts/pages/index";
</script>
</StarlightPage>
<HomePage
client:load
counts={{ ...counts, learningHub }}
searchIndex={searchIndex}
/>
</BaseLayout>
+55 -96
View File
@@ -1,115 +1,74 @@
---
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
import BackToTop from "../../components/BackToTop.astro";
import instructionsData from "../../../public/data/instructions.json";
import RawMarkdown from "../../components/pages/RawMarkdown.astro";
import type { HeaderItem } from "../../components/pages/Header.astro";
import DetailHeader from "../../components/pages/Header.astro";
import Breadcrumb from "../../components/pages/Breadcrumb.astro";
import Main from "../../components/pages/Main.astro";
import InstallButtons from "../../components/pages/InstallButtons.astro";
import Sidebar from "../../components/pages/Sidebar.astro";
import BaseLayout from "../../layouts/BaseLayout.astro";
import { InstructionDetail } from "../../components/brand/InstructionDetail";
import { buildDetailToc } from "../../components/brand/DetailChassis";
import { instructions, searchIndex } from "../../lib/site-data";
import { loadDetailPage } from "../../lib/detail-page";
interface InstructionItem extends HeaderItem {
const RAW_BASE = "https://raw.githubusercontent.com/github/awesome-copilot/main";
interface Item {
id: string;
title: string;
title?: string;
name?: string;
description?: string;
applyTo?: string | string[] | null;
applyToPatterns?: string[];
extensions?: string[];
path: string;
filename?: string;
lastUpdated?: string | null;
}
const labelOf = (entry: Item) => entry.title ?? entry.name ?? entry.id;
export function getStaticPaths() {
return (instructionsData.items as InstructionItem[]).map((item) => ({
const name = (entry: Item) => entry.title ?? entry.name ?? entry.id;
const list = [...(instructions as unknown as Item[])].sort((a, b) =>
name(a).localeCompare(name(b)),
);
return list.map((item, index) => ({
params: { id: item.id },
props: { item },
props: {
item,
previous: index > 0 ? list[index - 1] : undefined,
next: index < list.length - 1 ? list[index + 1] : undefined,
},
}));
}
const { item } = Astro.props as { item: InstructionItem };
const { item, previous, next } = Astro.props as {
item: Item;
previous?: Item;
next?: Item;
};
const {
vscodeUrl,
insidersUrl,
githubUrl,
markdownHtml,
frontmatterText,
rawMarkdown,
lastUpdated,
} = loadDetailPage(item, "instruction");
const detail = loadDetailPage(item, "instruction");
const { html } = buildDetailToc(detail.markdownHtml);
const applyToList = Array.isArray(item.applyTo)
? item.applyTo
: item.applyTo
? [item.applyTo]
: (item.applyToPatterns ?? []);
const extensions = item.extensions ?? [];
const base = import.meta.env.BASE_URL ?? "/";
const prefix = base.endsWith("/") ? base : base + "/";
const detailHref = (id: string) =>
(prefix + "instruction/" + id + "/").replace(/\/{2,}/g, "/");
const siblingOf = (sibling?: Item) =>
sibling ? { label: labelOf(sibling), href: detailHref(sibling.id) } : undefined;
const downloadUrl = RAW_BASE + "/" + item.path;
---
<StarlightPage
frontmatter={{
title: item.title,
description: item.description,
template: "splash",
prev: false,
next: false,
editUrl: false,
}}
<BaseLayout
title={labelOf(item) + " | Awesome GitHub Copilot"}
description={item.description}
>
<div
id="main-content"
class="resource-detail-page"
data-resource-detail
data-path={item.path}
>
<RawMarkdown markdown={rawMarkdown} />
<div class="container">
<Breadcrumb
paths={[
{ name: "Home", href: "/" },
{ name: "Instructions", href: "/instructions/" },
]}
title={item.title}
/>
<DetailHeader item={item} />
<div class="detail-layout">
<Main markdownHtml={markdownHtml} githubUrl={githubUrl} />
<Sidebar
item={item}
lastUpdated={lastUpdated ?? undefined}
frontmatterText={frontmatterText || undefined}
githubUrl={githubUrl}
chips={[
{ title: "Applies to", items: applyToList },
{
title: "Extensions",
items: extensions,
filterBase: "/instructions/",
filterParam: "extension",
},
]}
>
<InstallButtons
vscodeUrl={vscodeUrl}
insidersUrl={insidersUrl}
rawMarkdown={rawMarkdown}
slot="install"
/>
</Sidebar>
</div>
</div>
</div>
<BackToTop />
<script>
import "../../scripts/pages/resource-detail";
</script>
</StarlightPage>
<InstructionDetail
client:load
item={item as any}
markdownHtml={html}
vscodeUrl={detail.vscodeUrl}
insidersUrl={detail.insidersUrl}
githubUrl={detail.githubUrl}
downloadUrl={downloadUrl}
rawMarkdown={detail.rawMarkdown}
lastUpdated={detail.lastUpdated}
previous={siblingOf(previous)}
next={siblingOf(next)}
searchIndex={searchIndex}
/>
</BaseLayout>
+15 -53
View File
@@ -1,56 +1,18 @@
---
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
import instructionsData from '../../public/data/instructions.json';
import ContributeCTA from '../components/ContributeCTA.astro';
import EmbeddedPageData from '../components/EmbeddedPageData.astro';
import PageHeader from '../components/PageHeader.astro';
import BackToTop from '../components/BackToTop.astro';
import { renderInstructionsHtml, sortInstructions } from '../scripts/pages/instructions-render';
const initialItems = sortInstructions(instructionsData.items, 'title');
import BaseLayout from "../layouts/BaseLayout.astro";
import { InstructionsCatalog } from "../components/brand/InstructionsCatalog";
import { instructions, searchIndex } from "../lib/site-data";
import data from "../../public/data/instructions.json";
---
<StarlightPage frontmatter={{ title: 'Instructions', description: 'Coding standards and best practices for GitHub Copilot', template: 'splash', prev: false, next: false, editUrl: false }}>
<div id="main-content" class="instructions-page listing-cards-page">
<PageHeader title="Instructions" description="Coding standards and best practices for GitHub Copilot" icon="document" />
<div class="page-content">
<div class="container">
<div class="listing-toolbar">
<div class="listing-toolbar-row">
<div class="results-count" id="results-count" aria-live="polite">{initialItems.length} instructions</div>
<details class="listing-controls">
<summary class="listing-controls-trigger btn btn-secondary btn-small">Sort &amp; Filter</summary>
<div class="listing-controls-panel">
<div class="filters-bar" id="filters-bar">
<div class="filter-group">
<label for="filter-extension">File Extension:</label>
<select id="filter-extension" multiple aria-label="Filter by file extension"></select>
</div>
<div class="filter-group">
<label for="sort-select">Sort:</label>
<select id="sort-select" aria-label="Sort by">
<option value="title">Name (A-Z)</option>
<option value="lastUpdated">Recently Updated</option>
</select>
</div>
<button id="clear-filters" class="btn btn-secondary btn-small">Clear Filters</button>
</div>
</div>
</details>
</div>
</div>
<div class="resource-list" id="resource-list" role="list" set:html={renderInstructionsHtml(initialItems)}></div>
<ContributeCTA resourceType="instructions" />
</div>
</div>
</div>
<BackToTop />
<EmbeddedPageData filename="instructions.json" data={instructionsData} />
<script>
import '../scripts/listing-flyouts';
import '../scripts/pages/instructions';
</script>
</StarlightPage>
<BaseLayout
title="Instructions | Awesome GitHub Copilot"
description="Browse community-contributed instructions for GitHub Copilot."
>
<InstructionsCatalog
client:load
instructions={instructions as any}
filters={data.filters as any}
searchIndex={searchIndex}
/>
</BaseLayout>
@@ -0,0 +1,150 @@
---
import { getCollection } from "astro:content";
import LearningHubArticle from "../../layouts/LearningHubArticle.astro";
import BaseLayout from "../../layouts/BaseLayout.astro";
import AgentsAndSubagents from "../../components/brand/learning-hub/AgentsAndSubagents";
import CliForBeginnersOverview from "../../components/brand/learning-hub/CliForBeginnersOverview";
import CopilotConfigurationBasics from "../../components/brand/learning-hub/CopilotConfigurationBasics";
import GithubCopilotApp from "../../components/brand/learning-hub/GithubCopilotApp";
import GithubCopilotTerminologyGlossary from "../../components/brand/learning-hub/GithubCopilotTerminologyGlossary";
import UsingAutomationsInCopilotApp from "../../components/brand/learning-hub/UsingAutomationsInCopilotApp";
import WhatAreAgentsSkillsInstructions from "../../components/brand/learning-hub/WhatAreAgentsSkillsInstructions";
import WorkingWithCanvasExtensions from "../../components/brand/learning-hub/WorkingWithCanvasExtensions";
import {
hasTranslations,
isLearningHubArticle,
localizedEntry,
siteLocale,
splitLocale,
normalizeLearningHubPath,
} from "../../lib/learning-hub-routes";
import { isBespokeLearningHubArticle } from "../../lib/learning-hub-bespoke-articles";
// Only the English (unprefixed) articles are enumerated here. Astro's i18n
// `fallback` config auto-generates the locale-prefixed variant of every path in
// this route, so this one route file owns every locale URL. Declaring a
// separate `/[locale]/learning-hub/[...slug]` route as well would produce the
// same paths at lower priority and be dropped with a build warning, leaving the
// English fallback to shadow real translations.
export async function getStaticPaths() {
const docs = await getCollection("docs");
return docs
.filter((entry) => isLearningHubArticle(entry.id))
.filter((entry) => !splitLocale(entry.id).locale)
.map((entry) => ({
params: {
slug: normalizeLearningHubPath(
entry.id.replace(/^learning-hub\//, ""),
),
},
props: { englishId: entry.id },
}));
}
const { englishId } = Astro.props;
// On a fallback-generated page `Astro.url` is the rewritten English URL, so the
// requested locale has to come from `Astro.currentLocale`.
const locale = siteLocale(Astro.currentLocale);
const docs = await getCollection("docs");
const entry = localizedEntry(docs, englishId, locale);
const publicPath = normalizeLearningHubPath(englishId);
const slug = locale ? `${locale}/${publicPath}` : publicPath;
const canonicalPath = `/${slug}/`;
const showLanguageSelect = hasTranslations(englishId);
// Slugs adopted directly from the design prototype render their own bespoke
// component instead of the generic markdown pipeline — English only for now;
// locale routes for these slugs still fall through to the markdown renderer.
// Astro's client:* hydration directives require a statically-referenced
// component in the template, so each bespoke slug gets its own branch here
// rather than a dynamic lookup passed as a prop.
const bespokeSlug = !locale
? normalizeLearningHubPath(englishId.replace(/^learning-hub\//, ""))
: undefined;
---
{
bespokeSlug && isBespokeLearningHubArticle(bespokeSlug) ? (
bespokeSlug === "github-copilot-app" ? (
<BaseLayout
title={`${entry.data.title} | GitHub Copilot Learning Hub`}
description={entry.data.description}
canonicalPath={canonicalPath}
>
<GithubCopilotApp client:load />
</BaseLayout>
) : bespokeSlug === "agents-and-subagents" ? (
<BaseLayout
title={`${entry.data.title} | GitHub Copilot Learning Hub`}
description={entry.data.description}
canonicalPath={canonicalPath}
>
<AgentsAndSubagents client:load />
</BaseLayout>
) : bespokeSlug === "copilot-configuration-basics" ? (
<BaseLayout
title={`${entry.data.title} | GitHub Copilot Learning Hub`}
description={entry.data.description}
canonicalPath={canonicalPath}
>
<CopilotConfigurationBasics client:load />
</BaseLayout>
) : bespokeSlug === "github-copilot-terminology-glossary" ? (
<BaseLayout
title={`${entry.data.title} | GitHub Copilot Learning Hub`}
description={entry.data.description}
canonicalPath={canonicalPath}
>
<GithubCopilotTerminologyGlossary client:load />
</BaseLayout>
) : bespokeSlug === "using-automations-in-copilot-app" ? (
<BaseLayout
title={`${entry.data.title} | GitHub Copilot Learning Hub`}
description={entry.data.description}
canonicalPath={canonicalPath}
>
<UsingAutomationsInCopilotApp client:load />
</BaseLayout>
) : bespokeSlug === "what-are-agents-skills-instructions" ? (
<BaseLayout
title={`${entry.data.title} | GitHub Copilot Learning Hub`}
description={entry.data.description}
canonicalPath={canonicalPath}
>
<WhatAreAgentsSkillsInstructions client:load />
</BaseLayout>
) : bespokeSlug === "working-with-canvas-extensions" ? (
<BaseLayout
title={`${entry.data.title} | GitHub Copilot Learning Hub`}
description={entry.data.description}
canonicalPath={canonicalPath}
>
<WorkingWithCanvasExtensions client:load />
</BaseLayout>
) : bespokeSlug === "cli-for-beginners" ? (
<BaseLayout
title={`${entry.data.title} | GitHub Copilot Learning Hub`}
description={entry.data.description}
canonicalPath={canonicalPath}
>
<CliForBeginnersOverview client:load />
</BaseLayout>
) : (
<LearningHubArticle
entry={entry}
slug={slug}
showLanguageSelect={showLanguageSelect}
canonicalPath={canonicalPath}
/>
)
) : (
<LearningHubArticle
entry={entry}
slug={slug}
showLanguageSelect={showLanguageSelect}
canonicalPath={canonicalPath}
/>
)
}
@@ -1,229 +1,57 @@
---
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
import BackToTop from "../../../../../components/BackToTop.astro";
import Breadcrumb from "../../../../../components/pages/Breadcrumb.astro";
import FileBrowser from "../../../../../components/pages/FileBrowser.astro";
import BaseLayout from "../../../../../layouts/BaseLayout.astro";
import { CookbookRecipe } from "../../../../../components/brand/CookbookRecipe";
import { buildDetailToc } from "../../../../../components/brand/DetailChassis";
import {
GITHUB_BLOB_BASE,
GITHUB_RAW_BASE,
recipeRoutes,
} from "../../../../../lib/cookbook-routes";
import { readResourceMarkdown } from "../../../../../lib/detail-page";
import samplesData from "../../../../../../public/data/samples.json";
interface RecipeVariant {
doc: string;
example: string | null;
}
interface Recipe {
id: string;
name: string;
description: string;
tags: string[];
languages: string[];
variants: Record<string, RecipeVariant>;
external?: boolean;
url?: string | null;
author?: { name: string; url?: string } | null;
}
interface Language {
id: string;
name: string;
icon: string;
extension: string;
}
interface Cookbook {
id: string;
name: string;
description: string;
path: string;
featured: boolean;
languages: Language[];
recipes: Recipe[];
}
interface SamplesData {
cookbooks: Cookbook[];
}
const GITHUB_BASE = "https://github.com/github/awesome-copilot/blob/main";
function buildCookbookUrl(
cookbookId: string,
languageId: string,
recipeId: string,
filePath?: string | null
): string {
const basePath = `/learning-hub/cookbook/${encodeURIComponent(
cookbookId
)}/${encodeURIComponent(languageId)}/${encodeURIComponent(recipeId)}/`;
if (!filePath) return basePath;
return `${basePath}#file=${encodeURIComponent(filePath)}`;
}
import { searchIndex } from "../../../../../lib/site-data";
export function getStaticPaths() {
const entries = (samplesData as SamplesData).cookbooks.flatMap((cookbook) =>
cookbook.recipes.flatMap((recipe) =>
Object.keys(recipe.variants ?? {}).map((languageId) => ({
params: {
cookbook: cookbook.id,
language: languageId,
recipe: recipe.id,
},
}))
)
);
return entries;
return recipeRoutes().map(({ cookbook, recipe, language, variant }) => ({
params: { cookbook: cookbook.id, language: language.id, recipe: recipe.id },
props: { cookbook, recipe, language, variant },
}));
}
const { cookbook, language, recipe } = Astro.params as {
cookbook: string;
language: string;
recipe: string;
};
const { cookbook, recipe, language, variant } = Astro.props;
const cookbookEntry = (samplesData as SamplesData).cookbooks.find(
(item) => item.id === cookbook
);
const recipeEntry = cookbookEntry?.recipes.find((item) => item.id === recipe);
const languageEntry = cookbookEntry?.languages.find((item) => item.id === language);
const variant = recipeEntry?.variants?.[language];
if (!cookbookEntry || !recipeEntry || !languageEntry || !variant || recipeEntry.external) {
return Astro.redirect("/learning-hub/cookbook/");
}
const { markdownHtml } = readResourceMarkdown(variant.doc);
const files = [{ name: variant.doc.split("/").pop() ?? variant.doc, path: variant.doc }];
const { markdownHtml, rawMarkdown } = readResourceMarkdown(variant.doc);
const { html, toc } = buildDetailToc(markdownHtml);
const files = [
{ path: variant.doc, name: variant.doc.split("/").pop() ?? variant.doc },
];
if (variant.example) {
files.push({
name: variant.example.split("/").pop() ?? variant.example,
path: variant.example,
name: variant.example.split("/").pop() ?? variant.example,
});
}
const recipeUrl = buildCookbookUrl(cookbook, language, recipe);
const exampleUrl = variant.example
? buildCookbookUrl(cookbook, language, recipe, variant.example)
: null;
---
<StarlightPage
frontmatter={{
title: `${recipeEntry.name} (${languageEntry.name})`,
description: recipeEntry.description,
template: "splash",
prev: false,
next: false,
editUrl: false,
}}
<BaseLayout
title={`${recipe.name} (${language.name}) | Awesome GitHub Copilot`}
description={recipe.description}
>
<div
id="main-content"
class="resource-detail-page cookbook-detail-page"
data-file-browser-page
data-bundle-id={`${cookbookEntry.id}-${recipeEntry.id}-${languageEntry.id}`}
data-path={variant.doc}
>
<div class="container">
<Breadcrumb
paths={[
{ name: "Home", href: "/" },
{ name: "Learning Hub", href: "/learning-hub/" },
{ name: "Cookbook", href: "/learning-hub/cookbook/" },
{ name: cookbookEntry.name, href: "/learning-hub/cookbook/" },
]}
title={recipeEntry.name}
/>
<header class="cookbook-detail-header">
<h1>{recipeEntry.name}</h1>
<p>{recipeEntry.description}</p>
<div class="cookbook-detail-meta">
<span class="cookbook-detail-language">{languageEntry.icon} {languageEntry.name}</span>
<div class="cookbook-detail-tags">
{recipeEntry.tags.map((tag) => (
<span class="recipe-tag">{tag}</span>
))}
</div>
</div>
<div class="cookbook-detail-actions">
<a class="btn btn-secondary btn-small" href={recipeUrl}>
View Recipe
</a>
{exampleUrl && (
<a class="btn btn-secondary btn-small" href={exampleUrl}>
View Example
</a>
)}
<a
class="btn btn-secondary btn-small"
href={`https://github.com/github/awesome-copilot/blob/main/${variant.doc}`}
target="_blank"
rel="noopener"
>
GitHub
</a>
</div>
</header>
<FileBrowser
files={files}
primaryPath={variant.doc}
primaryName={variant.doc.split("/").pop() ?? variant.doc}
primaryHtml={markdownHtml}
githubBase={GITHUB_BASE}
/>
</div>
</div>
<BackToTop />
<script>
import "../../../../../scripts/pages/file-browser";
</script>
<style is:global>
.cookbook-detail-header {
margin-bottom: 24px;
display: flex;
flex-direction: column;
gap: 12px;
}
.cookbook-detail-header h1 {
margin: 0;
color: var(--color-text-emphasis);
}
.cookbook-detail-header p {
margin: 0;
color: var(--color-text-muted);
}
.cookbook-detail-meta {
display: flex;
flex-wrap: wrap;
gap: 12px;
align-items: center;
}
.cookbook-detail-language {
display: inline-flex;
align-items: center;
gap: 6px;
font-family: "Monaspace Argon NF", monospace;
}
.cookbook-detail-tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.cookbook-detail-actions {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
</style>
</StarlightPage>
<CookbookRecipe
client:load
cookbookName={cookbook.name}
recipeName={recipe.name}
recipeDescription={recipe.description}
languageName={language.name}
languageIcon={language.icon}
tags={recipe.tags}
files={files}
primaryPath={variant.doc}
markdownHtml={html}
rawMarkdown={rawMarkdown}
toc={toc}
githubBlobBase={GITHUB_BLOB_BASE}
rawBase={GITHUB_RAW_BASE}
searchIndex={searchIndex}
/>
</BaseLayout>
@@ -1,292 +1,21 @@
---
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
import samplesData from '../../../../public/data/samples.json';
import EmbeddedPageData from '../../../components/EmbeddedPageData.astro';
import {
getRecipeResultsCountText,
renderCookbookSectionsHtml,
} from '../../../scripts/pages/samples-render';
import BaseLayout from "../../../layouts/BaseLayout.astro";
import CookbookIndex from "../../../components/brand/learning-hub/CookbookIndex";
import { cookbookSections } from "../../../lib/cookbook-routes";
import { siteLocale } from "../../../lib/learning-hub-routes";
const initialRecipeMatches = samplesData.cookbooks.flatMap((cookbook) =>
cookbook.recipes.map((recipe) => ({ cookbook, recipe }))
);
// Astro's i18n `fallback` config renders this same component for every
// locale-prefixed URL, so recipe links must be prefixed to match the URL the
// visitor is actually on.
const sections = cookbookSections(siteLocale(Astro.currentLocale));
const languageOptions = Array.from(
new Map(
samplesData.cookbooks.flatMap((cookbook) =>
cookbook.languages.map((language) => [language.id, language])
)
).values()
);
const description =
"Code samples, recipes, and hands-on examples for building with GitHub Copilot.";
---
<StarlightPage frontmatter={{ title: 'Cookbook', description: 'Code samples, recipes, and examples for building with GitHub Copilot' }}>
<div class="cookbook-page">
<div class="listing-toolbar">
<div class="search-bar">
<label for="search-input" class="sr-only">Search recipes</label>
<input type="text" id="search-input" placeholder="Search recipes..." autocomplete="off">
</div>
<div class="filters-bar" id="filters-bar">
<div class="filter-group">
<label for="filter-language">Language:</label>
<select id="filter-language" aria-label="Filter by language">
<option value="">All Languages</option>
{languageOptions.map((language) => (
<option value={language.id}>{language.name}</option>
))}
</select>
</div>
<div class="filter-group">
<label for="filter-tag">Tags:</label>
<select id="filter-tag" multiple aria-label="Filter by tags">
{samplesData.filters.tags.map((tag) => (
<option value={tag}>{tag}</option>
))}
</select>
</div>
<button id="clear-filters" class="btn btn-secondary btn-small">Clear Filters</button>
</div>
</div>
<div class="results-count" id="results-count" aria-live="polite">{getRecipeResultsCountText(samplesData.totalRecipes, samplesData.totalRecipes)}</div>
<div id="samples-list" set:html={renderCookbookSectionsHtml(initialRecipeMatches)}></div>
</div>
<EmbeddedPageData filename="samples.json" data={samplesData} />
<style is:global>
/* Cookbook Section */
.cookbook-section {
margin-bottom: 48px;
}
.cookbook-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 24px;
margin-bottom: 24px;
padding-bottom: 16px;
border-bottom: 1px solid var(--color-border);
}
.cookbook-info h2 {
margin: 0 0 8px 0;
font-size: 24px;
color: var(--color-text-emphasis);
}
.cookbook-info p {
margin: 0;
color: var(--color-text-muted);
}
.cookbook-languages {
display: flex;
gap: 8px;
flex-shrink: 0;
}
.lang-tab {
padding: 8px 12px;
border: 1px solid var(--color-border);
border-radius: var(--border-radius);
background: var(--color-bg-secondary);
color: var(--color-text);
cursor: pointer;
font-family: 'Monaspace Argon NF', monospace;
font-size: 18px;
transition: all var(--transition);
}
.lang-tab:hover {
border-color: var(--color-link);
background: var(--color-bg-tertiary);
}
.lang-tab.active {
border-color: var(--color-link);
background: var(--color-link);
color: white;
}
/* Recipe Grid */
.recipes-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 20px;
}
/* Recipe Card */
.recipe-card {
background: var(--color-card-bg);
border: 1px solid var(--color-border);
border-radius: var(--border-radius-lg);
padding: 24px;
transition: all var(--transition);
}
.recipe-card:hover {
border-color: var(--color-link);
box-shadow: var(--shadow);
}
.recipe-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 12px;
margin-bottom: 12px;
}
.recipe-header h3 {
margin: 0;
font-size: 18px;
color: var(--color-text-emphasis);
}
.recipe-langs {
display: flex;
gap: 4px;
flex-shrink: 0;
}
.lang-indicator {
font-family: 'Monaspace Argon NF', monospace;
font-size: 16px;
opacity: 0.7;
}
.recipe-description {
color: var(--color-text-muted);
font-size: 14px;
margin: 0 0 16px 0;
line-height: 1.5;
}
.recipe-tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 16px;
}
.recipe-tag {
background: var(--color-bg-secondary);
color: var(--color-text-muted);
padding: 4px 10px;
border-radius: 12px;
font-size: 12px;
border: 1px solid var(--color-border);
}
.recipe-actions {
display: flex;
flex-wrap: wrap;
gap: 8px;
padding-top: 16px;
border-top: 1px solid var(--color-border);
}
.recipe-actions .btn {
font-size: 13px;
}
.recipe-actions .btn svg {
flex-shrink: 0;
}
.no-variant {
color: var(--color-text-muted);
font-size: 13px;
font-style: italic;
}
/* External recipe card */
.recipe-card.external {
border-style: dashed;
}
.recipe-badge.external-badge {
display: inline-flex;
align-items: center;
gap: 4px;
background: var(--color-bg-secondary);
color: var(--color-text-muted);
padding: 4px 10px;
border-radius: 12px;
font-size: 12px;
border: 1px solid var(--color-border);
white-space: nowrap;
}
.recipe-author-line {
margin-bottom: 12px;
font-size: 13px;
color: var(--color-text-muted);
}
.recipe-author-line a {
color: var(--color-link);
text-decoration: none;
}
.recipe-author-line a:hover {
text-decoration: underline;
}
/* Empty state */
.empty-state {
text-align: center;
padding: 64px 24px;
background: var(--color-bg-secondary);
border-radius: var(--border-radius-lg);
border: 1px dashed var(--color-border);
}
.empty-state h3 {
margin: 0 0 8px 0;
color: var(--color-text-muted);
}
.empty-state p {
margin: 0;
color: var(--color-text-muted);
}
/* Search highlight */
.search-highlight {
background-color: var(--color-warning);
color: var(--color-text-emphasis);
padding: 0 2px;
border-radius: 2px;
}
/* Responsive */
@media (max-width: 768px) {
.cookbook-header {
flex-direction: column;
}
.recipes-grid {
grid-template-columns: 1fr;
}
.recipe-actions {
flex-direction: column;
}
.recipe-actions .btn {
width: 100%;
justify-content: center;
}
}
</style>
<script>
import '../../../scripts/pages/samples';
</script>
</StarlightPage>
<BaseLayout
title="Cookbook | Awesome GitHub Copilot"
description={description}
>
<CookbookIndex client:load sections={sections} />
</BaseLayout>
@@ -0,0 +1,51 @@
---
import { getCollection } from "astro:content";
import BaseLayout from "../../layouts/BaseLayout.astro";
import { LearningHubIndex } from "../../components/brand/LearningHubIndex";
import type { LearningHubArticle } from "../../components/brand/LearningHubIndex";
import { searchIndex } from "../../lib/site-data";
import { normalizeLearningHubPath } from "../../lib/learning-hub-routes";
const docs = await getCollection("docs");
// Keep each workshop landing page explicit so the Learning Hub can surface tracks
// whose entries live below a nested directory, including translated app tracks.
const WORKSHOP_LANDING_IDS = [
"learning-hub/cli-for-beginners/index",
"learning-hub/copilot-workshops/index",
"learning-hub/copilot-workshops/app/index",
"learning-hub/copilot-workshops/cli/index",
"learning-hub/copilot-workshops/cloud/index",
"learning-hub/copilot-workshops/vscode/index",
];
const articles: LearningHubArticle[] = docs
.filter(
(entry) =>
/^learning-hub\/[^/]+$/.test(entry.id) ||
WORKSHOP_LANDING_IDS.includes(entry.id),
)
.filter((entry) => entry.id !== "learning-hub/index")
.map((entry) => ({
id: entry.id,
title: entry.data.title,
description: entry.data.description ?? "",
href: `/${normalizeLearningHubPath(entry.id)}/`,
tags: entry.data.tags ?? [],
lastUpdated:
entry.data.lastUpdated instanceof Date
? entry.data.lastUpdated.toISOString()
: (entry.data.lastUpdated ?? undefined),
}))
.sort((a, b) => (b.lastUpdated ?? "").localeCompare(a.lastUpdated ?? ""));
const description =
"Guides, tutorials, and reference material for getting the most out of GitHub Copilot — curated by the community.";
---
<BaseLayout
title="GitHub Copilot Learning Hub | Awesome GitHub Copilot"
description={description}
>
<LearningHubIndex client:load articles={articles} searchIndex={searchIndex} />
</BaseLayout>
+1 -1
View File
@@ -49,7 +49,7 @@ export const GET: APIRoute = async () => {
content +=
"- **Learning Hub**: Curated guides, tutorials, and reference material published on the website\n\n";
// Process Learning Hub documentation
// Process Learning Hub documentation.
content += "## Learning Hub\n\n";
for (const article of learningHubArticles) {
const description = normalizeDescription(article.data.description);
+71 -198
View File
@@ -1,235 +1,108 @@
---
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
import BackToTop from "../../components/BackToTop.astro";
import BaseLayout from "../../layouts/BaseLayout.astro";
import { PluginDetail } from "../../components/brand/PluginDetail";
import { buildDetailToc } from "../../components/brand/DetailChassis";
import pluginsData from "../../../public/data/plugins.json";
import RawMarkdown from "../../components/pages/RawMarkdown.astro";
import type { HeaderItem } from "../../components/pages/Header.astro";
import DetailHeader from "../../components/pages/Header.astro";
import Breadcrumb from "../../components/pages/Breadcrumb.astro";
import Sidebar from "../../components/pages/Sidebar.astro";
import IncludedItems from "../../components/pages/IncludedItems.astro";
import PluginInstall from "../../components/pages/PluginInstall.astro";
import { readResourceMarkdown, formatLastUpdated } from "../../lib/detail-page";
import { externalRepoUrl } from "../../lib/external-source";
import { searchIndex } from "../../lib/site-data";
import { formatLastUpdated, readResourceMarkdown } from "../../lib/detail-page";
import { externalRepoUrl, type ExternalSource } from "../../lib/external-source";
const GITHUB_TREE = "https://github.com/github/awesome-copilot/tree/main";
interface PluginItemRef {
interface PluginIncludedItem {
kind: string;
path: string;
title?: string;
path?: string;
title?: string | null;
detailUrl?: string | null;
}
interface PluginAuthor {
name: string;
url?: string;
}
interface PluginSource {
source: string;
repo?: string;
path?: string;
ref?: string;
sha?: string;
}
interface PluginEntry extends HeaderItem {
interface PluginItem {
id: string;
name: string;
title?: string;
description?: string;
path: string;
readmeFile?: string | null;
version?: string | null;
tags?: string[];
itemCount: number;
items: PluginItemRef[];
itemCount?: number;
items?: PluginIncludedItem[];
external?: boolean;
generatedFromExtension?: boolean;
repository?: string | null;
homepage?: string | null;
author?: PluginAuthor | null;
source?: PluginSource | null;
license?: string | null;
author?: { name?: string; url?: string } | null;
source?: ExternalSource | null;
lastUpdated?: string | null;
}
export function getStaticPaths() {
return (pluginsData.items as PluginEntry[]).map((item) => ({
const items = [...(pluginsData.items as PluginItem[])].sort((a, b) =>
a.name.localeCompare(b.name),
);
return items.map((item, index) => ({
params: { id: item.id },
props: { item },
props: {
item,
previous: index > 0 ? items[index - 1] : undefined,
next: index < items.length - 1 ? items[index + 1] : undefined,
},
}));
}
const { item } = Astro.props as { item: PluginEntry };
const { item, previous, next } = Astro.props as {
item: PluginItem;
previous?: PluginItem;
next?: PluginItem;
};
// DetailHeader reads item.title; plugins only have a name.
const headerItem = { ...item, title: item.title ?? item.name };
// External plugins are referenced, not vendored: no README exists in this
// repository for them, so the markdown body is simply empty.
const { markdownHtml } = item.readmeFile
? readResourceMarkdown(item.readmeFile)
: { markdownHtml: "" };
const isExternal = item.external === true;
const readmePath = item.readmeFile ?? null;
const { markdownHtml, frontmatterText, rawMarkdown } = readmePath
? readResourceMarkdown(readmePath)
: { markdownHtml: "", frontmatterText: "", rawMarkdown: "" };
const { html } = buildDetailToc(markdownHtml);
const githubUrl = item.external
? externalRepoUrl(item.source, [item.repository, item.homepage, GITHUB_TREE])
: `${GITHUB_TREE}/${item.path}`;
const installCommand = `copilot plugin install ${item.id}@awesome-copilot`;
// Deep link handled by the Copilot app's `ghapp://` protocol handler. The
// marketplace-qualified source is encoded because the `@` separator is not
// safe unescaped in a query value.
const appInstallUrl = `ghapp://plugins/install?source=${encodeURIComponent(
`${item.id}@awesome-copilot`,
)}`;
const lastUpdated = formatLastUpdated(item.lastUpdated);
function pluginRepoUrl(): string {
return externalRepoUrl(item.source, [
item.repository,
item.homepage,
GITHUB_TREE,
]);
}
const base = import.meta.env.BASE_URL ?? "/";
const detailHref = (id: string) =>
`${base.endsWith("/") ? base : `${base}/`}plugin/${id}/`.replace(
/\/{2,}/g,
"/",
);
const githubUrl = isExternal
? pluginRepoUrl()
: `${GITHUB_TREE}/${item.path}`;
// Install wiring
const externalSourceRef = item.source?.repo || item.repository || "";
const installCommand = isExternal
? null
: `copilot plugin install ${item.id}@awesome-copilot`;
// The sidebar "Source" shows item.path; for external plugins that repo-relative
// path doesn't exist here, so surface the upstream source reference instead.
const sidebarItem =
isExternal && externalSourceRef
? { ...item, path: externalSourceRef }
: item;
const chips: {
title: string;
items: string[];
filterBase?: string;
filterParam?: string;
}[] = [];
if (item.version) {
chips.push({ title: "Version", items: [item.version] });
}
if (item.tags && item.tags.length > 0) {
chips.push({
title: "Tags",
items: item.tags,
filterBase: "/plugins/",
filterParam: "tag",
});
}
if (!isExternal && item.itemCount > 0) {
chips.push({
title: "Bundled items",
items: [`${item.itemCount} item${item.itemCount === 1 ? "" : "s"}`],
});
}
if (isExternal && item.author?.name) {
chips.push({ title: "Author", items: [item.author.name] });
}
if (isExternal && item.license) {
chips.push({ title: "License", items: [item.license] });
}
if (isExternal) {
const shortHash = (value: string) =>
/^[0-9a-f]{40}$/i.test(value) ? value.slice(0, 7) : value;
const ref = item.source?.ref;
const sha = item.source?.sha;
if (ref) {
chips.push({ title: "Ref", items: [shortHash(ref)] });
}
if (sha) {
chips.push({ title: "Commit", items: [shortHash(sha)] });
}
}
const siblingOf = (sibling?: PluginItem) =>
sibling ? { label: sibling.name, href: detailHref(sibling.id) } : undefined;
---
<StarlightPage
frontmatter={{
title: item.title ?? item.name,
description: item.description,
template: "splash",
prev: false,
next: false,
editUrl: false,
}}
<BaseLayout
title={`${item.name} | Awesome GitHub Copilot`}
description={item.description}
>
<div
id="main-content"
class="resource-detail-page plugin-detail-page"
data-resource-detail
data-path={item.readmeFile ?? item.path}
>
<RawMarkdown markdown={rawMarkdown} />
<div class="container">
<Breadcrumb
paths={[
{ name: "Home", href: "/" },
{ name: "Plugins", href: "/plugins/" },
]}
title={item.title ?? item.name}
/>
<DetailHeader item={headerItem} />
<div class="detail-layout">
<div class="detail-main">
{
markdownHtml ? (
<section class="detail-section" aria-labelledby="doc-heading">
<h2 id="doc-heading">Documentation</h2>
<div class="article-content" set:html={markdownHtml} />
</section>
) : (
<section class="detail-section">
<h2>Overview</h2>
<p>{item.description}</p>
<p class="detail-empty">
{isExternal
? "This is a third-party plugin hosted outside this repository."
: "No README is bundled with this plugin."}{" "}
View it{" "}
<a href={githubUrl} target="_blank" rel="noopener">
on GitHub
</a>
.
</p>
</section>
)
}
<IncludedItems
items={item.items}
pluginPath={item.path}
githubBase={GITHUB_TREE}
/>
</div>
<Sidebar
item={sidebarItem}
lastUpdated={lastUpdated ?? undefined}
frontmatterText={frontmatterText || undefined}
githubUrl={githubUrl}
chips={chips}
>
<PluginInstall
slot="install"
isExternal={isExternal}
pluginId={item.id}
externalSource={externalSourceRef || null}
installCommand={installCommand}
label={isExternal ? "Install this plugin" : "Install"}
note={isExternal
? "This plugin is maintained in an external repository."
: undefined}
/>
</Sidebar>
</div>
</div>
</div>
<BackToTop />
<script>
import "../../scripts/pages/resource-detail";
</script>
</StarlightPage>
<PluginDetail
client:load
item={item}
markdownHtml={html}
githubUrl={githubUrl}
installCommand={installCommand}
appInstallUrl={appInstallUrl}
lastUpdated={lastUpdated}
previous={siblingOf(previous)}
next={siblingOf(next)}
searchIndex={searchIndex}
/>
</BaseLayout>
+26 -86
View File
@@ -1,90 +1,30 @@
---
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
import pluginsData from '../../public/data/plugins.json';
import ContributeCTA from '../components/ContributeCTA.astro';
import EmbeddedPageData from '../components/EmbeddedPageData.astro';
import PageHeader from '../components/PageHeader.astro';
import BackToTop from '../components/BackToTop.astro';
import { renderPluginsHtml, sortPlugins } from '../scripts/pages/plugins-render';
import BaseLayout from "../layouts/BaseLayout.astro";
import { PluginsCatalog } from "../components/brand/PluginsCatalog";
import { plugins, searchIndex } from "../lib/site-data";
const initialItems = sortPlugins(pluginsData.items, 'title');
// The prototype exposes a fixed tag rail. Real data has a long tail of tags, so
// surface the most-used ones to keep each filter group usefully populated.
const tagCounts = new Map<string, number>();
for (const plugin of plugins as { tags?: string[] }[]) {
for (const tag of plugin.tags ?? []) {
tagCounts.set(tag, (tagCounts.get(tag) ?? 0) + 1);
}
}
const tags = [...tagCounts.entries()]
.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
.slice(0, 12)
.map(([tag]) => tag);
---
<StarlightPage frontmatter={{ title: 'Plugins', description: 'Curated plugins of agents, hooks, and skills for specific workflows', template: 'splash', prev: false, next: false, editUrl: false }}>
<div id="main-content" class="plugins-page listing-cards-page">
<PageHeader title="Plugins" description="Curated plugins of agents, hooks, and skills for specific workflows" icon="plug" />
<div class="page-content">
<div class="container">
<div class="how-to-access">
<p><strong>Awesome Copilot is a default plugin marketplace</strong> — no setup required. Access it directly from your tools:</p>
<ul>
<li><strong>GitHub Copilot CLI</strong>: Type <code>/plugin marketplace browse awesome-copilot</code> in a Copilot session</li>
<li><strong>VS Code</strong>: Type <code>@agentPlugins</code> in the Extensions search view, or open the Command Palette and run <code>Chat: Plugins</code></li>
</ul>
<p>Install any plugin with: <code>copilot plugin install &lt;plugin-name&gt;@awesome-copilot</code></p>
</div>
<div class="listing-toolbar">
<div class="listing-toolbar-row">
<div class="results-count" id="results-count" aria-live="polite">{initialItems.length} plugins</div>
<details class="listing-controls">
<summary class="listing-controls-trigger btn btn-secondary btn-small">Sort &amp; Filter</summary>
<div class="listing-controls-panel">
<div class="filters-bar" id="filters-bar">
<div class="filter-group">
<label for="filter-tag">Tag:</label>
<select id="filter-tag" multiple aria-label="Filter by tag"></select>
</div>
<div class="filter-group">
<label for="sort-select">Sort:</label>
<select id="sort-select" aria-label="Sort plugins">
<option value="title">Title</option>
<option value="lastUpdated">Recently Updated</option>
</select>
</div>
<button id="clear-filters" class="btn btn-secondary btn-small">Clear Filters</button>
</div>
</div>
</details>
</div>
</div>
<div class="resource-list" id="resource-list" role="list" set:html={renderPluginsHtml(initialItems)}></div>
<ContributeCTA resourceType="plugins" />
</div>
</div>
</div>
<BackToTop />
<EmbeddedPageData filename="plugins.json" data={pluginsData} />
<script>
import '../scripts/listing-flyouts';
import '../scripts/pages/plugins';
</script>
<style>
.how-to-access {
background: var(--sl-color-bg-nav);
border: 1px solid var(--sl-color-hairline);
border-radius: 0.5rem;
padding: 1rem 1.25rem;
margin-bottom: 1.5rem;
font-size: 0.9rem;
line-height: 1.6;
}
.how-to-access p { margin: 0 0 0.5rem; }
.how-to-access p:last-child { margin-bottom: 0; }
.how-to-access ul {
margin: 0.25rem 0 0.5rem 1.25rem;
padding: 0;
}
.how-to-access li { margin: 0.2rem 0; }
.how-to-access code {
background: var(--sl-color-bg-inline-code);
padding: 0.1em 0.35em;
border-radius: 0.2rem;
font-size: 0.85em;
}
</style>
</StarlightPage>
<BaseLayout
title="Plugins | Awesome GitHub Copilot"
description="Browse community-contributed plugins for GitHub Copilot."
>
<PluginsCatalog
client:load
plugins={plugins as any}
tags={tags}
searchIndex={searchIndex}
/>
</BaseLayout>
+51 -132
View File
@@ -1,166 +1,85 @@
---
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
import BackToTop from "../../components/BackToTop.astro";
import BaseLayout from "../../layouts/BaseLayout.astro";
import { SkillDetail } from "../../components/brand/SkillDetail";
import { buildDetailToc } from "../../components/brand/DetailChassis";
import skillsData from "../../../public/data/skills.json";
import RawMarkdown from "../../components/pages/RawMarkdown.astro";
import type { HeaderItem } from "../../components/pages/Header.astro";
import DetailHeader from "../../components/pages/Header.astro";
import Breadcrumb from "../../components/pages/Breadcrumb.astro";
import Sidebar from "../../components/pages/Sidebar.astro";
import FileBrowser from "../../components/pages/FileBrowser.astro";
import { searchIndex } from "../../lib/site-data";
import { readResourceMarkdown, formatLastUpdated } from "../../lib/detail-page";
const GITHUB_BASE = "https://github.com/github/awesome-copilot/blob/main";
const GITHUB_TREE_BASE = "https://github.com/github/awesome-copilot/tree/main";
const GITHUB_BLOB_BASE = "https://github.com/github/awesome-copilot/blob/main";
const RAW_BASE = "https://raw.githubusercontent.com/github/awesome-copilot/main";
const REPO_IDENTIFIER = "github/awesome-copilot";
interface SkillFile {
name: string;
path: string;
name: string;
size?: number;
}
interface SkillItem extends HeaderItem {
interface SkillItem {
id: string;
name?: string;
title: string;
description?: string;
path: string;
skillFile: string;
files: SkillFile[];
files?: SkillFile[];
assets?: string[];
hasAssets?: boolean;
assetCount?: number;
lastUpdated?: string | null;
}
export function getStaticPaths() {
return (skillsData.items as SkillItem[]).map((item) => ({
const items = skillsData.items as SkillItem[];
return items.map((item, index) => ({
params: { id: item.id },
props: { item },
props: {
item,
previous: index > 0 ? items[index - 1] : undefined,
next: index < items.length - 1 ? items[index + 1] : undefined,
},
}));
}
const { item } = Astro.props as { item: SkillItem };
const { item, previous, next } = Astro.props as {
item: SkillItem;
previous?: SkillItem;
next?: SkillItem;
};
const { markdownHtml, frontmatterText, rawMarkdown } = readResourceMarkdown(
item.skillFile
);
// Skills are folders, so `loadDetailPage` (single-file, VS Code install URI)
// does not apply: SKILL.md is read directly and installation is a `gh` command.
const { markdownHtml, rawMarkdown } = readResourceMarkdown(item.skillFile);
const lastUpdated = formatLastUpdated(item.lastUpdated);
const { html } = buildDetailToc(markdownHtml);
const githubUrl = `${GITHUB_TREE_BASE}/${item.path}`;
const installCommand = `gh skills install ${REPO_IDENTIFIER} ${item.id}`;
const skillFileName = item.skillFile.split("/").pop() ?? "SKILL.md";
const fileCount = item.files.length;
const chips: { title: string; items: string[] }[] = [
{ title: "Files", items: [`${fileCount} file${fileCount === 1 ? "" : "s"}`] },
];
if (item.hasAssets && item.assetCount) {
chips.push({
title: "Assets",
items: [`${item.assetCount} asset${item.assetCount === 1 ? "" : "s"}`],
});
}
const base = import.meta.env.BASE_URL ?? "/";
const detailHref = (id: string) =>
`${base.endsWith("/") ? base : `${base}/`}skill/${id}/`.replace(/\/{2,}/g, "/");
const siblingOf = (sibling?: SkillItem) =>
sibling ? { label: sibling.title, href: detailHref(sibling.id) } : undefined;
---
<StarlightPage
frontmatter={{
title: item.title,
description: item.description,
template: "splash",
prev: false,
next: false,
editUrl: false,
}}
>
<div
id="main-content"
class="resource-detail-page skill-detail-page"
data-file-browser-page
data-bundle-id={item.id}
data-path={item.skillFile}
>
<RawMarkdown markdown={rawMarkdown} />
<div class="container">
<Breadcrumb
paths={[
{ name: "Home", href: "/" },
{ name: "Skills", href: "/skills/" },
]}
title={item.title}
/>
<DetailHeader item={item} />
<div class="detail-layout">
<div class="detail-main">
<FileBrowser
files={item.files}
primaryPath={item.skillFile}
primaryName={skillFileName}
primaryHtml={markdownHtml}
githubBase={GITHUB_BASE}
/>
</div>
<Sidebar
item={item}
lastUpdated={lastUpdated ?? undefined}
frontmatterText={frontmatterText || undefined}
githubUrl={githubUrl}
chips={chips}
>
<div class="skill-install" slot="install">
<p class="skill-install-label">Install with the GitHub CLI</p>
<div class="skill-install-command" data-install-command={installCommand}>
<code tabindex="0">{installCommand}</code>
<button
type="button"
class="skill-install-copy"
data-action="copy-install"
title="Copy install command"
aria-label="Copy install command"
>
<svg
viewBox="0 0 16 16"
width="15"
height="15"
fill="currentColor"
aria-hidden="true"
><path
d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"
></path><path
d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"
></path></svg
>
</button>
</div>
<button
type="button"
class="btn btn-primary skill-download-btn"
data-action="download-zip"
>
<svg
viewBox="0 0 16 16"
width="16"
height="16"
fill="currentColor"
aria-hidden="true"
><path
d="M2.75 14A1.75 1.75 0 0 1 1 12.25v-2.5a.75.75 0 0 1 1.5 0v2.5c0 .138.112.25.25.25h10.5a.25.25 0 0 0 .25-.25v-2.5a.75.75 0 0 1 1.5 0v2.5A1.75 1.75 0 0 1 13.25 14Z"
></path><path
d="M7.25 7.689V2a.75.75 0 0 1 1.5 0v5.689l1.97-1.969a.749.749 0 1 1 1.06 1.06l-3.25 3.25a.749.749 0 0 1-1.06 0L4.22 6.78a.749.749 0 1 1 1.06-1.06l1.97 1.969Z"
></path></svg
>
Download ZIP
</button>
</div>
</Sidebar>
</div>
</div>
</div>
<BackToTop />
<script>
import "../../scripts/pages/file-browser";
</script>
</StarlightPage>
<BaseLayout title={`${item.title} | Awesome GitHub Copilot`} description={item.description}>
<SkillDetail
client:load
item={item}
markdownHtml={html}
githubUrl={githubUrl}
githubBlobBase={GITHUB_BLOB_BASE}
rawBase={RAW_BASE}
installCommand={installCommand}
rawMarkdown={rawMarkdown}
lastUpdated={lastUpdated}
previous={siblingOf(previous)}
next={siblingOf(next)}
searchIndex={searchIndex}
/>
</BaseLayout>
+13 -47
View File
@@ -1,51 +1,17 @@
---
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
import ContributeCTA from '../components/ContributeCTA.astro';
import PageHeader from '../components/PageHeader.astro';
import EmbeddedPageData from '../components/EmbeddedPageData.astro';
import BackToTop from '../components/BackToTop.astro';
import skillsData from '../../public/data/skills.json';
import { renderSkillsHtml, sortSkills } from '../scripts/pages/skills-render';
import BaseLayout from "../layouts/BaseLayout.astro";
import { SkillsCatalog } from "../components/brand/SkillsCatalog";
import { skills, searchIndex } from "../lib/site-data";
const initialItems = sortSkills(skillsData.items, 'title');
---
<StarlightPage frontmatter={{ title: 'Skills', description: 'Self-contained agent skills with instructions and bundled resources', template: 'splash', prev: false, next: false, editUrl: false }}>
<div id="main-content" class="skills-page listing-cards-page">
<PageHeader title="Skills" description="Self-contained agent skills with instructions and bundled resources" icon="lightning" />
<div class="page-content">
<div class="container">
<div class="listing-toolbar">
<div class="listing-toolbar-row">
<div class="results-count" id="results-count" aria-live="polite">{initialItems.length} skills</div>
<details class="listing-controls">
<summary class="listing-controls-trigger btn btn-secondary btn-small">Sort</summary>
<div class="listing-controls-panel">
<div class="filters-bar" id="filters-bar">
<div class="filter-group">
<label for="sort-select">Sort:</label>
<select id="sort-select" aria-label="Sort by">
<option value="title">Name (A-Z)</option>
<option value="lastUpdated">Recently Updated</option>
</select>
</div>
</div>
</div>
</details>
</div>
</div>
<div class="resource-list" id="resource-list" role="list" set:html={renderSkillsHtml(initialItems)}></div>
<ContributeCTA resourceType="skills" />
</div>
</div>
</div>
<BackToTop />
<EmbeddedPageData filename="skills.json" data={skillsData} />
<script>
import '../scripts/listing-flyouts';
import '../scripts/pages/skills';
</script>
</StarlightPage>
<BaseLayout
title="Skills | Awesome GitHub Copilot"
description="Browse community-contributed agent skills for GitHub Copilot."
>
<SkillsCatalog
client:load
skills={skills as any}
searchIndex={searchIndex}
/>
</BaseLayout>
+5
View File
@@ -0,0 +1,5 @@
---
return Astro.redirect(
"https://github.com/github/awesome-copilot/blob/main/website/data/tools.yml",
);
---
+22
View File
@@ -0,0 +1,22 @@
---
export function getStaticPaths() {
return [
"daily-issues-report",
"ospo-contributors-report",
"ospo-org-health",
"ospo-release-compliance-checker",
"ospo-stale-repos",
"relevance-check",
"relevance-summary",
"weekly-comment-sync",
].map((slug) => ({
params: { slug },
props: {
target: `https://github.com/github/awesome-copilot/blob/main/workflows/${slug}.md`,
},
}));
}
const { target } = Astro.props;
return Astro.redirect(target);
---