openclacky 1.3.10 → 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 +48 -0
- data/lib/clacky/agent/skill_manager.rb +1 -0
- data/lib/clacky/brand_config.rb +32 -6
- data/lib/clacky/default_extensions/ext-studio/api/handler.rb +78 -2
- data/lib/clacky/default_extensions/ext-studio/panels/studio/view.js +390 -50
- 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 +13 -3
- 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 +107 -45
- data/lib/clacky/telemetry.rb +9 -0
- 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 +189 -24
- data/lib/clacky/web/features/extensions/store.js +14 -7
- data/lib/clacky/web/features/extensions/view.js +77 -13
- data/lib/clacky/web/features/skills/view.js +61 -1
- data/lib/clacky/web/i18n.js +38 -6
- data/lib/clacky/web/index.html +6 -11
- data/lib/clacky/web/sessions.js +12 -3
- metadata +2 -1
|
@@ -77,18 +77,35 @@ 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>` : "";
|
|
104
|
+
const canUpdate = ext.installed_version && ext.version && ext.installed_version !== ext.version;
|
|
90
105
|
const installedHtml = ext.installed
|
|
91
106
|
? `<span class="extension-installed">${escapeHtml(I18n.t("extensions.installed"))}</span>` : "";
|
|
107
|
+
const updatableHtml = canUpdate
|
|
108
|
+
? `<span class="extension-updatable">${escapeHtml(I18n.t("extensions.updatable"))}</span>` : "";
|
|
92
109
|
const unlistedHtml = (ext.unlisted && ext.installed)
|
|
93
110
|
? `<span class="extension-unlisted">${escapeHtml(I18n.t("extensions.unlisted"))}</span>` : "";
|
|
94
111
|
const unitsText = _formatUnits(ext.units);
|
|
@@ -98,22 +115,26 @@ const ExtensionsView = (() => {
|
|
|
98
115
|
? `<a class="extension-homepage" href="${escapeHtml(ext.homepage)}" target="_blank" rel="noopener noreferrer">${I18n.t("extensions.homepage")}</a>`
|
|
99
116
|
: "";
|
|
100
117
|
const authorHtml = ext.author
|
|
101
|
-
? `<span class="extension-author"
|
|
118
|
+
? `<span class="extension-author">${escapeHtml(I18n.t("extensions.by"))}${escapeHtml(ext.author)}</span>` : "";
|
|
119
|
+
const installsHtml = ext.download_count > 0
|
|
120
|
+
? `<span class="extension-installs">${escapeHtml(String(ext.download_count))} ${escapeHtml(I18n.t("extensions.installs"))}</span>` : "";
|
|
102
121
|
|
|
103
122
|
const card = document.createElement("div");
|
|
104
123
|
card.className = "extension-card extension-card-clickable";
|
|
105
124
|
card.dataset.extId = ext.id != null ? String(ext.id) : (ext.name || "");
|
|
106
125
|
card.innerHTML = `
|
|
107
126
|
<div class="extension-card-main">
|
|
108
|
-
|
|
127
|
+
${emojiHtml}
|
|
109
128
|
<div class="extension-info">
|
|
110
129
|
<div class="extension-title">
|
|
111
130
|
<span class="extension-name">${escapeHtml(name)}</span>
|
|
112
|
-
${authorHtml}
|
|
113
131
|
${versionHtml}
|
|
114
132
|
${installedHtml}
|
|
133
|
+
${updatableHtml}
|
|
115
134
|
${unlistedHtml}
|
|
116
135
|
${unitsHtml}
|
|
136
|
+
${authorHtml}
|
|
137
|
+
${installsHtml}
|
|
117
138
|
</div>
|
|
118
139
|
${description ? `<div class="extension-desc">${escapeHtml(description)}</div>` : ""}
|
|
119
140
|
${homepageHtml ? `<div class="extension-meta">${homepageHtml}</div>` : ""}
|
|
@@ -202,6 +223,16 @@ const ExtensionsView = (() => {
|
|
|
202
223
|
Extensions.install(id);
|
|
203
224
|
});
|
|
204
225
|
}
|
|
226
|
+
|
|
227
|
+
const updateBtn = document.querySelector("[data-ext-update]");
|
|
228
|
+
if (updateBtn) {
|
|
229
|
+
updateBtn.addEventListener("click", () => {
|
|
230
|
+
const id = updateBtn.getAttribute("data-ext-update");
|
|
231
|
+
updateBtn.disabled = true;
|
|
232
|
+
updateBtn.textContent = I18n.t("extensions.action.updating");
|
|
233
|
+
Extensions.update(id);
|
|
234
|
+
});
|
|
235
|
+
}
|
|
205
236
|
}
|
|
206
237
|
|
|
207
238
|
function _backToList() {
|
|
@@ -212,16 +243,23 @@ const ExtensionsView = (() => {
|
|
|
212
243
|
|
|
213
244
|
function _detailContent(ext) {
|
|
214
245
|
const currentLang = I18n.lang();
|
|
215
|
-
const name = (currentLang === "zh" && ext.
|
|
246
|
+
const name = (currentLang === "zh" && ext.display_name_zh) ? ext.display_name_zh : (ext.display_name || ext.name);
|
|
216
247
|
const description = (currentLang === "zh" && ext.description_zh)
|
|
217
248
|
? ext.description_zh
|
|
218
249
|
: ext.description || "";
|
|
219
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");
|
|
220
254
|
|
|
255
|
+
const canUpdate = ext.installed && ext.installed_version && ext.version && ext.installed_version !== ext.version;
|
|
221
256
|
const versionHtml = ext.version
|
|
222
257
|
? `<span class="extension-version">v${escapeHtml(String(ext.version))}</span>` : "";
|
|
258
|
+
const installedLabel = canUpdate && ext.installed_version
|
|
259
|
+
? `${I18n.t("extensions.installed")} v${escapeHtml(String(ext.installed_version))}`
|
|
260
|
+
: I18n.t("extensions.installed");
|
|
223
261
|
const installedHtml = ext.installed
|
|
224
|
-
? `<span class="extension-installed">${
|
|
262
|
+
? `<span class="extension-installed">${installedLabel}</span>` : "";
|
|
225
263
|
const unlistedHtml = ext.unlisted
|
|
226
264
|
? `<span class="extension-unlisted">${escapeHtml(I18n.t("extensions.unlisted"))}</span>` : "";
|
|
227
265
|
const unitsText = _formatUnits(ext.units);
|
|
@@ -231,26 +269,29 @@ const ExtensionsView = (() => {
|
|
|
231
269
|
? `<a class="extension-homepage" href="${escapeHtml(ext.homepage)}" target="_blank" rel="noopener noreferrer">${I18n.t("extensions.homepage")}</a>`
|
|
232
270
|
: "";
|
|
233
271
|
const authorHtml = ext.author
|
|
234
|
-
? `<span class="extension-author"
|
|
272
|
+
? `<span class="extension-author">${escapeHtml(I18n.t("extensions.by"))}${escapeHtml(ext.author)}</span>` : "";
|
|
273
|
+
const installsHtml = ext.download_count > 0
|
|
274
|
+
? `<span class="extension-installs">${escapeHtml(String(ext.download_count))} ${escapeHtml(I18n.t("extensions.installs"))}</span>` : "";
|
|
235
275
|
|
|
236
276
|
return `
|
|
237
277
|
<div class="extension-detail-hero">
|
|
238
|
-
|
|
278
|
+
${detailEmojiHtml}
|
|
239
279
|
<div class="extension-detail-heading">
|
|
240
280
|
<div class="extension-title">
|
|
241
281
|
<span class="extension-name extension-name-lg">${escapeHtml(name)}</span>
|
|
242
|
-
${authorHtml}
|
|
243
282
|
${versionHtml}
|
|
244
283
|
${installedHtml}
|
|
245
284
|
${unlistedHtml}
|
|
246
285
|
${unitsHtml}
|
|
286
|
+
${authorHtml}
|
|
287
|
+
${installsHtml}
|
|
247
288
|
</div>
|
|
248
289
|
${description ? `<div class="extension-desc extension-desc-detail">${escapeHtml(description)}</div>` : ""}
|
|
249
290
|
${homepageHtml ? `<div class="extension-meta">${homepageHtml}</div>` : ""}
|
|
250
291
|
${_renderActions(ext)}
|
|
251
292
|
</div>
|
|
252
293
|
</div>
|
|
253
|
-
${
|
|
294
|
+
${_renderEntryPoints(ext.contributes)}
|
|
254
295
|
${_renderVersions(ext.versions)}`;
|
|
255
296
|
}
|
|
256
297
|
|
|
@@ -275,9 +316,14 @@ const ExtensionsView = (() => {
|
|
|
275
316
|
const removeBtn = ext.removable
|
|
276
317
|
? `<button type="button" class="extension-action extension-action-remove" data-ext-remove="${escapeHtml(slug)}">${escapeHtml(I18n.t("extensions.action.remove"))}</button>`
|
|
277
318
|
: "";
|
|
319
|
+
const canUpdate = ext.installed_version && ext.version && ext.installed_version !== ext.version && ext.download_url;
|
|
320
|
+
const updateBtn = canUpdate
|
|
321
|
+
? `<button type="button" class="extension-action extension-action-update" data-ext-update="${escapeHtml(id)}">${escapeHtml(I18n.t("extensions.action.update"))}</button>`
|
|
322
|
+
: "";
|
|
278
323
|
return `
|
|
279
324
|
<div class="extension-detail-actions">
|
|
280
325
|
${disabledBadge}
|
|
326
|
+
${updateBtn}
|
|
281
327
|
<button type="button" class="extension-action ${toggleCls}" data-ext-toggle="${escapeHtml(slug)}" data-ext-enabled="${ext.disabled ? "1" : "0"}">${escapeHtml(I18n.t(toggleKey))}</button>
|
|
282
328
|
${removeBtn}
|
|
283
329
|
</div>`;
|
|
@@ -328,6 +374,24 @@ const ExtensionsView = (() => {
|
|
|
328
374
|
</div>`;
|
|
329
375
|
}
|
|
330
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
|
+
|
|
331
395
|
function _renderVersions(versions) {
|
|
332
396
|
if (!Array.isArray(versions) || versions.length === 0) return "";
|
|
333
397
|
const rows = versions.map((v) => {
|
|
@@ -336,9 +400,9 @@ const ExtensionsView = (() => {
|
|
|
336
400
|
<li class="extension-version-item">
|
|
337
401
|
<div class="extension-version-row">
|
|
338
402
|
<span class="extension-version">v${escapeHtml(String(v.version || ""))}</span>
|
|
339
|
-
${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>` : ""}
|
|
340
404
|
</div>
|
|
341
|
-
${v.release_notes ? `<div class="extension-version-notes">${escapeHtml(String(v.release_notes))}</div>` : ""}
|
|
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>` : ""}
|
|
342
406
|
</li>`;
|
|
343
407
|
}).join("");
|
|
344
408
|
return `
|
|
@@ -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",
|
|
@@ -512,15 +516,17 @@ const I18n = (() => {
|
|
|
512
516
|
"extensions.searchPlaceholder": "Search extensions…",
|
|
513
517
|
"extensions.sort.newest": "Newest",
|
|
514
518
|
"extensions.sort.updated": "Recently updated",
|
|
515
|
-
"extensions.sort.downloads": "Most
|
|
519
|
+
"extensions.sort.downloads": "Most installed",
|
|
516
520
|
"extensions.filter.all": "All",
|
|
517
521
|
"extensions.filter.installed": "Installed",
|
|
518
522
|
"extensions.empty": "No extensions available yet.",
|
|
519
523
|
"extensions.noResults": "No extensions match your search.",
|
|
520
524
|
"extensions.loadFailed": "Could not reach the extension store.",
|
|
521
525
|
"extensions.homepage": "Homepage",
|
|
522
|
-
"extensions.by": "by",
|
|
523
|
-
"extensions.
|
|
526
|
+
"extensions.by": "by ",
|
|
527
|
+
"extensions.installs": "installs",
|
|
528
|
+
"extensions.installed": "Installed",
|
|
529
|
+
"extensions.updatable": "Update available",
|
|
524
530
|
"extensions.unlisted": "Unlisted",
|
|
525
531
|
"extensions.unit.skill": "skill",
|
|
526
532
|
"extensions.unit.skills": "skills",
|
|
@@ -532,7 +538,15 @@ const I18n = (() => {
|
|
|
532
538
|
"extensions.unit.apis": "APIs",
|
|
533
539
|
"extensions.detail.back": "Back",
|
|
534
540
|
"extensions.detail.contributes": "What's inside",
|
|
541
|
+
"extensions.detail.entry_points": "Access points",
|
|
535
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",
|
|
536
550
|
"extensions.section.agents": "Agents",
|
|
537
551
|
"extensions.section.skills": "Skills",
|
|
538
552
|
"extensions.section.panels": "Panels",
|
|
@@ -540,6 +554,8 @@ const I18n = (() => {
|
|
|
540
554
|
"extensions.disabled": "Disabled",
|
|
541
555
|
"extensions.action.install": "Install",
|
|
542
556
|
"extensions.action.installing": "Installing…",
|
|
557
|
+
"extensions.action.update": "Update",
|
|
558
|
+
"extensions.action.updating": "Updating…",
|
|
543
559
|
"extensions.action.disable": "Disable",
|
|
544
560
|
"extensions.action.enable": "Enable",
|
|
545
561
|
"extensions.action.remove": "Remove",
|
|
@@ -1059,6 +1075,8 @@ const I18n = (() => {
|
|
|
1059
1075
|
"sessions.deleteTitle": "删除会话",
|
|
1060
1076
|
"sessions.createError": "错误:",
|
|
1061
1077
|
"sessions.dirNotEmpty": "该目录已存在且不为空,请换一个目录名。",
|
|
1078
|
+
"sessions.today": "今天",
|
|
1079
|
+
"sessions.yesterday": "昨天",
|
|
1062
1080
|
"sessions.export.tooltip": "下载会话文件(session.json + 归档片段 + 当天日志),用于调试",
|
|
1063
1081
|
"sessions.export.failed": "下载会话文件失败",
|
|
1064
1082
|
"sessions.actions.tooltip": "点击查看会话操作",
|
|
@@ -1377,6 +1395,8 @@ const I18n = (() => {
|
|
|
1377
1395
|
"skills.import.install": "安装",
|
|
1378
1396
|
"skills.tab.my": "我的技能",
|
|
1379
1397
|
"skills.tab.brand": "品牌技能",
|
|
1398
|
+
"skills.search.placeholder": "搜索技能…",
|
|
1399
|
+
"skills.search.empty": "没有匹配的技能。",
|
|
1380
1400
|
"skills.empty": "暂无技能。",
|
|
1381
1401
|
"skills.empty.createBtn": "用 /skill-creator 创建新技能",
|
|
1382
1402
|
"skills.noSkills": "无技能",
|
|
@@ -1475,15 +1495,17 @@ const I18n = (() => {
|
|
|
1475
1495
|
"extensions.searchPlaceholder": "搜索扩展…",
|
|
1476
1496
|
"extensions.sort.newest": "最新",
|
|
1477
1497
|
"extensions.sort.updated": "最近更新",
|
|
1478
|
-
"extensions.sort.downloads": "
|
|
1498
|
+
"extensions.sort.downloads": "安装最多",
|
|
1479
1499
|
"extensions.filter.all": "全部",
|
|
1480
1500
|
"extensions.filter.installed": "已安装",
|
|
1481
1501
|
"extensions.empty": "暂无可用扩展。",
|
|
1482
1502
|
"extensions.noResults": "没有匹配的扩展。",
|
|
1483
1503
|
"extensions.loadFailed": "无法连接扩展市场。",
|
|
1484
1504
|
"extensions.homepage": "主页",
|
|
1485
|
-
"extensions.by": "
|
|
1486
|
-
"extensions.
|
|
1505
|
+
"extensions.by": "作者:",
|
|
1506
|
+
"extensions.installs": "次安装",
|
|
1507
|
+
"extensions.installed": "已安装",
|
|
1508
|
+
"extensions.updatable": "有更新",
|
|
1487
1509
|
"extensions.unlisted": "已下架",
|
|
1488
1510
|
"extensions.unit.skill": "个技能",
|
|
1489
1511
|
"extensions.unit.skills": "个技能",
|
|
@@ -1495,7 +1517,15 @@ const I18n = (() => {
|
|
|
1495
1517
|
"extensions.unit.apis": "个 API",
|
|
1496
1518
|
"extensions.detail.back": "返回",
|
|
1497
1519
|
"extensions.detail.contributes": "包含内容",
|
|
1520
|
+
"extensions.detail.entry_points": "访问入口",
|
|
1498
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": "设置页标签",
|
|
1499
1529
|
"extensions.section.agents": "Agent",
|
|
1500
1530
|
"extensions.section.skills": "技能",
|
|
1501
1531
|
"extensions.section.panels": "面板",
|
|
@@ -1503,6 +1533,8 @@ const I18n = (() => {
|
|
|
1503
1533
|
"extensions.disabled": "已禁用",
|
|
1504
1534
|
"extensions.action.install": "安装",
|
|
1505
1535
|
"extensions.action.installing": "安装中…",
|
|
1536
|
+
"extensions.action.update": "更新",
|
|
1537
|
+
"extensions.action.updating": "更新中…",
|
|
1506
1538
|
"extensions.action.disable": "禁用",
|
|
1507
1539
|
"extensions.action.enable": "启用",
|
|
1508
1540
|
"extensions.action.remove": "移除",
|
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
|