pixelon 0.6.3 → 0.7.0
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/dist/components/molecules/chat/ChatNotice/ChatNotice.d.ts +17 -0
- package/dist/core/chat/ChatConfigContext.d.ts +11 -0
- package/dist/core/eventBus/chatEventBus.d.ts +17 -0
- package/dist/index.esm.js +98 -1
- package/dist/index.js +98 -1
- package/dist/styles.css +1 -1
- package/dist/styles.css.map +1 -1
- package/package.json +1 -1
- package/src/styles/domains/_chat.scss +22 -0
- package/src/styles/index.scss +1 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type FC } from 'react';
|
|
2
|
+
import './ChatNotice.scss';
|
|
3
|
+
export type ChatNoticeType = 'success' | 'warning' | 'error' | 'info';
|
|
4
|
+
export type ChatNoticeMode = 'temporary' | 'static';
|
|
5
|
+
export interface ChatNoticeData {
|
|
6
|
+
id: string;
|
|
7
|
+
type: ChatNoticeType;
|
|
8
|
+
message: string;
|
|
9
|
+
mode: ChatNoticeMode;
|
|
10
|
+
duration?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface ChatNoticeProps {
|
|
13
|
+
notice: ChatNoticeData;
|
|
14
|
+
onClose: (id: string) => void;
|
|
15
|
+
}
|
|
16
|
+
export declare const ChatNotice: FC<ChatNoticeProps>;
|
|
17
|
+
export default ChatNotice;
|
|
@@ -90,6 +90,17 @@ export interface ChatConfig {
|
|
|
90
90
|
* agente no tiene por qué conocerla). Sin ella, `map_view` muestra su estado de error.
|
|
91
91
|
*/
|
|
92
92
|
googleMapsApiKey?: string;
|
|
93
|
+
/**
|
|
94
|
+
* SPEC-015 — aviso temporal (5s, sin X) que se muestra automáticamente encima del input, una
|
|
95
|
+
* vez por instancia del chat — al entrar, sea una conversación nueva o con historial (no mira
|
|
96
|
+
* `messages` para nada). El texto y el tipo los pone el host — pixelon sigue sin saber de copy
|
|
97
|
+
* ni de dominio. Sin este campo, no se muestra nada. El nombre quedó de cuando solo aplicaba a
|
|
98
|
+
* chats vacíos; el campo ya no distingue eso.
|
|
99
|
+
*/
|
|
100
|
+
newChatNotice?: {
|
|
101
|
+
message: string;
|
|
102
|
+
type?: 'success' | 'warning' | 'error' | 'info';
|
|
103
|
+
};
|
|
93
104
|
}
|
|
94
105
|
export declare const CHAT_CONFIG_DEFAULTS: ChatConfig;
|
|
95
106
|
export interface ChatConfigProviderProps {
|
|
@@ -48,6 +48,8 @@ export declare const CHAT_EVENT: {
|
|
|
48
48
|
readonly REOPEN_CHAT: "reopenChat";
|
|
49
49
|
readonly CHAT_STATUS: "chatStatus";
|
|
50
50
|
readonly CONVERSATIONS_UPDATED: "conversationsUpdated";
|
|
51
|
+
readonly NOTICE_SHOW: "noticeShow";
|
|
52
|
+
readonly NOTICE_DISMISS: "noticeDismiss";
|
|
51
53
|
};
|
|
52
54
|
export type ChatEvents = {
|
|
53
55
|
/** UI component interaction (select, confirm, etc.) */
|
|
@@ -192,6 +194,21 @@ export type ChatEvents = {
|
|
|
192
194
|
conversationsUpdated: {
|
|
193
195
|
items: unknown[];
|
|
194
196
|
};
|
|
197
|
+
/** SPEC-015 — Consumer/Chat -> Chat: muestra un aviso encima del input. `mode` por omisión
|
|
198
|
+
* 'temporary' (se autocierra en `duration` ms, default 4000). Hay DOS slots independientes, uno
|
|
199
|
+
* por `mode` — un `temporary` reemplaza al `temporary` anterior, un `static` al `static`
|
|
200
|
+
* anterior; no compiten entre sí. */
|
|
201
|
+
noticeShow: {
|
|
202
|
+
id: string;
|
|
203
|
+
type: 'success' | 'warning' | 'error' | 'info';
|
|
204
|
+
message: string;
|
|
205
|
+
mode?: 'temporary' | 'static';
|
|
206
|
+
duration?: number;
|
|
207
|
+
};
|
|
208
|
+
/** SPEC-015 — cierra el aviso con este id, si sigue siendo el que está mostrando ese slot. */
|
|
209
|
+
noticeDismiss: {
|
|
210
|
+
id: string;
|
|
211
|
+
};
|
|
195
212
|
/** ReactiveForm -> Consumer: trace info for observability */
|
|
196
213
|
formTrace: {
|
|
197
214
|
chatId: string;
|
package/dist/index.esm.js
CHANGED
|
@@ -1881,7 +1881,12 @@ const CHAT_EVENT = {
|
|
|
1881
1881
|
// SPEC-029 B1
|
|
1882
1882
|
CHAT_STATUS: "chatStatus",
|
|
1883
1883
|
// SPEC-005
|
|
1884
|
-
CONVERSATIONS_UPDATED: "conversationsUpdated"
|
|
1884
|
+
CONVERSATIONS_UPDATED: "conversationsUpdated",
|
|
1885
|
+
// SPEC-015 (pixelon) — aviso encima del input: éxito/warning/error/info, temporal o fijo
|
|
1886
|
+
// (`static`). Cualquiera puede emitirlo (el propio Chat, para el disclaimer de IA; el host, para
|
|
1887
|
+
// redirigir aquí sus toasts mientras el chat está en pantalla — ver towing-web SPEC-015).
|
|
1888
|
+
NOTICE_SHOW: "noticeShow",
|
|
1889
|
+
NOTICE_DISMISS: "noticeDismiss"
|
|
1885
1890
|
};
|
|
1886
1891
|
const chatEventBus = createEventBus();
|
|
1887
1892
|
|
|
@@ -18913,6 +18918,42 @@ const NewMessageDivider = ({
|
|
|
18913
18918
|
] });
|
|
18914
18919
|
};
|
|
18915
18920
|
|
|
18921
|
+
const DEFAULT_DURATION_MS = 4e3;
|
|
18922
|
+
const NOTICE_ICONS = {
|
|
18923
|
+
success: "CHECK",
|
|
18924
|
+
error: "CLOSE",
|
|
18925
|
+
warning: "BELL",
|
|
18926
|
+
info: "HELP"
|
|
18927
|
+
};
|
|
18928
|
+
const ChatNotice = ({ notice, onClose }) => {
|
|
18929
|
+
const timeoutRef = useRef(null);
|
|
18930
|
+
useEffect(() => {
|
|
18931
|
+
if (notice.mode !== "temporary") return void 0;
|
|
18932
|
+
timeoutRef.current = setTimeout(() => onClose(notice.id), notice.duration ?? DEFAULT_DURATION_MS);
|
|
18933
|
+
return () => {
|
|
18934
|
+
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
18935
|
+
};
|
|
18936
|
+
}, [notice.id, notice.mode, notice.duration]);
|
|
18937
|
+
const cls = [
|
|
18938
|
+
`${COMPONENT_PREFIX$1}chat-notice`,
|
|
18939
|
+
`${COMPONENT_PREFIX$1}chat-notice--${notice.type}`
|
|
18940
|
+
].join(" ");
|
|
18941
|
+
return /* @__PURE__ */ jsxs("div", { className: cls, role: "status", "aria-live": "polite", children: [
|
|
18942
|
+
/* @__PURE__ */ jsx("span", { className: `${COMPONENT_PREFIX$1}chat-notice__icon`, children: /* @__PURE__ */ jsx(Icon, { type: LUMI_ICON_TYPES[NOTICE_ICONS[notice.type]], size: "small" }) }),
|
|
18943
|
+
/* @__PURE__ */ jsx("span", { className: `${COMPONENT_PREFIX$1}chat-notice__message`, children: notice.message }),
|
|
18944
|
+
notice.mode === "static" && /* @__PURE__ */ jsx(
|
|
18945
|
+
"button",
|
|
18946
|
+
{
|
|
18947
|
+
type: "button",
|
|
18948
|
+
className: `${COMPONENT_PREFIX$1}chat-notice__close`,
|
|
18949
|
+
onClick: () => onClose(notice.id),
|
|
18950
|
+
"aria-label": "Cerrar aviso",
|
|
18951
|
+
children: /* @__PURE__ */ jsx(Icon, { type: LUMI_ICON_TYPES.CLOSE, size: "small" })
|
|
18952
|
+
}
|
|
18953
|
+
)
|
|
18954
|
+
] });
|
|
18955
|
+
};
|
|
18956
|
+
|
|
18916
18957
|
const useAppHeight$2 = (enabled = true) => {
|
|
18917
18958
|
useEffect(() => {
|
|
18918
18959
|
if (!enabled) return;
|
|
@@ -19782,6 +19823,8 @@ const Chat = ({
|
|
|
19782
19823
|
const [isClosed, setIsClosed] = useState(false);
|
|
19783
19824
|
const [readOnly, setReadOnly] = useState(false);
|
|
19784
19825
|
const [readOnlyReason, setReadOnlyReason] = useState(null);
|
|
19826
|
+
const [noticiaTemporal, setNoticiaTemporal] = useState(null);
|
|
19827
|
+
const [noticiaFija, setNoticiaFija] = useState(null);
|
|
19785
19828
|
useEffect(() => {
|
|
19786
19829
|
const unsubMessages = eventBus.on(CHAT_EVENT.MESSAGES_UPDATED, ({ messages: newMessages }) => {
|
|
19787
19830
|
setMessages(newMessages);
|
|
@@ -19824,6 +19867,47 @@ const Chat = ({
|
|
|
19824
19867
|
unsubStatus();
|
|
19825
19868
|
};
|
|
19826
19869
|
}, [eventBus]);
|
|
19870
|
+
useEffect(() => {
|
|
19871
|
+
const unsubShow = eventBus.on(CHAT_EVENT.NOTICE_SHOW, (n) => {
|
|
19872
|
+
const notice = {
|
|
19873
|
+
id: n.id,
|
|
19874
|
+
type: n.type,
|
|
19875
|
+
message: n.message,
|
|
19876
|
+
mode: n.mode ?? "temporary",
|
|
19877
|
+
duration: n.duration
|
|
19878
|
+
};
|
|
19879
|
+
if (notice.mode === "static") {
|
|
19880
|
+
setNoticiaFija(notice);
|
|
19881
|
+
} else {
|
|
19882
|
+
setNoticiaTemporal(notice);
|
|
19883
|
+
}
|
|
19884
|
+
});
|
|
19885
|
+
const unsubDismiss = eventBus.on(CHAT_EVENT.NOTICE_DISMISS, ({ id }) => {
|
|
19886
|
+
setNoticiaTemporal((cur) => cur?.id === id ? null : cur);
|
|
19887
|
+
setNoticiaFija((cur) => cur?.id === id ? null : cur);
|
|
19888
|
+
});
|
|
19889
|
+
return () => {
|
|
19890
|
+
unsubShow();
|
|
19891
|
+
unsubDismiss();
|
|
19892
|
+
};
|
|
19893
|
+
}, [eventBus]);
|
|
19894
|
+
const cerrarAviso = (id) => {
|
|
19895
|
+
setNoticiaTemporal((cur) => cur?.id === id ? null : cur);
|
|
19896
|
+
setNoticiaFija((cur) => cur?.id === id ? null : cur);
|
|
19897
|
+
};
|
|
19898
|
+
const avisoNuevoChatMostrado = useRef(false);
|
|
19899
|
+
useEffect(() => {
|
|
19900
|
+
if (avisoNuevoChatMostrado.current) return;
|
|
19901
|
+
if (!resolvedConfig.newChatNotice) return;
|
|
19902
|
+
avisoNuevoChatMostrado.current = true;
|
|
19903
|
+
eventBus.emit(CHAT_EVENT.NOTICE_SHOW, {
|
|
19904
|
+
id: "new-chat-disclaimer",
|
|
19905
|
+
type: resolvedConfig.newChatNotice.type ?? "info",
|
|
19906
|
+
message: resolvedConfig.newChatNotice.message,
|
|
19907
|
+
mode: "temporary",
|
|
19908
|
+
duration: 5e3
|
|
19909
|
+
});
|
|
19910
|
+
}, [eventBus, resolvedConfig.newChatNotice]);
|
|
19827
19911
|
const handleSend = (content, files) => {
|
|
19828
19912
|
eventBus.emit(CHAT_EVENT.SEND_MESSAGE, { content, files });
|
|
19829
19913
|
};
|
|
@@ -19982,6 +20066,19 @@ const Chat = ({
|
|
|
19982
20066
|
className: `${COMPONENT_PREFIX$1}footer`,
|
|
19983
20067
|
onClick: (e) => handleFooterClick(e, COMPONENT_PREFIX$1),
|
|
19984
20068
|
children: [
|
|
20069
|
+
(noticiaFija || noticiaTemporal) && /* @__PURE__ */ jsxs(
|
|
20070
|
+
"div",
|
|
20071
|
+
{
|
|
20072
|
+
className: [
|
|
20073
|
+
`${COMPONENT_PREFIX$1}chat-notice-slot`,
|
|
20074
|
+
!isClosed && !readOnly ? `${COMPONENT_PREFIX$1}chat-notice-slot--anchored` : ""
|
|
20075
|
+
].filter(Boolean).join(" "),
|
|
20076
|
+
children: [
|
|
20077
|
+
noticiaFija && /* @__PURE__ */ jsx(ChatNotice, { notice: noticiaFija, onClose: cerrarAviso }, noticiaFija.id),
|
|
20078
|
+
noticiaTemporal && /* @__PURE__ */ jsx(ChatNotice, { notice: noticiaTemporal, onClose: cerrarAviso }, noticiaTemporal.id)
|
|
20079
|
+
]
|
|
20080
|
+
}
|
|
20081
|
+
),
|
|
19985
20082
|
isClosed ? (
|
|
19986
20083
|
// SPEC-028 T7 — chat cerrado: input off + botón "Continuar chat".
|
|
19987
20084
|
/* @__PURE__ */ jsxs("div", { className: `${COMPONENT_PREFIX$1}closed-banner`, children: [
|
package/dist/index.js
CHANGED
|
@@ -1902,7 +1902,12 @@ const CHAT_EVENT = {
|
|
|
1902
1902
|
// SPEC-029 B1
|
|
1903
1903
|
CHAT_STATUS: "chatStatus",
|
|
1904
1904
|
// SPEC-005
|
|
1905
|
-
CONVERSATIONS_UPDATED: "conversationsUpdated"
|
|
1905
|
+
CONVERSATIONS_UPDATED: "conversationsUpdated",
|
|
1906
|
+
// SPEC-015 (pixelon) — aviso encima del input: éxito/warning/error/info, temporal o fijo
|
|
1907
|
+
// (`static`). Cualquiera puede emitirlo (el propio Chat, para el disclaimer de IA; el host, para
|
|
1908
|
+
// redirigir aquí sus toasts mientras el chat está en pantalla — ver towing-web SPEC-015).
|
|
1909
|
+
NOTICE_SHOW: "noticeShow",
|
|
1910
|
+
NOTICE_DISMISS: "noticeDismiss"
|
|
1906
1911
|
};
|
|
1907
1912
|
const chatEventBus = createEventBus();
|
|
1908
1913
|
|
|
@@ -18934,6 +18939,42 @@ const NewMessageDivider = ({
|
|
|
18934
18939
|
] });
|
|
18935
18940
|
};
|
|
18936
18941
|
|
|
18942
|
+
const DEFAULT_DURATION_MS = 4e3;
|
|
18943
|
+
const NOTICE_ICONS = {
|
|
18944
|
+
success: "CHECK",
|
|
18945
|
+
error: "CLOSE",
|
|
18946
|
+
warning: "BELL",
|
|
18947
|
+
info: "HELP"
|
|
18948
|
+
};
|
|
18949
|
+
const ChatNotice = ({ notice, onClose }) => {
|
|
18950
|
+
const timeoutRef = React.useRef(null);
|
|
18951
|
+
React.useEffect(() => {
|
|
18952
|
+
if (notice.mode !== "temporary") return void 0;
|
|
18953
|
+
timeoutRef.current = setTimeout(() => onClose(notice.id), notice.duration ?? DEFAULT_DURATION_MS);
|
|
18954
|
+
return () => {
|
|
18955
|
+
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
18956
|
+
};
|
|
18957
|
+
}, [notice.id, notice.mode, notice.duration]);
|
|
18958
|
+
const cls = [
|
|
18959
|
+
`${COMPONENT_PREFIX$1}chat-notice`,
|
|
18960
|
+
`${COMPONENT_PREFIX$1}chat-notice--${notice.type}`
|
|
18961
|
+
].join(" ");
|
|
18962
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cls, role: "status", "aria-live": "polite", children: [
|
|
18963
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: `${COMPONENT_PREFIX$1}chat-notice__icon`, children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { type: LUMI_ICON_TYPES[NOTICE_ICONS[notice.type]], size: "small" }) }),
|
|
18964
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: `${COMPONENT_PREFIX$1}chat-notice__message`, children: notice.message }),
|
|
18965
|
+
notice.mode === "static" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
18966
|
+
"button",
|
|
18967
|
+
{
|
|
18968
|
+
type: "button",
|
|
18969
|
+
className: `${COMPONENT_PREFIX$1}chat-notice__close`,
|
|
18970
|
+
onClick: () => onClose(notice.id),
|
|
18971
|
+
"aria-label": "Cerrar aviso",
|
|
18972
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { type: LUMI_ICON_TYPES.CLOSE, size: "small" })
|
|
18973
|
+
}
|
|
18974
|
+
)
|
|
18975
|
+
] });
|
|
18976
|
+
};
|
|
18977
|
+
|
|
18937
18978
|
const useAppHeight$2 = (enabled = true) => {
|
|
18938
18979
|
React.useEffect(() => {
|
|
18939
18980
|
if (!enabled) return;
|
|
@@ -19803,6 +19844,8 @@ const Chat = ({
|
|
|
19803
19844
|
const [isClosed, setIsClosed] = React.useState(false);
|
|
19804
19845
|
const [readOnly, setReadOnly] = React.useState(false);
|
|
19805
19846
|
const [readOnlyReason, setReadOnlyReason] = React.useState(null);
|
|
19847
|
+
const [noticiaTemporal, setNoticiaTemporal] = React.useState(null);
|
|
19848
|
+
const [noticiaFija, setNoticiaFija] = React.useState(null);
|
|
19806
19849
|
React.useEffect(() => {
|
|
19807
19850
|
const unsubMessages = eventBus.on(CHAT_EVENT.MESSAGES_UPDATED, ({ messages: newMessages }) => {
|
|
19808
19851
|
setMessages(newMessages);
|
|
@@ -19845,6 +19888,47 @@ const Chat = ({
|
|
|
19845
19888
|
unsubStatus();
|
|
19846
19889
|
};
|
|
19847
19890
|
}, [eventBus]);
|
|
19891
|
+
React.useEffect(() => {
|
|
19892
|
+
const unsubShow = eventBus.on(CHAT_EVENT.NOTICE_SHOW, (n) => {
|
|
19893
|
+
const notice = {
|
|
19894
|
+
id: n.id,
|
|
19895
|
+
type: n.type,
|
|
19896
|
+
message: n.message,
|
|
19897
|
+
mode: n.mode ?? "temporary",
|
|
19898
|
+
duration: n.duration
|
|
19899
|
+
};
|
|
19900
|
+
if (notice.mode === "static") {
|
|
19901
|
+
setNoticiaFija(notice);
|
|
19902
|
+
} else {
|
|
19903
|
+
setNoticiaTemporal(notice);
|
|
19904
|
+
}
|
|
19905
|
+
});
|
|
19906
|
+
const unsubDismiss = eventBus.on(CHAT_EVENT.NOTICE_DISMISS, ({ id }) => {
|
|
19907
|
+
setNoticiaTemporal((cur) => cur?.id === id ? null : cur);
|
|
19908
|
+
setNoticiaFija((cur) => cur?.id === id ? null : cur);
|
|
19909
|
+
});
|
|
19910
|
+
return () => {
|
|
19911
|
+
unsubShow();
|
|
19912
|
+
unsubDismiss();
|
|
19913
|
+
};
|
|
19914
|
+
}, [eventBus]);
|
|
19915
|
+
const cerrarAviso = (id) => {
|
|
19916
|
+
setNoticiaTemporal((cur) => cur?.id === id ? null : cur);
|
|
19917
|
+
setNoticiaFija((cur) => cur?.id === id ? null : cur);
|
|
19918
|
+
};
|
|
19919
|
+
const avisoNuevoChatMostrado = React.useRef(false);
|
|
19920
|
+
React.useEffect(() => {
|
|
19921
|
+
if (avisoNuevoChatMostrado.current) return;
|
|
19922
|
+
if (!resolvedConfig.newChatNotice) return;
|
|
19923
|
+
avisoNuevoChatMostrado.current = true;
|
|
19924
|
+
eventBus.emit(CHAT_EVENT.NOTICE_SHOW, {
|
|
19925
|
+
id: "new-chat-disclaimer",
|
|
19926
|
+
type: resolvedConfig.newChatNotice.type ?? "info",
|
|
19927
|
+
message: resolvedConfig.newChatNotice.message,
|
|
19928
|
+
mode: "temporary",
|
|
19929
|
+
duration: 5e3
|
|
19930
|
+
});
|
|
19931
|
+
}, [eventBus, resolvedConfig.newChatNotice]);
|
|
19848
19932
|
const handleSend = (content, files) => {
|
|
19849
19933
|
eventBus.emit(CHAT_EVENT.SEND_MESSAGE, { content, files });
|
|
19850
19934
|
};
|
|
@@ -20003,6 +20087,19 @@ const Chat = ({
|
|
|
20003
20087
|
className: `${COMPONENT_PREFIX$1}footer`,
|
|
20004
20088
|
onClick: (e) => handleFooterClick(e, COMPONENT_PREFIX$1),
|
|
20005
20089
|
children: [
|
|
20090
|
+
(noticiaFija || noticiaTemporal) && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
20091
|
+
"div",
|
|
20092
|
+
{
|
|
20093
|
+
className: [
|
|
20094
|
+
`${COMPONENT_PREFIX$1}chat-notice-slot`,
|
|
20095
|
+
!isClosed && !readOnly ? `${COMPONENT_PREFIX$1}chat-notice-slot--anchored` : ""
|
|
20096
|
+
].filter(Boolean).join(" "),
|
|
20097
|
+
children: [
|
|
20098
|
+
noticiaFija && /* @__PURE__ */ jsxRuntime.jsx(ChatNotice, { notice: noticiaFija, onClose: cerrarAviso }, noticiaFija.id),
|
|
20099
|
+
noticiaTemporal && /* @__PURE__ */ jsxRuntime.jsx(ChatNotice, { notice: noticiaTemporal, onClose: cerrarAviso }, noticiaTemporal.id)
|
|
20100
|
+
]
|
|
20101
|
+
}
|
|
20102
|
+
),
|
|
20006
20103
|
isClosed ? (
|
|
20007
20104
|
// SPEC-028 T7 — chat cerrado: input off + botón "Continuar chat".
|
|
20008
20105
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${COMPONENT_PREFIX$1}closed-banner`, children: [
|