@scalar/api-client 2.39.2 → 2.39.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/style.css +45 -41
- package/dist/v2/blocks/request-block/components/RequestTableRow.vue.d.ts.map +1 -1
- package/dist/v2/blocks/request-block/components/RequestTableRow.vue.js.map +1 -1
- package/dist/v2/blocks/request-block/components/RequestTableRow.vue.script.js +10 -8
- package/dist/v2/blocks/request-block/components/RequestTableRow.vue.script.js.map +1 -1
- package/dist/v2/components/code-input/CodeInput.vue.d.ts +8 -2
- package/dist/v2/components/code-input/CodeInput.vue.d.ts.map +1 -1
- package/dist/v2/components/code-input/CodeInput.vue.js +1 -1
- package/dist/v2/components/code-input/CodeInput.vue.js.map +1 -1
- package/dist/v2/components/code-input/CodeInput.vue.script.js +5 -2
- package/dist/v2/components/code-input/CodeInput.vue.script.js.map +1 -1
- package/dist/v2/constants.js +1 -1
- package/dist/v2/features/collection/components/Editor/Editor.vue.d.ts.map +1 -1
- package/dist/v2/features/collection/components/Editor/Editor.vue.js +1 -1
- package/dist/v2/features/collection/components/Editor/Editor.vue.js.map +1 -1
- package/dist/v2/features/collection/components/Editor/Editor.vue.script.js +2 -2
- package/dist/v2/features/collection/components/Editor/Editor.vue.script.js.map +1 -1
- package/dist/v2/features/editor/helpers/theme/apply-scalar-theme.js.map +1 -1
- package/dist/v2/features/editor/helpers/theme/load-css-variables.d.ts +25 -3
- package/dist/v2/features/editor/helpers/theme/load-css-variables.d.ts.map +1 -1
- package/dist/v2/features/editor/helpers/theme/load-css-variables.js +92 -16
- package/dist/v2/features/editor/helpers/theme/load-css-variables.js.map +1 -1
- package/dist/views/Request/ResponseSection/ResponseEmpty.vue.script.js +1 -1
- package/package.json +11 -11
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeInput.vue.js","names":[],"sources":["../../../../src/v2/components/code-input/CodeInput.vue"],"sourcesContent":["<script lang=\"ts\">\nexport type CodeInputModelValue =\n | string\n | number\n | boolean\n | Array<string | number | boolean>\n | Record<string, unknown>\n\n/**\n * CodeInput\n *\n * A versatile input component that adapts its rendering based on props:\n * - Disabled mode: Read-only text display\n * - Select mode: Dropdown for enums, booleans, or examples\n * - Editor mode: CodeMirror with environment variable support\n *\n * Type `{{` to trigger environment variable autocomplete when an environment is provided.\n * It takes in any data but always will emit a string value,\n * this string should then be parsed in accordance to the schema or content type.\n *\n * @example\n * ```vue\n * <!-- Basic input with environment variables -->\n * <CodeInput v-model=\"value\" :environment=\"env\" />\n *\n * <!-- Boolean select -->\n * <CodeInput v-model=\"flag\" type=\"boolean\" />\n *\n * <!-- JSON editor with linting -->\n * <CodeInput v-model=\"data\" language=\"json\" :lint=\"true\" />\n * ```\n */\nexport default {\n inheritAttrs: false,\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { isDefined } from '@scalar/helpers/array/is-defined'\nimport {\n colorPicker as colorPickerExtension,\n useCodeMirror,\n useDropdown,\n type CodeMirrorLanguage,\n type Extension,\n} from '@scalar/use-codemirror'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport { nanoid } from 'nanoid'\nimport { computed, ref, toRef, useAttrs, watch, type Ref } from 'vue'\n\nimport DataTableInputSelect from '@/v2/components/data-table/DataTableInputSelect.vue'\nimport EnvironmentVariableDropdown from '@/v2/features/environments/components/EnvironmentVariablesDropdown.vue'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nimport { backspaceCommand, pillPlugin } from './code-variable-widget'\n\ntype Props = {\n modelValue: CodeInputModelValue\n /** Environment for variable substitution. Pass undefined to disable environment variables */\n environment: XScalarEnvironment | undefined\n /** Type of the input value, affects rendering mode for booleans */\n type?: string | string[]\n /** Render as disabled text display */\n disabled?: boolean\n /** Show error styling */\n error?: boolean\n /** Layout context affects styling and behavior */\n layout?: ClientLayout\n /** Predefined enum values, triggers select mode */\n enum?: string[]\n /** Example values, triggers select mode */\n examples?: string[]\n /** Default value to show in select mode */\n default?: Props['modelValue']\n /** Allow null in boolean select options */\n nullable?: boolean\n /** Placeholder text for empty input */\n placeholder?: string\n /** Show required indicator */\n required?: boolean\n /** Enable color picker extension */\n colorPicker?: boolean\n /** Show line numbers in editor */\n lineNumbers?: boolean\n /** Enable linting */\n lint?: boolean\n /** Enable line wrapping */\n lineWrapping?: boolean\n /** CodeMirror language mode */\n language?: CodeMirrorLanguage\n /** Additional CodeMirror extensions */\n extensions?: Extension[]\n /** Disable tab key for indentation */\n disableTabIndent?: boolean\n /** Disable enter key */\n disableEnter?: boolean\n /** Disable automatic bracket closing */\n disableCloseBrackets?: boolean\n /** Emit submit event on blur */\n emitOnBlur?: boolean\n /** Enable environment variable pills */\n withVariables?: boolean\n /** Emit change event even if the value is the same */\n alwaysEmitChange?: boolean\n /** Custom change handler, prevents default emit */\n handleFieldChange?: (value: string) => void\n /** Custom submit handler, prevents default emit */\n handleFieldSubmit?: (value: string) => void\n /** Put a linethrough on the input text */\n linethrough?: boolean\n}\n\nconst {\n modelValue,\n environment,\n type,\n disabled = false,\n error = false,\n layout = 'desktop',\n enum: enumProp,\n examples,\n default: defaultProp,\n nullable = false,\n placeholder,\n required,\n colorPicker = false,\n lineNumbers = false,\n lint = false,\n lineWrapping = false,\n language,\n extensions = [],\n disableTabIndent = false,\n disableEnter = false,\n disableCloseBrackets = false,\n emitOnBlur = true,\n alwaysEmitChange = false,\n withVariables = true,\n handleFieldChange,\n handleFieldSubmit,\n} = defineProps<Props>()\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string]\n 'submit': [value: string]\n 'blur': [value: string]\n 'redirectToEnvironment': []\n}>()\n\n// ---------------------------------------------------------------------------\n// Component identity and focus state\n\nconst attrs = useAttrs() as { id?: string }\nconst componentId = attrs.id || `id-${nanoid()}`\nconst isFocused = ref(false)\n\n// ---------------------------------------------------------------------------\n// Rendering mode detection\n\n/**\n * Determines if we should render a select dropdown for boolean types.\n */\nconst isBooleanMode = computed((): boolean => {\n if (enumProp?.length) {\n return false\n }\n return type === 'boolean' || (Array.isArray(type) && type.includes('boolean'))\n})\n\n/**\n * Options for boolean select mode.\n */\nconst booleanOptions = computed((): string[] =>\n nullable ? ['true', 'false', 'null'] : ['true', 'false'],\n)\n\n/**\n * Default type when dealing with type arrays.\n * Finds the first non-null type.\n */\nconst defaultType = computed((): string | undefined => {\n if (Array.isArray(type)) {\n return type.find((t) => t !== 'null') ?? 'string'\n }\n return type\n})\n\n// ---------------------------------------------------------------------------\n// Event handlers\n\n/**\n * Handles value changes during typing.\n */\nconst handleChange = (value: string): void => {\n if (!alwaysEmitChange && value === serializeValue(modelValue)) {\n return\n }\n\n // Use custom handler or emit update\n if (handleFieldChange) {\n handleFieldChange(value)\n } else {\n emit('update:modelValue', value)\n }\n}\n\n/**\n * Handles form submission (enter key or blur with emitOnBlur).\n */\nconst handleSubmit = (value: string): void => {\n if (handleFieldSubmit) {\n handleFieldSubmit(value)\n } else {\n emit('submit', value)\n }\n}\n\n/**\n * Handles input blur event.\n */\nconst handleBlur = (value: string): void => {\n isFocused.value = false\n\n if (emitOnBlur && modelValue) {\n handleSubmit(value)\n }\n\n emit('blur', value)\n}\n\n/**\n * Handles model value updates from select components.\n */\nconst handleSelectChange = (value: string): void =>\n emit('update:modelValue', value)\n\n// ---------------------------------------------------------------------------\n// CodeMirror setup\n\n/**\n * Build extensions array.\n * Note: Extensions are not reactive after initialization.\n */\nconst buildExtensions = (): Extension[] => {\n const extensionsList: Extension[] = [...extensions]\n\n if (colorPicker) {\n extensionsList.push(colorPickerExtension)\n }\n\n return extensionsList\n}\n\n/**\n * Reactive pill plugin for environment variable visualization.\n */\nconst pillPluginExtension = computed(() =>\n pillPlugin({\n environment,\n isReadOnly: layout === 'modal',\n }),\n)\n\n/**\n * Combined extensions for CodeMirror.\n */\nconst codeMirrorExtensions = computed((): Extension[] => [\n ...buildExtensions(),\n pillPluginExtension.value,\n backspaceCommand,\n])\n\nconst codeMirrorRef: Ref<HTMLDivElement | null> = ref(null)\n\n/** Converts the model value to a string for CodeMirror */\nconst serializeValue = (value: CodeInputModelValue): string => {\n if (typeof value === 'string') {\n return value\n }\n return JSON.stringify(value)\n}\n\nconst { codeMirror } = useCodeMirror({\n content: toRef(() => serializeValue(modelValue)),\n onChange: (value) => {\n handleChange(value)\n updateDropdownVisibility()\n },\n onFocus: () => {\n isFocused.value = true\n },\n onBlur: handleBlur,\n codeMirrorRef,\n disableTabIndent: toRef(() => disableTabIndent),\n disableEnter: toRef(() => disableEnter),\n disableCloseBrackets: toRef(() => disableCloseBrackets),\n lineNumbers: toRef(() => lineNumbers),\n language: toRef(() => language),\n lint: toRef(() => lint),\n extensions: codeMirrorExtensions,\n placeholder: toRef(() => placeholder),\n})\n\n/**\n * Handle autofocus attribute.\n */\nwatch(codeMirror, () => {\n if (codeMirror.value && Object.hasOwn(attrs, 'autofocus')) {\n codeMirror.value.focus()\n }\n})\n\n// ---------------------------------------------------------------------------\n// Environment variable dropdown\n\nconst showDropdown = ref(false)\nconst dropdownQuery = ref('')\nconst dropdownPosition = ref({ left: 0, top: 0 })\nconst dropdownRef = ref<InstanceType<\n typeof EnvironmentVariableDropdown\n> | null>(null)\n\nconst { handleDropdownSelect, updateDropdownVisibility } = useDropdown({\n codeMirror,\n query: dropdownQuery,\n showDropdown,\n dropdownPosition,\n})\n\n/**\n * Determines if the environment variable dropdown should be visible.\n */\nconst displayVariablesDropdown = computed((): boolean => {\n return (\n showDropdown.value &&\n withVariables &&\n layout !== 'modal' &&\n Boolean(environment)\n )\n})\n\n// ---------------------------------------------------------------------------\n// Keyboard event handling\n\n/**\n * Handles keyboard navigation for dropdown and form submission.\n */\nconst handleKeyDown = (key: string, event: KeyboardEvent): void => {\n if (showDropdown.value) {\n if (key === 'down' || key === 'up') {\n event.preventDefault()\n dropdownRef.value?.handleArrowKey(key)\n } else if (key === 'enter') {\n event.preventDefault()\n dropdownRef.value?.handleSelect()\n }\n return\n }\n\n if (key === 'escape' && !disableTabIndent) {\n event.stopPropagation()\n }\n\n if (key === 'enter' && event.target instanceof HTMLDivElement) {\n handleSubmit(event.target.textContent ?? '')\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n\ndefineExpose({\n /**\n * Focus the codemirror element\n *\n * @param cursorAtEnd boolean place the cursor at the end of the input\n */\n focus: (position?: 'start' | 'end' | number) => {\n if (!codeMirror.value) {\n return\n }\n codeMirror.value.focus()\n\n if (!isDefined(position)) {\n return\n }\n\n const anchor = (() => {\n if (position === 'start') {\n return 0\n }\n if (position === 'end') {\n return codeMirror.value.state.doc.length\n }\n return position\n })()\n\n // Move the cursor to the specified position\n codeMirror.value.dispatch({\n selection: { anchor },\n scrollIntoView: true,\n })\n },\n isFocused,\n handleChange,\n handleSubmit,\n handleBlur,\n booleanOptions,\n codeMirror,\n modelValue,\n cursorPosition: () => codeMirror.value?.state.selection.main.head,\n serializeValue,\n})\n</script>\n\n<template>\n <!-- Disabled mode: read-only text display -->\n <div\n v-if=\"disabled\"\n class=\"text-c-2 flex cursor-default items-center justify-center\"\n :class=\"{\n 'font-code pr-2 pl-1 text-base': layout === 'modal',\n 'px-2': layout !== 'modal',\n 'line-through': linethrough,\n }\"\n data-testid=\"code-input-disabled\">\n <span class=\"whitespace-nowrap\">{{ modelValue }}</span>\n </div>\n\n <!-- Enum mode: select dropdown with predefined values -->\n <DataTableInputSelect\n v-else-if=\"enumProp?.length\"\n :default=\"defaultProp\"\n :modelValue=\"modelValue\"\n :type=\"defaultType\"\n :value=\"enumProp\"\n @update:modelValue=\"handleSelectChange\" />\n\n <!-- Boolean mode: select dropdown with true/false (and optionally null) -->\n <DataTableInputSelect\n v-else-if=\"isBooleanMode\"\n :default=\"defaultProp\"\n :modelValue=\"modelValue\"\n :value=\"booleanOptions\"\n @update:modelValue=\"handleSelectChange\" />\n\n <!-- Examples mode: select dropdown with example values -->\n <DataTableInputSelect\n v-else-if=\"examples?.length\"\n :default=\"defaultProp\"\n :modelValue=\"modelValue\"\n :value=\"examples\"\n @update:modelValue=\"handleSelectChange\" />\n\n <!-- Editor mode: CodeMirror with environment variable support -->\n <div\n v-else\n :id=\"componentId\"\n v-bind=\"$attrs\"\n ref=\"codeMirrorRef\"\n class=\"group/input group-[.alert]:outline-orange group-[.error]:outline-red font-code peer relative w-full overflow-hidden text-xs leading-[1.44] whitespace-nowrap -outline-offset-1 has-[:focus-visible]:rounded-[4px] has-[:focus-visible]:outline\"\n :class=\"{\n 'line-wrapping has-[:focus-visible]:bg-b-1 has-[:focus-visible]:absolute has-[:focus-visible]:z-1':\n lineWrapping,\n 'flow-code-input--error': error,\n 'line-through': linethrough,\n }\"\n @keydown.down.stop=\"handleKeyDown('down', $event)\"\n @keydown.enter=\"handleKeyDown('enter', $event)\"\n @keydown.escape=\"handleKeyDown('escape', $event)\"\n @keydown.up.stop=\"handleKeyDown('up', $event)\">\n <!-- Tab exit hint (shown when focused) -->\n <div\n v-if=\"!disableTabIndent\"\n class=\"z-context text-c-2 absolute right-1.5 bottom-1 hidden font-sans group-has-[:focus-visible]/input:block\"\n role=\"alert\">\n Press\n <kbd class=\"-mx-0.25 rounded border px-0.5 font-mono\">Esc</kbd> then\n <kbd class=\"-mx-0.25 rounded border px-0.5 font-mono\">Tab</kbd> to exit\n </div>\n </div>\n\n <!-- Warning slot (positioned absolutely) -->\n <div\n v-if=\"$slots.warning\"\n class=\"centered-y text-orange absolute right-7 text-xs\">\n <slot name=\"warning\" />\n </div>\n\n <!-- Icon slot (positioned absolutely) -->\n <div\n v-if=\"$slots.icon\"\n class=\"centered-y absolute right-0 flex h-full items-center p-1.5 group-has-[.cm-focused]:z-1\">\n <slot name=\"icon\" />\n </div>\n\n <!-- Required indicator -->\n <div\n v-if=\"required\"\n class=\"required centered-y text-xxs text-c-3 group-[.error]:text-red bg-b-1 pointer-events-none absolute right-0 mr-0.5 pt-px pr-2 opacity-100 shadow-[-8px_0_4px_var(--scalar-background-1)] transition-opacity duration-150 group-[.alert]:bg-transparent group-[.alert]:shadow-none group-[.error]:bg-transparent group-[.error]:shadow-none peer-has-[.cm-focused]:opacity-0\">\n Required\n </div>\n\n <!-- Environment variable autocomplete dropdown -->\n <EnvironmentVariableDropdown\n v-if=\"displayVariablesDropdown && environment\"\n ref=\"dropdownRef\"\n :dropdownPosition=\"dropdownPosition\"\n :environment=\"environment\"\n :query=\"dropdownQuery\"\n @redirect=\"emit('redirectToEnvironment')\"\n @select=\"handleDropdownSelect\" />\n</template>\n<style scoped>\n/*\n Deep styling for customizing Codemirror\n */\n:deep(.cm-editor) {\n height: 100%;\n outline: none;\n padding: 0;\n background: transparent;\n}\n:deep(.cm-placeholder) {\n color: var(--scalar-color-3);\n}\n:deep(.cm-content) {\n font-family: var(--scalar-font-code);\n font-size: var(--scalar-small);\n max-height: 20px;\n padding: 8px 0;\n}\n/* Tooltip helper */\n:deep(.cm-tooltip) {\n background: transparent !important;\n filter: brightness(var(--scalar-lifted-brightness));\n border-radius: var(--scalar-radius);\n box-shadow: var(--scalar-shadow-2);\n border: none !important;\n outline: none !important;\n overflow: hidden !important;\n}\n:deep(.cm-tooltip-autocomplete ul li) {\n padding: 3px 6px !important;\n}\n:deep(.cm-completionIcon-type:after) {\n color: var(--scalar-color-3) !important;\n}\n:deep(.cm-tooltip-autocomplete ul li[aria-selected]) {\n background: var(--scalar-background-2) !important;\n color: var(--scalar-color-1) !important;\n}\n:deep(.cm-tooltip-autocomplete ul) {\n padding: 6px !important;\n position: relative;\n}\n:deep(.cm-tooltip-autocomplete ul li:hover) {\n border-radius: 3px;\n color: var(--scalar-color-1) !important;\n background: var(--scalar-background-3) !important;\n}\n/* Disable active line highlighting */\n:deep(.cm-activeLine),\n:deep(.cm-activeLineGutter) {\n background-color: transparent;\n}\n/* Color selection matching */\n:deep(.cm-selectionMatch),\n:deep(.cm-matchingBracket) {\n border-radius: var(--scalar-radius);\n background: var(--scalar-background-4) !important;\n}\n/* Color Picker Swatches */\n:deep(.cm-css-color-picker-wrapper) {\n display: inline-flex;\n outline: 1px solid var(--scalar-background-3);\n border-radius: 3px;\n overflow: hidden;\n}\n/* Number gutter */\n:deep(.cm-gutters) {\n background-color: transparent;\n border-right: none;\n color: var(--scalar-color-3);\n font-size: var(--scalar-small);\n line-height: 22px;\n border-radius: 0 0 0 3px;\n}\n:deep(.cm-gutters:before) {\n content: '';\n position: absolute;\n top: 2px;\n left: 2px;\n width: calc(100% - 2px);\n height: calc(100% - 4px);\n border-radius: var(--scalar-radius) 0 0 var(--scalar-radius);\n background-color: var(--scalar-background-1);\n}\n:deep(.cm-gutterElement) {\n font-family: var(--scalar-font-code) !important;\n padding-left: 0px !important;\n padding-right: 6px !important;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n position: relative;\n}\n:deep(.cm-lineNumbers .cm-gutterElement) {\n min-width: fit-content;\n}\n:deep(.cm-gutter + .cm-gutter :not(.cm-foldGutter) .cm-gutterElement) {\n padding-left: 0 !important;\n}\n:deep(.cm-scroller) {\n overflow: auto;\n}\n.line-wrapping:focus-within :deep(.cm-content) {\n display: inline-table;\n min-height: fit-content;\n padding: 3px 6px;\n white-space: break-spaces;\n word-break: break-all;\n}\n</style>\n<style>\n.cm-pill {\n color: var(--scalar-color-1) !important;\n padding: 0px 9px;\n border-radius: 3px;\n display: inline-block;\n border-radius: 30px;\n font-size: var(--scalar-small);\n}\n.light-mode .cm-pill {\n background: var(--scalar-background-3) !important;\n}\n.dark-mode .cm-pill {\n background: color-mix(in srgb, var(--tw-bg-base), transparent 90%) !important;\n}\n.cm-pill:first-of-type {\n margin-left: 0;\n}\n.cm-editor .cm-widgetBuffer {\n display: none;\n}\n.cm-foldPlaceholder:hover {\n color: var(--scalar-color-1);\n}\n.cm-foldGutter .cm-gutterElement {\n font-size: var(--scalar-heading-4);\n padding: 2px !important;\n}\n.cm-foldGutter .cm-gutterElement:first-of-type {\n display: none;\n}\n.cm-foldGutter .cm-gutterElement .cm-foldMarker {\n padding: 2px;\n padding-top: 2px;\n}\n.cm-foldGutter .cm-gutterElement:hover .cm-foldMarker {\n background: var(--scalar-background-2);\n border-radius: var(--scalar-radius);\n color: var(--scalar-color-1);\n}\n</style>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"CodeInput.vue.js","names":[],"sources":["../../../../src/v2/components/code-input/CodeInput.vue"],"sourcesContent":["<script lang=\"ts\">\nexport type CodeInputModelValue =\n | string\n | number\n | boolean\n | Array<string | number | boolean>\n | Record<string, unknown>\n\n/**\n * CodeInput\n *\n * A versatile input component that adapts its rendering based on props:\n * - Disabled mode: Read-only text display\n * - Select mode: Dropdown for enums, booleans, or examples\n * - Editor mode: CodeMirror with environment variable support\n *\n * Type `{{` to trigger environment variable autocomplete when an environment is provided.\n * It takes in any data but always will emit a string value,\n * this string should then be parsed in accordance to the schema or content type.\n *\n * @example\n * ```vue\n * <!-- Basic input with environment variables -->\n * <CodeInput v-model=\"value\" :environment=\"env\" />\n *\n * <!-- Boolean select -->\n * <CodeInput v-model=\"flag\" type=\"boolean\" />\n *\n * <!-- JSON editor with linting -->\n * <CodeInput v-model=\"data\" language=\"json\" :lint=\"true\" />\n * ```\n */\nexport default {\n inheritAttrs: false,\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { isDefined } from '@scalar/helpers/array/is-defined'\nimport {\n colorPicker as colorPickerExtension,\n useCodeMirror,\n useDropdown,\n type CodeMirrorLanguage,\n type Extension,\n} from '@scalar/use-codemirror'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport { nanoid } from 'nanoid'\nimport { computed, ref, toRef, useAttrs, watch, type Ref } from 'vue'\n\nimport DataTableInputSelect from '@/v2/components/data-table/DataTableInputSelect.vue'\nimport EnvironmentVariableDropdown from '@/v2/features/environments/components/EnvironmentVariablesDropdown.vue'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nimport { backspaceCommand, pillPlugin } from './code-variable-widget'\n\ntype Props = {\n modelValue: CodeInputModelValue\n /** Environment for variable substitution. Pass undefined to disable environment variables */\n environment: XScalarEnvironment | undefined\n /** Type of the input value, affects rendering mode for booleans */\n type?: string | string[]\n /** Render as disabled text display */\n disabled?: boolean\n /** Show error styling */\n error?: boolean\n /** Layout context affects styling and behavior */\n layout?: ClientLayout\n /** Predefined enum values, triggers select mode */\n enum?: string[]\n /** Example values, triggers select mode */\n examples?: string[]\n /** Default value to show in select mode */\n default?: Props['modelValue']\n /** Allow null in boolean select options */\n nullable?: boolean\n /** Placeholder text for empty input */\n placeholder?: string\n /** Show required indicator */\n required?: boolean\n /** Enable color picker extension */\n colorPicker?: boolean\n /** Show line numbers in editor */\n lineNumbers?: boolean\n /** Enable linting */\n lint?: boolean\n /** Enable line wrapping */\n lineWrapping?: boolean\n /** CodeMirror language mode */\n language?: CodeMirrorLanguage\n /** Additional CodeMirror extensions */\n extensions?: Extension[]\n /** Disable tab key for indentation */\n disableTabIndent?: boolean\n /** Disable enter key */\n disableEnter?: boolean\n /** Disable automatic bracket closing */\n disableCloseBrackets?: boolean\n /** Emit submit event on blur */\n emitOnBlur?: boolean\n /** Enable environment variable pills */\n withVariables?: boolean\n /** Emit change event even if the value is the same */\n alwaysEmitChange?: boolean\n /** Custom change handler, prevents default emit */\n handleFieldChange?: (value: string) => void\n /** Custom submit handler, prevents default emit */\n handleFieldSubmit?: (value: string) => void\n /** Put a linethrough on the input text */\n linethrough?: boolean\n}\n\nconst {\n modelValue,\n environment,\n type,\n disabled = false,\n error = false,\n layout = 'desktop',\n enum: enumProp,\n examples,\n default: defaultProp,\n nullable = false,\n placeholder,\n required,\n colorPicker = false,\n lineNumbers = false,\n lint = false,\n lineWrapping = false,\n language,\n extensions = [],\n disableTabIndent = false,\n disableEnter = false,\n disableCloseBrackets = false,\n emitOnBlur = true,\n alwaysEmitChange = false,\n withVariables = true,\n handleFieldChange,\n handleFieldSubmit,\n} = defineProps<Props>()\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string]\n 'submit': [value: string]\n 'blur': [value: string]\n 'navigate': [route: { page: 'document'; path: 'environment' }]\n}>()\n\n// ---------------------------------------------------------------------------\n// Component identity and focus state\n\nconst attrs = useAttrs() as { id?: string }\nconst componentId = attrs.id || `id-${nanoid()}`\nconst isFocused = ref(false)\n\n// ---------------------------------------------------------------------------\n// Rendering mode detection\n\n/**\n * Determines if we should render a select dropdown for boolean types.\n */\nconst isBooleanMode = computed((): boolean => {\n if (enumProp?.length) {\n return false\n }\n return type === 'boolean' || (Array.isArray(type) && type.includes('boolean'))\n})\n\n/**\n * Options for boolean select mode.\n */\nconst booleanOptions = computed((): string[] =>\n nullable ? ['true', 'false', 'null'] : ['true', 'false'],\n)\n\n/**\n * Default type when dealing with type arrays.\n * Finds the first non-null type.\n */\nconst defaultType = computed((): string | undefined => {\n if (Array.isArray(type)) {\n return type.find((t) => t !== 'null') ?? 'string'\n }\n return type\n})\n\n// ---------------------------------------------------------------------------\n// Event handlers\n\n/**\n * Handles value changes during typing.\n */\nconst handleChange = (value: string): void => {\n if (!alwaysEmitChange && value === serializeValue(modelValue)) {\n return\n }\n\n // Use custom handler or emit update\n if (handleFieldChange) {\n handleFieldChange(value)\n } else {\n emit('update:modelValue', value)\n }\n}\n\n/**\n * Handles form submission (enter key or blur with emitOnBlur).\n */\nconst handleSubmit = (value: string): void => {\n if (handleFieldSubmit) {\n handleFieldSubmit(value)\n } else {\n emit('submit', value)\n }\n}\n\n/**\n * Handles input blur event.\n */\nconst handleBlur = (value: string): void => {\n isFocused.value = false\n\n if (emitOnBlur && modelValue) {\n handleSubmit(value)\n }\n\n emit('blur', value)\n}\n\n/**\n * Handles model value updates from select components.\n */\nconst handleSelectChange = (value: string): void =>\n emit('update:modelValue', value)\n\n// ---------------------------------------------------------------------------\n// CodeMirror setup\n\n/**\n * Build extensions array.\n * Note: Extensions are not reactive after initialization.\n */\nconst buildExtensions = (): Extension[] => {\n const extensionsList: Extension[] = [...extensions]\n\n if (colorPicker) {\n extensionsList.push(colorPickerExtension)\n }\n\n return extensionsList\n}\n\n/**\n * Reactive pill plugin for environment variable visualization.\n */\nconst pillPluginExtension = computed(() =>\n pillPlugin({\n environment,\n isReadOnly: layout === 'modal',\n }),\n)\n\n/**\n * Combined extensions for CodeMirror.\n */\nconst codeMirrorExtensions = computed((): Extension[] => [\n ...buildExtensions(),\n pillPluginExtension.value,\n backspaceCommand,\n])\n\nconst codeMirrorRef: Ref<HTMLDivElement | null> = ref(null)\n\n/** Converts the model value to a string for CodeMirror */\nconst serializeValue = (value: CodeInputModelValue): string => {\n if (typeof value === 'string') {\n return value\n }\n return JSON.stringify(value)\n}\n\nconst { codeMirror } = useCodeMirror({\n content: toRef(() => serializeValue(modelValue)),\n onChange: (value) => {\n handleChange(value)\n updateDropdownVisibility()\n },\n onFocus: () => {\n isFocused.value = true\n },\n onBlur: handleBlur,\n codeMirrorRef,\n disableTabIndent: toRef(() => disableTabIndent),\n disableEnter: toRef(() => disableEnter),\n disableCloseBrackets: toRef(() => disableCloseBrackets),\n lineNumbers: toRef(() => lineNumbers),\n language: toRef(() => language),\n lint: toRef(() => lint),\n extensions: codeMirrorExtensions,\n placeholder: toRef(() => placeholder),\n})\n\n/**\n * Handle autofocus attribute.\n */\nwatch(codeMirror, () => {\n if (codeMirror.value && Object.hasOwn(attrs, 'autofocus')) {\n codeMirror.value.focus()\n }\n})\n\n// ---------------------------------------------------------------------------\n// Environment variable dropdown\n\nconst showDropdown = ref(false)\nconst dropdownQuery = ref('')\nconst dropdownPosition = ref({ left: 0, top: 0 })\nconst dropdownRef = ref<InstanceType<\n typeof EnvironmentVariableDropdown\n> | null>(null)\n\nconst { handleDropdownSelect, updateDropdownVisibility } = useDropdown({\n codeMirror,\n query: dropdownQuery,\n showDropdown,\n dropdownPosition,\n})\n\n/**\n * Determines if the environment variable dropdown should be visible.\n */\nconst displayVariablesDropdown = computed((): boolean => {\n return (\n showDropdown.value &&\n withVariables &&\n layout !== 'modal' &&\n Boolean(environment)\n )\n})\n\n// ---------------------------------------------------------------------------\n// Keyboard event handling\n\n/**\n * Handles keyboard navigation for dropdown and form submission.\n */\nconst handleKeyDown = (key: string, event: KeyboardEvent): void => {\n if (showDropdown.value) {\n if (key === 'down' || key === 'up') {\n event.preventDefault()\n dropdownRef.value?.handleArrowKey(key)\n } else if (key === 'enter') {\n event.preventDefault()\n dropdownRef.value?.handleSelect()\n }\n return\n }\n\n if (key === 'escape' && !disableTabIndent) {\n event.stopPropagation()\n }\n\n if (key === 'enter' && event.target instanceof HTMLDivElement) {\n handleSubmit(event.target.textContent ?? '')\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n\ndefineExpose({\n /**\n * Focus the codemirror element\n *\n * @param cursorAtEnd boolean place the cursor at the end of the input\n */\n focus: (position?: 'start' | 'end' | number) => {\n if (!codeMirror.value) {\n return\n }\n codeMirror.value.focus()\n\n if (!isDefined(position)) {\n return\n }\n\n const anchor = (() => {\n if (position === 'start') {\n return 0\n }\n if (position === 'end') {\n return codeMirror.value.state.doc.length\n }\n return position\n })()\n\n // Move the cursor to the specified position\n codeMirror.value.dispatch({\n selection: { anchor },\n scrollIntoView: true,\n })\n },\n isFocused,\n handleChange,\n handleSubmit,\n handleBlur,\n booleanOptions,\n codeMirror,\n modelValue,\n cursorPosition: () => codeMirror.value?.state.selection.main.head,\n serializeValue,\n})\n</script>\n\n<template>\n <!-- Disabled mode: read-only text display -->\n <div\n v-if=\"disabled\"\n class=\"text-c-2 flex cursor-default items-center justify-center\"\n :class=\"{\n 'font-code pr-2 pl-1 text-base': layout === 'modal',\n 'px-2': layout !== 'modal',\n 'line-through': linethrough,\n }\"\n data-testid=\"code-input-disabled\">\n <span class=\"whitespace-nowrap\">{{ modelValue }}</span>\n </div>\n\n <!-- Enum mode: select dropdown with predefined values -->\n <DataTableInputSelect\n v-else-if=\"enumProp?.length\"\n :default=\"defaultProp\"\n :modelValue=\"modelValue\"\n :type=\"defaultType\"\n :value=\"enumProp\"\n @update:modelValue=\"handleSelectChange\" />\n\n <!-- Boolean mode: select dropdown with true/false (and optionally null) -->\n <DataTableInputSelect\n v-else-if=\"isBooleanMode\"\n :default=\"defaultProp\"\n :modelValue=\"modelValue\"\n :value=\"booleanOptions\"\n @update:modelValue=\"handleSelectChange\" />\n\n <!-- Examples mode: select dropdown with example values -->\n <DataTableInputSelect\n v-else-if=\"examples?.length\"\n :default=\"defaultProp\"\n :modelValue=\"modelValue\"\n :value=\"examples\"\n @update:modelValue=\"handleSelectChange\" />\n\n <!-- Editor mode: CodeMirror with environment variable support -->\n <div\n v-else\n :id=\"componentId\"\n v-bind=\"$attrs\"\n ref=\"codeMirrorRef\"\n class=\"group/input group-[.alert]:outline-orange group-[.error]:outline-red font-code peer relative w-full overflow-hidden text-xs leading-[1.44] whitespace-nowrap -outline-offset-1 has-[:focus-visible]:rounded-[4px] has-[:focus-visible]:outline\"\n :class=\"{\n 'line-wrapping has-[:focus-visible]:bg-b-1 has-[:focus-visible]:absolute has-[:focus-visible]:z-1':\n lineWrapping,\n 'flow-code-input--error': error,\n 'line-through': linethrough,\n }\"\n @keydown.down.stop=\"handleKeyDown('down', $event)\"\n @keydown.enter=\"handleKeyDown('enter', $event)\"\n @keydown.escape=\"handleKeyDown('escape', $event)\"\n @keydown.up.stop=\"handleKeyDown('up', $event)\">\n <!-- Tab exit hint (shown when focused) -->\n <div\n v-if=\"!disableTabIndent\"\n class=\"z-context text-c-2 absolute right-1.5 bottom-1 hidden font-sans group-has-[:focus-visible]/input:block\"\n role=\"alert\">\n Press\n <kbd class=\"-mx-0.25 rounded border px-0.5 font-mono\">Esc</kbd> then\n <kbd class=\"-mx-0.25 rounded border px-0.5 font-mono\">Tab</kbd> to exit\n </div>\n </div>\n\n <!-- Warning slot (positioned absolutely) -->\n <div\n v-if=\"$slots.warning\"\n class=\"centered-y text-orange absolute right-7 text-xs\">\n <slot name=\"warning\" />\n </div>\n\n <!-- Icon slot (positioned absolutely) -->\n <div\n v-if=\"$slots.icon\"\n class=\"centered-y absolute right-0 flex h-full items-center p-1.5 group-has-[.cm-focused]:z-1\">\n <slot name=\"icon\" />\n </div>\n\n <!-- Required indicator -->\n <div\n v-if=\"required\"\n class=\"required centered-y text-xxs text-c-3 group-[.error]:text-red bg-b-1 pointer-events-none absolute right-0 mr-0.5 pt-px pr-2 opacity-100 shadow-[-8px_0_4px_var(--scalar-background-1)] transition-opacity duration-150 group-[.alert]:bg-transparent group-[.alert]:shadow-none group-[.error]:bg-transparent group-[.error]:shadow-none peer-has-[.cm-focused]:opacity-0\">\n Required\n </div>\n\n <!-- Environment variable autocomplete dropdown -->\n <EnvironmentVariableDropdown\n v-if=\"displayVariablesDropdown && environment\"\n ref=\"dropdownRef\"\n :dropdownPosition=\"dropdownPosition\"\n :environment=\"environment\"\n :query=\"dropdownQuery\"\n @redirect=\"emit('navigate', { page: 'document', path: 'environment' })\"\n @select=\"handleDropdownSelect\" />\n</template>\n<style scoped>\n/*\n Deep styling for customizing Codemirror\n */\n:deep(.cm-editor) {\n height: 100%;\n outline: none;\n padding: 0;\n background: transparent;\n}\n:deep(.cm-placeholder) {\n color: var(--scalar-color-3);\n}\n:deep(.cm-content) {\n font-family: var(--scalar-font-code);\n font-size: var(--scalar-small);\n max-height: 20px;\n padding: 8px 0;\n}\n/* Tooltip helper */\n:deep(.cm-tooltip) {\n background: transparent !important;\n filter: brightness(var(--scalar-lifted-brightness));\n border-radius: var(--scalar-radius);\n box-shadow: var(--scalar-shadow-2);\n border: none !important;\n outline: none !important;\n overflow: hidden !important;\n}\n:deep(.cm-tooltip-autocomplete ul li) {\n padding: 3px 6px !important;\n}\n:deep(.cm-completionIcon-type:after) {\n color: var(--scalar-color-3) !important;\n}\n:deep(.cm-tooltip-autocomplete ul li[aria-selected]) {\n background: var(--scalar-background-2) !important;\n color: var(--scalar-color-1) !important;\n}\n:deep(.cm-tooltip-autocomplete ul) {\n padding: 6px !important;\n position: relative;\n}\n:deep(.cm-tooltip-autocomplete ul li:hover) {\n border-radius: 3px;\n color: var(--scalar-color-1) !important;\n background: var(--scalar-background-3) !important;\n}\n/* Disable active line highlighting */\n:deep(.cm-activeLine),\n:deep(.cm-activeLineGutter) {\n background-color: transparent;\n}\n/* Color selection matching */\n:deep(.cm-selectionMatch),\n:deep(.cm-matchingBracket) {\n border-radius: var(--scalar-radius);\n background: var(--scalar-background-4) !important;\n}\n/* Color Picker Swatches */\n:deep(.cm-css-color-picker-wrapper) {\n display: inline-flex;\n outline: 1px solid var(--scalar-background-3);\n border-radius: 3px;\n overflow: hidden;\n}\n/* Number gutter */\n:deep(.cm-gutters) {\n background-color: transparent;\n border-right: none;\n color: var(--scalar-color-3);\n font-size: var(--scalar-small);\n line-height: 22px;\n border-radius: 0 0 0 3px;\n}\n:deep(.cm-gutters:before) {\n content: '';\n position: absolute;\n top: 2px;\n left: 2px;\n width: calc(100% - 2px);\n height: calc(100% - 4px);\n border-radius: var(--scalar-radius) 0 0 var(--scalar-radius);\n background-color: var(--scalar-background-1);\n}\n:deep(.cm-gutterElement) {\n font-family: var(--scalar-font-code) !important;\n padding-left: 0px !important;\n padding-right: 6px !important;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n position: relative;\n}\n:deep(.cm-lineNumbers .cm-gutterElement) {\n min-width: fit-content;\n}\n:deep(.cm-gutter + .cm-gutter :not(.cm-foldGutter) .cm-gutterElement) {\n padding-left: 0 !important;\n}\n:deep(.cm-scroller) {\n overflow: auto;\n}\n.line-wrapping:focus-within :deep(.cm-content) {\n display: inline-table;\n min-height: fit-content;\n padding: 3px 6px;\n white-space: break-spaces;\n word-break: break-all;\n}\n</style>\n<style>\n.cm-pill {\n color: var(--scalar-color-1) !important;\n padding: 0px 9px;\n border-radius: 3px;\n display: inline-block;\n border-radius: 30px;\n font-size: var(--scalar-small);\n}\n.light-mode .cm-pill {\n background: var(--scalar-background-3) !important;\n}\n.dark-mode .cm-pill {\n background: color-mix(in srgb, var(--tw-bg-base), transparent 90%) !important;\n}\n.cm-pill:first-of-type {\n margin-left: 0;\n}\n.cm-editor .cm-widgetBuffer {\n display: none;\n}\n.cm-foldPlaceholder:hover {\n color: var(--scalar-color-1);\n}\n.cm-foldGutter .cm-gutterElement {\n font-size: var(--scalar-heading-4);\n padding: 2px !important;\n}\n.cm-foldGutter .cm-gutterElement:first-of-type {\n display: none;\n}\n.cm-foldGutter .cm-gutterElement .cm-foldMarker {\n padding: 2px;\n padding-top: 2px;\n}\n.cm-foldGutter .cm-gutterElement:hover .cm-foldMarker {\n background: var(--scalar-background-2);\n border-radius: var(--scalar-radius);\n color: var(--scalar-color-1);\n}\n</style>\n"],"mappings":""}
|
|
@@ -112,7 +112,7 @@ var CodeInput_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defi
|
|
|
112
112
|
"update:modelValue",
|
|
113
113
|
"submit",
|
|
114
114
|
"blur",
|
|
115
|
-
"
|
|
115
|
+
"navigate"
|
|
116
116
|
],
|
|
117
117
|
setup(__props, { expose: __expose, emit: __emit }) {
|
|
118
118
|
const emit = __emit;
|
|
@@ -362,7 +362,10 @@ var CodeInput_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defi
|
|
|
362
362
|
dropdownPosition: dropdownPosition.value,
|
|
363
363
|
environment: __props.environment,
|
|
364
364
|
query: dropdownQuery.value,
|
|
365
|
-
onRedirect: _cache[4] || (_cache[4] = ($event) => emit("
|
|
365
|
+
onRedirect: _cache[4] || (_cache[4] = ($event) => emit("navigate", {
|
|
366
|
+
page: "document",
|
|
367
|
+
path: "environment"
|
|
368
|
+
})),
|
|
366
369
|
onSelect: unref(handleDropdownSelect)
|
|
367
370
|
}, null, 8, [
|
|
368
371
|
"dropdownPosition",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeInput.vue.script.js","names":["$attrs","$slots"],"sources":["../../../../src/v2/components/code-input/CodeInput.vue"],"sourcesContent":["<script lang=\"ts\">\nexport type CodeInputModelValue =\n | string\n | number\n | boolean\n | Array<string | number | boolean>\n | Record<string, unknown>\n\n/**\n * CodeInput\n *\n * A versatile input component that adapts its rendering based on props:\n * - Disabled mode: Read-only text display\n * - Select mode: Dropdown for enums, booleans, or examples\n * - Editor mode: CodeMirror with environment variable support\n *\n * Type `{{` to trigger environment variable autocomplete when an environment is provided.\n * It takes in any data but always will emit a string value,\n * this string should then be parsed in accordance to the schema or content type.\n *\n * @example\n * ```vue\n * <!-- Basic input with environment variables -->\n * <CodeInput v-model=\"value\" :environment=\"env\" />\n *\n * <!-- Boolean select -->\n * <CodeInput v-model=\"flag\" type=\"boolean\" />\n *\n * <!-- JSON editor with linting -->\n * <CodeInput v-model=\"data\" language=\"json\" :lint=\"true\" />\n * ```\n */\nexport default {\n inheritAttrs: false,\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { isDefined } from '@scalar/helpers/array/is-defined'\nimport {\n colorPicker as colorPickerExtension,\n useCodeMirror,\n useDropdown,\n type CodeMirrorLanguage,\n type Extension,\n} from '@scalar/use-codemirror'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport { nanoid } from 'nanoid'\nimport { computed, ref, toRef, useAttrs, watch, type Ref } from 'vue'\n\nimport DataTableInputSelect from '@/v2/components/data-table/DataTableInputSelect.vue'\nimport EnvironmentVariableDropdown from '@/v2/features/environments/components/EnvironmentVariablesDropdown.vue'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nimport { backspaceCommand, pillPlugin } from './code-variable-widget'\n\ntype Props = {\n modelValue: CodeInputModelValue\n /** Environment for variable substitution. Pass undefined to disable environment variables */\n environment: XScalarEnvironment | undefined\n /** Type of the input value, affects rendering mode for booleans */\n type?: string | string[]\n /** Render as disabled text display */\n disabled?: boolean\n /** Show error styling */\n error?: boolean\n /** Layout context affects styling and behavior */\n layout?: ClientLayout\n /** Predefined enum values, triggers select mode */\n enum?: string[]\n /** Example values, triggers select mode */\n examples?: string[]\n /** Default value to show in select mode */\n default?: Props['modelValue']\n /** Allow null in boolean select options */\n nullable?: boolean\n /** Placeholder text for empty input */\n placeholder?: string\n /** Show required indicator */\n required?: boolean\n /** Enable color picker extension */\n colorPicker?: boolean\n /** Show line numbers in editor */\n lineNumbers?: boolean\n /** Enable linting */\n lint?: boolean\n /** Enable line wrapping */\n lineWrapping?: boolean\n /** CodeMirror language mode */\n language?: CodeMirrorLanguage\n /** Additional CodeMirror extensions */\n extensions?: Extension[]\n /** Disable tab key for indentation */\n disableTabIndent?: boolean\n /** Disable enter key */\n disableEnter?: boolean\n /** Disable automatic bracket closing */\n disableCloseBrackets?: boolean\n /** Emit submit event on blur */\n emitOnBlur?: boolean\n /** Enable environment variable pills */\n withVariables?: boolean\n /** Emit change event even if the value is the same */\n alwaysEmitChange?: boolean\n /** Custom change handler, prevents default emit */\n handleFieldChange?: (value: string) => void\n /** Custom submit handler, prevents default emit */\n handleFieldSubmit?: (value: string) => void\n /** Put a linethrough on the input text */\n linethrough?: boolean\n}\n\nconst {\n modelValue,\n environment,\n type,\n disabled = false,\n error = false,\n layout = 'desktop',\n enum: enumProp,\n examples,\n default: defaultProp,\n nullable = false,\n placeholder,\n required,\n colorPicker = false,\n lineNumbers = false,\n lint = false,\n lineWrapping = false,\n language,\n extensions = [],\n disableTabIndent = false,\n disableEnter = false,\n disableCloseBrackets = false,\n emitOnBlur = true,\n alwaysEmitChange = false,\n withVariables = true,\n handleFieldChange,\n handleFieldSubmit,\n} = defineProps<Props>()\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string]\n 'submit': [value: string]\n 'blur': [value: string]\n 'redirectToEnvironment': []\n}>()\n\n// ---------------------------------------------------------------------------\n// Component identity and focus state\n\nconst attrs = useAttrs() as { id?: string }\nconst componentId = attrs.id || `id-${nanoid()}`\nconst isFocused = ref(false)\n\n// ---------------------------------------------------------------------------\n// Rendering mode detection\n\n/**\n * Determines if we should render a select dropdown for boolean types.\n */\nconst isBooleanMode = computed((): boolean => {\n if (enumProp?.length) {\n return false\n }\n return type === 'boolean' || (Array.isArray(type) && type.includes('boolean'))\n})\n\n/**\n * Options for boolean select mode.\n */\nconst booleanOptions = computed((): string[] =>\n nullable ? ['true', 'false', 'null'] : ['true', 'false'],\n)\n\n/**\n * Default type when dealing with type arrays.\n * Finds the first non-null type.\n */\nconst defaultType = computed((): string | undefined => {\n if (Array.isArray(type)) {\n return type.find((t) => t !== 'null') ?? 'string'\n }\n return type\n})\n\n// ---------------------------------------------------------------------------\n// Event handlers\n\n/**\n * Handles value changes during typing.\n */\nconst handleChange = (value: string): void => {\n if (!alwaysEmitChange && value === serializeValue(modelValue)) {\n return\n }\n\n // Use custom handler or emit update\n if (handleFieldChange) {\n handleFieldChange(value)\n } else {\n emit('update:modelValue', value)\n }\n}\n\n/**\n * Handles form submission (enter key or blur with emitOnBlur).\n */\nconst handleSubmit = (value: string): void => {\n if (handleFieldSubmit) {\n handleFieldSubmit(value)\n } else {\n emit('submit', value)\n }\n}\n\n/**\n * Handles input blur event.\n */\nconst handleBlur = (value: string): void => {\n isFocused.value = false\n\n if (emitOnBlur && modelValue) {\n handleSubmit(value)\n }\n\n emit('blur', value)\n}\n\n/**\n * Handles model value updates from select components.\n */\nconst handleSelectChange = (value: string): void =>\n emit('update:modelValue', value)\n\n// ---------------------------------------------------------------------------\n// CodeMirror setup\n\n/**\n * Build extensions array.\n * Note: Extensions are not reactive after initialization.\n */\nconst buildExtensions = (): Extension[] => {\n const extensionsList: Extension[] = [...extensions]\n\n if (colorPicker) {\n extensionsList.push(colorPickerExtension)\n }\n\n return extensionsList\n}\n\n/**\n * Reactive pill plugin for environment variable visualization.\n */\nconst pillPluginExtension = computed(() =>\n pillPlugin({\n environment,\n isReadOnly: layout === 'modal',\n }),\n)\n\n/**\n * Combined extensions for CodeMirror.\n */\nconst codeMirrorExtensions = computed((): Extension[] => [\n ...buildExtensions(),\n pillPluginExtension.value,\n backspaceCommand,\n])\n\nconst codeMirrorRef: Ref<HTMLDivElement | null> = ref(null)\n\n/** Converts the model value to a string for CodeMirror */\nconst serializeValue = (value: CodeInputModelValue): string => {\n if (typeof value === 'string') {\n return value\n }\n return JSON.stringify(value)\n}\n\nconst { codeMirror } = useCodeMirror({\n content: toRef(() => serializeValue(modelValue)),\n onChange: (value) => {\n handleChange(value)\n updateDropdownVisibility()\n },\n onFocus: () => {\n isFocused.value = true\n },\n onBlur: handleBlur,\n codeMirrorRef,\n disableTabIndent: toRef(() => disableTabIndent),\n disableEnter: toRef(() => disableEnter),\n disableCloseBrackets: toRef(() => disableCloseBrackets),\n lineNumbers: toRef(() => lineNumbers),\n language: toRef(() => language),\n lint: toRef(() => lint),\n extensions: codeMirrorExtensions,\n placeholder: toRef(() => placeholder),\n})\n\n/**\n * Handle autofocus attribute.\n */\nwatch(codeMirror, () => {\n if (codeMirror.value && Object.hasOwn(attrs, 'autofocus')) {\n codeMirror.value.focus()\n }\n})\n\n// ---------------------------------------------------------------------------\n// Environment variable dropdown\n\nconst showDropdown = ref(false)\nconst dropdownQuery = ref('')\nconst dropdownPosition = ref({ left: 0, top: 0 })\nconst dropdownRef = ref<InstanceType<\n typeof EnvironmentVariableDropdown\n> | null>(null)\n\nconst { handleDropdownSelect, updateDropdownVisibility } = useDropdown({\n codeMirror,\n query: dropdownQuery,\n showDropdown,\n dropdownPosition,\n})\n\n/**\n * Determines if the environment variable dropdown should be visible.\n */\nconst displayVariablesDropdown = computed((): boolean => {\n return (\n showDropdown.value &&\n withVariables &&\n layout !== 'modal' &&\n Boolean(environment)\n )\n})\n\n// ---------------------------------------------------------------------------\n// Keyboard event handling\n\n/**\n * Handles keyboard navigation for dropdown and form submission.\n */\nconst handleKeyDown = (key: string, event: KeyboardEvent): void => {\n if (showDropdown.value) {\n if (key === 'down' || key === 'up') {\n event.preventDefault()\n dropdownRef.value?.handleArrowKey(key)\n } else if (key === 'enter') {\n event.preventDefault()\n dropdownRef.value?.handleSelect()\n }\n return\n }\n\n if (key === 'escape' && !disableTabIndent) {\n event.stopPropagation()\n }\n\n if (key === 'enter' && event.target instanceof HTMLDivElement) {\n handleSubmit(event.target.textContent ?? '')\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n\ndefineExpose({\n /**\n * Focus the codemirror element\n *\n * @param cursorAtEnd boolean place the cursor at the end of the input\n */\n focus: (position?: 'start' | 'end' | number) => {\n if (!codeMirror.value) {\n return\n }\n codeMirror.value.focus()\n\n if (!isDefined(position)) {\n return\n }\n\n const anchor = (() => {\n if (position === 'start') {\n return 0\n }\n if (position === 'end') {\n return codeMirror.value.state.doc.length\n }\n return position\n })()\n\n // Move the cursor to the specified position\n codeMirror.value.dispatch({\n selection: { anchor },\n scrollIntoView: true,\n })\n },\n isFocused,\n handleChange,\n handleSubmit,\n handleBlur,\n booleanOptions,\n codeMirror,\n modelValue,\n cursorPosition: () => codeMirror.value?.state.selection.main.head,\n serializeValue,\n})\n</script>\n\n<template>\n <!-- Disabled mode: read-only text display -->\n <div\n v-if=\"disabled\"\n class=\"text-c-2 flex cursor-default items-center justify-center\"\n :class=\"{\n 'font-code pr-2 pl-1 text-base': layout === 'modal',\n 'px-2': layout !== 'modal',\n 'line-through': linethrough,\n }\"\n data-testid=\"code-input-disabled\">\n <span class=\"whitespace-nowrap\">{{ modelValue }}</span>\n </div>\n\n <!-- Enum mode: select dropdown with predefined values -->\n <DataTableInputSelect\n v-else-if=\"enumProp?.length\"\n :default=\"defaultProp\"\n :modelValue=\"modelValue\"\n :type=\"defaultType\"\n :value=\"enumProp\"\n @update:modelValue=\"handleSelectChange\" />\n\n <!-- Boolean mode: select dropdown with true/false (and optionally null) -->\n <DataTableInputSelect\n v-else-if=\"isBooleanMode\"\n :default=\"defaultProp\"\n :modelValue=\"modelValue\"\n :value=\"booleanOptions\"\n @update:modelValue=\"handleSelectChange\" />\n\n <!-- Examples mode: select dropdown with example values -->\n <DataTableInputSelect\n v-else-if=\"examples?.length\"\n :default=\"defaultProp\"\n :modelValue=\"modelValue\"\n :value=\"examples\"\n @update:modelValue=\"handleSelectChange\" />\n\n <!-- Editor mode: CodeMirror with environment variable support -->\n <div\n v-else\n :id=\"componentId\"\n v-bind=\"$attrs\"\n ref=\"codeMirrorRef\"\n class=\"group/input group-[.alert]:outline-orange group-[.error]:outline-red font-code peer relative w-full overflow-hidden text-xs leading-[1.44] whitespace-nowrap -outline-offset-1 has-[:focus-visible]:rounded-[4px] has-[:focus-visible]:outline\"\n :class=\"{\n 'line-wrapping has-[:focus-visible]:bg-b-1 has-[:focus-visible]:absolute has-[:focus-visible]:z-1':\n lineWrapping,\n 'flow-code-input--error': error,\n 'line-through': linethrough,\n }\"\n @keydown.down.stop=\"handleKeyDown('down', $event)\"\n @keydown.enter=\"handleKeyDown('enter', $event)\"\n @keydown.escape=\"handleKeyDown('escape', $event)\"\n @keydown.up.stop=\"handleKeyDown('up', $event)\">\n <!-- Tab exit hint (shown when focused) -->\n <div\n v-if=\"!disableTabIndent\"\n class=\"z-context text-c-2 absolute right-1.5 bottom-1 hidden font-sans group-has-[:focus-visible]/input:block\"\n role=\"alert\">\n Press\n <kbd class=\"-mx-0.25 rounded border px-0.5 font-mono\">Esc</kbd> then\n <kbd class=\"-mx-0.25 rounded border px-0.5 font-mono\">Tab</kbd> to exit\n </div>\n </div>\n\n <!-- Warning slot (positioned absolutely) -->\n <div\n v-if=\"$slots.warning\"\n class=\"centered-y text-orange absolute right-7 text-xs\">\n <slot name=\"warning\" />\n </div>\n\n <!-- Icon slot (positioned absolutely) -->\n <div\n v-if=\"$slots.icon\"\n class=\"centered-y absolute right-0 flex h-full items-center p-1.5 group-has-[.cm-focused]:z-1\">\n <slot name=\"icon\" />\n </div>\n\n <!-- Required indicator -->\n <div\n v-if=\"required\"\n class=\"required centered-y text-xxs text-c-3 group-[.error]:text-red bg-b-1 pointer-events-none absolute right-0 mr-0.5 pt-px pr-2 opacity-100 shadow-[-8px_0_4px_var(--scalar-background-1)] transition-opacity duration-150 group-[.alert]:bg-transparent group-[.alert]:shadow-none group-[.error]:bg-transparent group-[.error]:shadow-none peer-has-[.cm-focused]:opacity-0\">\n Required\n </div>\n\n <!-- Environment variable autocomplete dropdown -->\n <EnvironmentVariableDropdown\n v-if=\"displayVariablesDropdown && environment\"\n ref=\"dropdownRef\"\n :dropdownPosition=\"dropdownPosition\"\n :environment=\"environment\"\n :query=\"dropdownQuery\"\n @redirect=\"emit('redirectToEnvironment')\"\n @select=\"handleDropdownSelect\" />\n</template>\n<style scoped>\n/*\n Deep styling for customizing Codemirror\n */\n:deep(.cm-editor) {\n height: 100%;\n outline: none;\n padding: 0;\n background: transparent;\n}\n:deep(.cm-placeholder) {\n color: var(--scalar-color-3);\n}\n:deep(.cm-content) {\n font-family: var(--scalar-font-code);\n font-size: var(--scalar-small);\n max-height: 20px;\n padding: 8px 0;\n}\n/* Tooltip helper */\n:deep(.cm-tooltip) {\n background: transparent !important;\n filter: brightness(var(--scalar-lifted-brightness));\n border-radius: var(--scalar-radius);\n box-shadow: var(--scalar-shadow-2);\n border: none !important;\n outline: none !important;\n overflow: hidden !important;\n}\n:deep(.cm-tooltip-autocomplete ul li) {\n padding: 3px 6px !important;\n}\n:deep(.cm-completionIcon-type:after) {\n color: var(--scalar-color-3) !important;\n}\n:deep(.cm-tooltip-autocomplete ul li[aria-selected]) {\n background: var(--scalar-background-2) !important;\n color: var(--scalar-color-1) !important;\n}\n:deep(.cm-tooltip-autocomplete ul) {\n padding: 6px !important;\n position: relative;\n}\n:deep(.cm-tooltip-autocomplete ul li:hover) {\n border-radius: 3px;\n color: var(--scalar-color-1) !important;\n background: var(--scalar-background-3) !important;\n}\n/* Disable active line highlighting */\n:deep(.cm-activeLine),\n:deep(.cm-activeLineGutter) {\n background-color: transparent;\n}\n/* Color selection matching */\n:deep(.cm-selectionMatch),\n:deep(.cm-matchingBracket) {\n border-radius: var(--scalar-radius);\n background: var(--scalar-background-4) !important;\n}\n/* Color Picker Swatches */\n:deep(.cm-css-color-picker-wrapper) {\n display: inline-flex;\n outline: 1px solid var(--scalar-background-3);\n border-radius: 3px;\n overflow: hidden;\n}\n/* Number gutter */\n:deep(.cm-gutters) {\n background-color: transparent;\n border-right: none;\n color: var(--scalar-color-3);\n font-size: var(--scalar-small);\n line-height: 22px;\n border-radius: 0 0 0 3px;\n}\n:deep(.cm-gutters:before) {\n content: '';\n position: absolute;\n top: 2px;\n left: 2px;\n width: calc(100% - 2px);\n height: calc(100% - 4px);\n border-radius: var(--scalar-radius) 0 0 var(--scalar-radius);\n background-color: var(--scalar-background-1);\n}\n:deep(.cm-gutterElement) {\n font-family: var(--scalar-font-code) !important;\n padding-left: 0px !important;\n padding-right: 6px !important;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n position: relative;\n}\n:deep(.cm-lineNumbers .cm-gutterElement) {\n min-width: fit-content;\n}\n:deep(.cm-gutter + .cm-gutter :not(.cm-foldGutter) .cm-gutterElement) {\n padding-left: 0 !important;\n}\n:deep(.cm-scroller) {\n overflow: auto;\n}\n.line-wrapping:focus-within :deep(.cm-content) {\n display: inline-table;\n min-height: fit-content;\n padding: 3px 6px;\n white-space: break-spaces;\n word-break: break-all;\n}\n</style>\n<style>\n.cm-pill {\n color: var(--scalar-color-1) !important;\n padding: 0px 9px;\n border-radius: 3px;\n display: inline-block;\n border-radius: 30px;\n font-size: var(--scalar-small);\n}\n.light-mode .cm-pill {\n background: var(--scalar-background-3) !important;\n}\n.dark-mode .cm-pill {\n background: color-mix(in srgb, var(--tw-bg-base), transparent 90%) !important;\n}\n.cm-pill:first-of-type {\n margin-left: 0;\n}\n.cm-editor .cm-widgetBuffer {\n display: none;\n}\n.cm-foldPlaceholder:hover {\n color: var(--scalar-color-1);\n}\n.cm-foldGutter .cm-gutterElement {\n font-size: var(--scalar-heading-4);\n padding: 2px !important;\n}\n.cm-foldGutter .cm-gutterElement:first-of-type {\n display: none;\n}\n.cm-foldGutter .cm-gutterElement .cm-foldMarker {\n padding: 2px;\n padding-top: 2px;\n}\n.cm-foldGutter .cm-gutterElement:hover .cm-foldMarker {\n background: var(--scalar-background-2);\n border-radius: var(--scalar-radius);\n color: var(--scalar-color-1);\n}\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CodeInput.vue.script.js","names":["$attrs","$slots"],"sources":["../../../../src/v2/components/code-input/CodeInput.vue"],"sourcesContent":["<script lang=\"ts\">\nexport type CodeInputModelValue =\n | string\n | number\n | boolean\n | Array<string | number | boolean>\n | Record<string, unknown>\n\n/**\n * CodeInput\n *\n * A versatile input component that adapts its rendering based on props:\n * - Disabled mode: Read-only text display\n * - Select mode: Dropdown for enums, booleans, or examples\n * - Editor mode: CodeMirror with environment variable support\n *\n * Type `{{` to trigger environment variable autocomplete when an environment is provided.\n * It takes in any data but always will emit a string value,\n * this string should then be parsed in accordance to the schema or content type.\n *\n * @example\n * ```vue\n * <!-- Basic input with environment variables -->\n * <CodeInput v-model=\"value\" :environment=\"env\" />\n *\n * <!-- Boolean select -->\n * <CodeInput v-model=\"flag\" type=\"boolean\" />\n *\n * <!-- JSON editor with linting -->\n * <CodeInput v-model=\"data\" language=\"json\" :lint=\"true\" />\n * ```\n */\nexport default {\n inheritAttrs: false,\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { isDefined } from '@scalar/helpers/array/is-defined'\nimport {\n colorPicker as colorPickerExtension,\n useCodeMirror,\n useDropdown,\n type CodeMirrorLanguage,\n type Extension,\n} from '@scalar/use-codemirror'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport { nanoid } from 'nanoid'\nimport { computed, ref, toRef, useAttrs, watch, type Ref } from 'vue'\n\nimport DataTableInputSelect from '@/v2/components/data-table/DataTableInputSelect.vue'\nimport EnvironmentVariableDropdown from '@/v2/features/environments/components/EnvironmentVariablesDropdown.vue'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nimport { backspaceCommand, pillPlugin } from './code-variable-widget'\n\ntype Props = {\n modelValue: CodeInputModelValue\n /** Environment for variable substitution. Pass undefined to disable environment variables */\n environment: XScalarEnvironment | undefined\n /** Type of the input value, affects rendering mode for booleans */\n type?: string | string[]\n /** Render as disabled text display */\n disabled?: boolean\n /** Show error styling */\n error?: boolean\n /** Layout context affects styling and behavior */\n layout?: ClientLayout\n /** Predefined enum values, triggers select mode */\n enum?: string[]\n /** Example values, triggers select mode */\n examples?: string[]\n /** Default value to show in select mode */\n default?: Props['modelValue']\n /** Allow null in boolean select options */\n nullable?: boolean\n /** Placeholder text for empty input */\n placeholder?: string\n /** Show required indicator */\n required?: boolean\n /** Enable color picker extension */\n colorPicker?: boolean\n /** Show line numbers in editor */\n lineNumbers?: boolean\n /** Enable linting */\n lint?: boolean\n /** Enable line wrapping */\n lineWrapping?: boolean\n /** CodeMirror language mode */\n language?: CodeMirrorLanguage\n /** Additional CodeMirror extensions */\n extensions?: Extension[]\n /** Disable tab key for indentation */\n disableTabIndent?: boolean\n /** Disable enter key */\n disableEnter?: boolean\n /** Disable automatic bracket closing */\n disableCloseBrackets?: boolean\n /** Emit submit event on blur */\n emitOnBlur?: boolean\n /** Enable environment variable pills */\n withVariables?: boolean\n /** Emit change event even if the value is the same */\n alwaysEmitChange?: boolean\n /** Custom change handler, prevents default emit */\n handleFieldChange?: (value: string) => void\n /** Custom submit handler, prevents default emit */\n handleFieldSubmit?: (value: string) => void\n /** Put a linethrough on the input text */\n linethrough?: boolean\n}\n\nconst {\n modelValue,\n environment,\n type,\n disabled = false,\n error = false,\n layout = 'desktop',\n enum: enumProp,\n examples,\n default: defaultProp,\n nullable = false,\n placeholder,\n required,\n colorPicker = false,\n lineNumbers = false,\n lint = false,\n lineWrapping = false,\n language,\n extensions = [],\n disableTabIndent = false,\n disableEnter = false,\n disableCloseBrackets = false,\n emitOnBlur = true,\n alwaysEmitChange = false,\n withVariables = true,\n handleFieldChange,\n handleFieldSubmit,\n} = defineProps<Props>()\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string]\n 'submit': [value: string]\n 'blur': [value: string]\n 'navigate': [route: { page: 'document'; path: 'environment' }]\n}>()\n\n// ---------------------------------------------------------------------------\n// Component identity and focus state\n\nconst attrs = useAttrs() as { id?: string }\nconst componentId = attrs.id || `id-${nanoid()}`\nconst isFocused = ref(false)\n\n// ---------------------------------------------------------------------------\n// Rendering mode detection\n\n/**\n * Determines if we should render a select dropdown for boolean types.\n */\nconst isBooleanMode = computed((): boolean => {\n if (enumProp?.length) {\n return false\n }\n return type === 'boolean' || (Array.isArray(type) && type.includes('boolean'))\n})\n\n/**\n * Options for boolean select mode.\n */\nconst booleanOptions = computed((): string[] =>\n nullable ? ['true', 'false', 'null'] : ['true', 'false'],\n)\n\n/**\n * Default type when dealing with type arrays.\n * Finds the first non-null type.\n */\nconst defaultType = computed((): string | undefined => {\n if (Array.isArray(type)) {\n return type.find((t) => t !== 'null') ?? 'string'\n }\n return type\n})\n\n// ---------------------------------------------------------------------------\n// Event handlers\n\n/**\n * Handles value changes during typing.\n */\nconst handleChange = (value: string): void => {\n if (!alwaysEmitChange && value === serializeValue(modelValue)) {\n return\n }\n\n // Use custom handler or emit update\n if (handleFieldChange) {\n handleFieldChange(value)\n } else {\n emit('update:modelValue', value)\n }\n}\n\n/**\n * Handles form submission (enter key or blur with emitOnBlur).\n */\nconst handleSubmit = (value: string): void => {\n if (handleFieldSubmit) {\n handleFieldSubmit(value)\n } else {\n emit('submit', value)\n }\n}\n\n/**\n * Handles input blur event.\n */\nconst handleBlur = (value: string): void => {\n isFocused.value = false\n\n if (emitOnBlur && modelValue) {\n handleSubmit(value)\n }\n\n emit('blur', value)\n}\n\n/**\n * Handles model value updates from select components.\n */\nconst handleSelectChange = (value: string): void =>\n emit('update:modelValue', value)\n\n// ---------------------------------------------------------------------------\n// CodeMirror setup\n\n/**\n * Build extensions array.\n * Note: Extensions are not reactive after initialization.\n */\nconst buildExtensions = (): Extension[] => {\n const extensionsList: Extension[] = [...extensions]\n\n if (colorPicker) {\n extensionsList.push(colorPickerExtension)\n }\n\n return extensionsList\n}\n\n/**\n * Reactive pill plugin for environment variable visualization.\n */\nconst pillPluginExtension = computed(() =>\n pillPlugin({\n environment,\n isReadOnly: layout === 'modal',\n }),\n)\n\n/**\n * Combined extensions for CodeMirror.\n */\nconst codeMirrorExtensions = computed((): Extension[] => [\n ...buildExtensions(),\n pillPluginExtension.value,\n backspaceCommand,\n])\n\nconst codeMirrorRef: Ref<HTMLDivElement | null> = ref(null)\n\n/** Converts the model value to a string for CodeMirror */\nconst serializeValue = (value: CodeInputModelValue): string => {\n if (typeof value === 'string') {\n return value\n }\n return JSON.stringify(value)\n}\n\nconst { codeMirror } = useCodeMirror({\n content: toRef(() => serializeValue(modelValue)),\n onChange: (value) => {\n handleChange(value)\n updateDropdownVisibility()\n },\n onFocus: () => {\n isFocused.value = true\n },\n onBlur: handleBlur,\n codeMirrorRef,\n disableTabIndent: toRef(() => disableTabIndent),\n disableEnter: toRef(() => disableEnter),\n disableCloseBrackets: toRef(() => disableCloseBrackets),\n lineNumbers: toRef(() => lineNumbers),\n language: toRef(() => language),\n lint: toRef(() => lint),\n extensions: codeMirrorExtensions,\n placeholder: toRef(() => placeholder),\n})\n\n/**\n * Handle autofocus attribute.\n */\nwatch(codeMirror, () => {\n if (codeMirror.value && Object.hasOwn(attrs, 'autofocus')) {\n codeMirror.value.focus()\n }\n})\n\n// ---------------------------------------------------------------------------\n// Environment variable dropdown\n\nconst showDropdown = ref(false)\nconst dropdownQuery = ref('')\nconst dropdownPosition = ref({ left: 0, top: 0 })\nconst dropdownRef = ref<InstanceType<\n typeof EnvironmentVariableDropdown\n> | null>(null)\n\nconst { handleDropdownSelect, updateDropdownVisibility } = useDropdown({\n codeMirror,\n query: dropdownQuery,\n showDropdown,\n dropdownPosition,\n})\n\n/**\n * Determines if the environment variable dropdown should be visible.\n */\nconst displayVariablesDropdown = computed((): boolean => {\n return (\n showDropdown.value &&\n withVariables &&\n layout !== 'modal' &&\n Boolean(environment)\n )\n})\n\n// ---------------------------------------------------------------------------\n// Keyboard event handling\n\n/**\n * Handles keyboard navigation for dropdown and form submission.\n */\nconst handleKeyDown = (key: string, event: KeyboardEvent): void => {\n if (showDropdown.value) {\n if (key === 'down' || key === 'up') {\n event.preventDefault()\n dropdownRef.value?.handleArrowKey(key)\n } else if (key === 'enter') {\n event.preventDefault()\n dropdownRef.value?.handleSelect()\n }\n return\n }\n\n if (key === 'escape' && !disableTabIndent) {\n event.stopPropagation()\n }\n\n if (key === 'enter' && event.target instanceof HTMLDivElement) {\n handleSubmit(event.target.textContent ?? '')\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n\ndefineExpose({\n /**\n * Focus the codemirror element\n *\n * @param cursorAtEnd boolean place the cursor at the end of the input\n */\n focus: (position?: 'start' | 'end' | number) => {\n if (!codeMirror.value) {\n return\n }\n codeMirror.value.focus()\n\n if (!isDefined(position)) {\n return\n }\n\n const anchor = (() => {\n if (position === 'start') {\n return 0\n }\n if (position === 'end') {\n return codeMirror.value.state.doc.length\n }\n return position\n })()\n\n // Move the cursor to the specified position\n codeMirror.value.dispatch({\n selection: { anchor },\n scrollIntoView: true,\n })\n },\n isFocused,\n handleChange,\n handleSubmit,\n handleBlur,\n booleanOptions,\n codeMirror,\n modelValue,\n cursorPosition: () => codeMirror.value?.state.selection.main.head,\n serializeValue,\n})\n</script>\n\n<template>\n <!-- Disabled mode: read-only text display -->\n <div\n v-if=\"disabled\"\n class=\"text-c-2 flex cursor-default items-center justify-center\"\n :class=\"{\n 'font-code pr-2 pl-1 text-base': layout === 'modal',\n 'px-2': layout !== 'modal',\n 'line-through': linethrough,\n }\"\n data-testid=\"code-input-disabled\">\n <span class=\"whitespace-nowrap\">{{ modelValue }}</span>\n </div>\n\n <!-- Enum mode: select dropdown with predefined values -->\n <DataTableInputSelect\n v-else-if=\"enumProp?.length\"\n :default=\"defaultProp\"\n :modelValue=\"modelValue\"\n :type=\"defaultType\"\n :value=\"enumProp\"\n @update:modelValue=\"handleSelectChange\" />\n\n <!-- Boolean mode: select dropdown with true/false (and optionally null) -->\n <DataTableInputSelect\n v-else-if=\"isBooleanMode\"\n :default=\"defaultProp\"\n :modelValue=\"modelValue\"\n :value=\"booleanOptions\"\n @update:modelValue=\"handleSelectChange\" />\n\n <!-- Examples mode: select dropdown with example values -->\n <DataTableInputSelect\n v-else-if=\"examples?.length\"\n :default=\"defaultProp\"\n :modelValue=\"modelValue\"\n :value=\"examples\"\n @update:modelValue=\"handleSelectChange\" />\n\n <!-- Editor mode: CodeMirror with environment variable support -->\n <div\n v-else\n :id=\"componentId\"\n v-bind=\"$attrs\"\n ref=\"codeMirrorRef\"\n class=\"group/input group-[.alert]:outline-orange group-[.error]:outline-red font-code peer relative w-full overflow-hidden text-xs leading-[1.44] whitespace-nowrap -outline-offset-1 has-[:focus-visible]:rounded-[4px] has-[:focus-visible]:outline\"\n :class=\"{\n 'line-wrapping has-[:focus-visible]:bg-b-1 has-[:focus-visible]:absolute has-[:focus-visible]:z-1':\n lineWrapping,\n 'flow-code-input--error': error,\n 'line-through': linethrough,\n }\"\n @keydown.down.stop=\"handleKeyDown('down', $event)\"\n @keydown.enter=\"handleKeyDown('enter', $event)\"\n @keydown.escape=\"handleKeyDown('escape', $event)\"\n @keydown.up.stop=\"handleKeyDown('up', $event)\">\n <!-- Tab exit hint (shown when focused) -->\n <div\n v-if=\"!disableTabIndent\"\n class=\"z-context text-c-2 absolute right-1.5 bottom-1 hidden font-sans group-has-[:focus-visible]/input:block\"\n role=\"alert\">\n Press\n <kbd class=\"-mx-0.25 rounded border px-0.5 font-mono\">Esc</kbd> then\n <kbd class=\"-mx-0.25 rounded border px-0.5 font-mono\">Tab</kbd> to exit\n </div>\n </div>\n\n <!-- Warning slot (positioned absolutely) -->\n <div\n v-if=\"$slots.warning\"\n class=\"centered-y text-orange absolute right-7 text-xs\">\n <slot name=\"warning\" />\n </div>\n\n <!-- Icon slot (positioned absolutely) -->\n <div\n v-if=\"$slots.icon\"\n class=\"centered-y absolute right-0 flex h-full items-center p-1.5 group-has-[.cm-focused]:z-1\">\n <slot name=\"icon\" />\n </div>\n\n <!-- Required indicator -->\n <div\n v-if=\"required\"\n class=\"required centered-y text-xxs text-c-3 group-[.error]:text-red bg-b-1 pointer-events-none absolute right-0 mr-0.5 pt-px pr-2 opacity-100 shadow-[-8px_0_4px_var(--scalar-background-1)] transition-opacity duration-150 group-[.alert]:bg-transparent group-[.alert]:shadow-none group-[.error]:bg-transparent group-[.error]:shadow-none peer-has-[.cm-focused]:opacity-0\">\n Required\n </div>\n\n <!-- Environment variable autocomplete dropdown -->\n <EnvironmentVariableDropdown\n v-if=\"displayVariablesDropdown && environment\"\n ref=\"dropdownRef\"\n :dropdownPosition=\"dropdownPosition\"\n :environment=\"environment\"\n :query=\"dropdownQuery\"\n @redirect=\"emit('navigate', { page: 'document', path: 'environment' })\"\n @select=\"handleDropdownSelect\" />\n</template>\n<style scoped>\n/*\n Deep styling for customizing Codemirror\n */\n:deep(.cm-editor) {\n height: 100%;\n outline: none;\n padding: 0;\n background: transparent;\n}\n:deep(.cm-placeholder) {\n color: var(--scalar-color-3);\n}\n:deep(.cm-content) {\n font-family: var(--scalar-font-code);\n font-size: var(--scalar-small);\n max-height: 20px;\n padding: 8px 0;\n}\n/* Tooltip helper */\n:deep(.cm-tooltip) {\n background: transparent !important;\n filter: brightness(var(--scalar-lifted-brightness));\n border-radius: var(--scalar-radius);\n box-shadow: var(--scalar-shadow-2);\n border: none !important;\n outline: none !important;\n overflow: hidden !important;\n}\n:deep(.cm-tooltip-autocomplete ul li) {\n padding: 3px 6px !important;\n}\n:deep(.cm-completionIcon-type:after) {\n color: var(--scalar-color-3) !important;\n}\n:deep(.cm-tooltip-autocomplete ul li[aria-selected]) {\n background: var(--scalar-background-2) !important;\n color: var(--scalar-color-1) !important;\n}\n:deep(.cm-tooltip-autocomplete ul) {\n padding: 6px !important;\n position: relative;\n}\n:deep(.cm-tooltip-autocomplete ul li:hover) {\n border-radius: 3px;\n color: var(--scalar-color-1) !important;\n background: var(--scalar-background-3) !important;\n}\n/* Disable active line highlighting */\n:deep(.cm-activeLine),\n:deep(.cm-activeLineGutter) {\n background-color: transparent;\n}\n/* Color selection matching */\n:deep(.cm-selectionMatch),\n:deep(.cm-matchingBracket) {\n border-radius: var(--scalar-radius);\n background: var(--scalar-background-4) !important;\n}\n/* Color Picker Swatches */\n:deep(.cm-css-color-picker-wrapper) {\n display: inline-flex;\n outline: 1px solid var(--scalar-background-3);\n border-radius: 3px;\n overflow: hidden;\n}\n/* Number gutter */\n:deep(.cm-gutters) {\n background-color: transparent;\n border-right: none;\n color: var(--scalar-color-3);\n font-size: var(--scalar-small);\n line-height: 22px;\n border-radius: 0 0 0 3px;\n}\n:deep(.cm-gutters:before) {\n content: '';\n position: absolute;\n top: 2px;\n left: 2px;\n width: calc(100% - 2px);\n height: calc(100% - 4px);\n border-radius: var(--scalar-radius) 0 0 var(--scalar-radius);\n background-color: var(--scalar-background-1);\n}\n:deep(.cm-gutterElement) {\n font-family: var(--scalar-font-code) !important;\n padding-left: 0px !important;\n padding-right: 6px !important;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n position: relative;\n}\n:deep(.cm-lineNumbers .cm-gutterElement) {\n min-width: fit-content;\n}\n:deep(.cm-gutter + .cm-gutter :not(.cm-foldGutter) .cm-gutterElement) {\n padding-left: 0 !important;\n}\n:deep(.cm-scroller) {\n overflow: auto;\n}\n.line-wrapping:focus-within :deep(.cm-content) {\n display: inline-table;\n min-height: fit-content;\n padding: 3px 6px;\n white-space: break-spaces;\n word-break: break-all;\n}\n</style>\n<style>\n.cm-pill {\n color: var(--scalar-color-1) !important;\n padding: 0px 9px;\n border-radius: 3px;\n display: inline-block;\n border-radius: 30px;\n font-size: var(--scalar-small);\n}\n.light-mode .cm-pill {\n background: var(--scalar-background-3) !important;\n}\n.dark-mode .cm-pill {\n background: color-mix(in srgb, var(--tw-bg-base), transparent 90%) !important;\n}\n.cm-pill:first-of-type {\n margin-left: 0;\n}\n.cm-editor .cm-widgetBuffer {\n display: none;\n}\n.cm-foldPlaceholder:hover {\n color: var(--scalar-color-1);\n}\n.cm-foldGutter .cm-gutterElement {\n font-size: var(--scalar-heading-4);\n padding: 2px !important;\n}\n.cm-foldGutter .cm-gutterElement:first-of-type {\n display: none;\n}\n.cm-foldGutter .cm-gutterElement .cm-foldMarker {\n padding: 2px;\n padding-top: 2px;\n}\n.cm-foldGutter .cm-gutterElement:hover .cm-foldMarker {\n background: var(--scalar-background-2);\n border-radius: var(--scalar-radius);\n color: var(--scalar-color-1);\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCE,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4GhB,MAAM,OAAO;EAUb,MAAM,QAAQ,UAAU;EACxB,MAAM,cAAc,MAAM,MAAM,MAAM,QAAQ;EAC9C,MAAM,YAAY,IAAI,MAAK;;;;EAQ3B,MAAM,gBAAgB,eAAwB;AAC5C,OAAI,QAAA,MAAU,OACZ,QAAO;AAET,UAAO,QAAA,SAAS,aAAc,MAAM,QAAQ,QAAA,KAAK,IAAI,QAAA,KAAK,SAAS,UAAU;IAC9E;;;;EAKD,MAAM,iBAAiB,eACrB,QAAA,WAAW;GAAC;GAAQ;GAAS;GAAO,GAAG,CAAC,QAAQ,QAAQ,CAC1D;;;;;EAMA,MAAM,cAAc,eAAmC;AACrD,OAAI,MAAM,QAAQ,QAAA,KAAK,CACrB,QAAO,QAAA,KAAK,MAAM,MAAM,MAAM,OAAO,IAAI;AAE3C,UAAO,QAAA;IACR;;;;EAQD,MAAM,gBAAgB,UAAwB;AAC5C,OAAI,CAAC,QAAA,oBAAoB,UAAU,eAAe,QAAA,WAAW,CAC3D;AAIF,OAAI,QAAA,kBACF,SAAA,kBAAkB,MAAK;OAEvB,MAAK,qBAAqB,MAAK;;;;;EAOnC,MAAM,gBAAgB,UAAwB;AAC5C,OAAI,QAAA,kBACF,SAAA,kBAAkB,MAAK;OAEvB,MAAK,UAAU,MAAK;;;;;EAOxB,MAAM,cAAc,UAAwB;AAC1C,aAAU,QAAQ;AAElB,OAAI,QAAA,cAAc,QAAA,WAChB,cAAa,MAAK;AAGpB,QAAK,QAAQ,MAAK;;;;;EAMpB,MAAM,sBAAsB,UAC1B,KAAK,qBAAqB,MAAK;;;;;EASjC,MAAM,wBAAqC;GACzC,MAAM,iBAA8B,CAAC,GAAG,QAAA,WAAU;AAElD,OAAI,QAAA,YACF,gBAAe,KAAK,YAAoB;AAG1C,UAAO;;;;;EAMT,MAAM,sBAAsB,eAC1B,WAAW;GACT,aAAU,QAAA;GACV,YAAY,QAAA,WAAW;GACxB,CAAC,CACJ;;;;EAKA,MAAM,uBAAuB,eAA4B;GACvD,GAAG,iBAAiB;GACpB,oBAAoB;GACpB;GACD,CAAA;EAED,MAAM,gBAA4C,IAAI,KAAI;;EAG1D,MAAM,kBAAkB,UAAuC;AAC7D,OAAI,OAAO,UAAU,SACnB,QAAO;AAET,UAAO,KAAK,UAAU,MAAK;;EAG7B,MAAM,EAAE,eAAe,cAAc;GACnC,SAAS,YAAY,eAAe,QAAA,WAAW,CAAC;GAChD,WAAW,UAAU;AACnB,iBAAa,MAAK;AAClB,8BAAyB;;GAE3B,eAAe;AACb,cAAU,QAAQ;;GAEpB,QAAQ;GACR;GACA,kBAAkB,YAAY,QAAA,iBAAiB;GAC/C,cAAc,YAAY,QAAA,aAAa;GACvC,sBAAsB,YAAY,QAAA,qBAAqB;GACvD,aAAa,YAAY,QAAA,YAAY;GACrC,UAAU,YAAY,QAAA,SAAS;GAC/B,MAAM,YAAY,QAAA,KAAK;GACvB,YAAY;GACZ,aAAa,YAAY,QAAA,YAAY;GACtC,CAAA;;;;AAKD,QAAM,kBAAkB;AACtB,OAAI,WAAW,SAAS,OAAO,OAAO,OAAO,YAAY,CACvD,YAAW,MAAM,OAAM;IAE1B;EAKD,MAAM,eAAe,IAAI,MAAK;EAC9B,MAAM,gBAAgB,IAAI,GAAE;EAC5B,MAAM,mBAAmB,IAAI;GAAE,MAAM;GAAG,KAAK;GAAG,CAAA;EAChD,MAAM,cAAc,IAEV,KAAI;EAEd,MAAM,EAAE,sBAAsB,6BAA6B,YAAY;GACrE;GACA,OAAO;GACP;GACA;GACD,CAAA;;;;EAKD,MAAM,2BAA2B,eAAwB;AACvD,UACE,aAAa,SACb,QAAA,iBACA,QAAA,WAAW,WACX,QAAQ,QAAA,YAAW;IAEtB;;;;EAQD,MAAM,iBAAiB,KAAa,UAA+B;AACjE,OAAI,aAAa,OAAO;AACtB,QAAI,QAAQ,UAAU,QAAQ,MAAM;AAClC,WAAM,gBAAe;AACrB,iBAAY,OAAO,eAAe,IAAG;eAC5B,QAAQ,SAAS;AAC1B,WAAM,gBAAe;AACrB,iBAAY,OAAO,cAAa;;AAElC;;AAGF,OAAI,QAAQ,YAAY,CAAC,QAAA,iBACvB,OAAM,iBAAgB;AAGxB,OAAI,QAAQ,WAAW,MAAM,kBAAkB,eAC7C,cAAa,MAAM,OAAO,eAAe,GAAE;;AAO/C,WAAa;GAMX,QAAQ,aAAwC;AAC9C,QAAI,CAAC,WAAW,MACd;AAEF,eAAW,MAAM,OAAM;AAEvB,QAAI,CAAC,UAAU,SAAS,CACtB;IAGF,MAAM,gBAAgB;AACpB,SAAI,aAAa,QACf,QAAO;AAET,SAAI,aAAa,MACf,QAAO,WAAW,MAAM,MAAM,IAAI;AAEpC,YAAO;QACN;AAGH,eAAW,MAAM,SAAS;KACxB,WAAW,EAAE,QAAQ;KACrB,gBAAgB;KACjB,CAAA;;GAEH;GACA;GACA;GACA;GACA;GACA;GACA,YAAS,QAAA;GACT,sBAAsB,WAAW,OAAO,MAAM,UAAU,KAAK;GAC7D;GACD,CAAA;;;IAMS,QAAA,YAAA,WAAA,EADR,mBAUM,OAAA;;KARJ,OAAK,eAAA,CAAC,4DAA0D;uCACf,QAAA,WAAM;cAA4B,QAAA,WAAM;sBAAoC,QAAA;;KAK7H,eAAY;QACZ,mBAAuD,QAAvD,YAAuD,gBAApB,QAAA,WAAU,EAAA,EAAA,CAAA,EAAA,EAAA,IAKlC,QAAA,MAAU,UAAA,WAAA,EADvB,YAM4C,8BAAA;;KAJzC,SAAS,QAAA;KACT,YAAY,QAAA;KACZ,MAAM,YAAA;KACN,OAAO,QAAA;KACP,uBAAmB;;;;;;UAIT,cAAA,SAAA,WAAA,EADb,YAK4C,8BAAA;;KAHzC,SAAS,QAAA;KACT,YAAY,QAAA;KACZ,OAAO,eAAA;KACP,uBAAmB;;;;;UAIT,QAAA,UAAU,UAAA,WAAA,EADvB,YAK4C,8BAAA;;KAHzC,SAAS,QAAA;KACT,YAAY,QAAA;KACZ,OAAO,QAAA;KACP,uBAAmB;;;;;wBAGtB,mBAyBM,OAzBN,WAyBM;;KAvBH,IAAI,MAAA,YAAW;OACRA,KAAAA,QAAM;cACV;KAAJ,KAAI;KACJ,OAAK,CAAC,kPAAgP;0GAC1H,QAAA;gCAA8C,QAAA;sBAA6B,QAAA;;KAMtM,WAAO;mEAAY,cAAa,QAAS,OAAM,EAAA,CAAA,OAAA,CAAA,EAAA,CAAA,OAAA,CAAA;qDAChC,cAAa,SAAU,OAAM,EAAA,CAAA,QAAA,CAAA;qDAC5B,cAAa,UAAW,OAAM,EAAA,CAAA,SAAA,CAAA;mEAC7B,cAAa,MAAO,OAAM,EAAA,CAAA,OAAA,CAAA,EAAA,CAAA,KAAA,CAAA;;UAGnC,QAAA,oBAAA,WAAA,EADT,mBAOM,OAPN,YAOM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA;qBAJS,WAEb,GAAA;KAAA,mBAA+D,OAAA,EAA1D,OAAM,4CAA0C,EAAC,OAAG,GAAA;qBAAM,UAC/D,GAAA;KAAA,mBAA+D,OAAA,EAA1D,OAAM,4CAA0C,EAAC,OAAG,GAAA;qBAAM,aACjE,GAAA;;IAKMC,KAAAA,OAAO,WAAA,WAAA,EADf,mBAIM,OAJN,YAIM,CADJ,WAAuB,KAAA,QAAA,WAAA,EAAA,EAAA,KAAA,GAAA,KAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;IAKjBA,KAAAA,OAAO,QAAA,WAAA,EADf,mBAIM,OAJN,YAIM,CADJ,WAAoB,KAAA,QAAA,QAAA,EAAA,EAAA,KAAA,GAAA,KAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;IAKd,QAAA,YAAA,WAAA,EADR,mBAIM,OAJN,YAEoX,aAEpX,IAAA,mBAAA,IAAA,KAAA;IAIQ,yBAAA,SAA4B,QAAA,eAAA,WAAA,EADpC,YAOmC,sCAAA;;cAL7B;KAAJ,KAAI;KACH,kBAAkB,iBAAA;KAClB,aAAa,QAAA;KACb,OAAO,cAAA;KACP,YAAQ,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,YAAA;MAAA,MAAA;MAAA,MAAA;MAAA,CAAA;KACd,UAAQ,MAAA,qBAAoB"}
|
package/dist/v2/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//#region src/v2/constants.ts
|
|
2
2
|
/** The version number taken from the package.json. Consumers can override at build time via define (e.g. OVERRIDE_PACKAGE_VERSION: JSON.stringify('1.2.3')). */
|
|
3
|
-
var APP_VERSION = typeof OVERRIDE_PACKAGE_VERSION !== "undefined" ? OVERRIDE_PACKAGE_VERSION : "2.39.
|
|
3
|
+
var APP_VERSION = typeof OVERRIDE_PACKAGE_VERSION !== "undefined" ? OVERRIDE_PACKAGE_VERSION : "2.39.4";
|
|
4
4
|
//#endregion
|
|
5
5
|
export { APP_VERSION };
|
|
6
6
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Editor.vue.d.ts","sourceRoot":"","sources":["../../../../../../src/v2/features/collection/components/Editor/Editor.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Editor.vue.d.ts","sourceRoot":"","sources":["../../../../../../src/v2/features/collection/components/Editor/Editor.vue"],"names":[],"mappings":"AA4xCA,QAAA,MAAM,YAAY;;;;;;;;;;;;kGAEhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|
|
@@ -2,7 +2,7 @@ import _plugin_vue_export_helper_default from "../../../../../_virtual/_plugin-v
|
|
|
2
2
|
import Editor_vue_vue_type_script_setup_true_lang_default from "./Editor.vue.script.js";
|
|
3
3
|
/* empty css */
|
|
4
4
|
//#region src/v2/features/collection/components/Editor/Editor.vue
|
|
5
|
-
var Editor_default = /* @__PURE__ */ _plugin_vue_export_helper_default(Editor_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-
|
|
5
|
+
var Editor_default = /* @__PURE__ */ _plugin_vue_export_helper_default(Editor_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-e99ee660"]]);
|
|
6
6
|
//#endregion
|
|
7
7
|
export { Editor_default as default };
|
|
8
8
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Editor.vue.js","names":[],"sources":["../../../../../../src/v2/features/collection/components/Editor/Editor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton, ScalarHotkey, useLoadingState } from '@scalar/components'\nimport { debounce } from '@scalar/helpers/general/debounce'\nimport { isObject } from '@scalar/helpers/object/is-object'\nimport { ScalarIconArrowsIn, ScalarIconArrowsOut } from '@scalar/icons'\nimport * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js'\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n ref,\n shallowRef,\n watch,\n} from 'vue'\n\nimport type { CollectionProps } from '@/v2/features/app/helpers/routes'\nimport { useEditor, useJsonPointerLinkSupport } from '@/v2/features/editor'\nimport { createJsonModel } from '@/v2/features/editor/helpers/json/create-json-model'\nimport { createYamlModel } from '@/v2/features/editor/helpers/yaml/create-yaml-model'\nimport { useEditorMarkers } from '@/v2/features/editor/hooks/use-editor-markers'\n\nimport EditorDiagnosticsPanel from './components/EditorDiagnosticsPanel.vue'\nimport EditorSavePanel from './components/EditorSavePanel.vue'\nimport { getDiagnosticCounts } from './helpers/get-diagnostic-counts'\nimport { getOperationContext } from './helpers/get-operation-context'\nimport { getVisibleDiagnostics } from './helpers/get-visible-diagnostics'\nimport { parseEditorObject } from './helpers/parse-editor-object'\nimport { stringifyDocument } from './helpers/stringify-document'\nimport { useEditorState } from './hooks/use-editor-state'\n\nconst { collectionType, documentSlug, method, path, workspaceStore } =\n defineProps<CollectionProps>()\n\nconst MAX_VISIBLE_DIAGNOSTICS = 6\nconst EDITOR_PERSIST_DEBOUNCE_KEY = 'editor:replace-document'\n\nconst monacoEditorRef = ref<HTMLElement>()\nconst editorApi = shallowRef<ReturnType<typeof useEditor>>()\n\nconst saveLoader = useLoadingState()\nconst {\n isAutoSaveEnabled,\n isDirty,\n editorLanguage,\n isDiagnosticsPaneExpanded,\n isEditorMaximized,\n isYamlMode,\n editorBottomPadding,\n editorRootClass,\n getLanguageToggleClass,\n toggleEditorMaximized,\n} = useEditorState()\n\nconst jsonModel = createJsonModel()\nconst yamlModel = createYamlModel()\n\nconst selectedLanguage = ref<'json' | 'yaml'>('json')\n\nconst currentModel = computed(() => {\n return selectedLanguage.value === 'json' ? jsonModel : yamlModel\n})\n\nconst monacoEditorInstance = computed(() => editorApi.value?.editor)\nconst { markers: diagnostics } = useEditorMarkers(monacoEditorInstance)\n\nconst syncEditorBottomPadding = () => {\n editorApi.value?.editor.updateOptions({\n padding: {\n top: 0,\n bottom: editorBottomPadding.value,\n },\n })\n}\n\nconst diagnosticCounts = computed(() => getDiagnosticCounts(diagnostics.value))\nconst visibleDiagnostics = computed(() =>\n getVisibleDiagnostics(diagnostics.value, MAX_VISIBLE_DIAGNOSTICS),\n)\n\nconst focusDiagnostic = (marker: monaco.editor.IMarker) => {\n const editor = editorApi.value?.editor\n if (!editor) {\n return\n }\n editor.setSelection({\n startLineNumber: marker.startLineNumber,\n startColumn: marker.startColumn,\n endLineNumber: marker.endLineNumber,\n endColumn: marker.endColumn,\n })\n editor.revealPositionInCenter({\n lineNumber: marker.startLineNumber,\n column: marker.startColumn,\n })\n}\n\nconst getEditorValue = (): string | null => currentModel.value.model.getValue()\n\n/** Value from the editor for a specific language (use when switching to avoid reading the wrong model). */\nconst getEditorValueForLanguage = (language: 'json' | 'yaml'): string | null =>\n (language === 'json' ? jsonModel : yamlModel).model.getValue()\n\nconst getDocumentValue = async (\n language?: 'json' | 'yaml',\n): Promise<string> => {\n const document = await workspaceStore.getEditableDocument(documentSlug)\n return stringifyDocument(document, language ?? selectedLanguage.value)\n}\n\nconst debouncedPersist = debounce({ delay: 1500 })\n\nconst applyProgrammaticEditorValue = (value: string): void => {\n // Cancel pending auto-save work so synthetic model updates do not persist stale data.\n debouncedPersist.cleanup()\n editorApi.value?.setValue(value, true)\n}\n\nconst loadDocumentIntoEditor = async () => {\n applyProgrammaticEditorValue(await getDocumentValue())\n isDirty.value = false\n await focusOperation()\n}\n\nconst formatDocument = async () => {\n await editorApi.value?.formatDocument()\n}\n\nconst focusOperation = async () => {\n const operationContext = getOperationContext(path, method)\n if (!operationContext) {\n return\n }\n\n await editorApi.value?.focusPath([\n 'paths',\n operationContext.path,\n operationContext.method,\n ])\n}\n\nconst persistEditorToWorkspace = async (value: string) => {\n const parsed = parseEditorObject(value, editorLanguage.value)\n if (!parsed) {\n const firstError = diagnostics.value.find(\n (m) => m.severity === monaco.MarkerSeverity.Error,\n )\n if (firstError) {\n focusDiagnostic(firstError)\n }\n await saveLoader.invalidate()\n return\n }\n\n saveLoader.start()\n await workspaceStore.replaceDocument(documentSlug, parsed)\n isDirty.value = false\n await saveLoader.validate({ duration: 900 })\n}\n\nconst saveNow = async () => {\n const value = getEditorValue()\n if (!value) {\n return\n }\n await persistEditorToWorkspace(value)\n}\n\nconst handleEditorChange = (value: string) => {\n isDirty.value = true\n\n if (!isAutoSaveEnabled.value) {\n return\n }\n\n debouncedPersist.execute(EDITOR_PERSIST_DEBOUNCE_KEY, () =>\n persistEditorToWorkspace(value),\n )\n}\n\nconst focusOperationServers = async () => {\n const operationContext = getOperationContext(path, method)\n if (!operationContext) {\n return\n }\n\n const value = getEditorValue()\n if (!value) {\n return\n }\n\n const parsed = parseEditorObject(value, editorLanguage.value)\n\n if (!parsed || !isObject(parsed.paths)) {\n return\n }\n\n const pathsObject = parsed.paths as Record<string, unknown>\n const operationPathItem = pathsObject[operationContext.path]\n if (!isObject(operationPathItem)) {\n return\n }\n\n const operation = operationPathItem[operationContext.method]\n if (!isObject(operation)) {\n return\n }\n\n // Add default servers if not present\n operation.servers ??= []\n\n // Update the editor value\n editorApi.value?.setValue(stringifyDocument(parsed, editorLanguage.value))\n await editorApi.value?.focusPath([\n 'paths',\n operationContext.path,\n operationContext.method,\n 'servers',\n ])\n}\n\nuseJsonPointerLinkSupport(editorApi, currentModel)\n\nonMounted(() => {\n editorApi.value = useEditor({\n element: monacoEditorRef.value ?? document.createElement('div'),\n onChange: handleEditorChange,\n model: currentModel,\n actions: [\n {\n id: 'scalar.editor.focusOperation',\n label: 'Focus Operation',\n keybindings: [monaco.KeyMod.Alt | monaco.KeyCode.KeyO],\n run: async () => {\n await focusOperation()\n },\n },\n {\n id: 'scalar.editor.focusOperationServers',\n label: 'Focus Operation Servers',\n keybindings: [monaco.KeyMod.Alt | monaco.KeyCode.KeyS],\n run: async () => {\n await focusOperationServers()\n },\n },\n {\n id: 'scalar.editor.formatDocument',\n label: 'Format Document',\n keybindings: [\n monaco.KeyMod.Alt | monaco.KeyMod.Shift | monaco.KeyCode.KeyF,\n ],\n run: async () => {\n await formatDocument()\n },\n },\n ],\n })\n\n syncEditorBottomPadding()\n void loadDocumentIntoEditor()\n})\n\nonBeforeUnmount(() => {\n debouncedPersist.cleanup()\n\n // Persist if there is a pending save\n if (isDirty.value && isAutoSaveEnabled.value) {\n void saveNow()\n }\n editorApi.value?.dispose?.()\n // Dispose models created at setup; useEditor only disposes the editor widget, not external models.\n jsonModel.model.dispose()\n yamlModel.model.dispose()\n})\n\nwatch(() => documentSlug, loadDocumentIntoEditor)\n\nwatch(\n () => [path, method] as const,\n async () => {\n await focusOperation()\n },\n)\n\nwatch(isDiagnosticsPaneExpanded, () => {\n syncEditorBottomPadding()\n})\n\nwatch(editorLanguage, async (nextLanguage, previousLanguage) => {\n const wasDirty = isDirty.value\n // Read from the previous model before switching; getEditorValue() would use currentModel\n // which changes with selectedLanguage, so we would read the (empty) new model otherwise.\n const value = getEditorValueForLanguage(previousLanguage ?? 'json')\n if (!value) {\n selectedLanguage.value = nextLanguage\n await nextTick()\n await focusOperation()\n return\n }\n\n const parsed = parseEditorObject(value, previousLanguage ?? 'json')\n selectedLanguage.value = nextLanguage\n await nextTick()\n if (parsed) {\n applyProgrammaticEditorValue(stringifyDocument(parsed, nextLanguage))\n isDirty.value = wasDirty\n }\n await focusOperation()\n})\n\nwatch(\n isAutoSaveEnabled,\n (isEnabled) => {\n if (!isEnabled) {\n debouncedPersist.cleanup()\n return\n }\n\n if (isDirty.value) {\n const value = getEditorValue()\n if (!value) {\n return\n }\n debouncedPersist.execute(EDITOR_PERSIST_DEBOUNCE_KEY, () =>\n persistEditorToWorkspace(value),\n )\n }\n },\n { flush: 'post' },\n)\n</script>\n\n<template>\n <div\n v-if=\"\n collectionType === 'operation' &&\n getOperationContext(path, method) !== null\n \"\n class=\"flex w-full min-w-0 flex-1 flex-col gap-2\"\n :class=\"editorRootClass\">\n <div\n class=\"grid grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center gap-3\">\n <div\n class=\"flex min-w-0 items-center gap-1 overflow-x-auto whitespace-nowrap\">\n <span class=\"text-c-2 text-xs font-medium whitespace-nowrap\">\n Shortcuts\n </span>\n\n <ScalarButton\n class=\"whitespace-nowrap\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"focusOperation\">\n <span>Operation</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"O\"\n :modifier=\"['Alt']\" />\n </span>\n </ScalarButton>\n\n <ScalarButton\n class=\"whitespace-nowrap\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"focusOperationServers\">\n <span>Servers</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"S\"\n :modifier=\"['Alt']\" />\n </span>\n </ScalarButton>\n </div>\n\n <div\n aria-label=\"Editor language\"\n class=\"bg-b-1 shadow-border flex items-center justify-self-center overflow-hidden rounded-lg p-0.5\"\n role=\"tablist\">\n <ScalarButton\n :aria-selected=\"!isYamlMode\"\n :class=\"getLanguageToggleClass(!isYamlMode)\"\n role=\"tab\"\n size=\"xs\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"editorLanguage = 'json'\">\n JSON\n </ScalarButton>\n <ScalarButton\n :aria-selected=\"isYamlMode\"\n :class=\"getLanguageToggleClass(isYamlMode)\"\n role=\"tab\"\n size=\"xs\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"editorLanguage = 'yaml'\">\n YAML\n </ScalarButton>\n </div>\n\n <div class=\"flex min-w-0 shrink-0 items-center gap-2 justify-self-end\">\n <ScalarButton\n size=\"xs\"\n variant=\"ghost\"\n @click=\"formatDocument\">\n <span>Format {{ isYamlMode ? 'YAML' : 'JSON' }}</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"F\"\n :modifier=\"['Alt', 'Shift']\" />\n </span>\n </ScalarButton>\n <ScalarButton\n :aria-label=\"\n isEditorMaximized ? 'Restore editor size' : 'Maximize editor'\n \"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"toggleEditorMaximized\">\n <span>{{ isEditorMaximized ? 'Restore' : 'Maximize' }}</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarIconArrowsIn\n v-if=\"isEditorMaximized\"\n class=\"size-3.5\" />\n <ScalarIconArrowsOut\n v-else\n class=\"size-3.5\" />\n </span>\n </ScalarButton>\n </div>\n </div>\n\n <div class=\"flex min-h-0 w-full min-w-0 flex-1 rounded-lg border\">\n <div\n class=\"relative w-full min-w-0 flex-1\"\n :class=\"isEditorMaximized ? 'h-full min-h-0' : 'h-[500px]'\">\n <div class=\"pointer-events-none absolute top-2 right-2 z-10\">\n <EditorSavePanel\n :isAutoSaveEnabled=\"isAutoSaveEnabled\"\n :isDirty=\"isDirty\"\n :saveLoader=\"saveLoader\"\n @saveNow=\"saveNow\"\n @update:isAutoSaveEnabled=\"isAutoSaveEnabled = $event\" />\n </div>\n\n <EditorDiagnosticsPanel\n :diagnosticCounts=\"diagnosticCounts\"\n :expanded=\"isDiagnosticsPaneExpanded\"\n :visibleDiagnostics=\"visibleDiagnostics\"\n @focusDiagnostic=\"focusDiagnostic\"\n @toggle=\"isDiagnosticsPaneExpanded = !isDiagnosticsPaneExpanded\" />\n\n <div\n ref=\"monacoEditorRef\"\n class=\"h-full w-full min-w-0 flex-1\" />\n </div>\n </div>\n </div>\n <div v-else>No operation context found</div>\n</template>\n<style scoped>\n.editor-container {\n width: 100%;\n height: 100%;\n}\n\n:deep(.json-path-highlight) {\n background-color: rgba(255, 200, 0, 0.35);\n border-radius: 4px;\n}\n\n:deep(.json-focus-highlight) {\n background-color: color-mix(\n in srgb,\n var(--scalar-color-accent, #24b47e) 18%,\n transparent\n );\n border-radius: 4px;\n}\n</style>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"Editor.vue.js","names":[],"sources":["../../../../../../src/v2/features/collection/components/Editor/Editor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton, ScalarHotkey, useLoadingState } from '@scalar/components'\nimport { debounce } from '@scalar/helpers/general/debounce'\nimport { isObject } from '@scalar/helpers/object/is-object'\nimport { ScalarIconArrowsIn, ScalarIconArrowsOut } from '@scalar/icons'\nimport * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js'\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n ref,\n shallowRef,\n watch,\n} from 'vue'\n\nimport type { CollectionProps } from '@/v2/features/app/helpers/routes'\nimport { useEditor, useJsonPointerLinkSupport } from '@/v2/features/editor'\nimport { createJsonModel } from '@/v2/features/editor/helpers/json/create-json-model'\nimport { createYamlModel } from '@/v2/features/editor/helpers/yaml/create-yaml-model'\nimport { useEditorMarkers } from '@/v2/features/editor/hooks/use-editor-markers'\n\nimport EditorDiagnosticsPanel from './components/EditorDiagnosticsPanel.vue'\nimport EditorSavePanel from './components/EditorSavePanel.vue'\nimport { getDiagnosticCounts } from './helpers/get-diagnostic-counts'\nimport { getOperationContext } from './helpers/get-operation-context'\nimport { getVisibleDiagnostics } from './helpers/get-visible-diagnostics'\nimport { parseEditorObject } from './helpers/parse-editor-object'\nimport { stringifyDocument } from './helpers/stringify-document'\nimport { useEditorState } from './hooks/use-editor-state'\n\nconst { collectionType, documentSlug, method, path, workspaceStore } =\n defineProps<CollectionProps>()\n\nconst MAX_VISIBLE_DIAGNOSTICS = 6\nconst EDITOR_PERSIST_DEBOUNCE_KEY = 'editor:replace-document'\n\nconst monacoEditorRef = ref<HTMLElement>()\nconst editorApi = shallowRef<ReturnType<typeof useEditor>>()\n\nconst saveLoader = useLoadingState()\nconst {\n isAutoSaveEnabled,\n isDirty,\n editorLanguage,\n isDiagnosticsPaneExpanded,\n isEditorMaximized,\n isYamlMode,\n editorBottomPadding,\n editorRootClass,\n getLanguageToggleClass,\n toggleEditorMaximized,\n} = useEditorState()\n\nconst jsonModel = createJsonModel()\nconst yamlModel = createYamlModel()\n\nconst selectedLanguage = ref<'json' | 'yaml'>('json')\n\nconst currentModel = computed(() => {\n return selectedLanguage.value === 'json' ? jsonModel : yamlModel\n})\n\nconst monacoEditorInstance = computed(() => editorApi.value?.editor)\nconst { markers: diagnostics } = useEditorMarkers(monacoEditorInstance)\n\nconst syncEditorBottomPadding = () => {\n editorApi.value?.editor.updateOptions({\n padding: {\n top: 0,\n bottom: editorBottomPadding.value,\n },\n })\n}\n\nconst diagnosticCounts = computed(() => getDiagnosticCounts(diagnostics.value))\nconst visibleDiagnostics = computed(() =>\n getVisibleDiagnostics(diagnostics.value, MAX_VISIBLE_DIAGNOSTICS),\n)\n\nconst focusDiagnostic = (marker: monaco.editor.IMarker) => {\n const editor = editorApi.value?.editor\n if (!editor) {\n return\n }\n editor.setSelection({\n startLineNumber: marker.startLineNumber,\n startColumn: marker.startColumn,\n endLineNumber: marker.endLineNumber,\n endColumn: marker.endColumn,\n })\n editor.revealPositionInCenter({\n lineNumber: marker.startLineNumber,\n column: marker.startColumn,\n })\n}\n\nconst getEditorValue = (): string | null => currentModel.value.model.getValue()\n\n/** Value from the editor for a specific language (use when switching to avoid reading the wrong model). */\nconst getEditorValueForLanguage = (language: 'json' | 'yaml'): string | null =>\n (language === 'json' ? jsonModel : yamlModel).model.getValue()\n\nconst getDocumentValue = async (\n language?: 'json' | 'yaml',\n): Promise<string> => {\n const document = await workspaceStore.getEditableDocument(documentSlug)\n return stringifyDocument(document, language ?? selectedLanguage.value)\n}\n\nconst debouncedPersist = debounce({ delay: 1500 })\n\nconst applyProgrammaticEditorValue = (value: string): void => {\n // Cancel pending auto-save work so synthetic model updates do not persist stale data.\n debouncedPersist.cleanup()\n editorApi.value?.setValue(value, true)\n}\n\nconst loadDocumentIntoEditor = async () => {\n applyProgrammaticEditorValue(await getDocumentValue())\n isDirty.value = false\n await focusOperation()\n}\n\nconst formatDocument = async () => {\n await editorApi.value?.formatDocument()\n}\n\nconst focusOperation = async () => {\n const operationContext = getOperationContext(path, method)\n if (!operationContext) {\n return\n }\n\n await editorApi.value?.focusPath([\n 'paths',\n operationContext.path,\n operationContext.method,\n ])\n}\n\nconst persistEditorToWorkspace = async (value: string) => {\n const parsed = parseEditorObject(value, editorLanguage.value)\n if (!parsed) {\n const firstError = diagnostics.value.find(\n (m) => m.severity === monaco.MarkerSeverity.Error,\n )\n if (firstError) {\n focusDiagnostic(firstError)\n }\n await saveLoader.invalidate()\n return\n }\n\n saveLoader.start()\n await workspaceStore.replaceDocument(documentSlug, parsed)\n isDirty.value = false\n await saveLoader.validate({ duration: 900 })\n}\n\nconst saveNow = async () => {\n const value = getEditorValue()\n if (!value) {\n return\n }\n await persistEditorToWorkspace(value)\n}\n\nconst handleEditorChange = (value: string) => {\n isDirty.value = true\n\n if (!isAutoSaveEnabled.value) {\n return\n }\n\n debouncedPersist.execute(EDITOR_PERSIST_DEBOUNCE_KEY, () =>\n persistEditorToWorkspace(value),\n )\n}\n\nconst focusOperationServers = async () => {\n const operationContext = getOperationContext(path, method)\n if (!operationContext) {\n return\n }\n\n const value = getEditorValue()\n if (!value) {\n return\n }\n\n const parsed = parseEditorObject(value, editorLanguage.value)\n\n if (!parsed || !isObject(parsed.paths)) {\n return\n }\n\n const pathsObject = parsed.paths as Record<string, unknown>\n const operationPathItem = pathsObject[operationContext.path]\n if (!isObject(operationPathItem)) {\n return\n }\n\n const operation = operationPathItem[operationContext.method]\n if (!isObject(operation)) {\n return\n }\n\n // Add default servers if not present\n operation.servers ??= []\n\n // Update the editor value\n editorApi.value?.setValue(stringifyDocument(parsed, editorLanguage.value))\n await editorApi.value?.focusPath([\n 'paths',\n operationContext.path,\n operationContext.method,\n 'servers',\n ])\n}\n\nuseJsonPointerLinkSupport(editorApi, currentModel)\n\nonMounted(() => {\n editorApi.value = useEditor({\n element: monacoEditorRef.value ?? document.createElement('div'),\n onChange: handleEditorChange,\n model: currentModel,\n actions: [\n {\n id: 'scalar.editor.focusOperation',\n label: 'Focus Operation',\n keybindings: [monaco.KeyMod.Alt | monaco.KeyCode.KeyO],\n run: async () => {\n await focusOperation()\n },\n },\n {\n id: 'scalar.editor.focusOperationServers',\n label: 'Focus Operation Servers',\n keybindings: [monaco.KeyMod.Alt | monaco.KeyCode.KeyS],\n run: async () => {\n await focusOperationServers()\n },\n },\n {\n id: 'scalar.editor.formatDocument',\n label: 'Format Document',\n keybindings: [\n monaco.KeyMod.Alt | monaco.KeyMod.Shift | monaco.KeyCode.KeyF,\n ],\n run: async () => {\n await formatDocument()\n },\n },\n ],\n })\n\n syncEditorBottomPadding()\n void loadDocumentIntoEditor()\n})\n\nonBeforeUnmount(() => {\n debouncedPersist.cleanup()\n\n // Persist if there is a pending save\n if (isDirty.value && isAutoSaveEnabled.value) {\n void saveNow()\n }\n editorApi.value?.dispose?.()\n // Dispose models created at setup; useEditor only disposes the editor widget, not external models.\n jsonModel.model.dispose()\n yamlModel.model.dispose()\n})\n\nwatch(() => documentSlug, loadDocumentIntoEditor)\n\nwatch(\n () => [path, method] as const,\n async () => {\n await focusOperation()\n },\n)\n\nwatch(isDiagnosticsPaneExpanded, () => {\n syncEditorBottomPadding()\n})\n\nwatch(editorLanguage, async (nextLanguage, previousLanguage) => {\n const wasDirty = isDirty.value\n // Read from the previous model before switching; getEditorValue() would use currentModel\n // which changes with selectedLanguage, so we would read the (empty) new model otherwise.\n const value = getEditorValueForLanguage(previousLanguage ?? 'json')\n if (!value) {\n selectedLanguage.value = nextLanguage\n await nextTick()\n await focusOperation()\n return\n }\n\n const parsed = parseEditorObject(value, previousLanguage ?? 'json')\n selectedLanguage.value = nextLanguage\n await nextTick()\n if (parsed) {\n applyProgrammaticEditorValue(stringifyDocument(parsed, nextLanguage))\n isDirty.value = wasDirty\n }\n await focusOperation()\n})\n\nwatch(\n isAutoSaveEnabled,\n (isEnabled) => {\n if (!isEnabled) {\n debouncedPersist.cleanup()\n return\n }\n\n if (isDirty.value) {\n const value = getEditorValue()\n if (!value) {\n return\n }\n debouncedPersist.execute(EDITOR_PERSIST_DEBOUNCE_KEY, () =>\n persistEditorToWorkspace(value),\n )\n }\n },\n { flush: 'post' },\n)\n</script>\n\n<template>\n <div\n v-if=\"\n collectionType === 'operation' &&\n getOperationContext(path, method) !== null\n \"\n class=\"flex w-full min-w-0 flex-1 flex-col gap-2\"\n :class=\"editorRootClass\">\n <div\n class=\"grid grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center gap-3\">\n <div\n class=\"flex min-w-0 items-center gap-1 overflow-x-auto whitespace-nowrap\">\n <span class=\"text-c-2 text-xs font-medium whitespace-nowrap\">\n Shortcuts\n </span>\n\n <ScalarButton\n class=\"whitespace-nowrap\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"focusOperation\">\n <span>Operation</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"O\"\n :modifier=\"['Alt']\" />\n </span>\n </ScalarButton>\n\n <ScalarButton\n class=\"whitespace-nowrap\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"focusOperationServers\">\n <span>Servers</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"S\"\n :modifier=\"['Alt']\" />\n </span>\n </ScalarButton>\n </div>\n\n <div\n aria-label=\"Editor language\"\n class=\"bg-b-1 shadow-border flex items-center justify-self-center overflow-hidden rounded-lg p-0.5\"\n role=\"tablist\">\n <ScalarButton\n :aria-selected=\"!isYamlMode\"\n :class=\"getLanguageToggleClass(!isYamlMode)\"\n role=\"tab\"\n size=\"xs\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"editorLanguage = 'json'\">\n JSON\n </ScalarButton>\n <ScalarButton\n :aria-selected=\"isYamlMode\"\n :class=\"getLanguageToggleClass(isYamlMode)\"\n role=\"tab\"\n size=\"xs\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"editorLanguage = 'yaml'\">\n YAML\n </ScalarButton>\n </div>\n\n <div class=\"flex min-w-0 shrink-0 items-center gap-2 justify-self-end\">\n <ScalarButton\n size=\"xs\"\n variant=\"ghost\"\n @click=\"formatDocument\">\n <span>Format {{ isYamlMode ? 'YAML' : 'JSON' }}</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"F\"\n :modifier=\"['Alt', 'Shift']\" />\n </span>\n </ScalarButton>\n <ScalarButton\n :aria-label=\"\n isEditorMaximized ? 'Restore editor size' : 'Maximize editor'\n \"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"toggleEditorMaximized\">\n <span>{{ isEditorMaximized ? 'Restore' : 'Maximize' }}</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarIconArrowsIn\n v-if=\"isEditorMaximized\"\n class=\"size-3.5\" />\n <ScalarIconArrowsOut\n v-else\n class=\"size-3.5\" />\n </span>\n </ScalarButton>\n </div>\n </div>\n\n <div class=\"flex min-h-0 w-full min-w-0 flex-1 rounded-lg border\">\n <div\n class=\"relative w-full min-w-0 flex-1\"\n :class=\"isEditorMaximized ? 'h-full min-h-0' : 'h-125'\">\n <div class=\"pointer-events-none absolute top-2 right-2 z-10\">\n <EditorSavePanel\n :isAutoSaveEnabled=\"isAutoSaveEnabled\"\n :isDirty=\"isDirty\"\n :saveLoader=\"saveLoader\"\n @saveNow=\"saveNow\"\n @update:isAutoSaveEnabled=\"isAutoSaveEnabled = $event\" />\n </div>\n\n <EditorDiagnosticsPanel\n :diagnosticCounts=\"diagnosticCounts\"\n :expanded=\"isDiagnosticsPaneExpanded\"\n :visibleDiagnostics=\"visibleDiagnostics\"\n @focusDiagnostic=\"focusDiagnostic\"\n @toggle=\"isDiagnosticsPaneExpanded = !isDiagnosticsPaneExpanded\" />\n\n <div\n ref=\"monacoEditorRef\"\n class=\"h-full w-full min-w-0 flex-1 [&_.monaco-editor]:rounded-lg [&_.overflow-guard]:rounded-lg\" />\n </div>\n </div>\n </div>\n <div v-else>No operation context found</div>\n</template>\n<style scoped>\n.editor-container {\n width: 100%;\n height: 100%;\n}\n\n:deep(.json-path-highlight) {\n background-color: rgba(255, 200, 0, 0.35);\n border-radius: 4px;\n}\n\n:deep(.json-focus-highlight) {\n background-color: color-mix(\n in srgb,\n var(--scalar-color-accent, #24b47e) 18%,\n transparent\n );\n border-radius: 4px;\n}\n</style>\n"],"mappings":""}
|
|
@@ -322,7 +322,7 @@ var Editor_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineC
|
|
|
322
322
|
}))])]),
|
|
323
323
|
_: 1
|
|
324
324
|
}, 8, ["aria-label", "onClick"])])
|
|
325
|
-
]), createElementVNode("div", _hoisted_9, [createElementVNode("div", { class: normalizeClass(["relative w-full min-w-0 flex-1", unref(isEditorMaximized) ? "h-full min-h-0" : "h-
|
|
325
|
+
]), createElementVNode("div", _hoisted_9, [createElementVNode("div", { class: normalizeClass(["relative w-full min-w-0 flex-1", unref(isEditorMaximized) ? "h-full min-h-0" : "h-125"]) }, [
|
|
326
326
|
createElementVNode("div", _hoisted_10, [createVNode(EditorSavePanel_default, {
|
|
327
327
|
isAutoSaveEnabled: unref(isAutoSaveEnabled),
|
|
328
328
|
isDirty: unref(isDirty),
|
|
@@ -348,7 +348,7 @@ var Editor_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineC
|
|
|
348
348
|
createElementVNode("div", {
|
|
349
349
|
ref_key: "monacoEditorRef",
|
|
350
350
|
ref: monacoEditorRef,
|
|
351
|
-
class: "h-full w-full min-w-0 flex-1"
|
|
351
|
+
class: "h-full w-full min-w-0 flex-1 [&_.monaco-editor]:rounded-lg [&_.overflow-guard]:rounded-lg"
|
|
352
352
|
}, null, 512)
|
|
353
353
|
], 2)])], 2)) : (openBlock(), createElementBlock("div", _hoisted_11, "No operation context found"));
|
|
354
354
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Editor.vue.script.js","names":[],"sources":["../../../../../../src/v2/features/collection/components/Editor/Editor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton, ScalarHotkey, useLoadingState } from '@scalar/components'\nimport { debounce } from '@scalar/helpers/general/debounce'\nimport { isObject } from '@scalar/helpers/object/is-object'\nimport { ScalarIconArrowsIn, ScalarIconArrowsOut } from '@scalar/icons'\nimport * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js'\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n ref,\n shallowRef,\n watch,\n} from 'vue'\n\nimport type { CollectionProps } from '@/v2/features/app/helpers/routes'\nimport { useEditor, useJsonPointerLinkSupport } from '@/v2/features/editor'\nimport { createJsonModel } from '@/v2/features/editor/helpers/json/create-json-model'\nimport { createYamlModel } from '@/v2/features/editor/helpers/yaml/create-yaml-model'\nimport { useEditorMarkers } from '@/v2/features/editor/hooks/use-editor-markers'\n\nimport EditorDiagnosticsPanel from './components/EditorDiagnosticsPanel.vue'\nimport EditorSavePanel from './components/EditorSavePanel.vue'\nimport { getDiagnosticCounts } from './helpers/get-diagnostic-counts'\nimport { getOperationContext } from './helpers/get-operation-context'\nimport { getVisibleDiagnostics } from './helpers/get-visible-diagnostics'\nimport { parseEditorObject } from './helpers/parse-editor-object'\nimport { stringifyDocument } from './helpers/stringify-document'\nimport { useEditorState } from './hooks/use-editor-state'\n\nconst { collectionType, documentSlug, method, path, workspaceStore } =\n defineProps<CollectionProps>()\n\nconst MAX_VISIBLE_DIAGNOSTICS = 6\nconst EDITOR_PERSIST_DEBOUNCE_KEY = 'editor:replace-document'\n\nconst monacoEditorRef = ref<HTMLElement>()\nconst editorApi = shallowRef<ReturnType<typeof useEditor>>()\n\nconst saveLoader = useLoadingState()\nconst {\n isAutoSaveEnabled,\n isDirty,\n editorLanguage,\n isDiagnosticsPaneExpanded,\n isEditorMaximized,\n isYamlMode,\n editorBottomPadding,\n editorRootClass,\n getLanguageToggleClass,\n toggleEditorMaximized,\n} = useEditorState()\n\nconst jsonModel = createJsonModel()\nconst yamlModel = createYamlModel()\n\nconst selectedLanguage = ref<'json' | 'yaml'>('json')\n\nconst currentModel = computed(() => {\n return selectedLanguage.value === 'json' ? jsonModel : yamlModel\n})\n\nconst monacoEditorInstance = computed(() => editorApi.value?.editor)\nconst { markers: diagnostics } = useEditorMarkers(monacoEditorInstance)\n\nconst syncEditorBottomPadding = () => {\n editorApi.value?.editor.updateOptions({\n padding: {\n top: 0,\n bottom: editorBottomPadding.value,\n },\n })\n}\n\nconst diagnosticCounts = computed(() => getDiagnosticCounts(diagnostics.value))\nconst visibleDiagnostics = computed(() =>\n getVisibleDiagnostics(diagnostics.value, MAX_VISIBLE_DIAGNOSTICS),\n)\n\nconst focusDiagnostic = (marker: monaco.editor.IMarker) => {\n const editor = editorApi.value?.editor\n if (!editor) {\n return\n }\n editor.setSelection({\n startLineNumber: marker.startLineNumber,\n startColumn: marker.startColumn,\n endLineNumber: marker.endLineNumber,\n endColumn: marker.endColumn,\n })\n editor.revealPositionInCenter({\n lineNumber: marker.startLineNumber,\n column: marker.startColumn,\n })\n}\n\nconst getEditorValue = (): string | null => currentModel.value.model.getValue()\n\n/** Value from the editor for a specific language (use when switching to avoid reading the wrong model). */\nconst getEditorValueForLanguage = (language: 'json' | 'yaml'): string | null =>\n (language === 'json' ? jsonModel : yamlModel).model.getValue()\n\nconst getDocumentValue = async (\n language?: 'json' | 'yaml',\n): Promise<string> => {\n const document = await workspaceStore.getEditableDocument(documentSlug)\n return stringifyDocument(document, language ?? selectedLanguage.value)\n}\n\nconst debouncedPersist = debounce({ delay: 1500 })\n\nconst applyProgrammaticEditorValue = (value: string): void => {\n // Cancel pending auto-save work so synthetic model updates do not persist stale data.\n debouncedPersist.cleanup()\n editorApi.value?.setValue(value, true)\n}\n\nconst loadDocumentIntoEditor = async () => {\n applyProgrammaticEditorValue(await getDocumentValue())\n isDirty.value = false\n await focusOperation()\n}\n\nconst formatDocument = async () => {\n await editorApi.value?.formatDocument()\n}\n\nconst focusOperation = async () => {\n const operationContext = getOperationContext(path, method)\n if (!operationContext) {\n return\n }\n\n await editorApi.value?.focusPath([\n 'paths',\n operationContext.path,\n operationContext.method,\n ])\n}\n\nconst persistEditorToWorkspace = async (value: string) => {\n const parsed = parseEditorObject(value, editorLanguage.value)\n if (!parsed) {\n const firstError = diagnostics.value.find(\n (m) => m.severity === monaco.MarkerSeverity.Error,\n )\n if (firstError) {\n focusDiagnostic(firstError)\n }\n await saveLoader.invalidate()\n return\n }\n\n saveLoader.start()\n await workspaceStore.replaceDocument(documentSlug, parsed)\n isDirty.value = false\n await saveLoader.validate({ duration: 900 })\n}\n\nconst saveNow = async () => {\n const value = getEditorValue()\n if (!value) {\n return\n }\n await persistEditorToWorkspace(value)\n}\n\nconst handleEditorChange = (value: string) => {\n isDirty.value = true\n\n if (!isAutoSaveEnabled.value) {\n return\n }\n\n debouncedPersist.execute(EDITOR_PERSIST_DEBOUNCE_KEY, () =>\n persistEditorToWorkspace(value),\n )\n}\n\nconst focusOperationServers = async () => {\n const operationContext = getOperationContext(path, method)\n if (!operationContext) {\n return\n }\n\n const value = getEditorValue()\n if (!value) {\n return\n }\n\n const parsed = parseEditorObject(value, editorLanguage.value)\n\n if (!parsed || !isObject(parsed.paths)) {\n return\n }\n\n const pathsObject = parsed.paths as Record<string, unknown>\n const operationPathItem = pathsObject[operationContext.path]\n if (!isObject(operationPathItem)) {\n return\n }\n\n const operation = operationPathItem[operationContext.method]\n if (!isObject(operation)) {\n return\n }\n\n // Add default servers if not present\n operation.servers ??= []\n\n // Update the editor value\n editorApi.value?.setValue(stringifyDocument(parsed, editorLanguage.value))\n await editorApi.value?.focusPath([\n 'paths',\n operationContext.path,\n operationContext.method,\n 'servers',\n ])\n}\n\nuseJsonPointerLinkSupport(editorApi, currentModel)\n\nonMounted(() => {\n editorApi.value = useEditor({\n element: monacoEditorRef.value ?? document.createElement('div'),\n onChange: handleEditorChange,\n model: currentModel,\n actions: [\n {\n id: 'scalar.editor.focusOperation',\n label: 'Focus Operation',\n keybindings: [monaco.KeyMod.Alt | monaco.KeyCode.KeyO],\n run: async () => {\n await focusOperation()\n },\n },\n {\n id: 'scalar.editor.focusOperationServers',\n label: 'Focus Operation Servers',\n keybindings: [monaco.KeyMod.Alt | monaco.KeyCode.KeyS],\n run: async () => {\n await focusOperationServers()\n },\n },\n {\n id: 'scalar.editor.formatDocument',\n label: 'Format Document',\n keybindings: [\n monaco.KeyMod.Alt | monaco.KeyMod.Shift | monaco.KeyCode.KeyF,\n ],\n run: async () => {\n await formatDocument()\n },\n },\n ],\n })\n\n syncEditorBottomPadding()\n void loadDocumentIntoEditor()\n})\n\nonBeforeUnmount(() => {\n debouncedPersist.cleanup()\n\n // Persist if there is a pending save\n if (isDirty.value && isAutoSaveEnabled.value) {\n void saveNow()\n }\n editorApi.value?.dispose?.()\n // Dispose models created at setup; useEditor only disposes the editor widget, not external models.\n jsonModel.model.dispose()\n yamlModel.model.dispose()\n})\n\nwatch(() => documentSlug, loadDocumentIntoEditor)\n\nwatch(\n () => [path, method] as const,\n async () => {\n await focusOperation()\n },\n)\n\nwatch(isDiagnosticsPaneExpanded, () => {\n syncEditorBottomPadding()\n})\n\nwatch(editorLanguage, async (nextLanguage, previousLanguage) => {\n const wasDirty = isDirty.value\n // Read from the previous model before switching; getEditorValue() would use currentModel\n // which changes with selectedLanguage, so we would read the (empty) new model otherwise.\n const value = getEditorValueForLanguage(previousLanguage ?? 'json')\n if (!value) {\n selectedLanguage.value = nextLanguage\n await nextTick()\n await focusOperation()\n return\n }\n\n const parsed = parseEditorObject(value, previousLanguage ?? 'json')\n selectedLanguage.value = nextLanguage\n await nextTick()\n if (parsed) {\n applyProgrammaticEditorValue(stringifyDocument(parsed, nextLanguage))\n isDirty.value = wasDirty\n }\n await focusOperation()\n})\n\nwatch(\n isAutoSaveEnabled,\n (isEnabled) => {\n if (!isEnabled) {\n debouncedPersist.cleanup()\n return\n }\n\n if (isDirty.value) {\n const value = getEditorValue()\n if (!value) {\n return\n }\n debouncedPersist.execute(EDITOR_PERSIST_DEBOUNCE_KEY, () =>\n persistEditorToWorkspace(value),\n )\n }\n },\n { flush: 'post' },\n)\n</script>\n\n<template>\n <div\n v-if=\"\n collectionType === 'operation' &&\n getOperationContext(path, method) !== null\n \"\n class=\"flex w-full min-w-0 flex-1 flex-col gap-2\"\n :class=\"editorRootClass\">\n <div\n class=\"grid grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center gap-3\">\n <div\n class=\"flex min-w-0 items-center gap-1 overflow-x-auto whitespace-nowrap\">\n <span class=\"text-c-2 text-xs font-medium whitespace-nowrap\">\n Shortcuts\n </span>\n\n <ScalarButton\n class=\"whitespace-nowrap\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"focusOperation\">\n <span>Operation</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"O\"\n :modifier=\"['Alt']\" />\n </span>\n </ScalarButton>\n\n <ScalarButton\n class=\"whitespace-nowrap\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"focusOperationServers\">\n <span>Servers</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"S\"\n :modifier=\"['Alt']\" />\n </span>\n </ScalarButton>\n </div>\n\n <div\n aria-label=\"Editor language\"\n class=\"bg-b-1 shadow-border flex items-center justify-self-center overflow-hidden rounded-lg p-0.5\"\n role=\"tablist\">\n <ScalarButton\n :aria-selected=\"!isYamlMode\"\n :class=\"getLanguageToggleClass(!isYamlMode)\"\n role=\"tab\"\n size=\"xs\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"editorLanguage = 'json'\">\n JSON\n </ScalarButton>\n <ScalarButton\n :aria-selected=\"isYamlMode\"\n :class=\"getLanguageToggleClass(isYamlMode)\"\n role=\"tab\"\n size=\"xs\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"editorLanguage = 'yaml'\">\n YAML\n </ScalarButton>\n </div>\n\n <div class=\"flex min-w-0 shrink-0 items-center gap-2 justify-self-end\">\n <ScalarButton\n size=\"xs\"\n variant=\"ghost\"\n @click=\"formatDocument\">\n <span>Format {{ isYamlMode ? 'YAML' : 'JSON' }}</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"F\"\n :modifier=\"['Alt', 'Shift']\" />\n </span>\n </ScalarButton>\n <ScalarButton\n :aria-label=\"\n isEditorMaximized ? 'Restore editor size' : 'Maximize editor'\n \"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"toggleEditorMaximized\">\n <span>{{ isEditorMaximized ? 'Restore' : 'Maximize' }}</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarIconArrowsIn\n v-if=\"isEditorMaximized\"\n class=\"size-3.5\" />\n <ScalarIconArrowsOut\n v-else\n class=\"size-3.5\" />\n </span>\n </ScalarButton>\n </div>\n </div>\n\n <div class=\"flex min-h-0 w-full min-w-0 flex-1 rounded-lg border\">\n <div\n class=\"relative w-full min-w-0 flex-1\"\n :class=\"isEditorMaximized ? 'h-full min-h-0' : 'h-[500px]'\">\n <div class=\"pointer-events-none absolute top-2 right-2 z-10\">\n <EditorSavePanel\n :isAutoSaveEnabled=\"isAutoSaveEnabled\"\n :isDirty=\"isDirty\"\n :saveLoader=\"saveLoader\"\n @saveNow=\"saveNow\"\n @update:isAutoSaveEnabled=\"isAutoSaveEnabled = $event\" />\n </div>\n\n <EditorDiagnosticsPanel\n :diagnosticCounts=\"diagnosticCounts\"\n :expanded=\"isDiagnosticsPaneExpanded\"\n :visibleDiagnostics=\"visibleDiagnostics\"\n @focusDiagnostic=\"focusDiagnostic\"\n @toggle=\"isDiagnosticsPaneExpanded = !isDiagnosticsPaneExpanded\" />\n\n <div\n ref=\"monacoEditorRef\"\n class=\"h-full w-full min-w-0 flex-1\" />\n </div>\n </div>\n </div>\n <div v-else>No operation context found</div>\n</template>\n<style scoped>\n.editor-container {\n width: 100%;\n height: 100%;\n}\n\n:deep(.json-path-highlight) {\n background-color: rgba(255, 200, 0, 0.35);\n border-radius: 4px;\n}\n\n:deep(.json-focus-highlight) {\n background-color: color-mix(\n in srgb,\n var(--scalar-color-accent, #24b47e) 18%,\n transparent\n );\n border-radius: 4px;\n}\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Editor.vue.script.js","names":[],"sources":["../../../../../../src/v2/features/collection/components/Editor/Editor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton, ScalarHotkey, useLoadingState } from '@scalar/components'\nimport { debounce } from '@scalar/helpers/general/debounce'\nimport { isObject } from '@scalar/helpers/object/is-object'\nimport { ScalarIconArrowsIn, ScalarIconArrowsOut } from '@scalar/icons'\nimport * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js'\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n ref,\n shallowRef,\n watch,\n} from 'vue'\n\nimport type { CollectionProps } from '@/v2/features/app/helpers/routes'\nimport { useEditor, useJsonPointerLinkSupport } from '@/v2/features/editor'\nimport { createJsonModel } from '@/v2/features/editor/helpers/json/create-json-model'\nimport { createYamlModel } from '@/v2/features/editor/helpers/yaml/create-yaml-model'\nimport { useEditorMarkers } from '@/v2/features/editor/hooks/use-editor-markers'\n\nimport EditorDiagnosticsPanel from './components/EditorDiagnosticsPanel.vue'\nimport EditorSavePanel from './components/EditorSavePanel.vue'\nimport { getDiagnosticCounts } from './helpers/get-diagnostic-counts'\nimport { getOperationContext } from './helpers/get-operation-context'\nimport { getVisibleDiagnostics } from './helpers/get-visible-diagnostics'\nimport { parseEditorObject } from './helpers/parse-editor-object'\nimport { stringifyDocument } from './helpers/stringify-document'\nimport { useEditorState } from './hooks/use-editor-state'\n\nconst { collectionType, documentSlug, method, path, workspaceStore } =\n defineProps<CollectionProps>()\n\nconst MAX_VISIBLE_DIAGNOSTICS = 6\nconst EDITOR_PERSIST_DEBOUNCE_KEY = 'editor:replace-document'\n\nconst monacoEditorRef = ref<HTMLElement>()\nconst editorApi = shallowRef<ReturnType<typeof useEditor>>()\n\nconst saveLoader = useLoadingState()\nconst {\n isAutoSaveEnabled,\n isDirty,\n editorLanguage,\n isDiagnosticsPaneExpanded,\n isEditorMaximized,\n isYamlMode,\n editorBottomPadding,\n editorRootClass,\n getLanguageToggleClass,\n toggleEditorMaximized,\n} = useEditorState()\n\nconst jsonModel = createJsonModel()\nconst yamlModel = createYamlModel()\n\nconst selectedLanguage = ref<'json' | 'yaml'>('json')\n\nconst currentModel = computed(() => {\n return selectedLanguage.value === 'json' ? jsonModel : yamlModel\n})\n\nconst monacoEditorInstance = computed(() => editorApi.value?.editor)\nconst { markers: diagnostics } = useEditorMarkers(monacoEditorInstance)\n\nconst syncEditorBottomPadding = () => {\n editorApi.value?.editor.updateOptions({\n padding: {\n top: 0,\n bottom: editorBottomPadding.value,\n },\n })\n}\n\nconst diagnosticCounts = computed(() => getDiagnosticCounts(diagnostics.value))\nconst visibleDiagnostics = computed(() =>\n getVisibleDiagnostics(diagnostics.value, MAX_VISIBLE_DIAGNOSTICS),\n)\n\nconst focusDiagnostic = (marker: monaco.editor.IMarker) => {\n const editor = editorApi.value?.editor\n if (!editor) {\n return\n }\n editor.setSelection({\n startLineNumber: marker.startLineNumber,\n startColumn: marker.startColumn,\n endLineNumber: marker.endLineNumber,\n endColumn: marker.endColumn,\n })\n editor.revealPositionInCenter({\n lineNumber: marker.startLineNumber,\n column: marker.startColumn,\n })\n}\n\nconst getEditorValue = (): string | null => currentModel.value.model.getValue()\n\n/** Value from the editor for a specific language (use when switching to avoid reading the wrong model). */\nconst getEditorValueForLanguage = (language: 'json' | 'yaml'): string | null =>\n (language === 'json' ? jsonModel : yamlModel).model.getValue()\n\nconst getDocumentValue = async (\n language?: 'json' | 'yaml',\n): Promise<string> => {\n const document = await workspaceStore.getEditableDocument(documentSlug)\n return stringifyDocument(document, language ?? selectedLanguage.value)\n}\n\nconst debouncedPersist = debounce({ delay: 1500 })\n\nconst applyProgrammaticEditorValue = (value: string): void => {\n // Cancel pending auto-save work so synthetic model updates do not persist stale data.\n debouncedPersist.cleanup()\n editorApi.value?.setValue(value, true)\n}\n\nconst loadDocumentIntoEditor = async () => {\n applyProgrammaticEditorValue(await getDocumentValue())\n isDirty.value = false\n await focusOperation()\n}\n\nconst formatDocument = async () => {\n await editorApi.value?.formatDocument()\n}\n\nconst focusOperation = async () => {\n const operationContext = getOperationContext(path, method)\n if (!operationContext) {\n return\n }\n\n await editorApi.value?.focusPath([\n 'paths',\n operationContext.path,\n operationContext.method,\n ])\n}\n\nconst persistEditorToWorkspace = async (value: string) => {\n const parsed = parseEditorObject(value, editorLanguage.value)\n if (!parsed) {\n const firstError = diagnostics.value.find(\n (m) => m.severity === monaco.MarkerSeverity.Error,\n )\n if (firstError) {\n focusDiagnostic(firstError)\n }\n await saveLoader.invalidate()\n return\n }\n\n saveLoader.start()\n await workspaceStore.replaceDocument(documentSlug, parsed)\n isDirty.value = false\n await saveLoader.validate({ duration: 900 })\n}\n\nconst saveNow = async () => {\n const value = getEditorValue()\n if (!value) {\n return\n }\n await persistEditorToWorkspace(value)\n}\n\nconst handleEditorChange = (value: string) => {\n isDirty.value = true\n\n if (!isAutoSaveEnabled.value) {\n return\n }\n\n debouncedPersist.execute(EDITOR_PERSIST_DEBOUNCE_KEY, () =>\n persistEditorToWorkspace(value),\n )\n}\n\nconst focusOperationServers = async () => {\n const operationContext = getOperationContext(path, method)\n if (!operationContext) {\n return\n }\n\n const value = getEditorValue()\n if (!value) {\n return\n }\n\n const parsed = parseEditorObject(value, editorLanguage.value)\n\n if (!parsed || !isObject(parsed.paths)) {\n return\n }\n\n const pathsObject = parsed.paths as Record<string, unknown>\n const operationPathItem = pathsObject[operationContext.path]\n if (!isObject(operationPathItem)) {\n return\n }\n\n const operation = operationPathItem[operationContext.method]\n if (!isObject(operation)) {\n return\n }\n\n // Add default servers if not present\n operation.servers ??= []\n\n // Update the editor value\n editorApi.value?.setValue(stringifyDocument(parsed, editorLanguage.value))\n await editorApi.value?.focusPath([\n 'paths',\n operationContext.path,\n operationContext.method,\n 'servers',\n ])\n}\n\nuseJsonPointerLinkSupport(editorApi, currentModel)\n\nonMounted(() => {\n editorApi.value = useEditor({\n element: monacoEditorRef.value ?? document.createElement('div'),\n onChange: handleEditorChange,\n model: currentModel,\n actions: [\n {\n id: 'scalar.editor.focusOperation',\n label: 'Focus Operation',\n keybindings: [monaco.KeyMod.Alt | monaco.KeyCode.KeyO],\n run: async () => {\n await focusOperation()\n },\n },\n {\n id: 'scalar.editor.focusOperationServers',\n label: 'Focus Operation Servers',\n keybindings: [monaco.KeyMod.Alt | monaco.KeyCode.KeyS],\n run: async () => {\n await focusOperationServers()\n },\n },\n {\n id: 'scalar.editor.formatDocument',\n label: 'Format Document',\n keybindings: [\n monaco.KeyMod.Alt | monaco.KeyMod.Shift | monaco.KeyCode.KeyF,\n ],\n run: async () => {\n await formatDocument()\n },\n },\n ],\n })\n\n syncEditorBottomPadding()\n void loadDocumentIntoEditor()\n})\n\nonBeforeUnmount(() => {\n debouncedPersist.cleanup()\n\n // Persist if there is a pending save\n if (isDirty.value && isAutoSaveEnabled.value) {\n void saveNow()\n }\n editorApi.value?.dispose?.()\n // Dispose models created at setup; useEditor only disposes the editor widget, not external models.\n jsonModel.model.dispose()\n yamlModel.model.dispose()\n})\n\nwatch(() => documentSlug, loadDocumentIntoEditor)\n\nwatch(\n () => [path, method] as const,\n async () => {\n await focusOperation()\n },\n)\n\nwatch(isDiagnosticsPaneExpanded, () => {\n syncEditorBottomPadding()\n})\n\nwatch(editorLanguage, async (nextLanguage, previousLanguage) => {\n const wasDirty = isDirty.value\n // Read from the previous model before switching; getEditorValue() would use currentModel\n // which changes with selectedLanguage, so we would read the (empty) new model otherwise.\n const value = getEditorValueForLanguage(previousLanguage ?? 'json')\n if (!value) {\n selectedLanguage.value = nextLanguage\n await nextTick()\n await focusOperation()\n return\n }\n\n const parsed = parseEditorObject(value, previousLanguage ?? 'json')\n selectedLanguage.value = nextLanguage\n await nextTick()\n if (parsed) {\n applyProgrammaticEditorValue(stringifyDocument(parsed, nextLanguage))\n isDirty.value = wasDirty\n }\n await focusOperation()\n})\n\nwatch(\n isAutoSaveEnabled,\n (isEnabled) => {\n if (!isEnabled) {\n debouncedPersist.cleanup()\n return\n }\n\n if (isDirty.value) {\n const value = getEditorValue()\n if (!value) {\n return\n }\n debouncedPersist.execute(EDITOR_PERSIST_DEBOUNCE_KEY, () =>\n persistEditorToWorkspace(value),\n )\n }\n },\n { flush: 'post' },\n)\n</script>\n\n<template>\n <div\n v-if=\"\n collectionType === 'operation' &&\n getOperationContext(path, method) !== null\n \"\n class=\"flex w-full min-w-0 flex-1 flex-col gap-2\"\n :class=\"editorRootClass\">\n <div\n class=\"grid grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center gap-3\">\n <div\n class=\"flex min-w-0 items-center gap-1 overflow-x-auto whitespace-nowrap\">\n <span class=\"text-c-2 text-xs font-medium whitespace-nowrap\">\n Shortcuts\n </span>\n\n <ScalarButton\n class=\"whitespace-nowrap\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"focusOperation\">\n <span>Operation</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"O\"\n :modifier=\"['Alt']\" />\n </span>\n </ScalarButton>\n\n <ScalarButton\n class=\"whitespace-nowrap\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"focusOperationServers\">\n <span>Servers</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"S\"\n :modifier=\"['Alt']\" />\n </span>\n </ScalarButton>\n </div>\n\n <div\n aria-label=\"Editor language\"\n class=\"bg-b-1 shadow-border flex items-center justify-self-center overflow-hidden rounded-lg p-0.5\"\n role=\"tablist\">\n <ScalarButton\n :aria-selected=\"!isYamlMode\"\n :class=\"getLanguageToggleClass(!isYamlMode)\"\n role=\"tab\"\n size=\"xs\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"editorLanguage = 'json'\">\n JSON\n </ScalarButton>\n <ScalarButton\n :aria-selected=\"isYamlMode\"\n :class=\"getLanguageToggleClass(isYamlMode)\"\n role=\"tab\"\n size=\"xs\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"editorLanguage = 'yaml'\">\n YAML\n </ScalarButton>\n </div>\n\n <div class=\"flex min-w-0 shrink-0 items-center gap-2 justify-self-end\">\n <ScalarButton\n size=\"xs\"\n variant=\"ghost\"\n @click=\"formatDocument\">\n <span>Format {{ isYamlMode ? 'YAML' : 'JSON' }}</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"F\"\n :modifier=\"['Alt', 'Shift']\" />\n </span>\n </ScalarButton>\n <ScalarButton\n :aria-label=\"\n isEditorMaximized ? 'Restore editor size' : 'Maximize editor'\n \"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"toggleEditorMaximized\">\n <span>{{ isEditorMaximized ? 'Restore' : 'Maximize' }}</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarIconArrowsIn\n v-if=\"isEditorMaximized\"\n class=\"size-3.5\" />\n <ScalarIconArrowsOut\n v-else\n class=\"size-3.5\" />\n </span>\n </ScalarButton>\n </div>\n </div>\n\n <div class=\"flex min-h-0 w-full min-w-0 flex-1 rounded-lg border\">\n <div\n class=\"relative w-full min-w-0 flex-1\"\n :class=\"isEditorMaximized ? 'h-full min-h-0' : 'h-125'\">\n <div class=\"pointer-events-none absolute top-2 right-2 z-10\">\n <EditorSavePanel\n :isAutoSaveEnabled=\"isAutoSaveEnabled\"\n :isDirty=\"isDirty\"\n :saveLoader=\"saveLoader\"\n @saveNow=\"saveNow\"\n @update:isAutoSaveEnabled=\"isAutoSaveEnabled = $event\" />\n </div>\n\n <EditorDiagnosticsPanel\n :diagnosticCounts=\"diagnosticCounts\"\n :expanded=\"isDiagnosticsPaneExpanded\"\n :visibleDiagnostics=\"visibleDiagnostics\"\n @focusDiagnostic=\"focusDiagnostic\"\n @toggle=\"isDiagnosticsPaneExpanded = !isDiagnosticsPaneExpanded\" />\n\n <div\n ref=\"monacoEditorRef\"\n class=\"h-full w-full min-w-0 flex-1 [&_.monaco-editor]:rounded-lg [&_.overflow-guard]:rounded-lg\" />\n </div>\n </div>\n </div>\n <div v-else>No operation context found</div>\n</template>\n<style scoped>\n.editor-container {\n width: 100%;\n height: 100%;\n}\n\n:deep(.json-path-highlight) {\n background-color: rgba(255, 200, 0, 0.35);\n border-radius: 4px;\n}\n\n:deep(.json-focus-highlight) {\n background-color: color-mix(\n in srgb,\n var(--scalar-color-accent, #24b47e) 18%,\n transparent\n );\n border-radius: 4px;\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply-scalar-theme.js","names":[],"sources":["../../../../../../src/v2/features/editor/helpers/theme/apply-scalar-theme.ts"],"sourcesContent":["import * as monaco from 'monaco-editor'\n\nimport { loadCssVariables } from './load-css-variables'\n\nconst THEME_NAME = 'scalar-theme'\n\n/**\n * Applies a custom Scalar theme to the Monaco editor.\n *\n * This function loads CSS variables for either dark or light mode, maps the colors to Monaco's editor theme keys,\n * and then applies the new theme. Theme color tokens are resolved from CSS custom properties.\n *\n * Example usage:\n *\n * await applyScalarTheme('scalar', true); // Apply dark mode\n * await applyScalarTheme('scalar', false); // Apply light mode\n *\n * @param theme - The theme string key to load variables for (e.g. 'scalar').\n * @param isDarkMode - Whether to use dark or light variables.\n */\nexport const applyScalarTheme = async (theme: string, isDarkMode: boolean) => {\n // Load all CSS variables for the given theme\n const allVars = await loadCssVariables(theme)\n\n // Pick variables for dark or light mode\n const vars = isDarkMode ? allVars.dark : allVars.light\n\n /**\n * Map Monaco editor theme keys to CSS variable names.\n */\n const varsMap = {\n 'editor.background': '--scalar-background-1',\n 'editor.foreground': '--scalar-color-1',\n 'editorLineNumber.foreground': '--scalar-color-3',\n 'editorLineNumber.activeForeground': '--scalar-color-1',\n 'editorLineHighlight.background': '--scalar-background-2',\n 'editorCursor.foreground': '--scalar-color-1',\n 'editorCursor.background': '--scalar-background-1',\n 'editor.selectionBackground': '--scalar-background-3',\n 'editor.inactiveSelectionBackground': '--scalar-background-3',\n 'editorIndentGuide.background': '--scalar-background-3',\n 'editorIndentGuide.activeBackground': '--scalar-background-2',\n 'editorWhitespace.foreground': '--scalar-border-color',\n 'editorBracketMatch.background': '--scalar-background-3',\n 'editorBracketMatch.border': '--scalar-color-accent',\n 'editor.selectionHighlightBackground': '--scalar-background-3',\n 'editor.hoverHighlightBackground': '--scalar-background-3',\n 'editorLink.activeForeground': '--scalar-color-3',\n 'editorOverviewRuler.border': '--scalar-border-color',\n }\n\n /**\n * Build a colors object for Monaco from the variable map.\n * Only assign if the variable exists in the loaded vars.\n */\n const colors = Object.fromEntries(\n Object.entries(varsMap)\n .filter(([_, cssVar]) => !!vars[cssVar])\n .map(([prop, cssVar]) => [prop, vars[cssVar]]),\n )\n\n // Define the theme in Monaco using the chosen variables.\n monaco.editor.defineTheme(THEME_NAME, {\n base: isDarkMode ? 'vs-dark' : 'vs',\n inherit: true,\n rules: [\n // Default text\n { token: '', foreground: vars['--scalar-color-3'] },\n // Comments\n {\n token: 'comment',\n foreground: vars['--scalar-color-2'],\n fontStyle: 'italic',\n },\n // Keywords\n {\n token: 'keyword',\n foreground: vars['--scalar-color-accent'],\n fontStyle: 'bold',\n },\n // Numbers\n { token: 'number', foreground: vars['--scalar-color-purple'] },\n // Strings\n { token: 'string', foreground: vars['--scalar-color-2'] },\n // Delimiters (punctuation)\n { token: 'delimiter', foreground: vars['--scalar-color-3'] },\n ],\n colors,\n })\n\n // Finally, set the theme\n monaco.editor.setTheme(THEME_NAME)\n}\n"],"mappings":";;;AAIA,IAAM,aAAa;;;;;;;;;;;;;;;AAgBnB,IAAa,mBAAmB,OAAO,OAAe,eAAwB;CAE5E,MAAM,UAAU,MAAM,iBAAiB,MAAM;CAG7C,MAAM,OAAO,aAAa,QAAQ,OAAO,QAAQ;;;;;CA8BjD,MAAM,SAAS,OAAO,YACpB,OAAO,QA1BO;EACd,qBAAqB;EACrB,qBAAqB;EACrB,+BAA+B;EAC/B,qCAAqC;EACrC,kCAAkC;EAClC,2BAA2B;EAC3B,2BAA2B;EAC3B,8BAA8B;EAC9B,sCAAsC;EACtC,gCAAgC;EAChC,sCAAsC;EACtC,+BAA+B;EAC/B,iCAAiC;EACjC,6BAA6B;EAC7B,uCAAuC;EACvC,mCAAmC;EACnC,+BAA+B;EAC/B,8BAA8B;EAC/B,CAOwB,CACpB,QAAQ,CAAC,GAAG,YAAY,CAAC,CAAC,KAAK,QAAQ,CACvC,KAAK,CAAC,MAAM,YAAY,CAAC,MAAM,KAAK,
|
|
1
|
+
{"version":3,"file":"apply-scalar-theme.js","names":[],"sources":["../../../../../../src/v2/features/editor/helpers/theme/apply-scalar-theme.ts"],"sourcesContent":["import * as monaco from 'monaco-editor'\n\nimport { loadCssVariables } from './load-css-variables'\n\nconst THEME_NAME = 'scalar-theme'\n\n/**\n * Applies a custom Scalar theme to the Monaco editor.\n *\n * This function loads CSS variables for either dark or light mode, maps the colors to Monaco's editor theme keys,\n * and then applies the new theme. Theme color tokens are resolved from CSS custom properties.\n *\n * Example usage:\n *\n * await applyScalarTheme('scalar', true); // Apply dark mode\n * await applyScalarTheme('scalar', false); // Apply light mode\n *\n * @param theme - The theme string key to load variables for (e.g. 'scalar').\n * @param isDarkMode - Whether to use dark or light variables.\n */\nexport const applyScalarTheme = async (theme: string, isDarkMode: boolean) => {\n // Load all CSS variables for the given theme\n const allVars = await loadCssVariables(theme)\n\n // Pick variables for dark or light mode\n const vars = isDarkMode ? allVars.dark : allVars.light\n\n /**\n * Map Monaco editor theme keys to CSS variable names.\n */\n const varsMap = {\n 'editor.background': '--scalar-background-1',\n 'editor.foreground': '--scalar-color-1',\n 'editorLineNumber.foreground': '--scalar-color-3',\n 'editorLineNumber.activeForeground': '--scalar-color-1',\n 'editorLineHighlight.background': '--scalar-background-2',\n 'editorCursor.foreground': '--scalar-color-1',\n 'editorCursor.background': '--scalar-background-1',\n 'editor.selectionBackground': '--scalar-background-3',\n 'editor.inactiveSelectionBackground': '--scalar-background-3',\n 'editorIndentGuide.background': '--scalar-background-3',\n 'editorIndentGuide.activeBackground': '--scalar-background-2',\n 'editorWhitespace.foreground': '--scalar-border-color',\n 'editorBracketMatch.background': '--scalar-background-3',\n 'editorBracketMatch.border': '--scalar-color-accent',\n 'editor.selectionHighlightBackground': '--scalar-background-3',\n 'editor.hoverHighlightBackground': '--scalar-background-3',\n 'editorLink.activeForeground': '--scalar-color-3',\n 'editorOverviewRuler.border': '--scalar-border-color',\n }\n\n /**\n * Build a colors object for Monaco from the variable map.\n * Only assign if the variable exists in the loaded vars.\n */\n const colors = Object.fromEntries(\n Object.entries(varsMap)\n .filter(([_, cssVar]) => !!vars[cssVar])\n .map(([prop, cssVar]) => [prop, vars[cssVar] as string]),\n )\n\n // Define the theme in Monaco using the chosen variables.\n monaco.editor.defineTheme(THEME_NAME, {\n base: isDarkMode ? 'vs-dark' : 'vs',\n inherit: true,\n rules: [\n // Default text\n { token: '', foreground: vars['--scalar-color-3'] },\n // Comments\n {\n token: 'comment',\n foreground: vars['--scalar-color-2'],\n fontStyle: 'italic',\n },\n // Keywords\n {\n token: 'keyword',\n foreground: vars['--scalar-color-accent'],\n fontStyle: 'bold',\n },\n // Numbers\n { token: 'number', foreground: vars['--scalar-color-purple'] },\n // Strings\n { token: 'string', foreground: vars['--scalar-color-2'] },\n // Delimiters (punctuation)\n { token: 'delimiter', foreground: vars['--scalar-color-3'] },\n ],\n colors,\n })\n\n // Finally, set the theme\n monaco.editor.setTheme(THEME_NAME)\n}\n"],"mappings":";;;AAIA,IAAM,aAAa;;;;;;;;;;;;;;;AAgBnB,IAAa,mBAAmB,OAAO,OAAe,eAAwB;CAE5E,MAAM,UAAU,MAAM,iBAAiB,MAAM;CAG7C,MAAM,OAAO,aAAa,QAAQ,OAAO,QAAQ;;;;;CA8BjD,MAAM,SAAS,OAAO,YACpB,OAAO,QA1BO;EACd,qBAAqB;EACrB,qBAAqB;EACrB,+BAA+B;EAC/B,qCAAqC;EACrC,kCAAkC;EAClC,2BAA2B;EAC3B,2BAA2B;EAC3B,8BAA8B;EAC9B,sCAAsC;EACtC,gCAAgC;EAChC,sCAAsC;EACtC,+BAA+B;EAC/B,iCAAiC;EACjC,6BAA6B;EAC7B,uCAAuC;EACvC,mCAAmC;EACnC,+BAA+B;EAC/B,8BAA8B;EAC/B,CAOwB,CACpB,QAAQ,CAAC,GAAG,YAAY,CAAC,CAAC,KAAK,QAAQ,CACvC,KAAK,CAAC,MAAM,YAAY,CAAC,MAAM,KAAK,QAAkB,CAAC,CAC3D;AAGD,QAAO,OAAO,YAAY,YAAY;EACpC,MAAM,aAAa,YAAY;EAC/B,SAAS;EACT,OAAO;GAEL;IAAE,OAAO;IAAI,YAAY,KAAK;IAAqB;GAEnD;IACE,OAAO;IACP,YAAY,KAAK;IACjB,WAAW;IACZ;GAED;IACE,OAAO;IACP,YAAY,KAAK;IACjB,WAAW;IACZ;GAED;IAAE,OAAO;IAAU,YAAY,KAAK;IAA0B;GAE9D;IAAE,OAAO;IAAU,YAAY,KAAK;IAAqB;GAEzD;IAAE,OAAO;IAAa,YAAY,KAAK;IAAqB;GAC7D;EACD;EACD,CAAC;AAGF,QAAO,OAAO,SAAS,WAAW"}
|
|
@@ -1,14 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps a comma-separated selector list to the theme modes it applies to.
|
|
3
|
+
* Only **exact** `.light-mode` or `.dark-mode` selectors match (no compound selectors like `.light-mode .foo`).
|
|
4
|
+
*/
|
|
5
|
+
export declare const getColorModesFromSelectors: (text: string) => ("dark" | "light")[];
|
|
6
|
+
/**
|
|
7
|
+
* Parses a single custom property value from the stylesheet into a normalized form:
|
|
8
|
+
* - `#RRGGBB` / `#RRGGBBAA` (uppercase)
|
|
9
|
+
* - `#RGB` short hex expanded to six digits
|
|
10
|
+
* - `rgb()` / `rgba()` with comma-separated channels (lower input only)
|
|
11
|
+
* - `var(--x)` / `var(--x, fallback)` returned as-is for a later resolve pass
|
|
12
|
+
*/
|
|
13
|
+
export declare const parseVariableValue: (value: string) => string | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Recursively resolves a value if it is (or becomes) `var(--name)` against `variables`.
|
|
16
|
+
* Missing names or non-var values are returned unchanged.
|
|
17
|
+
*/
|
|
18
|
+
export declare const resolveVariableValue: (value: string, variables: Record<string, string>) => string;
|
|
19
|
+
/**
|
|
20
|
+
* Resolves `var(--*)` values in a flat map of custom properties in one pass.
|
|
21
|
+
* Values that are not var references are copied through.
|
|
22
|
+
*/
|
|
23
|
+
export declare const resolveVariables: (variables: Record<string, string>) => Record<string, string>;
|
|
1
24
|
/**
|
|
2
25
|
* Extracts CSS custom properties (variables) from a given CSS string
|
|
3
26
|
* for .light-mode and .dark-mode selectors and returns an object
|
|
4
27
|
* with 'light' and 'dark' keys containing the filtered variables.
|
|
5
|
-
* Only variables matching hex color values (#RRGGBB or #RRGGBBAA) are accepted.
|
|
6
28
|
*
|
|
7
29
|
* @param css - The CSS string to parse.
|
|
8
30
|
* @returns An object with `light` and `dark` properties containing the extracted CSS variables.
|
|
9
31
|
*/
|
|
10
32
|
export declare const loadCssVariables: (css: string) => Promise<{
|
|
11
|
-
light:
|
|
12
|
-
dark:
|
|
33
|
+
light: Record<string, string>;
|
|
34
|
+
dark: Record<string, string>;
|
|
13
35
|
}>;
|
|
14
36
|
//# sourceMappingURL=load-css-variables.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-css-variables.d.ts","sourceRoot":"","sources":["../../../../../../src/v2/features/editor/helpers/theme/load-css-variables.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"load-css-variables.d.ts","sourceRoot":"","sources":["../../../../../../src/v2/features/editor/helpers/theme/load-css-variables.ts"],"names":[],"mappings":"AAoBA;;;GAGG;AACH,eAAO,MAAM,0BAA0B,GAAI,MAAM,MAAM,yBActD,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,GAAI,OAAO,MAAM,uBAmC/C,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,OAAO,MAAM,EAAE,WAAW,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAG,MAcvF,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAI,WAAW,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAWzF,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,GAAU,KAAK,MAAM;;;EAsCjD,CAAA"}
|