@autono/pinbox-toolbar 0.19.0 → 0.21.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/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-DENhglDF.js";
1
+ import { S as CaptureResult, _ as appendThreadMessage, a as HubEvent, b as deriveUiStatus, c as WebSocketLike, d as mountPreviewSwitcher, f as PinboxToolbarElement, g as UiStatus, h as ToolbarState, i as ConnectionState, l as HubError, m as Store, n as PinboxConfig, o as HubTransport, p as Draft, r as defineToolbarElement, s as TransportOptions, t as Pinbox, u as StorageLike, v as applyHubEvent, x as upsertPin, y as createStore } from "./index-CMSOhoVs.js";
2
2
  import { Action } from "svelte/action";
3
3
  //#region src/svelte.d.ts
4
4
  /**
@@ -8,4 +8,4 @@ import { Action } from "svelte/action";
8
8
  */
9
9
  declare const pinbox: Action<HTMLElement, PinboxConfig>;
10
10
  //#endregion
11
- export { type CaptureResult, type ConnectionState, type Draft, HubError, type HubEvent, HubTransport, Pinbox, PinboxConfig, PinboxToolbarElement, type StorageLike, type Store, type ToolbarState, type TransportOptions, type UiStatus, type WebSocketLike, appendThreadMessage, applyHubEvent, createStore, defineToolbarElement, deriveUiStatus, pinbox, upsertPin };
11
+ export { type CaptureResult, type ConnectionState, type Draft, HubError, type HubEvent, HubTransport, Pinbox, PinboxConfig, PinboxToolbarElement, type StorageLike, type Store, type ToolbarState, type TransportOptions, type UiStatus, type WebSocketLike, appendThreadMessage, applyHubEvent, createStore, defineToolbarElement, deriveUiStatus, mountPreviewSwitcher, pinbox, upsertPin };
package/dist/svelte.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as HubError, c as createStore, i as HubTransport, l as deriveUiStatus, n as defineToolbarElement, o as appendThreadMessage, r as PinboxToolbarElement, s as applyHubEvent, t as Pinbox, u as upsertPin } from "./src-BsWnyGwB.js";
1
+ import { a as HubTransport, c as applyHubEvent, d as upsertPin, i as PinboxToolbarElement, l as createStore, n as defineToolbarElement, o as HubError, r as mountPreviewSwitcher, s as appendThreadMessage, t as Pinbox, u as deriveUiStatus } from "./src-B134WXZ8.js";
2
2
  //#region src/svelte.ts
