@oscharko-dev/keiko-contracts 0.2.8 → 0.2.9

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 (106) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/bff-wire.d.ts +33 -0
  3. package/dist/bff-wire.d.ts.map +1 -1
  4. package/dist/command-runner.d.ts +81 -0
  5. package/dist/command-runner.d.ts.map +1 -0
  6. package/dist/command-runner.js +209 -0
  7. package/dist/container-runtime.d.ts +125 -0
  8. package/dist/container-runtime.d.ts.map +1 -0
  9. package/dist/container-runtime.js +287 -0
  10. package/dist/context-engineering.js +2 -2
  11. package/dist/discussion-intelligence.d.ts +70 -0
  12. package/dist/discussion-intelligence.d.ts.map +1 -0
  13. package/dist/discussion-intelligence.js +440 -0
  14. package/dist/editor-agent-governance.d.ts +65 -0
  15. package/dist/editor-agent-governance.d.ts.map +1 -0
  16. package/dist/editor-agent-governance.js +180 -0
  17. package/dist/editor-agent.d.ts +29 -1
  18. package/dist/editor-agent.d.ts.map +1 -1
  19. package/dist/editor-agent.js +183 -6
  20. package/dist/editor-builtin-capabilities.d.ts +13 -0
  21. package/dist/editor-builtin-capabilities.d.ts.map +1 -0
  22. package/dist/editor-builtin-capabilities.js +135 -0
  23. package/dist/editor-language-mode-map.d.ts +11 -0
  24. package/dist/editor-language-mode-map.d.ts.map +1 -0
  25. package/dist/editor-language-mode-map.js +89 -0
  26. package/dist/editor-layout.d.ts +37 -0
  27. package/dist/editor-layout.d.ts.map +1 -1
  28. package/dist/editor-layout.js +125 -8
  29. package/dist/editor-workspace-path.d.ts +20 -0
  30. package/dist/editor-workspace-path.d.ts.map +1 -0
  31. package/dist/editor-workspace-path.js +133 -0
  32. package/dist/evidence.d.ts +3 -1
  33. package/dist/evidence.d.ts.map +1 -1
  34. package/dist/gateway.d.ts +72 -3
  35. package/dist/gateway.d.ts.map +1 -1
  36. package/dist/gateway.js +73 -3
  37. package/dist/git-commit-intent.d.ts +28 -0
  38. package/dist/git-commit-intent.d.ts.map +1 -0
  39. package/dist/git-commit-intent.js +155 -0
  40. package/dist/git-commit-policy.d.ts +29 -0
  41. package/dist/git-commit-policy.d.ts.map +1 -0
  42. package/dist/git-commit-policy.js +173 -0
  43. package/dist/git-delivery-action-sheet.d.ts +157 -0
  44. package/dist/git-delivery-action-sheet.d.ts.map +1 -0
  45. package/dist/git-delivery-action-sheet.js +430 -0
  46. package/dist/git-delivery-evidence.d.ts +92 -0
  47. package/dist/git-delivery-evidence.d.ts.map +1 -0
  48. package/dist/git-delivery-evidence.js +272 -0
  49. package/dist/git-delivery-policy.d.ts +40 -0
  50. package/dist/git-delivery-policy.d.ts.map +1 -0
  51. package/dist/git-delivery-policy.js +183 -0
  52. package/dist/git-delivery-provider.d.ts +53 -0
  53. package/dist/git-delivery-provider.d.ts.map +1 -0
  54. package/dist/git-delivery-provider.js +96 -0
  55. package/dist/git-delivery.d.ts +202 -0
  56. package/dist/git-delivery.d.ts.map +1 -0
  57. package/dist/git-delivery.js +410 -0
  58. package/dist/git-history.d.ts +27 -0
  59. package/dist/git-history.d.ts.map +1 -0
  60. package/dist/git-history.js +73 -0
  61. package/dist/git-merge.d.ts +67 -0
  62. package/dist/git-merge.d.ts.map +1 -0
  63. package/dist/git-merge.js +323 -0
  64. package/dist/git-pull-request.d.ts +112 -0
  65. package/dist/git-pull-request.d.ts.map +1 -0
  66. package/dist/git-pull-request.js +351 -0
  67. package/dist/git-repository-agent.d.ts +46 -0
  68. package/dist/git-repository-agent.d.ts.map +1 -0
  69. package/dist/git-repository-agent.js +198 -0
  70. package/dist/git-repository-summary.d.ts +54 -0
  71. package/dist/git-repository-summary.d.ts.map +1 -0
  72. package/dist/git-repository-summary.js +105 -0
  73. package/dist/git-repository.d.ts +60 -0
  74. package/dist/git-repository.d.ts.map +1 -0
  75. package/dist/git-repository.js +106 -0
  76. package/dist/git-sync.d.ts +49 -0
  77. package/dist/git-sync.d.ts.map +1 -0
  78. package/dist/git-sync.js +110 -0
  79. package/dist/index.d.ts +63 -9
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +33 -6
  82. package/dist/lsp-process.d.ts +38 -0
  83. package/dist/lsp-process.d.ts.map +1 -0
  84. package/dist/lsp-process.js +103 -0
  85. package/dist/runtime-capabilities.d.ts +44 -0
  86. package/dist/runtime-capabilities.d.ts.map +1 -0
  87. package/dist/runtime-capabilities.js +141 -0
  88. package/dist/task-workspace.d.ts +302 -0
  89. package/dist/task-workspace.d.ts.map +1 -0
  90. package/dist/task-workspace.js +1236 -0
  91. package/dist/voice-action-intent.d.ts +86 -0
  92. package/dist/voice-action-intent.d.ts.map +1 -0
  93. package/dist/voice-action-intent.js +387 -0
  94. package/dist/voice-playback.d.ts +50 -0
  95. package/dist/voice-playback.d.ts.map +1 -0
  96. package/dist/voice-playback.js +240 -0
  97. package/dist/voice-protocol.d.ts +165 -0
  98. package/dist/voice-protocol.d.ts.map +1 -0
  99. package/dist/voice-protocol.js +312 -0
  100. package/dist/voice-transcript.d.ts +57 -0
  101. package/dist/voice-transcript.d.ts.map +1 -0
  102. package/dist/voice-transcript.js +221 -0
  103. package/package.json +5 -1
  104. package/dist/conversation-budget.d.ts +0 -37
  105. package/dist/conversation-budget.d.ts.map +0 -1
  106. package/dist/conversation-budget.js +0 -97
