@loupekit/sdk 0.10.28 → 0.11.0-next.76
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 +11 -4
- package/dist/index.global.js +327 -33
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +327 -33
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -87,6 +87,14 @@ var STYLES = (
|
|
|
87
87
|
display: none; flex-direction: column; align-items: flex-end; gap: 10px;
|
|
88
88
|
}
|
|
89
89
|
.fab-cluster.show { display: flex; }
|
|
90
|
+
/* A dragged launcher is anchored to its nearest edges (JS sets left/right/top/bottom),
|
|
91
|
+
and the quick actions grow INTO the page: downward from the upper half, labels to the
|
|
92
|
+
right from the left half. */
|
|
93
|
+
.fab-cluster.at-top { flex-direction: column-reverse; }
|
|
94
|
+
.fab-cluster.at-left, .fab-cluster.at-left .fab-minis { align-items: flex-start; }
|
|
95
|
+
.fab-cluster.at-left .fab-mini .fab-tip { right: auto; left: calc(100% + 8px); }
|
|
96
|
+
.fab-cluster.dragging .launcher { cursor: grabbing; border-color: var(--accent); }
|
|
97
|
+
.fab-cluster.dragging .fab-mini .fab-tip { display: none; }
|
|
90
98
|
|
|
91
99
|
.fab-minis { display: none; flex-direction: column; align-items: flex-end; gap: 10px; }
|
|
92
100
|
.fab-cluster.expanded .fab-minis { display: flex; }
|
|
@@ -95,7 +103,10 @@ var STYLES = (
|
|
|
95
103
|
.fab-cluster.expanded .fab-minis .fab-mini:nth-child(2) { animation-delay: 30ms; }
|
|
96
104
|
.fab-cluster.expanded .fab-minis .fab-mini:nth-child(3) { animation-delay: 60ms; }
|
|
97
105
|
.fab-cluster.expanded .fab-minis .fab-mini:nth-child(4) { animation-delay: 90ms; }
|
|
106
|
+
.fab-cluster.expanded .fab-minis .fab-mini:nth-child(5) { animation-delay: 120ms; }
|
|
98
107
|
@keyframes loupe-fab-in { from { opacity: 0; transform: translateY(8px) scale(.9); } to { opacity: 1; transform: none; } }
|
|
108
|
+
.fab-cluster.at-top.expanded .fab-minis .fab-mini { animation-name: loupe-fab-in-down; }
|
|
109
|
+
@keyframes loupe-fab-in-down { from { opacity: 0; transform: translateY(-8px) scale(.9); } to { opacity: 1; transform: none; } }
|
|
99
110
|
|
|
100
111
|
.fab-mini {
|
|
101
112
|
position: relative; width: 40px; height: 40px; border-radius: 50%; padding: 0;
|
|
@@ -116,22 +127,32 @@ var STYLES = (
|
|
|
116
127
|
}
|
|
117
128
|
.fab-mini:hover .fab-tip { opacity: 1; }
|
|
118
129
|
|
|
130
|
+
/* The launcher and its chevron share a box so the chevron can sit on the launcher's
|
|
131
|
+
corner while being its own button (tap = quick actions; the launcher = open). */
|
|
132
|
+
.fab-main { position: relative; display: inline-flex; }
|
|
119
133
|
.launcher {
|
|
120
134
|
position: relative; width: 46px; height: 46px; border-radius: 50%; padding: 0;
|
|
121
135
|
border: 1px solid var(--line); background: var(--bg-2); color: var(--ink);
|
|
122
|
-
cursor:
|
|
136
|
+
cursor: grab; display: inline-flex; align-items: center; justify-content: center;
|
|
123
137
|
box-shadow: var(--shadow);
|
|
138
|
+
touch-action: none; user-select: none; -webkit-user-select: none; /* drag with a finger too */
|
|
124
139
|
}
|
|
125
140
|
.launcher:hover { border-color: var(--accent); }
|
|
141
|
+
.launcher:active { cursor: grabbing; }
|
|
126
142
|
.launcher .logo { font-size: 24px; line-height: 1; color: var(--accent); }
|
|
127
143
|
/* Chevron pinned to the corner: up = actions are tucked away, down = they are out. */
|
|
128
|
-
.
|
|
129
|
-
position: absolute; right: -3px; bottom: -3px; width:
|
|
144
|
+
.fab-more {
|
|
145
|
+
position: absolute; right: -3px; bottom: -3px; width: 20px; height: 20px; border-radius: 50%;
|
|
146
|
+
padding: 0; cursor: pointer; z-index: 1;
|
|
130
147
|
background: var(--bg); border: 1px solid var(--line); color: var(--muted);
|
|
131
148
|
display: grid; place-items: center; transition: transform 160ms cubic-bezier(.16, 1, .3, 1);
|
|
132
149
|
}
|
|
133
|
-
.
|
|
134
|
-
.fab-
|
|
150
|
+
.fab-more:hover { border-color: var(--accent); color: var(--accent); }
|
|
151
|
+
.fab-more .lchev { display: grid; place-items: center; }
|
|
152
|
+
.fab-more svg { width: 11px; height: 11px; display: block; }
|
|
153
|
+
.fab-cluster.expanded .fab-more { transform: rotate(180deg); }
|
|
154
|
+
.fab-cluster.at-top .fab-more { bottom: auto; top: -3px; transform: rotate(180deg); }
|
|
155
|
+
.fab-cluster.at-top.expanded .fab-more { transform: none; }
|
|
135
156
|
.launcher .lcount {
|
|
136
157
|
position: absolute; top: -5px; right: -5px; background: var(--pin); color: #fff;
|
|
137
158
|
font-size: 10px; font-weight: 700; line-height: 1; border-radius: 999px; padding: 3px 6px;
|
|
@@ -216,7 +237,10 @@ var STYLES = (
|
|
|
216
237
|
.item .num { background: var(--pin); color: #fff; width: 20px; height: 20px; border-radius: 50%; font-size: 11px; font-weight: 700; display: grid; place-items: center; flex: none; }
|
|
217
238
|
.item .num.detached { background: #9aa0af; }
|
|
218
239
|
.item .num.done { background: #10935a; }
|
|
219
|
-
|
|
240
|
+
/* Author + absolute time, visible collapsed too. */
|
|
241
|
+
.item .who { display: flex; flex-wrap: wrap; gap: 0 4px; font-size: 11.5px; color: var(--muted); margin-top: 3px; }
|
|
242
|
+
.item .who b { color: var(--ink); font-weight: 600; }
|
|
243
|
+
.item .who time { font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
220
244
|
.item .device { font-size: 10px; color: var(--muted); background: var(--bg-3); border-radius: 999px; padding: 1px 7px; white-space: nowrap; }
|
|
221
245
|
.item .body { font-size: 13px; line-height: 1.4; }
|
|
222
246
|
.item .meta { font-size: 11px; color: var(--muted); margin-top: 6px; font-family: ui-monospace, Menlo, monospace; word-break: break-all; }
|
|
@@ -411,6 +435,23 @@ var STYLES = (
|
|
|
411
435
|
@keyframes loupe-recpulse { 0%,100% { opacity: 1; } 50% { opacity: .25; } }
|
|
412
436
|
@media (prefers-reduced-motion: reduce) { .recbar .recdot { animation: none; } }
|
|
413
437
|
|
|
438
|
+
/* ---------------------------------------------------------------- toast */
|
|
439
|
+
/* A short notice (e.g. how to bring a hidden launcher back). Click to dismiss. */
|
|
440
|
+
.toast {
|
|
441
|
+
position: fixed; z-index: 2147483006; bottom: 24px; left: 50%;
|
|
442
|
+
transform: translateX(-50%) translateY(8px); opacity: 0; pointer-events: none;
|
|
443
|
+
max-width: calc(100vw - 32px); padding: 9px 14px; border-radius: 999px; text-align: center;
|
|
444
|
+
background: var(--bg); color: var(--ink); border: 1px solid var(--line); box-shadow: var(--shadow);
|
|
445
|
+
font-size: 12px; font-weight: 600; line-height: 1.4;
|
|
446
|
+
transition: opacity 160ms ease, transform 160ms ease;
|
|
447
|
+
}
|
|
448
|
+
.toast.show { opacity: 1; transform: translateX(-50%); pointer-events: auto; cursor: pointer; }
|
|
449
|
+
.toast kbd {
|
|
450
|
+
font-family: ui-monospace, Menlo, monospace; font-size: 11px; padding: 1px 5px;
|
|
451
|
+
border: 1px solid var(--line); border-radius: 4px; background: var(--bg-3);
|
|
452
|
+
}
|
|
453
|
+
@media (prefers-reduced-motion: reduce) { .toast { transition: none; } }
|
|
454
|
+
|
|
414
455
|
/* ---------------------------------------------- header popovers (pos + settings) */
|
|
415
456
|
.menu-wrap { position: relative; }
|
|
416
457
|
.menu {
|
|
@@ -585,6 +626,8 @@ var STYLES = (
|
|
|
585
626
|
}
|
|
586
627
|
.menu-ver b { color: var(--ink); font-family: ui-monospace, Menlo, monospace; font-weight: 600; }
|
|
587
628
|
.menu-mode { text-transform: uppercase; letter-spacing: .06em; font-size: 9.5px; }
|
|
629
|
+
/* The host package's version, shown only when it differs from this bundle's (a stale publish). */
|
|
630
|
+
.ver-stale { color: var(--pin); font-family: ui-monospace, Menlo, monospace; font-weight: 600; cursor: help; }
|
|
588
631
|
|
|
589
632
|
/* ------------------------------------------------ lifecycle chips + review flow */
|
|
590
633
|
.lifechip {
|
|
@@ -3847,7 +3890,10 @@ var HttpAdapter = class {
|
|
|
3847
3890
|
};
|
|
3848
3891
|
|
|
3849
3892
|
// src/app.ts
|
|
3850
|
-
var SDK_VERSION = true ? "0.
|
|
3893
|
+
var SDK_VERSION = true ? "0.11.0" : "dev";
|
|
3894
|
+
var FAB_SIZE = 46;
|
|
3895
|
+
var FAB_DRAG_THRESHOLD = 6;
|
|
3896
|
+
var LAUNCHER_SHORTCUT = "Alt+Shift+L";
|
|
3851
3897
|
var ACCENTS = [
|
|
3852
3898
|
{ id: "indigo", dark: "#6b73e6", light: "#4a55d6", soft: "rgba(107,115,230,0.12)" },
|
|
3853
3899
|
{ id: "violet", dark: "#a06be6", light: "#7c3fd4", soft: "rgba(160,107,230,0.14)" },
|
|
@@ -3954,6 +4000,21 @@ var LoupeApp = class {
|
|
|
3954
4000
|
this.fabExpanded = false;
|
|
3955
4001
|
/** Whether pin markers are hidden on the page (a quick-action toggle, persisted). */
|
|
3956
4002
|
this.markersHidden = false;
|
|
4003
|
+
/**
|
|
4004
|
+
* Whether the collapsed launcher is hidden altogether (persisted). The way back is
|
|
4005
|
+
* Alt+Shift+L, Settings → Launcher, or the host calling `showLauncher()`.
|
|
4006
|
+
*/
|
|
4007
|
+
this.launcherHidden = false;
|
|
4008
|
+
/**
|
|
4009
|
+
* Where the user dragged the launcher to — the launcher's top-left in viewport px —
|
|
4010
|
+
* or null for the default corner on the dock's side. Persisted; clamped on apply so
|
|
4011
|
+
* a position saved on a wide screen still lands inside a narrow one.
|
|
4012
|
+
*/
|
|
4013
|
+
this.fabPos = null;
|
|
4014
|
+
/** The in-flight launcher drag: pointer start, launcher origin, and whether it has moved past the tap threshold. */
|
|
4015
|
+
this.fabDrag = null;
|
|
4016
|
+
/** Set for one tick after a drag so the click the browser fires afterwards does not open the panel. */
|
|
4017
|
+
this.fabSuppressClick = false;
|
|
3957
4018
|
this.comments = [];
|
|
3958
4019
|
/** Free-text filter over the list (title / body / author). */
|
|
3959
4020
|
this.search = "";
|
|
@@ -4054,6 +4115,56 @@ var LoupeApp = class {
|
|
|
4054
4115
|
this.lastReplyId = "";
|
|
4055
4116
|
/** In-app notifications: mentions of you, newest first. */
|
|
4056
4117
|
this.notifications = [];
|
|
4118
|
+
// ---- launcher: drag anywhere, hide, bring back ------------------------------
|
|
4119
|
+
this.onFabPointerDown = (e) => {
|
|
4120
|
+
if (e.pointerType === "mouse" && e.button !== 0) return;
|
|
4121
|
+
const target = e.currentTarget;
|
|
4122
|
+
const r = target.getBoundingClientRect();
|
|
4123
|
+
this.fabDrag = { px: e.clientX, py: e.clientY, ox: r.left, oy: r.top, moved: false };
|
|
4124
|
+
let captured = false;
|
|
4125
|
+
try {
|
|
4126
|
+
if (typeof target.setPointerCapture === "function") {
|
|
4127
|
+
target.setPointerCapture(e.pointerId);
|
|
4128
|
+
captured = true;
|
|
4129
|
+
}
|
|
4130
|
+
} catch {
|
|
4131
|
+
captured = false;
|
|
4132
|
+
}
|
|
4133
|
+
if (!captured) {
|
|
4134
|
+
window.addEventListener("pointermove", this.onFabPointerMove);
|
|
4135
|
+
window.addEventListener("pointerup", this.onFabPointerUp);
|
|
4136
|
+
window.addEventListener("pointercancel", this.onFabPointerUp);
|
|
4137
|
+
}
|
|
4138
|
+
};
|
|
4139
|
+
this.onFabPointerMove = (e) => {
|
|
4140
|
+
const d = this.fabDrag;
|
|
4141
|
+
if (!d) return;
|
|
4142
|
+
const dx = e.clientX - d.px, dy = e.clientY - d.py;
|
|
4143
|
+
if (!d.moved && Math.hypot(dx, dy) < FAB_DRAG_THRESHOLD) return;
|
|
4144
|
+
if (!d.moved) {
|
|
4145
|
+
d.moved = true;
|
|
4146
|
+
this.fabCluster.classList.add("dragging");
|
|
4147
|
+
this.collapseFab();
|
|
4148
|
+
}
|
|
4149
|
+
e.preventDefault();
|
|
4150
|
+
this.fabPos = this.clampFabPos(d.ox + dx, d.oy + dy);
|
|
4151
|
+
this.applyFabPosition();
|
|
4152
|
+
};
|
|
4153
|
+
this.onFabPointerUp = () => {
|
|
4154
|
+
window.removeEventListener("pointermove", this.onFabPointerMove);
|
|
4155
|
+
window.removeEventListener("pointerup", this.onFabPointerUp);
|
|
4156
|
+
window.removeEventListener("pointercancel", this.onFabPointerUp);
|
|
4157
|
+
const d = this.fabDrag;
|
|
4158
|
+
this.fabDrag = null;
|
|
4159
|
+
if (!d?.moved) return;
|
|
4160
|
+
this.fabCluster.classList.remove("dragging");
|
|
4161
|
+
this.saveState();
|
|
4162
|
+
this.renderSettings();
|
|
4163
|
+
this.fabSuppressClick = true;
|
|
4164
|
+
window.setTimeout(() => {
|
|
4165
|
+
this.fabSuppressClick = false;
|
|
4166
|
+
}, 0);
|
|
4167
|
+
};
|
|
4057
4168
|
// ---- inspector ------------------------------------------------------------
|
|
4058
4169
|
// Pointer Events, not mouse events: a touch drag on a phone never fires
|
|
4059
4170
|
// mousemove/mouseup, so Region/Record selection was impossible on touch devices.
|
|
@@ -4096,6 +4207,18 @@ var LoupeApp = class {
|
|
|
4096
4207
|
this.closeComposer();
|
|
4097
4208
|
}
|
|
4098
4209
|
};
|
|
4210
|
+
/**
|
|
4211
|
+
* Alt+Shift+L toggles the launcher — the way back once it has been hidden, so it is
|
|
4212
|
+
* listened for the whole time the widget is mounted (unlike Escape, which only
|
|
4213
|
+
* matters while a tool is armed). `code` is checked too: on some keyboard layouts
|
|
4214
|
+
* Alt+Shift+L yields a different `key`.
|
|
4215
|
+
*/
|
|
4216
|
+
this.onLauncherKey = (e) => {
|
|
4217
|
+
if (!e.altKey || !e.shiftKey || e.ctrlKey || e.metaKey) return;
|
|
4218
|
+
if (e.code !== "KeyL" && (e.key ?? "").toLowerCase() !== "l") return;
|
|
4219
|
+
e.preventDefault();
|
|
4220
|
+
this.setLauncherHidden(!this.launcherHidden);
|
|
4221
|
+
};
|
|
4099
4222
|
// ---- region ("free-size screenshot") selection ----------------------------
|
|
4100
4223
|
this.onRegionDown = (e) => {
|
|
4101
4224
|
if (this.mode !== "region" && this.mode !== "record") return;
|
|
@@ -4297,6 +4420,7 @@ var LoupeApp = class {
|
|
|
4297
4420
|
this.loadState();
|
|
4298
4421
|
this.loadProject();
|
|
4299
4422
|
this.buildDom();
|
|
4423
|
+
document.addEventListener("keydown", this.onLauncherKey);
|
|
4300
4424
|
if (this.cfg.autoOpen) this.open = true;
|
|
4301
4425
|
this.applyDockLayout();
|
|
4302
4426
|
this.lastUrl = this.url;
|
|
@@ -4370,11 +4494,15 @@ var LoupeApp = class {
|
|
|
4370
4494
|
this.dock = this.buildDock();
|
|
4371
4495
|
this.fabCluster = this.buildFabCluster();
|
|
4372
4496
|
this.recBar = this.buildRecBar();
|
|
4497
|
+
this.toastEl = el("div", "toast");
|
|
4498
|
+
this.toastEl.setAttribute("role", "status");
|
|
4499
|
+
this.toastEl.setAttribute("aria-live", "polite");
|
|
4500
|
+
this.toastEl.onclick = () => this.toastEl.classList.remove("show");
|
|
4373
4501
|
this.tourEl = el("div", "tour");
|
|
4374
4502
|
this.tourSpot = el("div", "tour-spot");
|
|
4375
4503
|
this.tourCard = el("div", "tour-card");
|
|
4376
4504
|
this.tourEl.append(this.tourSpot, this.tourCard);
|
|
4377
|
-
this.shadow.append(this.dock, this.fabCluster, this.recBar, this.tourEl);
|
|
4505
|
+
this.shadow.append(this.dock, this.fabCluster, this.recBar, this.toastEl, this.tourEl);
|
|
4378
4506
|
this.shadow.addEventListener("click", (e) => {
|
|
4379
4507
|
const t = e.target;
|
|
4380
4508
|
if (t?.closest && t.closest(".menu-wrap")) return;
|
|
@@ -4427,7 +4555,7 @@ var LoupeApp = class {
|
|
|
4427
4555
|
setBtn.innerHTML = I_GEAR;
|
|
4428
4556
|
setBtn.onclick = () => this.toggleMenu(this.settingsMenu);
|
|
4429
4557
|
this.settingsMenu = el("div", "menu");
|
|
4430
|
-
this.settingsMenu.innerHTML = `<div class="menu-label">Appearance</div><div class="acc-dots">` + ACCENTS.map((a) => `<button class="acc-dot" data-accent="${a.id}" style="background:${a.dark}" title="${a.id}" aria-label="${a.id} accent"></button>`).join("") + `</div><div class="menu-sep"></div><div class="menu-label">Show</div><button class="menu-row" data-set="hoverHints" aria-pressed="true"><span>Hover hints</span><span class="sw"></span></button><button class="menu-row" data-set="markersHidden" aria-pressed="true"><span>Markers</span><span class="sw"></span></button><button class="menu-row" data-set="showPaths" aria-pressed="false"><span>Page paths</span><span class="sw"></span></button><div class="menu-sep"></div><button class="menu-row" data-set="tour"><span>Restart tour</span></button><div class="menu-ver">Loupe <b>v${escapeHtml(SDK_VERSION)}</b><span class="menu-mode">${this.cfg.apiBase ? "server" : "offline"}</span></div>`;
|
|
4558
|
+
this.settingsMenu.innerHTML = `<div class="menu-label">Appearance</div><div class="acc-dots">` + ACCENTS.map((a) => `<button class="acc-dot" data-accent="${a.id}" style="background:${a.dark}" title="${a.id}" aria-label="${a.id} accent"></button>`).join("") + `</div><div class="menu-sep"></div><div class="menu-label">Show</div><button class="menu-row" data-set="hoverHints" aria-pressed="true"><span>Hover hints</span><span class="sw"></span></button><button class="menu-row" data-set="markersHidden" aria-pressed="true"><span>Markers</span><span class="sw"></span></button><button class="menu-row" data-set="showPaths" aria-pressed="false"><span>Page paths</span><span class="sw"></span></button><button class="menu-row" data-set="launcherHidden" aria-pressed="true" title="The \u25CE button shown while the panel is closed. ${LAUNCHER_SHORTCUT} toggles it too."><span>Launcher</span><span class="sw"></span></button><div class="menu-sep"></div><button class="menu-row" data-set="fabReset" hidden><span>Reset launcher position</span></button><button class="menu-row" data-set="tour"><span>Restart tour</span></button><div class="menu-ver"><span>Loupe <b>v${escapeHtml(SDK_VERSION)}</b>${this.packageVersionNote()}</span><span class="menu-mode">${this.cfg.apiBase ? "server" : "offline"}</span></div>`;
|
|
4431
4559
|
this.settingsMenu.querySelectorAll("[data-accent]").forEach((b) => {
|
|
4432
4560
|
b.onclick = () => this.setAccent(b.dataset.accent);
|
|
4433
4561
|
});
|
|
@@ -4439,6 +4567,11 @@ var LoupeApp = class {
|
|
|
4439
4567
|
this.startTour();
|
|
4440
4568
|
return;
|
|
4441
4569
|
}
|
|
4570
|
+
if (key === "fabReset") {
|
|
4571
|
+
this.closeMenus();
|
|
4572
|
+
this.resetLauncherPosition();
|
|
4573
|
+
return;
|
|
4574
|
+
}
|
|
4442
4575
|
this.toggleSetting(key);
|
|
4443
4576
|
};
|
|
4444
4577
|
});
|
|
@@ -5037,7 +5170,7 @@ var LoupeApp = class {
|
|
|
5037
5170
|
*/
|
|
5038
5171
|
buildHomePanel() {
|
|
5039
5172
|
const title = this.cfg.label ?? "Loupe";
|
|
5040
|
-
this.homeEl.innerHTML = `<div class="hint-slot" id="loupe-hhint"></div><div class="hstat" id="loupe-hstats"></div><div class="hscope"><button class="hscope-b" data-scope="page">This page <b class="hscope-n"></b></button><button class="hscope-b" data-scope="all">All <b class="hscope-n"></b></button><button class="hrefresh" title="Refresh" aria-label="Refresh">\u27F3</button></div><button class="hpin" data-role="home-pin">\u271B Pin feedback on this page</button><div class="projbar"><span class="proj-label">Project</span><button class="proj-chip" data-role="proj-open" aria-haspopup="dialog" aria-expanded="false"><span class="proj-repo"></span><span class="proj-caret">\u25BE</span></button><div class="proj-pop" id="loupe-proj" role="dialog" aria-label="Project settings"></div></div><div class="hnotif" id="loupe-hnotif"></div><div class="hlabel">Recent</div><div class="hfeed" id="loupe-hfeed"></div><div class="hfoot">${escapeHtml(title)} \xB7 <span class="hver">v${escapeHtml(SDK_VERSION)}</span
|
|
5173
|
+
this.homeEl.innerHTML = `<div class="hint-slot" id="loupe-hhint"></div><div class="hstat" id="loupe-hstats"></div><div class="hscope"><button class="hscope-b" data-scope="page">This page <b class="hscope-n"></b></button><button class="hscope-b" data-scope="all">All <b class="hscope-n"></b></button><button class="hrefresh" title="Refresh" aria-label="Refresh">\u27F3</button></div><button class="hpin" data-role="home-pin">\u271B Pin feedback on this page</button><div class="projbar"><span class="proj-label">Project</span><button class="proj-chip" data-role="proj-open" aria-haspopup="dialog" aria-expanded="false"><span class="proj-repo"></span><span class="proj-caret">\u25BE</span></button><div class="proj-pop" id="loupe-proj" role="dialog" aria-label="Project settings"></div></div><div class="hnotif" id="loupe-hnotif"></div><div class="hlabel">Recent</div><div class="hfeed" id="loupe-hfeed"></div><div class="hfoot">${escapeHtml(title)} \xB7 <span class="hver">v${escapeHtml(SDK_VERSION)}</span>${this.packageVersionNote()}</div>`;
|
|
5041
5174
|
this.homeEl.querySelector('[data-role="proj-open"]').onclick = (e) => {
|
|
5042
5175
|
e.stopPropagation();
|
|
5043
5176
|
this.setProjectOpen(!this.projOpen);
|
|
@@ -5130,7 +5263,7 @@ var LoupeApp = class {
|
|
|
5130
5263
|
feed.innerHTML = recent.length ? recent.map((c) => {
|
|
5131
5264
|
const stage = STAGE_LABELS[normalizeStatus(c.status)];
|
|
5132
5265
|
const prio = normalizePriority(c.priority);
|
|
5133
|
-
return `<button class="hfeed-i" data-id="${escapeAttr(c.id)}"><span class="hfeed-t">${escapeHtml(c.title || (c.body.split("\n")[0] ?? "").slice(0, 60))}</span><span class="hfeed-m">${escapeHtml(c.author?.name ?? "")} \xB7 ${fmtAgo(c.createdAt)} \xB7 <span class="hfeed-s hfeed-s-${normalizeStatus(c.status)}">${stage}</span><span class="hfeed-p hfeed-p-${prio}">${PRIORITY_LABELS[prio]}</span></span></button>`;
|
|
5266
|
+
return `<button class="hfeed-i" data-id="${escapeAttr(c.id)}"><span class="hfeed-t">${escapeHtml(c.title || (c.body.split("\n")[0] ?? "").slice(0, 60))}</span><span class="hfeed-m">${escapeHtml(c.author?.name ?? "")} \xB7 <span title="${escapeAttr(this.fmtWhen(c.createdAt))}">${fmtAgo(c.createdAt)}</span> \xB7 <span class="hfeed-s hfeed-s-${normalizeStatus(c.status)}">${stage}</span><span class="hfeed-p hfeed-p-${prio}">${PRIORITY_LABELS[prio]}</span></span></button>`;
|
|
5134
5267
|
}).join("") : `<div class="hempty">Nothing here yet.</div>`;
|
|
5135
5268
|
feed.querySelectorAll(".hfeed-i").forEach((b) => {
|
|
5136
5269
|
b.onclick = () => {
|
|
@@ -5397,20 +5530,21 @@ var LoupeApp = class {
|
|
|
5397
5530
|
}
|
|
5398
5531
|
/**
|
|
5399
5532
|
* The collapsed-state FAB cluster. The primary brand button carries the comment
|
|
5400
|
-
* count and
|
|
5401
|
-
*
|
|
5402
|
-
*
|
|
5403
|
-
*
|
|
5533
|
+
* count and OPENS the panel — one tap, the thing most people came for. A drag on
|
|
5534
|
+
* it moves the whole cluster anywhere on screen (persisted). The chevron beside it
|
|
5535
|
+
* is its own button and toggles the quick actions: pin a comment, drop a note,
|
|
5536
|
+
* hide/show the markers already on the page, hide the launcher itself, and — when
|
|
5537
|
+
* the host registered one — jump straight to the Connect tab.
|
|
5404
5538
|
*/
|
|
5405
5539
|
buildFabCluster() {
|
|
5406
5540
|
const cluster = el("div", "fab-cluster");
|
|
5407
5541
|
const minis = el("div", "fab-minis");
|
|
5408
|
-
const mini = (role, icon,
|
|
5542
|
+
const mini = (role, icon, label2, title) => {
|
|
5409
5543
|
const b = el("button", "fab-mini");
|
|
5410
5544
|
b.dataset.fab = role;
|
|
5411
5545
|
b.title = title;
|
|
5412
5546
|
b.setAttribute("aria-label", title);
|
|
5413
|
-
b.innerHTML = `${icon}<span class="fab-tip">${
|
|
5547
|
+
b.innerHTML = `${icon}<span class="fab-tip">${label2}</span>`;
|
|
5414
5548
|
return b;
|
|
5415
5549
|
};
|
|
5416
5550
|
const comment = mini("comment", I_COMMENT, "Pin comment", "Pin feedback on any element");
|
|
@@ -5427,7 +5561,9 @@ var LoupeApp = class {
|
|
|
5427
5561
|
};
|
|
5428
5562
|
const markers = mini("markers", I_EYE, "Markers", "Show or hide the markers on this page");
|
|
5429
5563
|
markers.onclick = () => this.toggleMarkers();
|
|
5430
|
-
|
|
5564
|
+
const hide = mini("hide", I_EYE_OFF, "Hide launcher", `Hide this launcher \u2014 ${LAUNCHER_SHORTCUT} brings it back`);
|
|
5565
|
+
hide.onclick = () => this.setLauncherHidden(true);
|
|
5566
|
+
minis.append(comment, note, markers, hide);
|
|
5431
5567
|
if (this.tabList.some((t) => t.id === "connect")) {
|
|
5432
5568
|
const connect = mini("connect", I_PLUG, "Connect Claude", "Set up the Claude/MCP connection");
|
|
5433
5569
|
connect.onclick = () => {
|
|
@@ -5437,14 +5573,33 @@ var LoupeApp = class {
|
|
|
5437
5573
|
};
|
|
5438
5574
|
minis.appendChild(connect);
|
|
5439
5575
|
}
|
|
5576
|
+
const label = this.cfg.label ?? "Loupe";
|
|
5440
5577
|
const primary = el("button", "launcher");
|
|
5441
|
-
primary.title = `Open ${
|
|
5442
|
-
primary.setAttribute("aria-label",
|
|
5443
|
-
primary.
|
|
5444
|
-
primary.innerHTML = `<span class="logo">\u25CE</span>${I_FAB_CHEVRON}<span class="lcount"></span>`;
|
|
5578
|
+
primary.title = `Open ${label} \u2014 drag to move`;
|
|
5579
|
+
primary.setAttribute("aria-label", `Open ${label}`);
|
|
5580
|
+
primary.innerHTML = `<span class="logo">\u25CE</span><span class="lcount"></span>`;
|
|
5445
5581
|
this.fabBadge = primary.querySelector(".lcount");
|
|
5446
|
-
primary.onclick = () =>
|
|
5447
|
-
|
|
5582
|
+
primary.onclick = () => {
|
|
5583
|
+
if (this.fabSuppressClick) return;
|
|
5584
|
+
this.openDock();
|
|
5585
|
+
};
|
|
5586
|
+
primary.addEventListener("pointerdown", this.onFabPointerDown);
|
|
5587
|
+
primary.addEventListener("pointermove", this.onFabPointerMove);
|
|
5588
|
+
primary.addEventListener("pointerup", this.onFabPointerUp);
|
|
5589
|
+
primary.addEventListener("pointercancel", this.onFabPointerUp);
|
|
5590
|
+
const more = el("button", "fab-more");
|
|
5591
|
+
more.dataset.fab = "more";
|
|
5592
|
+
more.title = "Quick actions";
|
|
5593
|
+
more.setAttribute("aria-label", "Quick actions");
|
|
5594
|
+
more.setAttribute("aria-expanded", "false");
|
|
5595
|
+
more.innerHTML = I_FAB_CHEVRON;
|
|
5596
|
+
more.onclick = (e) => {
|
|
5597
|
+
e.stopPropagation();
|
|
5598
|
+
this.toggleFab();
|
|
5599
|
+
};
|
|
5600
|
+
const main = el("div", "fab-main");
|
|
5601
|
+
main.append(primary, more);
|
|
5602
|
+
cluster.append(minis, main);
|
|
5448
5603
|
this.fabMinis = minis;
|
|
5449
5604
|
return cluster;
|
|
5450
5605
|
}
|
|
@@ -5453,6 +5608,79 @@ var LoupeApp = class {
|
|
|
5453
5608
|
this.fabExpanded = !this.fabExpanded;
|
|
5454
5609
|
this.applyFab();
|
|
5455
5610
|
}
|
|
5611
|
+
/** Keep the launcher fully on screen, with a small margin so it never kisses the edge. */
|
|
5612
|
+
clampFabPos(x, y) {
|
|
5613
|
+
const m = 8;
|
|
5614
|
+
return {
|
|
5615
|
+
x: clampPx(Math.round(x), m, Math.max(m, window.innerWidth - FAB_SIZE - m)),
|
|
5616
|
+
y: clampPx(Math.round(y), m, Math.max(m, window.innerHeight - FAB_SIZE - m))
|
|
5617
|
+
};
|
|
5618
|
+
}
|
|
5619
|
+
/**
|
|
5620
|
+
* Place the cluster. A dragged launcher is anchored to the viewport edge it is
|
|
5621
|
+
* nearest, so the quick actions always grow INTO the page: minis open upward from
|
|
5622
|
+
* the lower half and downward from the upper half, and their labels point away
|
|
5623
|
+
* from the nearest side. With no saved position the cluster sits in the default
|
|
5624
|
+
* corner on the dock's side, so reopening feels like the panel sliding back in.
|
|
5625
|
+
*/
|
|
5626
|
+
applyFabPosition() {
|
|
5627
|
+
const s = this.fabCluster.style;
|
|
5628
|
+
if (this.fabPos) {
|
|
5629
|
+
const { x, y } = this.clampFabPos(this.fabPos.x, this.fabPos.y);
|
|
5630
|
+
const atTop = y + FAB_SIZE / 2 < window.innerHeight / 2;
|
|
5631
|
+
const atLeft = x + FAB_SIZE / 2 < window.innerWidth / 2;
|
|
5632
|
+
s.left = atLeft ? `${x}px` : "auto";
|
|
5633
|
+
s.right = atLeft ? "auto" : `${Math.max(0, window.innerWidth - x - FAB_SIZE)}px`;
|
|
5634
|
+
s.top = atTop ? `${y}px` : "auto";
|
|
5635
|
+
s.bottom = atTop ? "auto" : `${Math.max(0, window.innerHeight - y - FAB_SIZE)}px`;
|
|
5636
|
+
this.fabCluster.classList.toggle("at-top", atTop);
|
|
5637
|
+
this.fabCluster.classList.toggle("at-left", atLeft);
|
|
5638
|
+
return;
|
|
5639
|
+
}
|
|
5640
|
+
const leftSide = this.dockMode === "left";
|
|
5641
|
+
s.left = leftSide ? "20px" : "auto";
|
|
5642
|
+
s.right = leftSide ? "auto" : "20px";
|
|
5643
|
+
s.top = "auto";
|
|
5644
|
+
s.bottom = "";
|
|
5645
|
+
this.fabCluster.classList.remove("at-top");
|
|
5646
|
+
this.fabCluster.classList.toggle("at-left", leftSide);
|
|
5647
|
+
}
|
|
5648
|
+
resetLauncherPosition() {
|
|
5649
|
+
this.fabPos = null;
|
|
5650
|
+
this.saveState();
|
|
5651
|
+
this.applyDockLayout();
|
|
5652
|
+
this.renderSettings();
|
|
5653
|
+
}
|
|
5654
|
+
/**
|
|
5655
|
+
* Hide or show the collapsed launcher. Hidden is persisted, and the way back is
|
|
5656
|
+
* announced at the moment of hiding — a control that vanishes with no stated way
|
|
5657
|
+
* to return is a support ticket, not a feature.
|
|
5658
|
+
*/
|
|
5659
|
+
setLauncherHidden(hidden) {
|
|
5660
|
+
if (this.launcherHidden === hidden) return;
|
|
5661
|
+
this.launcherHidden = hidden;
|
|
5662
|
+
this.fabExpanded = false;
|
|
5663
|
+
this.saveState();
|
|
5664
|
+
this.applyDockLayout();
|
|
5665
|
+
this.renderSettings();
|
|
5666
|
+
const label = this.cfg.label ?? "Loupe";
|
|
5667
|
+
this.toast(hidden ? `${escapeHtml(label)} launcher hidden \u2014 press <kbd>${LAUNCHER_SHORTCUT}</kbd> to bring it back` : `${escapeHtml(label)} launcher is back`);
|
|
5668
|
+
}
|
|
5669
|
+
/** Public seam for the host (`Loupe.showLauncher()`). */
|
|
5670
|
+
showLauncher() {
|
|
5671
|
+
this.setLauncherHidden(false);
|
|
5672
|
+
}
|
|
5673
|
+
/** Public seam for the host (`Loupe.hideLauncher()`). */
|
|
5674
|
+
hideLauncher() {
|
|
5675
|
+
this.setLauncherHidden(true);
|
|
5676
|
+
}
|
|
5677
|
+
/** A short notice at the bottom of the viewport. `html` is trusted markup built by the caller. */
|
|
5678
|
+
toast(html, ms = 6e3) {
|
|
5679
|
+
this.toastEl.innerHTML = html;
|
|
5680
|
+
this.toastEl.classList.add("show");
|
|
5681
|
+
window.clearTimeout(this.toastTimer);
|
|
5682
|
+
this.toastTimer = window.setTimeout(() => this.toastEl.classList.remove("show"), ms);
|
|
5683
|
+
}
|
|
5456
5684
|
collapseFab() {
|
|
5457
5685
|
if (!this.fabExpanded) return;
|
|
5458
5686
|
this.fabExpanded = false;
|
|
@@ -5467,9 +5695,9 @@ var LoupeApp = class {
|
|
|
5467
5695
|
/** Reflect cluster expansion + marker visibility into the DOM. */
|
|
5468
5696
|
applyFab() {
|
|
5469
5697
|
this.fabCluster.classList.toggle("expanded", this.fabExpanded);
|
|
5470
|
-
const
|
|
5471
|
-
|
|
5472
|
-
|
|
5698
|
+
const more = this.fabCluster.querySelector('[data-fab="more"]');
|
|
5699
|
+
more?.setAttribute("aria-expanded", String(this.fabExpanded));
|
|
5700
|
+
more?.setAttribute("aria-label", this.fabExpanded ? "Close quick actions" : "Quick actions");
|
|
5473
5701
|
const markers = this.fabCluster.querySelector('[data-fab="markers"]');
|
|
5474
5702
|
markers?.classList.toggle("on", this.markersHidden);
|
|
5475
5703
|
markers?.setAttribute("aria-pressed", String(this.markersHidden));
|
|
@@ -6033,6 +6261,10 @@ var LoupeApp = class {
|
|
|
6033
6261
|
}
|
|
6034
6262
|
/** Flip one of the "Show …" settings and repaint whatever it governs. */
|
|
6035
6263
|
toggleSetting(key) {
|
|
6264
|
+
if (key === "launcherHidden") {
|
|
6265
|
+
this.setLauncherHidden(!this.launcherHidden);
|
|
6266
|
+
return;
|
|
6267
|
+
}
|
|
6036
6268
|
if (key === "markersHidden") this.markersHidden = !this.markersHidden;
|
|
6037
6269
|
else if (key === "hoverHints") this.hoverHints = !this.hoverHints;
|
|
6038
6270
|
else this.showPaths = !this.showPaths;
|
|
@@ -6051,11 +6283,13 @@ var LoupeApp = class {
|
|
|
6051
6283
|
const on = {
|
|
6052
6284
|
hoverHints: this.hoverHints,
|
|
6053
6285
|
markersHidden: !this.markersHidden,
|
|
6054
|
-
showPaths: this.showPaths
|
|
6286
|
+
showPaths: this.showPaths,
|
|
6287
|
+
launcherHidden: !this.launcherHidden
|
|
6055
6288
|
};
|
|
6056
6289
|
this.settingsMenu.querySelectorAll("[data-set]").forEach((b) => {
|
|
6057
6290
|
const key = b.dataset.set;
|
|
6058
6291
|
if (key in on) b.setAttribute("aria-pressed", String(on[key]));
|
|
6292
|
+
if (key === "fabReset") b.hidden = !this.fabPos;
|
|
6059
6293
|
});
|
|
6060
6294
|
this.settingsMenu.querySelectorAll("[data-accent]").forEach((b) => b.classList.toggle("on", b.dataset.accent === this.accent));
|
|
6061
6295
|
}
|
|
@@ -6189,6 +6423,8 @@ var LoupeApp = class {
|
|
|
6189
6423
|
if (typeof p?.repoFilter === "string") this.repoFilter = p.repoFilter;
|
|
6190
6424
|
if (p?.float && typeof p.float.w === "number") this.floatRect = { ...this.floatRect, ...p.float };
|
|
6191
6425
|
if (typeof p?.markersHidden === "boolean") this.markersHidden = p.markersHidden;
|
|
6426
|
+
if (typeof p?.launcherHidden === "boolean") this.launcherHidden = p.launcherHidden;
|
|
6427
|
+
if (p?.fab && typeof p.fab.x === "number" && typeof p.fab.y === "number") this.fabPos = { x: p.fab.x, y: p.fab.y };
|
|
6192
6428
|
if (p?.accent && ACCENT_IDS.includes(p.accent)) this.accent = p.accent;
|
|
6193
6429
|
if (typeof p?.minimized === "boolean") this.minimized = p.minimized;
|
|
6194
6430
|
if (typeof p?.hoverHints === "boolean") this.hoverHints = p.hoverHints;
|
|
@@ -6207,6 +6443,8 @@ var LoupeApp = class {
|
|
|
6207
6443
|
tab: this.tab,
|
|
6208
6444
|
float: this.floatRect,
|
|
6209
6445
|
markersHidden: this.markersHidden,
|
|
6446
|
+
launcherHidden: this.launcherHidden,
|
|
6447
|
+
fab: this.fabPos,
|
|
6210
6448
|
scope: this.scope,
|
|
6211
6449
|
statFilter: this.statFilter,
|
|
6212
6450
|
repoFilter: this.repoFilter,
|
|
@@ -6259,10 +6497,8 @@ var LoupeApp = class {
|
|
|
6259
6497
|
for (const p of ["left", "top", "right", "bottom", "width", "height"]) d.style[p] = "";
|
|
6260
6498
|
}
|
|
6261
6499
|
d.querySelectorAll(".dctl [data-dock]").forEach((b) => b.classList.toggle("on", b.dataset.dock === this.dockMode));
|
|
6262
|
-
this.fabCluster.classList.toggle("show", !this.open);
|
|
6263
|
-
|
|
6264
|
-
this.fabCluster.style.left = leftSide ? "20px" : "auto";
|
|
6265
|
-
this.fabCluster.style.right = leftSide ? "auto" : "20px";
|
|
6500
|
+
this.fabCluster.classList.toggle("show", !this.open && !this.launcherHidden);
|
|
6501
|
+
this.applyFabPosition();
|
|
6266
6502
|
this.applyFab();
|
|
6267
6503
|
this.pushPage();
|
|
6268
6504
|
}
|
|
@@ -6391,6 +6627,10 @@ var LoupeApp = class {
|
|
|
6391
6627
|
item.appendChild(top);
|
|
6392
6628
|
const summary = c.title || (c.body.split("\n")[0] ?? "").slice(0, 140) || "(no description)";
|
|
6393
6629
|
item.appendChild(el("div", "summary", summary));
|
|
6630
|
+
const who = el("div", "who");
|
|
6631
|
+
const when = this.fmtWhen(c.createdAt);
|
|
6632
|
+
who.innerHTML = `<b>${escapeHtml(c.author?.name?.trim() || "Unknown")}</b>` + (when ? ` \xB7 <time datetime="${escapeAttr(c.createdAt)}" title="${escapeAttr(fmtAgo(c.createdAt))}">${escapeHtml(when)}</time>` : "");
|
|
6633
|
+
item.appendChild(who);
|
|
6394
6634
|
const detail = el("div", "detail");
|
|
6395
6635
|
const repliesForState = this.messages.get(c.id) ?? [];
|
|
6396
6636
|
const lastMsg = repliesForState.length ? repliesForState[repliesForState.length - 1] : void 0;
|
|
@@ -6963,6 +7203,7 @@ ${c.body}` : c.body)}</div>` + (c.context?.html ? `<pre class="or-code">${escape
|
|
|
6963
7203
|
el("b", "msg-name", m.author.name),
|
|
6964
7204
|
el("span", "msg-when", fmtAgo(m.createdAt))
|
|
6965
7205
|
);
|
|
7206
|
+
head.lastElementChild.title = this.fmtWhen(m.createdAt);
|
|
6966
7207
|
if (fromAgent) head.appendChild(el("span", "msg-tag", "agent"));
|
|
6967
7208
|
const body = el("div", "msg-body");
|
|
6968
7209
|
for (const seg of mentionSegments(m.body, parseMentions(m.body))) {
|
|
@@ -7491,6 +7732,21 @@ ${c.body}` : c.body)}</div>` + (c.context?.html ? `<pre class="or-code">${escape
|
|
|
7491
7732
|
const elx = this.resolved.get(id);
|
|
7492
7733
|
if (elx) elx.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
7493
7734
|
}
|
|
7735
|
+
/** Absolute timestamp in the host's configured time zone and locale (see `LoupeConfig.timeZone`). */
|
|
7736
|
+
fmtWhen(iso) {
|
|
7737
|
+
return fmtAbsolute(iso, this.cfg.locale, this.cfg.timeZone);
|
|
7738
|
+
}
|
|
7739
|
+
/**
|
|
7740
|
+
* " · package v0.11.0", flagged, when the host's package version differs from this
|
|
7741
|
+
* bundle's. The two agree on a healthy install; they differ exactly when the
|
|
7742
|
+
* package was upgraded but its published JS was not — the trap this makes visible.
|
|
7743
|
+
*/
|
|
7744
|
+
packageVersionNote() {
|
|
7745
|
+
const pkg = (this.cfg.packageVersion ?? "").trim().replace(/^v/i, "");
|
|
7746
|
+
if (!pkg || pkg === SDK_VERSION) return "";
|
|
7747
|
+
const why = `This widget bundle is v${SDK_VERSION} but the installed package is v${pkg}. Re-publish the package assets so the served bundle matches.`;
|
|
7748
|
+
return ` \xB7 <span class="ver-stale" title="${escapeAttr(why)}">package v${escapeHtml(pkg)}</span>`;
|
|
7749
|
+
}
|
|
7494
7750
|
destroy() {
|
|
7495
7751
|
this.liveSource?.close();
|
|
7496
7752
|
this.liveSource = void 0;
|
|
@@ -7509,6 +7765,11 @@ ${c.body}` : c.body)}</div>` + (c.context?.html ? `<pre class="or-code">${escape
|
|
|
7509
7765
|
window.removeEventListener("pointerup", this.onHeadPointerUp);
|
|
7510
7766
|
window.removeEventListener("pointermove", this.onResizeMove);
|
|
7511
7767
|
window.removeEventListener("pointerup", this.onResizeUp);
|
|
7768
|
+
window.removeEventListener("pointermove", this.onFabPointerMove);
|
|
7769
|
+
window.removeEventListener("pointerup", this.onFabPointerUp);
|
|
7770
|
+
window.removeEventListener("pointercancel", this.onFabPointerUp);
|
|
7771
|
+
window.clearTimeout(this.toastTimer);
|
|
7772
|
+
document.removeEventListener("keydown", this.onLauncherKey);
|
|
7512
7773
|
document.removeEventListener("keydown", this.onKey, true);
|
|
7513
7774
|
const de = document.documentElement;
|
|
7514
7775
|
de.style.marginLeft = de.style.marginRight = de.style.marginBottom = "";
|
|
@@ -7535,6 +7796,27 @@ function fmtAgo(iso) {
|
|
|
7535
7796
|
if (h < 24) return `${h}h ago`;
|
|
7536
7797
|
return `${Math.round(h / 24)}d ago`;
|
|
7537
7798
|
}
|
|
7799
|
+
var warnedBadTimeZone = false;
|
|
7800
|
+
function fmtAbsolute(iso, locale, timeZone) {
|
|
7801
|
+
const t = Date.parse(iso);
|
|
7802
|
+
if (Number.isNaN(t)) return "";
|
|
7803
|
+
const opts = {
|
|
7804
|
+
day: "numeric",
|
|
7805
|
+
month: "short",
|
|
7806
|
+
year: "numeric",
|
|
7807
|
+
hour: "numeric",
|
|
7808
|
+
minute: "2-digit"
|
|
7809
|
+
};
|
|
7810
|
+
try {
|
|
7811
|
+
return new Intl.DateTimeFormat(locale || void 0, { ...opts, timeZone: timeZone || void 0 }).format(t);
|
|
7812
|
+
} catch (err) {
|
|
7813
|
+
if (!warnedBadTimeZone) {
|
|
7814
|
+
warnedBadTimeZone = true;
|
|
7815
|
+
console.warn(`[loupe] unusable timeZone/locale (${timeZone ?? "-"} / ${locale ?? "-"}); using the browser's`, err);
|
|
7816
|
+
}
|
|
7817
|
+
return new Intl.DateTimeFormat(void 0, opts).format(t);
|
|
7818
|
+
}
|
|
7819
|
+
}
|
|
7538
7820
|
function shortPath(url) {
|
|
7539
7821
|
try {
|
|
7540
7822
|
return new URL(url, location.origin).pathname || "/";
|
|
@@ -7617,6 +7899,9 @@ var I_EYE = svg(
|
|
|
7617
7899
|
var I_PLUG = svg(
|
|
7618
7900
|
`<path d="M6 1.8v3.1M10 1.8v3.1" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/><path d="M4.4 4.9h7.2v2.3a3.6 3.6 0 0 1-7.2 0z" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round"/><path d="M8 10.8v3.4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>`
|
|
7619
7901
|
);
|
|
7902
|
+
var I_EYE_OFF = svg(
|
|
7903
|
+
`<path d="M2 2l12 12M6.6 6.7A2 2 0 0 0 9.3 9.4M4.3 4.5C2.9 5.4 1.9 6.7 1.5 8c1.1 3 3.7 5 6.5 5 1.3 0 2.5-.4 3.6-1.1M6.9 3.2C7.3 3.1 7.6 3 8 3c2.8 0 5.4 2 6.5 5-.3.8-.8 1.6-1.4 2.3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>`
|
|
7904
|
+
);
|
|
7620
7905
|
var I_FAB_CHEVRON = `<span class="lchev">${svg(`<path d="M4.4 9.6 8 6l3.6 3.6" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/>`)}</span>`;
|
|
7621
7906
|
var REGION_ICON = `<svg width="15" height="15" viewBox="0 0 15 15" fill="none" aria-hidden="true"><rect x="1.5" y="2.5" width="12" height="10" rx="1.5" stroke="currentColor" stroke-width="1.4" stroke-dasharray="2.4 1.8"/></svg>`;
|
|
7622
7907
|
var NOTE_ICON = `<svg width="15" height="15" viewBox="0 0 15 15" fill="none" aria-hidden="true"><path d="M2 2.5h11v7.5H6l-3 2.5v-2.5H2z" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round"/></svg>`;
|
|
@@ -7745,15 +8030,24 @@ function setLocalAi(config) {
|
|
|
7745
8030
|
function openTool(tool) {
|
|
7746
8031
|
app?.openTool(tool);
|
|
7747
8032
|
}
|
|
8033
|
+
function showLauncher() {
|
|
8034
|
+
app?.showLauncher();
|
|
8035
|
+
}
|
|
8036
|
+
function hideLauncher() {
|
|
8037
|
+
app?.hideLauncher();
|
|
8038
|
+
}
|
|
7748
8039
|
export {
|
|
7749
8040
|
clearActivity,
|
|
7750
8041
|
connectTab,
|
|
7751
8042
|
destroy,
|
|
8043
|
+
hideLauncher,
|
|
7752
8044
|
init,
|
|
7753
8045
|
openTool,
|
|
7754
8046
|
requestNavigation2 as requestNavigation,
|
|
7755
8047
|
setActivityStatus,
|
|
7756
8048
|
setLocalAi,
|
|
7757
|
-
|
|
8049
|
+
showLauncher,
|
|
8050
|
+
trackActivity,
|
|
8051
|
+
SDK_VERSION as version
|
|
7758
8052
|
};
|
|
7759
8053
|
//# sourceMappingURL=index.js.map
|