/gi, '')
+ html = html.replace(/<\/div>/gi, '')
+ form.details = html
+}
+
+// Обработка ввода
+const onDetailsInput = () => {
+ syncDetailsFromEditor()
+}
+
+// Обработка клика (открытие ссылок)
+const onDetailsClick = (e) => {
+ // Если кликнули по ссылке — открываем в новой вкладке
+ if (e.target.tagName === 'A') {
+ e.preventDefault()
+ window.open(e.target.href, '_blank')
+ }
+}
+
+// Обработка вставки (очистка форматирования из внешних источников)
+const onDetailsPaste = (e) => {
+ e.preventDefault()
+ const text = e.clipboardData.getData('text/plain')
+ document.execCommand('insertText', false, text)
+ // Подсвечиваем ссылки после вставки
+ setTimeout(linkifyContent, 0)
+}
+
+// Горячие клавиши
+const onDetailsKeydown = (e) => {
+ if (e.ctrlKey || e.metaKey) {
+ if (e.key === 'b') {
+ e.preventDefault()
+ applyFormat('bold')
+ } else if (e.key === 'i') {
+ e.preventDefault()
+ applyFormat('italic')
+ } else if (e.key === 'u') {
+ e.preventDefault()
+ applyFormat('underline')
+ }
+ }
+
+ // После пробела или Enter — подсвечиваем ссылки
+ if (e.key === ' ' || e.key === 'Enter') {
+ setTimeout(linkifyContent, 0)
+ }
+}
+
+// Автоматическое преобразование URL в ссылки (вызывается после пробела/Enter)
+const linkifyContent = () => {
+ if (!detailsInput.value) return
+
+ const html = detailsInput.value.innerHTML
+
+ // Ищем URL которые ещё не в тегах
+ // Разбиваем HTML на части: теги ... и остальной текст
+ const parts = html.split(/(
]*>.*?<\/a>)/gi)
+
+ let changed = false
+ const newParts = parts.map(part => {
+ // Если это уже ссылка — не трогаем
+ if (part.startsWith('"]+)/g,
+ '$1'
+ )
+ if (newPart !== part) changed = true
+ return newPart
+ })
+
+ if (changed) {
+ // Сохраняем позицию курсора (в конце)
+ detailsInput.value.innerHTML = newParts.join('')
+
+ // Ставим курсор в конец
+ const range = document.createRange()
+ const selection = window.getSelection()
+ range.selectNodeContents(detailsInput.value)
+ range.collapse(false)
+ selection.removeAllRanges()
+ selection.addRange(range)
+
+ syncDetailsFromEditor()
+ }
+}
+
+// Установка содержимого редактора
+const setEditorContent = (html) => {
+ if (detailsInput.value) {
+ detailsInput.value.innerHTML = linkifyHtml(html || '')
+ }
+}
+
+// Простое преобразование URL в ссылки (без сложных regex)
+const linkifyHtml = (html) => {
+ if (!html) return ''
+
+ // Если уже есть теги
— не трогаем
+ if (html.includes('"]+)/g,
+ '$1'
+ )
+}
+
onMounted(() => {
refreshIcons()
document.addEventListener('click', handleClickOutside)
@@ -967,6 +1114,48 @@ onUpdated(refreshIcons)
letter-spacing: 0.5px;
}
+.field-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.format-buttons {
+ display: flex;
+ gap: 3px;
+}
+
+.format-btn {
+ width: 24px;
+ height: 24px;
+ background: rgba(255, 255, 255, 0.04);
+ border: 1px solid rgba(255, 255, 255, 0.08);
+ border-radius: 5px;
+ color: var(--text-muted);
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.15s;
+}
+
+.format-btn:hover {
+ background: rgba(255, 255, 255, 0.08);
+ border-color: rgba(255, 255, 255, 0.15);
+ color: var(--text-primary);
+}
+
+.format-btn:active {
+ background: var(--accent);
+ border-color: var(--accent);
+ color: #000;
+}
+
+.format-btn i {
+ width: 10px;
+ height: 10px;
+}
+
.field input {
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
@@ -1009,6 +1198,73 @@ onUpdated(refreshIcons)
transition: border-color 0.15s, background 0.15s;
}
+/* Редактор описания с форматированием */
+.details-editor {
+ background: rgba(255, 255, 255, 0.04);
+ border: 1px solid rgba(255, 255, 255, 0.08);
+ border-radius: 8px;
+ padding: 14px 16px;
+ min-height: 120px;
+ max-height: 400px;
+ overflow-y: auto;
+ color: var(--text-primary);
+ font-size: 14px;
+ line-height: 1.6;
+ transition: border-color 0.15s, background 0.15s;
+ word-break: break-word;
+ outline: none;
+ resize: vertical;
+}
+
+.details-editor:focus {
+ border-color: var(--accent);
+ background: rgba(255, 255, 255, 0.06);
+}
+
+.details-editor:empty::before {
+ content: attr(data-placeholder);
+ color: var(--text-muted);
+ pointer-events: none;
+}
+
+.details-editor::-webkit-scrollbar {
+ width: 6px;
+}
+
+.details-editor::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+.details-editor::-webkit-scrollbar-thumb {
+ background: rgba(255, 255, 255, 0.15);
+ border-radius: 3px;
+}
+
+.details-editor :deep(a) {
+ color: var(--accent);
+ text-decoration: none;
+ cursor: pointer;
+}
+
+.details-editor :deep(a:hover) {
+ text-decoration: underline;
+ cursor: pointer;
+}
+
+.details-editor :deep(b),
+.details-editor :deep(strong) {
+ font-weight: 600;
+}
+
+.details-editor :deep(i),
+.details-editor :deep(em) {
+ font-style: italic;
+}
+
+.details-editor :deep(u) {
+ text-decoration: underline;
+}
+
.field textarea::-webkit-scrollbar {
width: 6px;
}