1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
// https://glide-browser.app/changelog#0.1.55a-split-views
// Firefox relocates a split's tabs in the strip when the split is created.
// Anchor the block back to where its leftmost member started.
// forEach/find avoid Symbol.iterator, which Xray vision denies on browser-API values.
async function create_split_view(tab_ids: TabID[]) {
const tabs = await browser.tabs.query({ currentWindow: true });
const members: { id: TabID; index: number }[] = [];
tab_ids.forEach((id) => {
const tab = tabs.find((t) => t.id === id);
if (tab) members.push({ id, index: tab.index });
});
if (members.length !== tab_ids.length) return;
members.sort((a, b) => a.index - b.index);
glide.unstable.split_views.create(tab_ids);
await browser.tabs.move(members.map((m) => m.id), { index: members[0]!.index });
}
glide.keymaps.set(
"normal",
"<C-w>v",
async ({ tab_id }) => {
await glide.excmds.execute("tab_new");
const new_tab = await glide.tabs.active();
await create_split_view([tab_id, new_tab.id]);
},
{
description: "Create a split view with a new tab",
},
);
glide.keymaps.set(
"normal",
"<C-w>j",
async ({ tab_id }) => {
const allTabs = await glide.tabs.query({});
const currentIndex = allTabs.findIndex((t) => t.id === tab_id);
const next = allTabs[currentIndex + 1];
if (next?.id == null) return;
await create_split_view([tab_id, next.id]);
},
{
description: "Split view with the next tab",
},
);
glide.keymaps.set(
"normal",
"<C-h>",
async ({ tab_id }) => {
const split = glide.unstable.split_views.get(tab_id);
if (!split) return;
const index = split.tabs.findIndex((t) => t.id === tab_id);
const target = split.tabs.at((index - 1) % split.tabs.length);
if (target?.id == null) return;
await browser.tabs.update(target.id, { active: true });
},
{
description: "Focus the split to the left",
},
);
glide.keymaps.set(
"normal",
"<C-l>",
async ({ tab_id }) => {
const split = glide.unstable.split_views.get(tab_id);
if (!split) return;
const index = split.tabs.findIndex((t) => t.id === tab_id);
const target = split.tabs.at((index + 1) % split.tabs.length);
if (target?.id == null) return;
await browser.tabs.update(target.id, { active: true });
},
{
description: "Focus the split to the right",
},
);
glide.keymaps.set(
"normal",
"<C-j>",
async ({ tab_id }) => {
const split = glide.unstable.split_views.get(tab_id);
if (!split) {
await glide.excmds.execute("tab_next");
return;
}
const splitIds = new Set(split.tabs.map((t) => t.id));
const all_tabs = await glide.tabs.query({});
const currentIndex = all_tabs.findIndex((t) => t.id === tab_id);
for (let i = 1; i < all_tabs.length; i++) {
const target = all_tabs[(currentIndex + i) % all_tabs.length];
if (target?.id && !splitIds.has(target.id)) {
await browser.tabs.update(target.id, { active: true });
return;
}
}
},
{ description: "Next tab (skipping split siblings)" },
);
glide.keymaps.set(
"normal",
"<C-k>",
async ({ tab_id }) => {
const split = glide.unstable.split_views.get(tab_id);
if (!split) {
await glide.excmds.execute("tab_prev");
return;
}
const splitIds = new Set(split.tabs.map((t) => t.id));
const all_tabs = await glide.tabs.query({});
const currentIndex = all_tabs.findIndex((t) => t.id === tab_id);
for (let i = 1; i < all_tabs.length; i++) {
const target = all_tabs.at((currentIndex - i) % all_tabs.length);
if (target?.id && !splitIds.has(target.id)) {
await browser.tabs.update(target.id, { active: true });
return;
}
}
},
{ description: "Previous tab (skipping split siblings)" },
);
glide.keymaps.set(
"normal",
"<C-w>f",
async ({ tab_id }) => {
glide.hints.show({
action: async ({ content }) => {
const href = await content.execute((target) => {
const anchor = target.closest("a");
return anchor?.href ?? null;
});
if (!href) return;
const existing = glide.unstable.split_views.get(tab_id);
if (existing) {
// Navigate the other pane instead of creating a new split.
const other = existing.tabs.find((t) => t.id !== tab_id);
if (other?.id) {
await browser.tabs.update(other.id, { url: href });
}
} else {
const new_tab = await browser.tabs.create({ url: href, active: false });
if (new_tab.id) {
await create_split_view([tab_id, new_tab.id]);
}
}
},
});
},
{
description: "Follow a hint in a split view",
},
);
glide.keymaps.set(
"normal",
"<C-w>u",
async ({ tab_id }) => {
glide.unstable.split_views.separate(tab_id);
},
{
description: "Unsplit: revert the current tab out of its split view",
},
);