Skip to content

Commit 0584fcb

Browse files
committed
feat: Create loading overlay on inputs while generating forced prompt
1 parent 31874b9 commit 0584fcb

File tree

10 files changed

+335
-209
lines changed

10 files changed

+335
-209
lines changed

_build/js/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ export const window = {};
55
export { chatHistory } from './chatHistory'
66
export { history } from './history'
77
export { executor } from './executor'
8-
export { ui } from './ui';
8+
export { ui } from './ui'
9+

_build/js/src/resource.ts

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {executor, ImageData} from "./executor";
22
import {DataOutput, history} from './history';
33
import {ui} from "./ui";
4+
import {createLoadingOverlay} from "./ui/overlay";
45

56
type DataContext = {els: { field: any, wrapper: HistoryElement }[]};
67
type HistoryButton = HTMLButtonElement & {
@@ -137,8 +138,7 @@ const createForcedTextPrompt = (field: any, fieldName: string) => {
137138

138139
const wandEl = createWandEl();
139140
wandEl.addEventListener('click', async () => {
140-
//@ts-expect-error Ext
141-
Ext.Msg.wait(_('modai.cmp.generate_ing'), _('modai.cmp.please_wait'));
141+
const done = createLoadingOverlay(field.el.dom);
142142

143143
try {
144144
const result = await executor.mgr.prompt.text({
@@ -147,11 +147,9 @@ const createForcedTextPrompt = (field: any, fieldName: string) => {
147147
field: fieldName
148148
});
149149
cache.insert(result.content);
150-
//@ts-expect-error Ext
151-
Ext.Msg.hide();
150+
done();
152151
} catch (err) {
153-
//@ts-expect-error Ext
154-
Ext.Msg.hide();
152+
done();
155153
//@ts-expect-error Ext
156154
Ext.Msg.alert("Failed", _('modai.cmp.failed_try_again', {"msg": err.message}));
157155
}
@@ -162,7 +160,8 @@ const createForcedTextPrompt = (field: any, fieldName: string) => {
162160
const cache = history.init(
163161
fieldName,
164162
historyNavSync,
165-
field.getValue()
163+
field.getValue(),
164+
{} as DataContext
166165
);
167166

168167
if (!cache.cachedItem.context.els) {
@@ -216,8 +215,7 @@ const attachField = (cmp: string, fieldName: string) => {
216215

217216
const wandEl = createWandEl();
218217
wandEl.addEventListener('click', async () => {
219-
// @ts-expect-error Ext
220-
Ext.Msg.wait(_('modai.cmp.generate_ing'), _('modai.cmp.please_wait'));
218+
const done = createLoadingOverlay(field.el.dom);
221219

222220
try {
223221
const result = await executor.mgr.prompt.text({
@@ -228,11 +226,9 @@ const attachField = (cmp: string, fieldName: string) => {
228226
cache.insert(data.content, true);
229227
});
230228
cache.insert(result.content);
231-
// @ts-expect-error Ext
232-
Ext.Msg.hide();
229+
done();
233230
} catch (err) {
234-
// @ts-expect-error Ext
235-
Ext.Msg.hide();
231+
done();
236232
// @ts-expect-error Ext
237233
Ext.Msg.alert("Failed", _('modai.cmp.failed_try_again', {"msg": err.message}));
238234
}
@@ -292,8 +288,7 @@ const attachImagePlus = (imgPlusPanel: Element, fieldName: string) => {
292288

293289
const base64Data = canvas.toDataURL('image/png');
294290

295-
// @ts-expect-error Ext
296-
Ext.Msg.wait(_('modai.cmp.generate_ing'), _('modai.cmp.please_wait'));
291+
const done = createLoadingOverlay(imagePlus.altTextField.items.items[0].el.dom);
297292

298293
try {
299294
const result = await executor.mgr.prompt.vision({
@@ -308,11 +303,9 @@ const attachImagePlus = (imgPlusPanel: Element, fieldName: string) => {
308303
imagePlus.altTextField.items.items[0].setValue(result.content);
309304
imagePlus.image.altTag = result.content;
310305
imagePlus.updateValue();
311-
// @ts-expect-error Ext
312-
Ext.Msg.hide();
306+
done();
313307
} catch (err) {
314-
// @ts-expect-error Ext
315-
Ext.Msg.hide();
308+
done();
316309
// @ts-expect-error Ext
317310
Ext.Msg.alert("Failed", _('modai.cmp.failed_try_again', {"msg": err.message}));
318311
}
@@ -357,7 +350,7 @@ const attachTVs = () => {
357350

358351
if (field.xtype === 'textfield' || field.xtype === 'textarea') {
359352
// @ts-expect-error MODx
360-
const prompt = MODx.config[`modai.tv.${tvName}.prompt`];
353+
const prompt = MODx.config[`modai.tv.${tvName}.text.prompt`];
361354

362355
const label = wrapper.dom.querySelector('label');
363356
if (!label) return;

_build/js/src/ui/iframe.ts

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import {createElement, nlToBr} from "./utils";
2+
import type { Modal } from "./modal";
3+
4+
export type Iframe = HTMLIFrameElement & {
5+
syncHeight: () => void;
6+
}
7+
8+
export const createContentIframe = (providedContent: string, modal: Modal, customCSS: string[] = []) => {
9+
const content = nlToBr(providedContent);
10+
11+
const iframe = createElement('iframe', {
12+
width: '100%',
13+
border: 'none',
14+
backgroundColor: 'white',
15+
pointerEvents: 'none',
16+
maxWidth: '100%',
17+
boxSizing: 'border-box'
18+
}) as Iframe;
19+
20+
iframe.srcdoc = `
21+
<html>
22+
<head>
23+
<style>
24+
html {
25+
width: 100%;
26+
max-width: 100%;
27+
padding: 0;
28+
margin: 0;
29+
}
30+
body {
31+
margin: 0;
32+
padding: 0;
33+
font-family: Arial, sans-serif;
34+
overflow-x: hidden;
35+
width: 100%;
36+
max-width: 100%;
37+
box-sizing: border-box;
38+
}
39+
* {
40+
box-sizing: border-box;
41+
max-width: 100%;
42+
}
43+
pre {
44+
white-space: pre-wrap;
45+
word-wrap: break-word;
46+
overflow-wrap: break-word;
47+
max-width: 100%;
48+
padding: 12px;
49+
margin: 0;
50+
background-color: #f5f7fa;
51+
border-radius: 5px;
52+
font-size: 14px;
53+
}
54+
img {
55+
max-width: 100%;
56+
height: auto;
57+
display: block;
58+
}
59+
code {
60+
white-space: pre-wrap;
61+
word-wrap: break-word;
62+
max-width: 100%;
63+
display: block;
64+
overflow-x: hidden;
65+
font-size: 14px;
66+
}
67+
table {
68+
width: 100%;
69+
max-width: 100%;
70+
overflow-x: hidden;
71+
display: block;
72+
border-collapse: collapse;
73+
}
74+
div {
75+
max-width: 100%;
76+
overflow-wrap: break-word;
77+
word-wrap: break-word;
78+
box-sizing: border-box;
79+
}
80+
p {
81+
margin: 0 0 0.5em 0;
82+
max-width: 100%;
83+
}
84+
h1, h2, h3, h4, h5, h6 {
85+
margin: 0 0 0.1em 0;
86+
max-width: 100%;
87+
}
88+
</style>
89+
${customCSS.map((css) => {
90+
return `<link rel="stylesheet" type="text/css" href="${css}" />`;
91+
}).join('')}
92+
</head>
93+
<body style="padding: 0; max-width: 100%; width: 100%; box-sizing: border-box;">${content}</body>
94+
</html>
95+
`;
96+
97+
iframe.syncHeight = () => {
98+
const body = iframe.contentWindow?.document.body;
99+
if (!body) {
100+
return;
101+
}
102+
103+
const elements = body.getElementsByTagName('*');
104+
let maxHeight = body.offsetHeight;
105+
106+
for (let i = 0; i < elements.length; i++) {
107+
const element = elements[i] as HTMLElement;
108+
const bottom = element.offsetTop + element.offsetHeight;
109+
maxHeight = Math.max(maxHeight, bottom);
110+
}
111+
112+
iframe.style.height = (maxHeight + 5) + 'px';
113+
body.style.overflow = 'hidden';
114+
115+
modal.chatMessages.scrollTop = modal.chatMessages.scrollHeight;
116+
}
117+
118+
iframe.onload = iframe.syncHeight;
119+
120+
return iframe;
121+
}

_build/js/src/ui/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import {createModal, type ModalConfig} from "./modal";
2+
import { createLoadingOverlay } from './overlay'
3+
4+
5+
export const ui = {
6+
createLoadingOverlay,
7+
freePrompt: (config: ModalConfig) => {
8+
return createModal(config);
9+
}
10+
};

0 commit comments

Comments
 (0)