vocs 2.0.8 → 2.0.10

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.
@@ -1 +1 @@
1
- {"version":3,"file":"inline-cache.d.ts","sourceRoot":"","sources":["../../../src/internal/twoslash/inline-cache.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAGhF,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAE/C;;;;;;;;;;;;;;;;;;GAkBG;AAEH,8EAA8E;AAC9E,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;CACX,CAAA;AAED,OAAO,QAAQ,gBAAgB,CAAC;IAC9B,UAAU,2BAA2B;QACnC,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI,CAAA;QAC5B,OAAO,CAAC,EAAE,mBAAmB,CAAA;QAC7B,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;KACrC;CACF;AAUD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,MAAM,CAElF;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG;IACrD,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,SAAS,GAAG,IAAI,CAAA;CAC5B,CAWA;AAaD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,wBAAgB,sBAAsB,CACpC,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAAO,GAChF;IAAE,UAAU,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,CAyJ1D;AA+BD;;;GAGG;AACH,wBAAgB,OAAO,CAAC,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAIjE;AAWD,qEAAqE;AACrE,wBAAgB,WAAW,IAAI;IAAE,UAAU,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,CAGtF;AAED,iFAAiF;AACjF,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAExC;AAED,iDAAiD;AACjD,wBAAgB,KAAK,IAAI,IAAI,CAE5B"}
1
+ {"version":3,"file":"inline-cache.d.ts","sourceRoot":"","sources":["../../../src/internal/twoslash/inline-cache.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAEhF,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAE/C;;;;;;;;;;;;;;;;;;GAkBG;AAEH,8EAA8E;AAC9E,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;CACX,CAAA;AAED,OAAO,QAAQ,gBAAgB,CAAC;IAC9B,UAAU,2BAA2B;QACnC,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI,CAAA;QAC5B,OAAO,CAAC,EAAE,mBAAmB,CAAA;QAC7B,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;KACrC;CACF;AAUD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,MAAM,CAElF;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG;IACrD,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,SAAS,GAAG,IAAI,CAAA;CAC5B,CAWA;AAuBD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,wBAAgB,sBAAsB,CACpC,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAAO,GAChF;IAAE,UAAU,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,CAkJ1D;AA+BD;;;GAGG;AACH,wBAAgB,OAAO,CAAC,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAIjE;AAWD,qEAAqE;AACrE,wBAAgB,WAAW,IAAI;IAAE,UAAU,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,CAGtF;AAED,iFAAiF;AACjF,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAExC;AAED,iDAAiD;AACjD,wBAAgB,KAAK,IAAI,IAAI,CAE5B"}
@@ -1,6 +1,5 @@
1
1
  import * as crypto from 'node:crypto';
2
2
  import LZString from 'lz-string';
3
- import { getObjectHash } from 'twoslash/core';
4
3
  import { FilePatcher } from './file-patcher.js';
5
4
  /**
6
5
  * Tag used for the ephemeral source-map comment. This comment is injected into
@@ -25,6 +24,15 @@ export function extractSourceMapComment(code) {
25
24
  }
26
25
  return { code, sourceMap };
27
26
  }
27
+ /**
28
+ * Cache payload format version. Kept at `1` even though the cache key changed
29
+ * from `optionsHash:lang:code` to `lang:code` (see `cacheHash`): stale
30
+ * payloads from the old key naturally fail the hash check on `preprocess` and
31
+ * are transparently re-seeded on the next write, so a version bump (which
32
+ * would force a one-shot invalidation of every committed cache comment) is
33
+ * unnecessary.
34
+ */
35
+ const CACHE_VERSION = 1;
28
36
  const CODE_INLINE_CACHE_KEY = '@twoslash-cache';
29
37
  const CODE_INLINE_CACHE_REGEX = new RegExp(`// ${CODE_INLINE_CACHE_KEY}: (.*)(?:\n|$)`, 'g');
30
38
  /** Matches a cache comment anchored to the start of a code block body. */
@@ -44,25 +52,24 @@ export function stripInlineCacheComments(code) {
44
52
  export function createInlineTypesCache(options = {}) {
45
53
  const { remove, ignoreCache } = options;
46
54
  const patcher = new FilePatcher();
47
- const optionsHashCache = new WeakMap();
48
- function getOptionsHash(options = {}) {
49
- let hash = optionsHashCache.get(options);
50
- if (!hash) {
51
- hash = getObjectHash(options);
52
- optionsHashCache.set(options, hash);
53
- }
54
- return hash;
55
- }
56
- function cacheHash(code, lang, options) {
55
+ // Key the cache on the (already fully-composed) snippet code and language
56
+ // only — deliberately NOT on the twoslash options. The whole point of the
57
+ // inline cache is to travel with the repo across machines and CI, but
58
+ // `twoslashOptions` frequently contain environment-specific or volatile
59
+ // values (absolute paths, config that branches on whether built `.d.ts`
60
+ // files exist, etc). Hashing them in makes the committed cache miss on any
61
+ // machine other than the one that seeded it. This matches the filesystem
62
+ // types cache, which also keys on code alone.
63
+ function cacheHash(code, lang) {
57
64
  return crypto
58
65
  .createHash('sha256')
59
- .update(`${getOptionsHash(options)}:${lang ?? ''}:${code}`)
66
+ .update(`${lang ?? ''}:${code}`)
60
67
  .digest('hex');
61
68
  }
62
- function stringifyCachePayload(data, code, lang, options) {
69
+ function stringifyCachePayload(data, code, lang) {
63
70
  const payload = {
64
- v: 1,
65
- hash: cacheHash(code, lang, options),
71
+ v: CACHE_VERSION,
72
+ hash: cacheHash(code, lang),
66
73
  data: LZString.compressToBase64(JSON.stringify(data)),
67
74
  };
68
75
  return JSON.stringify(payload);
@@ -72,7 +79,7 @@ export function createInlineTypesCache(options = {}) {
72
79
  return null;
73
80
  try {
74
81
  const payload = JSON.parse(cache);
75
- if (payload.v === 1) {
82
+ if (payload.v === CACHE_VERSION) {
76
83
  return {
77
84
  payload,
78
85
  twoslash: () => {
@@ -132,7 +139,7 @@ export function createInlineTypesCache(options = {}) {
132
139
  };
133
140
  }
134
141
  const typesCache = {
135
- preprocess(code, lang, options, meta) {
142
+ preprocess(code, lang, _options, meta) {
136
143
  if (!meta)
137
144
  return;
138
145
  let rawCache = '';
@@ -148,7 +155,7 @@ export function createInlineTypesCache(options = {}) {
148
155
  const shouldLoadCache = !ignoreCache && !remove;
149
156
  if (shouldLoadCache) {
150
157
  const cache = resolveCachePayload(cacheString);
151
- if (cache?.payload.hash === cacheHash(code, lang, options)) {
158
+ if (cache?.payload.hash === cacheHash(code, lang)) {
152
159
  const twoslash = cache.twoslash();
153
160
  if (twoslash)
154
161
  meta.__cache = twoslash;
@@ -164,13 +171,13 @@ export function createInlineTypesCache(options = {}) {
164
171
  read(_code, _lang, _options, meta) {
165
172
  return meta?.__cache ?? null;
166
173
  },
167
- write(code, data, lang, options, meta) {
174
+ write(code, data, lang, _options, meta) {
168
175
  if (remove) {
169
176
  meta?.__patch?.('');
170
177
  return;
171
178
  }
172
179
  const twoslashShiki = simplifyTwoslashReturn(data);
173
- const cacheStr = `// ${CODE_INLINE_CACHE_KEY}: ${stringifyCachePayload(twoslashShiki, code, lang, options)}`;
180
+ const cacheStr = `// ${CODE_INLINE_CACHE_KEY}: ${stringifyCachePayload(twoslashShiki, code, lang)}`;
174
181
  meta?.__patch?.(cacheStr);
175
182
  },
176
183
  };
@@ -1 +1 @@
1
- {"version":3,"file":"inline-cache.js","sourceRoot":"","sources":["../../../src/internal/twoslash/inline-cache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"inline-cache.js","sourceRoot":"","sources":["../../../src/internal/twoslash/inline-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Lazily highlights code on the client.
3
+ *
4
+ * Twoslash hover popups embed a code snippet (the resolved type) for every
5
+ * hoverable token. Highlighting these with Shiki on the server forces RSC to
6
+ * render and serialize a fully highlighted snippet for every token across every
7
+ * page at build time — which dominates build memory and time even though the
8
+ * popups are only ever seen on hover.
9
+ *
10
+ * Instead we render the snippet as plain text in the RSC payload and highlight
11
+ * it on the client. Because the popup only mounts when it opens, the Shiki
12
+ * bundle is dynamically imported and the snippet highlighted on demand.
13
+ */
14
+ export declare function CodeToHtml(props: CodeToHtml.Props): import("react/jsx-runtime").JSX.Element;
15
+ export declare namespace CodeToHtml {
16
+ type Props = {
17
+ code: string;
18
+ lang: string;
19
+ };
20
+ }
21
+ /** Returns the highlighted HTML for a snippet if it has already been computed. */
22
+ export declare function getCached(code: string, lang: string): string | undefined;
23
+ /** Highlights a snippet (memoized), loading the Shiki bundle on first use. */
24
+ export declare function highlight(code: string, lang: string): Promise<string>;
25
+ /** Eagerly loads the Shiki bundle so the first real highlight is fast. */
26
+ export declare function prewarm(): void;
27
+ //# sourceMappingURL=CodeToHtml.client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CodeToHtml.client.d.ts","sourceRoot":"","sources":["../../../src/react/internal/CodeToHtml.client.tsx"],"names":[],"mappings":"AAKA;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,CAAC,KAAK,2CA2BjD;AAmCD,yBAAiB,UAAU,CAAC;IAC1B,KAAY,KAAK,GAAG;QAClB,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;CACF;AA0DD,kFAAkF;AAClF,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,sBAEnD;AAED,8EAA8E;AAC9E,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,mBAQzD;AAED,0EAA0E;AAC1E,wBAAgB,OAAO,SAEtB"}
@@ -0,0 +1,138 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { config } from 'virtual:vocs/config';
4
+ import { useEffect, useState } from 'react';
5
+ /**
6
+ * Lazily highlights code on the client.
7
+ *
8
+ * Twoslash hover popups embed a code snippet (the resolved type) for every
9
+ * hoverable token. Highlighting these with Shiki on the server forces RSC to
10
+ * render and serialize a fully highlighted snippet for every token across every
11
+ * page at build time — which dominates build memory and time even though the
12
+ * popups are only ever seen on hover.
13
+ *
14
+ * Instead we render the snippet as plain text in the RSC payload and highlight
15
+ * it on the client. Because the popup only mounts when it opens, the Shiki
16
+ * bundle is dynamically imported and the snippet highlighted on demand.
17
+ */
18
+ export function CodeToHtml(props) {
19
+ const { code, lang } = props;
20
+ // Initialize from the cache so a snippet that was already highlighted (e.g.
21
+ // pre-highlighted by the popover before opening, or shown previously) renders
22
+ // highlighted immediately without a plain-text flash.
23
+ const [html, setHtml] = useState(() => getCached(code, lang) ?? null);
24
+ useEffect(() => {
25
+ if (html)
26
+ return;
27
+ let cancelled = false;
28
+ highlight(code, lang)
29
+ .then((result) => {
30
+ if (!cancelled)
31
+ setHtml(result);
32
+ })
33
+ .catch(() => { });
34
+ return () => {
35
+ cancelled = true;
36
+ };
37
+ }, [code, lang, html]);
38
+ if (html)
39
+ // biome-ignore lint/security/noDangerouslySetInnerHtml: highlighted by Shiki
40
+ return _jsx("div", { dangerouslySetInnerHTML: { __html: html } });
41
+ // While highlighting, render a skeleton that mirrors the snippet's line
42
+ // structure so the popup is shown immediately without a layout shift.
43
+ return _jsx(Skeleton, { code: code });
44
+ }
45
+ function Skeleton(props) {
46
+ const lines = props.code.split('\n');
47
+ return (_jsx("div", { "data-v-code-skeleton": true, "aria-hidden": "true", children: _jsxs("pre", { className: "shiki", "data-v-overflow-fade": true, children: [_jsx("code", { children: lines.map((line, index) => {
48
+ const indent = line.length - line.trimStart().length;
49
+ const length = line.trim().length;
50
+ return (_jsx("span", { className: "line", children: length > 0 && (_jsx("span", { "data-v-skeleton-bar": true, style: {
51
+ marginLeft: `${indent}ch`,
52
+ width: `${Math.min(length, 48)}ch`,
53
+ } })) }, index));
54
+ }) }), _jsx("div", { "data-v-overflow-sentinel": true })] }) }));
55
+ }
56
+ let highlighterPromise;
57
+ function getHighlighter() {
58
+ if (!highlighterPromise)
59
+ highlighterPromise = (async () => {
60
+ const { bundledLanguages, createHighlighter, hastToHtml } = await import('shiki/bundle/web');
61
+ const { codeHighlight } = config;
62
+ const { langAlias = {}, themes } = codeHighlight;
63
+ // Note: `langAlias` is intentionally not passed to the highlighter.
64
+ // Passing a custom `langAlias` registers languages under their alias name
65
+ // when lazily loaded (e.g. `loadLanguage('ts')` registers as `ts` instead
66
+ // of `typescript`), which then makes `codeToHast({ lang })` fail to resolve
67
+ // the grammar. We resolve aliases to their base language ourselves below.
68
+ const highlighter = await createHighlighter({
69
+ themes: Object.values(themes),
70
+ langs: [],
71
+ });
72
+ return {
73
+ async codeToHtml(code, lang) {
74
+ const base = langAlias[lang] ?? lang;
75
+ const resolvedLang = base in bundledLanguages ? base : 'txt';
76
+ if (!highlighter.getLoadedLanguages().includes(resolvedLang))
77
+ await highlighter.loadLanguage(resolvedLang);
78
+ const hast = highlighter.codeToHast(code, {
79
+ defaultColor: 'light-dark()',
80
+ lang: resolvedLang,
81
+ rootStyle: false,
82
+ meta: { 'data-v-overflow-fade': true },
83
+ themes,
84
+ transformers: [transformerShrinkIndent()],
85
+ });
86
+ const pre = hast.children[0];
87
+ if (pre && pre.type === 'element' && pre.tagName === 'pre')
88
+ pre.children.push({
89
+ type: 'element',
90
+ tagName: 'div',
91
+ properties: { 'data-v-overflow-sentinel': true },
92
+ children: [],
93
+ });
94
+ return hastToHtml(hast);
95
+ },
96
+ };
97
+ })();
98
+ return highlighterPromise;
99
+ }
100
+ const cache = new Map();
101
+ function cacheKey(code, lang) {
102
+ return `${lang}\n${code}`;
103
+ }
104
+ /** Returns the highlighted HTML for a snippet if it has already been computed. */
105
+ export function getCached(code, lang) {
106
+ return cache.get(cacheKey(code, lang));
107
+ }
108
+ /** Highlights a snippet (memoized), loading the Shiki bundle on first use. */
109
+ export async function highlight(code, lang) {
110
+ const key = cacheKey(code, lang);
111
+ const cached = cache.get(key);
112
+ if (cached !== undefined)
113
+ return cached;
114
+ const highlighter = await getHighlighter();
115
+ const html = await highlighter.codeToHtml(code, lang);
116
+ cache.set(key, html);
117
+ return html;
118
+ }
119
+ /** Eagerly loads the Shiki bundle so the first real highlight is fast. */
120
+ export function prewarm() {
121
+ void getHighlighter();
122
+ }
123
+ function transformerShrinkIndent() {
124
+ return {
125
+ name: 'indent',
126
+ span(hast) {
127
+ const child = hast.children[0];
128
+ if (!child)
129
+ return;
130
+ if (child.type !== 'text')
131
+ return;
132
+ if (!child.value)
133
+ return;
134
+ hast.children[0] = { type: 'text', value: child.value.replace(/\s\s/g, ' ') };
135
+ },
136
+ };
137
+ }
138
+ //# sourceMappingURL=CodeToHtml.client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CodeToHtml.client.js","sourceRoot":"","sources":["../../../src/react/internal/CodeToHtml.client.tsx"],"names":[],"mappings":"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"}
@@ -1,8 +1,2 @@
1
- export declare function CodeToHtml(props: CodeToHtml.Props): Promise<import("react/jsx-runtime").JSX.Element>;
2
- export declare namespace CodeToHtml {
3
- type Props = {
4
- code: string;
5
- lang: string;
6
- };
7
- }
1
+ export { CodeToHtml } from './CodeToHtml.client.js';
8
2
  //# sourceMappingURL=CodeToHtml.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CodeToHtml.d.ts","sourceRoot":"","sources":["../../../src/react/internal/CodeToHtml.tsx"],"names":[],"mappings":"AAYA,wBAAsB,UAAU,CAAC,KAAK,EAAE,UAAU,CAAC,KAAK,oDAyCvD;AAED,yBAAiB,UAAU,CAAC;IAC1B,KAAY,KAAK,GAAG;QAClB,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;CACF"}
1
+ {"version":3,"file":"CodeToHtml.d.ts","sourceRoot":"","sources":["../../../src/react/internal/CodeToHtml.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA"}
@@ -1,57 +1,2 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { config } from 'virtual:vocs/config';
3
- import { langs as virtualLangs } from 'virtual:vocs/langs';
4
- import { bundledLanguages, createHighlighter, hastToHtml, makeSingletonHighlighter, } from 'shiki/bundle/web';
5
- const getHighlighter = makeSingletonHighlighter(createHighlighter);
6
- export async function CodeToHtml(props) {
7
- const { code, lang } = props;
8
- const { codeHighlight } = config;
9
- const { langAlias = {}, themes } = codeHighlight;
10
- const highlighter = await getHighlighter({
11
- themes: import.meta.env.DEV ? ['none'] : Object.values(themes),
12
- langs: import.meta.env.DEV
13
- ? ['txt']
14
- : [...Object.keys(bundledLanguages), ...virtualLangs],
15
- langAlias,
16
- });
17
- const loadedLangs = highlighter.getLoadedLanguages();
18
- const resolvedLang = loadedLangs.includes(lang) ? lang : 'txt';
19
- const hast = highlighter.codeToHast(code, {
20
- defaultColor: 'light-dark()',
21
- lang: import.meta.env.DEV ? 'txt' : resolvedLang,
22
- rootStyle: false,
23
- meta: {
24
- 'data-v-overflow-fade': true,
25
- },
26
- ...(import.meta.env.DEV ? { theme: 'none' } : { themes }),
27
- transformers: [transformerShrinkIndent()],
28
- });
29
- // Add overflow sentinel
30
- const pre = hast.children[0];
31
- if (pre && pre.type === 'element' && pre.tagName === 'pre')
32
- pre.children.push({
33
- type: 'element',
34
- tagName: 'div',
35
- properties: { 'data-v-overflow-sentinel': true },
36
- children: [],
37
- });
38
- const html = hastToHtml(hast);
39
- // biome-ignore lint/security/noDangerouslySetInnerHtml: _
40
- return _jsx("div", { dangerouslySetInnerHTML: { __html: html } });
41
- }
42
- function transformerShrinkIndent() {
43
- return {
44
- name: 'indent',
45
- span(hast) {
46
- const child = hast.children[0];
47
- if (!child)
48
- return;
49
- if (child.type !== 'text')
50
- return;
51
- if (!child.value)
52
- return;
53
- hast.children[0] = { type: 'text', value: child.value.replace(/\s\s/g, ' ') };
54
- },
55
- };
56
- }
1
+ export { CodeToHtml } from './CodeToHtml.client.js';
57
2
  //# sourceMappingURL=CodeToHtml.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CodeToHtml.js","sourceRoot":"","sources":["../../../src/react/internal/CodeToHtml.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CodeToHtml.js","sourceRoot":"","sources":["../../../src/react/internal/CodeToHtml.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"TwoslashHover.client.d.ts","sourceRoot":"","sources":["../../../src/react/internal/TwoslashHover.client.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAA;AAGnC,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,CAAC,KAAK,2CA+BvD;AAED,yBAAiB,aAAa,CAAC;IAC7B,KAAY,KAAK,GAAG;QAClB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QAC9B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;QACzB,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;QAC1B,OAAO,EAAE,KAAK,CAAC,SAAS,CAAA;KACzB,CAAA;IAED,SAAgB,eAAe;uBACK,cAAc,GAAG,IAAI;MAwBxD;CACF"}
1
+ {"version":3,"file":"TwoslashHover.client.d.ts","sourceRoot":"","sources":["../../../src/react/internal/TwoslashHover.client.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAA;AAInC,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,CAAC,KAAK,2CAqCvD;AAED,yBAAiB,aAAa,CAAC;IAC7B,KAAY,KAAK,GAAG;QAClB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QAC9B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;QACzB,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;QAC1B,OAAO,EAAE,KAAK,CAAC,SAAS,CAAA;KACzB,CAAA;IAED,SAAgB,eAAe;uBACK,cAAc,GAAG,IAAI;MAwBxD;CACF"}
@@ -1,11 +1,17 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Popover } from '@base-ui/react/popover';
4
- import { useCallback } from 'react';
4
+ import { useCallback, useEffect } from 'react';
5
+ import { prewarm } from './CodeToHtml.client.js';
5
6
  export function TwoslashHover(props) {
6
7
  const { className = '', children, trigger } = props;
7
8
  const { ref } = TwoslashHover.useOverflowFade();
8
9
  const open = className?.includes('twoslash-query-persisted');
10
+ // Warm the highlighter so the code inside the popup highlights as quickly as
11
+ // possible (the popup shows a skeleton placeholder until then).
12
+ useEffect(() => {
13
+ prewarm();
14
+ }, []);
9
15
  return (_jsxs(Popover.Root, { ...(open ? { open } : {}), children: [_jsx(Popover.Trigger, { "data-v-twoslash-trigger": true, openOnHover: true, delay: 0, children: _jsx("span", { children: trigger }) }), _jsx(Popover.Portal, { children: _jsx(Popover.Positioner, { align: "start", side: "bottom", sideOffset: 4, collisionAvoidance: { side: 'none' }, children: _jsxs(Popover.Popup, { className: className, initialFocus: false, children: [_jsx(Popover.Arrow, { className: "vocs:data-[side=bottom]:top-[-8px] vocs:data-[side=left]:right-[-13px] vocs:data-[side=left]:rotate-90 vocs:data-[side=right]:left-[-13px] vocs:data-[side=right]:-rotate-90 vocs:data-[side=top]:bottom-[-8px] vocs:data-[side=top]:rotate-180", children: _jsx(ArrowSvg, {}) }), _jsx("div", { "data-v-content": true, ref: ref, children: children })] }) }) })] }));
10
16
  }
11
17
  (function (TwoslashHover) {
@@ -1 +1 @@
1
- {"version":3,"file":"TwoslashHover.client.js","sourceRoot":"","sources":["../../../src/react/internal/TwoslashHover.client.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TwoslashHover.client.js","sourceRoot":"","sources":["../../../src/react/internal/TwoslashHover.client.tsx"],"names":[],"mappings":"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"}
@@ -63,6 +63,38 @@
63
63
  }
64
64
  }
65
65
 
66
+ [data-v-code-skeleton] [data-v-skeleton-bar] {
67
+ display: inline-block;
68
+ height: 0.72em;
69
+ vertical-align: middle;
70
+ border-radius: 0.1875rem;
71
+ background-color: var(--vocs-background-color-surfaceTint);
72
+ background-image: linear-gradient(
73
+ 90deg,
74
+ transparent 0%,
75
+ color-mix(in srgb, var(--vocs-color-gray10) 22%, transparent) 50%,
76
+ transparent 100%
77
+ );
78
+ background-repeat: no-repeat;
79
+ background-size: 200% 100%;
80
+ animation: vocs-skeleton-shimmer 1.25s ease-in-out infinite;
81
+ }
82
+
83
+ @keyframes vocs-skeleton-shimmer {
84
+ from {
85
+ background-position: 180% 0;
86
+ }
87
+ to {
88
+ background-position: -80% 0;
89
+ }
90
+ }
91
+
92
+ @media (prefers-reduced-motion: reduce) {
93
+ [data-v-code-skeleton] [data-v-skeleton-bar] {
94
+ animation: none;
95
+ }
96
+ }
97
+
66
98
  .twoslash-popup-docs {
67
99
  @apply vocs:max-h-40 vocs:overflow-y-scroll;
68
100
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vocs",
3
3
  "type": "module",
4
- "version": "2.0.8",
4
+ "version": "2.0.10",
5
5
  "main": "./dist/index.js",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -141,6 +141,32 @@ describe('inline types cache', () => {
141
141
  expect(cacheLines()).toHaveLength(1)
142
142
  })
143
143
 
144
+ it('hits regardless of twoslash options (portable across environments)', () => {
145
+ const code = 'const a: string = "x"'
146
+ const { file, from, to } = createMarkdown(code)
147
+ const data = { nodes: [], code: 'compiled-output' }
148
+
149
+ // Seed with one set of options (e.g. machine A, with `paths`).
150
+ {
151
+ const { typesCache, patcher } = createInlineTypesCache()
152
+ const meta = { sourceMap: { path: file, from, to } } as never
153
+ const options = { compilerOptions: { paths: { foo: ['/abs/a'] } } }
154
+ typesCache.preprocess?.(code, 'ts', options as never, meta)
155
+ typesCache.write(code, data as never, 'ts', options as never, meta)
156
+ patcher.patch(file)
157
+ }
158
+
159
+ // Read with different options (e.g. machine B / Vercel, without `paths`).
160
+ {
161
+ const { typesCache } = createInlineTypesCache()
162
+ const body = readBody(file, from)
163
+ const meta = { sourceMap: { path: file, from, to } } as never
164
+ const otherOptions = { compilerOptions: {} }
165
+ typesCache.preprocess?.(body, 'ts', otherOptions as never, meta)
166
+ expect(typesCache.read(body, 'ts', otherOptions as never, meta)).toEqual(data)
167
+ }
168
+ })
169
+
144
170
  it('invalidates the cache when the hash no longer matches', () => {
145
171
  const code = 'const a: string = "x"'
146
172
  const { file, from, to } = createMarkdown(code)
@@ -1,7 +1,6 @@
1
1
  import * as crypto from 'node:crypto'
2
2
  import type { TwoslashShikiReturn, TwoslashTypesCache } from '@shikijs/twoslash'
3
3
  import LZString from 'lz-string'
4
- import { getObjectHash, type TwoslashExecuteOptions } from 'twoslash/core'
5
4
  import { FilePatcher } from './file-patcher.js'
6
5
 
7
6
  /**
@@ -73,6 +72,16 @@ type CachePayload = {
73
72
  data: string
74
73
  }
75
74
 
75
+ /**
76
+ * Cache payload format version. Kept at `1` even though the cache key changed
77
+ * from `optionsHash:lang:code` to `lang:code` (see `cacheHash`): stale
78
+ * payloads from the old key naturally fail the hash check on `preprocess` and
79
+ * are transparently re-seeded on the next write, so a version bump (which
80
+ * would force a one-shot invalidation of every committed cache comment) is
81
+ * unnecessary.
82
+ */
83
+ const CACHE_VERSION = 1
84
+
76
85
  const CODE_INLINE_CACHE_KEY = '@twoslash-cache'
77
86
  const CODE_INLINE_CACHE_REGEX = new RegExp(`// ${CODE_INLINE_CACHE_KEY}: (.*)(?:\n|$)`, 'g')
78
87
  /** Matches a cache comment anchored to the start of a code block body. */
@@ -97,32 +106,25 @@ export function createInlineTypesCache(
97
106
  const { remove, ignoreCache } = options
98
107
  const patcher = new FilePatcher()
99
108
 
100
- const optionsHashCache = new WeakMap<TwoslashExecuteOptions, string>()
101
- function getOptionsHash(options: TwoslashExecuteOptions = {}): string {
102
- let hash = optionsHashCache.get(options)
103
- if (!hash) {
104
- hash = getObjectHash(options)
105
- optionsHashCache.set(options, hash)
106
- }
107
- return hash
108
- }
109
-
110
- function cacheHash(code: string, lang?: string, options?: TwoslashExecuteOptions): string {
109
+ // Key the cache on the (already fully-composed) snippet code and language
110
+ // only — deliberately NOT on the twoslash options. The whole point of the
111
+ // inline cache is to travel with the repo across machines and CI, but
112
+ // `twoslashOptions` frequently contain environment-specific or volatile
113
+ // values (absolute paths, config that branches on whether built `.d.ts`
114
+ // files exist, etc). Hashing them in makes the committed cache miss on any
115
+ // machine other than the one that seeded it. This matches the filesystem
116
+ // types cache, which also keys on code alone.
117
+ function cacheHash(code: string, lang?: string): string {
111
118
  return crypto
112
119
  .createHash('sha256')
113
- .update(`${getOptionsHash(options)}:${lang ?? ''}:${code}`)
120
+ .update(`${lang ?? ''}:${code}`)
114
121
  .digest('hex')
115
122
  }
116
123
 
117
- function stringifyCachePayload(
118
- data: TwoslashShikiReturn,
119
- code: string,
120
- lang?: string,
121
- options?: TwoslashExecuteOptions,
122
- ): string {
124
+ function stringifyCachePayload(data: TwoslashShikiReturn, code: string, lang?: string): string {
123
125
  const payload: CachePayload = {
124
- v: 1,
125
- hash: cacheHash(code, lang, options),
126
+ v: CACHE_VERSION,
127
+ hash: cacheHash(code, lang),
126
128
  data: LZString.compressToBase64(JSON.stringify(data)),
127
129
  }
128
130
  return JSON.stringify(payload)
@@ -135,7 +137,7 @@ export function createInlineTypesCache(
135
137
  if (!cache) return null
136
138
  try {
137
139
  const payload = JSON.parse(cache) as CachePayload
138
- if (payload.v === 1) {
140
+ if (payload.v === CACHE_VERSION) {
139
141
  return {
140
142
  payload,
141
143
  twoslash: () => {
@@ -200,7 +202,7 @@ export function createInlineTypesCache(
200
202
  }
201
203
 
202
204
  const typesCache: TwoslashTypesCache = {
203
- preprocess(code, lang, options, meta) {
205
+ preprocess(code, lang, _options, meta) {
204
206
  if (!meta) return
205
207
 
206
208
  let rawCache = ''
@@ -218,7 +220,7 @@ export function createInlineTypesCache(
218
220
  const shouldLoadCache = !ignoreCache && !remove
219
221
  if (shouldLoadCache) {
220
222
  const cache = resolveCachePayload(cacheString)
221
- if (cache?.payload.hash === cacheHash(code, lang, options)) {
223
+ if (cache?.payload.hash === cacheHash(code, lang)) {
222
224
  const twoslash = cache.twoslash()
223
225
  if (twoslash) meta.__cache = twoslash
224
226
  }
@@ -234,13 +236,13 @@ export function createInlineTypesCache(
234
236
  read(_code, _lang, _options, meta) {
235
237
  return meta?.__cache ?? null
236
238
  },
237
- write(code, data, lang, options, meta) {
239
+ write(code, data, lang, _options, meta) {
238
240
  if (remove) {
239
241
  meta?.__patch?.('')
240
242
  return
241
243
  }
242
244
  const twoslashShiki = simplifyTwoslashReturn(data)
243
- const cacheStr = `// ${CODE_INLINE_CACHE_KEY}: ${stringifyCachePayload(twoslashShiki, code, lang, options)}`
245
+ const cacheStr = `// ${CODE_INLINE_CACHE_KEY}: ${stringifyCachePayload(twoslashShiki, code, lang)}`
244
246
  meta?.__patch?.(cacheStr)
245
247
  },
246
248
  }
@@ -0,0 +1,176 @@
1
+ 'use client'
2
+
3
+ import { config } from 'virtual:vocs/config'
4
+ import { useEffect, useState } from 'react'
5
+
6
+ /**
7
+ * Lazily highlights code on the client.
8
+ *
9
+ * Twoslash hover popups embed a code snippet (the resolved type) for every
10
+ * hoverable token. Highlighting these with Shiki on the server forces RSC to
11
+ * render and serialize a fully highlighted snippet for every token across every
12
+ * page at build time — which dominates build memory and time even though the
13
+ * popups are only ever seen on hover.
14
+ *
15
+ * Instead we render the snippet as plain text in the RSC payload and highlight
16
+ * it on the client. Because the popup only mounts when it opens, the Shiki
17
+ * bundle is dynamically imported and the snippet highlighted on demand.
18
+ */
19
+ export function CodeToHtml(props: CodeToHtml.Props) {
20
+ const { code, lang } = props
21
+ // Initialize from the cache so a snippet that was already highlighted (e.g.
22
+ // pre-highlighted by the popover before opening, or shown previously) renders
23
+ // highlighted immediately without a plain-text flash.
24
+ const [html, setHtml] = useState<string | null>(() => getCached(code, lang) ?? null)
25
+
26
+ useEffect(() => {
27
+ if (html) return
28
+ let cancelled = false
29
+ highlight(code, lang)
30
+ .then((result) => {
31
+ if (!cancelled) setHtml(result)
32
+ })
33
+ .catch(() => {})
34
+ return () => {
35
+ cancelled = true
36
+ }
37
+ }, [code, lang, html])
38
+
39
+ if (html)
40
+ // biome-ignore lint/security/noDangerouslySetInnerHtml: highlighted by Shiki
41
+ return <div dangerouslySetInnerHTML={{ __html: html }} />
42
+
43
+ // While highlighting, render a skeleton that mirrors the snippet's line
44
+ // structure so the popup is shown immediately without a layout shift.
45
+ return <Skeleton code={code} />
46
+ }
47
+
48
+ function Skeleton(props: { code: string }) {
49
+ const lines = props.code.split('\n')
50
+ return (
51
+ <div data-v-code-skeleton aria-hidden="true">
52
+ {/* `shiki` class mirrors the real highlighted markup so shared styles
53
+ (e.g. code padding) apply identically and there is no layout shift. */}
54
+ <pre className="shiki" data-v-overflow-fade>
55
+ <code>
56
+ {lines.map((line, index) => {
57
+ const indent = line.length - line.trimStart().length
58
+ const length = line.trim().length
59
+ return (
60
+ // biome-ignore lint/suspicious/noArrayIndexKey: static, never reordered
61
+ <span className="line" key={index}>
62
+ {length > 0 && (
63
+ <span
64
+ data-v-skeleton-bar
65
+ style={{
66
+ marginLeft: `${indent}ch`,
67
+ width: `${Math.min(length, 48)}ch`,
68
+ }}
69
+ />
70
+ )}
71
+ </span>
72
+ )
73
+ })}
74
+ </code>
75
+ <div data-v-overflow-sentinel />
76
+ </pre>
77
+ </div>
78
+ )
79
+ }
80
+
81
+ export namespace CodeToHtml {
82
+ export type Props = {
83
+ code: string
84
+ lang: string
85
+ }
86
+ }
87
+
88
+ let highlighterPromise:
89
+ | Promise<{
90
+ codeToHtml: (code: string, lang: string) => Promise<string>
91
+ }>
92
+ | undefined
93
+
94
+ function getHighlighter() {
95
+ if (!highlighterPromise)
96
+ highlighterPromise = (async () => {
97
+ const { bundledLanguages, createHighlighter, hastToHtml } = await import('shiki/bundle/web')
98
+ const { codeHighlight } = config
99
+ const { langAlias = {}, themes } = codeHighlight
100
+ // Note: `langAlias` is intentionally not passed to the highlighter.
101
+ // Passing a custom `langAlias` registers languages under their alias name
102
+ // when lazily loaded (e.g. `loadLanguage('ts')` registers as `ts` instead
103
+ // of `typescript`), which then makes `codeToHast({ lang })` fail to resolve
104
+ // the grammar. We resolve aliases to their base language ourselves below.
105
+ const highlighter = await createHighlighter({
106
+ themes: Object.values(themes) as never,
107
+ langs: [],
108
+ })
109
+ return {
110
+ async codeToHtml(code: string, lang: string) {
111
+ const base = langAlias[lang] ?? lang
112
+ const resolvedLang = base in bundledLanguages ? base : 'txt'
113
+ if (!highlighter.getLoadedLanguages().includes(resolvedLang))
114
+ await highlighter.loadLanguage(resolvedLang as never)
115
+ const hast = highlighter.codeToHast(code, {
116
+ defaultColor: 'light-dark()',
117
+ lang: resolvedLang,
118
+ rootStyle: false,
119
+ meta: { 'data-v-overflow-fade': true },
120
+ themes,
121
+ transformers: [transformerShrinkIndent()],
122
+ })
123
+ const pre = hast.children[0]
124
+ if (pre && pre.type === 'element' && pre.tagName === 'pre')
125
+ pre.children.push({
126
+ type: 'element',
127
+ tagName: 'div',
128
+ properties: { 'data-v-overflow-sentinel': true },
129
+ children: [],
130
+ })
131
+ return hastToHtml(hast)
132
+ },
133
+ }
134
+ })()
135
+ return highlighterPromise
136
+ }
137
+
138
+ const cache = new Map<string, string>()
139
+
140
+ function cacheKey(code: string, lang: string) {
141
+ return `${lang}\n${code}`
142
+ }
143
+
144
+ /** Returns the highlighted HTML for a snippet if it has already been computed. */
145
+ export function getCached(code: string, lang: string) {
146
+ return cache.get(cacheKey(code, lang))
147
+ }
148
+
149
+ /** Highlights a snippet (memoized), loading the Shiki bundle on first use. */
150
+ export async function highlight(code: string, lang: string) {
151
+ const key = cacheKey(code, lang)
152
+ const cached = cache.get(key)
153
+ if (cached !== undefined) return cached
154
+ const highlighter = await getHighlighter()
155
+ const html = await highlighter.codeToHtml(code, lang)
156
+ cache.set(key, html)
157
+ return html
158
+ }
159
+
160
+ /** Eagerly loads the Shiki bundle so the first real highlight is fast. */
161
+ export function prewarm() {
162
+ void getHighlighter()
163
+ }
164
+
165
+ function transformerShrinkIndent() {
166
+ return {
167
+ name: 'indent',
168
+ span(hast: { children: { type: string; value?: string }[] }) {
169
+ const child = hast.children[0]
170
+ if (!child) return
171
+ if (child.type !== 'text') return
172
+ if (!child.value) return
173
+ hast.children[0] = { type: 'text', value: child.value.replace(/\s\s/g, ' ') }
174
+ },
175
+ }
176
+ }
@@ -1,74 +1 @@
1
- import { config } from 'virtual:vocs/config'
2
- import { langs as virtualLangs } from 'virtual:vocs/langs'
3
- import {
4
- bundledLanguages,
5
- createHighlighter,
6
- hastToHtml,
7
- makeSingletonHighlighter,
8
- type ShikiTransformer,
9
- } from 'shiki/bundle/web'
10
-
11
- const getHighlighter = makeSingletonHighlighter(createHighlighter)
12
-
13
- export async function CodeToHtml(props: CodeToHtml.Props) {
14
- const { code, lang } = props
15
- const { codeHighlight } = config
16
- const { langAlias = {}, themes } = codeHighlight
17
-
18
- const highlighter = await getHighlighter({
19
- themes: import.meta.env.DEV ? ['none'] : (Object.values(themes) as never),
20
- langs: import.meta.env.DEV
21
- ? ['txt']
22
- : ([...Object.keys(bundledLanguages), ...virtualLangs] as never),
23
- langAlias,
24
- })
25
-
26
- const loadedLangs = highlighter.getLoadedLanguages()
27
- const resolvedLang = loadedLangs.includes(lang) ? lang : 'txt'
28
-
29
- const hast = highlighter.codeToHast(code, {
30
- defaultColor: 'light-dark()',
31
- lang: import.meta.env.DEV ? 'txt' : resolvedLang,
32
- rootStyle: false,
33
- meta: {
34
- 'data-v-overflow-fade': true,
35
- },
36
- ...(import.meta.env.DEV ? { theme: 'none' } : { themes }),
37
- transformers: [transformerShrinkIndent()],
38
- })
39
-
40
- // Add overflow sentinel
41
- const pre = hast.children[0]
42
- if (pre && pre.type === 'element' && pre.tagName === 'pre')
43
- pre.children.push({
44
- type: 'element',
45
- tagName: 'div',
46
- properties: { 'data-v-overflow-sentinel': true },
47
- children: [],
48
- })
49
-
50
- const html = hastToHtml(hast)
51
-
52
- // biome-ignore lint/security/noDangerouslySetInnerHtml: _
53
- return <div dangerouslySetInnerHTML={{ __html: html }} />
54
- }
55
-
56
- export namespace CodeToHtml {
57
- export type Props = {
58
- code: string
59
- lang: string
60
- }
61
- }
62
-
63
- function transformerShrinkIndent(): ShikiTransformer {
64
- return {
65
- name: 'indent',
66
- span(hast) {
67
- const child = hast.children[0]
68
- if (!child) return
69
- if (child.type !== 'text') return
70
- if (!child.value) return
71
- hast.children[0] = { type: 'text', value: child.value.replace(/\s\s/g, ' ') }
72
- },
73
- }
74
- }
1
+ export { CodeToHtml } from './CodeToHtml.client.js'
@@ -2,7 +2,8 @@
2
2
 
3
3
  import { Popover } from '@base-ui/react/popover'
4
4
  import type * as React from 'react'
5
- import { useCallback } from 'react'
5
+ import { useCallback, useEffect } from 'react'
6
+ import { prewarm } from './CodeToHtml.client.js'
6
7
 
7
8
  export function TwoslashHover(props: TwoslashHover.Props) {
8
9
  const { className = '', children, trigger } = props
@@ -11,6 +12,12 @@ export function TwoslashHover(props: TwoslashHover.Props) {
11
12
 
12
13
  const open = className?.includes('twoslash-query-persisted')
13
14
 
15
+ // Warm the highlighter so the code inside the popup highlights as quickly as
16
+ // possible (the popup shows a skeleton placeholder until then).
17
+ useEffect(() => {
18
+ prewarm()
19
+ }, [])
20
+
14
21
  return (
15
22
  <Popover.Root {...(open ? { open } : {})}>
16
23
  <Popover.Trigger data-v-twoslash-trigger openOnHover delay={0}>
@@ -63,6 +63,38 @@
63
63
  }
64
64
  }
65
65
 
66
+ [data-v-code-skeleton] [data-v-skeleton-bar] {
67
+ display: inline-block;
68
+ height: 0.72em;
69
+ vertical-align: middle;
70
+ border-radius: 0.1875rem;
71
+ background-color: var(--vocs-background-color-surfaceTint);
72
+ background-image: linear-gradient(
73
+ 90deg,
74
+ transparent 0%,
75
+ color-mix(in srgb, var(--vocs-color-gray10) 22%, transparent) 50%,
76
+ transparent 100%
77
+ );
78
+ background-repeat: no-repeat;
79
+ background-size: 200% 100%;
80
+ animation: vocs-skeleton-shimmer 1.25s ease-in-out infinite;
81
+ }
82
+
83
+ @keyframes vocs-skeleton-shimmer {
84
+ from {
85
+ background-position: 180% 0;
86
+ }
87
+ to {
88
+ background-position: -80% 0;
89
+ }
90
+ }
91
+
92
+ @media (prefers-reduced-motion: reduce) {
93
+ [data-v-code-skeleton] [data-v-skeleton-bar] {
94
+ animation: none;
95
+ }
96
+ }
97
+
66
98
  .twoslash-popup-docs {
67
99
  @apply vocs:max-h-40 vocs:overflow-y-scroll;
68
100