hands: arm the wire before opening the pad
This commit is contained in:
parent
799f0650ed
commit
59b52a818b
7 changed files with 210 additions and 148 deletions
|
|
@ -23,27 +23,31 @@
|
|||
* she is still on screen while you work. Casey, 2026-08-15: *"I want her, and
|
||||
* then below her to have like a trackpad zone / keyboard zone stuff."*
|
||||
*
|
||||
* At 760/226 the split was the other way round and the controls had 226px to
|
||||
* share between a status row, an agent field, the pads and four buttons — so
|
||||
* the pads were 92px and the last row sat under the dock.
|
||||
*
|
||||
* `--dock-reserve` is a number here because the dock claims **no exclusive
|
||||
* zone** unless it is Pinned (`modules/ii/dock/Dock.qml:321`), so nothing
|
||||
* shrinks this surface for it and the bottom row would be untappable. Delete
|
||||
* this the day the dock yields on its own.
|
||||
* At 760/226 the controls had only 226px. At 320/the-rest the trackpad ate
|
||||
* nearly half the glass. This split keeps her proportions and gives the pad
|
||||
* one deliberate working area. The dock now yields only while this
|
||||
* conditional surface is open; the remaining reserve belongs to the system
|
||||
* gesture rail, not the dock.
|
||||
*/
|
||||
:root { --dock-reserve: 84px; }
|
||||
body.hid #live2d-widget { height: 320px; }
|
||||
:root { --gesture-rail-reserve: 32px; }
|
||||
body.hid #live2d-widget { height: 520px; }
|
||||
/*
|
||||
* Keep her proportions when the box shrinks. The canvas fills its element and
|
||||
* the rig has no letterboxing of its own, so a short full-width box draws the
|
||||
* same model squashed. The ratio is the full-height box's, 540:760.
|
||||
*/
|
||||
body.hid #live2d {
|
||||
display: block; height: 100%; width: auto;
|
||||
aspect-ratio: 540 / 760; margin: 0 auto;
|
||||
}
|
||||
body.hid #hands {
|
||||
top: 326px;
|
||||
top: 526px;
|
||||
height: auto;
|
||||
bottom: var(--dock-reserve);
|
||||
bottom: var(--gesture-rail-reserve);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
/* The pads take whatever the fixed rows leave, rather than a written-down 92. */
|
||||
body.hid #track-row,
|
||||
body.hid #hands.keyboard #keyboard-pane { flex: 1; height: auto; min-height: 92px; }
|
||||
body.hid #track-row { flex: 1; height: auto; min-height: 180px; }
|
||||
#live2d { width: 100%; height: 100%; }
|
||||
#bubble {
|
||||
position: fixed; left: 8px; right: 8px; top: 12px;
|
||||
|
|
@ -56,8 +60,7 @@
|
|||
#bubble.up { opacity: 1; }
|
||||
|
||||
/*
|
||||
* The room viewtop left under her, given a purpose. Trackpad first; the
|
||||
* literal keyboard only replaces it when summoned. The dark sheer gradient
|
||||
* The room viewtop left under her, given a purpose. The dark sheer gradient
|
||||
* keeps the controls legible without putting an app-shaped slab around her.
|
||||
*/
|
||||
#hands {
|
||||
|
|
@ -100,20 +103,18 @@
|
|||
#hands-listening { display: none; color: #e8b7d8; }
|
||||
#hands.listening #hands-listening { display: inline; }
|
||||
#hands-status { flex: 1; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: rgba(255,255,255,.68); }
|
||||
.thread-button { width: 30px; min-width: 30px !important; height: 26px !important; padding: 0 !important; border-radius: 10px !important; }
|
||||
|
||||
#agent-form, #host-form { height: 40px; display: flex; gap: 6px; }
|
||||
#agent-input, #host-input {
|
||||
#agent-form { height: 40px; display: flex; gap: 6px; }
|
||||
#agent-input {
|
||||
min-width: 0; flex: 1; height: 40px; padding: 0 13px;
|
||||
border: 1px solid rgba(255,255,255,.12); border-radius: 14px;
|
||||
outline: none; color: #fff; background: rgba(0,0,0,.20);
|
||||
-webkit-user-select: text; user-select: text;
|
||||
}
|
||||
#agent-input:focus, #host-input:focus { border-color: rgba(217,180,235,.55); }
|
||||
#agent-input::placeholder, #host-input::placeholder { color: rgba(255,255,255,.43); }
|
||||
#agent-input:focus { border-color: rgba(217,180,235,.55); }
|
||||
#agent-input::placeholder { color: rgba(255,255,255,.43); }
|
||||
#mic.listening { color: #f0b6db; box-shadow: inset 0 0 0 1px rgba(240,182,219,.45); }
|
||||
|
||||
#track-row, #keyboard-pane { height: 92px; margin-top: 6px; }
|
||||
#track-row { height: 92px; margin-top: 6px; }
|
||||
#track-row { display: flex; gap: 6px; }
|
||||
#trackpad, #scrollpad {
|
||||
position: relative; overflow: hidden;
|
||||
|
|
@ -133,12 +134,6 @@
|
|||
content: '↕'; position: absolute; inset: 0; display: grid; place-items: center;
|
||||
color: rgba(255,255,255,.35); font-size: 20px;
|
||||
}
|
||||
#keyboard-pane { display: none; }
|
||||
#keyboard-pane .quick-keys { display: flex; gap: 5px; margin-top: 6px; }
|
||||
#keyboard-pane .quick-keys button { flex: 1; height: 40px; padding: 0 5px; }
|
||||
#hands.keyboard #track-row { display: none; }
|
||||
#hands.keyboard #keyboard-pane { display: block; }
|
||||
|
||||
.hands-actions { height: 40px; display: flex; gap: 5px; margin-top: 6px; }
|
||||
.hands-actions button { flex: 1; height: 40px !important; padding: 0 5px !important; }
|
||||
#cad.holding {
|
||||
|
|
@ -155,8 +150,6 @@
|
|||
<span id="hands-dot"></span>
|
||||
<span id="hands-status">USB hand asleep</span>
|
||||
<span id="hands-listening">listening</span>
|
||||
<button class="thread-button" id="resume-thread" title="Resume latest conversation">↶</button>
|
||||
<button class="thread-button" id="new-thread" title="Begin a new conversation">+</button>
|
||||
</div>
|
||||
<form id="agent-form">
|
||||
<button type="button" id="mic" aria-label="Start or stop listening">●</button>
|
||||
|
|
@ -168,24 +161,9 @@
|
|||
<div id="trackpad" aria-label="Trackpad"></div>
|
||||
<div id="scrollpad" aria-label="Scroll"></div>
|
||||
</div>
|
||||
<div id="keyboard-pane">
|
||||
<form id="host-form">
|
||||
<input id="host-input" autocomplete="off" enterkeyhint="send"
|
||||
placeholder="Type into the attached host…">
|
||||
<button type="submit" aria-label="Type on host">↑</button>
|
||||
</form>
|
||||
<div class="quick-keys">
|
||||
<button data-key="escape">Esc</button>
|
||||
<button data-key="tab">Tab</button>
|
||||
<button data-key="backspace">⌫</button>
|
||||
<button data-key="enter">Enter</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hands-actions">
|
||||
<button id="left-click">Left</button>
|
||||
<button id="right-click">Right</button>
|
||||
<button id="keyboard-toggle">⌨</button>
|
||||
<button id="arm-hid">Arm</button>
|
||||
<button id="cad" title="Hold for Ctrl+Alt+Delete">CAD</button>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -363,7 +341,7 @@
|
|||
window.souveraine.hid(message);
|
||||
}
|
||||
|
||||
var agentPending = false, hostPending = false;
|
||||
var agentPending = false;
|
||||
window.hands = {
|
||||
state: function (state) {
|
||||
state = state || {};
|
||||
|
|
@ -386,8 +364,6 @@
|
|||
: 'Tell ' + (state.agent || 'your agent') + ' what the other screen needs…';
|
||||
hands.classList.toggle('thinking', !!state.thinking);
|
||||
document.getElementById('agent-input').disabled = !!state.thinking || agentPending;
|
||||
document.getElementById('resume-thread').classList.toggle('on', state.conversation === 'resumed');
|
||||
document.getElementById('new-thread').classList.toggle('on', state.conversation === 'new');
|
||||
requestAnimationFrame(publishShape);
|
||||
},
|
||||
agentResult: function (result) {
|
||||
|
|
@ -397,14 +373,6 @@
|
|||
input.disabled = hands.classList.contains('thinking');
|
||||
if (result.accepted) input.value = '';
|
||||
else if (result.reason) document.getElementById('hands-status').textContent = result.reason;
|
||||
},
|
||||
hostResult: function (result) {
|
||||
result = result || {};
|
||||
hostPending = false;
|
||||
var input = document.getElementById('host-input');
|
||||
input.disabled = false;
|
||||
if (result.accepted) input.value = '';
|
||||
else if (result.reason) document.getElementById('hands-status').textContent = result.reason;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -422,13 +390,6 @@
|
|||
if (!window.souveraine) return;
|
||||
window.souveraine.talk(hands.classList.contains('listening') ? 'end' : 'start');
|
||||
});
|
||||
document.getElementById('resume-thread').addEventListener('click', function () {
|
||||
if (window.souveraine) window.souveraine.thread('resume');
|
||||
});
|
||||
document.getElementById('new-thread').addEventListener('click', function () {
|
||||
if (window.souveraine) window.souveraine.thread('new');
|
||||
});
|
||||
|
||||
var trackpad = document.getElementById('trackpad');
|
||||
var trackDown = false, trackX = 0, trackY = 0, trackTravel = 0, trackAt = 0;
|
||||
trackpad.addEventListener('pointerdown', function (event) {
|
||||
|
|
@ -478,28 +439,6 @@
|
|||
document.getElementById('right-click').addEventListener('click', function () {
|
||||
hid('click', { button: 'right' });
|
||||
});
|
||||
document.getElementById('arm-hid').addEventListener('click', function () { hid('arm'); });
|
||||
|
||||
var keyboardToggle = document.getElementById('keyboard-toggle');
|
||||
keyboardToggle.addEventListener('click', function () {
|
||||
var showing = !hands.classList.contains('keyboard');
|
||||
hands.classList.toggle('keyboard', showing);
|
||||
keyboardToggle.classList.toggle('on', showing);
|
||||
var input = document.getElementById('host-input');
|
||||
if (showing) input.focus(); else input.blur();
|
||||
});
|
||||
document.getElementById('host-form').addEventListener('submit', function (event) {
|
||||
event.preventDefault();
|
||||
var input = document.getElementById('host-input');
|
||||
if (input.value.length === 0 || hostPending) return;
|
||||
hostPending = true;
|
||||
input.disabled = true;
|
||||
hid('type', { text: input.value });
|
||||
});
|
||||
Array.prototype.forEach.call(document.querySelectorAll('[data-key]'), function (button) {
|
||||
button.addEventListener('click', function () { hid('key', { key: button.dataset.key }); });
|
||||
});
|
||||
|
||||
// Ctrl+Alt+Delete is the one quick key a stray tap must not send. Its hold
|
||||
// is local feedback; only the completed edge crosses to the HID owner.
|
||||
var cad = document.getElementById('cad'), cadTimer = null, cadPointer = null;
|
||||
|
|
|
|||
Loading…
Reference in a new issue