koneck 2.34.0 → 2.35.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.
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +27 -0
- package/dist/engine.js.map +1 -1
- package/dist/router-failure.d.ts +56 -0
- package/dist/router-failure.d.ts.map +1 -0
- package/dist/router-failure.js +118 -0
- package/dist/router-failure.js.map +1 -0
- package/dist/web/ui-client.d.ts.map +1 -1
- package/dist/web/ui-client.js +261 -54
- package/dist/web/ui-client.js.map +1 -1
- package/dist/web/ui-css.d.ts.map +1 -1
- package/dist/web/ui-css.js +82 -5
- package/dist/web/ui-css.js.map +1 -1
- package/dist/web/ui.d.ts.map +1 -1
- package/dist/web/ui.js +9 -1
- package/dist/web/ui.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router-failure.js","sourceRoot":"","sources":["../src/router-failure.ts"],"names":[],"mappings":"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"}
|
|
@@ -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,
|
|
1
|
+
{"version":3,"file":"ui-client.d.ts","sourceRoot":"","sources":["../../src/web/ui-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAk1ErC"}
|
package/dist/web/ui-client.js
CHANGED
|
@@ -83,11 +83,14 @@ const el = (tag, cls, text) => {
|
|
|
83
83
|
};
|
|
84
84
|
const mark = (cls) => el('div', 'mark' + (cls ? ' ' + cls : ''));
|
|
85
85
|
const stream = () => $('stream');
|
|
86
|
+
// The conversation column, which is what scrolls. It used to be the whole chat tab, and the file
|
|
87
|
+
// panel beside it scrolled along with the transcript instead of on its own.
|
|
88
|
+
const conv = () => $('conv');
|
|
86
89
|
const atBottom = () => {
|
|
87
|
-
const f =
|
|
90
|
+
const f = conv();
|
|
88
91
|
return f.scrollHeight - f.scrollTop - f.clientHeight < 100;
|
|
89
92
|
};
|
|
90
|
-
const stick = (was) => { if (was)
|
|
93
|
+
const stick = (was) => { if (was) conv().scrollTop = conv().scrollHeight; };
|
|
91
94
|
const add = (node) => { const was = atBottom(); stream().appendChild(node); stick(was); return node; };
|
|
92
95
|
const clearHero = () => { const h = stream().querySelector('.hero'); if (h) h.remove(); };
|
|
93
96
|
/**
|
|
@@ -402,22 +405,24 @@ function onEvent(e) {
|
|
|
402
405
|
turn.tools += 1;
|
|
403
406
|
$('livetool').textContent = e.name + (e.detail ? ' ' + e.detail : '');
|
|
404
407
|
renderTool(e);
|
|
405
|
-
// Follow it:
|
|
406
|
-
|
|
408
|
+
// Follow it: not just which file, but which lines and which pattern.
|
|
409
|
+
watchTool(e, false);
|
|
407
410
|
break;
|
|
408
411
|
case 'tool-output':
|
|
409
412
|
if (state.lastTool) {
|
|
410
413
|
state.lastTool.body.textContent =
|
|
411
414
|
(state.lastTool.body.textContent + e.line + '\n').slice(-5000);
|
|
412
415
|
}
|
|
416
|
+
// And in the panel, where a running command is the thing being watched.
|
|
417
|
+
watchOutput(e.line);
|
|
413
418
|
break;
|
|
414
419
|
case 'tool-done': {
|
|
415
420
|
finishTool(e);
|
|
416
421
|
setBusy(true, 'thinking');
|
|
417
422
|
$('livetool').textContent = '';
|
|
418
423
|
// Once it has finished writing, the diff is what there is to see.
|
|
419
|
-
|
|
420
|
-
|
|
424
|
+
watchTool(e, true);
|
|
425
|
+
stopWatchClock();
|
|
421
426
|
break;
|
|
422
427
|
}
|
|
423
428
|
case 'plan': renderPlan(e.steps); break;
|
|
@@ -1627,7 +1632,7 @@ async function openPast(file) {
|
|
|
1627
1632
|
if (!data.entries.length) {
|
|
1628
1633
|
stream().appendChild(el('div', 'empty', 'This session has no transcript in it.'));
|
|
1629
1634
|
}
|
|
1630
|
-
|
|
1635
|
+
conv().scrollTop = 0;
|
|
1631
1636
|
}
|
|
1632
1637
|
|
|
1633
1638
|
async function refreshSessions() {
|
|
@@ -1849,40 +1854,59 @@ $('pmode').onclick = async () => {
|
|
|
1849
1854
|
}
|
|
1850
1855
|
$('modedlg').showModal();
|
|
1851
1856
|
};
|
|
1852
|
-
/* ──
|
|
1857
|
+
/* ── watching it work ─────────────────────────────────────────────────────────────────────── */
|
|
1853
1858
|
|
|
1854
1859
|
/**
|
|
1855
|
-
* The panel beside the conversation, showing
|
|
1860
|
+
* The panel beside the conversation, showing what the agent is doing while it does it.
|
|
1856
1861
|
*
|
|
1857
|
-
*
|
|
1858
|
-
*
|
|
1859
|
-
*
|
|
1862
|
+
* Not "the last file it mentioned". A tool call says more than which file: read_files says which
|
|
1863
|
+
* LINES, grep_search says which pattern, execute_command streams its output as it runs. All of that
|
|
1864
|
+
* was already arriving and being thrown away, and the difference between showing a file and showing
|
|
1865
|
+
* the four lines actually read is the difference between a decoration and a view.
|
|
1866
|
+
*
|
|
1867
|
+
* On by default, remembered per browser, and closable three ways — the pill, the panel's own
|
|
1868
|
+
* button, and Escape — because a panel that follows you around had better be easy to stop.
|
|
1860
1869
|
*/
|
|
1861
1870
|
const follow = {
|
|
1862
1871
|
on: (() => { try { return localStorage.getItem('koneck.follow') !== 'off'; } catch (e) { return true; } })(),
|
|
1863
1872
|
path: null,
|
|
1864
|
-
/**
|
|
1873
|
+
/** 'code' | 'diff' | 'term' — what the body is currently showing. */
|
|
1874
|
+
kind: null,
|
|
1875
|
+
verb: '',
|
|
1865
1876
|
busy: false,
|
|
1866
|
-
/** The path a newer tool named while a fetch was running. */
|
|
1867
1877
|
pending: null,
|
|
1878
|
+
/** Line numbers under attention, one-based. */
|
|
1879
|
+
attention: [],
|
|
1880
|
+
/** Text to mark inside those lines: a grep pattern, or the text a replace is looking for. */
|
|
1881
|
+
needle: null,
|
|
1882
|
+
/** Lines of a running command's output, in arrival order. */
|
|
1883
|
+
output: [],
|
|
1884
|
+
/** Everything this turn has touched: { label, path, verb, kind, ok }. */
|
|
1885
|
+
trail: [],
|
|
1886
|
+
startedAt: 0,
|
|
1887
|
+
timer: null,
|
|
1868
1888
|
};
|
|
1869
1889
|
|
|
1870
|
-
/**
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
if (
|
|
1885
|
-
return
|
|
1890
|
+
/** How many output lines a command keeps in view. Beyond this the head is dropped. */
|
|
1891
|
+
const WATCH_OUTPUT_LINES = 400;
|
|
1892
|
+
|
|
1893
|
+
/** What a tool is doing, as a word. The verb is the most informative thing in the header. */
|
|
1894
|
+
function verbFor(name) {
|
|
1895
|
+
const n = String(name || '');
|
|
1896
|
+
if (/^read_files$/.test(n)) return 'reading';
|
|
1897
|
+
if (/grep|search_files/.test(n)) return 'searching';
|
|
1898
|
+
if (/glob/.test(n)) return 'matching';
|
|
1899
|
+
if (/list_dir/.test(n)) return 'listing';
|
|
1900
|
+
if (/^write_file$/.test(n)) return 'writing';
|
|
1901
|
+
if (/edit|patch|apply_diff|search_replace/.test(n)) return 'editing';
|
|
1902
|
+
if (/execute_command|run/.test(n)) return 'running';
|
|
1903
|
+
if (/delete/.test(n)) return 'deleting';
|
|
1904
|
+
if (/move|rename/.test(n)) return 'moving';
|
|
1905
|
+
if (/^git_/.test(n)) return n.replace('git_', 'git ');
|
|
1906
|
+
if (/code_lookup/.test(n)) return 'asking the compiler';
|
|
1907
|
+
if (/fetch_url|web_search/.test(n)) return 'fetching';
|
|
1908
|
+
if (/^mcp__/.test(n)) return n.split('__')[1] || 'calling';
|
|
1909
|
+
return 'working';
|
|
1886
1910
|
}
|
|
1887
1911
|
|
|
1888
1912
|
/** Whether a tool changes a file, which decides whether the panel shows content or a diff. */
|
|
@@ -1890,37 +1914,217 @@ function toolWrites(name) {
|
|
|
1890
1914
|
return /write|edit|patch|replace|apply|create|delete|move|rename|format/i.test(name || '');
|
|
1891
1915
|
}
|
|
1892
1916
|
|
|
1917
|
+
/**
|
|
1918
|
+
* Everything a tool call says about where it is looking.
|
|
1919
|
+
*
|
|
1920
|
+
* The arguments are the only honest source for this: a detail line is a summary written for a
|
|
1921
|
+
* transcript row, and guessing a path out of prose is how a panel ends up confidently showing the
|
|
1922
|
+
* wrong file.
|
|
1923
|
+
*/
|
|
1924
|
+
function targetOf(e) {
|
|
1925
|
+
const out = { path: null, lines: [], needle: null, command: null };
|
|
1926
|
+
let parsed = null;
|
|
1927
|
+
try { parsed = e.args ? JSON.parse(e.args) : null; } catch (err) { parsed = null; }
|
|
1928
|
+
if (parsed) {
|
|
1929
|
+
for (const key of ['path', 'file', 'file_path', 'filename']) {
|
|
1930
|
+
if (typeof parsed[key] === 'string' && parsed[key].trim()) { out.path = parsed[key].trim(); break; }
|
|
1931
|
+
}
|
|
1932
|
+
if (!out.path && Array.isArray(parsed.paths) && typeof parsed.paths[0] === 'string') {
|
|
1933
|
+
out.path = parsed.paths[0];
|
|
1934
|
+
}
|
|
1935
|
+
// read_files says which lines. This is the whole point: showing the region, not the file.
|
|
1936
|
+
if (typeof parsed.offset === 'number') {
|
|
1937
|
+
const from = Math.max(1, parsed.offset);
|
|
1938
|
+
const count = typeof parsed.limit === 'number' && parsed.limit > 0 ? parsed.limit : 40;
|
|
1939
|
+
for (let i = from; i < from + Math.min(count, 400); i++) out.lines.push(i);
|
|
1940
|
+
}
|
|
1941
|
+
if (typeof parsed.pattern === 'string') out.needle = parsed.pattern;
|
|
1942
|
+
if (typeof parsed.search === 'string') out.needle = parsed.search;
|
|
1943
|
+
if (typeof parsed.command === 'string') out.command = parsed.command;
|
|
1944
|
+
}
|
|
1945
|
+
if (!out.path && !out.command && e.detail && /[./]/.test(e.detail) && !/\s/.test(e.detail.trim())) {
|
|
1946
|
+
out.path = e.detail.trim();
|
|
1947
|
+
}
|
|
1948
|
+
return out;
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1893
1951
|
function setFollow(on) {
|
|
1894
1952
|
follow.on = on;
|
|
1895
1953
|
try { localStorage.setItem('koneck.follow', on ? 'on' : 'off'); } catch (e) {}
|
|
1896
1954
|
$('pfollow').innerHTML = 'follow <b>' + (on ? 'on' : 'off') + '</b>';
|
|
1897
|
-
$('watch').hidden = !on || !follow.path;
|
|
1898
|
-
if (on
|
|
1955
|
+
$('watch').hidden = !on || (!follow.path && follow.kind !== 'term');
|
|
1956
|
+
if (!on) stopWatchClock();
|
|
1957
|
+
else if (follow.path) void showWatched(follow.path, follow.kind === 'diff');
|
|
1899
1958
|
}
|
|
1900
1959
|
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1960
|
+
function stopWatchClock() {
|
|
1961
|
+
if (follow.timer) { clearInterval(follow.timer); follow.timer = null; }
|
|
1962
|
+
$('wms').textContent = '';
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
/** The header: the verb, the target, and how long this step has been going. */
|
|
1966
|
+
function watchHead(verb, target) {
|
|
1967
|
+
$('wtool').textContent = verb;
|
|
1968
|
+
$('wpath').textContent = target;
|
|
1969
|
+
follow.startedAt = Date.now();
|
|
1970
|
+
if (!follow.timer) {
|
|
1971
|
+
follow.timer = setInterval(() => {
|
|
1972
|
+
$('wms').textContent = ms(Date.now() - follow.startedAt);
|
|
1973
|
+
}, 200);
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
/** Adds to the trail, or marks the existing entry live. Bounded, newest last. */
|
|
1978
|
+
function trailPush(entry) {
|
|
1979
|
+
const key = entry.label;
|
|
1980
|
+
const existing = follow.trail.find((t) => t.label === key);
|
|
1981
|
+
if (existing) { Object.assign(existing, entry); } else { follow.trail.push(entry); }
|
|
1982
|
+
while (follow.trail.length > 14) follow.trail.shift();
|
|
1983
|
+
drawTrail(key);
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
function drawTrail(activeLabel) {
|
|
1987
|
+
const host = $('wtrail');
|
|
1988
|
+
host.innerHTML = '';
|
|
1989
|
+
host.hidden = follow.trail.length === 0;
|
|
1990
|
+
for (const t of follow.trail) {
|
|
1991
|
+
const chip = el('div', 'wchip' + (t.label === activeLabel ? ' on' : '') + (t.ok === false ? ' bad' : ''));
|
|
1992
|
+
chip.appendChild(el('span', 'dot'));
|
|
1993
|
+
chip.appendChild(el('span', null, t.label));
|
|
1994
|
+
chip.title = t.verb + ' ' + (t.path || t.label);
|
|
1995
|
+
// A trail you can walk back along. Revisiting stops the panel following, because otherwise
|
|
1996
|
+
// the next tool call would yank it away from whatever you went back to look at.
|
|
1997
|
+
chip.onclick = () => {
|
|
1998
|
+
if (t.kind === 'term') { drawTerm(t.verb, t.command || t.label, t.lines || []); drawTrail(t.label); return; }
|
|
1999
|
+
follow.path = t.path;
|
|
2000
|
+
follow.attention = t.lines || [];
|
|
2001
|
+
follow.needle = t.needle || null;
|
|
2002
|
+
watchHead(t.verb, t.path || t.label);
|
|
2003
|
+
stopWatchClock();
|
|
2004
|
+
void showWatched(t.path, t.kind === 'diff');
|
|
2005
|
+
drawTrail(t.label);
|
|
2006
|
+
};
|
|
2007
|
+
host.appendChild(chip);
|
|
2008
|
+
}
|
|
2009
|
+
// Keep the live end in view without dragging the reader back if they scrolled away.
|
|
2010
|
+
if (activeLabel) host.scrollLeft = host.scrollWidth;
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
/** Points the panel at whatever a tool call is about. Cheap to call on every event. */
|
|
2014
|
+
function watchTool(e, done) {
|
|
2015
|
+
if (!follow.on) return;
|
|
2016
|
+
const t = targetOf(e);
|
|
2017
|
+
const verb = verbFor(e.name);
|
|
2018
|
+
|
|
2019
|
+
if (t.command) {
|
|
2020
|
+
// A command is not a file, and its output IS the thing to watch.
|
|
2021
|
+
follow.kind = 'term';
|
|
2022
|
+
follow.path = null;
|
|
2023
|
+
if (!done) { follow.output = []; follow.verb = verb; }
|
|
2024
|
+
$('watch').hidden = false;
|
|
2025
|
+
watchHead(verb, t.command);
|
|
2026
|
+
drawTerm(verb, t.command, follow.output);
|
|
2027
|
+
trailPush({ label: t.command.split(/\s+/)[0] || 'command', verb, kind: 'term',
|
|
2028
|
+
command: t.command, lines: follow.output, ok: done ? e.ok !== false : undefined });
|
|
2029
|
+
return;
|
|
2030
|
+
}
|
|
2031
|
+
if (!t.path) return;
|
|
2032
|
+
|
|
2033
|
+
follow.path = t.path;
|
|
2034
|
+
follow.attention = t.lines;
|
|
2035
|
+
follow.needle = t.needle;
|
|
2036
|
+
follow.kind = done && toolWrites(e.name) ? 'diff' : 'code';
|
|
2037
|
+
follow.verb = verb;
|
|
1905
2038
|
$('watch').hidden = false;
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
2039
|
+
watchHead(verb, t.path);
|
|
2040
|
+
trailPush({ label: t.path.split('/').pop() || t.path, verb, kind: follow.kind, path: t.path,
|
|
2041
|
+
lines: t.lines, needle: t.needle, ok: done ? e.ok !== false : undefined });
|
|
2042
|
+
|
|
2043
|
+
if (follow.busy) { follow.pending = [t.path, follow.kind === 'diff']; return; }
|
|
2044
|
+
void showWatched(t.path, follow.kind === 'diff');
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
/** A line of a running command, as it arrives. */
|
|
2048
|
+
function watchOutput(line) {
|
|
2049
|
+
if (!follow.on || follow.kind !== 'term') return;
|
|
2050
|
+
follow.output.push(line);
|
|
2051
|
+
while (follow.output.length > WATCH_OUTPUT_LINES) follow.output.shift();
|
|
2052
|
+
const host = $('wbody');
|
|
2053
|
+
const term = host.querySelector('.wterm');
|
|
2054
|
+
if (!term) return;
|
|
2055
|
+
const wasAtEnd = host.scrollHeight - host.scrollTop - host.clientHeight < 40;
|
|
2056
|
+
term.appendChild(el('span', 'ln', line));
|
|
2057
|
+
// Follows the tail, unless the reader has scrolled up to look at something.
|
|
2058
|
+
if (wasAtEnd) host.scrollTop = host.scrollHeight;
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
function drawTerm(verb, command, lines) {
|
|
2062
|
+
const host = $('wbody');
|
|
2063
|
+
host.innerHTML = '';
|
|
2064
|
+
const term = el('div', 'wterm');
|
|
2065
|
+
term.appendChild(el('span', 'cmd', '$ ' + command));
|
|
2066
|
+
for (const line of lines) term.appendChild(el('span', 'ln', line));
|
|
2067
|
+
if (lines.length === 0) term.appendChild(el('span', 'stub', 'waiting for output…'));
|
|
2068
|
+
host.appendChild(term);
|
|
1910
2069
|
}
|
|
1911
2070
|
|
|
1912
2071
|
/**
|
|
1913
|
-
* Draws the
|
|
2072
|
+
* Draws a file, with the region under attention marked.
|
|
1914
2073
|
*
|
|
1915
|
-
*
|
|
1916
|
-
*
|
|
1917
|
-
* lines is work the panel should be doing.
|
|
2074
|
+
* The gutter and the source are separate columns, so a highlight has to be applied to both or the
|
|
2075
|
+
* numbers and the tint drift apart the moment a line wraps — which is why neither column wraps.
|
|
1918
2076
|
*/
|
|
1919
|
-
|
|
2077
|
+
function drawCode(text, attention, needle) {
|
|
2078
|
+
const lines = text.split('\n');
|
|
2079
|
+
const hot = new Set(attention || []);
|
|
2080
|
+
const code = el('div', 'rows');
|
|
2081
|
+
|
|
2082
|
+
/*
|
|
2083
|
+
* One element per line in each column, and no newline characters inside them.
|
|
2084
|
+
*
|
|
2085
|
+
* The first version was a text node per line carrying its own newline, highlighted with an
|
|
2086
|
+
* inline-block. It measured correct — seventy rows in each column, the same four marked — and
|
|
2087
|
+
* rendered wrong: four consecutive highlighted lines came out as one, and the gutter jumped from
|
|
2088
|
+
* 44 to 49. An inline-block whose content ends in a newline, inside a pre-formatted parent, is
|
|
2089
|
+
* a layout argument nobody wins. Block rows cannot drift: row N of one column is row N of the
|
|
2090
|
+
* other, because each is exactly one line box tall by construction.
|
|
2091
|
+
*/
|
|
2092
|
+
const gut = el('div', 'gut');
|
|
2093
|
+
const src = el('div', 'src');
|
|
2094
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2095
|
+
const n = i + 1;
|
|
2096
|
+
const marked = hot.has(n);
|
|
2097
|
+
gut.appendChild(el('div', marked ? 'on' : null, String(n)));
|
|
2098
|
+
|
|
2099
|
+
const one = lines[i];
|
|
2100
|
+
const row = el('div', marked ? 'on' : null);
|
|
2101
|
+
// The exact text a pattern matched, inside the line — the finest grain the tool gives us.
|
|
2102
|
+
let did = false;
|
|
2103
|
+
if (needle && one) {
|
|
2104
|
+
const at = one.indexOf(needle);
|
|
2105
|
+
if (at !== -1) {
|
|
2106
|
+
row.appendChild(document.createTextNode(one.slice(0, at)));
|
|
2107
|
+
row.appendChild(el('span', 'hit', needle));
|
|
2108
|
+
row.appendChild(document.createTextNode(one.slice(at + needle.length)));
|
|
2109
|
+
did = true;
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
// An empty line still needs to occupy one, or every column below it is off by one.
|
|
2113
|
+
if (!did) row.textContent = one === '' ? '\u200b' : one;
|
|
2114
|
+
src.appendChild(row);
|
|
2115
|
+
}
|
|
2116
|
+
code.appendChild(gut);
|
|
2117
|
+
code.appendChild(src);
|
|
2118
|
+
return code;
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
/** Draws the panel for a path: the file itself, or its diff once it has been written. */
|
|
2122
|
+
async function showWatched(path, asDiff) {
|
|
2123
|
+
if (!path) return;
|
|
1920
2124
|
follow.busy = true;
|
|
1921
2125
|
const host = $('wbody');
|
|
1922
2126
|
try {
|
|
1923
|
-
if (
|
|
2127
|
+
if (asDiff && state.session) {
|
|
1924
2128
|
let ch = null;
|
|
1925
2129
|
try {
|
|
1926
2130
|
const out = await api('/api/changes?session=' + encodeURIComponent(state.session));
|
|
@@ -1929,7 +2133,7 @@ async function showWatched(path, wrote) {
|
|
|
1929
2133
|
if (ch && ch.diff) {
|
|
1930
2134
|
host.innerHTML = '';
|
|
1931
2135
|
const head = el('div', 'wempty');
|
|
1932
|
-
head.style.padding = '8px
|
|
2136
|
+
head.style.padding = '8px 13px';
|
|
1933
2137
|
head.textContent = '+' + ch.added + ' \u2212' + ch.removed + ' ' + ch.kind;
|
|
1934
2138
|
host.appendChild(head);
|
|
1935
2139
|
host.appendChild(renderDiff(ch.diff));
|
|
@@ -1948,15 +2152,12 @@ async function showWatched(path, wrote) {
|
|
|
1948
2152
|
host.appendChild(el('div', 'wempty', 'Binary file — nothing to show as text.'));
|
|
1949
2153
|
return;
|
|
1950
2154
|
}
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
host.appendChild(code);
|
|
2155
|
+
host.appendChild(drawCode(data.text, follow.attention, follow.needle));
|
|
2156
|
+
// Put the region being read in the middle of the panel, which is the point of knowing it.
|
|
2157
|
+
const first = host.querySelector('.src .on');
|
|
2158
|
+
if (first) first.scrollIntoView({ block: 'center' });
|
|
1956
2159
|
} finally {
|
|
1957
2160
|
follow.busy = false;
|
|
1958
|
-
// A newer target arrived while this was in flight: honour the last one and drop the rest,
|
|
1959
|
-
// which is what keeps a run of twenty edits to twenty files at one request rather than twenty.
|
|
1960
2161
|
if (follow.pending) {
|
|
1961
2162
|
const next = follow.pending;
|
|
1962
2163
|
follow.pending = null;
|
|
@@ -1967,6 +2168,12 @@ async function showWatched(path, wrote) {
|
|
|
1967
2168
|
|
|
1968
2169
|
$('pfollow').onclick = () => setFollow(!follow.on);
|
|
1969
2170
|
$('wclose').onclick = () => setFollow(false);
|
|
2171
|
+
// Escape closes it too, unless something modal is open — a dialog owns Escape while it is up.
|
|
2172
|
+
addEventListener('keydown', (ev) => {
|
|
2173
|
+
if (ev.key !== 'Escape' || $('watch').hidden) return;
|
|
2174
|
+
if (document.querySelector('dialog[open]') || !$('palette').hidden) return;
|
|
2175
|
+
setFollow(false);
|
|
2176
|
+
});
|
|
1970
2177
|
|
|
1971
2178
|
/* ── choosing a model ────────────────────────────────────────────────────────────────────── */
|
|
1972
2179
|
|
|
@@ -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
|
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAg1ElB,CAAC;AACF,CAAC"}
|
package/dist/web/ui-css.d.ts.map
CHANGED
|
@@ -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,
|
|
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,CA2nB3C"}
|
package/dist/web/ui-css.js
CHANGED
|
@@ -20,6 +20,8 @@ export function css(markUrl) {
|
|
|
20
20
|
--cyan:#0d7f92; --cyan-ink:#ffffff; --amber:#8a5a00; --green:#0f7a4a; --red:#b4232a;
|
|
21
21
|
--violet:#6d4bc4;
|
|
22
22
|
--add-bg:#e4f6ea; --add-ink:#0d5c37; --del-bg:#fdeaea; --del-ink:#8f1c22;
|
|
23
|
+
/* The region a tool is reading, and the exact text it matched inside that region. */
|
|
24
|
+
--attn-bg:#e8f4fb; --attn-hit:#bde4f7;
|
|
23
25
|
--shadow:0 1px 2px rgba(15,28,34,.07), 0 8px 24px rgba(15,28,34,.05);
|
|
24
26
|
--radius:10px; --mono:ui-monospace,'SF Mono',Menlo,Consolas,'DejaVu Sans Mono',monospace;
|
|
25
27
|
--mark:url("${markUrl}");
|
|
@@ -31,6 +33,8 @@ export function css(markUrl) {
|
|
|
31
33
|
--cyan:#4dd6e8; --cyan-ink:#04181c; --amber:#e8b04d; --green:#57d99a; --red:#ef767b;
|
|
32
34
|
--violet:#b39cfb;
|
|
33
35
|
--add-bg:#0f2a1e; --add-ink:#7ee0a7; --del-bg:#2b1416; --del-ink:#f4a3a6;
|
|
36
|
+
--attn-bg:#10222c; --attn-hit:#1c3f52;
|
|
37
|
+
--attn-bg:#10222c; --attn-hit:#1c3f52;
|
|
34
38
|
--shadow:0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.3);
|
|
35
39
|
}
|
|
36
40
|
}
|
|
@@ -114,6 +118,20 @@ header{height:47px;flex:0 0 47px;border-bottom:1px solid var(--line);display:fle
|
|
|
114
118
|
.stopbtn{border:1px solid var(--red);color:var(--red);border-radius:999px;padding:4px 11px}
|
|
115
119
|
|
|
116
120
|
.view{flex:1;overflow-y:auto;min-height:0}
|
|
121
|
+
/*
|
|
122
|
+
* The hidden attribute wins, always.
|
|
123
|
+
*
|
|
124
|
+
* A browser hides [hidden] with display:none at the same specificity as a single class, and this
|
|
125
|
+
* stylesheet comes later, so any rule setting display on a class silently beats it and the
|
|
126
|
+
* element can never be hidden. Reported as a panel that would not close: the button set the
|
|
127
|
+
* attribute, nothing happened, and because the state behind it HAD changed the visible panel
|
|
128
|
+
* then stopped updating. One cause, two symptoms that looked unrelated.
|
|
129
|
+
*
|
|
130
|
+
* Every element added since has needed its own guard — .view, .live, .attached, footer, #palette —
|
|
131
|
+
* and .watch was the one that did not get one. So it is stated once, for anything hidden ever.
|
|
132
|
+
*/
|
|
133
|
+
[hidden]{display:none!important}
|
|
134
|
+
|
|
117
135
|
.view[hidden]{display:none}
|
|
118
136
|
.wrap{max-width:840px;margin:0 auto;padding:24px}
|
|
119
137
|
.hero{text-align:center;padding:12vh 0 0;color:var(--muted)}
|
|
@@ -525,8 +543,18 @@ dialog:has(.dlg.wide){max-width:560px}
|
|
|
525
543
|
* find out about afterwards. This follows the tool calls: the path a tool names becomes the panel,
|
|
526
544
|
* and once the file has actually been written it becomes that file's diff.
|
|
527
545
|
*/
|
|
546
|
+
/*
|
|
547
|
+
* Two scrollers, not one.
|
|
548
|
+
*
|
|
549
|
+
* .view is the scroller for every other tab, and the panel started out inside it — so scrolling
|
|
550
|
+
* the conversation scrolled the file with it, and the panel's own overflow never came into play
|
|
551
|
+
* because nothing bounded its height. The chat tab therefore does not scroll itself: the split
|
|
552
|
+
* fills it, and each half scrolls independently.
|
|
553
|
+
*/
|
|
554
|
+
#chatview{overflow:hidden;display:flex}
|
|
528
555
|
.chatsplit{flex:1;display:flex;min-height:0;min-width:0}
|
|
529
|
-
.chatsplit .
|
|
556
|
+
.chatsplit .conv{flex:1 1 auto;min-width:0;overflow-y:auto;overflow-x:hidden}
|
|
557
|
+
.chatsplit .conv .wrap{min-width:0}
|
|
530
558
|
.watch{flex:0 0 44%;max-width:720px;min-width:0;border-left:1px solid var(--line);
|
|
531
559
|
display:flex;flex-direction:column;background:var(--panel-2)}
|
|
532
560
|
.whead{display:flex;gap:9px;align-items:center;padding:9px 12px;background:var(--panel);
|
|
@@ -535,13 +563,62 @@ dialog:has(.dlg.wide){max-width:560px}
|
|
|
535
563
|
flex:0 0 auto}
|
|
536
564
|
.whead .wp{font-family:var(--mono);font-size:12px;flex:1;min-width:0;overflow:hidden;
|
|
537
565
|
text-overflow:ellipsis;white-space:nowrap;direction:rtl;text-align:left}
|
|
538
|
-
.whead .
|
|
539
|
-
|
|
540
|
-
.
|
|
541
|
-
.
|
|
566
|
+
.whead .wms{flex:0 0 auto;font-size:10.5px;color:var(--dim);font-variant-numeric:tabular-nums;
|
|
567
|
+
font-family:var(--mono)}
|
|
568
|
+
/* A real hit area: a 15px glyph is a thing you aim at, not a thing you click. */
|
|
569
|
+
.whead .wx{flex:0 0 auto;background:none;border:0;color:var(--dim);font-size:17px;cursor:pointer;
|
|
570
|
+
line-height:1;padding:2px 8px;margin:-4px -6px -4px 0;border-radius:6px}
|
|
571
|
+
.whead .wx:hover{color:var(--red);background:var(--panel-2)}
|
|
572
|
+
.wbody{flex:1;overflow:auto;min-height:0;position:relative}
|
|
542
573
|
.wbody .code{min-height:100%}
|
|
543
574
|
.wbody .diff{border-top:0}
|
|
544
575
|
.wempty{padding:22px 14px;color:var(--dim);font-size:12.5px;line-height:1.6}
|
|
576
|
+
|
|
577
|
+
/*
|
|
578
|
+
* Where it is looking, not just what at.
|
|
579
|
+
*
|
|
580
|
+
* A tool call says which lines it is reading and which pattern it is matching, so the panel can
|
|
581
|
+
* mark them: the region under attention is tinted and carries a marker in the gutter. Watching a
|
|
582
|
+
* file scroll past is not the same as seeing which four lines were actually read.
|
|
583
|
+
*/
|
|
584
|
+
/*
|
|
585
|
+
* A row per line, in both columns.
|
|
586
|
+
*
|
|
587
|
+
* Not inline-blocks carrying their own newline: that measured correct and rendered wrong, four
|
|
588
|
+
* highlighted lines collapsing into one and the gutter jumping 44 → 49. A block row is exactly one
|
|
589
|
+
* line box by construction, so row N of the gutter is row N of the source and no highlight can
|
|
590
|
+
* make them drift.
|
|
591
|
+
*/
|
|
592
|
+
.rows{display:flex;font-family:var(--mono);font-size:12.5px;line-height:1.5;align-items:stretch}
|
|
593
|
+
.rows .gut{flex:0 0 auto;padding:10px 10px 10px 14px;text-align:right;color:var(--dim);
|
|
594
|
+
background:var(--panel);border-right:1px solid var(--line);user-select:none;
|
|
595
|
+
font-variant-numeric:tabular-nums}
|
|
596
|
+
.rows .src{flex:1 1 auto;padding:10px 0 10px 14px;min-width:0;overflow-x:auto;tab-size:4}
|
|
597
|
+
.rows .gut > div,.rows .src > div{white-space:pre;height:1.5em}
|
|
598
|
+
.rows .gut > div.on{color:var(--cyan);background:var(--attn-bg)}
|
|
599
|
+
.rows .src > div.on{background:var(--attn-bg);box-shadow:inset 2px 0 0 var(--cyan)}
|
|
600
|
+
.rows .src .hit{background:var(--attn-hit);border-radius:2px}
|
|
601
|
+
|
|
602
|
+
/* A command, as it runs. */
|
|
603
|
+
.wterm{font-family:var(--mono);font-size:11.5px;line-height:1.5;padding:10px 13px;
|
|
604
|
+
white-space:pre-wrap;word-break:break-word;color:var(--muted)}
|
|
605
|
+
.wterm .cmd{color:var(--cyan);display:block;margin-bottom:7px}
|
|
606
|
+
.wterm .ln{display:block}
|
|
607
|
+
.wterm .stub{color:var(--dim);font-style:italic}
|
|
608
|
+
|
|
609
|
+
/* The trail: everything this turn has touched, still reachable. */
|
|
610
|
+
.wtrail{flex:0 0 auto;display:flex;gap:6px;padding:8px 10px;overflow-x:auto;
|
|
611
|
+
border-top:1px solid var(--line);background:var(--panel)}
|
|
612
|
+
.wtrail::-webkit-scrollbar{height:0}
|
|
613
|
+
.wchip{flex:0 0 auto;display:flex;gap:5px;align-items:center;padding:3px 9px;border-radius:999px;
|
|
614
|
+
border:1px solid var(--line);background:var(--panel-2);font-size:11px;cursor:pointer;
|
|
615
|
+
white-space:nowrap;color:var(--muted);font-family:var(--mono)}
|
|
616
|
+
.wchip:hover{border-color:var(--cyan);color:var(--ink)}
|
|
617
|
+
.wchip.on{border-color:var(--cyan);color:var(--cyan)}
|
|
618
|
+
.wchip .dot{width:5px;height:5px;border-radius:50%;background:var(--dim);flex:0 0 auto}
|
|
619
|
+
.wchip.on .dot{background:var(--cyan)}
|
|
620
|
+
.wchip.bad .dot{background:var(--red)}
|
|
621
|
+
|
|
545
622
|
@media (max-width:1100px){ .watch{display:none} }
|
|
546
623
|
|
|
547
624
|
/*
|
package/dist/web/ui-css.js.map
CHANGED
|
@@ -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
|
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6mBtB,CAAC;AACF,CAAC"}
|
package/dist/web/ui.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/web/ui.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,wBAAgB,IAAI,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/web/ui.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,wBAAgB,IAAI,IAAI,MAAM,CAmP7B"}
|
package/dist/web/ui.js
CHANGED
|
@@ -65,6 +65,8 @@ export function page() {
|
|
|
65
65
|
|
|
66
66
|
<div class="view" id="chatview">
|
|
67
67
|
<div class="chatsplit">
|
|
68
|
+
<!-- The conversation is its own scroller so the panel beside it can be another. -->
|
|
69
|
+
<div class="conv" id="conv">
|
|
68
70
|
<div class="wrap" id="stream">
|
|
69
71
|
<div class="hero">
|
|
70
72
|
<div class="mark"></div>
|
|
@@ -77,15 +79,21 @@ export function page() {
|
|
|
77
79
|
</div>
|
|
78
80
|
</div>
|
|
79
81
|
</div>
|
|
82
|
+
</div>
|
|
80
83
|
<!-- The file being worked on, beside the conversation rather than behind a tab. A tool call
|
|
81
84
|
names a path; this shows it, and shows the diff once the file has actually been written. -->
|
|
82
85
|
<aside class="watch" id="watch" hidden>
|
|
83
86
|
<div class="whead">
|
|
84
87
|
<span class="wt" id="wtool">watching</span>
|
|
85
88
|
<span class="wp" id="wpath">—</span>
|
|
86
|
-
<
|
|
89
|
+
<span class="wms" id="wms"></span>
|
|
90
|
+
<button type="button" class="wx" id="wclose" aria-label="Stop following"
|
|
91
|
+
title="Stop following — or press Escape, or the follow pill">×</button>
|
|
87
92
|
</div>
|
|
88
93
|
<div class="wbody" id="wbody"></div>
|
|
94
|
+
<!-- Everything this turn has touched, in order. The current one is live; the rest are
|
|
95
|
+
one click away, which is what makes it a record rather than a glimpse. -->
|
|
96
|
+
<div class="wtrail" id="wtrail" hidden></div>
|
|
89
97
|
</aside>
|
|
90
98
|
</div>
|
|
91
99
|
</div>
|
package/dist/web/ui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../../src/web/ui.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,MAAM,UAAU,IAAI;IAClB,OAAO;;;;;;;SAOA,GAAG,CAAC,SAAS,CAAC
|
|
1
|
+
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../../src/web/ui.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,MAAM,UAAU,IAAI;IAClB,OAAO;;;;;;;SAOA,GAAG,CAAC,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAuOF,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC;EAC7C,YAAY,EAAE;;eAED,CAAC;AAChB,CAAC"}
|