From 97dc0d9ad63b718ee535c2e771050bec2dbac45a Mon Sep 17 00:00:00 2001 From: Kaan Kacar Date: Fri, 12 Jun 2026 16:56:06 +0300 Subject: [PATCH 1/8] Add community skills search, pagination, and an "Add your skill" section - CommunitySearch client island filters the server-rendered community cards by title or any part of the description, and paginates them at 8 per page. Cards stay in the static HTML for agents and crawlers. - "Add your skill" block explains how to get listed via a PR against ECOSYSTEM_CARDS in site/src/data/skills.ts, entry format inlined. - The llms.txt community blurb now tells agents how to submit a skill. --- site/CLAUDE.md | 15 ++- site/scripts/generate-llms-txt.mjs | 2 +- site/src/app/_components/CommunitySearch.tsx | 106 +++++++++++++++++++ site/src/app/page.tsx | 44 +++++++- site/src/app/styles.scss | 78 ++++++++++++++ 5 files changed, 238 insertions(+), 7 deletions(-) create mode 100644 site/src/app/_components/CommunitySearch.tsx diff --git a/site/CLAUDE.md b/site/CLAUDE.md index e6e05b1..a5764e1 100644 --- a/site/CLAUDE.md +++ b/site/CLAUDE.md @@ -34,10 +34,13 @@ client component. `scripts/generate-llms-txt.mjs`. - `src/app/page.tsx`: server-rendered landing page. - `src/app/_components/`: `SkillCard` (server), `icons` (server, inline - SVGs), `CopyButton` / `SkillsFilter` / `ThemeSwitchIsland` (client - islands). `SkillsFilter` is used twice — once for the skills grid - (`All` + categories) and once for the Installing section's tabs - (Claude Code / Cursor / npx skills / Clone repo). + SVGs), `CopyButton` / `SkillsFilter` / `CommunitySearch` / + `ThemeSwitchIsland` (client islands). `SkillsFilter` is used twice — once + for the skills grid (`All` + categories) and once for the Installing + section's tabs (Claude Code / Cursor / npx skills / Clone repo). + `CommunitySearch` adds search + pagination to the community grid; the + cards render server-side and are passed in as children, so every entry + stays in the static HTML. - `scripts/copy-skills.mjs`: mirrors `../skills/` into `public/` on `predev` / `prebuild`. No network; just `cp`. - `scripts/generate-llms-txt.mjs`: writes `public/llms.txt` from @@ -80,7 +83,9 @@ then append to `SKILL_CARD_SOURCES`: `pnpm sync:skills && pnpm dev` to verify. New category? Add it to the `FilterType` union and the `FILTERS` array. -**Ecosystem:** external link, no upstream copy. +**Ecosystem:** external link, no upstream copy. New entries are picked +up automatically by the community search, pagination, and llms.txt; no +other wiring needed. ```ts { diff --git a/site/scripts/generate-llms-txt.mjs b/site/scripts/generate-llms-txt.mjs index bd7afd0..dc6b9af 100644 --- a/site/scripts/generate-llms-txt.mjs +++ b/site/scripts/generate-llms-txt.mjs @@ -137,7 +137,7 @@ if (realEcosystem.length > 0) { lines.push("## Community Built"); lines.push(""); lines.push( - "Other community-built skills that may be helpful for your build. These aren't installed via the methods above; each project has its own setup, so follow the link on each entry. Not endorsed by the Stellar Foundation; do your own research.", + "Other community-built skills that may be helpful for your build. These aren't installed via the methods above; each project has its own setup, so follow the link on each entry. Not endorsed by the Stellar Foundation; do your own research. To get a skill listed here, open a pull request adding it to ECOSYSTEM_CARDS in site/src/data/skills.ts of https://github.com/stellar/stellar-dev-skill.", ); lines.push(""); for (const c of realEcosystem) { diff --git a/site/src/app/_components/CommunitySearch.tsx b/site/src/app/_components/CommunitySearch.tsx new file mode 100644 index 0000000..f926772 --- /dev/null +++ b/site/src/app/_components/CommunitySearch.tsx @@ -0,0 +1,106 @@ +"use client"; + +import { Children, ReactNode, useId, useState } from "react"; + +import { Input, Pagination } from "@stellar/design-system"; + +/** Cards shown per page in the 2-column community grid. */ +const PAGE_SIZE = 8; + +type Props = { + /** Lowercased "title description" haystack for each child, in the + * same render order as `children`. Search runs against these strings + * so the cards themselves stay server-rendered; this island only + * toggles wrapper visibility. */ + searchTexts: readonly string[]; + /** Pre-rendered card markup (server). One child per searchTexts + * entry, same order. */ + children: ReactNode; +}; + +/** + * Client island that adds search and pagination to the community + * skills grid. The cards render server-side and are passed in as + * `children` (same slot pattern as `SkillsFilter`), so every card's + * title/description/link stays in the static HTML for non-JS clients; + * this component only hides wrappers that don't match the query or + * fall outside the current page. + * + * Search matches a case-insensitive substring of the card title or any + * part of its description, so a query like "DEX" finds a card that + * only mentions it mid-description. + */ +export const CommunitySearch = ({ searchTexts, children }: Props) => { + const searchInputId = useId(); + const [query, setQuery] = useState(""); + const [page, setPage] = useState(1); + + const needle = query.trim().toLowerCase(); + const matches = searchTexts.reduce((acc, text, index) => { + if (text.includes(needle)) acc.push(index); + return acc; + }, []); + + // Clamp instead of trusting `page`: a new query can shrink the match + // list below the previously selected page. + const pageCount = Math.max(1, Math.ceil(matches.length / PAGE_SIZE)); + const currentPage = Math.min(page, pageCount); + const visible = new Set( + matches.slice((currentPage - 1) * PAGE_SIZE, currentPage * PAGE_SIZE), + ); + + return ( + <> +
+ { + setQuery(event.target.value); + setPage(1); + }} + /> +
+ +
+ {Children.map(children, (child, index) => ( +
+ {child} +
+ ))} +
+ + {matches.length === 0 && ( +

+ No community skills match your search. +

+ )} + + {matches.length > PAGE_SIZE && ( +
+ +
+ )} + + + {needle === "" + ? "" + : `${matches.length} community skill${ + matches.length === 1 ? "" : "s" + } matching`} + + + ); +}; diff --git a/site/src/app/page.tsx b/site/src/app/page.tsx index ab3c0cf..a409be0 100644 --- a/site/src/app/page.tsx +++ b/site/src/app/page.tsx @@ -8,6 +8,7 @@ import { } from "@/data/skills"; import { readSkillMeta } from "@/lib/skill-meta.mjs"; +import { CommunitySearch } from "./_components/CommunitySearch"; import { CopyButton } from "./_components/CopyButton"; import { GitHubIcon, LinkExternal01Icon } from "./_components/icons"; import { SkillCard } from "./_components/SkillCard"; @@ -50,6 +51,16 @@ const hostFromOrigin = (origin: string) => origin.replace(/^https?:\/\//, ""); const githubSourceUrl = (source: string) => `https://github.com/${GITHUB_REPOSITORY}/blob/${GITHUB_SOURCE_REF}/${source}`; +// Example ECOSYSTEM_CARDS entry shown in the "Add your skill" block. +// Mirrors the format documented in site/CLAUDE.md. +const ADD_SKILL_SNIPPET = `{ + title: "Project Name", + description: "Verb-led summary of what the skill does.", + pathLabel: "owner/repo", + copyValue: "https://github.com/owner/repo/blob/main/path/to/SKILL.md", + category: "Ecosystem", +}`; + export default function LandingPage() { const host = hostFromOrigin(SITE_ORIGIN); const heroValue = `Read ${host} before you start building on Stellar.`; @@ -188,7 +199,11 @@ export default function LandingPage() { tool or resource. Inclusion in this list does not imply any warranty, security audit, or official recommendation.

