@djangocfg/ui-tools 2.1.384 → 2.1.387
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 +25 -11
- package/dist/ChatRoot-4KM2JMGA.mjs +6 -0
- package/dist/{ChatRoot-JVR3M3H2.mjs.map → ChatRoot-4KM2JMGA.mjs.map} +1 -1
- package/dist/ChatRoot-OILWMMZ6.cjs +15 -0
- package/dist/{ChatRoot-LXIUBOXF.cjs.map → ChatRoot-OILWMMZ6.cjs.map} +1 -1
- package/dist/DictationField-AS2F33WI.cjs +13 -0
- package/dist/{DictationField-U25MEYAL.mjs.map → DictationField-AS2F33WI.cjs.map} +1 -1
- package/dist/DictationField-WPONUCYE.mjs +4 -0
- package/dist/{DictationField-XWR5VOID.cjs.map → DictationField-WPONUCYE.mjs.map} +1 -1
- package/dist/MapContainer-AKIPABJK.mjs +4 -0
- package/dist/MapContainer-AKIPABJK.mjs.map +1 -0
- package/dist/MapContainer-STVDMC36.cjs +17 -0
- package/dist/MapContainer-STVDMC36.cjs.map +1 -0
- package/dist/{MapContainer-76YL2JXL.cjs → chunk-5D2OCOPQ.cjs} +3 -2
- package/dist/chunk-5D2OCOPQ.cjs.map +1 -0
- package/dist/{MapContainer-7HXBI3OH.mjs → chunk-7CWGZPO3.mjs} +3 -3
- package/dist/chunk-7CWGZPO3.mjs.map +1 -0
- package/dist/{chunk-4PFW7MIJ.cjs → chunk-ADEN3UA4.cjs} +60 -5
- package/dist/chunk-ADEN3UA4.cjs.map +1 -0
- package/dist/chunk-BVESQTBM.mjs +1439 -0
- package/dist/chunk-BVESQTBM.mjs.map +1 -0
- package/dist/{chunk-PEKBT75W.mjs → chunk-DMX7W4XZ.mjs} +53 -1387
- package/dist/chunk-DMX7W4XZ.mjs.map +1 -0
- package/dist/chunk-HNIMIIFR.mjs +1361 -0
- package/dist/chunk-HNIMIIFR.mjs.map +1 -0
- package/dist/chunk-L25HA3TM.cjs +1478 -0
- package/dist/chunk-L25HA3TM.cjs.map +1 -0
- package/dist/{chunk-HPK3EWBF.cjs → chunk-TBSHZO5R.cjs} +50 -1409
- package/dist/chunk-TBSHZO5R.cjs.map +1 -0
- package/dist/chunk-TSNRU3UO.cjs +1387 -0
- package/dist/chunk-TSNRU3UO.cjs.map +1 -0
- package/dist/{chunk-C2YN6WEO.mjs → chunk-UNCS5V5F.mjs} +61 -7
- package/dist/chunk-UNCS5V5F.mjs.map +1 -0
- package/dist/index.cjs +1236 -1768
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +780 -780
- package/dist/index.d.ts +780 -780
- package/dist/index.mjs +853 -1423
- package/dist/index.mjs.map +1 -1
- package/dist/launcher-5WYPDPEP.mjs +7 -0
- package/dist/launcher-5WYPDPEP.mjs.map +1 -0
- package/dist/launcher-FCI3LTDY.css +7 -0
- package/dist/launcher-FCI3LTDY.css.map +1 -0
- package/dist/launcher-QAOG2NUI.cjs +60 -0
- package/dist/launcher-QAOG2NUI.cjs.map +1 -0
- package/package.json +23 -18
- package/src/tools/AudioPlayer/lazy.tsx +100 -0
- package/src/tools/Chat/README.md +85 -1
- package/src/tools/Chat/context/ChatProvider.tsx +42 -0
- package/src/tools/Chat/lazy.tsx +213 -1
- package/src/tools/CodeEditor/lazy.tsx +70 -0
- package/src/tools/Map/lazy.tsx +38 -1
- package/src/tools/MarkdownEditor/lazy.tsx +42 -0
- package/src/tools/SpeechRecognition/README.md +48 -0
- package/src/tools/SpeechRecognition/core/index.ts +6 -1
- package/src/tools/SpeechRecognition/core/logger.ts +107 -1
- package/src/tools/SpeechRecognition/hooks/useSpeechRecognition.ts +15 -4
- package/src/tools/SpeechRecognition/index.ts +9 -0
- package/src/tools/SpeechRecognition/widgets/VoiceComposerSlot.tsx +37 -2
- package/dist/ChatRoot-JVR3M3H2.mjs +0 -5
- package/dist/ChatRoot-LXIUBOXF.cjs +0 -14
- package/dist/DictationField-U25MEYAL.mjs +0 -4
- package/dist/DictationField-XWR5VOID.cjs +0 -13
- package/dist/MapContainer-76YL2JXL.cjs.map +0 -1
- package/dist/MapContainer-7HXBI3OH.mjs.map +0 -1
- package/dist/chunk-4PFW7MIJ.cjs.map +0 -1
- package/dist/chunk-C2YN6WEO.mjs.map +0 -1
- package/dist/chunk-HPK3EWBF.cjs.map +0 -1
- package/dist/chunk-PEKBT75W.mjs.map +0 -1
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { ChatDock, ChatFAB, ChatGreeting, ChatHeader, ChatHeaderActionButton, ChatHeaderAudioToggle, ChatHeaderLanguageButton, ChatHeaderModeToggle, ChatHeaderResetButton, ChatLauncher, ChatUnreadPreview, useChatPresence } from './chunk-BVESQTBM.mjs';
|
|
2
|
+
import './chunk-UNCS5V5F.mjs';
|
|
3
|
+
import './chunk-DMX7W4XZ.mjs';
|
|
4
|
+
import './chunk-HIK6BPL7.mjs';
|
|
5
|
+
import './chunk-N2XQF2OL.mjs';
|
|
6
|
+
//# sourceMappingURL=launcher-5WYPDPEP.mjs.map
|
|
7
|
+
//# sourceMappingURL=launcher-5WYPDPEP.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"launcher-5WYPDPEP.mjs"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/FloatingToolbar/FloatingToolbar.css"],"sourcesContent":["/* Focus ring when scroll isolation is unlocked */\n.scroll-unlocked {\n box-shadow: 0 0 0 2px hsl(var(--ring));\n transition: box-shadow 150ms;\n}\n"],"mappings":";AACA,CAAC;AACC,cAAY,EAAE,EAAE,EAAE,IAAI,IAAI,IAAI;AAC9B,cAAY,WAAW;AACzB;","names":[]}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkL25HA3TM_cjs = require('./chunk-L25HA3TM.cjs');
|
|
4
|
+
require('./chunk-ADEN3UA4.cjs');
|
|
5
|
+
require('./chunk-TBSHZO5R.cjs');
|
|
6
|
+
require('./chunk-FIRK5CEH.cjs');
|
|
7
|
+
require('./chunk-OLISEQHS.cjs');
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
Object.defineProperty(exports, "ChatDock", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
get: function () { return chunkL25HA3TM_cjs.ChatDock; }
|
|
14
|
+
});
|
|
15
|
+
Object.defineProperty(exports, "ChatFAB", {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: function () { return chunkL25HA3TM_cjs.ChatFAB; }
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports, "ChatGreeting", {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
get: function () { return chunkL25HA3TM_cjs.ChatGreeting; }
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(exports, "ChatHeader", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
get: function () { return chunkL25HA3TM_cjs.ChatHeader; }
|
|
26
|
+
});
|
|
27
|
+
Object.defineProperty(exports, "ChatHeaderActionButton", {
|
|
28
|
+
enumerable: true,
|
|
29
|
+
get: function () { return chunkL25HA3TM_cjs.ChatHeaderActionButton; }
|
|
30
|
+
});
|
|
31
|
+
Object.defineProperty(exports, "ChatHeaderAudioToggle", {
|
|
32
|
+
enumerable: true,
|
|
33
|
+
get: function () { return chunkL25HA3TM_cjs.ChatHeaderAudioToggle; }
|
|
34
|
+
});
|
|
35
|
+
Object.defineProperty(exports, "ChatHeaderLanguageButton", {
|
|
36
|
+
enumerable: true,
|
|
37
|
+
get: function () { return chunkL25HA3TM_cjs.ChatHeaderLanguageButton; }
|
|
38
|
+
});
|
|
39
|
+
Object.defineProperty(exports, "ChatHeaderModeToggle", {
|
|
40
|
+
enumerable: true,
|
|
41
|
+
get: function () { return chunkL25HA3TM_cjs.ChatHeaderModeToggle; }
|
|
42
|
+
});
|
|
43
|
+
Object.defineProperty(exports, "ChatHeaderResetButton", {
|
|
44
|
+
enumerable: true,
|
|
45
|
+
get: function () { return chunkL25HA3TM_cjs.ChatHeaderResetButton; }
|
|
46
|
+
});
|
|
47
|
+
Object.defineProperty(exports, "ChatLauncher", {
|
|
48
|
+
enumerable: true,
|
|
49
|
+
get: function () { return chunkL25HA3TM_cjs.ChatLauncher; }
|
|
50
|
+
});
|
|
51
|
+
Object.defineProperty(exports, "ChatUnreadPreview", {
|
|
52
|
+
enumerable: true,
|
|
53
|
+
get: function () { return chunkL25HA3TM_cjs.ChatUnreadPreview; }
|
|
54
|
+
});
|
|
55
|
+
Object.defineProperty(exports, "useChatPresence", {
|
|
56
|
+
enumerable: true,
|
|
57
|
+
get: function () { return chunkL25HA3TM_cjs.useChatPresence; }
|
|
58
|
+
});
|
|
59
|
+
//# sourceMappingURL=launcher-QAOG2NUI.cjs.map
|
|
60
|
+
//# sourceMappingURL=launcher-QAOG2NUI.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"launcher-QAOG2NUI.cjs"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/ui-tools",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.387",
|
|
4
4
|
"description": "Heavy React tools with lazy loading - for Electron, Vite, CRA, Next.js apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ui-tools",
|
|
@@ -65,9 +65,9 @@
|
|
|
65
65
|
"require": "./src/tools/Tour/index.ts"
|
|
66
66
|
},
|
|
67
67
|
"./code-editor": {
|
|
68
|
-
"types": "./src/tools/CodeEditor/
|
|
69
|
-
"import": "./src/tools/CodeEditor/
|
|
70
|
-
"require": "./src/tools/CodeEditor/
|
|
68
|
+
"types": "./src/tools/CodeEditor/lazy.tsx",
|
|
69
|
+
"import": "./src/tools/CodeEditor/lazy.tsx",
|
|
70
|
+
"require": "./src/tools/CodeEditor/lazy.tsx"
|
|
71
71
|
},
|
|
72
72
|
"./tree": {
|
|
73
73
|
"types": "./dist/tree/index.d.ts",
|
|
@@ -80,14 +80,14 @@
|
|
|
80
80
|
"require": "./dist/file-icon/index.cjs"
|
|
81
81
|
},
|
|
82
82
|
"./audio-player": {
|
|
83
|
-
"types": "./src/tools/AudioPlayer/
|
|
84
|
-
"import": "./src/tools/AudioPlayer/
|
|
85
|
-
"require": "./src/tools/AudioPlayer/
|
|
83
|
+
"types": "./src/tools/AudioPlayer/lazy.tsx",
|
|
84
|
+
"import": "./src/tools/AudioPlayer/lazy.tsx",
|
|
85
|
+
"require": "./src/tools/AudioPlayer/lazy.tsx"
|
|
86
86
|
},
|
|
87
87
|
"./chat": {
|
|
88
|
-
"types": "./src/tools/Chat/
|
|
89
|
-
"import": "./src/tools/Chat/
|
|
90
|
-
"require": "./src/tools/Chat/
|
|
88
|
+
"types": "./src/tools/Chat/lazy.tsx",
|
|
89
|
+
"import": "./src/tools/Chat/lazy.tsx",
|
|
90
|
+
"require": "./src/tools/Chat/lazy.tsx"
|
|
91
91
|
},
|
|
92
92
|
"./cron-scheduler": {
|
|
93
93
|
"types": "./src/tools/CronScheduler/lazy.tsx",
|
|
@@ -115,9 +115,14 @@
|
|
|
115
115
|
"require": "./src/tools/LottiePlayer/lazy.tsx"
|
|
116
116
|
},
|
|
117
117
|
"./markdown-editor": {
|
|
118
|
-
"types": "./src/tools/MarkdownEditor/
|
|
119
|
-
"import": "./src/tools/MarkdownEditor/
|
|
120
|
-
"require": "./src/tools/MarkdownEditor/
|
|
118
|
+
"types": "./src/tools/MarkdownEditor/lazy.tsx",
|
|
119
|
+
"import": "./src/tools/MarkdownEditor/lazy.tsx",
|
|
120
|
+
"require": "./src/tools/MarkdownEditor/lazy.tsx"
|
|
121
|
+
},
|
|
122
|
+
"./markdown-message": {
|
|
123
|
+
"types": "./src/components/markdown/MarkdownMessage/index.ts",
|
|
124
|
+
"import": "./src/components/markdown/MarkdownMessage/index.ts",
|
|
125
|
+
"require": "./src/components/markdown/MarkdownMessage/index.ts"
|
|
121
126
|
},
|
|
122
127
|
"./openapi-viewer": {
|
|
123
128
|
"types": "./src/tools/OpenapiViewer/lazy.tsx",
|
|
@@ -157,8 +162,8 @@
|
|
|
157
162
|
"test:watch": "vitest"
|
|
158
163
|
},
|
|
159
164
|
"peerDependencies": {
|
|
160
|
-
"@djangocfg/i18n": "^2.1.
|
|
161
|
-
"@djangocfg/ui-core": "^2.1.
|
|
165
|
+
"@djangocfg/i18n": "^2.1.387",
|
|
166
|
+
"@djangocfg/ui-core": "^2.1.387",
|
|
162
167
|
"consola": "^3.4.2",
|
|
163
168
|
"lodash-es": "^4.18.1",
|
|
164
169
|
"lucide-react": "^0.545.0",
|
|
@@ -212,9 +217,9 @@
|
|
|
212
217
|
"material-file-icons": "^2.4.0"
|
|
213
218
|
},
|
|
214
219
|
"devDependencies": {
|
|
215
|
-
"@djangocfg/i18n": "^2.1.
|
|
216
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
217
|
-
"@djangocfg/ui-core": "^2.1.
|
|
220
|
+
"@djangocfg/i18n": "^2.1.387",
|
|
221
|
+
"@djangocfg/typescript-config": "^2.1.387",
|
|
222
|
+
"@djangocfg/ui-core": "^2.1.387",
|
|
218
223
|
"@types/lodash-es": "^4.17.12",
|
|
219
224
|
"@types/mapbox__mapbox-gl-draw": "^1.4.8",
|
|
220
225
|
"@types/node": "^24.7.2",
|
|
@@ -1,8 +1,30 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* `@djangocfg/ui-tools/audio-player` subpath entrypoint.
|
|
5
|
+
*
|
|
6
|
+
* `LazyPlayer` is the only heavy export — it dynamically imports the full
|
|
7
|
+
* `Player` tree (PlayerShell + Layout + Cover + Waveform + Controls + audio
|
|
8
|
+
* decoding helpers). Everything else listed here is light:
|
|
9
|
+
* - types are erased,
|
|
10
|
+
* - the Zustand store, context hooks, and selectors carry no UI,
|
|
11
|
+
* - the slot components (`Cover`, `Title`, `PlayButton`, `Waveform`, …)
|
|
12
|
+
* are presentational React components that read from PlayerContext —
|
|
13
|
+
* they only become meaningful inside a `<PlayerProvider>` (which only
|
|
14
|
+
* exists inside `LazyPlayer` once loaded).
|
|
15
|
+
*
|
|
16
|
+
* Consumers building a fully custom layout: render `<LazyPlayer>` once to
|
|
17
|
+
* get the provider, then arrange slot components inside via render-children
|
|
18
|
+
* pattern, or use the bare `Player` re-exported from the root barrel.
|
|
19
|
+
*/
|
|
20
|
+
|
|
3
21
|
import { createLazyComponent } from '../../components';
|
|
4
22
|
import type { PlayerProps } from './types';
|
|
5
23
|
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// Lazy heavy component
|
|
26
|
+
// ============================================================================
|
|
27
|
+
|
|
6
28
|
export const LazyPlayer = createLazyComponent<PlayerProps>(
|
|
7
29
|
() => import('./Player').then((mod) => ({ default: mod.Player })),
|
|
8
30
|
{
|
|
@@ -14,3 +36,81 @@ export const LazyPlayer = createLazyComponent<PlayerProps>(
|
|
|
14
36
|
),
|
|
15
37
|
},
|
|
16
38
|
);
|
|
39
|
+
|
|
40
|
+
// ============================================================================
|
|
41
|
+
// Light surface — types, store, context, slot components, hooks
|
|
42
|
+
// ============================================================================
|
|
43
|
+
|
|
44
|
+
// Types
|
|
45
|
+
export type {
|
|
46
|
+
PlayerProps,
|
|
47
|
+
PlayerState,
|
|
48
|
+
PlayerStateKind,
|
|
49
|
+
PlayerControls,
|
|
50
|
+
PlayerHandle,
|
|
51
|
+
WaveformConfig,
|
|
52
|
+
WaveformMode,
|
|
53
|
+
ReactiveCoverMode,
|
|
54
|
+
PlayerVariant,
|
|
55
|
+
PlayerErrorReason,
|
|
56
|
+
} from './types';
|
|
57
|
+
|
|
58
|
+
// Context provider + selector hooks (no UI)
|
|
59
|
+
export {
|
|
60
|
+
PlayerProvider,
|
|
61
|
+
usePlayerAudio,
|
|
62
|
+
usePlayerControls,
|
|
63
|
+
usePlayerDuration,
|
|
64
|
+
usePlayerLevels,
|
|
65
|
+
usePlayerMeta,
|
|
66
|
+
usePlayerPaused,
|
|
67
|
+
usePlayerState,
|
|
68
|
+
} from './context';
|
|
69
|
+
|
|
70
|
+
// Cross-instance store (active player, preferences)
|
|
71
|
+
export {
|
|
72
|
+
setActivePlayer,
|
|
73
|
+
getActivePlayer,
|
|
74
|
+
getLastActivePlayer,
|
|
75
|
+
subscribeActivePlayer,
|
|
76
|
+
getPreferences,
|
|
77
|
+
setStoredVolume,
|
|
78
|
+
setStoredMuted,
|
|
79
|
+
subscribePreferences,
|
|
80
|
+
type PlayerPreferences,
|
|
81
|
+
} from './store';
|
|
82
|
+
|
|
83
|
+
// Store-backed hooks
|
|
84
|
+
export {
|
|
85
|
+
useActivePlayer,
|
|
86
|
+
useLastActivePlayer,
|
|
87
|
+
useIsActivePlayer,
|
|
88
|
+
} from './hooks/useActivePlayer';
|
|
89
|
+
export { usePlayerPreferences } from './hooks/usePlayerPreferences';
|
|
90
|
+
|
|
91
|
+
// Peak cache helpers
|
|
92
|
+
export { clearPeaksCache, setPeaks } from './audio';
|
|
93
|
+
|
|
94
|
+
// Slot components — presentational, read from PlayerContext. Safe to
|
|
95
|
+
// re-export synchronously: they don't import the heavy Player tree
|
|
96
|
+
// (audio decoding, layouts, shell) — only context selectors and types.
|
|
97
|
+
export { Cover, CoverPlaceholder, ReactivePulse } from './parts/Cover';
|
|
98
|
+
export { Title, Artist, TimeDisplay } from './parts/Meta';
|
|
99
|
+
export {
|
|
100
|
+
PlayButton,
|
|
101
|
+
SkipButton,
|
|
102
|
+
VolumeControl,
|
|
103
|
+
LoopButton,
|
|
104
|
+
ControlsRow,
|
|
105
|
+
IconButton,
|
|
106
|
+
} from './parts/Controls';
|
|
107
|
+
export {
|
|
108
|
+
Waveform,
|
|
109
|
+
PeaksWaveform,
|
|
110
|
+
LiveWaveform,
|
|
111
|
+
BarsWaveform,
|
|
112
|
+
ProgressBar,
|
|
113
|
+
WaveformSkeleton,
|
|
114
|
+
} from './parts/Waveform';
|
|
115
|
+
export { ErrorState } from './parts/ErrorState';
|
|
116
|
+
export { DefaultLayout, CompactLayout } from './parts/Layout';
|
package/src/tools/Chat/README.md
CHANGED
|
@@ -165,6 +165,67 @@ const prefs = useChatDockPrefs({ storageKey: 'crm.chat.dock' });
|
|
|
165
165
|
>{children}</ChatLauncher>
|
|
166
166
|
```
|
|
167
167
|
|
|
168
|
+
#### Side-mode body reserve + the `--chat-dock-reserve` token
|
|
169
|
+
|
|
170
|
+
When the dock is in `mode='side'` it pushes the rest of the page out of its way by writing two things to `<body>`:
|
|
171
|
+
|
|
172
|
+
| Surface | Value | Set by |
|
|
173
|
+
|---|---|---|
|
|
174
|
+
| `body.style.paddingRight` (or `paddingLeft`) | dock width in `px` | `<ChatDock>` mount effect |
|
|
175
|
+
| `--chat-dock-reserve` (CSS custom property) | same `px` value, as a token | same effect |
|
|
176
|
+
|
|
177
|
+
That covers two layout flows automatically:
|
|
178
|
+
|
|
179
|
+
- **Static content inside `<body>`** — `paddingRight` shifts it inward so the chat doesn't cover the right edge.
|
|
180
|
+
- **CSS-only consumers** — read the token from anywhere:
|
|
181
|
+
```css
|
|
182
|
+
.my-floating-thing {
|
|
183
|
+
padding-right: var(--chat-dock-reserve, 0px);
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**`position: fixed` consumers (navbars, FABs, sticky banners) are NOT auto-shifted** — body padding doesn't propagate to fixed positioning. They have to opt in by reading the token themselves. Example for a fixed top navbar:
|
|
188
|
+
|
|
189
|
+
```tsx
|
|
190
|
+
'use client';
|
|
191
|
+
|
|
192
|
+
import { useEffect, useState } from 'react';
|
|
193
|
+
|
|
194
|
+
/** Mirrors `<body>` padding onto a fixed element so it leaves room for the docked chat. */
|
|
195
|
+
function useChatDockReserve() {
|
|
196
|
+
const [reserve, setReserve] = useState({ left: 0, right: 0 });
|
|
197
|
+
useEffect(() => {
|
|
198
|
+
const read = () => {
|
|
199
|
+
const cs = getComputedStyle(document.body);
|
|
200
|
+
setReserve({
|
|
201
|
+
left: parseFloat(cs.paddingLeft) || 0,
|
|
202
|
+
right: parseFloat(cs.paddingRight) || 0,
|
|
203
|
+
});
|
|
204
|
+
};
|
|
205
|
+
read();
|
|
206
|
+
const mo = new MutationObserver(read);
|
|
207
|
+
mo.observe(document.body, { attributes: true, attributeFilter: ['style'] });
|
|
208
|
+
window.addEventListener('resize', read);
|
|
209
|
+
return () => { mo.disconnect(); window.removeEventListener('resize', read); };
|
|
210
|
+
}, []);
|
|
211
|
+
return reserve;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function MyNavbar() {
|
|
215
|
+
const reserve = useChatDockReserve();
|
|
216
|
+
return (
|
|
217
|
+
<header
|
|
218
|
+
className="fixed top-0 z-50 transition-all"
|
|
219
|
+
style={{ left: reserve.left, right: reserve.right }}
|
|
220
|
+
>
|
|
221
|
+
…
|
|
222
|
+
</header>
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Same recipe works for any other fixed element (cookie banner, FAB, sticky toolbar). When the dock is in `popover` mode (or closed) both values are `0` and the navbar spans the full viewport.
|
|
228
|
+
|
|
168
229
|
### Greeting
|
|
169
230
|
|
|
170
231
|
```tsx
|
|
@@ -256,7 +317,7 @@ const audio = useChatAudio();
|
|
|
256
317
|
</ChatLauncher>
|
|
257
318
|
```
|
|
258
319
|
|
|
259
|
-
**Built-in sounds.** The chat tool ships its own notification pack (`messageSent`, `messageReceived`, `streamStart`, `error`, `mention`, `notification`) inlined
|
|
320
|
+
**Built-in sounds.** The chat tool ships its own notification pack (`messageSent`, `messageReceived`, `streamStart`, `error`, `mention`, `notification`) inlined as `data:audio/mpeg;base64,…` URLs inside `core/audio/sounds/*.ts` modules. ~136KB total — paid only by hosts that actually import Chat. No `.mp3` loader, no `*.d.ts` shim, no CDN to host. Re-encode source mp3s via ffmpeg + `base64 -i …` and paste back into the matching `.ts` file (see `core/audio/defaults.ts` header for the recipe).
|
|
260
321
|
|
|
261
322
|
Customize:
|
|
262
323
|
|
|
@@ -318,6 +379,29 @@ Calls `window.dialog.confirm` (destructive variant) from `@djangocfg/ui-core/lib
|
|
|
318
379
|
| Reusing `dismissStorageKey` across products | Greeting won't show on the second product |
|
|
319
380
|
| Calling `useChatUnread()` outside `ChatProvider` | Hook reads the messages store; throws without provider |
|
|
320
381
|
| Forgetting `<DialogProvider>` for `<ChatHeaderResetButton confirm>` | Falls back to native browser confirm (ugly) |
|
|
382
|
+
| Mixing **root barrel** and **subpath** imports | Loads the package twice → two React contexts → `useChatContextOptional()` returns `null` → VoiceComposerSlot silently drops transcripts. See **Import discipline** below. |
|
|
383
|
+
|
|
384
|
+
#### Import discipline
|
|
385
|
+
|
|
386
|
+
`@djangocfg/ui-tools` ships its root export (`.`) through a compiled bundle in `dist/`, while most subpaths (`./chat`, `./speech-recognition`, `./audio-player`, …) resolve to raw `src/`. JavaScript bundlers treat them as **two separate modules**, so each gets its own `React.createContext(...)` call.
|
|
387
|
+
|
|
388
|
+
```tsx
|
|
389
|
+
// ❌ DON'T: ChatRoot from `dist`, VoiceComposerSlot from `src` → two contexts
|
|
390
|
+
import { ChatRoot } from '@djangocfg/ui-tools';
|
|
391
|
+
import { VoiceComposerSlot } from '@djangocfg/ui-tools/speech-recognition';
|
|
392
|
+
// ^^^^^^^^^^^^^^^^^^^
|
|
393
|
+
// VoiceComposerSlot's internal useChatContextOptional() reads a DIFFERENT
|
|
394
|
+
// React context than the one ChatProvider populated. ctx.composer is null,
|
|
395
|
+
// every transcript gets a "no composer handle registered" warning.
|
|
396
|
+
|
|
397
|
+
// ✅ DO: pull every Chat-side surface from the same `./chat` subpath
|
|
398
|
+
import { ChatRoot, ChatLauncher, useChatContextOptional } from '@djangocfg/ui-tools/chat';
|
|
399
|
+
import { VoiceComposerSlot } from '@djangocfg/ui-tools/speech-recognition';
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
The rule of thumb: **anything that talks to `ChatProvider` belongs on the `./chat` subpath**. The root barrel is for tooling glue (types, transports, generic helpers) and is safe to mix with subpaths as long as you don't import anything that uses the Chat React context from it.
|
|
403
|
+
|
|
404
|
+
If two ChatProvider context instances ever end up in the same page, `ChatProvider` emits a one-time `console.warn` in development with the same fix instructions — that's the runtime guard, look for `[@djangocfg/ui-tools/chat] Two ChatProvider context instances detected …` in DevTools.
|
|
321
405
|
|
|
322
406
|
## Styling — role-aware tokens
|
|
323
407
|
|
|
@@ -64,6 +64,48 @@ export interface ChatContextValue extends UseChatReturn {
|
|
|
64
64
|
|
|
65
65
|
const Ctx = createContext<ChatContextValue | null>(null);
|
|
66
66
|
|
|
67
|
+
// ─── Dual-bundle guard ────────────────────────────────────────────────────
|
|
68
|
+
//
|
|
69
|
+
// `@djangocfg/ui-tools` ships its root export through `dist/` (compiled) but
|
|
70
|
+
// most subpaths resolve to raw `src/`. If a consumer mixes them — e.g.
|
|
71
|
+
// `ChatRoot` from the root barrel and `VoiceComposerSlot` from
|
|
72
|
+
// `@djangocfg/ui-tools/speech-recognition` — `createContext(...)` runs twice
|
|
73
|
+
// and produces two distinct context instances. The Composer registers its
|
|
74
|
+
// handle on one; `useChatContextOptional()` in the voice slot reads from the
|
|
75
|
+
// other, sees `null`, and silently drops every transcript.
|
|
76
|
+
//
|
|
77
|
+
// We tag the global with our module identity. When more than one tag is
|
|
78
|
+
// observed the consumer is told exactly what to do.
|
|
79
|
+
type GuardSlot = { stamps: Set<symbol>; warned: boolean };
|
|
80
|
+
const GLOBAL_KEY = '__djangocfg_chat_ctx_stamps__';
|
|
81
|
+
const stamp = Symbol('djangocfg.chat.ctx');
|
|
82
|
+
function markCtxLoad(): void {
|
|
83
|
+
if (typeof globalThis === 'undefined') return;
|
|
84
|
+
const g = globalThis as unknown as Record<string, GuardSlot | undefined>;
|
|
85
|
+
let slot = g[GLOBAL_KEY];
|
|
86
|
+
if (!slot) {
|
|
87
|
+
slot = { stamps: new Set(), warned: false };
|
|
88
|
+
g[GLOBAL_KEY] = slot;
|
|
89
|
+
}
|
|
90
|
+
slot.stamps.add(stamp);
|
|
91
|
+
if (slot.stamps.size > 1 && !slot.warned && process.env.NODE_ENV !== 'production') {
|
|
92
|
+
slot.warned = true;
|
|
93
|
+
// eslint-disable-next-line no-console
|
|
94
|
+
console.warn(
|
|
95
|
+
'[@djangocfg/ui-tools/chat] Two ChatProvider context instances detected — ' +
|
|
96
|
+
'this means `@djangocfg/ui-tools` was loaded twice (one bundle via the root ' +
|
|
97
|
+
'`.` export → `dist/`, another via a `./chat` / `./speech-recognition` subpath → `src/`). ' +
|
|
98
|
+
'Symptom: `useChatContextOptional()` returns `null` for descendants of `<ChatProvider>`, ' +
|
|
99
|
+
'so VoiceComposerSlot drops transcripts, useChatReset silently no-ops, etc. ' +
|
|
100
|
+
'\n\nFix: import every Chat surface from the SAME subpath. Recommended:\n' +
|
|
101
|
+
" import { ChatRoot, ChatLauncher, useChatContextOptional, … } from '@djangocfg/ui-tools/chat';\n" +
|
|
102
|
+
" import { VoiceComposerSlot } from '@djangocfg/ui-tools/speech-recognition';\n" +
|
|
103
|
+
'\n(See packages/ui-tools/src/tools/Chat/README.md → "Anti-patterns".)',
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
markCtxLoad();
|
|
108
|
+
|
|
67
109
|
export interface ChatProviderProps {
|
|
68
110
|
transport: ChatTransport;
|
|
69
111
|
config?: ChatConfig;
|
package/src/tools/Chat/lazy.tsx
CHANGED
|
@@ -1,7 +1,36 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* `@djangocfg/ui-tools/chat` subpath entrypoint.
|
|
5
|
+
*
|
|
6
|
+
* This file is the public surface consumers see when they
|
|
7
|
+
* `import … from '@djangocfg/ui-tools/chat'`. It is designed so that the
|
|
8
|
+
* heavy chat UI (~hundreds of KB across MessageList, Composer, ToolCalls,
|
|
9
|
+
* Attachments, MarkdownMessage transitively) loads only when one of the
|
|
10
|
+
* `Lazy*` wrappers actually mounts.
|
|
11
|
+
*
|
|
12
|
+
* Rules of thumb:
|
|
13
|
+
* - **Heavy** (ChatRoot, ChatLauncher, ChatFAB, ChatDock, MessageList,
|
|
14
|
+
* MessageBubble, Composer, ToolCalls, Attachments*, ChatHeader*, …) —
|
|
15
|
+
* loaded only via `Lazy*` wrappers below. Do NOT add synchronous
|
|
16
|
+
* re-exports for these from this file.
|
|
17
|
+
* - **Light** (types, config constants, pure core reducer/utils,
|
|
18
|
+
* transports, hooks without UI, audio prefs, draft sanitizer) —
|
|
19
|
+
* re-exported synchronously here. Types are erased at compile time;
|
|
20
|
+
* helpers and hooks pull in no UI components.
|
|
21
|
+
*
|
|
22
|
+
* Consumers that need synchronous access to the heavy components
|
|
23
|
+
* (custom chat layouts, Storybook stories) can import from the root
|
|
24
|
+
* barrel `@djangocfg/ui-tools` instead.
|
|
25
|
+
*/
|
|
26
|
+
|
|
3
27
|
import { createLazyComponent, LoadingFallback } from '../../components/lazy-wrapper';
|
|
4
28
|
import type { ChatRootProps } from './components/ChatRoot';
|
|
29
|
+
import type { ChatLauncherProps } from './launcher';
|
|
30
|
+
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// Lazy UI components
|
|
33
|
+
// ============================================================================
|
|
5
34
|
|
|
6
35
|
export const LazyChat = createLazyComponent<ChatRootProps>(
|
|
7
36
|
() => import('./components/ChatRoot').then((m) => ({ default: m.ChatRoot })),
|
|
@@ -11,4 +40,187 @@ export const LazyChat = createLazyComponent<ChatRootProps>(
|
|
|
11
40
|
},
|
|
12
41
|
);
|
|
13
42
|
|
|
14
|
-
export
|
|
43
|
+
export const LazyChatLauncher = createLazyComponent<ChatLauncherProps>(
|
|
44
|
+
() => import('./launcher').then((m) => ({ default: m.ChatLauncher })),
|
|
45
|
+
{
|
|
46
|
+
displayName: 'LazyChatLauncher',
|
|
47
|
+
// Launcher renders a floating FAB by default — no inline placeholder.
|
|
48
|
+
fallback: null,
|
|
49
|
+
},
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// ============================================================================
|
|
53
|
+
// Light surface re-exports — types + pure helpers + hooks without UI
|
|
54
|
+
// ============================================================================
|
|
55
|
+
|
|
56
|
+
// Types (erased at compile time, free to re-export)
|
|
57
|
+
export type {
|
|
58
|
+
ChatRole,
|
|
59
|
+
ChatMessage,
|
|
60
|
+
ChatPersona,
|
|
61
|
+
ChatToolCall,
|
|
62
|
+
ChatAttachment,
|
|
63
|
+
ChatSource,
|
|
64
|
+
ChatDisplayMode,
|
|
65
|
+
ChatUserContext,
|
|
66
|
+
ChatAssistantContext,
|
|
67
|
+
ChatPrefs,
|
|
68
|
+
ChatConfig,
|
|
69
|
+
ChatLabels,
|
|
70
|
+
ChatTransport,
|
|
71
|
+
ChatStreamEvent,
|
|
72
|
+
CreateSessionOptions,
|
|
73
|
+
SessionInfo,
|
|
74
|
+
HistoryPage,
|
|
75
|
+
StreamOptions,
|
|
76
|
+
SendOptions,
|
|
77
|
+
} from './types';
|
|
78
|
+
export { DEFAULT_LABELS } from './types';
|
|
79
|
+
|
|
80
|
+
// Config — plain constants, no UI imports
|
|
81
|
+
export {
|
|
82
|
+
STORAGE_KEYS,
|
|
83
|
+
CSS_VARS,
|
|
84
|
+
DEFAULT_Z_INDEX,
|
|
85
|
+
LIMITS,
|
|
86
|
+
DEFAULT_SIDEBAR,
|
|
87
|
+
HOTKEYS,
|
|
88
|
+
CHAT_EVENT_NAME,
|
|
89
|
+
type ChatEventDetail,
|
|
90
|
+
} from './config';
|
|
91
|
+
|
|
92
|
+
// Core — pure reducer / id / token buffer / persona / initials
|
|
93
|
+
export {
|
|
94
|
+
reducer,
|
|
95
|
+
initialState,
|
|
96
|
+
createId,
|
|
97
|
+
createTokenBuffer,
|
|
98
|
+
resolvePersona,
|
|
99
|
+
deriveInitials,
|
|
100
|
+
type ChatState,
|
|
101
|
+
type ChatAction,
|
|
102
|
+
type TokenBuffer,
|
|
103
|
+
} from './core';
|
|
104
|
+
|
|
105
|
+
// Transports — pure functions, no UI
|
|
106
|
+
export {
|
|
107
|
+
createHttpTransport,
|
|
108
|
+
createMockTransport,
|
|
109
|
+
parseSSE,
|
|
110
|
+
TransportError,
|
|
111
|
+
createPydanticAIChatTransport,
|
|
112
|
+
createToolIdQueue,
|
|
113
|
+
mapPydanticAIEvent,
|
|
114
|
+
createPydanticAISSEMap,
|
|
115
|
+
type HttpTransportConfig,
|
|
116
|
+
type MockTransportOptions,
|
|
117
|
+
type ParseSSEOptions,
|
|
118
|
+
type PydanticAIChatTransportOpts,
|
|
119
|
+
type PydanticAIEvent,
|
|
120
|
+
type ToolIdQueue,
|
|
121
|
+
} from './core/transport';
|
|
122
|
+
|
|
123
|
+
// Hooks — no JSX, no UI
|
|
124
|
+
export {
|
|
125
|
+
useChat,
|
|
126
|
+
useChatComposer,
|
|
127
|
+
useChatScroll,
|
|
128
|
+
useChatHistory,
|
|
129
|
+
useChatLayout,
|
|
130
|
+
useChatAudio,
|
|
131
|
+
useAutoFocusOnStreamEnd,
|
|
132
|
+
useRegisterComposer,
|
|
133
|
+
useChatReset,
|
|
134
|
+
useVisitorFingerprint,
|
|
135
|
+
useChatDockPrefs,
|
|
136
|
+
DEFAULT_DOCK_PREFS,
|
|
137
|
+
useFocusOnEmptyClick,
|
|
138
|
+
useChatUnread,
|
|
139
|
+
useChatLightbox,
|
|
140
|
+
type UseChatUnreadOptions,
|
|
141
|
+
type UseChatUnreadReturn,
|
|
142
|
+
type UseChatConfig,
|
|
143
|
+
type UseChatReturn,
|
|
144
|
+
type UseChatComposerOptions,
|
|
145
|
+
type UseChatComposerReturn,
|
|
146
|
+
type UseChatScrollOptions,
|
|
147
|
+
type UseChatScrollReturn,
|
|
148
|
+
type UseChatHistoryOptions,
|
|
149
|
+
type UseChatLayoutConfig,
|
|
150
|
+
type UseChatLayoutReturn,
|
|
151
|
+
type UseAutoFocusOnStreamEndOptions,
|
|
152
|
+
type Focusable,
|
|
153
|
+
type UseChatResetOptions,
|
|
154
|
+
type UseChatResetReturn,
|
|
155
|
+
type UseVisitorFingerprintOptions,
|
|
156
|
+
type ChatDockPrefs,
|
|
157
|
+
type UseChatDockPrefsOptions,
|
|
158
|
+
type UseChatDockPrefsReturn,
|
|
159
|
+
type UseFocusOnEmptyClickOptions,
|
|
160
|
+
type UseChatLightboxReturn,
|
|
161
|
+
type ChatLightboxState,
|
|
162
|
+
} from './hooks';
|
|
163
|
+
|
|
164
|
+
// Audio
|
|
165
|
+
export {
|
|
166
|
+
useChatAudioPrefs,
|
|
167
|
+
DEFAULT_CHAT_SOUNDS,
|
|
168
|
+
type ChatAudioEvent,
|
|
169
|
+
type ChatAudioSounds,
|
|
170
|
+
type ChatAudioConfig,
|
|
171
|
+
type UseChatAudioReturn,
|
|
172
|
+
} from './core/audio';
|
|
173
|
+
|
|
174
|
+
// Tool-call payload dispatcher — pure
|
|
175
|
+
export {
|
|
176
|
+
dispatchToolPayload,
|
|
177
|
+
isPlainObject,
|
|
178
|
+
isLatLng,
|
|
179
|
+
isGeoJSONFeatureCollection,
|
|
180
|
+
isStringValue,
|
|
181
|
+
type ToolPayloadMatcher,
|
|
182
|
+
type ToolPayloadFallback,
|
|
183
|
+
} from './core/payload-dispatch';
|
|
184
|
+
|
|
185
|
+
// Logger — pure
|
|
186
|
+
export {
|
|
187
|
+
getChatLogger,
|
|
188
|
+
type ChatLogger,
|
|
189
|
+
type ChatLogScope,
|
|
190
|
+
} from './core/logger';
|
|
191
|
+
|
|
192
|
+
// Utils — pure
|
|
193
|
+
export { sanitizeDraft, isSubmittableDraft } from './utils/sanitizeDraft';
|
|
194
|
+
export { collectImageAttachments } from './utils/collectImageAttachments';
|
|
195
|
+
|
|
196
|
+
// Style tokens — strings + hooks, no UI components
|
|
197
|
+
export {
|
|
198
|
+
BUBBLE_SURFACE,
|
|
199
|
+
ANCHOR,
|
|
200
|
+
TOGGLE,
|
|
201
|
+
DESTRUCTIVE_SURFACE,
|
|
202
|
+
TOOL_CALL,
|
|
203
|
+
useChatBubbleStyles,
|
|
204
|
+
useChatRoleStyles,
|
|
205
|
+
useChatDestructiveStyles,
|
|
206
|
+
type ChatBubbleSurface,
|
|
207
|
+
type ChatBubbleStyles,
|
|
208
|
+
type ChatRoleStyles,
|
|
209
|
+
type ChatDestructiveStyles,
|
|
210
|
+
} from './styles';
|
|
211
|
+
|
|
212
|
+
// Provider / context — needed by callers wiring custom chat shells
|
|
213
|
+
export {
|
|
214
|
+
ChatProvider,
|
|
215
|
+
useChatContext,
|
|
216
|
+
useChatContextOptional,
|
|
217
|
+
type ChatContextValue,
|
|
218
|
+
type ChatProviderProps,
|
|
219
|
+
} from './context';
|
|
220
|
+
|
|
221
|
+
// Heavy-component prop types (consumers building custom layouts still need
|
|
222
|
+
// these for typing). The actual components are NOT re-exported.
|
|
223
|
+
export type {
|
|
224
|
+
ChatRootProps,
|
|
225
|
+
ChatLauncherProps,
|
|
226
|
+
};
|