radiant-docs 0.1.62 → 0.1.64

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.
@@ -4,6 +4,7 @@ import { withBasePath } from "../../lib/base-path";
4
4
  import type { OpenApiRoute } from "../../lib/routes";
5
5
  import { getConfig } from "../../lib/validation";
6
6
  import { renderMarkdown } from "../../lib/utils";
7
+ import { getClientShikiRuntimeConfig } from "../../lib/client-shiki-config";
7
8
  import {
8
9
  OPENAPI_REQUEST_SECTION_LABELS,
9
10
  type OpenApiRequestFields,
@@ -38,6 +39,7 @@ const {
38
39
  } = Astro.props;
39
40
  const headers: Record<string, string> = OPENAPI_REQUEST_SECTION_LABELS;
40
41
  const config = await getConfig();
42
+ const shikiRuntimeConfig = getClientShikiRuntimeConfig(config);
41
43
  const configuredProxyUrl =
42
44
  typeof import.meta.env.PUBLIC_PROXY_URL === "string"
43
45
  ? import.meta.env.PUBLIC_PROXY_URL.trim()
@@ -77,6 +79,7 @@ const sectionVariantFieldNames = Object.fromEntries(
77
79
  queryFieldMeta: ${JSON.stringify(queryFieldMeta)},
78
80
  sectionVariantFieldNames: ${JSON.stringify(sectionVariantFieldNames)},
79
81
  bodyDefaultKind: ${JSON.stringify(bodyDefaultKind ?? null)},
82
+ shiki: ${JSON.stringify(shikiRuntimeConfig ?? null)},
80
83
  selectedSectionVariants: {},
81
84
  inputs: {
82
85
  header: {},
@@ -476,7 +479,7 @@ const sectionVariantFieldNames = Object.fromEntries(
476
479
  }
477
480
  };
478
481
  }
479
- console.log("SEND", finalUrl, finalOptions)
482
+
480
483
  // 6. Execute Request
481
484
  const res = await fetch(finalUrl, finalOptions);
482
485
 
@@ -487,11 +490,12 @@ const sectionVariantFieldNames = Object.fromEntries(
487
490
  responseHeaders[key] = value;
488
491
  });
489
492
 
