@reformer/builder-plugin-api 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +206 -0
  3. package/dist/index.d.ts +147 -0
  4. package/dist/index.js +76 -0
  5. package/dist/internal.d.ts +78 -0
  6. package/dist/internal.js +289 -0
  7. package/dist/permissions-BWlsUVPs.js +551 -0
  8. package/dist/plugin/bundled-modules.d.ts +25 -0
  9. package/dist/plugin/layout.d.ts +27 -0
  10. package/dist/plugin/manifest-parser.d.ts +54 -0
  11. package/dist/plugin/manifest.d.ts +320 -0
  12. package/dist/plugin/messages-bundle.d.ts +24 -0
  13. package/dist/plugin/permissions.d.ts +48 -0
  14. package/dist/plugin/plugin-exports.d.ts +10 -0
  15. package/dist/plugin/runtime-modules.d.ts +20 -0
  16. package/dist/plugin/storage.d.ts +70 -0
  17. package/dist/plugin/types.d.ts +136 -0
  18. package/dist/primitives/capability.d.ts +72 -0
  19. package/dist/primitives/command.d.ts +262 -0
  20. package/dist/primitives/disposable.d.ts +23 -0
  21. package/dist/primitives/event.d.ts +38 -0
  22. package/dist/primitives/extension-point.d.ts +53 -0
  23. package/dist/primitives/module-path.d.ts +21 -0
  24. package/dist/primitives/resource.d.ts +159 -0
  25. package/dist/primitives/semver.d.ts +93 -0
  26. package/dist/primitives/service.d.ts +68 -0
  27. package/dist/primitives/when-context.d.ts +69 -0
  28. package/dist/primitives/when-expr.d.ts +177 -0
  29. package/dist/resource-clipboard-Bdk_Is5N.js +381 -0
  30. package/dist/runtime-modules-CiUDFMIn.js +597 -0
  31. package/dist/services/context-keys.d.ts +52 -0
  32. package/dist/services/diagnostics/fixes.d.ts +40 -0
  33. package/dist/services/diagnostics/service.d.ts +34 -0
  34. package/dist/services/diagnostics/types.d.ts +148 -0
  35. package/dist/services/document-models.d.ts +20 -0
  36. package/dist/services/documents.d.ts +94 -0
  37. package/dist/services/host-messages.d.ts +10 -0
  38. package/dist/services/i18n.d.ts +43 -0
  39. package/dist/services/modules.d.ts +49 -0
  40. package/dist/services/notifications.d.ts +56 -0
  41. package/dist/services/plugin-settings.d.ts +10 -0
  42. package/dist/services/plugins-catalog.d.ts +58 -0
  43. package/dist/services/preview.d.ts +163 -0
  44. package/dist/services/prompt.d.ts +111 -0
  45. package/dist/services/resource-clipboard.d.ts +28 -0
  46. package/dist/services/selection.d.ts +46 -0
  47. package/dist/services/settings.d.ts +44 -0
  48. package/dist/services/theme.d.ts +15 -0
  49. package/dist/services/validation/types.d.ts +98 -0
  50. package/dist/services/workspace-files.d.ts +84 -0
  51. package/dist/services/workspace-resources.d.ts +41 -0
  52. package/dist/services/workspace-save.d.ts +18 -0
  53. package/dist/tooling.d.ts +28 -0
  54. package/dist/tooling.js +35 -0
  55. package/dist/ui/contributions/decorations.d.ts +55 -0
  56. package/dist/ui/contributions/editors.d.ts +58 -0
  57. package/dist/ui/contributions/plugin-settings.d.ts +54 -0
  58. package/dist/ui/keyboard/keybinding-rules.d.ts +48 -0
  59. package/dist/ui/keyboard/keybindings.d.ts +55 -0
  60. package/dist/ui/keyboard/keymap.d.ts +81 -0
  61. package/dist/ui/keyboard/scope.d.ts +38 -0
  62. package/dist/ui/menu/editor-menu.d.ts +25 -0
  63. package/dist/ui/menu/menu.d.ts +228 -0
  64. package/dist/ui/menu/palette.d.ts +36 -0
  65. package/dist/ui/menu/resource-menu.d.ts +45 -0
  66. package/dist/ui/slots.d.ts +113 -0
  67. package/dist/ui/useActiveDocument.d.ts +5 -0
  68. package/dist/ui/useLocale.d.ts +9 -0
  69. package/dist/ui/useTranslate.d.ts +4 -0
  70. package/dist/workspace/document.d.ts +42 -0
  71. package/dist/workspace/model/editor-view-states.d.ts +29 -0
  72. package/dist/workspace/model/model-document.d.ts +86 -0
  73. package/dist/workspace/model/provider.d.ts +140 -0
  74. package/dist/workspace/model/text-editor-focus.d.ts +22 -0
  75. package/dist/workspace/resource-names.d.ts +92 -0
  76. package/dist/workspace/write-options.d.ts +36 -0
  77. package/package.json +69 -0
