@termaxjs/web-core 0.1.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 (226) hide show
  1. package/LICENSE +201 -0
  2. package/dist/block/blockRange.d.ts +11 -0
  3. package/dist/block/blockRange.d.ts.map +1 -0
  4. package/dist/block/blockRange.js +16 -0
  5. package/dist/block/blockRange.js.map +1 -0
  6. package/dist/block/modeMachine.d.ts +18 -0
  7. package/dist/block/modeMachine.d.ts.map +1 -0
  8. package/dist/block/modeMachine.js +21 -0
  9. package/dist/block/modeMachine.js.map +1 -0
  10. package/dist/cursorBlink.d.ts +2 -0
  11. package/dist/cursorBlink.d.ts.map +1 -0
  12. package/dist/cursorBlink.js +4 -0
  13. package/dist/cursorBlink.js.map +1 -0
  14. package/dist/explorer/HeadlessFileTree.d.ts +27 -0
  15. package/dist/explorer/HeadlessFileTree.d.ts.map +1 -0
  16. package/dist/explorer/HeadlessFileTree.js +147 -0
  17. package/dist/explorer/HeadlessFileTree.js.map +1 -0
  18. package/dist/explorer/expansionCache.d.ts +4 -0
  19. package/dist/explorer/expansionCache.d.ts.map +1 -0
  20. package/dist/explorer/expansionCache.js +26 -0
  21. package/dist/explorer/expansionCache.js.map +1 -0
  22. package/dist/explorer/index.d.ts +5 -0
  23. package/dist/explorer/index.d.ts.map +1 -0
  24. package/dist/explorer/index.js +5 -0
  25. package/dist/explorer/index.js.map +1 -0
  26. package/dist/explorer/pathUtils.d.ts +5 -0
  27. package/dist/explorer/pathUtils.d.ts.map +1 -0
  28. package/dist/explorer/pathUtils.js +19 -0
  29. package/dist/explorer/pathUtils.js.map +1 -0
  30. package/dist/explorer/types.d.ts +65 -0
  31. package/dist/explorer/types.d.ts.map +1 -0
  32. package/dist/explorer/types.js +2 -0
  33. package/dist/explorer/types.js.map +1 -0
  34. package/dist/git/HeadlessGitGraph.d.ts +39 -0
  35. package/dist/git/HeadlessGitGraph.d.ts.map +1 -0
  36. package/dist/git/HeadlessGitGraph.js +81 -0
  37. package/dist/git/HeadlessGitGraph.js.map +1 -0
  38. package/dist/images/KittyGraphicsParser.d.ts +24 -0
  39. package/dist/images/KittyGraphicsParser.d.ts.map +1 -0
  40. package/dist/images/KittyGraphicsParser.js +87 -0
  41. package/dist/images/KittyGraphicsParser.js.map +1 -0
  42. package/dist/images/SixelDecoder.d.ts +9 -0
  43. package/dist/images/SixelDecoder.d.ts.map +1 -0
  44. package/dist/images/SixelDecoder.js +110 -0
  45. package/dist/images/SixelDecoder.js.map +1 -0
  46. package/dist/index.d.ts +25 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +25 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/keymap.d.ts +9 -0
  51. package/dist/keymap.d.ts.map +1 -0
  52. package/dist/keymap.js +62 -0
  53. package/dist/keymap.js.map +1 -0
  54. package/dist/layout/HeadlessFloatingManager.d.ts +18 -0
  55. package/dist/layout/HeadlessFloatingManager.d.ts.map +1 -0
  56. package/dist/layout/HeadlessFloatingManager.js +41 -0
  57. package/dist/layout/HeadlessFloatingManager.js.map +1 -0
  58. package/dist/layout/HeadlessPaneTree.d.ts +27 -0
  59. package/dist/layout/HeadlessPaneTree.d.ts.map +1 -0
  60. package/dist/layout/HeadlessPaneTree.js +117 -0
  61. package/dist/layout/HeadlessPaneTree.js.map +1 -0
  62. package/dist/layout/PaneGeometry.d.ts +17 -0
  63. package/dist/layout/PaneGeometry.d.ts.map +1 -0
  64. package/dist/layout/PaneGeometry.js +27 -0
  65. package/dist/layout/PaneGeometry.js.map +1 -0
  66. package/dist/layout/index.d.ts +9 -0
  67. package/dist/layout/index.d.ts.map +1 -0
  68. package/dist/layout/index.js +12 -0
  69. package/dist/layout/index.js.map +1 -0
  70. package/dist/layout/paneTree.d.ts +39 -0
  71. package/dist/layout/paneTree.d.ts.map +1 -0
  72. package/dist/layout/paneTree.js +215 -0
  73. package/dist/layout/paneTree.js.map +1 -0
  74. package/dist/layout/storeUtils.d.ts +23 -0
  75. package/dist/layout/storeUtils.d.ts.map +1 -0
  76. package/dist/layout/storeUtils.js +9 -0
  77. package/dist/layout/storeUtils.js.map +1 -0
  78. package/dist/layout/tabsActions.d.ts +72 -0
  79. package/dist/layout/tabsActions.d.ts.map +1 -0
  80. package/dist/layout/tabsActions.js +975 -0
  81. package/dist/layout/tabsActions.js.map +1 -0
  82. package/dist/layout/tabsStore.d.ts +43 -0
  83. package/dist/layout/tabsStore.d.ts.map +1 -0
  84. package/dist/layout/tabsStore.js +69 -0
  85. package/dist/layout/tabsStore.js.map +1 -0
  86. package/dist/layout/types.d.ts +114 -0
  87. package/dist/layout/types.d.ts.map +1 -0
  88. package/dist/layout/types.js +2 -0
  89. package/dist/layout/types.js.map +1 -0
  90. package/dist/layout/useTabs.d.ts +55 -0
  91. package/dist/layout/useTabs.d.ts.map +1 -0
  92. package/dist/layout/useTabs.js +116 -0
  93. package/dist/layout/useTabs.js.map +1 -0
  94. package/dist/lsp/client.d.ts +16 -0
  95. package/dist/lsp/client.d.ts.map +1 -0
  96. package/dist/lsp/client.js +125 -0
  97. package/dist/lsp/client.js.map +1 -0
  98. package/dist/lsp/index.d.ts +3 -0
  99. package/dist/lsp/index.d.ts.map +1 -0
  100. package/dist/lsp/index.js +3 -0
  101. package/dist/lsp/index.js.map +1 -0
  102. package/dist/lsp/types.d.ts +44 -0
  103. package/dist/lsp/types.d.ts.map +1 -0
  104. package/dist/lsp/types.js +2 -0
  105. package/dist/lsp/types.js.map +1 -0
  106. package/dist/palette/HeadlessCommandPalette.d.ts +21 -0
  107. package/dist/palette/HeadlessCommandPalette.d.ts.map +1 -0
  108. package/dist/palette/HeadlessCommandPalette.js +48 -0
  109. package/dist/palette/HeadlessCommandPalette.js.map +1 -0
  110. package/dist/parser/VtEngine.d.ts +87 -0
  111. package/dist/parser/VtEngine.d.ts.map +1 -0
  112. package/dist/parser/VtEngine.js +1206 -0
  113. package/dist/parser/VtEngine.js.map +1 -0
  114. package/dist/parser/index.d.ts +2 -0
  115. package/dist/parser/index.d.ts.map +1 -0
  116. package/dist/parser/index.js +2 -0
  117. package/dist/parser/index.js.map +1 -0
  118. package/dist/protocol.d.ts +3 -0
  119. package/dist/protocol.d.ts.map +1 -0
  120. package/dist/protocol.js +94 -0
  121. package/dist/protocol.js.map +1 -0
  122. package/dist/settings/EditorSchema.d.ts +3 -0
  123. package/dist/settings/EditorSchema.d.ts.map +1 -0
  124. package/dist/settings/EditorSchema.js +78 -0
  125. package/dist/settings/EditorSchema.js.map +1 -0
  126. package/dist/settings/GeneralSchema.d.ts +3 -0
  127. package/dist/settings/GeneralSchema.d.ts.map +1 -0
  128. package/dist/settings/GeneralSchema.js +87 -0
  129. package/dist/settings/GeneralSchema.js.map +1 -0
  130. package/dist/settings/TerminalSchema.d.ts +3 -0
  131. package/dist/settings/TerminalSchema.d.ts.map +1 -0
  132. package/dist/settings/TerminalSchema.js +196 -0
  133. package/dist/settings/TerminalSchema.js.map +1 -0
  134. package/dist/settings/UiCustomizationSchema.d.ts +3 -0
  135. package/dist/settings/UiCustomizationSchema.d.ts.map +1 -0
  136. package/dist/settings/UiCustomizationSchema.js +85 -0
  137. package/dist/settings/UiCustomizationSchema.js.map +1 -0
  138. package/dist/settings/constants.d.ts +6 -0
  139. package/dist/settings/constants.d.ts.map +1 -0
  140. package/dist/settings/constants.js +27 -0
  141. package/dist/settings/constants.js.map +1 -0
  142. package/dist/settings/index.d.ts +7 -0
  143. package/dist/settings/index.d.ts.map +1 -0
  144. package/dist/settings/index.js +7 -0
  145. package/dist/settings/index.js.map +1 -0
  146. package/dist/settings/types.d.ts +34 -0
  147. package/dist/settings/types.d.ts.map +1 -0
  148. package/dist/settings/types.js +2 -0
  149. package/dist/settings/types.js.map +1 -0
  150. package/dist/spaces/HeadlessSpaces.d.ts +31 -0
  151. package/dist/spaces/HeadlessSpaces.d.ts.map +1 -0
  152. package/dist/spaces/HeadlessSpaces.js +48 -0
  153. package/dist/spaces/HeadlessSpaces.js.map +1 -0
  154. package/dist/theme/applyTheme.d.ts +4 -0
  155. package/dist/theme/applyTheme.d.ts.map +1 -0
  156. package/dist/theme/applyTheme.js +130 -0
  157. package/dist/theme/applyTheme.js.map +1 -0
  158. package/dist/theme/index.d.ts +3 -0
  159. package/dist/theme/index.d.ts.map +1 -0
  160. package/dist/theme/index.js +3 -0
  161. package/dist/theme/index.js.map +1 -0
  162. package/dist/theme/validateTheme.d.ts +10 -0
  163. package/dist/theme/validateTheme.d.ts.map +1 -0
  164. package/dist/theme/validateTheme.js +158 -0
  165. package/dist/theme/validateTheme.js.map +1 -0
  166. package/dist/theme.d.ts +12 -0
  167. package/dist/theme.d.ts.map +1 -0
  168. package/dist/theme.js +32 -0
  169. package/dist/theme.js.map +1 -0
  170. package/dist/themes/caffeine.d.ts +3 -0
  171. package/dist/themes/caffeine.d.ts.map +1 -0
  172. package/dist/themes/caffeine.js +121 -0
  173. package/dist/themes/caffeine.js.map +1 -0
  174. package/dist/themes/catppuccin.d.ts +3 -0
  175. package/dist/themes/catppuccin.d.ts.map +1 -0
  176. package/dist/themes/catppuccin.js +119 -0
  177. package/dist/themes/catppuccin.js.map +1 -0
  178. package/dist/themes/claude.d.ts +3 -0
  179. package/dist/themes/claude.d.ts.map +1 -0
  180. package/dist/themes/claude.js +121 -0
  181. package/dist/themes/claude.js.map +1 -0
  182. package/dist/themes/gruvbox.d.ts +3 -0
  183. package/dist/themes/gruvbox.d.ts.map +1 -0
  184. package/dist/themes/gruvbox.js +119 -0
  185. package/dist/themes/gruvbox.js.map +1 -0
  186. package/dist/themes/index.d.ts +16 -0
  187. package/dist/themes/index.d.ts.map +1 -0
  188. package/dist/themes/index.js +45 -0
  189. package/dist/themes/index.js.map +1 -0
  190. package/dist/themes/nord.d.ts +3 -0
  191. package/dist/themes/nord.d.ts.map +1 -0
  192. package/dist/themes/nord.js +103 -0
  193. package/dist/themes/nord.js.map +1 -0
  194. package/dist/themes/rose-pine.d.ts +3 -0
  195. package/dist/themes/rose-pine.d.ts.map +1 -0
  196. package/dist/themes/rose-pine.js +119 -0
  197. package/dist/themes/rose-pine.js.map +1 -0
  198. package/dist/themes/sage.d.ts +3 -0
  199. package/dist/themes/sage.d.ts.map +1 -0
  200. package/dist/themes/sage.js +121 -0
  201. package/dist/themes/sage.js.map +1 -0
  202. package/dist/themes/termax-default.d.ts +3 -0
  203. package/dist/themes/termax-default.d.ts.map +1 -0
  204. package/dist/themes/termax-default.js +119 -0
  205. package/dist/themes/termax-default.js.map +1 -0
  206. package/dist/themes/tide.d.ts +3 -0
  207. package/dist/themes/tide.d.ts.map +1 -0
  208. package/dist/themes/tide.js +121 -0
  209. package/dist/themes/tide.js.map +1 -0
  210. package/dist/themes/tokyo-night.d.ts +3 -0
  211. package/dist/themes/tokyo-night.d.ts.map +1 -0
  212. package/dist/themes/tokyo-night.js +64 -0
  213. package/dist/themes/tokyo-night.js.map +1 -0
  214. package/dist/themes/types.d.ts +75 -0
  215. package/dist/themes/types.d.ts.map +1 -0
  216. package/dist/themes/types.js +2 -0
  217. package/dist/themes/types.js.map +1 -0
  218. package/dist/types.d.ts +217 -0
  219. package/dist/types.d.ts.map +1 -0
  220. package/dist/types.js +16 -0
  221. package/dist/types.js.map +1 -0
  222. package/dist/utils/shellQuote.d.ts +3 -0
  223. package/dist/utils/shellQuote.d.ts.map +1 -0
  224. package/dist/utils/shellQuote.js +12 -0
  225. package/dist/utils/shellQuote.js.map +1 -0
  226. package/package.json +69 -0