3
3
  /**
4
4
  * `<div use:pinbox={{ endpoint }} />` — creates + configures the element BEFORE insertion
@@ -15,4 +15,4 @@ const pinbox = (_node, config) => {
15
15
  } };
16
16
  };
17
17
  //#endregion
18
- export { HubError, HubTransport, Pinbox, PinboxToolbarElement, appendThreadMessage, applyHubEvent, createStore, defineToolbarElement, deriveUiStatus, pinbox, upsertPin };
18
+ export { HubError, HubTransport, Pinbox, PinboxToolbarElement, appendThreadMessage, applyHubEvent, createStore, defineToolbarElement, deriveUiStatus, mountPreviewSwitcher, pinbox, upsertPin };
@@ -210,10 +210,11 @@ var Pinbox = (function(exports) {
210
210
  ];
211
211
  }
212
212
  function block(pin, thread) {
213
- const { selector, url, source } = pin.target ?? {};
213
+ const { selector, url, source, model } = pin.target ?? {};
214
214
  return [
215
215
  `## Pin ${pin.n === void 0 ? pin.id : `#${pin.n} (${pin.id})`} — ${pin.status.toUpperCase()}`,
216
216
  `- label: ${line(label(pin))}`,
217
+ ...model ? [`- model: ${line(model.modelId)} / ${line(model.partId)} @ ${line(model.revision)}`, `- position: ${model.position.join(", ")} ${model.units}`] : [],
217
218
  ...selector === void 0 ? [] : [`- selector: \`${line(selector)}\``],
218
219
  ...(pin.target?.targets ?? []).map((t) => t.selector ?? t.anchor ?? t.tag).filter((locus) => locus !== void 0).map((locus) => `- also: \`${line(locus)}\``),
219
220
  ...source === void 0 ? [] : [`- source: ${line(source.line === void 0 ? source.file : `${source.file}:${source.line}`)}`],
@@ -810,6 +811,153 @@ var Pinbox = (function(exports) {
810
811
  };
811
812
  }
812
813
  //#endregion
814
+ //#region src/model-capture.ts
815
+ const handlers = /* @__PURE__ */ new WeakMap();
816
+ function runModelCapture(owner) {
817
+ const entries = handlers.get(owner);
818
+ const entry = entries?.[0];
819
+ if (!entries || !entry) return false;
820
+ if (entries.some((candidate) => candidate.busy)) return true;
821
+ entry.busy = true;
822
+ try {
823
+ Promise.resolve(entry.run()).catch(entry.error).finally(() => {
824
+ entry.busy = false;
825
+ });
826
+ } catch (error) {
827
+ entry.busy = false;
828
+ entry.error(error);
829
+ }
830
+ return true;
831
+ }
832
+ /** Shared by the camera button, puck and S shortcut; preserve the 2D fallback. */
833
+ function toggleToolbarCapture(owner, endpoint, release) {
834
+ if (runModelCapture(owner)) return true;
835
+ const next = owner.store.get().captureMode === "tab" ? "dom" : "tab";
836
+ owner.store.update({ captureMode: next });
837
+ if (next === "dom") release();
838
+ saveCaptureMode(globalThis.localStorage, captureKey(`pinbox:${endpoint}`), next);
839
+ return true;
840
+ }
841
+ //#endregion
842
+ //#region src/reveal-settle.ts
843
+ const pending = /* @__PURE__ */ new WeakMap();
844
+ function cancelRevealSettle(doc) {
845
+ pending.get(doc)?.();
846
+ }
847
+ function settlePinReveal(doc, reveal) {
848
+ cancelRevealSettle(doc);
849
+ const win = doc.defaultView;
850
+ if (!win) return;
851
+ let attempts = 0;
852
+ let timer = 0;
853
+ const events = [
854
+ "wheel",
855
+ "touchstart",
856
+ "pointerdown",
857
+ "keydown"
858
+ ];
859
+ const stop = () => {
860
+ win.clearTimeout(timer);
861
+ for (const event of events) doc.removeEventListener(event, stop, true);
862
+ pending.delete(doc);
863
+ };
864
+ for (const event of events) doc.addEventListener(event, stop, {
865
+ capture: true,
866
+ passive: true
867
+ });
868
+ const tick = () => {
869
+ reveal();
870
+ if (++attempts >= 10) stop();
871
+ else timer = win.setTimeout(tick, 150);
872
+ };
873
+ pending.set(doc, stop);
874
+ timer = win.setTimeout(tick, 150);
875
+ }
876
+ //#endregion
877
+ //#region src/pin-reveal.ts
878
+ /** Query ordering is not a view change; repeated parameter ordering remains intact. */
879
+ function samePinView(win, value) {
880
+ if (!value) return true;
881
+ try {
882
+ const target = new URL(value, win.location.href);
883
+ const current = new URL(win.location.href);
884
+ target.searchParams.sort();
885
+ current.searchParams.sort();
886
+ return target.pathname === current.pathname && target.search === current.search;
887
+ } catch {
888
+ return true;
889
+ }
890
+ }
891
+ function pagePin(pin) {
892
+ return pin.target?.selector === "html" || pin.target?.selector === "body";
893
+ }
894
+ /** Resolve before clipping: an offscreen row is precisely the target we need to scroll. */
895
+ function scrollToPin(doc, pin) {
896
+ if (pin.target?.model || pagePin(pin)) return false;
897
+ const win = doc.defaultView;
898
+ if (!win || !samePinView(win, pin.target?.url) || !pin.target?.selector) return false;
899
+ try {
900
+ const element = doc.querySelector(pin.target.selector);
901
+ if (!element?.getClientRects().length) return false;
902
+ element.scrollIntoView({
903
+ behavior: "instant",
904
+ block: "center",
905
+ inline: "center"
906
+ });
907
+ return true;
908
+ } catch {
909
+ return false;
910
+ }
911
+ }
912
+ const key = (endpoint) => `pinbox:${endpoint}:reveal`;
913
+ /** Returns true when leaving this document. Never navigates to a different origin. */
914
+ function revealPin(doc, pin, endpoint) {
915
+ cancelRevealSettle(doc);
916
+ const win = doc.defaultView;
917
+ if (!win) return false;
918
+ try {
919
+ win.sessionStorage.removeItem(key(endpoint));
920
+ } catch {}
921
+ if (pagePin(pin) || pin.target?.model) return false;
922
+ if (pin.target?.url && !samePinView(win, pin.target.url)) try {
923
+ const target = new URL(pin.target.url, win.location.href);
924
+ if (target.origin !== win.location.origin || !["http:", "https:"].includes(target.protocol)) return false;
925
+ win.sessionStorage.setItem(key(endpoint), JSON.stringify({
926
+ id: pin.id,
927
+ expires: Date.now() + 3e4
928
+ }));
929
+ win.location.assign(target.href);
930
+ return true;
931
+ } catch {
932
+ return false;
933
+ }
934
+ scrollToPin(doc, pin);
935
+ return false;
936
+ }
937
+ /** Called on pin updates and DOM changes: SPA content may arrive after the toolbar. */
938
+ function pendingPinReveal(doc, pins, endpoint) {
939
+ const win = doc.defaultView;
940
+ if (!win) return null;
941
+ try {
942
+ const raw = win.sessionStorage.getItem(key(endpoint));
943
+ if (!raw) return null;
944
+ const pending = JSON.parse(raw);
945
+ if (typeof pending.id !== "string" || !Number.isFinite(pending.expires) || pending.expires < Date.now()) {
946
+ win.sessionStorage.removeItem(key(endpoint));
947
+ return null;
948
+ }
949
+ const pin = pins.find((p) => p.id === pending.id);
950
+ if (!pin || !scrollToPin(doc, pin)) return null;
951
+ win.sessionStorage.removeItem(key(endpoint));
952
+ settlePinReveal(doc, () => {
953
+ scrollToPin(doc, pin);
954
+ });
955
+ return pin.id;
956
+ } catch {
957
+ return null;
958
+ }
959
+ }
960
+ //#endregion
813
961
  //#region src/targeting/dom.ts
