clementine-agent 1.18.171 → 1.18.172
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.
- package/dist/agent/chat-skill-resolver.js +43 -5
- package/dist/cli/dashboard.js +84 -18
- package/package.json +1 -1
|
@@ -58,10 +58,21 @@ import pino from 'pino';
|
|
|
58
58
|
import { searchSkills } from './skill-extractor.js';
|
|
59
59
|
const logger = pino({ name: 'clementine.chat-skill-resolver' });
|
|
60
60
|
// ── Tunables ──────────────────────────────────────────────────────────
|
|
61
|
-
/** Default minimum score
|
|
62
|
-
* legacy `assistant.ts:1492` threshold.
|
|
63
|
-
* this filter keeps weak matches from injecting unrelated tooling. */
|
|
61
|
+
/** Default minimum score for user-authored skill matches. Mirrors the
|
|
62
|
+
* legacy `assistant.ts:1492` threshold. */
|
|
64
63
|
const DEFAULT_MIN_SCORE = 4;
|
|
64
|
+
/** Higher threshold applied when ALL matches are auto-generated MCP-derived
|
|
65
|
+
* skills (no user-authored signal). 1.18.171 hotfix: a vague chat message
|
|
66
|
+
* ("did our changes break it?") matched three unrelated auto-skills
|
|
67
|
+
* (ElevenLabs + apify) at score 5.5 each because semantic-only matching
|
|
68
|
+
* drifted toward whatever embeddings were closest. Bumping the bar for
|
|
69
|
+
* auto-only match-sets keeps that noise out of the system prompt. */
|
|
70
|
+
const AUTO_ONLY_MIN_SCORE = 8;
|
|
71
|
+
/** When ALL matches are auto-generated AND they reference this many or
|
|
72
|
+
* more distinct servers, the cluster is treated as semantic-noise and
|
|
73
|
+
* the injection is skipped entirely. Three different services have no
|
|
74
|
+
* business being "all relevant" to a single user message. */
|
|
75
|
+
const AUTO_ONLY_SERVER_NOISE_THRESHOLD = 3;
|
|
65
76
|
/** Default top-K matches to aggregate. Single-tool requests usually
|
|
66
77
|
* return one strong match; category requests ("salesforce") return
|
|
67
78
|
* several similarly-scored auto-skills. Top-3 covers both. Raising
|
|
@@ -228,9 +239,36 @@ export function resolveSkillsForChat(userMessage, opts = {}) {
|
|
|
228
239
|
logger.debug({ err }, 'chat-skill-resolver: searchSkills failed (non-fatal)');
|
|
229
240
|
return empty;
|
|
230
241
|
}
|
|
231
|
-
|
|
232
|
-
|
|
242
|
+
// 1.18.171 hotfix: detect auto-only match-sets and apply the higher
|
|
243
|
+
// threshold + noise-cluster filter so vague chat messages don't surface
|
|
244
|
+
// unrelated MCP context. See the comment block on AUTO_ONLY_MIN_SCORE.
|
|
245
|
+
const isAutoMatch = (m) => m.name.startsWith('auto-');
|
|
246
|
+
const candidatesAllAuto = candidates.length > 0 && candidates.every(isAutoMatch);
|
|
247
|
+
const effectiveMinScore = candidatesAllAuto
|
|
248
|
+
? Math.max(minScore, AUTO_ONLY_MIN_SCORE)
|
|
249
|
+
: minScore;
|
|
250
|
+
let matches = candidates
|
|
251
|
+
.filter((m) => m.score >= effectiveMinScore)
|
|
233
252
|
.slice(0, limit);
|
|
253
|
+
// Auto-only noise cluster filter: when every survivor is auto AND they
|
|
254
|
+
// collectively reference too many distinct servers (no semantic
|
|
255
|
+
// clustering on a single service), treat as drift and drop.
|
|
256
|
+
if (matches.length >= 2 && matches.every(isAutoMatch)) {
|
|
257
|
+
const seenServers = new Set();
|
|
258
|
+
for (const m of matches) {
|
|
259
|
+
for (const s of extractMcpServersFromMatch(m))
|
|
260
|
+
seenServers.add(s);
|
|
261
|
+
}
|
|
262
|
+
if (seenServers.size >= AUTO_ONLY_SERVER_NOISE_THRESHOLD) {
|
|
263
|
+
logger.info({
|
|
264
|
+
droppedMatches: matches.map(m => ({ name: m.name, score: Number(m.score.toFixed(2)) })),
|
|
265
|
+
distinctServers: [...seenServers],
|
|
266
|
+
reason: 'auto_only_server_cluster_too_wide',
|
|
267
|
+
queryChars,
|
|
268
|
+
}, 'chat-skill-resolver: dropped match-set (semantic noise)');
|
|
269
|
+
matches = [];
|
|
270
|
+
}
|
|
271
|
+
}
|
|
234
272
|
if (matches.length === 0) {
|
|
235
273
|
return {
|
|
236
274
|
...empty,
|
package/dist/cli/dashboard.js
CHANGED
|
@@ -21638,25 +21638,34 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
21638
21638
|
</div>
|
|
21639
21639
|
</div>
|
|
21640
21640
|
<div style="display:flex;align-items:center;gap:8px;flex-shrink:0">
|
|
21641
|
-
<button class="btn-secondary" onclick="openSkillStudio()" style="font-size:13px;padding:8px 14px;border-radius:6px;border:1px solid var(--border);background:var(--bg-secondary);color:var(--text-primary);font-weight:500;cursor:pointer;display:inline-flex;align-items:center;gap:6px" title="Open the
|
|
21641
|
+
<button class="btn-secondary" onclick="openSkillStudio()" style="font-size:13px;padding:8px 14px;border-radius:6px;border:1px solid var(--border);background:var(--bg-secondary);color:var(--text-primary);font-weight:500;cursor:pointer;display:inline-flex;align-items:center;gap:6px" title="Open the natural-language Skill Studio">
|
|
21642
21642
|
Open Studio
|
|
21643
21643
|
</button>
|
|
21644
|
-
<button class="btn-primary" onclick="openCreateSkillModalFromComposer()" style="font-size:13px;padding:8px 14px;border-radius:6px;border:none;background:var(--accent);color:#fff;font-weight:500;cursor:pointer;display:inline-flex;align-items:center;gap:6px" title="Open a
|
|
21645
|
-
|
|
21644
|
+
<button class="btn-primary" onclick="openCreateSkillModalFromComposer()" style="font-size:13px;padding:8px 14px;border-radius:6px;border:none;background:var(--accent);color:#fff;font-weight:500;cursor:pointer;display:inline-flex;align-items:center;gap:6px" title="Open a blank SKILL.md editor">
|
|
21645
|
+
Manual editor
|
|
21646
21646
|
</button>
|
|
21647
21647
|
</div>
|
|
21648
21648
|
</div>
|
|
21649
|
+
<div id="skill-composer-home">
|
|
21649
21650
|
<div id="skill-composer" style="margin:0 0 16px;border:1px solid var(--border);border-radius:8px;background:var(--bg-secondary);padding:14px 16px">
|
|
21650
|
-
<div style="display:
|
|
21651
|
+
<div style="display:flex;align-items:flex-start;justify-content:space-between;gap:14px;margin-bottom:12px">
|
|
21652
|
+
<div style="min-width:0">
|
|
21653
|
+
<div style="font-size:14px;font-weight:600;color:var(--text-primary);margin-bottom:3px">Skill Studio</div>
|
|
21654
|
+
<div style="font-size:12px;color:var(--text-muted);line-height:1.45">Describe reusable work in plain language. Optional starting points only seed the draft; nothing runs until you save or test it.</div>
|
|
21655
|
+
</div>
|
|
21656
|
+
<div style="font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.04em;white-space:nowrap;margin-top:2px">Natural language first</div>
|
|
21657
|
+
</div>
|
|
21658
|
+
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:14px;align-items:start">
|
|
21651
21659
|
<div>
|
|
21652
|
-
<label for="skill-composer-text" style="display:block;font-size:11px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.04em;margin-bottom:6px">
|
|
21653
|
-
<textarea id="skill-composer-text" rows="
|
|
21660
|
+
<label for="skill-composer-text" style="display:block;font-size:11px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.04em;margin-bottom:6px">What should Clementine learn?</label>
|
|
21661
|
+
<textarea id="skill-composer-text" rows="5" oninput="updateSkillComposerDraftState()" placeholder="Find Salesforce contacts I have not touched in 15 days, enrich the accounts with DataForSEO signals, draft cold prospecting emails, then report the drafts back for review." style="width:100%;box-sizing:border-box;padding:10px 12px;border:1px solid var(--border);border-radius:6px;background:var(--bg-primary);color:var(--text-primary);font-size:13px;line-height:1.45;resize:vertical;min-height:112px"></textarea>
|
|
21662
|
+
<div style="margin-top:8px;font-size:11px;color:var(--text-muted);line-height:1.45">Good skills name the repeatable outcome, required tools or data, approval boundaries, and what counts as done.</div>
|
|
21654
21663
|
</div>
|
|
21655
21664
|
<div>
|
|
21656
|
-
<div style="font-size:11px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.04em;margin-bottom:6px">
|
|
21665
|
+
<div style="font-size:11px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.04em;margin-bottom:6px">Optional starting points</div>
|
|
21657
21666
|
<div id="skill-composer-modes" style="display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:4px;margin-bottom:10px">
|
|
21658
21667
|
<button type="button" class="skill-composer-mode" data-kind="outcome" onclick="setSkillComposerMode('outcome')" style="padding:7px 6px;border:1px solid var(--accent);border-radius:6px;background:rgba(255,141,0,0.10);color:var(--accent);font-size:11px;font-weight:600;cursor:pointer">Outcome</button>
|
|
21659
|
-
<button type="button" class="skill-composer-mode" data-kind="tool" onclick="setSkillComposerMode('tool')" style="padding:7px 6px;border:1px solid var(--border);border-radius:6px;background:var(--bg-primary);color:var(--text-secondary);font-size:11px;font-weight:600;cursor:pointer">
|
|
21668
|
+
<button type="button" class="skill-composer-mode" data-kind="tool" onclick="setSkillComposerMode('tool')" style="padding:7px 6px;border:1px solid var(--border);border-radius:6px;background:var(--bg-primary);color:var(--text-secondary);font-size:11px;font-weight:600;cursor:pointer">MCP/API</button>
|
|
21660
21669
|
<button type="button" class="skill-composer-mode" data-kind="cli" onclick="setSkillComposerMode('cli')" style="padding:7px 6px;border:1px solid var(--border);border-radius:6px;background:var(--bg-primary);color:var(--text-secondary);font-size:11px;font-weight:600;cursor:pointer">CLI</button>
|
|
21661
21670
|
<button type="button" class="skill-composer-mode" data-kind="project" onclick="setSkillComposerMode('project')" style="padding:7px 6px;border:1px solid var(--border);border-radius:6px;background:var(--bg-primary);color:var(--text-secondary);font-size:11px;font-weight:600;cursor:pointer">Project</button>
|
|
21662
21671
|
<button type="button" class="skill-composer-mode" data-kind="memory" onclick="setSkillComposerMode('memory')" style="padding:7px 6px;border:1px solid var(--border);border-radius:6px;background:var(--bg-primary);color:var(--text-secondary);font-size:11px;font-weight:600;cursor:pointer">Memory</button>
|
|
@@ -21670,12 +21679,13 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
21670
21679
|
<div id="skill-composer-anchor-summary" style="margin-top:8px;min-height:22px;font-size:11px;color:var(--text-muted);line-height:1.45">No starting point selected.</div>
|
|
21671
21680
|
<div id="skill-composer-preview" style="margin-top:10px;max-height:180px;overflow:auto;border:1px solid var(--border);border-radius:6px;background:var(--bg-primary);padding:10px 12px;font-size:11px;line-height:1.45;color:var(--text-secondary)"></div>
|
|
21672
21681
|
<div style="display:flex;align-items:center;justify-content:flex-end;gap:8px;margin-top:12px">
|
|
21673
|
-
<button type="button" class="btn-secondary" onclick="
|
|
21674
|
-
<button type="button" class="btn-primary" id="skill-composer-draft-btn" onclick="startSkillComposerDraft()" disabled style="font-size:12px;padding:7px 14px;border-radius:6px;border:none;background:var(--accent);color:#fff;font-weight:600;cursor:pointer;opacity:0.55">
|
|
21682
|
+
<button type="button" class="btn-secondary" onclick="startSkillComposerChat()" style="font-size:12px;padding:7px 12px;border-radius:6px;border:1px solid var(--border);background:transparent;color:var(--text-primary);cursor:pointer">Build in chat</button>
|
|
21683
|
+
<button type="button" class="btn-primary" id="skill-composer-draft-btn" onclick="startSkillComposerDraft()" disabled style="font-size:12px;padding:7px 14px;border-radius:6px;border:none;background:var(--accent);color:#fff;font-weight:600;cursor:pointer;opacity:0.55">Review draft</button>
|
|
21675
21684
|
</div>
|
|
21676
21685
|
</div>
|
|
21677
21686
|
</div>
|
|
21678
21687
|
</div>
|
|
21688
|
+
</div>
|
|
21679
21689
|
<div style="display:grid;grid-template-columns:380px 1fr;gap:18px;height:calc(100vh - 360px);min-height:440px">
|
|
21680
21690
|
<div id="skills-list-pane" style="overflow-y:auto;border:1px solid var(--border);border-radius:8px;background:var(--bg-secondary)">
|
|
21681
21691
|
<div style="padding:14px 16px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px">
|
|
@@ -29214,6 +29224,17 @@ function startSkillComposerDraft() {
|
|
|
29214
29224
|
openCreateSkillModalFromComposer({ draft: true });
|
|
29215
29225
|
}
|
|
29216
29226
|
|
|
29227
|
+
function startSkillComposerChat() {
|
|
29228
|
+
var prompt = buildSkillComposerPrompt();
|
|
29229
|
+
if (typeof askClementineWith !== 'function') {
|
|
29230
|
+
toast('Chat is not ready yet. Try again after the dashboard finishes loading.', 'error');
|
|
29231
|
+
return;
|
|
29232
|
+
}
|
|
29233
|
+
closeSkillStudio({ silent: true });
|
|
29234
|
+
askClementineWith(prompt, { autoSend: false });
|
|
29235
|
+
toast('Skill-creator prompt loaded in chat. Press send when you are ready.', 'info');
|
|
29236
|
+
}
|
|
29237
|
+
|
|
29217
29238
|
function openCreateSkillModalFromComposer(opts) {
|
|
29218
29239
|
opts = opts || {};
|
|
29219
29240
|
var text = ((document.getElementById('skill-composer-text') || {}).value || '').trim();
|
|
@@ -29223,6 +29244,7 @@ function openCreateSkillModalFromComposer(opts) {
|
|
|
29223
29244
|
return;
|
|
29224
29245
|
}
|
|
29225
29246
|
var seed = buildSkillComposerDraftSeed();
|
|
29247
|
+
closeSkillStudio({ silent: true });
|
|
29226
29248
|
_openSkillModal({ mode: 'create', prefill: seed });
|
|
29227
29249
|
if (seed.note && typeof toast === 'function') toast(seed.note, 'success');
|
|
29228
29250
|
}
|
|
@@ -30003,7 +30025,7 @@ async function _openSkillModal(opts) {
|
|
|
30003
30025
|
+ '<strong style="color:var(--text-secondary);font-weight:600">Format:</strong> '
|
|
30004
30026
|
+ '<code style="font-size:10px;background:var(--bg-secondary);padding:1px 4px;border-radius:3px">[WHAT it does] + [WHEN to use it] + [trigger phrases]</code>'
|
|
30005
30027
|
+ ' · under 1024 chars · no <code style="font-size:10px">< ></code> · '
|
|
30006
|
-
+ '<a href="javascript:void(0)" onclick="
|
|
30028
|
+
+ '<a href="javascript:void(0)" onclick="askSkillCreatorForDescription()" style="color:var(--accent);text-decoration:none">use skill-creator</a>'
|
|
30007
30029
|
+ '</div>'
|
|
30008
30030
|
+ '<textarea id="skill-modal-desc" rows="2" oninput="updateSkillModalCounters()" placeholder="Example: Analyzes Outlook emails and drafts triage replies. Use when user asks to triage email or mentions inbox cleanup." style="width:100%;padding:8px 10px;font-size:13px;border:1px solid var(--border);border-radius:6px;background:var(--bg-secondary);color:var(--text-primary);margin-bottom:12px;font-family:inherit;resize:vertical"></textarea>'
|
|
30009
30031
|
+ '<label style="display:block;font-size:12px;color:var(--text-secondary);margin-bottom:4px;font-weight:500">Allowed tools <span style="color:var(--text-muted)">(comma-separated, leave blank for default)</span></label>'
|
|
@@ -30023,7 +30045,9 @@ async function _openSkillModal(opts) {
|
|
|
30023
30045
|
+ '</div>';
|
|
30024
30046
|
document.body.appendChild(modal);
|
|
30025
30047
|
}
|
|
30026
|
-
document.getElementById('skill-modal-heading').textContent = opts.mode === 'edit'
|
|
30048
|
+
document.getElementById('skill-modal-heading').textContent = opts.mode === 'edit'
|
|
30049
|
+
? 'Edit skill: ' + nameVal
|
|
30050
|
+
: (prefill.note ? 'Review skill draft' : 'Manual skill editor');
|
|
30027
30051
|
document.getElementById('skill-modal-original-name').value = opts.mode === 'edit' ? nameVal : '';
|
|
30028
30052
|
document.getElementById('skill-modal-name').value = nameVal;
|
|
30029
30053
|
document.getElementById('skill-modal-name').disabled = opts.mode === 'edit';
|
|
@@ -30044,7 +30068,8 @@ async function _openSkillModal(opts) {
|
|
|
30044
30068
|
var errEl = document.getElementById('skill-modal-error');
|
|
30045
30069
|
if (errEl) { errEl.style.display = 'none'; errEl.textContent = ''; }
|
|
30046
30070
|
modal.style.display = 'flex';
|
|
30047
|
-
document.getElementById('skill-modal-
|
|
30071
|
+
var initialFocus = prefill.note ? document.getElementById('skill-modal-desc') : document.getElementById('skill-modal-name');
|
|
30072
|
+
if (initialFocus) initialFocus.focus();
|
|
30048
30073
|
if (typeof updateSkillModalCounters === 'function') updateSkillModalCounters();
|
|
30049
30074
|
if (typeof renderSkillModalToolsPreview === 'function') renderSkillModalToolsPreview();
|
|
30050
30075
|
// 1.18.168 — render the compact optional template seed in create mode only.
|
|
@@ -35396,7 +35421,7 @@ document.addEventListener('click', function(e) {
|
|
|
35396
35421
|
// Back-compat shim — older call sites still reference loadProfiles().
|
|
35397
35422
|
function loadProfiles() { return refreshChatAgentPicker(); }
|
|
35398
35423
|
|
|
35399
|
-
// ── Skill Studio — opens the
|
|
35424
|
+
// ── Skill Studio — opens the natural-language composer as a real modal ──────────
|
|
35400
35425
|
|
|
35401
35426
|
function openSkillStudio() {
|
|
35402
35427
|
navigateTo('skills');
|
|
@@ -35404,15 +35429,56 @@ function openSkillStudio() {
|
|
|
35404
35429
|
try { initSkillComposer(); } catch (_) { /* non-fatal */ }
|
|
35405
35430
|
var composer = document.getElementById('skill-composer');
|
|
35406
35431
|
var input = document.getElementById('skill-composer-text');
|
|
35407
|
-
|
|
35408
|
-
if (
|
|
35409
|
-
|
|
35410
|
-
|
|
35432
|
+
var modal = document.getElementById('skill-studio-modal');
|
|
35433
|
+
if (!modal) {
|
|
35434
|
+
modal = document.createElement('div');
|
|
35435
|
+
modal.id = 'skill-studio-modal';
|
|
35436
|
+
modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.42);z-index:1050;display:none;align-items:center;justify-content:center;padding:20px';
|
|
35437
|
+
modal.innerHTML =
|
|
35438
|
+
'<div style="width:min(1040px,96vw);max-height:92vh;background:var(--bg-primary);border:1px solid var(--border);border-radius:10px;box-shadow:0 18px 56px rgba(0,0,0,0.35);display:flex;flex-direction:column;overflow:hidden">'
|
|
35439
|
+
+ '<div style="display:flex;align-items:flex-start;justify-content:space-between;gap:14px;padding:16px 20px;border-bottom:1px solid var(--border);background:var(--bg-secondary)">'
|
|
35440
|
+
+ '<div style="min-width:0">'
|
|
35441
|
+
+ '<div style="font-size:16px;font-weight:600;color:var(--text-primary);margin-bottom:3px">Skill Studio</div>'
|
|
35442
|
+
+ '<div style="font-size:12px;color:var(--text-muted);line-height:1.45">Start with the outcome. Add MCP, CLI, project, or memory anchors only when they are real dependencies. Review the generated SKILL.md before saving.</div>'
|
|
35443
|
+
+ '</div>'
|
|
35444
|
+
+ '<button type="button" onclick="closeSkillStudio()" title="Close Skill Studio" style="background:none;border:none;font-size:20px;color:var(--text-muted);cursor:pointer;padding:0 4px;line-height:1">×</button>'
|
|
35445
|
+
+ '</div>'
|
|
35446
|
+
+ '<div id="skill-studio-modal-body" style="padding:18px 20px;overflow:auto;flex:1;min-height:0"></div>'
|
|
35447
|
+
+ '<div style="display:flex;align-items:center;gap:8px;justify-content:flex-end;padding:14px 20px;border-top:1px solid var(--border);background:var(--bg-secondary)">'
|
|
35448
|
+
+ '<button type="button" onclick="closeSkillStudio()" style="font-size:13px;padding:7px 14px;border-radius:6px;border:1px solid var(--border);background:transparent;color:var(--text-primary);cursor:pointer">Keep on page</button>'
|
|
35449
|
+
+ '<button type="button" onclick="startSkillComposerChat()" style="font-size:13px;padding:7px 14px;border-radius:6px;border:1px solid var(--border);background:var(--bg-primary);color:var(--text-primary);cursor:pointer">Build in chat</button>'
|
|
35450
|
+
+ '<button type="button" onclick="startSkillComposerDraft()" class="btn-primary" style="font-size:13px;padding:7px 16px;border-radius:6px;border:none;background:var(--accent);color:#fff;font-weight:600;cursor:pointer">Review draft</button>'
|
|
35451
|
+
+ '</div>'
|
|
35452
|
+
+ '</div>';
|
|
35453
|
+
document.body.appendChild(modal);
|
|
35411
35454
|
}
|
|
35455
|
+
var body = document.getElementById('skill-studio-modal-body');
|
|
35456
|
+
if (composer && body && composer.parentElement !== body) {
|
|
35457
|
+
composer.dataset.originalMargin = composer.style.margin || '';
|
|
35458
|
+
body.appendChild(composer);
|
|
35459
|
+
composer.style.margin = '0';
|
|
35460
|
+
}
|
|
35461
|
+
modal.style.display = 'flex';
|
|
35462
|
+
updateSkillComposerDraftState();
|
|
35412
35463
|
if (input) input.focus();
|
|
35413
35464
|
}, 80);
|
|
35414
35465
|
}
|
|
35415
35466
|
|
|
35467
|
+
function closeSkillStudio(opts) {
|
|
35468
|
+
opts = opts || {};
|
|
35469
|
+
var modal = document.getElementById('skill-studio-modal');
|
|
35470
|
+
var composer = document.getElementById('skill-composer');
|
|
35471
|
+
var home = document.getElementById('skill-composer-home');
|
|
35472
|
+
if (composer && home && composer.parentElement !== home) {
|
|
35473
|
+
home.appendChild(composer);
|
|
35474
|
+
composer.style.margin = composer.dataset.originalMargin || '0 0 16px';
|
|
35475
|
+
}
|
|
35476
|
+
if (modal) modal.style.display = 'none';
|
|
35477
|
+
if (!opts.silent && composer && composer.scrollIntoView) {
|
|
35478
|
+
composer.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
35479
|
+
}
|
|
35480
|
+
}
|
|
35481
|
+
|
|
35416
35482
|
function updateBuilderMode() {
|
|
35417
35483
|
var type = (document.getElementById('builder-type') || {}).value || 'skill';
|
|
35418
35484
|
var title = document.getElementById('builder-page-title');
|