@@ -0,0 +1,18 @@
1
+ import { Capability } from '../primitives/capability.js';
2
+ import { ResourceId } from '../primitives/resource.js';
3
+ export interface WorkspaceSaveService {
4
+ /**
5
+ * Сохраняет перечисленные ресурсы в источник проекта.
6
+ *
7
+ * `false` — проекта нет или хотя бы один ресурс не сохранился. Причины по одному ресурсу
8
+ * наружу не отдаются: показать их некому, кроме рабочей области, а она о них знает и так.
9
+ */
10
+ save(ids: readonly ResourceId[]): Promise<boolean>;
11
+ }
12
+ /**
13
+ * Возможность «сохранить в источник». Привилегированная: оболочка сверяет право
14
+ * `workspace.save` перед тем, как отдать её плагину.
15
+ */
16
+ export declare const WorkspaceSaveCapability: Capability<WorkspaceSaveService>;
17
+ /** Токен службы — ТОТ ЖЕ объект: возможность расширяет токен, второго реестра нет. */
18
+ export declare const WorkspaceSaveServiceToken: Capability<WorkspaceSaveService>;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Вход для ИНСТРУМЕНТОВ автора плагина: сборщика, валидатора, упаковщика.
3
+ *
4
+ * Не для кода плагина — тому хватает `.`. Здесь то, что нужно тем, кто плагин проверяет
5
+ * и собирает ДО оболочки, и что обязано совпадать с оболочкой буквально:
6
+ *
7
+ * - **разбор манифеста** — тот же, которым оболочка решает, грузить ли плагин;
8
+ * - **список модулей рантайма** — те же спецификаторы, которые оболочка подставляет своими
9
+ * объектами и которые сборка обязана держать внешними;
10
+ * - **нормализация пути модуля** — та же, которой линковщик ищет точку входа;
11
+ * - **раскладка каталога и разбор словаря** — те же потолок файлов, расширения кода и форма
12
+ * `locales/*.json`, по которым загрузчик оболочки отказывает плагину;
13
+ * - **узнавание плагина в экспортах** точки входа — то же, что у загрузчика (`not-a-plugin`).
14
+ *
15
+ * В отличие от `./internal`, это контракт: он версионируется вместе с пакетом, потому что
16
+ * инструмент, собранный против одной версии, проверяет плагины для оболочек этой версии.
17
+ *
18
+ * @module @reformer/builder-plugin-api/tooling
19
+ */
20
+ export { isPluginCodeFile, PLUGIN_CATALOG_DIR, PLUGIN_CODE_EXTENSIONS, PLUGIN_FILE_LIMIT, PLUGIN_SKIPPED_DIRS, } from './plugin/layout.js';
21
+ export { parsePluginManifest, parsePluginManifestValue, parsePluginSourceManifest, } from './plugin/manifest-parser.js';
22
+ export { parseMessagesBundle } from './plugin/messages-bundle.js';
23
+ export { isPluginPermission, PLUGIN_PERMISSIONS, type PluginPermission, } from './plugin/permissions.js';
24
+ export { pluginFromExports } from './plugin/plugin-exports.js';
25
+ export { BUILDER_API_VERSION, PLUGIN_MANIFEST_FILE, type BuiltinDelivery, type BuiltinPluginManifest, type DeclaredKeybinding, type ManifestOf, type ManifestParseResult, type PluginContributes, type PluginManifest, type PluginManifestBase, type PluginProblem, type PluginProblemCode, type PluginRequirements, type PluginSource, type PluginSourceManifest, type PluginStyles, type ProjectPluginManifest, } from './plugin/manifest.js';
26
+ export { PLUGIN_RUNTIME_MODULES } from './plugin/runtime-modules.js';
27
+ export { isBundledPluginModule, packageNameOf, PLUGIN_BUNDLED_PACKAGES, } from './plugin/bundled-modules.js';
28
+ export { normalizeModulePath } from './primitives/module-path.js';
@@ -0,0 +1,35 @@
1
+ import { B as t, P as I, a as P, b as l, h as u, j as _, c as N, i as E, n as L, f, p, d as S, e as U, g as c } from "./runtime-modules-CiUDFMIn.js";
2
+ import { P as M, i as g } from "./permissions-BWlsUVPs.js";
3
+ const a = Object.freeze([
4
+ "@reformer/builder-toolkit",
5
+ "@reformer/builder-stack-reformer",
6
+ "@reformer/builder-stack-plain"
7
+ ]);
8
+ function r(e) {
9
+ const s = e.split("/");
10
+ return e.startsWith("@") ? s.slice(0, 2).join("/") : s[0];
11
+ }
12
+ function i(e) {
13
+ return a.includes(r(e));
14
+ }
15
+ export {
16
+ t as BUILDER_API_VERSION,
17
+ a as PLUGIN_BUNDLED_PACKAGES,
18
+ I as PLUGIN_CATALOG_DIR,
19
+ P as PLUGIN_CODE_EXTENSIONS,
20
+ l as PLUGIN_FILE_LIMIT,
21
+ u as PLUGIN_MANIFEST_FILE,
22
+ M as PLUGIN_PERMISSIONS,
23
+ _ as PLUGIN_RUNTIME_MODULES,
24
+ N as PLUGIN_SKIPPED_DIRS,
25
+ i as isBundledPluginModule,
26
+ E as isPluginCodeFile,
27
+ g as isPluginPermission,
28
+ L as normalizeModulePath,
29
+ r as packageNameOf,
30
+ f as parseMessagesBundle,
31
+ p as parsePluginManifest,
32
+ S as parsePluginManifestValue,
33
+ U as parsePluginSourceManifest,
34
+ c as pluginFromExports
35
+ };
@@ -0,0 +1,55 @@
1
+ import { ComponentType } from 'react';
2
+ import { Disposable } from '../../primitives/disposable.js';
3
+ import { ResourceRef } from '../../primitives/resource.js';
4
+ import { EditorProbe } from '../../workspace/model/provider.js';
5
+ /**
6
+ * Тон пометки. Предметного смысла у Host нет: он переносит значение от вклада к отрисовке.
7
+ *
8
+ * Порядок значений несущий: при слиянии пометок нескольких вкладов побеждает более тревожный тон.
9
+ */
10
+ export type DecorationTone = 'default' | 'accent' | 'warning' | 'danger';
11
+ /** Пометка на ресурсе: короткий значок и подсказка, а не текст произвольной длины. */
12
+ export interface Decoration {
13
+ /** Короткая пометка: «S», «3», «●». Строка, а не число: это и счётчик, и буква вида. */
14
+ readonly badge?: string;
15
+ readonly icon?: ComponentType;
16
+ /** Ключ i18n подсказки — в пространстве имён внёсшего плагина, как у заголовка панели. */
17
+ readonly tooltipKey?: string;
18
+ /**
19
+ * Параметры подсказки: подставляются в момент показа, вместе с ключом.
20
+ *
21
+ * Без них пометка «3» умеет сказать только «есть проблемы», а «три ошибки» — уже нет:
22
+ * счётчик пришлось бы вклеивать в строку самим, то есть переводить у себя. Ключ и его
23
+ * параметры путешествуют парой и при слиянии не разлучаются.
24
+ */
25
+ readonly tooltipParams?: Record<string, unknown>;
26
+ readonly tone?: DecorationTone;
27
+ }
28
+ export interface ResourceDecorationContribution {
29
+ /** Уникален среди декораций; попадает в диагностику и служит React-ключом. */
30
+ readonly id: string;
31
+ /**
32
+ * Пометка для ресурса или `null`, если этому вкладу сказать нечего.
33
+ *
34
+ * Синхронна и обязана быть дешёвой: её зовут для каждой строки видимого уровня дерева
35
+ * на каждую перерисовку. Содержимое через пробу читается лениво — см. шапку модуля.
36
+ */
37
+ decorate(ref: ResourceRef, probe: EditorProbe): Decoration | null;
38
+ /**
39
+ * «Мой ответ изменился» — повод спросить {@link decorate} заново.
40
+ *
41
+ * Необязателен, и большинству вкладов не нужен: пометка «это схема формы» зависит только
42
+ * от `ref`, а `ref` меняется вместе со строкой дерева. Нужен тем, чей ответ зависит от
43
+ * состояния СНАРУЖИ дерева — диагностике прежде всего: находки приходят от валидатора,
44
+ * дерево про них ничего не знает и перерисовываться ему не с чего.
45
+ *
46
+ * Пока этого крючка не было, единственным способом обновить пометку было снять вклад
47
+ * и внести заново (так и записано в шапке модуля — «объявить результат вторым проходом»).
48
+ * Для содержимого файла это годится: оно меняется редко. Для диагностики — нет: она
49
+ * меняется на каждой правке, и перерегистрация вклада означала бы две рассылки реестра
50
+ * на каждый набранный символ, а заодно потерю порядка вкладов.
51
+ */
52
+ onDidChange?(cb: () => void): Disposable;
53
+ }
54
+ /** Точка расширения декораций. Заполняется только плагинами. */
55
+ export declare const ResourceDecorationPoint: import('../../internal.js').ExtensionPoint<ResourceDecorationContribution>;
@@ -0,0 +1,58 @@
1
+ import { ComponentType } from 'react';
2
+ import { CommandContribution } from '../../primitives/command.js';
3
+ import { ResourceId, ResourceRef } from '../../primitives/resource.js';
4
+ import { EditorProbe } from '../../workspace/model/provider.js';
5
+ import { PanelContribution } from '../slots.js';
6
+ /**
7
+ * Редактор — вклад, отвечающий на два вопроса: берётся ли он за ресурс и чем его рисовать.
8
+ *
9
+ * Панели и команды перечислены ЗДЕСЬ, а не вносятся отдельно, потому что у них другой срок
10
+ * жизни: инспектор без своего редактора бессмыслен. Вносит их тот, кто редактор подключает;
11
+ * видимостью по-прежнему управляет `when` (см. `../slots`), а не перерегистрация.
12
+ */
13
+ export interface EditorContribution {
14
+ /** Уникален среди редакторов; служит ключом состояния вида и адресом в диагностике. */
15
+ readonly id: string;
16
+ /**
17
+ * Приоритет или отказ. Больше — предпочтительнее; `false` — «это не ко мне».
18
+ *
19
+ * Обязана быть синхронной, чистой и быстрой: её зовут для каждого кандидата на каждое
20
+ * открытие. Содержимое берётся из пробы, а не читается самостоятельно.
21
+ */
22
+ canOpen(ref: ResourceRef, probe: EditorProbe): number | false;
23
+ /**
24
+ * Ключ заголовка для выбора «открыть с помощью» — необязателен.
25
+ *
26
+ * Разрешается в пространстве имён ВНЁСШЕГО плагина, как у панели (см. `panelTitle`
27
+ * в `./panels`): словарь всегда чей-то, и `editor.label` двух разных плагинов — две
28
+ * разные строки. Редактор, который себя не назвал, показывается своим `id`: это честно
29
+ * (имя вклада — то, что про него известно) и не требует правки чужого плагина ради
30
+ * появления пункта в меню.
31
+ */
32
+ readonly titleKey?: string;
33
+ readonly Body: ComponentType<{
34
+ documentId: ResourceId;
35
+ }>;
36
+ readonly contributes?: {
37
+ readonly panels?: readonly PanelContribution[];
38
+ readonly commands?: readonly CommandContribution[];
39
+ };
40
+ /**
41
+ * Состояние вида: прокрутка, свёрнутые ветки, позиция каретки — и ничего больше.
42
+ *
43
+ * Обе операции адресуются документом, а не редактором: один редактор обслуживает много
44
+ * вкладок, и «восстанови прокрутку» без адреса означало бы одну прокрутку на всех.
45
+ */
46
+ readonly viewState?: {
47
+ capture(id: ResourceId): unknown;
48
+ restore(id: ResourceId, state: unknown): void;
49
+ };
50
+ }
51
+ /**
52
+ * Точка расширения редакторов.
53
+ *
54
+ * Заполняется только через `PluginContext.extensions`: у корневого реестра метода `contribute`
55
+ * нет вовсе, поэтому «редактор, внесённый самим Host» невыразим. Пока вкладов нет, центр
56
+ * оболочки показывает пустое состояние — это нормальное состояние Э5, а не незавершённость.
57
+ */
58
+ export declare const EditorPoint: import('../../internal.js').ExtensionPoint<EditorContribution>;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Точка расширения «настройки плагина каталога».
3
+ *
4
+ * Плагин объявляет, что у него есть настраиваемое, и отдаёт ОПИСАНИЕ формы. Рисует эту форму
5
+ * раздел «Плагины» окна настроек, значения кладутся в службу настроек под ключом плагина
6
+ * (`platform/services/plugin-settings`).
7
+ *
8
+ * ## Почему полезная нагрузка непрозрачна для платформы
9
+ *
10
+ * `schema` объявлена как `unknown`, хотя на деле это `JsonFormSchema`. Сузить тип здесь значило
11
+ * бы, что ПЛАТФОРМА узнала про формы, — а она не знает предметных сущностей вовсе (правило
12
+ * слоёв, `eslint.config.js`). Сужение делает тот, кому предметное знание разрешено:
13
+ * композиция, когда собирает тело раздела. Платформа же переносит вклад, не заглядывая внутрь,
14
+ * ровно как переносит `Body` раздела настроек.
15
+ *
16
+ * ## Точка КАТАЛОЖНАЯ, и это сказано в имени
17
+ *
18
+ * Форма рисуется только в строке плагина каталога — у встроенных плагинов строки нет, и вклад
19
+ * от встроенного никто не покажет. Имя точки и этот абзац существуют, чтобы автор встроенного
20
+ * плагина узнал об этом отсюда, а не из пустого места в интерфейсе. Понадобятся настройки
21
+ * встроенному — это отдельное решение: у него есть свой раздел или своё место в чужом.
22
+ *
23
+ * @module @reformer/builder-plugin-api/ui/contributions/plugin-settings
24
+ */
25
+ /**
26
+ * Что плагин рассказывает о своих настройках.
27
+ *
28
+ * Умолчаний здесь НЕТ намеренно: их объявляет сам плагин через `settings.registerDefault`
29
+ * в `activate` — тем же вызовом, каким это делает плагин китов. Второе поле с умолчаниями
30
+ * во вкладе дало бы два источника одной сущности и правило приоритета между ними.
31
+ */
32
+ export interface CatalogPluginSettingsContribution {
33
+ /**
34
+ * Чьи это настройки. Обязателен: точка глобальная, а строка в списке — конкретного плагина.
35
+ *
36
+ * Проверяется на совпадение с `pluginId` вклада (его проставляет реестр, а не вносящий):
37
+ * плагин не настраивает соседа.
38
+ */
39
+ readonly pluginId: string;
40
+ /**
41
+ * Описание формы — `JsonFormSchema` пакета `@reformer/renderer-json`.
42
+ *
43
+ * Тип намеренно широкий: см. шапку модуля. Разбирает и проверяет схему композиция,
44
+ * непригодная показывается человеку как отказ, а не роняет раздел.
45
+ */
46
+ readonly schema: unknown;
47
+ /**
48
+ * Заголовок формы в карточке. Ключ СЛОВАРЯ ПЛАГИНА либо готовая строка — плагин решает сам,
49
+ * потому что его словарь знает только он.
50
+ */
51
+ readonly titleKey?: string;
52
+ }
53
+ /** Точка расширения. Вносят плагины каталога, читает раздел «Плагины» окна настроек. */
54
+ export declare const CatalogPluginSettingsPoint: import('../../internal.js').ExtensionPoint<CatalogPluginSettingsContribution>;
@@ -0,0 +1,48 @@
1
+ import { WhenExpr } from '../../primitives/when-expr.js';
2
+ /**
3
+ * Откуда правило пришло. Порядок в {@link LAYER_RANK} и есть порядок старшинства.
4
+ *
5
+ * Плагин каталога стоит выше встроенного намеренно: встроенный набор — это то, что мы
6
+ * решили за человека, а плагин, положенный в проект, — то, что он решил сам.
7
+ */
8
+ export type KeybindingLayer = 'host' | 'builtin-plugin' | 'catalog-plugin' | 'user';
9
+ /** Привязка сочетания к команде. */
10
+ export interface KeybindingRule {
11
+ /** Устойчивый адрес правила: его показывает редактор клавиш и называет диагностика. */
12
+ readonly id: string;
13
+ /**
14
+ * Ступени аккорда в каноническом написании. Одна ступень — обычное сочетание.
15
+ *
16
+ * Массив, а не строка, с самого начала: аккорд появляется позже, но переписывать под него
17
+ * указатель, редактор и диагностику дороже, чем сразу назвать вещь тем, что она есть.
18
+ */
19
+ readonly chord: readonly string[];
20
+ readonly commandId: string;
21
+ /** Аргументы вызова. Есть у правил из манифеста и раскладки, у команд их не бывает. */
22
+ readonly args?: unknown;
23
+ readonly when: WhenExpr;
24
+ readonly layer: KeybindingLayer;
25
+ /** Кто принёс правило: идентификатор плагина либо `undefined` у оболочки и человека. */
26
+ readonly pluginId?: string;
27
+ readonly allowInEditable: boolean;
28
+ /** Порядковый номер появления. Последний критерий сортировки. */
29
+ readonly seq: number;
30
+ }
31
+ /** Указатель по первой ступени сочетания. */
32
+ export interface KeybindingIndex {
33
+ /** Правила, у которых первая ступень равна `binding`. Уже отсортированы. */
34
+ rulesFor(binding: string): readonly KeybindingRule[];
35
+ /** Является ли сочетание НАЧАЛОМ аккорда — признак входа в режим ожидания. */
36
+ isChordPrefix(binding: string): boolean;
37
+ /** Продолжения после уже нажатых ступеней. */
38
+ rulesAfter(prefix: readonly string[], binding: string): readonly KeybindingRule[];
39
+ /** Все действующие правила — редактору клавиш и справке. */
40
+ all(): readonly KeybindingRule[];
41
+ }
42
+ /** Пара правил на одном сочетании, которую разрешить нечем. */
43
+ export interface KeybindingConflict {
44
+ readonly chord: readonly string[];
45
+ /** Не меньше двух, в порядке указателя: первый и есть победитель. */
46
+ readonly rules: readonly KeybindingRule[];
47
+ readonly winner: string;
48
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Сочетания клавиш как текст: модификатор платформы и подпись для человека.
3
+ *
4
+ * Здесь то, что плагин показывает: подпись сочетания обязана совпадать с тем, что человек
5
+ * нажимает, поэтому форматирует её тот же код, что разбирает. Диспетчер нажатий и его
6
+ * установка в документ живут в оболочке билдера.
7
+ *
8
+ * @module @reformer/builder-plugin-api/ui/keyboard/keybindings
9
+ */
10
+ /**
11
+ * Модификатор, в который разворачивается `mod` на этой платформе.
12
+ *
13
+ * `meta` — Cmd на macOS, `ctrl` на остальных. Значение вычисляется один раз при установке
14
+ * обработчика: платформа в течение сессии не меняется, а вычислять её на каждое нажатие
15
+ * означало бы читать `navigator` десятки раз в секунду при обычном наборе текста.
16
+ */
17
+ export type PlatformModifier = 'ctrl' | 'meta';
18
+ /**
19
+ * Определяет платформенный модификатор.
20
+ *
21
+ * Строка принимается параметром, а не читается изнутри безусловно: обе ветки — часть
22
+ * контракта, и проверять их надо обе, а `node` про macOS ничего не знает.
23
+ */
24
+ export declare function detectPlatformModifier(platform?: string): PlatformModifier;
25
+ /**
26
+ * Разворачивает `mod` в платформенный модификатор, сохраняя канонический порядок.
27
+ *
28
+ * Работает по каноническому виду, поэтому обходится без повторного разбора: `mod` в порядке
29
+ * модификаторов стоит первым (см. `MODIFIER_ORDER` в `primitives/command`), а за ним идут
30
+ * `ctrl`, `meta`, `alt`, `shift`. Вставка сводится к проверке начала строки.
31
+ *
32
+ * Совпадение с уже указанным модификатором не является ошибкой: `mod+ctrl+k` на Windows —
33
+ * это `ctrl+k`, потому что `mod` там и есть `ctrl`. Повторно нормализовать результат нельзя —
34
+ * `normalizeKeybinding` отвергает повторённый модификатор.
35
+ *
36
+ * @throws {CommandError} `invalid-keybinding`, если исходное сочетание не разбирается.
37
+ */
38
+ export declare function resolvePlatformKeybinding(keybinding: string, modifier: PlatformModifier): string;
39
+ /**
40
+ * Подпись сочетания для интерфейса: `mod+shift+p` → `Ctrl+Shift+P`.
41
+ *
42
+ * Не переводится и через словарь не идёт: `Ctrl` и `Shift` — это надписи на клавишах,
43
+ * а не текст интерфейса, и переводить их значило бы называть клавишу не тем, что на ней
44
+ * написано. Локализуется тут разве что порядок, а он у сочетаний один.
45
+ *
46
+ * Неразбираемое сочетание возвращается как есть: подпись — не то место, где стоит падать.
47
+ */
48
+ export declare function formatKeybinding(keybinding: string, modifier: PlatformModifier): string;
49
+ /**
50
+ * Подпись аккорда: `['mod+k', 'mod+s']` → `Ctrl+K Ctrl+S`.
51
+ *
52
+ * Ступени разделены, а не слиты: это два НАЖАТИЯ, и подпись `Ctrl+K+Ctrl+S` описывала бы
53
+ * несуществующее сочетание из четырёх одновременно зажатых клавиш.
54
+ */
55
+ export declare function formatChord(chord: readonly string[], modifier: PlatformModifier): string;
@@ -0,0 +1,81 @@
1
+ import { Disposable } from '../../primitives/disposable.js';
2
+ import { KeybindingIndex, KeybindingLayer, KeybindingRule, KeybindingConflict } from './keybinding-rules.js';
3
+ /**
4
+ * Запись раскладки, как её пишет человек.
5
+ *
6
+ * Строками, а не разобранными структурами: это содержимое файла настроек, и до разбора
7
+ * у него нет ничего, кроме текста.
8
+ */
9
+ export interface UserKeybinding {
10
+ /** Сочетание как написал человек; нормализуется при сборке. */
11
+ readonly key: string;
12
+ /**
13
+ * Идентификатор команды. Ведущий минус означает СНЯТИЕ: `-files.delete` убирает привязку
14
+ * этой команды к этой клавише, одинокий `-` освобождает клавишу целиком.
15
+ *
16
+ * Форма взята у VS Code, и она правильная: снятие адресуется парой «клавиша + команда»,
17
+ * потому что снятие по одной клавише убило бы и те привязки, которых человек не видел.
18
+ */
19
+ readonly command: string;
20
+ readonly when?: string;
21
+ readonly args?: unknown;
22
+ readonly allowInEditable?: boolean;
23
+ }
24
+ /** Почему запись раскладки не применилась. */
25
+ export type KeymapIssueKind = 'invalid-key' | 'invalid-when' | 'not-an-object';
26
+ export interface KeymapIssue {
27
+ readonly kind: KeymapIssueKind;
28
+ /** Номер записи в списке — по нему человек находит строку в своём файле. */
29
+ readonly at: number;
30
+ readonly message: string;
31
+ }
32
+ export interface KeymapService {
33
+ /** Действующий указатель. Пересобирается лениво — см. шапку модуля. */
34
+ index(): KeybindingIndex;
35
+ /** Записи раскладки человека — как они лежат в настройках. */
36
+ userRules(): readonly UserKeybinding[];
37
+ /**
38
+ * Записывает раскладку человека целиком.
39
+ *
40
+ * Целиком, а не по одной записи: редактор клавиш правит список, и частичная запись
41
+ * потребовала бы от него знать, какая строка какой записи соответствует, — то есть
42
+ * держать вторую модель того же списка.
43
+ */
44
+ setUserRules(rules: readonly UserKeybinding[]): Promise<void>;
45
+ /** Записи, которые не применились. Считается вместе с указателем. */
46
+ issues(): readonly KeymapIssue[];
47
+ /** Уведомление о смене раскладки: подписаны меню, справка и редактор клавиш. */
48
+ onDidChange(cb: () => void): Disposable;
49
+ /**
50
+ * Правила внешнего источника. Замещает набор ЭТОГО источника целиком.
51
+ *
52
+ * Форма и довод те же, что у публикации диагностик: источник приносит «всё, что у меня
53
+ * есть сейчас», и его прошлое исчезает без отдельного вызова. Иначе снятие пришлось бы
54
+ * делать вторым вызовом, и первый же забытый оставил бы клавишу от плагина, которого нет.
55
+ */
56
+ registerRules(source: string, layer: KeybindingLayer, rules: readonly ExternalKeybinding[]): Disposable;
57
+ /** Неразрешимые пары. Считается лениво, вместе с указателем. */
58
+ conflicts(): readonly KeybindingConflict[];
59
+ }
60
+ /**
61
+ * Правило от внешнего источника — манифеста плагина или раскладки человека.
62
+ *
63
+ * Сочетание и условие здесь СТРОКАМИ: источник — это файл, и до разбора у него нет ничего,
64
+ * кроме текста. Разбор делает служба, а испорченную запись отбрасывает поштучно.
65
+ */
66
+ export interface ExternalKeybinding {
67
+ readonly chord: readonly string[];
68
+ readonly commandId: string;
69
+ readonly when: KeybindingRule['when'];
70
+ readonly args?: unknown;
71
+ readonly allowInEditable?: boolean;
72
+ readonly pluginId?: string;
73
+ }
74
+ export declare const KeymapServiceToken: import('../../index.js').ServiceToken<KeymapService>;
75
+ /**
76
+ * Каким сочетанием вызывается команда сейчас. `undefined` — ни одним.
77
+ *
78
+ * Берётся ПЕРВОЕ правило по порядку указателя, то есть то же, которое выиграет у диспетчера.
79
+ * Иначе подсказка называла бы одно сочетание, а срабатывало бы другое.
80
+ */
81
+ export declare function chordOfCommand(index: KeybindingIndex, commandId: string): readonly string[] | undefined;
@@ -0,0 +1,38 @@
1
+ import { Disposable } from '../../primitives/disposable.js';
2
+ /**
3
+ * Имя области: `palette`, `dialog`. Непрозрачная строка, как `activeResourceKind`.
4
+ *
5
+ * Платформа её не интерпретирует и списка не держит: окна заводят плагины, и перечислить
6
+ * их Host не может — ровно как пути подменю.
7
+ */
8
+ export type ScopeId = string;
9
+ export interface ScopeStack {
10
+ /** Верх стека или `null`, если окон нет. Это значение ключа `scope`. */
11
+ top(): ScopeId | null;
12
+ /**
13
+ * Весь стек снизу вверх. Значение ключа `scopes` — правая часть оператора `in`,
14
+ * которым пишут «где-то внутри диалога, пусть и не в самом верхнем».
15
+ *
16
+ * Ссылка стабильна между изменениями: её читает снимок контекстных ключей.
17
+ */
18
+ all(): readonly ScopeId[];
19
+ /**
20
+ * Кладёт область. `dispose()` снимает ИМЕННО эту запись, где бы она ни оказалась в стеке.
21
+ *
22
+ * Снятие по идентичности записи, а не «снять верхнюю»: два вложенных диалога, оба
23
+ * объявившие `dialog`, дают `['dialog', 'dialog']`, и закрытие внутреннего не должно
24
+ * снимать внешний. Тот же приём, что у реестра команд, где `dispose` сверяет значение,
25
+ * а не только ключ.
26
+ */
27
+ push(scope: ScopeId): Disposable;
28
+ subscribe(listener: () => void): Disposable;
29
+ }
30
+ export declare const ScopeStackServiceToken: import('../../index.js').ServiceToken<ScopeStack>;
31
+ /**
32
+ * Область модального окна — общая для всех диалогов оболочки.
33
+ *
34
+ * Одна на всех, а не своя у каждого: условие «пока открыт какой-нибудь диалог» пишется
35
+ * человеком чаще, чем «пока открыт именно этот», а вложенные окна различает уже стек.
36
+ * Диалогу, которому нужна своя клавиша, ничто не мешает положить рядом собственную область.
37
+ */
38
+ export declare const DIALOG_SCOPE = "dialog";
@@ -0,0 +1,25 @@
1
+ import { ResourceId, ResourceRef } from '../../primitives/resource.js';
2
+ import { WhenContext } from '../../primitives/when-context.js';
3
+ import { ContextMenuId, MenuTarget } from './menu.js';
4
+ /** Адрес ряда действий над активным документом. */
5
+ export declare const EDITOR_TITLE_MENU: ContextMenuId;
6
+ /** Документ, к которому относятся кнопки ряда. */
7
+ export interface EditorMenuTarget {
8
+ /** Адрес активного документа. */
9
+ readonly documentId: ResourceId;
10
+ /** Ссылка на ресурс: по ней вклад решает, его ли это документ. */
11
+ readonly ref: ResourceRef;
12
+ /** Идентификатор редактора, который сейчас рисует документ; `null` — ещё не выбран. */
13
+ readonly editorId: string | null;
14
+ }
15
+ /**
16
+ * Сужает непрозрачную цель до цели ряда действий; `null` — кнопку рисуют не там.
17
+ *
18
+ * Проверка структурная, а не `instanceof`: цель проходит через модель меню как `unknown`,
19
+ * и вклад обязан уметь получить `null`, если его пункт по ошибке внесли в чужое меню.
20
+ */
21
+ export declare function asEditorTarget(target: MenuTarget): EditorMenuTarget | null;
22
+ /** Предикат видимости кнопки по открытому документу. */
23
+ export declare function whenEditor(predicate: (target: EditorMenuTarget, ctx: WhenContext) => boolean): (ctx: WhenContext, target: MenuTarget) => boolean;
24
+ /** Аргументы команды по открытому документу; для чужой цели их нет вовсе. */
25
+ export declare function argsOfEditor<T>(compute: (target: EditorMenuTarget) => T): (target: MenuTarget) => T | undefined;