DRAKON Diagram Editor for VS Code
Визуальный редактор DRAKON-схем в VS Code: рисуешь диаграмму мышкой — получаешь код, и обратно: из кода — диаграмму.
Расширение стоит на плечах готового движка DrakonWidget (Unlicense), а вся логика работы с диаграммой — своё ядро без зависимостей.
Что умеет
-
Редактирование
*.drakon— собственный редактор (CustomTextEditorProvider), не просто просмотрщик: узлы добавляются, переименовываются и связываются прямо в webview. - Генерация кода — команда «DRAKON: Generate Code from Diagram» превращает схему в код рядом с файлом.
-
Диаграмма из текста — команда «DRAKON: Show Diagram from Code» принимает JSON диаграммы, обёрнутый как угодно: голый файл, блок
```jsonили сниппетvar diagram = {…};из примеров виджета. Для.js/.tsработает отдельный парсер подмножества: функция превращается в DRAKON-схему. -
Живой предпросмотр JS/TS — команда «DRAKON: Open Diagram Preview» рисует схему активного
.js/.ts-файла в панели рядом с кодом и перерисовывает её при каждом изменении. Схема — снимок только для чтения, кнопкой её можно сохранить как*.drakon. Готовый пример для пробы —samples/fibonacci.js. - Плагинные языки генерации — JavaScript и псевдокод из коробки; новый язык — это отдельный модуль и одна строка регистрации, ядро не трогается.
- Настройки — целевой язык, имя функции, режим только для чтения, тема.
Быстрый старт
Нужен Node.js 22 или новее.
npm ci
npm run check # линтер + формат + типы + тесты
npm run build # dist/extension.js + dist/webview.js
Запуск в живом VS Code: откройте репозиторий и нажмите F5 — поднимется Extension Development Host с папкой samples/ в качестве рабочего пространства (см. .vscode/launch.json).
Первым делом: в открывшемся окне откройте samples/fibonacci.js и нажмите Ctrl+Alt+D — рядом появится DRAKON-схема этой функции.
Команды
| Команда | Что делает |
|---|---|
DRAKON: Generate Code from Diagram |
Генерирует код из открытой *.drakon в файл рядом (diagram.drakon → diagram.js). Существующий файл перезаписывается, только если он тоже сгенерирован; иначе — вопрос с выбором. |
DRAKON: Show Diagram from Code |
Открывает диаграмму из активного файла или выделенного ресурса: канонический JSON виджета или подмножество .js/.ts (функция → схема). |
DRAKON: Open Diagram Preview |
Панель рядом с редактором: живая DRAKON-схема активного .js/.ts-файла (обновляется на каждую правку, только чтение). Кнопка в заголовке сохраняет схему как *.drakon. Ctrl+Alt+D. |
DRAKON: Export Diagram as SVG |
Пока не реализовано: показывает уведомление. |
Настройки
| Ключ | Тип | По умолчанию | Смысл |
|---|---|---|---|
drakon.codegen.language |
javascript | pseudocode
|
javascript |
Целевой язык генерации. |
drakon.codegen.functionName |
string | "" |
Имя функции; пусто — вывести из имени диаграммы. |
drakon.editor.readOnly |
boolean | false |
Открывать диаграммы только для чтения. |
drakon.editor.theme |
auto | light | dark
|
auto |
Тема редактора. |
Формат файла
*.drakon — это нативный JSON виджета, ровно то, что отдаёт exportJson():
{
"type": "drakon",
"name": "Price check",
"items": {
"1": { "type": "branch", "one": "2", "branchId": 0 },
"2": { "type": "question", "content": "price > 100?", "one": "3", "two": "4" },
"3": { "type": "action", "content": "approve", "one": "5" },
"4": { "type": "action", "content": "reject", "one": "5" },
"5": { "type": "end" }
}
}
Поскольку формат канонический, round-trip «виджет → файл → виджет» точный, а лишние поля сохраняются дословно — диаграмма не деградирует при сохранении.
Архитектура
Два пакета в npm workspaces, зависимость строго в одну сторону: расширение знает про ядро, ядро про расширение — нет.
packages/core @drakon/core — платформо-независимое ядро (L0–L2)
src/model.ts модель диаграммы, validate/parse/serialize/normalize
src/edit.ts применение батчей правок (applyEdit/applyEdits)
src/codegen/ir.ts языко-независимый IR: DRAKON-граф → поток управления
src/codegen/*.ts генераторы (javascript, pseudocode) + реестр
src/parse/*.ts парсеры текста в модель + реестр
src/parse/javascript.ts подмножество JS/TS → модель (обратный ход генератора)
packages/extension адаптер VS Code (L3–L5)
src/drakonEditorProvider.ts CustomTextEditorProvider для *.drakon
src/diagramDocument.ts синхронизация модели с TextDocument, undo, dirty
src/codegen.ts команда генерации кода
src/showDiagram.ts команда «диаграмма из кода»
src/preview.ts живая панель предпросмотра JS/TS
src/webviewHtml.ts общий HTML/CSP редактора и панели
webview/main.ts webview, EditSender, postMessage-мост
media/drakonwidget.js vendored движок DrakonWidget 1.5.7
Уровни абстракции
Нумерация L0–L5 идёт снизу вверх: чем меньше номер, тем меньше слой знает о мире вокруг. Импорты разрешены только вниз — L5 знает про L4, L4 про L3 и так далее; обратный импорт (ядро, знающее про VS Code или webview) считается ошибкой слоя.
| Слой | Файлы | Отвечает за | Не знает о |
|---|---|---|---|
| L0 | packages/core/src/model.ts |
Типы документа, валидация, парсинг и сериализация JSON | виджете, VS Code, DOM, генерации |
| L1 |
packages/core/src/edit.ts, codegen/ir.ts, parse/types.ts, parse/drakonJson.ts, parse/javascript.ts, parse/registry.ts
|
Правки модели, обход графа в языко-независимый поток, разбор текста в модель | целевом языке, VS Code, DOM |
| L2 |
packages/core/src/codegen/types.ts, codegen/javascript.ts, codegen/pseudocode.ts, codegen/registry.ts
|
Рендеринг потока управления в текст конкретного языка + реестр генераторов | VS Code, webview, форме хранения |
| L3 | packages/extension/src/diagramDocument.ts |
Синхронизация модели с TextDocument: чтение, запись, undo, грязный флаг |
webview, DOM, командах |
| L4 |
packages/extension/src/drakonEditorProvider.ts, codegen.ts, showDiagram.ts, preview.ts, webviewHtml.ts
|
CustomTextEditorProvider, команды, файловый ввод-вывод, панель предпросмотра, общий HTML/CSP |
DOM и устройстве webview |
| L5 |
packages/extension/src/extension.ts, packages/extension/webview/main.ts, webview/widget.ts, webview/icons.ts
|
Точка входа расширения, рендер в webview, обвязка DrakonWidget, мост postMessage
|
правилах предметной области — они в ядре |
L0 — модель данных. DrakonItemType (список видов узлов: action, question, select, case, branch, end, loopbegin/loopend, sinput и прочие), DrakonItem, DrakonDiagram. Операции: validateDiagram, parseDiagram, serializeDiagram, normalizeDiagram, findEntryIds, createEmptyDiagram, compareNumericIds. Слой знает ровно одно — форму JSON-документа *.drakon; ни виджет, ни генерация его не касаются.
L1 — операции над моделью. Здесь живёт всё, что работает с графом, но ещё не знает целевого языка:
-
edit.ts—EditChange(insert | update | delete),applyEdit/applyEdits/applyEditInPlace,cloneDiagram. Батч правок из webview превращается в новую неизменяемую модель. -
codegen/ir.ts—buildFlowобходит DRAKON-граф и строитFlowProgram: языко-независимый поток управления (Flow,LoopKind,SelectBranch). ВспомогательныеfindEntryId,successors,findLoopHeaders,readSelect. -
parse/*— контрактDiagramParser, утилитаextractJsonObject, парсерыDrakonJsonParser(канонический JSON виджета в любой обёртке) иparseJavaScript(подмножество JS/TS → модель), плюс реестр парсеров.
L2 — генерация текста. codegen/types.ts объявляет контракт CodeGenerator (id, label, languageId, fileExtension, generate(program, options)) и общие утилиты indenter, identifierFrom, contentLines. Конкретные языки — JavaScriptGenerator и PseudocodeGenerator; codegen/registry.ts (listGenerators, findGenerator, resolveGenerator) — точка подключения нового языка. Генератор — чистая функция FlowProgram → string: он не видит ни типов узлов, ни идентификаторов, ни рёбер, поэтому новый язык не требует правок ни в ядре, ни в расширении.
L3 — документ VS Code. DiagramDocument держит модель в согласии с TextDocument: читает файл, применяет батчи правок, пишет обратно, ведёт undo и грязный флаг. Единственное место, где модель встречается с файловой системой VS Code.
L4 — интеграция с VS Code. DrakonEditorProvider (это CustomTextEditorProvider, тип представления — DRAKON_VIEW_TYPE), команды генерации и показа диаграммы, parserIdForUri (выбор парсера по расширению файла), панель живого предпросмотра (PREVIEW_EXTENSIONS, openPreview, savePreview), общий HTML/CSP редактора и панели (createNonce, buildDiagramHtml). Слой знает про API VS Code и про то, что нужно отдать в webview, но не трогает DOM.
L5 — webview. main.ts монтирует DrakonWidget, собирает правки виджета в батчи EditSender и шлёт их через postMessage; widget.ts — единственное место, типизирующее необъявленный API вендоренного движка; icons.ts — иконки контекстного меню; media/drakonwidget.js — сам движок. Webview намеренно без состояния: хост всегда присылает полную диаграмму, модель здесь не мутируется.
Поток данных
Поток данных однонаправленный: parse → model → codegen. TextDocument — единственный источник правды; webview рисует модель и шлёт батчи правок, а не правит файл напрямую. Именно поэтому генерация даёт тот же результат, что видно на экране.
Ядро не импортирует vscode и не трогает DOM — это сделано сознательно: этап «другие платформы» (Android/веб) переиспользует @drakon/core, добавив только новый адаптер. Правило проверяется глазами при каждом рефакторинге: любой импорт vscode или обращение к document/window в packages/core/src/ — ошибка слоя. Ядро обязано оставаться запускаемым под node --test без VS Code; тот же шов даёт бесплатную проверку качества — второй генератор (псевдокод) ловит ошибки обхода графа, которые первый может замаскировать совпадением по тексту.
Разработка
npm run lint # ESLint 9
npm run format # Prettier --write
npm run format:check # Prettier --check (входит в check)
npm run typecheck # tsc --noEmit
npm test # node --test (нативный раннер, TypeScript напрямую)
npm run check # всё вышеперечисленное
npm run build # esbuild: расширение + webview
Сборки и зависимости статического анализа в git не попадают (dist/, node_modules/). Тесты гоняются нативным node --test — Node исполняет TypeScript сам, отдельный шаг компиляции для тестов не нужен.
Тестов 97: модель, правки, парсеры (round-trip JSON и подмножества JS/TS), генератор JavaScript и отдельно генератор псевдокода — второй генератор работает как бесплатная проверка плагинного шва: он ловит то, что первый генератор может замаскировать совпадением по тексту. Round-trip диаграмма → JavaScriptGenerator → parseJavaScript → диаграмма проверяет обратимость подмножества.
Лицензия
Unlicense — программное обеспечение передано в общественное достояние, делайте с ним что хотите. Текст — в файле LICENSE.
Вендоренный движок DrakonWidget (v1.5.7, файл packages/extension/media/drakonwidget.js) распространяется под той же лицензией Unlicense.