Skip to content

Commit f27ac52

Browse files
committed
feat: add clear chat button
1 parent 1408c26 commit f27ac52

File tree

12 files changed

+273
-151
lines changed

12 files changed

+273
-151
lines changed

_build/js/src/chatHistory.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export type ChatHistory = {
8787
getAssistantMessage: (id: string) => Message | undefined;
8888
getMessages: () => Message[];
8989
getMessagesHistory: () => Pick<Message, 'role' | 'content'>[];
90+
clearHistory: () => void;
9091
};
9192

9293
export const chatHistory = {
@@ -128,6 +129,9 @@ export const chatHistory = {
128129
content: m.content,
129130
}));
130131
},
132+
clearHistory: () => {
133+
_namespace[key].history = [];
134+
},
131135
};
132136
},
133137
};

_build/js/src/ui/dom/button.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { applyStyles, createElement } from '../utils';
2+
3+
export type Button = HTMLButtonElement & {
4+
enable: () => void;
5+
disable: () => void;
6+
};
7+
8+
export const button = (
9+
content: string | HTMLElement | (HTMLElement | string)[],
10+
onClick: () => Promise<void> | void,
11+
styleObj: Partial<CSSStyleDeclaration> = {},
12+
disabledStyleObj: Partial<CSSStyleDeclaration> = {},
13+
btnProps?: Partial<HTMLButtonElement>,
14+
) => {
15+
const textContent = typeof content === 'string' ? content : '';
16+
17+
if (typeof content !== 'string' && !Array.isArray(content)) {
18+
content = [content];
19+
}
20+
21+
const el = createElement('button', styleObj, textContent) as Button;
22+
el.type = 'button';
23+
24+
if (Array.isArray(content)) {
25+
content.forEach((i) => {
26+
if (typeof i === 'string') {
27+
el.append(document.createTextNode(i));
28+
} else {
29+
el.append(i);
30+
}
31+
});
32+
}
33+
34+
el.addEventListener('click', onClick);
35+
36+
el.enable = () => {
37+
el.disabled = false;
38+
applyStyles(el, styleObj);
39+
};
40+
41+
el.disable = () => {
42+
el.disabled = true;
43+
applyStyles(el, { ...styleObj, ...disabledStyleObj });
44+
};
45+
46+
if (btnProps) {
47+
Object.assign(el, btnProps);
48+
}
49+
50+
return el;
51+
};

_build/js/src/ui/globalStyles.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export const globalStyles = {
2+
resetStyles: {
3+
margin: '0',
4+
padding: '0',
5+
boxSizing: 'border-box',
6+
fontFamily: 'Arial, sans-serif',
7+
},
8+
} satisfies Record<string, Partial<CSSStyleDeclaration>>;
Lines changed: 83 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { applyStyles, createElement } from '../utils';
1+
import { button } from '../dom/button';
2+
import { createElement } from '../utils';
23

34
import type { Modal } from './types';
45
import type { Message } from '../../chatHistory';
@@ -15,11 +16,6 @@ type ActionButtonConfig = {
1516
disabled?: boolean;
1617
};
1718

