@apiboost/omnispec 1.1.0-next.5 → 1.1.0-next.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +4 -0
  2. package/dist/cjs/asyncapi/index.js +4 -4
  3. package/dist/cjs/chunks/{AsyncApiSpec-B-RGmOUL.js → AsyncApiSpec-g7Htcn89.js} +264 -119
  4. package/dist/cjs/chunks/AsyncApiSpec-g7Htcn89.js.map +1 -0
  5. package/dist/cjs/chunks/{Collapsible-qjYtxwP0.js → Collapsible-2_XxRGv-.js} +15 -3
  6. package/dist/cjs/chunks/Collapsible-2_XxRGv-.js.map +1 -0
  7. package/dist/cjs/chunks/{ErrorBoundary-CezYgQDG.js → ErrorBoundary-DokoK3KD.js} +2 -2
  8. package/dist/cjs/chunks/{ErrorBoundary-CezYgQDG.js.map → ErrorBoundary-DokoK3KD.js.map} +1 -1
  9. package/dist/cjs/chunks/{OmniSpecRenderer-Bh-IQod3.js → OmniSpecRenderer-CNPtTBa-.js} +6 -6
  10. package/dist/cjs/chunks/{OmniSpecRenderer-Bh-IQod3.js.map → OmniSpecRenderer-CNPtTBa-.js.map} +1 -1
  11. package/dist/cjs/chunks/{OpenApiSpec-5V9E-wQH.js → OpenApiSpec-BiJMyHZw.js} +5 -5
  12. package/dist/cjs/chunks/{OpenApiSpec-5V9E-wQH.js.map → OpenApiSpec-BiJMyHZw.js.map} +1 -1
  13. package/dist/cjs/chunks/{OpenIdConnectManual-DXDG8bcp.js → OpenIdConnectManual-u1xHZG3n.js} +3 -3
  14. package/dist/cjs/chunks/{OpenIdConnectManual-DXDG8bcp.js.map → OpenIdConnectManual-u1xHZG3n.js.map} +1 -1
  15. package/dist/cjs/chunks/{useSpecDetector-BemsWW9u.js → useSpecDetector-CAauxHEE.js} +3 -24
  16. package/dist/cjs/chunks/useSpecDetector-CAauxHEE.js.map +1 -0
  17. package/dist/cjs/chunks/{useSpecFetcher-BAbHnXax.js → useSpecFetcher-CpXe_imr.js} +5 -2
  18. package/dist/{esm/chunks/useSpecFetcher-BvVfMuzf.js.map → cjs/chunks/useSpecFetcher-CpXe_imr.js.map} +1 -1
  19. package/dist/cjs/core/public.js +4 -4
  20. package/dist/cjs/index.js +8 -8
  21. package/dist/cjs/openapi/index.js +5 -5
  22. package/dist/cjs/wc/index.js +4 -4
  23. package/dist/esm/asyncapi/index.js +4 -4
  24. package/dist/esm/chunks/{AsyncApiSpec-C0tdvyEP.js → AsyncApiSpec-dPymWhNP.js} +265 -120
  25. package/dist/esm/chunks/AsyncApiSpec-dPymWhNP.js.map +1 -0
  26. package/dist/esm/chunks/{Collapsible-CPxS90KX.js → Collapsible-DJnJiyJI.js} +15 -3
  27. package/dist/esm/chunks/Collapsible-DJnJiyJI.js.map +1 -0
  28. package/dist/esm/chunks/{ErrorBoundary-9pDlZFfM.js → ErrorBoundary-Ba5GPfFd.js} +2 -2
  29. package/dist/esm/chunks/{ErrorBoundary-9pDlZFfM.js.map → ErrorBoundary-Ba5GPfFd.js.map} +1 -1
  30. package/dist/esm/chunks/{OmniSpecRenderer-DmXZtPRr.js → OmniSpecRenderer-DykIUIbY.js} +6 -6
  31. package/dist/esm/chunks/{OmniSpecRenderer-DmXZtPRr.js.map → OmniSpecRenderer-DykIUIbY.js.map} +1 -1
  32. package/dist/esm/chunks/{OpenApiSpec-CoPhPro1.js → OpenApiSpec-CFtR52Jv.js} +5 -5
  33. package/dist/esm/chunks/{OpenApiSpec-CoPhPro1.js.map → OpenApiSpec-CFtR52Jv.js.map} +1 -1
  34. package/dist/esm/chunks/{OpenIdConnectManual-DhrAcSCa.js → OpenIdConnectManual-Cqh9kU1R.js} +3 -3
  35. package/dist/esm/chunks/{OpenIdConnectManual-DhrAcSCa.js.map → OpenIdConnectManual-Cqh9kU1R.js.map} +1 -1
  36. package/dist/esm/chunks/{useSpecDetector-BJBNbfN5.js → useSpecDetector-YPvOiNwd.js} +3 -24
  37. package/dist/esm/chunks/useSpecDetector-YPvOiNwd.js.map +1 -0
  38. package/dist/esm/chunks/{useSpecFetcher-BvVfMuzf.js → useSpecFetcher-DRDJ1CzD.js} +5 -2
  39. package/dist/{cjs/chunks/useSpecFetcher-BAbHnXax.js.map → esm/chunks/useSpecFetcher-DRDJ1CzD.js.map} +1 -1
  40. package/dist/esm/core/public.js +4 -4
  41. package/dist/esm/index.js +8 -8
  42. package/dist/esm/openapi/index.js +5 -5
  43. package/dist/esm/wc/index.js +4 -4
  44. package/dist/types/asyncapi/AsyncApiSpec.d.ts.map +1 -1
  45. package/dist/types/asyncapi/components/ChannelDetail.d.ts +7 -1
  46. package/dist/types/asyncapi/components/ChannelDetail.d.ts.map +1 -1
  47. package/dist/types/asyncapi/components/ChannelReferenceDetail.d.ts +7 -1
  48. package/dist/types/asyncapi/components/ChannelReferenceDetail.d.ts.map +1 -1
  49. package/dist/types/asyncapi/components/ReplyMeta.d.ts +25 -0
  50. package/dist/types/asyncapi/components/ReplyMeta.d.ts.map +1 -0
  51. package/dist/types/asyncapi/components/TagChips.d.ts +6 -1
  52. package/dist/types/asyncapi/components/TagChips.d.ts.map +1 -1
  53. package/dist/types/asyncapi/components/channel-actions.d.ts +25 -0
  54. package/dist/types/asyncapi/components/channel-actions.d.ts.map +1 -0
  55. package/dist/types/asyncapi/parser/asyncapi-parser.d.ts.map +1 -1
  56. package/dist/types/core/components/Layout/DocLayout.d.ts.map +1 -1
  57. package/dist/types/core/components/common/UpgradePrompt.d.ts.map +1 -1
  58. package/dist/types/core/hooks/useHashScroll.d.ts.map +1 -1
  59. package/dist/wc/standalone.js +1 -1
  60. package/dist/wc/standalone.js.map +1 -1
  61. package/package.json +1 -1
  62. package/dist/cjs/chunks/AsyncApiSpec-B-RGmOUL.js.map +0 -1
  63. package/dist/cjs/chunks/Collapsible-qjYtxwP0.js.map +0 -1
  64. package/dist/cjs/chunks/useSpecDetector-BemsWW9u.js.map +0 -1
  65. package/dist/esm/chunks/AsyncApiSpec-C0tdvyEP.js.map +0 -1
  66. package/dist/esm/chunks/Collapsible-CPxS90KX.js.map +0 -1
  67. package/dist/esm/chunks/useSpecDetector-BJBNbfN5.js.map +0 -1
