@@ -63,6 +63,84 @@ auto GENERATE_WEB_SCHEMA::handler(
6363
6464 content_children.emplace_back (div (header_children));
6565
66+ // Integration snippets
67+ const auto schema_name{
68+ std::filesystem::path{meta.at (" path" ).to_string ()}.filename ().string ()};
69+ const auto cli_snippet{" jsonschema install " + canonical + " schemas/" +
70+ schema_name + " .json" };
71+ const auto openapi_snippet{R"( $ref: ")" + canonical + R"( ")" };
72+ const auto deno_snippet{R"( import schema from ")" + canonical +
73+ R"( " with { type: "json" };)" };
74+
75+ std::vector<sourcemeta::core::HTMLNode> usage_buttons;
76+ usage_buttons.emplace_back (
77+ button ({{" class" , " btn btn-sm btn-outline-secondary" },
78+ {" type" , " button" },
79+ {" data-sourcemeta-ui-tab-target" , " usage-cli" }},
80+ " CLI" ));
81+ usage_buttons.emplace_back (
82+ button ({{" class" , " btn btn-sm btn-outline-secondary" },
83+ {" type" , " button" },
84+ {" data-sourcemeta-ui-tab-target" , " usage-openapi" }},
85+ " OpenAPI" ));
86+ usage_buttons.emplace_back (
87+ button ({{" class" , " btn btn-sm btn-outline-secondary" },
88+ {" type" , " button" },
89+ {" data-sourcemeta-ui-tab-target" , " usage-deno" }},
90+ " Deno" ));
91+
92+ std::vector<sourcemeta::core::HTMLNode> usage_row;
93+ usage_row.emplace_back (
94+ span ({{" class" , " text-secondary fw-light text-nowrap" }}, " Use with" ));
95+ usage_row.emplace_back (
96+ div ({{" class" , " btn-group flex-shrink-0 me-2" }, {" role" , " group" }},
97+ usage_buttons));
98+ usage_row.emplace_back (
99+ div ({{" data-sourcemeta-ui-tab-id" , " usage-cli" },
100+ {" class" , " d-none d-flex align-items-center flex-grow-1 gap-2" },
101+ {" style" , " min-width: 0" }},
102+ code ({{" class" , " bg-white border p-2 font-monospace flex-grow-1 "
103+ " text-dark text-break" }},
104+ span (" $ " ),
105+ a ({{" href" , " /integrations/#json-schema-cli" },
106+ {" target" , " _blank" },
107+ {" class" , " text-dark" }},
108+ " jsonschema install" ),
109+ span (" " + canonical + " schemas/" + schema_name + " .json" )),
110+ button ({{" class" , " btn btn-sm btn-outline-secondary" },
111+ {" type" , " button" },
112+ {" data-sourcemeta-ui-copy" , cli_snippet}},
113+ i ({{" class" , " bi bi-clipboard" }}))));
114+ usage_row.emplace_back (
115+ div ({{" data-sourcemeta-ui-tab-id" , " usage-openapi" },
116+ {" class" , " d-none d-flex align-items-center flex-grow-1 gap-2" },
117+ {" style" , " min-width: 0" }},
118+ code ({{" class" , " bg-white border p-2 font-monospace flex-grow-1 "
119+ " text-dark text-break" }},
120+ openapi_snippet),
121+ button ({{" class" , " btn btn-sm btn-outline-secondary" },
122+ {" type" , " button" },
123+ {" data-sourcemeta-ui-copy" , openapi_snippet}},
124+ i ({{" class" , " bi bi-clipboard" }}))));
125+ usage_row.emplace_back (
126+ div ({{" data-sourcemeta-ui-tab-id" , " usage-deno" },
127+ {" class" , " d-none d-flex align-items-center flex-grow-1 gap-2" },
128+ {" style" , " min-width: 0" }},
129+ code ({{" class" , " bg-white border p-2 font-monospace flex-grow-1 "
130+ " text-dark text-break" }},
131+ deno_snippet),
132+ button ({{" class" , " btn btn-sm btn-outline-secondary" },
133+ {" type" , " button" },
134+ {" data-sourcemeta-ui-copy" , deno_snippet}},
135+ i ({{" class" , " bi bi-clipboard" }}))));
136+
137+ content_children.emplace_back (
138+ div ({{" data-sourcemeta-ui-tab-group" , " usage" },
139+ {" class" , " bg-light border rounded px-3 py-2 mt-4 d-flex "
140+ " flex-wrap flex-md-nowrap align-items-center small "
141+ " gap-2" }},
142+ usage_row));
143+
66144 // Information table
67145 std::vector<sourcemeta::core::HTMLNode> table_rows;
68146
@@ -145,6 +223,8 @@ auto GENERATE_WEB_SCHEMA::handler(
145223 indirect_dependent_schemas.erase (schema);
146224 }
147225
226+ std::vector<sourcemeta::core::HTMLNode> details_children;
227+
148228 // Tab navigation
149229 std::vector<sourcemeta::core::HTMLNode> nav_items;
150230 nav_items.emplace_back (li (
@@ -193,7 +273,7 @@ auto GENERATE_WEB_SCHEMA::handler(
193273 " ms-2 badge rounded-pill text-bg-secondary align-text-top" }},
194274 std::to_string (health.at (" errors" ).size ())))));
195275
196- container_children .emplace_back (
276+ details_children .emplace_back (
197277 ul ({{" class" , " nav nav-tabs mt-4 mb-3" }}, nav_items));
198278
199279 // Examples tab
@@ -212,7 +292,7 @@ auto GENERATE_WEB_SCHEMA::handler(
212292 examples_content.emplace_back (
213293 div ({{" class" , " list-group" }}, example_items));
214294 }
215- container_children .emplace_back (
295+ details_children .emplace_back (
216296 div ({{" data-sourcemeta-ui-tab-id" , " examples" }, {" class" , " d-none" }},
217297 examples_content));
218298
@@ -278,7 +358,7 @@ auto GENERATE_WEB_SCHEMA::handler(
278358 th ({{" scope" , " col" }}, " Dependency" ))),
279359 tbody (dep_table_rows)));
280360 }
281- container_children .emplace_back (
361+ details_children .emplace_back (
282362 div ({{" data-sourcemeta-ui-tab-id" , " dependencies" }, {" class" , " d-none" }},
283363 dependencies_content));
284364
@@ -341,7 +421,7 @@ auto GENERATE_WEB_SCHEMA::handler(
341421 th ({{" scope" , " col" }}, " Dependent" ))),
342422 tbody (dep_tab_rows)));
343423 }
344- container_children .emplace_back (
424+ details_children .emplace_back (
345425 div ({{" data-sourcemeta-ui-tab-id" , " dependents" }, {" class" , " d-none" }},
346426 dependents_content));
347427
@@ -387,10 +467,15 @@ auto GENERATE_WEB_SCHEMA::handler(
387467 }
388468 health_content.emplace_back (div ({{" class" , " list-group" }}, error_items));
389469 }
390- container_children .emplace_back (
470+ details_children .emplace_back (
391471 div ({{" data-sourcemeta-ui-tab-id" , " health" }, {" class" , " d-none" }},
392472 health_content));
393473
474+ container_children.emplace_back (
475+ div ({{" data-sourcemeta-ui-tab-group" , " details" },
476+ {" data-sourcemeta-ui-tab-url-param" , " tab" }},
477+ details_children));
478+
394479 std::ostringstream html_content;
395480 html_content << " <!DOCTYPE html>"
396481 << html::make_page (configuration, canonical, title, description,
0 commit comments