mirror of
https://github.com/github/awesome-copilot.git
synced 2026-08-21 16:36:51 +00:00
927c1b5b38
* canvas-extension: new chat cards extension * codespell: resolve mispelling * code review, debug extension finding github app issue * Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * apply edits from code review * apply edits from code review * apply edits from code review --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
155 lines
6.7 KiB
Markdown
155 lines
6.7 KiB
Markdown
# Chat Cards
|
|
|
|
Interactive card deck for GitHub Copilot canvas. Instead of walls of text, the
|
|
agent explains things visually with a live deck of interactive cards: tab boxes,
|
|
tables, SVG charts, forms, collapsible sections, numbered outlines, rendered
|
|
markdown documents, and short video clips. Forms are two-way: submitting one
|
|
sends the values back to the conversation as the agent's next prompt, so a card
|
|
can gather context and steer the session as easily as it presents information.
|
|
|
|
The extension is a port of an MCP server, which is similar; but renders the cards
|
|
inline of the conversation, instead of rendering the HTML in a separate panel.
|
|
|
|

|
|
|
|
## What is in the deck
|
|
|
|
| Action | Card |
|
|
| ------ | ---- |
|
|
| `create_tab_card` | Tabbed views of one subject (markdown, text, HTML, or code per tab with copy buttons) |
|
|
| `create_table_card` | HTML table from explicit rows or loosely delimited raw text (delimiter auto-detected, URL cells become links) |
|
|
| `create_chart_card` | SVG bar, line, pie, or donut chart with legend and a collapsible data table |
|
|
| `create_form_card` | Form whose submission becomes the next conversation prompt |
|
|
| `create_reveal_card` | Collapsible show/hide sections with show-all/hide-all controls |
|
|
| `create_list_card` | Nested sequential outline numbered 1., 1.1., 1.1.1. |
|
|
| `create_markdown_card` | Render a markdown document as one card: H1 becomes the title, H2 sections fold into reveals |
|
|
| `create_video_card` | HTML video player for a short clip (direct file URL, `data:video/*`, or `blob:`) |
|
|
| `update_card` | Re-render an existing card in place |
|
|
| `remove_card` / `clear_cards` | Take cards off the deck |
|
|
| `list_cards` | See the deck (the user may have reordered or removed cards) |
|
|
| `get_form_responses` | Read form submissions, newest first |
|
|
|
|
Every card keeps the signature interactions of the MCP version: a "Copy card" button that
|
|
copies the card as a standalone HTML document, a `</>` toggle that shows the card's own
|
|
HTML source, a collapse toggle, drag to reorder cards in the deck, tutor terms that show
|
|
tooltips after a hover dwell, and model-defined right-click actions that send prompts back
|
|
to the conversation (`{{selection}}` in an action prompt is replaced with the user's
|
|
selected text). The deck has a light and a dark theme.
|
|
|
|
## Example action inputs
|
|
|
|
`create_table_card`:
|
|
|
|
```json
|
|
{
|
|
"title": "JavaScript array methods",
|
|
"headers": ["Method", "Purpose"],
|
|
"rows": [
|
|
["map", "Transform each item"],
|
|
["filter", "Keep matching items"],
|
|
["reduce", "Fold items into one value"]
|
|
]
|
|
}
|
|
```
|
|
|
|
`create_form_card`:
|
|
|
|
```json
|
|
{
|
|
"title": "Study preferences",
|
|
"promptTemplate": "Teach {{topic}} with {{style}} examples.",
|
|
"fields": [
|
|
{ "name": "topic", "label": "Topic", "required": true },
|
|
{ "name": "style", "type": "select", "options": ["practical", "theoretical"] }
|
|
]
|
|
}
|
|
```
|
|
|
|
When the user submits the form, the filled template is handed to the agent as the next
|
|
prompt. If the handoff fails, the card reveals the prompt text with a copy button, and the
|
|
agent can always read submissions later with `get_form_responses`.
|
|
|
|
## Installation
|
|
|
|
Requires Node.js 18.17 or newer and a GitHub Copilot client that supports
|
|
canvas extensions (*such as GitHub Copilot CLI*).
|
|
|
|
Drop this folder at `~/.copilot/extensions/chat-cards/` for user scope, or
|
|
in a repository at `.github/extensions/chat-cards/` for project scope. Then
|
|
install dependencies from inside the copied folder:
|
|
|
|
```bash
|
|
# User scope
|
|
cd ~/.copilot/extensions/chat-cards
|
|
|
|
# Or project scope, from the repository root
|
|
cd .github/extensions/chat-cards
|
|
|
|
npm install
|
|
```
|
|
|
|
`npm install` pulls the extension's single dependency (`@github/copilot-sdk`).
|
|
Then register the `extension/` folder with your Copilot client as a local
|
|
extension, start a session, and ask the agent to open the Chat Cards canvas.
|
|
|
|
Reload extensions in the GitHub Copilot app, then open the chat-cards canvas in
|
|
a conversation.
|
|
|
|
## How the port maps to the MCP server
|
|
|
|
| MCP server | Canvas extension |
|
|
| ---------- | ---------------- |
|
|
| MCP tools (`create_tab_card`, ...) | Canvas actions with the same names and input shapes |
|
|
| MCP Apps iframe / embedded HTML resource | Live canvas page served on `127.0.0.1` |
|
|
| Form submit posts `ui/message` to the host | Form submit calls `session.send({ prompt })` |
|
|
| Card HTML per tool result | Deck state pushed to the page over SSE |
|
|
| `src/cards/*` + `src/util/*` (TypeScript) | `cards-core.mjs` (dependency-free port, testable without the SDK) |
|
|
|
|
Deliberate differences:
|
|
|
|
- **Stricter HTML sanitizing.** Tab and reveal content can be markdown, text, code, or
|
|
HTML, like the MCP server, but where the server sanitizes HTML with a parser dependency,
|
|
the extension rebuilds it against a strict tag and attribute allowlist: unknown or
|
|
malformed tags render as visible literal text, attributes are re-emitted from scratch,
|
|
and closing tags are balanced. A tab or section with no usable content is rejected with
|
|
an error naming it, instead of rendering an empty panel.
|
|
- **No file, archive, or web-fetch actions.** `read_local_file`, `unpack_archive`,
|
|
`fetch_reference`, `mirror_web_form`, and `submit_web_form` stay MCP-only: a Copilot
|
|
agent already reads files and pages natively, so it passes content inline (for example
|
|
`create_markdown_card` takes the markdown itself rather than a path).
|
|
- **No multi-part splitting.** MCP hosts cap tool-result sizes, so the server splits large
|
|
documents into parts. The canvas renders directly and needs no parts; the deck instead
|
|
keeps at most 60 cards, dropping the oldest.
|
|
|
|
## Folder layout
|
|
|
|
```text
|
|
extension/
|
|
extension.mjs Canvas/session wiring, actions, local HTTP + SSE server
|
|
cards-core.mjs Card builders and rendering (no SDK import; unit-testable)
|
|
copilot-extension.json Plugin manifest for the awesome-copilot submission
|
|
package.json Extension package manifest
|
|
assets/
|
|
canvas.html The canvas page (theme, card runtime, deck UI)
|
|
preview.png Screenshot used as the extension logo/preview
|
|
```
|
|
|
|
## Security notes
|
|
|
|
- The card server binds to `127.0.0.1` on an ephemeral port; every request must carry the
|
|
per-canvas token, and request bodies are size-capped.
|
|
- All model- and user-supplied text is HTML-escaped before it reaches card markup, and the
|
|
markdown renderer emits only escaped text nodes.
|
|
- The canvas page loads no external scripts, stylesheets, or fonts.
|
|
- Prompts sent from the canvas (form submissions and context actions) are length-capped.
|
|
|
|
## Development
|
|
|
|
The rendering core has no SDK dependency, so it can be exercised directly:
|
|
|
|
```bash
|
|
node -e "import('./cards-core.mjs').then(m => console.log(m.buildTableCard({ title: 'Demo', rows: [['a', 'b']] }).summary))"
|
|
```
|
|
|
|
To run the extension itself, follow [Installation](#installation) above.
|