openclacky 1.3.11 → 1.4.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 +30 -0
- data/lib/clacky/agent/skill_manager.rb +1 -0
- data/lib/clacky/default_extensions/ext-studio/api/handler.rb +58 -2
- data/lib/clacky/default_extensions/ext-studio/panels/studio/view.js +355 -44
- data/lib/clacky/default_skills/media-gen/SKILL.md +182 -7
- data/lib/clacky/default_skills/media-gen/scripts/video_seq.sh +5 -3
- data/lib/clacky/extension/packager.rb +11 -0
- data/lib/clacky/extension/verifier.rb +1 -1
- data/lib/clacky/media/generator.rb +41 -0
- data/lib/clacky/media/volcengine.rb +394 -0
- data/lib/clacky/server/http_server.rb +48 -4
- data/lib/clacky/tools/terminal.rb +24 -8
- data/lib/clacky/ui2/layout_manager.rb +4 -0
- data/lib/clacky/ui2/screen_buffer.rb +31 -3
- data/lib/clacky/utils/file_processor.rb +13 -8
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.css +110 -16
- data/lib/clacky/web/features/extensions/view.js +42 -7
- data/lib/clacky/web/features/skills/view.js +61 -1
- data/lib/clacky/web/i18n.js +24 -0
- data/lib/clacky/web/index.html +6 -11
- data/lib/clacky/web/sessions.js +12 -3
- metadata +2 -1
data/lib/clacky/version.rb
CHANGED
data/lib/clacky/web/app.css
CHANGED
|
@@ -782,7 +782,7 @@ body {
|
|
|
782
782
|
color: var(--color-accent-primary);
|
|
783
783
|
}
|
|
784
784
|
|
|
785
|
-
/* ── Command-palette search overlay
|
|
785
|
+
/* ── Command-palette search overlay ──────────────────────────────────────── */
|
|
786
786
|
.cmd-palette-overlay {
|
|
787
787
|
position: fixed;
|
|
788
788
|
inset: 0;
|
|
@@ -6484,6 +6484,28 @@ body {
|
|
|
6484
6484
|
overflow-y: auto;
|
|
6485
6485
|
}
|
|
6486
6486
|
|
|
6487
|
+
/* ── Skills search ──────────────────────────────────────────────────────── */
|
|
6488
|
+
.skills-search {
|
|
6489
|
+
font-size: 0.8125rem;
|
|
6490
|
+
padding: 0.375rem 0.75rem;
|
|
6491
|
+
border-radius: 6px;
|
|
6492
|
+
border: 1px solid var(--color-border-primary);
|
|
6493
|
+
background: var(--color-bg-secondary);
|
|
6494
|
+
color: var(--color-text-primary);
|
|
6495
|
+
width: 12rem;
|
|
6496
|
+
}
|
|
6497
|
+
.skills-search::placeholder { color: var(--color-text-tertiary); }
|
|
6498
|
+
.skills-search:focus {
|
|
6499
|
+
outline: none;
|
|
6500
|
+
border-color: var(--color-accent, #6366f1);
|
|
6501
|
+
}
|
|
6502
|
+
.skills-search-empty {
|
|
6503
|
+
padding: 2rem 1rem;
|
|
6504
|
+
text-align: center;
|
|
6505
|
+
color: var(--color-text-tertiary);
|
|
6506
|
+
font-size: 0.875rem;
|
|
6507
|
+
}
|
|
6508
|
+
|
|
6487
6509
|
/* ── My Skills upload area (user-licensed only) ───────────────────────────── */
|
|
6488
6510
|
.my-skills-upload-area {
|
|
6489
6511
|
display: flex;
|
|
@@ -7171,26 +7193,38 @@ body {
|
|
|
7171
7193
|
background: var(--color-bg-secondary);
|
|
7172
7194
|
border: 1px solid var(--color-border-primary);
|
|
7173
7195
|
border-radius: 8px;
|
|
7174
|
-
padding: 0.
|
|
7196
|
+
padding: 0.6rem 1rem;
|
|
7175
7197
|
transition: border-color .15s;
|
|
7176
7198
|
}
|
|
7177
7199
|
.extension-card:hover { border-color: var(--color-text-muted); }
|
|
7178
7200
|
.extension-card-main {
|
|
7179
7201
|
display: flex;
|
|
7180
|
-
align-items:
|
|
7202
|
+
align-items: stretch;
|
|
7181
7203
|
gap: 0.75rem;
|
|
7182
7204
|
}
|
|
7183
7205
|
.extension-emoji {
|
|
7184
7206
|
font-size: 1.5rem;
|
|
7185
7207
|
line-height: 1.4;
|
|
7186
7208
|
flex-shrink: 0;
|
|
7209
|
+
display: flex;
|
|
7210
|
+
align-items: flex-start;
|
|
7211
|
+
}
|
|
7212
|
+
.extension-default-icon {
|
|
7213
|
+
width: 2rem;
|
|
7214
|
+
height: 100%;
|
|
7215
|
+
display: block;
|
|
7216
|
+
border-radius: 7px;
|
|
7217
|
+
}
|
|
7218
|
+
.extension-emoji-lg .extension-default-icon {
|
|
7219
|
+
width: 2.75rem;
|
|
7220
|
+
height: 2.75rem;
|
|
7221
|
+
border-radius: 10px;
|
|
7187
7222
|
}
|
|
7188
|
-
.extension-info { flex: 1; min-width: 0; }
|
|
7223
|
+
.extension-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
|
|
7189
7224
|
.extension-title {
|
|
7190
7225
|
display: flex;
|
|
7191
7226
|
align-items: center;
|
|
7192
7227
|
gap: 0.5rem;
|
|
7193
|
-
margin-bottom: 0.25rem;
|
|
7194
7228
|
flex-wrap: wrap;
|
|
7195
7229
|
}
|
|
7196
7230
|
.extension-name {
|
|
@@ -7203,7 +7237,6 @@ body {
|
|
|
7203
7237
|
color: var(--color-text-secondary);
|
|
7204
7238
|
background: var(--color-bg-tertiary, rgba(255,255,255,0.05));
|
|
7205
7239
|
border-radius: 4px;
|
|
7206
|
-
padding: 0.0625rem 0.375rem;
|
|
7207
7240
|
}
|
|
7208
7241
|
.extension-units {
|
|
7209
7242
|
font-size: 0.6875rem;
|
|
@@ -7234,11 +7267,10 @@ body {
|
|
|
7234
7267
|
font-size: 0.75rem;
|
|
7235
7268
|
color: var(--color-text-secondary);
|
|
7236
7269
|
line-height: 1.5;
|
|
7237
|
-
margin-bottom: 0.375rem;
|
|
7238
7270
|
}
|
|
7239
7271
|
.extension-desc-detail {
|
|
7240
7272
|
font-size: 0.8125rem;
|
|
7241
|
-
margin-bottom: 0
|
|
7273
|
+
margin-bottom: 0;
|
|
7242
7274
|
}
|
|
7243
7275
|
.extension-detail-author {
|
|
7244
7276
|
font-size: 0.6875rem;
|
|
@@ -7300,8 +7332,13 @@ body {
|
|
|
7300
7332
|
padding-bottom: 1.25rem;
|
|
7301
7333
|
border-bottom: 1px solid var(--color-border-primary);
|
|
7302
7334
|
}
|
|
7303
|
-
.extension-detail-heading { flex: 1; min-width: 0; }
|
|
7335
|
+
.extension-detail-heading { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
|
|
7304
7336
|
.extension-emoji-lg { font-size: 2.5rem; line-height: 1; margin-top: 0.125rem; }
|
|
7337
|
+
.extension-emoji-lg .extension-default-icon {
|
|
7338
|
+
width: 3.5rem;
|
|
7339
|
+
height: 3.5rem;
|
|
7340
|
+
border-radius: 12px;
|
|
7341
|
+
}
|
|
7305
7342
|
.extension-name-lg { font-size: 1.125rem; font-weight: 700; }
|
|
7306
7343
|
.extension-detail-block { margin-top: 1.5rem; }
|
|
7307
7344
|
.extension-detail-block-title {
|
|
@@ -7309,7 +7346,6 @@ body {
|
|
|
7309
7346
|
font-weight: 600;
|
|
7310
7347
|
text-transform: uppercase;
|
|
7311
7348
|
letter-spacing: 0.04em;
|
|
7312
|
-
color: var(--color-text-muted);
|
|
7313
7349
|
margin: 0 0 0.75rem;
|
|
7314
7350
|
}
|
|
7315
7351
|
.extension-detail-section { margin-bottom: 0.875rem; }
|
|
@@ -7371,13 +7407,17 @@ body {
|
|
|
7371
7407
|
border-bottom: 1px solid var(--color-border-primary);
|
|
7372
7408
|
}
|
|
7373
7409
|
.extension-version-row .extension-version {
|
|
7374
|
-
font-size: 0.
|
|
7410
|
+
font-size: 0.8375rem;
|
|
7375
7411
|
font-weight: 700;
|
|
7376
7412
|
color: var(--color-text-primary);
|
|
7377
7413
|
background: none;
|
|
7378
7414
|
padding: 0;
|
|
7379
7415
|
border-radius: 0;
|
|
7380
7416
|
}
|
|
7417
|
+
.extension-version-separator {
|
|
7418
|
+
font-size: 0.8125rem;
|
|
7419
|
+
color: var(--color-text-secondary);
|
|
7420
|
+
}
|
|
7381
7421
|
.extension-version-date {
|
|
7382
7422
|
font-size: 0.6875rem;
|
|
7383
7423
|
color: var(--color-text-muted);
|
|
@@ -7423,7 +7463,7 @@ body {
|
|
|
7423
7463
|
display: flex;
|
|
7424
7464
|
align-items: center;
|
|
7425
7465
|
gap: 0.5rem;
|
|
7426
|
-
margin-top: 0.
|
|
7466
|
+
margin-top: 0.25rem;
|
|
7427
7467
|
}
|
|
7428
7468
|
.extension-disabled {
|
|
7429
7469
|
font-size: 0.6875rem;
|
|
@@ -7631,7 +7671,7 @@ body {
|
|
|
7631
7671
|
color: #ffffff;
|
|
7632
7672
|
border: 1px solid var(--color-accent-primary);
|
|
7633
7673
|
}
|
|
7634
|
-
.btn-brand-update:hover { background: var(--color-
|
|
7674
|
+
.btn-brand-update:hover { background: var(--color-button-primary-hover); }
|
|
7635
7675
|
.btn-brand-install:disabled,
|
|
7636
7676
|
.btn-brand-update:disabled {
|
|
7637
7677
|
opacity: 0.5;
|
|
@@ -10055,7 +10095,7 @@ body.setup-mode[data-theme="dark"] {
|
|
|
10055
10095
|
|
|
10056
10096
|
.memory-card-actions {
|
|
10057
10097
|
display: flex;
|
|
10058
|
-
align-items:
|
|
10098
|
+
align-items: center;
|
|
10059
10099
|
gap: 0.375rem;
|
|
10060
10100
|
flex-shrink: 0;
|
|
10061
10101
|
}
|
|
@@ -10095,6 +10135,7 @@ body.setup-mode[data-theme="dark"] {
|
|
|
10095
10135
|
|
|
10096
10136
|
.btn-memory-expand {
|
|
10097
10137
|
min-width: 1.75rem;
|
|
10138
|
+
align-self: stretch;
|
|
10098
10139
|
justify-content: center;
|
|
10099
10140
|
color: var(--color-text-muted);
|
|
10100
10141
|
}
|
|
@@ -10608,7 +10649,7 @@ body.setup-mode[data-theme="dark"] {
|
|
|
10608
10649
|
|
|
10609
10650
|
/* ── Model Filter ────────────────────────────────────────────────────── */
|
|
10610
10651
|
.billing-model-filter {
|
|
10611
|
-
padding: 0.4375rem 0.75rem;
|
|
10652
|
+
padding: 0.4375rem 1.75rem 0.4375rem 0.75rem;
|
|
10612
10653
|
border: 1px solid var(--color-border-primary);
|
|
10613
10654
|
border-radius: 8px;
|
|
10614
10655
|
background: var(--color-bg-secondary);
|
|
@@ -10616,6 +10657,23 @@ body.setup-mode[data-theme="dark"] {
|
|
|
10616
10657
|
font-size: 0.8125rem;
|
|
10617
10658
|
cursor: pointer;
|
|
10618
10659
|
min-width: 130px;
|
|
10660
|
+
appearance: none;
|
|
10661
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23888' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
|
10662
|
+
background-repeat: no-repeat;
|
|
10663
|
+
background-position: right 0.625rem center;
|
|
10664
|
+
transition: border-color 0.15s ease;
|
|
10665
|
+
}
|
|
10666
|
+
.billing-model-filter:hover {
|
|
10667
|
+
border-color: var(--color-border-strong);
|
|
10668
|
+
}
|
|
10669
|
+
.billing-model-filter:focus {
|
|
10670
|
+
outline: none;
|
|
10671
|
+
border-color: var(--color-accent-primary);
|
|
10672
|
+
box-shadow: 0 0 0 3px var(--color-accent-soft);
|
|
10673
|
+
}
|
|
10674
|
+
.billing-model-filter option {
|
|
10675
|
+
background: var(--color-bg-secondary);
|
|
10676
|
+
color: var(--color-text-primary);
|
|
10619
10677
|
}
|
|
10620
10678
|
|
|
10621
10679
|
/* ── Action Buttons ──────────────────────────────────────────────────── */
|
|
@@ -12398,11 +12456,48 @@ body.setup-mode[data-theme="dark"] {
|
|
|
12398
12456
|
display: none;
|
|
12399
12457
|
}
|
|
12400
12458
|
|
|
12459
|
+
/* Collapse session search into the right-side header actions */
|
|
12460
|
+
#header-center {
|
|
12461
|
+
justify-content: flex-end;
|
|
12462
|
+
padding: 0;
|
|
12463
|
+
}
|
|
12464
|
+
#header-cmdbar {
|
|
12465
|
+
flex: 0 0 1.875rem;
|
|
12466
|
+
width: 1.875rem;
|
|
12467
|
+
max-width: 1.875rem;
|
|
12468
|
+
padding: 0;
|
|
12469
|
+
border: none;
|
|
12470
|
+
border-radius: var(--radius-sm);
|
|
12471
|
+
background: transparent;
|
|
12472
|
+
justify-content: center;
|
|
12473
|
+
gap: 0;
|
|
12474
|
+
margin-right: 0.125rem;
|
|
12475
|
+
}
|
|
12476
|
+
#header-cmdbar:hover {
|
|
12477
|
+
color: var(--color-text-primary);
|
|
12478
|
+
}
|
|
12479
|
+
#header-cmdbar svg {
|
|
12480
|
+
width: 1rem;
|
|
12481
|
+
height: 1rem;
|
|
12482
|
+
opacity: 1;
|
|
12483
|
+
}
|
|
12484
|
+
#header-cmdbar .cmdbar-ph,
|
|
12485
|
+
#header-cmdbar .cmdbar-kbd,
|
|
12486
|
+
#ext-slot-header-right:empty {
|
|
12487
|
+
display: none;
|
|
12488
|
+
}
|
|
12489
|
+
|
|
12401
12490
|
/* Tighten right-side icon gap on mobile */
|
|
12402
12491
|
#header-right {
|
|
12403
12492
|
gap: 0.125rem;
|
|
12404
12493
|
}
|
|
12405
12494
|
|
|
12495
|
+
@media (max-width: 360px) {
|
|
12496
|
+
#top-header {
|
|
12497
|
+
padding: 0 0.375rem;
|
|
12498
|
+
}
|
|
12499
|
+
}
|
|
12500
|
+
|
|
12406
12501
|
/* Session info bar: single-line, no hover-expand, font smaller */
|
|
12407
12502
|
#session-info-bar {
|
|
12408
12503
|
padding: 0.1875rem 0.5rem;
|
|
@@ -12819,4 +12914,3 @@ body.setup-mode[data-theme="dark"] {
|
|
|
12819
12914
|
background: var(--color-bg-primary);
|
|
12820
12915
|
}
|
|
12821
12916
|
}
|
|
12822
|
-
|
|
@@ -77,13 +77,27 @@ const ExtensionsView = (() => {
|
|
|
77
77
|
return parts.join(" · ");
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
function _defaultIcon(name, extraClass) {
|
|
81
|
+
const letter = (name || "?")[0].toUpperCase();
|
|
82
|
+
const colors = [
|
|
83
|
+
["#6366f1","#818cf8"], ["#8b5cf6","#a78bfa"], ["#ec4899","#f472b6"],
|
|
84
|
+
["#f59e0b","#fbbf24"], ["#10b981","#34d399"], ["#3b82f6","#60a5fa"],
|
|
85
|
+
["#ef4444","#f87171"], ["#14b8a6","#2dd4bf"],
|
|
86
|
+
];
|
|
87
|
+
const idx = (name || "").charCodeAt(0) % colors.length;
|
|
88
|
+
const [c1, c2] = colors[idx];
|
|
89
|
+
const gid = `eg-${idx}-${Math.random().toString(36).slice(2,7)}`;
|
|
90
|
+
const cls = extraClass ? `extension-emoji ${extraClass}` : "extension-emoji";
|
|
91
|
+
return `<span class="${cls}"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" class="extension-default-icon"><defs><linearGradient id="${gid}" x1="0" y1="0" x2="1" y2="1"><stop offset="0%" stop-color="${c1}"/><stop offset="100%" stop-color="${c2}"/></linearGradient></defs><rect width="32" height="32" rx="8" fill="url(#${gid})"/><text x="16" y="22" text-anchor="middle" font-family="system-ui,sans-serif" font-size="16" font-weight="700" fill="white">${letter}</text></svg></span>`;
|
|
92
|
+
}
|
|
93
|
+
|
|
80
94
|
function _renderCard(ext) {
|
|
81
95
|
const currentLang = I18n.lang();
|
|
82
|
-
const name = (currentLang === "zh" && ext.
|
|
96
|
+
const name = (currentLang === "zh" && ext.display_name_zh) ? ext.display_name_zh : (ext.display_name || ext.name);
|
|
83
97
|
const description = (currentLang === "zh" && ext.description_zh)
|
|
84
98
|
? ext.description_zh
|
|
85
99
|
: ext.description || "";
|
|
86
|
-
const
|
|
100
|
+
const emojiHtml = ext.emoji ? `<span class="extension-emoji">${escapeHtml(ext.emoji)}</span>` : _defaultIcon(ext.name);
|
|
87
101
|
|
|
88
102
|
const versionHtml = ext.version
|
|
89
103
|
? `<span class="extension-version">v${escapeHtml(String(ext.version))}</span>` : "";
|
|
@@ -110,7 +124,7 @@ const ExtensionsView = (() => {
|
|
|
110
124
|
card.dataset.extId = ext.id != null ? String(ext.id) : (ext.name || "");
|
|
111
125
|
card.innerHTML = `
|
|
112
126
|
<div class="extension-card-main">
|
|
113
|
-
|
|
127
|
+
${emojiHtml}
|
|
114
128
|
<div class="extension-info">
|
|
115
129
|
<div class="extension-title">
|
|
116
130
|
<span class="extension-name">${escapeHtml(name)}</span>
|
|
@@ -229,11 +243,14 @@ const ExtensionsView = (() => {
|
|
|
229
243
|
|
|
230
244
|
function _detailContent(ext) {
|
|
231
245
|
const currentLang = I18n.lang();
|
|
232
|
-
const name = (currentLang === "zh" && ext.
|
|
246
|
+
const name = (currentLang === "zh" && ext.display_name_zh) ? ext.display_name_zh : (ext.display_name || ext.name);
|
|
233
247
|
const description = (currentLang === "zh" && ext.description_zh)
|
|
234
248
|
? ext.description_zh
|
|
235
249
|
: ext.description || "";
|
|
236
250
|
const emoji = ext.emoji || "🧩";
|
|
251
|
+
const detailEmojiHtml = ext.emoji
|
|
252
|
+
? `<span class="extension-emoji extension-emoji-lg">${escapeHtml(ext.emoji)}</span>`
|
|
253
|
+
: _defaultIcon(ext.name, "extension-emoji-lg");
|
|
237
254
|
|
|
238
255
|
const canUpdate = ext.installed && ext.installed_version && ext.version && ext.installed_version !== ext.version;
|
|
239
256
|
const versionHtml = ext.version
|
|
@@ -258,7 +275,7 @@ const ExtensionsView = (() => {
|
|
|
258
275
|
|
|
259
276
|
return `
|
|
260
277
|
<div class="extension-detail-hero">
|
|
261
|
-
|
|
278
|
+
${detailEmojiHtml}
|
|
262
279
|
<div class="extension-detail-heading">
|
|
263
280
|
<div class="extension-title">
|
|
264
281
|
<span class="extension-name extension-name-lg">${escapeHtml(name)}</span>
|
|
@@ -274,7 +291,7 @@ const ExtensionsView = (() => {
|
|
|
274
291
|
${_renderActions(ext)}
|
|
275
292
|
</div>
|
|
276
293
|
</div>
|
|
277
|
-
${
|
|
294
|
+
${_renderEntryPoints(ext.contributes)}
|
|
278
295
|
${_renderVersions(ext.versions)}`;
|
|
279
296
|
}
|
|
280
297
|
|
|
@@ -357,6 +374,24 @@ const ExtensionsView = (() => {
|
|
|
357
374
|
</div>`;
|
|
358
375
|
}
|
|
359
376
|
|
|
377
|
+
function _renderEntryPoints(contributes) {
|
|
378
|
+
if (!contributes || !Array.isArray(contributes.panels)) return "";
|
|
379
|
+
const eps = contributes.panels.flatMap((p) => Array.isArray(p.entry_points) ? p.entry_points : []);
|
|
380
|
+
if (eps.length === 0) return "";
|
|
381
|
+
const rows = eps.map((ep) => {
|
|
382
|
+
const slotKey = "extensions.slot." + ep.slot;
|
|
383
|
+
const label = I18n.t(slotKey) !== slotKey ? I18n.t(slotKey) : ep.slot;
|
|
384
|
+
return `<li class="extension-contrib-item"><span class="extension-contrib-title">${escapeHtml(label)}</span></li>`;
|
|
385
|
+
}).join("");
|
|
386
|
+
return `
|
|
387
|
+
<div class="extension-detail-block">
|
|
388
|
+
<h3 class="extension-detail-block-title">${escapeHtml(I18n.t("extensions.detail.entry_points"))}</h3>
|
|
389
|
+
<div class="extension-detail-section">
|
|
390
|
+
<ul class="extension-contrib-list">${rows}</ul>
|
|
391
|
+
</div>
|
|
392
|
+
</div>`;
|
|
393
|
+
}
|
|
394
|
+
|
|
360
395
|
function _renderVersions(versions) {
|
|
361
396
|
if (!Array.isArray(versions) || versions.length === 0) return "";
|
|
362
397
|
const rows = versions.map((v) => {
|
|
@@ -365,7 +400,7 @@ const ExtensionsView = (() => {
|
|
|
365
400
|
<li class="extension-version-item">
|
|
366
401
|
<div class="extension-version-row">
|
|
367
402
|
<span class="extension-version">v${escapeHtml(String(v.version || ""))}</span>
|
|
368
|
-
${date ? `<span class="extension-version-date">${escapeHtml(date)}</span>` : ""}
|
|
403
|
+
${date ? `<span class="extension-version-separator">-</span><span class="extension-version-date">${escapeHtml(date)}</span>` : ""}
|
|
369
404
|
</div>
|
|
370
405
|
${v.release_notes ? `<div class="extension-version-notes">${typeof marked !== "undefined" ? marked.parse(String(v.release_notes).replace(/^#{1,3}[^\n]*\n?/, ""), { breaks: true, gfm: true }) : escapeHtml(String(v.release_notes))}</div>` : ""}
|
|
371
406
|
</li>`;
|
|
@@ -16,6 +16,62 @@
|
|
|
16
16
|
|
|
17
17
|
const SkillsView = (() => {
|
|
18
18
|
let _domWired = false;
|
|
19
|
+
let _searchQuery = "";
|
|
20
|
+
|
|
21
|
+
// ── Search filtering ─────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
function _applySearch() {
|
|
24
|
+
const q = _searchQuery.trim().toLowerCase();
|
|
25
|
+
const activeTab = SkillsStore.state.activeTab;
|
|
26
|
+
|
|
27
|
+
if (activeTab === "my-skills") {
|
|
28
|
+
const list = $("skills-list");
|
|
29
|
+
if (!list) return;
|
|
30
|
+
list.querySelectorAll(".skill-card").forEach(card => {
|
|
31
|
+
card.style.display = (!q || card.textContent.toLowerCase().includes(q)) ? "" : "none";
|
|
32
|
+
});
|
|
33
|
+
let noResult = list.querySelector(".skills-search-empty");
|
|
34
|
+
const anyVisible = [...list.querySelectorAll(".skill-card")].some(c => c.style.display !== "none");
|
|
35
|
+
if (q && !anyVisible) {
|
|
36
|
+
if (!noResult) {
|
|
37
|
+
noResult = document.createElement("div");
|
|
38
|
+
noResult.className = "skills-search-empty";
|
|
39
|
+
noResult.textContent = I18n.t("skills.search.empty");
|
|
40
|
+
list.appendChild(noResult);
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
noResult && noResult.remove();
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
const list = $("brand-skills-list");
|
|
47
|
+
if (!list) return;
|
|
48
|
+
list.querySelectorAll(".brand-skill-card").forEach(card => {
|
|
49
|
+
card.style.display = (!q || card.textContent.toLowerCase().includes(q)) ? "" : "none";
|
|
50
|
+
});
|
|
51
|
+
let noResult = list.querySelector(".skills-search-empty");
|
|
52
|
+
const anyVisible = [...list.querySelectorAll(".brand-skill-card")].some(c => c.style.display !== "none");
|
|
53
|
+
if (q && !anyVisible) {
|
|
54
|
+
if (!noResult) {
|
|
55
|
+
noResult = document.createElement("div");
|
|
56
|
+
noResult.className = "skills-search-empty";
|
|
57
|
+
noResult.textContent = I18n.t("skills.search.empty");
|
|
58
|
+
list.appendChild(noResult);
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
noResult && noResult.remove();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function _wireSearch() {
|
|
67
|
+
const input = $("skills-search-input");
|
|
68
|
+
if (!input) return;
|
|
69
|
+
input.placeholder = I18n.t("skills.search.placeholder");
|
|
70
|
+
input.addEventListener("input", () => {
|
|
71
|
+
_searchQuery = input.value;
|
|
72
|
+
_applySearch();
|
|
73
|
+
});
|
|
74
|
+
}
|
|
19
75
|
|
|
20
76
|
// ── My Skills rendering ──────────────────────────────────────────────────
|
|
21
77
|
|
|
@@ -45,6 +101,7 @@ const SkillsView = (() => {
|
|
|
45
101
|
console.error("[Skills] _renderSkillCard failed for skill", i, skill.name, e);
|
|
46
102
|
}
|
|
47
103
|
});
|
|
104
|
+
_applySearch();
|
|
48
105
|
}
|
|
49
106
|
|
|
50
107
|
function _renderEmptyState() {
|
|
@@ -247,6 +304,7 @@ const SkillsView = (() => {
|
|
|
247
304
|
if (freeMode && paidSkillsCount > 0) {
|
|
248
305
|
container.appendChild(_renderPaidHint(paidSkillsCount));
|
|
249
306
|
}
|
|
307
|
+
_applySearch();
|
|
250
308
|
}
|
|
251
309
|
|
|
252
310
|
function _renderPaidHint(paidSkillsCount) {
|
|
@@ -383,6 +441,8 @@ const SkillsView = (() => {
|
|
|
383
441
|
function _wireDom() {
|
|
384
442
|
if (_domWired) return;
|
|
385
443
|
|
|
444
|
+
_wireSearch();
|
|
445
|
+
|
|
386
446
|
document.querySelectorAll(".skills-tab").forEach(btn => {
|
|
387
447
|
btn.addEventListener("click", () => Skills.setActiveTab(btn.dataset.tab));
|
|
388
448
|
});
|
|
@@ -430,7 +490,7 @@ const SkillsView = (() => {
|
|
|
430
490
|
_renderBrandSkills();
|
|
431
491
|
});
|
|
432
492
|
|
|
433
|
-
Skills.on("tab:changed", (p) => _applyTab(p.tab));
|
|
493
|
+
Skills.on("tab:changed", (p) => { _applyTab(p.tab); _applySearch(); });
|
|
434
494
|
|
|
435
495
|
Skills.on("brandStatus:changed", (p) => {
|
|
436
496
|
const brandTab = $("tab-brand-skills");
|
data/lib/clacky/web/i18n.js
CHANGED
|
@@ -95,6 +95,8 @@ const I18n = (() => {
|
|
|
95
95
|
"sessions.deleteTitle": "Delete session",
|
|
96
96
|
"sessions.createError": "Error: ",
|
|
97
97
|
"sessions.dirNotEmpty": "Directory already exists and is not empty.",
|
|
98
|
+
"sessions.today": "Today",
|
|
99
|
+
"sessions.yesterday": "Yesterday",
|
|
98
100
|
"sessions.export.tooltip": "Download session files (session.json + chunks + today's log) for debugging",
|
|
99
101
|
"sessions.export.failed": "Failed to download session",
|
|
100
102
|
"sessions.actions.tooltip": "Click for session actions",
|
|
@@ -414,6 +416,8 @@ const I18n = (() => {
|
|
|
414
416
|
"skills.import.install": "Install",
|
|
415
417
|
"skills.tab.my": "My Skills",
|
|
416
418
|
"skills.tab.brand": "Brand Skills",
|
|
419
|
+
"skills.search.placeholder": "Search skills…",
|
|
420
|
+
"skills.search.empty": "No skills match your search.",
|
|
417
421
|
"skills.empty": "No skills loaded.",
|
|
418
422
|
"skills.empty.createBtn": "Create a new skill with /skill-creator",
|
|
419
423
|
"skills.noSkills": "No skills",
|
|
@@ -534,7 +538,15 @@ const I18n = (() => {
|
|
|
534
538
|
"extensions.unit.apis": "APIs",
|
|
535
539
|
"extensions.detail.back": "Back",
|
|
536
540
|
"extensions.detail.contributes": "What's inside",
|
|
541
|
+
"extensions.detail.entry_points": "Access points",
|
|
537
542
|
"extensions.detail.versions": "Version history",
|
|
543
|
+
"extensions.slot.sidebar.nav": "Sidebar nav",
|
|
544
|
+
"extensions.slot.sidebar.nav.top": "Sidebar nav (top)",
|
|
545
|
+
"extensions.slot.sidebar.nav.bottom": "Sidebar nav (bottom)",
|
|
546
|
+
"extensions.slot.sidebar.footer": "Sidebar footer",
|
|
547
|
+
"extensions.slot.main.workspace": "Main workspace",
|
|
548
|
+
"extensions.slot.session.aside": "Session aside",
|
|
549
|
+
"extensions.slot.settings.tabs": "Settings tab",
|
|
538
550
|
"extensions.section.agents": "Agents",
|
|
539
551
|
"extensions.section.skills": "Skills",
|
|
540
552
|
"extensions.section.panels": "Panels",
|
|
@@ -1063,6 +1075,8 @@ const I18n = (() => {
|
|
|
1063
1075
|
"sessions.deleteTitle": "删除会话",
|
|
1064
1076
|
"sessions.createError": "错误:",
|
|
1065
1077
|
"sessions.dirNotEmpty": "该目录已存在且不为空,请换一个目录名。",
|
|
1078
|
+
"sessions.today": "今天",
|
|
1079
|
+
"sessions.yesterday": "昨天",
|
|
1066
1080
|
"sessions.export.tooltip": "下载会话文件(session.json + 归档片段 + 当天日志),用于调试",
|
|
1067
1081
|
"sessions.export.failed": "下载会话文件失败",
|
|
1068
1082
|
"sessions.actions.tooltip": "点击查看会话操作",
|
|
@@ -1381,6 +1395,8 @@ const I18n = (() => {
|
|
|
1381
1395
|
"skills.import.install": "安装",
|
|
1382
1396
|
"skills.tab.my": "我的技能",
|
|
1383
1397
|
"skills.tab.brand": "品牌技能",
|
|
1398
|
+
"skills.search.placeholder": "搜索技能…",
|
|
1399
|
+
"skills.search.empty": "没有匹配的技能。",
|
|
1384
1400
|
"skills.empty": "暂无技能。",
|
|
1385
1401
|
"skills.empty.createBtn": "用 /skill-creator 创建新技能",
|
|
1386
1402
|
"skills.noSkills": "无技能",
|
|
@@ -1501,7 +1517,15 @@ const I18n = (() => {
|
|
|
1501
1517
|
"extensions.unit.apis": "个 API",
|
|
1502
1518
|
"extensions.detail.back": "返回",
|
|
1503
1519
|
"extensions.detail.contributes": "包含内容",
|
|
1520
|
+
"extensions.detail.entry_points": "访问入口",
|
|
1504
1521
|
"extensions.detail.versions": "版本历史",
|
|
1522
|
+
"extensions.slot.sidebar.nav": "侧边栏导航",
|
|
1523
|
+
"extensions.slot.sidebar.nav.top": "侧边栏导航(顶部)",
|
|
1524
|
+
"extensions.slot.sidebar.nav.bottom": "侧边栏导航(底部)",
|
|
1525
|
+
"extensions.slot.sidebar.footer": "侧边栏底部",
|
|
1526
|
+
"extensions.slot.main.workspace": "主工作区",
|
|
1527
|
+
"extensions.slot.session.aside": "会话侧栏",
|
|
1528
|
+
"extensions.slot.settings.tabs": "设置页标签",
|
|
1505
1529
|
"extensions.section.agents": "Agent",
|
|
1506
1530
|
"extensions.section.skills": "技能",
|
|
1507
1531
|
"extensions.section.panels": "面板",
|
data/lib/clacky/web/index.html
CHANGED
|
@@ -46,21 +46,14 @@
|
|
|
46
46
|
</div>
|
|
47
47
|
<div id="header-center">
|
|
48
48
|
<button id="header-cmdbar" type="button" title="Search sessions">
|
|
49
|
-
<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="
|
|
49
|
+
<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="8"/><path d="M21 21l-4.3-4.3"/></svg>
|
|
50
50
|
<span class="cmdbar-ph" data-i18n="header.cmdbar.placeholder">Search sessions…</span>
|
|
51
|
-
<span class="cmdbar-kbd"
|
|
51
|
+
<span class="cmdbar-kbd">Ctrl + K</span>
|
|
52
52
|
</button>
|
|
53
53
|
</div>
|
|
54
54
|
<div id="header-right">
|
|
55
55
|
<span id="ext-slot-header-right" data-slot="header.right"></span>
|
|
56
|
-
|
|
57
|
-
<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">
|
|
58
|
-
<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/>
|
|
59
|
-
<path d="M21 3v5h-5"/>
|
|
60
|
-
<path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/>
|
|
61
|
-
<path d="M8 16H3v5"/>
|
|
62
|
-
</svg>
|
|
63
|
-
</button>
|
|
56
|
+
|
|
64
57
|
<button id="share-toggle-header" class="theme-toggle-btn" data-i18n-title="share.tooltip" title="Share">
|
|
65
58
|
<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">
|
|
66
59
|
<circle cx="18" cy="5" r="3"/>
|
|
@@ -79,7 +72,7 @@
|
|
|
79
72
|
</div>
|
|
80
73
|
</header>
|
|
81
74
|
|
|
82
|
-
<!-- ── Command-palette search overlay (
|
|
75
|
+
<!-- ── Command-palette search overlay (platform shortcut / top cmdbar) ───── -->
|
|
83
76
|
<!-- Search lives here, decoupled from the sidebar list: results render in
|
|
84
77
|
#session-search-results and the left session list is never replaced. -->
|
|
85
78
|
<div id="session-search-overlay" class="cmd-palette-overlay" hidden>
|
|
@@ -573,6 +566,8 @@
|
|
|
573
566
|
<button class="skills-tab" data-tab="brand-skills" id="tab-brand-skills" data-i18n="skills.tab.brand">Brand Skills</button>
|
|
574
567
|
</div>
|
|
575
568
|
<div class="skills-tabs-controls">
|
|
569
|
+
<!-- Search input -->
|
|
570
|
+
<input type="text" id="skills-search-input" class="skills-search" placeholder="Search skills…" data-i18n-placeholder="skills.search.placeholder" />
|
|
576
571
|
<!-- Show system skills toggle (only visible in My Skills tab) -->
|
|
577
572
|
<label class="skills-filter-toggle" id="label-show-system">
|
|
578
573
|
<input type="checkbox" id="chk-show-system-skills">
|
data/lib/clacky/web/sessions.js
CHANGED
|
@@ -884,8 +884,17 @@ const Sessions = (() => {
|
|
|
884
884
|
//
|
|
885
885
|
// Everything uses event delegation because some elements (e.g. the clear
|
|
886
886
|
// buttons) are re-rendered as filter state changes.
|
|
887
|
+
function _sessionSearchShortcutLabel() {
|
|
888
|
+
const uaDataPlatform = navigator.userAgentData && navigator.userAgentData.platform;
|
|
889
|
+
const platform = (uaDataPlatform || navigator.platform || "").toString();
|
|
890
|
+
return /mac/i.test(platform) ? "⌘ + K" : "Ctrl + K";
|
|
891
|
+
}
|
|
892
|
+
|
|
887
893
|
function _initSearch() {
|
|
888
|
-
|
|
894
|
+
const cmdbarKbd = document.querySelector("#header-cmdbar .cmdbar-kbd");
|
|
895
|
+
if (cmdbarKbd) cmdbarKbd.textContent = _sessionSearchShortcutLabel();
|
|
896
|
+
|
|
897
|
+
// Open the palette: top cmdbar button (or the keyboard shortcut, bound below).
|
|
889
898
|
document.addEventListener("click", (e) => {
|
|
890
899
|
if (e.target && e.target.closest("#header-cmdbar")) {
|
|
891
900
|
if (!Sessions.searchOpen) Sessions.toggleSearch();
|
|
@@ -1660,8 +1669,8 @@ const Sessions = (() => {
|
|
|
1660
1669
|
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
1661
1670
|
const dDay = new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
|
1662
1671
|
const diffDays = Math.round((today - dDay) / 86400000);
|
|
1663
|
-
if (diffDays === 0) return
|
|
1664
|
-
if (diffDays === 1) return
|
|
1672
|
+
if (diffDays === 0) return `${I18n.t("sessions.today")} ${hhmm}`;
|
|
1673
|
+
if (diffDays === 1) return `${I18n.t("sessions.yesterday")} ${hhmm}`;
|
|
1665
1674
|
return `${d.getMonth() + 1}/${d.getDate()} ${hhmm}`;
|
|
1666
1675
|
}
|
|
1667
1676
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: openclacky
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- windy
|
|
@@ -480,6 +480,7 @@ files:
|
|
|
480
480
|
- lib/clacky/media/gemini.rb
|
|
481
481
|
- lib/clacky/media/generator.rb
|
|
482
482
|
- lib/clacky/media/openai_compat.rb
|
|
483
|
+
- lib/clacky/media/volcengine.rb
|
|
483
484
|
- lib/clacky/message_format/anthropic.rb
|
|
484
485
|
- lib/clacky/message_format/bedrock.rb
|
|
485
486
|
- lib/clacky/message_format/open_ai.rb
|