Знаете это чувство, когда правишь JSON и ищешь, где же ты забыл запятую? Я устал и сделал редактор, где JSON — это таблица. А теперь он ещё и открывает папку проекта целиком: дерево файлов, Ctrl+S прямо в файл, drag & drop. Как в VS Code, только заточено под данные. Читать далее
Краткое описание: Продолжаю историю CraftHub — кроссплатформенного редактора, который показывает JSON как таблицу. В прошлых частях мы разбирались с табличным редактированием и расширением для VS Code. В этот раз — самое частое пожелание из issue: «дайте работать с директорией, а не открывать файлы через диалог каждый раз». Рассказываю, как я прикрутил файловый проводник в стиле VS Code, почему сохранение в файл оказалось не таким тривиальным, и на какие грабли Avalonia наступил по дороге.
Для тех, кто пропустил предыдущие серииCraftHub — это open-source редактор JSON, который открывает массив объектов не как «простыню» текста, а как таблицу: колонки — это поля, строки — элементы массива. Есть десктопная версия на Avalonia (Windows/macOS/Linux) и расширение для VS Code. Идея простая: перестать редактировать данные вручную в текстовом виде и ошибаться в запятых.
Проект живёт за счёт обратной связи. И самое частое, что просили в последнее время, звучало примерно так:
«Неудобно каждый раз открывать файл через диалог. Хочу видеть папку проекта и переключаться между файлами, как в VS Code».
Это стало issue про работу в директории. В этой статье — как я его закрывал.
Проблема: диалог открытия файла — это трениеРаньше цикл работы выглядел так: «Импорт JSON» → системный диалог → найти файл → открыть → поправить → «Экспорт» → снова диалог → выбрать путь. Если файлов в проекте десяток и ты прыгаешь между ними, это утомляет. Каждое переключение — несколько кликов и потеря контекста.
По сути не хватало двух вещей:
Навигации — видеть дерево файлов проекта и открывать их в один-два клика.
Нормального сохранения — чтобы Ctrl+S писал в тот же файл, а не спрашивал путь заново.
Звучит как «просто добавь панель слева». На практике вторая часть оказалась интереснее первой.
Решение: панель проводника в стиле VS CodeПервое — сама панель. Выбираешь корневую папку проекта, и слева появляется дерево: папки и .json-файлы. Двойной клик — файл открывается во вкладке. Есть фильтр (по умолчанию показываем только JSON, чтобы не тонуть в мусоре), поиск по файлам и кнопка показать/скрыть панель. Состояние — корневая папка, ширина, видимость — запоминается между запусками.
Наивная реализация — рекурсивно обойти всю папку при открытии — умрёт на большом проекте (привет, node_modules). Поэтому дерево грузится лениво, как в нормальных проводниках: у папки, в которой есть содержимое, добавляется «пустышка»-заглушка, чтобы отрисовать стрелку раскрытия, а реальные дети подгружаются только в момент разворачивания.
partial void OnIsExpandedChanged(bool value)
{
if (value && IsDirectory && !_childrenLoaded)
LoadChildren(); // читаем диск только когда узел реально раскрыли
}
Самое интересное: сохранение обратно в файлВот тут пришлось подумать про модель. У вкладки раньше не было понятия «я привязана к файлу на диске». Импорт просто читал содержимое, экспорт каждый раз спрашивал путь. Чтобы Ctrl+S работал как в редакторе, я ввёл у вкладки состояние «связана с файлом»:
Открыл файл (из проводника или через «Импорт») → вкладка запоминает путь.
Ctrl+S сериализует данные и пишет в этот же файл, без диалога.
Если файла ещё нет (новая вкладка) → Ctrl+S предлагает «Сохранить как», по умолчанию в текущую папку проекта, и после этого вкладка привязывается к новому файлу.
Дальше пошли нюансы, без которых «сохранение» ощущается сломанным:
Индикатор несохранённого. На вкладке загорается точка ●, если данные изменены. Сначала я хотел ловить это через события коллекций строк — но они срабатывают и при импорте, и пришлось бы городить флаги «сейчас идёт загрузка». В итоге сигналом «данные поменялись» стал сам сервис Undo/Redo: раз действие попало в историю — значит, пользователь что-то изменил.
Внешние изменения. Если файл на диске поменяли другой программой (сравниваю время модификации), перед перезаписью честно спрашиваю, а не молча затираю чужие правки.
Переименование = переименование файла. Логичное следствие модели: переименовал вкладку — переименовался и файл на диске (с сохранением расширения), а дерево обновилось. Иначе получается враньё: подпись одна, файл другой.
Файловые операции: как в проводникеРаз уж есть дерево — от него ждут поведения файлового менеджера. Добавил прямо в панель:
создание нового файла (в корне или в конкретной папке);
удаление — через контекстное меню и по клавише Delete;
копирование / вырезание / вставку файлов (Ctrl+C / Ctrl+X / Ctrl+V);
drag & drop — перетащить файл или папку в другую папку;
«показать в системном проводнике» и «скопировать путь».
Копирование-вставка и drag & drop под капотом ходят в один и тот же движок переноса: он разруливает конфликты имён (создаёт name copy.json), не даёт бросить папку саму в себя или в свою подпапку, и обновляет только те узлы дерева, которых коснулись, а не перестраивает всё целиком.
Дорожную карту во многом рисует не мой список, а ваши issue. Если чего-то не хватает или что-то раздражает — заходите в репозиторий и напишите. Ровно так появилась и эта фича.
GitHub: https://github.com/c3n9/CraftHub
Прошлые части — про табличное редактирование и расширение для VS Code — в моём профиле.
Спасибо, что читаете и подсказываете.
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | CraftHub: я устал редактировать JSON руками — а сообщество помогло превратить это в удобный инструмент | 0 | 10.31 | 03-07-2026 |
| 2 | Почему JSON на практике — быстрый формат для баз данных (если выкинуть лишние абстракции) | 5 | 7 | 07-07-2026 |
| 3 | Метод, которого не существовало: как я собрал локальный RAG для CAD API | 0 | 7.24 | 10-07-2026 |
| 4 | Мне надоело писать один и тот же код. Поэтому я сделал Featuregen | 3 | 6 | 09-07-2026 |
| 5 | Ночной джоб, который сам нарезает партиции: cron, SQL-функция и сплиттер | 0 | 11.55 | 21-07-2026 |
| 6 | Видеомонтаж в blender, бесплатный и надёжный | 0 | 9.44 | 10-07-2026 |
| 7 | Когда контекстное окно кончается, а проект — нет | 5 | 7 | 23-06-2026 |
| 8 | [Перевод] Рисуем ASCII-арт в Vim | 0 | 8.82 | 24-07-2026 |
| 9 | Я делал свой продукт, пока работал в найме — и вот что получилось | 0 | 5 | 12-07-2026 |
| 10 | Как я создаю «Черный ящик»: почему разработчику не подходят Jira, Битрикс24 и другие трекеры «для маркетологов» | 5 | 7 | 22-06-2026 |