koneck 2.57.0 → 2.59.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/browser.d.ts +39 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +97 -19
- package/dist/browser.js.map +1 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +14 -0
- package/dist/engine.js.map +1 -1
- package/dist/handoff.d.ts +76 -0
- package/dist/handoff.d.ts.map +1 -0
- package/dist/handoff.js +152 -0
- package/dist/handoff.js.map +1 -0
- package/dist/ink-chat.d.ts.map +1 -1
- package/dist/ink-chat.js +2 -0
- package/dist/ink-chat.js.map +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/web/api.d.ts.map +1 -1
- package/dist/web/api.js +15 -3
- package/dist/web/api.js.map +1 -1
- package/dist/web/sessions.d.ts +4 -0
- package/dist/web/sessions.d.ts.map +1 -1
- package/dist/web/sessions.js +15 -0
- package/dist/web/sessions.js.map +1 -1
- package/dist/web/ui-client.d.ts.map +1 -1
- package/dist/web/ui-client.js +97 -7
- 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 +11 -1
- package/dist/web/ui-css.js.map +1 -1
- package/package.json +1 -1
package/dist/web/ui-client.js
CHANGED
|
@@ -2985,6 +2985,69 @@ $('tfontdown').onclick = () => { term.fontPx = Math.max(9, term.fontPx - 1); app
|
|
|
2985
2985
|
*/
|
|
2986
2986
|
const preview = { shot: null, width: 0 };
|
|
2987
2987
|
|
|
2988
|
+
/*
|
|
2989
|
+
* Typing into the page.
|
|
2990
|
+
*
|
|
2991
|
+
* The panel could click a page and not type into it: you clicked a field, the caret appeared, and
|
|
2992
|
+
* every key went nowhere. So a form could be reached and never filled — which made the panel
|
|
2993
|
+
* something to look at rather than something to use.
|
|
2994
|
+
*
|
|
2995
|
+
* Characters are batched. One request per keystroke would put a round trip between each letter and
|
|
2996
|
+
* the next, which on a page that answers in eighty milliseconds is a keyboard that lags behind the
|
|
2997
|
+
* fingers. They accumulate for a moment and go as one string, which is also how a page sees a paste
|
|
2998
|
+
* — indistinguishable from fast typing, and considerably faster than it.
|
|
2999
|
+
*
|
|
3000
|
+
* Keys that do something other than insert a character — Enter, Tab, Backspace, the arrows — are
|
|
3001
|
+
* sent on their own and at once, because their whole purpose is the thing that happens next.
|
|
3002
|
+
*/
|
|
3003
|
+
const typing = { pending: '', timer: null };
|
|
3004
|
+
|
|
3005
|
+
/** Keys the page must be told about individually, with what they are called on the wire. */
|
|
3006
|
+
const SPECIAL_KEYS = {
|
|
3007
|
+
Enter: 'Enter', Tab: 'Tab', Backspace: 'Backspace', Escape: 'Escape',
|
|
3008
|
+
ArrowUp: 'ArrowUp', ArrowDown: 'ArrowDown', ArrowLeft: 'ArrowLeft', ArrowRight: 'ArrowRight',
|
|
3009
|
+
};
|
|
3010
|
+
|
|
3011
|
+
function flushTyping() {
|
|
3012
|
+
if (typing.timer) { clearTimeout(typing.timer); typing.timer = null; }
|
|
3013
|
+
const text = typing.pending;
|
|
3014
|
+
typing.pending = '';
|
|
3015
|
+
if (text === '') return Promise.resolve();
|
|
3016
|
+
return browserDo({ action: 'text', text: text });
|
|
3017
|
+
}
|
|
3018
|
+
|
|
3019
|
+
async function pageKey(ev) {
|
|
3020
|
+
// Left alone: the browser's own shortcuts, and anything with a modifier that is not Shift.
|
|
3021
|
+
if (ev.metaKey || ev.ctrlKey || ev.altKey) return;
|
|
3022
|
+
const special = SPECIAL_KEYS[ev.key];
|
|
3023
|
+
if (special) {
|
|
3024
|
+
ev.preventDefault();
|
|
3025
|
+
await flushTyping();
|
|
3026
|
+
// Enter can submit, so it takes the full settle the press path already gives it.
|
|
3027
|
+
await browserDo({ action: 'press', key: special });
|
|
3028
|
+
return;
|
|
3029
|
+
}
|
|
3030
|
+
// One printable character. Anything longer is a named key this does not handle.
|
|
3031
|
+
if (ev.key.length !== 1) return;
|
|
3032
|
+
ev.preventDefault();
|
|
3033
|
+
typing.pending += ev.key;
|
|
3034
|
+
if (typing.timer) clearTimeout(typing.timer);
|
|
3035
|
+
typing.timer = setTimeout(() => { void flushTyping(); }, TYPE_BATCH_MS);
|
|
3036
|
+
}
|
|
3037
|
+
|
|
3038
|
+
/** Long enough to gather a burst of keys, short enough that the page keeps up with the hand. */
|
|
3039
|
+
const TYPE_BATCH_MS = 110;
|
|
3040
|
+
|
|
3041
|
+
/** Where in the page a pointer event landed, scaled back from however wide the picture is drawn. */
|
|
3042
|
+
function pageCoords(img, ev) {
|
|
3043
|
+
const box = img.getBoundingClientRect();
|
|
3044
|
+
const scale = img.naturalWidth / box.width;
|
|
3045
|
+
return {
|
|
3046
|
+
x: Math.round((ev.clientX - box.left) * scale),
|
|
3047
|
+
y: Math.round((ev.clientY - box.top) * scale),
|
|
3048
|
+
};
|
|
3049
|
+
}
|
|
3050
|
+
|
|
2988
3051
|
function drawPreview(data) {
|
|
2989
3052
|
const host = $('pvbody');
|
|
2990
3053
|
host.innerHTML = '';
|
|
@@ -3008,19 +3071,46 @@ function drawPreview(data) {
|
|
|
3008
3071
|
img.className = 'pvshot';
|
|
3009
3072
|
img.src = data.shot;
|
|
3010
3073
|
img.alt = data.title || 'the page';
|
|
3074
|
+
/*
|
|
3075
|
+
* The cursor answers the question a browser answers: is there something here to click?
|
|
3076
|
+
*
|
|
3077
|
+
* It was a crosshair over the whole page, which is the language of a screenshot tool. The
|
|
3078
|
+
* controls carry their boxes, so hovering one shows a hand and its label, and everywhere else
|
|
3079
|
+
* shows an arrow — the same two states a real page has, from information the panel already had
|
|
3080
|
+
* and was discarding.
|
|
3081
|
+
*/
|
|
3082
|
+
const controls = (data.controls || []).filter((c) => c.box);
|
|
3083
|
+
img.onmousemove = (ev) => {
|
|
3084
|
+
const at = pageCoords(img, ev);
|
|
3085
|
+
const over = controls.find((c) =>
|
|
3086
|
+
at.x >= c.box.x && at.x <= c.box.x + c.box.w &&
|
|
3087
|
+
at.y >= c.box.y && at.y <= c.box.y + c.box.h);
|
|
3088
|
+
img.style.cursor = over ? 'pointer' : 'default';
|
|
3089
|
+
img.title = over ? (over.label || over.tag) : '';
|
|
3090
|
+
};
|
|
3091
|
+
img.onmouseleave = () => { img.style.cursor = 'default'; img.title = ''; };
|
|
3092
|
+
/*
|
|
3093
|
+
* Focusable, so the keyboard has somewhere to go.
|
|
3094
|
+
*
|
|
3095
|
+
* An image cannot receive key events without this, which is the mechanical reason typing did
|
|
3096
|
+
* nothing: there was no element for the keys to arrive at. Clicking the page focuses it, so
|
|
3097
|
+
* clicking a field and then typing works the way it reads.
|
|
3098
|
+
*/
|
|
3099
|
+
img.tabIndex = 0;
|
|
3100
|
+
img.onkeydown = (ev) => { void pageKey(ev); };
|
|
3101
|
+
img.onblur = () => { void flushTyping(); };
|
|
3102
|
+
|
|
3011
3103
|
// The picture is 1280 wide whatever it is displayed at, so a click has to be scaled back into
|
|
3012
3104
|
// page coordinates or it lands somewhere else entirely.
|
|
3013
3105
|
img.onclick = async (ev) => {
|
|
3014
|
-
|
|
3015
|
-
const
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
$('pvinfo').textContent = 'clicking ' + x + ',' + y + '…';
|
|
3019
|
-
await browserDo({ action: 'click', x: x, y: y });
|
|
3106
|
+
img.focus();
|
|
3107
|
+
const at = pageCoords(img, ev);
|
|
3108
|
+
$('pvinfo').textContent = 'clicking ' + at.x + ',' + at.y + '…';
|
|
3109
|
+
await browserDo({ action: 'click', x: at.x, y: at.y });
|
|
3020
3110
|
};
|
|
3021
3111
|
host.appendChild(img);
|
|
3022
3112
|
const hint = el('div', 'pvhint',
|
|
3023
|
-
'Click the page to click it for real. ' + (data.controls || []).length
|
|
3113
|
+
'Click the page to click it for real, then type. ' + (data.controls || []).length
|
|
3024
3114
|
+ ' interactive elements.');
|
|
3025
3115
|
host.appendChild(hint);
|
|
3026
3116
|
// The same numbered elements the agent is given, clickable — so a person can drive the flow the
|
|
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6nIlB,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,CAm9B3C"}
|
package/dist/web/ui-css.js
CHANGED
|
@@ -871,7 +871,17 @@ body[data-tint="soft"]{filter:contrast(.92) brightness(1.04)}
|
|
|
871
871
|
.pvbody{flex:1;overflow:auto;min-height:0;display:flex;flex-direction:column;
|
|
872
872
|
align-items:center;padding:12px;background:var(--panel-2)}
|
|
873
873
|
/* The screenshot is the page. Clicking it clicks the page at the same point. */
|
|
874
|
-
|
|
874
|
+
/*
|
|
875
|
+
* A page behaves like a page: an arrow over it, a hand over what can be clicked.
|
|
876
|
+
*
|
|
877
|
+
* It was a crosshair everywhere, which is the language of a screenshot tool — "pick a coordinate" —
|
|
878
|
+
* and the panel is not a screenshot tool. The controls already carry their boxes, so the client
|
|
879
|
+
* switches between the two the way a browser does, and clicking still lands as a real mouse event
|
|
880
|
+
* at the same point.
|
|
881
|
+
*/
|
|
882
|
+
/* Focused, it says so — otherwise there is no telling where the keyboard is pointed. */
|
|
883
|
+
.pvshot:focus{outline:2px solid var(--cyan);outline-offset:1px}
|
|
884
|
+
.pvshot{max-width:100%;border:1px solid var(--line);border-radius:8px;cursor:default;
|
|
875
885
|
background:#fff;box-shadow:var(--shadow)}
|
|
876
886
|
.pvhint{margin-top:11px;font-size:11.5px;color:var(--dim);text-align:center;line-height:1.6}
|
|
877
887
|
/* The same numbered elements the agent is given, so a person can drive the flow it is stuck on. */
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA27B9B,CAAC;AACF,CAAC"}
|