@autono/pinbox-toolbar 0.1.0 → 0.7.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/{index-ZR3JBQu-.d.ts → index-DnhwJYxE.d.ts} +18 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/plugins/vite.js +11 -7
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{src-BoLg81_j.js → src-D1qn1iet.js} +430 -33
- package/dist/svelte.d.ts +1 -1
- package/dist/svelte.js +1 -1
- package/dist/toolbar.iife.js +430 -33
- package/dist/vue.d.ts +1 -1
- package/dist/vue.js +1 -1
- package/package.json +2 -2
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
//#region src/targeting/dom.ts
|
|
2
2
|
/**
|
|
3
|
-
* Deepest element under (clientX, clientY), or null when
|
|
4
|
-
* chrome
|
|
3
|
+
* Deepest element under (clientX, clientY) that the caller does not ignore, or null when there is
|
|
4
|
+
* nothing there but page chrome (html/body).
|
|
5
|
+
*
|
|
6
|
+
* Looks THROUGH our own overlay rather than giving up at it. The single-element form could not:
|
|
7
|
+
* the drag-aim grip sits exactly on the point being aimed at, so it is always the topmost thing
|
|
8
|
+
* under the crosshair, and every probe came back "nothing" the moment touch aiming existed.
|
|
5
9
|
*/
|
|
6
10
|
function hitTest(doc, x, y, ignore) {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
const stack = doc.elementsFromPoint?.(x, y) ?? [doc.elementFromPoint(x, y)];
|
|
12
|
+
for (const el of stack) {
|
|
13
|
+
if (!el || el === doc.body || el === doc.documentElement) return null;
|
|
14
|
+
if (!ignore(el)) return el;
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
10
17
|
}
|
|
11
18
|
/** CLASS-or-TAG display name with a sibling index when needed (prototype nodeName). */
|
|
12
19
|
function nodeName(el) {
|
|
@@ -146,6 +153,44 @@ function isFixed(win, el) {
|
|
|
146
153
|
}
|
|
147
154
|
return false;
|
|
148
155
|
}
|
|
156
|
+
/** Beyond this an element is not a thing you pinned, it is a region. Too many to rewrite as a set. */
|
|
157
|
+
const MAX_RUNS = 40;
|
|
158
|
+
const MAX_RUN_LENGTH = 200;
|
|
159
|
+
/** Text that is not content: a script body or a stylesheet is not something to rewrite. */
|
|
160
|
+
const NON_CONTENT = /* @__PURE__ */ new Set([
|
|
161
|
+
"SCRIPT",
|
|
162
|
+
"STYLE",
|
|
163
|
+
"NOSCRIPT",
|
|
164
|
+
"TEMPLATE"
|
|
165
|
+
]);
|
|
166
|
+
/**
|
|
167
|
+
* The element's text, split the way the browser stores it: one entry per run of characters.
|
|
168
|
+
*
|
|
169
|
+
* This walks TEXT NODES, not elements, and that distinction is the whole point — it makes no
|
|
170
|
+
* assumption about how a site is built. A heading is one run. A nav bar is one per link. A
|
|
171
|
+
* paragraph with a bold word in the middle is three, in reading order, including the halves either
|
|
172
|
+
* side of the bold. An earlier version keyed off "elements with no element children", which
|
|
173
|
+
* quietly lost the "Hello " in `<p>Hello <b>world</b></p>` — text a person can obviously see and
|
|
174
|
+
* would obviously expect to be able to change.
|
|
175
|
+
*
|
|
176
|
+
* `nearbyText` runs them all together, which is fine to read and useless to edit: it cannot tell
|
|
177
|
+
* an agent that "work approach people contact" is four separate places. This can.
|
|
178
|
+
*/
|
|
179
|
+
function textRuns(el) {
|
|
180
|
+
const runs = [];
|
|
181
|
+
const walk = (node) => {
|
|
182
|
+
if (node.nodeType === 3) {
|
|
183
|
+
const text = (node.nodeValue ?? "").trim();
|
|
184
|
+
if (text.length > 0) runs.push(text.slice(0, MAX_RUN_LENGTH));
|
|
185
|
+
return runs.length <= MAX_RUNS;
|
|
186
|
+
}
|
|
187
|
+
if (node.nodeType !== 1 || NON_CONTENT.has(node.tagName)) return true;
|
|
188
|
+
for (const child of node.childNodes) if (!walk(child)) return false;
|
|
189
|
+
return true;
|
|
190
|
+
};
|
|
191
|
+
if (!walk(el) || runs.length === 0) return void 0;
|
|
192
|
+
return runs;
|
|
193
|
+
}
|
|
149
194
|
function buildContext(win, el) {
|
|
150
195
|
const context = {};
|
|
151
196
|
if (el.classList.length > 0) context.classes = [...el.classList];
|
|
@@ -157,6 +202,8 @@ function buildContext(win, el) {
|
|
|
157
202
|
if (nearby !== void 0) context.nearbyText = nearby;
|
|
158
203
|
const selected = selectedText(win, el);
|
|
159
204
|
if (selected !== void 0) context.selectedText = selected;
|
|
205
|
+
const runs = textRuns(el);
|
|
206
|
+
if (runs !== void 0) context.textRuns = runs;
|
|
160
207
|
return Object.keys(context).length > 0 ? context : void 0;
|
|
161
208
|
}
|
|
162
209
|
/** Fills PinInput.target/env from a chosen element (shapes come from the pin schema). */
|
|
@@ -176,6 +223,14 @@ function captureTarget(el, opts) {
|
|
|
176
223
|
fixed: isFixed(win, el)
|
|
177
224
|
};
|
|
178
225
|
if (opts?.anchor !== void 0) target.anchor = opts.anchor;
|
|
226
|
+
if (opts?.at !== void 0 && r.width > 0 && r.height > 0) {
|
|
227
|
+
const fx = (opts.at.x - (r.left + win.scrollX)) / r.width;
|
|
228
|
+
const fy = (opts.at.y - (r.top + win.scrollY)) / r.height;
|
|
229
|
+
if (fx >= 0 && fx <= 1 && fy >= 0 && fy <= 1) target.spot = {
|
|
230
|
+
x: fx,
|
|
231
|
+
y: fy
|
|
232
|
+
};
|
|
233
|
+
}
|
|
179
234
|
const context = buildContext(win, el);
|
|
180
235
|
if (context !== void 0) target.context = context;
|
|
181
236
|
return {
|
|
@@ -940,6 +995,118 @@ var HubTransport = class {
|
|
|
940
995
|
}
|
|
941
996
|
};
|
|
942
997
|
//#endregion
|
|
998
|
+
//#region src/ui/aim.ts
|
|
999
|
+
/**
|
|
1000
|
+
* True when aiming has to be done by dragging rather than by pointing.
|
|
1001
|
+
*
|
|
1002
|
+
* Two independent reasons, either of which is sufficient. A coarse pointer means there is no
|
|
1003
|
+
* hover to follow at all — the mouse crosshair cannot work, whatever the screen size. The width
|
|
1004
|
+
* check is the design's own rule (720px) and catches the case a media query cannot: a device that
|
|
1005
|
+
* reports a fine pointer but is being used at phone width.
|
|
1006
|
+
*/
|
|
1007
|
+
function needsDragAim(win) {
|
|
1008
|
+
return win.matchMedia?.("(pointer: coarse)").matches === true || win.innerWidth < 720;
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Where the reticle starts.
|
|
1012
|
+
*
|
|
1013
|
+
* Slightly above centre: the confirm bar owns the bottom of the screen, and a reticle that opens
|
|
1014
|
+
* underneath your own thumb is one you have to move before you can even see it.
|
|
1015
|
+
*/
|
|
1016
|
+
function startPoint(win) {
|
|
1017
|
+
return {
|
|
1018
|
+
x: win.innerWidth / 2,
|
|
1019
|
+
y: win.innerHeight * .42
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
const MARKUP = "<div class=\"h\"></div><div class=\"v\"></div><div class=\"grip\" aria-label=\"Pin position — arrow keys to aim\" tabindex=\"0\"><i></i></div><div class=\"bar\"><span class=\"lab\" role=\"status\" aria-live=\"polite\"></span><button type=\"button\" class=\"cancel\" data-aim=\"cancel\">CANCEL</button><button type=\"button\" class=\"ok\" data-aim=\"confirm\">PIN IT HERE</button></div>";
|
|
1023
|
+
function createAim(doc, handlers) {
|
|
1024
|
+
const win = doc.defaultView;
|
|
1025
|
+
const root = doc.createElement("div");
|
|
1026
|
+
root.className = "pb-aim";
|
|
1027
|
+
root.innerHTML = MARKUP;
|
|
1028
|
+
const h = root.querySelector(".h");
|
|
1029
|
+
const v = root.querySelector(".v");
|
|
1030
|
+
const grip = root.querySelector(".grip");
|
|
1031
|
+
const label = root.querySelector(".lab");
|
|
1032
|
+
const point = {
|
|
1033
|
+
x: 0,
|
|
1034
|
+
y: 0
|
|
1035
|
+
};
|
|
1036
|
+
/** Grab offset, so the reticle does not jump to your fingertip when you take hold of it. */
|
|
1037
|
+
let grab = null;
|
|
1038
|
+
function put(x, y) {
|
|
1039
|
+
point.x = Math.max(0, Math.min(win.innerWidth, x));
|
|
1040
|
+
point.y = Math.max(0, Math.min(win.innerHeight, y));
|
|
1041
|
+
h.style.top = `${point.y}px`;
|
|
1042
|
+
v.style.left = `${point.x}px`;
|
|
1043
|
+
grip.style.left = `${point.x}px`;
|
|
1044
|
+
grip.style.top = `${point.y}px`;
|
|
1045
|
+
}
|
|
1046
|
+
const onPointerMove = (e) => {
|
|
1047
|
+
if (grab === null) return;
|
|
1048
|
+
e.preventDefault();
|
|
1049
|
+
put(e.clientX + grab.dx, e.clientY + grab.dy);
|
|
1050
|
+
handlers.onAim(point.x, point.y);
|
|
1051
|
+
};
|
|
1052
|
+
const onPointerUp = () => {
|
|
1053
|
+
grab = null;
|
|
1054
|
+
};
|
|
1055
|
+
grip.addEventListener("pointerdown", (e) => {
|
|
1056
|
+
e.preventDefault();
|
|
1057
|
+
e.stopPropagation();
|
|
1058
|
+
grab = {
|
|
1059
|
+
dx: point.x - e.clientX,
|
|
1060
|
+
dy: point.y - e.clientY
|
|
1061
|
+
};
|
|
1062
|
+
grip.setPointerCapture?.(e.pointerId);
|
|
1063
|
+
});
|
|
1064
|
+
grip.addEventListener("keydown", (e) => {
|
|
1065
|
+
const step = e.shiftKey ? 20 : 2;
|
|
1066
|
+
const delta = {
|
|
1067
|
+
ArrowLeft: [-step, 0],
|
|
1068
|
+
ArrowRight: [step, 0],
|
|
1069
|
+
ArrowUp: [0, -step],
|
|
1070
|
+
ArrowDown: [0, step]
|
|
1071
|
+
}[e.key];
|
|
1072
|
+
if (!delta) return;
|
|
1073
|
+
e.preventDefault();
|
|
1074
|
+
put(point.x + delta[0], point.y + delta[1]);
|
|
1075
|
+
handlers.onAim(point.x, point.y);
|
|
1076
|
+
});
|
|
1077
|
+
root.addEventListener("click", (e) => {
|
|
1078
|
+
const action = e.target.closest?.("[data-aim]")?.getAttribute("data-aim");
|
|
1079
|
+
if (!action) return;
|
|
1080
|
+
e.preventDefault();
|
|
1081
|
+
e.stopPropagation();
|
|
1082
|
+
if (action === "confirm") handlers.onConfirm();
|
|
1083
|
+
else handlers.onCancel();
|
|
1084
|
+
});
|
|
1085
|
+
win.addEventListener("pointermove", onPointerMove, { passive: false });
|
|
1086
|
+
win.addEventListener("pointerup", onPointerUp);
|
|
1087
|
+
win.addEventListener("pointercancel", onPointerUp);
|
|
1088
|
+
return {
|
|
1089
|
+
root,
|
|
1090
|
+
point,
|
|
1091
|
+
show(x, y) {
|
|
1092
|
+
put(x, y);
|
|
1093
|
+
root.classList.add("on");
|
|
1094
|
+
},
|
|
1095
|
+
hide() {
|
|
1096
|
+
grab = null;
|
|
1097
|
+
root.classList.remove("on");
|
|
1098
|
+
},
|
|
1099
|
+
setLabel(text) {
|
|
1100
|
+
label.textContent = text;
|
|
1101
|
+
},
|
|
1102
|
+
destroy() {
|
|
1103
|
+
win.removeEventListener("pointermove", onPointerMove);
|
|
1104
|
+
win.removeEventListener("pointerup", onPointerUp);
|
|
1105
|
+
win.removeEventListener("pointercancel", onPointerUp);
|
|
1106
|
+
}
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
//#endregion
|
|
943
1110
|
//#region src/ui/bar.ts
|
|
944
1111
|
const PIN_ICON = "<svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.4\"><rect x=\"3\" y=\"1.5\" width=\"10\" height=\"6.5\" rx=\"1\"/><path d=\"M8 8v6.5\"/></svg>";
|
|
945
1112
|
const INBOX_ICON = "<svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.4\"><path d=\"M1.8 8.5h3.4l1 2h3.6l1-2h3.4\"/><path d=\"M2.6 3.2h10.8l1.2 5.3v4a1 1 0 01-1 1H2.4a1 1 0 01-1-1v-4z\"/></svg>";
|
|
@@ -1048,6 +1215,31 @@ function messageHtml(m) {
|
|
|
1048
1215
|
const via = origin ? `<span class="via-tag"><span>${esc(origin)}</span></span>` : "";
|
|
1049
1216
|
return `<div class="pb-msg you"><div class="pb-av${mirror ? " via" : ""}">${esc(initials)}</div><div class="col"><div class="line"><span class="who">${esc(who)}</span><span class="tm">${esc(timeOf(m.at))}</span>${via}</div><div class="txt">${esc(m.text)}</div>${attachmentsHtml(m)}</div></div>`;
|
|
1050
1217
|
}
|
|
1218
|
+
/** The agent has the message and has not answered yet. Its own node, so patching never rebuilds. */
|
|
1219
|
+
const TYPING_HTML = "<div class=\"pb-typing\"><div class=\"pb-av agent\">AI</div><div class=\"dots\"><i></i><i></i><i></i></div><div class=\"lbl\">THINKING</div></div>";
|
|
1220
|
+
/**
|
|
1221
|
+
* Show or hide the "working on it" row.
|
|
1222
|
+
*
|
|
1223
|
+
* Without it the card sits silent from the moment you comment until the answer lands, which reads
|
|
1224
|
+
* as nothing happening — the single most common report on the demo.
|
|
1225
|
+
*/
|
|
1226
|
+
function patchTyping(threadEl, pending) {
|
|
1227
|
+
const existing = threadEl.querySelector("[data-iid=\"pb-typing\"]");
|
|
1228
|
+
if (!pending) {
|
|
1229
|
+
existing?.remove();
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
if (existing) {
|
|
1233
|
+
threadEl.appendChild(existing);
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
const node = threadEl.ownerDocument.createElement("div");
|
|
1237
|
+
node.className = "pb-msg-w";
|
|
1238
|
+
node.setAttribute("data-iid", "pb-typing");
|
|
1239
|
+
node.innerHTML = TYPING_HTML;
|
|
1240
|
+
threadEl.appendChild(node);
|
|
1241
|
+
threadEl.scrollTop = threadEl.scrollHeight;
|
|
1242
|
+
}
|
|
1051
1243
|
/** Keyed thread patching: appends/patches [data-iid] nodes only, never rebuilds. */
|
|
1052
1244
|
function patchThread(threadEl, messages) {
|
|
1053
1245
|
let appended = false;
|
|
@@ -1213,6 +1405,22 @@ function viewOf(state) {
|
|
|
1213
1405
|
};
|
|
1214
1406
|
}
|
|
1215
1407
|
/** Render the thread card for a state snapshot: the active pin, or the draft. */
|
|
1408
|
+
/**
|
|
1409
|
+
* The pin's own text, as the first message in its thread.
|
|
1410
|
+
*
|
|
1411
|
+
* A pin stores what you wrote on the pin itself, not in the thread — so a card that renders only
|
|
1412
|
+
* `thread` shows an empty box the moment you hit Comment, and your words look lost. They are not
|
|
1413
|
+
* lost; they were never drawn.
|
|
1414
|
+
*/
|
|
1415
|
+
function pinAsMessage(pin) {
|
|
1416
|
+
return {
|
|
1417
|
+
id: `pin:${pin.id}`,
|
|
1418
|
+
pinId: pin.id,
|
|
1419
|
+
role: "human",
|
|
1420
|
+
text: pin.text,
|
|
1421
|
+
at: pin.createdAt
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1216
1424
|
function renderCard(root, state, actions) {
|
|
1217
1425
|
const card = ensureShell(root);
|
|
1218
1426
|
const ctx = ctxByCard.get(card);
|
|
@@ -1226,7 +1434,7 @@ function renderCard(root, state, actions) {
|
|
|
1226
1434
|
if (ctx.pid !== view.pid) {
|
|
1227
1435
|
ctx.pid = view.pid;
|
|
1228
1436
|
ctx.parts = {};
|
|
1229
|
-
buildSkeleton(card, ctx, view.pid === "draft", view.thread.length > 0);
|
|
1437
|
+
buildSkeleton(card, ctx, view.pid === "draft", view.pin !== null || view.thread.length > 0);
|
|
1230
1438
|
}
|
|
1231
1439
|
card.hidden = false;
|
|
1232
1440
|
const queued = view.pin !== null && state.queuedIds.has(view.pin.id);
|
|
@@ -1235,9 +1443,13 @@ function renderCard(root, state, actions) {
|
|
|
1235
1443
|
setPart(card, ctx, "hd", hdHtml(view.n, view.label, statusLabel, resolvable));
|
|
1236
1444
|
setPart(card, ctx, "link", linkHtml(view.pin));
|
|
1237
1445
|
setPart(card, ctx, "verify", verifyHtml(view.status));
|
|
1238
|
-
|
|
1446
|
+
const messages = view.pin === null ? view.thread : [pinAsMessage(view.pin), ...view.thread];
|
|
1447
|
+
setPart(card, ctx, "row", rowHtml(messages.length > 0));
|
|
1239
1448
|
const threadEl = card.querySelector("[data-ref=\"thread\"]");
|
|
1240
|
-
if (threadEl)
|
|
1449
|
+
if (threadEl) {
|
|
1450
|
+
patchThread(threadEl, messages);
|
|
1451
|
+
patchTyping(threadEl, !queued && view.pin?.status === "open" && view.status === "waiting");
|
|
1452
|
+
}
|
|
1241
1453
|
position(card, view.at);
|
|
1242
1454
|
}
|
|
1243
1455
|
//#endregion
|
|
@@ -1337,11 +1549,19 @@ function createDrawer(doc, on) {
|
|
|
1337
1549
|
//#region src/ui/pins.ts
|
|
1338
1550
|
/** The prototype's `_h` innerHTML memo, kept off the DOM node. */
|
|
1339
1551
|
const chipMemo = /* @__PURE__ */ new WeakMap();
|
|
1340
|
-
/**
|
|
1341
|
-
|
|
1552
|
+
/**
|
|
1553
|
+
* Where the needle lands: the point inside the element that was actually clicked, when the pin
|
|
1554
|
+
* recorded one, else the centre of its box.
|
|
1555
|
+
*
|
|
1556
|
+
* `spot` is a fraction of the element, so the pin still tracks the element when it moves or
|
|
1557
|
+
* resizes — it just stops sliding to the middle of a wide block the moment you commit it.
|
|
1558
|
+
*/
|
|
1559
|
+
function pinPoint(r, spot) {
|
|
1560
|
+
const fx = spot?.x ?? .5;
|
|
1561
|
+
const fy = spot?.y ?? .5;
|
|
1342
1562
|
return {
|
|
1343
|
-
x: r.x + r.width
|
|
1344
|
-
y: r.y + r.height
|
|
1563
|
+
x: r.x + r.width * fx,
|
|
1564
|
+
y: r.y + r.height * fy
|
|
1345
1565
|
};
|
|
1346
1566
|
}
|
|
1347
1567
|
/** Chip contents (prototype chipBtnInner, lines 546–550): number + linked-channel tag,
|
|
@@ -1383,21 +1603,28 @@ function renderPins(layer, state) {
|
|
|
1383
1603
|
const placed = [];
|
|
1384
1604
|
visible.forEach((pin, i) => {
|
|
1385
1605
|
const rect = pin.target?.rect;
|
|
1386
|
-
if (rect
|
|
1606
|
+
if (rect === void 0) return;
|
|
1607
|
+
const spot = pin.target?.spot;
|
|
1608
|
+
placed.push(spot === void 0 ? {
|
|
1387
1609
|
pin,
|
|
1388
1610
|
n: i + 1,
|
|
1389
1611
|
rect
|
|
1612
|
+
} : {
|
|
1613
|
+
pin,
|
|
1614
|
+
n: i + 1,
|
|
1615
|
+
rect,
|
|
1616
|
+
spot
|
|
1390
1617
|
});
|
|
1391
1618
|
});
|
|
1392
1619
|
const keys = new Set(placed.map((entry) => entry.pin.id));
|
|
1393
1620
|
if (state.draft) keys.add("draft");
|
|
1394
1621
|
for (const node of [...layer.children]) if (!keys.has(node.getAttribute("data-pin") ?? "")) node.remove();
|
|
1395
|
-
for (const { pin, n, rect } of placed) {
|
|
1622
|
+
for (const { pin, n, rect, spot } of placed) {
|
|
1396
1623
|
const node = ensureNode(layer, pin.id, false);
|
|
1397
1624
|
const hot = pin.id === state.activePinId;
|
|
1398
1625
|
const queued = state.queuedIds.has(pin.id);
|
|
1399
1626
|
node.classList.toggle("queued", queued);
|
|
1400
|
-
patchNode(node, pinPoint(rect), hot, chipInner(n, pin, queued));
|
|
1627
|
+
patchNode(node, pinPoint(rect, spot), hot, chipInner(n, pin, queued));
|
|
1401
1628
|
}
|
|
1402
1629
|
if (state.draft) patchNode(ensureNode(layer, "draft", true), state.draft.placedAt, true, chipInner(visible.length + 1, null));
|
|
1403
1630
|
}
|
|
@@ -1538,6 +1765,26 @@ button { font: inherit; color: inherit; background: none; border: 0; cursor: poi
|
|
|
1538
1765
|
.pb-reticle .box { position: absolute; width: 15px; height: 15px; margin: -8px 0 0 -8px; border: 1px solid var(--pb-amber); border-radius: 2px; }
|
|
1539
1766
|
.pb-reticle .ro { position: absolute; margin: 14px 0 0 14px; padding: 3px 6px; background: var(--pb-amber); color: var(--pb-amber-ink); font-family: var(--pb-font-mono); font-size: 9.5px; letter-spacing: .12em; border-radius: 2px; white-space: nowrap; }
|
|
1540
1767
|
|
|
1768
|
+
/* Drag-to-aim, for touch. The layer never takes pointer events — only the grip and the bar do —
|
|
1769
|
+
so what is under the crosshair can still be probed, and the page underneath is still visible. */
|
|
1770
|
+
/* Above the command bar (90), below the shortcuts modal (120). The confirm bar sits at the very
|
|
1771
|
+
bottom of the screen, where the command bar already is — under it, CONFIRM was unclickable. */
|
|
1772
|
+
.pb-aim { position: fixed; inset: 0; z-index: 100; display: none; pointer-events: none; }
|
|
1773
|
+
.pb-aim.on { display: block; animation: pb-fade 160ms ease-out both; }
|
|
1774
|
+
.pb-aim .h { position: absolute; left: 0; right: 0; height: 1px; background: color-mix(in srgb, var(--pb-amber) 30%, transparent); }
|
|
1775
|
+
.pb-aim .v { position: absolute; top: 0; bottom: 0; width: 1px; background: color-mix(in srgb, var(--pb-amber) 30%, transparent); }
|
|
1776
|
+
/* 72px: a finger-sized target, per the design. Smaller and you cannot hold it accurately;
|
|
1777
|
+
touch-action:none is what stops the page scrolling instead of the reticle moving. */
|
|
1778
|
+
.pb-aim .grip { position: absolute; width: 72px; height: 72px; margin: -36px 0 0 -36px; border-radius: 999px; border: 1px solid var(--pb-amber); background: var(--pb-amber-soft); backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px); display: flex; align-items: center; justify-content: center; pointer-events: auto; touch-action: none; cursor: grab; }
|
|
1779
|
+
.pb-aim .grip:active { cursor: grabbing; }
|
|
1780
|
+
.pb-aim .grip i { width: 10px; height: 10px; border-radius: 999px; background: var(--pb-amber); box-shadow: 0 0 0 3px var(--pb-canvas); }
|
|
1781
|
+
.pb-aim .bar { position: absolute; left: 12px; right: 12px; bottom: 12px; display: flex; align-items: center; gap: 8px; padding: 7px; background: var(--pb-bar); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border: 1px solid var(--pb-line-2); border-radius: 4px; box-shadow: var(--pb-shadow); pointer-events: auto; }
|
|
1782
|
+
.pb-aim .bar .lab { flex: 1; min-width: 0; padding-left: 8px; font-family: var(--pb-font-mono); font-size: 10px; letter-spacing: .14em; color: var(--pb-fg3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
1783
|
+
/* 48px tall: the minimum a thumb hits reliably. */
|
|
1784
|
+
.pb-aim .bar button { height: 48px; border-radius: 2px; font-family: var(--pb-font-mono); font-size: 11px; letter-spacing: .16em; cursor: pointer; }
|
|
1785
|
+
.pb-aim .bar .cancel { flex: none; padding: 0 18px; border: 1px solid var(--pb-line-2); background: transparent; color: var(--pb-fg2); }
|
|
1786
|
+
.pb-aim .bar .ok { flex: none; padding: 0 20px; border: none; background: var(--pb-amber); color: var(--pb-amber-ink); }
|
|
1787
|
+
|
|
1541
1788
|
.pb-pin { position: absolute; }
|
|
1542
1789
|
.pb-pin.resolving { animation: pb-resolve 380ms var(--pb-ease) forwards; }
|
|
1543
1790
|
.pb-pin .ring { position: absolute; left: 0; top: 0; width: 26px; height: 26px; border: 1px solid var(--pb-amber); border-radius: 999px; animation: pb-ring 900ms var(--pb-ease) forwards; pointer-events: none; }
|
|
@@ -1573,6 +1820,12 @@ button { font: inherit; color: inherit; background: none; border: 0; cursor: poi
|
|
|
1573
1820
|
.pb-msg-w { animation: pb-in 260ms var(--pb-ease) both; }
|
|
1574
1821
|
.pb-msg { padding: 13px 14px; display: flex; gap: 10px; }
|
|
1575
1822
|
.pb-msg.you { border-bottom: 1px solid var(--pb-line); }
|
|
1823
|
+
.pb-typing { padding: 13px 14px; display: flex; gap: 10px; align-items: center; }
|
|
1824
|
+
.pb-typing .dots { display: flex; gap: 4px; }
|
|
1825
|
+
.pb-typing .dots i { width: 4px; height: 4px; border-radius: 999px; background: var(--pb-amber); animation: pb-pulse 1.1s var(--pb-ease) infinite; }
|
|
1826
|
+
.pb-typing .dots i:nth-child(2) { animation-delay: .18s; }
|
|
1827
|
+
.pb-typing .dots i:nth-child(3) { animation-delay: .36s; }
|
|
1828
|
+
.pb-typing .lbl { font-family: var(--pb-font-mono); font-size: 9px; letter-spacing: .14em; color: var(--pb-fg3); }
|
|
1576
1829
|
.pb-msg .steps { display: flex; flex-direction: column; gap: 7px; }
|
|
1577
1830
|
.pb-av { flex: none; width: 22px; height: 22px; border-radius: 999px; display: flex; align-items: center; justify-content: center; font-family: var(--pb-font-mono); font-size: 9px; letter-spacing: .04em; background: var(--pb-invert-bg); color: var(--pb-invert-fg); border: 1px solid var(--pb-invert-bg); }
|
|
1578
1831
|
.pb-av.via { background: transparent; color: var(--pb-info); border-color: var(--pb-info); }
|
|
@@ -1670,17 +1923,31 @@ function isTextEntry(target) {
|
|
|
1670
1923
|
}
|
|
1671
1924
|
var PinboxToolbarElement = class extends BaseElement {
|
|
1672
1925
|
static tagName = "pinbox-toolbar";
|
|
1926
|
+
/** Watched so a config that arrives after insertion can still start the transport. */
|
|
1927
|
+
static observedAttributes = ["hub", "token"];
|
|
1673
1928
|
store = createStore();
|
|
1674
1929
|
/** Card → transport seam (wired by #startTransport once a config exists). */
|
|
1675
1930
|
actions = {};
|
|
1676
1931
|
#config = null;
|
|
1677
1932
|
#transport = null;
|
|
1933
|
+
/**
|
|
1934
|
+
* Connected-lifetime counter, bumped on disconnect. #startTransport can park at
|
|
1935
|
+
* an await (getToken, or `await undefined` on the token-less path); a
|
|
1936
|
+
* continuation that crossed a disconnect must not install a transport into a
|
|
1937
|
+
* later lifetime, where it would shadow or duplicate that lifetime's own start.
|
|
1938
|
+
*/
|
|
1939
|
+
#lifetime = 0;
|
|
1940
|
+
/** One deferred start per tick, however many attribute callbacks land in it. */
|
|
1941
|
+
#startQueued = false;
|
|
1678
1942
|
#token = "";
|
|
1679
1943
|
#built = false;
|
|
1680
1944
|
#bar = null;
|
|
1681
1945
|
#reticle = null;
|
|
1682
1946
|
#pinsLayer = null;
|
|
1683
1947
|
#drawer = null;
|
|
1948
|
+
#aim = null;
|
|
1949
|
+
/** Pending viewport-refresh frame, 0 when none is queued. */
|
|
1950
|
+
#viewportFrame = 0;
|
|
1684
1951
|
#modal = null;
|
|
1685
1952
|
#helpOpen = false;
|
|
1686
1953
|
#pageStyle = null;
|
|
@@ -1696,6 +1963,7 @@ var PinboxToolbarElement = class extends BaseElement {
|
|
|
1696
1963
|
/** Programmatic path (Pinbox.init). The snippet path reads hub/token attributes. */
|
|
1697
1964
|
configure(config) {
|
|
1698
1965
|
this.#config = config;
|
|
1966
|
+
if (this.isConnected) this.#queueStart();
|
|
1699
1967
|
}
|
|
1700
1968
|
get config() {
|
|
1701
1969
|
if (this.#config) return this.#config;
|
|
@@ -1720,25 +1988,77 @@ var PinboxToolbarElement = class extends BaseElement {
|
|
|
1720
1988
|
style.textContent = PAGE_CSS;
|
|
1721
1989
|
document.head.appendChild(style);
|
|
1722
1990
|
this.#pageStyle = style;
|
|
1991
|
+
if (this.#aim === null) this.#mountAim();
|
|
1723
1992
|
document.addEventListener("mousemove", this.#onMouseMove);
|
|
1993
|
+
window.addEventListener("scroll", this.#onViewportChange, { passive: true });
|
|
1994
|
+
window.addEventListener("resize", this.#onViewportChange);
|
|
1724
1995
|
document.addEventListener("click", this.#onClickCapture, true);
|
|
1725
1996
|
document.addEventListener("keydown", this.#onKeyDown);
|
|
1726
1997
|
this.#unsubscribe = this.store.subscribe((s) => this.#render(s));
|
|
1727
1998
|
this.#render(this.store.get());
|
|
1728
|
-
this.#
|
|
1999
|
+
this.#queueStart();
|
|
2000
|
+
}
|
|
2001
|
+
/**
|
|
2002
|
+
* Late-config rescue: an element inserted BEFORE its hub/token attributes were
|
|
2003
|
+
* set connected configless and #startTransport bailed. Starting here the moment
|
|
2004
|
+
* a config first exists keeps such an element from staying silently dead.
|
|
2005
|
+
* Config is still read once — a running transport is never reconfigured.
|
|
2006
|
+
*/
|
|
2007
|
+
attributeChangedCallback() {
|
|
2008
|
+
if (this.isConnected && this.config !== null) this.#queueStart();
|
|
2009
|
+
}
|
|
2010
|
+
/**
|
|
2011
|
+
* Start at the END of the current tick, not synchronously: a config assembled
|
|
2012
|
+
* attribute-by-attribute on a connected element (append → set hub → set token)
|
|
2013
|
+
* must be read whole. A synchronous start at the first fragment would connect
|
|
2014
|
+
* token-less and, per the read-once rule, drop the token forever.
|
|
2015
|
+
*/
|
|
2016
|
+
#queueStart() {
|
|
2017
|
+
if (this.#startQueued) return;
|
|
2018
|
+
this.#startQueued = true;
|
|
2019
|
+
queueMicrotask(() => {
|
|
2020
|
+
this.#startQueued = false;
|
|
2021
|
+
this.#startTransport();
|
|
2022
|
+
});
|
|
1729
2023
|
}
|
|
1730
2024
|
disconnectedCallback() {
|
|
2025
|
+
this.#lifetime += 1;
|
|
1731
2026
|
this.#transport?.close();
|
|
1732
2027
|
this.#transport = null;
|
|
1733
2028
|
document.removeEventListener("mousemove", this.#onMouseMove);
|
|
2029
|
+
window.removeEventListener("scroll", this.#onViewportChange);
|
|
2030
|
+
window.removeEventListener("resize", this.#onViewportChange);
|
|
1734
2031
|
document.removeEventListener("click", this.#onClickCapture, true);
|
|
1735
2032
|
document.removeEventListener("keydown", this.#onKeyDown);
|
|
2033
|
+
if (this.#viewportFrame !== 0) cancelAnimationFrame(this.#viewportFrame);
|
|
2034
|
+
this.#viewportFrame = 0;
|
|
2035
|
+
this.#aim?.destroy();
|
|
2036
|
+
this.#aim = null;
|
|
1736
2037
|
this.#unsubscribe?.();
|
|
1737
2038
|
this.#unsubscribe = null;
|
|
1738
2039
|
this.#pageStyle?.remove();
|
|
1739
2040
|
this.#pageStyle = null;
|
|
1740
2041
|
document.body.classList.remove(PAGE_PLACING_CLASS);
|
|
1741
2042
|
}
|
|
2043
|
+
/**
|
|
2044
|
+
* Create the aim controller and put its layer in the shadow root.
|
|
2045
|
+
*
|
|
2046
|
+
* Separate from `#build` because the two have different lifetimes: `#build` runs once, but
|
|
2047
|
+
* `disconnectedCallback` tears this controller's window listeners down. A re-parented element
|
|
2048
|
+
* would otherwise come back with no controller and its markup still in place — a grip that
|
|
2049
|
+
* renders and does nothing, with no error to explain it.
|
|
2050
|
+
*/
|
|
2051
|
+
#mountAim() {
|
|
2052
|
+
const shadow = this.shadowRoot;
|
|
2053
|
+
if (!shadow) return;
|
|
2054
|
+
shadow.querySelector(".pb-aim")?.remove();
|
|
2055
|
+
this.#aim = createAim(document, {
|
|
2056
|
+
onAim: (x, y) => this.#probe(x, y),
|
|
2057
|
+
onConfirm: () => this.#confirmAim(),
|
|
2058
|
+
onCancel: () => this.#dismiss()
|
|
2059
|
+
});
|
|
2060
|
+
shadow.appendChild(this.#aim.root);
|
|
2061
|
+
}
|
|
1742
2062
|
#build() {
|
|
1743
2063
|
const shadow = this.attachShadow({ mode: "open" });
|
|
1744
2064
|
try {
|
|
@@ -1775,6 +2095,7 @@ var PinboxToolbarElement = class extends BaseElement {
|
|
|
1775
2095
|
onClose: () => this.store.update({ inboxOpen: false })
|
|
1776
2096
|
});
|
|
1777
2097
|
shadow.appendChild(this.#drawer.root);
|
|
2098
|
+
this.#mountAim();
|
|
1778
2099
|
this.#modal = createShortcutsModal(document, () => this.#setHelp(false));
|
|
1779
2100
|
shadow.appendChild(this.#modal.root);
|
|
1780
2101
|
this.#pinsLayer.addEventListener("click", (e) => this.#onChipClick(e));
|
|
@@ -1785,10 +2106,13 @@ var PinboxToolbarElement = class extends BaseElement {
|
|
|
1785
2106
|
* still renders read-only threads and queued drafts.
|
|
1786
2107
|
*/
|
|
1787
2108
|
async #startTransport() {
|
|
1788
|
-
if (this.#transport !== null) return;
|
|
2109
|
+
if (this.#transport !== null || !this.isConnected) return;
|
|
1789
2110
|
const cfg = this.config;
|
|
1790
2111
|
if (cfg === null) return;
|
|
1791
|
-
|
|
2112
|
+
const lifetime = this.#lifetime;
|
|
2113
|
+
const token = cfg.token ?? await cfg.getToken?.().catch(() => void 0) ?? "";
|
|
2114
|
+
if (this.#lifetime !== lifetime || this.#transport !== null || !this.isConnected) return;
|
|
2115
|
+
this.#token = token;
|
|
1792
2116
|
const transport = new HubTransport({
|
|
1793
2117
|
endpoint: cfg.endpoint,
|
|
1794
2118
|
token: this.#token,
|
|
@@ -1804,7 +2128,10 @@ var PinboxToolbarElement = class extends BaseElement {
|
|
|
1804
2128
|
if (queued.length > 0) this.store.update({ queuedIds: new Set(queued.map((p) => p.id)) });
|
|
1805
2129
|
this.actions.send = (pinId, text) => void this.#send(transport, pinId, text);
|
|
1806
2130
|
this.actions.resolve = (pinId) => void transport.resolve(pinId).then((pin) => upsertPin(this.store, pin)).catch(() => {});
|
|
1807
|
-
this.actions.verify = (pinId, outcome) => void transport.verify(pinId, outcome).then((pin) =>
|
|
2131
|
+
this.actions.verify = (pinId, outcome) => void transport.verify(pinId, outcome).then((pin) => {
|
|
2132
|
+
upsertPin(this.store, pin);
|
|
2133
|
+
if (outcome === "accepted") this.#dismiss();
|
|
2134
|
+
}).catch(() => {});
|
|
1808
2135
|
transport.connect();
|
|
1809
2136
|
}
|
|
1810
2137
|
/** draft ⇒ compose PinInput (+ best-effort screenshot) and createPin; else thread reply. */
|
|
@@ -1830,6 +2157,7 @@ var PinboxToolbarElement = class extends BaseElement {
|
|
|
1830
2157
|
async #screenshot(selector) {
|
|
1831
2158
|
const cfg = this.config;
|
|
1832
2159
|
if (cfg === null) return null;
|
|
2160
|
+
if (cfg.screenshots === false) return null;
|
|
1833
2161
|
try {
|
|
1834
2162
|
const el = document.querySelector(selector);
|
|
1835
2163
|
if (el === null) return null;
|
|
@@ -1913,28 +2241,71 @@ var PinboxToolbarElement = class extends BaseElement {
|
|
|
1913
2241
|
});
|
|
1914
2242
|
if (this.store.get().draft) this.store.discardDraft();
|
|
1915
2243
|
}
|
|
2244
|
+
/**
|
|
2245
|
+
* Work out what sits under a viewport point and highlight it.
|
|
2246
|
+
*
|
|
2247
|
+
* Shared by both ways of aiming — following a mouse, and dragging the reticle — so the two can
|
|
2248
|
+
* never disagree about what is under the crosshair.
|
|
2249
|
+
*/
|
|
2250
|
+
#probe(clientX, clientY) {
|
|
2251
|
+
const el = hitTest(document, clientX, clientY, (hit) => hit === this);
|
|
2252
|
+
this.#hover = el;
|
|
2253
|
+
if (el) this.#reticle?.snap(el.getBoundingClientRect(), targetLabel(el), {
|
|
2254
|
+
x: window.scrollX,
|
|
2255
|
+
y: window.scrollY
|
|
2256
|
+
});
|
|
2257
|
+
else this.#reticle?.release();
|
|
2258
|
+
this.#aim?.setLabel(el ? targetLabel(el) : "NOTHING UNDER THE PIN");
|
|
2259
|
+
}
|
|
2260
|
+
/**
|
|
2261
|
+
* Keep the drag-aim reticle honest while the viewport moves under it.
|
|
2262
|
+
*
|
|
2263
|
+
* Scrolling changes what is beneath a fixed reticle, and resizing (a phone rotating, a window
|
|
2264
|
+
* dragged narrow) can both strand it off-screen and flip which way of aiming applies.
|
|
2265
|
+
*/
|
|
2266
|
+
#onViewportChange = () => {
|
|
2267
|
+
if (this.store.get().mode !== "placing" || this.#viewportFrame !== 0) return;
|
|
2268
|
+
this.#viewportFrame = requestAnimationFrame(() => {
|
|
2269
|
+
this.#viewportFrame = 0;
|
|
2270
|
+
if (this.store.get().mode !== "placing") return;
|
|
2271
|
+
this.#syncAim(true);
|
|
2272
|
+
const aim = this.#aim;
|
|
2273
|
+
if (aim?.root.classList.contains("on") === true) this.#probe(aim.point.x, aim.point.y);
|
|
2274
|
+
});
|
|
2275
|
+
};
|
|
1916
2276
|
#onMouseMove = (e) => {
|
|
1917
2277
|
if (this.store.get().mode !== "placing" || !this.#reticle) return;
|
|
1918
2278
|
this.#reticle.move(e);
|
|
1919
|
-
|
|
1920
|
-
if (el) {
|
|
1921
|
-
this.#hover = el;
|
|
1922
|
-
this.#reticle.snap(el.getBoundingClientRect(), targetLabel(el), {
|
|
1923
|
-
x: window.scrollX,
|
|
1924
|
-
y: window.scrollY
|
|
1925
|
-
});
|
|
1926
|
-
} else {
|
|
1927
|
-
this.#hover = null;
|
|
1928
|
-
this.#reticle.release();
|
|
1929
|
-
}
|
|
2279
|
+
this.#probe(e.clientX, e.clientY);
|
|
1930
2280
|
};
|
|
2281
|
+
/** Commit the pin the drag-aim reticle is sitting on. */
|
|
2282
|
+
#confirmAim() {
|
|
2283
|
+
const aim = this.#aim;
|
|
2284
|
+
if (!aim) return;
|
|
2285
|
+
this.#probe(aim.point.x, aim.point.y);
|
|
2286
|
+
const el = this.#hover ?? document.body;
|
|
2287
|
+
this.store.place({
|
|
2288
|
+
target: captureTarget(el, { at: {
|
|
2289
|
+
x: aim.point.x + window.scrollX,
|
|
2290
|
+
y: aim.point.y + window.scrollY
|
|
2291
|
+
} }),
|
|
2292
|
+
placedAt: {
|
|
2293
|
+
x: aim.point.x + window.scrollX,
|
|
2294
|
+
y: aim.point.y + window.scrollY
|
|
2295
|
+
}
|
|
2296
|
+
});
|
|
2297
|
+
this.#reticle?.release();
|
|
2298
|
+
}
|
|
1931
2299
|
/** Placement click: capture the hovered target (or body) into a client-only draft. */
|
|
1932
2300
|
#placeDraft(e) {
|
|
1933
2301
|
e.preventDefault();
|
|
1934
2302
|
e.stopPropagation();
|
|
1935
2303
|
const el = this.#hover ?? document.body;
|
|
1936
2304
|
this.store.place({
|
|
1937
|
-
target: captureTarget(el
|
|
2305
|
+
target: captureTarget(el, { at: {
|
|
2306
|
+
x: e.pageX,
|
|
2307
|
+
y: e.pageY
|
|
2308
|
+
} }),
|
|
1938
2309
|
placedAt: {
|
|
1939
2310
|
x: e.pageX,
|
|
1940
2311
|
y: e.pageY
|
|
@@ -1945,8 +2316,12 @@ var PinboxToolbarElement = class extends BaseElement {
|
|
|
1945
2316
|
#onClickCapture = (e) => {
|
|
1946
2317
|
if (e.composedPath().includes(this)) return;
|
|
1947
2318
|
const state = this.store.get();
|
|
1948
|
-
if (state.mode === "placing")
|
|
1949
|
-
|
|
2319
|
+
if (state.mode === "placing") {
|
|
2320
|
+
if (!needsDragAim(window)) this.#placeDraft(e);
|
|
2321
|
+
return;
|
|
2322
|
+
}
|
|
2323
|
+
if (state.inboxOpen) this.store.update({ inboxOpen: false });
|
|
2324
|
+
if (state.activePinId || state.draft) this.#dismiss();
|
|
1950
2325
|
};
|
|
1951
2326
|
/** Prototype keyboard map (v2-command-bar.html lines 701–712). */
|
|
1952
2327
|
#shortcuts = {
|
|
@@ -1962,11 +2337,33 @@ var PinboxToolbarElement = class extends BaseElement {
|
|
|
1962
2337
|
if (isTextEntry(e.composedPath()[0])) return;
|
|
1963
2338
|
this.#shortcuts[e.key === "?" ? "?" : e.key.toLowerCase()]?.();
|
|
1964
2339
|
};
|
|
2340
|
+
/**
|
|
2341
|
+
* Bring the drag-aim reticle up with placing mode, seeded mid-screen and already showing what it
|
|
2342
|
+
* is over — so the first thing you see is a live target, not an empty crosshair waiting for a
|
|
2343
|
+
* mouse that is never coming.
|
|
2344
|
+
*/
|
|
2345
|
+
#syncAim(placing) {
|
|
2346
|
+
const aim = this.#aim;
|
|
2347
|
+
if (!aim) return;
|
|
2348
|
+
if (!placing || !needsDragAim(window)) {
|
|
2349
|
+
aim.hide();
|
|
2350
|
+
return;
|
|
2351
|
+
}
|
|
2352
|
+
if (aim.root.classList.contains("on")) {
|
|
2353
|
+
if (aim.point.x <= window.innerWidth && aim.point.y <= window.innerHeight) return;
|
|
2354
|
+
aim.show(Math.min(aim.point.x, window.innerWidth), Math.min(aim.point.y, window.innerHeight));
|
|
2355
|
+
return;
|
|
2356
|
+
}
|
|
2357
|
+
const { x, y } = startPoint(window);
|
|
2358
|
+
aim.show(x, y);
|
|
2359
|
+
this.#probe(x, y);
|
|
2360
|
+
}
|
|
1965
2361
|
#render(state) {
|
|
1966
2362
|
const placing = state.mode === "placing";
|
|
1967
2363
|
this.toggleAttribute("data-placing", placing);
|
|
1968
2364
|
document.body.classList.toggle(PAGE_PLACING_CLASS, placing);
|
|
1969
2365
|
if (!placing) this.#reticle?.release();
|
|
2366
|
+
this.#syncAim(placing);
|
|
1970
2367
|
if (this.#pinsLayer) renderPins(this.#pinsLayer, state);
|
|
1971
2368
|
if (this.shadowRoot) renderCard(this.shadowRoot, state, this.#cardActions);
|
|
1972
2369
|
this.#drawer?.update(state);
|
package/dist/svelte.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as applyHubEvent, a as HubEvent, b as upsertPin, c as WebSocketLike, d as PinboxToolbarElement, f as Draft, g as appendThreadMessage, h as UiStatus, i as ConnectionState, l as HubError, m as ToolbarState, n as PinboxConfig, o as HubTransport, p as Store, r as defineToolbarElement, s as TransportOptions, t as Pinbox, u as StorageLike, v as createStore, x as CaptureResult, y as deriveUiStatus } from "./index-
|
|
1
|
+
import { _ as applyHubEvent, a as HubEvent, b as upsertPin, c as WebSocketLike, d as PinboxToolbarElement, f as Draft, g as appendThreadMessage, h as UiStatus, i as ConnectionState, l as HubError, m as ToolbarState, n as PinboxConfig, o as HubTransport, p as Store, r as defineToolbarElement, s as TransportOptions, t as Pinbox, u as StorageLike, v as createStore, x as CaptureResult, y as deriveUiStatus } from "./index-DnhwJYxE.js";
|
|
2
2
|
import { Action } from "svelte/action";
|
|
3
3
|
//#region src/svelte.d.ts
|
|
4
4
|
/**
|