mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Add raw editor toggle for using variables in flows operations (#14021)
* add raw editor for flows operation options * add comment to explain reasoning for watcher * add simple raw editor with syntax highlighting * Add multiline to text fields & hide in json fields * update input icon for toggle * do not unset value for text fields * fix mustache tag value checking * enable raw editor for Insights * remove lint warning * Reduce size + inline icons * add background-highlight when active toggle * change multiline prop to type prop * show toggle for all field types (including json) * remove watcher to toggle rawEditor on load * fix raw editor emit * fix request operation headers field type json * fix raw editor value passed to codemirror * prevent tags from crashing * do not unset values anymore when toggling raw Co-authored-by: rijkvanzanten <rijkvanzanten@me.com>
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
? `interface-${field.meta.interface}`
|
||||
: `interface-${getDefaultInterfaceForType(field.type)}`
|
||||
"
|
||||
v-if="interfaceExists"
|
||||
v-if="interfaceExists && !rawEditorActive"
|
||||
v-bind="(field.meta && field.meta.options) || {}"
|
||||
:autofocus="disabled !== true && autofocus"
|
||||
:disabled="disabled"
|
||||
@@ -30,6 +30,13 @@
|
||||
@set-field-value="$emit('setFieldValue', $event)"
|
||||
/>
|
||||
|
||||
<interface-system-raw-editor
|
||||
v-else-if="rawEditorEnabled && rawEditorActive"
|
||||
:value="modelValue === undefined ? field.schema?.default_value : modelValue"
|
||||
:type="field.type"
|
||||
@input="$emit('update:modelValue', $event)"
|
||||
/>
|
||||
|
||||
<v-notice v-else type="warning">
|
||||
{{ t('interface_not_found', { interface: field.meta && field.meta.interface }) }}
|
||||
</v-notice>
|
||||
@@ -77,6 +84,14 @@ export default defineComponent({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
rawEditorEnabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
rawEditorActive: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue', 'setFieldValue'],
|
||||
setup(props) {
|
||||
|
||||
@@ -11,6 +11,16 @@
|
||||
<v-text-overflow :text="field.name" />
|
||||
<v-icon v-if="field.meta?.required === true" class="required" :class="{ 'has-badge': badge }" sup name="star" />
|
||||
<v-chip v-if="badge" x-small>{{ badge }}</v-chip>
|
||||
<v-icon
|
||||
v-if="!disabled && rawEditorEnabled"
|
||||
v-tooltip="t('toggle_raw_editor')"
|
||||
class="raw-editor-toggle"
|
||||
:class="{ active: rawEditorActive }"
|
||||
name="data_object"
|
||||
:filled="!rawEditorActive"
|
||||
small
|
||||
@click.stop="$emit('toggle-raw', !rawEditorActive)"
|
||||
/>
|
||||
<v-icon v-if="!disabled" class="ctx-arrow" :class="{ active }" name="arrow_drop_down" />
|
||||
</span>
|
||||
</div>
|
||||
@@ -63,8 +73,16 @@ export default defineComponent({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
rawEditorEnabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
rawEditorActive: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['toggle-batch'],
|
||||
emits: ['toggle-batch', 'toggle-raw'],
|
||||
setup() {
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -127,6 +145,28 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
.raw-editor-toggle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
margin-top: -2px;
|
||||
margin-left: 5px;
|
||||
color: var(--foreground-subdued);
|
||||
transition: color var(--fast) var(--transition);
|
||||
|
||||
&:hover {
|
||||
color: var(--foreground-normal);
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: var(--primary);
|
||||
background-color: var(--primary-alt);
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
&.edited {
|
||||
.edit-dot {
|
||||
position: absolute;
|
||||
|
||||
@@ -11,8 +11,11 @@
|
||||
:edited="isEdited"
|
||||
:has-error="!!validationError"
|
||||
:badge="badge"
|
||||
:raw-editor-enabled="rawEditorEnabled"
|
||||
:raw-editor-active="rawEditorActive"
|
||||
:loading="loading"
|
||||
@toggle-batch="$emit('toggle-batch', $event)"
|
||||
@toggle-raw="$emit('toggle-raw', $event)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -39,6 +42,8 @@
|
||||
:batch-active="batchActive"
|
||||
:disabled="isDisabled"
|
||||
:primary-key="primaryKey"
|
||||
:raw-editor-enabled="rawEditorEnabled"
|
||||
:raw-editor-active="rawEditorActive"
|
||||
@update:model-value="emitValue($event)"
|
||||
@set-field-value="$emit('setFieldValue', $event)"
|
||||
/>
|
||||
@@ -91,6 +96,8 @@ interface Props {
|
||||
validationError?: ValidationError;
|
||||
autofocus?: boolean;
|
||||
badge?: string;
|
||||
rawEditorEnabled?: boolean;
|
||||
rawEditorActive?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
@@ -104,9 +111,11 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
validationError: undefined,
|
||||
autofocus: false,
|
||||
badge: undefined,
|
||||
rawEditorEnabled: false,
|
||||
rawEditorActive: false,
|
||||
});
|
||||
|
||||
const emit = defineEmits(['toggle-batch', 'unset', 'update:modelValue', 'setFieldValue']);
|
||||
const emit = defineEmits(['toggle-batch', 'toggle-raw', 'unset', 'update:modelValue', 'setFieldValue']);
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
:loading="loading"
|
||||
:validation-errors="validationErrors"
|
||||
:badge="badge"
|
||||
:raw-editor-enabled="rawEditorEnabled"
|
||||
v-bind="fieldsMeta[field.field]?.options || {}"
|
||||
@apply="apply"
|
||||
/>
|
||||
@@ -62,10 +63,13 @@
|
||||
)
|
||||
"
|
||||
:badge="badge"
|
||||
:raw-editor-enabled="rawEditorEnabled"
|
||||
:raw-editor-active="rawActiveFields.has(field.field)"
|
||||
@update:model-value="setValue(field.field, $event)"
|
||||
@set-field-value="setValue($event.field, $event.value)"
|
||||
@unset="unsetValue(field)"
|
||||
@toggle-batch="toggleBatchField(field)"
|
||||
@toggle-raw="toggleRawField(field)"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
@@ -145,6 +149,10 @@ export default defineComponent({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
rawEditorEnabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
setup(props, { emit }) {
|
||||
@@ -178,6 +186,7 @@ export default defineComponent({
|
||||
|
||||
const { formFields, getFieldsForGroup, fieldsForGroup, isDisabled, fieldsMeta } = useForm();
|
||||
const { toggleBatchField, batchActiveFields } = useBatch();
|
||||
const { toggleRawField, rawActiveFields } = useRawEditor();
|
||||
|
||||
const firstEditableFieldIndex = computed(() => {
|
||||
for (let i = 0; i < formFields.value.length; i++) {
|
||||
@@ -215,6 +224,8 @@ export default defineComponent({
|
||||
setValue,
|
||||
batchActiveFields,
|
||||
toggleBatchField,
|
||||
rawActiveFields,
|
||||
toggleRawField,
|
||||
unsetValue,
|
||||
firstEditableFieldIndex,
|
||||
firstVisibleFieldIndex,
|
||||
@@ -406,6 +417,20 @@ export default defineComponent({
|
||||
if (!formFieldEls.value[field]) return;
|
||||
formFieldEls.value[field].$el.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
|
||||
function useRawEditor() {
|
||||
const rawActiveFields = ref(new Set<string>());
|
||||
|
||||
return { rawActiveFields, toggleRawField };
|
||||
|
||||
function toggleRawField(field: Field) {
|
||||
if (rawActiveFields.value.has(field.field)) {
|
||||
rawActiveFields.value.delete(field.field);
|
||||
} else {
|
||||
rawActiveFields.value.add(field.field);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
27
app/src/interfaces/_system/system-raw-editor/index.ts
Normal file
27
app/src/interfaces/_system/system-raw-editor/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { defineInterface } from '@directus/shared/utils';
|
||||
import InterfaceInputTranslatedString from './system-raw-editor.vue';
|
||||
|
||||
export default defineInterface({
|
||||
id: 'system-raw-editor',
|
||||
name: '$t:interfaces.system-raw-editor.system-raw-editor',
|
||||
description: '$t:interfaces.system-raw-editor.description',
|
||||
icon: 'code',
|
||||
component: InterfaceInputTranslatedString,
|
||||
system: true,
|
||||
types: ['string', 'text'],
|
||||
group: 'standard',
|
||||
options: [
|
||||
{
|
||||
field: 'placeholder',
|
||||
name: '$t:placeholder',
|
||||
type: 'string',
|
||||
meta: {
|
||||
width: 'half',
|
||||
interface: 'input',
|
||||
options: {
|
||||
placeholder: '$t:enter_a_placeholder',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
19
app/src/interfaces/_system/system-raw-editor/mustacheMode.ts
Normal file
19
app/src/interfaces/_system/system-raw-editor/mustacheMode.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export const mustacheMode = {
|
||||
start: [{ regex: /\{\{/, push: 'mustache', token: 'tag' }],
|
||||
mustache: [
|
||||
{ regex: /\}\}/, pop: true, token: 'tag' },
|
||||
|
||||
// Double and single quotes
|
||||
{ regex: /"(?:[^\\"]|\\.)*"?/, token: 'string' },
|
||||
{ regex: /'(?:[^\\']|\\.)*'?/, token: 'string' },
|
||||
|
||||
// Flows variables keywords
|
||||
{ regex: />|[$/]([A-Za-z0-9_-]\w*)/, token: 'keyword' },
|
||||
|
||||
// Numeral
|
||||
{ regex: /\d+/i, token: 'number' },
|
||||
|
||||
// Paths
|
||||
{ regex: /(?:\.\.\/)*(?:[A-Za-z_][\w.]*)+/, token: 'variable-2' },
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<div class="system-raw-editor" :class="{ disabled, 'multi-line': isMultiLine }">
|
||||
<div ref="codemirrorEl"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useWindowSize } from '@/composables/use-window-size';
|
||||
import CodeMirror from 'codemirror';
|
||||
import 'codemirror/addon/mode/simple';
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { mustacheMode } from './mustacheMode';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
value?: string;
|
||||
autofocus?: boolean;
|
||||
disabled?: boolean;
|
||||
type?: string;
|
||||
}>(),
|
||||
{
|
||||
value: undefined,
|
||||
autofocus: false,
|
||||
disabled: false,
|
||||
type: undefined,
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits(['input']);
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const { width } = useWindowSize();
|
||||
|
||||
const codemirrorEl = ref<HTMLTextAreaElement | null>();
|
||||
let codemirror: CodeMirror.Editor | null;
|
||||
|
||||
const isMultiLine = computed(() => ['text', 'json'].includes(props.type));
|
||||
|
||||
onMounted(async () => {
|
||||
if (codemirrorEl.value) {
|
||||
CodeMirror.defineSimpleMode('mustache', mustacheMode);
|
||||
|
||||
codemirror = CodeMirror(codemirrorEl.value, {
|
||||
mode: 'mustache',
|
||||
value: typeof props.value === 'object' ? JSON.stringify(props.value, null, 4) : String(props.value ?? ''),
|
||||
tabSize: 0,
|
||||
autoRefresh: true,
|
||||
indentUnit: 4,
|
||||
styleActiveLine: true,
|
||||
highlightSelectionMatches: { showToken: /\w/, annotateScrollbar: true, delay: 100 },
|
||||
matchBrackets: true,
|
||||
showCursorWhenSelecting: true,
|
||||
lineWiseCopyCut: false,
|
||||
theme: 'default',
|
||||
scrollbarStyle: isMultiLine.value ? 'native' : 'null',
|
||||
extraKeys: { Ctrl: 'autocomplete' },
|
||||
cursorBlinkRate: props.disabled ? -1 : 530,
|
||||
placeholder: t('raw_editor_placeholder'),
|
||||
});
|
||||
|
||||
// prevent new lines for single lines
|
||||
if (!isMultiLine.value) {
|
||||
codemirror.on('beforeChange', function (_doc, { origin, text, cancel, update }) {
|
||||
const typedNewLine = origin === '+input' && typeof text === 'object' && text.join('') === '';
|
||||
if (typedNewLine) return cancel();
|
||||
|
||||
const pastedNewLine = origin === 'paste' && typeof text === 'object' && text.length > 1;
|
||||
if (pastedNewLine) {
|
||||
const newText = text.join(' ');
|
||||
if (!update) return;
|
||||
return update(undefined, undefined, [newText]);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
codemirror.on('change', (doc, { origin }) => {
|
||||
if (origin === 'setValue') return;
|
||||
const content = doc.getValue();
|
||||
emit('input', content !== '' ? content : null);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const readOnly = computed(() => {
|
||||
if (width.value < 600) {
|
||||
// mobile requires 'nocursor' to avoid bringing up the keyboard
|
||||
return props.disabled ? 'nocursor' : false;
|
||||
} else {
|
||||
// desktop cannot use 'nocursor' as it prevents copy/paste
|
||||
return props.disabled;
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.disabled,
|
||||
(disabled) => {
|
||||
codemirror?.setOption('readOnly', readOnly.value);
|
||||
codemirror?.setOption('cursorBlinkRate', disabled ? -1 : 530);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.system-raw-editor {
|
||||
position: relative;
|
||||
height: var(--input-height);
|
||||
min-height: var(--input-height);
|
||||
border-radius: var(--border-radius);
|
||||
|
||||
:deep(.CodeMirror) {
|
||||
width: 100%;
|
||||
line-height: 18px;
|
||||
padding: var(--input-padding);
|
||||
|
||||
.cm-tag {
|
||||
color: var(--foreground-subdued);
|
||||
}
|
||||
|
||||
.cm-variable-2 {
|
||||
color: var(--secondary);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.CodeMirror),
|
||||
:deep(.CodeMirror-scroll) {
|
||||
max-height: var(--input-height);
|
||||
}
|
||||
|
||||
&.multi-line {
|
||||
height: auto;
|
||||
|
||||
:deep(.CodeMirror),
|
||||
:deep(.CodeMirror-scroll) {
|
||||
max-height: 480px;
|
||||
}
|
||||
|
||||
:deep(.CodeMirror-scroll) {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -14,6 +14,7 @@
|
||||
:loading="loading"
|
||||
:validation-errors="validationErrors"
|
||||
:badge="badge"
|
||||
:raw-editor-enabled="rawEditorEnabled"
|
||||
:group="field.meta.field"
|
||||
:multiple="accordionMode === false"
|
||||
@apply="$emit('apply', $event)"
|
||||
@@ -77,6 +78,10 @@ export default defineComponent({
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
rawEditorEnabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
accordionMode: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
:loading="loading"
|
||||
:disabled="disabled"
|
||||
:badge="badge"
|
||||
:raw-editor-enabled="rawEditorEnabled"
|
||||
nested
|
||||
@update:model-value="$emit('apply', $event)"
|
||||
/>
|
||||
@@ -66,6 +67,10 @@ export default defineComponent({
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
rawEditorEnabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['apply'],
|
||||
});
|
||||
|
||||
@@ -55,7 +55,7 @@ export default defineComponent({
|
||||
default: false,
|
||||
},
|
||||
value: {
|
||||
type: Array as PropType<string[]>,
|
||||
type: [Array, String] as PropType<string[] | string>,
|
||||
default: null,
|
||||
},
|
||||
placeholder: {
|
||||
@@ -100,7 +100,7 @@ export default defineComponent({
|
||||
return [];
|
||||
});
|
||||
|
||||
const selectedValsLocal = ref<string[]>(processArray(props.value || []));
|
||||
const selectedValsLocal = ref<string[]>(Array.isArray(props.value) ? processArray(props.value) : []);
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
|
||||
@@ -315,6 +315,8 @@ primary_key: Primary Key
|
||||
foreign_key: Foreign Key
|
||||
finish_setup: Finish Setup
|
||||
dismiss: Dismiss
|
||||
toggle_raw_editor: Toggle Raw Editor
|
||||
raw_editor_placeholder: "{'{'}{'{'}$trigger.payload.example{'}'}{'}'}"
|
||||
raw_value: Raw value
|
||||
copy_raw_value: Copy Raw Value
|
||||
copy_raw_value_success: Copied
|
||||
@@ -1725,6 +1727,9 @@ interfaces:
|
||||
regenerate: Regenerate Token
|
||||
generate_success_copy: Make sure to backup and copy the token above. For security reasons, you will not be able to view the token again after saving and navigate off this page.
|
||||
remove_token: Remove Token
|
||||
system-raw-editor:
|
||||
system-raw-editor: Raw Editor
|
||||
description: Allow entering of raw or mustache templating values
|
||||
displays:
|
||||
translations:
|
||||
translations: Translations
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
:options="customOptionsFields"
|
||||
type="panel"
|
||||
:extension="panel.type"
|
||||
raw-editor-enabled
|
||||
@update:model-value="edits.options = $event"
|
||||
/>
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
:fields="optionsFields"
|
||||
:initial-values="disabled ? optionsValues : null"
|
||||
:disabled="disabled"
|
||||
:raw-editor-enabled="rawEditorEnabled"
|
||||
primary-key="+"
|
||||
/>
|
||||
|
||||
@@ -59,6 +60,10 @@ export default defineComponent({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
rawEditorEnabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
setup(props, { emit }) {
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
v-model="options"
|
||||
:extension="operationType"
|
||||
:options="operationOptions"
|
||||
raw-editor-enabled
|
||||
type="operation"
|
||||
></extension-options>
|
||||
<component
|
||||
|
||||
@@ -69,7 +69,7 @@ export default defineOperationApp({
|
||||
{
|
||||
field: 'payload',
|
||||
name: '$t:operations.item-create.payload',
|
||||
type: 'string',
|
||||
type: 'json',
|
||||
meta: {
|
||||
width: 'full',
|
||||
interface: 'input-code',
|
||||
|
||||
@@ -86,7 +86,7 @@ export default defineOperationApp({
|
||||
{
|
||||
field: 'query',
|
||||
name: '$t:operations.item-delete.query',
|
||||
type: 'string',
|
||||
type: 'json',
|
||||
meta: {
|
||||
width: 'full',
|
||||
interface: 'input-code',
|
||||
|
||||
@@ -74,7 +74,7 @@ export default defineOperationApp({
|
||||
{
|
||||
field: 'query',
|
||||
name: '$t:operations.item-read.query',
|
||||
type: 'string',
|
||||
type: 'json',
|
||||
meta: {
|
||||
width: 'full',
|
||||
interface: 'input-code',
|
||||
|
||||
@@ -85,7 +85,7 @@ export default defineOperationApp({
|
||||
{
|
||||
field: 'payload',
|
||||
name: '$t:operations.item-update.payload',
|
||||
type: 'string',
|
||||
type: 'json',
|
||||
meta: {
|
||||
width: 'full',
|
||||
interface: 'input-code',
|
||||
@@ -98,7 +98,7 @@ export default defineOperationApp({
|
||||
{
|
||||
field: 'query',
|
||||
name: '$t:operations.item-update.query',
|
||||
type: 'string',
|
||||
type: 'json',
|
||||
meta: {
|
||||
width: 'full',
|
||||
interface: 'input-code',
|
||||
|
||||
@@ -48,7 +48,7 @@ export default defineOperationApp({
|
||||
{
|
||||
field: 'body',
|
||||
name: '$t:operations.mail.body',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
meta: {
|
||||
width: 'full',
|
||||
interface: 'input-rich-text-md',
|
||||
|
||||
@@ -73,7 +73,7 @@ export default defineOperationApp({
|
||||
{
|
||||
field: 'message',
|
||||
name: '$t:operations.notification.message',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
meta: {
|
||||
width: 'full',
|
||||
interface: 'input-rich-text-md',
|
||||
|
||||
@@ -52,7 +52,7 @@ export default defineOperationApp({
|
||||
{
|
||||
field: 'headers',
|
||||
name: '$t:operations.request.headers',
|
||||
type: 'string',
|
||||
type: 'json',
|
||||
meta: {
|
||||
width: 'full',
|
||||
interface: 'list',
|
||||
@@ -89,7 +89,7 @@ export default defineOperationApp({
|
||||
{
|
||||
field: 'body',
|
||||
name: '$t:request_body',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
meta: {
|
||||
width: 'full',
|
||||
interface: 'input-multiline',
|
||||
|
||||
@@ -15,7 +15,7 @@ export default defineOperationApp({
|
||||
{
|
||||
field: 'json',
|
||||
name: '$t:json',
|
||||
type: 'string',
|
||||
type: 'json',
|
||||
meta: {
|
||||
width: 'full',
|
||||
interface: 'input-code',
|
||||
|
||||
@@ -28,7 +28,7 @@ export default defineOperationApp({
|
||||
{
|
||||
field: 'payload',
|
||||
name: '$t:payload',
|
||||
type: 'string',
|
||||
type: 'json',
|
||||
meta: {
|
||||
width: 'full',
|
||||
interface: 'input-code',
|
||||
|
||||
Reference in New Issue
Block a user