openclacky 1.3.6 → 1.3.8

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.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +54 -0
  3. data/lib/clacky/agent/hook_manager.rb +10 -1
  4. data/lib/clacky/agent/system_prompt_builder.rb +1 -1
  5. data/lib/clacky/agent.rb +17 -3
  6. data/lib/clacky/agent_profile.rb +124 -53
  7. data/lib/clacky/billing/billing_store.rb +3 -2
  8. data/lib/clacky/brand_config.rb +309 -6
  9. data/lib/clacky/cli.rb +4 -57
  10. data/lib/clacky/default_extensions/coding/agents/coding/avatar.png +0 -0
  11. data/lib/clacky/default_extensions/coding/ext.yml +19 -0
  12. data/lib/clacky/default_extensions/ext-studio/agents/ext-developer/avatar.png +0 -0
  13. data/lib/clacky/default_extensions/ext-studio/agents/ext-developer/system_prompt.md +95 -0
  14. data/lib/clacky/default_extensions/ext-studio/api/handler.rb +254 -0
  15. data/lib/clacky/default_extensions/ext-studio/ext.yml +33 -0
  16. data/lib/clacky/default_extensions/ext-studio/panels/studio/view.js +1288 -0
  17. data/lib/clacky/default_extensions/ext-studio/skills/ext-debug/SKILL.md +71 -0
  18. data/lib/clacky/default_extensions/ext-studio/skills/ext-publish/SKILL.md +74 -0
  19. data/lib/clacky/default_extensions/ext-studio/skills/ext-scaffold/SKILL.md +65 -0
  20. data/lib/clacky/default_extensions/general/agents/general/avatar.png +0 -0
  21. data/lib/clacky/default_extensions/general/ext.yml +19 -0
  22. data/lib/clacky/default_extensions/git/ext.yml +12 -0
  23. data/lib/clacky/{default_agents/_panels/git/panel.js → default_extensions/git/panels/git/view.js} +4 -5
  24. data/lib/clacky/default_extensions/meeting/{handler.rb → api/handler.rb} +78 -26
  25. data/lib/clacky/default_extensions/meeting/ext.yml +15 -0
  26. data/lib/clacky/default_extensions/meeting/{meeting.js → panels/meeting/view.js} +266 -90
  27. data/lib/clacky/default_extensions/meeting/skills/meeting-summarizer/SKILL.md +2 -1
  28. data/lib/clacky/default_extensions/time_machine/ext.yml +12 -0
  29. data/lib/clacky/{default_agents/_panels/time_machine/panel.js → default_extensions/time_machine/panels/time_machine/view.js} +58 -47
  30. data/lib/clacky/default_skills/channel-manager/discord_setup.rb +1 -1
  31. data/lib/clacky/default_skills/extend-openclacky/SKILL.md +83 -18
  32. data/lib/clacky/{api_extension.rb → extension/api_extension.rb} +27 -11
  33. data/lib/clacky/extension/api_loader.rb +136 -0
  34. data/lib/clacky/extension/cli_commands.rb +226 -0
  35. data/lib/clacky/{server/api_extension_dispatcher.rb → extension/dispatcher.rb} +22 -9
  36. data/lib/clacky/extension/hook_loader.rb +77 -0
  37. data/lib/clacky/extension/loader.rb +489 -0
  38. data/lib/clacky/extension/packager.rb +226 -0
  39. data/lib/clacky/{patch_loader.rb → extension/patch_loader.rb} +45 -0
  40. data/lib/clacky/extension/scaffold/template_renderer.rb +64 -0
  41. data/lib/clacky/extension/scaffold/templates/full/README.md.erb +48 -0
  42. data/lib/clacky/extension/scaffold/templates/full/agents/designer.md +5 -0
  43. data/lib/clacky/extension/scaffold/templates/full/api/handler.rb.erb +16 -0
  44. data/lib/clacky/extension/scaffold/templates/full/channels/noop.rb.erb +40 -0
  45. data/lib/clacky/extension/scaffold/templates/full/ext.yml.erb +41 -0
  46. data/lib/clacky/extension/scaffold/templates/full/hooks/audit.rb +9 -0
  47. data/lib/clacky/extension/scaffold/templates/full/panels/dashboard/view.js.erb +194 -0
  48. data/lib/clacky/extension/scaffold/templates/full/patches/audit.rb +15 -0
  49. data/lib/clacky/extension/scaffold/templates/full/skills/__slug__-skill/SKILL.md.erb +8 -0
  50. data/lib/clacky/extension/scaffold/templates/hello/api/handler.rb.erb +9 -0
  51. data/lib/clacky/extension/scaffold/templates/hello/ext.yml.erb +12 -0
  52. data/lib/clacky/extension/scaffold/templates/hello/panels/hello/view.js.erb +31 -0
  53. data/lib/clacky/extension/scaffold.rb +55 -0
  54. data/lib/clacky/extension/verifier.rb +196 -0
  55. data/lib/clacky/identity.rb +70 -0
  56. data/lib/clacky/locales/en.rb +19 -1
  57. data/lib/clacky/locales/zh.rb +19 -1
  58. data/lib/clacky/platform_http_client.rb +18 -15
  59. data/lib/clacky/server/channel/extension_adapter_loader.rb +32 -0
  60. data/lib/clacky/server/channel.rb +6 -0
  61. data/lib/clacky/server/http_server.rb +439 -146
  62. data/lib/clacky/server/session_registry.rb +9 -2
  63. data/lib/clacky/shell_hook_loader.rb +266 -22
  64. data/lib/clacky/skill_loader.rb +28 -1
  65. data/lib/clacky/ui2/components/welcome_banner.rb +1 -1
  66. data/lib/clacky/ui2/ui_controller.rb +9 -6
  67. data/lib/clacky/utils/workspace_rules.rb +2 -2
  68. data/lib/clacky/version.rb +1 -1
  69. data/lib/clacky/web/app.css +1034 -675
  70. data/lib/clacky/web/app.js +63 -15
  71. data/lib/clacky/web/auth.js +2 -0
  72. data/lib/clacky/web/components/notify.js +2 -0
  73. data/lib/clacky/web/components/sidebar.js +1 -5
  74. data/lib/clacky/web/core/ext.js +432 -89
  75. data/lib/clacky/web/features/backup/store.js +1 -0
  76. data/lib/clacky/web/features/backup/view.js +0 -1
  77. data/lib/clacky/web/features/brand/view.js +13 -5
  78. data/lib/clacky/web/features/extensions/store.js +184 -0
  79. data/lib/clacky/web/features/extensions/view.js +390 -0
  80. data/lib/clacky/web/features/new-session/store.js +199 -0
  81. data/lib/clacky/web/features/new-session/view.js +619 -0
  82. data/lib/clacky/web/features/share/view.js +63 -14
  83. data/lib/clacky/web/features/skills/store.js +3 -1
  84. data/lib/clacky/web/features/skills/view.js +1 -1
  85. data/lib/clacky/web/features/tasks/view.js +12 -0
  86. data/lib/clacky/web/features/trash/store.js +24 -6
  87. data/lib/clacky/web/features/trash/view.js +48 -12
  88. data/lib/clacky/web/features/workspace/store.js +2 -0
  89. data/lib/clacky/web/features/workspace/view.js +0 -1
  90. data/lib/clacky/web/i18n.js +152 -106
  91. data/lib/clacky/web/index.html +172 -190
  92. data/lib/clacky/web/sessions.js +67 -272
  93. data/lib/clacky/web/settings.js +1 -1
  94. data/lib/clacky/web/skills.js +88 -48
  95. data/lib/clacky/web/theme.js +5 -0
  96. data/lib/clacky/web/ws-dispatcher.js +12 -2
  97. data/lib/clacky/web/ws.js +2 -0
  98. data/lib/clacky.rb +21 -10
  99. metadata +52 -21
  100. data/benchmark/runner.rb +0 -441
  101. data/lib/clacky/api_extension_loader.rb +0 -168
  102. data/lib/clacky/default_agents/SOUL.md +0 -3
  103. data/lib/clacky/default_agents/USER.md +0 -1
  104. data/lib/clacky/default_agents/coding/profile.yml +0 -5
  105. data/lib/clacky/default_agents/coding/webui/.gitkeep +0 -0
  106. data/lib/clacky/default_agents/general/profile.yml +0 -2
  107. data/lib/clacky/default_extensions/meeting/meta.yml +0 -3
  108. data/lib/clacky/web/features/creator/store.js +0 -81
  109. data/lib/clacky/web/features/creator/view.js +0 -380
  110. /data/lib/clacky/{default_agents → default_extensions/coding/agents}/coding/system_prompt.md +0 -0
  111. /data/lib/clacky/{default_agents → default_extensions/general/agents}/general/system_prompt.md +0 -0
  112. /data/lib/clacky/{default_agents/base_prompt.md → prompts/base.md} +0 -0