-
+ + `${c.title} ${c.description}`.toLowerCase(), + )} + > {ECOSYSTEM_CARDS.map((c) => ( ))} + + +
+

Add your skill

+

+ Built a skill that helps people develop on Stellar?{" "} + + Open a pull request + {" "} + that adds an entry to ECOSYSTEM_CARDS in{" "} + + site/src/data/skills.ts + + . Once merged, your skill shows up here and in{" "} + llms.txt. +

+
+              {ADD_SKILL_SNIPPET}
+            
diff --git a/site/src/app/styles.scss b/site/src/app/styles.scss index bf2b319..9b2cbfa 100644 --- a/site/src/app/styles.scss +++ b/site/src/app/styles.scss @@ -557,6 +557,84 @@ p.SkillsLanding__sectionDescription { } } +// Community search input (client island; the cards stay server-rendered). +.SkillsLanding__communitySearch { + max-width: pxToRem(360px); + margin-bottom: pxToRem(16px); +} + +// Each grid cell wraps one server-rendered card. `display: grid` makes +// the card stretch to the full row height, like a direct grid child. +.SkillsLanding__communityItem { + display: grid; +} + +// Hidden by search/pagination. The card markup stays in the HTML so +// non-JS clients (AI agents, crawlers) still see every entry. +.SkillsLanding__communityItem[data-hidden="true"] { + display: none; +} + +.SkillsLanding__communityEmpty { + font-family: "Inter", sans-serif; + font-size: pxToRem(12px); + line-height: pxToRem(18px); + color: var(--sds-clr-gray-11); + margin: pxToRem(8px) 0 0 0; +} + +.SkillsLanding__communityPagination { + display: flex; + justify-content: center; + margin-top: pxToRem(16px); +} + +// "Add your skill" block at the bottom of the community panel. +.SkillsLanding__addSkill { + margin-top: pxToRem(24px); + padding-top: pxToRem(16px); + border-top: 1px solid var(--sds-clr-gray-06); +} + +h3.SkillsLanding__addSkillTitle { + font-family: "Inter", sans-serif; + font-size: pxToRem(14px); + font-weight: 600; + line-height: pxToRem(20px); + margin: 0 0 pxToRem(4px) 0; + color: var(--sds-clr-gray-12); +} + +p.SkillsLanding__addSkillText { + font-family: "Inter", sans-serif; + font-size: pxToRem(12px); + font-weight: 400; + line-height: pxToRem(18px); + margin: 0 0 pxToRem(12px) 0; + color: var(--sds-clr-gray-11); + + a { + color: var(--sds-clr-gray-12); + } + + code { + font-family: "Roboto Mono", monospace; + } +} + +.SkillsLanding__addSkillSnippet { + font-family: "Roboto Mono", monospace; + font-size: pxToRem(12px); + line-height: pxToRem(18px); + color: var(--sds-clr-gray-11); + background-color: var(--sds-clr-gray-03); + border: 1px solid var(--sds-clr-gray-06); + border-radius: pxToRem(8px); + padding: pxToRem(12px); + margin: 0; + overflow-x: auto; +} + // Installer panel: independent tablist; only the active tab's panel // shows. Stacks via flex column because exactly one panel is visible. .SkillsLanding__installerPanel { From 2d5fb7d6247b5688878031e2ee99a23e5e8fdcfb Mon Sep 17 00:00:00 2001 From: Kaan Kacar Date: Fri, 12 Jun 2026 17:48:05 +0300 Subject: [PATCH 2/8] Drop the unused category field from ecosystem entries Nothing reads it: the community grid renders outside SkillsFilter, the search haystack is title + description only, and llms.txt lists the community section flat. Requiring contributors to copy a field that does nothing (and silently accepts any FilterType value) just invites confusion, so remove it from the type, the four entries, the on-site snippet, and the docs. The standards skill's "Ecosystem" category in SKILL_CARD_SOURCES stays; that one drives the main grid's filter tab. --- site/CLAUDE.md | 1 - site/README.md | 2 -- site/scripts/generate-llms-txt.mjs | 1 - site/src/app/page.tsx | 1 - site/src/data/skills.ts | 5 ----- 5 files changed, 10 deletions(-) diff --git a/site/CLAUDE.md b/site/CLAUDE.md index a5764e1..00c0061 100644 --- a/site/CLAUDE.md +++ b/site/CLAUDE.md @@ -93,7 +93,6 @@ other wiring needed. description: "Verb-led summary of what the skill does.", pathLabel: "owner/repo", copyValue: "https://github.com/owner/repo/blob/main/path/to/SKILL.md", - category: "Ecosystem", } ``` diff --git a/site/README.md b/site/README.md index 13506f5..4a97fdc 100644 --- a/site/README.md +++ b/site/README.md @@ -151,7 +151,6 @@ Append to `ECOSYSTEM_CARDS` in `src/data/skills.ts`: description: "Verb-led summary of what the skill does.", pathLabel: "owner/repo", copyValue: "https://github.com/owner/repo/blob/main/path/to/SKILL.md", - category: "Ecosystem", } ``` @@ -160,7 +159,6 @@ Append to `ECOSYSTEM_CARDS` in `src/data/skills.ts`: - `copyValue` is the full URL written to the clipboard when the user clicks the pill — point it directly at the raw SKILL.md so an agent can fetch it. -- `category` is always `"Ecosystem"` for this section. Run `pnpm dev` to verify the card renders. No `sync:skills` step needed since ecosystem entries aren't mirrored locally. diff --git a/site/scripts/generate-llms-txt.mjs b/site/scripts/generate-llms-txt.mjs index dc6b9af..a822c54 100644 --- a/site/scripts/generate-llms-txt.mjs +++ b/site/scripts/generate-llms-txt.mjs @@ -53,7 +53,6 @@ const ecosystemCards = ECOSYSTEM_CARDS.map((c) => ({ title: c.title, description: c.description, copyValue: c.copyValue, - category: c.category, })); const filters = [...FILTERS]; diff --git a/site/src/app/page.tsx b/site/src/app/page.tsx index a409be0..0828915 100644 --- a/site/src/app/page.tsx +++ b/site/src/app/page.tsx @@ -58,7 +58,6 @@ const ADD_SKILL_SNIPPET = `{ description: "Verb-led summary of what the skill does.", pathLabel: "owner/repo", copyValue: "https://github.com/owner/repo/blob/main/path/to/SKILL.md", - category: "Ecosystem", }`; export default function LandingPage() { diff --git a/site/src/data/skills.ts b/site/src/data/skills.ts index fcd5960..3db3892 100644 --- a/site/src/data/skills.ts +++ b/site/src/data/skills.ts @@ -71,7 +71,6 @@ export type EcosystemCardSource = { pathLabel: string; /** Full URL copied to clipboard when the user clicks the pill. */ copyValue: string; - category: FilterType; }; /** @@ -142,7 +141,6 @@ export const ECOSYSTEM_CARDS: readonly EcosystemCardSource[] = [ pathLabel: "OpenZeppelin/openzeppelin-skills", copyValue: "https://github.com/OpenZeppelin/openzeppelin-skills/blob/main/skills/setup-stellar-contracts/SKILL.md", - category: "Ecosystem", }, { title: "DeFindex SDK", @@ -151,7 +149,6 @@ export const ECOSYSTEM_CARDS: readonly EcosystemCardSource[] = [ pathLabel: "paltalabs/defindex-sdk", copyValue: "https://github.com/paltalabs/defindex-sdk/blob/main/defindex-sdk-skill.md", - category: "Ecosystem", }, { title: "Soroswap SDK", @@ -159,7 +156,6 @@ export const ECOSYSTEM_CARDS: readonly EcosystemCardSource[] = [ "Trade on Soroswap DEX from a backend, bot, or swap widget using the @soroswap/sdk TypeScript package. Covers token swaps, liquidity pool operations, price and route queries, API key handling, and signing flows for both server keypairs and browser wallets.", pathLabel: "soroswap/sdk", copyValue: "https://github.com/soroswap/sdk/blob/main/soroswap-sdk-skill.md", - category: "Ecosystem", }, { title: "Trustless Work Escrow", @@ -168,6 +164,5 @@ export const ECOSYSTEM_CARDS: readonly EcosystemCardSource[] = [ pathLabel: "Trustless-Work/trustless-work-dev-skill", copyValue: "https://github.com/Trustless-Work/trustless-work-dev-skill/blob/main/SKILL.md", - category: "Ecosystem", }, ] as const; From 07c77a5a1e46c78d560f5fce39ca621310674826 Mon Sep 17 00:00:00 2001 From: Kaan Kacar Date: Fri, 12 Jun 2026 18:00:28 +0300 Subject: [PATCH 3/8] Match search width to the card column and show a result count - Search input now spans exactly one grid column so it lines up with the card below it (full width on narrow screens) - "Showing x of y skills" line renders under the community grid, above the Add your skill block, and doubles as the aria-live status for search results (replaces the screen-reader-only span) --- site/src/app/_components/CommunitySearch.tsx | 17 +++++++++-------- site/src/app/styles.scss | 17 ++++++++++++++++- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/site/src/app/_components/CommunitySearch.tsx b/site/src/app/_components/CommunitySearch.tsx index f926772..ee0095e 100644 --- a/site/src/app/_components/CommunitySearch.tsx +++ b/site/src/app/_components/CommunitySearch.tsx @@ -83,6 +83,15 @@ export const CommunitySearch = ({ searchTexts, children }: Props) => {

)} +

