forked from tronicum/inkpour
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsettings.html
More file actions
475 lines (435 loc) · 19.4 KB
/
Copy pathsettings.html
File metadata and controls
475 lines (435 loc) · 19.4 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
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title data-i18n="settingsPageTitle">Inkpour — Settings</title>
<style>
:root {
--bg: #ffffff;
--surface: #f4f4f5;
--text: #18181b;
--subtext: #71717a;
--accent: #5b5bd6;
--border: #e4e4e7;
--success: #16a34a;
--radius: 10px;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #18181b;
--surface: #27272a;
--text: #fafafa;
--subtext: #a1a1aa;
--accent: #818cf8;
--border: #3f3f46;
}
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
background: var(--bg);
color: var(--text);
font-size: 14px;
line-height: 1.5;
padding: 2rem;
max-width: 540px;
margin: 0 auto;
}
/* ── Header ── */
.header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 2rem;
}
.logo {
width: 40px; height: 40px;
background: var(--accent);
border-radius: 10px;
display: flex; align-items: center; justify-content: center;
color: #fff; font-size: 15px; font-weight: 700;
letter-spacing: -1px; flex-shrink: 0;
}
.header h1 { font-size: 18px; font-weight: 700; }
.header p { font-size: 12px; color: var(--subtext); margin-top: 1px; }
/* ── Browser badge ── */
.browser-row {
display: flex;
align-items: center;
gap: 10px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 12px 14px;
margin-bottom: 1.75rem;
}
.browser-icon { font-size: 22px; line-height: 1; }
.browser-info { flex: 1; }
.browser-info strong { display: block; font-size: 13px; }
.browser-info span { font-size: 11px; color: var(--subtext); }
.badge {
font-size: 11px; font-weight: 600;
background: var(--accent);
color: #fff;
border-radius: 20px;
padding: 2px 10px;
}
/* ── Section ── */
section { margin-bottom: 1.75rem; }
section h2 {
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.07em;
color: var(--subtext);
margin-bottom: 10px;
}
/* ── Field ── */
.field {
display: flex;
align-items: center;
justify-content: space-between;
padding: 11px 0;
border-bottom: 1px solid var(--border);
gap: 12px;
}
.field:last-child { border-bottom: none; }
.field-label { flex: 1; }
.field-label strong { display: block; font-size: 13px; font-weight: 600; }
.field-label span { font-size: 11px; color: var(--subtext); }
.field-label .fineprint { display: block; margin-top: 4px; color: #d97706; }
.field-label .value-line { display: block; margin-top: 4px; color: var(--text); font-weight: 600; }
.btn-secondary {
padding: 8px 16px;
background: var(--surface);
color: var(--text);
border: 1px solid var(--border);
border-radius: 7px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
white-space: nowrap;
}
.btn-secondary:hover { border-color: var(--accent); }
select, input[type="text"] {
background: var(--surface);
color: var(--text);
border: 1px solid var(--border);
border-radius: 7px;
padding: 6px 10px;
font-size: 13px;
font-family: inherit;
outline: none;
min-width: 140px;
}
select:focus, input[type="text"]:focus {
border-color: var(--accent);
}
/* ── Toggle ── */
.toggle {
position: relative;
width: 40px; height: 22px;
flex-shrink: 0;
}
.toggle input { opacity: 0; width: 0; height: 0; }
.toggle-track {
position: absolute; inset: 0;
background: var(--border);
border-radius: 99px;
cursor: pointer;
transition: background 0.2s;
}
.toggle-track::after {
content: '';
position: absolute;
inset-inline-start: 3px; top: 3px;
width: 16px; height: 16px;
background: #fff;
border-radius: 50%;
transition: transform 0.2s;
}
.toggle input:checked + .toggle-track { background: var(--accent); }
.toggle input:checked + .toggle-track::after { transform: translateX(18px); }
[dir="rtl"] .toggle input:checked + .toggle-track::after { transform: translateX(-18px); }
/* ── Save button ──
Sticky to the bottom of the viewport so it (and the autosave "Saved"
status next to it) stays visible while scrolling a long settings page,
instead of only showing up once you've scrolled all the way down. */
.save-row {
display: flex;
align-items: center;
gap: 12px;
margin-top: 0.5rem;
position: sticky;
bottom: 0;
background: var(--bg);
padding: 12px 0;
border-top: 1px solid var(--border);
}
.btn-save {
padding: 9px 22px;
background: var(--accent);
color: #fff;
border: none;
border-radius: var(--radius);
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: opacity 0.15s;
}
.btn-save:hover { opacity: 0.88; }
#saveStatus { font-size: 12px; color: var(--success); }
</style>
</head>
<body>
<div class="header">
<div class="logo">ip</div>
<div>
<h1 data-i18n="settingsHeaderTitle">Inkpour Settings</h1>
<p data-i18n="settingsHeaderSubtitle">Preferences are saved locally in your browser</p>
</div>
</div>
<!-- Detected browser -->
<div class="browser-row">
<div class="browser-icon" id="browserIcon">🌐</div>
<div class="browser-info">
<strong id="browserName" data-i18n="settingsBrowserDetecting">Detecting…</strong>
<span id="browserNote"></span>
</div>
<div class="badge" id="browserBadge" data-i18n="settingsBrowserBadge">Auto-detected</div>
</div>
<!-- Language -->
<section>
<h2 data-i18n="settingsSectionLanguage">Language</h2>
<div class="field">
<div class="field-label">
<strong data-i18n="settingsLanguageLabel">Display language</strong>
<span data-i18n="settingsLanguageDesc">Overrides your browser's language for Inkpour's own screens only</span>
</div>
<select id="languageOverride"></select>
</div>
</section>
<!-- Export defaults -->
<section>
<h2 data-i18n="settingsSectionExport">Export</h2>
<div class="field">
<div class="field-label">
<strong data-i18n="settingsDefaultFormatLabel">Default format</strong>
<span data-i18n="settingsDefaultFormatDesc">Which button gets keyboard focus first</span>
</div>
<select id="defaultFormat">
<option value="md" data-i18n="settingsFormatOptionMd">Markdown (.md)</option>
<option value="pdf" data-i18n="settingsFormatOptionPdf">PDF</option>
<option value="html" data-i18n="settingsFormatOptionHtml">HTML</option>
<option value="json" data-i18n="settingsFormatOptionJson">JSON</option>
<option value="zip" data-i18n="settingsFormatOptionZip">ZIP (chat + code files)</option>
</select>
</div>
<div class="field">
<div class="field-label">
<strong data-i18n="settingsFilenameTemplateLabel">Filename template</strong>
<span data-i18n="settingsFilenameTemplateDesc">Tokens: {platform} {title} {date} {time} {url} {words} {msgcount}</span>
</div>
<input type="text" id="filenameTemplate" value="{platform}-{title}" style="min-width:160px" />
</div>
<div class="field">
<div class="field-label">
<strong data-i18n="settingsPdfAutoPrintLabel">PDF auto-print</strong>
<span data-i18n="settingsPdfAutoPrintDesc">Open print dialog automatically after PDF tab loads</span>
</div>
<label class="toggle">
<input type="checkbox" id="pdfAutoPrint" checked />
<div class="toggle-track"></div>
</label>
</div>
<div class="field">
<div class="field-label">
<strong data-i18n="settingsYamlLabel">Include YAML front matter</strong>
<span data-i18n="settingsYamlDesc">Adds title, date, platform, URL to top of .md files</span>
</div>
<label class="toggle">
<input type="checkbox" id="yamlFrontMatter" />
<div class="toggle-track"></div>
</label>
</div>
<div class="field">
<div class="field-label">
<strong data-i18n="settingsTocLabel">Generate table of contents</strong>
<span data-i18n="settingsTocDesc">Prepend a clickable TOC for chats with more than 4 messages</span>
</div>
<label class="toggle">
<input type="checkbox" id="generateTOC" />
<div class="toggle-track"></div>
</label>
</div>
<div class="field">
<div class="field-label">
<strong data-i18n="settingsSubfolderLabel">Downloads subfolder</strong>
<span data-i18n="settingsSubfolderDesc">Optional sub-path within your default downloads folder (e.g. <code>AI Chats</code>)</span>
</div>
<input type="text" id="downloadSubfolder" data-i18n-placeholder="settingsSubfolderPlaceholder" placeholder="e.g. AI Chats" style="min-width:160px" />
</div>
<div class="field">
<div class="field-label">
<strong data-i18n="settingsObsidianVaultLabel">Obsidian vault subfolder</strong>
<span data-i18n="settingsObsidianVaultDesc">When set, exports go to this subfolder in Downloads — useful for Obsidian vaults. Overrides the subfolder field above.</span>
</div>
<input type="text" id="obsidianVault" data-i18n-placeholder="settingsSubfolderPlaceholder" placeholder="e.g. AI Chats" style="min-width:160px" />
</div>
<div class="field">
<div class="field-label">
<strong data-i18n="settingsObsidianTagsLabel">Obsidian tags in YAML</strong>
<span data-i18n="settingsObsidianTagsDesc">Adds <code>tags: [ai-chat, {platform}]</code> to front matter (requires YAML enabled)</span>
</div>
<label class="toggle">
<input type="checkbox" id="obsidianTags" />
<div class="toggle-track"></div>
</label>
</div>
</section>
<!-- Direct-to-vault (File System Access API — Chrome/Edge only). Hidden
entirely (not just disabled) on browsers that don't implement
showDirectoryPicker(); see the feature-detect at the top of
settings.js. Firefox/Safari users keep using the Downloads-subfolder
fields above, unaffected. -->
<section id="vaultSection" hidden style="display:none;">
<h2 data-i18n="settingsSectionVault">Direct-to-vault (Chrome only)</h2>
<div class="field">
<div class="field-label">
<strong data-i18n="settingsVaultFolderLabel">Vault folder</strong>
<span data-i18n="settingsVaultFolderDesc">Pick a folder once — Inkpour remembers it (stored locally in this browser, not synced) and can write exports straight there instead of your Downloads folder.</span>
<span class="value-line" id="vaultFolderName" data-i18n="settingsVaultNoFolderChosen">No folder chosen yet</span>
</div>
<div style="display:flex; flex-direction:column; gap:6px; align-items:flex-end;">
<button type="button" class="btn-secondary" id="chooseVaultBtn" data-i18n="settingsVaultChooseBtn">Choose vault folder…</button>
<button type="button" class="btn-secondary" id="forgetVaultBtn" data-i18n="settingsVaultForgetBtn" hidden style="display:none;">Forget folder</button>
</div>
</div>
<div class="field" id="writeToVaultField" hidden style="display:none;">
<div class="field-label">
<strong data-i18n="settingsWriteToVaultLabel">Write exports directly to this folder</strong>
<span data-i18n="settingsWriteToVaultDesc">When on, the Markdown export button in the popup saves straight into the chosen folder instead of Downloads. You may be asked to re-grant permission after restarting your browser.</span>
</div>
<label class="toggle">
<input type="checkbox" id="writeToVault" />
<div class="toggle-track"></div>
</label>
</div>
</section>
<!-- Integrations -->
<section>
<h2 data-i18n="settingsSectionIntegrations">Integrations</h2>
<div class="field">
<div class="field-label">
<strong data-i18n="settingsGithubTokenLabel">GitHub token</strong>
<span data-i18n="settingsGithubTokenDesc">Personal access token with <code>gist</code> scope — enables "Upload to Gist" button in popup.</span>
<a href="https://github.com/settings/tokens/new?scopes=gist" target="_blank" rel="noopener" data-i18n="settingsGithubTokenLink">Create one →</a>
<span class="fineprint" data-i18n="settingsGithubTokenFineprint">⚠ Stored as plain text in this browser's local extension storage — treat it like a password. Use a token scoped to <code>gist</code> only, never one with broader repo/account access.</span>
</div>
<input type="password" id="githubToken" placeholder="ghp_…" style="min-width:180px; font-family: monospace;" />
</div>
<div class="field">
<div class="field-label">
<strong data-i18n="settingsGistVisibilityLabel">Gist visibility</strong>
<span data-i18n="settingsGistVisibilityDesc">Secret Gists are unlisted (anyone with the link can view)</span>
</div>
<select id="gistPublic">
<option value="false" data-i18n="settingsGistSecretOption">Secret (unlisted)</option>
<option value="true" data-i18n="settingsGistPublicOption">Public</option>
</select>
</div>
<div class="field">
<div class="field-label">
<strong data-i18n="settingsGistTagsLabel">Gist tags</strong>
<span data-i18n="settingsGistTagsDesc">Extra tags added to the YAML header of every Gist export, comma-separated. Search your Gists later on GitHub with <code>user:you "tag"</code>. The tags <code>ai-chat</code> and the platform name are always included automatically.</span>
</div>
<input type="text" id="gistTags" data-i18n-placeholder="settingsGistTagsPlaceholder" placeholder="work, project-x, research" style="min-width:220px;" />
</div>
<div class="field">
<div class="field-label">
<strong data-i18n="settingsNotionTokenLabel">Notion integration token</strong>
<span data-i18n="settingsNotionTokenDesc">Internal integration token — enables the "Notion ↑" button in popup, appending exports to the page below.</span>
<a href="https://www.notion.so/developers/tokens" target="_blank" rel="noopener" data-i18n="settingsNotionTokenLink">Create one →</a>
<span class="fineprint" data-i18n="settingsNotionTokenFineprint">⚠ Stored as plain text in this browser's local extension storage — treat it like a password. The token also needs to be connected to the target page in Notion (open the page → ••• menu → Connections → your integration), or uploads will fail with a 404.</span>
</div>
<input type="password" id="notionToken" placeholder="ntn_…" style="min-width:180px; font-family: monospace;" />
</div>
<div class="field">
<div class="field-label">
<strong data-i18n="settingsNotionPageIdLabel">Notion page ID</strong>
<span data-i18n="settingsNotionPageIdDesc">The page (or block) exports are appended to — copy the 32-character ID from the end of the page's Notion URL, with or without dashes.</span>
</div>
<input type="text" id="notionPageId" data-i18n-placeholder="settingsNotionPageIdPlaceholder" placeholder="a1b2c3d4e5f67890abcdef1234567890" style="min-width:220px; font-family: monospace;" />
</div>
<div class="field">
<div class="field-label">
<strong data-i18n="settingsScrubLabel">Scrub secrets before upload</strong>
<span data-i18n="settingsScrubDesc">Redact likely API keys, tokens & emails before Gist/webhook uploads</span>
</div>
<label class="toggle">
<input type="checkbox" id="scrubSecrets" checked />
<div class="toggle-track"></div>
</label>
</div>
<div class="field">
<div class="field-label">
<strong data-i18n="settingsWebhookUrlLabel">Webhook URL</strong>
<span data-i18n="settingsWebhookUrlDesc">After each export, Inkpour sends a POST request here with export metadata (title, platform, format, word count). Works with n8n, Zapier, Make.com, or any HTTP endpoint.</span>
</div>
<input type="url" id="webhookUrl" placeholder="https://hooks.example.com/…" style="min-width:260px; font-family: monospace; font-size: 12px;" />
</div>
<div class="field">
<div class="field-label">
<strong data-i18n="settingsWebhookContentLabel">Include content in webhook</strong>
<span data-i18n="settingsWebhookContentDesc">Send the full exported text in the webhook payload (increases payload size significantly for long chats)</span>
</div>
<label class="toggle" for="webhookIncludeContent">
<input type="checkbox" id="webhookIncludeContent" role="switch" />
<div class="toggle-track"></div>
</label>
</div>
</section>
<!-- Advanced -->
<section>
<h2 data-i18n="settingsSectionAdvanced">Advanced</h2>
<div class="field">
<div class="field-label">
<strong data-i18n="settingsDebugModeLabel">Debug mode</strong>
<span data-i18n="settingsDebugModeDesc">Adds "Copy debug info" and "Report bug" buttons to the popup — never includes chat content, only page structure (tag/class names, no text), selector diagnostics, and a generalized URL.</span>
</div>
<label class="toggle" for="debugMode">
<input type="checkbox" id="debugMode" role="switch" />
<div class="toggle-track"></div>
</label>
</div>
<div class="field">
<div class="field-label">
<strong data-i18n="settingsDebugAttachGistLabel">Attach full debug info as a secret Gist (for developers)</strong>
<span data-i18n="settingsDebugAttachGistDesc">Requires the GitHub token above. "Report bug" also uploads the same content-free diagnostic as a secret Gist and links it in the pre-filled report, so it's complete without manually pasting anything.</span>
</div>
<label class="toggle" for="debugAttachGist">
<input type="checkbox" id="debugAttachGist" role="switch" />
<div class="toggle-track"></div>
</label>
</div>
<div class="field" id="devToolsField" hidden style="display:none;">
<div class="field-label">
<strong data-i18n="settingsDevToolsLabel">Local debug/fuzzing tools</strong>
<span data-i18n="settingsDevToolsDesc">Standalone pages for testing the import parser — not shipped in the packaged extension, only present when loaded unpacked for development.</span>
</div>
<div style="display:flex;flex-direction:column;gap:4px;align-items:flex-end;">
<a href="#" id="openImportDebugLink" data-i18n="settingsDevToolsImportDebug">Import parser debug tool →</a>
<a href="#" id="openPdfFuzzerLink" data-i18n="settingsDevToolsPdfFuzzer">PDF import fuzzer →</a>
</div>
</div>
</section>
<div class="save-row">
<button class="btn-save" id="saveBtn" data-i18n="settingsSaveBtn">Save</button>
<span id="saveStatus"></span>
</div>
<script src="src/i18n.js"></script>
<script src="src/vaultHandle.js"></script>
<script src="settings.js"></script>
</body>
</html>