490
- let highlightedData;
491
- const highlightedHeaders = Prism.highlight(
493
+ let responseDataText;
494
+ let responseDataLanguage = 'plaintext';
495
+ const highlightedHeaders = await window.RadiantPlaygroundHighlightCode(
492
496
  JSON.stringify(responseHeaders, null, 2),
493
- Prism.languages.json,
494
- "json",
497
+ 'json',
498
+ this.shiki,
495
499
  );
496
500
  const contentType = res.headers.get('content-type') || '';
497
501
  const responseText = await res.text();
@@ -500,25 +504,19 @@ const sectionVariantFieldNames = Object.fromEntries(
500
504
  try {
501
505
  const parsed =
502
506
  responseText.trim().length > 0 ? JSON.parse(responseText) : null;
503
- highlightedData = Prism.highlight(
504
- JSON.stringify(parsed, null, 2),
505
- Prism.languages.json,
506
- 'json'
507
- );
507
+ responseDataText = JSON.stringify(parsed, null, 2);
508
+ responseDataLanguage = 'json';
508
509
  } catch (parseError) {
509
- highlightedData = Prism.highlight(
510
- responseText || '(empty response)',
511
- Prism.languages.plaintext,
512
- 'plaintext'
513
- );
510
+ responseDataText = responseText || '(empty response)';
514
511
  }
515
512
  } else {
516
- highlightedData = Prism.highlight(
517
- responseText || '(empty response)',
518
- Prism.languages.plaintext,
519
- 'plaintext'
520
- );
513
+ responseDataText = responseText || '(empty response)';
521
514
  }
515
+ const highlightedData = await window.RadiantPlaygroundHighlightCode(
516
+ responseDataText,
517
+ responseDataLanguage,
518
+ this.shiki,
519
+ );
522
520
 
523
521
  this.response = {
524
522
  status,
@@ -682,45 +680,61 @@ const sectionVariantFieldNames = Object.fromEntries(
682
680
  </div>
683
681
 
684
682
  <script>
685
- import Prism from "prismjs";
686
- import "prismjs/components/prism-json";
687
- // Use a dark theme that matches the #0d1117 background
688
- import "prismjs/themes/prism-tomorrow.css";
689
- // Make Prism available to Alpine
690
- window.Prism = Prism;
691
- </script>
692
-
693
- <style>
694
- @reference "../../styles/global.css";
695
-
696
- :global(pre code span.token.punctuation) {
697
- color: #24292e;
683
+ import {
684
+ highlightAssistantCodeToHtml,
685
+ type AssistantShikiRuntimeConfig,
686
+ } from "../../lib/assistant-shiki-client";
687
+
688
+ type PlaygroundShikiConfig = AssistantShikiRuntimeConfig | null | undefined;
689
+
690
+ function escapeHtml(value: string): string {
691
+ return value
692
+ .replaceAll("&", "&amp;")
693
+ .replaceAll("<", "&lt;")
694
+ .replaceAll(">", "&gt;")
695
+ .replaceAll('"', "&quot;")
696
+ .replaceAll("'", "&#39;");
698
697
  }
699
698
 
700
- :global(pre code span.token.operator) {
701
- color: #24292e;
702
- }
699
+ function renderPlainCodeToHtml(code: string): string {
700
+ const lines = code.split("\n");
701
+ const normalizedLines = lines.length > 0 ? lines : [""];
703
702
 
704
- :global(pre code span.token.property) {
705
- color: #c62c2c;
706
- }
707
- :global(pre code span.token.string) {
708
- color: #1e7734;
703
+ return normalizedLines
704
+ .map((line) => {
705
+ const lineHtml = line.length > 0 ? escapeHtml(line) : "&nbsp;";
706
+ return `<span class="flex w-max min-w-full"><span class="flex-1 whitespace-pre pl-3 pr-3">${lineHtml}</span></span>`;
707
+ })
708
+ .join("");
709
709
  }
710
710
 
711
- :global(.dark pre code span.token.punctuation) {
712
- color: #c9d1d9;
713
- }
714
-
715
- :global(.dark pre code span.token.operator) {
716
- color: #c9d1d9;
717
- }
718
-
719
- :global(.dark pre code span.token.property) {
720
- color: #ff7b72;
711
+ async function highlightPlaygroundCodeToHtml(
712
+ code: unknown,
713
+ language: string,
714
+ config: PlaygroundShikiConfig,
715
+ ): Promise<string> {
716
+ const normalizedCode = String(code ?? "");
717
+ if (!config) return renderPlainCodeToHtml(normalizedCode);
718
+
719
+ try {
720
+ const highlighted = await highlightAssistantCodeToHtml({
721
+ code: normalizedCode,
722
+ config,
723
+ language,
724
+ });
725
+ return highlighted.html;
726
+ } catch (error) {
727
+ console.error("Playground code highlighting failed", {
728
+ error,
729
+ language,
730
+ });
731
+ return renderPlainCodeToHtml(normalizedCode);
732
+ }
721
733
  }
722
734
 
723
- :global(.dark pre code span.token.string) {
724
- color: #a5d6ff;
725
- }
726
- </style>
735
+ (
736
+ window as typeof window & {
737
+ RadiantPlaygroundHighlightCode?: typeof highlightPlaygroundCodeToHtml;
738
+ }
739
+ ).RadiantPlaygroundHighlightCode = highlightPlaygroundCodeToHtml;
740
+ </script>
@@ -161,7 +161,7 @@ import { Icon } from "astro-icon/components";
161
161
  </div>
162
162
  <div x-show="response && response.highlightedData">
163
163
  <pre
164
- class="relative m-0 min-w-full p-0 text-[13px] leading-6"><code data-rd-code-theme class="block min-w-full px-4 py-2.5 font-mono text-neutral-800 dark:text-neutral-200" x-html="response?.highlightedData" /></pre>
164
+ class="relative m-0 min-w-full p-0 text-[13px] leading-6"><code data-rd-code-theme class="block min-w-full py-2.5 font-mono text-neutral-800 dark:text-neutral-200" x-html="response?.highlightedData" /></pre>
165
165
  </div>
166
166
  </div>
167
167
 
@@ -185,7 +185,7 @@ import { Icon } from "astro-icon/components";
185
185
  </div>
186
186
  <div x-show="response && response.highlightedHeaders">
187
187
  <pre
188
- class="relative m-0 min-w-full bg-(--rd-code-surface) p-0 text-[13px] leading-6"><code data-rd-code-theme class="block min-w-full px-4 py-2.5 font-mono text-neutral-800 dark:text-neutral-200" x-html="response?.highlightedHeaders" /></pre>
188
+ class="relative m-0 min-w-full bg-(--rd-code-surface) p-0 text-[13px] leading-6"><code data-rd-code-theme class="block min-w-full py-2.5 font-mono text-neutral-800 dark:text-neutral-200" x-html="response?.highlightedHeaders" /></pre>
189
189
  </div>
190
190
  </div>
191
191
  </div>
@@ -1,20 +1,20 @@
1
1
  {
2
- "assetVersion": "shiki-3.23.0-c5695a7656dd",
2
+ "assetVersion": "shiki-4.2.0-c0d7e21cea71",
3
3
  "counts": {
4
4
  "languages": 347,
5
5
  "themes": 66
6
6
  },
7
7
  "manifest": {
8
- "bytes": 172640,
8
+ "bytes": 172635,
9
9
  "module": "manifest.json",
10
- "sha256": "d8ce64a7c4247eac90030e4dd92a9ac00f1de67114e0ac6c5e003e810c8e9cfb"
10
+ "sha256": "1840b6f21a79266d9bf3a74d5e6973d407fb06b96d71decd5542ca37eebf182d"
11
11
  },
12
12
  "packageVersions": {
13
- "@shikijs/core": "3.23.0",
14
- "@shikijs/engine-javascript": "3.23.0",
15
- "@shikijs/langs": "3.23.0",
16
- "@shikijs/themes": "3.23.0",
17
- "shiki": "3.23.0"
13
+ "@shikijs/core": "4.2.0",
14
+ "@shikijs/engine-javascript": "4.2.0",
15
+ "@shikijs/langs": "4.2.0",
16
+ "@shikijs/themes": "4.2.0",
17
+ "shiki": "4.2.0"
18
18
  },
19
19
  "prefix": "_platform/shiki",
20
20
  "runtime": {
@@ -5,12 +5,8 @@ import {
5
5
  import { withBasePath } from "./base-path";
6
6
  import { getAssistantLauncherIconConfig } from "./assistant-embed-script";
7
7
  import type { AssistantShikiRuntimeConfig } from "./assistant-shiki-client";
8
+ import { getClientShikiRuntimeConfig } from "./client-shiki-config";
8
9
  import type { DocsConfig } from "./validation";
9
- import shikiPlatformAssets from "../generated/shiki-platform-assets.json";
10
- import {
11
- DEFAULT_SHIKI_DARK_THEME,
12
- DEFAULT_SHIKI_LIGHT_THEME,
13
- } from "radiant-docs-validator/shiki-theme-config";
14
10
 
15
11
  export type AssistantPanelRuntimeConfig = {
16
12
  apiPath: string;
@@ -35,57 +31,6 @@ export type AssistantPanelRuntimeConfig = {
35
31
  shiki?: AssistantShikiRuntimeConfig;
36
32
  };
37
33
 
38
- function normalizeStaticAssetHost(value: unknown): string {
39
- const rawValue = typeof value === "string" ? value.trim() : "";
40
- if (!rawValue) return "";
41
-
42
- const normalizedHost = rawValue.replace(/\/+$/, "");
43
- return /^https?:\/\//i.test(normalizedHost)
44
- ? normalizedHost
45
- : `https://${normalizedHost}`;
46
- }
47
-
48
- function joinUrl(baseUrl: string, path: string): string {
49
- return `${baseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
50
- }
51
-
52
- function getConfiguredCodeSyntaxThemes(
53
- config: DocsConfig,
54
- ): AssistantShikiRuntimeConfig["syntaxThemes"] {
55
- const configuredSyntaxTheme = config.theme?.code?.syntaxTheme;
56
-
57
- if (typeof configuredSyntaxTheme === "string") {
58
- return {
59
- light: configuredSyntaxTheme,
60
- dark: configuredSyntaxTheme,
61
- };
62
- }
63
-
64
- return {
65
- light: configuredSyntaxTheme?.light ?? DEFAULT_SHIKI_LIGHT_THEME,
66
- dark: configuredSyntaxTheme?.dark ?? DEFAULT_SHIKI_DARK_THEME,
67
- };
68
- }
69
-
70
- function getAssistantShikiRuntimeConfig(
71
- config: DocsConfig,
72
- ): AssistantShikiRuntimeConfig | undefined {
73
- const staticAssetHost = normalizeStaticAssetHost(
74
- import.meta.env.STATIC_ASSET_HOST ?? process.env.STATIC_ASSET_HOST,
75
- );
76
- if (!staticAssetHost) {
77
- return undefined;
78
- }
79
-
80
- return {
81
- assetBaseUrl: joinUrl(
82
- staticAssetHost,
83
- `${shikiPlatformAssets.prefix}/${shikiPlatformAssets.assetVersion}`,
84
- ),
85
- syntaxThemes: getConfiguredCodeSyntaxThemes(config),
86
- };
87
- }
88
-
89
34
  export function getAssistantPanelRuntimeConfig(
90
35
  config: DocsConfig,
91
36
  ): AssistantPanelRuntimeConfig {
@@ -134,6 +79,6 @@ export function getAssistantPanelRuntimeConfig(
134
79
  emptyStateQuestions: assistantConfig?.questions,
135
80
  devProxyToken: isDev ? assistantDevProxySecret : undefined,
136
81
  chrome: getAssistantChromeConfig(config),
137
- shiki: getAssistantShikiRuntimeConfig(config),
82
+ shiki: getClientShikiRuntimeConfig(config),
138
83
  };
139
84
  }
@@ -175,9 +175,20 @@ async function loadRuntime(
175
175
  })();
176
176
 
177
177
  runtimePromiseByKey.set(runtimeCacheKey, runtimePromise);
178
+ runtimePromise.catch(() => {
179
+ if (runtimePromiseByKey.get(runtimeCacheKey) === runtimePromise) {
180
+ runtimePromiseByKey.delete(runtimeCacheKey);
181
+ }
182
+ });
178
183
  return runtimePromise;
179
184
  }
180
185
 
186
+ export async function warmAssistantShikiRuntime(
187
+ config: AssistantShikiRuntimeConfig,
188
+ ): Promise<void> {
189
+ await loadRuntime(config);
190
+ }
191
+
181
192
  function normalizeLanguageToken(rawLanguage: string): string {
182
193
  const normalized = rawLanguage.trim().toLowerCase();
183
194
  if (!normalized) return "";
@@ -254,6 +265,11 @@ async function ensureLanguageLoaded({
254
265
  await highlighter.loadLanguage(...registrations);
255
266
  })();
256
267
  languageLoadPromiseByKey.set(cacheKey, loadPromise);
268
+ loadPromise.catch(() => {
269
+ if (languageLoadPromiseByKey.get(cacheKey) === loadPromise) {
270
+ languageLoadPromiseByKey.delete(cacheKey);
271
+ }
272
+ });
257
273
  }
258
274
 
259
275
  await loadPromise;
@@ -0,0 +1,63 @@
1
+ import type { AssistantShikiRuntimeConfig } from "./assistant-shiki-client";
2
+ import type { DocsConfig } from "./validation";
3
+ import shikiPlatformAssets from "../generated/shiki-platform-assets.json";
4
+ import {
5
+ DEFAULT_SHIKI_DARK_THEME,
6
+ DEFAULT_SHIKI_LIGHT_THEME,
7
+ } from "radiant-docs-validator/shiki-theme-config";
8
+
9
+ export type ClientShikiRuntimeConfig = AssistantShikiRuntimeConfig;
10
+
11
+ const DEFAULT_SHIKI_PLATFORM_ASSET_HOST = "https://static.radiantdocs.com";
12
+
13
+ function normalizeStaticAssetHost(value: unknown): string {
14
+ const rawValue = typeof value === "string" ? value.trim() : "";
15
+ if (!rawValue) return "";
16
+
17
+ const normalizedHost = rawValue.replace(/\/+$/, "");
18
+ return /^https?:\/\//i.test(normalizedHost)
19
+ ? normalizedHost
20
+ : `https://${normalizedHost}`;
21
+ }
22
+
23
+ function joinUrl(baseUrl: string, path: string): string {
24
+ return `${baseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
25
+ }
26
+
27
+ function getConfiguredCodeSyntaxThemes(
28
+ config: DocsConfig,
29
+ ): ClientShikiRuntimeConfig["syntaxThemes"] {
30
+ const configuredSyntaxTheme = config.theme?.code?.syntaxTheme;
31
+
32
+ if (typeof configuredSyntaxTheme === "string") {
33
+ return {
34
+ light: configuredSyntaxTheme,
35
+ dark: configuredSyntaxTheme,
36
+ };
37
+ }
38
+
39
+ return {
40
+ light: configuredSyntaxTheme?.light ?? DEFAULT_SHIKI_LIGHT_THEME,
41
+ dark: configuredSyntaxTheme?.dark ?? DEFAULT_SHIKI_DARK_THEME,
42
+ };
43
+ }
44
+
45
+ export function getClientShikiRuntimeConfig(
46
+ config: DocsConfig,
47
+ ): ClientShikiRuntimeConfig | undefined {
48
+ const staticAssetHost =
49
+ normalizeStaticAssetHost(
50
+ import.meta.env.STATIC_ASSET_HOST ?? process.env.STATIC_ASSET_HOST,
51
+ ) || DEFAULT_SHIKI_PLATFORM_ASSET_HOST;
52
+ if (!staticAssetHost) {
53
+ return undefined;
54
+ }
55
+
56
+ return {
57
+ assetBaseUrl: joinUrl(
58
+ staticAssetHost,
59
+ `${shikiPlatformAssets.prefix}/${shikiPlatformAssets.assetVersion}`,
60
+ ),
61
+ syntaxThemes: getConfiguredCodeSyntaxThemes(config),
62
+ };
63
+ }