openclacky 1.2.18 → 1.3.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -0
- data/lib/clacky/agent/time_machine.rb +256 -74
- data/lib/clacky/agent/tool_executor.rb +12 -0
- data/lib/clacky/agent.rb +15 -20
- data/lib/clacky/agent_config.rb +18 -0
- data/lib/clacky/cli.rb +55 -3
- data/lib/clacky/default_skills/media-gen/SKILL.md +172 -5
- data/lib/clacky/media/base.rb +93 -0
- data/lib/clacky/media/gemini.rb +10 -0
- data/lib/clacky/media/generator.rb +57 -0
- data/lib/clacky/media/openai_compat.rb +160 -0
- data/lib/clacky/message_history.rb +12 -7
- data/lib/clacky/providers.rb +28 -0
- data/lib/clacky/rich_ui_controller.rb +3 -1
- data/lib/clacky/server/backup_manager.rb +200 -0
- data/lib/clacky/server/channel/adapters/feishu/adapter.rb +10 -2
- data/lib/clacky/server/channel/adapters/feishu/bot.rb +68 -15
- data/lib/clacky/server/channel/channel_manager.rb +65 -50
- data/lib/clacky/server/http_server.rb +345 -14
- data/lib/clacky/server/scheduler.rb +19 -0
- data/lib/clacky/server/session_registry.rb +8 -4
- data/lib/clacky/session_manager.rb +40 -2
- data/lib/clacky/tools/trash_manager.rb +14 -0
- data/lib/clacky/ui2/components/command_suggestions.rb +1 -0
- data/lib/clacky/ui2/components/modal_component.rb +34 -7
- data/lib/clacky/ui2/ui_controller.rb +150 -19
- data/lib/clacky/utils/file_processor.rb +75 -4
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.css +2038 -1147
- data/lib/clacky/web/app.js +22 -1
- data/lib/clacky/web/backup.js +119 -0
- data/lib/clacky/web/billing.js +94 -7
- data/lib/clacky/web/channels.js +81 -11
- data/lib/clacky/web/design-sample.css +247 -0
- data/lib/clacky/web/design-sample.html +127 -0
- data/lib/clacky/web/favicon.svg +16 -0
- data/lib/clacky/web/i18n.js +159 -31
- data/lib/clacky/web/index.html +175 -55
- data/lib/clacky/web/logo_nav_dark.png +0 -0
- data/lib/clacky/web/onboard.js +114 -28
- data/lib/clacky/web/sessions.js +436 -192
- data/lib/clacky/web/settings.js +21 -1
- data/lib/clacky/web/skills.js +1 -1
- data/lib/clacky/web/tasks.js +129 -61
- data/lib/clacky/web/utils.js +72 -0
- data/lib/clacky/web/ws-dispatcher.js +6 -0
- data/lib/clacky.rb +1 -0
- metadata +7 -3
- data/lib/clacky/server/channel/group_message_buffer.rb +0 -53
data/lib/clacky/web/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title id="page-title">{{BRAND_NAME}}</title>
|
|
7
|
-
<link rel="icon" href="/favicon.
|
|
7
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
|
8
8
|
<link rel="apple-touch-icon" href="/apple-touch-icon-180.png">
|
|
9
9
|
<link rel="stylesheet" href="/vendor/katex/katex.min.css">
|
|
10
10
|
<link rel="stylesheet" href="/vendor/hljs/hljs-theme.css">
|
|
@@ -43,6 +43,13 @@
|
|
|
43
43
|
title="Creator / Owner" data-i18n-title="header.owner.tooltip">OWNER</button>
|
|
44
44
|
</div>
|
|
45
45
|
</div>
|
|
46
|
+
<div id="header-center">
|
|
47
|
+
<button id="header-cmdbar" type="button" title="Search sessions">
|
|
48
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>
|
|
49
|
+
<span class="cmdbar-ph" data-i18n="header.cmdbar.placeholder">Search sessions…</span>
|
|
50
|
+
<span class="cmdbar-kbd">⌘K</span>
|
|
51
|
+
</button>
|
|
52
|
+
</div>
|
|
46
53
|
<div id="header-right">
|
|
47
54
|
<button id="share-toggle-header" class="theme-toggle-btn" data-i18n-title="share.tooltip" title="Share">
|
|
48
55
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon-sm">
|
|
@@ -62,6 +69,42 @@
|
|
|
62
69
|
</div>
|
|
63
70
|
</header>
|
|
64
71
|
|
|
72
|
+
<!-- ── Command-palette search overlay (⌘K / top cmdbar) ─────────────────── -->
|
|
73
|
+
<!-- Search lives here, decoupled from the sidebar list: results render in
|
|
74
|
+
#session-search-results and the left session list is never replaced. -->
|
|
75
|
+
<div id="session-search-overlay" class="cmd-palette-overlay" hidden>
|
|
76
|
+
<div class="cmd-palette" role="dialog" aria-modal="true" aria-label="Search sessions">
|
|
77
|
+
<div class="search-panel-card">
|
|
78
|
+
<!-- Input row -->
|
|
79
|
+
<div class="search-input-row">
|
|
80
|
+
<span class="search-icon">🔍</span>
|
|
81
|
+
<input id="session-search-q" type="text" class="search-input"
|
|
82
|
+
data-i18n-placeholder="sessions.search.placeholder" placeholder="Search sessions…"
|
|
83
|
+
autocomplete="off" />
|
|
84
|
+
<button id="btn-search-q-clear" class="btn-search-q-clear" aria-label="Clear text" hidden>✕</button>
|
|
85
|
+
<button id="btn-session-search-close" class="cmd-palette-esc" type="button" aria-label="Close">ESC</button>
|
|
86
|
+
</div>
|
|
87
|
+
<!-- Filter row -->
|
|
88
|
+
<div class="search-filter-row">
|
|
89
|
+
<select id="session-search-type" class="search-select">
|
|
90
|
+
<option value="" data-i18n="sessions.search.typeAll">All types</option>
|
|
91
|
+
<option value="manual" data-i18n="sessions.search.typeManual">Default</option>
|
|
92
|
+
<option value="cron" data-i18n="sessions.search.typeCron">Scheduled</option>
|
|
93
|
+
<option value="channel" data-i18n="sessions.search.typeChannel">Channel</option>
|
|
94
|
+
<option value="setup" data-i18n="sessions.search.typeSetup">Setup</option>
|
|
95
|
+
<option value="coding" data-i18n="sessions.search.typeCoding">Coding</option>
|
|
96
|
+
</select>
|
|
97
|
+
<div class="search-date-wrap">
|
|
98
|
+
<button id="session-search-date" class="search-date datepicker-trigger" type="button" data-value="" data-i18n="sessions.search.datePlaceholder"></button>
|
|
99
|
+
</div>
|
|
100
|
+
<button id="btn-search-clear-all" class="btn-search-clear-all" aria-label="Clear filters" hidden>✕</button>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
<!-- Results render here (independent from the sidebar session list) -->
|
|
104
|
+
<div id="session-search-results" class="cmd-palette-results"></div>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
65
108
|
<!-- ── Sidebar overlay (mobile only) ───────────────────────────────────── -->
|
|
66
109
|
<div id="sidebar-overlay"></div>
|
|
67
110
|
|
|
@@ -74,17 +117,10 @@
|
|
|
74
117
|
<div id="sidebar-list">
|
|
75
118
|
<!-- Chat Group -->
|
|
76
119
|
<div id="chat-section">
|
|
77
|
-
<!-- Header: "Sessions" label +
|
|
120
|
+
<!-- Header: "Sessions" label + [+ ▾] split button -->
|
|
78
121
|
<div class="sidebar-divider">
|
|
79
122
|
<span data-i18n="sidebar.chat">Sessions</span>
|
|
80
123
|
<div class="sidebar-divider-actions">
|
|
81
|
-
<!-- Magnifier toggle (shown when ≥10 sessions, managed by JS) -->
|
|
82
|
-
<button id="btn-session-search-toggle" class="btn-icon-sm" title="Search sessions" style="display:none" aria-label="Search sessions">
|
|
83
|
-
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
84
|
-
<circle cx="6.5" cy="6.5" r="4.5" stroke="currentColor" stroke-width="1.6"/>
|
|
85
|
-
<line x1="10.3" y1="10.3" x2="14" y2="14" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/>
|
|
86
|
-
</svg>
|
|
87
|
-
</button>
|
|
88
124
|
<div class="btn-split-wrap">
|
|
89
125
|
<button id="btn-new-session-inline" class="btn-split-main" title="New Session" data-i18n="sessions.newSession">+ New Session</button>
|
|
90
126
|
<button id="btn-new-session-arrow" class="btn-split-arrow" title="Options">▾</button>
|
|
@@ -101,35 +137,6 @@
|
|
|
101
137
|
</div>
|
|
102
138
|
</div>
|
|
103
139
|
|
|
104
|
-
<!-- Search panel (hidden by default, toggled by magnifier button) -->
|
|
105
|
-
<div id="session-search-bar" class="session-search-panel" hidden>
|
|
106
|
-
<div class="search-panel-card">
|
|
107
|
-
<!-- Input row -->
|
|
108
|
-
<div class="search-input-row">
|
|
109
|
-
<span class="search-icon">🔍</span>
|
|
110
|
-
<input id="session-search-q" type="text" class="search-input"
|
|
111
|
-
data-i18n-placeholder="sessions.search.placeholder" placeholder="Search sessions…"
|
|
112
|
-
autocomplete="off" />
|
|
113
|
-
<button id="btn-search-q-clear" class="btn-search-q-clear" aria-label="Clear text" hidden>✕</button>
|
|
114
|
-
</div>
|
|
115
|
-
<!-- Filter row -->
|
|
116
|
-
<div class="search-filter-row">
|
|
117
|
-
<select id="session-search-type" class="search-select">
|
|
118
|
-
<option value="" data-i18n="sessions.search.typeAll">All types</option>
|
|
119
|
-
<option value="manual" data-i18n="sessions.search.typeManual">Default</option>
|
|
120
|
-
<option value="cron" data-i18n="sessions.search.typeCron">Scheduled</option>
|
|
121
|
-
<option value="channel" data-i18n="sessions.search.typeChannel">Channel</option>
|
|
122
|
-
<option value="setup" data-i18n="sessions.search.typeSetup">Setup</option>
|
|
123
|
-
<option value="coding" data-i18n="sessions.search.typeCoding">Coding</option>
|
|
124
|
-
</select>
|
|
125
|
-
<div class="search-date-wrap">
|
|
126
|
-
<button id="session-search-date" class="search-date datepicker-trigger" type="button" data-value="" data-i18n="sessions.search.datePlaceholder"></button>
|
|
127
|
-
</div>
|
|
128
|
-
<button id="btn-search-clear-all" class="btn-search-clear-all" aria-label="Clear filters" hidden>✕</button>
|
|
129
|
-
</div>
|
|
130
|
-
</div>
|
|
131
|
-
</div>
|
|
132
|
-
|
|
133
140
|
<!-- Cron view header (hidden by default, shown when viewing cron sessions) -->
|
|
134
141
|
<div id="cron-view-header" class="sidebar-divider" style="display:none">
|
|
135
142
|
<button id="btn-cron-back" class="btn-icon-sm" title="Back" aria-label="Back to session list">
|
|
@@ -295,10 +302,30 @@
|
|
|
295
302
|
|
|
296
303
|
<!-- Welcome screen -->
|
|
297
304
|
<div id="welcome" class="centered">
|
|
298
|
-
<div class="
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
305
|
+
<div class="ce-mark">
|
|
306
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round">
|
|
307
|
+
<path d="M12 2l2.4 6.6L21 11l-6.6 2.4L12 20l-2.4-6.6L3 11l6.6-2.4z"/>
|
|
308
|
+
</svg>
|
|
309
|
+
</div>
|
|
310
|
+
<div class="ce-head">
|
|
311
|
+
<h2 id="welcome-title" class="ce-title" data-i18n="welcome.title" data-i18n-vars="brand={{BRAND_NAME}}">What should we build?</h2>
|
|
312
|
+
<p class="ce-sub" data-i18n="welcome.body">Your agent is standing by. Pick a starting point or just type.</p>
|
|
313
|
+
</div>
|
|
314
|
+
<div class="chips">
|
|
315
|
+
<button class="chip" data-welcome-prompt="welcome.chip.code.prompt">
|
|
316
|
+
<span class="ci"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M16 18l6-6-6-6M8 6l-6 6 6 6"/></svg></span>
|
|
317
|
+
<span class="ct-wrap"><span class="ct" data-i18n="welcome.chip.code.title">Write code</span><span class="cd" data-i18n="welcome.chip.code.desc">build, refactor, debug</span></span>
|
|
318
|
+
</button>
|
|
319
|
+
<button class="chip" data-welcome-prompt="welcome.chip.task.prompt">
|
|
320
|
+
<span class="ci"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></span>
|
|
321
|
+
<span class="ct-wrap"><span class="ct" data-i18n="welcome.chip.task.title">Automate a task</span><span class="cd" data-i18n="welcome.chip.task.desc">schedule, run, repeat</span></span>
|
|
322
|
+
</button>
|
|
323
|
+
<button class="chip" data-welcome-prompt="welcome.chip.research.prompt">
|
|
324
|
+
<span class="ci"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5V6a2 2 0 0 1 2-2h12v16H6a2 2 0 0 1-2-2z"/><path d="M8 7h7"/></svg></span>
|
|
325
|
+
<span class="ct-wrap"><span class="ct" data-i18n="welcome.chip.research.title">Research</span><span class="cd" data-i18n="welcome.chip.research.desc">search, read, summarize</span></span>
|
|
326
|
+
</button>
|
|
327
|
+
</div>
|
|
328
|
+
<button id="btn-welcome-new" class="ce-blank" data-i18n="welcome.btn">Start a blank session</button>
|
|
302
329
|
</div>
|
|
303
330
|
|
|
304
331
|
<!-- Scheduled Tasks list panel (shown when user clicks "Scheduled Tasks") -->
|
|
@@ -406,9 +433,9 @@
|
|
|
406
433
|
</button>
|
|
407
434
|
<textarea id="user-input" rows="1"
|
|
408
435
|
data-i18n-placeholder="chat.input.placeholder"
|
|
409
|
-
placeholder="Message… (Enter to send, Shift
|
|
410
|
-
<button id="btn-send"
|
|
411
|
-
<button id="btn-interrupt" style="display:none" title="Stop"></button>
|
|
436
|
+
placeholder="Message… (Enter to send, Shift-Enter for newline)"></textarea>
|
|
437
|
+
<button id="btn-send" title="Send message"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg></button>
|
|
438
|
+
<button id="btn-interrupt" style="display:none" title="Stop"><svg viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="6" width="12" height="12" rx="1.5"/></svg></button>
|
|
412
439
|
</div>
|
|
413
440
|
</div>
|
|
414
441
|
</div><!-- /#chat-main -->
|
|
@@ -777,7 +804,7 @@
|
|
|
777
804
|
<!-- Models section -->
|
|
778
805
|
<section class="settings-section">
|
|
779
806
|
<div class="settings-section-title">
|
|
780
|
-
<span data-i18n="settings.models.title">
|
|
807
|
+
<span data-i18n="settings.models.title">Primary Model</span>
|
|
781
808
|
<button id="btn-add-model" class="btn-settings-add" data-i18n="settings.models.add">+ Add Model</button>
|
|
782
809
|
</div>
|
|
783
810
|
<div id="model-cards"></div>
|
|
@@ -786,10 +813,10 @@
|
|
|
786
813
|
<!-- Media generation section -->
|
|
787
814
|
<section class="settings-section" id="media-section">
|
|
788
815
|
<div class="settings-section-title">
|
|
789
|
-
<span data-i18n="settings.media.title">
|
|
816
|
+
<span data-i18n="settings.media.title">Secondary Models</span>
|
|
790
817
|
</div>
|
|
791
818
|
<div class="settings-section-desc" data-i18n="settings.media.desc">
|
|
792
|
-
|
|
819
|
+
Optional. Image / video / audio / vision models.
|
|
793
820
|
</div>
|
|
794
821
|
<div id="media-rows"></div>
|
|
795
822
|
</section>
|
|
@@ -898,6 +925,33 @@
|
|
|
898
925
|
</div>
|
|
899
926
|
</section>
|
|
900
927
|
|
|
928
|
+
<!-- Backup section -->
|
|
929
|
+
<section class="settings-section" id="backup-section">
|
|
930
|
+
<div class="settings-section-title">
|
|
931
|
+
<span data-i18n="settings.backup.title">Backup</span>
|
|
932
|
+
</div>
|
|
933
|
+
<p class="settings-section-desc" data-i18n="settings.backup.desc">Back up your ~/.clacky directory (config, skills, memories, tasks, sessions). Regenerable caches and logs are excluded.</p>
|
|
934
|
+
|
|
935
|
+
<div class="backup-auto-row">
|
|
936
|
+
<label class="toggle-switch">
|
|
937
|
+
<input type="checkbox" id="backup-auto-toggle">
|
|
938
|
+
<span class="toggle-slider"></span>
|
|
939
|
+
</label>
|
|
940
|
+
<span class="backup-auto-label" data-i18n="settings.backup.autoLabel">Automatic backup</span>
|
|
941
|
+
<span class="backup-auto-hint" data-i18n="settings.backup.autoHint">Daily at 03:00, keeps the latest 7</span>
|
|
942
|
+
</div>
|
|
943
|
+
|
|
944
|
+
<label class="backup-option">
|
|
945
|
+
<input type="checkbox" id="backup-include-sessions">
|
|
946
|
+
<span data-i18n="settings.backup.includeSessions">Include session history (larger archive)</span>
|
|
947
|
+
</label>
|
|
948
|
+
|
|
949
|
+
<div class="backup-actions">
|
|
950
|
+
<button id="btn-backup-now" class="btn-settings-action" data-i18n="settings.backup.runNow">💾 Download backup</button>
|
|
951
|
+
<span id="backup-status" class="model-test-result"></span>
|
|
952
|
+
</div>
|
|
953
|
+
</section>
|
|
954
|
+
|
|
901
955
|
<!-- Brand & License section -->
|
|
902
956
|
<section class="settings-section" id="brand-license-section">
|
|
903
957
|
<div class="settings-section-title">
|
|
@@ -1153,6 +1207,63 @@
|
|
|
1153
1207
|
<div id="setup-phase-key" style="display:none">
|
|
1154
1208
|
<p class="setup-phase-label" data-i18n="onboard.key.title">Connect your AI model</p>
|
|
1155
1209
|
|
|
1210
|
+
<!-- Primary path: one-click device login -->
|
|
1211
|
+
<div id="setup-device-block">
|
|
1212
|
+
<div id="setup-device-card" class="setup-device-card">
|
|
1213
|
+
<div class="setup-device-card-head">
|
|
1214
|
+
<span class="setup-device-card-title" data-i18n="onboard.device.card.title">OpenClacky AI Keys</span>
|
|
1215
|
+
<span class="setup-device-card-badge" data-i18n="provider.recommended">Recommended</span>
|
|
1216
|
+
</div>
|
|
1217
|
+
<p class="setup-device-card-lead" data-i18n="onboard.device.card.lead">Sign up and get $1 free credit · no subscription · pay as you go</p>
|
|
1218
|
+
<ul class="setup-device-card-points">
|
|
1219
|
+
<li data-i18n="onboard.device.card.point1">One key for all frontier models — Claude, Gemini, DeepSeek & more</li>
|
|
1220
|
+
<li data-i18n="onboard.device.card.point2">Same pricing as official APIs, switch models instantly</li>
|
|
1221
|
+
</ul>
|
|
1222
|
+
<button id="setup-btn-device-login" class="setup-submit-btn setup-device-btn">
|
|
1223
|
+
<span data-i18n="onboard.device.btn">Get started free →</span>
|
|
1224
|
+
</button>
|
|
1225
|
+
</div>
|
|
1226
|
+
|
|
1227
|
+
<!-- Pending state: shown while waiting for browser approval -->
|
|
1228
|
+
<div id="setup-device-pending" style="display:none">
|
|
1229
|
+
<div class="setup-device-spinner"></div>
|
|
1230
|
+
<p class="setup-device-pending-text" data-i18n="onboard.device.pending">Waiting for confirmation in your browser…</p>
|
|
1231
|
+
<p class="setup-device-code">
|
|
1232
|
+
<span data-i18n="onboard.device.code">Verification code:</span>
|
|
1233
|
+
<strong id="setup-device-usercode">—</strong>
|
|
1234
|
+
</p>
|
|
1235
|
+
<a id="setup-device-link" href="#" target="_blank" rel="noopener" class="setup-device-reopen" data-i18n="onboard.device.reopen">Open browser again →</a>
|
|
1236
|
+
<button id="setup-device-cancel" class="setup-back-btn" data-i18n="onboard.device.cancel">Cancel</button>
|
|
1237
|
+
</div>
|
|
1238
|
+
|
|
1239
|
+
<!-- Success state: shown after approval, before launching onboard session -->
|
|
1240
|
+
<div id="setup-device-success" style="display:none" class="setup-device-success">
|
|
1241
|
+
<div class="setup-device-success-head">
|
|
1242
|
+
<span class="setup-device-success-icon">✓</span>
|
|
1243
|
+
<span class="setup-device-success-title" data-i18n="onboard.device.success.title">You're in!</span>
|
|
1244
|
+
</div>
|
|
1245
|
+
<p class="setup-device-success-lead" data-i18n="onboard.device.success.lead">Your account is connected. Here's what you got:</p>
|
|
1246
|
+
<ul class="setup-device-success-points">
|
|
1247
|
+
<li>
|
|
1248
|
+
<strong data-i18n="onboard.device.success.credit.label">Free trial credit:</strong>
|
|
1249
|
+
<span data-i18n="onboard.device.success.credit.value">$1.00 (one-time, on us)</span>
|
|
1250
|
+
</li>
|
|
1251
|
+
<li>
|
|
1252
|
+
<strong data-i18n="onboard.device.success.model.label">Trial model:</strong>
|
|
1253
|
+
<span id="setup-device-success-model">or-gemini-3-5-flash</span>
|
|
1254
|
+
</li>
|
|
1255
|
+
<li class="setup-device-success-note" data-i18n="onboard.device.success.note">During the trial only Gemini 3.5 Flash is available — top up any amount to unlock all frontier models on this same key.</li>
|
|
1256
|
+
</ul>
|
|
1257
|
+
<button id="setup-btn-device-continue" class="setup-submit-btn" data-i18n="onboard.device.success.btn">Start using Clacky →</button>
|
|
1258
|
+
</div>
|
|
1259
|
+
|
|
1260
|
+
<div id="setup-device-error" class="setup-test-result" style="min-height:0;"></div>
|
|
1261
|
+
</div>
|
|
1262
|
+
|
|
1263
|
+
<!-- Secondary path: manual key entry, collapsed by default -->
|
|
1264
|
+
<details id="setup-manual-details">
|
|
1265
|
+
<summary id="setup-manual-summary" data-i18n="onboard.manual.toggle">Configure manually with your own API key</summary>
|
|
1266
|
+
|
|
1156
1267
|
<div class="setup-field">
|
|
1157
1268
|
<label class="setup-label" data-i18n="onboard.key.provider">Provider</label>
|
|
1158
1269
|
<div class="custom-select-wrapper" id="setup-provider-wrapper">
|
|
@@ -1166,7 +1277,6 @@
|
|
|
1166
1277
|
<div class="custom-select-option" data-value="" data-i18n="onboard.key.provider.placeholder">— Choose provider —</div>
|
|
1167
1278
|
</div>
|
|
1168
1279
|
</div>
|
|
1169
|
-
<div id="setup-provider-promo" class="provider-promo-hint"></div>
|
|
1170
1280
|
</div>
|
|
1171
1281
|
|
|
1172
1282
|
<div class="setup-field">
|
|
@@ -1218,6 +1328,7 @@
|
|
|
1218
1328
|
<button id="setup-btn-back" class="setup-back-btn" data-i18n="onboard.key.btn.back">← Back</button>
|
|
1219
1329
|
<button id="setup-btn-test" class="setup-submit-btn" data-i18n="onboard.key.btn.test">Test & Continue →</button>
|
|
1220
1330
|
</div>
|
|
1331
|
+
</details>
|
|
1221
1332
|
</div>
|
|
1222
1333
|
|
|
1223
1334
|
</div>
|
|
@@ -1254,9 +1365,14 @@
|
|
|
1254
1365
|
|
|
1255
1366
|
<div class="modal-field">
|
|
1256
1367
|
<label class="modal-label" data-i18n="sessions.modal.directory">Working Directory</label>
|
|
1257
|
-
<
|
|
1258
|
-
|
|
1259
|
-
|
|
1368
|
+
<div class="modal-input-row">
|
|
1369
|
+
<input id="new-session-directory" type="text" class="modal-input"
|
|
1370
|
+
data-i18n-placeholder="sessions.modal.directory.placeholder"
|
|
1371
|
+
placeholder="~/workspace/my-project">
|
|
1372
|
+
<button id="new-session-browse-btn" type="button" class="modal-browse-btn" data-i18n-title="sessions.modal.directory.browse" title="Browse…" aria-label="Browse">
|
|
1373
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>
|
|
1374
|
+
</button>
|
|
1375
|
+
</div>
|
|
1260
1376
|
</div>
|
|
1261
1377
|
|
|
1262
1378
|
<div id="new-session-init-project-field" class="modal-field-checkbox" style="display:none">
|
|
@@ -1302,7 +1418,9 @@
|
|
|
1302
1418
|
<!-- Rename Session Modal -->
|
|
1303
1419
|
<div id="rename-modal-overlay" class="modal-overlay" style="display:none">
|
|
1304
1420
|
<div class="modal-box sm">
|
|
1305
|
-
<div class="modal-
|
|
1421
|
+
<div class="modal-header">
|
|
1422
|
+
<h3 class="modal-title" data-i18n="sessions.actions.rename">Rename</h3>
|
|
1423
|
+
</div>
|
|
1306
1424
|
<div class="modal-body">
|
|
1307
1425
|
<div class="modal-field">
|
|
1308
1426
|
<label class="modal-label" for="rename-modal-input">
|
|
@@ -1312,14 +1430,14 @@
|
|
|
1312
1430
|
<input type="text" id="rename-modal-input" class="modal-input" autocomplete="off" spellcheck="false">
|
|
1313
1431
|
</div>
|
|
1314
1432
|
</div>
|
|
1315
|
-
<div class="modal-
|
|
1433
|
+
<div class="modal-footer">
|
|
1316
1434
|
<button id="rename-modal-cancel" class="btn-secondary" data-i18n="modal.cancel">Cancel</button>
|
|
1317
1435
|
<button id="rename-modal-save" class="btn-primary" data-i18n="modal.ok">OK</button>
|
|
1318
1436
|
</div>
|
|
1319
1437
|
</div>
|
|
1320
1438
|
</div>
|
|
1321
1439
|
|
|
1322
|
-
|
|
1440
|
+
<div id="tooltip" style="display:none"></div>
|
|
1323
1441
|
|
|
1324
1442
|
<script src="/marked.min.js"></script>
|
|
1325
1443
|
<script src="/vendor/hljs/highlight.min.js"></script>
|
|
@@ -1339,6 +1457,7 @@
|
|
|
1339
1457
|
<script src="/skills.js"></script>
|
|
1340
1458
|
<script src="/channels.js"></script>
|
|
1341
1459
|
<script src="/mcp.js"></script>
|
|
1460
|
+
<script src="/backup.js"></script>
|
|
1342
1461
|
<script src="/model-tester.js"></script>
|
|
1343
1462
|
<script src="/settings.js"></script>
|
|
1344
1463
|
<script src="/billing.js"></script>
|
|
@@ -1352,5 +1471,6 @@
|
|
|
1352
1471
|
<script src="/vendor/qrcode/qrcode.min.js"></script>
|
|
1353
1472
|
<script src="/share.js"></script>
|
|
1354
1473
|
<script src="/app.js"></script>
|
|
1474
|
+
<script>Tooltip.init();</script>
|
|
1355
1475
|
</body>
|
|
1356
1476
|
</html>
|
|
Binary file
|
data/lib/clacky/web/onboard.js
CHANGED
|
@@ -132,9 +132,6 @@ const Onboard = (() => {
|
|
|
132
132
|
_renderProviderOptions();
|
|
133
133
|
// Bind event listeners only once (delegation-based, safe to skip on re-entry)
|
|
134
134
|
_bindCustomDropdown();
|
|
135
|
-
// Show promo hint by default (no provider selected on initial entry)
|
|
136
|
-
const promoHint = $("setup-provider-promo");
|
|
137
|
-
if (promoHint && !promoHint.classList.contains("visible")) _showPromoHint(promoHint);
|
|
138
135
|
}
|
|
139
136
|
|
|
140
137
|
function _renderProviderOptions() {
|
|
@@ -249,18 +246,6 @@ const Onboard = (() => {
|
|
|
249
246
|
});
|
|
250
247
|
}
|
|
251
248
|
|
|
252
|
-
function _showPromoHint(promoHint) {
|
|
253
|
-
const items = [
|
|
254
|
-
I18n.t("provider.promo.openclacky.1"),
|
|
255
|
-
I18n.t("provider.promo.openclacky.2"),
|
|
256
|
-
I18n.t("provider.promo.openclacky.3"),
|
|
257
|
-
];
|
|
258
|
-
const title = `<div class="promo-title">${I18n.t("provider.promo.openclacky.title")}</div>`;
|
|
259
|
-
const body = items.map(s => `<div class="promo-item"><span class="promo-icon">✦</span>${s}</div>`).join("");
|
|
260
|
-
promoHint.innerHTML = `<div class="promo-inner">${title}${body}</div>`;
|
|
261
|
-
promoHint.classList.add("visible");
|
|
262
|
-
}
|
|
263
|
-
|
|
264
249
|
function _bindCustomDropdown() {
|
|
265
250
|
if (_dropdownBound) return; // listeners already attached
|
|
266
251
|
_dropdownBound = true;
|
|
@@ -292,7 +277,6 @@ const Onboard = (() => {
|
|
|
292
277
|
trigger.classList.remove("open");
|
|
293
278
|
|
|
294
279
|
const getApiKeyLink = $("setup-get-apikey-link");
|
|
295
|
-
const promoHint = $("setup-provider-promo");
|
|
296
280
|
if (value === "__custom__") {
|
|
297
281
|
// Custom: clear presets so the user can fill in their own values
|
|
298
282
|
$("setup-model").value = "";
|
|
@@ -300,7 +284,6 @@ const Onboard = (() => {
|
|
|
300
284
|
_updateSetupModelDropdown([]);
|
|
301
285
|
_updateSetupBaseUrlDropdown(null);
|
|
302
286
|
if (getApiKeyLink) getApiKeyLink.style.display = "none";
|
|
303
|
-
if (promoHint) promoHint.classList.remove("visible");
|
|
304
287
|
} else if (value) {
|
|
305
288
|
const preset = _providers.find(p => p.id === value);
|
|
306
289
|
if (preset) {
|
|
@@ -316,18 +299,8 @@ const Onboard = (() => {
|
|
|
316
299
|
getApiKeyLink.style.display = "none";
|
|
317
300
|
}
|
|
318
301
|
}
|
|
319
|
-
// Show promo hint for openclacky, hide for others
|
|
320
|
-
if (promoHint) {
|
|
321
|
-
if (value === "openclacky") {
|
|
322
|
-
_showPromoHint(promoHint);
|
|
323
|
-
} else {
|
|
324
|
-
promoHint.classList.remove("visible");
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
302
|
} else {
|
|
328
303
|
if (getApiKeyLink) getApiKeyLink.style.display = "none";
|
|
329
|
-
// Show promo hint when no provider selected (default state)
|
|
330
|
-
if (promoHint) _showPromoHint(promoHint);
|
|
331
304
|
}
|
|
332
305
|
});
|
|
333
306
|
|
|
@@ -398,6 +371,119 @@ const Onboard = (() => {
|
|
|
398
371
|
$("setup-btn-back").addEventListener("click", () => {
|
|
399
372
|
_showSetupStep("lang");
|
|
400
373
|
});
|
|
374
|
+
|
|
375
|
+
_bindDeviceStep();
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// ── Device-authorization login (primary onboarding path) ──────────────────
|
|
379
|
+
let _devicePolling = false;
|
|
380
|
+
|
|
381
|
+
function _bindDeviceStep() {
|
|
382
|
+
const btn = $("setup-btn-device-login");
|
|
383
|
+
if (btn) btn.addEventListener("click", _startDeviceLogin);
|
|
384
|
+
|
|
385
|
+
const cancel = $("setup-device-cancel");
|
|
386
|
+
if (cancel) cancel.addEventListener("click", () => {
|
|
387
|
+
_devicePolling = false;
|
|
388
|
+
_showDevicePending(false);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
const continueBtn = $("setup-btn-device-continue");
|
|
392
|
+
if (continueBtn) continueBtn.addEventListener("click", () => {
|
|
393
|
+
_launchOnboardSession();
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function _showDevicePending(on) {
|
|
398
|
+
const pending = $("setup-device-pending");
|
|
399
|
+
const card = $("setup-device-card");
|
|
400
|
+
if (pending) pending.style.display = on ? "" : "none";
|
|
401
|
+
if (card) card.style.display = on ? "none" : "";
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function _showDeviceSuccess(model) {
|
|
405
|
+
const pending = $("setup-device-pending");
|
|
406
|
+
const card = $("setup-device-card");
|
|
407
|
+
const success = $("setup-device-success");
|
|
408
|
+
const modelEl = $("setup-device-success-model");
|
|
409
|
+
if (pending) pending.style.display = "none";
|
|
410
|
+
if (card) card.style.display = "none";
|
|
411
|
+
if (success) success.style.display = "";
|
|
412
|
+
if (modelEl && model) modelEl.textContent = model;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function _setDeviceError(msg) {
|
|
416
|
+
const el = $("setup-device-error");
|
|
417
|
+
if (!el) return;
|
|
418
|
+
el.textContent = msg ? "✗ " + msg : "";
|
|
419
|
+
el.className = "setup-test-result" + (msg ? " result-fail" : "");
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
async function _startDeviceLogin() {
|
|
423
|
+
const zh = _selectedLang === "zh";
|
|
424
|
+
_setDeviceError("");
|
|
425
|
+
|
|
426
|
+
let data;
|
|
427
|
+
try {
|
|
428
|
+
const res = await fetch("/api/onboard/device/start", { method: "POST" });
|
|
429
|
+
data = await res.json();
|
|
430
|
+
} catch (_) {
|
|
431
|
+
data = null;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (!data || !data.ok) {
|
|
435
|
+
_setDeviceError((data && data.error) || (zh ? "无法发起登录,请稍后重试。" : "Could not start login. Please try again."));
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const url = data.verification_uri_complete || data.verification_uri;
|
|
440
|
+
const codeEl = $("setup-device-usercode");
|
|
441
|
+
if (codeEl) codeEl.textContent = data.user_code || "—";
|
|
442
|
+
const link = $("setup-device-link");
|
|
443
|
+
if (link && url) link.href = url;
|
|
444
|
+
|
|
445
|
+
_showDevicePending(true);
|
|
446
|
+
if (url) window.open(url, "_blank", "noopener");
|
|
447
|
+
|
|
448
|
+
_devicePolling = true;
|
|
449
|
+
_pollDevice(data.device_code, (data.interval || 5) * 1000);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
async function _pollDevice(deviceCode, intervalMs) {
|
|
453
|
+
const zh = _selectedLang === "zh";
|
|
454
|
+
|
|
455
|
+
while (_devicePolling) {
|
|
456
|
+
await new Promise(r => setTimeout(r, intervalMs));
|
|
457
|
+
if (!_devicePolling) return;
|
|
458
|
+
|
|
459
|
+
let data;
|
|
460
|
+
try {
|
|
461
|
+
const res = await fetch("/api/onboard/device/poll", {
|
|
462
|
+
method: "POST",
|
|
463
|
+
headers: { "Content-Type": "application/json" },
|
|
464
|
+
body: JSON.stringify({ device_code: deviceCode })
|
|
465
|
+
});
|
|
466
|
+
data = await res.json();
|
|
467
|
+
} catch (_) {
|
|
468
|
+
continue; // transient network error — keep polling
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (data.status === "approved") {
|
|
472
|
+
_devicePolling = false;
|
|
473
|
+
_showDeviceSuccess(data.default_model);
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
if (data.status === "pending") continue;
|
|
477
|
+
|
|
478
|
+
// denied / expired / consumed / error
|
|
479
|
+
_devicePolling = false;
|
|
480
|
+
_showDevicePending(false);
|
|
481
|
+
const msg = data.status === "denied"
|
|
482
|
+
? (zh ? "授权已被拒绝。" : "Authorization was denied.")
|
|
483
|
+
: (zh ? "授权已过期,请重新登录。" : "Authorization expired. Please try again.");
|
|
484
|
+
_setDeviceError(data.error || msg);
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
401
487
|
}
|
|
402
488
|
|
|
403
489
|
async function _testAndSave() {
|
|
@@ -469,7 +555,7 @@ const Onboard = (() => {
|
|
|
469
555
|
const res = await fetch("/api/sessions", {
|
|
470
556
|
method: "POST",
|
|
471
557
|
headers: { "Content-Type": "application/json" },
|
|
472
|
-
body: JSON.stringify({ name: "
|
|
558
|
+
body: JSON.stringify({ name: "Onboard", source: "setup" })
|
|
473
559
|
});
|
|
474
560
|
const data = await res.json();
|
|
475
561
|
const session = data.session;
|