@@ -0,0 +1,135 @@
1
+ // Editor built-in capability registry (Issue #1380, Epic #1491, ADR-0068 D1/D6). This is the single,
2
+ // authoritative, frozen const table describing, per known source language, the **browser** built-in
3
+ // editor capabilities: whether Monaco tokenises/colours it locally, whether bracket matching applies,
4
+ // and — crucially — *how* its "Format Document" command is served in the browser.
5
+ //
6
+ // It sits BESIDE the source-language mode map (`editor-language-mode-map.ts`, ADR-0067 D1), not inside
7
+ // it (ADR-0068 D2): the mode map is the SERVER-SHARED source-language universe; browser formatting
8
+ // reachability and Monaco syntax/bracket semantics are an editor-tier concern the server has no
9
+ // business consuming. Folding `documentFormatting` into the mode map would invite a future server
10
+ // consumer to misread it as *server* formatting capability — the exact "server can format yaml, the
11
+ // browser can't" confusion this registry exists to end. The two leaves are kept coherent by test
12
+ // (ADR-0068 D6), not by a runtime import coupling.
13
+ //
14
+ // Leaf-package rules (ADR-0019): pure types and frozen const tables plus pure functions only. No
15
+ // filesystem, no clock, no crypto, no randomness, and no imports of other `@oscharko-dev/keiko-*`
16
+ // packages. `plaintext` is deliberately ABSENT (ADR-0067 D5): it is the editor's render fallback for
17
+ // unknown/unsupported buffers, never a known source language, and has no built-in formatter.
18
+ // The canonical table, covering exactly the 15 `EDITOR_LANGUAGE_MODE_IDS` languages. The
19
+ // `documentFormatting` split (ADR-0068 D1): ts/js → the Keiko language-service bridge; the
20
+ // json/css/scss/less/html set → Monaco's bundled workers ("JSON/CSS/HTML … where safe"); everything
21
+ // else → "none" (no reachable in-browser formatter). `as const` makes every entry deeply readonly at
22
+ // compile time; each entry and the outer array are `Object.freeze`d below so the registry is deeply
23
+ // immutable at runtime too — no consumer holding a reference can mutate it.
24
+ const RAW_EDITOR_BUILTIN_CAPABILITIES = [
25
+ {
26
+ languageId: "typescript",
27
+ syntaxHighlighting: true,
28
+ bracketMatching: true,
29
+ documentFormatting: "keiko-language-service",
30
+ },
31
+ {
32
+ languageId: "javascript",
33
+ syntaxHighlighting: true,
34
+ bracketMatching: true,
35
+ documentFormatting: "keiko-language-service",
36
+ },
37
+ {
38
+ languageId: "json",
39
+ syntaxHighlighting: true,
40
+ bracketMatching: true,
41
+ documentFormatting: "monaco-builtin",
42
+ },
43
+ {
44
+ languageId: "css",
45
+ syntaxHighlighting: true,
46
+ bracketMatching: true,
47
+ documentFormatting: "monaco-builtin",
48
+ },
49
+ {
50
+ languageId: "scss",
51
+ syntaxHighlighting: true,
52
+ bracketMatching: true,
53
+ documentFormatting: "monaco-builtin",
54
+ },
55
+ {
56
+ languageId: "less",
57
+ syntaxHighlighting: true,
58
+ bracketMatching: true,
59
+ documentFormatting: "monaco-builtin",
60
+ },
61
+ {
62
+ languageId: "html",
63
+ syntaxHighlighting: true,
64
+ bracketMatching: true,
65
+ documentFormatting: "monaco-builtin",
66
+ },
67
+ {
68
+ languageId: "markdown",
69
+ syntaxHighlighting: true,
70
+ bracketMatching: true,
71
+ documentFormatting: "none",
72
+ },
73
+ {
74
+ languageId: "yaml",
75
+ syntaxHighlighting: true,
76
+ bracketMatching: true,
77
+ documentFormatting: "none",
78
+ },
79
+ {
80
+ languageId: "python",
81
+ syntaxHighlighting: true,
82
+ bracketMatching: true,
83
+ documentFormatting: "none",
84
+ },
85
+ {
86
+ languageId: "java",
87
+ syntaxHighlighting: true,
88
+ bracketMatching: true,
89
+ documentFormatting: "none",
90
+ },
91
+ {
92
+ languageId: "go",
93
+ syntaxHighlighting: true,
94
+ bracketMatching: true,
95
+ documentFormatting: "none",
96
+ },
97
+ {
98
+ languageId: "rust",
99
+ syntaxHighlighting: true,
100
+ bracketMatching: true,
101
+ documentFormatting: "none",
102
+ },
103
+ {
104
+ languageId: "sql",
105
+ syntaxHighlighting: true,
106
+ bracketMatching: true,
107
+ documentFormatting: "none",
108
+ },
109
+ {
110
+ languageId: "shell",
111
+ syntaxHighlighting: true,
112
+ bracketMatching: true,
113
+ documentFormatting: "none",
114
+ },
115
+ ];
116
+ export const EDITOR_BUILTIN_CAPABILITIES = Object.freeze(RAW_EDITOR_BUILTIN_CAPABILITIES.map((capability) => Object.freeze({ ...capability })));
117
+ // languageId -> capability, derived from the table. Frozen. Every registry id appears here.
118
+ export const EDITOR_BUILTIN_CAPABILITY_BY_LANGUAGE = Object.freeze(EDITOR_BUILTIN_CAPABILITIES.reduce((accumulator, capability) => {
119
+ accumulator[capability.languageId] = capability;
120
+ return accumulator;
121
+ }, {}));
122
+ // The built-in capability for a language id, or `null` when the id is not a known source language.
123
+ export function editorBuiltinCapability(languageId) {
124
+ return EDITOR_BUILTIN_CAPABILITY_BY_LANGUAGE[languageId] ?? null;
125
+ }
126
+ // How a language's "Format Document" is served in the browser. Unknown ids are `"none"`: an
127
+ // unsupported buffer has no reachable in-browser formatter, matching the mode map's safe-degrade.
128
+ export function editorBuiltinDocumentFormatting(languageId) {
129
+ return editorBuiltinCapability(languageId)?.documentFormatting ?? "none";
130
+ }
131
+ // True when *some* in-browser document formatter is reachable for the language (Monaco-builtin or the
132
+ // Keiko language-service bridge); false for unknown ids and for the `"none"` set.
133
+ export function isBuiltinFormattingAvailable(languageId) {
134
+ return editorBuiltinDocumentFormatting(languageId) !== "none";
135
+ }
@@ -0,0 +1,11 @@
1
+ export interface EditorLanguageMode {
2
+ readonly languageId: string;
3
+ readonly fileExtensions: readonly string[];
4
+ readonly syntaxHighlighting: boolean;
5
+ }
6
+ export declare const EDITOR_LANGUAGE_MODE_MAP: readonly EditorLanguageMode[];
7
+ export declare const EDITOR_LANGUAGE_MODE_IDS: readonly string[];
8
+ export declare const EDITOR_LANGUAGE_MODE_BY_EXTENSION: Readonly<Record<string, string>>;
9
+ export declare function inferEditorLanguageModeId(pathOrName: string): string | null;
10
+ export declare function isEditorLanguageModeId(value: string): boolean;
11
+ //# sourceMappingURL=editor-language-mode-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editor-language-mode-map.d.ts","sourceRoot":"","sources":["../src/editor-language-mode-map.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC;CACtC;AAkCD,eAAO,MAAM,wBAAwB,EAAE,SAAS,kBAAkB,EAIjE,CAAC;AAGF,eAAO,MAAM,wBAAwB,EAAE,SAAS,MAAM,EAErD,CAAC;AAGF,eAAO,MAAM,iCAAiC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAO9E,CAAC;AAuBF,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM3E;AAMD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE7D"}
@@ -0,0 +1,89 @@
1
+ // Canonical editor language mode map (Issue #1379, Epic #1491, ADR-0067 D1). This is the single,
2
+ // authoritative, frozen const table describing the known SOURCE-LANGUAGE universe shared by the
3
+ // browser editor tier (`keiko-editor`) and the server language service (`keiko-server`). It answers
4
+ // the issue's "language id", "file-extension matching", and "syntax support" dimensions with data,
5
+ // not with code scattered across packages.
6
+ //
7
+ // It enumerates exactly the source languages the product knows. `plaintext` is deliberately EXCLUDED
8
+ // (ADR-0067 D5): it is the editor's render fallback for unknown/unsupported buffers, not a known
9
+ // source language the registry advertises. "known" (AC2) is exactly this id set; everything else
10
+ // degrades to plain text without governed intelligence (AC3) — the two sets are disjoint by design.
11
+ //
12
+ // Leaf-package rules (ADR-0019): pure types and frozen const tables plus pure functions only. No
13
+ // filesystem, no clock, no crypto, no randomness, and no imports of other `@oscharko-dev/keiko-*`
14
+ // packages. The small basename/extension helper is re-implemented locally rather than imported from
15
+ // `keiko-editor` so this module stays a strict, browser-importable leaf.
16
+ // The canonical table. Extensions match `keiko-editor`'s historic `MONACO_LANGUAGE_BY_EXTENSION`
17
+ // keys exactly (including the TSX -> typescript / JSX -> javascript fold) so the editor's inference
18
+ // behaviour is unchanged when it derives from this table (ADR-0067 D2). `as const` makes every entry
19
+ // and extension list deeply readonly at compile time; the entries, their `fileExtensions` arrays, and
20
+ // the outer array are each `Object.freeze`d so the shared universe is deeply immutable at runtime too
21
+ // — no consumer holding a reference to an entry can mutate it.
22
+ const RAW_EDITOR_LANGUAGE_MODES = [
23
+ {
24
+ languageId: "typescript",
25
+ fileExtensions: ["ts", "mts", "cts", "tsx"],
26
+ syntaxHighlighting: true,
27
+ },
28
+ {
29
+ languageId: "javascript",
30
+ fileExtensions: ["js", "mjs", "cjs", "jsx"],
31
+ syntaxHighlighting: true,
32
+ },
33
+ { languageId: "json", fileExtensions: ["json", "jsonc"], syntaxHighlighting: true },
34
+ { languageId: "css", fileExtensions: ["css"], syntaxHighlighting: true },
35
+ { languageId: "scss", fileExtensions: ["scss"], syntaxHighlighting: true },
36
+ { languageId: "less", fileExtensions: ["less"], syntaxHighlighting: true },
37
+ { languageId: "html", fileExtensions: ["html", "htm"], syntaxHighlighting: true },
38
+ { languageId: "markdown", fileExtensions: ["md", "markdown"], syntaxHighlighting: true },
39
+ { languageId: "yaml", fileExtensions: ["yaml", "yml"], syntaxHighlighting: true },
40
+ { languageId: "python", fileExtensions: ["py", "pyi"], syntaxHighlighting: true },
41
+ { languageId: "java", fileExtensions: ["java"], syntaxHighlighting: true },
42
+ { languageId: "go", fileExtensions: ["go"], syntaxHighlighting: true },
43
+ { languageId: "rust", fileExtensions: ["rs"], syntaxHighlighting: true },
44
+ { languageId: "sql", fileExtensions: ["sql"], syntaxHighlighting: true },
45
+ { languageId: "shell", fileExtensions: ["sh", "bash", "zsh"], syntaxHighlighting: true },
46
+ ];
47
+ export const EDITOR_LANGUAGE_MODE_MAP = Object.freeze(RAW_EDITOR_LANGUAGE_MODES.map((mode) => Object.freeze({ ...mode, fileExtensions: Object.freeze([...mode.fileExtensions]) })));
48
+ // The canonical language ids, derived from the map. Frozen.
49
+ export const EDITOR_LANGUAGE_MODE_IDS = Object.freeze(EDITOR_LANGUAGE_MODE_MAP.map((mode) => mode.languageId));
50
+ // Extension -> languageId, derived from the map. Frozen. Every extension in the map appears here.
51
+ export const EDITOR_LANGUAGE_MODE_BY_EXTENSION = Object.freeze(EDITOR_LANGUAGE_MODE_MAP.reduce((accumulator, mode) => {
52
+ for (const extension of mode.fileExtensions) {
53
+ accumulator[extension] = mode.languageId;
54
+ }
55
+ return accumulator;
56
+ }, {}));
57
+ // Final path segment (basename) from a POSIX or Windows path. Pure, allocation-light.
58
+ function modeMapBasename(pathOrName) {
59
+ const segments = pathOrName.split(/[/\\]/u);
60
+ return segments[segments.length - 1] ?? pathOrName;
61
+ }
62
+ // Lower-case extension (without the leading dot) of a file name, or "" when there is none. A leading
63
+ // dot is treated as "no extension" (dotfiles such as `.gitignore` have no source language). Mirrors
64
+ // `keiko-editor`'s `fileExtension` rules exactly so the two tiers agree on extension matching.
65
+ function modeMapExtension(pathOrName) {
66
+ const name = modeMapBasename(pathOrName).trim();
67
+ const dot = name.lastIndexOf(".");
68
+ if (dot <= 0 || dot === name.length - 1) {
69
+ return "";
70
+ }
71
+ return name.slice(dot + 1).toLowerCase();
72
+ }
73
+ // Infer the canonical source-language id for a file name or path, or `null` when the buffer is not a
74
+ // known source language (unknown extension, dotfile, extension-less name, or empty input). `null` is
75
+ // the AC3 safe-degrade signal: such buffers render as plain text with no governed intelligence.
76
+ export function inferEditorLanguageModeId(pathOrName) {
77
+ const extension = modeMapExtension(pathOrName);
78
+ if (extension === "") {
79
+ return null;
80
+ }
81
+ return EDITOR_LANGUAGE_MODE_BY_EXTENSION[extension] ?? null;
82
+ }
83
+ // Runtime membership check: is `value` one of the canonical mode-map language ids? This is a plain
84
+ // boolean predicate, not a narrowing type guard — the mode ids are `string` (the contract names no
85
+ // concrete language literal type), so a `value is …` annotation would narrow nothing and mislead
86
+ // callers.
87
+ export function isEditorLanguageModeId(value) {
88
+ return EDITOR_LANGUAGE_MODE_IDS.includes(value);
89
+ }
@@ -1,3 +1,33 @@
1
+ /**
2
+ * Editor layout state and reducer for multi-tab, split-pane workspaces (Issue #1375, ADR-0064).
3
+ *
4
+ * The reducer is the single authority for layout structure: drag/keyboard intents in the UI are
5
+ * translated into EditorLayoutActions and applied here, never by mutating pane state directly.
6
+ * editorLayoutReducer is pure and total — it returns a structurally valid EditorLayoutStateV2 for
7
+ * every action and returns the input state unchanged for actions that target a missing pane, split,
8
+ * or file.
9
+ *
10
+ * Invariants maintained by every operation:
11
+ *
12
+ * 1. Tab order is stable and lossless. Within a pane openFiles and tabOrder are the same
13
+ * de-duplicated, non-empty list; reorder/move only permute it. The order survives a
14
+ * serialize -> parse round trip, so tabs never shuffle on reload (AC1).
15
+ * 2. Layout operations never read or write file content or dirty state. Files are identified by
16
+ * path only, so reordering or moving a tab cannot alter a buffer or its dirty marker (AC2/AC3);
17
+ * the dirty index is re-homed onto the layout by the UI layer.
18
+ * 3. Empty panes collapse predictably. Closing or moving the last tab out of a pane removes that
19
+ * pane (when others remain) and promotes the sibling of its enclosing split, so the tree never
20
+ * retains an empty pane alongside populated ones (AC4). The final remaining pane is kept even
21
+ * when it holds no file.
22
+ * 4. Splits always have exactly two children and resize never restructures. resize-split only
23
+ * clamps an existing split's ratio into [MIN_SPLIT_RATIO, MAX_SPLIT_RATIO]; it adds or removes
24
+ * no pane, so nested splits resize without orphan panes or layout jumps (AC5).
25
+ * 5. Identifiers are unique. Pane ids (pane-N) and split ids (split-N) are allocated to avoid
26
+ * collisions with existing nodes, and every tree pane node resolves to an entry in panes.
27
+ * 6. Persisted values are clamped on read and write. Split ratios and the sidebar width are
28
+ * clamped, and unknown or malformed persisted state falls back to a fresh single-pane layout
29
+ * rather than blocking the editor.
30
+ */
1
31
  export declare const EDITOR_LAYOUT_SCHEMA_VERSION: 2;
