koneck 2.69.0 → 2.71.0

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.
@@ -1709,6 +1709,23 @@ async function loadSettings() {
1709
1709
  .catch((e) => { sbox.appendChild(el('div', 'err', e.message)); });
1710
1710
 
1711
1711
  /* MCP */
1712
+ /*
1713
+ * Providers, which this panel claimed to show and did not.
1714
+ *
1715
+ * The tab has always been labelled "providers, type, health" and there was no providers section in
1716
+ * it: the only list anywhere was the dropdown inside the model dialog. So somebody who declared
1717
+ * one and then came here to check looked at a panel that promised providers and showed none, and
1718
+ * concluded reasonably that the declaration had not taken. It had.
1719
+ *
1720
+ * Everything KONECK knows is listed, declared entries included, with the endpoint each would use
1721
+ * and whether a credential is actually reachable — and a declared one can be removed from here,
1722
+ * which is the other half of being able to add one.
1723
+ */
1724
+ wrap.appendChild(el('div', 'sect', 'Providers'));
1725
+ const provbox = el('div');
1726
+ wrap.appendChild(provbox);
1727
+ drawProviderList(provbox);
1728
+
1712
1729
  wrap.appendChild(el('div', 'sect', 'MCP integrations'));
1713
1730
  const mbox = el('div');
1714
1731
  wrap.appendChild(mbox);
@@ -1763,6 +1780,85 @@ async function loadSettings() {
1763
1780
  }
1764
1781
 
1765
1782
 
1783
+ /**
1784
+ * The providers list in Settings.
1785
+ *
1786
+ * Ordered as the registry holds them — what KONECK ships with, then whatever you declared — because
1787
+ * that grouping is the answer to "is mine actually in here", which is the question this list exists
1788
+ * for. Rows say the endpoint rather than hiding it: a provider pointing somewhere unexpected is the
1789
+ * single most useful thing to be able to see at a glance, and it is what one wrong /endpoint looks
1790
+ * like.
1791
+ */
1792
+ async function drawProviderList(host) {
1793
+ host.innerHTML = '';
1794
+ host.appendChild(el('div', 'smeta', 'reading…'));
1795
+ let info;
1796
+ try {
1797
+ const who = state.session ? '?session=' + encodeURIComponent(state.session) : '';
1798
+ info = await api('/api/providers' + who);
1799
+ } catch (e) {
1800
+ host.innerHTML = '';
1801
+ host.appendChild(el('div', 'err', e.message));
1802
+ return;
1803
+ }
1804
+ host.innerHTML = '';
1805
+
1806
+ const rows = info.providers || [];
1807
+ const mine = rows.filter((p) => p.declared);
1808
+ host.appendChild(el('div', 'smeta',
1809
+ rows.length + ' known · ' + mine.length + (mine.length === 1 ? ' declared by you' : ' declared by you')
1810
+ + (mine.length ? ' · from ' + info.file : '')));
1811
+
1812
+ for (const p of rows) {
1813
+ const row = el('div', 'hrow');
1814
+ /*
1815
+ * Three states, and they are different questions. A local server needs no credential at all; a
1816
+ * keyed provider either has one reachable or does not and will fail on its first request.
1817
+ */
1818
+ row.appendChild(el('span', 'hs ' + (p.keyless ? 'pass' : p.keyPresent ? 'pass' : 'warn'),
1819
+ p.keyless ? 'local' : p.keyPresent ? 'key' : 'no key'));
1820
+ const name = el('span', 'hn', p.label);
1821
+ if (p.declared) name.appendChild(el('em', 'pmine', ' yours'));
1822
+ row.appendChild(name);
1823
+ row.appendChild(el('span', 'hr', p.baseURL));
1824
+
1825
+ if (p.declared) {
1826
+ const drop = el('button', 'tinybtn', 'remove');
1827
+ drop.title = p.shadowsBuiltIn
1828
+ ? 'Removing this leaves the built-in ' + p.name + ' in place'
1829
+ : 'Forget this provider';
1830
+ drop.onclick = async () => {
1831
+ drop.disabled = true;
1832
+ try {
1833
+ await api('/api/providers?name=' + encodeURIComponent(p.name), { method: 'DELETE' });
1834
+ } catch (e) {
1835
+ drop.disabled = false;
1836
+ host.appendChild(el('div', 'err', e.message));
1837
+ return;
1838
+ }
1839
+ // Re-read rather than splice the row out: the built-in it was shadowing comes back, and
1840
+ // guessing what the list looks like afterwards is how a list starts telling small lies.
1841
+ void drawProviderList(host);
1842
+ };
1843
+ row.appendChild(drop);
1844
+ }
1845
+ host.appendChild(row);
1846
+ }
1847
+
1848
+ const add = el('button', 'act', '+ add a provider');
1849
+ add.style.marginTop = '10px';
1850
+ add.onclick = () => {
1851
+ $('pverr').hidden = true;
1852
+ $('provdlg').showModal();
1853
+ // Whatever the dialog does, this list should be right afterwards.
1854
+ $('provdlg').addEventListener('close', () => drawProviderList(host), { once: true });
1855
+ };
1856
+ host.appendChild(add);
1857
+ host.appendChild(el('div', 'smeta',
1858
+ 'A declared provider needs an OpenAI-compatible endpoint and the name of the environment '
1859
+ + 'variable holding its key — the name, never the key itself.'));
1860
+ }
1861
+
1766
1862
  /* ── attachments ───────────────────────────────────────────── */
1767
1863
  /**
1768
1864
  * Images waiting to go with the next message.
@@ -2489,10 +2585,13 @@ function drawMention() {
2489
2585
  * everything that distinguishes it is at the end.
2490
2586
  */
2491
2587
  const p = el('div', 'mp');
2588
+ // One isolated LTR run, for the same reason the watch header needs one: the box is RTL so the
2589
+ // front is what gets truncated, and an RTL box reorders the slashes at either edge.
2590
+ const run = el('span', 'pathltr');
2492
2591
  const cut = item.path.lastIndexOf('/');
2493
- if (cut > -1) p.appendChild(document.createTextNode(item.path.slice(0, cut + 1)));
2494
- const name = el('b', null, cut > -1 ? item.path.slice(cut + 1) : item.path);
2495
- p.appendChild(name);
2592
+ if (cut > -1) run.appendChild(document.createTextNode(item.path.slice(0, cut + 1)));
2593
+ run.appendChild(el('b', null, cut > -1 ? item.path.slice(cut + 1) : item.path));
2594
+ p.appendChild(run);
2496
2595
  row.appendChild(p);
2497
2596
 
2498
2597
  if (item.because && item.because.length) {
@@ -4117,7 +4216,16 @@ function stopWatchClock() {
4117
4216
  /** The header: the verb, the target, and how long this step has been going. */
4118
4217
  function watchHead(verb, target) {
4119
4218
  $('wtool').textContent = verb;
4120
- $('wpath').textContent = target;
4219
+ /*
4220
+ * The path in an isolated LTR run.
4221
+ *
4222
+ * Set as text directly, the box's own direction:rtl — which is what truncates the front rather
4223
+ * than the end — moved the leading slash to the far side, so /home/kona/…/page.tsx was shown as
4224
+ * home/kona/…/page.tsx/. The slash was never in the data.
4225
+ */
4226
+ const held = $('wpath');
4227
+ held.textContent = '';
4228
+ held.appendChild(el('span', 'pathltr', target));
4121
4229
  follow.startedAt = Date.now();
4122
4230
  if (!follow.timer) {
4123
4231
  follow.timer = setInterval(() => {
@@ -4337,29 +4445,165 @@ function prettyArgs(args) {
4337
4445
  }
4338
4446
 
4339
4447
  /*
4340
- * The write in progress, repainted as its content arrives.
4448
+ * The write in progress, coloured as it arrives.
4449
+ *
4450
+ * This used to be deliberately plain, on the reasoning that colouring needs a round trip per
4451
+ * repaint and four of those a second to colour text about to change again was a cost with no
4452
+ * reader. Two thirds of that was wrong. There is a reader — the panel exists to be read while the
4453
+ * work happens, and uncoloured code is the one thing there that looks unfinished. And the cost was
4454
+ * never measured: tokenising a 610-line tsx file is 7.7ms, and the expensive part was never the
4455
+ * highlighting at all, it was rebuilding every row on every repaint.
4341
4456
  *
4342
- * Plain while it streams and coloured the moment it lands: highlighting needs a round trip per
4343
- * repaint, and four of those a second to colour text that is about to change again is a cost with
4344
- * no reader. The finished render replaces it with the coloured version a fraction of a second
4345
- * later, which is what the eye actually follows.
4457
+ * So it paints incrementally, which a streaming write allows because it is append-only:
4458
+ *
4459
+ * - the new lines are appended plain, immediately, so the text never waits for colour;
4460
+ * - the server is asked to colour only the lines that arrived, debounced;
4461
+ * - the returned colours are painted into the rows already on screen.
4462
+ *
4463
+ * When the content is not an extension of what was there — a replace, or the model starting over —
4464
+ * the prefix check fails and it repaints from scratch, because guessing wrong about that shows
4465
+ * somebody the wrong file.
4346
4466
  */
4347
4467
  function drawWriteInProgress(path, content) {
4348
4468
  const host = $('wbody');
4349
4469
  let head = host.querySelector('.wwrite');
4350
- if (!head) {
4470
+ const fresh = !head || write.path !== path || !content.startsWith(write.text);
4471
+ if (fresh) {
4351
4472
  host.innerHTML = '';
4352
4473
  head = el('div', 'wwrite');
4353
4474
  host.appendChild(head);
4354
- host.appendChild(el('div', 'wwbody'));
4475
+ const body = el('div', 'wwbody');
4476
+ /*
4477
+ * An empty frame, built here rather than by drawCode.
4478
+ *
4479
+ * drawCode('') is not empty: an empty string splits into one line, so it produced a placeholder
4480
+ * row and every index after it was off by one. That was worse than a cosmetic slip — painting
4481
+ * replaces a row's text with the token text, so row N was being given line N+1's content and
4482
+ * the panel showed text that was never written on that line. Found by comparing each rendered
4483
+ * row against its gutter number: the first mismatch was at index 1.
4484
+ */
4485
+ const frame = el('div', 'rows');
4486
+ frame.appendChild(el('div', 'gut'));
4487
+ frame.appendChild(el('div', 'src'));
4488
+ body.appendChild(frame);
4489
+ host.appendChild(body);
4490
+ write.path = path;
4491
+ write.text = '';
4492
+ write.done = 0;
4493
+ write.asked = 0;
4494
+ // Anything still in flight for the previous file belongs to a generation that no longer exists.
4495
+ write.gen++;
4496
+ if (write.timer) { clearTimeout(write.timer); write.timer = null; }
4355
4497
  }
4498
+
4356
4499
  const lines = content.split('\n');
4357
4500
  head.textContent = 'writing ' + path + ' — ' + lines.length
4358
4501
  + (lines.length === 1 ? ' line so far' : ' lines so far');
4359
- const body = host.querySelector('.wwbody');
4360
- body.innerHTML = '';
4361
- body.appendChild(drawCode(content, [], null, null));
4362
- host.scrollTop = host.scrollHeight;
4502
+
4503
+ /*
4504
+ * The last line is left off until something follows it.
4505
+ *
4506
+ * A streaming write's final line is half-typed by definition, so painting it and then repainting
4507
+ * it on the next frame makes the bottom row flicker for the whole write. Holding it back one
4508
+ * frame costs nothing — the next chunk is milliseconds away — and the bottom of the panel stops
4509
+ * jittering.
4510
+ */
4511
+ const ready = lines.length - 1;
4512
+ const rows = host.querySelector('.rows');
4513
+ const gut = rows.querySelector('.gut');
4514
+ const src = rows.querySelector('.src');
4515
+
4516
+ const wasAtEnd = host.scrollHeight - host.scrollTop - host.clientHeight < 60;
4517
+ for (let i = write.done; i < ready; i++) {
4518
+ gut.appendChild(el('div', null, String(i + 1)));
4519
+ const row = el('div');
4520
+ row.textContent = lines[i] === '' ? '​' : lines[i];
4521
+ src.appendChild(row);
4522
+ }
4523
+ if (ready > write.done) write.done = ready;
4524
+ write.text = content;
4525
+ // Follows the tail, unless somebody has scrolled up to look at something.
4526
+ if (wasAtEnd) host.scrollTop = host.scrollHeight;
4527
+
4528
+ askColour(path, content);
4529
+ }
4530
+
4531
+ /**
4532
+ * The state of the write being watched, so the next chunk knows what is already on screen.
4533
+ *
4534
+ * "done" is how many rows exist; "asked" is how many have been coloured or are being coloured, so a
4535
+ * slow answer cannot cause the same lines to be requested twice.
4536
+ */
4537
+ const write = { path: null, text: '', done: 0, asked: 0, timer: null, gen: 0 };
4538
+
4539
+ /*
4540
+ * Asks for the colours of the lines that have arrived since the last time.
4541
+ *
4542
+ * Debounced, because chunks arrive faster than anybody reads and a request per chunk would be a
4543
+ * request per keystroke of the model's. Coalescing means one request covers everything that landed
4544
+ * while the previous one was in flight, which is exactly the behaviour wanted: the colour lags the
4545
+ * text by one round trip and never by more.
4546
+ */
4547
+ function askColour(path, content) {
4548
+ if (write.timer) return;
4549
+ write.timer = setTimeout(() => {
4550
+ write.timer = null;
4551
+ if (write.path !== path || write.asked >= write.done) return;
4552
+ const from = write.asked;
4553
+ const upto = write.done;
4554
+ /*
4555
+ * Kept against the generation, not against a request counter.
4556
+ *
4557
+ * The first version dropped any answer that was not the newest request, on the reasoning that a
4558
+ * stale answer must not paint. That is true of a *different file* and false of these: two
4559
+ * requests in flight cover different rows, so discarding the older one left the rows it covered
4560
+ * plain for ever while the newer ones coloured around them. Visible in a screenshot as line 28
4561
+ * coloured, 29 plain, 30 onward coloured — which is what sent me looking.
4562
+ *
4563
+ * The generation changes only when the panel resets to another file or the content stops being
4564
+ * an extension of itself, which is the only case where an in-flight answer is genuinely about
4565
+ * something else.
4566
+ */
4567
+ const gen = write.gen;
4568
+ write.asked = upto;
4569
+ api('/api/highlight', { method: 'POST',
4570
+ body: JSON.stringify({ path: path, text: write.text, from: from }) })
4571
+ .then((out) => {
4572
+ if (gen !== write.gen || write.path !== path || !out.tokens) return;
4573
+ paintRows(from, upto, out.tokens);
4574
+ })
4575
+ .catch(() => {
4576
+ // Colour is a bonus and the code is the point, so a failure leaves the plain rows alone —
4577
+ // but the lines are released so a later attempt can still colour them.
4578
+ if (gen === write.gen && write.asked === upto) write.asked = from;
4579
+ });
4580
+ }, 70);
4581
+ }
4582
+
4583
+ /**
4584
+ * Replaces the plain text of rows from..upto with their coloured spans.
4585
+ *
4586
+ * Each row is checked against the text the colours are for before it is touched. That guard is not
4587
+ * defensive habit: painting replaces a row's content with the token text, so an index that is off by
4588
+ * one does not merely mis-colour a line, it shows text that was never written on it. One off-by-one
4589
+ * had already done exactly that. With the check, the worst a future one can do is leave a line
4590
+ * uncoloured, which is a thing somebody can notice and nobody can be misled by.
4591
+ */
4592
+ function paintRows(from, upto, tokens) {
4593
+ const src = $('wbody').querySelector('.src');
4594
+ if (!src) return;
4595
+ for (let i = from; i < upto; i++) {
4596
+ const row = src.children[i];
4597
+ const forLine = tokens[i - from];
4598
+ if (!row || !forLine || !forLine.length) continue;
4599
+ const held = row.textContent;
4600
+ let joined = '';
4601
+ for (const t of forLine) joined += (t && t.text) || '';
4602
+ // The zero-width space stands in for an empty line, so an empty row matches empty tokens.
4603
+ if (joined !== held && !(held === '​' && joined === '')) continue;
4604
+ row.textContent = '';
4605
+ if (!paintTokens(row, forLine, null)) row.textContent = held;
4606
+ }
4363
4607
  }
4364
4608
 
4365
4609
  /** Paints the content a write is carrying, coloured for the file it is going into. */
@@ -1 +1 @@
1
- {"version":3,"file":"ui-client.js","sourceRoot":"","sources":["../../src/web/ui-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAw4JlB,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"ui-client.js","sourceRoot":"","sources":["../../src/web/ui-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4nKlB,CAAC;AACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ui-css.d.ts","sourceRoot":"","sources":["../../src/web/ui-css.ts"],"names":[],"mappings":"AA8CA,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAmnC3C"}
1
+ {"version":3,"file":"ui-css.d.ts","sourceRoot":"","sources":["../../src/web/ui-css.ts"],"names":[],"mappings":"AA8CA,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAqoC3C"}
@@ -427,7 +427,7 @@ footer{flex:0 0 auto;border-top:1px solid var(--line);background:var(--panel);pa
427
427
  .mrow:hover{background:var(--panel-2)}
428
428
  .mrow.on{background:var(--panel-2);border-left-color:var(--cyan)}
429
429
  /* The name carries the weight; the directory it sits in is context, not the answer. */
430
- .mrow .mp{font-family:var(--mono);font-size:12.5px;color:var(--muted);
430
+ .mrow .mp{font-family:var(--mono);font-size:12.5px;color:var(--muted);flex:1;min-width:0;
431
431
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;direction:rtl;text-align:left}
432
432
  .mrow .mp b{color:var(--ink);font-weight:600}
433
433
  .mrow .mwhy{display:flex;gap:5px;flex:0 0 auto}
@@ -445,6 +445,9 @@ footer{flex:0 0 auto;border-top:1px solid var(--line);background:var(--panel);pa
445
445
  .uref{font-family:var(--mono);font-size:10.5px;color:var(--dim);padding:1px 7px;
446
446
  border:1px solid var(--line-2);border-radius:999px}
447
447
  .uref.bad{border-color:var(--red);color:var(--red)}
448
+ /* "yours" beside a declared provider: a label, not an emphasis. */
449
+ .pmine{font-style:normal;font-size:9.5px;letter-spacing:.05em;color:var(--cyan);
450
+ border:1px solid var(--cyan);border-radius:999px;padding:1px 6px;margin-left:7px}
448
451
  .attached{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:9px}
449
452
  .attached[hidden]{display:none}
450
453
  .thumb{position:relative;width:60px;height:60px;border-radius:8px;overflow:hidden;
@@ -688,6 +691,21 @@ dialog:has(.dlg.wide){max-width:560px}
688
691
  border-bottom:1px solid var(--line);font-size:11.5px;flex:0 0 auto}
689
692
  .whead .wt{color:var(--cyan);font-size:10px;letter-spacing:.09em;text-transform:uppercase;
690
693
  flex:0 0 auto}
694
+ /*
695
+ * A path truncated at the front, without bidi rearranging it.
696
+ *
697
+ * direction:rtl is what puts the ellipsis at the start, which is the only useful place for a path —
698
+ * everything that identifies it is at the end. But a slash is a bidi-neutral character, so in an
699
+ * RTL paragraph the leading one is reordered to the far side and /home/kona/…/page.tsx renders as
700
+ * home/kona/…/page.tsx/ — which is what somebody reported seeing, and it was not a stray character
701
+ * in the data.
702
+ *
703
+ * The fix is an isolated LTR run inside the RTL box: the truncation stays at the front and the text
704
+ * inside is laid out as the plain string it is. Checked in Chromium against the alternatives —
705
+ * unicode-bidi:plaintext fixes the order but moves the ellipsis to the end, which throws away the
706
+ * filename.
707
+ */
708
+ .pathltr{direction:ltr;unicode-bidi:isolate}
691
709
  .whead .wp{font-family:var(--mono);font-size:12px;flex:1;min-width:0;overflow:hidden;
692
710
  text-overflow:ellipsis;white-space:nowrap;direction:rtl;text-align:left}
693
711
  .whead .wms{flex:0 0 auto;font-size:10.5px;color:var(--dim);font-variant-numeric:tabular-nums;
@@ -1 +1 @@
1
- {"version":3,"file":"ui-css.js","sourceRoot":"","sources":["../../src/web/ui-css.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH;;;;;;;;;;;;GAYG;AACH,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;CAkBZ,CAAC;AAEF,MAAM,UAAU,GAAG,CAAC,OAAe;IACjC,OAAO;;;;;;;;;;;;;;;;;gBAiBO,OAAO;;;oCAGa,IAAI;;2BAEb,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2lC9B,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"ui-css.js","sourceRoot":"","sources":["../../src/web/ui-css.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH;;;;;;;;;;;;GAYG;AACH,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;CAkBZ,CAAC;AAEF,MAAM,UAAU,GAAG,CAAC,OAAe;IACjC,OAAO;;;;;;;;;;;;;;;;;gBAiBO,OAAO;;;oCAGa,IAAI;;2BAEb,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6mC9B,CAAC;AACF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koneck",
3
- "version": "2.69.0",
3
+ "version": "2.71.0",
4
4
  "description": "Kinetically Orchestrated Neural Execution Engine for Code",
5
5
  "type": "module",
6
6
  "bin": {
@@ -63,6 +63,7 @@
63
63
  "@types/diff": "^5.2.1",
64
64
  "@types/node": "^22.0.0",
65
65
  "@types/react": "^18.3.31",
66
+ "ink-testing-library": "^4.0.0",
66
67
  "tsx": "^4.19.0",
67
68
  "typescript": "^5.5.0",
68
69
  "vitest": "^2.0.0"