koneck 2.56.0 → 2.57.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.
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +10 -1
- package/dist/tools.js.map +1 -1
- package/dist/web/api.d.ts.map +1 -1
- package/dist/web/api.js +58 -0
- package/dist/web/api.js.map +1 -1
- package/dist/web/jobs.d.ts.map +1 -1
- package/dist/web/jobs.js +13 -2
- package/dist/web/jobs.js.map +1 -1
- package/dist/web/ui-client.d.ts.map +1 -1
- package/dist/web/ui-client.js +301 -78
- 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 +60 -27
- package/dist/web/ui-css.js.map +1 -1
- package/dist/web/ui.d.ts.map +1 -1
- package/dist/web/ui.js +28 -14
- package/dist/web/ui.js.map +1 -1
- package/img/konect terminal.jpeg +0 -0
- package/package.json +2 -1
package/dist/web/ui-client.js
CHANGED
|
@@ -168,7 +168,14 @@ function setView(name) {
|
|
|
168
168
|
if (name === 'changes') loadChanges();
|
|
169
169
|
if (name === 'timing') loadTiming();
|
|
170
170
|
if (name === 'files') loadTree('');
|
|
171
|
-
if (name === 'terminal') {
|
|
171
|
+
if (name === 'terminal') {
|
|
172
|
+
// Built on first sight and kept, so a pane's scrollback survives leaving the tab and coming
|
|
173
|
+
// back — a terminal that forgets what it printed when you look away is not a terminal.
|
|
174
|
+
if (term.panes.length === 0 || !term.panes[0].el) buildTerminal();
|
|
175
|
+
watchTerminal();
|
|
176
|
+
const here = term.panes[term.active];
|
|
177
|
+
if (here && here.el) here.el.input.focus();
|
|
178
|
+
}
|
|
172
179
|
if (name === 'search') $('q').focus();
|
|
173
180
|
if (name === 'review') loadReview();
|
|
174
181
|
if (name === 'refactor') loadRefactor();
|
|
@@ -2620,8 +2627,17 @@ addEventListener('keydown', (ev) => {
|
|
|
2620
2627
|
*/
|
|
2621
2628
|
function ansiToHtml(text) {
|
|
2622
2629
|
const escChar = String.fromCharCode(27);
|
|
2630
|
+
/*
|
|
2631
|
+
* Colour is kept; everything else a terminal emits is swallowed.
|
|
2632
|
+
*
|
|
2633
|
+
* The last alternative is the one that was missing: an escape followed by a single character —
|
|
2634
|
+
* ESC = for keypad mode, ESC > , ESC 7 to save the cursor — has no bracket, so the CSI pattern
|
|
2635
|
+
* never matched it and it reached the page as a control glyph. That is what appeared at the top
|
|
2636
|
+
* of a pane running git.
|
|
2637
|
+
*/
|
|
2623
2638
|
const pattern = new RegExp(escChar + '\\[([0-9;]*)m|' + escChar + '\\[[0-9;?]*[A-Za-z]|'
|
|
2624
|
-
+ escChar + '\\][^\\u0007]
|
|
2639
|
+
+ escChar + '\\][^\\u0007]*(?:\\u0007|' + escChar + '\\\\)|'
|
|
2640
|
+
+ escChar + '[=>78MDEHcZ]|[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f]', 'g');
|
|
2625
2641
|
let out = '';
|
|
2626
2642
|
let open = 0;
|
|
2627
2643
|
let last = 0;
|
|
@@ -2659,96 +2675,303 @@ function ansiToHtml(text) {
|
|
|
2659
2675
|
* matters is not a setting but a fact about the command: a test run finishes and a dev server
|
|
2660
2676
|
* does not, and a runner that waits for the second one looks broken.
|
|
2661
2677
|
*/
|
|
2662
|
-
|
|
2678
|
+
/*
|
|
2679
|
+
* A terminal, not a form with an output box.
|
|
2680
|
+
*
|
|
2681
|
+
* What was here was a job list beside a pane of text: you typed into a field at the top, picked a
|
|
2682
|
+
* row, and read the result — which arrived in whole-buffer snapshots a few times a second, because
|
|
2683
|
+
* the view polled while the runner had been emitting a line-by-line event all along. Everything
|
|
2684
|
+
* about it said "web page".
|
|
2685
|
+
*
|
|
2686
|
+
* This is a scrollback: the command you typed stays above what it printed, the cursor waits at the
|
|
2687
|
+
* bottom, and the next thing you type continues the same column of text. Up to three panes, each
|
|
2688
|
+
* its own shell with its own history, because two is the common case — watch the server, run the
|
|
2689
|
+
* tests — and four on a laptop is four things too narrow to read.
|
|
2690
|
+
*/
|
|
2691
|
+
const term = {
|
|
2692
|
+
panes: [],
|
|
2693
|
+
/** Which pane the keyboard belongs to. */
|
|
2694
|
+
active: 0,
|
|
2695
|
+
stream: null,
|
|
2696
|
+
/** Pane index by job id, so a line can be routed to the pane that asked for it. */
|
|
2697
|
+
ownerOf: {},
|
|
2698
|
+
fontPx: 12.5,
|
|
2699
|
+
};
|
|
2663
2700
|
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2701
|
+
function newPane() {
|
|
2702
|
+
return { job: null, history: [], at: -1, lines: [], running: false, el: null,
|
|
2703
|
+
/** Where this command's output begins, so a catch-up can replace exactly that. */
|
|
2704
|
+
outputStart: 0,
|
|
2705
|
+
/** How many output lines have arrived on the stream, for the same reason. */
|
|
2706
|
+
received: 0 };
|
|
2707
|
+
}
|
|
2708
|
+
|
|
2709
|
+
/** The pane a job's output belongs to, or null when nothing here started it. */
|
|
2710
|
+
function paneForJob(id) {
|
|
2711
|
+
const idx = term.ownerOf[id];
|
|
2712
|
+
return idx === undefined ? null : term.panes[idx] || null;
|
|
2713
|
+
}
|
|
2714
|
+
|
|
2715
|
+
function buildTerminal() {
|
|
2716
|
+
if (term.panes.length === 0) term.panes.push(newPane());
|
|
2717
|
+
const host = $('termpanes');
|
|
2668
2718
|
host.innerHTML = '';
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
if (j.state === 'running') anyRunning = true;
|
|
2676
|
-
const row = el('div', 'jrow' + (j.id === pick ? ' on' : ''));
|
|
2677
|
-
row.appendChild(el('div', 'jc', j.command));
|
|
2678
|
-
const meta = el('div', 'jm');
|
|
2679
|
-
meta.appendChild(el('span', 'js ' + j.state, j.state
|
|
2680
|
-
+ (j.exitCode !== null && j.state !== 'running' ? ' ' + j.exitCode : '')));
|
|
2681
|
-
// Who ran it is a column, not a separate screen.
|
|
2682
|
-
if (j.by === 'agent') meta.appendChild(el('span', null, 'by the agent'));
|
|
2683
|
-
if (j.background) meta.appendChild(el('span', null, 'background'));
|
|
2684
|
-
if (j.pty) meta.appendChild(el('span', null, 'tty'));
|
|
2685
|
-
meta.appendChild(el('span', null, j.lines + (j.lines === 1 ? ' line' : ' lines')));
|
|
2686
|
-
if (j.state === 'running' && j.by !== 'agent') {
|
|
2687
|
-
const stop = el('button', 'jk', 'kill');
|
|
2688
|
-
stop.onclick = async (ev) => {
|
|
2689
|
-
ev.stopPropagation();
|
|
2690
|
-
try { await api('/api/kill', { method: 'POST', body: JSON.stringify({ id: j.id }) }); }
|
|
2691
|
-
catch (e) {}
|
|
2692
|
-
loadJobs(j.id);
|
|
2693
|
-
};
|
|
2694
|
-
meta.appendChild(stop);
|
|
2719
|
+
term.panes.forEach((pane, index) => {
|
|
2720
|
+
if (index > 0) {
|
|
2721
|
+
// A draggable divider, so a pane can be made wide enough for the thing being read.
|
|
2722
|
+
const grip = el('div', 'termgrip');
|
|
2723
|
+
grip.onmousedown = (ev) => startPaneDrag(ev, index);
|
|
2724
|
+
host.appendChild(grip);
|
|
2695
2725
|
}
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2726
|
+
const box = el('div', 'termpane' + (index === term.active ? ' on' : ''));
|
|
2727
|
+
box.style.flex = String(pane.flex || 1);
|
|
2728
|
+
box.onmousedown = () => { term.active = index; markActivePane(); };
|
|
2729
|
+
|
|
2730
|
+
const scroll = el('div', 'termscroll');
|
|
2731
|
+
const body = el('pre', 'termbody');
|
|
2732
|
+
scroll.appendChild(body);
|
|
2733
|
+
|
|
2734
|
+
const row = el('div', 'termrow');
|
|
2735
|
+
row.appendChild(el('span', 'termps', '$'));
|
|
2736
|
+
const input = document.createElement('input');
|
|
2737
|
+
input.className = 'termin';
|
|
2738
|
+
input.spellcheck = false;
|
|
2739
|
+
input.autocomplete = 'off';
|
|
2740
|
+
input.placeholder = index === 0 ? 'npm test, git status, npm run dev…' : '';
|
|
2741
|
+
input.onkeydown = (ev) => paneKey(ev, index);
|
|
2742
|
+
input.onfocus = () => { term.active = index; markActivePane(); };
|
|
2743
|
+
row.appendChild(input);
|
|
2744
|
+
const stop = el('button', 'termstop', 'stop');
|
|
2745
|
+
stop.title = 'Kill what is running in this pane';
|
|
2746
|
+
stop.onclick = () => void killPane(index);
|
|
2747
|
+
row.appendChild(stop);
|
|
2748
|
+
|
|
2749
|
+
box.appendChild(scroll);
|
|
2750
|
+
box.appendChild(row);
|
|
2751
|
+
host.appendChild(box);
|
|
2752
|
+
pane.el = { box: box, scroll: scroll, body: body, input: input, stop: stop };
|
|
2753
|
+
paintPane(index);
|
|
2754
|
+
});
|
|
2755
|
+
applyTerminalLook();
|
|
2756
|
+
markActivePane();
|
|
2757
|
+
const here = term.panes[term.active];
|
|
2758
|
+
if (here && here.el) here.el.input.focus();
|
|
2711
2759
|
}
|
|
2712
2760
|
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
const
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2761
|
+
function markActivePane() {
|
|
2762
|
+
term.panes.forEach((pane, i) => {
|
|
2763
|
+
if (pane.el) pane.el.box.className = 'termpane' + (i === term.active ? ' on' : '');
|
|
2764
|
+
});
|
|
2765
|
+
}
|
|
2766
|
+
|
|
2767
|
+
/** Everything this pane has printed, as one column of text. */
|
|
2768
|
+
function paintPane(index) {
|
|
2769
|
+
const pane = term.panes[index];
|
|
2770
|
+
if (!pane || !pane.el) return;
|
|
2771
|
+
const atEnd = pane.el.scroll.scrollHeight - pane.el.scroll.scrollTop
|
|
2772
|
+
- pane.el.scroll.clientHeight < 60;
|
|
2773
|
+
pane.el.body.innerHTML = ansiToHtml(pane.lines.join('\n'));
|
|
2774
|
+
pane.el.stop.hidden = !pane.running;
|
|
2775
|
+
if (atEnd) pane.el.scroll.scrollTop = pane.el.scroll.scrollHeight;
|
|
2776
|
+
}
|
|
2777
|
+
|
|
2778
|
+
function pushPane(index, line, redraw) {
|
|
2779
|
+
const pane = term.panes[index];
|
|
2780
|
+
if (!pane) return;
|
|
2781
|
+
if (redraw && pane.lines.length > 0) pane.lines[pane.lines.length - 1] = line;
|
|
2782
|
+
else pane.lines.push(line);
|
|
2783
|
+
while (pane.lines.length > 4000) pane.lines.shift();
|
|
2784
|
+
paintPane(index);
|
|
2785
|
+
}
|
|
2786
|
+
|
|
2787
|
+
async function paneKey(ev, index) {
|
|
2788
|
+
const pane = term.panes[index];
|
|
2789
|
+
if (!pane || !pane.el) return;
|
|
2790
|
+
const input = pane.el.input;
|
|
2791
|
+
if (ev.key === 'Enter') {
|
|
2792
|
+
const command = input.value.trim();
|
|
2793
|
+
input.value = '';
|
|
2794
|
+
if (command === '') { pushPane(index, '$ '); return; }
|
|
2795
|
+
if (command === 'clear') { pane.lines = []; paintPane(index); return; }
|
|
2796
|
+
pane.history.push(command);
|
|
2797
|
+
pane.at = pane.history.length;
|
|
2798
|
+
// Echoed above its own output, which is the whole difference from a form.
|
|
2799
|
+
pushPane(index, '$ ' + command);
|
|
2800
|
+
pane.outputStart = pane.lines.length;
|
|
2801
|
+
pane.received = 0;
|
|
2802
|
+
await runInPane(index, command);
|
|
2803
|
+
return;
|
|
2729
2804
|
}
|
|
2730
|
-
|
|
2805
|
+
// A history nobody has to think about: up and down, as everywhere else.
|
|
2806
|
+
if (ev.key === 'ArrowUp') {
|
|
2807
|
+
ev.preventDefault();
|
|
2808
|
+
if (pane.at > 0) pane.at--;
|
|
2809
|
+
input.value = pane.history[pane.at] || '';
|
|
2810
|
+
return;
|
|
2811
|
+
}
|
|
2812
|
+
if (ev.key === 'ArrowDown') {
|
|
2813
|
+
ev.preventDefault();
|
|
2814
|
+
if (pane.at < pane.history.length) pane.at++;
|
|
2815
|
+
input.value = pane.history[pane.at] || '';
|
|
2816
|
+
return;
|
|
2817
|
+
}
|
|
2818
|
+
if (ev.key === 'c' && ev.ctrlKey) { void killPane(index); }
|
|
2731
2819
|
}
|
|
2732
2820
|
|
|
2733
|
-
async function
|
|
2734
|
-
const
|
|
2735
|
-
if (!
|
|
2821
|
+
async function runInPane(index, command) {
|
|
2822
|
+
const pane = term.panes[index];
|
|
2823
|
+
if (!pane) return;
|
|
2824
|
+
// Anything that does not exit on its own is a background job; a runner that waits for a dev
|
|
2825
|
+
// server looks broken, and the fact is about the command rather than a box somebody ticked.
|
|
2826
|
+
const looksLikeServer = /\b(dev|serve|start|watch|nodemon|vite|next\s+dev)\b/.test(command);
|
|
2736
2827
|
let out;
|
|
2737
2828
|
try {
|
|
2738
|
-
out = await api('/api/run', { method: 'POST',
|
|
2739
|
-
command: command, background:
|
|
2829
|
+
out = await api('/api/run', { method: 'POST',
|
|
2830
|
+
body: JSON.stringify({ command: command, background: looksLikeServer }) });
|
|
2740
2831
|
} catch (e) {
|
|
2741
|
-
|
|
2832
|
+
pushPane(index, e.message);
|
|
2742
2833
|
return;
|
|
2743
2834
|
}
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2835
|
+
pane.job = out.id;
|
|
2836
|
+
pane.running = true;
|
|
2837
|
+
term.ownerOf[out.id] = index;
|
|
2838
|
+
paintPane(index);
|
|
2839
|
+
|
|
2840
|
+
/*
|
|
2841
|
+
* One catch-up read, because a command can outrun its own stream.
|
|
2842
|
+
*
|
|
2843
|
+
* The event stream is opened when the tab is first shown and takes a moment to establish. A
|
|
2844
|
+
* command typed inside that moment printed its first lines before anything was listening, and
|
|
2845
|
+
* they were gone: the pane showed the echoed command and nothing under it. Measured — the stream
|
|
2846
|
+
* was still in readyState 0 when the lines were emitted.
|
|
2847
|
+
*
|
|
2848
|
+
* So the runner is asked once, shortly after, what the job has actually produced, and anything
|
|
2849
|
+
* the stream missed is filled in. Counted rather than merged, so a line never appears twice.
|
|
2850
|
+
*/
|
|
2851
|
+
setTimeout(() => { void catchUpPane(index, out.id); }, 400);
|
|
2747
2852
|
}
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2853
|
+
|
|
2854
|
+
async function catchUpPane(index, jobId) {
|
|
2855
|
+
const pane = term.panes[index];
|
|
2856
|
+
if (!pane || pane.job !== jobId) return;
|
|
2857
|
+
let job;
|
|
2858
|
+
try { job = await api('/api/jobs?id=' + encodeURIComponent(jobId)); } catch (e) { return; }
|
|
2859
|
+
const produced = (job.lines || []).length;
|
|
2860
|
+
if (produced <= pane.received) return;
|
|
2861
|
+
pane.lines = pane.lines.slice(0, pane.outputStart).concat(job.lines || []);
|
|
2862
|
+
pane.received = produced;
|
|
2863
|
+
if (job.state && job.state !== 'running') pane.running = false;
|
|
2864
|
+
paintPane(index);
|
|
2865
|
+
}
|
|
2866
|
+
|
|
2867
|
+
async function killPane(index) {
|
|
2868
|
+
const pane = term.panes[index];
|
|
2869
|
+
if (!pane || !pane.job || !pane.running) return;
|
|
2870
|
+
try { await api('/api/kill', { method: 'POST', body: JSON.stringify({ id: pane.job }) }); }
|
|
2871
|
+
catch (e) { pushPane(index, e.message); }
|
|
2872
|
+
}
|
|
2873
|
+
|
|
2874
|
+
/*
|
|
2875
|
+
* Live, from the runner's own event stream.
|
|
2876
|
+
*
|
|
2877
|
+
* Its own stream rather than the session's: a command belongs to the workspace, not to whichever
|
|
2878
|
+
* conversation was open when it was typed.
|
|
2879
|
+
*/
|
|
2880
|
+
function watchTerminal() {
|
|
2881
|
+
if (term.stream) return;
|
|
2882
|
+
const url = '/api/terminal/events' + (state.token ? '?token=' + encodeURIComponent(state.token) : '');
|
|
2883
|
+
const source = new EventSource(url);
|
|
2884
|
+
term.stream = source;
|
|
2885
|
+
source.onmessage = (ev) => {
|
|
2886
|
+
let e;
|
|
2887
|
+
try { e = JSON.parse(ev.data); } catch (err) { return; }
|
|
2888
|
+
if (e.t === 'job-line') {
|
|
2889
|
+
const idx = term.ownerOf[e.id];
|
|
2890
|
+
if (idx === undefined) return;
|
|
2891
|
+
const pane = term.panes[idx];
|
|
2892
|
+
if (pane && !e.redraw) pane.received++;
|
|
2893
|
+
pushPane(idx, e.line, e.redraw === true);
|
|
2894
|
+
noticeServedUrl(e.line);
|
|
2895
|
+
return;
|
|
2896
|
+
}
|
|
2897
|
+
if (e.t === 'job' && e.job) {
|
|
2898
|
+
const idx = term.ownerOf[e.job.id];
|
|
2899
|
+
if (idx === undefined) return;
|
|
2900
|
+
const pane = term.panes[idx];
|
|
2901
|
+
if (!pane) return;
|
|
2902
|
+
if (e.job.state !== 'running') {
|
|
2903
|
+
pane.running = false;
|
|
2904
|
+
// Said the way a shell says it, rather than as a status badge.
|
|
2905
|
+
pushPane(idx, e.job.exitCode === 0 || e.job.exitCode === null
|
|
2906
|
+
? ''
|
|
2907
|
+
: '[' + e.job.state + ' ' + e.job.exitCode + ']');
|
|
2908
|
+
}
|
|
2909
|
+
paintPane(idx);
|
|
2910
|
+
}
|
|
2911
|
+
};
|
|
2912
|
+
source.onerror = () => { /* the browser reconnects on its own */ };
|
|
2913
|
+
}
|
|
2914
|
+
|
|
2915
|
+
/** Font size and backdrop, applied to every pane at once. */
|
|
2916
|
+
function applyTerminalLook() {
|
|
2917
|
+
const host = $('termpanes');
|
|
2918
|
+
host.style.setProperty('--term-font', term.fontPx + 'px');
|
|
2919
|
+
const on = $('tbgimg').checked;
|
|
2920
|
+
const strength = Number($('tbgop').value) / 100;
|
|
2921
|
+
host.classList.toggle('backdrop', on);
|
|
2922
|
+
host.style.setProperty('--term-bg-strength', on ? String(strength) : '0');
|
|
2923
|
+
if (on && !host.style.getPropertyValue('--term-bg-image')) {
|
|
2924
|
+
const url = '/api/terminal/background'
|
|
2925
|
+
+ (state.token ? '?token=' + encodeURIComponent(state.token) : '');
|
|
2926
|
+
host.style.setProperty('--term-bg-image', 'url("' + url + '")');
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
|
|
2930
|
+
function startPaneDrag(ev, dividerIndex) {
|
|
2931
|
+
ev.preventDefault();
|
|
2932
|
+
const left = term.panes[dividerIndex - 1];
|
|
2933
|
+
const right = term.panes[dividerIndex];
|
|
2934
|
+
if (!left || !right || !left.el || !right.el) return;
|
|
2935
|
+
const startX = ev.clientX;
|
|
2936
|
+
const leftWas = left.el.box.getBoundingClientRect().width;
|
|
2937
|
+
const rightWas = right.el.box.getBoundingClientRect().width;
|
|
2938
|
+
const total = leftWas + rightWas;
|
|
2939
|
+
const move = (m) => {
|
|
2940
|
+
const wanted = Math.min(Math.max(leftWas + (m.clientX - startX), 120), total - 120);
|
|
2941
|
+
left.flex = wanted / total;
|
|
2942
|
+
right.flex = (total - wanted) / total;
|
|
2943
|
+
left.el.box.style.flex = String(left.flex);
|
|
2944
|
+
right.el.box.style.flex = String(right.flex);
|
|
2945
|
+
};
|
|
2946
|
+
const up = () => {
|
|
2947
|
+
window.removeEventListener('mousemove', move);
|
|
2948
|
+
window.removeEventListener('mouseup', up);
|
|
2949
|
+
};
|
|
2950
|
+
window.addEventListener('mousemove', move);
|
|
2951
|
+
window.addEventListener('mouseup', up);
|
|
2952
|
+
}
|
|
2953
|
+
|
|
2954
|
+
$('tsplit').onclick = () => {
|
|
2955
|
+
// Three, deliberately. Four on a laptop is four things too narrow to read.
|
|
2956
|
+
if (term.panes.length >= 3) return;
|
|
2957
|
+
term.panes.push(newPane());
|
|
2958
|
+
term.active = term.panes.length - 1;
|
|
2959
|
+
buildTerminal();
|
|
2960
|
+
};
|
|
2961
|
+
|
|
2962
|
+
$('tunsplit').onclick = () => {
|
|
2963
|
+
if (term.panes.length <= 1) return;
|
|
2964
|
+
const gone = term.panes.pop();
|
|
2965
|
+
if (gone && gone.job) delete term.ownerOf[gone.job];
|
|
2966
|
+
term.active = Math.min(term.active, term.panes.length - 1);
|
|
2967
|
+
buildTerminal();
|
|
2968
|
+
};
|
|
2969
|
+
|
|
2970
|
+
$('tbgimg').onchange = applyTerminalLook;
|
|
2971
|
+
$('tbgop').oninput = applyTerminalLook;
|
|
2972
|
+
$('tfontup').onclick = () => { term.fontPx = Math.min(20, term.fontPx + 1); applyTerminalLook(); };
|
|
2973
|
+
$('tfontdown').onclick = () => { term.fontPx = Math.max(9, term.fontPx - 1); applyTerminalLook(); };
|
|
2974
|
+
|
|
2752
2975
|
|
|
2753
2976
|
/* ── the page, as it really renders ───────────────────────────────────────────────────────── */
|
|
2754
2977
|
|
|
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmiIlB,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":"AA8CA,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":"AA8CA,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAy8B3C"}
|
package/dist/web/ui-css.js
CHANGED
|
@@ -789,34 +789,67 @@ body[data-tint="high"]{filter:contrast(1.14)}
|
|
|
789
789
|
body[data-tint="soft"]{filter:contrast(.92) brightness(1.04)}
|
|
790
790
|
|
|
791
791
|
/* ── a terminal ───────────────────────────────────────────────────────────────────────────── */
|
|
792
|
-
|
|
793
|
-
.
|
|
792
|
+
/*
|
|
793
|
+
* A terminal that reads as one.
|
|
794
|
+
*
|
|
795
|
+
* The difference from the box that was here is not decoration. A terminal is a single column of
|
|
796
|
+
* monospaced text where the command you typed stays above what it printed and the cursor waits at
|
|
797
|
+
* the bottom of it. So: no card around the output, no border between the prompt and the scrollback,
|
|
798
|
+
* one background, and the input line styled as the last line of the text rather than as a form
|
|
799
|
+
* field sitting under it.
|
|
800
|
+
*/
|
|
801
|
+
/*
|
|
802
|
+
* The terminal view fills what it is given.
|
|
803
|
+
*
|
|
804
|
+
* A view scrolls its own overflow, which is right for a document and wrong for a terminal: the
|
|
805
|
+
* panes ended a third of the way down the window with dead space under them, because nothing told
|
|
806
|
+
* them how tall they were allowed to be.
|
|
807
|
+
*/
|
|
808
|
+
#terminalview{display:flex;flex-direction:column;overflow:hidden}
|
|
809
|
+
.termtop{display:flex;gap:8px;align-items:center;padding:9px 14px;background:var(--panel);
|
|
794
810
|
border-bottom:1px solid var(--line);flex:0 0 auto}
|
|
795
|
-
.
|
|
796
|
-
.
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
.
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
.
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
.
|
|
808
|
-
|
|
809
|
-
.
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
.
|
|
819
|
-
|
|
811
|
+
.termttl{font-size:12px;color:var(--muted);letter-spacing:.02em}
|
|
812
|
+
.termgap{flex:0 0 10px}
|
|
813
|
+
.termbtn{padding:4px 10px;border-radius:7px;border:1px solid var(--line-2);color:var(--muted);
|
|
814
|
+
background:transparent;cursor:pointer;font-size:11.5px}
|
|
815
|
+
.termbtn:hover{border-color:var(--cyan);color:var(--cyan)}
|
|
816
|
+
.termchk{display:flex;gap:5px;align-items:center;font-size:11.5px;color:var(--muted);cursor:pointer}
|
|
817
|
+
.termchk input{accent-color:var(--cyan);cursor:pointer}
|
|
818
|
+
#tbgop{width:88px;accent-color:var(--cyan)}
|
|
819
|
+
|
|
820
|
+
.termpanes{--term-font:12.5px;--term-bg-strength:0;--term-bg-image:none;
|
|
821
|
+
flex:1;display:flex;min-height:0;background:var(--bg)}
|
|
822
|
+
.termgrip{flex:0 0 5px;cursor:col-resize;background:var(--line);opacity:.6}
|
|
823
|
+
.termgrip:hover{background:var(--cyan);opacity:1}
|
|
824
|
+
|
|
825
|
+
.termpane{position:relative;display:flex;flex-direction:column;min-width:0;min-height:0;
|
|
826
|
+
border-left:1px solid transparent}
|
|
827
|
+
.termpane.on{border-left-color:var(--cyan)}
|
|
828
|
+
/*
|
|
829
|
+
* The backdrop sits behind the text at a weight the reader chooses, and never on top of it.
|
|
830
|
+
*
|
|
831
|
+
* Its own layer rather than a background on the pane: an image behind live text needs to be dimmed
|
|
832
|
+
* without dimming the text, and an opacity on the pane itself would take the characters with it.
|
|
833
|
+
*/
|
|
834
|
+
.termpane::before{content:'';position:absolute;inset:0;pointer-events:none;
|
|
835
|
+
background-image:var(--term-bg-image);background-size:cover;background-position:center;
|
|
836
|
+
opacity:var(--term-bg-strength);transition:opacity .18s ease}
|
|
837
|
+
.termscroll{position:relative;flex:1;min-height:0;overflow:auto;padding:12px 14px 4px}
|
|
838
|
+
.termbody{margin:0;font-family:var(--mono);font-size:var(--term-font);line-height:1.55;
|
|
839
|
+
white-space:pre-wrap;word-break:break-word;color:var(--ink)}
|
|
840
|
+
.termrow{position:relative;display:flex;gap:8px;align-items:center;padding:2px 14px 12px}
|
|
841
|
+
.termps{font-family:var(--mono);font-size:var(--term-font);color:var(--cyan);flex:0 0 auto}
|
|
842
|
+
/*
|
|
843
|
+
* The input is the last line of the scrollback, not a control under it: no border, no background,
|
|
844
|
+
* the same font and size as the text above, and a caret that looks like a terminal's.
|
|
845
|
+
*/
|
|
846
|
+
.termin{flex:1;min-width:0;border:0;outline:0;background:transparent;color:var(--ink);
|
|
847
|
+
font-family:var(--mono);font-size:var(--term-font);line-height:1.55;caret-color:var(--cyan);
|
|
848
|
+
padding:0}
|
|
849
|
+
.termin::placeholder{color:var(--dim)}
|
|
850
|
+
.termstop{flex:0 0 auto;padding:2px 9px;border-radius:6px;border:1px solid var(--red);
|
|
851
|
+
color:var(--red);background:transparent;cursor:pointer;font-size:10.5px}
|
|
852
|
+
.termpanes .oe{color:var(--red)}
|
|
820
853
|
|
|
821
854
|
/* ── the page, as it really renders ───────────────────────────────────────────────────────── */
|
|
822
855
|
.pvbar{display:flex;gap:8px;align-items:center;padding:11px 14px;background:var(--panel);
|
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;;;;;;;;;;;;GAYG;AACH,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;CAkBZ,CAAC;AAEF,MAAM,UAAU,GAAG,CAAC,OAAe;IACjC,OAAO;;;;;;;;;;;;;;;;;gBAiBO,OAAO;;;oCAGa,IAAI;;2BAEb,IAAI
|
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAi7B9B,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,CAiX7B"}
|
package/dist/web/ui.js
CHANGED
|
@@ -127,21 +127,35 @@ export function page() {
|
|
|
127
127
|
</div>
|
|
128
128
|
|
|
129
129
|
<!-- A terminal. Commands you run yourself, in the foreground or left running behind. -->
|
|
130
|
+
<!--
|
|
131
|
+
A terminal, not a form with an output box.
|
|
132
|
+
==========================================
|
|
133
|
+
What was here was a job list beside a pane of text: you typed into a field at the top, picked a
|
|
134
|
+
row, and read the result. Everything about it said "web page". A terminal is a scrollback where
|
|
135
|
+
the command you typed stays visible above what it printed, the cursor waits at the bottom, and
|
|
136
|
+
the next thing you type continues the same column of text.
|
|
137
|
+
|
|
138
|
+
Up to three panes, because two is the common case for "watch the server, run the tests" and
|
|
139
|
+
four on a laptop is four things too narrow to read. Each pane is its own shell with its own
|
|
140
|
+
scrollback and its own history; the dividers between them are draggable.
|
|
141
|
+
-->
|
|
130
142
|
<div class="view" id="terminalview" hidden>
|
|
131
|
-
<div class="
|
|
132
|
-
<
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
<
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
<div class="termtop">
|
|
144
|
+
<span class="termttl">Terminal</span>
|
|
145
|
+
<button type="button" class="termbtn" id="tsplit" title="Add a pane (up to three)">split</button>
|
|
146
|
+
<button type="button" class="termbtn" id="tunsplit" title="Close the last pane">unsplit</button>
|
|
147
|
+
<span class="termgap"></span>
|
|
148
|
+
<label class="termchk" title="A picture behind the text, at a weight that keeps it readable">
|
|
149
|
+
<input type="checkbox" id="tbgimg"> backdrop
|
|
150
|
+
</label>
|
|
151
|
+
<input type="range" id="tbgop" min="4" max="40" value="14" title="How strongly it shows"
|
|
152
|
+
aria-label="Backdrop strength">
|
|
153
|
+
<span class="termgap"></span>
|
|
154
|
+
<button type="button" class="termbtn" id="tfontdown" title="Smaller text">A−</button>
|
|
155
|
+
<button type="button" class="termbtn" id="tfontup" title="Larger text">A+</button>
|
|
156
|
+
</div>
|
|
157
|
+
<div class="termpanes" id="termpanes"></div>
|
|
158
|
+
</div>
|
|
145
159
|
</div>
|
|
146
160
|
</div>
|
|
147
161
|
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAqWF,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC;EAC7C,YAAY,EAAE;;eAED,CAAC;AAChB,CAAC"}
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koneck",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.57.0",
|
|
4
4
|
"description": "Kinetically Orchestrated Neural Execution Engine for Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
],
|
|
31
31
|
"files": [
|
|
32
32
|
"dist/",
|
|
33
|
+
"img/",
|
|
33
34
|
"README.md",
|
|
34
35
|
"LICENSE"
|
|
35
36
|
],
|