diff --git a/install_prerequisite.sh b/install_prerequisite.sh index 2ae3e5c8..c13f6256 100755 --- a/install_prerequisite.sh +++ b/install_prerequisite.sh @@ -137,7 +137,20 @@ else rm -rf evcxr-v0.17.0-x86_64-unknown-linux-gnu fi - +#install rappel, nasm and set all the required dependencies +if [ -e "/usr/local/bin/rappel" ]; then + echo "File /usr/local/bin/rappel exists." +else + echo "File /usr/local/bin/rappel does not exist. installing..." + apt-get install -y --no-install-recommends libedit-dev + apt-get install -y --no-install-recommends nasm + git clone https://github.com/yrp604/rappel.git + cd rappel + make + ln -s $GOTTY_DIR/rappel/bin/rappel /usr/local/bin/rappel + chmod 755 /usr/local/bin/rappel + cd .. +fi #install gointerpreter git clone https://github.com/vickeykumar/Go-interpreter.git @@ -236,6 +249,7 @@ if [ $run_tests -eq 1 ]; then "sqlite3 --version" "tsc --version" "ts-node --version" + "echo "nop" | rappel" ) diff --git a/src/Makefile b/src/Makefile index 9eed528a..15a1b015 100755 --- a/src/Makefile +++ b/src/Makefile @@ -33,9 +33,10 @@ bindata: bindata/static: bindata mkdir bindata/static -bindata/static/index.html: bindata/static resources/index.html resources/profile.html resources/robots.txt jsconsole/build/static/jsconsole.html +bindata/static/index.html: bindata/static resources/index.html resources/profile.html resources/practice.html resources/robots.txt jsconsole/build/static/jsconsole.html cp resources/index.html bindata/static/index.html cp resources/profile.html bindata/static/profile.html + cp resources/practice.html bindata/static/practice.html cp resources/robots.txt bindata/static/robots.txt cp jsconsole/build/static/jsconsole.html bindata/static/jsconsole.html diff --git a/src/containers/container.go b/src/containers/container.go index 27fa4706..e78833c1 100644 --- a/src/containers/container.go +++ b/src/containers/container.go @@ -35,6 +35,7 @@ var Commands2memLimitMap = map[string]int64{ "evcxr": 50, // rust REPL "sqlite3": 10, "ts-node": 50, + "rappel": 2, } var memLimitMutex sync.Mutex diff --git a/src/resources/chat-widget/src/index.ts b/src/resources/chat-widget/src/index.ts index 2fbdd15f..c44aef56 100644 --- a/src/resources/chat-widget/src/index.ts +++ b/src/resources/chat-widget/src/index.ts @@ -13,6 +13,29 @@ const WIDGET_MESSAGES_HISTORY_CONTAINER_ID = const WIDGET_THINKING_BUBBLE_ID = "chat-widget__thinking_bubble"; const CHAT_LIST_KEY = "chat-list" +const interviewPrompt = ` +You are an expert coding interviewer conducting a technical interview. Your goal is to assess the candidate's ability to solve a coding problem independently. + +### Interview Process: +1. Start by presenting the problem statement and constraints clearly from IDE. +2. Do NOT give the solution or direct hints unless the user explicitly asks for help or is stuck. +3. Encourage the candidate to **think aloud** and explain their approach. +4. If the candidate provides an incorrect approach, ask **clarifying questions** to guide them. +5. Only give small hints when necessary, helping them think in the right direction without revealing the full solution. +6. Use Socratic questioning to probe their understanding: + - "What data structure might be useful for this problem?" + - "Can you optimize your current approach?" + - "What are the edge cases you need to consider?" +7. If the candidate asks for a full solution, politely **decline** and encourage them to try again. +8. If they are truly stuck (e.g., multiple failed attempts), provide a **small hint** to unblock them. +9. Once they reach a correct approach, let them implement it and provide constructive feedback. + +### Response Guidelines: +- Be professional and supportive but **not too helpful**. +- Encourage the user to debug their code rather than fixing it for them. +- Give feedback in a way that promotes learning and problem-solving skills. +` + function generateFiveCharUUID(): string { // Generate a UUID and extract the first 5 characters const uuid: string = crypto.randomUUID(); @@ -108,6 +131,16 @@ const MAX_HISTORY_SIZE = 20; // Initialize the conversationHistory array let conversationHistory: MessageType[] = []; +function getcurrentIDECode(): MessageType { + let idecodemsg = { + role: "system", + content: `Openrepl IDE/Editor real-time Code Content user is working on, + (refer this code whenever user ask to debug editor/ide + code without providing any code in message): `+ fetchEditorContent(), + } + return idecodemsg; +} + // Function to add a message to the conversation history function addMessageToHistory(role: string, content: string, uid: string=UID): void { if (role=="user") { @@ -115,10 +148,7 @@ function addMessageToHistory(role: string, content: string, uid: string=UID): vo content = `[${role}-${uid}] ` + content; if (conversationHistory.length >= NUM_MANDATORY_ENTRIES) { // update editors content to msg history everytime user writes/sends message - conversationHistory[NUM_MANDATORY_ENTRIES-1] = { - role: "system", - content: "Openrepl IDE/Editor Code Content: "+ fetchEditorContent(), - } + conversationHistory[NUM_MANDATORY_ENTRIES-1] = getcurrentIDECode(); } } @@ -225,8 +255,16 @@ async function init() { ); open({ target } as Event); } - addMessageToHistory("system", "welcome to openrepl.com!! I am Genie. your OpenRepl AI assistant."); - addMessageToHistory("system", "documentation: "+documentation); + + let welcomeprompt = "welcome to openrepl.com!! you are Genie. An OpenRepl AI"; + // only four permanent prompts + if (window.location.pathname.includes("practice")) { + addMessageToHistory("system", welcomeprompt+" Interviewer."); + addMessageToHistory("system", interviewPrompt); + } else { + addMessageToHistory("system", welcomeprompt+" Assistant."); + addMessageToHistory("system", "documentation: "+documentation); + } addMessageToHistory("system", "keywords: "+ keywords); addMessageToHistory("system", "Openrepl IDE/EditorCodeContent: "+ fetchEditorContent()); setupFBListener(); @@ -256,6 +294,87 @@ const trap = createFocusTrap(containerElement, { allowOutsideClick: true, }); +function makeResizable(containerElement: HTMLElement, target: HTMLElement) { + // Create a resizer div + const resizer = document.createElement("div"); + resizer.innerHTML = ` + + `; + resizer.style.position = "absolute"; + resizer.style.left = "5px"; + resizer.style.top = "5px"; + resizer.style.cursor = "nwse-resize"; + resizer.style.opacity = "0.7"; + resizer.style.transition = "opacity 0.2s"; + resizer.style.display = "flex"; + resizer.style.alignItems = "center"; + resizer.style.justifyContent = "center"; + resizer.style.width = "15px"; + resizer.style.height = "15px"; + + // Style the container for a modern feel + Object.assign(containerElement.style, { + position: "absolute", + borderRadius: "10px", + boxShadow: "0 4px 10px rgba(0, 0, 0, 0.2)", + overflow: "hidden", + resize: "none", // Disable native resize + transition: "width 0.2s ease, height 0.2s ease", + }); + + containerElement.appendChild(resizer); + + let isResizing = false; + + resizer.addEventListener("mousedown", (e) => { + e.preventDefault(); + isResizing = true; + + const startX = e.clientX; + const startY = e.clientY; + const startWidth = containerElement.offsetWidth; + const startHeight = containerElement.offsetHeight; + + function resize(e: MouseEvent) { + if (!isResizing) return; + const newWidth = Math.max(150, startWidth + (startX - e.clientX)); // Min width: 150px + const newHeight = Math.max(100, startHeight + (startY - e.clientY)); // Min height: 100px + containerElement.style.width = `${newWidth}px`; + containerElement.style.height = `${newHeight}px`; + + // Recompute floating position to keep alignment + updatePosition(); + } + + function stopResize() { + isResizing = false; + document.removeEventListener("mousemove", resize); + document.removeEventListener("mouseup", stopResize); + } + + document.addEventListener("mousemove", resize); + document.addEventListener("mouseup", stopResize); + }); + + function updatePosition() { + computePosition(target, containerElement, { + placement: "top-start", + middleware: [flip(), shift({ crossAxis: true, padding: 8 })], + strategy: "fixed", + }).then(({ x, y }) => { + Object.assign(containerElement.style, { + left: `${x}px`, + top: `${y}px`, + }); + }); + } + + return updatePosition; // Return the function so it can be used if needed +} + function open(e: Event) { if (config.closeOnOutsideClick) { document.body.appendChild(optionalBackdrop); @@ -293,6 +412,7 @@ function open(e: Event) { }); }); + makeResizable(containerElement, target); trap.activate(); if (config.closeOnOutsideClick) { @@ -486,7 +606,7 @@ async function submit(e: Event) { const data = { ...config.user, model: config.model, - messages: conversationHistory, + messages: [...conversationHistory, getcurrentIDECode()], temperature: config.temperature, max_tokens: config.max_tokens, stream: config.responseIsAStream, diff --git a/src/resources/chat-widget/src/widget.css b/src/resources/chat-widget/src/widget.css index c7c74e93..ccdfebf3 100644 --- a/src/resources/chat-widget/src/widget.css +++ b/src/resources/chat-widget/src/widget.css @@ -166,7 +166,7 @@ width: 100%; height: 100%; background-image: url('images/genie-large.png'); /* Path to your animated GIF */ - background-size: cover; /* Make the background cover the entire container */ + background-size: contain; /* Make the background contained */ background-position: center; /* Center the background image */ background-repeat: no-repeat; /* Prevent the background image from repeating */ opacity: 0.5; /* Adjust opacity as needed */ diff --git a/src/resources/css/scribbler-global.css b/src/resources/css/scribbler-global.css index 92a5ecf6..694480fb 100755 --- a/src/resources/css/scribbler-global.css +++ b/src/resources/css/scribbler-global.css @@ -23,6 +23,10 @@ --terminal-bg-color: black; --gutter-rotate: 90deg; /* Initial rotation angle */ --gutter-right: 3px; + --modal-duration: 1s; + --clear-color: #ebebec; + --shadow-color: rgba(0, 0, 0, 0.3); + --overlay-color: rgba(0, 0, 0, 0.6); } /* normalized */ @@ -506,4 +510,33 @@ input[type=text],[type=email], select, textarea, email { .rev-accent-background { background: var(--rev-accent-color); } -/* them classes ends */ \ No newline at end of file +/* them classes ends */ + +/* loaders */ +.loader, #loader { + position: absolute; + left: 50%; + top: 50%; + z-index: 11; + width: 150px; + height: 150px; + margin: -75px 0 0 -75px; + border: 16px solid #f3f3f3; + border-radius: 50%; + border-top: 16px solid #64CEAA; + width: 80px; + height: 80px; + -webkit-animation: spin 1s linear infinite; + animation: spin 1s linear infinite; +} + +@-webkit-keyframes spin { + 0% { -webkit-transform: rotate(0deg); } + 100% { -webkit-transform: rotate(360deg); } +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} +/* loader ends */ \ No newline at end of file diff --git a/src/resources/css/scribbler-landing.css b/src/resources/css/scribbler-landing.css index 7ae77d05..05a385f0 100755 --- a/src/resources/css/scribbler-landing.css +++ b/src/resources/css/scribbler-landing.css @@ -771,33 +771,6 @@ h2 { padding-right:10px; } -#loader { - position: absolute; - left: 50%; - top: 50%; - z-index: 11; - width: 150px; - height: 150px; - margin: -75px 0 0 -75px; - border: 16px solid #f3f3f3; - border-radius: 50%; - border-top: 16px solid #64CEAA; - width: 80px; - height: 80px; - -webkit-animation: spin 1s linear infinite; - animation: spin 1s linear infinite; -} - -@-webkit-keyframes spin { - 0% { -webkit-transform: rotate(0deg); } - 100% { -webkit-transform: rotate(360deg); } -} - -@keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } -} - /* tooltip */ .tooltip { diff --git a/src/resources/css/scribbler-misc.css b/src/resources/css/scribbler-misc.css new file mode 100644 index 00000000..551a3408 --- /dev/null +++ b/src/resources/css/scribbler-misc.css @@ -0,0 +1,197 @@ +.wrapper .wrapper-body { + padding: 10px; + width: 100%; + background-color: #f5f5f5; +} +.toolbar { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 15px; + background: #fff; + padding: 10px 15px; + border-radius: 8px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); +} +.toolbar select, .toolbar input { + padding: 7px; + border: 1px solid #ccc; + border-radius: 4px; + font-size: 14px; +} +#newQuestionBtn, #randomQuestionBtn, #completeBtn { + background-color: #007bff; + color: white; + border: none; + padding: 10px 15px; + border-radius: 6px; + cursor: pointer; + font-size: 14px; + transition: background 0.3s; +} +#newQuestionBtn:hover, #randomQuestionBtn:hover, #completeBtn:hover { + background-color: #0056b3; +} + +.question-link { + text-decoration: none; + color: #007bff; + font-weight: bold; +} +.question-link:hover { + text-decoration: underline; +} +.bookmarked-row { + background-color: #d1ffd1 !important; +} + +.bookmark { + appearance: none; /* Remove default checkbox */ + width: 18px; + height: 18px; + border: 2px solid #ccc; + border-radius: 5px; + cursor: pointer; + display: inline-block; + position: relative; +} + +.bookmark:checked::before { + content: "β "; /* Unicode checkmark */ + font-size: 18px; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.delete-btn { + background: #dc3545; + color: white; + border: none; + padding: 5px 10px; + border-radius: 4px; + cursor: pointer; + font-size: 12px; +} +.delete-btn:hover { + background: #b02a37; +} + +/* modal */ +.select2-container { + min-width: 200px !important; /* Adjust as needed */ + width: 100% !important; /* Ensure it takes full available space */ +} + +.modal-container { + background-color: var(--overlay-color); + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: none; +} + +.modal-container.show-modal { + display: block; + z-index: 100; +} + +.modal { + background-color: var(--clear-color); + border-radius: 5px; + box-shadow: 0 0 10px var(--shadow-color); + position: absolute; + overflow: hidden; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + max-width: 100%; + width: 400px; + animation-name: modalopen; + animation-duration: var(--modal-duration); +} + +.modal-header { + background: var(--primary-color); + color: var(--clear-color); + padding: 15px; +} + +.modal-header h3 { + margin: 0; +} + +.modal-content { + padding: 20px; +} + +.modal-form div { + margin: 15px 0; +} + +.modal-form label { + display: block; + margin-bottom: 5px; +} + +.modal-form .form-input { + padding: 8px; + width: 100%; +} + +.modal-rows { + display: flex; + align-items: center; + gap: 10px; /* Adjust spacing */ +} + +.close-btn { + background: transparent; + font-size: 25px; + position: absolute; + top: 0; + right: 0; +} + +@keyframes modalopen { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +/* question table */ +#questionsTable { + width: 100% !important; +} + +#questionsTable th, +#questionsTable td { + overflow: hidden; + white-space: nowrap; /* Prevent text wrapping */ + text-overflow: ellipsis; /* Truncate long text with '...' */ +} + +/* Bookmark column (smallest width, checkbox) */ +#questionsTable th:nth-child(1), +#questionsTable td:nth-child(1) { + width: 5% !important; + text-align: center; +} + +/* Name column (widest) */ +#questionsTable th:nth-child(2), +#questionsTable td:nth-child(2) { + width: 35% !important; +} + +/* Other columns (equal distribution) */ +#questionsTable th:nth-child(n+3), +#questionsTable td:nth-child(n+3) { + width: 15% !important; +} diff --git a/src/resources/editblog.html b/src/resources/editblog.html index 70e1f94b..23becd14 100644 --- a/src/resources/editblog.html +++ b/src/resources/editblog.html @@ -162,6 +162,7 @@