add: CodeMirror

This commit is contained in:
2026-06-11 14:52:12 +03:00
parent 1ea2292833
commit c7049f7d8d
8 changed files with 1317 additions and 0 deletions

9
frontend-assembly/.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
# Каталог для NPM-сборки Codemirror6 (для фронтенда админки).
# Папка `node_modules/` будет создана в процессе NPM-сборки Codemirror 6 (для фронтенда админки).
# Её соджержимое не должно попадать в репозиторий. Готовая сборка будет
# перемещна в ../public/static/codemirror/editor.js
node_modules/
src/

View File

@@ -0,0 +1,275 @@
# CodeMirror 6 для Django Admin на LPON.RU
## Обзор
CodeMirror 6 — это мощный редактор кода/текста для использования в Django Admin.
Используется для редактирования HTML, JavaScript, CSS и других текстовых полей со синтаксис-хайлайтингом.
## Структура проекта
```
frontend-assembly/
├── package.json # Зависимости CodeMirror 6 (обновляй версии здесь!)
├── package-lock.json # Заблокированные версии (обновляется через npm install)
├── build-codemirror6.sh # Скрипт сборки (создает минифицированный бандл)
└── README.md
public/static/codemirror/
├── editor.js # Готовый минифицированный бандл (427 KB, GZIP ~130 KB)
└── README.md
```
## Версии пакетов (обновлены 11 июня 2026)
```json
{
"@babel/runtime": "7.29.7",
"@codemirror/autocomplete": "6.20.3",
"@codemirror/commands": "6.10.3",
"@codemirror/lang-css": "6.3.1",
"@codemirror/lang-html": "6.4.11",
"@codemirror/lang-javascript": "6.2.5",
"@codemirror/language": "6.12.3",
"@codemirror/state": "6.6.0",
"@codemirror/view": "6.43.1",
"@uiw/codemirror-theme-solarized": "4.25.10",
"esbuild": "0.28.0"
}
```
### Что обновилось
- `@babel/runtime` 7.29.2 → 7.29.7
- `@codemirror/autocomplete` 6.20.1 → 6.20.3
- `@codemirror/view` 6.41.0 → 6.43.1
- `@uiw/codemirror-theme-solarized` 4.25.9 → 4.25.10
## О package-lock.json
**НИКОГДА не удаляй `package-lock.json` из репозитория!**
### Почему package-lock.json важен
1. **Воспроизводимость** - гарантирует одинаковые версии пакетов на всех машинах
2. **Скрипт сборки использует `npm ci`** - это команда для CI/CD, работает с package-lock.json
3. **Безопасность** - lockfile содержит хеши пакетов, которые проверяются при установке
### Правильный workflow обновления
```bash
# 1. Обновляешь версии в package.json
# 2. Удаляешь node_modules (опционально, можешь оставить)
# 3. Запускаешь:
npm install
# 4. npm автоматически обновит package-lock.json
# 5. Коммитишь обновленные package.json и package-lock.json в гит
```
## Как собрать CodeMirror
### Один раз (после обновления версий пакетов)
```bash
# Обновляешь версии в frontend-assembly/package.json
# Затем запускаешь сборку:
bash ./frontend-assembly/build-codemirror6.sh
```
### Что делает скрипт
1. Создает временную рабочую папку
2. Копирует `package.json` и `package-lock.json` туда
3. Генерирует файл `src/editor.js` с интеграцией CodeMirror 6
4. Запускает `npm ci` для установки точно таких же версий
5. Запускает `npm run build` для минификации через esbuild
6. Копирует результат в `public/static/codemirror/editor.js`
7. Удаляет временные файлы и папки
### Размеры
- Минифицированный бандл: **427 KB** (js файл)
- После GZIP: ~**130 KB** (в половине случаев браузер будет получать сжатый файл)
## Как использовать CodeMirror в Django Admin
### 1. Подключи скрипт в template админки
Добавь в `lpon_site/templates/admin/base_site.html` (или создай этот файл):
```html
{% extends "admin/base_site.html" %}
{% block extrahead %}
{{ block.super }}
<link rel="stylesheet" href="{% static 'codemirror/editor.css' %}">
<script type="module" src="{% static 'codemirror/editor.js' %}"></script>
<style>
/* Стили для обертки CodeMirror */
.cm6-editor-wrapper {
height: 400px;
border: 1px solid #ccc;
border-radius: 4px;
margin-bottom: 20px;
font-size: 14px;
}
.cm-editor {
height: 100% !important;
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
}
</style>
{% endblock %}
```
### 2. Используй в ModelForm
В `frontend/admin.py` для текстовых полей используй виджет с `data-codemirror-editor`:
```python
from django import forms
class ArticleForm(forms.ModelForm):
"""Форма для статей с CodeMirror"""
class Meta:
model = TbArticle
fields = '__all__'
widgets = {
's_article_teaser_html': forms.Textarea(attrs={
'data-codemirror-editor': 'true',
'data-language': 'html',
'class': 'vLargeTextField',
}),
's_article_content_html': forms.Textarea(attrs={
'data-codemirror-editor': 'true',
'data-language': 'html',
'class': 'vLargeTextField',
}),
}
class ArticleAdmin(admin.ModelAdmin):
form = ArticleForm
# ... остальная конфигурация
```
### 3. Поддерживаемые языки
CodeMirror автоматически определяет язык по атрибуту `data-language`:
- `html` (по умолчанию) - HTML/Vue/Svelte шаблоны
- `javascript` - JavaScript/TypeScript код
- `css` - CSS стили
Пример:
```html
<textarea data-codemirror-editor data-language="javascript">
let x = 42;
console.log(x);
</textarea>
```
## Фишки CodeMirror 6
### Подсветка синтаксиса
- HTML / CSS / JavaScript с красивой подсветкой
- Тема Solarized (dark/light) автоматически определяется по теме админки
### Возможности
- :leftwards_arrow_with_hook: Undo/Redo
- :mag: Find and Replace (Ctrl+H / Cmd+Ctrl+H)
- :keyboard: Автодополнение (Ctrl+Space)
- :arrow_right: Автоотступы
- :straight_ruler: Нумерация строк
- :paperclip: Перенос слов на новую строку
### Синхронизация
Все изменения в CodeMirror:
1. Живо синхронизируются в скрытое текстовое поле
2. При сохранении формы отправляются на сервер
## Проверка работы
1. Откройся на http://localhost:8000/admin/
2. Перейди в раздел "Статьи" (или другой с HTML полями)
3. Создай или отредактируй статью
4. В полях с HTML кодом должен появиться CodeMirror с подсветкой синтаксиса
Вместо обычного текстового поля должен быть редактор с:
- Нумерацией строк слева
- Подсветкой синтаксиса (теги, атрибуты, значения...)
- Темой Solarized (light или dark)
## Обновление CodeMirror в будущем
Когда выйдут новые версии пакетов:
```bash
# 1. Проверь обновления
cd frontend-assembly
npm outdated
# 2. При необходимости обнови версии в package.json вручную
# Или используй npm update (осторожнее, может нарушить совместимость)
# 3. Пересоздай package-lock.json
rm package-lock.json
npm install
# 4. Собери новый бандл
bash build-codemirror6.sh
# 5. Коммит обновов (если через гит)
git add package.json package-lock.json ../public/static/codemirror/editor.js
git commit -m "Update CodeMirror 6 and dependencies"
```
## Возможные ошибки и решения
### Ошибка: "npm: command not found"
```bash
# Установи Node.js
brew install node
```
### Ошибка: "не найден package-lock.json"
```bash
# Пересоздай его
cd frontend-assembly
npm install
bash build-codemirror6.sh
```
### CodeMirror не появляется в админке
1. Проверь, что `public/static/codemirror/editor.js` существует
2. Проверь, что `static/` папка будет скопирована при deploy
3. Запусти `python manage.py collectstatic` если используешь Django collectstatic
### Медленная загрузка админки
CodeMirror бандл ~427 KB - это нормально. Он:
- :white_check_mark: кэшируется браузером
- :white_check_mark: отправляется в GZIP (~130 KB)
- :white_check_mark: загружается асинхронно
Если всё еще медленно, можно:
1. Использовать CDN (например, jsDelivr)
2. Лениво загружать CodeMirror только на нужных страницах
## Файлы конфигурации
### frontend-assembly/package.json
Главный файл для управления версиями. Обновляй здесь версии пакетов.
### frontend-assembly/package-lock.json
**Не трогай вручную!** Обновляется автоматически через `npm install`.
### frontend-assembly/build-codemirror6.sh
Скрипт сборки. Не требует изменений если только не нужна другая сборка.
### public/static/codemirror/
**Не редактируй вручную!** Пересоздается каждый раз при запуске `build-codemirror6.sh`.
## Дополнительное чтение
- [CodeMirror 6 документация](https://codemirror.net/docs/guide/)
- [Доступные расширения](https://codemirror.net/docs/ref/)
- [Solarized тема](https://github.com/uiwjs/codemirror-theme-solarized)

View File

@@ -0,0 +1,91 @@
# frontend-assembly
Эта папка отвечает за сборку CodeMirror 6 для админки Django.
Идея здесь простая: код редактора собирается как готовый минифицированный бандл,
а в репозиторий и в `public/static` попадает только готовый JS CodeMirror.
## Что здесь лежит
### `package.json`
Главный файл npm-проекта.
В нём описано:
- имя сборочного пакета;
- версия;
- зависимость от `esbuild` и пакетов CodeMirror 6;
- npm-скрипт `build`.
Скрипт `build` собирает `src/editor.js` в файл:
```bash
../public/static/codemirror/editor.js
```
При сборке используется `--minify`, потому что редактор нужен как готовый
чёрный ящик для админки, а не как объект для отладки.
### `package-lock.json`
Файл фиксации версий npm-зависимостей.
Он нужен, чтобы сборка была воспроизводимой:
- `npm ci` ставит ровно те версии, которые уже зафиксированы;
- одинаковый результат получается и на любом компьютере, и при любом новом клоне репозитория;
- при повторной сборке не подтягиваются случайные новые версии пакетов.
### `build-codemirror6.sh`
Основной скрипт сборки.
Он делает всё сам:
1. проверяет наличие `npm`;
2. создаёт временную рабочую папку;
3. временно создаёт там `src/editor.js`;
4. устанавливает зависимости через `npm ci`;
5. запускает `npm run build`;
6. кладёт готовый бандл в `public/static/codemirror/editor.js`;
7. удаляет временные `src/` и `node_modules/` после завершения.
Это означает, что в рабочем дереве не остаётся мусора от сборки.
### `src/editor.js`
Временный исходник редактора.
Он **не хранится в репозитории** как постоянный файл: скрипт создаёт его во
временной директории только на время сборки.
Внутри этого файла живёт минимальная инициализация CodeMirror 6:
- монтирование на `textarea[data-codemirror-editor]`;
- HTML-режим;
- JavaScript-режим;
- CSS-режим;
- тема `oneDark`;
- перенос строк;
- синхронизация значения редактора обратно в скрытую textarea.
Если позже захочется менять поведение редактора, логика правится либо в шаблоне,
который выдаёт этот исходник, либо прямо в сборочном скрипте.
### `public/static/codemirror/editor.js`
Готовый результат сборки -- `public/static/codemirror/editor.js`. Именно этот файл подключается в Django-админку.
Он уже минифицирован и готов к использованию как обычная статика.
## Как запускать сборку
Из корня проекта:
```bash
bash ./frontend-assembly/build-codemirror6.sh
```
После успешного запуска в проекте должен обновиться только один полезный артефакт:
```bash
public/static/codemirror/editor.js
```
## Коротко о логике работы
Смысл этой папки такой:
- собрать CodeMirror 6 один раз;
- не держать в репозитории и контейнере лишний фронтенд-мусор;
- оставить Django только готовый JS-бандл CodeMirror для админки.

View File

@@ -0,0 +1,153 @@
#!/usr/bin/env bash
# Скрипт создаёт временную рабочую папку, ставит зависимости через `npm ci`, собирает
# минимизированный бандл и затем сам удаляет временные `src/` и `node_modules/`.
# В проекте остаётся только готовая статика:
# * `public/static/codemirror/editor.js`
#
# Запуск:
# bash ./frontend-assembly/build-codemirror6.sh
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
OUTPUT_DIR="$ROOT_DIR/../public/static/codemirror"
WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/codemirror6.XXXXXX")"
log() {
printf '[codemirror6] %s\n' "$*"
}
fail() {
printf '[codemirror6] %s\n' "$*" >&2
exit 1
}
cleanup() {
rm -rf "$WORK_DIR"
rm -rf "$ROOT_DIR/src" "$ROOT_DIR/node_modules"
}
trap cleanup EXIT INT TERM
if ! command -v npm >/dev/null 2>&1; then
fail 'Не найден `npm`. Установи Node.js и повтори сборку.'
fi
if [[ ! -f "$ROOT_DIR/package.json" ]]; then
fail "Не найден package.json: $ROOT_DIR/package.json"
fi
if [[ ! -f "$ROOT_DIR/package-lock.json" ]]; then
fail "Не найден package-lock.json: $ROOT_DIR/package-lock.json"
fi
mkdir -p "$WORK_DIR/src" "$OUTPUT_DIR"
cp "$ROOT_DIR/package.json" "$ROOT_DIR/package-lock.json" "$WORK_DIR/"
cat > "$WORK_DIR/src/editor.js" <<'EOF'
import { Compartment, EditorState } from '@codemirror/state';
import { EditorView } from '@codemirror/view';
import { defaultHighlightStyle, syntaxHighlighting } from '@codemirror/language';
import { html } from '@codemirror/lang-html';
import { javascript } from '@codemirror/lang-javascript';
import { css } from '@codemirror/lang-css';
import { solarizedDark, solarizedLight } from '@uiw/codemirror-theme-solarized';
import { lineNumbers } from '@codemirror/view';
const themeCompartment = new Compartment();
function isDarkTheme() {
const rootTheme = document.documentElement.dataset.theme;
if (rootTheme === 'dark') {
return true;
}
if (rootTheme === 'light') {
return false;
}
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
}
function reconfigureTheme(view) {
view.dispatch({
effects: themeCompartment.reconfigure(isDarkTheme() ? solarizedDark : solarizedLight),
});
}
function initCodeMirrorEditors() {
document.querySelectorAll('textarea[data-codemirror-editor]').forEach((textarea) => {
const language = textarea.dataset.language || 'html';
const initialDoc = textarea.value ?? '';
const wrapper = document.createElement('div');
wrapper.className = 'cm6-editor-wrapper';
textarea.insertAdjacentElement('beforebegin', wrapper);
textarea.hidden = true;
const syncTextarea = EditorView.updateListener.of((update) => {
if (update.docChanged) {
textarea.value = update.state.doc.toString();
}
});
const extensions = [
lineNumbers(),
EditorView.lineWrapping,
syntaxHighlighting(defaultHighlightStyle),
syncTextarea,
themeCompartment.of(isDarkTheme() ? solarizedDark : solarizedLight),
];
if (language === 'javascript') {
extensions.unshift(javascript());
} else if (language === 'css') {
extensions.unshift(css());
} else {
extensions.unshift(html());
}
const state = EditorState.create({
doc: initialDoc,
extensions,
});
const view = new EditorView({
state,
parent: wrapper,
});
reconfigureTheme(view);
const observer = new MutationObserver(() => reconfigureTheme(view));
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-theme', 'class'],
});
const colorScheme = window.matchMedia('(prefers-color-scheme: dark)');
colorScheme.addEventListener('change', () => reconfigureTheme(view));
textarea.value = view.state.doc.toString();
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initCodeMirrorEditors, { once: true });
} else {
initCodeMirrorEditors();
}
EOF
log "СОБИРАЮ CodeMirror 6 ДЛЯ ФРОНТЕНДА АДМИНКИ ПРОЕКТА"
log "Временная рабочая папка: $WORK_DIR"
cd "$WORK_DIR"
log 'Устанавливаю зависимости через npm ci'
npm ci
log 'Собираю CodeMirror 6'
export CM6_OUTPUT_DIR="$OUTPUT_DIR"
npm run build
log 'ГОТОВО'

729
frontend-assembly/package-lock.json generated Normal file
View File

@@ -0,0 +1,729 @@
{
"name": "lpon-codemirror6",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "lpon-codemirror6",
"version": "0.1.0",
"devDependencies": {
"@babel/runtime": "7.29.7",
"@codemirror/autocomplete": "6.20.3",
"@codemirror/commands": "6.10.3",
"@codemirror/lang-css": "6.3.1",
"@codemirror/lang-html": "6.4.11",
"@codemirror/lang-javascript": "6.2.5",
"@codemirror/language": "6.12.3",
"@codemirror/state": "6.6.0",
"@codemirror/view": "6.43.1",
"@uiw/codemirror-theme-solarized": "4.25.10",
"esbuild": "0.28.0"
}
},
"node_modules/@babel/runtime": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz",
"integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@codemirror/autocomplete": {
"version": "6.20.3",
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.3.tgz",
"integrity": "sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.17.0",
"@lezer/common": "^1.0.0"
}
},
"node_modules/@codemirror/commands": {
"version": "6.10.3",
"dev": true,
"license": "MIT",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.6.0",
"@codemirror/view": "^6.27.0",
"@lezer/common": "^1.1.0"
}
},
"node_modules/@codemirror/lang-css": {
"version": "6.3.1",
"dev": true,
"license": "MIT",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@lezer/common": "^1.0.2",
"@lezer/css": "^1.1.7"
}
},
"node_modules/@codemirror/lang-html": {
"version": "6.4.11",
"dev": true,
"license": "MIT",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/lang-css": "^6.0.0",
"@codemirror/lang-javascript": "^6.0.0",
"@codemirror/language": "^6.4.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.17.0",
"@lezer/common": "^1.0.0",
"@lezer/css": "^1.1.0",
"@lezer/html": "^1.3.12"
}
},
"node_modules/@codemirror/lang-javascript": {
"version": "6.2.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/language": "^6.6.0",
"@codemirror/lint": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.17.0",
"@lezer/common": "^1.0.0",
"@lezer/javascript": "^1.0.0"
}
},
"node_modules/@codemirror/language": {
"version": "6.12.3",
"dev": true,
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.23.0",
"@lezer/common": "^1.5.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0",
"style-mod": "^4.0.0"
}
},
"node_modules/@codemirror/lint": {
"version": "6.9.5",
"dev": true,
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.35.0",
"crelt": "^1.0.5"
}
},
"node_modules/@codemirror/state": {
"version": "6.6.0",
"dev": true,
"license": "MIT",
"dependencies": {
"@marijn/find-cluster-break": "^1.0.0"
}
},
"node_modules/@codemirror/view": {
"version": "6.43.1",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.1.tgz",
"integrity": "sha512-+BIjw/AG3tDQ4pJgTLPYdAW25eDE66YsvM4LKyVPgGzVgZ4a9Wj1SRX8kPVKgBDdPt8oHtZ15F0qx7p0oOHdHw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.6.0",
"crelt": "^1.0.6",
"style-mod": "^4.1.0",
"w3c-keyname": "^2.2.4"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz",
"integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz",
"integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz",
"integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz",
"integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.28.0",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz",
"integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz",
"integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz",
"integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz",
"integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz",
"integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz",
"integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz",
"integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz",
"integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz",
"integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz",
"integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz",
"integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz",
"integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz",
"integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz",
"integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz",
"integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz",
"integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz",
"integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz",
"integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz",
"integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz",
"integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz",
"integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@lezer/common": {
"version": "1.5.2",
"dev": true,
"license": "MIT"
},
"node_modules/@lezer/css": {
"version": "1.3.3",
"dev": true,
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.3.0"
}
},
"node_modules/@lezer/highlight": {
"version": "1.2.3",
"dev": true,
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.3.0"
}
},
"node_modules/@lezer/html": {
"version": "1.3.13",
"dev": true,
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0"
}
},
"node_modules/@lezer/javascript": {
"version": "1.5.4",
"dev": true,
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.1.3",
"@lezer/lr": "^1.3.0"
}
},
"node_modules/@lezer/lr": {
"version": "1.4.8",
"dev": true,
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.0.0"
}
},
"node_modules/@marijn/find-cluster-break": {
"version": "1.0.2",
"dev": true,
"license": "MIT"
},
"node_modules/@uiw/codemirror-theme-solarized": {
"version": "4.25.10",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-solarized/-/codemirror-theme-solarized-4.25.10.tgz",
"integrity": "sha512-mTP09g375qD86T09aRKQLHqkRSWDoot4dFUCtiyIsrvp1VeMBcA5ZSJHnI8A0lc8+hHRrvpL5fe3SnRFE+FoZg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.25.10"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-themes": {
"version": "4.25.10",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-themes/-/codemirror-themes-4.25.10.tgz",
"integrity": "sha512-Fqiz1HIuDlDftcL+/O53V333UOH6MqQ84VbiQB5egn6u+uDwAqACp1FrdAoi4wgpR3b3TGW4Gr0wIYcrJSSz1A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
},
"peerDependencies": {
"@codemirror/language": ">=6.0.0",
"@codemirror/state": ">=6.0.0",
"@codemirror/view": ">=6.0.0"
}
},
"node_modules/crelt": {
"version": "1.0.6",
"dev": true,
"license": "MIT"
},
"node_modules/esbuild": {
"version": "0.28.0",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.28.0",
"@esbuild/android-arm": "0.28.0",
"@esbuild/android-arm64": "0.28.0",
"@esbuild/android-x64": "0.28.0",
"@esbuild/darwin-arm64": "0.28.0",
"@esbuild/darwin-x64": "0.28.0",
"@esbuild/freebsd-arm64": "0.28.0",
"@esbuild/freebsd-x64": "0.28.0",
"@esbuild/linux-arm": "0.28.0",
"@esbuild/linux-arm64": "0.28.0",
"@esbuild/linux-ia32": "0.28.0",
"@esbuild/linux-loong64": "0.28.0",
"@esbuild/linux-mips64el": "0.28.0",
"@esbuild/linux-ppc64": "0.28.0",
"@esbuild/linux-riscv64": "0.28.0",
"@esbuild/linux-s390x": "0.28.0",
"@esbuild/linux-x64": "0.28.0",
"@esbuild/netbsd-arm64": "0.28.0",
"@esbuild/netbsd-x64": "0.28.0",
"@esbuild/openbsd-arm64": "0.28.0",
"@esbuild/openbsd-x64": "0.28.0",
"@esbuild/openharmony-arm64": "0.28.0",
"@esbuild/sunos-x64": "0.28.0",
"@esbuild/win32-arm64": "0.28.0",
"@esbuild/win32-ia32": "0.28.0",
"@esbuild/win32-x64": "0.28.0"
}
},
"node_modules/style-mod": {
"version": "4.1.3",
"dev": true,
"license": "MIT"
},
"node_modules/w3c-keyname": {
"version": "2.2.8",
"dev": true,
"license": "MIT"
}
}
}