18-
export type ActionButton = HTMLButtonElement & {
19-
disable?: () => void;
20-
enable?: () => void;
21-
};
22-
2319
const defaultConfig: Partial<ActionButtonConfig> = {
2420
loadingText: 'Loading...',
2521
completedText: 'Completed!',
@@ -67,107 +63,103 @@ export const createActionButton = (config: ActionButtonConfig) => {
6763
...config,
6864
};
6965

70-
const button = createElement('button', styles.actionButton) as ActionButton;
71-
button.className = 'action-button';
72-
button.enable = () => {
73-
button.disabled = false;
74-
applyStyles(button, styles.actionButton);
75-
};
76-
button.disable = () => {
77-
button.disabled = true;
78-
applyStyles(button, { ...styles.actionButton, ...styles.disabledButton });
79-
};
80-
81-
if (config.disabled) {
82-
button.disable();
83-
}
84-
85-
const copyIcon = createElement('span', {
86-
...styles.icon,
87-
backgroundImage: `url("${icons[config.icon]}")`,
88-
});
89-
button.append(copyIcon);
90-
91-
button.append(document.createTextNode(config.label));
92-
button.addEventListener('click', async () => {
93-
const originalHTML = button.innerHTML;
66+
const onClick = async () => {
67+
const originalHTML = btn.innerHTML;
9468
const result = config.onClick(config.message, config.modal);
9569

9670
if (result instanceof Promise) {
97-
button.innerHTML = `
98-
<span style="
99-
display: inline-block;
100-
margin-right: 5px;
101-
width: 12px;
102-
height: 12px;
103-
position: relative;
104-
animation: spin 1s linear infinite;
105-
">
106-
<span style="
107-
position: absolute;
108-
width: 3px;
109-
height: 3px;
110-
background-color: currentColor;
111-
border-radius: 50%;
112-
top: 0;
113-
left: 50%;
114-
transform: translate(-50%, 0);
115-
"></span>
116-
<span style="
117-
position: absolute;
118-
width: 3px;
119-
height: 3px;
120-
background-color: currentColor;
121-
border-radius: 50%;
122-
top: 50%;
123-
right: 0;
124-
transform: translate(0, -50%);
125-
"></span>
126-
<span style="
127-
position: absolute;
128-
width: 3px;
129-
height: 3px;
130-
background-color: currentColor;
131-
border-radius: 50%;
132-
bottom: 0;
133-
left: 50%;
134-
transform: translate(-50%, 0);
135-
"></span>
136-
<span style="
137-
position: absolute;
138-
width: 3px;
139-
height: 3px;
140-
background-color: currentColor;
141-
border-radius: 50%;
142-
top: 50%;
143-
left: 0;
144-
transform: translate(0, -50%);
145-
"></span>
146-
</span>
147-
${config.loadingText}
148-
`;
71+
btn.innerHTML = `
72+
<span style="
73+
display: inline-block;
74+
margin-right: 5px;
75+
width: 12px;
76+
height: 12px;
77+
position: relative;
78+
animation: spin 1s linear infinite;
79+
">
80+
<span style="
81+
position: absolute;
82+
width: 3px;
83+
height: 3px;
84+
background-color: currentColor;
85+
border-radius: 50%;
86+
top: 0;
87+
left: 50%;
88+
transform: translate(-50%, 0);
89+
"></span>
90+
<span style="
91+
position: absolute;
92+
width: 3px;
93+
height: 3px;
94+
background-color: currentColor;
95+
border-radius: 50%;
96+
top: 50%;
97+
right: 0;
98+
transform: translate(0, -50%);
99+
"></span>
100+
<span style="
101+
position: absolute;
102+
width: 3px;
103+
height: 3px;
104+
background-color: currentColor;
105+
border-radius: 50%;
106+
bottom: 0;
107+
left: 50%;
108+
transform: translate(-50%, 0);
109+
"></span>
110+
<span style="
111+
position: absolute;
112+
width: 3px;
113+
height: 3px;
114+
background-color: currentColor;
115+
border-radius: 50%;
116+
top: 50%;
117+
left: 0;
118+
transform: translate(0, -50%);
119+
"></span>
120+
</span>
121+
${config.loadingText}
122+
`;
149123

150124
const style = document.createElement('style');
151125
style.textContent = `
152-
@keyframes spin {
153-
from { transform: rotate(0deg); }
154-
to { transform: rotate(360deg); }
155-
}
156-
`;
126+
@keyframes spin {
127+
from { transform: rotate(0deg); }
128+
to { transform: rotate(360deg); }
129+
}
130+
`;
157131
document.head.appendChild(style);
158132

159133
await result;
160134

161135
document.head.removeChild(style);
162136
}
163137

164-
button.innerHTML = `
138+
btn.innerHTML = `
165139
<span style="margin-right: 5px;">✓</span>
166140
${config.completedText}
167141
`;
168142
await new Promise((resolve) => setTimeout(resolve, 2000));
169-
button.innerHTML = originalHTML;
170-
});
143+
btn.innerHTML = originalHTML;
144+
};
145+
146+
const btn = button(
147+
[
148+
createElement('span', {
149+
...styles.icon,
150+
backgroundImage: `url("${icons[config.icon]}")`,
151+
}),
152+
config.label,
153+
],
154+
onClick,
155+
styles.actionButton,
156+
styles.disabledButton,
157+
);
158+
btn.className = 'action-button';
159+
160+
if (config.disabled) {
161+
btn.disable();
162+
}
171163

172-
return button;
164+
return btn;
173165
};

_build/js/src/ui/localChat/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export const createModal = (config: ModalConfig) => {
2525
}
2626

2727
if (!config.availableTypes) {
28-
config.availableTypes = [];
28+
config.availableTypes = [config.type];
2929
}
3030

3131
config.availableTypes = config.availableTypes.filter((type) => modalTypes.includes(type));

_build/js/src/ui/localChat/modalActions.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,5 +180,21 @@ export const switchType = (type: ModalType, modal: Modal, config: ModalConfig) =
180180
messages.forEach((msg) => {
181181
renderMessage(msg, modal, config);
182182
});
183+
modal.tryAgainBtn.enable();
184+
modal.clearChatBtn.enable();
185+
} else {
186+
modal.tryAgainBtn.disable();
187+
modal.clearChatBtn.disable();
183188
}
184189
};
190+
191+
export const clearChat = (modal: Modal) => {
192+
while (modal.chatMessages.firstChild) {
193+
modal.chatMessages.removeChild(modal.chatMessages.firstChild);
194+
}
195+
modal.chatMessages.style.display = 'none';
196+
197+
modal.history.clearHistory();
198+
modal.tryAgainBtn.disable();
199+
modal.clearChatBtn.disable();
200+
};

0 commit comments

Comments
 (0)