2
32
  export type EditorSplitDirection = "row" | "column";
3
33
  export type EditorSplitDropZone = "left" | "right" | "top" | "bottom" | "center";
@@ -85,6 +115,13 @@ export type EditorLayoutAction = {
85
115
  readonly type: "replace-root";
86
116
  readonly root: string;
87
117
  readonly sidebarWidth?: number | undefined;
118
+ } | {
119
+ readonly type: "rename-file";
120
+ readonly from: string;
121
+ readonly to: string;
122
+ } | {
123
+ readonly type: "remove-file";
124
+ readonly file: string;
88
125
  };
89
126
  export interface CreateEditorLayoutStateV2Input {
90
127
  readonly root: string;
@@ -1 +1 @@
1
- {"version":3,"file":"editor-layout.d.ts","sourceRoot":"","sources":["../src/editor-layout.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"editor-layout.d.ts","sourceRoot":"","sources":["../src/editor-layout.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,33 @@
1
+ /**
2
+ * Editor layout state and reducer for multi-tab, split-pane workspaces (Issue #1375, ADR-0064).
3
+ *
4
+ * The reducer is the single authority for layout structure: drag/keyboard intents in the UI are
5
+ * translated into EditorLayoutActions and applied here, never by mutating pane state directly.
6
+ * editorLayoutReducer is pure and total — it returns a structurally valid EditorLayoutStateV2 for
7
+ * every action and returns the input state unchanged for actions that target a missing pane, split,
8
+ * or file.
9
+ *
10
+ * Invariants maintained by every operation:
11
+ *
12
+ * 1. Tab order is stable and lossless. Within a pane openFiles and tabOrder are the same
13
+ * de-duplicated, non-empty list; reorder/move only permute it. The order survives a
14
+ * serialize -> parse round trip, so tabs never shuffle on reload (AC1).
15
+ * 2. Layout operations never read or write file content or dirty state. Files are identified by
16
+ * path only, so reordering or moving a tab cannot alter a buffer or its dirty marker (AC2/AC3);
17
+ * the dirty index is re-homed onto the layout by the UI layer.
18
+ * 3. Empty panes collapse predictably. Closing or moving the last tab out of a pane removes that
19
+ * pane (when others remain) and promotes the sibling of its enclosing split, so the tree never
20
+ * retains an empty pane alongside populated ones (AC4). The final remaining pane is kept even
21
+ * when it holds no file.
22
+ * 4. Splits always have exactly two children and resize never restructures. resize-split only
23
+ * clamps an existing split's ratio into [MIN_SPLIT_RATIO, MAX_SPLIT_RATIO]; it adds or removes
24
+ * no pane, so nested splits resize without orphan panes or layout jumps (AC5).
25
+ * 5. Identifiers are unique. Pane ids (pane-N) and split ids (split-N) are allocated to avoid
26
+ * collisions with existing nodes, and every tree pane node resolves to an entry in panes.
27
+ * 6. Persisted values are clamped on read and write. Split ratios and the sidebar width are
28
+ * clamped, and unknown or malformed persisted state falls back to a fresh single-pane layout
29
+ * rather than blocking the editor.
30
+ */
1
31
  export const EDITOR_LAYOUT_SCHEMA_VERSION = 2;
2
32
  const MIN_SPLIT_RATIO = 15;
3
33
  const MAX_SPLIT_RATIO = 85;
@@ -70,7 +100,9 @@ function paneIdsFromTree(node, out = []) {
70
100
  return out;
71
101
  }
72
102
  function normalizePaneNode(value, panes) {
73
- if (value.type === "pane" && typeof value.paneId === "string" && panes[value.paneId] !== undefined) {
103
+ if (value.type === "pane" &&
104
+ typeof value.paneId === "string" &&
105
+ panes[value.paneId] !== undefined) {
74
106
  return { type: "pane", paneId: value.paneId };
75
107
  }
76
108
  return null;
@@ -245,7 +277,9 @@ export function editorLayoutPanes(layout) {
245
277
  .filter((pane) => pane !== undefined);
246
278
  }
247
279
  export function activeEditorPane(layout) {
248
- return layout.panes[layout.activePaneId] ?? editorLayoutPanes(layout)[0] ?? createPane("pane-1", "", []);
280
+ return (layout.panes[layout.activePaneId] ??
281
+ editorLayoutPanes(layout)[0] ??
282
+ createPane("pane-1", "", []));
249
283
  }
250
284
  function updatePane(layout, paneId, update) {
251
285
  const pane = layout.panes[paneId];
@@ -263,7 +297,12 @@ function insertFile(files, file, index) {
263
297
  }
264
298
  function withPaneFiles(pane, files, activeFile) {
265
299
  const tabOrder = orderedFiles(activeFile, files);
266
- return { ...pane, openFiles: tabOrder, tabOrder, activeFile: activeFile.length > 0 ? activeFile : (tabOrder[0] ?? "") };
300
+ return {
301
+ ...pane,
302
+ openFiles: tabOrder,
303
+ tabOrder,
304
+ activeFile: activeFile.length > 0 ? activeFile : (tabOrder[0] ?? ""),
305
+ };
267
306
  }
268
307
  function nextPaneId(layout) {
269
308
  let index = Object.keys(layout.panes).length + 1;
@@ -353,7 +392,9 @@ function removePane(layout, paneId) {
353
392
  if (id !== paneId)
354
393
  panes[id] = pane;
355
394
  }
356
- const nextActivePaneId = layout.activePaneId === paneId ? (paneIdsFromTree(nextTree)[0] ?? layout.activePaneId) : layout.activePaneId;
395
+ const nextActivePaneId = layout.activePaneId === paneId
396
+ ? (paneIdsFromTree(nextTree)[0] ?? layout.activePaneId)
397
+ : layout.activePaneId;
357
398
  return { ...layout, tree: nextTree, panes, activePaneId: nextActivePaneId };
358
399
  }
359
400
  function closeTab(layout, paneId, file) {
@@ -367,7 +408,9 @@ function closeTab(layout, paneId, file) {
367
408
  return removePane(layout, paneId);
368
409
  return updatePane(layout, paneId, (current) => withPaneFiles(current, [], ""));
369
410
  }
370
- const nextActive = pane.activeFile === file ? (nextFiles[closingIndex] ?? nextFiles[closingIndex - 1] ?? nextFiles[0] ?? "") : pane.activeFile;
411
+ const nextActive = pane.activeFile === file
412
+ ? (nextFiles[closingIndex] ?? nextFiles[closingIndex - 1] ?? nextFiles[0] ?? "")
413
+ : pane.activeFile;
371
414
  return updatePane(layout, paneId, (current) => withPaneFiles(current, nextFiles, nextActive));
372
415
  }
373
416
  function moveTab(layout, fromPaneId, toPaneId, file, targetIndex) {
@@ -384,6 +427,70 @@ function moveTab(layout, fromPaneId, toPaneId, file, targetIndex) {
384
427
  next = updatePane(next, toPaneId, (pane) => withPaneFiles(pane, insertFile(pane.tabOrder, file, targetIndex), file));
385
428
  return { ...next, activePaneId: toPaneId };
386
429
  }
430
+ // Re-home one identifier under a prefix rename: an exact match becomes `to`; a descendant of the
431
+ // renamed folder keeps its suffix (`src/app.ts` under `src` -> `lib` becomes `lib/app.ts`). Any
432
+ // other path is unchanged. Folder boundaries use a trailing slash so `src` never matches `srcgen`.
433
+ function renamePathUnderPrefix(file, from, to) {
434
+ if (file === from)
435
+ return to;
436
+ return file.startsWith(`${from}/`) ? `${to}${file.slice(from.length)}` : file;
437
+ }
438
+ function dedupeFiles(files) {
439
+ const out = [];
440
+ for (const file of files) {
441
+ if (file.length > 0 && !out.includes(file))
442
+ out.push(file);
443
+ }
444
+ return out;
445
+ }
446
+ function renameFileInPane(pane, from, to) {
447
+ const swap = (file) => renamePathUnderPrefix(file, from, to);
448
+ const openFiles = dedupeFiles(pane.openFiles.map(swap));
449
+ const tabOrder = dedupeFiles(pane.tabOrder.map(swap));
450
+ const activeFile = swap(pane.activeFile);
451
+ // Nothing in this pane referenced the renamed path; leave it byte-identical so React can bail out.
452
+ if (openFiles.length === pane.openFiles.length &&
453
+ openFiles.every((file, index) => file === pane.openFiles[index]) &&
454
+ activeFile === pane.activeFile) {
455
+ return pane;
456
+ }
457
+ return {
458
+ ...pane,
459
+ openFiles,
460
+ tabOrder,
461
+ activeFile: activeFile.length > 0 ? activeFile : (tabOrder[0] ?? openFiles[0] ?? ""),
462
+ };
463
+ }
464
+ function renameFileEverywhere(layout, from, to) {
465
+ if (from.length === 0 || to.length === 0 || from === to)
466
+ return layout;
467
+ const panes = {};
468
+ let changed = false;
469
+ for (const [id, pane] of Object.entries(layout.panes)) {
470
+ const next = renameFileInPane(pane, from, to);
471
+ if (next !== pane)
472
+ changed = true;
473
+ panes[id] = next;
474
+ }
475
+ // No pane referenced the renamed path: return the original layout so React can bail out of the
476
+ // re-render, matching the referential no-op contract of the other reducer branches.
477
+ return changed ? { ...layout, panes } : layout;
478
+ }
479
+ function removeFileEverywhere(layout, file) {
480
+ if (file.length === 0)
481
+ return layout;
482
+ // Collect every (pane, openFile) pair the delete touches up front, then fold `closeTab` over them:
483
+ // `closeTab` already owns the active-file fallback and the single-tab pane collapse, and tolerates a
484
+ // pane that an earlier collapse removed, so a file open in several panes is closed in each.
485
+ const targets = [];
486
+ for (const pane of editorLayoutPanes(layout)) {
487
+ for (const open of pane.openFiles) {
488
+ if (open === file || open.startsWith(`${file}/`))
489
+ targets.push([pane.id, open]);
490
+ }
491
+ }
492
+ return targets.reduce((acc, [paneId, open]) => closeTab(acc, paneId, open), layout);
493
+ }
387
494
  const PANE_FILE_ACTION_TYPES = new Set([
388
495
  "open-file",
389
496
  "select-file",
@@ -407,7 +514,9 @@ function reducePaneFileAction(layout, action) {
407
514
  case "open-file":
408
515
  return updatePane(layout, action.paneId, (pane) => withPaneFiles(pane, orderedFiles(action.file, pane.openFiles), action.file));
409
516
  case "select-file":
410
- return updatePane({ ...layout, activePaneId: action.paneId }, action.paneId, (pane) => pane.openFiles.includes(action.file) ? withPaneFiles(pane, pane.openFiles, action.file) : pane);
517
+ return updatePane({ ...layout, activePaneId: action.paneId }, action.paneId, (pane) => pane.openFiles.includes(action.file)
518
+ ? withPaneFiles(pane, pane.openFiles, action.file)
519
+ : pane);
411
520
  case "close-tab":
412
521
  return closeTab(layout, action.paneId, action.file);
413
522
  case "reorder-tab":
@@ -449,7 +558,9 @@ function replaceRoot(layout, action) {
449
558
  function reduceLayoutStateAction(layout, action) {
450
559
  switch (action.type) {
451
560
  case "set-active-pane":
452
- return layout.panes[action.paneId] === undefined ? layout : { ...layout, activePaneId: action.paneId };
561
+ return layout.panes[action.paneId] === undefined
562
+ ? layout
563
+ : { ...layout, activePaneId: action.paneId };
453
564
  case "resize-split":
454
565
  return { ...layout, tree: resizeSplitNode(layout.tree, action.splitId, action.ratio) };
455
566
  case "set-sidebar":
@@ -460,12 +571,18 @@ function reduceLayoutStateAction(layout, action) {
460
571
  };
461
572
  case "replace-root":
462
573
  return replaceRoot(layout, action);
574
+ case "rename-file":
575
+ return renameFileEverywhere(layout, action.from, action.to);
576
+ case "remove-file":
577
+ return removeFileEverywhere(layout, action.file);
463
578
  }
464
579
  }
465
580
  export function editorLayoutReducer(layout, action) {
466
581
  if (isPaneFileAction(action))
467
582
  return reducePaneFileAction(layout, action);
468
- return isStructureAction(action) ? reduceStructureAction(layout, action) : reduceLayoutStateAction(layout, action);
583
+ return isStructureAction(action)
584
+ ? reduceStructureAction(layout, action)
585
+ : reduceLayoutStateAction(layout, action);
469
586
  }
470
587
  export function editorLayoutOpenFiles(layout) {
471
588
  const out = [];
@@ -0,0 +1,20 @@
1
+ export type RootRelativeFileIdentifier = string;
2
+ export type WorkspaceFileIdentifierResolution = {
3
+ readonly kind: "empty";
4
+ } | {
5
+ readonly kind: "root";
6
+ } | {
7
+ readonly kind: "relative";
8
+ readonly path: RootRelativeFileIdentifier;
9
+ } | {
10
+ readonly kind: "outside-root";
11
+ readonly candidate: string;
12
+ };
13
+ export interface WorkspaceFileTarget {
14
+ readonly root: string;
15
+ readonly file: RootRelativeFileIdentifier;
16
+ }
17
+ export declare function isRootRelativeFileIdentifier(value: string): boolean;
18
+ export declare function resolveWorkspaceFileIdentifier(root: string, candidate: string | undefined): WorkspaceFileIdentifierResolution;
19
+ export declare function selectWorkspaceFileTarget(root: string, candidate: string | undefined): WorkspaceFileTarget | null;
20
+ //# sourceMappingURL=editor-workspace-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editor-workspace-path.d.ts","sourceRoot":"","sources":["../src/editor-workspace-path.ts"],"names":[],"mappings":"AAqBA,MAAM,MAAM,0BAA0B,GAAG,MAAM,CAAC;AAGhD,MAAM,MAAM,iCAAiC,GAEzC;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAE1B;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAEzB;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,0BAA0B,CAAA;CAAE,GAIxE;IAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAIlE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,0BAA0B,CAAC;CAC3C;AA+CD,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEnE;AAeD,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAAG,SAAS,GAC5B,iCAAiC,CAWnC;AAuBD,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAAG,SAAS,GAC5B,mBAAmB,GAAG,IAAI,CAkB5B"}