@djangocfg/ui-tools 2.1.385 → 2.1.389
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-EFNXQXXN.cjs +15 -0
- package/dist/{ChatRoot-JVR3M3H2.mjs.map → ChatRoot-EFNXQXXN.cjs.map} +1 -1
- package/dist/ChatRoot-FITF5RVP.mjs +6 -0
- package/dist/{ChatRoot-LXIUBOXF.cjs.map → ChatRoot-FITF5RVP.mjs.map} +1 -1
- package/dist/{DocsLayout-2P3ONDWJ.mjs → DocsLayout-EKASBSP7.mjs} +3 -3
- package/dist/{DocsLayout-2P3ONDWJ.mjs.map → DocsLayout-EKASBSP7.mjs.map} +1 -1
- package/dist/{DocsLayout-2YZNS5VK.cjs → DocsLayout-OURFYWQE.cjs} +8 -8
- package/dist/{DocsLayout-2YZNS5VK.cjs.map → DocsLayout-OURFYWQE.cjs.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/{chunk-HIK6BPL7.mjs → chunk-2NG4SXEP.mjs} +6 -5
- package/dist/chunk-2NG4SXEP.mjs.map +1 -0
- package/dist/chunk-4LFB7I5K.cjs +1387 -0
- package/dist/chunk-4LFB7I5K.cjs.map +1 -0
- package/dist/{MapContainer-76YL2JXL.cjs → chunk-5D2OCOPQ.cjs} +3 -2
- package/dist/chunk-5D2OCOPQ.cjs.map +1 -0
- package/dist/chunk-6ZX2G25W.mjs +1361 -0
- package/dist/chunk-6ZX2G25W.mjs.map +1 -0
- package/dist/{MapContainer-7HXBI3OH.mjs → chunk-7CWGZPO3.mjs} +3 -3
- package/dist/chunk-7CWGZPO3.mjs.map +1 -0
- package/dist/{chunk-FIRK5CEH.cjs → chunk-7IYXZUJO.cjs} +8 -4
- package/dist/chunk-7IYXZUJO.cjs.map +1 -0
- package/dist/{chunk-PEKBT75W.mjs → chunk-DMX7W4XZ.mjs} +53 -1387
- package/dist/chunk-DMX7W4XZ.mjs.map +1 -0
- package/dist/chunk-NTVBIIUD.mjs +1439 -0
- package/dist/chunk-NTVBIIUD.mjs.map +1 -0
- package/dist/{chunk-HPK3EWBF.cjs → chunk-TBSHZO5R.cjs} +50 -1409
- package/dist/chunk-TBSHZO5R.cjs.map +1 -0
- package/dist/chunk-W75B7Y6C.cjs +1478 -0
- package/dist/chunk-W75B7Y6C.cjs.map +1 -0
- package/dist/index.cjs +1269 -1790
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +660 -623
- package/dist/index.d.ts +660 -623
- package/dist/index.mjs +856 -1427
- package/dist/index.mjs.map +1 -1
- package/dist/launcher-5Y42OBSN.mjs +6 -0
- package/dist/launcher-5Y42OBSN.mjs.map +1 -0
- package/dist/launcher-PMW2YB24.cjs +59 -0
- package/dist/launcher-PMW2YB24.cjs.map +1 -0
- package/package.json +23 -18
- package/src/components/index.ts +2 -2
- package/src/index.ts +20 -2
- package/src/tools/AudioPlayer/lazy.tsx +100 -0
- package/src/tools/Chat/README.md +85 -1
- package/src/tools/Chat/components/MessageBubble.tsx +1 -1
- package/src/tools/Chat/context/ChatProvider.tsx +42 -0
- package/src/tools/Chat/index.ts +1 -1
- package/src/tools/Chat/lazy.tsx +300 -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/{components/markdown → tools}/MarkdownMessage/CodeBlock.tsx +1 -1
- package/src/{components/markdown → tools}/MarkdownMessage/CollapseToggle.tsx +1 -1
- package/src/{components/markdown → tools}/MarkdownMessage/MarkdownMessage.tsx +1 -1
- package/src/{components/markdown → tools}/MarkdownMessage/components.tsx +2 -2
- package/src/tools/OpenapiViewer/components/DocsLayout/ApiIntroSection.tsx +1 -1
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Header/index.tsx +1 -1
- package/src/tools/SpeechRecognition/README.md +48 -0
- package/dist/ChatRoot-JVR3M3H2.mjs +0 -5
- package/dist/ChatRoot-LXIUBOXF.cjs +0 -14
- package/dist/MapContainer-76YL2JXL.cjs.map +0 -1
- package/dist/MapContainer-7HXBI3OH.mjs.map +0 -1
- package/dist/chunk-FIRK5CEH.cjs.map +0 -1
- package/dist/chunk-HIK6BPL7.mjs.map +0 -1
- package/dist/chunk-HPK3EWBF.cjs.map +0 -1
- package/dist/chunk-PEKBT75W.mjs.map +0 -1
- package/src/components/markdown/index.ts +0 -19
- /package/src/{components/markdown → hooks}/useCollapsibleContent.ts +0 -0
- /package/src/{components/markdown → tools}/MarkdownMessage/ActionRow.tsx +0 -0
- /package/src/{components/markdown → tools}/MarkdownMessage/ChatMessageRow.tsx +0 -0
- /package/src/{components/markdown → tools}/MarkdownMessage/README.md +0 -0
- /package/src/{components/markdown → tools}/MarkdownMessage/index.ts +0 -0
- /package/src/{components/markdown → tools}/MarkdownMessage/linkRules.ts +0 -0
- /package/src/{components/markdown → tools}/MarkdownMessage/plainText.ts +0 -0
- /package/src/{components/markdown → tools}/MarkdownMessage/sanitize.ts +0 -0
- /package/src/{components/markdown → tools}/MarkdownMessage/types.ts +0 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { ChatDock, ChatFAB, ChatGreeting, ChatHeader, ChatHeaderActionButton, ChatHeaderAudioToggle, ChatHeaderLanguageButton, ChatHeaderModeToggle, ChatHeaderResetButton, ChatLauncher, ChatUnreadPreview, useChatPresence } from './chunk-NTVBIIUD.mjs';
|
|
2
|
+
import './chunk-DMX7W4XZ.mjs';
|
|
3
|
+
import './chunk-UNCS5V5F.mjs';
|
|
4
|
+
import './chunk-N2XQF2OL.mjs';
|
|
5
|
+
//# sourceMappingURL=launcher-5Y42OBSN.mjs.map
|
|
6
|
+
//# sourceMappingURL=launcher-5Y42OBSN.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"launcher-5Y42OBSN.mjs"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkW75B7Y6C_cjs = require('./chunk-W75B7Y6C.cjs');
|
|
4
|
+
require('./chunk-TBSHZO5R.cjs');
|
|
5
|
+
require('./chunk-ADEN3UA4.cjs');
|
|
6
|
+
require('./chunk-OLISEQHS.cjs');
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
Object.defineProperty(exports, "ChatDock", {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: function () { return chunkW75B7Y6C_cjs.ChatDock; }
|
|
13
|
+
});
|
|
14
|
+
Object.defineProperty(exports, "ChatFAB", {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
get: function () { return chunkW75B7Y6C_cjs.ChatFAB; }
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(exports, "ChatGreeting", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () { return chunkW75B7Y6C_cjs.ChatGreeting; }
|
|
21
|
+
});
|
|
22
|
+
Object.defineProperty(exports, "ChatHeader", {
|
|
23
|
+
enumerable: true,
|
|
24
|
+
get: function () { return chunkW75B7Y6C_cjs.ChatHeader; }
|
|
25
|
+
});
|
|
26
|
+
Object.defineProperty(exports, "ChatHeaderActionButton", {
|
|
27
|
+
enumerable: true,
|
|
28
|
+
get: function () { return chunkW75B7Y6C_cjs.ChatHeaderActionButton; }
|
|
29
|
+
});
|
|
30
|
+
Object.defineProperty(exports, "ChatHeaderAudioToggle", {
|
|
31
|
+
enumerable: true,
|
|
32
|
+
get: function () { return chunkW75B7Y6C_cjs.ChatHeaderAudioToggle; }
|
|
33
|
+
});
|
|
34
|
+
Object.defineProperty(exports, "ChatHeaderLanguageButton", {
|
|
35
|
+
enumerable: true,
|
|
36
|
+
get: function () { return chunkW75B7Y6C_cjs.ChatHeaderLanguageButton; }
|
|
37
|
+
});
|
|
38
|
+
Object.defineProperty(exports, "ChatHeaderModeToggle", {
|
|
39
|
+
enumerable: true,
|
|
40
|
+
get: function () { return chunkW75B7Y6C_cjs.ChatHeaderModeToggle; }
|
|
41
|
+
});
|
|
42
|
+
Object.defineProperty(exports, "ChatHeaderResetButton", {
|
|
43
|
+
enumerable: true,
|
|
44
|
+
get: function () { return chunkW75B7Y6C_cjs.ChatHeaderResetButton; }
|
|
45
|
+
});
|
|
46
|
+
Object.defineProperty(exports, "ChatLauncher", {
|
|
47
|
+
enumerable: true,
|
|
48
|
+
get: function () { return chunkW75B7Y6C_cjs.ChatLauncher; }
|
|
49
|
+
});
|
|
50
|
+
Object.defineProperty(exports, "ChatUnreadPreview", {
|
|
51
|
+
enumerable: true,
|
|
52
|
+
get: function () { return chunkW75B7Y6C_cjs.ChatUnreadPreview; }
|
|
53
|
+
});
|
|
54
|
+
Object.defineProperty(exports, "useChatPresence", {
|
|
55
|
+
enumerable: true,
|
|
56
|
+
get: function () { return chunkW75B7Y6C_cjs.useChatPresence; }
|
|
57
|
+
});
|
|
58
|
+
//# sourceMappingURL=launcher-PMW2YB24.cjs.map
|
|
59
|
+
//# sourceMappingURL=launcher-PMW2YB24.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"launcher-PMW2YB24.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.389",
|
|
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.389",
|
|
166
|
+
"@djangocfg/ui-core": "^2.1.389",
|
|
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.389",
|
|
221
|
+
"@djangocfg/typescript-config": "^2.1.389",
|
|
222
|
+
"@djangocfg/ui-core": "^2.1.389",
|
|
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",
|
package/src/components/index.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -127,10 +127,28 @@ export * from './tools/Chat';
|
|
|
127
127
|
export * from './tools';
|
|
128
128
|
|
|
129
129
|
// ============================================================================
|
|
130
|
-
//
|
|
130
|
+
// Markdown renderer (sits alongside MarkdownEditor under tools/)
|
|
131
131
|
// ============================================================================
|
|
132
132
|
|
|
133
|
-
export
|
|
133
|
+
export {
|
|
134
|
+
MarkdownMessage,
|
|
135
|
+
default as MarkdownMessageDefault,
|
|
136
|
+
ChatMessageRow,
|
|
137
|
+
ActionRow,
|
|
138
|
+
extractTextFromChildren,
|
|
139
|
+
type MarkdownMessageProps,
|
|
140
|
+
type LinkRule,
|
|
141
|
+
} from './tools/MarkdownMessage';
|
|
142
|
+
|
|
143
|
+
// ============================================================================
|
|
144
|
+
// Shared hooks
|
|
145
|
+
// ============================================================================
|
|
146
|
+
|
|
147
|
+
export {
|
|
148
|
+
useCollapsibleContent,
|
|
149
|
+
type UseCollapsibleContentOptions,
|
|
150
|
+
type UseCollapsibleContentResult,
|
|
151
|
+
} from './hooks/useCollapsibleContent';
|
|
134
152
|
|
|
135
153
|
// ============================================================================
|
|
136
154
|
// Stores
|
|
@@ -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
|
|
|
@@ -5,7 +5,7 @@ import { memo, type ReactNode } from 'react';
|
|
|
5
5
|
import { Avatar, AvatarFallback, AvatarImage } from '@djangocfg/ui-core/components';
|
|
6
6
|
import { cn } from '@djangocfg/ui-core/lib';
|
|
7
7
|
|
|
8
|
-
import { MarkdownMessage } from '
|
|
8
|
+
import { MarkdownMessage } from '../../MarkdownMessage';
|
|
9
9
|
import type {
|
|
10
10
|
ChatAssistantContext,
|
|
11
11
|
ChatAttachment,
|
|
@@ -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/index.ts
CHANGED