814
962
  /**
815
963
  * Deepest element under (clientX, clientY) that the caller does not ignore, or null when there is
@@ -2370,8 +2518,8 @@ var Pinbox = (function(exports) {
2370
2518
  inboxBtn.classList.toggle("lit", state.inboxOpen);
2371
2519
  const open = String(openTaskCount(state.pins));
2372
2520
  if (count.textContent !== open) count.textContent = open;
2373
- captureBtn.classList.toggle("lit", state.captureMode === "tab");
2374
- captureBtn.title = state.captureMode === "tab" ? "Tab capture on — real pixels, Chrome asks once per page load (S)" : "Screenshots: DOM snapshot, no prompt — press for tab capture (S)";
2521
+ captureBtn.classList.toggle("lit", !state.captureLabel && state.captureMode === "tab");
2522
+ captureBtn.title = state.captureLabel ?? (state.captureMode === "tab" ? "Tab capture on — real pixels, Chrome asks once per page load (S)" : "Screenshots: DOM snapshot, no prompt — press for tab capture (S)");
2375
2523
  if (hideShown !== state.pinsHidden) {
2376
2524
  hideShown = state.pinsHidden;
2377
2525
  hideBtn.innerHTML = icon(state.pinsHidden ? EYE_GLYPH : EYE_OFF_GLYPH, 14);
@@ -2644,6 +2792,21 @@ var Pinbox = (function(exports) {
2644
2792
  return `${isDraft ? `<div class="pb-seg" role="radiogroup" aria-label="Pin kind"><button type="button" role="radio" aria-checked="${kind === "note"}" class="${kind === "note" ? "on" : ""}" data-kind="note" title="An agent picks this up">Ask agent</button><button type="button" role="radio" aria-checked="${kind === "comment"}" class="${kind === "comment" ? "on" : ""}" data-kind="comment" title="A remark for people; no agent acts on it">Note</button></div>` : ""}<div class="pb-kbd">⌘ ↵</div><button type="button" class="pb-bt-solid" data-action="send">${hasThread ? "Reply" : kind === "comment" ? "Leave note" : "Comment"}</button>`;
2645
2793
  }
2646
2794
  //#endregion
2795
+ //#region src/model-target.ts
2796
+ const projectors = /* @__PURE__ */ new WeakMap();
2797
+ function projectModelTarget(doc, anchor) {
2798
+ for (const project of projectors.get(doc) ?? []) {
2799
+ const p = project(anchor);
2800
+ if (p && Number.isFinite(p.x) && Number.isFinite(p.y)) return {
2801
+ x: p.x,
2802
+ y: p.y,
2803
+ width: 0,
2804
+ height: 0
2805
+ };
2806
+ }
2807
+ return null;
2808
+ }
2809
+ //#endregion
2647
2810
  //#region src/ui/pins.ts
