-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathoptions.html
More file actions
353 lines (325 loc) · 19.9 KB
/
Copy pathoptions.html
File metadata and controls
353 lines (325 loc) · 19.9 KB
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Smart Tab Organiser — Settings</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700;1,9..40,400&display=swap" rel="stylesheet">
<link rel="stylesheet" href="options.css">
</head>
<body>
<div class="container">
<header>
<h1>Smart Tab Organiser</h1>
<p class="subtitle">Settings & Configuration</p>
</header>
<div class="content">
<section class="info-section">
<h2>How to Use</h2>
<div class="info-content">
<p><strong>Click the extension icon</strong> to deduplicate tabs and tidy your pinned tabs (unpin tabs not in your list, pin matching ones, reorder to match your list). If no tabs are pinned yet, the extension will pin tabs from your list (opening new tabs for any URL not already open). Duplicate detection uses your settings (e.g. keep highest anchor). The icon badge shows how many duplicate tabs are open.</p>
<p><strong>Three ways to run:</strong> (1) Click the extension icon to deduplicate and tidy pinned tabs. (2) Right-click → "Deduplicate and organize tabs with AI" to dedupe and reorganize with AI. (3) Right-click → "Deduplicate and tidy pinned tabs" does the same as the icon click.</p>
<p><strong>AI Tab Organization:</strong> Use "Organize tabs with AI" from the context menu, keyboard shortcut (⌘+Shift+O / Ctrl+Shift+O), or "Organize Tabs" in the popup. Configure your API keys below to enable it.</p>
</div>
</section>
<section class="actions-section">
<h2>Actions</h2>
<div class="actions">
<button id="testBtn" class="btn btn-secondary">Test Duplicate Detection</button>
<button id="reloadAllBtn" class="btn btn-secondary">Reload All Tabs</button>
<button id="organizeTabsBtn" class="btn btn-primary">Organize Tabs with AI</button>
<button id="tidyPinnedBtn" class="btn btn-secondary">Deduplicate and tidy pinned tabs</button>
<button id="ungroupTabsBtn" class="btn btn-secondary">Ungroup All Tabs</button>
</div>
<div id="actionStatus" class="status"></div>
</section>
<section class="settings-section">
<h2>Duplicate Detection Settings</h2>
<p class="section-description">Configure how the extension identifies duplicate tabs.</p>
<div class="setting-item">
<div class="setting-info">
<label class="setting-label">Ignore query parameters</label>
<p class="setting-description">
When enabled, URLs with different query strings are treated as duplicates.
<br><strong>Example:</strong> <code>example.com/?a=1</code> and <code>example.com/?b=2</code> are considered duplicates.
</p>
</div>
<label class="switch">
<input type="checkbox" id="ignoreQuery">
<span class="slider"></span>
</label>
</div>
<div class="setting-item">
<div class="setting-info">
<label class="setting-label">Ignore hash when comparing</label>
<p class="setting-description">
When enabled, URLs are compared without their hash/anchor, but the hash is still used to determine which tab to keep (highest anchor number).
<br><strong>Example:</strong> <code>example.com/#comment-123</code> and <code>example.com/#comment-456</code> are duplicates, but the one with #comment-456 is kept.
</p>
</div>
<label class="switch">
<input type="checkbox" id="ignoreHash">
<span class="slider"></span>
</label>
</div>
<div class="setting-item">
<div class="setting-info">
<label class="setting-label">Reload remaining tabs</label>
<p class="setting-description">
When enabled, remaining tabs will be automatically reloaded after closing duplicates.
</p>
</div>
<label class="switch">
<input type="checkbox" id="reloadTabs">
<span class="slider"></span>
</label>
</div>
</section>
<section class="settings-section">
<h2>Pinned tabs</h2>
<p class="section-description">Define which URLs should be Chrome-pinned (compact favicon tabs on the left of the tab bar). When you run "Deduplicate and tidy pinned tabs" or organize with AI, any pinned tab not in this list will be unpinned, and matching unpinned tabs will be pinned and ordered. <strong>If no tabs are pinned yet, the extension will pin tabs from this list (opening new tabs for any URL not already open).</strong></p>
<div class="setting-item setting-item--stacked">
<div class="setting-info">
<label class="setting-label" for="pinnedUrls">Pinned URLs (one per line, order = tab order)</label>
<p class="setting-description">
<strong>Exact match</strong> (no prefix): only that exact URL stays pinned (after normalizing query/hash per your duplicate settings).<br>
<strong>Prefix match</strong>: start a line with <code>*</code> so any tab whose URL starts with the rest of the line stays pinned (e.g. <code>*https://mail.google.com/mail/u/0</code> keeps inbox, drafts, etc.; <code>*https://chatgpt.com/</code> keeps any ChatGPT page; <code>*https://app.slack.com/client/E047TPA624F</code> keeps that workspace and any channel).<br>
<strong>Soft (only when already open):</strong> start with <code>~</code> so the tab is only pinned when it already exists in the window; the extension never opens it. Use <code>~*https://...</code> for soft prefix or <code>~https://...</code> for soft exact. Example: <code>~*https://app.slack.com/client/E047TPA624F</code> keeps Slack pinned only in windows where you have Slack open — it won’t be recreated in new windows. Leave empty to keep all currently pinned tabs as-is when tidying.
</p>
</div>
<textarea id="pinnedUrls" class="custom-instructions-textarea" placeholder="*https://mail.google.com/mail/u/0 *https://app.slack.com/client/E047TPA624F https://staging.new.expensify.com/home *https://chatgpt.com/" rows="5"></textarea>
</div>
<div class="setting-item">
<div class="setting-info">
<label class="setting-label" for="bookmarksGroupColor">BOOKMARKS group colour</label>
<p class="setting-description">Colour for the BOOKMARKS tab group. Applied whenever you run dedupe/tidy so the group keeps this colour.</p>
</div>
<select id="bookmarksGroupColor" class="provider-select">
<option value="yellow">Yellow</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
<option value="red">Red</option>
<option value="pink">Pink</option>
<option value="purple">Purple</option>
<option value="cyan">Cyan</option>
<option value="orange">Orange</option>
<option value="grey">Grey</option>
</select>
</div>
</section>
<section class="settings-section">
<h2>GitHub PR tab group</h2>
<p class="section-description">When enabled, the extension maintains a tab group named <strong>PRs</strong> with one tab per open pull request (authored by you or awaiting your review). Only the extension adds or removes tabs in this group. Your token is stored locally and used only to call the GitHub API.</p>
<div class="setting-item">
<div class="setting-info">
<label class="setting-label" for="githubToken">GitHub personal access token</label>
<p class="setting-description">
Used only to fetch your open PRs and PRs awaiting your review. Requires at least <code>read:user</code>; for private repos use <code>repo</code>.
<br><strong>Create a token:</strong> <a href="https://github.com/settings/tokens?type=beta" target="_blank">github.com/settings/tokens</a>
</p>
</div>
<div class="input-with-toggle">
<input type="password" id="githubToken" class="api-key-input" placeholder="ghp_...">
<button type="button" class="toggle-password" aria-label="Show token" title="Show token"><span class="eye-icon" aria-hidden="true"></span></button>
</div>
</div>
<div class="setting-item">
<div class="setting-info">
<label class="setting-label">Enable PR tab group</label>
<p class="setting-description">
When enabled, a tab group named <strong>PRs</strong> is created or updated with one tab per open PR. When you run dedupe/organize, the extension refreshes this group first and treats the tab in the PR group as the main copy when closing duplicates elsewhere (split-view tabs are still never closed).
</p>
</div>
<label class="switch">
<input type="checkbox" id="prGroupEnabled">
<span class="slider"></span>
</label>
</div>
<div class="setting-item">
<button id="refreshPrGroupBtn" class="btn btn-secondary">Refresh PR group</button>
<p class="setting-description" style="margin-top: 0.5em;">Manually refresh the PRs tab group now. It also refreshes automatically when you run dedupe/organize.</p>
</div>
</section>
<section class="settings-section settings-section--ai">
<h2>AI Tab Organization Settings</h2>
<p class="section-description">Configure API keys for AI-powered tab organization. Your API keys are stored locally and never shared.</p>
<div class="setting-item">
<div class="setting-info">
<label class="setting-label" for="openaiKey">OpenAI API Key</label>
<p class="setting-description">
Enter your OpenAI API key to use GPT models for tab organization. Leave empty to disable.
<br><strong>Get your key:</strong> <a href="https://platform.openai.com/api-keys" target="_blank">platform.openai.com/api-keys</a>
</p>
</div>
<div class="input-with-toggle">
<input type="password" id="openaiKey" class="api-key-input" placeholder="sk-...">
<button type="button" class="toggle-password" aria-label="Show key" title="Show key"><span class="eye-icon" aria-hidden="true"></span></button>
</div>
</div>
<div class="setting-item">
<div class="setting-info">
<label class="setting-label" for="claudeKey">Claude API Key</label>
<p class="setting-description">
Enter your Anthropic Claude API key to use Claude models for tab organization. Leave empty to disable.
<br><strong>Get your key:</strong> <a href="https://console.anthropic.com/settings/keys" target="_blank">console.anthropic.com/settings/keys</a>
</p>
</div>
<div class="input-with-toggle">
<input type="password" id="claudeKey" class="api-key-input" placeholder="sk-ant-...">
<button type="button" class="toggle-password" aria-label="Show key" title="Show key"><span class="eye-icon" aria-hidden="true"></span></button>
</div>
</div>
<div class="setting-item">
<div class="setting-info">
<label class="setting-label" for="geminiKey">Gemini API Key</label>
<p class="setting-description">
Enter your Google Gemini API key to use Gemini models for tab organization. Leave empty to disable.
<br><strong>Get your key:</strong> <a href="https://aistudio.google.com/app/apikey" target="_blank">aistudio.google.com/app/apikey</a>
</p>
</div>
<div class="input-with-toggle">
<input type="password" id="geminiKey" class="api-key-input" placeholder="AIza...">
<button type="button" class="toggle-password" aria-label="Show key" title="Show key"><span class="eye-icon" aria-hidden="true"></span></button>
</div>
</div>
<div class="setting-item">
<div class="setting-info">
<label class="setting-label" for="aiProvider">Preferred AI Provider</label>
<p class="setting-description">
Choose which AI provider to use for tab organization. The selected provider must have a valid API key configured.
</p>
</div>
<select id="aiProvider" class="provider-select">
<option value="openai">OpenAI</option>
<option value="claude">Claude</option>
<option value="gemini">Gemini</option>
</select>
</div>
<div class="setting-item">
<div class="setting-info">
<label class="setting-label" for="openaiModel">OpenAI Model</label>
<p class="setting-description">
Choose which OpenAI model to use for tab organization.
</p>
</div>
<select id="openaiModel" class="provider-select">
<option value="gpt-5.2">GPT-5.2 (Most capable)</option>
<option value="gpt-5.2-pro">GPT-5.2 Pro (Smarter, more precise)</option>
<option value="gpt-5-mini">GPT-5 mini (Balanced, cost-efficient)</option>
<option value="gpt-5-nano">GPT-5 nano (Fastest, cheapest)</option>
<option value="gpt-5">GPT-5 (Reasoning model)</option>
<option value="gpt-4.1">GPT-4.1 (Smartest non-reasoning)</option>
<option value="gpt-4.1-mini">GPT-4.1 mini (Fast, cost-effective)</option>
<option value="gpt-4.1-nano">GPT-4.1 nano (Lightweight)</option>
<option value="gpt-4o">GPT-4o (Legacy)</option>
<option value="gpt-4o-mini">GPT-4o mini (Legacy)</option>
</select>
</div>
<div class="setting-item">
<div class="setting-info">
<label class="setting-label" for="claudeModel">Claude Model</label>
<p class="setting-description">
Choose which Claude model to use for tab organization.
</p>
</div>
<select id="claudeModel" class="provider-select">
<option value="claude-opus-4-6">Claude Opus 4.6 (Most capable)</option>
<option value="claude-sonnet-4-6">Claude Sonnet 4.6 (Balanced)</option>
<option value="claude-haiku-4-5-20251001">Claude Haiku 4.5 (Fast, Cost-effective)</option>
<option value="claude-3-5-haiku-20241022">Claude 3.5 Haiku (Legacy)</option>
<option value="claude-3-5-sonnet-20241022">Claude 3.5 Sonnet (Legacy)</option>
<option value="claude-3-opus-20240229">Claude 3 Opus (Legacy)</option>
<option value="claude-3-sonnet-20240229">Claude 3 Sonnet (Legacy)</option>
</select>
</div>
<div class="setting-item">
<div class="setting-info">
<label class="setting-label" for="geminiModel">Gemini Model</label>
<p class="setting-description">
Choose which Gemini model to use for tab organization.
</p>
</div>
<select id="geminiModel" class="provider-select">
<option value="gemini-2.0-flash">Gemini 2.0 Flash (Fast, capable)</option>
<option value="gemini-2.0-flash-lite">Gemini 2.0 Flash Lite (Fastest)</option>
<option value="gemini-1.5-flash">Gemini 1.5 Flash (Balanced)</option>
<option value="gemini-1.5-flash-8b">Gemini 1.5 Flash-8B (Lightweight)</option>
<option value="gemini-1.5-pro">Gemini 1.5 Pro (Most capable)</option>
</select>
</div>
<div id="custom-instructions" class="setting-item setting-item--stacked">
<div class="setting-info">
<label class="setting-label" for="customInstructionsOptions">Custom Instructions</label>
<p class="setting-description">
Provide custom instructions for how tabs should be organized (e.g., "group by domain", "group by topic", "prioritize work-related tabs").
<br>These instructions will be used when organizing tabs from the options page.
</p>
</div>
<textarea id="customInstructionsOptions" class="custom-instructions-textarea" placeholder="e.g., 'group by domain' or 'group by topic'" rows="3"></textarea>
</div>
<div class="setting-item">
<div class="setting-info">
<label class="setting-label">Preserve existing groups</label>
<p class="setting-description">
When enabled, tabs that are already in groups will be skipped during organization.
<br>When disabled, all tabs will be reorganized into new groups.
<br><strong>Pinned tabs and groups named "BOOKMARKS" and "PRs" are always preserved</strong>, regardless of this setting.
</p>
</div>
<label class="switch">
<input type="checkbox" id="preserveGroups">
<span class="slider"></span>
</label>
</div>
<div id="preserveGroupsMinTabsRow" class="setting-item">
<div class="setting-info">
<label class="setting-label" for="preserveGroupsMinTabs">Only preserve groups with more than N tabs</label>
<p class="setting-description">
When set, only groups that have <strong>more than N</strong> tabs are preserved. Groups with N tabs or fewer are ungrouped and sent through AI organization again.
<br>AI will also never create a new group with N or fewer tabs; such tabs go into Misc.
<br>Example: with <strong>2</strong>, groups with 3+ tabs are preserved; groups with 1–2 tabs are re-organized, and new groups have at least 3 tabs.
</p>
<p id="preserveGroupsMinTabsWarning" class="setting-warning" role="alert" style="display: none;">
This feature may not work well if the number is higher than 3.
</p>
</div>
<input type="number" id="preserveGroupsMinTabs" min="0" max="20" value="1" class="api-key-input" style="width: 4em;">
</div>
<div class="setting-item">
<div class="setting-info">
<label class="setting-label">Merge tabs into existing groups</label>
<p class="setting-description">
When enabled, the AI will intelligently merge ungrouped tabs into existing groups where appropriate.
<br>This works best when "Preserve existing groups" is also enabled. The AI will analyze existing groups and merge new tabs into them.
<br><strong>Pinned tabs and the "BOOKMARKS" group are always preserved</strong>; these options apply to other groups only.
</p>
</div>
<label class="switch">
<input type="checkbox" id="mergeIntoExisting">
<span class="slider"></span>
</label>
</div>
<div class="setting-item">
<div class="setting-info">
<label class="setting-label">Organize tabs on extension icon click</label>
<p class="setting-description">
When enabled, clicking the extension icon will deduplicate tabs AND organize them using AI.
<br>When disabled, the extension icon will only deduplicate tabs (default behavior).
</p>
</div>
<label class="switch">
<input type="checkbox" id="organizeOnClick">
<span class="slider"></span>
</label>
</div>
</section>
</div>
<footer>
<p>Settings are automatically saved.</p>
</footer>
</div>
<script src="options.js"></script>
</body>
</html>