openclacky 1.3.8 → 1.3.10
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 +40 -0
- data/lib/clacky/brand_config.rb +2 -1
- data/lib/clacky/default_extensions/coding/ext.yml +0 -1
- data/lib/clacky/default_extensions/ext-studio/agents/ext-developer/system_prompt.md +0 -1
- data/lib/clacky/default_extensions/ext-studio/api/handler.rb +31 -4
- data/lib/clacky/default_extensions/ext-studio/ext.yml +2 -3
- data/lib/clacky/default_extensions/ext-studio/panels/studio/view.js +221 -114
- data/lib/clacky/default_extensions/ext-studio/skills/ext-publish/SKILL.md +1 -2
- data/lib/clacky/default_extensions/general/ext.yml +0 -1
- data/lib/clacky/default_extensions/git/ext.yml +0 -1
- data/lib/clacky/default_extensions/meeting/ext.yml +0 -1
- data/lib/clacky/default_extensions/time_machine/ext.yml +0 -1
- data/lib/clacky/extension/api_extension.rb +11 -2
- data/lib/clacky/extension/dispatcher.rb +3 -2
- data/lib/clacky/extension/packager.rb +0 -6
- data/lib/clacky/locales/en.rb +1 -1
- data/lib/clacky/locales/zh.rb +1 -1
- data/lib/clacky/prompts/base.md +1 -1
- data/lib/clacky/server/http_server.rb +115 -7
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.css +123 -32
- data/lib/clacky/web/features/extensions/store.js +62 -9
- data/lib/clacky/web/features/extensions/view.js +54 -17
- data/lib/clacky/web/features/new-session/view.js +1 -1
- data/lib/clacky/web/i18n.js +16 -4
- data/lib/clacky/web/index.html +12 -6
- data/lib/clacky/web/sessions.js +32 -2
- metadata +1 -1
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"verify.errors": "{{n}} error(s)",
|
|
30
30
|
"verify.warnings": "{{n}} warning(s)",
|
|
31
31
|
"verify.hint": "Hint",
|
|
32
|
-
"hint.reload": "After fixing, reload this page
|
|
32
|
+
"hint.reload": "After fixing, reload this page to apply changes.",
|
|
33
33
|
"publish.status": "Status",
|
|
34
34
|
"publish.status.draft": "Draft",
|
|
35
35
|
"publish.status.published": "Published",
|
|
@@ -39,18 +39,20 @@
|
|
|
39
39
|
"btn.publish": "Publish to marketplace",
|
|
40
40
|
"btn.publishing": "Publishing…",
|
|
41
41
|
"btn.pack": "Pack (.zip)",
|
|
42
|
+
"btn.packing": "Packing…",
|
|
42
43
|
"publish.needLicense": "Publishing requires an activated user license.",
|
|
43
44
|
"publish.done": "Published {{id}} {{ver}} — {{status}}",
|
|
44
|
-
"publish.packed": "Packed → {{path}}",
|
|
45
45
|
"publish.already": "Already published. Enable \"publish a new version\" to ship.",
|
|
46
46
|
"published.title": "Your published extensions",
|
|
47
47
|
"published.empty": "You haven't published anything yet.",
|
|
48
48
|
"btn.unpublish": "Unpublish",
|
|
49
49
|
"published.confirm": "Unpublish {{id}} from the marketplace?",
|
|
50
50
|
"err.generic": "Something went wrong: {{msg}}",
|
|
51
|
+
"err.version_conflict": "Version {{ver}} has already been published. Please enter a higher version number above and try again.",
|
|
52
|
+
"pub.version.label": "Version",
|
|
51
53
|
|
|
52
54
|
"publish.confirm.title": "Publish to the marketplace?",
|
|
53
|
-
"publish.confirm.body": "\"{{id}}\" will be packed and uploaded to the
|
|
55
|
+
"publish.confirm.body": "\"{{id}}\" will be packed and uploaded to the extension marketplace, where anyone can discover and install it. Make sure it's ready to share publicly.",
|
|
54
56
|
"publish.confirm.ok": "Publish",
|
|
55
57
|
"publish.confirm.cancel": "Cancel",
|
|
56
58
|
"bind.title": "Authorize this device",
|
|
@@ -67,7 +69,7 @@
|
|
|
67
69
|
|
|
68
70
|
"pub.title.new": "Publish to the marketplace",
|
|
69
71
|
"pub.title.update": "Publish a new version",
|
|
70
|
-
"pub.intro": "\"{{name}}\" will be packed and uploaded to the
|
|
72
|
+
"pub.intro": "\"{{name}}\" will be packed and uploaded to the extension marketplace, where anyone can discover and install it.",
|
|
71
73
|
"pub.version.new": "First release · v{{ver}}",
|
|
72
74
|
"pub.version.update": "v{{prev}} → v{{ver}}",
|
|
73
75
|
"pub.version.missing": "No version in ext.yml — add a \"version\" field before publishing.",
|
|
@@ -77,10 +79,13 @@
|
|
|
77
79
|
"pub.readme.ok": "README.md detected",
|
|
78
80
|
"pub.readme.missing": "No README.md — consider adding one so users understand your extension.",
|
|
79
81
|
"pub.btn.publish": "Publish v{{ver}}",
|
|
82
|
+
"pub.btn.publishing": "Publishing…",
|
|
83
|
+
"pub.btn.done": "Done",
|
|
80
84
|
"pub.btn.cancel": "Cancel",
|
|
81
85
|
"pub.progress.packing": "Packing…",
|
|
82
86
|
"pub.progress.uploading": "Uploading to marketplace…",
|
|
83
87
|
"pub.done": "Published {{id}} v{{ver}} — {{status}}",
|
|
88
|
+
"pub.success": "Published successfully",
|
|
84
89
|
"pub.done.close": "Done",
|
|
85
90
|
"pub.retry": "Try again",
|
|
86
91
|
"extlist.section.cloud": "Published Extensions",
|
|
@@ -98,6 +103,7 @@
|
|
|
98
103
|
"extlist.btn.publish": "Publish",
|
|
99
104
|
"extlist.btn.update": "Update",
|
|
100
105
|
"extlist.btn.pack": "Pack",
|
|
106
|
+
"extlist.btn.packing": "Packing…",
|
|
101
107
|
"extlist.btn.unpublish": "Unpublish",
|
|
102
108
|
"extlist.btn.iterate": "Iterate",
|
|
103
109
|
"extlist.iterate.seed": "Iterate on extension {{id}}",
|
|
@@ -105,7 +111,6 @@
|
|
|
105
111
|
"extlist.overwrite.confirm": "\"{{id}}\" is already published. Publish a new version?",
|
|
106
112
|
"extlist.unpublish.confirm": "Unpublish {{id}} from the marketplace?",
|
|
107
113
|
"extlist.publishing": "Publishing…",
|
|
108
|
-
"extlist.packed": "Packed → {{path}}",
|
|
109
114
|
"extlist.needLicense": "Publishing requires an activated user license.",
|
|
110
115
|
"extlist.newExt.label": "Create a new extension",
|
|
111
116
|
"extlist.newExt.hint": "Opens an AI session that scaffolds and builds it for you.",
|
|
@@ -132,7 +137,7 @@
|
|
|
132
137
|
"skills.shadow.tooltip": "Local copy shadows a same-named brand skill",
|
|
133
138
|
"skills.newSkill.label": "Create a new skill with /skill-creator",
|
|
134
139
|
"skills.newSkill.btn": "Create New Skill",
|
|
135
|
-
"skills.promo.text": "Publish your skills & build your own brand
|
|
140
|
+
"skills.promo.text": "Publish your skills & build your own brand.",
|
|
136
141
|
"skills.promo.link": "Learn more →",
|
|
137
142
|
"skills.locked": "Creator license required to publish cloud skills.",
|
|
138
143
|
"skills.publishing": "Publishing…",
|
|
@@ -159,7 +164,7 @@
|
|
|
159
164
|
"verify.errors": "{{n}} 个错误",
|
|
160
165
|
"verify.warnings": "{{n}} 个警告",
|
|
161
166
|
"verify.hint": "提示",
|
|
162
|
-
"hint.reload": "
|
|
167
|
+
"hint.reload": "修复问题后,刷新页面即可生效。",
|
|
163
168
|
"publish.status": "状态",
|
|
164
169
|
"publish.status.draft": "草稿",
|
|
165
170
|
"publish.status.published": "已发布",
|
|
@@ -169,18 +174,20 @@
|
|
|
169
174
|
"btn.publish": "发布到市场",
|
|
170
175
|
"btn.publishing": "发布中…",
|
|
171
176
|
"btn.pack": "打包 (.zip)",
|
|
177
|
+
"btn.packing": "打包中…",
|
|
172
178
|
"publish.needLicense": "发布需要已激活的用户授权。",
|
|
173
179
|
"publish.done": "已发布 {{id}} {{ver}} — {{status}}",
|
|
174
|
-
"publish.packed": "已打包 → {{path}}",
|
|
175
180
|
"publish.already": "已发布过。勾选「发布新版本」后再试。",
|
|
176
181
|
"published.title": "你发布的扩展",
|
|
177
182
|
"published.empty": "你还没有发布任何扩展。",
|
|
178
183
|
"btn.unpublish": "下架",
|
|
179
184
|
"published.confirm": "确定要从市场下架 {{id}} 吗?",
|
|
180
185
|
"err.generic": "出错了:{{msg}}",
|
|
186
|
+
"err.version_conflict": "版本 {{ver}} 已发布过,请在上方输入更高的版本号后重试。",
|
|
187
|
+
"pub.version.label": "版本号",
|
|
181
188
|
|
|
182
189
|
"publish.confirm.title": "确定发布到市场?",
|
|
183
|
-
"publish.confirm.body": "「{{id}}
|
|
190
|
+
"publish.confirm.body": "「{{id}}」将被打包并上传到扩展市场,任何人都能发现并安装它。请确认它已准备好公开分享。",
|
|
184
191
|
"publish.confirm.ok": "发布",
|
|
185
192
|
"publish.confirm.cancel": "取消",
|
|
186
193
|
"bind.title": "授权此设备",
|
|
@@ -197,7 +204,7 @@
|
|
|
197
204
|
|
|
198
205
|
"pub.title.new": "发布到市场",
|
|
199
206
|
"pub.title.update": "发布新版本",
|
|
200
|
-
"pub.intro": "「{{name}}
|
|
207
|
+
"pub.intro": "「{{name}}」将被打包并上传到扩展市场,任何人都能发现并安装它。",
|
|
201
208
|
"pub.version.new": "首次发布 · v{{ver}}",
|
|
202
209
|
"pub.version.update": "v{{prev}} → v{{ver}}",
|
|
203
210
|
"pub.version.missing": "ext.yml 里没有 version — 请先补上 version 字段再发布。",
|
|
@@ -207,10 +214,13 @@
|
|
|
207
214
|
"pub.readme.ok": "已检测到 README.md",
|
|
208
215
|
"pub.readme.missing": "未找到 README.md — 建议补一份,方便用户了解你的扩展。",
|
|
209
216
|
"pub.btn.publish": "发布 v{{ver}}",
|
|
217
|
+
"pub.btn.publishing": "发布中…",
|
|
218
|
+
"pub.btn.done": "完成",
|
|
210
219
|
"pub.btn.cancel": "取消",
|
|
211
220
|
"pub.progress.packing": "打包中…",
|
|
212
221
|
"pub.progress.uploading": "上传到市场…",
|
|
213
222
|
"pub.done": "已发布 {{id}} v{{ver}} — {{status}}",
|
|
223
|
+
"pub.success": "已发布成功",
|
|
214
224
|
"pub.done.close": "完成",
|
|
215
225
|
"pub.retry": "重试",
|
|
216
226
|
"extlist.section.cloud": "已发布扩展",
|
|
@@ -228,6 +238,7 @@
|
|
|
228
238
|
"extlist.btn.publish": "发布",
|
|
229
239
|
"extlist.btn.update": "更新",
|
|
230
240
|
"extlist.btn.pack": "打包",
|
|
241
|
+
"extlist.btn.packing": "打包中…",
|
|
231
242
|
"extlist.btn.unpublish": "下架",
|
|
232
243
|
"extlist.btn.iterate": "迭代",
|
|
233
244
|
"extlist.iterate.seed": "迭代扩展 {{id}}",
|
|
@@ -235,7 +246,6 @@
|
|
|
235
246
|
"extlist.overwrite.confirm": "「{{id}}」已经发布过了。要发布新版本吗?",
|
|
236
247
|
"extlist.unpublish.confirm": "确定要从市场下架 {{id}} 吗?",
|
|
237
248
|
"extlist.publishing": "发布中…",
|
|
238
|
-
"extlist.packed": "已打包 → {{path}}",
|
|
239
249
|
"extlist.needLicense": "发布需要已激活的用户授权。",
|
|
240
250
|
"extlist.newExt.label": "新建扩展",
|
|
241
251
|
"extlist.newExt.hint": "打开一个 AI 会话,帮你生成并开发它。",
|
|
@@ -262,7 +272,7 @@
|
|
|
262
272
|
"skills.shadow.tooltip": "本地副本覆盖了同名品牌 skill",
|
|
263
273
|
"skills.newSkill.label": "用 /skill-creator 创建新 skill",
|
|
264
274
|
"skills.newSkill.btn": "创建新 Skill",
|
|
265
|
-
"skills.promo.text": "发布你的 skill
|
|
275
|
+
"skills.promo.text": "发布你的 skill,打造自己的品牌。",
|
|
266
276
|
"skills.promo.link": "了解更多 →",
|
|
267
277
|
"skills.locked": "发布云端 skill 需要创作者授权。",
|
|
268
278
|
"skills.publishing": "发布中…",
|
|
@@ -302,6 +312,28 @@
|
|
|
302
312
|
return data;
|
|
303
313
|
}
|
|
304
314
|
|
|
315
|
+
async function downloadPack(ext_id) {
|
|
316
|
+
const res = await fetch(api("/pack"), {
|
|
317
|
+
method: "POST",
|
|
318
|
+
headers: { "Content-Type": "application/json" },
|
|
319
|
+
body: JSON.stringify({ ext_id }),
|
|
320
|
+
});
|
|
321
|
+
if (!res.ok) {
|
|
322
|
+
let msg = `Request failed (${res.status})`;
|
|
323
|
+
try { msg = (await res.json()).error || msg; } catch (_e) {}
|
|
324
|
+
throw new Error(msg);
|
|
325
|
+
}
|
|
326
|
+
const blob = await res.blob();
|
|
327
|
+
const url = URL.createObjectURL(blob);
|
|
328
|
+
const a = document.createElement("a");
|
|
329
|
+
a.href = url;
|
|
330
|
+
a.download = `${ext_id}.zip`;
|
|
331
|
+
document.body.appendChild(a);
|
|
332
|
+
a.click();
|
|
333
|
+
document.body.removeChild(a);
|
|
334
|
+
URL.revokeObjectURL(url);
|
|
335
|
+
}
|
|
336
|
+
|
|
305
337
|
// Skills tab talks to host-owned endpoints (not the ext prefix). We surface
|
|
306
338
|
// the HTTP status so the caller can treat 403 as "locked" rather than error.
|
|
307
339
|
async function getHost(path) {
|
|
@@ -443,47 +475,48 @@
|
|
|
443
475
|
|
|
444
476
|
// Unified publish flow: one modal that walks the creator from a release form
|
|
445
477
|
// (version + notes) through progress → device binding (if needed) → done.
|
|
446
|
-
// The creator owns the version
|
|
447
|
-
//
|
|
448
|
-
//
|
|
478
|
+
// The creator owns the version; if it conflicts with the latest published
|
|
479
|
+
// version they can bump it inline — the modal writes it back to ext.yml
|
|
480
|
+
// before uploading. `prevVersionOrPromise` may be a value or a Promise.
|
|
449
481
|
// Resolves true when a publish succeeded, false otherwise.
|
|
450
|
-
function runPublishFlow(ext,
|
|
482
|
+
function runPublishFlow(ext, prevVersionOrPromise) {
|
|
451
483
|
return new Promise((resolve) => {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
const unitList = (ext.units || []).map((u) => u.kind).join(" · ");
|
|
465
|
-
const unitsLine = unitList
|
|
466
|
-
? el("p", { class: "studio-skill-hint", text: t("pub.units", { units: unitList }) })
|
|
467
|
-
: null;
|
|
484
|
+
let currentVersion = ext.version || "";
|
|
485
|
+
let isUpdate = false;
|
|
486
|
+
|
|
487
|
+
const verField = el("div", { class: "studio-field" });
|
|
488
|
+
verField.appendChild(el("label", { class: "studio-label", text: t("pub.version.label") }));
|
|
489
|
+
const verInput = el("input", { class: "studio-input", type: "text", value: currentVersion, placeholder: "1.0.0" });
|
|
490
|
+
verInput.addEventListener("input", () => {
|
|
491
|
+
currentVersion = verInput.value.trim();
|
|
492
|
+
publishBtn.disabled = !currentVersion;
|
|
493
|
+
publishBtn.textContent = currentVersion ? t("pub.btn.publish", { ver: currentVersion }) : t("pub.btn.publish", { ver: "?" });
|
|
494
|
+
});
|
|
495
|
+
verField.appendChild(verInput);
|
|
468
496
|
|
|
469
|
-
const
|
|
470
|
-
|
|
497
|
+
const notesField = el("div", { class: "studio-field" });
|
|
498
|
+
notesField.appendChild(el("label", { class: "studio-label", text: t("pub.notes.label") }));
|
|
499
|
+
const notes = el("textarea", { class: "studio-textarea", rows: "4", placeholder: t("pub.notes.placeholder") });
|
|
500
|
+
notesField.appendChild(notes);
|
|
471
501
|
|
|
472
502
|
const status = el("p", { class: "studio-modal-status" });
|
|
473
503
|
status.style.display = "none";
|
|
474
504
|
|
|
475
|
-
const bodyChildren = [
|
|
476
|
-
|
|
477
|
-
|
|
505
|
+
const bodyChildren = [
|
|
506
|
+
el("p", { class: "studio-modal-intro", text: t("pub.intro", { name: ext.name }) }),
|
|
507
|
+
verField,
|
|
508
|
+
notesField,
|
|
509
|
+
status,
|
|
510
|
+
];
|
|
478
511
|
|
|
479
512
|
let done = false;
|
|
480
513
|
const modal = openModal({
|
|
481
|
-
title:
|
|
514
|
+
title: t("pub.title.new"),
|
|
482
515
|
body: el("div", null, bodyChildren),
|
|
483
516
|
buttons: [
|
|
484
517
|
{ label: t("pub.btn.cancel"), keepOpen: false, onClick: () => { if (!done) resolve(false); } },
|
|
485
518
|
{
|
|
486
|
-
label:
|
|
519
|
+
label: currentVersion ? t("pub.btn.publish", { ver: currentVersion }) : t("pub.btn.publish", { ver: "?" }),
|
|
487
520
|
primary: true,
|
|
488
521
|
keepOpen: true,
|
|
489
522
|
onClick: () => submit(),
|
|
@@ -493,50 +526,82 @@
|
|
|
493
526
|
|
|
494
527
|
const publishBtn = modal.footer.querySelector(".studio-btn-primary");
|
|
495
528
|
const cancelBtn = modal.footer.querySelector(".studio-btn:not(.studio-btn-primary)");
|
|
496
|
-
if (!
|
|
529
|
+
if (!currentVersion) publishBtn.disabled = true;
|
|
530
|
+
|
|
531
|
+
Promise.resolve(prevVersionOrPromise).then((prevVersion) => {
|
|
532
|
+
if (done || !prevVersion) return;
|
|
533
|
+
isUpdate = true;
|
|
534
|
+
const titleEl = modal.overlay.querySelector(".studio-modal-title");
|
|
535
|
+
if (titleEl) titleEl.textContent = t("pub.title.update");
|
|
536
|
+
if (prevVersion !== currentVersion) {
|
|
537
|
+
currentVersion = prevVersion;
|
|
538
|
+
verInput.value = currentVersion;
|
|
539
|
+
publishBtn.disabled = false;
|
|
540
|
+
publishBtn.textContent = t("pub.btn.publish", { ver: currentVersion });
|
|
541
|
+
}
|
|
542
|
+
});
|
|
497
543
|
|
|
498
|
-
function setProgress(msg) {
|
|
544
|
+
function setProgress(msg, isError) {
|
|
499
545
|
status.style.display = "";
|
|
500
546
|
status.textContent = msg;
|
|
547
|
+
status.className = "studio-modal-status" + (isError ? " studio-modal-status-error" : "");
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
function resetButtons() {
|
|
551
|
+
publishBtn.disabled = !currentVersion;
|
|
552
|
+
publishBtn.textContent = currentVersion ? t("pub.btn.publish", { ver: currentVersion }) : t("pub.btn.publish", { ver: "?" });
|
|
553
|
+
cancelBtn.disabled = false;
|
|
554
|
+
notes.disabled = false;
|
|
555
|
+
verInput.disabled = false;
|
|
501
556
|
}
|
|
502
557
|
|
|
503
558
|
async function submit() {
|
|
504
559
|
if (done || publishBtn.disabled) return;
|
|
560
|
+
const ver = currentVersion;
|
|
561
|
+
if (!ver) return;
|
|
562
|
+
|
|
505
563
|
publishBtn.disabled = true;
|
|
564
|
+
publishBtn.textContent = t("pub.btn.publishing");
|
|
506
565
|
cancelBtn.disabled = true;
|
|
507
566
|
notes.disabled = true;
|
|
508
|
-
|
|
567
|
+
verInput.disabled = true;
|
|
568
|
+
status.style.display = "none";
|
|
569
|
+
|
|
509
570
|
try {
|
|
510
|
-
|
|
571
|
+
if (ver !== ext.version) {
|
|
572
|
+
await postJson("/set_version", { ext_id: ext.id, version: ver });
|
|
573
|
+
}
|
|
574
|
+
|
|
511
575
|
const data = await publishWithBinding({
|
|
512
576
|
ext_id: ext.id,
|
|
513
577
|
force: isUpdate,
|
|
514
578
|
changelog: notes.value.trim(),
|
|
515
579
|
});
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
cancelBtn.disabled = false;
|
|
519
|
-
notes.disabled = false;
|
|
520
|
-
status.style.display = "none";
|
|
521
|
-
return;
|
|
522
|
-
}
|
|
580
|
+
|
|
581
|
+
if (data === null) { resetButtons(); return; }
|
|
523
582
|
if (!data.ok) throw new Error(data.error || "Publish failed");
|
|
583
|
+
|
|
524
584
|
done = true;
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
status: data.status || "",
|
|
529
|
-
}));
|
|
530
|
-
publishBtn.style.display = "none";
|
|
531
|
-
cancelBtn.textContent = t("pub.done.close");
|
|
532
|
-
cancelBtn.disabled = false;
|
|
533
|
-
cancelBtn.onclick = () => { modal.close(); resolve(true); };
|
|
534
|
-
} catch (e) {
|
|
535
|
-
setProgress(t("err.generic", { msg: e.message }));
|
|
585
|
+
const successLabel = el("span", { class: "studio-pub-success", text: t("pub.success") });
|
|
586
|
+
modal.footer.insertBefore(successLabel, modal.footer.firstChild);
|
|
587
|
+
cancelBtn.style.display = "none";
|
|
536
588
|
publishBtn.disabled = false;
|
|
537
|
-
publishBtn.textContent = t("pub.
|
|
538
|
-
|
|
539
|
-
|
|
589
|
+
publishBtn.textContent = t("pub.btn.done");
|
|
590
|
+
publishBtn.onclick = () => { modal.close(); resolve(true); };
|
|
591
|
+
} catch (e) {
|
|
592
|
+
const msg = e.message || "";
|
|
593
|
+
const isConflict = /must be greater than/i.test(msg);
|
|
594
|
+
if (isConflict) {
|
|
595
|
+
setProgress(t("err.version_conflict", { ver }), true);
|
|
596
|
+
verInput.classList.add("studio-input-error");
|
|
597
|
+
verInput.disabled = false;
|
|
598
|
+
verInput.focus();
|
|
599
|
+
verInput.select();
|
|
600
|
+
verInput.addEventListener("input", () => verInput.classList.remove("studio-input-error"), { once: true });
|
|
601
|
+
} else {
|
|
602
|
+
setProgress(t("err.generic", { msg }), true);
|
|
603
|
+
}
|
|
604
|
+
resetButtons();
|
|
540
605
|
}
|
|
541
606
|
}
|
|
542
607
|
});
|
|
@@ -586,6 +651,7 @@
|
|
|
586
651
|
function createDebugPanel() {
|
|
587
652
|
let container = null;
|
|
588
653
|
let unsub = null;
|
|
654
|
+
let summaryFadeTimer = null;
|
|
589
655
|
|
|
590
656
|
async function runVerify() {
|
|
591
657
|
const ext = store.selected();
|
|
@@ -597,6 +663,8 @@
|
|
|
597
663
|
renderVerify(data);
|
|
598
664
|
} catch (e) {
|
|
599
665
|
renderError(e);
|
|
666
|
+
} finally {
|
|
667
|
+
if (status) status.textContent = t("btn.recheck");
|
|
600
668
|
}
|
|
601
669
|
}
|
|
602
670
|
|
|
@@ -607,16 +675,22 @@
|
|
|
607
675
|
const errs = (data.issues || []).filter((i) => i.level === "error");
|
|
608
676
|
const warns = (data.issues || []).filter((i) => i.level === "warning");
|
|
609
677
|
|
|
610
|
-
const summary =
|
|
611
|
-
if (
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
678
|
+
const summary = container.querySelector(".studio-verify-summary");
|
|
679
|
+
if (summary) {
|
|
680
|
+
clearTimeout(summaryFadeTimer);
|
|
681
|
+
summary.textContent = "";
|
|
682
|
+
if (data.ok && !warns.length) {
|
|
683
|
+
summary.textContent = "✓ " + t("verify.ok");
|
|
684
|
+
summary.className = "studio-verify-summary studio-verify-ok";
|
|
685
|
+
summaryFadeTimer = setTimeout(() => { summary.textContent = ""; }, 3000);
|
|
686
|
+
} else {
|
|
687
|
+
const parts = [];
|
|
688
|
+
if (errs.length) parts.push(t("verify.errors", { n: errs.length }));
|
|
689
|
+
if (warns.length) parts.push(t("verify.warnings", { n: warns.length }));
|
|
690
|
+
summary.textContent = parts.join(" · ");
|
|
691
|
+
summary.className = "studio-verify-summary studio-verify-fail";
|
|
692
|
+
}
|
|
618
693
|
}
|
|
619
|
-
box.appendChild(summary);
|
|
620
694
|
|
|
621
695
|
(data.issues || []).forEach((i) => {
|
|
622
696
|
const item = el("div", { class: "studio-issue studio-issue-" + i.level });
|
|
@@ -630,7 +704,9 @@
|
|
|
630
704
|
|
|
631
705
|
function renderError(e) {
|
|
632
706
|
const box = container.querySelector(".studio-verify");
|
|
633
|
-
|
|
707
|
+
const summary = container.querySelector(".studio-verify-summary");
|
|
708
|
+
if (summary) { summary.textContent = t("err.generic", { msg: e.message }); summary.className = "studio-verify-summary studio-verify-fail"; }
|
|
709
|
+
if (box) { box.innerHTML = ""; }
|
|
634
710
|
}
|
|
635
711
|
|
|
636
712
|
function renderDetail() {
|
|
@@ -647,7 +723,6 @@
|
|
|
647
723
|
const row = (label, value) => { meta.appendChild(el("dt", { text: label })); meta.appendChild(el("dd", { text: value || "—" })); };
|
|
648
724
|
row(t("detail.version"), ext.version);
|
|
649
725
|
row(t("detail.layer"), ext.layer);
|
|
650
|
-
row(t("detail.origin"), ext.origin);
|
|
651
726
|
detail.appendChild(meta);
|
|
652
727
|
|
|
653
728
|
detail.appendChild(el("div", { class: "studio-label", text: t("detail.units") }));
|
|
@@ -674,6 +749,7 @@
|
|
|
674
749
|
|
|
675
750
|
const bar = el("div", { class: "studio-actions" });
|
|
676
751
|
bar.appendChild(el("button", { class: "studio-btn studio-btn-primary studio-verify-status", text: t("btn.recheck"), onclick: runVerify }));
|
|
752
|
+
bar.appendChild(el("span", { class: "studio-verify-summary" }));
|
|
677
753
|
container.appendChild(bar);
|
|
678
754
|
|
|
679
755
|
container.appendChild(el("div", { class: "studio-verify" }));
|
|
@@ -709,24 +785,28 @@
|
|
|
709
785
|
async function doPublish() {
|
|
710
786
|
const ext = store.selected();
|
|
711
787
|
if (!ext) return;
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
const ok = await runPublishFlow(ext,
|
|
788
|
+
const prevVersionPromise = getJson("/published")
|
|
789
|
+
.then((data) => {
|
|
790
|
+
const match = (data.extensions || []).find((e) => e.id === ext.id);
|
|
791
|
+
return match ? match.version : null;
|
|
792
|
+
})
|
|
793
|
+
.catch(() => null);
|
|
794
|
+
const ok = await runPublishFlow(ext, prevVersionPromise);
|
|
719
795
|
if (ok) loadPublished();
|
|
720
796
|
}
|
|
721
797
|
|
|
722
798
|
async function doPack() {
|
|
723
799
|
const ext = store.selected();
|
|
724
800
|
if (!ext) return;
|
|
801
|
+
const btn = container.querySelector(".studio-pack-btn");
|
|
802
|
+
const orig = btn ? btn.textContent : null;
|
|
803
|
+
if (btn) { btn.disabled = true; btn.textContent = t("btn.packing"); }
|
|
725
804
|
try {
|
|
726
|
-
|
|
727
|
-
feedback(t("publish.packed", { path: data.path }), "success");
|
|
805
|
+
await downloadPack(ext.id);
|
|
728
806
|
} catch (e) {
|
|
729
807
|
feedback(t("err.generic", { msg: e.message }), "error");
|
|
808
|
+
} finally {
|
|
809
|
+
if (btn) { btn.disabled = false; btn.textContent = orig; }
|
|
730
810
|
}
|
|
731
811
|
}
|
|
732
812
|
|
|
@@ -740,17 +820,23 @@
|
|
|
740
820
|
const exts = data.extensions || [];
|
|
741
821
|
if (!exts.length) { box.appendChild(el("p", { class: "studio-empty", text: t("published.empty") })); return; }
|
|
742
822
|
exts.forEach((e) => {
|
|
743
|
-
const
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
823
|
+
const card = el("div", { class: "studio-skill-card" });
|
|
824
|
+
const head = el("div", { class: "studio-skill-head" });
|
|
825
|
+
head.appendChild(el("span", { class: "studio-skill-name", text: e.name || e.id }));
|
|
826
|
+
const statusKind = e.status === "draft" ? "local" : "published";
|
|
827
|
+
head.appendChild(el("span", { class: `studio-skill-badge studio-skill-badge-${statusKind}`, text: e.status === "draft" ? t("extlist.badge.draft") : t("extlist.badge.published") }));
|
|
828
|
+
if (e.version) head.appendChild(el("span", { style: "font-size:11px;color:var(--color-text-muted);", text: "v" + e.version }));
|
|
829
|
+
const unpubBtn = el("button", { class: "studio-btn studio-btn-danger", text: t("btn.unpublish") });
|
|
830
|
+
unpubBtn.style.marginLeft = "auto";
|
|
831
|
+
unpubBtn.addEventListener("click", async () => {
|
|
832
|
+
if (!window.confirm(t("published.confirm", { id: e.id }))) return;
|
|
833
|
+
unpubBtn.disabled = true;
|
|
834
|
+
try { await postJson("/unpublish", { ext_id: e.id }); loadPublished(); }
|
|
835
|
+
catch (err) { unpubBtn.disabled = false; feedback(t("err.generic", { msg: err.message }), "error"); }
|
|
836
|
+
});
|
|
837
|
+
head.appendChild(unpubBtn);
|
|
838
|
+
card.appendChild(head);
|
|
839
|
+
box.appendChild(card);
|
|
754
840
|
});
|
|
755
841
|
} catch (e) {
|
|
756
842
|
box.appendChild(el("p", { class: "studio-empty", text: t("err.generic", { msg: e.message }) }));
|
|
@@ -766,7 +852,7 @@
|
|
|
766
852
|
|
|
767
853
|
const bar = el("div", { class: "studio-actions" });
|
|
768
854
|
bar.appendChild(el("button", { class: "studio-btn studio-btn-primary studio-publish-btn", text: t("btn.publish"), onclick: doPublish }));
|
|
769
|
-
bar.appendChild(el("button", { class: "studio-btn studio-btn-ghost", text: t("btn.pack"), onclick: doPack }));
|
|
855
|
+
bar.appendChild(el("button", { class: "studio-btn studio-btn-ghost studio-pack-btn", text: t("btn.pack"), onclick: doPack }));
|
|
770
856
|
container.appendChild(bar);
|
|
771
857
|
|
|
772
858
|
container.appendChild(el("div", { class: "studio-feedback" }));
|
|
@@ -890,17 +976,22 @@
|
|
|
890
976
|
actions.appendChild(pub);
|
|
891
977
|
|
|
892
978
|
actions.appendChild(el("button", { class: "studio-btn", text: t("extlist.btn.iterate"), onclick: () => createExtension(t("extlist.iterate.seed", { id: ext.id })) }));
|
|
893
|
-
|
|
979
|
+
const packBtn = el("button", { class: "studio-btn studio-btn-ghost", text: t("extlist.btn.pack") });
|
|
980
|
+
packBtn.addEventListener("click", () => doPack(ext, packBtn));
|
|
981
|
+
actions.appendChild(packBtn);
|
|
894
982
|
card.appendChild(actions);
|
|
895
983
|
return card;
|
|
896
984
|
}
|
|
897
985
|
|
|
898
|
-
async function doPack(ext) {
|
|
986
|
+
async function doPack(ext, btn) {
|
|
987
|
+
const orig = btn ? btn.textContent : null;
|
|
988
|
+
if (btn) { btn.disabled = true; btn.textContent = t("extlist.btn.packing"); }
|
|
899
989
|
try {
|
|
900
|
-
|
|
901
|
-
alert(t("extlist.packed", { path: data.path }));
|
|
990
|
+
await downloadPack(ext.id);
|
|
902
991
|
} catch (e) {
|
|
903
992
|
alert(t("err.generic", { msg: e.message }));
|
|
993
|
+
} finally {
|
|
994
|
+
if (btn) { btn.disabled = false; btn.textContent = orig; }
|
|
904
995
|
}
|
|
905
996
|
}
|
|
906
997
|
|
|
@@ -1210,6 +1301,8 @@
|
|
|
1210
1301
|
.studio-page .studio-panel { padding: 0; }
|
|
1211
1302
|
.studio-field { margin-bottom: 14px; }
|
|
1212
1303
|
.studio-label { display: block; font-size: 12px; font-weight: 600; color: var(--color-text-tertiary); margin-bottom: 6px; }
|
|
1304
|
+
.studio-field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
|
|
1305
|
+
.studio-label { font-size: 12px; font-weight: 500; color: var(--color-text-secondary); }
|
|
1213
1306
|
.studio-select, .studio-textarea { width: 100%; box-sizing: border-box; background: var(--color-bg-input); border: 1px solid var(--color-border-primary); border-radius: var(--radius-sm); padding: 7px 8px; color: var(--color-text-primary); font-size: 13px; font-family: inherit; }
|
|
1214
1307
|
.studio-select:focus, .studio-textarea:focus { border-color: var(--color-accent-primary); outline: none; }
|
|
1215
1308
|
.studio-empty { color: var(--color-text-muted); font-size: 12px; margin: 4px 0; }
|
|
@@ -1222,7 +1315,7 @@
|
|
|
1222
1315
|
.studio-meta dd { margin: 0; color: var(--color-text-primary); }
|
|
1223
1316
|
.studio-units { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
1224
1317
|
.studio-unit-chip { display: inline-flex; background: var(--color-bg-hover); color: var(--color-text-primary); border: 1px solid var(--color-border-primary); border-radius: var(--radius-sm); padding: 2px 8px; font-size: 12px; }
|
|
1225
|
-
.studio-actions { display: flex; gap: 8px; margin: 12px 0; }
|
|
1318
|
+
.studio-actions { display: flex; align-items: center; gap: 8px; margin: 12px 0; }
|
|
1226
1319
|
.studio-skill-promo .studio-actions { margin: 12px 0 0; }
|
|
1227
1320
|
.studio-btn { padding: 7px 14px; border-radius: var(--radius-sm); border: 1px solid var(--color-border-primary); background: transparent; color: var(--color-text-secondary); cursor: pointer; font-size: 13px; font-weight: 500; }
|
|
1228
1321
|
.studio-btn:hover { background: var(--color-bg-hover); color: var(--color-text-primary); }
|
|
@@ -1231,11 +1324,13 @@
|
|
|
1231
1324
|
.studio-btn-primary:disabled { opacity: 0.6; cursor: default; }
|
|
1232
1325
|
.studio-btn-danger { color: var(--color-error); border-color: var(--color-error-border); padding: 4px 10px; }
|
|
1233
1326
|
.studio-btn-danger:hover { background: var(--color-error-bg); color: var(--color-error); }
|
|
1234
|
-
.studio-btn-ghost {
|
|
1235
|
-
.studio-btn-ghost:hover { background: var(--color-bg-hover); color: var(--color-text-
|
|
1327
|
+
.studio-btn-ghost { color: var(--color-text-secondary); font-weight: 500; }
|
|
1328
|
+
.studio-btn-ghost:hover { background: var(--color-bg-hover); color: var(--color-text-primary); }
|
|
1236
1329
|
.studio-verify { margin-top: 8px; }
|
|
1237
|
-
.studio-verify-
|
|
1238
|
-
.studio-verify-
|
|
1330
|
+
.studio-verify-summary { font-size: 13px; font-weight: 600; }
|
|
1331
|
+
.studio-verify-ok { color: var(--color-success); font-size: 12px; font-weight: 600; }
|
|
1332
|
+
.studio-verify-fail { color: var(--color-error); font-size: 12px; font-weight: 600; }
|
|
1333
|
+
.studio-verify > *:first-child { margin-bottom: 8px; }
|
|
1239
1334
|
.studio-issue { border-left: 3px solid var(--color-border-primary); padding: 6px 10px; margin-bottom: 8px; background: var(--color-bg-secondary); border-radius: 0 var(--radius-sm) var(--radius-sm) 0; }
|
|
1240
1335
|
.studio-issue-error { border-left-color: var(--color-error); }
|
|
1241
1336
|
.studio-issue-warning { border-left-color: var(--color-warning, var(--color-text-tertiary)); }
|
|
@@ -1244,13 +1339,13 @@
|
|
|
1244
1339
|
.studio-issue-file { font-size: 11px; font-family: monospace; color: var(--color-text-muted); margin-top: 2px; }
|
|
1245
1340
|
.studio-issue-hint { font-size: 11px; color: var(--color-text-secondary); margin-top: 4px; font-style: italic; }
|
|
1246
1341
|
.studio-check { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--color-text-secondary); margin-bottom: 12px; cursor: pointer; }
|
|
1247
|
-
.studio-feedback { font-size: 12px; margin:
|
|
1342
|
+
.studio-feedback { font-size: 12px; margin: 6px 0 0; line-height: 1.4; }
|
|
1248
1343
|
.studio-feedback-success { color: var(--color-success); }
|
|
1249
1344
|
.studio-feedback-error { color: var(--color-error); }
|
|
1250
1345
|
.studio-feedback-warn { color: var(--color-warning, var(--color-text-secondary)); }
|
|
1251
1346
|
.studio-published { margin-top: 16px; border-top: 1px solid var(--color-border-primary); padding-top: 12px; }
|
|
1252
|
-
.studio-published-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding:
|
|
1253
|
-
.studio-published-name { color: var(--color-text-primary); }
|
|
1347
|
+
.studio-published-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 8px 0; font-size: 12px; border-bottom: 1px solid var(--color-border-primary); }
|
|
1348
|
+
.studio-published-name { display: flex; align-items: center; gap: 6px; color: var(--color-text-primary); }
|
|
1254
1349
|
.studio-page { width: 100%; }
|
|
1255
1350
|
.studio-page-head { margin-bottom: 20px; }
|
|
1256
1351
|
.studio-page-title { margin: 0; font-size: 22px; font-weight: 600; color: var(--color-text-primary); }
|
|
@@ -1263,7 +1358,7 @@
|
|
|
1263
1358
|
.studio-skill-section { margin-bottom: 28px; }
|
|
1264
1359
|
.studio-skill-section-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 12px; }
|
|
1265
1360
|
.studio-skill-hint { font-size: 11px; color: var(--color-text-muted); }
|
|
1266
|
-
.studio-skill-card { border: 1px solid var(--color-border-primary); border-radius: var(--radius-md, 8px); padding:
|
|
1361
|
+
.studio-skill-card { border: 1px solid var(--color-border-primary); border-radius: var(--radius-md, 8px); padding: 12px; margin: 0 0 10px; background: var(--color-bg-secondary); }
|
|
1267
1362
|
.studio-skill-head { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
|
|
1268
1363
|
.studio-skill-name { font-size: 14px; font-weight: 600; color: var(--color-text-primary); }
|
|
1269
1364
|
.studio-skill-badge { font-size: 11px; padding: 1px 8px; border-radius: 10px; }
|
|
@@ -1272,17 +1367,29 @@
|
|
|
1272
1367
|
.studio-skill-badge-changed { background: var(--color-warning-bg, var(--color-bg-hover)); color: var(--color-warning, var(--color-text-secondary)); }
|
|
1273
1368
|
.studio-skill-badge-shadow { background: var(--color-bg-hover); color: var(--color-text-secondary); }
|
|
1274
1369
|
.studio-skill-desc { margin: 6px 0 8px; font-size: 12px; color: var(--color-text-secondary); line-height: 1.5; }
|
|
1275
|
-
.studio-skill-meta { display: flex; gap: 12px; font-size: 11px; color: var(--color-text-muted);
|
|
1370
|
+
.studio-skill-meta { display: flex; gap: 12px; font-size: 11px; color: var(--color-text-muted); }
|
|
1276
1371
|
.studio-skill-promo { border: 1px solid var(--color-border-primary); border-radius: var(--radius-md, 8px); padding: 16px 18px; margin: 0 0 24px; background: var(--color-bg-secondary); }
|
|
1277
1372
|
.studio-skill-promo-text { margin: 0 0 4px; font-size: 14px; font-weight: 600; color: var(--color-text-primary); }
|
|
1278
|
-
.studio-modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 9999; }
|
|
1279
|
-
.studio-modal { background: var(--color-bg-primary, var(--color-bg-secondary)); border: 1px solid var(--color-border-primary); border-radius: var(--radius-md, 8px); padding:
|
|
1280
|
-
|
|
1373
|
+
.studio-modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 9999; animation: studio-overlay-in 0.15s ease; }
|
|
1374
|
+
.studio-modal { background: var(--color-bg-primary, var(--color-bg-secondary)); border: 1px solid var(--color-border-primary); border-radius: var(--radius-md, 8px); padding: 28px 28px 22px; width: 520px; max-width: calc(100vw - 40px); box-shadow: 0 12px 40px rgba(0,0,0,0.35); animation: studio-modal-in 0.2s cubic-bezier(0.34,1.4,0.64,1); }
|
|
1375
|
+
@keyframes studio-overlay-in { from { opacity: 0; } to { opacity: 1; } }
|
|
1376
|
+
@keyframes studio-modal-in { from { opacity: 0; transform: scale(0.93) translateY(6px); } to { opacity: 1; transform: scale(1) translateY(0); } }
|
|
1377
|
+
.studio-modal-title { margin: 0 0 16px; font-size: 16px; font-weight: 600; color: var(--color-text-primary); }
|
|
1378
|
+
.studio-modal-intro { margin: 0 0 16px; font-size: 13px; color: var(--color-text-secondary); line-height: 1.6; }
|
|
1379
|
+
.studio-pub-meta { display: flex; align-items: center; gap: 10px; background: var(--color-bg-secondary); border: 1px solid var(--color-border-primary); border-radius: var(--radius-sm, 6px); padding: 10px 14px; margin-bottom: 16px; }
|
|
1380
|
+
.studio-pub-version { font-size: 13px; font-weight: 600; color: var(--color-text-primary); font-family: monospace; }
|
|
1381
|
+
.studio-pub-units { font-size: 12px; color: var(--color-text-tertiary); }
|
|
1382
|
+
.studio-pub-units::before { content: "·"; margin-right: 10px; }
|
|
1281
1383
|
.studio-modal-body { font-size: 13px; color: var(--color-text-secondary); line-height: 1.6; }
|
|
1282
1384
|
.studio-modal-status { margin: 0 0 8px; font-size: 13px; color: var(--color-text-secondary); line-height: 1.6; }
|
|
1385
|
+
.studio-modal-status-error { color: var(--color-error); }
|
|
1386
|
+
.studio-input { width: 100%; box-sizing: border-box; background: var(--color-bg-input); border: 1px solid var(--color-border-primary); border-radius: var(--radius-sm); padding: 7px 10px; color: var(--color-text-primary); font-size: 13px; font-family: monospace; }
|
|
1387
|
+
.studio-input:focus { border-color: var(--color-accent-primary); outline: none; }
|
|
1388
|
+
.studio-input-error { border-color: var(--color-error) !important; }
|
|
1283
1389
|
.studio-modal-code { margin: 0 0 8px; font-size: 13px; font-family: monospace; color: var(--color-text-primary); }
|
|
1284
1390
|
.studio-modal-link { display: inline-block; font-size: 13px; color: var(--color-accent-primary); }
|
|
1285
|
-
.studio-modal-footer { display: flex; justify-content: flex-end; gap: 8px; margin-top:
|
|
1391
|
+
.studio-modal-footer { display: flex; justify-content: flex-end; gap: 8px; margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--color-border-primary); }
|
|
1392
|
+
.studio-pub-success { margin-right: auto; font-size: 13px; font-weight: 500; color: var(--color-success); }
|
|
1286
1393
|
`;
|
|
1287
1394
|
document.head.appendChild(style);
|
|
1288
1395
|
})();
|