diff --git a/frontend/src/components/BlockContextMenu.vue b/frontend/src/components/BlockContextMenu.vue index 9b9823bb6..ba4af97ff 100644 --- a/frontend/src/components/BlockContextMenu.vue +++ b/frontend/src/components/BlockContextMenu.vue @@ -13,15 +13,19 @@ import NewComponent from "@/components/Modals/NewComponent.vue"; import useBuilderStore from "@/stores/builderStore"; import useCanvasStore from "@/stores/canvasStore"; import useComponentStore from "@/stores/componentStore"; +import usePageStore from "@/stores/pageStore"; import getBlockTemplate from "@/utils/blockTemplate"; import { confirm, detachBlockFromComponent, getBlockCopy, triggerCopyEvent } from "@/utils/helpers"; import { useStorage } from "@vueuse/core"; -import { Ref, inject, nextTick, ref } from "vue"; +import { Ref, inject, nextTick, ref, computed } from "vue"; import { toast } from "vue-sonner"; +import { useExternalEditor, createEditorContext } from "@/composables/useExternalEditor"; const builderStore = useBuilderStore(); const componentStore = useComponentStore(); const canvasStore = useCanvasStore(); +const pageStore = usePageStore(); +const { isExternalEditorActive, openInExternalEditor, editorName } = useExternalEditor(); const contextMenu = ref(null) as unknown as Ref>; const triggeredFromLayersPanel = ref(false); @@ -60,11 +64,33 @@ const pasteStyle = () => { block.value.updateStyles(copiedStyle.value?.style as BlockStyleObjects); }; +const openScriptInExternalEditor = async (scriptType: "blockClientScript" | "blockDataScript") => { + if (!block.value.blockId) return; + + const context = createEditorContext( + "Builder Page", + pageStore.selectedPage || pageStore.pageName, + undefined, + block.value.blockId, + scriptType, + ); + + if (!context) return; + + const result = await openInExternalEditor(context); + const scriptName = scriptType === "blockClientScript" ? "Client Script" : "Data Script"; + if (!result.success) { + toast.error(result.error || `Failed to open ${scriptName.toLowerCase()} in ${editorName.value}`); + } else { + toast.success(`${scriptName} opened in ${editorName.value}`); + } +}; + const duplicateBlock = () => { block.value.duplicateBlock(); }; -const contextMenuOptions: ContextMenuOption[] = [ +const contextMenuOptions = computed((): ContextMenuOption[] => [ { label: "Edit with AI", action: () => { @@ -251,6 +277,16 @@ const contextMenuOptions: ContextMenuOption[] = [ condition: () => block.value.isExtendedFromComponent(), disabled: () => builderStore.readOnlyMode, }, + { + label: `Open Client Script in ${editorName.value}`, + action: () => openScriptInExternalEditor("blockClientScript"), + condition: () => isExternalEditorActive.value && !block.value.isRoot(), + }, + { + label: `Open Data Script in ${editorName.value}`, + action: () => openScriptInExternalEditor("blockDataScript"), + condition: () => isExternalEditorActive.value && !block.value.isRoot(), + }, { label: "Save as Block Template", action: () => { @@ -317,7 +353,7 @@ const contextMenuOptions: ContextMenuOption[] = [ }, disabled: () => builderStore.readOnlyMode, }, -]; +]); defineExpose({ showContextMenu, diff --git a/frontend/src/components/Controls/CodeEditor.vue b/frontend/src/components/Controls/CodeEditor.vue index 6ef989fe8..edb1200b2 100644 --- a/frontend/src/components/Controls/CodeEditor.vue +++ b/frontend/src/components/Controls/CodeEditor.vue @@ -1,9 +1,26 @@ @@ -146,6 +147,7 @@ import CodeEditor from "./Controls/CodeEditor.vue"; import CSSIcon from "./Icons/CSS.vue"; import GripVertical from "./Icons/GripVertical.vue"; import JavaScriptIcon from "./Icons/JavaScript.vue"; +import { createEditorContext } from "@/composables/useExternalEditor"; const { capture } = useTelemetry(); @@ -207,6 +209,10 @@ const selectScript = (script: attachedScript) => { }); }; +const getEditorContext = () => { + return createEditorContext("Builder Client Script", activeScript.value?.script_name, "script"); +}; + const updateScript = (value: string) => { if (!activeScript.value || builderStore.readOnlyMode) return; diff --git a/frontend/src/components/PageScript.vue b/frontend/src/components/PageScript.vue index b0555e4de..99e6ff759 100644 --- a/frontend/src/components/PageScript.vue +++ b/frontend/src/components/PageScript.vue @@ -79,7 +79,8 @@ :autofocus="true" @save="savePageDataScript" :showSaveButton="true" - :show-line-numbers="true"> + :show-line-numbers="true" + :external-editor-context="getPageEditorContext('page_data_script')"> + Example:
this.addEventListener("click", () => { console.log(props) })


+ For more details on how to write data script, refer to this documentation.'>
@@ -126,7 +128,8 @@ :autofocus="true" @save="saveBlockDataScript" :showSaveButton="true" - :show-line-numbers="true"> + :show-line-numbers="true" + :external-editor-context="getBlockEditorContext('blockDataScript')">
{ ? blockDataStore.getBlockData( blockController.getFirstSelectedBlock().blockId, showInheritedBlockData.value ? "all" : "own", - ) || {} + ) || {} : {}; }); +const getPageEditorContext = (field: string) => { + return createEditorContext("Builder Page", props.page?.name, field); +}; + +const getBlockEditorContext = (blockField: "blockClientScript" | "blockDataScript") => { + const block = blockController.getFirstSelectedBlock(); + return createEditorContext("Builder Page", props.page?.name, undefined, block?.blockId, blockField); +}; + const savePageDataScript = (value: string) => { webPages.setValue .submit({ diff --git a/frontend/src/components/Settings/GlobalCode.vue b/frontend/src/components/Settings/GlobalCode.vue index 403afc286..06631d828 100644 --- a/frontend/src/components/Settings/GlobalCode.vue +++ b/frontend/src/components/Settings/GlobalCode.vue @@ -8,7 +8,8 @@ height="100px" class="shrink-0" @update:modelValue="builderStore.updateBuilderSettings('head_html', $event)" - :showLineNumbers="true"> + :showLineNumbers="true" + :externalEditorContext="getEditorContext('head_html')"> + :showLineNumbers="true" + :externalEditorContext="getEditorContext('body_html')"> + :showLineNumbers="true" + :externalEditorContext="getEditorContext('script')"> + :showLineNumbers="true" + :externalEditorContext="getEditorContext('style')">
diff --git a/frontend/src/components/Settings/GlobalDeveloper.vue b/frontend/src/components/Settings/GlobalDeveloper.vue index 96c3d68a9..95ee9dbc6 100644 --- a/frontend/src/components/Settings/GlobalDeveloper.vue +++ b/frontend/src/components/Settings/GlobalDeveloper.vue @@ -1,11 +1,19 @@ @@ -38,8 +83,9 @@ import Switch from "@/components/Controls/Switch.vue"; import { builderSettings } from "@/data/builderSettings"; import useBuilderStore from "@/stores/builderStore"; -import { Select } from "frappe-ui"; -import InlineInput from "../Controls/InlineInput.vue"; +import { useExternalEditor } from "@/composables/useExternalEditor"; +import { Button, Select } from "frappe-ui"; const builderStore = useBuilderStore(); +const { lnaPermissionStatus, isRequestingAccess, requestLocalNetworkAccess } = useExternalEditor(); diff --git a/frontend/src/components/Settings/PageCode.vue b/frontend/src/components/Settings/PageCode.vue index 71ce45e83..a23019cf9 100644 --- a/frontend/src/components/Settings/PageCode.vue +++ b/frontend/src/components/Settings/PageCode.vue @@ -9,7 +9,8 @@ class="shrink-0" :modelValue="pageStore.activePage?.head_html" @update:modelValue="(val) => pageStore.updateActivePage('head_html', val)" - :showLineNumbers="true"> + :showLineNumbers="true" + :externalEditorContext="getEditorContext('head_html')"> + :showLineNumbers="true" + :externalEditorContext="getEditorContext('body_html')">