koneck 2.38.0 → 2.38.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"ui-client.d.ts","sourceRoot":"","sources":["../../src/web/ui-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,IAAI,MAAM,CA0yFrC"}
1
+ {"version":3,"file":"ui-client.d.ts","sourceRoot":"","sources":["../../src/web/ui-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,IAAI,MAAM,CA29FrC"}
@@ -724,6 +724,117 @@ function renderTreeInto(host, entries, depth) {
724
724
  if (!entries.length) host.appendChild(el('div', 'shead', 'empty'));
725
725
  }
726
726
 
727
+ /* ── colouring code ──────────────────────────────────────────────────────────────────────── */
728
+
729
+ /**
730
+ * Syntax colouring, as one pass over the text.
731
+ *
732
+ * Not a parser. A parser for a dozen languages is a project, and what makes code readable is
733
+ * separating the four things the eye needs first: what is prose (comments), what is data (strings
734
+ * and numbers), what is structure (keywords), and what is a name being used. A tokeniser that gets
735
+ * those right at a glance beats a parser that gets everything right next week.
736
+ *
737
+ * One regular expression, its alternatives ordered so the greedy ones win: a keyword inside a
738
+ * string is part of the string, and a string inside a comment is part of the comment. Getting that
739
+ * order wrong is how one apostrophe in a comment colours the next forty lines as text.
740
+ */
741
+ const KEYWORDS = {
742
+ js: 'abstract as async await break case catch class const continue debugger declare default delete do else enum export extends finally for from function get if implements import in infer instanceof interface is keyof let new of package private protected public readonly return satisfies set static super switch symbol this throw try type typeof var void while with yield',
743
+ py: 'and as assert async await break class continue def del elif else except finally for from global if import in is lambda nonlocal not or pass raise return try while with yield match case',
744
+ go: 'break case chan const continue default defer else fallthrough for func go goto if import interface map package range return select struct switch type var',
745
+ rs: 'as async await break const continue crate dyn else enum extern fn for if impl in let loop match mod move mut pub ref return self static struct super trait type unsafe use where while',
746
+ sh: 'if then else elif fi for while do done case esac function return local export source alias unset trap exit',
747
+ css: 'important media supports keyframes import charset font-face root',
748
+ sql: 'select from where insert into values update set delete create table alter drop join left right inner outer on group by order having limit offset union as and or not null primary key foreign references index view',
749
+ };
750
+ const LITERALS = 'true false null undefined None True False nil NaN Infinity self this super void';
751
+
752
+ /** Which vocabulary a file is written in. */
753
+ function langOf(name) {
754
+ const ext = (String(name).match(/\.([a-z0-9]+)$/i) || [, ''])[1].toLowerCase();
755
+ if (/^(ts|tsx|js|jsx|mjs|cjs|mts|cts|json|jsonc)$/.test(ext)) return 'js';
756
+ if (/^(py|pyi)$/.test(ext)) return 'py';
757
+ if (ext === 'go') return 'go';
758
+ if (ext === 'rs') return 'rs';
759
+ if (/^(sh|bash|zsh|env)$/.test(ext)) return 'sh';
760
+ if (/^(dockerfile|makefile)/i.test(String(name).split('/').pop() || '')) return 'sh';
761
+ if (/^(css|scss|less)$/.test(ext)) return 'css';
762
+ if (ext === 'sql') return 'sql';
763
+ if (/^(html|htm|xml|svg|vue|svelte)$/.test(ext)) return 'html';
764
+ if (/^(yml|yaml|toml|ini|conf)$/.test(ext)) return 'yaml';
765
+ return 'plain';
766
+ }
767
+
768
+ const escHtml = (t) => t.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
769
+
770
+ /** The pattern for a language, built once and kept. */
771
+ const PATTERNS = {};
772
+ function patternFor(lang) {
773
+ if (PATTERNS[lang]) return PATTERNS[lang];
774
+ const hash = /^(py|sh|yaml)$/.test(lang);
775
+ // A backtick, built from its code point: a literal one would end the template this whole script
776
+ // lives in.
777
+ const T = String.fromCharCode(96);
778
+ const parts = [
779
+ // Comments first, longest form first, so nothing inside one is read again.
780
+ '(?<com>/\\*[\\s\\S]*?\\*/' + (hash ? '|#[^\\n]*' : '') + '|//[^\\n]*|<!--[\\s\\S]*?-->)',
781
+ // Then strings: double, single, Python triple, and template literals.
782
+ '(?<str>"""[\\s\\S]*?"""' +
783
+ "|'''[\\s\\S]*?'''" +
784
+ '|"(?:\\\\.|[^"\\\\\\n])*"' +
785
+ "|'(?:\\\\.|[^'\\\\\\n])*'" +
786
+ '|' + T + '(?:\\\\.|[^' + T + '\\\\])*' + T + ')',
787
+ '(?<num>\\b(?:0[xXbBoO][0-9a-fA-F_]+|\\d[\\d_]*(?:\\.[\\d_]+)?(?:[eE][+-]?\\d+)?)\\b)',
788
+ // A name followed by an opening bracket is being called.
789
+ '(?<fun>\\b[A-Za-z_$][\\w$]*(?=\\s*\\())',
790
+ // A capitalised name is a type or a class, near enough to be worth colouring as one.
791
+ '(?<typ>\\b[A-Z][A-Za-z0-9_$]*\\b)',
792
+ '(?<wrd>\\b[A-Za-z_$][\\w$-]*\\b)',
793
+ '(?<pun>[{}()\\[\\];:,.<>=+\\-*/%!?&|^~@#]+)',
794
+ ];
795
+ const re = new RegExp(parts.join('|'), 'g');
796
+ const words = new Set(((KEYWORDS[lang] || '') + ' ' + LITERALS).split(/\s+/).filter(Boolean));
797
+ PATTERNS[lang] = { re: re, words: words };
798
+ return PATTERNS[lang];
799
+ }
800
+
801
+ /**
802
+ * The text as coloured HTML.
803
+ *
804
+ * Line structure is preserved exactly. The caller lays this behind a textarea, and one swallowed
805
+ * newline would shift every colour below it onto the wrong line.
806
+ */
807
+ function colourise(text, filename) {
808
+ const lang = langOf(filename);
809
+ if (lang === 'plain') return escHtml(text);
810
+ const spec = patternFor(lang);
811
+ const re = spec.re, words = spec.words;
812
+ let out = '';
813
+ let last = 0;
814
+ re.lastIndex = 0;
815
+ let m;
816
+ while ((m = re.exec(text)) !== null) {
817
+ if (m.index > last) out += escHtml(text.slice(last, m.index));
818
+ const g = m.groups || {};
819
+ const raw = m[0];
820
+ let cls = null;
821
+ if (g.com !== undefined) cls = 'tk-com';
822
+ else if (g.str !== undefined) cls = 'tk-str';
823
+ else if (g.num !== undefined) cls = 'tk-num';
824
+ else if (g.fun !== undefined) cls = words.has(raw) ? 'tk-key' : 'tk-fun';
825
+ else if (g.typ !== undefined) cls = words.has(raw) ? 'tk-key' : 'tk-typ';
826
+ else if (g.wrd !== undefined) cls = words.has(raw) ? 'tk-key' : null;
827
+ else if (g.pun !== undefined) cls = 'tk-pun';
828
+ out += cls ? '<span class="' + cls + '">' + escHtml(raw) + '</span>' : escHtml(raw);
829
+ last = m.index + raw.length;
830
+ // A zero-length match would spin for ever. Nothing here should make one, but a hung tab is
831
+ // unrecoverable and the guard costs nothing.
832
+ if (raw.length === 0) re.lastIndex++;
833
+ }
834
+ out += escHtml(text.slice(last));
835
+ return out;
836
+ }
837
+
727
838
  /**
728
839
  * A file, open and editable.
729
840
  *
@@ -774,48 +885,110 @@ async function openFile(rel) {
774
885
  const redo = el('button', 'ebtn', 'redo');
775
886
  const save = el('button', 'ebtn save', 'save');
776
887
  const revert = el('button', 'ebtn', 'revert');
777
- const status = el('span', 'est', data.truncated ? 'read only opened truncated' : 'saved');
888
+ const status = el('span', 'est', data.truncated ? 'read only - opened truncated' : 'saved');
778
889
  status.id = 'edstate';
779
890
  for (const b of [undo, redo, save, revert]) b.type = 'button';
780
891
  bar.appendChild(undo); bar.appendChild(redo); bar.appendChild(save); bar.appendChild(revert);
781
892
  bar.appendChild(status);
782
893
  host.appendChild(bar);
783
894
 
895
+ /*
896
+ * Three columns that must agree: numbers, colours, and the text you actually type into.
897
+ *
898
+ * The textarea is on top with transparent text and a visible caret; the coloured copy is a pre
899
+ * directly behind it; the numbers are a third column. All three share one font, size, line
900
+ * height and padding, and the textarea's scroll drives the other two — a pixel of difference
901
+ * anywhere and the colours sit on the wrong characters.
902
+ */
903
+ const ed = el('div', 'ed');
904
+ const gut = el('div', 'egut');
905
+ const stack = el('div', 'estack');
906
+ const hl = document.createElement('pre');
907
+ hl.className = 'ehl';
908
+ hl.setAttribute('aria-hidden', 'true');
784
909
  const area = document.createElement('textarea');
785
910
  area.className = 'edit';
786
911
  area.spellcheck = false;
787
912
  area.value = data.text;
788
- // A truncated file must never be saved: the editor only ever had the first part of it.
789
913
  area.readOnly = data.truncated === true;
790
914
  save.disabled = data.truncated === true;
791
- host.appendChild(area);
915
+ stack.appendChild(hl);
916
+ stack.appendChild(area);
917
+ ed.appendChild(gut);
918
+ ed.appendChild(stack);
919
+ host.appendChild(ed);
920
+
921
+ /** Redraws the colours and the numbers for whatever the textarea now holds. */
922
+ const repaint = () => {
923
+ const text = area.value;
924
+ hl.innerHTML = colourise(text, data.path);
925
+ const count = text.split('\n').length;
926
+ // Rebuilt only when the count changes: typing inside a line must not rebuild a 900-row column
927
+ // on every keystroke.
928
+ if (gut.dataset.count !== String(count)) {
929
+ const numbers = [];
930
+ for (let i = 1; i <= count; i++) numbers.push(i);
931
+ gut.textContent = numbers.join('\n');
932
+ gut.dataset.count = String(count);
933
+ }
934
+ };
935
+ repaint();
936
+
937
+ // Colouring a large file on every keystroke is wasted work nobody sees; a frame's delay is
938
+ // invisible and keeps typing smooth in a 20,000-character file.
939
+ let pending = null;
940
+ const repaintSoon = () => {
941
+ if (pending) return;
942
+ pending = requestAnimationFrame(() => { pending = null; repaint(); });
943
+ };
792
944
 
945
+ const sync = () => {
946
+ hl.scrollTop = area.scrollTop;
947
+ hl.scrollLeft = area.scrollLeft;
948
+ gut.scrollTop = area.scrollTop;
949
+ };
950
+ area.addEventListener('scroll', sync);
793
951
  area.addEventListener('input', () => {
794
952
  editing.dirty = area.value !== editing.text;
795
953
  editState(editing.dirty ? 'unsaved' : 'saved', editing.dirty ? 'dirty' : '');
954
+ repaintSoon();
955
+ sync();
796
956
  });
797
- // The browser's own undo, so every shortcut a person knows already works.
957
+
958
+ // The browser's own undo, so every shortcut a person already knows works — including the ones
959
+ // nobody thinks about, and undo history that survives switching tabs.
798
960
  undo.onclick = () => { area.focus(); document.execCommand('undo'); area.dispatchEvent(new Event('input')); };
799
961
  redo.onclick = () => { area.focus(); document.execCommand('redo'); area.dispatchEvent(new Event('input')); };
800
962
  revert.onclick = () => {
801
963
  area.value = editing.text;
802
964
  editing.dirty = false;
803
965
  editState('saved');
966
+ repaint();
804
967
  };
805
968
 
969
+ // Tab inserts a tab rather than leaving the editor, which is what it means in code.
970
+ area.addEventListener('keydown', (ev) => {
971
+ if (ev.key === 'Tab' && !ev.ctrlKey && !ev.metaKey) {
972
+ ev.preventDefault();
973
+ const at = area.selectionStart, to = area.selectionEnd;
974
+ area.setRangeText(' ', at, to, 'end');
975
+ area.dispatchEvent(new Event('input'));
976
+ }
977
+ });
978
+
806
979
  const doSave = async () => {
807
980
  if (area.readOnly) return;
808
- editState('saving');
981
+ editState('saving...');
809
982
  let out;
810
983
  try {
811
984
  out = await api('/api/file', { method: 'POST', body: JSON.stringify({
812
985
  path: editing.path, text: area.value, size: editing.size, mtimeMs: editing.mtimeMs }) });
813
986
  } catch (e) {
814
987
  // A refusal because the agent wrote the file while it was open is the one worth acting on,
815
- // so it comes with the way through rather than just a message.
988
+ // so it comes with the way through rather than only a message.
816
989
  editState(e.message.slice(0, 90), 'bad');
817
- if (e.status === 409) {
818
- const again = el('button', 'ebtn', 'reload from disk');
990
+ if (e.status === 409 && !bar.querySelector('.reload')) {
991
+ const again = el('button', 'ebtn reload', 'reload from disk');
819
992
  again.type = 'button';
820
993
  again.onclick = () => openFile(rel);
821
994
  bar.insertBefore(again, status);
@@ -1945,6 +2118,10 @@ const abort = () => api('/api/abort', { method: 'POST',
1945
2118
  $('stop').onclick = abort;
1946
2119
  $('livestop').onclick = abort;
1947
2120
  $('tab-chat').onclick = () => setView('chat');
2121
+ // Wired one by one, and these two were added to the tab bar and to setView but never here — so
2122
+ // they were painted, highlighted on hover, and did nothing at all when clicked.
2123
+ $('tab-terminal').onclick = () => setView('terminal');
2124
+ $('tab-preview').onclick = () => setView('preview');
1948
2125
  $('tab-files').onclick = () => setView('files');
1949
2126
  $('tab-search').onclick = () => setView('search');
1950
2127
  $('tab-review').onclick = () => setView('review');
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwyFlB,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAy9FlB,CAAC;AACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ui-css.d.ts","sourceRoot":"","sources":["../../src/web/ui-css.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAmuB3C"}
1
+ {"version":3,"file":"ui-css.d.ts","sourceRoot":"","sources":["../../src/web/ui-css.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAqxB3C"}
@@ -22,6 +22,9 @@ export function css(markUrl) {
22
22
  --add-bg:#e4f6ea; --add-ink:#0d5c37; --del-bg:#fdeaea; --del-ink:#8f1c22;
23
23
  /* The region a tool is reading, and the exact text it matched inside that region. */
24
24
  --attn-bg:#e8f4fb; --attn-hit:#bde4f7;
25
+ /* Syntax. Light theme. */
26
+ --syn-com:#6a7d8c; --syn-str:#0a6b4f; --syn-num:#9a4b00; --syn-key:#8a2be2;
27
+ --syn-typ:#0b6ca8; --syn-fun:#0a5ea8; --syn-pun:#6b7785;
25
28
  --shadow:0 1px 2px rgba(15,28,34,.07), 0 8px 24px rgba(15,28,34,.05);
26
29
  --radius:10px; --mono:ui-monospace,'SF Mono',Menlo,Consolas,'DejaVu Sans Mono',monospace;
27
30
  --mark:url("${markUrl}");
@@ -34,7 +37,11 @@ export function css(markUrl) {
34
37
  --violet:#b39cfb;
35
38
  --add-bg:#0f2a1e; --add-ink:#7ee0a7; --del-bg:#2b1416; --del-ink:#f4a3a6;
36
39
  --attn-bg:#10222c; --attn-hit:#1c3f52;
40
+ --syn-com:#6b8296; --syn-str:#7ee0a7; --syn-num:#f0b166; --syn-key:#c792ea;
41
+ --syn-typ:#63c8f0; --syn-fun:#82aaff; --syn-pun:#7a8899;
37
42
  --attn-bg:#10222c; --attn-hit:#1c3f52;
43
+ --syn-com:#6b8296; --syn-str:#7ee0a7; --syn-num:#f0b166; --syn-key:#c792ea;
44
+ --syn-typ:#63c8f0; --syn-fun:#82aaff; --syn-pun:#7a8899;
38
45
  --shadow:0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.3);
39
46
  }
40
47
  }
@@ -264,7 +271,9 @@ header{height:47px;flex:0 0 47px;border-bottom:1px solid var(--line);display:fle
264
271
  .empty{text-align:center;padding:12vh 0;color:var(--dim);font-size:13px}
265
272
 
266
273
  /* ── file tree ───────────────────────────────────────────── */
267
- .split{display:flex;height:100%;min-height:0}
274
+ /* Like #chatview: the tab itself does not scroll, so the panes inside it can. */
275
+ #filesview{overflow:hidden;display:flex}
276
+ .split{display:flex;flex:1;min-height:0;min-width:0}
268
277
  .tree{width:300px;flex:0 0 300px;border-right:1px solid var(--line);overflow-y:auto;padding:8px 0}
269
278
  .tnode{display:flex;align-items:center;gap:6px;padding:3px 12px;width:100%;text-align:left;
270
279
  font-size:12.5px;font-family:var(--mono);color:var(--muted);white-space:nowrap}
@@ -273,7 +282,7 @@ header{height:47px;flex:0 0 47px;border-bottom:1px solid var(--line);display:fle
273
282
  .tnode .ic{width:13px;flex:0 0 13px;color:var(--dim);text-align:center}
274
283
  .tnode.d .ic{color:var(--cyan)}
275
284
  .tnode .sz{margin-left:auto;font-size:10.5px;color:var(--dim)}
276
- .fileview{flex:1;min-width:0;overflow:auto;display:flex;flex-direction:column}
285
+ .fileview{flex:1;min-width:0;min-height:0;overflow:hidden;display:flex;flex-direction:column}
277
286
  .fvhead{position:sticky;top:0;background:var(--panel);border-bottom:1px solid var(--line);
278
287
  padding:8px 14px;font-family:var(--mono);font-size:12px;display:flex;gap:12px;align-items:center}
279
288
  .fvhead .fsz{color:var(--dim);font-size:11px}
@@ -360,7 +369,16 @@ footer[hidden]{display:none}
360
369
  background:var(--panel-2);border-radius:var(--radius);padding:9px 10px;
361
370
  transition:border-color .14s,box-shadow .14s}
362
371
  .cin:focus-within{border-color:var(--cyan);box-shadow:0 0 0 3px rgba(77,214,232,.13)}
363
- textarea{flex:1;background:none;border:0;outline:0;resize:none;max-height:200px;
372
+ /*
373
+ * The composer's textarea, scoped to the composer.
374
+ *
375
+ * This was a bare element selector, so its max-height applied to every textarea on the page —
376
+ * including the file editor, which is why a 21K file was shown 200 pixels tall with 570 pixels of
377
+ * empty panel under it. A broad element selector setting a property that a later class does not
378
+ * happen to override is the same trap as [hidden]: measured 766px of panel, 200px of editor, and
379
+ * flex-grow reading 1 the whole time.
380
+ */
381
+ .cin textarea{flex:1;background:none;border:0;outline:0;resize:none;max-height:200px;
364
382
  line-height:1.5;font-size:14px}
365
383
  textarea::placeholder{color:var(--dim)}
366
384
  .send{width:31px;height:31px;flex:0 0 31px;border-radius:50%;
@@ -724,10 +742,42 @@ body[data-tint="soft"]{filter:contrast(.92) brightness(1.04)}
724
742
  .edbar .est{margin-left:auto;color:var(--dim);white-space:nowrap}
725
743
  .edbar .est.dirty{color:var(--amber)}
726
744
  .edbar .est.bad{color:var(--red)}
727
- /* One textarea, so the browser's own undo and redo work — a hand-rolled stack would be worse. */
728
- .edit{flex:1;width:100%;min-height:0;resize:none;border:0;outline:none;padding:12px 14px;
729
- background:var(--bg);color:var(--ink);font-family:var(--mono);font-size:12.5px;line-height:1.5;
730
- tab-size:2;white-space:pre;overflow:auto}
745
+ /*
746
+ * An editor that is coloured and numbered, and still a real textarea.
747
+ *
748
+ * The textarea sits on top with transparent text and a visible caret; the coloured copy is a <pre>
749
+ * directly behind it, and the line numbers are a third column. All three share the same font,
750
+ * size, line height and padding, and the textarea's scroll drives the other two — if any of those
751
+ * differ by a pixel the colours drift off the characters they belong to.
752
+ *
753
+ * Done this way so undo and redo stay the browser's own. A contenteditable div with syntax colours
754
+ * would look the same and break every shortcut a person already knows.
755
+ */
756
+ .ed{flex:1;display:flex;min-height:0;background:var(--bg)}
757
+ .ed .egut{flex:0 0 auto;overflow:hidden;padding:12px 10px 12px 14px;text-align:right;
758
+ color:var(--dim);background:var(--panel);border-right:1px solid var(--line);user-select:none;
759
+ font-family:var(--mono);font-size:12.5px;line-height:1.5;font-variant-numeric:tabular-nums;
760
+ white-space:pre}
761
+ .estack{position:relative;flex:1;min-width:0}
762
+ .ehl,.edit{position:absolute;inset:0;margin:0;padding:12px 14px;border:0;
763
+ font-family:var(--mono);font-size:12.5px;line-height:1.5;tab-size:2;
764
+ white-space:pre;overflow:auto;word-break:normal;overflow-wrap:normal}
765
+ .ehl{pointer-events:none;color:var(--ink)}
766
+ /* Transparent text over the coloured copy, with the caret and the selection still visible. */
767
+ .edit{background:transparent;color:transparent;caret-color:var(--cyan);resize:none;outline:none;
768
+ max-height:none}
769
+ .edit::selection{background:var(--attn-hit);color:transparent}
770
+
771
+ /* The colours. Chosen to read at 12.5px in both themes rather than to look like a screenshot. */
772
+ .tk-com{color:var(--syn-com);font-style:italic}
773
+ .tk-str{color:var(--syn-str)}
774
+ .tk-num{color:var(--syn-num)}
775
+ .tk-key{color:var(--syn-key)}
776
+ .tk-typ{color:var(--syn-typ)}
777
+ .tk-fun{color:var(--syn-fun)}
778
+ .tk-pun{color:var(--syn-pun)}
779
+ .tk-tag{color:var(--syn-key)}
780
+ .tk-att{color:var(--syn-fun)}
731
781
 
732
782
  /* Carrying on from a transcript, offered in the bar that says it is read only. */
733
783
  .readonly .rgap{flex:1}
@@ -1 +1 @@
1
- {"version":3,"file":"ui-css.js","sourceRoot":"","sources":["../../src/web/ui-css.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,GAAG,CAAC,OAAe;IACjC,OAAO;;;;;;;;;;;;gBAYO,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqtBtB,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"ui-css.js","sourceRoot":"","sources":["../../src/web/ui-css.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,GAAG,CAAC,OAAe;IACjC,OAAO;;;;;;;;;;;;;;;gBAeO,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAowBtB,CAAC;AACF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koneck",
3
- "version": "2.38.0",
3
+ "version": "2.38.1",
4
4
  "description": "Kinetically Orchestrated Neural Execution Engine for Code",
5
5
  "type": "module",
6
6
  "bin": {