@@ -0,0 +1,975 @@
1
+ export let layoutAdapter = { disposeSession: () => { } };
2
+ export const setLayoutAdapter = (a) => layoutAdapter = a;
3
+ import { findLeafCwd, hasLeaf, leafIds, moveLeaf, nextLeafId, removeLeaf, setLeafCwd as setLeafCwdInTree, siblingLeafOf, splitLeaf, swapLeaves, } from "./paneTree.js";
4
+ import { MAX_PANES_PER_TAB, nextActiveInSpace } from "./useTabs.js";
5
+ import { useTabsStore } from "./tabsStore.js";
6
+ function basename(path) {
7
+ const parts = path.split(/[\\/]/).filter(Boolean);
8
+ return parts.length ? parts[parts.length - 1] : path;
9
+ }
10
+ function titleFromUrl(url) {
11
+ try {
12
+ const u = new URL(url);
13
+ return u.host || url;
14
+ }
15
+ catch {
16
+ return url || "preview";
17
+ }
18
+ }
19
+ export function splitActivePane(tabId, dir) {
20
+ let newLeafId = null;
21
+ useTabsStore.getState().setTabs((curr) => curr.map((t) => {
22
+ if (t.id !== tabId || t.kind !== "terminal" || t.blocks)
23
+ return t;
24
+ if (leafIds(t.paneTree).length >= MAX_PANES_PER_TAB)
25
+ return t;
26
+ const splitId = useTabsStore.getState().allocId();
27
+ const leafId = useTabsStore.getState().allocId();
28
+ newLeafId = leafId;
29
+ const paneTree = splitLeaf(t.paneTree, t.activeLeafId, splitId, leafId, dir, t.cwd);
30
+ return { ...t, paneTree, activeLeafId: leafId };
31
+ }));
32
+ return newLeafId;
33
+ }
34
+ export function splitPaneDirectly(tabId, targetLeafId, dir, edge) {
35
+ let newLeafId = null;
36
+ useTabsStore.getState().setTabs((curr) => curr.map((t) => {
37
+ if (t.id !== tabId || t.kind !== "terminal" || t.blocks)
38
+ return t;
39
+ if (leafIds(t.paneTree).length >= MAX_PANES_PER_TAB)
40
+ return t;
41
+ const splitId = useTabsStore.getState().allocId();
42
+ const leafId = useTabsStore.getState().allocId();
43
+ newLeafId = leafId;
44
+ const paneTree = splitLeaf(t.paneTree, targetLeafId, splitId, leafId, dir, t.cwd, undefined, edge);
45
+ return { ...t, paneTree, activeLeafId: leafId };
46
+ }));
47
+ return newLeafId;
48
+ }
49
+ export function splitPaneWithEditor(tabId, leafIdToSplit, path, dir, edge = "after") {
50
+ let newLeafId = null;
51
+ useTabsStore.getState().setTabs((curr) => curr.map((t) => {
52
+ if (t.id !== tabId || t.kind !== "terminal" || t.blocks)
53
+ return t;
54
+ if (leafIds(t.paneTree).length >= MAX_PANES_PER_TAB)
55
+ return t;
56
+ const splitId = useTabsStore.getState().allocId();
57
+ const leafId = useTabsStore.getState().allocId();
58
+ newLeafId = leafId;
59
+ const paneTree = splitLeaf(t.paneTree, leafIdToSplit, splitId, leafId, dir, t.cwd, path, edge);
60
+ return { ...t, paneTree, activeLeafId: leafId };
61
+ }));
62
+ return newLeafId;
63
+ }
64
+ export function swapPanes(tabId, leafA, leafB) {
65
+ useTabsStore.getState().setTabs((curr) => curr.map((t) => {
66
+ if (t.id !== tabId || t.kind !== "terminal")
67
+ return t;
68
+ const paneTree = swapLeaves(t.paneTree, leafA, leafB);
69
+ return { ...t, paneTree };
70
+ }));
71
+ }
72
+ export function movePane(tabId, sourceId, targetId, dir, edge) {
73
+ useTabsStore.getState().setTabs((curr) => curr.map((t) => {
74
+ if (t.id !== tabId || t.kind !== "terminal")
75
+ return t;
76
+ const newSplitId = useTabsStore.getState().allocId();
77
+ const paneTree = moveLeaf(t.paneTree, sourceId, targetId, newSplitId, dir, edge);
78
+ return { ...t, paneTree };
79
+ }));
80
+ }
81
+ export function closePaneByLeaf(leafId) {
82
+ let didRemove = false;
83
+ useTabsStore.getState().setTabs((curr) => {
84
+ const tab = curr.find((t) => t.kind === "terminal" && hasLeaf(t.paneTree, leafId));
85
+ if (tab?.kind !== "terminal")
86
+ return curr;
87
+ const newTree = removeLeaf(tab.paneTree, leafId);
88
+ if (newTree === null) {
89
+ const fallback = nextActiveInSpace(curr, tab.id);
90
+ const next = curr.filter((x) => x.id !== tab.id);
91
+ const newActive = fallback !== null ? fallback : -1;
92
+ useTabsStore
93
+ .getState()
94
+ .setActiveId(useTabsStore.getState().activeId === tab.id
95
+ ? newActive
96
+ : useTabsStore.getState().activeId);
97
+ didRemove = true;
98
+ return next;
99
+ }
100
+ const remaining = leafIds(newTree);
101
+ let newActive = tab.activeLeafId;
102
+ if (tab.activeLeafId === leafId) {
103
+ const sib = siblingLeafOf(tab.paneTree, leafId);
104
+ newActive = sib && remaining.includes(sib) ? sib : remaining[0];
105
+ }
106
+ didRemove = true;
107
+ return curr.map((x) => x.id === tab.id
108
+ ? { ...x, paneTree: newTree, activeLeafId: newActive }
109
+ : x);
110
+ });
111
+ if (didRemove) {
112
+ layoutAdapter.disposeSession(leafId);
113
+ window.dispatchEvent(new Event("resize"));
114
+ }
115
+ }
116
+ export function closeActivePane(tabId) {
117
+ let closedTab = false;
118
+ let removedLeaf = null;
119
+ useTabsStore.getState().setTabs((curr) => {
120
+ const t = curr.find((x) => x.id === tabId);
121
+ if (t?.kind !== "terminal")
122
+ return curr;
123
+ const target = t.activeLeafId;
124
+ const newTree = removeLeaf(t.paneTree, target);
125
+ if (newTree === null) {
126
+ const fallback = nextActiveInSpace(curr, tabId);
127
+ const next = curr.filter((x) => x.id !== tabId);
128
+ const newActive = fallback !== null ? fallback : -1;
129
+ useTabsStore
130
+ .getState()
131
+ .setActiveId(useTabsStore.getState().activeId === tabId
132
+ ? newActive
133
+ : useTabsStore.getState().activeId);
134
+ closedTab = true;
135
+ removedLeaf = target;
136
+ return next;
137
+ }
138
+ const remaining = leafIds(newTree);
139
+ const sib = siblingLeafOf(t.paneTree, target);
140
+ const newActive = sib && remaining.includes(sib) ? sib : remaining[0];
141
+ removedLeaf = target;
142
+ return curr.map((x) => x.id === tabId ? { ...x, paneTree: newTree, activeLeafId: newActive } : x);
143
+ });
144
+ if (removedLeaf !== null) {
145
+ layoutAdapter.disposeSession(removedLeaf);
146
+ window.dispatchEvent(new Event("resize"));
147
+ }
148
+ return closedTab;
149
+ }
150
+ export function mergeTabIntoPane(sourceTabId, activeTabId, targetPaneId, dir, edge) {
151
+ useTabsStore.getState().setTabs((curr) => {
152
+ const sourceTab = curr.find((t) => t.id === sourceTabId);
153
+ const activeTab = curr.find((t) => t.id === activeTabId);
154
+ if (!sourceTab || !activeTab || activeTab.kind !== "terminal")
155
+ return curr;
156
+ if (sourceTabId === activeTabId)
157
+ return curr;
158
+ const newSplitId = useTabsStore.getState().allocId();
159
+ let sourceNode;
160
+ if (sourceTab.kind === "terminal") {
161
+ const leaves = leafIds(sourceTab.paneTree);
162
+ if (leaves.length !== 1)
163
+ return curr;
164
+ let foundLeaf = null;
165
+ const findLeaf = (n) => {
166
+ if (n.kind === "leaf" && n.id === leaves[0])
167
+ foundLeaf = n;
168
+ else if (n.kind === "split")
169
+ n.children.forEach(findLeaf);
170
+ };
171
+ findLeaf(sourceTab.paneTree);
172
+ if (!foundLeaf)
173
+ return curr;
174
+ sourceNode = foundLeaf;
175
+ }
176
+ else if (sourceTab.kind === "editor" || sourceTab.kind === "markdown") {
177
+ sourceNode = {
178
+ kind: "leaf",
179
+ id: useTabsStore.getState().allocId(),
180
+ editorPath: sourceTab.path,
181
+ };
182
+ }
183
+ else {
184
+ return curr;
185
+ }
186
+ function insertNode(n) {
187
+ if (n.kind === "split" && n.dir === dir) {
188
+ const leafIdx = n.children.findIndex((c) => c.kind === "leaf" && c.id === targetPaneId);
189
+ if (leafIdx >= 0) {
190
+ const newChildren = [...n.children];
191
+ newChildren.splice(edge === "before" ? leafIdx : leafIdx + 1, 0, sourceNode);
192
+ return { ...n, children: newChildren };
193
+ }
194
+ }
195
+ if (n.kind === "leaf") {
196
+ if (n.id !== targetPaneId)
197
+ return n;
198
+ return {
199
+ kind: "split",
200
+ id: newSplitId,
201
+ dir,
202
+ children: edge === "before" ? [sourceNode, n] : [n, sourceNode],
203
+ };
204
+ }
205
+ let changed = false;
206
+ const newChildren = n.children.map((c) => {
207
+ const inserted = insertNode(c);
208
+ if (inserted !== c)
209
+ changed = true;
210
+ return inserted;
211
+ });
212
+ return changed ? { ...n, children: newChildren } : n;
213
+ }
214
+ const newActiveTree = insertNode(activeTab.paneTree);
215
+ return curr
216
+ .filter((t) => t.id !== sourceTabId)
217
+ .map((t) => t.id === activeTabId ? { ...t, paneTree: newActiveTree, activeLeafId: sourceNode.id } : t);
218
+ });
219
+ }
220
+ export function setActiveSpaceForNewTabs(spaceId) {
221
+ useTabsStore.getState().activeSpaceId = spaceId;
222
+ }
223
+ export function replaceTabs(next, nextActiveId) {
224
+ if (next.length === 0)
225
+ return;
226
+ useTabsStore.getState().setTabs(next);
227
+ useTabsStore.getState().setActiveId(nextActiveId);
228
+ }
229
+ export function newTabInSpace(spaceId, cwd) {
230
+ const tabId = useTabsStore.getState().allocId();
231
+ const leafId = useTabsStore.getState().allocId();
232
+ useTabsStore.getState().setTabs((curr) => [
233
+ ...curr,
234
+ {
235
+ id: tabId,
236
+ kind: "terminal",
237
+ spaceId,
238
+ cold: true,
239
+ title: cwd ? basename(cwd) : "shell",
240
+ cwd,
241
+ paneTree: { kind: "leaf", id: leafId, cwd },
242
+ activeLeafId: leafId,
243
+ },
244
+ ]);
245
+ // Focus the new tab when it lands in the active space (e.g. opened from the
246
+ // empty-space landing) so the surface actually switches to it. Skip when
247
+ // seeding a background space — pointing the global activeId at a hidden
248
+ // space would blank the visible one.
249
+ if (useTabsStore.getState().activeSpaceId === spaceId) {
250
+ useTabsStore.getState().setActiveId(tabId);
251
+ }
252
+ return tabId;
253
+ }
254
+ export function moveTabToSpace(tabId, targetSpaceId) {
255
+ const curr = useTabsStore.getState().tabs;
256
+ const tab = curr.find((t) => t.id === tabId);
257
+ if (!tab || tab.spaceId === targetSpaceId)
258
+ return false;
259
+ useTabsStore
260
+ .getState()
261
+ .setTabs((prev) => prev.map((t) => t.id === tabId ? { ...t, spaceId: targetSpaceId } : t));
262
+ if (useTabsStore.getState().activeId !== tabId)
263
+ return false;
264
+ const fallback = nextActiveInSpace(curr, tabId);
265
+ if (fallback !== null) {
266
+ useTabsStore.getState().setActiveId(fallback);
267
+ return false;
268
+ }
269
+ return true;
270
+ }
271
+ export function copyTabToSpace(tabId, targetSpaceId) {
272
+ const curr = useTabsStore.getState().tabs;
273
+ const tab = curr.find((t) => t.id === tabId);
274
+ if (!tab)
275
+ return null;
276
+ const newTabId = useTabsStore.getState().allocId();
277
+ const newLeafId = useTabsStore.getState().allocId();
278
+ let cloned;
279
+ if (tab.kind === "terminal") {
280
+ cloned = {
281
+ ...tab,
282
+ id: newTabId,
283
+ spaceId: targetSpaceId,
284
+ cold: true,
285
+ paneTree: {
286
+ kind: "leaf",
287
+ id: newLeafId,
288
+ ...(tab.cwd && { cwd: tab.cwd }),
289
+ },
290
+ activeLeafId: newLeafId,
291
+ };
292
+ }
293
+ else {
294
+ cloned = {
295
+ ...tab,
296
+ id: newTabId,
297
+ spaceId: targetSpaceId,
298
+ cold: true,
299
+ };
300
+ }
301
+ useTabsStore.getState().setTabs((prev) => [...prev, cloned]);
302
+ return newTabId;
303
+ }
304
+ export function reorderTab(tabId, targetTabId, edge) {
305
+ if (tabId === targetTabId)
306
+ return false;
307
+ const curr = useTabsStore.getState().tabs;
308
+ const moved = curr.find((t) => t.id === tabId);
309
+ const target = curr.find((t) => t.id === targetTabId);
310
+ if (!moved || !target)
311
+ return false;
312
+ const crossSpace = moved.spaceId !== target.spaceId;
313
+ useTabsStore.getState().setTabs((prev) => {
314
+ const without = prev.filter((t) => t.id !== tabId);
315
+ let idx = without.findIndex((t) => t.id === targetTabId);
316
+ if (idx < 0)
317
+ return prev;
318
+ if (edge === "bottom")
319
+ idx += 1;
320
+ const next = crossSpace
321
+ ? { ...moved, spaceId: target.spaceId }
322
+ : moved;
323
+ without.splice(idx, 0, next);
324
+ return without;
325
+ });
326
+ if (!crossSpace || useTabsStore.getState().activeId !== tabId)
327
+ return false;
328
+ const fallback = nextActiveInSpace(curr, tabId);
329
+ if (fallback !== null) {
330
+ useTabsStore.getState().setActiveId(fallback);
331
+ return false;
332
+ }
333
+ return true;
334
+ }
335
+ export function removeTabsForSpace(spaceId) {
336
+ let toDispose = [];
337
+ useTabsStore.getState().setTabs((curr) => {
338
+ const next = curr.filter((t) => t.spaceId !== spaceId);
339
+ if (next.length === curr.length)
340
+ return curr;
341
+ toDispose = curr
342
+ .filter((t) => t.spaceId === spaceId && t.kind === "terminal")
343
+ .flatMap((t) => leafIds(t.paneTree));
344
+ const activeTab = curr.find((t) => t.id === useTabsStore.getState().activeId);
345
+ if (activeTab?.spaceId === spaceId) {
346
+ useTabsStore.getState().setActiveId(next.length > 0 ? next[0].id : -1);
347
+ }
348
+ return next;
349
+ });
350
+ for (const lid of toDispose)
351
+ layoutAdapter.disposeSession(lid);
352
+ }
353
+ export function newTab(cwd, command, title) {
354
+ const tabId = useTabsStore.getState().allocId();
355
+ const leafId = useTabsStore.getState().allocId();
356
+ useTabsStore.getState().setTabs((t) => [
357
+ ...t,
358
+ {
359
+ id: tabId,
360
+ kind: "terminal",
361
+ spaceId: useTabsStore.getState().activeSpaceId,
362
+ title: title || "shell",
363
+ cwd,
364
+ paneTree: { kind: "leaf", id: leafId, cwd, command },
365
+ activeLeafId: leafId,
366
+ },
367
+ ]);
368
+ useTabsStore.getState().setActiveId(tabId);
369
+ return tabId;
370
+ }
371
+ export function newBlockTab(cwd) {
372
+ const tabId = useTabsStore.getState().allocId();
373
+ const leafId = useTabsStore.getState().allocId();
374
+ useTabsStore.getState().setTabs((t) => [
375
+ ...t,
376
+ {
377
+ id: tabId,
378
+ kind: "terminal",
379
+ spaceId: useTabsStore.getState().activeSpaceId,
380
+ title: "blocks",
381
+ cwd,
382
+ paneTree: { kind: "leaf", id: leafId, cwd },
383
+ activeLeafId: leafId,
384
+ blocks: true,
385
+ },
386
+ ]);
387
+ useTabsStore.getState().setActiveId(tabId);
388
+ return tabId;
389
+ }
390
+ export function newAgentTab(cwd, title) {
391
+ const tabId = useTabsStore.getState().allocId();
392
+ const leafId = useTabsStore.getState().allocId();
393
+ useTabsStore.getState().setTabs((t) => [
394
+ ...t,
395
+ {
396
+ id: tabId,
397
+ kind: "terminal",
398
+ spaceId: useTabsStore.getState().activeSpaceId,
399
+ title,
400
+ cwd,
401
+ paneTree: { kind: "leaf", id: leafId, cwd },
402
+ activeLeafId: leafId,
403
+ },
404
+ ]);
405
+ useTabsStore.getState().setActiveId(tabId);
406
+ return { tabId, leafId };
407
+ }
408
+ export function newPrivateTab(cwd) {
409
+ const tabId = useTabsStore.getState().allocId();
410
+ const leafId = useTabsStore.getState().allocId();
411
+ useTabsStore.getState().setTabs((t) => [
412
+ ...t,
413
+ {
414
+ id: tabId,
415
+ kind: "terminal",
416
+ spaceId: useTabsStore.getState().activeSpaceId,
417
+ title: "private",
418
+ cwd,
419
+ paneTree: { kind: "leaf", id: leafId, cwd },
420
+ activeLeafId: leafId,
421
+ private: true,
422
+ },
423
+ ]);
424
+ useTabsStore.getState().setActiveId(tabId);
425
+ return tabId;
426
+ }
427
+ export function openFileTab(path, pin = true, gotoLine) {
428
+ let targetId = null;
429
+ useTabsStore.getState().setTabs((curr) => {
430
+ if (pin) {
431
+ // Persistent open: find any existing editor tab, pin it if needed.
432
+ const existing = curr.find((t) => t.kind === "editor" && t.path === path);
433
+ if (existing) {
434
+ targetId = existing.id;
435
+ let needsUpdate = false;
436
+ if (gotoLine !== undefined &&
437
+ existing.gotoLine !== gotoLine) {
438
+ needsUpdate = true;
439
+ }
440
+ if (existing.preview) {
441
+ needsUpdate = true;
442
+ }
443
+ if (needsUpdate) {
444
+ return curr.map((t) => t.id === existing.id
445
+ ? {
446
+ ...t,
447
+ preview: false,
448
+ ...(gotoLine !== undefined ? { gotoLine } : {}),
449
+ }
450
+ : t);
451
+ }
452
+ return curr;
453
+ }
454
+ const id = useTabsStore.getState().allocId();
455
+ targetId = id;
456
+ return [
457
+ ...curr,
458
+ {
459
+ id,
460
+ kind: "editor",
461
+ spaceId: useTabsStore.getState().activeSpaceId,
462
+ title: basename(path),
463
+ path,
464
+ dirty: false,
465
+ preview: false,
466
+ },
467
+ ];
468
+ }
469
+ else {
470
+ // Preview open: persistent tab for this path takes priority.
471
+ const persistent = curr.find((t) => t.kind === "editor" && t.path === path && !t.preview);
472
+ if (persistent) {
473
+ targetId = persistent.id;
474
+ return curr;
475
+ }
476
+ // Reuse the slot if it already shows the same path.
477
+ const existingPreview = curr.find((t) => t.kind === "editor" && t.path === path && t.preview);
478
+ if (existingPreview) {
479
+ targetId = existingPreview.id;
480
+ return curr;
481
+ }
482
+ // Replace the current preview slot, or append a new one.
483
+ const previewIdx = curr.findIndex((t) => t.kind === "editor" && t.preview);
484
+ const id = useTabsStore.getState().allocId();
485
+ targetId = id;
486
+ const tab = {
487
+ id,
488
+ kind: "editor",
489
+ spaceId: useTabsStore.getState().activeSpaceId,
490
+ title: basename(path),
491
+ path,
492
+ dirty: false,
493
+ preview: true,
494
+ };
495
+ if (previewIdx === -1)
496
+ return [...curr, tab];
497
+ const next = [...curr];
498
+ next[previewIdx] = tab;
499
+ return next;
500
+ }
501
+ });
502
+ if (targetId !== null)
503
+ useTabsStore.getState().setActiveId(targetId);
504
+ return targetId;
505
+ }
506
+ export function openFileTabInSpace(spaceId, path, pin = false) {
507
+ let targetId = null;
508
+ useTabsStore.getState().setTabs((curr) => {
509
+ const existing = curr.find((t) => t.kind === "editor" && t.path === path && t.spaceId === spaceId);
510
+ if (existing) {
511
+ targetId = existing.id;
512
+ if (pin && existing.preview) {
513
+ return curr.map((t) => t.id === existing.id ? { ...t, preview: false } : t);
514
+ }
515
+ return curr;
516
+ }
517
+ const id = useTabsStore.getState().allocId();
518
+ targetId = id;
519
+ const tab = {
520
+ id,
521
+ kind: "editor",
522
+ spaceId,
523
+ title: basename(path) || "untitled",
524
+ path,
525
+ dirty: false,
526
+ preview: !pin,
527
+ };
528
+ return [...curr, tab];
529
+ });
530
+ if (targetId !== null)
531
+ useTabsStore.getState().setActiveId(targetId);
532
+ return targetId;
533
+ }
534
+ export function pinTab(id) {
535
+ useTabsStore
536
+ .getState()
537
+ .setTabs((curr) => curr.map((t) => t.id === id && t.kind === "editor" ? { ...t, preview: false } : t));
538
+ }
539
+ export function openAiDiffTab(input) {
540
+ let targetId = null;
541
+ useTabsStore.getState().setTabs((curr) => {
542
+ const existing = curr.find((t) => t.kind === "ai-diff" && t.approvalId === input.approvalId);
543
+ if (existing) {
544
+ targetId = existing.id;
545
+ return curr;
546
+ }
547
+ const id = useTabsStore.getState().allocId();
548
+ targetId = id;
549
+ const title = `${basename(input.path)} (AI diff)`;
550
+ return [
551
+ ...curr,
552
+ {
553
+ id,
554
+ kind: "ai-diff",
555
+ spaceId: useTabsStore.getState().activeSpaceId,
556
+ title,
557
+ path: input.path,
558
+ originalContent: input.originalContent,
559
+ proposedContent: input.proposedContent,
560
+ approvalId: input.approvalId,
561
+ status: "pending",
562
+ isNewFile: input.isNewFile,
563
+ },
564
+ ];
565
+ });
566
+ if (targetId !== null)
567
+ useTabsStore.getState().setActiveId(targetId);
568
+ return targetId;
569
+ }
570
+ export function setAiDiffStatus(approvalId, status) {
571
+ useTabsStore
572
+ .getState()
573
+ .setTabs((curr) => curr.map((t) => t.kind === "ai-diff" && t.approvalId === approvalId
574
+ ? { ...t, status }
575
+ : t));
576
+ }
577
+ export function closeAiDiffTab(approvalId) {
578
+ useTabsStore.getState().setTabs((curr) => {
579
+ const target = curr.find((t) => t.kind === "ai-diff" && t.approvalId === approvalId);
580
+ if (!target)
581
+ return curr;
582
+ const fallback = nextActiveInSpace(curr, target.id);
583
+ const next = curr.filter((t) => t.id !== target.id);
584
+ const newActiveId = fallback !== null ? fallback : -1;
585
+ useTabsStore
586
+ .getState()
587
+ .setActiveId((active) => (target.id === active ? newActiveId : active));
588
+ return next;
589
+ });
590
+ }
591
+ export function newPreviewTab(url) {
592
+ const id = useTabsStore.getState().allocId();
593
+ useTabsStore.getState().setTabs((t) => [
594
+ ...t,
595
+ {
596
+ id,
597
+ kind: "preview",
598
+ spaceId: useTabsStore.getState().activeSpaceId,
599
+ title: titleFromUrl(url),
600
+ url,
601
+ },
602
+ ]);
603
+ useTabsStore.getState().setActiveId(id);
604
+ return id;
605
+ }
606
+ export function newMarkdownTab(path) {
607
+ let targetId = null;
608
+ useTabsStore.getState().setTabs((curr) => {
609
+ const existing = curr.find((t) => t.kind === "markdown" && t.path === path);
610
+ if (existing) {
611
+ targetId = existing.id;
612
+ return curr;
613
+ }
614
+ const id = useTabsStore.getState().allocId();
615
+ targetId = id;
616
+ return [
617
+ ...curr,
618
+ {
619
+ id,
620
+ kind: "markdown",
621
+ spaceId: useTabsStore.getState().activeSpaceId,
622
+ title: basename(path),
623
+ path,
624
+ },
625
+ ];
626
+ });
627
+ if (targetId !== null)
628
+ useTabsStore.getState().setActiveId(targetId);
629
+ return targetId;
630
+ }
631
+ export function openGitDiffTab(input) {
632
+ const curr = useTabsStore.getState().tabs;
633
+ const existing = curr.find((t) => t.kind === "git-diff" &&
634
+ t.repoRoot === input.repoRoot &&
635
+ t.path === input.path &&
636
+ t.mode === input.mode);
637
+ const computedTitle = input.title ?? `${basename(input.path)} (${input.mode})`;
638
+ const originalPath = input.originalPath ?? null;
639
+ if (existing) {
640
+ const nextTabs = curr.map((t) => t.id === existing.id ? { ...t, title: computedTitle, originalPath } : t);
641
+ useTabsStore.getState().tabs = nextTabs;
642
+ useTabsStore.getState().setTabs(nextTabs);
643
+ useTabsStore.getState().setActiveId(existing.id);
644
+ return existing.id;
645
+ }
646
+ const id = useTabsStore.getState().allocId();
647
+ const nextTabs = [
648
+ ...curr,
649
+ {
650
+ id,
651
+ kind: "git-diff",
652
+ spaceId: useTabsStore.getState().activeSpaceId,
653
+ title: computedTitle,
654
+ path: input.path,
655
+ repoRoot: input.repoRoot,
656
+ mode: input.mode,
657
+ originalPath,
658
+ },
659
+ ];
660
+ useTabsStore.getState().tabs = nextTabs;
661
+ useTabsStore.getState().setTabs(nextTabs);
662
+ useTabsStore.getState().setActiveId(id);
663
+ return id;
664
+ }
665
+ export function openCommitHistoryTab(input) {
666
+ const curr = useTabsStore.getState().tabs;
667
+ const existing = curr.find((t) => t.kind === "git-history" && t.repoRoot === input.repoRoot);
668
+ const title = input.branch ? `History · ${input.branch}` : "Git History";
669
+ if (existing) {
670
+ const nextTabs = curr.map((t) => t.id === existing.id ? { ...t, title } : t);
671
+ useTabsStore.getState().tabs = nextTabs;
672
+ useTabsStore.getState().setTabs(nextTabs);
673
+ useTabsStore.getState().setActiveId(existing.id);
674
+ return existing.id;
675
+ }
676
+ const id = useTabsStore.getState().allocId();
677
+ const nextTabs = [
678
+ ...curr,
679
+ {
680
+ id,
681
+ kind: "git-history",
682
+ spaceId: useTabsStore.getState().activeSpaceId,
683
+ title,
684
+ repoRoot: input.repoRoot,
685
+ },
686
+ ];
687
+ useTabsStore.getState().tabs = nextTabs;
688
+ useTabsStore.getState().setTabs(nextTabs);
689
+ useTabsStore.getState().setActiveId(id);
690
+ return id;
691
+ }
692
+ export function openCommitFileDiffTab(input) {
693
+ const curr = useTabsStore.getState().tabs;
694
+ const existing = curr.find((t) => t.kind === "git-commit-file" &&
695
+ t.repoRoot === input.repoRoot &&
696
+ t.sha === input.sha &&
697
+ t.path === input.path);
698
+ const title = `${basename(input.path)} @ ${input.shortSha}`;
699
+ if (existing) {
700
+ const nextTabs = curr.map((t) => t.id === existing.id
701
+ ? {
702
+ ...t,
703
+ title,
704
+ subject: input.subject,
705
+ originalPath: input.originalPath,
706
+ }
707
+ : t);
708
+ useTabsStore.getState().tabs = nextTabs;
709
+ useTabsStore.getState().setTabs(nextTabs);
710
+ useTabsStore.getState().setActiveId(existing.id);
711
+ return existing.id;
712
+ }
713
+ const id = useTabsStore.getState().allocId();
714
+ const nextTabs = [
715
+ ...curr,
716
+ {
717
+ id,
718
+ kind: "git-commit-file",
719
+ spaceId: useTabsStore.getState().activeSpaceId,
720
+ title,
721
+ repoRoot: input.repoRoot,
722
+ sha: input.sha,
723
+ shortSha: input.shortSha,
724
+ subject: input.subject,
725
+ path: input.path,
726
+ originalPath: input.originalPath,
727
+ },
728
+ ];
729
+ useTabsStore.getState().tabs = nextTabs;
730
+ useTabsStore.getState().setTabs(nextTabs);
731
+ useTabsStore.getState().setActiveId(id);
732
+ return id;
733
+ }
734
+ export function closeTab(id) {
735
+ let toDispose = [];
736
+ useTabsStore.getState().setTabs((curr) => {
737
+ const target = curr.find((t) => t.id === id);
738
+ if (!target)
739
+ return curr;
740
+ if (target.kind === "terminal") {
741
+ toDispose = leafIds(target.paneTree);
742
+ }
743
+ const fallback = nextActiveInSpace(curr, id);
744
+ const next = curr.filter((t) => t.id !== id);
745
+ const newActiveId = fallback !== null ? fallback : -1;
746
+ useTabsStore
747
+ .getState()
748
+ .setActiveId((active) => (id === active ? newActiveId : active));
749
+ return next;
750
+ });
751
+ for (const lid of toDispose)
752
+ layoutAdapter.disposeSession(lid);
753
+ }
754
+ export function updateTab(id, patch) {
755
+ useTabsStore.getState().setTabs((t) => t.map((x) => {
756
+ if (x.id !== id)
757
+ return x;
758
+ if (x.kind === "terminal") {
759
+ return {
760
+ ...x,
761
+ ...(patch.title !== undefined && { title: patch.title }),
762
+ ...(patch.dynamicTitle !== undefined && {
763
+ dynamicTitle: patch.dynamicTitle,
764
+ }),
765
+ ...(patch.alert !== undefined && { alert: patch.alert }),
766
+ ...(patch.interaction !== undefined && {
767
+ interaction: patch.interaction,
768
+ }),
769
+ ...(patch.cwd !== undefined && { cwd: patch.cwd }),
770
+ ...(patch.customTitle !== undefined && {
771
+ customTitle: patch.customTitle === "" ? undefined : patch.customTitle,
772
+ }),
773
+ };
774
+ }
775
+ if (x.kind === "preview") {
776
+ return {
777
+ ...x,
778
+ ...(patch.title !== undefined && { title: patch.title }),
779
+ ...(patch.url !== undefined && {
780
+ url: patch.url,
781
+ title: patch.title ?? titleFromUrl(patch.url),
782
+ }),
783
+ };
784
+ }
785
+ if (x.kind === "markdown") {
786
+ return {
787
+ ...x,
788
+ ...(patch.title !== undefined && { title: patch.title }),
789
+ };
790
+ }
791
+ // editor tab: auto-promote from preview the moment the file becomes dirty.
792
+ const autoPin = patch.dirty === true && x.preview
793
+ ? { preview: false }
794
+ : {};
795
+ return {
796
+ ...x,
797
+ ...autoPin,
798
+ ...(patch.title !== undefined && { title: patch.title }),
799
+ ...(patch.dirty !== undefined && { dirty: patch.dirty }),
800
+ ...(patch.path !== undefined && { path: patch.path }),
801
+ };
802
+ }));
803
+ }
804
+ export function selectByIndex(idx) {
805
+ const t = useTabsStore.getState().tabs[idx];
806
+ if (t)
807
+ useTabsStore.getState().setActiveId(t.id);
808
+ }
809
+ export function setLeafCwd(leafId, cwd) {
810
+ useTabsStore.getState().setTabs((curr) => {
811
+ let changed = false;
812
+ const next = curr.map((t) => {
813
+ if (t.kind !== "terminal" || !hasLeaf(t.paneTree, leafId))
814
+ return t;
815
+ const paneTree = setLeafCwdInTree(t.paneTree, leafId, cwd);
816
+ const isActive = t.activeLeafId === leafId;
817
+ const cwdChanged = isActive && t.cwd !== cwd;
818
+ if (paneTree === t.paneTree && !cwdChanged)
819
+ return t;
820
+ changed = true;
821
+ return {
822
+ ...t,
823
+ paneTree,
824
+ ...(cwdChanged && { cwd, title: cwd ? basename(cwd) : "shell" }),
825
+ };
826
+ });
827
+ return changed ? next : curr;
828
+ });
829
+ }
830
+ export function focusPane(tabId, leafId) {
831
+ useTabsStore.getState().setTabs((curr) => curr.map((t) => {
832
+ if (t.id !== tabId || t.kind !== "terminal")
833
+ return t;
834
+ if (!hasLeaf(t.paneTree, leafId))
835
+ return t;
836
+ if (t.activeLeafId === leafId)
837
+ return t;
838
+ const cwd = findLeafCwd(t.paneTree, leafId);
839
+ return {
840
+ ...t,
841
+ activeLeafId: leafId,
842
+ ...(cwd !== undefined && { cwd }),
843
+ };
844
+ }));
845
+ }
846
+ export function focusNextPaneInTab(tabId, delta) {
847
+ useTabsStore.getState().setTabs((curr) => curr.map((t) => {
848
+ if (t.id !== tabId || t.kind !== "terminal")
849
+ return t;
850
+ const next = nextLeafId(t.paneTree, t.activeLeafId, delta);
851
+ if (next === t.activeLeafId)
852
+ return t;
853
+ const cwd = findLeafCwd(t.paneTree, next);
854
+ return { ...t, activeLeafId: next, ...(cwd !== undefined && { cwd }) };
855
+ }));
856
+ }
857
+ export function resetWorkspace(cwd) {
858
+ const tabId = useTabsStore.getState().allocId();
859
+ const leafId = useTabsStore.getState().allocId();
860
+ let toDispose = [];
861
+ useTabsStore.getState().setTabs((curr) => {
862
+ toDispose = curr.flatMap((t) => t.kind === "terminal" ? leafIds(t.paneTree) : []);
863
+ return [
864
+ {
865
+ id: tabId,
866
+ kind: "terminal",
867
+ spaceId: useTabsStore.getState().activeSpaceId,
868
+ title: "shell",
869
+ cwd,
870
+ paneTree: { kind: "leaf", id: leafId, cwd },
871
+ activeLeafId: leafId,
872
+ },
873
+ ];
874
+ });
875
+ useTabsStore.getState().setActiveId(tabId);
876
+ for (const lid of toDispose)
877
+ layoutAdapter.disposeSession(lid);
878
+ }
879
+ export function extractPaneToTab(tabId, leafId) {
880
+ let newTabId = null;
881
+ useTabsStore.getState().setTabs((curr) => {
882
+ const tab = curr.find((t) => t.id === tabId && t.kind === "terminal");
883
+ if (!tab || tab.kind !== "terminal")
884
+ return curr;
885
+ if (!hasLeaf(tab.paneTree, leafId))
886
+ return curr;
887
+ const leaves = leafIds(tab.paneTree);
888
+ if (leaves.length <= 1)
889
+ return curr;
890
+ const newTree = removeLeaf(tab.paneTree, leafId);
891
+ if (!newTree)
892
+ return curr;
893
+ const remaining = leafIds(newTree);
894
+ const newActive = tab.activeLeafId === leafId ? remaining[0] : tab.activeLeafId;
895
+ newTabId = useTabsStore.getState().allocId();
896
+ const createdTab = {
897
+ kind: "terminal",
898
+ id: newTabId,
899
+ spaceId: tab.spaceId,
900
+ paneTree: { kind: "leaf", id: leafId },
901
+ activeLeafId: leafId,
902
+ cwd: tab.cwd,
903
+ title: tab.title,
904
+ };
905
+ return curr
906
+ .map((x) => (x.id === tabId ? { ...x, paneTree: newTree, activeLeafId: newActive } : x))
907
+ .concat(createdTab);
908
+ });
909
+ if (newTabId !== null) {
910
+ useTabsStore.getState().setActiveId(newTabId);
911
+ if (typeof window !== "undefined")
912
+ window.dispatchEvent(new Event("resize"));
913
+ }
914
+ return newTabId;
915
+ }
916
+ export function unsplitAllPanesToTabs(tabId) {
917
+ let createdTabIds = [];
918
+ useTabsStore.getState().setTabs((curr) => {
919
+ const tab = curr.find((t) => t.id === tabId && t.kind === "terminal");
920
+ if (!tab || tab.kind !== "terminal")
921
+ return curr;
922
+ const leaves = leafIds(tab.paneTree);
923
+ if (leaves.length <= 1)
924
+ return curr;
925
+ const firstLeaf = leaves[0];
926
+ const firstTab = {
927
+ ...tab,
928
+ paneTree: { kind: "leaf", id: firstLeaf },
929
+ activeLeafId: firstLeaf,
930
+ };
931
+ const extraTabs = leaves.slice(1).map((leafId) => {
932
+ const newId = useTabsStore.getState().allocId();
933
+ createdTabIds.push(newId);
934
+ return {
935
+ kind: "terminal",
936
+ id: newId,
937
+ spaceId: tab.spaceId,
938
+ paneTree: { kind: "leaf", id: leafId },
939
+ activeLeafId: leafId,
940
+ cwd: tab.cwd,
941
+ title: tab.title,
942
+ };
943
+ });
944
+ return curr.map((x) => (x.id === tabId ? firstTab : x)).concat(extraTabs);
945
+ });
946
+ if (typeof window !== "undefined")
947
+ window.dispatchEvent(new Event("resize"));
948
+ return createdTabIds;
949
+ }
950
+ export function closeOtherPanes(tabId, keepLeafId) {
951
+ const leavesToDispose = [];
952
+ useTabsStore.getState().setTabs((curr) => {
953
+ const tab = curr.find((t) => t.id === tabId && t.kind === "terminal");
954
+ if (!tab || tab.kind !== "terminal")
955
+ return curr;
956
+ const leaves = leafIds(tab.paneTree);
957
+ for (const l of leaves) {
958
+ if (l !== keepLeafId)
959
+ leavesToDispose.push(l);
960
+ }
961
+ return curr.map((x) => x.id === tabId
962
+ ? {
963
+ ...x,
964
+ paneTree: { kind: "leaf", id: keepLeafId },
965
+ activeLeafId: keepLeafId,
966
+ }
967
+ : x);
968
+ });
969
+ for (const l of leavesToDispose) {
970
+ layoutAdapter.disposeSession(l);
971
+ }
972
+ if (typeof window !== "undefined")
973
+ window.dispatchEvent(new Event("resize"));
974
+ }
975
+ //# sourceMappingURL=tabsActions.js.map