Skip to content

Commit a6ac351

Browse files
committed
feat: keybinding to edit title of selected/active tab
(resolves #966) (resolves #1848) (resolves #1845)
1 parent d6e1bf8 commit a6ac351

File tree

14 files changed

+320
-18
lines changed

14 files changed

+320
-18
lines changed

build/scripts.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ async function main() {
298298
'src/popup.panel-config/panel-config.ts',
299299
'src/popup.proxy/proxy.ts',
300300
'src/popup.search/search.ts',
301+
'src/popup.editing/editing.ts',
301302
'src/_locales/dict.common.ts',
302303
'src/_locales/dict.sidebar.ts',
303304
'src/_locales/dict.setup-page.ts',

src/_locales/dict.browser.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,9 @@
276276
"zh_TW": "樹序列化(壓平選定分頁所在的樹)",
277277
"ja": "選択したタブをフラット化"
278278
},
279+
"KbEditTitle": {
280+
"en": "Edit title"
281+
},
279282
"KbOpenNewTabInCurrPanel": {
280283
"en": "Open a new tab in the active panel",
281284
"de": "Öffne neuen Tab im aktiven Panel",

src/defaults.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export const URL_URL = browser.runtime.getURL('/sidebery/url.html')
1212
export const URL_URL_LEN = URL_URL.length
1313
export const SETUP_URL = browser.runtime.getURL('/page.setup/setup.html')
1414
export const SEARCH_URL = browser.runtime.getURL('/popup.search/search.html')
15+
export const EDITING_POPUP_URL = browser.runtime.getURL('/popup.editing/editing.html')
1516
export const V4_GROUP_URL_LEN = 69
1617
export const V4_URL_URL_LEN = 65
1718
export const RGB_COLORS: Record<browser.ColorName, string> = {

src/manifest.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,9 @@
388388
"flatten_tabs": {
389389
"description": "__MSG_KbFlattenTabs__"
390390
},
391+
"edit_title": {
392+
"description": "__MSG_KbEditTitle__"
393+
},
391394
"sel_child_tabs": {
392395
"description": "__MSG_KbSelChildTabs__"
393396
},

src/page.setup/components/keybindings.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
KeybindingField(:keybinding="Keybindings.reactive.byName.group_tabs")
4646
KeybindingField(:keybinding="Keybindings.reactive.byName.group_tabs_act")
4747
KeybindingField(:keybinding="Keybindings.reactive.byName.flatten_tabs")
48+
KeybindingField(:keybinding="Keybindings.reactive.byName.edit_title")
4849

4950
section
5051
h2 {{translate('settings.kb_tabs_open')}}

src/popup.editing/editing.html

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<!DOCTYPE html>
2+
<html color-scheme="system-color">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
6+
<meta name="viewport" content="width=device-width, initial-scale=1">
7+
<style>
8+
:root {
9+
color-scheme: light dark;
10+
background-color: light-dark(#eee, #333);
11+
}
12+
html {
13+
padding: 0;
14+
margin: 0;
15+
overflow: clip;
16+
}
17+
body {
18+
--edit-bg: var(--s-popup-bg, var(--s-frame-bg, -moz-dialog));
19+
--edit-fg: var(--s-popup-fg, var(--s-frame-fg, -moz-dialogtext));
20+
width: 450px;
21+
background-color: var(--edit-bg);
22+
padding: 0 12px 0 0;
23+
margin: 0;
24+
}
25+
#textInput {
26+
position: relative;
27+
display: block;
28+
width: calc(100% - 56px);
29+
height: 32px;
30+
font-size: 1.25rem;
31+
line-height: 32px;
32+
color: var(--edit-fg);
33+
appearance: none;
34+
border: none;
35+
outline: none;
36+
margin: 0;
37+
padding: 10px 0 12px 56px;
38+
background-color: transparent;
39+
resize: none;
40+
z-index: 1;
41+
}
42+
#icon_edit {
43+
position: absolute;
44+
width: 24px;
45+
height: 24px;
46+
top: 14px;
47+
left: 16px;
48+
fill: var(--edit-fg);
49+
}
50+
</style>
51+
</head>
52+
<body id="root" class="root -animate">
53+
<div
54+
id="moz_dialog_color_scheme_probe"
55+
style="background-color: -moz-dialog; color: -moz-dialogtext; position: absolute; width: 0; height: 0;">
56+
</div>
57+
<inject>svg://src/assets/edit.svg#icon_edit</inject>
58+
<textarea id="textInput" type="text" spellcheck="false">.</textarea>
59+
<script type="module" src="./editing.js" defer></script>
60+
</body>
61+
</html>

src/popup.editing/editing.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { InstanceType } from 'src/types'
2+
import * as IPC from 'src/services/ipc'
3+
import { Info } from 'src/services/info'
4+
import { Settings } from 'src/services/settings'
5+
import { Styles } from 'src/services/styles'
6+
import { Windows } from 'src/services/windows'
7+
import { Logs } from 'src/services/_services'
8+
9+
const VERTICAL_MARGINS = 22
10+
const el = document.getElementById('textInput') as HTMLInputElement | null
11+
12+
el?.focus()
13+
14+
el?.addEventListener('blur', () => {
15+
if (Windows.id !== undefined) IPC.sendToSidebar(Windows.id, 'onOutsideEditingExit')
16+
})
17+
18+
let ctxMenuKeyPressed: number | undefined
19+
el?.addEventListener('keydown', (e: KeyboardEvent) => {
20+
// Enter
21+
if (e.key === 'Enter' && !e.altKey) {
22+
e.preventDefault()
23+
if (Windows.id !== undefined) IPC.sendToSidebar(Windows.id, 'onOutsideEditingEnter')
24+
}
25+
})
26+
27+
el?.addEventListener('contextmenu', (e: Event) => {
28+
if (ctxMenuKeyPressed !== undefined) e.preventDefault()
29+
})
30+
31+
el?.addEventListener('input', (e: Event) => {
32+
if (Windows.id !== undefined) IPC.sendToSidebar(Windows.id, 'onOutsideEditingInput', el.value)
33+
})
34+
35+
function closePopup(): void {
36+
window.close()
37+
}
38+
39+
void (async () => {
40+
Info.setInstanceType(InstanceType.editing)
41+
Logs.setInstanceType(InstanceType.editing)
42+
IPC.setInstanceType(InstanceType.editing)
43+
IPC.setupGlobalMessageListener()
44+
IPC.registerActions({ closePopup })
45+
46+
const sp = new URLSearchParams(document.location.search)
47+
const winIdStr = sp.get('winId')
48+
if (!winIdStr) return
49+
50+
const winId = parseInt(winIdStr)
51+
if (isNaN(winId)) return
52+
53+
const value = sp.get('value')
54+
if (value && el) {
55+
el.value = value
56+
57+
// 1px less, so later I can update height to fix graphical glitches
58+
el.style.height = `${el.scrollHeight - VERTICAL_MARGINS - 1}px`
59+
}
60+
61+
const placeholder = sp.get('placeholder')
62+
if (placeholder && el) el.placeholder = placeholder
63+
64+
if (winId !== undefined) {
65+
IPC.setWinId(winId)
66+
Windows.id = winId
67+
IPC.connectTo(InstanceType.sidebar, Windows.id)
68+
}
69+
70+
Settings.loadSettings().then(() => Styles.initColorScheme())
71+
72+
setTimeout(() => {
73+
if (el) {
74+
// Focus input again, although there are still cases of popups with no focus
75+
// https://bugzilla.mozilla.org/show_bug.cgi?id=1918031
76+
el.focus()
77+
el.select()
78+
79+
// Update height to fix graphical glitches
80+
el.style.height = `${el.scrollHeight - VERTICAL_MARGINS}px`
81+
}
82+
}, 3)
83+
84+
setTimeout(() => {
85+
// And again...
86+
if (el) {
87+
el.focus()
88+
el.select()
89+
}
90+
}, 250)
91+
})()

src/services/info.actions.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export function setInstanceType(t: InstanceType): void {
3838
else if (t === InstanceType.url) Info.isUrl = true
3939
else if (t === InstanceType.proxy) Info.isProxy = true
4040
else if (t === InstanceType.search) Info.isSearch = true
41+
else if (t === InstanceType.editing) Info.isEditing = true
4142
else if (t === InstanceType.preview) Info.isPreview = true
4243
else if (t === InstanceType.sync) Info.isSync = true
4344
else if (t === InstanceType.panelConfig) Info.isPanelConfig = true
@@ -51,6 +52,7 @@ export function getInstanceName(instance?: InstanceType): string {
5152
else if (instance === InstanceType.proxy) return 'proxy'
5253
else if (instance === InstanceType.url) return 'url'
5354
else if (instance === InstanceType.search) return 'search'
55+
else if (instance === InstanceType.editing) return 'editing'
5456
else if (instance === InstanceType.preview) return 'preview'
5557
else if (instance === InstanceType.sync) return 'sync'
5658
else if (instance === InstanceType.panelConfig) return 'panel-config'

src/services/info.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export const Info = {
2121
isUrl: false,
2222
isBg: false,
2323
isSearch: false,
24+
isEditing: false,
2425
isPreview: false,
2526
isSync: false,
2627
isPanelConfig: false,

src/services/ipc.ts

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ let _localTabId = NOID
7272
export const state = {
7373
bgConnection: undefined as ConnectionInfo | undefined,
7474
searchPopupConnections: new Map<ID, ConnectionInfo>(),
75+
editingPopupConnections: new Map<ID, ConnectionInfo>(),
7576
sidebarConnections: new Map<ID, ConnectionInfo>(),
7677
setupPageConnections: new Map<ID, ConnectionInfo>(),
7778
groupPageConnections: new Map<ID, ConnectionInfo>(),
@@ -101,6 +102,7 @@ export function isConnected(type: InstanceType, id = NOID): boolean {
101102
else if (type === InstanceType.sidebar) return state.sidebarConnections.has(id)
102103
else if (type === InstanceType.setup) return state.setupPageConnections.has(id)
103104
else if (type === InstanceType.search) return state.searchPopupConnections.has(id)
105+
else if (type === InstanceType.editing) return state.editingPopupConnections.has(id)
104106
else if (type === InstanceType.group) return state.groupPageConnections.has(id)
105107
else if (type === InstanceType.sync) return state.syncConnections.has(id)
106108
else if (type === InstanceType.panelConfig) return state.panelConfigConnections.has(id)
@@ -113,18 +115,20 @@ export function getConnection(type: InstanceType, id: ID): ConnectionInfo | unde
113115
else if (type === InstanceType.sidebar) return state.sidebarConnections.get(id)
114116
else if (type === InstanceType.setup) return state.setupPageConnections.get(id)
115117
else if (type === InstanceType.search) return state.searchPopupConnections.get(id)
118+
else if (type === InstanceType.editing) return state.editingPopupConnections.get(id)
116119
else if (type === InstanceType.group) return state.groupPageConnections.get(id)
117120
else if (type === InstanceType.sync) return state.syncConnections.get(id)
118121
else if (type === InstanceType.panelConfig) return state.panelConfigConnections.get(id)
119122
else if (type === InstanceType.preview) return state.previewConnection
120123
}
121124

122125
function removeConnection(type: InstanceType, id: ID) {
123-
Logs.info('IPC.REMOVE:', getInstanceName(type), id)
126+
// Logs.info('IPC.REMOVE:', getInstanceName(type), id)
124127
if (type === InstanceType.bg) state.bgConnection = undefined
125128
else if (type === InstanceType.sidebar) state.sidebarConnections.delete(id)
126129
else if (type === InstanceType.setup) state.setupPageConnections.delete(id)
127130
else if (type === InstanceType.search) state.searchPopupConnections.delete(id)
131+
else if (type === InstanceType.editing) state.editingPopupConnections.delete(id)
128132
else if (type === InstanceType.sync) state.syncConnections.delete(id)
129133
else if (type === InstanceType.panelConfig) state.panelConfigConnections.delete(id)
130134
else if (type === InstanceType.group) state.groupPageConnections.delete(id)
@@ -144,6 +148,7 @@ function createConnection(type: InstanceType, id: ID): ConnectionInfo {
144148
else if (type === InstanceType.sidebar) state.sidebarConnections.set(id, connection)
145149
else if (type === InstanceType.setup) state.setupPageConnections.set(id, connection)
146150
else if (type === InstanceType.search) state.searchPopupConnections.set(id, connection)
151+
else if (type === InstanceType.editing) state.editingPopupConnections.set(id, connection)
147152
else if (type === InstanceType.group) state.groupPageConnections.set(id, connection)
148153
else if (type === InstanceType.sync) state.syncConnections.set(id, connection)
149154
else if (type === InstanceType.panelConfig) state.panelConfigConnections.set(id, connection)
@@ -167,6 +172,7 @@ export function connectTo(
167172
const toSidebar = dstType === InstanceType.sidebar
168173
const toSetup = dstType === InstanceType.setup
169174
const toSearch = dstType === InstanceType.search
175+
const toEditing = dstType === InstanceType.editing
170176
const toSync = dstType === InstanceType.sync
171177
const toPanelConfig = dstType === InstanceType.panelConfig
172178
const toGroup = dstType === InstanceType.group
@@ -176,8 +182,9 @@ export function connectTo(
176182
// Check destination id
177183
let id
178184
if (toBg || toPreview) id = NOID
179-
else if ((toSidebar || toSearch || toSync || toPanelConfig) && dstWinId !== NOID) id = dstWinId
180-
else if ((toSetup || toGroup) && dstTabId !== NOID) id = dstTabId
185+
else if ((toSidebar || toSearch || toEditing || toSync || toPanelConfig) && dstWinId !== NOID) {
186+
id = dstWinId
187+
} else if ((toSetup || toGroup) && dstTabId !== NOID) id = dstTabId
181188
else {
182189
Logs.err(`${dbgPrefix} No destination id`)
183190
return
@@ -187,7 +194,7 @@ export function connectTo(
187194
const portNameData: PortNameData = { srcType, dstType }
188195
if (srcWinId !== NOID) portNameData.srcWinId = srcWinId
189196
if (srcTabId !== NOID) portNameData.srcTabId = srcTabId
190-
if (dstWinId !== NOID && (toSidebar || toSearch || toSync || toPanelConfig)) {
197+
if (dstWinId !== NOID && (toSidebar || toSearch || toEditing || toSync || toPanelConfig)) {
191198
portNameData.dstWinId = dstWinId
192199
}
193200
if (dstTabId !== NOID && (toSetup || toGroup)) portNameData.dstTabId = dstTabId
@@ -274,7 +281,7 @@ export function connectTo(
274281
// Reset reconnection count after 5s
275282
clearTimeout(connection.reconnectingTimeout)
276283
connection.reconnectingTimeout = setTimeout(() => {
277-
Logs.info(`${dbgPrefix} Reseting reconnection count:`, connection.reconnectCount)
284+
// Logs.info(`${dbgPrefix} Reseting reconnection count:`, connection.reconnectCount)
278285
connection.reconnectCount = 0
279286
}, RESET_RECON_COUNT_TIMEOUT)
280287

@@ -304,7 +311,7 @@ export function connectTo(
304311
timeout,
305312
portName: '',
306313
ok: () => {
307-
Logs.info(`IPC.connectTo(${getInstanceName(dstType)}): CONFIRMED`)
314+
// Logs.info(`IPC.connectTo(${getInstanceName(dstType)}): CONFIRMED`)
308315
connection.state = ConnectionState.Ready
309316
if (connectionIsNew) {
310317
const handlers = connectionHandlers.get(dstType)
@@ -426,6 +433,17 @@ export function sendToSearchPopup<T extends InstanceType.search, A extends Actio
426433
send({ dstType: InstanceType.search, dstWinId, action, args })
427434
}
428435

436+
/**
437+
* Sends message to editing popup.
438+
*/
439+
export function sendToEditingPopup<T extends InstanceType.editing, A extends ActionsKeys<T>>(
440+
dstWinId: ID,
441+
action: A,
442+
...args: Parameters<ActionsType<T>[A]>
443+
): void {
444+
send({ dstType: InstanceType.editing, dstWinId, action, args })
445+
}
446+
429447
/**
430448
* Sends message to background.
431449
*/
@@ -460,6 +478,7 @@ export function send<T extends InstanceType, A extends ActionsKeys<T>>(msg: Mess
460478
if (msg.dstType === InstanceType.sidebar && msg.dstWinId !== undefined) id = msg.dstWinId
461479
else if (msg.dstType === InstanceType.setup && msg.dstTabId !== undefined) id = msg.dstTabId
462480
else if (msg.dstType === InstanceType.search && msg.dstWinId !== undefined) id = msg.dstWinId
481+
else if (msg.dstType === InstanceType.editing && msg.dstWinId !== undefined) id = msg.dstWinId
463482
else if (msg.dstType === InstanceType.group && msg.dstTabId !== undefined) id = msg.dstTabId
464483

465484
// Get port
@@ -521,6 +540,7 @@ export async function request<T extends InstanceType, A extends ActionsKeys<T>>(
521540
if (dstType === InstanceType.sidebar && msg.dstWinId !== undefined) id = msg.dstWinId
522541
else if (dstType === InstanceType.setup && msg.dstTabId !== undefined) id = msg.dstTabId
523542
else if (dstType === InstanceType.search && msg.dstWinId !== undefined) id = msg.dstWinId
543+
else if (dstType === InstanceType.editing && msg.dstWinId !== undefined) id = msg.dstWinId
524544
else if (dstType === InstanceType.sync && msg.dstWinId !== undefined) id = msg.dstWinId
525545
else if (dstType === InstanceType.panelConfig && msg.dstWinId !== undefined) id = msg.dstWinId
526546
else if (dstType === InstanceType.group && msg.dstTabId !== undefined) id = msg.dstTabId
@@ -668,13 +688,15 @@ function onConnect(port: browser.runtime.Port) {
668688
const fromSidebar = srcType === InstanceType.sidebar
669689
const fromSetup = srcType === InstanceType.setup
670690
const fromSearch = srcType === InstanceType.search
691+
const fromEditing = srcType === InstanceType.editing
671692
const fromGroup = srcType === InstanceType.group
672693
const fromSync = srcType === InstanceType.sync
673694
const fromPanelConfig = srcType === InstanceType.panelConfig
674695
const fromPreview = srcType === InstanceType.preview
675696
if (fromSidebar && srcWinId === NOID) return Logs.err('IPC.onConnect: Sidebar: No srcWinId')
676697
if (fromSetup && srcTabId === NOID) return Logs.err('IPC.onConnect: Setup page: No srcTabId')
677698
if (fromSearch && srcWinId === NOID) return Logs.err('IPC.onConnect: Search popup: No srcWinId')
699+
if (fromEditing && srcWinId === NOID) return Logs.err('IPC.onConnect: Editing popup: No srcWinId')
678700
if (fromSync && srcWinId === NOID) return Logs.err('IPC.onConnect: Sync: No srcWinId')
679701
if (fromPanelConfig && srcWinId === NOID) return Logs.err('IPC.onConnect: PanelConf: No srcWinId')
680702
if (fromGroup && srcTabId === NOID) return Logs.err('IPC.onConnect: Group page: No srcTabId')
@@ -683,7 +705,10 @@ function onConnect(port: browser.runtime.Port) {
683705
let id
684706
if (fromBg || fromPreview) {
685707
id = NOID
686-
} else if ((fromSidebar || fromSearch || fromSync || fromPanelConfig) && srcWinId !== NOID) {
708+
} else if (
709+
(fromSidebar || fromSearch || fromEditing || fromSync || fromPanelConfig) &&
710+
srcWinId !== NOID
711+
) {
687712
id = srcWinId
688713
} else if ((fromSetup || fromGroup) && srcTabId !== NOID) {
689714
id = srcTabId
@@ -764,6 +789,7 @@ export function onConnected(type: InstanceType, cb: (winOrTabId: ID) => void) {
764789
if (type === InstanceType.sidebar) state.sidebarConnections.forEach(con => cb(con.id))
765790
if (type === InstanceType.setup) state.setupPageConnections.forEach(con => cb(con.id))
766791
if (type === InstanceType.search) state.searchPopupConnections.forEach(con => cb(con.id))
792+
if (type === InstanceType.editing) state.editingPopupConnections.forEach(con => cb(con.id))
767793
if (type === InstanceType.sync) state.syncConnections.forEach(con => cb(con.id))
768794
if (type === InstanceType.panelConfig) state.panelConfigConnections.forEach(con => cb(con.id))
769795
if (type === InstanceType.group) state.groupPageConnections.forEach(con => cb(con.id))

0 commit comments

Comments
 (0)