mirror of
https://github.com/open-webui/open-webui.git
synced 2025-12-12 04:15:25 +00:00
enh: support commands in note chat
This commit is contained in:
parent
e9821c0881
commit
9632f40335
3 changed files with 375 additions and 134 deletions
|
|
@ -7,8 +7,18 @@
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
import { config, mobile, settings, socket } from '$lib/stores';
|
import { config, mobile, settings, socket, user } from '$lib/stores';
|
||||||
import { blobToFile, compressImage } from '$lib/utils';
|
import {
|
||||||
|
blobToFile,
|
||||||
|
compressImage,
|
||||||
|
extractInputVariables,
|
||||||
|
getCurrentDateTime,
|
||||||
|
getFormattedDate,
|
||||||
|
getFormattedTime,
|
||||||
|
getUserPosition,
|
||||||
|
getUserTimezone,
|
||||||
|
getWeekday
|
||||||
|
} from '$lib/utils';
|
||||||
|
|
||||||
import Tooltip from '../common/Tooltip.svelte';
|
import Tooltip from '../common/Tooltip.svelte';
|
||||||
import RichTextInput from '../common/RichTextInput.svelte';
|
import RichTextInput from '../common/RichTextInput.svelte';
|
||||||
|
|
@ -19,6 +29,8 @@
|
||||||
import FileItem from '../common/FileItem.svelte';
|
import FileItem from '../common/FileItem.svelte';
|
||||||
import Image from '../common/Image.svelte';
|
import Image from '../common/Image.svelte';
|
||||||
import FilesOverlay from '../chat/MessageInput/FilesOverlay.svelte';
|
import FilesOverlay from '../chat/MessageInput/FilesOverlay.svelte';
|
||||||
|
import Commands from '../chat/MessageInput/Commands.svelte';
|
||||||
|
import InputVariablesModal from '../chat/MessageInput/InputVariablesModal.svelte';
|
||||||
|
|
||||||
export let placeholder = $i18n.t('Send a Message');
|
export let placeholder = $i18n.t('Send a Message');
|
||||||
export let transparentBackground = false;
|
export let transparentBackground = false;
|
||||||
|
|
@ -32,6 +44,8 @@
|
||||||
let files = [];
|
let files = [];
|
||||||
|
|
||||||
export let chatInputElement;
|
export let chatInputElement;
|
||||||
|
|
||||||
|
let commandsElement;
|
||||||
let filesInputElement;
|
let filesInputElement;
|
||||||
let inputFiles;
|
let inputFiles;
|
||||||
|
|
||||||
|
|
@ -47,6 +61,166 @@
|
||||||
|
|
||||||
export let acceptFiles = true;
|
export let acceptFiles = true;
|
||||||
|
|
||||||
|
let showInputVariablesModal = false;
|
||||||
|
let inputVariables: Record<string, any> = {};
|
||||||
|
let inputVariableValues = {};
|
||||||
|
|
||||||
|
const inputVariableHandler = async (text: string) => {
|
||||||
|
inputVariables = extractInputVariables(text);
|
||||||
|
if (Object.keys(inputVariables).length > 0) {
|
||||||
|
showInputVariablesModal = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const textVariableHandler = async (text: string) => {
|
||||||
|
if (text.includes('{{CLIPBOARD}}')) {
|
||||||
|
const clipboardText = await navigator.clipboard.readText().catch((err) => {
|
||||||
|
toast.error($i18n.t('Failed to read clipboard contents'));
|
||||||
|
return '{{CLIPBOARD}}';
|
||||||
|
});
|
||||||
|
|
||||||
|
const clipboardItems = await navigator.clipboard.read();
|
||||||
|
|
||||||
|
let imageUrl = null;
|
||||||
|
for (const item of clipboardItems) {
|
||||||
|
// Check for known image types
|
||||||
|
for (const type of item.types) {
|
||||||
|
if (type.startsWith('image/')) {
|
||||||
|
const blob = await item.getType(type);
|
||||||
|
imageUrl = URL.createObjectURL(blob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imageUrl) {
|
||||||
|
files = [
|
||||||
|
...files,
|
||||||
|
{
|
||||||
|
type: 'image',
|
||||||
|
url: imageUrl
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
text = text.replaceAll('{{CLIPBOARD}}', clipboardText);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.includes('{{USER_LOCATION}}')) {
|
||||||
|
let location;
|
||||||
|
try {
|
||||||
|
location = await getUserPosition();
|
||||||
|
} catch (error) {
|
||||||
|
toast.error($i18n.t('Location access not allowed'));
|
||||||
|
location = 'LOCATION_UNKNOWN';
|
||||||
|
}
|
||||||
|
text = text.replaceAll('{{USER_LOCATION}}', String(location));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.includes('{{USER_NAME}}')) {
|
||||||
|
const name = $user?.name || 'User';
|
||||||
|
text = text.replaceAll('{{USER_NAME}}', name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.includes('{{USER_LANGUAGE}}')) {
|
||||||
|
const language = localStorage.getItem('locale') || 'en-US';
|
||||||
|
text = text.replaceAll('{{USER_LANGUAGE}}', language);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.includes('{{CURRENT_DATE}}')) {
|
||||||
|
const date = getFormattedDate();
|
||||||
|
text = text.replaceAll('{{CURRENT_DATE}}', date);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.includes('{{CURRENT_TIME}}')) {
|
||||||
|
const time = getFormattedTime();
|
||||||
|
text = text.replaceAll('{{CURRENT_TIME}}', time);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.includes('{{CURRENT_DATETIME}}')) {
|
||||||
|
const dateTime = getCurrentDateTime();
|
||||||
|
text = text.replaceAll('{{CURRENT_DATETIME}}', dateTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.includes('{{CURRENT_TIMEZONE}}')) {
|
||||||
|
const timezone = getUserTimezone();
|
||||||
|
text = text.replaceAll('{{CURRENT_TIMEZONE}}', timezone);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.includes('{{CURRENT_WEEKDAY}}')) {
|
||||||
|
const weekday = getWeekday();
|
||||||
|
text = text.replaceAll('{{CURRENT_WEEKDAY}}', weekday);
|
||||||
|
}
|
||||||
|
|
||||||
|
inputVariableHandler(text);
|
||||||
|
return text;
|
||||||
|
};
|
||||||
|
|
||||||
|
const replaceVariables = (variables: Record<string, any>) => {
|
||||||
|
if (!chatInputElement) return;
|
||||||
|
console.log('Replacing variables:', variables);
|
||||||
|
|
||||||
|
chatInputElement.replaceVariables(variables);
|
||||||
|
chatInputElement.focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setText = async (text?: string) => {
|
||||||
|
if (!chatInputElement) return;
|
||||||
|
|
||||||
|
text = await textVariableHandler(text || '');
|
||||||
|
|
||||||
|
chatInputElement?.setText(text);
|
||||||
|
chatInputElement?.focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCommand = () => {
|
||||||
|
if (!chatInputElement) return;
|
||||||
|
|
||||||
|
let word = '';
|
||||||
|
word = chatInputElement?.getWordAtDocPos();
|
||||||
|
|
||||||
|
return word;
|
||||||
|
};
|
||||||
|
|
||||||
|
const replaceCommandWithText = (text) => {
|
||||||
|
if (!chatInputElement) return;
|
||||||
|
|
||||||
|
chatInputElement?.replaceCommandWithText(text);
|
||||||
|
};
|
||||||
|
|
||||||
|
const insertTextAtCursor = async (text: string) => {
|
||||||
|
text = await textVariableHandler(text);
|
||||||
|
|
||||||
|
if (command) {
|
||||||
|
replaceCommandWithText(text);
|
||||||
|
} else {
|
||||||
|
const selection = window.getSelection();
|
||||||
|
if (selection && selection.rangeCount > 0) {
|
||||||
|
const range = selection.getRangeAt(0);
|
||||||
|
range.deleteContents();
|
||||||
|
range.insertNode(document.createTextNode(text));
|
||||||
|
range.collapse(false);
|
||||||
|
selection.removeAllRanges();
|
||||||
|
selection.addRange(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await tick();
|
||||||
|
const chatInputContainer = document.getElementById('chat-input-container');
|
||||||
|
if (chatInputContainer) {
|
||||||
|
chatInputContainer.scrollTop = chatInputContainer.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
await tick();
|
||||||
|
if (chatInputElement) {
|
||||||
|
chatInputElement.focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let command = '';
|
||||||
|
|
||||||
|
export let showCommands = false;
|
||||||
|
$: showCommands = ['/'].includes(command?.charAt(0));
|
||||||
|
|
||||||
const screenCaptureHandler = async () => {
|
const screenCaptureHandler = async () => {
|
||||||
try {
|
try {
|
||||||
// Request screen media
|
// Request screen media
|
||||||
|
|
@ -355,6 +529,15 @@
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<InputVariablesModal
|
||||||
|
bind:show={showInputVariablesModal}
|
||||||
|
variables={inputVariables}
|
||||||
|
onSave={(variableValues) => {
|
||||||
|
inputVariableValues = { ...inputVariableValues, ...variableValues };
|
||||||
|
replaceVariables(inputVariableValues);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="bg-transparent">
|
<div class="bg-transparent">
|
||||||
<div
|
<div
|
||||||
class="{($settings?.widescreenMode ?? null)
|
class="{($settings?.widescreenMode ?? null)
|
||||||
|
|
@ -403,6 +586,13 @@
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Commands
|
||||||
|
bind:this={commandsElement}
|
||||||
|
show={showCommands}
|
||||||
|
{command}
|
||||||
|
insertTextHandler={insertTextAtCursor}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -518,10 +708,59 @@
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const { md } = e;
|
const { md } = e;
|
||||||
content = md;
|
content = md;
|
||||||
|
command = getCommand();
|
||||||
}}
|
}}
|
||||||
on:keydown={async (e) => {
|
on:keydown={async (e) => {
|
||||||
e = e.detail.event;
|
e = e.detail.event;
|
||||||
const isCtrlPressed = e.ctrlKey || e.metaKey; // metaKey is for Cmd key on Mac
|
const isCtrlPressed = e.ctrlKey || e.metaKey; // metaKey is for Cmd key on Mac
|
||||||
|
|
||||||
|
const commandsContainerElement = document.getElementById('commands-container');
|
||||||
|
|
||||||
|
if (commandsContainerElement) {
|
||||||
|
if (commandsContainerElement && e.key === 'ArrowUp') {
|
||||||
|
e.preventDefault();
|
||||||
|
commandsElement.selectUp();
|
||||||
|
|
||||||
|
const commandOptionButton = [
|
||||||
|
...document.getElementsByClassName('selected-command-option-button')
|
||||||
|
]?.at(-1);
|
||||||
|
commandOptionButton.scrollIntoView({ block: 'center' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commandsContainerElement && e.key === 'ArrowDown') {
|
||||||
|
e.preventDefault();
|
||||||
|
commandsElement.selectDown();
|
||||||
|
|
||||||
|
const commandOptionButton = [
|
||||||
|
...document.getElementsByClassName('selected-command-option-button')
|
||||||
|
]?.at(-1);
|
||||||
|
commandOptionButton.scrollIntoView({ block: 'center' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commandsContainerElement && e.key === 'Tab') {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const commandOptionButton = [
|
||||||
|
...document.getElementsByClassName('selected-command-option-button')
|
||||||
|
]?.at(-1);
|
||||||
|
|
||||||
|
commandOptionButton?.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commandsContainerElement && e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const commandOptionButton = [
|
||||||
|
...document.getElementsByClassName('selected-command-option-button')
|
||||||
|
]?.at(-1);
|
||||||
|
|
||||||
|
if (commandOptionButton) {
|
||||||
|
commandOptionButton?.click();
|
||||||
|
} else {
|
||||||
|
document.getElementById('send-message-button')?.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (
|
if (
|
||||||
!$mobile ||
|
!$mobile ||
|
||||||
!(
|
!(
|
||||||
|
|
@ -541,6 +780,7 @@
|
||||||
submitHandler();
|
submitHandler();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (e.key === 'Escape') {
|
if (e.key === 'Escape') {
|
||||||
console.info('Escape');
|
console.info('Escape');
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@
|
||||||
compressImage,
|
compressImage,
|
||||||
createMessagesList,
|
createMessagesList,
|
||||||
extractCurlyBraceWords,
|
extractCurlyBraceWords,
|
||||||
|
extractInputVariables,
|
||||||
getCurrentDateTime,
|
getCurrentDateTime,
|
||||||
getFormattedDate,
|
getFormattedDate,
|
||||||
getFormattedTime,
|
getFormattedTime,
|
||||||
|
|
@ -118,124 +119,6 @@
|
||||||
codeInterpreterEnabled
|
codeInterpreterEnabled
|
||||||
});
|
});
|
||||||
|
|
||||||
const extractInputVariables = (text: string): Record<string, any> => {
|
|
||||||
const regex = /{{\s*([^|}\s]+)\s*\|\s*([^}]+)\s*}}/g;
|
|
||||||
const regularRegex = /{{\s*([^|}\s]+)\s*}}/g;
|
|
||||||
const variables: Record<string, any> = {};
|
|
||||||
let match;
|
|
||||||
// Use exec() loop instead of matchAll() for better compatibility
|
|
||||||
while ((match = regex.exec(text)) !== null) {
|
|
||||||
const varName = match[1].trim();
|
|
||||||
const definition = match[2].trim();
|
|
||||||
variables[varName] = parseVariableDefinition(definition);
|
|
||||||
}
|
|
||||||
// Then, extract regular variables (without pipe) - only if not already processed
|
|
||||||
while ((match = regularRegex.exec(text)) !== null) {
|
|
||||||
const varName = match[1].trim();
|
|
||||||
// Only add if not already processed as custom variable
|
|
||||||
if (!variables.hasOwnProperty(varName)) {
|
|
||||||
variables[varName] = { type: 'text' }; // Default type for regular variables
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return variables;
|
|
||||||
};
|
|
||||||
|
|
||||||
const splitProperties = (str: string, delimiter: string): string[] => {
|
|
||||||
const result: string[] = [];
|
|
||||||
let current = '';
|
|
||||||
let depth = 0;
|
|
||||||
let inString = false;
|
|
||||||
let escapeNext = false;
|
|
||||||
|
|
||||||
for (let i = 0; i < str.length; i++) {
|
|
||||||
const char = str[i];
|
|
||||||
|
|
||||||
if (escapeNext) {
|
|
||||||
current += char;
|
|
||||||
escapeNext = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (char === '\\') {
|
|
||||||
current += char;
|
|
||||||
escapeNext = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (char === '"' && !escapeNext) {
|
|
||||||
inString = !inString;
|
|
||||||
current += char;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!inString) {
|
|
||||||
if (char === '{' || char === '[') {
|
|
||||||
depth++;
|
|
||||||
} else if (char === '}' || char === ']') {
|
|
||||||
depth--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (char === delimiter && depth === 0) {
|
|
||||||
result.push(current.trim());
|
|
||||||
current = '';
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
current += char;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current.trim()) {
|
|
||||||
result.push(current.trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
const parseVariableDefinition = (definition: string): Record<string, any> => {
|
|
||||||
// Use splitProperties for the main colon delimiter to handle quoted strings
|
|
||||||
const parts = splitProperties(definition, ':');
|
|
||||||
const [firstPart, ...propertyParts] = parts;
|
|
||||||
|
|
||||||
// Parse type (explicit or implied)
|
|
||||||
const type = firstPart.startsWith('type=') ? firstPart.slice(5) : firstPart;
|
|
||||||
|
|
||||||
// Parse properties using reduce
|
|
||||||
const properties = propertyParts.reduce((props, part) => {
|
|
||||||
// Use splitProperties for the equals sign as well, in case there are nested quotes
|
|
||||||
const equalsParts = splitProperties(part, '=');
|
|
||||||
const [propertyName, ...valueParts] = equalsParts;
|
|
||||||
const propertyValue = valueParts.join('='); // Handle values with = signs
|
|
||||||
|
|
||||||
return propertyName && propertyValue
|
|
||||||
? {
|
|
||||||
...props,
|
|
||||||
[propertyName.trim()]: parseJsonValue(propertyValue.trim())
|
|
||||||
}
|
|
||||||
: props;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
return { type, ...properties };
|
|
||||||
};
|
|
||||||
|
|
||||||
const parseJsonValue = (value: string): any => {
|
|
||||||
// Remove surrounding quotes if present (for string values)
|
|
||||||
if (value.startsWith('"') && value.endsWith('"')) {
|
|
||||||
return value.slice(1, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if it starts with square or curly brackets (JSON)
|
|
||||||
if (/^[\[{]/.test(value)) {
|
|
||||||
try {
|
|
||||||
return JSON.parse(value);
|
|
||||||
} catch {
|
|
||||||
return value; // Return as string if JSON parsing fails
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
const inputVariableHandler = async (text: string) => {
|
const inputVariableHandler = async (text: string) => {
|
||||||
inputVariables = extractInputVariables(text);
|
inputVariables = extractInputVariables(text);
|
||||||
if (Object.keys(inputVariables).length > 0) {
|
if (Object.keys(inputVariables).length > 0) {
|
||||||
|
|
|
||||||
|
|
@ -1389,3 +1389,121 @@ export const slugify = (str: string): string => {
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const extractInputVariables = (text: string): Record<string, any> => {
|
||||||
|
const regex = /{{\s*([^|}\s]+)\s*\|\s*([^}]+)\s*}}/g;
|
||||||
|
const regularRegex = /{{\s*([^|}\s]+)\s*}}/g;
|
||||||
|
const variables: Record<string, any> = {};
|
||||||
|
let match;
|
||||||
|
// Use exec() loop instead of matchAll() for better compatibility
|
||||||
|
while ((match = regex.exec(text)) !== null) {
|
||||||
|
const varName = match[1].trim();
|
||||||
|
const definition = match[2].trim();
|
||||||
|
variables[varName] = parseVariableDefinition(definition);
|
||||||
|
}
|
||||||
|
// Then, extract regular variables (without pipe) - only if not already processed
|
||||||
|
while ((match = regularRegex.exec(text)) !== null) {
|
||||||
|
const varName = match[1].trim();
|
||||||
|
// Only add if not already processed as custom variable
|
||||||
|
if (!variables.hasOwnProperty(varName)) {
|
||||||
|
variables[varName] = { type: 'text' }; // Default type for regular variables
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return variables;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const splitProperties = (str: string, delimiter: string): string[] => {
|
||||||
|
const result: string[] = [];
|
||||||
|
let current = '';
|
||||||
|
let depth = 0;
|
||||||
|
let inString = false;
|
||||||
|
let escapeNext = false;
|
||||||
|
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
const char = str[i];
|
||||||
|
|
||||||
|
if (escapeNext) {
|
||||||
|
current += char;
|
||||||
|
escapeNext = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (char === '\\') {
|
||||||
|
current += char;
|
||||||
|
escapeNext = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (char === '"' && !escapeNext) {
|
||||||
|
inString = !inString;
|
||||||
|
current += char;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inString) {
|
||||||
|
if (char === '{' || char === '[') {
|
||||||
|
depth++;
|
||||||
|
} else if (char === '}' || char === ']') {
|
||||||
|
depth--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (char === delimiter && depth === 0) {
|
||||||
|
result.push(current.trim());
|
||||||
|
current = '';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
current += char;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current.trim()) {
|
||||||
|
result.push(current.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const parseVariableDefinition = (definition: string): Record<string, any> => {
|
||||||
|
// Use splitProperties for the main colon delimiter to handle quoted strings
|
||||||
|
const parts = splitProperties(definition, ':');
|
||||||
|
const [firstPart, ...propertyParts] = parts;
|
||||||
|
|
||||||
|
// Parse type (explicit or implied)
|
||||||
|
const type = firstPart.startsWith('type=') ? firstPart.slice(5) : firstPart;
|
||||||
|
|
||||||
|
// Parse properties using reduce
|
||||||
|
const properties = propertyParts.reduce((props, part) => {
|
||||||
|
// Use splitProperties for the equals sign as well, in case there are nested quotes
|
||||||
|
const equalsParts = splitProperties(part, '=');
|
||||||
|
const [propertyName, ...valueParts] = equalsParts;
|
||||||
|
const propertyValue = valueParts.join('='); // Handle values with = signs
|
||||||
|
|
||||||
|
return propertyName && propertyValue
|
||||||
|
? {
|
||||||
|
...props,
|
||||||
|
[propertyName.trim()]: parseJsonValue(propertyValue.trim())
|
||||||
|
}
|
||||||
|
: props;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return { type, ...properties };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const parseJsonValue = (value: string): any => {
|
||||||
|
// Remove surrounding quotes if present (for string values)
|
||||||
|
if (value.startsWith('"') && value.endsWith('"')) {
|
||||||
|
return value.slice(1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it starts with square or curly brackets (JSON)
|
||||||
|
if (/^[\[{]/.test(value)) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(value);
|
||||||
|
} catch {
|
||||||
|
return value; // Return as string if JSON parsing fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue