Skip to content

Commit 5a0a2fb

Browse files
author
Jicheng Lu
committed
add code script component
1 parent b450472 commit 5a0a2fb

13 files changed

Lines changed: 225 additions & 52 deletions

File tree

package-lock.json

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"type": "module",
3939
"dependencies": {
4040
"@codemirror/commands": "^6.10.0",
41+
"@codemirror/lang-javascript": "^6.2.4",
4142
"@codemirror/lang-python": "^6.2.1",
4243
"@codemirror/language": "^6.11.3",
4344
"@codemirror/state": "^6.5.2",

src/lib/common/CollapsibleCard.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script>
2-
import { createEventDispatcher } from 'svelte'
2+
import { createEventDispatcher } from 'svelte';
33
import collapse from 'svelte-collapse';
44
55
/** @type {boolean} */
@@ -11,7 +11,7 @@
1111
/** @type {string} */
1212
export let easing = 'ease';
1313
14-
const dispatch = createEventDispatcher()
14+
const dispatch = createEventDispatcher();
1515
1616
function handleToggle () {
1717
open = !open;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<script>
2+
import { Modal, ModalBody, ModalHeader } from "@sveltestrap/sveltestrap";
3+
4+
/** @type {boolean} */
5+
export let isOpen;
6+
7+
/** @type {string} */
8+
export let size = 'lg';
9+
10+
/** @type {string | any} */
11+
export let title;
12+
13+
/** @type {string} */
14+
export let containerClasses = '';
15+
16+
/** @type {string} */
17+
export let containerStyles = '';
18+
19+
/** @type {() => void} */
20+
export let toggleModal;
21+
</script>
22+
23+
<Modal
24+
class={containerClasses}
25+
style={containerStyles}
26+
fade
27+
size={size}
28+
isOpen={isOpen}
29+
header={title}
30+
toggle={() => toggleModal()}
31+
unmountOnClose
32+
>
33+
<ModalBody>
34+
<slot />
35+
</ModalBody>
36+
</Modal>
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<script>
2+
import { onMount, createEventDispatcher } from "svelte";
3+
import CodeMirror from "svelte-codemirror-editor";
4+
import { keymap } from "@codemirror/view";
5+
import { indentUnit, indentOnInput, indentService } from "@codemirror/language";
6+
import { defaultKeymap, history, indentWithTab, historyKeymap } from "@codemirror/commands";
7+
import { EditorState } from "@codemirror/state";
8+
import { python } from "@codemirror/lang-python";
9+
import { javascript } from "@codemirror/lang-javascript";
10+
import { oneDark } from "@codemirror/theme-one-dark";
11+
12+
const dispatch = createEventDispatcher();
13+
14+
/** @type {string} */
15+
export let language = 'python';
16+
17+
/** @type {string} */
18+
export let scriptText;
19+
20+
/** @type {string} */
21+
export let containerClasses = '';
22+
23+
24+
/** @type {import("@codemirror/state").Extension[]} */
25+
const baseExtensions = [
26+
indentUnit.of(" "),
27+
EditorState.tabSize.of(4),
28+
indentOnInput(),
29+
history(),
30+
keymap.of([...defaultKeymap, ...historyKeymap, indentWithTab])
31+
];
32+
33+
/** @type {import("@codemirror/state").Extension[]} */
34+
let extensions = [];
35+
36+
onMount(() => {
37+
if (language === 'python') {
38+
extensions = [
39+
python(),
40+
indentService.of((context, pos) => {
41+
const prevLine = pos > 0 ? context.state.doc.lineAt(pos - 1) : null;
42+
if (prevLine) {
43+
const prevText = prevLine.text;
44+
const match = prevText.match(/^(\s*)/);
45+
const baseIndent = match ? match[1].length : 0;
46+
47+
// Check if previous line ends with : (control structure)
48+
if (prevText.trimEnd().endsWith(':')) {
49+
return baseIndent + 4;
50+
}
51+
return baseIndent;
52+
}
53+
return 0;
54+
}),
55+
...baseExtensions
56+
];
57+
} else if (language === 'javascript') {
58+
extensions = [
59+
javascript(),
60+
...baseExtensions
61+
];
62+
}
63+
});
64+
65+
/** @param {any} e */
66+
function handleChange(e) {
67+
dispatch('change', {
68+
text: e.detail
69+
});
70+
}
71+
</script>
72+
73+
74+
<CodeMirror
75+
class={`code-script-container ${containerClasses}`}
76+
theme={oneDark}
77+
lineWrapping
78+
extensions={extensions}
79+
value={scriptText}
80+
on:change={e => handleChange(e)}
81+
/>

src/lib/scss/app.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ File: Main Css File
4646
@import "custom/components/select";
4747
@import "custom/components/markdown";
4848
@import "custom/components/state";
49+
@import "custom/components/codeScript";
4950

5051
// Plugins
5152
@import "custom/plugins/custom-scrollbar";
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.code-script-container {
2+
min-height: 300px;
3+
max-height: 500px;
4+
overflow: auto;
5+
scrollbar-width: thin;
6+
}

src/routes/chat/[agentId]/[conversationId]/chat-box.svelte

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,19 @@
4747
import { BOT_SENDERS, LEARNER_AGENT_ID, TRAINING_MODE, ADMIN_ROLES, IMAGE_DATA_PREFIX } from '$lib/helpers/constants';
4848
import { signalr } from '$lib/services/signalr-service.js';
4949
import { newConversation } from '$lib/services/conversation-service';
50-
import DialogModal from '$lib/common/DialogModal.svelte';
50+
import GlobalHeader from '$lib/common/shared/GlobalHeader.svelte';
5151
import HeadTitle from '$lib/common/HeadTitle.svelte';
5252
import LoadingDots from '$lib/common/LoadingDots.svelte';
53-
import StateModal from '$lib/common/StateModal.svelte';
53+
import DialogModal from '$lib/common/modals/DialogModal.svelte';
54+
import StateModal from '$lib/common/modals/StateModal.svelte';
55+
import PlainModal from '$lib/common/modals/PlainModal.svelte';
5456
import LoadingToComplete from '$lib/common/LoadingToComplete.svelte';
5557
import AudioSpeaker from '$lib/common/audio-player/AudioSpeaker.svelte';
58+
import CodeScript from '$lib/common/shared/CodeScript.svelte';
59+
import { realtimeChat } from '$lib/services/realtime-chat-service';
60+
import { webSpeech } from '$lib/services/web-speech';
61+
import LocalStorageManager from '$lib/helpers/utils/storage-manager';
62+
import { delay } from '$lib/helpers/utils/common';
5663
import { AgentExtensions } from '$lib/helpers/utils/agent';
5764
import { utcToLocal } from '$lib/helpers/datetime';
5865
import { replaceNewLine } from '$lib/helpers/http';
@@ -69,13 +76,8 @@
6976
import ChatBigMessage from './chat-util/chat-big-message.svelte';
7077
import PersistLog from './persist-log/persist-log.svelte';
7178
import InstantLog from './instant-log/instant-log.svelte';
72-
import LocalStorageManager from '$lib/helpers/utils/storage-manager';
73-
import { realtimeChat } from '$lib/services/realtime-chat-service';
74-
import { webSpeech } from '$lib/services/web-speech';
75-
import { delay } from '$lib/helpers/utils/common';
76-
import GlobalHeader from '$lib/common/shared/GlobalHeader.svelte';
77-
7879
80+
7981
const options = {
8082
scrollbars: {
8183
visibility: 'auto',
@@ -117,6 +119,8 @@
117119
let notificationText = '';
118120
let successText = "Done";
119121
let errorText = "Error";
122+
let codeScript = '';
123+
let codeLanguage = 'python';
120124
121125
/** @type {number} */
122126
let messageInputTimeout;
@@ -192,6 +196,7 @@
192196
let isOpenEditBotMsgModal = false;
193197
let isOpenUserAddStateModal = false;
194198
let isOpenTagModal = false;
199+
let isOpenCodeScriptModal = false;
195200
let isSendingMsg = false;
196201
let isThinking = false;
197202
let isListening = false;
@@ -1353,6 +1358,26 @@
13531358
});
13541359
}
13551360
1361+
/**
1362+
* @param {any} e
1363+
* @param {any} message
1364+
*/
1365+
function openCodeScriptModal(e, message) {
1366+
e.preventDefault();
1367+
1368+
codeScript = message?.rich_content?.message?.code_script || '';
1369+
codeLanguage = message?.rich_content?.message?.language || 'python';
1370+
isOpenCodeScriptModal = true;
1371+
}
1372+
1373+
function toggleCodeScriptModal() {
1374+
isOpenCodeScriptModal = !isOpenCodeScriptModal;
1375+
if (!isOpenCodeScriptModal) {
1376+
codeScript = '';
1377+
codeLanguage = '';
1378+
}
1379+
}
1380+
13561381
function toggleNotificationModal() {
13571382
isDisplayNotification = !isDisplayNotification;
13581383
if (!isDisplayNotification) {
@@ -1629,6 +1654,17 @@
16291654
</div>
16301655
</DialogModal>
16311656
1657+
<PlainModal
1658+
title={'Code script'}
1659+
isOpen={isOpenCodeScriptModal}
1660+
toggleModal={() => toggleCodeScriptModal()}
1661+
>
1662+
<CodeScript
1663+
language={codeLanguage || 'python'}
1664+
scriptText={codeScript}
1665+
/>
1666+
</PlainModal>
1667+
16321668
<StateModal
16331669
isOpen={isOpenUserAddStateModal}
16341670
bind:states={userAddStates}
@@ -1936,6 +1972,22 @@
19361972
{/if}
19371973
</div>
19381974
</div>
1975+
{#if message?.rich_content?.message?.rich_type === RichType.ProgramCode}
1976+
<div style="font-size: 17px;">
1977+
<!-- svelte-ignore a11y-click-events-have-key-events -->
1978+
<!-- svelte-ignore a11y-no-static-element-interactions -->
1979+
<div
1980+
class="line-align-center text-primary"
1981+
style="height: 85%;"
1982+
data-bs-toggle="tooltip"
1983+
data-bs-placement="top"
1984+
title="Code script"
1985+
on:click={e => openCodeScriptModal(e, message)}
1986+
>
1987+
<i class="bx bx-terminal clickable" />
1988+
</div>
1989+
</div>
1990+
{/if}
19391991
</div>
19401992
{/if}
19411993
{#if !!message.is_chat_message || !!message.has_message_files || message?.data?.startsWith(IMAGE_DATA_PREFIX)}

0 commit comments

Comments
 (0)