@djangocfg/ui-tools 2.1.402 → 2.1.407
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.
- package/README.md +16 -1
- package/package.json +11 -9
- package/src/tools/AudioPlayer/lazy.tsx +13 -27
- package/src/tools/ImageViewer/components/ImageViewer.tsx +10 -2
- package/src/tools/JsonForm/JsonSchemaForm.tsx +3 -1
- package/src/tools/JsonForm/README.md +12 -2
- package/src/tools/JsonForm/widgets/TextareaWidget.tsx +25 -0
- package/src/tools/JsonForm/widgets/index.ts +1 -0
- package/src/tools/JsonTree/README.md +12 -0
- package/src/tools/PrettyCode/README.md +81 -0
- package/src/tools/VideoPlayer/README.md +87 -230
- package/src/tools/VideoPlayer/VideoPlayer.tsx +82 -0
- package/src/tools/VideoPlayer/canvas/canvas-dispatcher.tsx +34 -0
- package/src/tools/VideoPlayer/canvas/hls-canvas.tsx +38 -0
- package/src/tools/VideoPlayer/canvas/iframe-canvas.tsx +33 -0
- package/src/tools/VideoPlayer/canvas/index.ts +12 -0
- package/src/tools/VideoPlayer/canvas/jsx.d.ts +54 -0
- package/src/tools/VideoPlayer/canvas/native-canvas.tsx +38 -0
- package/src/tools/VideoPlayer/canvas/vimeo-canvas.tsx +39 -0
- package/src/tools/VideoPlayer/canvas/youtube-canvas.tsx +77 -0
- package/src/tools/VideoPlayer/index.ts +51 -65
- package/src/tools/VideoPlayer/lazy.tsx +11 -54
- package/src/tools/VideoPlayer/parts/controls-bar.tsx +35 -0
- package/src/tools/VideoPlayer/parts/fullscreen.tsx +19 -0
- package/src/tools/VideoPlayer/parts/index.ts +15 -0
- package/src/tools/VideoPlayer/parts/pip.tsx +19 -0
- package/src/tools/VideoPlayer/parts/play-button.tsx +19 -0
- package/src/tools/VideoPlayer/parts/playback-rate.tsx +31 -0
- package/src/tools/VideoPlayer/parts/poster.tsx +3 -0
- package/src/tools/VideoPlayer/parts/seek-bar.tsx +26 -0
- package/src/tools/VideoPlayer/parts/volume.tsx +32 -0
- package/src/tools/VideoPlayer/styles/video-player.css +141 -0
- package/src/tools/VideoPlayer/types.ts +82 -0
- package/src/tools/VideoPlayer/utils/parse-embed-url.ts +70 -0
- package/src/tools/VideoPlayer/utils/vimeo-id.ts +24 -0
- package/src/tools/VideoPlayer/utils/youtube-id.ts +64 -0
- package/src/tools/index.ts +35 -28
- package/src/tools/VideoPlayer/components/VideoControls.tsx +0 -138
- package/src/tools/VideoPlayer/components/VideoErrorFallback.tsx +0 -172
- package/src/tools/VideoPlayer/components/VideoPlayer.tsx +0 -201
- package/src/tools/VideoPlayer/components/index.ts +0 -14
- package/src/tools/VideoPlayer/context/VideoPlayerContext.tsx +0 -52
- package/src/tools/VideoPlayer/context/index.ts +0 -8
- package/src/tools/VideoPlayer/hooks/index.ts +0 -12
- package/src/tools/VideoPlayer/hooks/useVideoPlayerSettings.ts +0 -71
- package/src/tools/VideoPlayer/hooks/useVideoPositionCache.ts +0 -117
- package/src/tools/VideoPlayer/providers/NativeProvider.tsx +0 -284
- package/src/tools/VideoPlayer/providers/StreamProvider.tsx +0 -505
- package/src/tools/VideoPlayer/providers/VidstackProvider.tsx +0 -397
- package/src/tools/VideoPlayer/providers/index.ts +0 -8
- package/src/tools/VideoPlayer/types/index.ts +0 -38
- package/src/tools/VideoPlayer/types/player.ts +0 -116
- package/src/tools/VideoPlayer/types/provider.ts +0 -93
- package/src/tools/VideoPlayer/types/sources.ts +0 -97
- package/src/tools/VideoPlayer/utils/debug.ts +0 -14
- package/src/tools/VideoPlayer/utils/fileSource.ts +0 -78
- package/src/tools/VideoPlayer/utils/index.ts +0 -12
- package/src/tools/VideoPlayer/utils/resolvers.ts +0 -75
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ Sixteen tools, each one lazy-loaded so it doesn't ship until used. Bundle size i
|
|
|
32
32
|
| `LottiePlayer` | ~200KB | Lottie animation player |
|
|
33
33
|
| `Chat` | ~150KB | Streaming chat (SSE + tool calls + attachments). [README](src/tools/Chat/README.md) |
|
|
34
34
|
| `SpeechRecognition` | ~40KB | Mic capture + STT with pluggable engines (Web Speech / HTTP / WS). [README](src/tools/SpeechRecognition/README.md) |
|
|
35
|
-
| `VideoPlayer` | ~
|
|
35
|
+
| `VideoPlayer` | ~12KB core | media-chrome player — YouTube / Vimeo / HLS / MP4 in one composable shell. [README](src/tools/VideoPlayer/README.md) |
|
|
36
36
|
| `MarkdownMessage` | ~120KB | Read-only chat-tuned markdown. **SSR-safe** — use as a Client Component, the result is server-rendered. [README](src/components/markdown/MarkdownMessage/README.md) |
|
|
37
37
|
| `JsonTree` | ~100KB | JSON visualization (full/compact/inline modes) |
|
|
38
38
|
| `AudioPlayer` | ~80KB | WebView-safe waveform player |
|
|
@@ -78,10 +78,23 @@ Subpaths come in three flavors:
|
|
|
78
78
|
| `@djangocfg/ui-tools/markdown-editor` | `LazyMarkdownEditor`, `mentionPresets`, types | TipTap + ProseMirror (~200 KB) only loads via the lazy wrapper. |
|
|
79
79
|
| `@djangocfg/ui-tools/map` | `LazyMapContainer`, `LazyMapView`, plus light primitives (`MapMarker`, `MapPopup`, `MapCluster`, `MapSource`, `MapLayer`, `MapControls`, `MapProvider`, types) | The heavy MapLibre GL chunk (~800 KB) only loads when `LazyMapContainer` actually mounts. Markers and popups are thin `react-map-gl` wrappers — exported synchronously. |
|
|
80
80
|
| `@djangocfg/ui-tools/markdown-message` | `MarkdownMessage`, `ChatMessageRow`, `ActionRow`, `extractTextFromChildren`, types | **SSR-safe.** The component itself is `'use client'`, but rendering produces plain HTML — Next.js will pre-render it on the server when imported from a Client Component. Use this when you want the markdown renderer without dragging in the full chat. |
|
|
81
|
+
| `@djangocfg/ui-tools/video-player` | `VideoPlayer` (+ `LazyVideoPlayer` alias), `parseEmbedUrl`, composable parts (`PlayButton`, `SeekBar`, `Volume`, `ControlsBar`, …), per-engine canvases, types | media-chrome shell — YouTube / Vimeo / HLS / MP4 / iframe behind one API. Provider engines (`youtube-video-element`, `hls-video-element`, …) are imported only by the matching canvas, so unused engines tree-shake. [README](src/tools/VideoPlayer/README.md) |
|
|
81
82
|
| `@djangocfg/ui-tools/<tool-name>` | One tool | `mermaid`, `speech-recognition`, `json-tree`, `pretty-code`, `openapi-viewer`, `json-form`, `lottie-player`, `video-player`, `image-viewer`, `cron-scheduler`, `gallery`, `tour`, `tree`, `file-icon`, `upload` |
|
|
83
|
+
| `@djangocfg/ui-tools/json-form/full` | `JsonSchemaForm`, `ObjectFieldTemplate`, `evaluateDisabledWhen`, all widgets / templates / utils | Eager bundle. Use only for storybook / internal tooling that needs the template + util APIs at module scope. Production code should import `LazyJsonSchemaForm` from `/json-form` instead. |
|
|
82
84
|
| `@djangocfg/ui-tools/styles` | Tailwind source CSS | |
|
|
83
85
|
| `@djangocfg/ui-tools/dist.css` | Pre-compiled CSS | |
|
|
84
86
|
|
|
87
|
+
### Code & data inspectors are always-dark by design
|
|
88
|
+
|
|
89
|
+
`PrettyCode` and `JsonTree` render on a fixed dark surface (`#0d1117`)
|
|
90
|
+
regardless of the host UI theme. Same convention as GitHub, VSCode,
|
|
91
|
+
ChatGPT, Chrome DevTools. Syntax highlighting / typed-value coloring
|
|
92
|
+
ship their own contrast model — mixing them with light UI surfaces
|
|
93
|
+
flattens everything into low-contrast pastels.
|
|
94
|
+
|
|
95
|
+
The chrome (border, padding, toolbars) still uses semantic UI tokens.
|
|
96
|
+
Only the *content surface* is fixed. See per-tool READMEs for details.
|
|
97
|
+
|
|
85
98
|
---
|
|
86
99
|
|
|
87
100
|
## Lazy loading
|
|
@@ -146,6 +159,8 @@ function Chat() {
|
|
|
146
159
|
|
|
147
160
|
**What's wired by default:** desktop side-mode toggle (auto-hides on narrow screens), persisted dock prefs, two-step Escape, click-to-focus composer, mobile fullscreen with `dvh` heights, push-preview bubble for inbound messages while closed, **ChatGPT-style autoscroll** (sticky-to-bottom within 120 px, every user-send re-anchors the viewport), **bundled chat notification sounds** (sent/received/start/error/mention/notification, ~136KB inlined as `data:`-URLs inside the lazy chat chunk — zero host setup). Native hosts (cmdop_go / Tauri) pass `audio={{ silenced: true, onSoundEvent }}` to keep web silent while routing triggers to the backend.
|
|
148
161
|
|
|
162
|
+
Need a fully custom input row (e.g. mention autocomplete via `MarkdownEditor`)? Pass `renderComposer={({ composer }) => <YourComposer composer={composer} />}` to `<ChatRoot>` — it replaces the default `<Composer>` while keeping autoscroll, JumpToLatest, and history behaviour. Story: `UI Tools / Chat / Mentions / Machine Mentions`.
|
|
163
|
+
|
|
149
164
|
Drop `<VoiceComposerSlot />` from `@djangocfg/ui-tools/speech-recognition` into `composerToolbarEnd` for live mic-to-text — **zero props**, reads / writes the composer through `ComposerHandle` registered in chat context. Set `headerSlots={{ languagePicker: true }}` on `<ChatLauncher>` for a flag-button language picker (66 BCP-47 tags). Both auto-hide on Firefox / in-app browsers / missing `getUserMedia`. See [`SpeechRecognition`](#speech-recognition--quick-start) below.
|
|
150
165
|
|
|
151
166
|
Full docs: [`Chat/README.md`](src/tools/Chat/README.md). Stories: `Tools/Chat/{Basic,Bubbles,ToolCalls,Personas,Launcher,Header,Audio & Actions,Voice composer}`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/ui-tools",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.407",
|
|
4
4
|
"description": "Heavy React tools with lazy loading - for Electron, Vite, CRA, Next.js apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ui-tools",
|
|
@@ -159,8 +159,8 @@
|
|
|
159
159
|
"test:watch": "vitest"
|
|
160
160
|
},
|
|
161
161
|
"peerDependencies": {
|
|
162
|
-
"@djangocfg/i18n": "^2.1.
|
|
163
|
-
"@djangocfg/ui-core": "^2.1.
|
|
162
|
+
"@djangocfg/i18n": "^2.1.407",
|
|
163
|
+
"@djangocfg/ui-core": "^2.1.407",
|
|
164
164
|
"consola": "^3.4.2",
|
|
165
165
|
"lodash-es": "^4.18.1",
|
|
166
166
|
"lucide-react": "^0.545.0",
|
|
@@ -183,9 +183,10 @@
|
|
|
183
183
|
"@tiptap/react": "^3.20.1",
|
|
184
184
|
"@tiptap/starter-kit": "^3.20.1",
|
|
185
185
|
"@tiptap/suggestion": "^3.20.1",
|
|
186
|
-
"@vidstack/react": "next",
|
|
187
186
|
"@wavesurfer/react": "^1.0.12",
|
|
187
|
+
"hls-video-element": "^1.5.11",
|
|
188
188
|
"maplibre-gl": "^4.7.1",
|
|
189
|
+
"media-chrome": "^4.19.0",
|
|
189
190
|
"media-icons": "next",
|
|
190
191
|
"mermaid": "^11.12.0",
|
|
191
192
|
"monaco-editor": "^0.55.1",
|
|
@@ -205,8 +206,9 @@
|
|
|
205
206
|
"remark-emoji": "^5.0.2",
|
|
206
207
|
"remark-gfm": "4.0.1",
|
|
207
208
|
"remark-smartypants": "^3.0.2",
|
|
208
|
-
"
|
|
209
|
-
"wavesurfer.js": "^7.12.1"
|
|
209
|
+
"vimeo-video-element": "^1.7.2",
|
|
210
|
+
"wavesurfer.js": "^7.12.1",
|
|
211
|
+
"youtube-video-element": "^1.9.0"
|
|
210
212
|
},
|
|
211
213
|
"optionalDependencies": {
|
|
212
214
|
"@mapbox/mapbox-gl-draw": "^1.4.3",
|
|
@@ -214,9 +216,9 @@
|
|
|
214
216
|
"material-file-icons": "^2.4.0"
|
|
215
217
|
},
|
|
216
218
|
"devDependencies": {
|
|
217
|
-
"@djangocfg/i18n": "^2.1.
|
|
218
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
219
|
-
"@djangocfg/ui-core": "^2.1.
|
|
219
|
+
"@djangocfg/i18n": "^2.1.407",
|
|
220
|
+
"@djangocfg/typescript-config": "^2.1.407",
|
|
221
|
+
"@djangocfg/ui-core": "^2.1.407",
|
|
220
222
|
"@types/lodash-es": "^4.17.12",
|
|
221
223
|
"@types/mapbox__mapbox-gl-draw": "^1.4.8",
|
|
222
224
|
"@types/node": "^24.7.2",
|
|
@@ -3,39 +3,25 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* `@djangocfg/ui-tools/audio-player` subpath entrypoint.
|
|
5
5
|
*
|
|
6
|
-
* `LazyPlayer` is
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* exists inside `LazyPlayer` once loaded).
|
|
6
|
+
* `LazyPlayer` is exported as a direct (synchronous) alias of `Player`. We
|
|
7
|
+
* intentionally avoid `React.lazy` + `import('./Player')` here: under bundlers
|
|
8
|
+
* that pre-bundle subpath entries (Vite optimizeDeps in Next.js/Vite/SB), the
|
|
9
|
+
* dynamic import creates a second chunk that re-instantiates the React
|
|
10
|
+
* Contexts (AudioRefCtx/ControlsCtx/MetaCtx/StateCtx/LevelsCtx). The slot
|
|
11
|
+
* components and selector hooks re-exported below would then read from a
|
|
12
|
+
* different context instance than `<PlayerProvider>` writes to, which made
|
|
13
|
+
* `usePlayerAudio` throw "must be used inside <PlayerProvider>".
|
|
15
14
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
15
|
+
* Heavy audio-decoding work (peaks, AudioContext) already happens lazily at
|
|
16
|
+
* runtime via effects inside `PlayerProvider`/`PlayerShell` — there is no
|
|
17
|
+
* benefit to splitting the React shell behind a second chunk.
|
|
19
18
|
*/
|
|
20
19
|
|
|
21
|
-
import { createLazyComponent } from '../../components';
|
|
22
|
-
import type { PlayerProps } from './types';
|
|
23
|
-
|
|
24
20
|
// ============================================================================
|
|
25
|
-
//
|
|
21
|
+
// Player component (synchronous; previously lazy — see note above)
|
|
26
22
|
// ============================================================================
|
|
27
23
|
|
|
28
|
-
export
|
|
29
|
-
() => import('./Player').then((mod) => ({ default: mod.Player })),
|
|
30
|
-
{
|
|
31
|
-
displayName: 'LazyAudioPlayer',
|
|
32
|
-
fallback: (
|
|
33
|
-
<div className="rounded-lg border border-border/60 bg-card px-4 py-6 text-sm text-muted-foreground">
|
|
34
|
-
Loading audio player…
|
|
35
|
-
</div>
|
|
36
|
-
),
|
|
37
|
-
},
|
|
38
|
-
);
|
|
24
|
+
export { Player, Player as LazyPlayer } from './Player';
|
|
39
25
|
|
|
40
26
|
// ============================================================================
|
|
41
27
|
// Light surface — types, store, context, slot components, hooks
|
|
@@ -196,13 +196,21 @@ export function ImageViewer({
|
|
|
196
196
|
wrapperClass="!w-full !h-full cursor-grab active:cursor-grabbing"
|
|
197
197
|
contentClass="!w-full !h-full flex items-center justify-center"
|
|
198
198
|
>
|
|
199
|
-
|
|
199
|
+
{/*
|
|
200
|
+
Fill the TransformComponent content box so the children's
|
|
201
|
+
`max-w-full / max-h-full` resolve against the actual viewport
|
|
202
|
+
instead of the image's natural box. Without `w-full h-full`
|
|
203
|
+
this wrapper shrink-fits the image, which collapses the
|
|
204
|
+
max-* constraints and renders the image at intrinsic size —
|
|
205
|
+
visible as cropping / half-height in tall containers.
|
|
206
|
+
*/}
|
|
207
|
+
<div className="relative w-full h-full flex items-center justify-center">
|
|
200
208
|
{useProgressiveLoading && lqip && !isFullyLoaded && (
|
|
201
209
|
<img
|
|
202
210
|
src={lqip}
|
|
203
211
|
alt=""
|
|
204
212
|
aria-hidden="true"
|
|
205
|
-
className="absolute
|
|
213
|
+
className="absolute max-w-full max-h-full object-contain select-none"
|
|
206
214
|
style={{ transform: transformStyle, filter: 'blur(20px)', transition: 'opacity 0.3s ease-out', opacity: isFullyLoaded ? 0 : 1 }}
|
|
207
215
|
draggable={false}
|
|
208
216
|
/>
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
import { JsonFormContext, JsonSchemaFormProps } from './types';
|
|
18
18
|
import { normalizeFormData, validateSchema } from './utils';
|
|
19
19
|
import {
|
|
20
|
-
CheckboxWidget, ColorWidget, NumberWidget, SelectWidget, SliderWidget, SwitchWidget, TextWidget
|
|
20
|
+
CheckboxWidget, ColorWidget, NumberWidget, SelectWidget, SliderWidget, SwitchWidget, TextareaWidget, TextWidget
|
|
21
21
|
} from './widgets';
|
|
22
22
|
|
|
23
23
|
/**
|
|
@@ -104,6 +104,7 @@ export function JsonSchemaForm<T = any>(props: JsonSchemaFormProps<T>) {
|
|
|
104
104
|
const widgets: RegistryWidgetsType = useMemo(() => ({
|
|
105
105
|
// Standard widget names (PascalCase) - used by RJSF internally
|
|
106
106
|
TextWidget,
|
|
107
|
+
TextareaWidget,
|
|
107
108
|
NumberWidget,
|
|
108
109
|
CheckboxWidget,
|
|
109
110
|
SelectWidget,
|
|
@@ -112,6 +113,7 @@ export function JsonSchemaForm<T = any>(props: JsonSchemaFormProps<T>) {
|
|
|
112
113
|
SliderWidget,
|
|
113
114
|
// Lowercase aliases - for uiSchema 'ui:widget' references
|
|
114
115
|
text: TextWidget,
|
|
116
|
+
textarea: TextareaWidget,
|
|
115
117
|
number: NumberWidget,
|
|
116
118
|
checkbox: CheckboxWidget,
|
|
117
119
|
select: SelectWidget,
|
|
@@ -3,10 +3,20 @@
|
|
|
3
3
|
Schema-driven forms on top of [react-jsonschema-form](https://github.com/rjsf-team/react-jsonschema-form) (RJSF) with `@djangocfg/ui` widgets, AJV8 validation, and a few custom extensions for compact playground-style UIs.
|
|
4
4
|
|
|
5
5
|
```tsx
|
|
6
|
-
|
|
7
|
-
//
|
|
6
|
+
// Lazy entry — what production code should import. Ships only the
|
|
7
|
+
// component reference + a Suspense fallback; the ~300KB RJSF bundle
|
|
8
|
+
// loads on first mount.
|
|
8
9
|
import { LazyJsonSchemaForm } from '@djangocfg/ui-tools/json-form';
|
|
9
10
|
|
|
11
|
+
// Full entry — eager bundle. Use only for storybook / internal
|
|
12
|
+
// tooling that needs templates, widgets, or `evaluateDisabledWhen`
|
|
13
|
+
// at module scope. Pulls RJSF synchronously, no code-splitting.
|
|
14
|
+
import {
|
|
15
|
+
JsonSchemaForm,
|
|
16
|
+
ObjectFieldTemplate,
|
|
17
|
+
evaluateDisabledWhen,
|
|
18
|
+
} from '@djangocfg/ui-tools/json-form/full';
|
|
19
|
+
|
|
10
20
|
const schema = {
|
|
11
21
|
type: 'object',
|
|
12
22
|
properties: {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
|
|
5
|
+
import type { WidgetProps } from '@rjsf/utils';
|
|
6
|
+
|
|
7
|
+
import { TextWidget } from './TextWidget';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Multiline text widget for JSON Schema Form. Thin wrapper around
|
|
11
|
+
* `TextWidget` that pins `options.widget = 'textarea'` so the
|
|
12
|
+
* underlying renderer reaches the `<textarea>` branch.
|
|
13
|
+
*
|
|
14
|
+
* Registered under the `textarea` ui:widget alias — schemas can opt
|
|
15
|
+
* into multiline editing without setting `ui:options.widget`:
|
|
16
|
+
*
|
|
17
|
+
* uiSchema = { description: { 'ui:widget': 'textarea' } }
|
|
18
|
+
*
|
|
19
|
+
* Honors all the same `ui:options` as `TextWidget` — most usefully
|
|
20
|
+
* `rows` (default `3`).
|
|
21
|
+
*/
|
|
22
|
+
export function TextareaWidget(props: WidgetProps) {
|
|
23
|
+
const options = { ...(props.options ?? {}), widget: 'textarea' };
|
|
24
|
+
return <TextWidget {...props} options={options} />;
|
|
25
|
+
}
|
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
Interactive JSON tree viewer with expand/collapse, copy, and download.
|
|
4
4
|
|
|
5
|
+
## Why dark by default
|
|
6
|
+
|
|
7
|
+
JsonTree renders on a fixed dark surface (`#0d1117`) regardless of
|
|
8
|
+
the host UI theme — same convention as Chrome DevTools, Insomnia,
|
|
9
|
+
Bruno, Postman. JSON inspectors carry their own data-typed palette
|
|
10
|
+
(blue keys, green strings, orange numbers, red null) tuned for dark
|
|
11
|
+
backgrounds; mixing it with light UI tokens flattens values into
|
|
12
|
+
unreadable pastels.
|
|
13
|
+
|
|
14
|
+
The palette is intentionally **not** a semantic token. See PrettyCode
|
|
15
|
+
README for the same rationale.
|
|
16
|
+
|
|
5
17
|
## Structure
|
|
6
18
|
|
|
7
19
|
```
|
|
@@ -0,0 +1,81 @@
|
|
|
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/ui-tools/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`.
|