Add files via upload

This commit is contained in:
Андрей Кудлай 2026-04-30 21:45:25 +03:00 committed by GitHub
parent e5e9c08bd8
commit a5ef5548f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 3536 additions and 2 deletions

189
README.md
View file

@ -1,2 +1,187 @@
# structure-commander
Obsidian plugin for managing Markdown structure as a tree: move branches, change heading levels, fold sections, renumber outlines, export branches, and navigate long notes.
# Structure Commander v2.2.1
Плагин Obsidian для управления многоуровневой Markdown-структурой: перемещение веток, повышение и понижение уровня (включая преобразования между текстом, заголовками и списками), сворачивание/разворачивание, перенумерация, экспорт ветки, боковая панель и узкая панель инструментов над редактором.
## Назначение
Плагин превращает заголовки документа в управляемое дерево. Удобно работать с длинными заметками, ТЗ, конспектами, документацией: переставлять разделы, менять глубину иерархии, быстро сворачивать/разворачивать ветки, экспортировать отдельные подразделы в самостоятельные заметки.
## Команды
Все команды доступны через палитру `Ctrl+P`. Названия команд начинаются с `Structure Commander`.
| Команда | Действие |
|---|---|
| Ветка выше | переместить ветку выше (или, если есть выделение — переместить выделенные строки выше) |
| Ветка ниже | переместить ветку ниже (или, если есть выделение — переместить выделенные строки ниже) |
| Повысить (заголовок/текст/список) | универсальное «повышение» по цепочке: `а)``1)``1.``- ` → текст → H6 → H5 → … → H1 |
| Понизить (заголовок/текст/список) | универсальное «понижение» по цепочке: H1 → … → H6 → текст → `- ``1.``1)``а)` |
| Свернуть ветку | свернуть только текущую ветку |
| Развернуть ветку | развернуть только текущую ветку |
| Показать документ до Hx | свернуть всё, что глубже Hx |
| Показать всё | полностью развернуть документ |
| Скрыть/показать боковую панель | переключатель видимости панели структуры |
| Скрыть/показать панель инструментов | переключатель видимости тулбара над редактором |
| Поставить заголовку ветки H1…H6 | назначить уровень верхнему заголовку, всю ветку сдвинуть с сохранением иерархии |
| Перенумеровать структуру | пронумеровать заголовки и/или списки |
| Удалить нумерацию | убрать существующие префиксы нумерации |
| Emoji-цифры | заменить нумерацию на 1⃣, 2⃣, … |
| Исправить пропуски уровней | подтянуть уровни так, чтобы не было прыжков H1 → H3 |
| Экспорт ветки | сохранить текущую ветку в отдельный `.md` |
## Хоткеи по умолчанию
| Действие | Хоткей |
|---|---|
| Ветка выше | `Alt+Shift+↑` |
| Ветка ниже | `Alt+Shift+↓` |
| Повысить ветку | `Alt+Shift+←` |
| Понизить ветку | `Alt+Shift+→` |
| Свернуть ветку | `Alt+←` |
| Развернуть ветку | `Alt+→` |
Хоткеи `Alt+←/→` выбраны намеренно вместо `Ctrl+Shift+←/→`: последние конфликтуют со стандартным выделением текста по словам в любом текстовом поле.
Изменить хоткеи: `Настройки → Hotkeys`, ищите `Structure`. В настройках плагина есть кнопка `Открыть Hotkeys`.
## Панель инструментов над редактором
Над Markdown-редактором есть узкая горизонтальная панель:
```
[↑] [↓] [←] [→] [] [+] [Показать: H1 ▾] [H? ▾] [№]
```
| Кнопка | Действие |
|---|---|
| ↑ ↓ | переместить ветку выше/ниже |
| ← → | повысить/понизить (заголовок, текст или список) |
| + | свернуть / развернуть ветку |
| Показать: Hn ▾ | дропдаун с уровнями документа + «Показать всё». В тексте кнопки показан последний выбранный уровень |
| H? ▾ | уровень текущего заголовка; меню позволяет сменить уровень или назначить уровень верхнему заголовку всей ветки |
| № | перенумерация (ПКМ — подменю с другими режимами) |
В tooltip на кнопках показано действие и текущий хоткей. ПКМ по кнопке открывает меню с пунктом **«Назначить хоткей»** — это всплывающее окно, в котором достаточно нажать желаемую комбинацию клавиш (включая `Numpad1..9`, `F1..F12`, любые модификаторы), и плагин сразу её сохранит. Можно также очистить хоткей или открыть штатный редактор Obsidian.
Все выпадающие меню тулбара (`Показать ▾`, `Hn ▾`, ПКМ по `№`) показывают рядом с пунктами их текущие хоткеи, если хоткей назначен.
Кнопка `×` справа на тулбаре скрывает его. Вернуть тулбар: команда `Скрыть/показать панель инструментов над редактором` (или галка в настройках плагина).
Когда курсор стоит вне ветки заголовков, кнопки операций над веткой подсвечены приглушённо.
Если не нужна — отключается в настройках плагина.
## Контекстное меню редактора (ПКМ)
ПКМ в Markdown-редакторе добавляет в контекстное меню несколько верхнеуровневых пунктов с подменю:
```
Развернуть/свернуть ▸
├─ Развернуть Alt+→
├─ Свернуть Alt+←
└─ Показать/скрыть панель структуры
Показать до ▸ H1, H2, …, Показать всё
(только реально существующие в документе уровни)
Уровень | Перенос ▸
├─ Повысить Alt+Shift+←
├─ Понизить Alt+Shift+→
├─ Вверх Alt+Shift+↑
├─ Вниз Alt+Shift+↓
├─ Понизить до ▸ H2, H3, … (вся ветка с сохранением иерархии)
└─ Повысить до ▸ H1, H2, … (вся ветка с сохранением иерархии)
Нумерация ▸ Перенумеровать, Удалить, Emoji, Исправить пропуски
Экспорт ветки…
```
В подменю «Понизить до» / «Повысить до» — только те уровни, при сдвиге к которым ни один заголовок ветки не выйдет за пределы H1…H6.
`Повысить` / `Понизить` универсальны: работают и на заголовке (вся ветка), и на обычном тексте (текст → H6 / текст → список), и на маркированном списке (список → текст).
## Боковая панель
Команды:
- `Открыть боковую панель структуры справа`
- `Открыть боковую панель структуры слева`
- `Открыть боковую панель структуры снизу`
- `Скрыть/показать боковую панель структуры` (можно повесить хоткей)
В панели:
- поиск по заголовкам;
- фильтр глубины (`До H1` … `До H6`, `Все`);
- счётчик `видно / всего`;
- подсветка текущего заголовка (по позиции курсора в активном редакторе);
- кнопка `×` в шапке скрывает панель;
- ПКМ по пункту: `Перейти`, свернуть/развернуть, переместить выше/ниже, повысить/понизить, экспорт.
Уровень заголовка в панели передаётся **визуально** — отступом, жирностью, наклоном и цветом текста. Подписи `H1`, `H2` намеренно не дублируются в каждой строке.
Панель обновляется при смене активного файла, при изменении файла, при смене активного редактора. Чтение файла защищено от устаревших async-результатов и работает с debounce (≈220 мс).
## Экспорт ветки
Окно экспорта:
- куда сохранить: рядом с текущей заметкой / в указанную папку / спрашивать каждый раз;
- папка по умолчанию: `_Exports`;
- чекбокс «Открыть файл после экспорта» (по умолчанию выключен);
- чекбокс «Привести корневой заголовок к H1» — при экспорте сдвигает уровни так, чтобы корень стал H1;
- имя файла берётся из заголовка корня ветки, запрещённые символы вычищаются;
- при коллизии добавляется суффикс ` 2`, ` 3`, ` 4` …;
- последний выбор запоминается, `Enter` повторяет.
## Перенумерация
Окно перенумерации:
- область: весь документ / текущая ветка / выделенный фрагмент;
- что менять: только заголовки / только списки / заголовки и списки;
- стиль: `1. / 1.1.` или `1⃣ / 1⃣.1️⃣`;
- предпросмотр: первые 8 изменений в виде `было → станет`;
- сводка `Будет изменено строк: N`.
Применение — одной транзакцией. Курсор и позиция скролла сохраняются. `Ctrl+Z` откатывает всю операцию одним действием.
## Транзакционность
Все массовые изменения (перемещение ветки, повышение/понижение, нормализация, перенумерация, удаление нумерации, emoji-цифры) применяются одной транзакцией редактора — `Ctrl+Z` откатывает каждую операцию одним действием.
`moveBlock` переписан так, чтобы:
- корректно работать на первой и последней ветке;
- корректно работать на файле без финального перевода строки;
- не создавать лишних пустых строк;
- не склеивать заголовки.
## Ограничения
- Markdown поддерживает только заголовки **H1 … H6**.
- Плагин работает с **ATX-заголовками**: `#`, `##`, `###`, …
- **Setext-заголовки** (с подчёркиванием `===` / `---`) не поддерживаются.
- Заголовки внутри **fenced code blocks** (` ``` ` или `~~~`) игнорируются.
- Глобальные хоткеи Windows / macOS / других приложений изнутри Obsidian проверить нельзя — коллизии с системными или сторонними хоткеями плагин не покажет, только коллизии внутри Obsidian.
- При использовании `Alt+←/→` для свёртывания: на macOS `Alt` это `Option` — учитывайте раскладки клавиатуры.
## Установка
1. Закрыть Obsidian.
2. Скопировать папку `structure-commander` в `.obsidian/plugins/` хранилища.
3. Проверить структуру файлов:
```
.obsidian/plugins/structure-commander/manifest.json
.obsidian/plugins/structure-commander/main.js
.obsidian/plugins/structure-commander/styles.css
```
4. Открыть Obsidian.
5. `Настройки → Community plugins` → включить `Structure Commander`.
## Совместимость
`minAppVersion`: 1.0.0. Тестировалось с актуальными десктопными сборками Obsidian. На мобильных Obsidian панель инструментов над редактором тоже работает, но физических хоткеев на телефоне нет — пользуйтесь верхней панелью и подменю ПКМ.

2946
main.js Normal file

File diff suppressed because it is too large Load diff

9
manifest.json Normal file
View file

@ -0,0 +1,9 @@
{
"id": "structure-commander",
"name": "Structure Commander",
"version": "2.2.1",
"minAppVersion": "1.0.0",
"description": "Управление Markdown-структурой: ветки, уровни, сворачивание, перенумерация, экспорт ветки, боковая панель и панель инструментов над редактором.",
"author": "Structure Commander",
"isDesktopOnly": false
}

394
styles.css Normal file
View file

@ -0,0 +1,394 @@
/* ========================== Modal: общий пульт ========================== */
.structure-commander-modal {
max-width: 820px;
}
.structure-commander-small-modal {
max-width: 580px;
}
.structure-commander-section {
margin: 14px 0;
padding: 10px 12px;
border: 1px solid var(--background-modifier-border);
border-radius: 8px;
background: var(--background-secondary);
}
.structure-commander-section h3 {
margin: 0 0 8px 0;
font-size: 15px;
}
.structure-commander-buttons {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.structure-commander-buttons button {
cursor: pointer;
padding: 4px 10px;
}
.structure-commander-levels button {
min-width: 42px;
}
.structure-commander-note,
.structure-commander-muted {
color: var(--text-muted);
font-size: 12px;
margin-top: 12px;
}
.structure-radio-group,
.structure-radio-block {
display: flex;
flex-direction: column;
gap: 6px;
margin: 12px 0;
}
.structure-radio-title {
font-weight: 600;
margin-bottom: 2px;
}
.structure-radio-group label,
.structure-radio-block label {
cursor: pointer;
}
.structure-modal-buttons {
display: flex;
justify-content: flex-end;
gap: 8px;
margin-top: 16px;
}
/* Renumber preview */
.structure-commander-preview {
margin: 14px 0 4px 0;
padding: 10px 12px;
border: 1px solid var(--background-modifier-border);
border-radius: 8px;
background: var(--background-primary-alt, var(--background-secondary));
}
.structure-commander-preview h3 {
margin: 0 0 8px 0;
font-size: 14px;
}
.structure-preview-list {
display: flex;
flex-direction: column;
gap: 4px;
margin-bottom: 6px;
}
.structure-preview-row {
display: grid;
grid-template-columns: 1fr auto 1fr;
gap: 8px;
font-family: var(--font-monospace);
font-size: 12px;
align-items: center;
}
.structure-preview-before { color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.structure-preview-arrow { color: var(--text-faint); }
.structure-preview-after { color: var(--text-normal); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* ========================== Боковая панель ========================== */
.structure-panel-root {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}
.structure-panel-header {
display: flex;
align-items: center;
gap: 6px;
padding: 8px;
border-bottom: 1px solid var(--background-modifier-border);
background: var(--background-primary);
flex: 0 0 auto;
}
.structure-panel-title {
font-weight: 700;
flex: 1 1 auto;
}
.structure-panel-level-select {
max-width: 100px;
}
.structure-panel-icon-btn {
padding: 2px 8px;
}
.structure-panel-searchrow {
padding: 6px 8px 4px 8px;
border-bottom: 1px solid var(--background-modifier-border);
background: var(--background-primary);
}
.structure-panel-search {
width: 100%;
padding: 4px 8px;
border: 1px solid var(--background-modifier-border);
border-radius: 6px;
background: var(--background-modifier-form-field);
color: var(--text-normal);
font-size: 13px;
}
.structure-panel-body {
overflow: auto;
padding: 8px;
flex: 1 1 auto;
}
.structure-panel-file {
color: var(--text-muted);
font-size: 12px;
margin-bottom: 4px;
word-break: break-word;
}
.structure-panel-counter {
font-size: 11px;
color: var(--text-faint);
margin-bottom: 6px;
}
.structure-panel-list {
display: flex;
flex-direction: column;
gap: 1px;
}
.structure-panel-item {
cursor: pointer;
border-radius: 6px;
padding: 3px 8px;
line-height: 1.3;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 13px;
border-left: 2px solid transparent;
}
.structure-panel-item:hover {
background: var(--background-modifier-hover);
}
/* — уровни передаются ВИЗУАЛЬНО, без подписей H1/H2 — */
.structure-panel-level-1 {
font-weight: 700;
font-size: 14px;
color: var(--text-normal);
margin-left: 0;
}
.structure-panel-level-2 {
font-weight: 600;
color: var(--text-normal);
margin-left: 12px;
}
.structure-panel-level-3 {
font-weight: 500;
color: var(--text-normal);
margin-left: 24px;
}
.structure-panel-level-4 {
font-weight: 400;
font-style: italic;
color: var(--text-muted);
margin-left: 36px;
}
.structure-panel-level-5 {
font-weight: 400;
font-style: italic;
color: var(--text-muted);
margin-left: 48px;
font-size: 12px;
}
.structure-panel-level-6 {
font-weight: 400;
font-style: italic;
color: var(--text-faint);
margin-left: 60px;
font-size: 12px;
}
/* Подсветка активного заголовка */
.structure-panel-item-active {
background: var(--background-modifier-active-hover, var(--background-modifier-hover));
border-left-color: var(--interactive-accent);
}
.structure-panel-empty {
padding: 12px;
color: var(--text-muted);
}
/* ========================== Toolbar над редактором ========================== */
.scmd-toolbar {
display: flex;
flex-wrap: nowrap;
align-items: center;
gap: 4px;
padding: 4px 6px;
height: auto;
min-height: 30px;
border-bottom: 1px solid var(--background-modifier-border);
background: var(--background-primary);
overflow-x: auto;
flex: 0 0 auto;
}
.scmd-toolbar.scmd-tb-empty .scmd-tb-btn {
opacity: 0.45;
pointer-events: none;
}
.scmd-toolbar.scmd-tb-empty .scmd-tb-btn-text {
pointer-events: auto; /* "Показать до" / "Экспорт" пусть остаются доступны */
opacity: 0.6;
}
.scmd-tb-btn {
cursor: pointer;
padding: 2px 8px;
height: 24px;
min-width: 26px;
font-size: 13px;
border: 1px solid var(--background-modifier-border);
border-radius: 5px;
background: var(--background-secondary);
color: var(--text-normal);
display: inline-flex;
align-items: center;
justify-content: center;
white-space: nowrap;
}
.scmd-tb-btn:hover {
background: var(--background-modifier-hover);
}
.scmd-tb-btn:active {
background: var(--background-modifier-active-hover);
}
.scmd-tb-btn-text {
font-size: 12px;
padding: 2px 10px;
}
.scmd-tb-disabled {
opacity: 0.55;
}
.scmd-tb-dd {
display: inline-flex;
}
/* ========================== Settings tab: таблица хоткеев ========================== */
.structure-settings-hotkeys table {
width: 100%;
border-collapse: collapse;
margin-top: 8px;
}
.structure-settings-hotkeys td {
padding: 4px 6px;
border-bottom: 1px solid var(--background-modifier-border);
}
.structure-settings-hotkeys td:last-child {
font-family: var(--font-monospace);
color: var(--text-muted);
text-align: right;
}
/* ========================== Toolbar: spacer + кнопка скрытия ========================== */
.scmd-tb-spacer {
flex: 1 1 auto;
}
.scmd-tb-hide {
font-size: 16px;
line-height: 1;
padding: 2px 8px;
color: var(--text-muted);
}
.scmd-tb-hide:hover {
color: var(--text-error);
}
/* ========================== HotkeyCaptureModal ========================== */
.scmd-hk-current {
margin: 8px 0;
color: var(--text-muted);
font-size: 13px;
}
.scmd-hk-capture {
margin: 12px 0;
padding: 14px 16px;
border: 2px dashed var(--background-modifier-border);
border-radius: 8px;
background: var(--background-secondary);
text-align: center;
font-family: var(--font-monospace);
font-size: 14px;
cursor: text;
outline: none;
user-select: none;
}
.scmd-hk-capture:focus {
border-color: var(--interactive-accent);
}
.scmd-hk-capture.scmd-hk-captured {
border-style: solid;
border-color: var(--interactive-accent);
color: var(--text-normal);
background: var(--background-modifier-form-field);
}
/* ========================== Settings: список хоткеев команд ========================== */
.scmd-hk-section h3 {
margin-top: 0.5em;
}
.scmd-hk-section h4.scmd-hk-group {
margin: 18px 0 6px 0;
padding-bottom: 4px;
border-bottom: 1px solid var(--background-modifier-border);
font-size: 13px;
font-weight: 600;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.04em;
}
.scmd-hk-list {
display: flex;
flex-direction: column;
gap: 2px;
}
.scmd-hk-row {
display: grid;
grid-template-columns: 1fr auto auto auto;
gap: 8px;
align-items: center;
padding: 6px 0;
border-bottom: 1px solid var(--background-modifier-border-hover);
}
.scmd-hk-row-label {
font-size: 13px;
}
.scmd-hk-row-current {
font-family: var(--font-monospace);
font-size: 12px;
color: var(--text-normal);
padding: 2px 8px;
background: var(--background-secondary);
border-radius: 4px;
min-width: 140px;
text-align: center;
}
.scmd-hk-row-current.scmd-hk-row-empty {
color: var(--text-faint);
font-style: italic;
}
.scmd-hk-row-btn {
padding: 2px 10px;
font-size: 12px;
}
.scmd-hk-row-clear {
padding: 2px 8px;
font-size: 14px;
line-height: 1;
color: var(--text-muted);
}
.scmd-hk-row-clear:hover {
color: var(--text-error);
}