@@ -1,9 +1,9 @@
1
1
  import { jsxs, jsx, Fragment as Fragment$1 } from 'react/jsx-runtime';
2
- import { useCallback, Fragment, useState, useMemo } from 'react';
2
+ import { useCallback, Fragment, useState, useMemo, useRef, useEffect } from 'react';
3
3
  import { cx, css } from '@emotion/css';
4
- import { a as useSpecFetcher, I as Icon, m as mq, r as resolveSchemaStyle, S as SpecType, T as ThemeProvider, L as LoadingScreen, b as SpecStatusScreen, E as ErrorMessage, N as NavTree, h as buildSidebar, C as ConfigProvider, e as DocLayout } from './useSpecFetcher-BvVfMuzf.js';
5
- import { M as MarkdownRenderer, T as Tabs, C as CodeBlock, t as SchemaTree, A as schemaToNodes, x as generateExample, J as ExampleSelector, d as ExpandableCard, f as MethodBar, K as buildConstraints, S as SchemaBadge, R as ReferenceLayout, a as Collapsible, D as useHashScroll, y as requestExpand, w as flattenNavItemIds, G as useScrollSpy, u as SearchBar, L as ExpandProvider, v as SpecToolbar, z as resolveDownloadLink } from './Collapsible-CPxS90KX.js';
6
- import { E as ErrorBoundary } from './ErrorBoundary-9pDlZFfM.js';
4
+ import { a as useSpecFetcher, I as Icon, m as mq, r as resolveSchemaStyle, S as SpecType, T as ThemeProvider, L as LoadingScreen, b as SpecStatusScreen, E as ErrorMessage, N as NavTree, h as buildSidebar, C as ConfigProvider, e as DocLayout } from './useSpecFetcher-DRDJ1CzD.js';
5
+ import { M as MarkdownRenderer, T as Tabs, C as CodeBlock, t as SchemaTree, A as schemaToNodes, x as generateExample, J as ExampleSelector, d as ExpandableCard, f as MethodBar, K as buildConstraints, S as SchemaBadge, R as ReferenceLayout, a as Collapsible, D as useHashScroll, y as requestExpand, w as flattenNavItemIds, G as useScrollSpy, u as SearchBar, L as ExpandProvider, v as SpecToolbar, z as resolveDownloadLink } from './Collapsible-DJnJiyJI.js';
6
+ import { E as ErrorBoundary } from './ErrorBoundary-Ba5GPfFd.js';
7
7
  import { r as resolveRefs, p as parse, d as decodeJsonPointerSegment } from './ref-resolver-B0C36NuQ.js';
8
8
  import '@emotion/styled';
9
9
  import './ssrf-guard-CsZoYdEq.js';
@@ -49,6 +49,27 @@ function deriveAppliedTraitNames(traits, rawTraits) {
49
49
  return trait.name ?? trait.title ?? `trait ${i + 1}`;
50
50
  });
51
51
  }
