openclacky 1.2.10 → 1.2.13
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/.clacky/skills/gem-release/SKILL.md +1 -1
- data/.clacky/skills/gem-release/scripts/release.sh +4 -1
- data/CHANGELOG.md +56 -1
- data/lib/clacky/agent/llm_caller.rb +40 -25
- data/lib/clacky/agent/memory_updater.rb +12 -0
- data/lib/clacky/agent/session_serializer.rb +1 -1
- data/lib/clacky/agent/skill_auto_creator.rb +7 -4
- data/lib/clacky/agent/skill_evolution.rb +23 -5
- data/lib/clacky/agent/skill_manager.rb +86 -1
- data/lib/clacky/agent/skill_reflector.rb +18 -23
- data/lib/clacky/agent/tool_registry.rb +10 -0
- data/lib/clacky/agent.rb +68 -23
- data/lib/clacky/agent_config.rb +59 -15
- data/lib/clacky/anthropic_stream_aggregator.rb +17 -1
- data/lib/clacky/bedrock_stream_aggregator.rb +17 -1
- data/lib/clacky/cli.rb +55 -0
- data/lib/clacky/client.rb +25 -3
- data/lib/clacky/default_skills/channel-manager/SKILL.md +47 -42
- data/lib/clacky/default_skills/channel-manager/feishu_setup.rb +134 -0
- data/lib/clacky/default_skills/media-gen/SKILL.md +5 -0
- data/lib/clacky/default_skills/persist-memory/SKILL.md +4 -3
- data/lib/clacky/default_skills/search-skills/SKILL.md +61 -0
- data/lib/clacky/idle_compression_timer.rb +1 -1
- data/lib/clacky/message_format/open_ai.rb +7 -1
- data/lib/clacky/message_history.rb +57 -0
- data/lib/clacky/openai_stream_aggregator.rb +30 -3
- data/lib/clacky/providers.rb +40 -12
- data/lib/clacky/server/channel/adapters/dingtalk/adapter.rb +10 -1
- data/lib/clacky/server/channel/adapters/discord/adapter.rb +8 -2
- data/lib/clacky/server/channel/adapters/feishu/adapter.rb +10 -1
- data/lib/clacky/server/channel/adapters/feishu/bot.rb +12 -0
- data/lib/clacky/server/channel/adapters/feishu/message_parser.rb +23 -3
- data/lib/clacky/server/channel/adapters/telegram/adapter.rb +12 -2
- data/lib/clacky/server/channel/adapters/wecom/adapter.rb +5 -1
- data/lib/clacky/server/channel/channel_manager.rb +65 -4
- data/lib/clacky/server/channel/group_message_buffer.rb +53 -0
- data/lib/clacky/server/http_server.rb +190 -10
- data/lib/clacky/server/session_registry.rb +34 -14
- data/lib/clacky/server/web_ui_controller.rb +24 -1
- data/lib/clacky/session_manager.rb +120 -0
- data/lib/clacky/tools/trash_manager.rb +1 -1
- data/lib/clacky/tools/web_search.rb +59 -8
- data/lib/clacky/ui2/layout_manager.rb +15 -5
- data/lib/clacky/ui2/progress_handle.rb +7 -1
- data/lib/clacky/ui2/ui_controller.rb +27 -0
- data/lib/clacky/ui_interface.rb +22 -0
- data/lib/clacky/utils/model_pricing.rb +96 -0
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.css +230 -7
- data/lib/clacky/web/app.js +6 -5
- data/lib/clacky/web/apple-touch-icon-180.png +0 -0
- data/lib/clacky/web/brand.js +22 -2
- data/lib/clacky/web/favicon.ico +0 -0
- data/lib/clacky/web/i18n.js +22 -4
- data/lib/clacky/web/index.html +6 -4
- data/lib/clacky/web/logo_nav_dark.png +0 -0
- data/lib/clacky/web/model-tester.js +8 -1
- data/lib/clacky/web/sessions.js +576 -120
- data/lib/clacky/web/settings.js +213 -51
- data/lib/clacky/web/skills.js +5 -14
- data/lib/clacky/web/theme.js +1 -0
- data/lib/clacky/web/utils.js +57 -0
- data/lib/clacky/web/ws-dispatcher.js +136 -0
- data/scripts/build/lib/gem.sh +9 -2
- data/scripts/build/src/install_full.sh.cc +2 -0
- data/scripts/build/src/uninstall.sh.cc +1 -1
- data/scripts/install.ps1 +19 -5
- data/scripts/install.sh +9 -2
- data/scripts/install_full.sh +11 -2
- data/scripts/install_rails_deps.sh +9 -2
- data/scripts/uninstall.sh +10 -3
- metadata +9 -2
data/lib/clacky/web/app.css
CHANGED
|
@@ -256,8 +256,7 @@ body {
|
|
|
256
256
|
letter-spacing: 0;
|
|
257
257
|
}
|
|
258
258
|
.header-logo-img {
|
|
259
|
-
height:
|
|
260
|
-
max-width: 6.875rem;
|
|
259
|
+
height: 2.5rem;
|
|
261
260
|
object-fit: contain;
|
|
262
261
|
display: block;
|
|
263
262
|
flex-shrink: 0;
|
|
@@ -857,6 +856,43 @@ body {
|
|
|
857
856
|
opacity: 0.5;
|
|
858
857
|
}
|
|
859
858
|
|
|
859
|
+
.session-search-group {
|
|
860
|
+
padding: 0.5rem 0.75rem 0.25rem;
|
|
861
|
+
font-size: 0.6875rem;
|
|
862
|
+
font-weight: 600;
|
|
863
|
+
color: var(--color-text-muted);
|
|
864
|
+
text-transform: none;
|
|
865
|
+
letter-spacing: 0.02em;
|
|
866
|
+
}
|
|
867
|
+
.session-search-group + .session-search-group {
|
|
868
|
+
margin-top: 0.25rem;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
.session-snippet {
|
|
872
|
+
white-space: nowrap;
|
|
873
|
+
overflow: hidden;
|
|
874
|
+
text-overflow: ellipsis;
|
|
875
|
+
font-size: 0.6875rem;
|
|
876
|
+
color: var(--color-text-muted);
|
|
877
|
+
margin-top: 2px;
|
|
878
|
+
line-height: 1.35;
|
|
879
|
+
opacity: 0.85;
|
|
880
|
+
}
|
|
881
|
+
.session-snippet mark {
|
|
882
|
+
background: var(--color-warning-bg, #fef3c7);
|
|
883
|
+
color: var(--color-text-primary);
|
|
884
|
+
padding: 0 2px;
|
|
885
|
+
border-radius: 2px;
|
|
886
|
+
font-weight: 600;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
.session-name__text mark {
|
|
890
|
+
background: var(--color-warning-bg, #fef3c7);
|
|
891
|
+
color: var(--color-text-primary);
|
|
892
|
+
padding: 0 1px;
|
|
893
|
+
border-radius: 2px;
|
|
894
|
+
}
|
|
895
|
+
|
|
860
896
|
/* Legacy .btn-new-inline — kept for compat, can be removed after cleanup */
|
|
861
897
|
.btn-new-inline {
|
|
862
898
|
display: none;
|
|
@@ -985,9 +1021,22 @@ body {
|
|
|
985
1021
|
z-index: 1000;
|
|
986
1022
|
animation: fadeIn 0.15s ease;
|
|
987
1023
|
}
|
|
1024
|
+
.session-context-menu {
|
|
1025
|
+
background: var(--color-bg-secondary);
|
|
1026
|
+
border: 1px solid var(--color-border-primary);
|
|
1027
|
+
border-radius: 8px;
|
|
1028
|
+
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.08), 0 2px 6px rgba(0, 0, 0, 0.04);
|
|
1029
|
+
padding: 0.25rem;
|
|
1030
|
+
min-width: 10rem;
|
|
1031
|
+
z-index: 1001;
|
|
1032
|
+
animation: fadeIn 0.15s ease;
|
|
1033
|
+
}
|
|
988
1034
|
[data-theme="dark"] .session-actions-menu {
|
|
989
1035
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5), 0 2px 6px rgba(0, 0, 0, 0.3);
|
|
990
1036
|
}
|
|
1037
|
+
[data-theme="dark"] .session-context-menu {
|
|
1038
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5), 0 2px 6px rgba(0, 0, 0, 0.3);
|
|
1039
|
+
}
|
|
991
1040
|
.session-actions-menu-item {
|
|
992
1041
|
display: flex;
|
|
993
1042
|
align-items: center;
|
|
@@ -1623,7 +1672,7 @@ body {
|
|
|
1623
1672
|
.sib-status-* classes instead. Legacy .status-* styles removed. */
|
|
1624
1673
|
|
|
1625
1674
|
/* ── Messages ────────────────────────────────────────────────────────────── */
|
|
1626
|
-
|
|
1675
|
+
.chat-messages-scroll {
|
|
1627
1676
|
flex: 1;
|
|
1628
1677
|
overflow-y: auto;
|
|
1629
1678
|
overflow-x: hidden;
|
|
@@ -2109,6 +2158,15 @@ body {
|
|
|
2109
2158
|
.tu-delta-ok { color: #34d399; } /* green — normal */
|
|
2110
2159
|
.tu-delta-mid { color: #f59e0b; } /* yellow — moderate */
|
|
2111
2160
|
.tu-delta-high { color: #f87171; } /* red — large */
|
|
2161
|
+
|
|
2162
|
+
.tool-item .token-usage-line.tu-attached {
|
|
2163
|
+
align-self: stretch;
|
|
2164
|
+
margin: 0.25rem 0 0;
|
|
2165
|
+
padding: 2px 0 0;
|
|
2166
|
+
opacity: 0.55;
|
|
2167
|
+
flex-wrap: wrap;
|
|
2168
|
+
}
|
|
2169
|
+
.tool-item .token-usage-line.tu-attached:hover { opacity: 0.95; }
|
|
2112
2170
|
.tu-delta-neg { color: var(--color-accent-primary); } /* cyan — compression */
|
|
2113
2171
|
|
|
2114
2172
|
/* Detail fields: hidden by default, revealed on hover */
|
|
@@ -2172,7 +2230,26 @@ body {
|
|
|
2172
2230
|
gap: 0.375rem;
|
|
2173
2231
|
}
|
|
2174
2232
|
.tool-item-name { color: var(--color-warning); font-weight: 600; }
|
|
2175
|
-
.tool-item-arg { color: var(--color-text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
2233
|
+
.tool-item-arg { color: var(--color-text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; flex: 1 1 auto; }
|
|
2234
|
+
.tool-item-header.tool-item-expandable { cursor: pointer; user-select: none; }
|
|
2235
|
+
.tool-item-header.tool-item-expandable:hover .tool-item-name,
|
|
2236
|
+
.tool-item-header.tool-item-expandable:hover .tool-item-arg { color: var(--color-text-primary); }
|
|
2237
|
+
.tool-item.expanded .tool-item-arg { white-space: normal; overflow: visible; text-overflow: clip; word-break: break-all; }
|
|
2238
|
+
.tool-item-details {
|
|
2239
|
+
margin: 0.25rem 0 2px 0;
|
|
2240
|
+
padding: 0.5rem;
|
|
2241
|
+
background: var(--color-bg-secondary);
|
|
2242
|
+
border: 1px solid var(--color-border-secondary);
|
|
2243
|
+
border-radius: 4px;
|
|
2244
|
+
font-size: 0.6875rem;
|
|
2245
|
+
font-family: monospace;
|
|
2246
|
+
color: var(--color-text-primary);
|
|
2247
|
+
white-space: pre-wrap;
|
|
2248
|
+
word-break: break-all;
|
|
2249
|
+
line-height: 1.5;
|
|
2250
|
+
max-height: 18rem;
|
|
2251
|
+
overflow-y: auto;
|
|
2252
|
+
}
|
|
2176
2253
|
.tool-item-status { margin-left: auto; font-size: 0.6875rem; flex-shrink: 0; }
|
|
2177
2254
|
.tool-item-status.ok { color: var(--color-success); }
|
|
2178
2255
|
.tool-item-status.err { color: var(--color-error); }
|
|
@@ -2203,6 +2280,30 @@ body {
|
|
|
2203
2280
|
.tool-item-stdout .ansi-black { color: #5c6370; }
|
|
2204
2281
|
.tool-item-stdout .ansi-bold { font-weight: 700; }
|
|
2205
2282
|
|
|
2283
|
+
/* ── Diff block (rendered inline within edit/write tool-item) ─────────────── */
|
|
2284
|
+
.tool-item-diff {
|
|
2285
|
+
margin: 0.25rem 0 0.25rem 1.25rem;
|
|
2286
|
+
padding: 0.375rem 0.5rem;
|
|
2287
|
+
background: var(--color-bg-secondary);
|
|
2288
|
+
border: 1px solid var(--color-border-secondary);
|
|
2289
|
+
border-radius: 4px;
|
|
2290
|
+
font-size: 0.6875rem;
|
|
2291
|
+
font-family: monospace;
|
|
2292
|
+
line-height: 1.5;
|
|
2293
|
+
max-height: 20rem;
|
|
2294
|
+
overflow: auto;
|
|
2295
|
+
}
|
|
2296
|
+
.diff-line {
|
|
2297
|
+
white-space: pre;
|
|
2298
|
+
padding: 0 0.25rem;
|
|
2299
|
+
border-radius: 2px;
|
|
2300
|
+
}
|
|
2301
|
+
.diff-line.diff-add { background: rgba(46, 160, 67, 0.18); color: #98c379; }
|
|
2302
|
+
.diff-line.diff-del { background: rgba(248, 81, 73, 0.18); color: #e06c75; }
|
|
2303
|
+
.diff-line.diff-hunk { color: var(--color-text-tertiary); font-style: italic; }
|
|
2304
|
+
.diff-line.diff-ctx { color: var(--color-text-secondary); }
|
|
2305
|
+
.diff-line.diff-more { color: var(--color-text-tertiary); font-style: italic; margin-top: 0.25rem; }
|
|
2306
|
+
|
|
2206
2307
|
/* ── Thinking block (collapsible <think>...</think> sections) ─────────────── */
|
|
2207
2308
|
.thinking-block {
|
|
2208
2309
|
margin: 0 0 0.375rem 0;
|
|
@@ -9948,6 +10049,7 @@ body.setup-mode[data-theme="dark"] {
|
|
|
9948
10049
|
background: var(--color-bg-secondary);
|
|
9949
10050
|
margin-bottom: 0.5rem;
|
|
9950
10051
|
overflow: hidden;
|
|
10052
|
+
max-width: 48rem;
|
|
9951
10053
|
}
|
|
9952
10054
|
.media-row.is-expanded {
|
|
9953
10055
|
background: var(--color-bg-secondary);
|
|
@@ -9970,25 +10072,28 @@ body.setup-mode[data-theme="dark"] {
|
|
|
9970
10072
|
background: var(--color-bg-primary);
|
|
9971
10073
|
border: 1px solid var(--color-border-primary);
|
|
9972
10074
|
border-radius: 6px;
|
|
9973
|
-
padding:
|
|
10075
|
+
padding: 2px;
|
|
9974
10076
|
}
|
|
9975
10077
|
.media-row-segmented button {
|
|
9976
10078
|
background: transparent;
|
|
9977
10079
|
border: none;
|
|
9978
|
-
padding: 0.
|
|
10080
|
+
padding: 0.3125rem 0.875rem;
|
|
9979
10081
|
font-size: 0.75rem;
|
|
10082
|
+
font-weight: 500;
|
|
9980
10083
|
color: var(--color-text-secondary);
|
|
9981
10084
|
border-radius: 5px;
|
|
9982
10085
|
cursor: pointer;
|
|
9983
|
-
transition: background-color 0.15s ease, color 0.15s ease;
|
|
10086
|
+
transition: background-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
|
|
9984
10087
|
font-family: inherit;
|
|
9985
10088
|
}
|
|
9986
10089
|
.media-row-segmented button:hover:not(.is-active):not(:disabled) {
|
|
9987
10090
|
color: var(--color-text-primary);
|
|
10091
|
+
background: var(--color-bg-secondary);
|
|
9988
10092
|
}
|
|
9989
10093
|
.media-row-segmented button.is-active {
|
|
9990
10094
|
background: var(--color-button-primary);
|
|
9991
10095
|
color: #fff;
|
|
10096
|
+
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
|
|
9992
10097
|
}
|
|
9993
10098
|
.media-row-segmented button:disabled {
|
|
9994
10099
|
opacity: 0.4;
|
|
@@ -10090,3 +10195,121 @@ body.setup-mode[data-theme="dark"] {
|
|
|
10090
10195
|
justify-content: flex-end;
|
|
10091
10196
|
margin-top: 0.25rem;
|
|
10092
10197
|
}
|
|
10198
|
+
|
|
10199
|
+
.media-auto-grid {
|
|
10200
|
+
display: grid;
|
|
10201
|
+
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
10202
|
+
gap: 0.625rem 1rem;
|
|
10203
|
+
}
|
|
10204
|
+
.media-provider-line {
|
|
10205
|
+
display: flex;
|
|
10206
|
+
align-items: baseline;
|
|
10207
|
+
gap: 0.5rem;
|
|
10208
|
+
font-size: 0.8125rem;
|
|
10209
|
+
margin-bottom: 0.625rem;
|
|
10210
|
+
}
|
|
10211
|
+
.media-provider-label {
|
|
10212
|
+
color: var(--color-text-secondary);
|
|
10213
|
+
}
|
|
10214
|
+
.media-provider-value {
|
|
10215
|
+
color: var(--color-text-primary);
|
|
10216
|
+
font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
|
10217
|
+
}
|
|
10218
|
+
.media-auto-readonly {
|
|
10219
|
+
background: var(--color-bg-primary);
|
|
10220
|
+
border: 1px solid var(--color-border-primary);
|
|
10221
|
+
border-radius: 6px;
|
|
10222
|
+
padding: 0.4375rem 0.625rem;
|
|
10223
|
+
font-size: 0.8125rem;
|
|
10224
|
+
color: var(--color-text-primary);
|
|
10225
|
+
font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
|
10226
|
+
word-break: break-all;
|
|
10227
|
+
min-height: 1.125rem;
|
|
10228
|
+
line-height: 1.4;
|
|
10229
|
+
}
|
|
10230
|
+
.media-custom-fields {
|
|
10231
|
+
display: grid;
|
|
10232
|
+
grid-template-columns: 1fr;
|
|
10233
|
+
gap: 0.625rem;
|
|
10234
|
+
}
|
|
10235
|
+
.media-custom-readonly-list {
|
|
10236
|
+
display: flex;
|
|
10237
|
+
flex-direction: column;
|
|
10238
|
+
gap: 0.375rem;
|
|
10239
|
+
}
|
|
10240
|
+
.media-custom-readonly-row {
|
|
10241
|
+
display: flex;
|
|
10242
|
+
align-items: baseline;
|
|
10243
|
+
gap: 0.5rem;
|
|
10244
|
+
font-size: 0.8125rem;
|
|
10245
|
+
line-height: 1.4;
|
|
10246
|
+
}
|
|
10247
|
+
.media-custom-readonly-label {
|
|
10248
|
+
flex: 0 0 auto;
|
|
10249
|
+
min-width: 4.5rem;
|
|
10250
|
+
color: var(--color-text-secondary);
|
|
10251
|
+
}
|
|
10252
|
+
.media-custom-readonly-value {
|
|
10253
|
+
flex: 1 1 auto;
|
|
10254
|
+
color: var(--color-text-primary);
|
|
10255
|
+
font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
|
10256
|
+
word-break: break-all;
|
|
10257
|
+
}
|
|
10258
|
+
.media-row-btn-sm {
|
|
10259
|
+
font-size: 0.75rem;
|
|
10260
|
+
padding: 0.3125rem 0.875rem;
|
|
10261
|
+
}
|
|
10262
|
+
.media-row-detail .model-test-result {
|
|
10263
|
+
margin-top: 0.5rem;
|
|
10264
|
+
min-height: 1rem;
|
|
10265
|
+
}
|
|
10266
|
+
.media-row-detail .media-row-actions {
|
|
10267
|
+
margin-top: 0.625rem;
|
|
10268
|
+
}
|
|
10269
|
+
|
|
10270
|
+
/* ── Phase grouping (folded subagent runs like skill evolution) ────────── */
|
|
10271
|
+
.msg-phase {
|
|
10272
|
+
align-self: stretch;
|
|
10273
|
+
margin: 0.25rem 0;
|
|
10274
|
+
border: 1px dashed var(--color-border-primary);
|
|
10275
|
+
border-radius: 8px;
|
|
10276
|
+
background: var(--color-bg-secondary);
|
|
10277
|
+
font-size: 0.8125rem;
|
|
10278
|
+
}
|
|
10279
|
+
.msg-phase-summary {
|
|
10280
|
+
cursor: pointer;
|
|
10281
|
+
padding: 0.375rem 0.625rem;
|
|
10282
|
+
color: var(--color-text-secondary);
|
|
10283
|
+
list-style: none;
|
|
10284
|
+
user-select: none;
|
|
10285
|
+
display: flex;
|
|
10286
|
+
align-items: center;
|
|
10287
|
+
gap: 0.375rem;
|
|
10288
|
+
}
|
|
10289
|
+
.msg-phase-summary::-webkit-details-marker { display: none; }
|
|
10290
|
+
.msg-phase-summary::before {
|
|
10291
|
+
content: "\25B8";
|
|
10292
|
+
font-size: 0.6875rem;
|
|
10293
|
+
transition: transform 0.15s;
|
|
10294
|
+
}
|
|
10295
|
+
.msg-phase[open] .msg-phase-summary::before { transform: rotate(90deg); }
|
|
10296
|
+
.msg-phase-icon { font-size: 0.875rem; }
|
|
10297
|
+
.msg-phase-label { font-weight: 500; }
|
|
10298
|
+
.msg-phase-status { opacity: 0.7; font-size: 0.75rem; }
|
|
10299
|
+
.msg-phase-status-incomplete { color: var(--color-warning, #c97f00); }
|
|
10300
|
+
.msg-phase-body {
|
|
10301
|
+
padding: 0.625rem 0.75rem 0.75rem;
|
|
10302
|
+
border-top: 1px dashed var(--color-border-primary);
|
|
10303
|
+
display: flex;
|
|
10304
|
+
flex-direction: column;
|
|
10305
|
+
gap: 0.625rem;
|
|
10306
|
+
}
|
|
10307
|
+
.msg-phase-body .msg { opacity: 0.9; }
|
|
10308
|
+
|
|
10309
|
+
.msg-phase-empty {
|
|
10310
|
+
opacity: 0.55;
|
|
10311
|
+
border-style: dotted;
|
|
10312
|
+
}
|
|
10313
|
+
.msg-phase-empty .msg-phase-summary { cursor: default; }
|
|
10314
|
+
.msg-phase-empty .msg-phase-summary::before { visibility: hidden; }
|
|
10315
|
+
.msg-phase-empty .msg-phase-body { display: none; }
|
data/lib/clacky/web/app.js
CHANGED
|
@@ -335,15 +335,15 @@ const Modal = (() => {
|
|
|
335
335
|
$("prompt-modal-ok").onclick = null;
|
|
336
336
|
$("prompt-modal-cancel").onclick = null;
|
|
337
337
|
input.onkeydown = null;
|
|
338
|
+
unbindEnter();
|
|
338
339
|
resolve(result);
|
|
339
340
|
};
|
|
340
341
|
|
|
341
342
|
$("prompt-modal-ok").onclick = () => cleanup(input.value.trim() || null);
|
|
342
343
|
$("prompt-modal-cancel").onclick = () => cleanup(null);
|
|
343
|
-
|
|
344
|
-
|
|
344
|
+
|
|
345
|
+
const unbindEnter = IME.bindEnter(input, () => cleanup(input.value.trim() || null));
|
|
345
346
|
input.onkeydown = (e) => {
|
|
346
|
-
if (e.key === "Enter") cleanup(input.value.trim() || null);
|
|
347
347
|
if (e.key === "Escape") cleanup(null);
|
|
348
348
|
};
|
|
349
349
|
});
|
|
@@ -369,6 +369,7 @@ const Modal = (() => {
|
|
|
369
369
|
$("rename-modal-overlay").onclick = null;
|
|
370
370
|
input.onkeydown = null;
|
|
371
371
|
input.oninput = null;
|
|
372
|
+
unbindEnter();
|
|
372
373
|
resolve(result);
|
|
373
374
|
};
|
|
374
375
|
|
|
@@ -386,9 +387,9 @@ const Modal = (() => {
|
|
|
386
387
|
|
|
387
388
|
$("rename-modal-save").onclick = saveHandler;
|
|
388
389
|
$("rename-modal-cancel").onclick = () => cleanup(null);
|
|
389
|
-
|
|
390
|
+
|
|
391
|
+
const unbindEnter = IME.bindEnter(input, saveHandler);
|
|
390
392
|
input.onkeydown = (e) => {
|
|
391
|
-
if (e.key === "Enter") { e.preventDefault(); saveHandler(); }
|
|
392
393
|
if (e.key === "Escape") cleanup(null);
|
|
393
394
|
};
|
|
394
395
|
|
|
Binary file
|
data/lib/clacky/web/brand.js
CHANGED
|
@@ -329,13 +329,16 @@ const Brand = (() => {
|
|
|
329
329
|
};
|
|
330
330
|
img.src = info.logo_url;
|
|
331
331
|
}
|
|
332
|
-
} else {
|
|
333
|
-
//
|
|
332
|
+
} else if (info.product_name) {
|
|
333
|
+
// Brand configured but no logo — hide the image, brand name text is enough
|
|
334
334
|
if (logoImg) {
|
|
335
335
|
logoImg.style.display = "none";
|
|
336
336
|
logoImg.src = "";
|
|
337
337
|
}
|
|
338
338
|
if (brandWrap) brandWrap.classList.remove("has-logo");
|
|
339
|
+
} else {
|
|
340
|
+
// No brand at all — show default OpenClacky logo
|
|
341
|
+
_applyDefaultLogo();
|
|
339
342
|
}
|
|
340
343
|
|
|
341
344
|
// Always show brand name text; hide it only when no brand name is set
|
|
@@ -355,6 +358,18 @@ const Brand = (() => {
|
|
|
355
358
|
});
|
|
356
359
|
}
|
|
357
360
|
|
|
361
|
+
// Apply the default OpenClacky logo based on current theme.
|
|
362
|
+
function _applyDefaultLogo() {
|
|
363
|
+
const logoImg = document.getElementById("header-logo-img");
|
|
364
|
+
const brandWrap = document.getElementById("header-brand");
|
|
365
|
+
if (!logoImg) return;
|
|
366
|
+
|
|
367
|
+
logoImg.src = "/logo_nav_dark.png";
|
|
368
|
+
logoImg.alt = "OpenClacky";
|
|
369
|
+
logoImg.style.display = "";
|
|
370
|
+
if (brandWrap) brandWrap.classList.add("has-logo");
|
|
371
|
+
}
|
|
372
|
+
|
|
358
373
|
// Replace the browser tab favicon with the given URL.
|
|
359
374
|
// Works for both image URLs and SVG data URIs.
|
|
360
375
|
function _applyFavicon(url) {
|
|
@@ -510,5 +525,10 @@ const Brand = (() => {
|
|
|
510
525
|
}
|
|
511
526
|
}
|
|
512
527
|
|
|
528
|
+
// Both themes use the same transparent A版 logo via CSS, no swap needed.
|
|
529
|
+
window.addEventListener("clacky-theme-change", e => {
|
|
530
|
+
// No-op
|
|
531
|
+
});
|
|
532
|
+
|
|
513
533
|
return { check, refresh, applyBrandName: _applyBrandName, applyHeaderLogo: _applyHeaderLogo, applyOwnerBadge: _applyOwnerBadge, applyGetSerialLink: _applyGetSerialLink, clearBrandCache: _clearBrandCache, goToLicenseInput: _goToLicenseInput, get userLicensed() { return _userLicensed; }, get branded() { return _branded; } };
|
|
514
534
|
})();
|
|
Binary file
|
data/lib/clacky/web/i18n.js
CHANGED
|
@@ -69,6 +69,9 @@ const I18n = (() => {
|
|
|
69
69
|
// ── Session list ──
|
|
70
70
|
"sessions.empty": "No sessions yet",
|
|
71
71
|
"sessions.confirmDelete": "Delete session {{name}}? This cannot be undone.",
|
|
72
|
+
"sessions.search.byName": "Title matches ({{n}})",
|
|
73
|
+
"sessions.search.byContent": "Content matches ({{n}})",
|
|
74
|
+
"sessions.search.contentEmpty": "No content matches",
|
|
72
75
|
"sessions.meta": "{{tasks}} tasks · ${{cost}}",
|
|
73
76
|
"sessions.metaTasks": "{{n}} tasks",
|
|
74
77
|
"sessions.deleteTitle": "Delete session",
|
|
@@ -99,6 +102,8 @@ const I18n = (() => {
|
|
|
99
102
|
"workspace.downloadFailed": "Download failed",
|
|
100
103
|
"sib.model.tooltip": "Click to switch model",
|
|
101
104
|
"sib.model.tooltip.busy": "Model switching is disabled while the agent is responding",
|
|
105
|
+
"sib.variant.header": "Quick switch",
|
|
106
|
+
"sib.variant.default": "default",
|
|
102
107
|
"sib.signal.tooltip": "Recent LLM latency",
|
|
103
108
|
"sib.reasoning.tooltip": "Click to change reasoning effort",
|
|
104
109
|
"sib.reasoning.label": "Reasoning",
|
|
@@ -120,6 +125,7 @@ const I18n = (() => {
|
|
|
120
125
|
"sessions.newSession": "+ New Session",
|
|
121
126
|
"sessions.actions.pin": "Pin",
|
|
122
127
|
"sessions.actions.unpin": "Unpin",
|
|
128
|
+
"sessions.actions.fork": "Fork",
|
|
123
129
|
"sessions.actions.rename": "Rename",
|
|
124
130
|
"sessions.actions.delete": "Delete",
|
|
125
131
|
"sessions.newSessionAdvanced": "More Options",
|
|
@@ -513,16 +519,19 @@ const I18n = (() => {
|
|
|
513
519
|
"settings.media.field.apiKey": "API Key",
|
|
514
520
|
"settings.media.field.provider":"Provider",
|
|
515
521
|
"settings.media.off.hint": "Disabled.",
|
|
516
|
-
"settings.media.auto.followsDefault": "
|
|
522
|
+
"settings.media.auto.followsDefault": "Auto-enabled via your default provider",
|
|
523
|
+
"settings.media.auto.stale": "Selected model {{requested}} is unavailable on the current provider — using {{current}} instead.",
|
|
517
524
|
"settings.media.auto.noDefaultModel": "Set a default chat model first.",
|
|
518
525
|
"settings.media.auto.unsupported": "Current provider has no built-in model for this kind. Switch to Custom.",
|
|
519
526
|
"settings.media.auto.comingSoon": "Not available yet — no built-in providers.",
|
|
520
|
-
"settings.media.auto.disabledTitle": "
|
|
527
|
+
"settings.media.auto.disabledTitle": "Provider has no built-in model — use Custom.",
|
|
521
528
|
"settings.media.action.edit": "Edit",
|
|
522
529
|
"settings.media.action.save": "Save",
|
|
523
530
|
"settings.media.action.cancel": "Cancel",
|
|
524
531
|
"settings.media.action.saving": "Saving…",
|
|
525
532
|
"settings.media.action.saved": "Saved",
|
|
533
|
+
"settings.media.action.test": "Test",
|
|
534
|
+
"settings.media.testing": "Testing connection…",
|
|
526
535
|
"settings.media.apiKey.placeholder": "Enter API key",
|
|
527
536
|
"settings.media.apiKey.required": "API key required",
|
|
528
537
|
"settings.media.model.required": "Model name required",
|
|
@@ -801,6 +810,9 @@ const I18n = (() => {
|
|
|
801
810
|
// ── Session list ──
|
|
802
811
|
"sessions.empty": "暂无会话",
|
|
803
812
|
"sessions.confirmDelete": "删除会话 {{name}}?此操作不可撤销。",
|
|
813
|
+
"sessions.search.byName": "标题匹配 ({{n}})",
|
|
814
|
+
"sessions.search.byContent": "内容匹配 ({{n}})",
|
|
815
|
+
"sessions.search.contentEmpty": "没有内容匹配",
|
|
804
816
|
"sessions.meta": "{{tasks}} 个任务 · ${{cost}}",
|
|
805
817
|
"sessions.metaTasks": "{{n}} 个任务",
|
|
806
818
|
"sessions.deleteTitle": "删除会话",
|
|
@@ -831,6 +843,8 @@ const I18n = (() => {
|
|
|
831
843
|
"workspace.downloadFailed": "下载失败",
|
|
832
844
|
"sib.model.tooltip": "点击切换模型",
|
|
833
845
|
"sib.model.tooltip.busy": "Agent 回复中,暂时无法切换模型",
|
|
846
|
+
"sib.variant.header": "快速切换",
|
|
847
|
+
"sib.variant.default": "默认",
|
|
834
848
|
"sib.signal.tooltip": "最近一次 LLM 响应延迟",
|
|
835
849
|
"sib.reasoning.tooltip": "点击调整思考等级",
|
|
836
850
|
"sib.reasoning.label": "思考",
|
|
@@ -854,6 +868,7 @@ const I18n = (() => {
|
|
|
854
868
|
"sessions.loadMore": "加载更多会话",
|
|
855
869
|
"sessions.actions.pin": "置顶",
|
|
856
870
|
"sessions.actions.unpin": "取消置顶",
|
|
871
|
+
"sessions.actions.fork": "复制会话",
|
|
857
872
|
"sessions.actions.rename": "重命名",
|
|
858
873
|
"sessions.actions.delete": "删除",
|
|
859
874
|
"sessions.loadingMore": "加载中…",
|
|
@@ -1244,16 +1259,19 @@ const I18n = (() => {
|
|
|
1244
1259
|
"settings.media.field.apiKey": "API Key",
|
|
1245
1260
|
"settings.media.field.provider":"服务商",
|
|
1246
1261
|
"settings.media.off.hint": "已关闭。",
|
|
1247
|
-
"settings.media.auto.followsDefault": "
|
|
1262
|
+
"settings.media.auto.followsDefault": "该服务商已支持,已自动启用",
|
|
1263
|
+
"settings.media.auto.stale": "你选定的 {{requested}} 在当前服务商不可用,已临时使用 {{current}}。",
|
|
1248
1264
|
"settings.media.auto.noDefaultModel": "请先设置默认聊天模型。",
|
|
1249
1265
|
"settings.media.auto.unsupported": "当前服务商无内置模型,请切换到「自定义」。",
|
|
1250
1266
|
"settings.media.auto.comingSoon": "暂无内置服务商,敬请期待。",
|
|
1251
|
-
"settings.media.auto.disabledTitle": "
|
|
1267
|
+
"settings.media.auto.disabledTitle": "服务商尚未配置支持,请使用自定义",
|
|
1252
1268
|
"settings.media.action.edit": "编辑",
|
|
1253
1269
|
"settings.media.action.save": "保存",
|
|
1254
1270
|
"settings.media.action.cancel": "取消",
|
|
1255
1271
|
"settings.media.action.saving": "保存中…",
|
|
1256
1272
|
"settings.media.action.saved": "已保存",
|
|
1273
|
+
"settings.media.action.test": "测试",
|
|
1274
|
+
"settings.media.testing": "测试连接中…",
|
|
1257
1275
|
"settings.media.apiKey.placeholder": "请输入 API Key",
|
|
1258
1276
|
"settings.media.apiKey.required": "请填写 API Key",
|
|
1259
1277
|
"settings.media.model.required": "请填写模型名称",
|
data/lib/clacky/web/index.html
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
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"
|
|
7
|
+
<link rel="icon" href="/favicon.ico">
|
|
8
|
+
<link rel="apple-touch-icon" href="/apple-touch-icon-180.png">
|
|
8
9
|
<link rel="stylesheet" href="/vendor/katex/katex.min.css">
|
|
9
10
|
<link rel="stylesheet" href="/vendor/hljs/hljs-theme.css">
|
|
10
11
|
<link rel="stylesheet" href="/app.css">
|
|
@@ -30,8 +31,8 @@
|
|
|
30
31
|
<path d="M9 3v18"/>
|
|
31
32
|
</svg>
|
|
32
33
|
</button>
|
|
33
|
-
<div id="header-brand" style="cursor:pointer" onclick="Router.navigate('chat')">
|
|
34
|
-
<img class="header-logo-img" id="header-logo-img" src="" alt=""
|
|
34
|
+
<div id="header-brand" class="has-logo" style="cursor:pointer" onclick="Router.navigate('chat')">
|
|
35
|
+
<img class="header-logo-img" id="header-logo-img" src="/logo_nav_dark.png" alt="OpenClacky">
|
|
35
36
|
<span class="header-logo-divider"></span>
|
|
36
37
|
<span class="header-logo" id="header-logo">{{BRAND_NAME}}</span>
|
|
37
38
|
<!-- Owner badge: visible only for creator-tier licenses (user_licensed=true).
|
|
@@ -315,7 +316,7 @@
|
|
|
315
316
|
|
|
316
317
|
<!-- Chat column (messages + info bar + input) -->
|
|
317
318
|
<div id="chat-main">
|
|
318
|
-
<div id="messages"></div>
|
|
319
|
+
<div id="messages" class="chat-messages-scroll"></div>
|
|
319
320
|
<!-- New message notification banner -->
|
|
320
321
|
<div id="new-message-banner" class="new-message-banner" style="display:none">
|
|
321
322
|
<span data-i18n="chat.newMessageHint">New messages ↓</span>
|
|
@@ -1295,6 +1296,7 @@
|
|
|
1295
1296
|
<script src="/vendor/hljs/highlight.min.js"></script>
|
|
1296
1297
|
<script src="/vendor/katex/katex.min.js"></script>
|
|
1297
1298
|
<script src="/vendor/katex/auto-render.min.js"></script>
|
|
1299
|
+
<script src="/utils.js"></script>
|
|
1298
1300
|
<script src="/i18n.js"></script>
|
|
1299
1301
|
<script src="/auth.js"></script>
|
|
1300
1302
|
<script src="/theme.js"></script>
|
|
Binary file
|
|
@@ -23,7 +23,14 @@ window.ModelTester = (function () {
|
|
|
23
23
|
return { ok: false, message: e.message };
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
if (!data.ok)
|
|
26
|
+
if (!data.ok) {
|
|
27
|
+
const msg = data.message || "";
|
|
28
|
+
const code = data.error_code || "";
|
|
29
|
+
if (code === "insufficient_credit") {
|
|
30
|
+
return { ok: false, message: I18n.t("error.insufficient_credit"), error_code: code };
|
|
31
|
+
}
|
|
32
|
+
return { ok: false, message: msg, error_code: code };
|
|
33
|
+
}
|
|
27
34
|
|
|
28
35
|
if (data.effective_base_url && data.effective_base_url !== base_url) {
|
|
29
36
|
return { ok: true, base_url: data.effective_base_url, message: data.message || "", rewrote: true };
|