mirror of
https://github.com/tabler/tabler.git
synced 2026-10-12 05:06:55 +04:00
Publish agent skills index and ARD manifest in docs (#3078)
This commit is contained in:
1 parent
bcf5991167
commit
8ceb56bf42
9 files changed
+263
No files matched your search
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@tabler/docs": minor
|
||||
---
|
||||
|
||||
Added an agent skill under `/.well-known/agent-skills/` and an `ai-catalog.json` manifest to the docs.
|
||||
@@ -69,6 +69,17 @@ A short prompt is usually enough:
|
||||
Read https://docs.tabler.io/ui/components/card.md and build a card with a header, a footer and a table inside.
|
||||
```
|
||||
|
||||
## Agent skill
|
||||
|
||||
Coding agents that support [Agent Skills](https://agentskills.io) can load a ready-made Tabler skill instead of a prompt. It tells the agent how to install Tabler, how a page is structured, where to read the markup of a component and which rules keep the output consistent.
|
||||
|
||||
- `https://docs.tabler.io/.well-known/agent-skills/index.json` - the discovery index, per the [Agent Skills Discovery RFC](https://github.com/cloudflare/agent-skills-discovery-rfc).
|
||||
- `https://docs.tabler.io/.well-known/agent-skills/tabler/SKILL.md` - the skill itself.
|
||||
|
||||
To use it in a project, copy `SKILL.md` into your agent's skills directory, for example `.claude/skills/tabler/SKILL.md` for Claude Code. The index carries a `sha256` digest of the file, so a tool can check the copy is intact.
|
||||
|
||||
The same resources are also listed in `https://docs.tabler.io/.well-known/ai-catalog.json`, an [Agentic Resource Discovery](https://agenticresourcediscovery.org) manifest that registries use to index a site.
|
||||
|
||||
## Where the markup comes from
|
||||
|
||||
The examples are taken from the rendered page, not from the documentation source. A `.md` file gives you the same markup as the copy button on the html page, with every icon and helper already expanded to plain html.
|
||||
@@ -0,0 +1,45 @@
|
||||
// The agent skills the docs publish under /.well-known/agent-skills/ (Agent
|
||||
// Skills Discovery RFC 0.2.0, https://github.com/cloudflare/agent-skills-discovery-rfc).
|
||||
// The markdown lives next to this file; the `${site.*}` tokens in it are resolved
|
||||
// here so the skill and the rest of the docs quote the same urls and cdn version.
|
||||
import { createHash } from 'node:crypto'
|
||||
import { site } from '@shared/lib/site'
|
||||
import { cdnPackageSnippet } from './cdn-snippets.ts'
|
||||
import tablerSource from './agent-skills/tabler/SKILL.md?raw'
|
||||
|
||||
export const AGENT_SKILLS_SCHEMA = 'https://schemas.agentskills.io/discovery/0.2.0/schema.json'
|
||||
|
||||
const tokens: Record<string, string> = {
|
||||
'${site.docsUrl}': site.docsUrl,
|
||||
'${site.cdnUrl}': site.cdnUrl,
|
||||
'${site.previewUrl}': site.previewUrl,
|
||||
// same tags as the docs and llms.txt, with the SRI attributes when the hashes are current
|
||||
'${cdnPackageSnippet()}': cdnPackageSnippet(),
|
||||
}
|
||||
|
||||
const resolveTokens = (text: string) => Object.entries(tokens).reduce((result, [token, value]) => result.replaceAll(token, value), text)
|
||||
|
||||
const frontmatterField = (text: string, field: string) => text.match(new RegExp(`^${field}:\\s*(.+)$`, 'm'))?.[1]?.trim() ?? ''
|
||||
|
||||
export type AgentSkill = {
|
||||
name: string
|
||||
description: string
|
||||
/** the exact bytes served at `url`; the digest is computed over them */
|
||||
content: string
|
||||
url: string
|
||||
digest: string
|
||||
}
|
||||
|
||||
const define = (source: string): AgentSkill => {
|
||||
const content = resolveTokens(source)
|
||||
const name = frontmatterField(content, 'name')
|
||||
return {
|
||||
name,
|
||||
description: frontmatterField(content, 'description'),
|
||||
content,
|
||||
url: `/.well-known/agent-skills/${name}/SKILL.md`,
|
||||
digest: `sha256:${createHash('sha256').update(content).digest('hex')}`,
|
||||
}
|
||||
}
|
||||
|
||||
export const agentSkills: AgentSkill[] = [define(tablerSource)]
|
||||
@@ -0,0 +1,103 @@
|
||||
---
|
||||
name: tabler
|
||||
description: Build admin dashboards and app UIs with Tabler, the open source UI kit built on Bootstrap 5. Use when a project uses Tabler or @tabler/core, or when asked for a dashboard, admin panel, settings page, data table or form styled with Tabler. Covers installation, the page layout, where to read the real component markup and the rules that keep the output consistent.
|
||||
---
|
||||
|
||||
# Tabler
|
||||
|
||||
Tabler is a free, MIT-licensed UI kit for admin dashboards built on Bootstrap 5. It ships compiled CSS and JavaScript in the `@tabler/core` npm package, and every Bootstrap 5 class and JavaScript behaviour keeps working underneath it.
|
||||
|
||||
## Read the docs, do not guess class names
|
||||
|
||||
The documentation at ${site.docsUrl} is published for agents:
|
||||
|
||||
- `${site.docsUrl}/llms.txt` lists every page with a one-line summary and a class reference for every component. Read it first to find the right page.
|
||||
- Append `.md` to any docs url to get that page as markdown, for example `${site.docsUrl}/ui/components/card.md`. The file holds the prose and the exact markup of every example.
|
||||
- `${site.docsUrl}/llms-full.txt` is the whole documentation in one file.
|
||||
|
||||
Before writing markup for a component, fetch its `.md` page and copy the example that is closest to the task. Only use classes that appear in the docs or in Bootstrap 5. Do not invent `tabler-*` classes.
|
||||
|
||||
## Install
|
||||
|
||||
Plain HTML, from the CDN:
|
||||
|
||||
```html
|
||||
${cdnPackageSnippet()}
|
||||
```
|
||||
|
||||
Put the stylesheet in `<head>` and the script at the end of `<body>`. When the tags carry `integrity` and `crossorigin` attributes, keep them unchanged; the hashes belong to this exact version. In a project with a bundler install the package and import the same files:
|
||||
|
||||
```shell
|
||||
npm install @tabler/core
|
||||
```
|
||||
|
||||
```js
|
||||
import '@tabler/core/dist/css/tabler.min.css'
|
||||
import '@tabler/core/dist/js/tabler.min.js'
|
||||
```
|
||||
|
||||
Optional plugin stylesheets live next to the core file: `tabler-flags.min.css`, `tabler-socials.min.css`, `tabler-payments.min.css`, `tabler-vendors.min.css`, `tabler-marketing.min.css`, `tabler-themes.min.css`. Framework guides for React, Vue, Next.js, Nuxt, Angular, SvelteKit, Astro, Laravel, Rails, Django and Symfony are at `${site.docsUrl}/ui/getting-started/frameworks`.
|
||||
|
||||
## Page skeleton
|
||||
|
||||
Every app page follows the same structure. `${site.docsUrl}/ui/layout/page-layouts.md` has the complete markup for the horizontal, vertical and combined navigation variants.
|
||||
|
||||
```html
|
||||
<div class="page">
|
||||
<header class="navbar navbar-expand-md d-print-none">
|
||||
<!-- brand, navigation, user menu -->
|
||||
</header>
|
||||
<div class="page-wrapper">
|
||||
<div class="page-header d-print-none">
|
||||
<div class="container-xl">
|
||||
<div class="row g-2 align-items-center">
|
||||
<div class="col">
|
||||
<div class="page-pretitle">Overview</div>
|
||||
<h2 class="page-title">Dashboard</h2>
|
||||
</div>
|
||||
<div class="col-auto ms-auto d-print-none">
|
||||
<!-- page actions -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="page-body">
|
||||
<div class="container-xl">
|
||||
<div class="row row-deck row-cards">
|
||||
<!-- cards -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="footer footer-transparent d-print-none">
|
||||
<div class="container-xl"><!-- footer links --></div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
- A sidebar is `<aside class="navbar navbar-vertical navbar-expand-lg">` placed before `.page-wrapper`.
|
||||
- Content goes in cards: `.card` with `.card-header`, `.card-body`, `.card-footer`. Put cards in `.row.row-deck.row-cards` so they align in height.
|
||||
- Use `.container-xl` for boxed pages and `.container-fluid` for full-width ones.
|
||||
|
||||
## Icons
|
||||
|
||||
Icons are inline SVGs from Tabler Icons (`https://tabler.io/icons`, also the `@tabler/icons` packages). Give the `<svg>` the `icon` class, and add `icon-sm`, `icon-md` or `icon-lg` for size. In a button or link the icon goes before the text. Decorative icons get `aria-hidden="true"`.
|
||||
|
||||
## Colour modes and theming
|
||||
|
||||
Set `data-bs-theme="dark"`, `"light"` or `"auto"` on `<html>` to pick the colour mode. `data-bs-theme-base`, `data-bs-theme-font`, `data-bs-theme-primary` and `data-bs-theme-radius` adjust the gray shade, font family, primary colour and corner radius. Details are at `${site.docsUrl}/ui/getting-started/color-modes.md` and `${site.docsUrl}/ui/getting-started/customize.md`.
|
||||
|
||||
Colours are Bootstrap-style names: `primary`, `secondary`, `success`, `warning`, `danger`, `info` plus Tabler's palette (`blue`, `azure`, `indigo`, `purple`, `pink`, `red`, `orange`, `yellow`, `lime`, `green`, `teal`, `cyan`). They work as `bg-{color}`, `text-{color}`, `btn-{color}`, `badge bg-{color}`, `alert-{color}` and so on.
|
||||
|
||||
## Interactive components
|
||||
|
||||
Dropdowns, modals, offcanvas, tooltips, popovers, toasts, tabs and collapse use the Bootstrap 5 data API: `data-bs-toggle`, `data-bs-target`, `data-bs-dismiss`. Tooltips and popovers are initialised automatically by `tabler.min.js` when the element has `data-bs-toggle="tooltip"` or `data-bs-toggle="popover"`. No extra JavaScript is needed for the rest.
|
||||
|
||||
## Rules of thumb
|
||||
|
||||
- Reach for a Tabler component before writing custom CSS. Spacing, colour and typography come from Bootstrap utilities (`mb-3`, `text-secondary`, `fw-bold`).
|
||||
- Wrap groups of buttons, badges, avatars or tags in `.btn-list`, `.badge-list`, `.avatar-list` or `.tag-list` instead of adding margins by hand.
|
||||
- Forms use `.form-label`, `.form-control`, `.form-select`, `.form-check` and `.form-hint`, the same as Bootstrap.
|
||||
- Tables use `.table` inside `.table-responsive`, often with `.card-table` when they fill a card.
|
||||
- Keep markup accessible: real `<button>` elements, labels on inputs, `aria-label` on icon-only buttons.
|
||||
- The live demo at ${site.previewUrl} shows every layout and component in use. When unsure how pieces fit together, look at the matching demo page.
|
||||
@@ -0,0 +1,16 @@
|
||||
// Agent Skills Discovery index: which skills this site publishes and where.
|
||||
import type { APIRoute } from 'astro'
|
||||
import { AGENT_SKILLS_SCHEMA, agentSkills } from '@lib/agent-skills'
|
||||
|
||||
export const prerender = true
|
||||
|
||||
export const GET: APIRoute = () => {
|
||||
const body = {
|
||||
$schema: AGENT_SKILLS_SCHEMA,
|
||||
skills: agentSkills.map(({ name, description, url, digest }) => ({ name, type: 'skill-md', description, url, digest })),
|
||||
}
|
||||
|
||||
return new Response(`${JSON.stringify(body, null, 2)}\n`, {
|
||||
headers: { 'Content-Type': 'application/json; charset=utf-8' },
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// The `tabler` skill, byte-identical to what index.json's digest was computed over.
|
||||
import type { APIRoute } from 'astro'
|
||||
import { agentSkills } from '@lib/agent-skills'
|
||||
|
||||
export const prerender = true
|
||||
|
||||
export const GET: APIRoute = () => {
|
||||
const skill = agentSkills.find((entry) => entry.name === 'tabler')
|
||||
if (!skill) return new Response('Not found', { status: 404 })
|
||||
|
||||
return new Response(skill.content, {
|
||||
headers: { 'Content-Type': 'text/markdown; charset=utf-8' },
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// Agentic Resource Discovery manifest (https://agenticresourcediscovery.org):
|
||||
// the machine-readable resources this site offers, so registries can index
|
||||
// them. Everything listed here already exists elsewhere on the site.
|
||||
import type { APIRoute } from 'astro'
|
||||
import { site } from '@shared/lib/site'
|
||||
import { agentSkills } from '@lib/agent-skills'
|
||||
|
||||
export const prerender = true
|
||||
|
||||
const domain = new URL(site.docsUrl).hostname
|
||||
const urn = (namespace: string, name: string) => `urn:air:${domain}:${namespace}:${name}`
|
||||
|
||||
export const GET: APIRoute = () => {
|
||||
const body = {
|
||||
specVersion: '1.0',
|
||||
host: {
|
||||
displayName: site.title,
|
||||
identifier: `did:web:${domain}`,
|
||||
url: site.docsUrl,
|
||||
},
|
||||
entries: [
|
||||
{
|
||||
identifier: urn('docs', 'llms-txt'),
|
||||
displayName: 'Tabler documentation index (llms.txt)',
|
||||
description: 'Index of every documentation page with a one-line summary, links to the markdown version of each page and a class reference for every component.',
|
||||
type: 'text/markdown',
|
||||
url: `${site.docsUrl}/llms.txt`,
|
||||
representativeQueries: ['Which components does Tabler have?', 'What classes does the Tabler card component use?', 'Where is the Tabler documentation for forms?'],
|
||||
},
|
||||
{
|
||||
identifier: urn('docs', 'llms-full-txt'),
|
||||
displayName: 'Tabler documentation, full text (llms-full.txt)',
|
||||
description: 'The whole Tabler documentation as a single markdown file, including the markup of every example.',
|
||||
type: 'text/markdown',
|
||||
url: `${site.docsUrl}/llms-full.txt`,
|
||||
representativeQueries: ['Give me the complete Tabler documentation', 'How do I build a dashboard layout with Tabler?', 'Show the markup of a Tabler modal'],
|
||||
},
|
||||
...agentSkills.map((skill) => ({
|
||||
identifier: urn('skills', skill.name),
|
||||
displayName: `Agent skill: ${skill.name}`,
|
||||
description: skill.description,
|
||||
type: 'text/markdown',
|
||||
url: `${site.docsUrl}${skill.url}`,
|
||||
representativeQueries: ['Build an admin dashboard with Tabler', 'Install Tabler in my project', 'Which Tabler classes should I use for a settings page?'],
|
||||
})),
|
||||
{
|
||||
identifier: urn('docs', 'sitemap'),
|
||||
displayName: 'Sitemap',
|
||||
description: 'Every html page of the documentation.',
|
||||
type: 'application/xml',
|
||||
url: `${site.docsUrl}/sitemap.xml`,
|
||||
representativeQueries: ['List all Tabler documentation pages', 'Crawl the Tabler docs'],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
return new Response(`${JSON.stringify(body, null, 2)}\n`, {
|
||||
headers: { 'Content-Type': 'application/json; charset=utf-8' },
|
||||
})
|
||||
}
|
||||
@@ -89,6 +89,8 @@ Every page is available as markdown by appending \`.md\` to its url, for example
|
||||
|
||||
The markup is taken from the rendered pages, so every example is plain html — the same markup the copy button on the page gives you.
|
||||
|
||||
An agent skill for building with Tabler is published at \`${site.docsUrl}/.well-known/agent-skills/tabler/SKILL.md\` (index: \`${site.docsUrl}/.well-known/agent-skills/index.json\`).
|
||||
|
||||
${sections.join('\n\n')}
|
||||
|
||||
${classReference}
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
"source": "/(dist|preview|img|static|css|js)/(.*)",
|
||||
"headers": [{ "key": "Cache-Control", "value": "public, max-age=3600, stale-while-revalidate=86400" }]
|
||||
},
|
||||
{
|
||||
"source": "/.well-known/(.*)",
|
||||
"headers": [
|
||||
{ "key": "Access-Control-Allow-Origin", "value": "*" },
|
||||
{ "key": "Cache-Control", "value": "public, max-age=3600, stale-while-revalidate=86400" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "/(llms|llms-full)\\.txt",
|
||||
"headers": [{ "key": "Cache-Control", "value": "public, max-age=3600, stale-while-revalidate=86400" }]
|
||||
|
||||
Reference in new issue
Block a user