52
+ function resolveLocalPointer(root, ref) {
53
+ if (!ref.startsWith("#/")) return void 0;
54
+ let cur = root;
55
+ for (const seg of ref.slice(2).split("/")) {
56
+ if (cur === null || typeof cur !== "object") return void 0;
57
+ cur = cur[decodeJsonPointerSegment(seg)];
58
+ }
59
+ return cur;
60
+ }
61
+ function rawMessageTraits(rawMsg, rawApi) {
62
+ let msg = rawMsg;
63
+ const seen = /* @__PURE__ */ new Set();
64
+ while (msg && typeof msg === "object" && typeof msg.$ref === "string") {
65
+ const ref = msg.$ref;
66
+ if (seen.has(ref)) return void 0;
67
+ seen.add(ref);
68
+ msg = resolveLocalPointer(rawApi, ref);
69
+ }
70
+ if (!msg || typeof msg !== "object") return void 0;
71
+ return msg.traits;
72
+ }
52
73
  function applyMessageTraits(msg, isV3) {
53
74
  if (!msg.traits || msg.traits.length === 0) return msg;
54
75
  if (isV3) {
@@ -91,10 +112,12 @@ async function parseAsyncApiSpec(specInput) {
91
112
  const servers = extractServers(api, raw);
92
113
  const rawApi = raw;
93
114
  const defaultContentType = api.defaultContentType;
94
- const channels = isV3 ? extractChannelsV3(api, rawApi, defaultContentType) : extractChannelsV2(api, defaultContentType);
115
+ const channels = isV3 ? extractChannelsV3(api, rawApi, defaultContentType) : extractChannelsV2(api, rawApi, defaultContentType);
95
116
  const components = extractComponents(api, rawApi, isV3, defaultContentType);
96
117
  const root = api;
97
- const tags = api.tags?.map((tag) => {
118
+ const rootTags = isV3 ? api.info.tags ?? api.tags : api.tags;
119
+ const rootExternalDocs = isV3 ? api.info.externalDocs ?? api.externalDocs : api.externalDocs;
120
+ const tags = rootTags?.map((tag) => {
98
121
  const raw2 = tag;
99
122
  return {
100
123
  name: tag.name,
@@ -113,7 +136,7 @@ async function parseAsyncApiSpec(specInput) {
113
136
  components,
114
137
  tags,
115
138
  tagGroups: root["x-tagGroups"],
116
- externalDocs: api.externalDocs,
139
+ externalDocs: rootExternalDocs,
117
140
  contact: api.info.contact,
118
141
  license: api.info.license,
119
142
  termsOfService: api.info.termsOfService,
@@ -143,15 +166,17 @@ function extractServers(api, rawApi) {
143
166
  const rawServers = !Array.isArray(rawApi.servers) && rawApi.servers || {};
144
167
  return Object.entries(api.servers).map(([name, s]) => toServer(s, name, rawServers[name]?.security));
145
168
  }
146
- function extractChannelsV2(api, defaultContentType) {
169
+ function extractChannelsV2(api, rawApi, defaultContentType) {
147
170
  if (!api.channels) return [];
171
+ const rawChannels = rawApi.channels ?? {};
148
172
  return Object.entries(api.channels).map(([channelName, ch]) => {
149
173
  const operations = [];
174
+ const rawCh = rawChannels[channelName];
150
175
  if (ch.publish) {
151
- operations.push(convertOperationV2("publish", ch.publish, defaultContentType));
176
+ operations.push(convertOperationV2("publish", ch.publish, rawCh?.publish, rawApi, defaultContentType));
152
177
  }
153
178
  if (ch.subscribe) {
154
- operations.push(convertOperationV2("subscribe", ch.subscribe, defaultContentType));
179
+ operations.push(convertOperationV2("subscribe", ch.subscribe, rawCh?.subscribe, rawApi, defaultContentType));
155
180
  }
156
181
  const raw = ch;
157
182
  return {
@@ -166,16 +191,20 @@ function extractChannelsV2(api, defaultContentType) {
166
191
  };
167
192
  });
168
193
  }
169
- function convertOperationV2(action, rawOp, defaultContentType) {
194
+ function convertOperationV2(action, rawOp, rawOperation, rawApi, defaultContentType) {
170
195
  const op = applyOperationTraits(rawOp, false);
171
196
  const messages = [];
172
197
  if (op.message) {
198
+ const rawMessage = rawOperation?.message;
173
199
  if ("oneOf" in op.message) {
174
- for (const m of op.message.oneOf) {
175
- messages.push(convertMessage(m, false, defaultContentType));
176
- }
200
+ op.message.oneOf.forEach((m, i) => {
201
+ const rawOneOf = rawMessage?.oneOf;
202
+ messages.push(convertMessage(m, false, defaultContentType, rawMessageTraits(rawOneOf?.[i], rawApi)));
203
+ });
177
204
  } else {
178
- messages.push(convertMessage(op.message, false, defaultContentType));
205
+ messages.push(
206
+ convertMessage(op.message, false, defaultContentType, rawMessageTraits(rawMessage, rawApi))
207
+ );
179
208
  }
180
209
  }
181
210
  const raw = op;
@@ -242,21 +271,26 @@ function extractChannelsV3(api, rawApi, defaultContentType) {
242
271
  const op = applyOperationTraits(resolvedOp, true);
243
272
  const rawOp = rawApi.operations?.[opKey];
244
273
  const channelName = resolveChannelName(rawOp?.channel, op.channel, addressToName);
245
- const collectMessages = (list) => {
274
+ const collectMessages = (list, rawList) => {
246
275
  const out = [];
247
- for (const msg of list ?? []) {
248
- if (msg && typeof msg === "object" && "$ref" in msg) continue;
249
- out.push(convertMessage(msg, true, defaultContentType));
250
- }
276
+ (list ?? []).forEach((msg, i) => {
277
+ if (msg && typeof msg === "object" && "$ref" in msg) return;
278
+ out.push(convertMessage(msg, true, defaultContentType, rawMessageTraits(rawList?.[i], rawApi)));
279
+ });
251
280
  return out;
252
281
  };
253
- let messages = collectMessages(op.messages);
282
+ let messages = collectMessages(op.messages, rawOp?.messages);
254
283
  if (messages.length === 0 && channelName && api.channels?.[channelName]?.messages) {
284
+ const rawChannelMessages = rawApi.channels?.[channelName]?.messages ?? {};
255
285
  messages = collectMessages(
256
- Object.values(api.channels[channelName].messages)
286
+ Object.values(api.channels[channelName].messages),
287
+ Object.values(rawChannelMessages)
257
288
  );
258
289
  }
259
- const replyMessages = collectMessages(op.reply?.messages);
290
+ const replyMessages = collectMessages(
291
+ op.reply?.messages,
292
+ rawOp?.reply?.messages
293
+ );
260
294
  const rawReplyChannel = rawOp?.reply?.channel;
261
295
  const replyChannelName = resolveChannelName(
262
296
  rawReplyChannel,
@@ -425,7 +459,7 @@ function ExternalLink({ href, children, label, className }) {
425
459
  target: "_blank",
426
460
  rel: "noopener noreferrer",
427
461
  "aria-label": `${label}, opens in new tab`,
428
- className: cx(linkStyle$1, className),
462
+ className: cx(linkStyle$2, className),
429
463
  children: [
430
464
  children,
431
465
  /* @__PURE__ */ jsx(Icon, { name: "external-link", size: "0.875em" })
@@ -433,7 +467,7 @@ function ExternalLink({ href, children, label, className }) {
433
467
  }
434
468
  );
435
469
  }
436
- const linkStyle$1 = css({
470
+ const linkStyle$2 = css({
437
471
  display: "inline-flex",
438
472
  alignItems: "center",
439
473
  gap: "0.25rem",
@@ -487,7 +521,7 @@ const metaStyle = css({
487
521
  color: "var(--omnispec-fg-secondary)",
488
522
  marginTop: "0.25rem"
489
523
  });
490
- const linkStyle = css({
524
+ const linkStyle$1 = css({
491
525
  color: "var(--omnispec-fg-link)",
492
526
  textDecoration: "none",
493
527
  "&:hover": {
@@ -513,7 +547,7 @@ function AsyncOverview({ spec }) {
513
547
  ] }),
514
548
  spec.contact.email && /* @__PURE__ */ jsxs("span", { children: [
515
549
  " <",
516
- /* @__PURE__ */ jsx("a", { href: `mailto:${spec.contact.email}`, className: linkStyle, children: spec.contact.email }),
550
+ /* @__PURE__ */ jsx("a", { href: `mailto:${spec.contact.email}`, className: linkStyle$1, children: spec.contact.email }),
517
551
  ">"
518
552
  ] }),
519
553
  spec.contact.url && /* @__PURE__ */ jsxs("span", { children: [
@@ -673,9 +707,9 @@ function FieldRows({
673
707
  verbatim = false
674
708
  }) {
675
709
  if (fields.length === 0) return null;
676
- return /* @__PURE__ */ jsx("dl", { className: listStyle$1, children: fields.map(([field, value]) => /* @__PURE__ */ jsxs("div", { className: rowStyle$1, children: [
710
+ return /* @__PURE__ */ jsx("dl", { className: listStyle$1, children: fields.map(([field, value]) => /* @__PURE__ */ jsxs("div", { className: rowStyle$2, children: [
677
711
  /* @__PURE__ */ jsx("dt", { className: nameStyle$1, children: verbatim ? field : humanizeField(field) }),
678
- /* @__PURE__ */ jsx("dd", { className: valueStyle, children: renderValue(value, verbatim) })
712
+ /* @__PURE__ */ jsx("dd", { className: valueStyle$1, children: renderValue(value, verbatim) })
679
713
  ] }, field)) });
680
714
  }
681
715
  const listStyle$1 = css({
@@ -685,13 +719,13 @@ const listStyle$1 = css({
685
719
  margin: "0.25rem 0 0",
686
720
  minWidth: 0
687
721
  });
688
- const rowStyle$1 = css({ display: "contents" });
722
+ const rowStyle$2 = css({ display: "contents" });
689
723
  const nameStyle$1 = css({
690
724
  fontSize: "var(--omnispec-font-size-xs)",
691
725
  fontWeight: 600,
692
726
  color: "var(--omnispec-fg-secondary)"
693
727
  });
694
- const valueStyle = css({
728
+ const valueStyle$1 = css({
695
729
  fontSize: "var(--omnispec-font-size-xs)",
696
730
  color: "var(--omnispec-fg-primary)",
697
731
  margin: 0,
@@ -836,8 +870,8 @@ const emptyStyle = css({
836
870
 
837
871
  function SecurityRequirementBadges({ requirements, label = "Security" }) {
838
872
  if (!requirements || requirements.length === 0) return null;
839
- return /* @__PURE__ */ jsxs("div", { className: rowStyle, children: [
840
- /* @__PURE__ */ jsx("span", { className: labelStyle, children: label }),
873
+ return /* @__PURE__ */ jsxs("div", { className: rowStyle$1, children: [
874
+ /* @__PURE__ */ jsx("span", { className: labelStyle$1, children: label }),
841
875
  requirements.map((alternative, altIndex) => {
842
876
  const chips = alternative.map((scheme) => /* @__PURE__ */ jsxs("a", { href: `#security-${scheme.name}`, className: badgeStyle, children: [
843
877
  /* @__PURE__ */ jsx(Icon, { name: "lock", size: "0.625rem", strokeWidth: 2.5 }),
@@ -854,7 +888,7 @@ function SecurityRequirementBadges({ requirements, label = "Security" }) {
854
888
  })
855
889
  ] });
856
890
  }
857
- const rowStyle = css({
891
+ const rowStyle$1 = css({
858
892
  display: "flex",
859
893
  alignItems: "center",
860
894
  gap: "0.375rem",
@@ -870,7 +904,7 @@ const orGroupStyle = css({
870
904
  gap: "0.375rem",
871
905
  whiteSpace: "nowrap"
872
906
  });
873
- const labelStyle = css({
907
+ const labelStyle$1 = css({
874
908
  fontSize: "var(--omnispec-font-size-xxs)",
875
909
  lineHeight: 1.5,
876
910
  fontWeight: 600,
@@ -1066,7 +1100,7 @@ function ServerList({ servers }) {
1066
1100
  ] });
1067
1101
  }
1068
1102
 
1069
- function TagChips({ tags, onTagClick }) {
1103
+ function TagChips({ tags, onTagClick, activeTag }) {
1070
1104
  if (!tags || tags.length === 0) return null;
1071
1105
  return /* @__PURE__ */ jsx("div", { className: tagChipsStyle$1, children: tags.map((tag) => /* @__PURE__ */ jsxs("span", { className: tagItemStyle, children: [
1072
1106
  onTagClick ? /* @__PURE__ */ jsx(
@@ -1075,6 +1109,7 @@ function TagChips({ tags, onTagClick }) {
1075
1109
  type: "button",
1076
1110
  className: tagChipButtonStyle$1,
1077
1111
  title: tag.description,
1112
+ "aria-pressed": tag.name === activeTag,
1078
1113
  onClick: () => onTagClick(tag.name),
1079
1114
  children: tag.name
1080
1115
  }
@@ -1333,22 +1368,94 @@ const unresolvedRefCodeStyle = css({
1333
1368
  fontSize: "var(--omnispec-font-size-xs)"
1334
1369
  });
1335
1370
 
1336
- const actionLabels$1 = {
1371
+ const actionLabels = {
1337
1372
  publish: "PUB",
1338
1373
  subscribe: "SUB",
1339
1374
  send: "SEND",
1340
1375
  receive: "RECV"
1341
1376
  };
1342
- const actionColors$1 = {
1377
+ const actionColors = {
1343
1378
  publish: "var(--omnispec-color-publish)",
1344
1379
  subscribe: "var(--omnispec-color-subscribe)",
1345
1380
  send: "var(--omnispec-color-publish)",
1346
1381
  receive: "var(--omnispec-color-subscribe)"
1347
1382
  };
1348
- function ChannelDetail({ channel, id, expandAll, expandGeneration, onTagClick }) {
1349
- const primaryAction = channel.operations[0]?.action;
1350
- const primaryLabel = actionLabels$1[primaryAction] ?? primaryAction?.toUpperCase() ?? "";
1351
- const primaryColor = actionColors$1[primaryAction] ?? "var(--omnispec-fg-muted)";
1383
+ function channelActionBadge(channel) {
1384
+ const distinct = [...new Set(channel.operations.map((op) => op.action))];
1385
+ const label = distinct.map((a) => actionLabels[a] ?? a.toUpperCase()).join(" / ");
1386
+ const color = distinct.length === 1 ? actionColors[distinct[0]] ?? "var(--omnispec-fg-muted)" : "var(--omnispec-color-primary)";
1387
+ return { label, color };
1388
+ }
1389
+
1390
+ function ReplyMeta({
1391
+ reply,
1392
+ channelHref,
1393
+ onChannelNavigate
1394
+ }) {
1395
+ const hasAddress = reply.address && (reply.address.value || reply.address.location || reply.address.description);
1396
+ const replyHref = reply.channelName ? channelHref?.(reply.channelName) : void 0;
1397
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
1398
+ reply.channelName && /* @__PURE__ */ jsxs("div", { className: rowStyle, children: [
1399
+ /* @__PURE__ */ jsx("span", { className: labelStyle, children: "channel" }),
1400
+ replyHref ? /* @__PURE__ */ jsx(
1401
+ "a",
1402
+ {
1403
+ href: replyHref,
1404
+ className: linkStyle,
1405
+ onClick: onChannelNavigate ? (e) => {
1406
+ e.preventDefault();
1407
+ onChannelNavigate(reply.channelName);
1408
+ } : void 0,
1409
+ children: reply.channelName
1410
+ }
1411
+ ) : /* @__PURE__ */ jsx("code", { className: valueStyle, children: reply.channelName })
1412
+ ] }),
1413
+ hasAddress && /* @__PURE__ */ jsxs("div", { className: rowStyle, children: [
1414
+ /* @__PURE__ */ jsx("span", { className: labelStyle, children: "address" }),
1415
+ reply.address?.value && /* @__PURE__ */ jsx("code", { className: valueStyle, children: reply.address.value }),
1416
+ reply.address?.location && /* @__PURE__ */ jsx("code", { className: valueStyle, children: reply.address.location }),
1417
+ reply.address?.description && /* @__PURE__ */ jsx("span", { className: descStyle$2, children: reply.address.description })
1418
+ ] })
1419
+ ] });
1420
+ }
1421
+ const rowStyle = css({
1422
+ display: "flex",
1423
+ alignItems: "center",
1424
+ gap: "0.5rem",
1425
+ flexWrap: "wrap",
1426
+ marginBottom: "0.5rem"
1427
+ });
1428
+ const labelStyle = css({
1429
+ fontSize: "var(--omnispec-font-size-xs)",
1430
+ fontWeight: 700,
1431
+ color: "var(--omnispec-fg-primary)",
1432
+ fontFamily: "var(--omnispec-font-mono)"
1433
+ });
1434
+ const valueStyle = css({
1435
+ fontFamily: "var(--omnispec-font-mono)",
1436
+ fontSize: "var(--omnispec-font-size-xs)",
1437
+ color: "var(--omnispec-fg-code)",
1438
+ backgroundColor: "var(--omnispec-bg-tertiary)",
1439
+ padding: "0.0625rem 0.375rem",
1440
+ borderRadius: "0.1875rem"
1441
+ });
1442
+ const linkStyle = css({
1443
+ fontFamily: "var(--omnispec-font-mono)",
1444
+ fontSize: "var(--omnispec-font-size-xs)",
1445
+ color: "var(--omnispec-color-primary)",
1446
+ backgroundColor: "var(--omnispec-bg-tertiary)",
1447
+ padding: "0.0625rem 0.375rem",
1448
+ borderRadius: "0.1875rem",
1449
+ textDecoration: "none",
1450
+ "&:hover": { textDecoration: "underline" }
1451
+ });
1452
+ const descStyle$2 = css({
1453
+ fontSize: "var(--omnispec-font-size-xs)",
1454
+ color: "var(--omnispec-fg-secondary)"
1455
+ });
1456
+
1457
+ function ChannelDetail({ channel, id, expandAll, expandGeneration, onTagClick, activeTag, channelHref, onChannelNavigate }) {
1458
+ const headerBadge = channelActionBadge(channel);
1352
1459
  return /* @__PURE__ */ jsxs(
1353
1460
  ExpandableCard,
1354
1461
  {
@@ -1358,10 +1465,10 @@ function ChannelDetail({ channel, id, expandAll, expandGeneration, onTagClick })
1358
1465
  expandAll,
1359
1466
  expandGeneration,
1360
1467
  children: [
1361
- /* @__PURE__ */ jsx(MethodBar, { label: primaryLabel, path: channel.address, color: primaryColor }),
1468
+ /* @__PURE__ */ jsx(MethodBar, { label: headerBadge.label, path: channel.address, color: headerBadge.color }),
1362
1469
  /* @__PURE__ */ jsxs("div", { className: bodyStyle, children: [
1363
1470
  channel.description && /* @__PURE__ */ jsx("div", { className: sectionStyle$1, children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: channel.description }) }),
1364
- channel.tags && channel.tags.length > 0 && /* @__PURE__ */ jsx(TagChips, { tags: channel.tags, onTagClick }),
1471
+ channel.tags && channel.tags.length > 0 && /* @__PURE__ */ jsx(TagChips, { tags: channel.tags, onTagClick, activeTag }),
1365
1472
  channel.externalDocs && /* @__PURE__ */ jsx("div", { className: sectionStyle$1, children: /* @__PURE__ */ jsx(ExternalDocsLink, { externalDocs: channel.externalDocs, context: channel.address }) }),
1366
1473
  channel.parameters && Object.keys(channel.parameters).length > 0 && /* @__PURE__ */ jsxs("div", { className: sectionStyle$1, children: [
1367
1474
  /* @__PURE__ */ jsx("h4", { className: sectionTitleStyle$1, children: "Parameters" }),
@@ -1396,9 +1503,9 @@ function ChannelDetail({ channel, id, expandAll, expandGeneration, onTagClick })
1396
1503
  "span",
1397
1504
  {
1398
1505
  className: cx(actionBadgeBase$1, css({
1399
- backgroundColor: actionColors$1[op.action] ?? "var(--omnispec-fg-muted)"
1506
+ backgroundColor: actionColors[op.action] ?? "var(--omnispec-fg-muted)"
1400
1507
  })),
1401
- children: actionLabels$1[op.action] ?? op.action.toUpperCase()
1508
+ children: actionLabels[op.action] ?? op.action.toUpperCase()
1402
1509
  }
1403
1510
  ),
1404
1511
  op.operationId && /* @__PURE__ */ jsx("span", { className: opIdStyle$1, children: op.operationId }),
@@ -1406,7 +1513,7 @@ function ChannelDetail({ channel, id, expandAll, expandGeneration, onTagClick })
1406
1513
  ] }),
1407
1514
  op.summary && /* @__PURE__ */ jsx("span", { className: opSummaryStyle$1, children: op.summary })
1408
1515
  ] }),
1409
- op.tags && op.tags.length > 0 && /* @__PURE__ */ jsx(TagChips, { tags: op.tags, onTagClick }),
1516
+ op.tags && op.tags.length > 0 && /* @__PURE__ */ jsx(TagChips, { tags: op.tags, onTagClick, activeTag }),
1410
1517
  op.description && /* @__PURE__ */ jsx("div", { className: sectionStyle$1, children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: op.description }) }),
1411
1518
  op.externalDocs && /* @__PURE__ */ jsx("div", { className: opExternalDocsStyle, children: /* @__PURE__ */ jsx(
1412
1519
  ExternalDocsLink,
@@ -1419,16 +1526,7 @@ function ChannelDetail({ channel, id, expandAll, expandGeneration, onTagClick })
1419
1526
  /* @__PURE__ */ jsx(OperationMessages, { operation: op, idx }),
1420
1527
  op.reply && (op.reply.messages.length > 0 || op.reply.address || op.reply.channelName) && /* @__PURE__ */ jsxs("div", { className: replyStyle$1, children: [
1421
1528
  /* @__PURE__ */ jsx("h4", { className: replyTitleStyle, children: "Reply" }),
1422
- op.reply.channelName && /* @__PURE__ */ jsxs("div", { className: replyMetaRowStyle, children: [
1423
- /* @__PURE__ */ jsx("span", { className: replyMetaLabelStyle, children: "channel" }),
1424
- /* @__PURE__ */ jsx("code", { className: replyMetaValueStyle, children: op.reply.channelName })
1425
- ] }),
1426
- op.reply.address && (op.reply.address.value || op.reply.address.location || op.reply.address.description) && /* @__PURE__ */ jsxs("div", { className: replyMetaRowStyle, children: [
1427
- /* @__PURE__ */ jsx("span", { className: replyMetaLabelStyle, children: "address" }),
1428
- op.reply.address.value && /* @__PURE__ */ jsx("code", { className: replyMetaValueStyle, children: op.reply.address.value }),
1429
- op.reply.address.location && /* @__PURE__ */ jsx("code", { className: replyMetaValueStyle, children: op.reply.address.location }),
1430
- op.reply.address.description && /* @__PURE__ */ jsx("span", { className: replyMetaDescStyle, children: op.reply.address.description })
1431
- ] }),
1529
+ /* @__PURE__ */ jsx(ReplyMeta, { reply: op.reply, channelHref, onChannelNavigate }),
1432
1530
  op.reply.messages.map((replyMsg, i) => /* @__PURE__ */ jsx(MessageView, { message: replyMsg, idx: `${idx}-reply-${i}` }, i))
1433
1531
  ] }),
1434
1532
  /* @__PURE__ */ jsx(BindingsSection, { bindings: op.bindings, title: "Operation Bindings" })
@@ -1674,48 +1772,9 @@ const replyTitleStyle = css({
1674
1772
  color: "var(--omnispec-fg-primary)",
1675
1773
  letterSpacing: "0.02em"
1676
1774
  });
1677
- const replyMetaRowStyle = css({
1678
- display: "flex",
1679
- alignItems: "center",
1680
- gap: "0.5rem",
1681
- flexWrap: "wrap",
1682
- marginBottom: "0.5rem"
1683
- });
1684
- const replyMetaLabelStyle = css({
1685
- fontSize: "var(--omnispec-font-size-xs)",
1686
- fontWeight: 700,
1687
- color: "var(--omnispec-fg-primary)",
1688
- fontFamily: "var(--omnispec-font-mono)"
1689
- });
1690
- const replyMetaValueStyle = css({
1691
- fontFamily: "var(--omnispec-font-mono)",
1692
- fontSize: "var(--omnispec-font-size-xs)",
1693
- color: "var(--omnispec-fg-code)",
1694
- backgroundColor: "var(--omnispec-bg-tertiary)",
1695
- padding: "1px 0.375rem",
1696
- borderRadius: "0.1875rem"
1697
- });
1698
- const replyMetaDescStyle = css({
1699
- fontSize: "var(--omnispec-font-size-xs)",
1700
- color: "var(--omnispec-fg-secondary)"
1701
- });
1702
1775
 
1703
- const actionLabels = {
1704
- publish: "PUB",
1705
- subscribe: "SUB",
1706
- send: "SEND",
1707
- receive: "RECV"
1708
- };
1709
- const actionColors = {
1710
- publish: "var(--omnispec-color-publish)",
1711
- subscribe: "var(--omnispec-color-subscribe)",
1712
- send: "var(--omnispec-color-publish)",
1713
- receive: "var(--omnispec-color-subscribe)"
1714
- };
1715
- function ChannelReferenceDetail({ channel, id, expandAll, expandGeneration, onTagClick }) {
1716
- const primaryAction = channel.operations[0]?.action;
1717
- const primaryLabel = actionLabels[primaryAction] ?? primaryAction?.toUpperCase() ?? "";
1718
- const primaryColor = actionColors[primaryAction] ?? "var(--omnispec-fg-muted)";
1776
+ function ChannelReferenceDetail({ channel, id, expandAll, expandGeneration, onTagClick, activeTag, channelHref, onChannelNavigate }) {
1777
+ const headerBadge = channelActionBadge(channel);
1719
1778
  return /* @__PURE__ */ jsxs(
1720
1779
  ExpandableCard,
1721
1780
  {
@@ -1725,23 +1784,32 @@ function ChannelReferenceDetail({ channel, id, expandAll, expandGeneration, onTa
1725
1784
  expandAll,
1726
1785
  expandGeneration,
1727
1786
  children: [
1728
- /* @__PURE__ */ jsx(MethodBar, { label: primaryLabel, path: channel.address, color: primaryColor }),
1787
+ /* @__PURE__ */ jsx(MethodBar, { label: headerBadge.label, path: channel.address, color: headerBadge.color }),
1729
1788
  channel.description && /* @__PURE__ */ jsx("div", { className: channelMetaStyle, children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: channel.description }) }),
1730
1789
  channel.externalDocs && /* @__PURE__ */ jsx("div", { className: channelMetaStyle, children: /* @__PURE__ */ jsx(ExternalDocsLink, { externalDocs: channel.externalDocs, context: channel.address }) }),
1731
1790
  channel.operations.map((op, idx) => /* @__PURE__ */ jsx("div", { id: id ? `${id}-op-${idx}` : void 0, className: operationWrapStyle, children: /* @__PURE__ */ jsx(
1732
1791
  ReferenceLayout,
1733
1792
  {
1734
1793
  operationId: op.operationId ?? `${channel.name}-op-${idx}`,
1735
- schema: /* @__PURE__ */ jsx(
1794
+ schema: /* @__PURE__ */ jsx("div", { className: refMobilePadStyle, children: /* @__PURE__ */ jsx(
1736
1795
  OperationSchemaColumn,
1737
1796
  {
1738
1797
  channel,
1739
1798
  operation: op,
1740
- onTagClick
1799
+ onTagClick,
1800
+ activeTag
1801
+ }
1802
+ ) }),
1803
+ samples: /* @__PURE__ */ jsx("div", { className: refMobilePadStyle, children: /* @__PURE__ */ jsx(
1804
+ OperationSamplesColumn,
1805
+ {
1806
+ operation: op,
1807
+ idx,
1808
+ channelHref,
1809
+ onChannelNavigate
1741
1810
  }
1742
- ),
1743
- samples: /* @__PURE__ */ jsx(OperationSamplesColumn, { operation: op, idx }),
1744
- tryIt: /* @__PURE__ */ jsx("div", { className: tryItNaStyle, children: "Interactive testing is not available for AsyncAPI protocols." })
1811
+ ) }),
1812
+ tryIt: /* @__PURE__ */ jsx("div", { className: cx(refMobilePadStyle, tryItNaStyle), children: "Interactive testing is not available for AsyncAPI protocols." })
1745
1813
  }
1746
1814
  ) }, idx))
1747
1815
  ]
@@ -1751,7 +1819,8 @@ function ChannelReferenceDetail({ channel, id, expandAll, expandGeneration, onTa
1751
1819
  function OperationSchemaColumn({
1752
1820
  channel,
1753
1821
  operation,
1754
- onTagClick
1822
+ onTagClick,
1823
+ activeTag
1755
1824
  }) {
1756
1825
  return /* @__PURE__ */ jsxs("div", { children: [
1757
1826
  /* @__PURE__ */ jsxs("div", { className: opHeaderStyle, children: [
@@ -1775,6 +1844,7 @@ function OperationSchemaColumn({
1775
1844
  type: "button",
1776
1845
  className: tagChipButtonStyle,
1777
1846
  title: tag.description,
1847
+ "aria-pressed": tag.name === activeTag,
1778
1848
  onClick: () => onTagClick(tag.name),
1779
1849
  children: tag.name
1780
1850
  },
@@ -1819,10 +1889,11 @@ function OperationSchemaColumn({
1819
1889
  /* @__PURE__ */ jsx(BindingsSection, { bindings: channel.bindings, title: "Channel Bindings" })
1820
1890
  ] });
1821
1891
  }
1822
- function OperationSamplesColumn({ operation, idx }) {
1892
+ function OperationSamplesColumn({ operation, idx, channelHref, onChannelNavigate }) {
1823
1893
  const messages = operation.messages ?? [];
1824
- const replyMessages = operation.reply?.messages ?? [];
1825
- if (messages.length === 0 && replyMessages.length === 0) {
1894
+ const reply = operation.reply;
1895
+ const hasReply = !!(reply && (reply.messages.length > 0 || reply.address || reply.channelName));
1896
+ if (messages.length === 0 && !hasReply) {
1826
1897
  return /* @__PURE__ */ jsx("div", { className: emptySamplesStyle, children: "No message payload is defined for this operation." });
1827
1898
  }
1828
1899
  return /* @__PURE__ */ jsxs("div", { className: samplesColumnStyle, children: [
@@ -1837,9 +1908,10 @@ function OperationSamplesColumn({ operation, idx }) {
1837
1908
  message.contentType && /* @__PURE__ */ jsx("code", { className: contentTypeStyle$1, children: message.contentType }),
1838
1909
  /* @__PURE__ */ jsx(MessagePayload, { message, idx: `${idx}-msg-${i}` })
1839
1910
  ] }, i)),
1840
- replyMessages.length > 0 && /* @__PURE__ */ jsxs("div", { className: replyStyle, children: [
1911
+ hasReply && reply && /* @__PURE__ */ jsxs("div", { className: replyStyle, children: [
1841
1912
  /* @__PURE__ */ jsx("h4", { className: sectionTitleStyle, children: "Reply" }),
1842
- replyMessages.map((message, i) => /* @__PURE__ */ jsx(MessagePayload, { message, idx: `${idx}-reply-${i}` }, i))
1913
+ /* @__PURE__ */ jsx(ReplyMeta, { reply, channelHref, onChannelNavigate }),
1914
+ reply.messages.map((message, i) => /* @__PURE__ */ jsx(MessagePayload, { message, idx: `${idx}-reply-${i}` }, i))
1843
1915
  ] })
1844
1916
  ] });
1845
1917
  }
@@ -1849,6 +1921,9 @@ const channelMetaStyle = css({
1849
1921
  padding: "1rem 1.25rem 0"
1850
1922
  }
1851
1923
  });
1924
+ const refMobilePadStyle = css({
1925
+ [mq.mobile]: { paddingInline: "1rem" }
1926
+ });
1852
1927
  const operationWrapStyle = css({
1853
1928
  borderTop: "1px solid var(--omnispec-border-color)"
1854
1929
  });
@@ -1916,7 +1991,9 @@ const paramConstraintsStyle = css({
1916
1991
  const opHeaderStyle = css({
1917
1992
  display: "flex",
1918
1993
  alignItems: "center",
1919
- gap: "0.5rem",
1994
+ flexWrap: "wrap",
1995
+ gap: "0.25rem 0.5rem",
1996
+ minWidth: 0,
1920
1997
  marginBottom: "0.5rem"
1921
1998
  });
1922
1999
  const actionBadgeBase = css({
@@ -1933,16 +2010,21 @@ const opIdStyle = css({
1933
2010
  fontSize: "var(--omnispec-font-size-sm)",
1934
2011
  fontWeight: 700,
1935
2012
  backgroundColor: "transparent",
1936
- padding: 0
2013
+ padding: 0,
2014
+ overflowWrap: "anywhere"
1937
2015
  });
1938
2016
  const opTitleStyle = css({
1939
2017
  fontSize: "var(--omnispec-font-size-sm)",
1940
2018
  fontWeight: 600,
1941
- color: "var(--omnispec-fg-primary)"
2019
+ color: "var(--omnispec-fg-primary)",
2020
+ overflowWrap: "anywhere"
1942
2021
  });
1943
2022
  const opSummaryStyle = css({
2023
+ flexBasis: "100%",
2024
+ minWidth: 0,
1944
2025
  fontSize: "var(--omnispec-font-size-sm)",
1945
- color: "var(--omnispec-fg-secondary)"
2026
+ color: "var(--omnispec-fg-secondary)",
2027
+ overflowWrap: "anywhere"
1946
2028
  });
1947
2029
  const messageTitleStyle = css({
1948
2030
  fontWeight: 400,
@@ -2392,17 +2474,39 @@ function AsyncApiSpec({
2392
2474
  setActiveTagFilter((prev) => {
2393
2475
  const next = prev === tagName ? null : tagName;
2394
2476
  setSearchQuery(next ?? "");
2477
+ writeTagParam(next);
2395
2478
  return next;
2396
2479
  });
2397
2480
  }, []);
2398
2481
  const handleSearch = useCallback((query) => {
2399
2482
  setSearchQuery(query);
2400
2483
  setActiveTagFilter(null);
2484
+ writeTagParam(null);
2401
2485
  }, []);
2402
2486
  const clearTagFilter = useCallback(() => {
2403
2487
  setActiveTagFilter(null);
2404
2488
  setSearchQuery("");
2489
+ writeTagParam(null);
2405
2490
  }, []);
2491
+ const availableTags = useMemo(() => {
2492
+ const names = /* @__PURE__ */ new Set();
2493
+ for (const tag of parsedSpec?.tags ?? []) names.add(tag.name);
2494
+ for (const ch of parsedSpec?.channels ?? []) {
2495
+ for (const tag of ch.tags ?? []) names.add(tag.name);
2496
+ for (const op of ch.operations) for (const t of op.tags ?? []) names.add(t.name);
2497
+ }
2498
+ return names;
2499
+ }, [parsedSpec]);
2500
+ const didInitFromUrl = useRef(false);
2501
+ useEffect(() => {
2502
+ if (didInitFromUrl.current || !parsedSpec) return;
2503
+ didInitFromUrl.current = true;
2504
+ const urlTag = readTagParam();
2505
+ if (urlTag && availableTags.has(urlTag)) {
2506
+ setActiveTagFilter(urlTag);
2507
+ setSearchQuery(urlTag);
2508
+ }
2509
+ }, [parsedSpec, availableTags]);
2406
2510
  useMemo(() => {
2407
2511
  if (parsedSpec && onSpecLoaded) {
2408
2512
  const major = parseInt(parsedSpec.specVersion.split(".")[0], 10);
@@ -2452,6 +2556,13 @@ function AsyncApiSpec({
2452
2556
  }
2453
2557
  return map;
2454
2558
  }, [filteredChannels, channelDomId]);
2559
+ const channelHref = useCallback(
2560
+ (channelName) => {
2561
+ const domId = channelDomId.get(channelName);
2562
+ return domId ? `#${domId}` : void 0;
2563
+ },
2564
+ [channelDomId]
2565
+ );
2455
2566
  const hasOperations = useMemo(
2456
2567
  () => filteredChannels.some((ch) => ch.operations.length > 0),
2457
2568
  [filteredChannels]
@@ -2536,6 +2647,10 @@ function AsyncApiSpec({
2536
2647
  }
2537
2648
  navigateTo(id);
2538
2649
  }, [navigateTo, opToCard]);
2650
+ const onChannelNavigate = useCallback((channelName) => {
2651
+ const domId = channelDomId.get(channelName);
2652
+ if (domId) navigateTo(domId);
2653
+ }, [channelDomId, navigateTo]);
2539
2654
  const navSectionIds = useMemo(() => flattenNavItemIds(navItems), [navItems]);
2540
2655
  const activeSection = useScrollSpy(navSectionIds);
2541
2656
  if (state.status === "loading") {
@@ -2561,7 +2676,7 @@ function AsyncApiSpec({
2561
2676
  /* @__PURE__ */ jsx(
2562
2677
  SearchBar,
2563
2678
  {
2564
- placeholder: "Filter channels...",
2679
+ placeholder: navGrouping === "operation" ? "Filter operations..." : "Filter channels...",
2565
2680
  onSearch: handleSearch,
2566
2681
  value: searchQuery
2567
2682
  }
@@ -2656,7 +2771,10 @@ function AsyncApiSpec({
2656
2771
  displayMode: resolvedDisplayMode,
2657
2772
  expandAll: allExpanded,
2658
2773
  expandGeneration,
2659
- onTagClick: handleTagFilter
2774
+ onTagClick: handleTagFilter,
2775
+ activeTag: activeTagFilter,
2776
+ channelHref,
2777
+ onChannelNavigate
2660
2778
  },
2661
2779
  `${group.id}-${channel.name}`
2662
2780
  ))
@@ -2668,7 +2786,10 @@ function AsyncApiSpec({
2668
2786
  displayMode: resolvedDisplayMode,
2669
2787
  expandAll: allExpanded,
2670
2788
  expandGeneration,
2671
- onTagClick: handleTagFilter
2789
+ onTagClick: handleTagFilter,
2790
+ activeTag: activeTagFilter,
2791
+ channelHref,
2792
+ onChannelNavigate
2672
2793
  },
2673
2794
  channel.name
2674
2795
  ))
@@ -2685,7 +2806,10 @@ function ChannelCard({
2685
2806
  displayMode,
2686
2807
  expandAll,
2687
2808
  expandGeneration,
2688
- onTagClick
2809
+ onTagClick,
2810
+ activeTag,
2811
+ channelHref,
2812
+ onChannelNavigate
2689
2813
  }) {
2690
2814
  const Renderer = displayMode === "reference" ? ChannelReferenceDetail : ChannelDetail;
2691
2815
  return /* @__PURE__ */ jsx(
@@ -2695,10 +2819,31 @@ function ChannelCard({
2695
2819
  channel,
2696
2820
  expandAll,
2697
2821
  expandGeneration,
2698
- onTagClick
2822
+ onTagClick,
2823
+ activeTag,
2824
+ channelHref,
2825
+ onChannelNavigate
2699
2826
  }
2700
2827
  );
2701
2828
  }
2829
+ const TAG_PARAM = "omnispec-tag";
2830
+ function readTagParam() {
2831
+ if (typeof window === "undefined") return null;
2832
+ return new URLSearchParams(window.location.search).get(TAG_PARAM);
2833
+ }
2834
+ function writeTagParam(tag) {
2835
+ if (typeof window === "undefined") return;
2836
+ const params = new URLSearchParams(window.location.search);
2837
+ if (params.get(TAG_PARAM) === (tag ?? null)) return;
2838
+ if (tag) params.set(TAG_PARAM, tag);
2839
+ else params.delete(TAG_PARAM);
2840
+ const query = params.toString();
2841
+ window.history.replaceState(
2842
+ window.history.state,
2843
+ "",
2844
+ `${window.location.pathname}${query ? `?${query}` : ""}${window.location.hash}`
2845
+ );
2846
+ }
2702
2847
  function nestByTagGroups(groups, xTagGroups, toNav) {
2703
2848
  if (!xTagGroups?.length) return groups.map(toNav);
2704
2849
  const byTagName = /* @__PURE__ */ new Map();
@@ -2819,4 +2964,4 @@ const activeFilterClearStyle = css({
2819
2964
  });
2820
2965
 
2821
2966
  export { AsyncApiSpec };
2822
- //# sourceMappingURL=AsyncApiSpec-C0tdvyEP.js.map
2967
+ //# sourceMappingURL=AsyncApiSpec-dPymWhNP.js.map