+ Showing {visible.size} of {searchTexts.length} skill + {searchTexts.length === 1 ? "" : "s"} +

+ {matches.length > PAGE_SIZE && (
{ />
)} - - - {needle === "" - ? "" - : `${matches.length} community skill${ - matches.length === 1 ? "" : "s" - } matching`} - ); }; diff --git a/site/src/app/styles.scss b/site/src/app/styles.scss index 9b2cbfa..04fda3f 100644 --- a/site/src/app/styles.scss +++ b/site/src/app/styles.scss @@ -558,9 +558,15 @@ p.SkillsLanding__sectionDescription { } // Community search input (client island; the cards stay server-rendered). +// Width matches one grid column (50% minus half the 16px column gap) so +// the input lines up with the card below it. .SkillsLanding__communitySearch { - max-width: pxToRem(360px); + width: calc(50% - #{pxToRem(8px)}); margin-bottom: pxToRem(16px); + + @media (max-width: 600px) { + width: 100%; + } } // Each grid cell wraps one server-rendered card. `display: grid` makes @@ -583,6 +589,15 @@ p.SkillsLanding__sectionDescription { margin: pxToRem(8px) 0 0 0; } +// "Showing x of y skills" line under the community grid. +.SkillsLanding__communityCount { + font-family: "Inter", sans-serif; + font-size: pxToRem(12px); + line-height: pxToRem(18px); + color: var(--sds-clr-gray-11); + margin: pxToRem(12px) 0 0 0; +} + .SkillsLanding__communityPagination { display: flex; justify-content: center; From 500e2a4d8e4fb5eb9e07e56c029bbf57e49269e0 Mon Sep 17 00:00:00 2001 From: Kaan Kacar Date: Fri, 12 Jun 2026 19:01:46 +0300 Subject: [PATCH 4/8] Size the community count line like the card descriptions --- site/src/app/styles.scss | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/site/src/app/styles.scss b/site/src/app/styles.scss index 04fda3f..b58a408 100644 --- a/site/src/app/styles.scss +++ b/site/src/app/styles.scss @@ -589,11 +589,13 @@ p.SkillsLanding__sectionDescription { margin: pxToRem(8px) 0 0 0; } -// "Showing x of y skills" line under the community grid. +// "Showing x of y skills" line under the community grid. Sized to match +// the card description text (.SkillsCard__description). .SkillsLanding__communityCount { font-family: "Inter", sans-serif; - font-size: pxToRem(12px); - line-height: pxToRem(18px); + font-size: pxToRem(14px); + font-weight: 400; + line-height: pxToRem(20px); color: var(--sds-clr-gray-11); margin: pxToRem(12px) 0 0 0; } From 286c31af7204f90ea99e4a6bd005047ff3bd0cd7 Mon Sep 17 00:00:00 2001 From: Kaan Kacar Date: Mon, 22 Jun 2026 23:40:15 +0300 Subject: [PATCH 5/8] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- site/src/app/_components/CommunitySearch.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/site/src/app/_components/CommunitySearch.tsx b/site/src/app/_components/CommunitySearch.tsx index ee0095e..d6fdaaa 100644 --- a/site/src/app/_components/CommunitySearch.tsx +++ b/site/src/app/_components/CommunitySearch.tsx @@ -69,6 +69,7 @@ export const CommunitySearch = ({ searchTexts, children }: Props) => {
{Children.map(children, (child, index) => (
From 69eafc79aae9a39e120b873f27cccf47b01fa434 Mon Sep 17 00:00:00 2001 From: Kaan Kacar Date: Mon, 22 Jun 2026 23:49:10 +0300 Subject: [PATCH 6/8] Size the community count line as a caption, not body text Showing it at 14px (card-description size) made the status line as prominent as the cards themselves and inconsistent with the panel's other captions. Drop it to 12px to match sectionDescription and addSkillText, so it reads as a quiet status line. --- site/src/app/styles.scss | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/site/src/app/styles.scss b/site/src/app/styles.scss index b58a408..30c4073 100644 --- a/site/src/app/styles.scss +++ b/site/src/app/styles.scss @@ -589,13 +589,15 @@ p.SkillsLanding__sectionDescription { margin: pxToRem(8px) 0 0 0; } -// "Showing x of y skills" line under the community grid. Sized to match -// the card description text (.SkillsCard__description). +// "Showing x of y skills" status line under the community grid. Sized +// like the panel's other captions (.SkillsLanding__sectionDescription, +// .SkillsLanding__addSkillText) rather than card body text, so it reads +// as a quiet status line instead of content. .SkillsLanding__communityCount { font-family: "Inter", sans-serif; - font-size: pxToRem(14px); + font-size: pxToRem(12px); font-weight: 400; - line-height: pxToRem(20px); + line-height: pxToRem(18px); color: var(--sds-clr-gray-11); margin: pxToRem(12px) 0 0 0; } From 1f0badd9ace486d2741c220f0e05aabf4932a245 Mon Sep 17 00:00:00 2001 From: Kaan Kacar Date: Tue, 23 Jun 2026 00:00:47 +0300 Subject: [PATCH 7/8] Style the community count line at 14px semibold Match the card-description size (14px) and the section-heading weight (Inter semibold), so the status line is legible and clearly distinct from the muted helper text around it. --- site/src/app/styles.scss | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/site/src/app/styles.scss b/site/src/app/styles.scss index 30c4073..55abb6b 100644 --- a/site/src/app/styles.scss +++ b/site/src/app/styles.scss @@ -589,15 +589,14 @@ p.SkillsLanding__sectionDescription { margin: pxToRem(8px) 0 0 0; } -// "Showing x of y skills" status line under the community grid. Sized -// like the panel's other captions (.SkillsLanding__sectionDescription, -// .SkillsLanding__addSkillText) rather than card body text, so it reads -// as a quiet status line instead of content. +// "Showing x of y skills" status line under the community grid. +// Sized like the card description text (14px) and weighted like the +// "Community skills" section heading (Inter, semibold). .SkillsLanding__communityCount { font-family: "Inter", sans-serif; - font-size: pxToRem(12px); - font-weight: 400; - line-height: pxToRem(18px); + font-size: pxToRem(14px); + font-weight: 600; + line-height: pxToRem(20px); color: var(--sds-clr-gray-11); margin: pxToRem(12px) 0 0 0; } From a915c3170e234fd1d0a72445f9dc98224a74ff5b Mon Sep 17 00:00:00 2001 From: Kaan Kacar Date: Tue, 23 Jun 2026 00:07:50 +0300 Subject: [PATCH 8/8] Match the community count line color to the card titles (gray-12) --- site/src/app/styles.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/site/src/app/styles.scss b/site/src/app/styles.scss index 55abb6b..50a90b9 100644 --- a/site/src/app/styles.scss +++ b/site/src/app/styles.scss @@ -590,14 +590,14 @@ p.SkillsLanding__sectionDescription { } // "Showing x of y skills" status line under the community grid. -// Sized like the card description text (14px) and weighted like the -// "Community skills" section heading (Inter, semibold). +// Matches the ecosystem card title (h3.SkillsCard__title): 14px Inter +// semibold in gray-12. .SkillsLanding__communityCount { font-family: "Inter", sans-serif; font-size: pxToRem(14px); font-weight: 600; line-height: pxToRem(20px); - color: var(--sds-clr-gray-11); + color: var(--sds-clr-gray-12); margin: pxToRem(12px) 0 0 0; }