fix(vscode): improve paste support for vscode extension

This commit is contained in:
Ruben Fiszel
2025-12-26 23:21:42 +00:00
parent 20a49a30f8
commit e9a960dca7
7 changed files with 199 additions and 13 deletions
+35 -2
View File
@@ -1,5 +1,6 @@
<script lang="ts">
import SchemaForm from '$lib/components/SchemaForm.svelte'
import JsonInputs from '$lib/components/JsonInputs.svelte'
import JobLoader from '$lib/components/JobLoader.svelte'
import { Button } from '$lib/components/common'
import { WindmillIcon } from '$lib/components/icons'
@@ -121,6 +122,9 @@
// Test args input
let args: Record<string, any> = $state({})
let isValid: boolean = $state(true)
let jsonView: boolean = $state(false)
let jsonEditor: JsonInputs | undefined = $state(undefined)
let schemaHeight = $state(0)
// Test
let testIsLoading = $state(false)
@@ -781,9 +785,38 @@
<Splitpanes horizontal style="height: 1000px;">
<Pane size={33}>
<div class="px-2">
<div class="break-words relative font-sans">
<SchemaForm compact {schema} bind:args bind:isValid />
<div class="flex justify-end mb-2">
<Toggle
bind:checked={jsonView}
size="xs"
options={{
right: 'JSON',
rightTooltip: 'Fill args from JSON'
}}
lightMode
on:change={() => {
jsonEditor?.setCode(JSON.stringify(args ?? {}, null, '\t'))
}}
/>
</div>
{#if jsonView}
<div class="py-2" style="height: {Math.max(schemaHeight, 300)}px">
<JsonInputs
bind:this={jsonEditor}
on:select={(e) => {
if (e.detail) {
args = e.detail
}
}}
updateOnBlur={false}
placeholder={`Write args as JSON.<br/><br/>Example:<br/><br/>{<br/>&nbsp;&nbsp;"foo": "12"<br/>}`}
/>
</div>
{:else}
<div class="break-words relative font-sans" bind:clientHeight={schemaHeight}>
<SchemaForm compact {schema} bind:args bind:isValid />
</div>
{/if}
</div>
</Pane>
<Pane size={67}>
+32 -1
View File
@@ -5,7 +5,7 @@
import '@codingame/monaco-vscode-standalone-languages'
import '@codingame/monaco-vscode-standalone-json-language-features'
import '@codingame/monaco-vscode-standalone-typescript-language-features'
import { editor as meditor } from 'monaco-editor'
import { editor as meditor, KeyMod, KeyCode } from 'monaco-editor'
import { initializeVscode } from './vscode'
import EditorTheme from './EditorTheme.svelte'
@@ -75,6 +75,37 @@
scrollbar: { alwaysConsumeMouseWheel: false }
})
// In VSCode webview (iframe), clipboard operations need special handling
// because the webview has restricted clipboard API access
if (window.parent !== window) {
const modifiedEditor = diffEditor.getModifiedEditor()
modifiedEditor.addCommand(KeyMod.CtrlCmd | KeyCode.KeyC, function () {
document.execCommand('copy')
})
modifiedEditor.addCommand(KeyMod.CtrlCmd | KeyCode.KeyX, function () {
document.execCommand('cut')
})
modifiedEditor.addCommand(KeyMod.CtrlCmd | KeyCode.KeyV, async function () {
try {
const text = await navigator.clipboard.readText()
if (text) {
const selection = modifiedEditor.getSelection()
if (selection) {
modifiedEditor.executeEdits('paste', [
{
range: selection,
text: text,
forceMoveMarkers: true
}
])
}
}
} catch (e) {
document.execCommand('paste')
}
})
}
console.log('defaultModified', defaultModified)
if (defaultLang !== undefined) {
setupModel(defaultLang, defaultOriginal, defaultModified, defaultModifiedLang)
+32
View File
@@ -1376,6 +1376,38 @@
keepModelAroundToAvoidDisposalOfWorkers()
// In VSCode webview (iframe), clipboard operations need special handling
// because the webview has restricted clipboard API access
if (window.parent !== window) {
editor.addCommand(KeyMod.CtrlCmd | KeyCode.KeyC, function () {
document.execCommand('copy')
})
editor.addCommand(KeyMod.CtrlCmd | KeyCode.KeyX, function () {
document.execCommand('cut')
})
editor.addCommand(KeyMod.CtrlCmd | KeyCode.KeyV, async function () {
try {
// Use Clipboard API to read text, then insert via Monaco's API
const text = await navigator.clipboard.readText()
if (text && editor) {
const selection = editor.getSelection()
if (selection) {
editor.executeEdits('paste', [
{
range: selection,
text: text,
forceMoveMarkers: true
}
])
}
}
} catch (e) {
// Clipboard API failed, try execCommand as fallback
document.execCommand('paste')
}
})
}
// updateEditorKeybindingsMode(editor, 'vim', undefined)
// Raw app lint collection: listen for marker changes and report to store
@@ -1,7 +1,7 @@
<script lang="ts">
import { BROWSER } from 'esm-env'
import { editor as meditor } from 'monaco-editor'
import { editor as meditor, KeyMod, KeyCode } from 'monaco-editor'
import { onDestroy, onMount } from 'svelte'
@@ -20,6 +20,14 @@
lineNumbers: 'off',
minimap: { enabled: false }
})
// In VSCode webview (iframe), clipboard operations need to use execCommand
// because the webview has restricted clipboard API access
if (window.parent !== window) {
editor.addCommand(KeyMod.CtrlCmd | KeyCode.KeyC, function () {
document.execCommand('copy')
})
}
}
onMount(async () => {
+16 -9
View File
@@ -4,14 +4,21 @@
const dispatch = createEventDispatcher()
export let updateOnBlur = true
export let placeholder =
'Write a JSON payload. The input schema will be inferred.<br/><br/>Example:<br/><br/>{<br/>&nbsp;&nbsp;"foo": "12"<br/>}'
export let selected: boolean = false
interface Props {
updateOnBlur?: boolean
placeholder?: string
selected?: boolean
}
let pendingJson = ''
let simpleEditor: SimpleEditor | undefined = undefined
let focusTrap: HTMLElement | undefined
let {
updateOnBlur = true,
placeholder = 'Write a JSON payload. The input schema will be inferred.<br/><br/>Example:<br/><br/>{<br/>&nbsp;&nbsp;"foo": "12"<br/>}',
selected = false
}: Props = $props()
let pendingJson = $state('')
let simpleEditor: SimpleEditor | undefined = $state(undefined)
let focusTrap: HTMLElement | undefined = $state()
function updatePayloadFromJson(jsonInput: string) {
if (jsonInput === undefined || jsonInput === null || jsonInput.trim() === '') {
@@ -44,12 +51,12 @@
}
</script>
<svelte:window on:keydown={handleKeydown} />
<svelte:window onkeydown={handleKeydown} />
<!-- Add a hidden button that can receive focus -->
<button bind:this={focusTrap} class="sr-only" tabindex="-1" aria-hidden="true">Focus trap</button>
<div class="h-full py-3 rounded-md border">
<div class="h-full rounded-md border">
<SimpleEditor
bind:this={simpleEditor}
on:focus={() => {
@@ -360,6 +360,21 @@
}
keepModelAroundToAvoidDisposalOfWorkers()
// In VSCode webview (iframe), clipboard operations need special handling
// because the webview has restricted clipboard API access
if (window.parent !== window) {
editor.addCommand(KeyMod.CtrlCmd | KeyCode.KeyC, function () {
document.execCommand('copy')
})
editor.addCommand(KeyMod.CtrlCmd | KeyCode.KeyX, function () {
document.execCommand('cut')
})
editor.addCommand(KeyMod.CtrlCmd | KeyCode.KeyV, async function () {
inputEl?.focus()
document.execCommand('paste')
})
}
let timeoutModel: number | undefined = undefined
editor.onDidChangeModelContent((event) => {
timeoutModel && clearTimeout(timeoutModel)
@@ -583,8 +598,38 @@
}
updatePlaceholderVisibility(code ?? '')
let inputEl = $state<HTMLInputElement | null>(null)
let pasteValue = $state('')
$effect(() => {
if (inputEl && pasteValue) {
untrack(() => {
editor?.executeEdits('paste', [
{
range: editor?.getSelection() ?? {
startLineNumber: 1,
startColumn: 1,
endLineNumber: 1,
endColumn: 1
},
text: pasteValue,
forceMoveMarkers: true
}
])
pasteValue = ''
})
}
})
</script>
{#if parent.window !== window}
<input
style="height: 0; width: 0; opacity: 0; position: absolute; top: 0; left: 0; z-index: -1;"
type="text"
bind:this={inputEl}
bind:value={pasteValue}
/>
{/if}
<EditorTheme />
{#if !editor || suggestion}
<FakeMonacoPlaceHolder
@@ -464,6 +464,36 @@
return
}
// In VSCode webview (iframe), clipboard operations need special handling
// because the webview has restricted clipboard API access
if (window.parent !== window) {
editor.addCommand(KeyMod.CtrlCmd | KeyCode.KeyC, function () {
document.execCommand('copy')
})
editor.addCommand(KeyMod.CtrlCmd | KeyCode.KeyX, function () {
document.execCommand('cut')
})
editor.addCommand(KeyMod.CtrlCmd | KeyCode.KeyV, async function () {
try {
const text = await navigator.clipboard.readText()
if (text && editor) {
const selection = editor.getSelection()
if (selection) {
editor.executeEdits('paste', [
{
range: selection,
text: text,
forceMoveMarkers: true
}
])
}
}
} catch (e) {
document.execCommand('paste')
}
})
}
editor.onDidFocusEditorText(() => {
dispatch('focus')