@oml/server 0.16.1 → 0.16.2

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.
@@ -0,0 +1,643 @@
1
+ // Copyright (c) 2026 Modelware. All rights reserved.
2
+ import path from 'path';
3
+ function buildFileTree(relPaths) {
4
+ const root = { name: '', children: [] };
5
+ for (const relPath of relPaths) {
6
+ const parts = relPath.split('/');
7
+ let node = root;
8
+ for (let i = 0; i < parts.length; i++) {
9
+ const part = parts[i];
10
+ if (i === parts.length - 1) {
11
+ node.children.push({ name: part, path: relPath });
12
+ }
13
+ else {
14
+ let child = node.children.find(c => c.name === part && c.children);
15
+ if (!child) {
16
+ child = { name: part, children: [] };
17
+ node.children.push(child);
18
+ }
19
+ node = child;
20
+ }
21
+ }
22
+ }
23
+ sortNodes(root);
24
+ return root.children;
25
+ }
26
+ function sortNodes(node) {
27
+ if (!node.children)
28
+ return;
29
+ node.children.sort((a, b) => {
30
+ const aFolder = !a.path, bFolder = !b.path;
31
+ if (aFolder !== bFolder)
32
+ return aFolder ? -1 : 1;
33
+ return a.name.localeCompare(b.name);
34
+ });
35
+ node.children.forEach(sortNodes);
36
+ }
37
+ export function generateShellIndex(outputDir, htmlPaths, projectTitle, homePath, allHtmlPaths) {
38
+ // Explorer tree: md-derived paths only, excluding home and index
39
+ const homeRelPath = homePath ? path.relative(outputDir, path.resolve(outputDir, homePath)).replace(/\\/g, '/') : undefined;
40
+ const relPaths = htmlPaths
41
+ .map(p => path.relative(outputDir, p).replace(/\\/g, '/'))
42
+ .filter(p => !p.startsWith('..') && p !== 'index.html' && p !== homeRelPath)
43
+ .sort((a, b) => {
44
+ const aDir = a.includes('/'), bDir = b.includes('/');
45
+ if (aDir !== bDir)
46
+ return aDir ? -1 : 1;
47
+ return a.localeCompare(b);
48
+ });
49
+ // Search index: all html paths (includes OML member pages)
50
+ const searchPaths = (allHtmlPaths ?? htmlPaths)
51
+ .map(p => path.relative(outputDir, p).replace(/\\/g, '/'))
52
+ .filter(p => !p.startsWith('..') && p !== 'index.html')
53
+ .sort();
54
+ const searchIndex = searchPaths.map(p => {
55
+ const name = path.basename(p, '.html');
56
+ const dir = path.dirname(p) === '.' ? '' : path.dirname(p).replace(/\\/g, '/');
57
+ return { name, dir, path: p };
58
+ });
59
+ const treeJson = JSON.stringify(buildFileTree(relPaths))
60
+ .replace(/<\/script>/gi, '<\\/script>');
61
+ const searchIndexJson = JSON.stringify(searchIndex)
62
+ .replace(/<\/script>/gi, '<\\/script>');
63
+ const siteTitle = projectTitle ?? 'Home';
64
+ const titleJson = JSON.stringify(siteTitle);
65
+ const homePathJson = homePath ? JSON.stringify(homePath) : 'null';
66
+ const mdPrefixJson = JSON.stringify('');
67
+ return `<!DOCTYPE html>
68
+ <html lang="en">
69
+ <head>
70
+ <meta charset="UTF-8">
71
+ <meta name="viewport" content="width=device-width,initial-scale=1">
72
+ <title>${siteTitle}</title>
73
+ <style>
74
+ *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
75
+ :root{
76
+ --bg:#ffffff;--sidebar-bg:#ffffff;--titlebar-bg:#ffffff;
77
+ --text:#0d0d0d;--text-dim:#676767;--border:#e5e5e5;
78
+ --hover:#ebebeb;--active:#e3e3e3;--accent:#007acc;
79
+ --ai-msg-bg:#ececec;--ai-msg-border:#e0e0e0;
80
+ --input-bg:#ffffff;--scrollbar:#c8c8c8;--shadow:rgba(0,0,0,.1);
81
+ }
82
+ @media(prefers-color-scheme:dark){:root{
83
+ --bg:#212121;--sidebar-bg:#171717;--titlebar-bg:#171717;
84
+ --text:#ececec;--text-dim:#8e8e8e;--border:#2e2e2e;
85
+ --hover:#2a2a2a;--active:#303030;--accent:#2f81f7;
86
+ --ai-msg-bg:#2a2a2a;--ai-msg-border:#3a3a3a;
87
+ --input-bg:#2f2f2f;--scrollbar:#555555;--shadow:rgba(0,0,0,.5);
88
+ }}
89
+ html,body{height:100%;overflow:hidden;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;font-size:13px;background:var(--bg);color:var(--text)}
90
+ #titlebar{height:35px;display:flex;align-items:center;padding:0 8px;background:var(--titlebar-bg);border-bottom:1px solid var(--border);flex-shrink:0;user-select:none;gap:6px}
91
+ #tb-left{display:flex;align-items:center;gap:4px;flex:0 0 auto;min-width:0}
92
+ #tb-home{display:flex;align-items:center;gap:5px;text-decoration:none;color:var(--text-dim);font-size:12px;padding:3px 6px;border-radius:4px;flex-shrink:1;min-width:0;overflow:hidden;cursor:pointer;background:none;border:none;white-space:nowrap}
93
+ #tb-home:hover{color:var(--text);background:var(--hover)}
94
+ #tb-home svg{width:15px;height:15px;flex-shrink:0;color:var(--text-dim)}
95
+ #tb-home:hover svg{color:var(--text)}
96
+ #tb-home-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
97
+ #tb-search-wrap{flex:1;display:flex;justify-content:center;align-items:center;min-width:0;position:relative}
98
+ #tb-search{width:100%;max-width:380px;height:24px;background:var(--hover);border:1px solid var(--border);border-radius:5px;color:var(--text);font-size:12px;font-family:inherit;padding:0 28px 0 28px;outline:none;transition:border-color .12s,box-shadow .12s}
99
+ #tb-search:focus{border-color:var(--accent);box-shadow:0 0 0 2px color-mix(in srgb,var(--accent) 20%,transparent)}
100
+ #tb-search-icon{position:absolute;left:calc(50% - 190px + 8px);width:14px;height:14px;color:var(--text-dim);pointer-events:none}
101
+ #tb-search-clear{position:absolute;right:calc(50% - 190px + 8px);width:16px;height:16px;color:var(--text-dim);cursor:pointer;display:none;background:none;border:none;padding:0;font-size:13px;line-height:1}
102
+ #tb-search-clear:hover{color:var(--text)}
103
+ #search-results{position:absolute;top:calc(100% + 4px);left:50%;transform:translateX(-50%);width:100%;max-width:380px;background:var(--sidebar-bg);border:1px solid var(--border);border-radius:6px;box-shadow:0 4px 16px var(--shadow);z-index:100;max-height:320px;overflow-y:auto;display:none;scrollbar-width:thin;scrollbar-color:var(--scrollbar) transparent}
104
+ #search-results.open{display:block}
105
+ .sr-item{padding:6px 10px;cursor:pointer;font-size:12px;color:var(--text);border-bottom:1px solid var(--border);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
106
+ .sr-item:last-child{border-bottom:none}
107
+ .sr-item:hover,.sr-item.active{background:var(--hover)}
108
+ .sr-item-name{font-weight:500}
109
+ .sr-item-path{color:var(--text-dim);font-size:11px;margin-top:1px;overflow:hidden;text-overflow:ellipsis}
110
+ #tb-btns{display:flex;align-items:center;gap:1px;flex:0 0 auto}
111
+ .tb-btn{background:none;border:none;padding:4px 5px;border-radius:3px;cursor:pointer;color:var(--text-dim);display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:background .12s,color .12s}
112
+ .tb-btn:hover{background:rgba(128,128,128,.2);color:var(--text)}
113
+ .tb-btn.on{color:var(--text)}
114
+ .tb-btn svg{width:18px;height:18px}
115
+ #layout{display:flex;height:calc(100vh - 35px);overflow:hidden}
116
+ #left-panel,#right-panel{display:flex;flex-direction:column;overflow:hidden;flex-shrink:0;transition:width .15s ease,opacity .15s ease,border-color .15s ease}
117
+ #left-panel{width:250px;background:var(--sidebar-bg);border-right:1px solid var(--border)}
118
+ #right-panel{width:320px;background:var(--sidebar-bg);border-left:1px solid var(--border);position:relative}
119
+ #left-panel.closed,#right-panel.closed{width:0;opacity:0;border-color:transparent;pointer-events:none}
120
+ #content-panel{flex:1;min-width:0;background:var(--bg)}
121
+ #content-frame{width:100%;height:100%;border:none}
122
+ .h-resize{width:4px;cursor:col-resize;background:transparent;flex-shrink:0;transition:background .12s}
123
+ .h-resize:hover,.h-resize.drag{background:var(--accent)}
124
+ .panel-hdr{height:35px;display:flex;align-items:center;padding:0 12px;font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--text-dim);flex-shrink:0;border-bottom:1px solid var(--border);gap:6px}
125
+ .panel-hdr-title{flex:1}
126
+ #explorer{flex:1;overflow-y:auto;overflow-x:hidden;padding:2px 0;scrollbar-width:thin;scrollbar-color:var(--scrollbar) transparent;overscroll-behavior:contain}
127
+ #explorer::-webkit-scrollbar{width:6px}
128
+ #explorer::-webkit-scrollbar-thumb{background:var(--scrollbar);border-radius:3px}
129
+ .ti{display:flex;align-items:center;height:22px;padding-right:8px;cursor:pointer;white-space:nowrap;overflow:hidden;font-size:13px;color:var(--text)}
130
+ .ti:hover{background:var(--hover)}
131
+ .ti.sel{background:var(--active)}
132
+ .ti-chev{width:16px;flex-shrink:0;text-align:center;font-size:10px;color:var(--text-dim);transition:transform .1s;line-height:1}
133
+ .ti.open>.ti-chev{transform:rotate(90deg)}
134
+ .ti-icon{width:16px;flex-shrink:0;display:flex;align-items:center;justify-content:center}.ti-icon svg{width:14px;height:14px;flex-shrink:0}
135
+ .ti-label{flex:1;overflow:hidden;text-overflow:ellipsis}
136
+ .tc{display:none}
137
+ .tc.open{display:block}
138
+ #chat-msgs{flex:1;overflow-y:auto;padding:10px 12px;display:flex;flex-direction:column;gap:8px;scrollbar-width:thin;scrollbar-color:var(--scrollbar) transparent;overscroll-behavior:contain}
139
+ #chat-msgs::-webkit-scrollbar{width:6px}
140
+ #chat-msgs::-webkit-scrollbar-thumb{background:var(--scrollbar);border-radius:3px}
141
+ .msg{padding:8px 10px;border-radius:6px;line-height:1.5;font-size:12px;word-break:break-word;max-width:90%;white-space:pre-wrap}
142
+ .msg.user{background:var(--accent);color:#fff;align-self:flex-end;border-bottom-right-radius:2px}
143
+ .msg.ai{background:var(--ai-msg-bg);color:var(--text);align-self:flex-start;border-bottom-left-radius:2px;border:1px solid var(--ai-msg-border)}
144
+ .msg.sys{color:var(--text-dim);align-self:center;font-size:11px;font-style:italic;background:none;max-width:100%;text-align:center}
145
+ #chat-foot{flex-shrink:0;border-top:1px solid var(--border);display:flex;flex-direction:column}
146
+ #chat-vr{height:4px;cursor:row-resize;background:transparent;transition:background .12s;flex-shrink:0}
147
+ #chat-vr:hover,#chat-vr.drag{background:var(--accent)}
148
+ #chat-row{display:flex;align-items:flex-end;gap:6px;padding:8px 10px}
149
+ #chat-ta{flex:1;resize:none;height:80px;min-height:40px;background:var(--input-bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px;font-family:inherit;padding:7px 9px;outline:none;line-height:1.5;scrollbar-width:thin;overscroll-behavior:contain}
150
+ #chat-ta:focus{border-color:var(--accent)}
151
+ #chat-send{width:30px;height:30px;background:var(--accent);border:none;border-radius:6px;color:#fff;cursor:pointer;font-size:16px;flex-shrink:0;display:flex;align-items:center;justify-content:center;transition:opacity .12s}
152
+ #chat-send:hover{opacity:.85}
153
+ #chat-send:disabled{opacity:.35;cursor:default}
154
+ #api-overlay{display:none;position:absolute;inset:0;background:rgba(0,0,0,.45);z-index:50;align-items:center;justify-content:center}
155
+ #api-overlay.open{display:flex}
156
+ #api-card{background:var(--sidebar-bg);border:1px solid var(--border);border-radius:8px;padding:18px;width:270px;display:flex;flex-direction:column;gap:10px;box-shadow:0 8px 32px var(--shadow)}
157
+ #api-card h3{font-size:13px;font-weight:600;color:var(--text)}
158
+ #api-card p{font-size:11px;color:var(--text-dim);line-height:1.5}
159
+ #api-key-inp{background:var(--input-bg);border:1px solid var(--border);border-radius:4px;color:var(--text);padding:6px 8px;font-size:13px;outline:none;width:100%}
160
+ #api-key-inp:focus{border-color:var(--accent)}
161
+ .api-row{display:flex;gap:6px;justify-content:flex-end}
162
+ .api-btn{padding:5px 12px;border-radius:4px;border:1px solid var(--border);background:var(--active);color:var(--text);font-size:12px;cursor:pointer;transition:opacity .12s}
163
+ .api-btn.ok{background:var(--accent);border-color:var(--accent);color:#fff}
164
+ .api-btn:hover{opacity:.85}
165
+ </style>
166
+ </head>
167
+ <body>
168
+ <div id="titlebar">
169
+ <div id="tb-left">
170
+ <button id="tb-home" title="Go to home page">
171
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.3">
172
+ <path d="M2 6.5L8 2l6 4.5V14a.5.5 0 01-.5.5h-4V10H6.5v4.5h-4A.5.5 0 012 14z"/>
173
+ </svg>
174
+ <span id="tb-home-label">${siteTitle}</span>
175
+ </button>
176
+ </div>
177
+ <div id="tb-search-wrap">
178
+ <svg id="tb-search-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5">
179
+ <circle cx="6.5" cy="6.5" r="4"/>
180
+ <line x1="10" y1="10" x2="14" y2="14"/>
181
+ </svg>
182
+ <input id="tb-search" type="text" placeholder="Search files…" autocomplete="off" spellcheck="false">
183
+ <button id="tb-search-clear" title="Clear search">&#x2715;</button>
184
+ <div id="search-results"></div>
185
+ </div>
186
+ <div id="tb-btns">
187
+ <button class="tb-btn on" id="btn-left" title="Toggle Explorer (Ctrl+B)">
188
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.2">
189
+ <rect x="1.5" y="1.5" width="13" height="13" rx="1.2"/>
190
+ <line x1="5.5" y1="1.5" x2="5.5" y2="14.5"/>
191
+ </svg>
192
+ </button>
193
+ <button class="tb-btn" id="btn-right" title="Toggle Chat">
194
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.2">
195
+ <rect x="1.5" y="1.5" width="13" height="13" rx="1.2"/>
196
+ <line x1="10.5" y1="1.5" x2="10.5" y2="14.5"/>
197
+ </svg>
198
+ </button>
199
+ </div>
200
+ </div>
201
+ <div id="layout">
202
+ <div id="left-panel">
203
+ <div class="panel-hdr"><span class="panel-hdr-title">Explorer</span></div>
204
+ <div id="explorer"></div>
205
+ </div>
206
+ <div class="h-resize" id="lr"></div>
207
+ <div id="content-panel">
208
+ <iframe id="content-frame" name="content"></iframe>
209
+ </div>
210
+ <div class="h-resize" id="rr"></div>
211
+ <div id="right-panel">
212
+ <div class="panel-hdr">
213
+ <span class="panel-hdr-title">Chat</span>
214
+ <button class="tb-btn" id="btn-api" title="Configure API Key" style="font-size:15px">&#9881;</button>
215
+ </div>
216
+ <div id="chat-msgs"></div>
217
+ <div id="chat-foot">
218
+ <div id="chat-vr"></div>
219
+ <div id="chat-row">
220
+ <textarea id="chat-ta" placeholder="Ask about this model… (Ctrl+Enter to send)"></textarea>
221
+ <button id="chat-send" title="Send (Ctrl+Enter)">&#8593;</button>
222
+ </div>
223
+ </div>
224
+ <div id="api-overlay">
225
+ <div id="api-card">
226
+ <h3>Anthropic API Key</h3>
227
+ <p>Your key is stored locally in this browser and never sent anywhere except the Anthropic API.</p>
228
+ <input id="api-key-inp" type="password" placeholder="sk-ant-…" autocomplete="off">
229
+ <div class="api-row">
230
+ <button class="api-btn" id="api-cancel">Cancel</button>
231
+ <button class="api-btn ok" id="api-save">Save</button>
232
+ </div>
233
+ </div>
234
+ </div>
235
+ </div>
236
+ </div>
237
+ <script>
238
+ (function () {
239
+ 'use strict';
240
+ var TREE = ${treeJson};
241
+ var SEARCH_INDEX = ${searchIndexJson};
242
+ var SITE_TITLE = ${titleJson};
243
+ var HOME_PATH = ${homePathJson};
244
+ var MD_PREFIX = ${mdPrefixJson};
245
+ var leftPanel = document.getElementById('left-panel');
246
+ var rightPanel = document.getElementById('right-panel');
247
+ var frame = document.getElementById('content-frame');
248
+ var chatMsgs = document.getElementById('chat-msgs');
249
+ var chatTa = document.getElementById('chat-ta');
250
+ var chatSend = document.getElementById('chat-send');
251
+ var apiOverlay = document.getElementById('api-overlay');
252
+ var apiKeyInp = document.getElementById('api-key-inp');
253
+ var searchInput = document.getElementById('tb-search');
254
+ var searchClear = document.getElementById('tb-search-clear');
255
+ var searchResults = document.getElementById('search-results');
256
+
257
+
258
+ // --- Panel toggle ---
259
+ var leftW = 250, rightW = 320;
260
+ function setPanelOpen(panel, btn, open, getW, setW) {
261
+ if (open) {
262
+ panel.classList.remove('closed');
263
+ panel.style.width = getW() + 'px';
264
+ } else {
265
+ setW(panel.offsetWidth || getW());
266
+ panel.classList.add('closed');
267
+ panel.style.width = '0';
268
+ }
269
+ btn.classList.toggle('on', open);
270
+ }
271
+ document.getElementById('btn-left').addEventListener('click', function () {
272
+ setPanelOpen(leftPanel, this, leftPanel.classList.contains('closed'),
273
+ function () { return leftW; }, function (w) { leftW = w; });
274
+ });
275
+ document.getElementById('btn-right').addEventListener('click', function () {
276
+ setPanelOpen(rightPanel, this, rightPanel.classList.contains('closed'),
277
+ function () { return rightW; }, function (w) { rightW = w; });
278
+ });
279
+ document.addEventListener('keydown', function (e) {
280
+ if ((e.ctrlKey || e.metaKey) && e.key === 'b') {
281
+ e.preventDefault();
282
+ document.getElementById('btn-left').click();
283
+ }
284
+ });
285
+ leftPanel.style.width = leftW + 'px';
286
+ rightPanel.classList.add('closed');
287
+ rightPanel.style.width = '0';
288
+
289
+ // --- Horizontal panel resize ---
290
+ function makeHResize(handle, getPanel, side, getW, setW) {
291
+ var dragging = false, startX = 0, startW = 0;
292
+ handle.addEventListener('mousedown', function (e) {
293
+ if (getPanel().classList.contains('closed')) return;
294
+ dragging = true; startX = e.clientX; startW = getPanel().offsetWidth;
295
+ handle.classList.add('drag');
296
+ document.body.style.cursor = 'col-resize';
297
+ document.body.style.userSelect = 'none';
298
+ e.preventDefault();
299
+ });
300
+ document.addEventListener('mousemove', function (e) {
301
+ if (!dragging) return;
302
+ var delta = side === 'left' ? e.clientX - startX : startX - e.clientX;
303
+ var w = Math.max(160, Math.min(600, startW + delta));
304
+ getPanel().style.width = w + 'px';
305
+ getPanel().style.transition = 'none';
306
+ setW(w);
307
+ });
308
+ document.addEventListener('mouseup', function () {
309
+ if (!dragging) return;
310
+ dragging = false;
311
+ handle.classList.remove('drag');
312
+ document.body.style.cursor = '';
313
+ document.body.style.userSelect = '';
314
+ getPanel().style.transition = '';
315
+ });
316
+ }
317
+ makeHResize(document.getElementById('lr'), function () { return leftPanel; }, 'left',
318
+ function () { return leftW; }, function (w) { leftW = w; });
319
+ makeHResize(document.getElementById('rr'), function () { return rightPanel; }, 'right',
320
+ function () { return rightW; }, function (w) { rightW = w; });
321
+
322
+ // --- Chat input vertical resize ---
323
+ var chatVr = document.getElementById('chat-vr');
324
+ var vDragging = false, vStartY = 0, vStartH = 0;
325
+ chatVr.addEventListener('mousedown', function (e) {
326
+ vDragging = true; vStartY = e.clientY; vStartH = chatTa.offsetHeight;
327
+ chatVr.classList.add('drag');
328
+ document.body.style.cursor = 'row-resize';
329
+ document.body.style.userSelect = 'none';
330
+ e.preventDefault();
331
+ });
332
+ document.addEventListener('mousemove', function (e) {
333
+ if (!vDragging) return;
334
+ var h = Math.max(40, Math.min(400, vStartH + (vStartY - e.clientY)));
335
+ chatTa.style.height = h + 'px';
336
+ });
337
+ document.addEventListener('mouseup', function () {
338
+ if (!vDragging) return;
339
+ vDragging = false;
340
+ chatVr.classList.remove('drag');
341
+ document.body.style.cursor = '';
342
+ document.body.style.userSelect = '';
343
+ });
344
+
345
+ // --- Navigate to a file ---
346
+ var selItem = null;
347
+ function navigate(relPath, label, treeItem) {
348
+ frame.src = MD_PREFIX + relPath;
349
+ document.title = label + ' — ' + SITE_TITLE;
350
+ if (selItem) selItem.classList.remove('sel');
351
+ selItem = treeItem || null;
352
+ if (selItem) selItem.classList.add('sel');
353
+ }
354
+
355
+ // --- Home button ---
356
+ document.getElementById('tb-home').addEventListener('click', function () {
357
+ if (HOME_PATH) {
358
+ navigate(HOME_PATH, SITE_TITLE, null);
359
+ }
360
+ });
361
+
362
+ // --- Icons ---
363
+ var SVG_FILE = '<svg viewBox="0 0 16 16" fill="none"><path d="M4.5 1.5h6l3 3v10a.5.5 0 01-.5.5h-8a.5.5 0 01-.5-.5v-13z" fill="#7bafd4" stroke="none"/><path d="M10.5 1.5v3h3" fill="none" stroke="#5a9fc0" stroke-width="1"/></svg>';
364
+ var SVG_FOLDER_CLOSED = '<svg viewBox="0 0 16 16"><path d="M1 5V3.5a.5.5 0 01.5-.5H6.8l1.4 2z" fill="#c4973f"/><path d="M1 5h14v9a.5.5 0 01-.5.5h-13a.5.5 0 01-.5-.5z" fill="#dcb67a"/></svg>';
365
+ var SVG_FOLDER_OPEN = '<svg viewBox="0 0 16 16"><path d="M1 5V3.5a.5.5 0 01.5-.5H6.8l1.4 2z" fill="#b8872f"/><rect x="1" y="5" width="14" height="1.5" fill="#e6c97a"/><path d="M2 6.5L1 14.5h12.5l1.5-8z" fill="#dcb67a"/></svg>';
366
+
367
+ // --- File tree ---
368
+ // autoOpen: expand this folder automatically; continue auto-opening children
369
+ // only while a folder has no direct file children (keep going until files appear)
370
+ function renderTree(nodes, container, depth, autoOpen) {
371
+ nodes.forEach(function (node) {
372
+ var isFolder = !node.path;
373
+ var item = document.createElement('div');
374
+ item.className = 'ti';
375
+ item.style.paddingLeft = (4 + depth * 16) + 'px';
376
+
377
+ var chev = document.createElement('span');
378
+ chev.className = 'ti-chev';
379
+ chev.textContent = isFolder ? '›' : '';
380
+ item.appendChild(chev);
381
+
382
+ var icon = document.createElement('span');
383
+ icon.className = 'ti-icon';
384
+ icon.innerHTML = isFolder ? SVG_FOLDER_CLOSED : SVG_FILE;
385
+ item.appendChild(icon);
386
+
387
+ var label = document.createElement('span');
388
+ label.className = 'ti-label';
389
+ label.textContent = node.name.replace(/\\.html$/, '');
390
+ label.title = node.name;
391
+ item.appendChild(label);
392
+
393
+ container.appendChild(item);
394
+
395
+ if (isFolder) {
396
+ var children = document.createElement('div');
397
+ children.className = 'tc';
398
+ var childNodes = node.children || [];
399
+ var childHasFiles = childNodes.some(function (c) { return !!c.path; });
400
+ // Keep auto-opening deeper only while no files have appeared yet
401
+ renderTree(childNodes, children, depth + 1, autoOpen && !childHasFiles);
402
+ container.appendChild(children);
403
+ if (autoOpen) {
404
+ children.classList.add('open');
405
+ item.classList.add('open');
406
+ icon.innerHTML = SVG_FOLDER_OPEN;
407
+ }
408
+ item.addEventListener('click', function (e) {
409
+ e.stopPropagation();
410
+ var open = children.classList.toggle('open');
411
+ item.classList.toggle('open', open);
412
+ icon.innerHTML = open ? SVG_FOLDER_OPEN : SVG_FOLDER_CLOSED;
413
+ });
414
+ } else {
415
+ item.addEventListener('click', function (e) {
416
+ e.stopPropagation();
417
+ navigate(node.path, node.name.replace(/\\.html$/, ''), item);
418
+ });
419
+ }
420
+ });
421
+ }
422
+ renderTree(TREE, document.getElementById('explorer'), 0, true);
423
+
424
+ // --- Load initial page ---
425
+ if (HOME_PATH) {
426
+ navigate(HOME_PATH, SITE_TITLE, null);
427
+ } else {
428
+ function findFirst(nodes) {
429
+ for (var i = 0; i < nodes.length; i++) {
430
+ var n = nodes[i];
431
+ if (n.path) return n;
432
+ var f = n.children ? findFirst(n.children) : null;
433
+ if (f) return f;
434
+ }
435
+ return null;
436
+ }
437
+ var first = findFirst(TREE);
438
+ if (first) navigate(first.path, first.name.replace(/\\.html$/, ''), null);
439
+ }
440
+
441
+ // --- Search ---
442
+ var srActiveIdx = -1;
443
+ function renderSearchResults(query) {
444
+ searchResults.innerHTML = '';
445
+ srActiveIdx = -1;
446
+ if (!query) {
447
+ searchResults.classList.remove('open');
448
+ searchClear.style.display = 'none';
449
+ return;
450
+ }
451
+ searchClear.style.display = 'block';
452
+ var q = query.toLowerCase();
453
+ var hits = SEARCH_INDEX.filter(function (f) {
454
+ return f.name.toLowerCase().includes(q);
455
+ }).slice(0, 20);
456
+ if (!hits.length) {
457
+ var empty = document.createElement('div');
458
+ empty.className = 'sr-item';
459
+ empty.style.color = 'var(--text-dim)';
460
+ empty.textContent = 'No results';
461
+ searchResults.appendChild(empty);
462
+ } else {
463
+ hits.forEach(function (hit) {
464
+ var item = document.createElement('div');
465
+ item.className = 'sr-item';
466
+ var nameEl = document.createElement('div');
467
+ nameEl.className = 'sr-item-name';
468
+ nameEl.textContent = hit.name;
469
+ item.appendChild(nameEl);
470
+ if (hit.dir) {
471
+ var pathEl = document.createElement('div');
472
+ pathEl.className = 'sr-item-path';
473
+ pathEl.textContent = hit.dir;
474
+ item.appendChild(pathEl);
475
+ }
476
+ item.addEventListener('mousedown', function (e) {
477
+ e.preventDefault();
478
+ navigate(hit.path, hit.name, null);
479
+ searchInput.value = '';
480
+ searchResults.classList.remove('open');
481
+ searchClear.style.display = 'none';
482
+ });
483
+ searchResults.appendChild(item);
484
+ });
485
+ }
486
+ searchResults.classList.add('open');
487
+ }
488
+ searchInput.addEventListener('input', function () {
489
+ renderSearchResults(this.value.trim());
490
+ });
491
+ searchInput.addEventListener('keydown', function (e) {
492
+ var items = searchResults.querySelectorAll('.sr-item');
493
+ if (e.key === 'ArrowDown') {
494
+ e.preventDefault();
495
+ srActiveIdx = Math.min(srActiveIdx + 1, items.length - 1);
496
+ } else if (e.key === 'ArrowUp') {
497
+ e.preventDefault();
498
+ srActiveIdx = Math.max(srActiveIdx - 1, 0);
499
+ } else if (e.key === 'Enter') {
500
+ e.preventDefault();
501
+ if (srActiveIdx >= 0 && items[srActiveIdx]) items[srActiveIdx].dispatchEvent(new MouseEvent('mousedown'));
502
+ else if (items.length === 1) items[0].dispatchEvent(new MouseEvent('mousedown'));
503
+ return;
504
+ } else if (e.key === 'Escape') {
505
+ searchInput.value = '';
506
+ renderSearchResults('');
507
+ return;
508
+ }
509
+ items.forEach(function (it, i) { it.classList.toggle('active', i === srActiveIdx); });
510
+ if (items[srActiveIdx]) items[srActiveIdx].scrollIntoView({ block: 'nearest' });
511
+ });
512
+ searchInput.addEventListener('blur', function () {
513
+ setTimeout(function () { searchResults.classList.remove('open'); }, 150);
514
+ });
515
+ searchInput.addEventListener('focus', function () {
516
+ if (this.value.trim()) renderSearchResults(this.value.trim());
517
+ });
518
+ searchClear.addEventListener('click', function () {
519
+ searchInput.value = '';
520
+ renderSearchResults('');
521
+ searchInput.focus();
522
+ });
523
+
524
+ // --- API key overlay ---
525
+ document.getElementById('btn-api').addEventListener('click', function () {
526
+ apiKeyInp.value = localStorage.getItem('oml-apikey') || '';
527
+ apiOverlay.classList.add('open');
528
+ apiKeyInp.focus();
529
+ });
530
+ document.getElementById('api-cancel').addEventListener('click', function () {
531
+ apiOverlay.classList.remove('open');
532
+ });
533
+ document.getElementById('api-save').addEventListener('click', function () {
534
+ var k = apiKeyInp.value.trim();
535
+ if (k) localStorage.setItem('oml-apikey', k);
536
+ else localStorage.removeItem('oml-apikey');
537
+ apiOverlay.classList.remove('open');
538
+ });
539
+ apiOverlay.addEventListener('click', function (e) {
540
+ if (e.target === apiOverlay) apiOverlay.classList.remove('open');
541
+ });
542
+
543
+ // --- Chat ---
544
+ var history = [];
545
+ function addMsg(role, text) {
546
+ var div = document.createElement('div');
547
+ div.className = 'msg ' + role;
548
+ div.textContent = text;
549
+ chatMsgs.appendChild(div);
550
+ chatMsgs.scrollTop = chatMsgs.scrollHeight;
551
+ return div;
552
+ }
553
+ addMsg('sys', 'AI chat powered by Claude. Click ⚙ to set your Anthropic API key.');
554
+
555
+ async function send() {
556
+ var text = chatTa.value.trim();
557
+ if (!text) return;
558
+ var key = localStorage.getItem('oml-apikey');
559
+ if (!key) { addMsg('sys', 'Set your Anthropic API key via ⚙ to start chatting.'); return; }
560
+ chatTa.value = '';
561
+ chatSend.disabled = true;
562
+ history.push({ role: 'user', content: text });
563
+ addMsg('user', text);
564
+ var div = addMsg('ai', '…');
565
+
566
+ var pageCtx = '';
567
+ try {
568
+ var doc = frame.contentDocument || (frame.contentWindow && frame.contentWindow.document);
569
+ if (doc && doc.body) {
570
+ pageCtx = '\\n\\n[Current page: ' + (doc.title || frame.src) + ']\\n' + doc.body.innerText.slice(0, 3000);
571
+ }
572
+ } catch (e) {}
573
+
574
+ var msgs = history.map(function (m, i) {
575
+ return { role: m.role, content: m.content + (i === history.length - 1 && m.role === 'user' ? pageCtx : '') };
576
+ });
577
+
578
+ try {
579
+ var res = await fetch('https://api.anthropic.com/v1/messages', {
580
+ method: 'POST',
581
+ headers: {
582
+ 'x-api-key': key,
583
+ 'anthropic-version': '2023-06-01',
584
+ 'content-type': 'application/json',
585
+ 'anthropic-dangerous-allow-browser': 'true'
586
+ },
587
+ body: JSON.stringify({
588
+ model: 'claude-sonnet-4-6',
589
+ max_tokens: 1024,
590
+ stream: true,
591
+ system: 'You are a helpful assistant for an OML (Ontological Modeling Language) model documentation site. Answer questions concisely based on the page content provided.',
592
+ messages: msgs
593
+ })
594
+ });
595
+ if (!res.ok) {
596
+ div.textContent = 'API error ' + res.status + ': ' + (await res.text()).slice(0, 200);
597
+ history.pop();
598
+ return;
599
+ }
600
+ var reader = res.body.getReader();
601
+ var decoder = new TextDecoder();
602
+ var full = '';
603
+ div.textContent = '';
604
+ for (;;) {
605
+ var chunk = await reader.read();
606
+ if (chunk.done) break;
607
+ var lines = decoder.decode(chunk.value, { stream: true }).split('\\n');
608
+ for (var i = 0; i < lines.length; i++) {
609
+ var line = lines[i];
610
+ if (!line.startsWith('data: ')) continue;
611
+ var data = line.slice(6).trim();
612
+ if (data === '[DONE]') break;
613
+ try {
614
+ var evt = JSON.parse(data);
615
+ if (evt.type === 'content_block_delta' && evt.delta && evt.delta.text) {
616
+ full += evt.delta.text;
617
+ div.textContent = full;
618
+ chatMsgs.scrollTop = chatMsgs.scrollHeight;
619
+ }
620
+ } catch (ex) {}
621
+ }
622
+ }
623
+ if (full) history.push({ role: 'assistant', content: full });
624
+ else { div.textContent = '(empty response)'; history.pop(); }
625
+ } catch (e) {
626
+ div.textContent = 'Error: ' + e.message;
627
+ history.pop();
628
+ } finally {
629
+ chatSend.disabled = false;
630
+ chatTa.focus();
631
+ }
632
+ }
633
+
634
+ chatSend.addEventListener('click', send);
635
+ chatTa.addEventListener('keydown', function (e) {
636
+ if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { e.preventDefault(); send(); }
637
+ });
638
+ })();
639
+ </script>
640
+ </body>
641
+ </html>`;
642
+ }
643
+ //# sourceMappingURL=shell-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-index.js","sourceRoot":"","sources":["../../src/rest/shell-index.ts"],"names":[],"mappings":"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"}
@@ -122,9 +122,6 @@ export async function expandTemplateComposeBlocks(markdown, templateCatalog, con
122
122
  args: invocationArgs,
123
123
  };
124
124
  const rendered = renderTemplate(template.definition, invocation);
125
- if (rendered.missingRequired.length > 0) {
126
- throw new Error(`Template '${template.definition.id}' is missing required parameter(s): ${rendered.missingRequired.join(', ')}.`);
127
- }
128
125
  replaced += absolutizeTemplateHrefValues(rendered.output, template.sourcePath, context.sourceMarkdownPath, context.workspaceRoot);
129
126
  lastIndex = blockEnd;
130
127
  }