View File

@@ -0,0 +1,23 @@
{
"name": "lpon-codemirror6",
"version": "0.1.0",
"description": "CodeMirror build for LPON.RU Django Admin",
"private": true,
"type": "module",
"scripts": {
"build": "esbuild src/editor.js --bundle --format=esm --minify --outfile=${CM6_OUTPUT_DIR:-../public/static/codemirror}/editor.js"
},
"devDependencies": {
"@babel/runtime": "7.29.7",
"@codemirror/autocomplete": "6.20.3",
"@codemirror/commands": "6.10.3",
"@codemirror/lang-css": "6.3.1",
"@codemirror/lang-html": "6.4.11",
"@codemirror/lang-javascript": "6.2.5",
"@codemirror/language": "6.12.3",
"@codemirror/state": "6.6.0",
"@codemirror/view": "6.43.1",
"@uiw/codemirror-theme-solarized": "4.25.10",
"esbuild": "0.28.0"
}
}

View File

@@ -0,0 +1,15 @@
# CodeMirror 6 static bundle
Эта папка содержит готовый результат сборки CodeMirror 6 для админки Django.
Главный файл здесь — `editor.js`. Его не нужно править вручную: это собранный и
минифицированный бандл.
Если нужна более свежая версия CodeMirror, меняй сборку в
`frontend-assembly/build-codemirror6.sh` и затем заново запускай:
```bash
bash ./frontend-assembly/build-codemirror6.sh
```
После пересборки в этой папке должен обновиться только `editor.js`.

File diff suppressed because one or more lines are too long