copyhub-cli 1.0.6 → 1.0.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.
- package/.env.example +2 -1
- package/README.md +195 -133
- package/package.json +1 -1
- package/src/cli.js +108 -70
- package/src/oauth.js +16 -12
- package/ui/main.mjs +51 -9
package/.env.example
CHANGED
|
@@ -16,7 +16,8 @@ COPYHUB_GOOGLE_CLIENT_ID=
|
|
|
16
16
|
COPYHUB_GOOGLE_CLIENT_SECRET=
|
|
17
17
|
COPYHUB_OAUTH_REDIRECT_PORT=19999
|
|
18
18
|
|
|
19
|
-
# Overlay accelerator (optional):
|
|
19
|
+
# Overlay accelerator (optional): overrides config. Default app-wide is Control+Shift+H (⌃/Ctrl + Shift + H).
|
|
20
|
+
# Example Mac ⌘ shortcut: COPYHUB_OVERLAY_ACCELERATOR=CommandOrControl+Shift+H
|
|
20
21
|
COPYHUB_OVERLAY_ACCELERATOR=
|
|
21
22
|
|
|
22
23
|
# Set to 1 to NOT show the window when overlay starts (open via shortcut / tray only).
|
package/README.md
CHANGED
|
@@ -1,229 +1,291 @@
|
|
|
1
1
|
# CopyHub
|
|
2
2
|
|
|
3
|
-
CopyHub watches your **clipboard**,
|
|
3
|
+
CopyHub watches your **clipboard**, stores **local history** (`~/.copyhub/history.jsonl`), optionally syncs to **Google Sheets** (one tab per day), and opens an **Electron overlay** to browse and pick recent copies.
|
|
4
4
|
|
|
5
5
|
Runs on **Windows**, **macOS**, and **Linux**.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
---
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
- A **Google Cloud** project with:
|
|
11
|
-
- **Google Sheets API** enabled for the *same* project as your OAuth client
|
|
12
|
-
- **OAuth 2.0 Client** (Desktop app type works well for localhost redirect)
|
|
9
|
+
## Table of contents
|
|
13
10
|
|
|
14
|
-
|
|
11
|
+
Sections below: **Features** · **Requirements** · **Installation** · **Environment files** · **Google Cloud & OAuth** · **OAuth config vs env** · **First run** · **CLI commands** · **Environment variables** · **Data directory** · **Google Sheets** · **Overlay** · **Clipboard & history** · **Updating** · **Troubleshooting** · **Security** · **License**.
|
|
15
12
|
|
|
16
|
-
|
|
13
|
+
---
|
|
17
14
|
|
|
18
|
-
|
|
15
|
+
## Features
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
- Clipboard polling (tunable via `COPYHUB_POLL_MS`).
|
|
18
|
+
- Skips saving the **same content twice in a row** to `history.jsonl` / Sheets.
|
|
19
|
+
- Writes Sheets to tabs named **`COPYHUB-YYYY-MM-DD`** (machine timezone).
|
|
20
|
+
- Overlay: paginated history, incremental Sheet sync (not all tabs at once), hints while Sheet data loads.
|
|
23
21
|
|
|
24
|
-
|
|
22
|
+
---
|
|
25
23
|
|
|
26
|
-
|
|
24
|
+
## Requirements
|
|
25
|
+
|
|
26
|
+
- **Node.js** ≥ 18
|
|
27
|
+
- A **Google Cloud project** with:
|
|
28
|
+
- **Google Sheets API** enabled **on the same project** as the OAuth client
|
|
29
|
+
- OAuth client type **Web application** and redirect URI configured correctly (see below)
|
|
27
30
|
|
|
28
|
-
|
|
31
|
+
---
|
|
29
32
|
|
|
30
|
-
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
### Global install (npm)
|
|
31
36
|
|
|
32
37
|
```bash
|
|
33
|
-
npm install
|
|
38
|
+
npm install -g copyhub-cli
|
|
34
39
|
```
|
|
35
40
|
|
|
36
|
-
|
|
41
|
+
Ensure `node` and `copyhub` are on your `PATH`. On Linux/macOS you may need an npm global prefix for your user — see [npm global installation](https://docs.npmjs.com/cli/v10/commands/npm-install#global-installation).
|
|
42
|
+
|
|
43
|
+
### From source (this repo)
|
|
37
44
|
|
|
38
45
|
```bash
|
|
46
|
+
npm install
|
|
39
47
|
npm link
|
|
40
48
|
```
|
|
41
49
|
|
|
42
|
-
|
|
50
|
+
Without linking:
|
|
43
51
|
|
|
44
52
|
```bash
|
|
45
53
|
node src/cli.js <command>
|
|
46
54
|
```
|
|
47
55
|
|
|
48
|
-
|
|
56
|
+
---
|
|
49
57
|
|
|
50
|
-
|
|
58
|
+
## Environment files
|
|
51
59
|
|
|
52
|
-
**
|
|
60
|
+
The CLI and Electron overlay call `loadCopyhubEnv()`: each `.env` file is parsed and merged into **one object** — **later files override keys** from earlier ones. Then each key is applied to `process.env` **only if that variable is not already set** in the process environment (values you `export` in the shell before starting Node always win).
|
|
53
61
|
|
|
54
|
-
|
|
55
|
-
copyhub stop
|
|
56
|
-
```
|
|
62
|
+
File order:
|
|
57
63
|
|
|
58
|
-
|
|
64
|
+
1. `<package>/.env` (installed package directory / repo when developing)
|
|
65
|
+
2. `~/.copyhub/.env`
|
|
66
|
+
3. `./.env` in the **current working directory** (`cwd`)
|
|
59
67
|
|
|
60
|
-
|
|
68
|
+
So after `npm install -g`, variables in `~/.copyhub/.env` still load regardless of `cwd`.
|
|
61
69
|
|
|
62
|
-
|
|
70
|
+
See the template: `.env.example`.
|
|
63
71
|
|
|
64
|
-
|
|
65
|
-
npm view copyhub-cli version
|
|
66
|
-
```
|
|
72
|
+
---
|
|
67
73
|
|
|
68
|
-
|
|
74
|
+
## Google Cloud & OAuth
|
|
69
75
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
76
|
+
1. Enable **[Google Sheets API](https://console.cloud.google.com/apis/library/sheets.googleapis.com)** for your project.
|
|
77
|
+
2. **Credentials** → **Create credentials** → **OAuth client ID** → **Web application**.
|
|
78
|
+
3. **Authorized redirect URIs** — add **exactly** (CopyHub uses `127.0.0.1`, not `localhost`, for the default redirect):
|
|
73
79
|
|
|
74
|
-
|
|
80
|
+
```text
|
|
81
|
+
http://127.0.0.1:19999/oauth2callback
|
|
82
|
+
```
|
|
75
83
|
|
|
76
|
-
|
|
77
|
-
npm update -g copyhub-cli
|
|
78
|
-
```
|
|
84
|
+
If you change the port (`COPYHUB_OAUTH_REDIRECT_PORT` or `redirectPort` in config), the Console URI must use that port.
|
|
79
85
|
|
|
80
|
-
|
|
86
|
+
4. **Do not** mix Client ID from env with Secret from file (CopyHub refuses mixed half-pairs). Credential precedence — see the next section.
|
|
81
87
|
|
|
82
|
-
|
|
83
|
-
copyhub start
|
|
84
|
-
```
|
|
88
|
+
### How to supply Client ID / Secret
|
|
85
89
|
|
|
86
|
-
|
|
90
|
+
| Method | Notes |
|
|
91
|
+
|--------|--------|
|
|
92
|
+
| **`copyhub login`** | Recommended first time: localhost wizard for ID/Secret → saves `config.json` → Google sign-in → spreadsheet ID / shortcut setup. |
|
|
93
|
+
| **`copyhub config --client-id … --client-secret …`** | Writes `config.json` directly. |
|
|
94
|
+
| **`.env`** or shell | Use only when **`config.json` does not** contain a full ID+Secret pair, or you intentionally rely on env only (no OAuth in config). |
|
|
87
95
|
|
|
88
|
-
|
|
96
|
+
On the wizard (Mac/Safari): prefer **Download JSON** from the Console and paste `client_id` / `client_secret`; clear the fields before pasting to avoid Keychain filling an old secret.
|
|
89
97
|
|
|
90
|
-
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## OAuth: config vs env (important)
|
|
101
|
+
|
|
102
|
+
- If **`~/.copyhub/config.json` contains both** `clientId` **and** `clientSecret` → CopyHub **always uses the file pair** for OAuth; **`COPYHUB_GOOGLE_*` from env/.env are ignored** for those two fields.
|
|
103
|
+
- If the file **does not** have both → use **`COPYHUB_GOOGLE_CLIENT_ID`** + **`COPYHUB_GOOGLE_CLIENT_SECRET`** from env (merged from `.env`).
|
|
104
|
+
- Never combine ID from env with Secret from file (or the reverse).
|
|
105
|
+
|
|
106
|
+
ID/Secret values are **sanitized** on read/write (BOM, CRLF, NBSP, zero-width characters, stray brackets around strings).
|
|
107
|
+
|
|
108
|
+
Check which source is active:
|
|
91
109
|
|
|
92
110
|
```bash
|
|
93
|
-
copyhub
|
|
94
|
-
git pull
|
|
95
|
-
npm install
|
|
111
|
+
copyhub status
|
|
96
112
|
```
|
|
97
113
|
|
|
98
|
-
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## First run
|
|
99
117
|
|
|
100
118
|
```bash
|
|
101
|
-
|
|
119
|
+
copyhub login
|
|
102
120
|
```
|
|
103
121
|
|
|
104
|
-
|
|
122
|
+
1. If OAuth is not fully configured in config/env → the browser opens **`http://127.0.0.1:<port>/credentials`** to enter Client ID / Secret.
|
|
123
|
+
2. Then Google sign-in; callback **`/oauth2callback`**.
|
|
124
|
+
3. Setup page: **Spreadsheet ID** (from URL `…/d/<ID>/edit`), **platform**, **overlay shortcut** (optional).
|
|
125
|
+
|
|
126
|
+
Start the daemon (clipboard + Sheet + overlay by default):
|
|
105
127
|
|
|
106
128
|
```bash
|
|
107
129
|
copyhub start
|
|
108
130
|
```
|
|
109
131
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
1. Enable **[Google Sheets API](https://console.cloud.google.com/apis/library/sheets.googleapis.com)** on your OAuth project.
|
|
113
|
-
2. Create **OAuth 2.0 credentials** and add this **Authorized redirect URI** (adjust the port if you change it):
|
|
114
|
-
|
|
115
|
-
```text
|
|
116
|
-
http://127.0.0.1:19999/oauth2callback
|
|
117
|
-
```
|
|
132
|
+
You can close the terminal; the process runs in the background. Use `copyhub list`, stop with `copyhub stop`.
|
|
118
133
|
|
|
119
|
-
|
|
134
|
+
After editing `config.json`, `~/.copyhub/.env`, or shell variables that affect the daemon/overlay, **reload** without manual stop/start:
|
|
120
135
|
|
|
121
|
-
|
|
136
|
+
```bash
|
|
137
|
+
copyhub restart
|
|
138
|
+
```
|
|
122
139
|
|
|
123
|
-
|
|
140
|
+
(Same flags as `start`: `--no-sheet`, `--no-overlay`, `--foreground`.)
|
|
124
141
|
|
|
125
|
-
|
|
142
|
+
Foreground (Ctrl+C stops everything):
|
|
126
143
|
|
|
127
144
|
```bash
|
|
128
|
-
copyhub
|
|
145
|
+
copyhub start --foreground
|
|
129
146
|
```
|
|
130
147
|
|
|
131
|
-
|
|
148
|
+
---
|
|
132
149
|
|
|
133
|
-
|
|
150
|
+
## CLI commands
|
|
134
151
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
152
|
+
| Command | Description |
|
|
153
|
+
|---------|-------------|
|
|
154
|
+
| `copyhub config --client-id ID --client-secret SEC [--redirect-port P] [--sheet-id ID]` | Writes OAuth (and optional Sheet ID, port) to `config.json`. |
|
|
155
|
+
| `copyhub login` | OAuth flow + browser setup. |
|
|
156
|
+
| `copyhub logout` | Deletes `tokens.json` (config unchanged). |
|
|
157
|
+
| `copyhub status` | OAuth, Sheet, token, overlay, daemon. |
|
|
158
|
+
| `copyhub start [--no-sheet] [--no-overlay] [--foreground]` | Default **background**; single instance. |
|
|
159
|
+
| `copyhub restart [--no-sheet] [--no-overlay] [--foreground]` | Stops the daemon if running, then **`start`s again** — reloads config, `.env`, overlay shortcut. |
|
|
160
|
+
| `copyhub list` / `copyhub ls` | Daemon PID (if any). |
|
|
161
|
+
| `copyhub stop` | Stops daemon and child overlay. |
|
|
162
|
+
| `copyhub overlay` | Electron window only (no clipboard daemon). |
|
|
163
|
+
| `copyhub reset --yes` | **Deletes all** of `~/.copyhub` (config, tokens, history, run state). `.env` files outside that folder are untouched. |
|
|
164
|
+
| `copyhub commands` / `copyhub cmds` | Quick command list. |
|
|
165
|
+
| `copyhub --help` | Commander help. |
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Environment variables
|
|
170
|
+
|
|
171
|
+
| Variable | Meaning |
|
|
172
|
+
|----------|---------|
|
|
173
|
+
| `COPYHUB_GOOGLE_CLIENT_ID` | OAuth Client ID (only used when config **does not** contain a full ID+Secret pair). |
|
|
174
|
+
| `COPYHUB_GOOGLE_CLIENT_SECRET` | OAuth Client Secret (same rule). |
|
|
175
|
+
| `COPYHUB_OAUTH_REDIRECT_PORT` | Localhost port for OAuth (default `19999`). Must match redirect URI in Google Console. |
|
|
176
|
+
| `COPYHUB_OVERLAY_ACCELERATOR` | Electron shortcut ([Accelerator](https://www.electronjs.org/docs/latest/api/accelerator)); **overrides** config when set. |
|
|
177
|
+
| `COPYHUB_START_NO_OVERLAY` | `=1` → `copyhub start` does not spawn overlay. |
|
|
178
|
+
| `COPYHUB_OVERLAY_STICKY` | `=1` → overlay does not hide on blur (only Esc / picking a row). |
|
|
179
|
+
| `COPYHUB_OVERLAY_HIDE_ON_START` | `=1` → do not show window at overlay startup (open via shortcut/tray). |
|
|
180
|
+
| `COPYHUB_OVERLAY_SKIP_TASKBAR` | `=1` → hide from taskbar (Windows/Electron). |
|
|
181
|
+
| `COPYHUB_POLL_MS` | Clipboard poll interval (ms). |
|
|
182
|
+
|
|
183
|
+
Electron inherits `process.env` from the daemon/CLI parent, so these apply once present in that environment.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Data directory
|
|
188
|
+
|
|
189
|
+
Everything lives under **`~/.copyhub/`** (Windows: **`%USERPROFILE%\.copyhub`**):
|
|
138
190
|
|
|
139
|
-
|
|
140
|
-
|
|
191
|
+
| File | Contents |
|
|
192
|
+
|------|----------|
|
|
193
|
+
| `config.json` | OAuth (`clientId`, `clientSecret`, `redirectPort`), `googleSheetId`, `overlayAccelerator`, `overlayPlatform`, … |
|
|
194
|
+
| `tokens.json` | OAuth refresh / access tokens |
|
|
195
|
+
| `history.jsonl` | Clipboard history (JSON Lines) |
|
|
196
|
+
| `run.json` | PID and metadata when `copyhub start` runs in the background |
|
|
141
197
|
|
|
142
|
-
|
|
198
|
+
---
|
|
143
199
|
|
|
144
|
-
|
|
200
|
+
## Google Sheets
|
|
145
201
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
202
|
+
- Appends rows when Sheets are enabled and tokens are valid.
|
|
203
|
+
- New tab per **calendar day**: `COPYHUB-YYYY-MM-DD`.
|
|
204
|
+
- The spreadsheet must be shared with the Google account used for OAuth (or owned by that account).
|
|
205
|
+
- If the API reports disabled / permission errors: check logs — some errors include Enable API links from formatted messages in code.
|
|
149
206
|
|
|
150
|
-
|
|
207
|
+
---
|
|
151
208
|
|
|
152
|
-
|
|
209
|
+
## Overlay (Electron)
|
|
153
210
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
| Override shortcut | `COPYHUB_OVERLAY_ACCELERATOR` in `.env` (overrides saved config) |
|
|
160
|
-
| Overlay stays open when clicking outside | `COPYHUB_OVERLAY_STICKY=1` |
|
|
211
|
+
- Default shortcut (**all platforms**, including macOS): **`Control+Shift+H`** — **⌃ Control** (bottom-left on Apple keyboards) or **Ctrl** on PC layouts — **same physical position**, not ⌘ / Win.
|
|
212
|
+
- For **⌘ Command + Shift + H** on Mac: set `overlayAccelerator` to `Command+Shift+H` (avoid `CommandOrControl+…` — on macOS Electron maps that to ⌘, so **⌃ Control** will not trigger the overlay). Legacy preset `CommandOrControl+Shift+H` is migrated to ⌃+Shift+H when the overlay starts.
|
|
213
|
+
- **macOS**: you may need **Accessibility** (*Privacy & Security*) for the app that launches Electron (Terminal, iTerm, …). If shortcuts still fail with non-US layouts, try **Input Source QWERTY** (Electron `globalShortcut` limitation).
|
|
214
|
+
- Overlay paginates ~10 items; Sheet data loads incrementally (not all tabs at once).
|
|
215
|
+
- Click outside the window usually closes the overlay (unless `COPYHUB_OVERLAY_STICKY=1`). **Esc** closes.
|
|
161
216
|
|
|
162
|
-
|
|
217
|
+
---
|
|
163
218
|
|
|
164
|
-
##
|
|
219
|
+
## Clipboard & history
|
|
220
|
+
|
|
221
|
+
- Watcher skips consecutive clipboard duplicates (same hash).
|
|
222
|
+
- Before writing file/Sheet, if content **exactly matches the newest row** in `history.jsonl`, it is **skipped** (avoids re-saving the same string after clipboard churn).
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Updating
|
|
165
227
|
|
|
166
|
-
|
|
228
|
+
`~/.copyhub` data is kept when upgrading the package.
|
|
167
229
|
|
|
168
230
|
```bash
|
|
169
|
-
copyhub config --client-id "<ID>" --client-secret "<SECRET>" [--redirect-port 19999] [--sheet-id "<SPREADSHEET_ID>"]
|
|
170
|
-
copyhub login
|
|
171
|
-
copyhub logout
|
|
172
|
-
copyhub status
|
|
173
|
-
copyhub start [--no-sheet] [--no-overlay] [--foreground]
|
|
174
|
-
copyhub list # alias: copyhub ls
|
|
175
231
|
copyhub stop
|
|
176
|
-
copyhub
|
|
177
|
-
copyhub
|
|
178
|
-
copyhub --help
|
|
232
|
+
npm install -g copyhub-cli@latest
|
|
233
|
+
copyhub start
|
|
179
234
|
```
|
|
180
235
|
|
|
181
|
-
|
|
182
|
-
|---------|----------------|
|
|
183
|
-
| `copyhub config` | Writes OAuth client ID/secret (and optional Sheet ID, redirect port) to `~/.copyhub/config.json`. `--client-id` and `--client-secret` are required. |
|
|
184
|
-
| `copyhub login` | Opens browser: localhost wizard for Client ID/secret if missing, then Google OAuth, then spreadsheet/platform setup. |
|
|
185
|
-
| `copyhub logout` | Deletes saved OAuth tokens (`~/.copyhub/tokens.json`). |
|
|
186
|
-
| `copyhub status` | Prints OAuth config source, sheet target, tokens, overlay settings, and whether the background daemon is running. |
|
|
187
|
-
| `copyhub start` | Starts clipboard watcher + optional Sheets sync + Electron overlay in the **background** (closing the terminal does not stop it). Only one instance at a time. |
|
|
188
|
-
| `copyhub start --foreground` | Same as above but attached to the terminal; **Ctrl+C** stops everything. |
|
|
189
|
-
| `copyhub start --no-sheet` | Local history only; no Google Sheets writes. |
|
|
190
|
-
| `copyhub start --no-overlay` | No Electron window; use env `COPYHUB_START_NO_OVERLAY=1` for the same effect. |
|
|
191
|
-
| `copyhub list` / `copyhub ls` | Shows PID and start time if the daemon from `copyhub start` is running. |
|
|
192
|
-
| `copyhub stop` | Stops the background daemon and its overlay child process. |
|
|
193
|
-
| `copyhub overlay` | Runs **only** the Electron overlay (no clipboard daemon). Useful if you run the daemon separately or for debugging. |
|
|
236
|
+
(If you only changed config / `.env`: `copyhub restart`.)
|
|
194
237
|
|
|
195
|
-
|
|
238
|
+
From source: `git pull`, `npm install`, then `copyhub start` (or `npm link` while developing).
|
|
196
239
|
|
|
197
|
-
|
|
240
|
+
---
|
|
198
241
|
|
|
199
|
-
|
|
200
|
-
|------|----------|
|
|
201
|
-
| `config.json` | OAuth credentials (if not only in `.env`), `googleSheetId`, `overlayAccelerator`, `overlayPlatform` |
|
|
202
|
-
| `tokens.json` | OAuth refresh/access tokens |
|
|
203
|
-
| `history.jsonl` | Local clipboard history (JSON Lines) |
|
|
204
|
-
| `run.json` | Daemon PID and metadata (when using `copyhub start` without `--foreground`) |
|
|
242
|
+
## Troubleshooting
|
|
205
243
|
|
|
206
|
-
|
|
244
|
+
### `invalid_client` or “client secret is invalid” after Google sign-in
|
|
207
245
|
|
|
208
|
-
-
|
|
209
|
-
-
|
|
246
|
+
- Use OAuth client **Web application**, redirect `http://127.0.0.1:<port>/oauth2callback`.
|
|
247
|
+
- Rotate secret or **Download JSON** for a fresh client; enter again via wizard; on Mac **clear fields** before paste.
|
|
248
|
+
- `copyhub status` — verify Client ID/Secret source.
|
|
249
|
+
- CopyHub may show an HTML error page when exchanging the `code` fails.
|
|
210
250
|
|
|
211
|
-
|
|
251
|
+
### OAuth port already in use (`EADDRINUSE`)
|
|
252
|
+
|
|
253
|
+
Change port: `COPYHUB_OAUTH_REDIRECT_PORT` or `copyhub config … --redirect-port P`, and update the redirect URI in Google Console.
|
|
254
|
+
|
|
255
|
+
### `copyhub start` says already running
|
|
256
|
+
|
|
257
|
+
Single background instance: `copyhub list` / `copyhub stop`, then start again.
|
|
258
|
+
|
|
259
|
+
### Sheet not writing / API errors
|
|
260
|
+
|
|
261
|
+
- Enable Google Sheets API on the correct project.
|
|
262
|
+
- Check `copyhub status` (token, Sheet ID).
|
|
263
|
+
- Share the spreadsheet with the signed-in account.
|
|
264
|
+
|
|
265
|
+
### Overlay won’t open / shortcut doesn’t work
|
|
266
|
+
|
|
267
|
+
- **macOS**: enable **Accessibility** for Terminal / Node / Electron.
|
|
268
|
+
- Default is **Control+Shift+H** (⌃ or Ctrl + Shift + H), not the Win key.
|
|
269
|
+
- Ensure the daemon is running (`copyhub list`) or try `copyhub overlay`.
|
|
270
|
+
- Avoid conflicts with other apps (Spotlight, Alfred, …).
|
|
271
|
+
|
|
272
|
+
### Reset and start clean
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
copyhub stop
|
|
276
|
+
copyhub reset --yes
|
|
277
|
+
```
|
|
212
278
|
|
|
213
|
-
|
|
214
|
-
- **macOS**: you may need to grant **Accessibility** permissions for global shortcuts.
|
|
215
|
-
- Some **`Control+Alt+…`** combinations do not register reliably on Windows; prefer alternatives suggested on the setup page.
|
|
279
|
+
Then remove or edit `COPYHUB_GOOGLE_*` in your shell / `~/.copyhub/.env` if you no longer want env-based OAuth. `.env` files are **not** removed by `reset`.
|
|
216
280
|
|
|
217
|
-
|
|
281
|
+
---
|
|
218
282
|
|
|
219
|
-
|
|
283
|
+
## Security notes
|
|
220
284
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
3. **Paste cleanly**: Re-open the localhost credential wizard or edit `config.json` so ID and secret have **no extra spaces or line breaks**.
|
|
224
|
-
4. Run **`copyhub status`** and confirm **Client ID/Secret source** matches what you expect.
|
|
285
|
+
- `config.json` and `tokens.json` contain OAuth secrets — standard user-only permissions under `~/.copyhub`.
|
|
286
|
+
- Do not commit `.env` or CopyHub data to public git.
|
|
225
287
|
|
|
226
|
-
|
|
288
|
+
---
|
|
227
289
|
|
|
228
290
|
## License
|
|
229
291
|
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -37,6 +37,91 @@ loadCopyhubEnv();
|
|
|
37
37
|
|
|
38
38
|
const CLI_JS = fileURLToPath(new URL('./cli.js', import.meta.url));
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Stop background daemon if present. Same behavior as `copyhub stop`.
|
|
42
|
+
* @returns {'stopped' | 'cleared-stale' | 'none'}
|
|
43
|
+
*/
|
|
44
|
+
function stopBackgroundDaemonSync() {
|
|
45
|
+
pruneStaleRunState();
|
|
46
|
+
const s = readRunState();
|
|
47
|
+
if (!s) return 'none';
|
|
48
|
+
if (!isPidAlive(s.pid)) {
|
|
49
|
+
console.log(`PID ${s.pid} is not running — cleared run.json.`);
|
|
50
|
+
clearRunState();
|
|
51
|
+
return 'cleared-stale';
|
|
52
|
+
}
|
|
53
|
+
killDaemonTree(s.pid);
|
|
54
|
+
clearRunState();
|
|
55
|
+
console.log(`Stopped process PID ${s.pid}.`);
|
|
56
|
+
return 'stopped';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Shared by `start` and `restart`.
|
|
61
|
+
* @param {{ sheet?: boolean, overlay?: boolean, foreground?: boolean }} opts
|
|
62
|
+
*/
|
|
63
|
+
async function runCopyhubStart(opts) {
|
|
64
|
+
pruneStaleRunState();
|
|
65
|
+
|
|
66
|
+
const useSheet = opts.sheet !== false;
|
|
67
|
+
const skipOverlay =
|
|
68
|
+
opts.overlay === false || process.env.COPYHUB_START_NO_OVERLAY === '1';
|
|
69
|
+
|
|
70
|
+
const existing = readRunState();
|
|
71
|
+
if (existing && isPidAlive(existing.pid)) {
|
|
72
|
+
console.error(
|
|
73
|
+
`CopyHub already running in background (PID ${existing.pid}). See: copyhub list — Stop: copyhub stop`,
|
|
74
|
+
);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
if (existing && !isPidAlive(existing.pid)) {
|
|
78
|
+
clearRunState();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (opts.foreground) {
|
|
82
|
+
console.log('CopyHub foreground mode. Press Ctrl+C to stop.');
|
|
83
|
+
await ensureDir();
|
|
84
|
+
|
|
85
|
+
const ctrl = await runCopyhubDaemon({ useSheet, skipOverlay });
|
|
86
|
+
|
|
87
|
+
const onStop = () => {
|
|
88
|
+
ctrl.stopSync();
|
|
89
|
+
process.exit(0);
|
|
90
|
+
};
|
|
91
|
+
process.on('SIGINT', onStop);
|
|
92
|
+
process.on('SIGTERM', onStop);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
await ensureDir();
|
|
97
|
+
const daemonArgs = [CLI_JS, '_daemon'];
|
|
98
|
+
if (!useSheet) daemonArgs.push('--no-sheet');
|
|
99
|
+
if (skipOverlay) daemonArgs.push('--no-overlay');
|
|
100
|
+
|
|
101
|
+
const child = spawn(process.execPath, daemonArgs, {
|
|
102
|
+
detached: true,
|
|
103
|
+
stdio: 'ignore',
|
|
104
|
+
windowsHide: process.platform === 'win32',
|
|
105
|
+
env: { ...process.env },
|
|
106
|
+
});
|
|
107
|
+
child.unref();
|
|
108
|
+
|
|
109
|
+
if (!child.pid) {
|
|
110
|
+
console.error('Could not spawn background process.');
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
writeRunState({
|
|
115
|
+
pid: child.pid,
|
|
116
|
+
startedAt: new Date().toISOString(),
|
|
117
|
+
foreground: false,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
console.log(`CopyHub running in background (PID ${child.pid}). You may close this terminal.`);
|
|
121
|
+
console.log('Check: copyhub list | Stop: copyhub stop');
|
|
122
|
+
process.exit(0);
|
|
123
|
+
}
|
|
124
|
+
|
|
40
125
|
program.name('copyhub').description(
|
|
41
126
|
'CopyHub — clipboard, overlay history, Google Sheets sync (COPYHUB-daily tabs). Windows, macOS, Linux.',
|
|
42
127
|
);
|
|
@@ -166,20 +251,30 @@ program
|
|
|
166
251
|
.command('stop')
|
|
167
252
|
.description('Stop the background process started by copyhub start (and overlay child)')
|
|
168
253
|
.action(() => {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (!s) {
|
|
254
|
+
const r = stopBackgroundDaemonSync();
|
|
255
|
+
if (r === 'none') {
|
|
172
256
|
console.log('No background process to stop.');
|
|
173
|
-
return;
|
|
174
257
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
program
|
|
261
|
+
.command('restart')
|
|
262
|
+
.description(
|
|
263
|
+
'Stop the background daemon if running, then start again (reloads ~/.copyhub/config.json, .env, shortcut). Same flags as start.',
|
|
264
|
+
)
|
|
265
|
+
.option('--no-sheet', 'Local history only, do not write to Sheets')
|
|
266
|
+
.option('--no-overlay', 'Do not launch Electron')
|
|
267
|
+
.option('--foreground', 'Run in foreground after restart (Ctrl+C stops)')
|
|
268
|
+
.action(async (opts) => {
|
|
269
|
+
const r = stopBackgroundDaemonSync();
|
|
270
|
+
if (r === 'stopped') {
|
|
271
|
+
console.log('Starting again — config, ~/.copyhub/.env, and shell env will be re-read.');
|
|
272
|
+
} else if (r === 'none') {
|
|
273
|
+
console.log('No background daemon — starting CopyHub.');
|
|
274
|
+
} else {
|
|
275
|
+
console.log('Stale run state cleared — starting CopyHub.');
|
|
179
276
|
}
|
|
180
|
-
|
|
181
|
-
clearRunState();
|
|
182
|
-
console.log(`Stopped process PID ${s.pid}.`);
|
|
277
|
+
await runCopyhubStart(opts);
|
|
183
278
|
});
|
|
184
279
|
|
|
185
280
|
program
|
|
@@ -256,65 +351,7 @@ program
|
|
|
256
351
|
.option('--no-overlay', 'Do not launch Electron')
|
|
257
352
|
.option('--foreground', 'Run in foreground (Ctrl+C stops; no background PID file)')
|
|
258
353
|
.action(async (opts) => {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
const useSheet = opts.sheet !== false;
|
|
262
|
-
const skipOverlay =
|
|
263
|
-
opts.overlay === false || process.env.COPYHUB_START_NO_OVERLAY === '1';
|
|
264
|
-
|
|
265
|
-
const existing = readRunState();
|
|
266
|
-
if (existing && isPidAlive(existing.pid)) {
|
|
267
|
-
console.error(
|
|
268
|
-
`CopyHub already running in background (PID ${existing.pid}). See: copyhub list — Stop: copyhub stop`,
|
|
269
|
-
);
|
|
270
|
-
process.exit(1);
|
|
271
|
-
}
|
|
272
|
-
if (existing && !isPidAlive(existing.pid)) {
|
|
273
|
-
clearRunState();
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
if (opts.foreground) {
|
|
277
|
-
console.log('CopyHub foreground mode. Press Ctrl+C to stop.');
|
|
278
|
-
await ensureDir();
|
|
279
|
-
|
|
280
|
-
const ctrl = await runCopyhubDaemon({ useSheet, skipOverlay });
|
|
281
|
-
|
|
282
|
-
const onStop = () => {
|
|
283
|
-
ctrl.stopSync();
|
|
284
|
-
process.exit(0);
|
|
285
|
-
};
|
|
286
|
-
process.on('SIGINT', onStop);
|
|
287
|
-
process.on('SIGTERM', onStop);
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
await ensureDir();
|
|
292
|
-
const daemonArgs = [CLI_JS, '_daemon'];
|
|
293
|
-
if (!useSheet) daemonArgs.push('--no-sheet');
|
|
294
|
-
if (skipOverlay) daemonArgs.push('--no-overlay');
|
|
295
|
-
|
|
296
|
-
const child = spawn(process.execPath, daemonArgs, {
|
|
297
|
-
detached: true,
|
|
298
|
-
stdio: 'ignore',
|
|
299
|
-
windowsHide: process.platform === 'win32',
|
|
300
|
-
env: { ...process.env },
|
|
301
|
-
});
|
|
302
|
-
child.unref();
|
|
303
|
-
|
|
304
|
-
if (!child.pid) {
|
|
305
|
-
console.error('Could not spawn background process.');
|
|
306
|
-
process.exit(1);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
writeRunState({
|
|
310
|
-
pid: child.pid,
|
|
311
|
-
startedAt: new Date().toISOString(),
|
|
312
|
-
foreground: false,
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
console.log(`CopyHub running in background (PID ${child.pid}). You may close this terminal.`);
|
|
316
|
-
console.log('Check: copyhub list | Stop: copyhub stop');
|
|
317
|
-
process.exit(0);
|
|
354
|
+
await runCopyhubStart(opts);
|
|
318
355
|
});
|
|
319
356
|
|
|
320
357
|
program
|
|
@@ -359,6 +396,7 @@ program
|
|
|
359
396
|
copyhub login | copyhub logout | copyhub status
|
|
360
397
|
copyhub reset --yes (delete ~/.copyhub — stop daemon first is recommended)
|
|
361
398
|
copyhub start [--no-sheet] [--no-overlay] [--foreground]
|
|
399
|
+
copyhub restart [--no-sheet] [--no-overlay] [--foreground] (stop daemon if running, then start — reloads config/.env)
|
|
362
400
|
Default runs in background (terminal can close). Single instance — second start is blocked.
|
|
363
401
|
copyhub list (ls) | copyhub stop
|
|
364
402
|
copyhub overlay | copyhub commands / copyhub --help`);
|
package/src/oauth.js
CHANGED
|
@@ -383,18 +383,22 @@ async function runCredentialBootstrap() {
|
|
|
383
383
|
/** Shortcut presets (Electron Accelerator) per platform — embedded as JSON in the setup page. */
|
|
384
384
|
const PLATFORM_PRESETS = {
|
|
385
385
|
win: [
|
|
386
|
-
{ label: 'Ctrl + Shift + H · recommended', value: '
|
|
387
|
-
{ label: 'Control + Shift + H', value: 'Control+Shift+H' },
|
|
386
|
+
{ label: 'Ctrl + Shift + H · recommended', value: 'Control+Shift+H' },
|
|
388
387
|
{ label: 'Alt + Shift + H', value: 'Alt+Shift+H' },
|
|
389
388
|
],
|
|
390
389
|
mac: [
|
|
391
|
-
{
|
|
392
|
-
|
|
390
|
+
{
|
|
391
|
+
label: '⌃ Control + Shift + H · recommended (Apple & PC keyboards)',
|
|
392
|
+
value: 'Control+Shift+H',
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
label: '⌘ Command + Shift + H',
|
|
396
|
+
value: 'Command+Shift+H',
|
|
397
|
+
},
|
|
393
398
|
{ label: '⌘ + Shift + V', value: 'Command+Shift+V' },
|
|
394
399
|
],
|
|
395
400
|
linux: [
|
|
396
|
-
{ label: 'Ctrl + Shift + H · recommended', value: '
|
|
397
|
-
{ label: 'Control + Shift + H', value: 'Control+Shift+H' },
|
|
401
|
+
{ label: 'Ctrl + Shift + H · recommended', value: 'Control+Shift+H' },
|
|
398
402
|
{ label: 'Alt + Shift + H', value: 'Alt+Shift+H' },
|
|
399
403
|
],
|
|
400
404
|
};
|
|
@@ -632,7 +636,7 @@ function setupPageHtml(setupToken, currentSheetId, currentAccelerator, currentPl
|
|
|
632
636
|
<button type="button" class="platform-btn" data-platform="mac" aria-pressed="false">
|
|
633
637
|
<span class="ico" aria-hidden="true">⌘</span>
|
|
634
638
|
<span class="name">macOS</span>
|
|
635
|
-
<span class="tag"
|
|
639
|
+
<span class="tag">⌃ Control default</span>
|
|
636
640
|
</button>
|
|
637
641
|
<button type="button" class="platform-btn" data-platform="linux" aria-pressed="false">
|
|
638
642
|
<span class="ico" aria-hidden="true">🐧</span>
|
|
@@ -641,10 +645,10 @@ function setupPageHtml(setupToken, currentSheetId, currentAccelerator, currentPl
|
|
|
641
645
|
</button>
|
|
642
646
|
</div>
|
|
643
647
|
|
|
644
|
-
<label class="field-label" for="acc">Accelerator (blank =
|
|
648
|
+
<label class="field-label" for="acc">Accelerator (blank = Control + Shift + H)</label>
|
|
645
649
|
<input id="acc" type="text" name="overlayAccelerator" value="${accVal}" placeholder="Pick a preset below or type your own" autocomplete="off" spellcheck="false" />
|
|
646
650
|
|
|
647
|
-
<p class="hint">On Windows
|
|
651
|
+
<p class="hint">Default everywhere: <code>Control+Shift+H</code> (⌃ or Ctrl + Shift + H — same key on Mac Apple keyboard & PC keyboard). On Windows type <code>Control</code> in config, not <code>Ctrl</code>. Avoid <code>Control+Alt+…</code> (often grabbed by drivers).</p>
|
|
648
652
|
<p class="hint"><code>COPYHUB_OVERLAY_ACCELERATOR</code> in <code>.env</code>, if set, overrides this value.</p>
|
|
649
653
|
|
|
650
654
|
<div id="chipRegion" class="chips" aria-live="polite"></div>
|
|
@@ -670,9 +674,9 @@ function setupPageHtml(setupToken, currentSheetId, currentAccelerator, currentPl
|
|
|
670
674
|
var btns = document.querySelectorAll('.platform-btn');
|
|
671
675
|
|
|
672
676
|
var hints = {
|
|
673
|
-
win: 'Windows:
|
|
674
|
-
mac: 'macOS:
|
|
675
|
-
linux: 'Linux:
|
|
677
|
+
win: 'Windows: default Control+Shift+H.',
|
|
678
|
+
mac: 'macOS: default ⌃ Control+Shift+H (same as Ctrl on a PC keyboard). Use a ⌘ preset only if you prefer Command.',
|
|
679
|
+
linux: 'Linux: default Control+Shift+H; clipboard depends on your desktop.'
|
|
676
680
|
};
|
|
677
681
|
|
|
678
682
|
function setPlatform(p) {
|
package/ui/main.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
Tray,
|
|
11
11
|
Menu,
|
|
12
12
|
nativeImage,
|
|
13
|
+
systemPreferences,
|
|
13
14
|
} from 'electron';
|
|
14
15
|
import { loadCopyhubEnv } from '../src/load-env.js';
|
|
15
16
|
import { readRecentHistorySync } from '../src/storage.js';
|
|
@@ -32,17 +33,37 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
32
33
|
/** Set to 1 so the window does not hide on blur (only Esc / pick row to copy). */
|
|
33
34
|
const STICKY_NO_BLUR = process.env.COPYHUB_OVERLAY_STICKY === '1';
|
|
34
35
|
|
|
35
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Electron Accelerator: use `Control`, not `Ctrl`; Unicode ⌃/⌘ → words.
|
|
38
|
+
* See migrateDarwinOverlayAccelerator — `CommandOrControl` is ⌘ on macOS, not ⌃.
|
|
39
|
+
*/
|
|
36
40
|
function normalizeAccelerator(raw) {
|
|
37
41
|
if (!raw || typeof raw !== 'string') return '';
|
|
38
|
-
let s = raw.trim();
|
|
42
|
+
let s = raw.trim().normalize('NFKC');
|
|
43
|
+
s = s.replace(/\u2303/g, 'Control'); // ⌃
|
|
44
|
+
s = s.replace(/\u2318/g, 'Command'); // ⌘
|
|
45
|
+
s = s.replace(/\u2325/g, 'Alt'); // ⌥
|
|
39
46
|
s = s.replace(/\bCtrl\b/gi, 'Control');
|
|
40
47
|
s = s.replace(/\bCmd\b/gi, 'Command');
|
|
48
|
+
s = s.replace(/\bCmdOrCtrl\b/gi, 'CommandOrControl');
|
|
41
49
|
s = s.replace(/\s*\+\s*/g, '+');
|
|
42
50
|
return s;
|
|
43
51
|
}
|
|
44
52
|
|
|
45
|
-
|
|
53
|
+
/**
|
|
54
|
+
* On macOS, Electron maps `CommandOrControl` to ⌘. CopyHub defaults want ⌃ Control + Shift + H.
|
|
55
|
+
* Migrate only this chord so ⌃+Shift+H works if config still has the cross-platform preset.
|
|
56
|
+
* @param {string} normalized output of normalizeAccelerator
|
|
57
|
+
*/
|
|
58
|
+
function migrateDarwinOverlayAccelerator(normalized) {
|
|
59
|
+
if (process.platform !== 'darwin' || !normalized) return normalized;
|
|
60
|
+
const compact = normalized.replace(/\s+/g, '');
|
|
61
|
+
if (/^commandorcontrol\+shift\+h$/i.test(compact)) return 'Control+Shift+H';
|
|
62
|
+
return normalized;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Same physical ⌃ / Ctrl key on Mac (Apple & Windows-layout keyboards) and on Win/Linux — one default everywhere. */
|
|
66
|
+
const DEFAULT_ACCEL = 'Control+Shift+H';
|
|
46
67
|
const HIDE_ON_START = process.env.COPYHUB_OVERLAY_HIDE_ON_START === '1';
|
|
47
68
|
|
|
48
69
|
/** Overlay size (slightly larger than earlier ~70% width). */
|
|
@@ -213,7 +234,7 @@ function toggleOverlay() {
|
|
|
213
234
|
}
|
|
214
235
|
|
|
215
236
|
/**
|
|
216
|
-
* Register global shortcut: try .env (normalized) then default
|
|
237
|
+
* Register global shortcut: try .env / saved config (normalized) then default Control+Shift+H.
|
|
217
238
|
* @returns {{ accelerator: string, usedFallback: boolean }}
|
|
218
239
|
*/
|
|
219
240
|
function registerHotkeys() {
|
|
@@ -222,7 +243,7 @@ function registerHotkeys() {
|
|
|
222
243
|
loadOverlayAcceleratorFromConfigSync();
|
|
223
244
|
const candidates = [];
|
|
224
245
|
if (raw) {
|
|
225
|
-
const n = normalizeAccelerator(raw);
|
|
246
|
+
const n = migrateDarwinOverlayAccelerator(normalizeAccelerator(raw));
|
|
226
247
|
if (n) candidates.push(n);
|
|
227
248
|
}
|
|
228
249
|
candidates.push(DEFAULT_ACCEL);
|
|
@@ -231,10 +252,14 @@ function registerHotkeys() {
|
|
|
231
252
|
for (let i = 0; i < candidates.length; i++) {
|
|
232
253
|
const acc = candidates[i];
|
|
233
254
|
try {
|
|
234
|
-
|
|
255
|
+
const ok = globalShortcut.register(acc, () => toggleOverlay());
|
|
256
|
+
if (ok) {
|
|
235
257
|
if (i > 0) usedFallback = true;
|
|
236
258
|
return { accelerator: acc, usedFallback };
|
|
237
259
|
}
|
|
260
|
+
console.warn(
|
|
261
|
+
`CopyHub overlay — globalShortcut could not register "${acc}" (in use by another app, or macOS Input Source / permissions).`,
|
|
262
|
+
);
|
|
238
263
|
} catch (e) {
|
|
239
264
|
console.warn('Invalid accelerator:', acc, /** @type {Error} */ (e).message);
|
|
240
265
|
}
|
|
@@ -244,6 +269,23 @@ function registerHotkeys() {
|
|
|
244
269
|
/* ignore */
|
|
245
270
|
}
|
|
246
271
|
}
|
|
272
|
+
if (process.platform === 'darwin') {
|
|
273
|
+
const trusted = systemPreferences.isTrustedAccessibilityClient(false);
|
|
274
|
+
if (!trusted) {
|
|
275
|
+
console.error(
|
|
276
|
+
'CopyHub overlay — macOS: enable Accessibility for the app that launches Electron (e.g. Terminal, iTerm, or Node) in System Settings → Privacy & Security → Accessibility.',
|
|
277
|
+
);
|
|
278
|
+
try {
|
|
279
|
+
systemPreferences.isTrustedAccessibilityClient(true);
|
|
280
|
+
} catch {
|
|
281
|
+
/* ignore */
|
|
282
|
+
}
|
|
283
|
+
} else {
|
|
284
|
+
console.error(
|
|
285
|
+
'CopyHub overlay — shortcut still failed: try Input Source US/QWERTY (Electron globalShortcut quirk on macOS), pick another chord in config, or open from the menu bar icon.',
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
247
289
|
return { accelerator: '', usedFallback: false };
|
|
248
290
|
}
|
|
249
291
|
|
|
@@ -546,12 +588,12 @@ if (gotLock) {
|
|
|
546
588
|
};
|
|
547
589
|
if (accelerator) {
|
|
548
590
|
console.log('CopyHub overlay — shortcut in use:', accelerator);
|
|
549
|
-
console.log('
|
|
591
|
+
console.log('Default shortcut: Control+Shift+H (⌃ or Ctrl + Shift + H).');
|
|
550
592
|
if (usedFallback) {
|
|
551
593
|
console.warn(
|
|
552
|
-
'COPYHUB_OVERLAY_ACCELERATOR could not be registered. Using default
|
|
594
|
+
'COPYHUB_OVERLAY_ACCELERATOR could not be registered. Using default Control+Shift+H.',
|
|
553
595
|
);
|
|
554
|
-
console.warn('Leave COPYHUB_OVERLAY_ACCELERATOR unset
|
|
596
|
+
console.warn('Leave COPYHUB_OVERLAY_ACCELERATOR unset to use the default Control+Shift+H.');
|
|
555
597
|
}
|
|
556
598
|
} else {
|
|
557
599
|
console.error(
|