2648
2811
  /** The prototype's `_h` innerHTML memo, kept off the DOM node. */
2649
2812
  const chipMemo = /* @__PURE__ */ new WeakMap();
@@ -2651,20 +2814,6 @@ var Pinbox = (function(exports) {
2651
2814
  function nextOrdinal(pins) {
2652
2815
  return Math.max(pins.length, ...pins.map((p) => p.n ?? 0)) + 1;
2653
2816
  }
2654
- /**
2655
- * Does the pin's captured URL still describe the view on screen? Path + search
2656
- * only — hashes are anchors, not views. An absent or unparseable URL never
2657
- * gates: old pins (and CLI pins) keep rendering exactly as before.
2658
- */
2659
- function sameView(win, url) {
2660
- if (url === void 0) return true;
2661
- try {
2662
- const target = new URL(url, win.location.href);
2663
- return target.pathname === win.location.pathname && target.search === win.location.search;
2664
- } catch {
2665
- return true;
2666
- }
2667
- }
2668
2817
  /** A stored (document-space) rect or point, in today's viewport. */
2669
2818
  function toViewport(win, p) {
2670
2819
  return {
@@ -2689,22 +2838,23 @@ var Pinbox = (function(exports) {
2689
2838
  }
2690
2839
  /** The same resolution for any captured target — a pin's, or the draft's before it commits. */
2691
2840
  function targetRect(doc, target) {
2841
+ if (target?.model) return projectModelTarget(doc, target.model);
2692
2842
  const stored = target?.rect;
2693
- if (stored === void 0) return null;
2694
2843
  const win = doc.defaultView;
2695
- if (win === null) return stored;
2696
- if (!sameView(win, target?.url)) return null;
2844
+ if (win === null) return stored ?? null;
2845
+ if (!samePinView(win, target?.url)) return null;
2697
2846
  const selector = target?.selector;
2698
- if (selector === void 0) return toViewport(win, stored);
2847
+ if (selector === void 0) return stored ? toViewport(win, stored) : null;
2848
+ if (!stored && (selector === "html" || selector === "body")) return null;
2699
2849
  let el;
2700
2850
  try {
2701
2851
  el = doc.querySelector(selector);
2702
2852
  } catch {
2703
- return toViewport(win, stored);
2853
+ return stored ? toViewport(win, stored) : null;
2704
2854
  }
2705
2855
  if (el === null) return null;
2706
2856
  const r = el.getBoundingClientRect();
2707
- if (r.width <= 0 && r.height <= 0) return toViewport(win, stored);
2857
+ if (r.width <= 0 && r.height <= 0) return stored ? toViewport(win, stored) : null;
2708
2858
  return clipToScrollAncestors(win, el, {
2709
2859
  x: r.left,
2710
2860
  y: r.top,
@@ -3018,6 +3168,8 @@ var Pinbox = (function(exports) {
3018
3168
  * labels the card without claiming an element that was never captured.
3019
3169
  */
3020
3170
  function labelOf(target) {
3171
+ if (target?.selector === "html" || target?.selector === "body") return "PAGE";
3172
+ if (target?.model) return `3D · ${target.model.partId}`;
3021
3173
  return target?.anchor ?? target?.tag?.toUpperCase() ?? "PIN";
3022
3174
  }
3023
3175
  function viewOf(root, state) {
@@ -3755,11 +3907,7 @@ button { font: inherit; color: inherit; background: none; border: 0; cursor: poi
3755
3907
  return loadCaptureMode(globalThis.localStorage, key, this.config?.capture ?? "dom");
3756
3908
  }
3757
3909
  #toggleCapture() {
3758
- const next = this.store.get().captureMode === "tab" ? "dom" : "tab";
3759
- this.store.update({ captureMode: next });
3760
- if (next === "dom") releaseCapture();
3761
- saveCaptureMode(globalThis.localStorage, captureKey(`pinbox:${this.config?.endpoint ?? ""}`), next);
3762
- return true;
3910
+ return toggleToolbarCapture(this, this.config?.endpoint ?? "", releaseCapture);
3763
3911
  }
3764
3912
  /** Theme from the OS when the host set none; the page-level CSS (placing cursor) into <head>. */
3765
3913
  #applyPageDefaults() {
@@ -4030,16 +4178,15 @@ button { font: inherit; color: inherit; background: none; border: 0; cursor: poi
4030
4178
  /** Inbox item click: activate the pin and scroll it into view (prototype line 700). */
4031
4179
  #activateFromInbox(pinId) {
4032
4180
  const pin = this.store.get().pins.find((p) => p.id === pinId);
4181
+ if (!pin) return;
4182
+ this.store.update({
4183
+ inboxOpen: false,
4184
+ activePinId: null,
4185
+ pinsHidden: false
4186
+ });
4187
+ if (revealPin(document, pin, this.config?.endpoint ?? "")) return;
4033
4188
  this.#ensureThread(pinId);
4034
4189
  this.store.update({ activePinId: pinId });
4035
- const rect = pin === void 0 ? null : anchorRect(document, pin);
4036
- if (rect) {
4037
- const y = window.scrollY + rect.y + rect.height / 2;
4038
- window.scrollTo({
4039
- top: Math.max(0, y - window.innerHeight / 2),
4040
- behavior: "smooth"
4041
- });
4042
- }
4043
4190
  }
4044
4191
  /**
4045
4192
  * Nudge a stale pin: re-post the last human message as a new thread message. A watcher that
@@ -4188,6 +4335,16 @@ button { font: inherit; color: inherit; background: none; border: 0; cursor: poi
4188
4335
  });
4189
4336
  };
4190
4337
  #render(state) {
4338
+ const restored = pendingPinReveal(document, state.pins, this.config?.endpoint ?? "");
4339
+ if (restored) {
4340
+ this.#ensureThread(restored);
4341
+ this.store.update({
4342
+ activePinId: restored,
4343
+ inboxOpen: false,
4344
+ pinsHidden: false
4345
+ });
4346
+ return;
4347
+ }
4191
4348
  const placing = state.mode === "placing";
4192
4349
  this.toggleAttribute("data-placing", placing);
4193
4350
  document.body.classList.toggle(PAGE_PLACING_CLASS, placing);
package/dist/vue.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { d as PinboxToolbarElement, n as PinboxConfig } from "./index-DENhglDF.js";
1
+ import { f as PinboxToolbarElement, n as PinboxConfig } from "./index-CMSOhoVs.js";
2
2
  import { VNode } from "vue";
3
3
  //#region src/vue.d.ts
4
4
  /** The slice of the Vue component instance this wrapper touches. */
package/dist/vue.js CHANGED
@@ -1,4 +1,4 @@
1
- import { n as defineToolbarElement, r as PinboxToolbarElement } from "./src-BsWnyGwB.js";
1
+ import { i as PinboxToolbarElement, n as defineToolbarElement } from "./src-B134WXZ8.js";
2
2
  import { h } from "vue";
3
3
  //#region src/vue.ts
4
4
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autono/pinbox-toolbar",
3
- "version": "0.19.0",
3
+ "version": "0.21.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -42,7 +42,11 @@
42
42
  "types": "./dist/plugins/next.d.ts",
43
43
  "default": "./dist/plugins/next.js"
44
44
  },
45
- "./package.json": "./package.json"
45
+ "./package.json": "./package.json",
46
+ "./model": {
47
+ "types": "./dist/model.d.ts",
48
+ "default": "./dist/model.js"
49
+ }
46
50
  },
47
51
  "scripts": {
48
52
  "typecheck": "tsc --noEmit",
@@ -71,7 +75,7 @@
71
75
  }
72
76
  },
73
77
  "devDependencies": {
74
- "@autono/pinbox-core": "0.19.0",
78
+ "@autono/pinbox-core": "0.21.0",
75
79
  "@types/react": "^19.2.0",
76
80
  "typescript": "^7.0.0",
77
81
  "tsdown": "^0.22.0",