@vpxa/kb 0.1.32 → 0.1.34
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/package.json +1 -1
- package/packages/analyzers/dist/diagram-generator.js +2 -2
- package/packages/analyzers/dist/pattern-analyzer.js +1 -1
- package/packages/analyzers/dist/regex-call-graph.js +1 -1
- package/packages/cli/dist/commands/init/constants.d.ts +2 -0
- package/packages/cli/dist/commands/init/constants.js +1 -1
- package/packages/present/dist/index.html +321 -22
- package/packages/server/dist/server.js +1 -1
- package/packages/server/dist/tools/onboard.tool.js +2 -2
- package/packages/server/dist/tools/present/browser.d.ts +4 -0
- package/packages/server/dist/tools/present/browser.js +93 -0
- package/packages/server/dist/tools/present/helpers.d.ts +18 -0
- package/packages/server/dist/tools/present/helpers.js +1 -0
- package/packages/server/dist/tools/present/html.d.ts +18 -0
- package/packages/server/dist/tools/present/html.js +5 -0
- package/packages/server/dist/tools/present/index.d.ts +2 -0
- package/packages/server/dist/tools/present/index.js +1 -0
- package/packages/server/dist/tools/present/markdown.d.ts +17 -0
- package/packages/server/dist/tools/present/markdown.js +8 -0
- package/packages/server/dist/tools/present/templates.d.ts +14 -0
- package/packages/server/dist/tools/present/templates.js +472 -0
- package/packages/server/dist/tools/present/tool.d.ts +25 -0
- package/packages/server/dist/tools/present/tool.js +19 -0
- package/packages/server/dist/tools/present.tool.d.ts +1 -6
- package/packages/server/dist/tools/present.tool.js +1 -114
- package/packages/tools/dist/compact.js +1 -1
- package/packages/tools/dist/file-cache.js +3 -3
- package/packages/tools/dist/file-summary.js +1 -1
- package/packages/tools/dist/onboard.d.ts +7 -0
- package/packages/tools/dist/onboard.js +4 -4
- package/skills/present/SKILL.md +283 -12
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
import{escHtml as e}from"../present-utils.js";function t(t,f){let p=typeof f==`string`?JSON.parse(f):f;switch(t){case`list-sort`:return n(p);case`data-table`:return r(p);case`picker`:return i(p);case`flame-graph`:return a(p);case`form`:return o(p);case`timeline`:return s(p);case`kanban`:return c(p);case`tree`:return l(p);case`diff-view`:return u(p);case`dashboard`:return d(p);default:return`<pre>${e(JSON.stringify(p,null,2))}</pre>`}}function n(t){let n=t.items??[];return`
|
|
2
|
+
<style>
|
|
3
|
+
.sort-list{list-style:none;padding:0;margin:0}
|
|
4
|
+
.sort-item{display:flex;align-items:center;gap:10px;padding:12px 16px;margin:4px 0;
|
|
5
|
+
background:var(--kb-surface);border:1px solid var(--kb-border);border-radius:8px;
|
|
6
|
+
cursor:grab;transition:all .15s;user-select:none}
|
|
7
|
+
.sort-item:hover{border-color:var(--kb-accent)}
|
|
8
|
+
.sort-item.dragging{opacity:.4;transform:scale(.98)}
|
|
9
|
+
.sort-item.drag-over{border-color:var(--kb-accent);background:rgba(0,120,212,.1);
|
|
10
|
+
box-shadow:0 0 0 2px rgba(0,120,212,.2)}
|
|
11
|
+
.drag-handle{font-size:20px;color:var(--kb-muted);cursor:grab}
|
|
12
|
+
.sort-label{flex:1;color:var(--kb-text);font-family:'SF Mono',Consolas,monospace;font-size:13px}
|
|
13
|
+
.sort-pos{font-size:12px;color:var(--kb-muted);font-variant-numeric:tabular-nums;
|
|
14
|
+
min-width:28px;text-align:right}
|
|
15
|
+
.sort-status{margin-top:12px;padding:10px 14px;background:var(--kb-surface);
|
|
16
|
+
border-radius:6px;font-size:12px;color:var(--kb-muted)}
|
|
17
|
+
</style>
|
|
18
|
+
<ul class="sort-list" id="sortList">${n.map((t,n)=>`
|
|
19
|
+
<li class="sort-item" draggable="true" data-idx="${n}" data-id="${e(t.id)}">
|
|
20
|
+
<span class="drag-handle">⠿</span>
|
|
21
|
+
<span class="sort-label">${e(t.label)}</span>
|
|
22
|
+
<span class="sort-pos">#${n+1}</span>
|
|
23
|
+
</li>`).join(``)}</ul>
|
|
24
|
+
<div class="sort-status" id="sortStatus">Drag items to reorder. ${n.length} items.</div>
|
|
25
|
+
<script>
|
|
26
|
+
(function(){
|
|
27
|
+
const list = document.getElementById('sortList');
|
|
28
|
+
let dragIdx = -1;
|
|
29
|
+
list.addEventListener('dragstart', e => {
|
|
30
|
+
const li = e.target.closest('.sort-item');
|
|
31
|
+
if(!li) return;
|
|
32
|
+
dragIdx = +li.dataset.idx;
|
|
33
|
+
li.classList.add('dragging');
|
|
34
|
+
e.dataTransfer.effectAllowed = 'move';
|
|
35
|
+
});
|
|
36
|
+
list.addEventListener('dragend', e => {
|
|
37
|
+
const li = e.target.closest('.sort-item');
|
|
38
|
+
if(li) li.classList.remove('dragging');
|
|
39
|
+
});
|
|
40
|
+
list.addEventListener('dragover', e => {
|
|
41
|
+
e.preventDefault();
|
|
42
|
+
const li = e.target.closest('.sort-item');
|
|
43
|
+
if(li) li.classList.add('drag-over');
|
|
44
|
+
});
|
|
45
|
+
list.addEventListener('dragleave', e => {
|
|
46
|
+
const li = e.target.closest('.sort-item');
|
|
47
|
+
if(li) li.classList.remove('drag-over');
|
|
48
|
+
});
|
|
49
|
+
list.addEventListener('drop', e => {
|
|
50
|
+
e.preventDefault();
|
|
51
|
+
const li = e.target.closest('.sort-item');
|
|
52
|
+
if(!li) return;
|
|
53
|
+
li.classList.remove('drag-over');
|
|
54
|
+
const dropIdx = +li.dataset.idx;
|
|
55
|
+
if(dragIdx < 0 || dragIdx === dropIdx) return;
|
|
56
|
+
const items = [...list.children];
|
|
57
|
+
const dragged = items[dragIdx];
|
|
58
|
+
if(dropIdx < dragIdx) li.before(dragged);
|
|
59
|
+
else li.after(dragged);
|
|
60
|
+
[...list.children].forEach((c,i) => {
|
|
61
|
+
c.dataset.idx = i;
|
|
62
|
+
c.querySelector('.sort-pos').textContent = '#'+(i+1);
|
|
63
|
+
});
|
|
64
|
+
const order = [...list.children].map(c => c.dataset.id);
|
|
65
|
+
document.getElementById('sortStatus').textContent = 'Reordered: ' + order.join(', ');
|
|
66
|
+
if(window.__kbCallback) {
|
|
67
|
+
fetch(window.__kbCallback, {method:'POST',headers:{'Content-Type':'application/json'},
|
|
68
|
+
body: JSON.stringify({actionId:'reorder',value:JSON.stringify(order)})}).catch(()=>{});
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
})();
|
|
72
|
+
<\/script>`}function r(t){let n=t.columns??[],r=t.rows??[],i=t.stats??[],a=i.length?`<div class="dt-stats">${i.map(t=>`<div class="dt-stat"><div class="dt-stat-value"${t.color?` style="color:${t.color}"`:``}>${e(String(t.value))}</div><div class="dt-stat-label">${e(t.label)}</div></div>`).join(``)}</div>`:``,o=n.map(t=>`<th data-key="${e(t.key)}">${e(t.label)} <span class="sort-icon">⇅</span></th>`).join(``),s=r.map(t=>`<tr>${n.map(n=>`<td>${e(String(t[n.key]??``))}</td>`).join(``)}</tr>`).join(``);return`
|
|
73
|
+
<style>
|
|
74
|
+
.dt-stats{display:flex;gap:20px;padding:14px 0;flex-wrap:wrap}
|
|
75
|
+
.dt-stat{text-align:center;min-width:90px;padding:10px 16px;background:var(--kb-surface);border-radius:8px}
|
|
76
|
+
.dt-stat-value{font-size:24px;font-weight:700;color:var(--kb-accent)}
|
|
77
|
+
.dt-stat-label{font-size:11px;color:var(--kb-muted);text-transform:uppercase;letter-spacing:.5px;margin-top:4px}
|
|
78
|
+
.dt-filter{display:flex;align-items:center;gap:10px;padding:10px 0}
|
|
79
|
+
.dt-search{flex:1;padding:8px 14px;background:var(--kb-surface);border:1px solid var(--kb-border);
|
|
80
|
+
border-radius:8px;color:var(--kb-text);font-size:13px;outline:none}
|
|
81
|
+
.dt-search:focus{border-color:var(--kb-accent)}
|
|
82
|
+
.dt-count{font-size:12px;color:var(--kb-muted);white-space:nowrap}
|
|
83
|
+
.dt-export{padding:6px 14px;background:var(--kb-surface);border:1px solid var(--kb-border);
|
|
84
|
+
border-radius:6px;color:var(--kb-text);cursor:pointer;font-size:12px}
|
|
85
|
+
.dt-export:hover{border-color:var(--kb-accent)}
|
|
86
|
+
.dt-table{width:100%;border-collapse:collapse;font-size:13px;margin-top:4px}
|
|
87
|
+
.dt-table th{text-align:left;padding:10px 14px;border-bottom:2px solid var(--kb-border);
|
|
88
|
+
color:var(--kb-muted);cursor:pointer;user-select:none;white-space:nowrap;font-weight:600}
|
|
89
|
+
.dt-table th:hover{color:var(--kb-text)}
|
|
90
|
+
.dt-table th.sorted-asc .sort-icon::after{content:" ▲"}
|
|
91
|
+
.dt-table th.sorted-desc .sort-icon::after{content:" ▼"}
|
|
92
|
+
.dt-table td{padding:9px 14px;border-bottom:1px solid var(--kb-border);color:var(--kb-text)}
|
|
93
|
+
.dt-table tr:hover td{background:rgba(255,255,255,.03)}
|
|
94
|
+
.sort-icon{margin-left:4px;font-size:10px;opacity:.4}
|
|
95
|
+
</style>
|
|
96
|
+
${a}
|
|
97
|
+
<div class="dt-filter">
|
|
98
|
+
<input class="dt-search" id="dtSearch" placeholder="Search across all columns...">
|
|
99
|
+
<span class="dt-count" id="dtCount">${r.length} rows</span>
|
|
100
|
+
<button class="dt-export" id="dtExport">Export</button>
|
|
101
|
+
</div>
|
|
102
|
+
<table class="dt-table">
|
|
103
|
+
<thead><tr>${o}</tr></thead>
|
|
104
|
+
<tbody id="dtBody">${s}</tbody>
|
|
105
|
+
</table>
|
|
106
|
+
<script>
|
|
107
|
+
(function(){
|
|
108
|
+
const cols = ${JSON.stringify(n)};
|
|
109
|
+
const allRows = ${JSON.stringify(r)};
|
|
110
|
+
let sortKey = '', sortAsc = true;
|
|
111
|
+
const tbody = document.getElementById('dtBody');
|
|
112
|
+
const search = document.getElementById('dtSearch');
|
|
113
|
+
const countEl = document.getElementById('dtCount');
|
|
114
|
+
|
|
115
|
+
function rebuild() {
|
|
116
|
+
const q = search.value.toLowerCase();
|
|
117
|
+
let filtered = allRows.filter(r => !q || cols.some(c => String(r[c.key]||'').toLowerCase().includes(q)));
|
|
118
|
+
if (sortKey) {
|
|
119
|
+
filtered.sort((a,b) => {
|
|
120
|
+
const av = String(a[sortKey]||''), bv = String(b[sortKey]||'');
|
|
121
|
+
const cmp = av.localeCompare(bv, undefined, {numeric:true});
|
|
122
|
+
return sortAsc ? cmp : -cmp;
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
tbody.innerHTML = filtered.map(r => '<tr>' + cols.map(c =>
|
|
126
|
+
'<td>' + String(r[c.key]||'').replace(/</g,'<') + '</td>'
|
|
127
|
+
).join('') + '</tr>').join('');
|
|
128
|
+
countEl.textContent = filtered.length + ' rows';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
search.addEventListener('input', rebuild);
|
|
132
|
+
document.querySelectorAll('.dt-table th').forEach(th => {
|
|
133
|
+
th.addEventListener('click', () => {
|
|
134
|
+
const key = th.dataset.key;
|
|
135
|
+
if (sortKey === key) sortAsc = !sortAsc;
|
|
136
|
+
else { sortKey = key; sortAsc = true; }
|
|
137
|
+
document.querySelectorAll('.dt-table th').forEach(t => t.className='');
|
|
138
|
+
th.className = sortAsc ? 'sorted-asc' : 'sorted-desc';
|
|
139
|
+
rebuild();
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
document.getElementById('dtExport').addEventListener('click', () => {
|
|
143
|
+
const csv = [cols.map(c=>c.label).join(','), ...allRows.map(r => cols.map(c=>JSON.stringify(r[c.key]||'')).join(','))].join('\\n');
|
|
144
|
+
const blob = new Blob([csv], {type:'text/csv'});
|
|
145
|
+
const a = document.createElement('a'); a.href = URL.createObjectURL(blob);
|
|
146
|
+
a.download = 'export.csv'; a.click();
|
|
147
|
+
});
|
|
148
|
+
})();
|
|
149
|
+
<\/script>`}function i(t){let n=t.categories??[],r=t.items??[];return`
|
|
150
|
+
<style>
|
|
151
|
+
.pk-tabs{display:flex;gap:4px;padding:4px;background:var(--kb-surface);border-radius:10px;
|
|
152
|
+
margin-bottom:12px;overflow-x:auto;flex-wrap:wrap}
|
|
153
|
+
.pk-tab{padding:7px 16px;border:none;background:transparent;color:var(--kb-muted);
|
|
154
|
+
border-radius:8px;cursor:pointer;font-size:12px;white-space:nowrap;transition:all .15s}
|
|
155
|
+
.pk-tab:hover{color:var(--kb-text)}
|
|
156
|
+
.pk-tab.active{background:var(--kb-accent);color:#fff}
|
|
157
|
+
.pk-search{width:100%;padding:9px 14px;margin-bottom:10px;background:var(--kb-surface);
|
|
158
|
+
border:1px solid var(--kb-border);border-radius:8px;color:var(--kb-text);font-size:13px;
|
|
159
|
+
outline:none;box-sizing:border-box}
|
|
160
|
+
.pk-search:focus{border-color:var(--kb-accent)}
|
|
161
|
+
.pk-summary{font-size:12px;color:var(--kb-muted);padding:4px 0}
|
|
162
|
+
.pk-list{max-height:360px;overflow-y:auto}
|
|
163
|
+
.pk-item{display:flex;align-items:center;gap:10px;padding:10px 12px;
|
|
164
|
+
border-bottom:1px solid var(--kb-border);transition:background .1s}
|
|
165
|
+
.pk-item:hover{background:rgba(255,255,255,.03)}
|
|
166
|
+
.pk-item input[type=checkbox]{accent-color:var(--kb-accent);width:16px;height:16px}
|
|
167
|
+
.pk-item-label{flex:1;color:var(--kb-text);font-size:13px}
|
|
168
|
+
.pk-item-status{font-size:11px;padding:2px 10px;border-radius:12px;
|
|
169
|
+
background:var(--kb-surface2);color:var(--kb-muted)}
|
|
170
|
+
.pk-item-status.success{background:rgba(78,201,176,.15);color:var(--kb-success)}
|
|
171
|
+
.pk-tags{display:flex;gap:4px}
|
|
172
|
+
.pk-tag{font-size:10px;padding:2px 7px;border-radius:4px;background:var(--kb-surface2);color:var(--kb-muted)}
|
|
173
|
+
.pk-apply{margin-top:12px;width:100%;padding:10px;background:var(--kb-accent);color:#fff;
|
|
174
|
+
border:none;border-radius:8px;cursor:pointer;font-size:14px;font-weight:600}
|
|
175
|
+
.pk-apply:hover{filter:brightness(1.1)}
|
|
176
|
+
</style>
|
|
177
|
+
<div class="pk-tabs" id="pkTabs">${[`<button class="pk-tab active" data-cat="all">All (${r.length})</button>`].concat(n.map(t=>{let n=r.filter(e=>e.category===t.id).length;return`<button class="pk-tab" data-cat="${e(t.id)}">${e(t.label)} (${n})</button>`})).join(``)}</div>
|
|
178
|
+
<input class="pk-search" id="pkSearch" placeholder="Search items...">
|
|
179
|
+
<div class="pk-summary" id="pkSummary">0 selected</div>
|
|
180
|
+
<div class="pk-list" id="pkList">${r.map(t=>{let n=t.status&&[`active`,`enabled`,`on`].includes(t.status.toLowerCase())?`success`:``,r=t.tags?.length?`<div class="pk-tags">${t.tags.map(t=>`<span class="pk-tag">${e(t)}</span>`).join(``)}</div>`:``;return`<div class="pk-item" data-cat="${e(t.category??``)}" data-label="${e(t.label.toLowerCase())}">
|
|
181
|
+
<input type="checkbox" data-id="${e(t.id)}">
|
|
182
|
+
<span class="pk-item-label">${e(t.label)}</span>
|
|
183
|
+
${t.status?`<span class="pk-item-status ${n}">${e(t.status)}</span>`:``}
|
|
184
|
+
${r}
|
|
185
|
+
</div>`}).join(``)}</div>
|
|
186
|
+
<button class="pk-apply" id="pkApply">Apply Selection</button>
|
|
187
|
+
<script>
|
|
188
|
+
(function(){
|
|
189
|
+
let activeCat = 'all', query = '';
|
|
190
|
+
const tabs = document.getElementById('pkTabs');
|
|
191
|
+
const list = document.getElementById('pkList');
|
|
192
|
+
const search = document.getElementById('pkSearch');
|
|
193
|
+
const summary = document.getElementById('pkSummary');
|
|
194
|
+
|
|
195
|
+
function rebuild() {
|
|
196
|
+
tabs.querySelectorAll('.pk-tab').forEach(t => t.classList.toggle('active', t.dataset.cat === activeCat));
|
|
197
|
+
list.querySelectorAll('.pk-item').forEach(item => {
|
|
198
|
+
const catMatch = activeCat === 'all' || item.dataset.cat === activeCat;
|
|
199
|
+
const qMatch = !query || item.dataset.label.includes(query);
|
|
200
|
+
item.style.display = (catMatch && qMatch) ? '' : 'none';
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
tabs.addEventListener('click', e => {
|
|
204
|
+
const tab = e.target.closest('.pk-tab');
|
|
205
|
+
if(tab) { activeCat = tab.dataset.cat; rebuild(); }
|
|
206
|
+
});
|
|
207
|
+
search.addEventListener('input', () => { query = search.value.toLowerCase(); rebuild(); });
|
|
208
|
+
list.addEventListener('change', () => {
|
|
209
|
+
const count = list.querySelectorAll('input:checked').length;
|
|
210
|
+
summary.textContent = count + ' selected';
|
|
211
|
+
});
|
|
212
|
+
document.getElementById('pkApply').addEventListener('click', () => {
|
|
213
|
+
const selected = [...list.querySelectorAll('input:checked')].map(cb => cb.dataset.id);
|
|
214
|
+
if(window.__kbCallback) {
|
|
215
|
+
fetch(window.__kbCallback, {method:'POST',headers:{'Content-Type':'application/json'},
|
|
216
|
+
body:JSON.stringify({actionId:'pick',value:JSON.stringify(selected)})}).catch(()=>{});
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
})();
|
|
220
|
+
<\/script>`}function a(e){let t=e.profile;return t?`
|
|
221
|
+
<style>
|
|
222
|
+
.fg-breadcrumb{display:flex;align-items:center;gap:4px;padding:10px 0;flex-wrap:wrap}
|
|
223
|
+
.fg-crumb{background:var(--kb-surface);border:1px solid var(--kb-border);color:var(--kb-text);
|
|
224
|
+
padding:4px 12px;border-radius:6px;cursor:pointer;font-size:12px}
|
|
225
|
+
.fg-crumb:hover{border-color:var(--kb-accent)}
|
|
226
|
+
.fg-sep{color:var(--kb-muted);font-size:14px}
|
|
227
|
+
.fg-canvas{overflow-y:auto;max-height:450px;padding:4px 0}
|
|
228
|
+
.fg-row{padding:1px 0}
|
|
229
|
+
.fg-bar{height:24px;border-radius:4px;display:flex;align-items:center;padding:0 8px;
|
|
230
|
+
min-width:24px;transition:filter .1s;cursor:pointer;position:relative}
|
|
231
|
+
.fg-bar:hover{filter:brightness(1.15)}
|
|
232
|
+
.fg-bar-label{font-size:11px;color:#000;white-space:nowrap;overflow:hidden;
|
|
233
|
+
text-overflow:ellipsis;font-weight:500}
|
|
234
|
+
.fg-info{font-size:12px;color:var(--kb-muted);padding:10px 0;
|
|
235
|
+
border-top:1px solid var(--kb-border);margin-top:8px}
|
|
236
|
+
</style>
|
|
237
|
+
<div class="fg-breadcrumb" id="fgBreadcrumb"></div>
|
|
238
|
+
<div class="fg-canvas" id="fgCanvas"></div>
|
|
239
|
+
<div class="fg-info" id="fgInfo"></div>
|
|
240
|
+
<script>
|
|
241
|
+
(function(){
|
|
242
|
+
const profile = ${JSON.stringify(t)};
|
|
243
|
+
const colors = ['#e24d28','#e8602a','#edad2a','#f5c842','#fce94f','#c4a000','#e07028','#d45500'];
|
|
244
|
+
let current = profile;
|
|
245
|
+
const history = [profile];
|
|
246
|
+
const bc = document.getElementById('fgBreadcrumb');
|
|
247
|
+
const canvas = document.getElementById('fgCanvas');
|
|
248
|
+
const info = document.getElementById('fgInfo');
|
|
249
|
+
|
|
250
|
+
function renderFlame() {
|
|
251
|
+
bc.innerHTML = '';
|
|
252
|
+
history.forEach((node, i) => {
|
|
253
|
+
if(i > 0) { const sep = document.createElement('span'); sep.className='fg-sep'; sep.textContent='›'; bc.appendChild(sep); }
|
|
254
|
+
const btn = document.createElement('button');
|
|
255
|
+
btn.className = 'fg-crumb';
|
|
256
|
+
btn.textContent = node.name;
|
|
257
|
+
btn.addEventListener('click', () => { history.splice(i+1); current = node; renderFlame(); });
|
|
258
|
+
bc.appendChild(btn);
|
|
259
|
+
});
|
|
260
|
+
canvas.innerHTML = '';
|
|
261
|
+
renderLevel(current, 0, current.total);
|
|
262
|
+
info.textContent = current.name + ' — total: ' + current.total + (current.self != null ? ', self: '+current.self : '');
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function renderLevel(node, depth, rootTotal) {
|
|
266
|
+
const row = document.createElement('div');
|
|
267
|
+
row.className = 'fg-row';
|
|
268
|
+
row.style.paddingLeft = (depth * 4) + 'px';
|
|
269
|
+
const bar = document.createElement('div');
|
|
270
|
+
bar.className = 'fg-bar';
|
|
271
|
+
bar.style.width = Math.max(2, (node.total / rootTotal) * 100) + '%';
|
|
272
|
+
bar.style.background = colors[depth % colors.length];
|
|
273
|
+
bar.title = node.name + ': ' + node.total;
|
|
274
|
+
const lbl = document.createElement('span');
|
|
275
|
+
lbl.className = 'fg-bar-label';
|
|
276
|
+
lbl.textContent = node.name + ' (' + node.total + ')';
|
|
277
|
+
bar.appendChild(lbl);
|
|
278
|
+
bar.addEventListener('mouseenter', () => {
|
|
279
|
+
info.textContent = node.name + ' — total: ' + node.total + (node.self != null ? ', self: '+node.self : '');
|
|
280
|
+
});
|
|
281
|
+
if(node.children && node.children.length) {
|
|
282
|
+
bar.addEventListener('click', () => { current = node; history.push(node); renderFlame(); });
|
|
283
|
+
}
|
|
284
|
+
row.appendChild(bar);
|
|
285
|
+
canvas.appendChild(row);
|
|
286
|
+
if(node.children) node.children.forEach(c => renderLevel(c, depth+1, rootTotal));
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
renderFlame();
|
|
290
|
+
})();
|
|
291
|
+
<\/script>`:`<p>No profile data provided</p>`}function o(t){let n=t.fields??[];return`
|
|
292
|
+
<style>
|
|
293
|
+
.fm-container{display:grid;grid-template-columns:1fr 280px;gap:24px}
|
|
294
|
+
@media(max-width:640px){.fm-container{grid-template-columns:1fr}}
|
|
295
|
+
.fm-fields{display:flex;flex-direction:column;gap:16px}
|
|
296
|
+
.fm-group{display:flex;flex-direction:column;gap:5px}
|
|
297
|
+
.fm-label{font-size:11px;color:var(--kb-muted);font-weight:600;text-transform:uppercase;letter-spacing:.5px}
|
|
298
|
+
.fm-input{padding:9px 14px;background:var(--kb-surface);border:1px solid var(--kb-border);
|
|
299
|
+
border-radius:8px;color:var(--kb-text);font-size:13px;outline:none;font-family:inherit;width:100%;box-sizing:border-box}
|
|
300
|
+
.fm-input:focus{border-color:var(--kb-accent)}
|
|
301
|
+
.fm-textarea{resize:vertical;min-height:80px}
|
|
302
|
+
.fm-multi{display:flex;flex-wrap:wrap;gap:10px;padding:4px 0}
|
|
303
|
+
.fm-check-label{display:flex;align-items:center;gap:4px;font-size:13px;color:var(--kb-text);cursor:pointer}
|
|
304
|
+
.fm-check-label input{accent-color:var(--kb-accent)}
|
|
305
|
+
.fm-submit{margin-top:8px;width:100%;padding:10px;background:var(--kb-accent);color:#fff;
|
|
306
|
+
border:none;border-radius:8px;cursor:pointer;font-size:14px;font-weight:600}
|
|
307
|
+
.fm-submit:hover{filter:brightness(1.1)}
|
|
308
|
+
.fm-preview{background:var(--kb-surface);border:1px solid var(--kb-border);border-radius:10px;
|
|
309
|
+
padding:16px;height:fit-content;position:sticky;top:20px}
|
|
310
|
+
.fm-preview-title{font-size:11px;color:var(--kb-muted);text-transform:uppercase;
|
|
311
|
+
letter-spacing:.5px;margin-bottom:10px}
|
|
312
|
+
.fm-preview-json{font-size:12px;color:var(--kb-text);margin:0;white-space:pre-wrap;
|
|
313
|
+
word-break:break-all;font-family:'SF Mono',Consolas,monospace;line-height:1.5}
|
|
314
|
+
</style>
|
|
315
|
+
<div class="fm-container">
|
|
316
|
+
<div class="fm-fields" id="fmFields">
|
|
317
|
+
${n.map(t=>{let n=t.type??`text`,r;if(n===`select`&&t.options){let n=t.options.map(n=>`<option value="${e(n)}"${n===t.value?` selected`:``}>${e(n)}</option>`).join(``);r=`<select class="fm-input" data-field="${e(t.name)}">${n}</select>`}else r=n===`textarea`?`<textarea class="fm-input fm-textarea" data-field="${e(t.name)}" rows="4">${e(t.value??``)}</textarea>`:n===`multi-select`&&t.options?`<div class="fm-multi">${t.options.map(n=>`<label class="fm-check-label"><input type="checkbox" data-field="${e(t.name)}" value="${e(n)}"${t.value?.includes(n)?` checked`:``}> ${e(n)}</label>`).join(``)}</div>`:`<input class="fm-input" type="${e(n)}" data-field="${e(t.name)}" value="${e(t.value??``)}">`;return`<div class="fm-group"><label class="fm-label">${e(t.label)}</label>${r}</div>`}).join(``)}
|
|
318
|
+
<button class="fm-submit" id="fmSubmit">Submit</button>
|
|
319
|
+
</div>
|
|
320
|
+
<div class="fm-preview">
|
|
321
|
+
<div class="fm-preview-title">Live Preview</div>
|
|
322
|
+
<pre class="fm-preview-json" id="fmPreview">{}</pre>
|
|
323
|
+
</div>
|
|
324
|
+
</div>
|
|
325
|
+
<script>
|
|
326
|
+
(function(){
|
|
327
|
+
const fields = document.getElementById('fmFields');
|
|
328
|
+
const preview = document.getElementById('fmPreview');
|
|
329
|
+
const fieldDefs = ${JSON.stringify(n.map(e=>({name:e.name,type:e.type??`text`})))};
|
|
330
|
+
|
|
331
|
+
function getValues() {
|
|
332
|
+
const vals = {};
|
|
333
|
+
fieldDefs.forEach(f => {
|
|
334
|
+
if(f.type === 'multi-select') {
|
|
335
|
+
vals[f.name] = [...fields.querySelectorAll('input[data-field="'+f.name+'"]:checked')].map(cb => cb.value).join(', ');
|
|
336
|
+
} else {
|
|
337
|
+
const el = fields.querySelector('[data-field="'+f.name+'"]');
|
|
338
|
+
if(el) vals[f.name] = el.value;
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
return vals;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function updatePreview() { preview.textContent = JSON.stringify(getValues(), null, 2); }
|
|
345
|
+
fields.addEventListener('input', updatePreview);
|
|
346
|
+
fields.addEventListener('change', updatePreview);
|
|
347
|
+
updatePreview();
|
|
348
|
+
|
|
349
|
+
document.getElementById('fmSubmit').addEventListener('click', () => {
|
|
350
|
+
if(window.__kbCallback) {
|
|
351
|
+
fetch(window.__kbCallback, {method:'POST',headers:{'Content-Type':'application/json'},
|
|
352
|
+
body:JSON.stringify({actionId:'submit',value:JSON.stringify(getValues())})}).catch(()=>{});
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
})();
|
|
356
|
+
<\/script>`}function s(t){let n=t.events??[],r={complete:`#22c55e`,active:`#3b82f6`,pending:`#9ca3af`,error:`#ef4444`};return`
|
|
357
|
+
<style>
|
|
358
|
+
.tl-wrap{position:relative;padding-left:28px}
|
|
359
|
+
.tl-wrap::before{content:'';position:absolute;top:0;bottom:0;left:10px;width:2px;background:var(--kb-border,#3c3c3c)}
|
|
360
|
+
.tl-event{position:relative;margin-bottom:16px;display:flex;align-items:flex-start;gap:12px}
|
|
361
|
+
.tl-dot{position:absolute;left:-28px;width:22px;height:22px;border-radius:50%;display:flex;align-items:center;
|
|
362
|
+
justify-content:center;font-size:11px;color:#fff;flex-shrink:0}
|
|
363
|
+
.tl-body{flex:1}
|
|
364
|
+
.tl-title{color:var(--kb-text,#e5e7eb);font:600 13px/1.4 var(--kb-font-sans,system-ui,sans-serif)}
|
|
365
|
+
.tl-time{color:var(--kb-muted,#9ca3af);font:400 11px var(--kb-font-sans,system-ui,sans-serif);margin-top:2px}
|
|
366
|
+
.tl-desc{color:var(--kb-muted,#9ca3af);font:400 12px/1.5 var(--kb-font-sans,system-ui,sans-serif);margin-top:4px}
|
|
367
|
+
.tl-cat{display:inline-block;padding:2px 8px;border-radius:999px;background:var(--kb-surface,#252526);
|
|
368
|
+
color:var(--kb-muted,#9ca3af);font:500 10px var(--kb-font-sans,system-ui,sans-serif);margin-top:4px}
|
|
369
|
+
</style>
|
|
370
|
+
<div class="tl-wrap">${n.map(t=>`
|
|
371
|
+
<div class="tl-event">
|
|
372
|
+
<span class="tl-dot" style="background:${r[t.status??`pending`]??r.pending}">${e(t.icon??(t.status===`complete`?`✓`:t.status===`error`?`✗`:`●`))}</span>
|
|
373
|
+
<div class="tl-body">
|
|
374
|
+
<div class="tl-title">${e(t.title)}</div>
|
|
375
|
+
${t.timestamp?`<div class="tl-time">${e(t.timestamp)}</div>`:``}
|
|
376
|
+
${t.description?`<div class="tl-desc">${e(t.description)}</div>`:``}
|
|
377
|
+
${t.category?`<span class="tl-cat">${e(t.category)}</span>`:``}
|
|
378
|
+
</div>
|
|
379
|
+
</div>`).join(``)}</div>`}function c(t){let n=t.columns??[],r=t.cards??[];return`
|
|
380
|
+
<style>
|
|
381
|
+
.kbn-board{display:flex;gap:14px;overflow-x:auto;padding-bottom:4px}
|
|
382
|
+
.kbn-col{min-width:220px;flex:1;border:1px solid var(--kb-border,#3c3c3c);border-radius:8px;background:var(--kb-surface,#252526)}
|
|
383
|
+
.kbn-col-hdr{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid var(--kb-border,#3c3c3c)}
|
|
384
|
+
.kbn-col-dot{width:10px;height:10px;border-radius:50%}
|
|
385
|
+
.kbn-col-title{flex:1;color:var(--kb-text,#e5e7eb);font:600 13px var(--kb-font-sans,system-ui,sans-serif)}
|
|
386
|
+
.kbn-col-count{padding:2px 8px;border-radius:999px;background:var(--kb-surface2,#2d2d30);color:var(--kb-muted,#9ca3af);font:600 11px var(--kb-font-sans,system-ui,sans-serif)}
|
|
387
|
+
.kbn-cards{display:grid;gap:8px;padding:10px}
|
|
388
|
+
.kbn-card{padding:10px;border:1px solid var(--kb-border,#3c3c3c);border-left-width:3px;border-radius:6px;background:var(--kb-surface2,#2d2d30)}
|
|
389
|
+
.kbn-card.priority-high{border-left-color:#ef4444}
|
|
390
|
+
.kbn-card.priority-medium{border-left-color:#f59e0b}
|
|
391
|
+
.kbn-card.priority-low{border-left-color:#9ca3af}
|
|
392
|
+
.kbn-card-title{color:var(--kb-text,#e5e7eb);font:600 13px/1.4 var(--kb-font-sans,system-ui,sans-serif)}
|
|
393
|
+
.kbn-card-desc{color:var(--kb-muted,#9ca3af);font:400 12px/1.4 var(--kb-font-sans,system-ui,sans-serif);margin-top:6px}
|
|
394
|
+
.kbn-tags{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px}
|
|
395
|
+
.kbn-tag{padding:2px 6px;border-radius:999px;background:var(--kb-surface,#252526);color:var(--kb-muted,#9ca3af);font:500 10px var(--kb-font-sans,system-ui,sans-serif)}
|
|
396
|
+
</style>
|
|
397
|
+
<div class="kbn-board">${n.map(t=>{let n=r.filter(e=>e.column===t.id),i=n.map(t=>`
|
|
398
|
+
<div class="kbn-card ${t.priority?`priority-${t.priority}`:``}">
|
|
399
|
+
<div class="kbn-card-title">${e(t.title)}</div>
|
|
400
|
+
${t.description?`<div class="kbn-card-desc">${e(t.description)}</div>`:``}
|
|
401
|
+
${t.tags?.length?`<div class="kbn-tags">${t.tags.map(t=>`<span class="kbn-tag">${e(t)}</span>`).join(``)}</div>`:``}
|
|
402
|
+
</div>`).join(``);return`
|
|
403
|
+
<div class="kbn-col">
|
|
404
|
+
<div class="kbn-col-hdr">
|
|
405
|
+
<span class="kbn-col-dot" style="background:${t.color??`var(--kb-accent,#3b82f6)`}"></span>
|
|
406
|
+
<span class="kbn-col-title">${e(t.label)}</span>
|
|
407
|
+
<span class="kbn-col-count">${n.length}</span>
|
|
408
|
+
</div>
|
|
409
|
+
<div class="kbn-cards">${i}</div>
|
|
410
|
+
</div>`}).join(``)}</div>`}function l(t){let n=Array.isArray(t.root)?t.root:t.root?[t.root]:t.roots??[],r=0;function i(t,n){let a=`tn-${r++}`,o=t.children&&t.children.length>0,s=t.icon?`<span class="tr-icon">${e(t.icon)}</span>`:``,c=o?`<button class="tr-toggle" data-target="${a}">▸</button>`:`<span class="tr-spacer"></span>`,l=t.metadata?Object.entries(t.metadata).map(([t,n])=>`<span class="tr-meta">${e(t)}: ${e(n)}</span>`).join(` `):``,u=`<div class="tr-row">${c}${s}<span class="tr-label">${e(t.label)}</span>${l}</div>`;return o&&(u+=`<div class="tr-children" id="${a}" hidden>${(t.children??[]).map(e=>i(e,n+1)).join(``)}</div>`),`<div class="tr-node">${u}</div>`}return`
|
|
411
|
+
<style>
|
|
412
|
+
.tr-wrap{font:13px var(--kb-font-sans,system-ui,sans-serif)}
|
|
413
|
+
.tr-node{margin-left:16px}
|
|
414
|
+
.tr-row{display:flex;align-items:center;gap:6px;padding:4px 6px;border-radius:4px;color:var(--kb-text,#e5e7eb)}
|
|
415
|
+
.tr-row:hover{background:var(--kb-surface2,#2d2d30)}
|
|
416
|
+
.tr-toggle{border:0;background:0;color:var(--kb-muted,#9ca3af);cursor:pointer;font-size:12px;width:18px;padding:0}
|
|
417
|
+
.tr-toggle.open{transform:rotate(90deg)}
|
|
418
|
+
.tr-spacer{width:18px;display:inline-block}
|
|
419
|
+
.tr-icon{width:18px;text-align:center}
|
|
420
|
+
.tr-label{color:var(--kb-text,#e5e7eb)}
|
|
421
|
+
.tr-meta{color:var(--kb-muted,#9ca3af);font-size:11px;margin-left:8px}
|
|
422
|
+
.tr-children{margin-left:4px;padding-left:10px;border-left:1px dashed var(--kb-border,#3c3c3c)}
|
|
423
|
+
.tr-children[hidden]{display:none}
|
|
424
|
+
</style>
|
|
425
|
+
<div class="tr-wrap">${n.map(e=>i(e,0)).join(``)}</div>
|
|
426
|
+
<script>
|
|
427
|
+
(function(){
|
|
428
|
+
document.querySelectorAll('.tr-toggle').forEach(btn => {
|
|
429
|
+
btn.addEventListener('click', () => {
|
|
430
|
+
const target = document.getElementById(btn.dataset.target);
|
|
431
|
+
if(target) { target.hidden = !target.hidden; btn.classList.toggle('open'); btn.textContent = target.hidden ? '▸' : '▾'; }
|
|
432
|
+
});
|
|
433
|
+
});
|
|
434
|
+
})();
|
|
435
|
+
<\/script>`}function u(t){let n=t.files??[],r=t.stats,i={added:`🟢`,modified:`🟡`,deleted:`🔴`,renamed:`🔵`};return`
|
|
436
|
+
<style>
|
|
437
|
+
.dv-stats{padding:8px 12px;background:var(--kb-surface,#252526);border-radius:6px;color:var(--kb-text,#e5e7eb);font:600 13px var(--kb-font-sans,system-ui,sans-serif);margin-bottom:12px}
|
|
438
|
+
.dv-add{color:#22c55e}.dv-del{color:#ef4444}
|
|
439
|
+
.dv-file{border:1px solid var(--kb-border,#3c3c3c);border-radius:6px;margin-bottom:8px;overflow:hidden}
|
|
440
|
+
.dv-file-hdr{padding:10px 12px;background:var(--kb-surface,#252526);cursor:pointer;color:var(--kb-text,#e5e7eb);font:500 13px var(--kb-font-sans,system-ui,sans-serif)}
|
|
441
|
+
.dv-hunk{border-top:1px solid var(--kb-border,#3c3c3c)}
|
|
442
|
+
.dv-hunk-hdr{padding:4px 12px;background:var(--kb-surface2,#2d2d30);color:var(--kb-muted,#9ca3af);font:400 11px 'SF Mono',Consolas,monospace}
|
|
443
|
+
.dv-line-add,.dv-line-del,.dv-line-ctx{padding:1px 12px;font:400 12px/1.6 'SF Mono',Consolas,monospace}
|
|
444
|
+
.dv-line-add{background:rgba(34,197,94,.1);color:#22c55e}
|
|
445
|
+
.dv-line-del{background:rgba(239,68,68,.1);color:#ef4444}
|
|
446
|
+
.dv-line-ctx{color:var(--kb-muted,#9ca3af)}
|
|
447
|
+
.dv-ln{display:inline-block;width:40px;text-align:right;margin-right:8px;color:var(--kb-muted,#9ca3af);font-size:11px}
|
|
448
|
+
</style>
|
|
449
|
+
${r?`<div class="dv-stats"><span class="dv-add">+${r.additions}</span> <span class="dv-del">-${r.deletions}</span> in ${r.filesChanged} files</div>`:``}
|
|
450
|
+
<div class="dv-wrap">${n.map(t=>{let n=t.hunks.map(t=>{let n=t.changes.map(t=>{let n=t.type===`add`?`dv-line-add`:t.type===`delete`?`dv-line-del`:`dv-line-ctx`,r=t.type===`add`?`+`:t.type===`delete`?`-`:` `;return`<div class="${n}">${t.line==null?``:`<span class="dv-ln">${t.line}</span>`}<code>${r} ${e(t.content)}</code></div>`}).join(``);return`<div class="dv-hunk"><div class="dv-hunk-hdr">${e(t.header)}</div>${n}</div>`}).join(``),r=i[t.status]??`⚪`,a=t.oldPath?` ← ${e(t.oldPath)}`:``;return`
|
|
451
|
+
<details class="dv-file" open>
|
|
452
|
+
<summary class="dv-file-hdr">${r} <strong>${e(t.path)}</strong>${a}
|
|
453
|
+
<span class="dv-add">+${t.additions}</span> <span class="dv-del">-${t.deletions}</span></summary>
|
|
454
|
+
${n}
|
|
455
|
+
</details>`}).join(``)}</div>`}function d(t){let n=t.metrics??[],r={success:`#22c55e`,warning:`#f59e0b`,error:`#ef4444`,info:`#38bdf8`},i={up:`↑`,down:`↓`,neutral:`→`};return`
|
|
456
|
+
<style>
|
|
457
|
+
.db-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:14px}
|
|
458
|
+
.db-card{padding:14px;border:1px solid var(--kb-border,#3c3c3c);border-left-width:4px;border-radius:8px;background:var(--kb-surface,#252526)}
|
|
459
|
+
.db-label{color:var(--kb-muted,#9ca3af);font:600 11px var(--kb-font-sans,system-ui,sans-serif);text-transform:uppercase;letter-spacing:.06em}
|
|
460
|
+
.db-value{color:var(--kb-text,#e5e7eb);font:700 24px var(--kb-font-sans,system-ui,sans-serif);margin-top:6px}
|
|
461
|
+
.db-trend{font:600 12px var(--kb-font-sans,system-ui,sans-serif);margin-left:8px}
|
|
462
|
+
.db-trend.up{color:#22c55e}.db-trend.down{color:#ef4444}.db-trend.neutral{color:#9ca3af}
|
|
463
|
+
.db-progress{height:6px;background:var(--kb-surface2,#2d2d30);border-radius:3px;margin-top:8px;overflow:hidden}
|
|
464
|
+
.db-progress-bar{height:100%;border-radius:3px;transition:width .3s}
|
|
465
|
+
.db-list{margin-top:8px;display:grid;gap:4px}
|
|
466
|
+
.db-list-row{display:flex;justify-content:space-between;padding:4px 0;color:var(--kb-muted,#9ca3af);font:400 12px var(--kb-font-sans,system-ui,sans-serif);border-bottom:1px solid var(--kb-border,#3c3c3c)}
|
|
467
|
+
</style>
|
|
468
|
+
<div class="db-grid">${n.map(t=>{let n=r[t.status??`info`]??r.info,a=t.trend?`<span class="db-trend ${t.trend.direction}">${i[t.trend.direction]??``} ${e(t.trend.value)}</span>`:``,o=`<div class="db-value">${e(String(t.value))}</div>${a}`;return t.type===`progress`&&t.progress!=null&&(o+=`<div class="db-progress"><div class="db-progress-bar" style="width:${Math.min(100,Math.max(0,t.progress))}%;background:${n}"></div></div>`),t.type===`list`&&t.items?.length&&(o+=`<div class="db-list">${t.items.map(t=>`<div class="db-list-row"><span>${e(t.label)}</span><span>${e(t.value)}</span></div>`).join(``)}</div>`),`
|
|
469
|
+
<div class="db-card" style="border-left-color:${n}">
|
|
470
|
+
<div class="db-label">${e(t.label)}</div>
|
|
471
|
+
${o}
|
|
472
|
+
</div>`}).join(``)}</div>`}export{d as buildDashboardHtml,r as buildDataTableHtml,u as buildDiffViewHtml,a as buildFlameGraphHtml,o as buildFormHtml,c as buildKanbanHtml,n as buildListSortHtml,i as buildPickerHtml,t as buildTemplateHtml,s as buildTimelineHtml,l as buildTreeHtml};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { UIResource } from "@mcp-ui/server";
|
|
3
|
+
import { Elicitor } from "../../../../elicitation/dist/index.js";
|
|
4
|
+
|
|
5
|
+
//#region packages/server/src/tools/present/tool.d.ts
|
|
6
|
+
declare function resolvePresentHtml(): string;
|
|
7
|
+
declare function getPresentHtml(): string;
|
|
8
|
+
declare function registerPresentTool(server: McpServer, elicitor?: Elicitor): void;
|
|
9
|
+
declare function formatAsBrowser(title: string | undefined, content: unknown, actions: unknown, elicitor: Elicitor | undefined, template?: string): Promise<{
|
|
10
|
+
content: Array<{
|
|
11
|
+
type: 'text';
|
|
12
|
+
text: string;
|
|
13
|
+
} | UIResource>;
|
|
14
|
+
}>;
|
|
15
|
+
declare function formatAsHtml(title: string | undefined, content: unknown, actions: unknown, template?: string): {
|
|
16
|
+
content: Array<{
|
|
17
|
+
type: 'text';
|
|
18
|
+
text: string;
|
|
19
|
+
} | UIResource>;
|
|
20
|
+
structuredContent: {
|
|
21
|
+
actions: Array<Record<string, unknown>>;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
export { formatAsBrowser, formatAsHtml, getPresentHtml, registerPresentTool, resolvePresentHtml };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import{getToolMeta as e}from"../../tool-metadata.js";import{buildBrowserHtml as t}from"./browser.js";import{buildMarkdown as n}from"./markdown.js";import{readFileSync as r}from"node:fs";import{dirname as i,join as a}from"node:path";import{fileURLToPath as o}from"node:url";import{z as s}from"zod";import{exec as c}from"node:child_process";import{createServer as l}from"node:http";import{createUIResource as u}from"@mcp-ui/server";import{RESOURCE_MIME_TYPE as d,registerAppResource as f,registerAppTool as p}from"@modelcontextprotocol/ext-apps/server";const m=import.meta.dirname??i(o(import.meta.url)),h=`ui://kb/present.html`,g=s.object({type:s.enum([`button`,`select`]).describe(`Action type`),id:s.string().describe(`Unique action identifier`),label:s.string().describe(`Display label`),variant:s.enum([`primary`,`danger`,`default`]).optional().describe(`Button style variant`),options:s.array(s.union([s.string(),s.object({label:s.string(),value:s.string()})])).optional().describe(`Select options (for type=select)`)}),_={format:s.enum([`html`,`browser`]).default(`html`).describe(`Output format.
|
|
2
|
+
- "html" (default): Rich markdown in chat + embedded UIResource for MCP-UI hosts. Actions shown as numbered choices.
|
|
3
|
+
- "browser": Rich markdown in chat + opens beautiful themed dashboard in the browser. When actions are provided, the browser shows interactive buttons and the tool blocks until user clicks, returning their selection.`),title:s.string().optional().describe(`Optional heading`),content:s.any().describe(`Content to present. Accepts: markdown string, array of objects (→ table), { nodes, edges } (→ mermaid graph), typed blocks [{ type, value }], or any JSON (→ tree).`),actions:s.array(g).optional().describe(`Interactive actions (buttons/selects). In html mode, shown as numbered choices. In browser mode, rendered as clickable buttons and the tool blocks until user clicks.`),template:s.enum([`auto`,`list-sort`,`data-table`,`picker`,`flame-graph`,`form`,`timeline`,`kanban`,`tree`,`diff-view`,`dashboard`]).optional().describe(`UI template for interactive display in MCP Apps hosts.
|
|
4
|
+
- auto (default): detect from content shape
|
|
5
|
+
- list-sort: drag-drop reorderable list — content: { items: [{id, label}] }
|
|
6
|
+
- data-table: filterable sortable table — content: { columns: [{key, label}], rows: [{...}], stats?: [{label, value}] }
|
|
7
|
+
- picker: multi-select with categories/search — content: { categories: [{id, label}], items: [{id, label, category?, tags?[]}] }
|
|
8
|
+
- flame-graph: hierarchical zoom visualization — content: { profile: {name, total, children:[]} }
|
|
9
|
+
- form: input fields with live preview — content: { fields: [{name, label, type?, options?[], value?}] }
|
|
10
|
+
- timeline: vertical event timeline — content: { events: [{title, description?, timestamp?, status?}] }
|
|
11
|
+
- kanban: drag-drop board — content: { columns: [{id, label, color?}], cards: [{id, title, column, tags?[], priority?}] }
|
|
12
|
+
- tree: collapsible tree view — content: { root: {label, children?:[...]} | [...] }
|
|
13
|
+
- diff-view: side-by-side diff — content: { files: [{path, status, additions, deletions, hunks:[{header, changes:[{type, content}]}]}] }
|
|
14
|
+
- dashboard: metric cards grid — content: { metrics: [{label, value, trend?, status?, type?}] }`)};let v,y=!1,b=null;process.on(`exit`,()=>{if(b){try{b.close()}catch{}b=null}});function x(){return a(m,`..`,`..`,`..`,`..`,`present`,`dist`,`index.html`)}function S(){if(!v)try{v=r(x(),`utf-8`)}catch{v=``}return v}function C(t,n){let r=e(`present`);y||=(f(t,`KB Present App`,h,{description:`Rich interactive content viewer for KB tools`},async()=>({contents:[{uri:h,mimeType:d,text:S()}]})),!0),p(t,`present`,{title:r.title,description:`Present content to the user in the best format. Two modes:
|
|
15
|
+
- "html" (default): Rich markdown in chat + embedded UIResource. Use for display-only content (tables, charts, reports, status boards) where no user interaction is needed.
|
|
16
|
+
- "browser": Serves a themed dashboard on a local URL. Use ONLY when you need user interaction back (confirmations, selections, form input). The tool blocks until user clicks an action button, then returns their selection.
|
|
17
|
+
FORMAT RULE: If no user interaction is needed → use "html". If you need user input back → use "browser".
|
|
18
|
+
BROWSER WORKFLOW: After calling present with format "browser", you MUST extract the URL from the response and call openBrowserPage({ url }) to open it in VS Code Simple Browser. A system browser fallback also opens automatically, but always call openBrowserPage yourself.`,annotations:r.annotations,inputSchema:_,_meta:{ui:{resourceUri:h}}},async({format:e,title:t,content:r,actions:i,template:a})=>(e??`html`)===`browser`?await w(t,r,i,n,a):T(t,r,i,a))}async function w(e,r,i,a,o){let s=n(e,r),d=t(e,r,i,o),f=u({uri:`ui://kb/present-browser.html`,content:{type:`rawHtml`,htmlString:d},encoding:`text`,adapters:{mcpApps:{enabled:!0}}}),p,m,h=Array.isArray(i)?i:[],g=``,_;try{b&&=(b.close(),null),h.length>0&&(p=new Promise(e=>{m=e}));let e=!1;g=await new Promise((t,n)=>{let r=l((t,n)=>{if(e||(e=!0,_&&clearTimeout(_)),t.method===`POST`&&t.url===`/callback`){let e=``;t.on(`data`,t=>{e+=t.toString()}),t.on(`end`,()=>{n.writeHead(200,{"Content-Type":`application/json`,"Access-Control-Allow-Origin":`*`}),n.end(`{"ok":true}`);try{let t=JSON.parse(e);m?.(t)}catch{}});return}if(t.method===`OPTIONS`){n.writeHead(204,{"Access-Control-Allow-Origin":`*`,"Access-Control-Allow-Methods":`POST`,"Access-Control-Allow-Headers":`Content-Type`}),n.end();return}n.writeHead(200,{"Content-Type":`text/html; charset=utf-8`}),n.end(d)});r.listen(0,`127.0.0.1`,()=>{let i=r.address();if(typeof i==`object`&&i){b=r;let n=`http://127.0.0.1:${i.port}`;_=setTimeout(()=>{if(!e)try{c(process.platform===`win32`?`start "" "${n}"`:process.platform===`darwin`?`open "${n}"`:`xdg-open "${n}"`)}catch{}},8e3),_.unref(),t(n)}else n(Error(`Failed to start present server`))}),setTimeout(()=>{r.close(),b===r&&(b=null)},300*1e3).unref()})}catch{}let v=g?`${s}\n\n---\n🌐 **Dashboard opened in browser:** ${g}\n\n**IMPORTANT:** You MUST now open this URL using the \`openBrowserPage\` tool (Simple Browser). If that tool is unavailable, open the URL in the system browser as a fallback. If neither works, ask the user to open the URL manually.`:s;if(h.length>0&&a?.available&&p)try{let e=await Promise.race([p,new Promise((e,t)=>setTimeout(()=>t(Error(`timeout`)),300*1e3))]);return{content:[{type:`text`,text:`${v}\n\n✅ **Selected:** ${e.actionId} = \`${e.value}\``},f]}}catch{return{content:[{type:`text`,text:`${v}\n\n⚠️ *No selection received (timed out).*`},f]}}return{content:[{type:`text`,text:v},f]}}function T(e,r,i,a){let o=Array.isArray(i)?i:[],s=n(e,r);if(o.length>0){let e=[``];for(let t=0;t<o.length;t++){let n=o[t],r=typeof n.label==`string`?n.label:`Action ${t+1}`;if(n.type===`select`&&Array.isArray(n.options)){let i=n.options.map(e=>typeof e==`string`?e:e.label).join(`, `);e.push(`${t+1}. **${r}** — choose: ${i}`)}else e.push(`${t+1}. **${r}**`)}s+=`\n${e.join(`
|
|
19
|
+
`)}`}let c=u({uri:`ui://kb/present-static.html`,content:{type:`rawHtml`,htmlString:t(e,r,i,a)},encoding:`text`,adapters:{mcpApps:{enabled:!0}}});return{content:[{type:`text`,text:s},c],structuredContent:{actions:o}}}export{w as formatAsBrowser,T as formatAsHtml,S as getPresentHtml,C as registerPresentTool,x as resolvePresentHtml};
|
|
@@ -1,7 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Elicitor } from "../../../elicitation/dist/index.js";
|
|
3
|
-
|
|
4
|
-
//#region packages/server/src/tools/present.tool.d.ts
|
|
5
|
-
declare function registerPresentTool(server: McpServer, elicitor?: Elicitor): void;
|
|
6
|
-
//#endregion
|
|
1
|
+
import { registerPresentTool } from "./present/tool.js";
|
|
7
2
|
export { registerPresentTool };
|