@djangocfg/widget-code 0.1.18 → 0.1.20

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,402 +0,0 @@
1
- 'use client';
2
-
3
- import { Highlight, Language, themes } from 'prism-react-renderer';
4
- import React, { useMemo, useRef } from 'react';
5
-
6
- import { useAppT } from '@djangocfg/i18n';
7
- import { useResolvedTheme } from '@djangocfg/ui-core/hooks';
8
- import { CodePanelHeader } from '../panel-header';
9
-
10
- // Surface palette — driven by the ui-core `--code*` semantic tokens so the
11
- // code panel matches the rest of the theme system (neutral, preset-aware)
12
- // instead of a hard-coded GitHub blue. The tokens are already tuned per
13
- // theme (`light.css` / `dark.css`): a near-white gray in light, a near-black
14
- // gray in dark — so the surface stays a calm neutral in either mode and never
15
- // reads blue. Syntax colors still come from Prism's vsDark/github palettes.
16
- const CODE_SURFACE_BG = 'var(--code)';
17
- const CODE_INLINE_BG = 'var(--code-inline)';
18
-
19
- // Corner radius comes from the ui-core token scale, never a hard-coded
20
- // Tailwind step: `--radius-popover` is the container tier (the same one
21
- // nav popovers and menu surfaces use), so the panel re-rounds with the
22
- // active preset (`dense`, `windows`, …) instead of pinning 0.5rem forever.
23
- const CODE_SURFACE_RADIUS = 'var(--radius-popover)';
24
-
25
- // The panel is BORDERLESS: `--code` already separates it from the page in both
26
- // themes (96% on a 98% light page, 9% on a 14.5% dark one), so a border only
27
- // added a second boundary around a shape the fill had already drawn — most
28
- // visible when the panel nests inside another card, where the two frames
29
- // competed. Contrast does the separating; `--code-border` stays defined in the
30
- // theme for hosts that genuinely need an outline.
31
-
32
- // Load extra Prism grammars (``bash``, ``ruby``, ``java``, ``php``)
33
- // that aren't in ``prism-react-renderer``'s default bundle. The hook
34
- // below subscribes to its ready state and re-renders this component
35
- // once loading completes, so the first tab click gets highlighted
36
- // correctly even if it happened before the dynamic import resolved.
37
- import { useEnsurePrismLanguages } from './registerPrismLanguages';
38
-
39
- interface PrettyCodeProps {
40
- data: string | object;
41
- language: Language;
42
- className?: string;
43
- /**
44
- * Force a specific Prism palette + surface. **Defaults to the resolved
45
- * host theme** (light → light surface + GitHub palette, dark → dark
46
- * surface + vsDark palette), so the panel matches the rest of the UI and
47
- * its `--code*` tokens. Pass `mode` explicitly only to pin a palette
48
- * regardless of theme — e.g. printing a PDF on a light page.
49
- */
50
- mode?: 'dark' | 'light';
51
- inline?: boolean;
52
- customBg?: string; // Custom background class
53
- isCompact?: boolean; // Compact mode for smaller font sizes
54
- /** @deprecated No-op since the toolbar moved into a persistent
55
- * `CodePanelHeader` (no scroll-capturing overlay). Kept for API
56
- * back-compat; will be removed in a future major. */
57
- scrollIsolation?: boolean;
58
- /**
59
- * Line count at which the viewer starts to scroll instead of growing.
60
- * ``undefined`` (default) = always grows to fit content, no scroll.
61
- * Set e.g. ``50`` to cap short snippets inline and scroll long ones.
62
- */
63
- maxLines?: number;
64
- /**
65
- * When set, an expand (⤢) button appears in the header — but ONLY while
66
- * the content is actually height-capped (i.e. exceeds `maxLines`). Lets a
67
- * host open the full code full-screen. No-op when the code fits.
68
- */
69
- onExpand?: () => void;
70
- /**
71
- * Visual variant. ``"card"`` (default) ships full chrome (border,
72
- * background, hover toolbar, optional internal scroll). ``"plain"``
73
- * is chrome-less — use when embedding inside another scroll
74
- * container so the surface manages its own chrome and scroll. */
75
- variant?: 'card' | 'plain';
76
- }
77
-
78
- const PrettyCode = ({ data, language, className, mode, inline = false, customBg, isCompact = false, maxLines, onExpand, variant = 'card' }: PrettyCodeProps) => {
79
- const containerRef = useRef<HTMLDivElement>(null);
80
- const t = useAppT();
81
-
82
- // Default to the host theme so the surface (`--code*` tokens) and the Prism
83
- // palette move together; an explicit `mode` prop still pins a palette.
84
- const resolvedTheme = useResolvedTheme();
85
- const effectiveMode = mode ?? (resolvedTheme === 'light' ? 'light' : 'dark');
86
-
87
- // Subscribe to the extra-grammars ready state. When ``bash`` /
88
- // ``ruby`` / ``java`` / ``php`` finish loading, this hook triggers a
89
- // re-render so the code block picks up the new grammar.
90
- useEnsurePrismLanguages();
91
-
92
- const labels = useMemo(() => ({
93
- copyCode: t('tools.code.copyCode'),
94
- noContent: t('tools.code.noContent'),
95
- }), [t]);
96
-
97
- // Font size based on compact mode
98
- const fontSize = isCompact ? '0.75rem' : '0.875rem'; // 12px vs 14px
99
-
100
- const isDarkMode = effectiveMode !== 'light';
101
- const prismTheme = isDarkMode ? themes.vsDark : themes.github;
102
- // The surface now reads the `--code*` tokens in BOTH modes — they already
103
- // carry the right neutral per theme, so the panel matches the UI and never
104
- // reads blue. (Previously light mode fell back to `bg-card` and dark mode
105
- // hard-coded GitHub's #0d1117.)
106
- const surfaceStyle = { backgroundColor: CODE_SURFACE_BG, borderRadius: CODE_SURFACE_RADIUS };
107
- const inlineSurfaceStyle = { backgroundColor: CODE_INLINE_BG };
108
-
109
- // Convert form object to JSON string with proper formatting
110
- const contentJson = typeof data === 'string' ? data : JSON.stringify(data || {}, null, 2);
111
-
112
- // Enable scroll only when content exceeds maxLines. Otherwise the block
113
- // grows to fit — short snippets feel natural, long ones get a cap.
114
- const lineCount = contentJson ? contentJson.split('\n').length : 0;
115
- const lineHeightRatio = isCompact ? 1.4 : 1.5;
116
- const fontSizePx = isCompact ? 12 : 14;
117
- // Vertical padding of the <pre> (top + bottom, in px) — keep in sync
118
- // with the padding string below.
119
- const verticalPadPx = 16 + 12; // 1rem top + 0.75rem bottom (≈)
120
- const shouldScroll = maxLines !== undefined && lineCount > maxLines;
121
- const maxHeightPx = maxLines !== undefined
122
- ? maxLines * fontSizePx * lineHeightRatio + verticalPadPx
123
- : undefined;
124
-
125
- // Handle empty content
126
- if (!contentJson || contentJson.trim() === '') {
127
- const emptyBgClass = customBg || '';
128
- return (
129
- <div
130
- // Same borderless token-rounded surface as the populated panel — the
131
- // empty state used `rounded-sm`, so a panel visibly changed shape as
132
- // soon as content arrived.
133
- className={`relative h-full rounded-[var(--radius-popover)] ${emptyBgClass} ${className || ''}`}
134
- style={customBg ? undefined : surfaceStyle}
135
- >
136
- <div className="h-full overflow-auto p-4">
137
- <p
138
- className="text-sm italic text-muted-foreground"
139
- role="status"
140
- >
141
- {labels.noContent}
142
- </p>
143
- </div>
144
- </div>
145
- );
146
- }
147
-
148
- // Get display name for language badge
149
- const getLanguageDisplayName = (lang: string): string => {
150
- switch (lang.toLowerCase()) {
151
- case 'bash':
152
- case 'shell':
153
- return 'Bash';
154
- case 'python':
155
- case 'py':
156
- return 'Python';
157
- case 'javascript':
158
- case 'js':
159
- return 'JavaScript';
160
- case 'typescript':
161
- case 'ts':
162
- return 'TypeScript';
163
- case 'json':
164
- return 'JSON';
165
- case 'yaml':
166
- case 'yml':
167
- return 'YAML';
168
- case 'html':
169
- return 'HTML';
170
- case 'css':
171
- return 'CSS';
172
- case 'sql':
173
- return 'SQL';
174
- case 'xml':
175
- return 'XML';
176
- case 'markdown':
177
- case 'md':
178
- return 'Markdown';
179
- case 'plaintext':
180
- case 'text':
181
- return 'Text';
182
- case 'mermaid':
183
- return 'Mermaid';
184
- case 'ruby':
185
- case 'rb':
186
- return 'Ruby';
187
- case 'java':
188
- return 'Java';
189
- case 'php':
190
- return 'PHP';
191
- case 'go':
192
- case 'golang':
193
- return 'Go';
194
- default:
195
- return lang.charAt(0).toUpperCase() + lang.slice(1);
196
- }
197
- };
198
-
199
- // Normalize language for Prism - use only basic supported languages
200
- const normalizedLanguage = (() => {
201
- const lang = language.toLowerCase();
202
-
203
- // Try basic languages that are definitely supported
204
- switch (lang) {
205
- case 'javascript':
206
- case 'js':
207
- return 'javascript';
208
- case 'typescript':
209
- case 'ts':
210
- return 'typescript'; // Try TypeScript first
211
- case 'python':
212
- case 'py':
213
- return 'python';
214
- case 'json':
215
- return 'json';
216
- case 'css':
217
- return 'css';
218
- case 'html':
219
- return 'markup';
220
- case 'xml':
221
- return 'markup';
222
- case 'bash':
223
- case 'shell':
224
- case 'sh':
225
- return 'bash';
226
- case 'ruby':
227
- case 'rb':
228
- return 'ruby';
229
- case 'java':
230
- return 'java';
231
- case 'php':
232
- return 'php';
233
- case 'go':
234
- case 'golang':
235
- return 'go';
236
- case 'sql':
237
- return 'sql';
238
- case 'yaml':
239
- case 'yml':
240
- return 'yaml';
241
- case 'markdown':
242
- case 'md':
243
- return 'markdown';
244
- case 'mermaid':
245
- return 'text'; // Mermaid is handled separately in MarkdownMessage
246
- default:
247
- // For unknown languages, try to use the original name first
248
- // If it doesn't work, Prism will fallback to plain text
249
- return lang || 'text';
250
- }
251
- })();
252
-
253
- const displayLanguage = getLanguageDisplayName(language);
254
-
255
- // Plain variant — chrome-less render for embedding inside other
256
- // scroll containers. No border, no background, no hover toolbar, no
257
- // internal scroll. The caller's ScrollArea/panel owns the chrome
258
- // and scroll responsibilities.
259
- if (variant === 'plain') {
260
- return (
261
- <Highlight theme={prismTheme} code={contentJson} language={normalizedLanguage as Language}>
262
- {({ className: prismClassName, style, tokens, getLineProps, getTokenProps }) => {
263
- const { backgroundColor: _bg, ...restStyle } = style;
264
- return (
265
- <pre
266
- className={`${prismClassName} ${className || ''}`}
267
- style={{
268
- ...restStyle,
269
- background: 'transparent',
270
- margin: 0,
271
- padding: isCompact ? '0.75rem 1rem' : '1rem',
272
- fontSize,
273
- lineHeight: lineHeightRatio,
274
- fontFamily: 'monospace',
275
- // ``break-all`` (not ``break-word``) so long unbroken
276
- // strings without whitespace — typical of escaped
277
- // JSON bodies in generated code samples — wrap at any
278
- // character rather than overflowing the container.
279
- whiteSpace: 'pre-wrap',
280
- wordBreak: 'break-all',
281
- overflowWrap: 'anywhere',
282
- // Hard cap on width — parents (grid cells, panels)
283
- // should constrain us but some callers render inside
284
- // ``flex: 1 1 auto`` which lets the pre grow past
285
- // intended bounds. ``max-width: 100%`` pins to parent.
286
- maxWidth: '100%',
287
- }}
288
- >
289
- {tokens.map((line, i) => (
290
- <div key={i} {...getLineProps({ line })}>
291
- {line.map((token, key) => (
292
- <span key={key} {...getTokenProps({ token })} />
293
- ))}
294
- </div>
295
- ))}
296
- </pre>
297
- );
298
- }}
299
- </Highlight>
300
- );
301
- }
302
-
303
- if (inline) {
304
- // Inline chip surface also comes from `--code-inline` via inlineSurfaceStyle.
305
- const inlineBgClass = customBg || '';
306
- return (
307
- <Highlight theme={prismTheme} code={contentJson} language={normalizedLanguage as Language}>
308
- {({ className: prismClassName, style, tokens, getTokenProps }) => (
309
- <code
310
- // `--radius-sm`, not the panel's container radius: an inline chip
311
- // sits inside a line of prose, so it rounds like a small control
312
- // rather than like a card.
313
- className={`${prismClassName} ${inlineBgClass} px-2 py-1 rounded-[var(--radius-sm)] ${isCompact ? 'text-xs' : 'text-sm'} font-mono inline-block ${className || ''}`}
314
- style={{
315
- ...style,
316
- ...(customBg ? undefined : inlineSurfaceStyle),
317
- fontSize,
318
- fontFamily: 'monospace',
319
- }}
320
- >
321
- {tokens.map((line, i) =>
322
- line.map((token, key) => (
323
- <span key={`${i}-${key}`} {...getTokenProps({ token })} />
324
- )),
325
- )}
326
- </code>
327
- )}
328
- </Highlight>
329
- );
330
- }
331
-
332
- // Surface comes from the `--code*` tokens via `surfaceStyle` (both modes),
333
- // unless the host pins its own `customBg` class.
334
- const bgClass = customBg || '';
335
-
336
- return (
337
- <div
338
- ref={containerRef}
339
- // Radius lives in the CLASS, not in `surfaceStyle`: a host passing
340
- // `customBg` drops the inline style entirely (see below), and the panel
341
- // must stay rounded either way. Borderless by design — see the note on
342
- // CODE_SURFACE_RADIUS.
343
- className={`group relative overflow-hidden rounded-[var(--radius-popover)] ${bgClass} ${className || ''}`}
344
- style={customBg ? undefined : surfaceStyle}
345
- >
346
- {/* Persistent header bar (GitHub / ChatGPT style): language label
347
- left, copy button right, in flow — never floats over the code. */}
348
- <CodePanelHeader
349
- language={displayLanguage}
350
- copyValue={contentJson}
351
- copyLabel={labels.copyCode}
352
- dark={isDarkMode}
353
- // Expand only matters when the code is actually height-capped.
354
- onExpand={onExpand && shouldScroll ? onExpand : undefined}
355
- />
356
-
357
- <div
358
- className={shouldScroll ? 'h-full overflow-auto' : ''}
359
- style={shouldScroll ? { maxHeight: maxHeightPx } : undefined}
360
- // When the viewer scrolls internally, make it keyboard-reachable
361
- // so users without a wheel/trackpad can scroll with arrow keys.
362
- {...(shouldScroll
363
- ? { tabIndex: 0, role: 'region', 'aria-label': displayLanguage }
364
- : {})}
365
- >
366
- <Highlight theme={prismTheme} code={contentJson} language={normalizedLanguage as Language}>
367
- {({ className: prismClassName, style, tokens, getLineProps, getTokenProps }) => {
368
- // Remove background from Prism theme - we use our own via CSS
369
- const { backgroundColor: _bg, ...restStyle } = style;
370
- return (
371
- <pre
372
- className={prismClassName}
373
- style={{
374
- ...restStyle,
375
- margin: 0,
376
- // The header now occupies the top; the code body just needs
377
- // even padding (no extra top room for an overlay anymore).
378
- padding: '0.875rem 1rem',
379
- fontSize,
380
- lineHeight: lineHeightRatio,
381
- fontFamily: 'monospace',
382
- whiteSpace: 'pre-wrap',
383
- wordBreak: 'break-word',
384
- overflowWrap: 'break-word',
385
- }}
386
- >
387
- {tokens.map((line, i) => (
388
- <div key={i} {...getLineProps({ line })}>
389
- {line.map((token, key) => (
390
- <span key={key} {...getTokenProps({ token })} />
391
- ))}
392
- </div>
393
- ))}
394
- </pre>
395
- )}}
396
- </Highlight>
397
- </div>
398
- </div>
399
- );
400
- };
401
-
402
- export default PrettyCode;
@@ -1,81 +0,0 @@
1
- # PrettyCode
2
-
3
- Syntax-highlighted code block. Prism-powered, dark-by-default surface.
4
-
5
- ## Why dark by default
6
-
7
- PrettyCode renders on a fixed dark surface (`#0d1117` + `vsDark` palette)
8
- regardless of the host theme — the same convention as GitHub, VSCode,
9
- ChatGPT, Slack. Syntax highlighting ships its own contrast model
10
- (keyword / string / number / comment) that flattens to low-contrast
11
- pastels when forced onto a light UI surface.
12
-
13
- The surface is intentionally **not** a semantic token. Semantic tokens
14
- (`--primary`, `--accent`, `--card`) describe UI affordances; code
15
- display is data, not UI.
16
-
17
- ## Structure
18
-
19
- ```
20
- PrettyCode/
21
- ├── PrettyCode.client.tsx # Main component (Highlight + dark surface)
22
- ├── registerPrismLanguages.ts # Lazy-load extra grammars (bash, ruby, java, php)
23
- ├── index.tsx # Public re-export
24
- └── lazy.tsx # Lazy-loaded export
25
- ```
26
-
27
- ## Usage
28
-
29
- ```tsx
30
- import { PrettyCode } from '@djangocfg/widget-code/pretty-code';
31
-
32
- <PrettyCode data={sourceCode} language="typescript" />
33
-
34
- // Inline snippet (used in markdown rendering)
35
- <PrettyCode data={`const x = 1`} language="ts" inline />
36
-
37
- // Capped at 50 visible lines, scrolls if longer
38
- <PrettyCode data={longBlob} language="json" maxLines={50} />
39
-
40
- // Chrome-less variant — embed inside another scroll container
41
- <PrettyCode data={src} language="bash" variant="plain" />
42
- ```
43
-
44
- ## Props
45
-
46
- | Prop | Type | Default | Description |
47
- |------|------|---------|-------------|
48
- | `data` | `string \| object` | — | Code string. Objects are `JSON.stringify`d. |
49
- | `language` | `Language` | — | Prism language id. Unknown → plain text. |
50
- | `mode` | `'dark' \| 'light'` | `'dark'` | Palette override. Default is always dark (see "Why dark"). |
51
- | `inline` | `boolean` | `false` | Render as inline `<code>` (no card / toolbar). |
52
- | `variant` | `'card' \| 'plain'` | `'card'` | `plain` strips border, background, and toolbar. |
53
- | `isCompact` | `boolean` | `false` | 12px font instead of 14px. |
54
- | `maxLines` | `number` | `undefined` | When set, block scrolls past this many lines. |
55
- | `customBg` | `string` | — | Tailwind class to override the dark surface. |
56
- | `scrollIsolation` | `boolean` | `true` | Block scroll capture until clicked (long snippets). |
57
- | `className` | `string` | — | Extra classes on the wrapper. |
58
-
59
- ## When to override `mode`
60
-
61
- Almost never. The dark default is correct for ~all cases. Pass
62
- `mode="light"` only for deliberate light-on-light renders (e.g. PDF
63
- export targeting a printed page). The surface falls back to
64
- `bg-card` semantic token in that mode.
65
-
66
- ## Supported languages
67
-
68
- Out of the box: `javascript`, `typescript`, `python`, `json`, `css`,
69
- `markup` (html/xml), `sql`, `yaml`, `markdown`, `go`.
70
-
71
- Lazy-loaded on first use: `bash`/`shell`, `ruby`, `java`, `php`.
72
- Lazy load is tracked through `useEnsurePrismLanguages()` — the
73
- component re-renders once the grammar resolves.
74
-
75
- Unknown languages fall back to plain text (no highlighting, no error).
76
-
77
- ## Storybook
78
-
79
- `UI Tools / Code / PrettyCode` — `Playground`, `TypeScript`, `Json`,
80
- `Python`, `Bash`, `Sql`, `Yaml`, `LongFileWithScroll`, `Inline`,
81
- `Compact`, `LightMode`, `PlainVariant`.
@@ -1,72 +0,0 @@
1
- /**
2
- * PrettyCode Component - Dynamic Import Wrapper
3
- *
4
- * Lazy loads the heavy Prism library (~500KB) only when needed.
5
- * This significantly reduces the initial bundle size.
6
- */
7
-
8
- 'use client';
9
-
10
- import React, { lazy, Suspense } from 'react';
11
- import type { Language } from 'prism-react-renderer';
12
-
13
- import { LoadingFallback } from '@djangocfg/widget-kit/lazy';
14
-
15
- // Lazy load the client component
16
- const PrettyCodeClient = lazy(() => import('./PrettyCode.client'));
17
-
18
- // Shared compact placeholder (16px muted spinner), inside the code-surface
19
- // border so nothing shifts when PrettyCode mounts. Same look as LazyPrettyCode
20
- // (lazy.tsx) and the JSON viewer.
21
- const CodeFallback = () => (
22
- <LoadingFallback
23
- minHeight={64}
24
- text="Loading code…"
25
- className="rounded-sm border border-border bg-muted dark:bg-code"
26
- />
27
- );
28
-
29
- export interface PrettyCodeProps {
30
- data: string | object;
31
- language: Language;
32
- className?: string;
33
- mode?: 'dark' | 'light';
34
- inline?: boolean;
35
- customBg?: string;
36
- isCompact?: boolean;
37
- /** Block scroll capture until user clicks (default: true) */
38
- scrollIsolation?: boolean;
39
- /**
40
- * Line count above which the block starts scrolling instead of growing.
41
- * ``undefined`` (default) = no cap, block grows to fit. Set e.g. ``50``
42
- * to inline short snippets and cap long ones.
43
- */
44
- maxLines?: number;
45
- /** When set, a header expand (⤢) button appears while the code is
46
- * height-capped (exceeds `maxLines`). Lets a host open it full-screen. */
47
- onExpand?: () => void;
48
- /**
49
- * Visual variant:
50
- * - ``"card"`` (default) — full chrome: border, background, hover
51
- * toolbar (Copy + language tag), optional internal scroll. Suits
52
- * a standalone code block in documentation prose where the block
53
- * is its own surface.
54
- * - ``"plain"`` — chrome-less: no border, no background, no toolbar,
55
- * no internal scroll. Grows to fit its content. Use when
56
- * embedding inside another scroll container (e.g. the Response
57
- * panel) — avoids double-scroll surfaces and lets the parent
58
- * manage copy/language affordances.
59
- */
60
- variant?: 'card' | 'plain';
61
- }
62
-
63
- const PrettyCode: React.FC<PrettyCodeProps> = (props) => {
64
- return (
65
- <Suspense fallback={<CodeFallback />}>
66
- <PrettyCodeClient {...props} />
67
- </Suspense>
68
- );
69
- };
70
-
71
- export default PrettyCode;
72
- export type { Language };
@@ -1,69 +0,0 @@
1
- 'use client';
2
-
3
- /**
4
- * Lazy-loaded PrettyCode Component
5
- *
6
- * Heavy Prism library (~500KB) is loaded only when component is rendered.
7
- * Use this for automatic code-splitting with Suspense fallback.
8
- *
9
- * For direct imports without lazy loading, use:
10
- * import PrettyCode from '@djangocfg/widget-code/pretty-code'
11
- */
12
-
13
- import { createLazyComponent, LoadingFallback } from '@djangocfg/widget-kit/lazy';
14
- import type { Language } from 'prism-react-renderer';
15
-
16
- // ============================================================================
17
- // Types
18
- // ============================================================================
19
-
20
- export interface PrettyCodeProps {
21
- data: string | object;
22
- language: Language;
23
- className?: string;
24
- mode?: 'dark' | 'light';
25
- inline?: boolean;
26
- customBg?: string;
27
- isCompact?: boolean;
28
- scrollIsolation?: boolean;
29
- maxLines?: number;
30
- /** Header expand (⤢) callback, shown while the code is height-capped. */
31
- onExpand?: () => void;
32
- /** ``'card'`` (default) ships the full chrome. ``'plain'`` is
33
- * chrome-less — for embedding inside another scroll container. */
34
- variant?: 'card' | 'plain';
35
- }
36
-
37
- export type { Language };
38
-
39
- // ============================================================================
40
- // Lazy Component
41
- // ============================================================================
42
-
43
- /**
44
- * LazyPrettyCode - Lazy-loaded code syntax highlighter
45
- *
46
- * Automatically shows loading state while Prism loads (~500KB). Uses the shared
47
- * compact `LoadingFallback` (16px muted spinner) so the code viewer and the JSON
48
- * viewer show the SAME quiet placeholder while their chunks load — kept inside a
49
- * subtle code-surface border so nothing shifts when PrettyCode mounts.
50
- */
51
- export const LazyPrettyCode = createLazyComponent<PrettyCodeProps>(
52
- () => import('./PrettyCode.client'),
53
- {
54
- displayName: 'LazyPrettyCode',
55
- fallback: (
56
- <LoadingFallback
57
- minHeight={64}
58
- text="Loading code…"
59
- className="rounded-sm border border-border bg-muted dark:bg-code"
60
- />
61
- ),
62
- }
63
- );
64
-
65
- // `PrettyCode` is the historical named export — same component as
66
- // `LazyPrettyCode`, kept for backwards compatibility with callers that
67
- // imported it from the old root barrel.
68
- export { LazyPrettyCode as PrettyCode };
69
- export { default } from './index';