@@ -1,81 +0,0 @@
1
- // ── Creator · store — cloud/local skill data + publish network ─────────────
2
- //
3
- // Owns the creator skill lists (cloud / local), loading flag, and the network
4
- // calls (load catalog, publish a skill). It never renders.
5
- //
6
- // Emits store events the view reacts to; mirrors them to the extension bus via
7
- // Clacky.ext.emit.
8
- //
9
- // `Creator` stays the single public facade.
10
- //
11
- // Depends on: Clacky.ext.
12
- // ───────────────────────────────────────────────────────────────────────────
13
-
14
- const CreatorStore = (() => {
15
- let _cloudSkills = [];
16
- let _localSkills = [];
17
- let _loading = false;
18
-
19
- const _listeners = {};
20
-
21
- function _on(event, handler) {
22
- (_listeners[event] ||= []).push(handler);
23
- return () => {
24
- const list = _listeners[event];
25
- const i = list ? list.indexOf(handler) : -1;
26
- if (i >= 0) list.splice(i, 1);
27
- };
28
- }
29
-
30
- function _emit(event, payload) {
31
- (_listeners[event] || []).forEach((h) => h(payload));
32
- if (window.Clacky && Clacky.ext) Clacky.ext.emit(event, payload);
33
- }
34
-
35
- const state = {
36
- get cloudSkills() { return _cloudSkills; },
37
- get localSkills() { return _localSkills; },
38
- get loading() { return _loading; },
39
- };
40
-
41
- const Creator = {
42
- on: _on,
43
- state,
44
-
45
- async load() {
46
- if (_loading) return;
47
- _loading = true;
48
- _emit("creator:loading");
49
- try {
50
- const res = await fetch("/api/creator/skills");
51
- const data = await res.json();
52
- if (!res.ok) throw new Error(data.error || "Load failed");
53
-
54
- _cloudSkills = data.cloud_skills || [];
55
- _localSkills = data.local_skills || [];
56
- _emit("creator:changed", { platformFetchError: data.platform_fetch_error || null });
57
- } catch (e) {
58
- console.error("[Creator] load failed", e);
59
- _emit("creator:error", { message: e.message });
60
- } finally {
61
- _loading = false;
62
- }
63
- },
64
-
65
- /** Publish (or update) a skill. Returns { ok, already_exists, error }. */
66
- async publish(skillName, { force = false } = {}) {
67
- const url = `/api/my-skills/${encodeURIComponent(skillName)}/publish${force ? "?force=true" : ""}`;
68
- const res = await fetch(url, { method: "POST" });
69
- const data = await res.json();
70
- return {
71
- ok: res.ok && !!data.ok,
72
- already_exists: !!data.already_exists,
73
- error: data.error || null,
74
- };
75
- },
76
- };
77
-
78
- return Creator;
79
- })();
80
-
81
- const Creator = CreatorStore;
@@ -1,380 +0,0 @@
1
- // ── Creator · view — Creator Hub rendering + publish UI + DOM wiring ───────
2
- //
3
- // Owns rendering of cloud/local skill cards, the publish progress UI (animation
4
- // + overwrite confirm), the new-skill entry, and sidebar/panel visibility.
5
- // Reads through CreatorStore.state; load/publish go through store actions.
6
- //
7
- // Augments the `Creator` facade with onPanelShow and updateSidebarVisibility.
8
- //
9
- // Depends on: CreatorStore, I18n, Brand, Skills, Modal.
10
- // ───────────────────────────────────────────────────────────────────────────
11
-
12
- const CreatorView = (() => {
13
- let _domWired = false;
14
-
15
- function escapeHtml(s) {
16
- return String(s ?? "")
17
- .replace(/&/g, "&amp;").replace(/</g, "&lt;")
18
- .replace(/>/g, "&gt;").replace(/"/g, "&quot;");
19
- }
20
-
21
- function _t(key) {
22
- return I18n.t ? I18n.t(key) : key;
23
- }
24
-
25
- // ── Loading / error ─────────────────────────────────────────────────────
26
-
27
- function _renderLoading() {
28
- const cloudList = document.getElementById("creator-cloud-list");
29
- const localList = document.getElementById("creator-local-list");
30
- const placeholder = `<div class="creator-loading">${_t("creator.loading")}</div>`;
31
- if (cloudList) cloudList.innerHTML = placeholder;
32
- if (localList) localList.innerHTML = "";
33
- }
34
-
35
- function _renderError(payload) {
36
- const cloudList = document.getElementById("creator-cloud-list");
37
- if (cloudList) cloudList.innerHTML = `<div class="creator-empty creator-error">${escapeHtml(payload.message)}</div>`;
38
- const localList = document.getElementById("creator-local-list");
39
- if (localList) localList.innerHTML = "";
40
- }
41
-
42
- function _render(payload) {
43
- _renderCloudSection();
44
- _renderLocalSection();
45
- _wireNewSkillEntry();
46
-
47
- if (payload && payload.platformFetchError) {
48
- _showNotice(
49
- I18n.lang() === "zh"
50
- ? `平台数据加载失败:${payload.platformFetchError}`
51
- : `Platform data unavailable: ${payload.platformFetchError}`,
52
- "warn"
53
- );
54
- }
55
- }
56
-
57
- function _renderCloudSection() {
58
- const list = document.getElementById("creator-cloud-list");
59
- if (!list) return;
60
-
61
- const cloudSkills = CreatorStore.state.cloudSkills;
62
- if (cloudSkills.length === 0) {
63
- list.innerHTML = `<div class="creator-empty">${_t("creator.cloud.empty")}</div>`;
64
- return;
65
- }
66
-
67
- list.innerHTML = "";
68
- cloudSkills.forEach(skill => list.appendChild(_buildCloudCard(skill)));
69
- }
70
-
71
- function _renderLocalSection() {
72
- const list = document.getElementById("creator-local-list");
73
- if (!list) return;
74
-
75
- const localSkills = CreatorStore.state.localSkills;
76
- if (localSkills.length === 0) {
77
- list.innerHTML = `<div class="creator-empty">${_t("creator.local.empty")}</div>`;
78
- return;
79
- }
80
-
81
- list.innerHTML = "";
82
- localSkills.forEach(skill => list.appendChild(_buildLocalCard(skill)));
83
- }
84
-
85
- // ── Cloud card ───────────────────────────────────────────────────────────
86
-
87
- function _buildCloudCard(skill) {
88
- const card = document.createElement("div");
89
- card.className = "creator-skill-card creator-cloud-card";
90
- card.dataset.name = skill.name;
91
-
92
- const versionHtml = skill.version
93
- ? `<span class="creator-version">v${escapeHtml(skill.version)}</span>`
94
- : "";
95
-
96
- const downloadHtml = typeof skill.download_count === "number"
97
- ? `<span class="creator-dl-count" title="${_t("creator.downloads")}">
98
- <svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
99
- <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
100
- <polyline points="7 10 12 15 17 10"/>
101
- <line x1="12" y1="15" x2="12" y2="3"/>
102
- </svg>
103
- ${skill.download_count}
104
- </span>`
105
- : "";
106
-
107
- const changesHtml = skill.has_local_changes
108
- ? `<span class="creator-changes-badge" title="${_t("creator.hasLocalChanges")}">● ${_t("creator.changed")}</span>`
109
- : "";
110
-
111
- let actionBtnsHtml = "";
112
- if (skill.local_present) {
113
- if (skill.has_local_changes) {
114
- actionBtnsHtml = `
115
- <div class="creator-upload-wrap">
116
- <button class="btn-creator-publish" data-state="">
117
- <svg class="btn-upload-icon" xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
118
- <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
119
- <polyline points="17 8 12 3 7 8"/>
120
- <line x1="12" y1="3" x2="12" y2="15"/>
121
- </svg>
122
- <span class="btn-upload-label">${_t("creator.btn.update")}</span>
123
- </button>
124
- <div class="skill-upload-progress-wrap" style="display:none">
125
- <div class="skill-upload-progress-bar"></div>
126
- </div>
127
- </div>
128
- <button class="btn-creator-iterate" title="${_t("creator.btn.iterate")}">
129
- <svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
130
- <polyline points="23 4 23 10 17 10"/>
131
- <path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>
132
- </svg>
133
- ${_t("creator.btn.iterate")}
134
- </button>`;
135
- } else {
136
- actionBtnsHtml = `
137
- <button class="btn-creator-up-to-date" disabled title="${_t("creator.btn.upToDate")}">
138
- <svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
139
- <polyline points="20 6 9 17 4 12"/>
140
- </svg>
141
- ${_t("creator.btn.upToDate")}
142
- </button>
143
- <button class="btn-creator-iterate" title="${_t("creator.btn.iterate")}">
144
- <svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
145
- <polyline points="23 4 23 10 17 10"/>
146
- <path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>
147
- </svg>
148
- ${_t("creator.btn.iterate")}
149
- </button>`;
150
- }
151
- }
152
-
153
- card.innerHTML = `
154
- <div class="creator-card-main">
155
- <div class="creator-card-info">
156
- <div class="creator-card-title">
157
- <span class="creator-skill-name">${escapeHtml(skill.name)}</span>
158
- <span class="creator-badge creator-badge-published">${_t("creator.badge.published")}</span>
159
- ${changesHtml}
160
- </div>
161
- <div class="creator-card-desc">${escapeHtml(skill.description || "")}</div>
162
- <div class="creator-card-meta">${versionHtml}${downloadHtml}</div>
163
- </div>
164
- <div class="creator-card-actions">${actionBtnsHtml}</div>
165
- </div>`;
166
-
167
- if (skill.local_present && skill.has_local_changes) {
168
- const btn = card.querySelector(".btn-creator-publish");
169
- const wrap = card.querySelector(".skill-upload-progress-wrap");
170
- const bar = card.querySelector(".skill-upload-progress-bar");
171
- btn.addEventListener("click", () => {
172
- if (btn.disabled) return;
173
- _publishSkill(skill.name, btn, wrap, bar, true, card, true);
174
- });
175
- }
176
-
177
- if (skill.local_present) {
178
- const iterBtn = card.querySelector(".btn-creator-iterate");
179
- if (iterBtn) iterBtn.addEventListener("click", () => _iterateSkill(skill.name));
180
- }
181
-
182
- return card;
183
- }
184
-
185
- // ── Local card ─────────────────────────────────────────────────────────
186
-
187
- function _buildLocalCard(skill) {
188
- const card = document.createElement("div");
189
- card.className = "creator-skill-card creator-local-card";
190
- card.dataset.name = skill.name;
191
-
192
- const shadowHtml = skill.shadowing_brand
193
- ? `<span class="creator-shadow-badge" title="${_t("creator.shadow.tooltip")}">⚡ ${_t("creator.shadow.label")}</span>`
194
- : "";
195
-
196
- card.innerHTML = `
197
- <div class="creator-card-main">
198
- <div class="creator-card-info">
199
- <div class="creator-card-title">
200
- <span class="creator-skill-name">${escapeHtml(skill.name)}</span>
201
- <span class="creator-badge creator-badge-local">${_t("creator.badge.unpublished")}</span>
202
- ${shadowHtml}
203
- </div>
204
- <div class="creator-card-desc">${escapeHtml(skill.description || "")}</div>
205
- </div>
206
- <div class="creator-card-actions">
207
- <div class="creator-upload-wrap">
208
- <button class="btn-creator-publish" data-state="">
209
- <svg class="btn-upload-icon" xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
210
- <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
211
- <polyline points="17 8 12 3 7 8"/>
212
- <line x1="12" y1="3" x2="12" y2="15"/>
213
- </svg>
214
- <span class="btn-upload-label">${_t("creator.btn.publish")}</span>
215
- </button>
216
- <div class="skill-upload-progress-wrap" style="display:none">
217
- <div class="skill-upload-progress-bar"></div>
218
- </div>
219
- </div>
220
- </div>
221
- </div>`;
222
-
223
- const btn = card.querySelector(".btn-creator-publish");
224
- const wrap = card.querySelector(".skill-upload-progress-wrap");
225
- const bar = card.querySelector(".skill-upload-progress-bar");
226
- btn.addEventListener("click", () => {
227
- if (btn.disabled) return;
228
- _publishSkill(skill.name, btn, wrap, bar, false, card, false);
229
- });
230
-
231
- return card;
232
- }
233
-
234
- // ── Publish UI ───────────────────────────────────────────────────────────
235
-
236
- async function _publishSkill(skillName, publishBtn, progressWrap, progressBar, force, card, isUpdate) {
237
- publishBtn.disabled = true;
238
- publishBtn.dataset.state = "uploading";
239
- const btnLabel = publishBtn.querySelector(".btn-upload-label");
240
- if (btnLabel) btnLabel.textContent = I18n.lang() === "zh" ? "发布中…" : "Publishing…";
241
-
242
- progressWrap.style.display = "block";
243
- progressBar.style.width = "0%";
244
-
245
- let animPct = 0;
246
- const animInterval = setInterval(() => {
247
- const remaining = 85 - animPct;
248
- animPct += Math.max(1, remaining * 0.08);
249
- if (animPct > 85) animPct = 85;
250
- progressBar.style.width = animPct + "%";
251
- }, 150);
252
-
253
- let alreadyExists = false;
254
- let skipFinalReset = false;
255
-
256
- try {
257
- const result = await Creator.publish(skillName, { force });
258
- clearInterval(animInterval);
259
-
260
- if (!result.ok) {
261
- alreadyExists = result.already_exists;
262
- throw new Error(result.error || "Publish failed");
263
- }
264
-
265
- progressBar.style.width = "100%";
266
- progressBar.dataset.state = "success";
267
- publishBtn.dataset.state = "success";
268
- if (btnLabel) btnLabel.textContent = I18n.lang() === "zh" ? "已发布 ✓" : "Published ✓";
269
-
270
- await new Promise(r => setTimeout(r, 1400));
271
- await Creator.load();
272
- skipFinalReset = true;
273
-
274
- } catch (e) {
275
- clearInterval(animInterval);
276
-
277
- progressBar.style.width = "100%";
278
- progressBar.dataset.state = "error";
279
- publishBtn.dataset.state = "error";
280
- if (btnLabel) btnLabel.textContent = I18n.lang() === "zh" ? "发布失败" : "Failed";
281
- console.error("[Creator] publish failed", e);
282
- publishBtn.title = e.message;
283
- await new Promise(r => setTimeout(r, 2000));
284
-
285
- if (alreadyExists && !force) {
286
- skipFinalReset = true;
287
-
288
- publishBtn.disabled = false;
289
- publishBtn.dataset.state = "";
290
- if (btnLabel) btnLabel.textContent = I18n.lang() === "zh" ? "更新" : "Update";
291
- progressWrap.style.display = "none";
292
- progressBar.style.width = "0%";
293
- delete progressBar.dataset.state;
294
-
295
- const confirmed = await Modal.confirm(
296
- I18n.lang() === "zh"
297
- ? `"${skillName}" 已存在于平台。\n\n是否用当前版本覆盖?`
298
- : `"${skillName}" already exists on the platform.\n\nOverwrite with the current version?`
299
- );
300
- if (confirmed) {
301
- _publishSkill(skillName, publishBtn, progressWrap, progressBar, true, card, isUpdate);
302
- }
303
- }
304
- } finally {
305
- if (!skipFinalReset) {
306
- publishBtn.disabled = false;
307
- publishBtn.dataset.state = "";
308
- if (btnLabel) {
309
- btnLabel.textContent = isUpdate
310
- ? (I18n.lang() === "zh" ? "更新" : "Update")
311
- : (I18n.lang() === "zh" ? "发布" : "Publish");
312
- }
313
- progressWrap.style.display = "none";
314
- progressBar.style.width = "0%";
315
- delete progressBar.dataset.state;
316
- }
317
- }
318
- }
319
-
320
- function _iterateSkill(skillName) {
321
- Skills.createInSession(`/skill-creator ${I18n.t("creator.iterate.prompt")}${skillName}`);
322
- }
323
-
324
- function _wireNewSkillEntry() {
325
- const entry = document.getElementById("creator-new-entry");
326
- if (!entry || entry.dataset.wired) return;
327
- entry.dataset.wired = "1";
328
- entry.addEventListener("click", () => Skills.createInSession());
329
- }
330
-
331
- function _showNotice(msg, type = "warn") {
332
- const container = document.getElementById("creator-cloud-list");
333
- if (!container) return;
334
- const old = container.querySelector(".creator-notice");
335
- if (old) old.remove();
336
- const el = document.createElement("div");
337
- el.className = `creator-notice creator-notice-${type}`;
338
- el.textContent = msg;
339
- container.prepend(el);
340
- }
341
-
342
- function _subscribe() {
343
- Creator.on("creator:loading", _renderLoading);
344
- Creator.on("creator:changed", _render);
345
- Creator.on("creator:error", _renderError);
346
- }
347
-
348
- const viewApi = {
349
- /** Show/hide the creator section.
350
- * Hidden for brand consumer users (branded=true, userLicensed=false).
351
- * Visible for creators (userLicensed=true) and unbranded users. */
352
- updateSidebarVisibility() {
353
- const section = document.getElementById("creator-section");
354
- if (!section) return;
355
- const isBrandConsumer = Brand.branded && !Brand.userLicensed;
356
- section.style.display = isBrandConsumer ? "none" : "";
357
- },
358
-
359
- /** Called by Router when the creator panel becomes active. */
360
- onPanelShow() {
361
- if (!_domWired) {
362
- _domWired = true;
363
- _wireNewSkillEntry();
364
- }
365
- const licensed = Brand.userLicensed;
366
- const banner = document.getElementById("creator-promo-banner");
367
- const lock = document.getElementById("creator-cloud-lock");
368
- const list = document.getElementById("creator-cloud-list");
369
- if (banner) banner.style.display = licensed ? "none" : "";
370
- if (lock) lock.style.display = licensed ? "none" : "";
371
- if (list) list.style.display = licensed ? "" : "none";
372
- Creator.load();
373
- },
374
- };
375
-
376
- return { init: _subscribe, api: viewApi };
377
- })();
378
-
379
- Object.assign(Creator, CreatorView.api);
380
- CreatorView.init();