shadok-ai 0.2.46 → 0.2.47
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/README.md +6 -0
- package/package.json +1 -1
- package/public/index.html +132 -6
package/README.md
CHANGED
|
@@ -75,6 +75,12 @@ Enter to skip; you can add it later from the web UI).
|
|
|
75
75
|
the chat can't express. With tmux there is also an **experimental real
|
|
76
76
|
terminal** (xterm.js over the pane's byte stream) when a snapshot isn't enough.
|
|
77
77
|
- **Diff panel** — what an agent actually changed, against its base.
|
|
78
|
+
- **Phone layout** — the cockpit is a fixed chassis that never pans sideways,
|
|
79
|
+
sized on the *visual* viewport so the composer stays clear of the browser's
|
|
80
|
+
own bar and, when the keyboard opens, sits right above it (the instrument bar
|
|
81
|
+
folds away while you type). Fields are 16px, which is what stops Safari from
|
|
82
|
+
zooming in on focus. The agents column is replaced by a channel selector in
|
|
83
|
+
the channel bar.
|
|
78
84
|
- **Tweak Shadok-AI** — a card pinned at the bottom of the agents column. One
|
|
79
85
|
click clones shadok-ai's own source into `~/.shadok-ai/self/shadok-ai` and
|
|
80
86
|
starts an agent on it in its own worktree; that agent delivers its change as a
|
package/package.json
CHANGED
package/public/index.html
CHANGED
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
|
-
|
|
5
|
+
<!-- viewport-fit=cover: the page reaches under the notch and the home indicator;
|
|
6
|
+
the `env(safe-area-inset-*)` rules below put the CONTENT back in the clear.
|
|
7
|
+
No `maximum-scale` / `user-scalable=no`: banning zoom to avoid Safari's
|
|
8
|
+
focus zoom would take pinch-zoom from the people who need it — the 16px
|
|
9
|
+
fields (see the coarse-pointer query) fix it at the source instead. -->
|
|
10
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
6
11
|
<title>shadok-ai — cockpit</title>
|
|
7
12
|
<link rel="icon" id="favicon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='7' fill='%2314161d'/%3E%3Ctext x='16' y='25' font-family='monospace' font-size='26' font-weight='700' fill='%23f0a848' text-anchor='middle'%3E%E2%80%BA%3C/text%3E%3C/svg%3E">
|
|
8
13
|
<style>
|
|
@@ -24,6 +29,7 @@
|
|
|
24
29
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
25
30
|
[hidden] { display: none !important; }
|
|
26
31
|
html, body { height: 100%; }
|
|
32
|
+
html { overflow: hidden; }
|
|
27
33
|
body {
|
|
28
34
|
background: var(--bg);
|
|
29
35
|
color: var(--text);
|
|
@@ -32,9 +38,31 @@
|
|
|
32
38
|
display: flex;
|
|
33
39
|
flex-direction: column;
|
|
34
40
|
/* Châssis fixe : la page ne scrolle jamais — seuls les panneaux internes
|
|
35
|
-
(liste des canaux, transcript, pickers) ont leur propre overflow.
|
|
41
|
+
(liste des canaux, transcript, pickers) ont leur propre overflow.
|
|
42
|
+
`position: fixed` pins the chassis TO the viewport: on a phone
|
|
43
|
+
`overflow: hidden` alone does not stop the browser from dragging the page
|
|
44
|
+
around — that is what let the whole interface slide left and right as soon
|
|
45
|
+
as a zoom (even an accidental one) made the layout viewport wider than the
|
|
46
|
+
screen. `overscroll-behavior` kills the rubber-band and pull-to-refresh. */
|
|
47
|
+
position: fixed;
|
|
48
|
+
top: 0;
|
|
49
|
+
left: 0;
|
|
50
|
+
width: 100%;
|
|
36
51
|
overflow: hidden;
|
|
37
|
-
|
|
52
|
+
overscroll-behavior: none;
|
|
53
|
+
/* Landscape: the notch eats one edge. The content steps aside, the background
|
|
54
|
+
does not. */
|
|
55
|
+
padding-left: env(safe-area-inset-left, 0px);
|
|
56
|
+
padding-right: env(safe-area-inset-right, 0px);
|
|
57
|
+
}
|
|
58
|
+
/* Chassis height — three levels, coarsest first:
|
|
59
|
+
1. `100%`: the LAYOUT viewport. On a phone it assumes the URL bar retracted,
|
|
60
|
+
so the bottom of the page (the composer) ended up UNDER the browser bar.
|
|
61
|
+
2. `100dvh`: follows the URL bar as it retracts and comes back.
|
|
62
|
+
3. `--app-h`: additionally follows the on-screen KEYBOARD, which no CSS unit
|
|
63
|
+
reports. Set from `visualViewport` (see `syncViewport`), touch only. */
|
|
64
|
+
body { height: 100dvh; }
|
|
65
|
+
body.vv-sized { height: var(--app-h); }
|
|
38
66
|
.label {
|
|
39
67
|
font-family: var(--mono);
|
|
40
68
|
font-size: 10px;
|
|
@@ -66,6 +94,9 @@
|
|
|
66
94
|
}
|
|
67
95
|
/* Keep the toolbar buttons together so they never split across lines. */
|
|
68
96
|
header button { flex: 0 0 auto; }
|
|
97
|
+
/* A box that is no box: the buttons stay flex items of `header`. It exists only
|
|
98
|
+
so the mobile query can give them a line of their own. */
|
|
99
|
+
.hdr-tools { display: contents; }
|
|
69
100
|
header .brand {
|
|
70
101
|
font-family: var(--mono);
|
|
71
102
|
font-size: 14px;
|
|
@@ -870,12 +901,17 @@
|
|
|
870
901
|
#composer {
|
|
871
902
|
display: flex;
|
|
872
903
|
gap: 10px;
|
|
873
|
-
padding
|
|
904
|
+
/* The bottom padding absorbs the home indicator (viewport-fit=cover): without
|
|
905
|
+
it the field slides under it and half the text becomes unreadable. */
|
|
906
|
+
padding: 14px 22px calc(18px + env(safe-area-inset-bottom, 0px));
|
|
874
907
|
border-top: 1px solid var(--line);
|
|
875
908
|
background: var(--bg-raised);
|
|
876
909
|
}
|
|
877
910
|
#composer textarea {
|
|
878
911
|
flex: 1;
|
|
912
|
+
/* Without it a textarea never shrinks below its intrinsic width (~20 columns)
|
|
913
|
+
and pushes the composer off the screen on a small phone. */
|
|
914
|
+
min-width: 0;
|
|
879
915
|
font-family: var(--sans);
|
|
880
916
|
font-size: 14px;
|
|
881
917
|
background: var(--bg-inset);
|
|
@@ -955,13 +991,43 @@
|
|
|
955
991
|
/* ── Mobile : sidebar cachée, navigation par le sélecteur du header canal ── */
|
|
956
992
|
@media (max-width: 640px) {
|
|
957
993
|
#tabbar { display: none; }
|
|
958
|
-
#chanSelect { display: inline-block; }
|
|
994
|
+
#chanSelect { display: inline-block; min-width: 0; }
|
|
959
995
|
#chanBar #chanBarName { display: none; }
|
|
960
996
|
/* Bandeau canal condensé : on garde Status + Session, on cache le reste. */
|
|
961
997
|
#chanBar #dirGauge, #chanBar #branchGauge { display: none !important; }
|
|
962
998
|
#chanBar .gauge .label { display: none; } /* valeurs seules, plus compact */
|
|
963
999
|
#chanBar { gap: 6px; padding: 6px 10px; }
|
|
964
|
-
header { flex-wrap: wrap; row-gap:
|
|
1000
|
+
header { flex-wrap: wrap; row-gap: 6px; gap: 8px; padding: 8px 12px; }
|
|
1001
|
+
/* Row 1: brand + dials. Row 2: every icon, left-aligned. They used to spread
|
|
1002
|
+
across the two rows as space allowed, and the first of them (🔑) ended up
|
|
1003
|
+
flush against the right edge. */
|
|
1004
|
+
.hdr-tools { display: flex; flex: 1 0 100%; align-items: center; gap: 6px; }
|
|
1005
|
+
header button.icon { padding: 5px 7px; }
|
|
1006
|
+
.quota { min-width: 46px; }
|
|
1007
|
+
.dial { width: 46px; height: 36px; }
|
|
1008
|
+
/* A bit more room for a thumb, a bit less margin wasted on a narrow screen. */
|
|
1009
|
+
.transcript { padding: 14px 14px 6px; }
|
|
1010
|
+
#composer { padding: 10px 14px calc(12px + env(safe-area-inset-bottom, 0px)); gap: 8px; }
|
|
1011
|
+
#thinking { padding: 0 14px 6px; }
|
|
1012
|
+
.turn, .turn.activity { max-width: 92%; }
|
|
1013
|
+
/* Keyboard up: the instrument bar steps aside. It is of no use while typing
|
|
1014
|
+
and it costs a good third of the conversation still visible; it comes back
|
|
1015
|
+
the moment the keyboard closes. */
|
|
1016
|
+
body.kb-up header { display: none; }
|
|
1017
|
+
}
|
|
1018
|
+
/* iOS Safari ZOOMS INTO any focused field whose font is under 16px. That zoom is
|
|
1019
|
+
the single cause of two symptoms that look unrelated: the layout viewport
|
|
1020
|
+
becomes wider than the screen, so the interface starts sliding left and
|
|
1021
|
+
right, and the page jumps up on focus. We do not ban zoom in the viewport tag
|
|
1022
|
+
(that would take it from whoever needs it) — the fields get 16px. Keyed on
|
|
1023
|
+
the POINTER, not on width: a large touch tablet behaves exactly the same.
|
|
1024
|
+
Every selector here has to match or beat the specificity of the rule it
|
|
1025
|
+
corrects (`#composer textarea`, `input[type="text"]`) — otherwise the
|
|
1026
|
+
original wins and the zoom is back, in silence. */
|
|
1027
|
+
@media (pointer: coarse) {
|
|
1028
|
+
textarea, select,
|
|
1029
|
+
input[type="text"], input[type="password"], input[type="number"], input[type="search"],
|
|
1030
|
+
#composer textarea, #chanSelect { font-size: 16px; }
|
|
965
1031
|
}
|
|
966
1032
|
#xtermHost { flex: 1; min-height: 0; overflow: auto; padding: 8px 10px; }
|
|
967
1033
|
#xtermHost .xterm { height: 100%; }
|
|
@@ -1092,6 +1158,11 @@
|
|
|
1092
1158
|
portant `.overlay`, il n'y a plus de liste à ne pas oublier. */
|
|
1093
1159
|
.overlay {
|
|
1094
1160
|
position: fixed; inset: 0; z-index: 50;
|
|
1161
|
+
/* Same trap as the chassis: `inset: 0` spans the LAYOUT viewport, so with the
|
|
1162
|
+
keyboard up a popin centres itself over a height that includes the keyboard
|
|
1163
|
+
— and its field ends up underneath. `--app-h` brings it back to what is
|
|
1164
|
+
visible; unset (desktop), `auto` lets `inset: 0` stretch as before. */
|
|
1165
|
+
height: var(--app-h, auto);
|
|
1095
1166
|
display: flex; align-items: center; justify-content: center;
|
|
1096
1167
|
padding: 24px;
|
|
1097
1168
|
background: rgba(8, 9, 13, 0.62);
|
|
@@ -1114,6 +1185,15 @@
|
|
|
1114
1185
|
border-radius: 6px; padding: 14px 16px;
|
|
1115
1186
|
box-shadow: 0 18px 50px rgba(0, 0, 0, 0.55);
|
|
1116
1187
|
}
|
|
1188
|
+
/* Popins on a phone: near full screen, and bounded by the POPIN (hence by
|
|
1189
|
+
`--app-h`, keyboard deducted) instead of `80vh`, which counts the keyboard in
|
|
1190
|
+
and leaves the field under it. This query has to stay AFTER the rules it
|
|
1191
|
+
corrects — the main mobile query sits higher in the sheet, and at equal
|
|
1192
|
+
specificity order decides: both rules were mute when written up there. */
|
|
1193
|
+
@media (max-width: 640px) {
|
|
1194
|
+
.overlay { padding: 10px; }
|
|
1195
|
+
#secretsPanel, .sec-panel { width: 100%; max-height: 100%; }
|
|
1196
|
+
}
|
|
1117
1197
|
/* Squelette entête / corps / pied. Seul le CORPS défile, donc l'action
|
|
1118
1198
|
principale reste atteignable : le formulaire de création dépasse l'écran sur
|
|
1119
1199
|
un portable, et son bouton « Start » se trouvait en bas d'une page à
|
|
@@ -1208,6 +1288,12 @@
|
|
|
1208
1288
|
<text class="unit" x="28" y="41" text-anchor="middle">7d</text>
|
|
1209
1289
|
</svg>
|
|
1210
1290
|
</div>
|
|
1291
|
+
<!-- The global actions, in ONE container. `display: contents` on desktop: they
|
|
1292
|
+
stay direct items of the bar and the layout does not move a pixel. On a
|
|
1293
|
+
phone this container becomes a ROW OF ITS OWN, so no icon gets pushed
|
|
1294
|
+
against the right edge (the 🔑 was there, 16px from it — out of sight at
|
|
1295
|
+
the slightest zoom). -->
|
|
1296
|
+
<div class="hdr-tools">
|
|
1211
1297
|
<button id="secretsBtn" class="icon" title="Repo secrets (injected as env)">🔑</button>
|
|
1212
1298
|
<button id="profilesBtn" class="icon" title="Agent profiles (role + guardrails + secrets)">👤</button>
|
|
1213
1299
|
<button id="cronBtn" class="icon" title="Scheduled prompts for this agent (monitoring / reporting)">⏰</button>
|
|
@@ -1219,6 +1305,7 @@
|
|
|
1219
1305
|
<button id="toggleDiff">Diff</button>
|
|
1220
1306
|
</div>
|
|
1221
1307
|
</div>
|
|
1308
|
+
</div>
|
|
1222
1309
|
</header>
|
|
1223
1310
|
|
|
1224
1311
|
<div id="verMenu" hidden>
|
|
@@ -4122,6 +4209,45 @@
|
|
|
4122
4209
|
persistDraft(active);
|
|
4123
4210
|
});
|
|
4124
4211
|
|
|
4212
|
+
/* ── The on-screen keyboard is not a CSS unit ─────────────────────────────
|
|
4213
|
+
When it opens, the LAYOUT viewport keeps its full height: the chassis still
|
|
4214
|
+
believes it owns the screen, the composer ends up under the keyboard, and the
|
|
4215
|
+
browser then scrolls the document to reveal the field — that is the
|
|
4216
|
+
"everything jumps up", the header leaving by the top. `visualViewport` is the
|
|
4217
|
+
only thing that reports the keyboard, so the chassis is sized from it and the
|
|
4218
|
+
browser's rescue scroll has nothing left to do. Coarse pointers only: on a
|
|
4219
|
+
desktop `visualViewport` also tracks pinch-zoom, and resizing the page on
|
|
4220
|
+
every pinch would be a regression for nobody's benefit. */
|
|
4221
|
+
if (matchMedia("(pointer: coarse)").matches) {
|
|
4222
|
+
// The shortcut reminder needs two lines and gets clipped by a one-line field
|
|
4223
|
+
// — and it does not address a phone anyway, where there is neither
|
|
4224
|
+
// Shift+Enter nor a physical keyboard.
|
|
4225
|
+
promptInput.placeholder = "Write your prompt";
|
|
4226
|
+
}
|
|
4227
|
+
if (window.visualViewport && matchMedia("(pointer: coarse)").matches) {
|
|
4228
|
+
const vv = window.visualViewport;
|
|
4229
|
+
let pending = 0;
|
|
4230
|
+
const syncViewport = () => {
|
|
4231
|
+
pending = 0;
|
|
4232
|
+
document.documentElement.style.setProperty("--app-h", vv.height + "px");
|
|
4233
|
+
document.body.classList.add("vv-sized");
|
|
4234
|
+
// `innerHeight` is the LAYOUT viewport: it does not move when the keyboard
|
|
4235
|
+
// opens, so the gap between the two IS the keyboard. The threshold keeps a
|
|
4236
|
+
// retracting URL bar from being read as one.
|
|
4237
|
+
document.body.classList.toggle("kb-up", window.innerHeight - vv.height > 120);
|
|
4238
|
+
// The chassis now fits above the keyboard: if the browser already dragged
|
|
4239
|
+
// the document, that offset hides nothing but the header.
|
|
4240
|
+
if (window.scrollY || window.scrollX) window.scrollTo(0, 0);
|
|
4241
|
+
// The transcript just shrank: the last message must stay the last message
|
|
4242
|
+
// visible (pinBottom leaves a reader who scrolled up alone).
|
|
4243
|
+
if (active) pinBottom(active);
|
|
4244
|
+
};
|
|
4245
|
+
const schedule = () => { if (!pending) pending = requestAnimationFrame(syncViewport); };
|
|
4246
|
+
vv.addEventListener("resize", schedule);
|
|
4247
|
+
vv.addEventListener("scroll", schedule);
|
|
4248
|
+
syncViewport();
|
|
4249
|
+
}
|
|
4250
|
+
|
|
4125
4251
|
// ── Interactive terminal (xterm.js over the raw tmux pipe), per channel ──
|
|
4126
4252
|
// The ⌨️ button toggles it for the ACTIVE channel; it fills the main area
|
|
4127
4253
|
// (.chat-pane gets .terminal-mode → chat hidden, terminal shown). Raw pane
|