feat: livecode settings

This commit is contained in:
Jannat Patel
2025-06-25 19:57:07 +05:30
parent e71275a0dc
commit b8981c249f
6 changed files with 92 additions and 35 deletions

View File

@@ -5,7 +5,7 @@ echo "Setting Up System Dependencies..."
sudo apt update sudo apt update
sudo apt remove mysql-server mysql-client sudo apt remove mysql-server mysql-client
sudo apt-get install libcups2-dev redis-server mariadb-client sudo apt-get install libcups2-dev redis-server mariadb-client libmariadb-dev
install_wkhtmltopdf() { install_wkhtmltopdf() {
wget -q https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb wget -q https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb

View File

@@ -166,6 +166,12 @@ const tabsStructure = computed(() => {
doctype: 'Email Template', doctype: 'Email Template',
type: 'Link', type: 'Link',
}, },
{
label: 'Livecode URL',
name: 'livecode_url',
doctype: 'Livecode URL',
type: 'text',
},
{ {
label: 'Unsplash Access Key', label: 'Unsplash Access Key',
name: 'unsplash_access_key', name: 'unsplash_access_key',

View File

@@ -5,6 +5,20 @@
> >
<Breadcrumbs :items="breadcrumbs" /> <Breadcrumbs :items="breadcrumbs" />
</header> </header>
<div
v-if="falconError"
class="flex items-center justify-between p-3 text-sm bg-surface-amber-1 text-ink-amber-3"
>
<span>
{{ falconError }}
</span>
<Button v-if="user.data?.is_moderator" @click="openSettings('General')">
<template #prefix>
<Settings class="size-4 stroke-1.5" />
</template>
{{ __('Settings') }}
</Button>
</div>
<div class="grid grid-cols-2 h-[calc(100vh_-_3rem)]"> <div class="grid grid-cols-2 h-[calc(100vh_-_3rem)]">
<div class="border-r py-5 px-8 h-full"> <div class="border-r py-5 px-8 h-full">
<div class="font-semibold mb-2"> <div class="font-semibold mb-2">
@@ -29,7 +43,9 @@
</Badge> </Badge>
<Button <Button
v-if=" v-if="
submissionID == 'new' || user.data?.name == submission.doc?.owner !falconError &&
(submissionID == 'new' ||
user.data?.name == submission.doc?.owner)
" "
variant="solid" variant="solid"
@click="submitCode" @click="submitCode"
@@ -131,9 +147,10 @@ import {
usePageMeta, usePageMeta,
} from 'frappe-ui' } from 'frappe-ui'
import { computed, inject, onMounted, ref, watch } from 'vue' import { computed, inject, onMounted, ref, watch } from 'vue'
import { Play, X, Check } from 'lucide-vue-next' import { Play, X, Check, Settings } from 'lucide-vue-next'
import { sessionStore } from '@/stores/session' import { sessionStore } from '@/stores/session'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { openSettings } from '@/utils'
const user = inject<any>('$user') const user = inject<any>('$user')
const code = ref<string | null>('') const code = ref<string | null>('')
@@ -141,19 +158,13 @@ const output = ref<string | null>(null)
const error = ref<boolean | null>(null) const error = ref<boolean | null>(null)
const errorMessage = ref<string | null>(null) const errorMessage = ref<string | null>(null)
const testCaseSection = ref<HTMLElement | null>(null) const testCaseSection = ref<HTMLElement | null>(null)
const testCases = ref< const testCases = ref<TestCase[]>([])
Array<{
input: string
output: string
expected_output: string
status: string
}>
>([])
const boilerplate = ref<string>('') const boilerplate = ref<string>('')
const { brand } = sessionStore() const { brand, livecodeURL } = sessionStore()
const router = useRouter() const router = useRouter()
const fromLesson = ref(false) const fromLesson = ref(false)
const falconURL = 'https://falcon.frappe.io/' const falconURL = ref<string>('https://falcon.frappe.io/')
const falconError = ref<string | null>(null)
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@@ -273,9 +284,23 @@ watch(
) )
const loadFalcon = () => { const loadFalcon = () => {
if (livecodeURL.data.includes('falcon.frappe.io') && !user.data?.is_fc_site) {
falconError.value = __(
'Only Frappe Cloud sites can use Falcon Live Code. Please migrate your site to Frappe Cloud or setup Falcon Live Code on your own server.'
)
return
} else if (livecodeURL.data) {
falconURL.value = livecodeURL.data
} else if (!livecodeURL.data && !user.data?.is_fc_site) {
falconError.value = __(
'Live Code URL is not set. Please set it from the Settings.'
)
return
}
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const script = document.createElement('script') const script = document.createElement('script')
script.src = `${falconURL}static/livecode.js` script.src = `${falconURL.value}static/livecode.js`
script.onload = resolve script.onload = resolve
script.onerror = reject script.onerror = reject
document.head.appendChild(script) document.head.appendChild(script)
@@ -351,7 +376,7 @@ const execute = (stdin = ''): Promise<string> => {
let hasError = false let hasError = false
let session = new LiveCodeSession({ let session = new LiveCodeSession({
base_url: 'https://falcon.frappe.io', base_url: falconURL.value,
runtime: exercise.doc?.language.toLowerCase() || 'python', runtime: exercise.doc?.language.toLowerCase() || 'python',
code: code.value, code: code.value,
files: [{ filename: 'stdin', contents: stdin }], files: [{ filename: 'stdin', contents: stdin }],

View File

@@ -3,6 +3,19 @@
class="sticky flex items-center justify-between top-0 z-10 border-b bg-surface-white px-3 py-2.5 sm:px-5" class="sticky flex items-center justify-between top-0 z-10 border-b bg-surface-white px-3 py-2.5 sm:px-5"
> >
<Breadcrumbs :items="breadcrumbs" /> <Breadcrumbs :items="breadcrumbs" />
<div class="space-x-2">
<router-link
:to="{
name: 'ProgrammingExerciseSubmissions',
}"
>
<Button>
<template #prefix>
<ClipboardList class="size-4 stroke-1.5" />
</template>
{{ __('Check All Submissions') }}
</Button>
</router-link>
<Button <Button
v-if="!readOnlyMode" v-if="!readOnlyMode"
variant="solid" variant="solid"
@@ -18,6 +31,7 @@
</template> </template>
{{ __('New') }} {{ __('New') }}
</Button> </Button>
</div>
</header> </header>
<div class="md:w-4/5 md:mx-auto p-5"> <div class="md:w-4/5 md:mx-auto p-5">
<div class="flex items-center justify-between mb-5"> <div class="flex items-center justify-between mb-5">
@@ -89,7 +103,7 @@ import {
createListResource, createListResource,
usePageMeta, usePageMeta,
} from 'frappe-ui' } from 'frappe-ui'
import { Plus } from 'lucide-vue-next' import { ClipboardList, Plus } from 'lucide-vue-next'
import { sessionStore } from '@/stores/session' import { sessionStore } from '@/stores/session'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import ProgrammingExerciseForm from '@/pages/ProgrammingExercises/ProgrammingExerciseForm.vue' import ProgrammingExerciseForm from '@/pages/ProgrammingExercises/ProgrammingExerciseForm.vue'

View File

@@ -60,6 +60,16 @@ export const sessionStore = defineStore('lms-session', () => {
auto: false, auto: false,
}) })
const livecodeURL = createResource({
url: 'frappe.client.get_single_value',
params: {
doctype: 'LMS Settings',
field: 'livecode_url',
},
cache: 'livecodeURL',
auto: true,
})
return { return {
user, user,
isLoggedIn, isLoggedIn,
@@ -68,5 +78,6 @@ export const sessionStore = defineStore('lms-session', () => {
brand, brand,
branding, branding,
sidebarSettings, sidebarSettings,
livecodeURL,
} }
}) })

View File

@@ -578,16 +578,17 @@ export const enablePlyr = () => {
}) })
} }
export const openSettings = (category, close) => { export const openSettings = (category, close = null) => {
const settingsStore = useSettings() const settingsStore = useSettings()
if (close) {
close() close()
}
settingsStore.activeTab = category settingsStore.activeTab = category
settingsStore.isSettingsOpen = true settingsStore.isSettingsOpen = true
console.log(settingsStore.activeTab, settingsStore.isSettingsOpen)
} }
export const cleanError = (message) => { export const cleanError = (message) => {
// Remove HTML tags but keep the text within the tags
const cleanMessage = message.replace(/<[^>]+>/g, (match) => { const cleanMessage = message.replace(/<[^>]+>/g, (match) => {
return match.replace(/<\/?[^>]+(>|$)/g, '') return match.replace(/<\/?[^>]+(>|$)/g, '')
}) })