@fiodos/web-core 0.1.3 → 0.1.4

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.
@@ -65,6 +65,18 @@ function ensureStyles() {
65
65
  el.textContent = CSS;
66
66
  document.head.appendChild(el);
67
67
  }
68
+ // Content box of the layout viewport. `documentElement.clientWidth/Height`
69
+ // EXCLUDE a classic scrollbar that occupies real width/height (Windows, some
70
+ // Linux/Chromium); overlay scrollbars (macOS, mobile) report the same value as
71
+ // the layout viewport. Anchoring against this — not `innerWidth` — keeps a
72
+ // right/bottom-edge orb just INSIDE the scrollbar instead of under/over it.
73
+ function viewportContentSize() {
74
+ const de = typeof document !== 'undefined' ? document.documentElement : null;
75
+ return {
76
+ width: de?.clientWidth || window.innerWidth,
77
+ height: de?.clientHeight || window.innerHeight,
78
+ };
79
+ }
68
80
  function cornerToPosition(corner) {
69
81
  switch (corner) {
70
82
  case 'bottom-left':
@@ -161,14 +173,15 @@ function mountOrb(controller, options = {}) {
161
173
  }
162
174
  function reposition() {
163
175
  const size = orbDiameterPx();
164
- const insets = (0, core_1.resolveOrbLayerInsets)({
176
+ const content = viewportContentSize();
177
+ const { left, top } = (0, core_1.resolveOrbAnchorPx)({
165
178
  viewportWidth: window.innerWidth,
166
179
  viewportHeight: window.innerHeight,
180
+ contentWidth: content.width,
181
+ contentHeight: content.height,
167
182
  orbDiameter: size,
168
183
  position: effectivePosition(),
169
184
  });
170
- const left = Math.max(8, window.innerWidth - insets.right - size);
171
- const top = Math.max(8, window.innerHeight - insets.bottom - size);
172
185
  root.style.left = `${left}px`;
173
186
  root.style.top = `${top}px`;
174
187
  root.style.right = '';
@@ -197,6 +210,14 @@ function mountOrb(controller, options = {}) {
197
210
  }
198
211
  }
199
212
  window.addEventListener('resize', reposition);
213
+ // Recalc when a scrollbar appears/disappears as the page content changes (the
214
+ // content box shrinks/grows even when the window size doesn't), so the orb
215
+ // never ends up overlapped by — or detached from — the scrollbar edge.
216
+ let contentObserver;
217
+ if (typeof ResizeObserver !== 'undefined' && document.documentElement) {
218
+ contentObserver = new ResizeObserver(() => reposition());
219
+ contentObserver.observe(document.documentElement);
220
+ }
200
221
  // Keep the conversation bar above the keyboard as the visual viewport resizes
201
222
  // (keyboard open/close) or scrolls on mobile browsers.
202
223
  const visualViewport = window.visualViewport;
@@ -240,9 +261,10 @@ function mountOrb(controller, options = {}) {
240
261
  dragging = true;
241
262
  e.preventDefault();
242
263
  const size = orbDiameterPx();
264
+ const content = viewportContentSize();
243
265
  const margin = 8;
244
- const maxLeft = window.innerWidth - margin - size;
245
- const maxTop = window.innerHeight - margin - size;
266
+ const maxLeft = content.width - margin - size;
267
+ const maxTop = content.height - margin - size;
246
268
  const left = Math.min(maxLeft, Math.max(margin, originLeft + dx));
247
269
  const top = Math.min(maxTop, Math.max(margin, originTop + dy));
248
270
  root.style.left = `${left}px`;
@@ -252,8 +274,8 @@ function mountOrb(controller, options = {}) {
252
274
  draggedPosition = (0, core_1.viewportPixelsToOrbScreenPosition)({
253
275
  centerX: left + size / 2,
254
276
  centerY: top + size / 2,
255
- viewportWidth: window.innerWidth,
256
- viewportHeight: window.innerHeight,
277
+ viewportWidth: content.width,
278
+ viewportHeight: content.height,
257
279
  orbDiameter: size,
258
280
  });
259
281
  applyDeployment();
@@ -688,6 +710,7 @@ function mountOrb(controller, options = {}) {
688
710
  unsubscribe();
689
711
  stopWatch();
690
712
  window.removeEventListener('resize', reposition);
713
+ contentObserver?.disconnect();
691
714
  visualViewport?.removeEventListener('resize', onViewportChange);
692
715
  visualViewport?.removeEventListener('scroll', onViewportChange);
693
716
  clearOverlay();
@@ -16,7 +16,7 @@
16
16
  * - CONFIRMATIONS: the same security model (manifest decides; strict phrase /
17
17
  * deliberate tap for payment-grade; loose "yes" never resolves strict).
18
18
  */
19
- import { DEFAULT_ORB_SCREEN_POSITION, createWebOrbHaptics, resolveOrbLayerInsets, resolveOrbDeployment, snapOrbScreenPositionToSide, viewportPixelsToOrbScreenPosition, } from '@fiodos/core';
19
+ import { DEFAULT_ORB_SCREEN_POSITION, createWebOrbHaptics, resolveOrbAnchorPx, resolveOrbDeployment, snapOrbScreenPositionToSide, viewportPixelsToOrbScreenPosition, } from '@fiodos/core';
20
20
  import { buildKeyboardChip, createOrbVisual, DEFAULT_ORB_APPEARANCE, } from './orbView.js';
21
21
  import { watchPublishedConfig } from './publishedConfig.js';
22
22
  const STYLE_ID = 'fyodos-orb-styles';
@@ -62,6 +62,18 @@ function ensureStyles() {
62
62
  el.textContent = CSS;
63
63
  document.head.appendChild(el);
64
64
  }
65
+ // Content box of the layout viewport. `documentElement.clientWidth/Height`
66
+ // EXCLUDE a classic scrollbar that occupies real width/height (Windows, some
67
+ // Linux/Chromium); overlay scrollbars (macOS, mobile) report the same value as
68
+ // the layout viewport. Anchoring against this — not `innerWidth` — keeps a
69
+ // right/bottom-edge orb just INSIDE the scrollbar instead of under/over it.
70
+ function viewportContentSize() {
71
+ const de = typeof document !== 'undefined' ? document.documentElement : null;
72
+ return {
73
+ width: de?.clientWidth || window.innerWidth,
74
+ height: de?.clientHeight || window.innerHeight,
75
+ };
76
+ }
65
77
  function cornerToPosition(corner) {
66
78
  switch (corner) {
67
79
  case 'bottom-left':
@@ -158,14 +170,15 @@ export function mountOrb(controller, options = {}) {
158
170
  }
159
171
  function reposition() {
160
172
  const size = orbDiameterPx();
161
- const insets = resolveOrbLayerInsets({
173
+ const content = viewportContentSize();
174
+ const { left, top } = resolveOrbAnchorPx({
162
175
  viewportWidth: window.innerWidth,
163
176
  viewportHeight: window.innerHeight,
177
+ contentWidth: content.width,
178
+ contentHeight: content.height,
164
179
  orbDiameter: size,
165
180
  position: effectivePosition(),
166
181
  });
167
- const left = Math.max(8, window.innerWidth - insets.right - size);
168
- const top = Math.max(8, window.innerHeight - insets.bottom - size);
169
182
  root.style.left = `${left}px`;
170
183
  root.style.top = `${top}px`;
171
184
  root.style.right = '';
@@ -194,6 +207,14 @@ export function mountOrb(controller, options = {}) {
194
207
  }
195
208
  }
196
209
  window.addEventListener('resize', reposition);
210
+ // Recalc when a scrollbar appears/disappears as the page content changes (the
211
+ // content box shrinks/grows even when the window size doesn't), so the orb
212
+ // never ends up overlapped by — or detached from — the scrollbar edge.
213
+ let contentObserver;
214
+ if (typeof ResizeObserver !== 'undefined' && document.documentElement) {
215
+ contentObserver = new ResizeObserver(() => reposition());
216
+ contentObserver.observe(document.documentElement);
217
+ }
197
218
  // Keep the conversation bar above the keyboard as the visual viewport resizes
198
219
  // (keyboard open/close) or scrolls on mobile browsers.
199
220
  const visualViewport = window.visualViewport;
@@ -237,9 +258,10 @@ export function mountOrb(controller, options = {}) {
237
258
  dragging = true;
238
259
  e.preventDefault();
239
260
  const size = orbDiameterPx();
261
+ const content = viewportContentSize();
240
262
  const margin = 8;
241
- const maxLeft = window.innerWidth - margin - size;
242
- const maxTop = window.innerHeight - margin - size;
263
+ const maxLeft = content.width - margin - size;
264
+ const maxTop = content.height - margin - size;
243
265
  const left = Math.min(maxLeft, Math.max(margin, originLeft + dx));
244
266
  const top = Math.min(maxTop, Math.max(margin, originTop + dy));
245
267
  root.style.left = `${left}px`;
@@ -249,8 +271,8 @@ export function mountOrb(controller, options = {}) {
249
271
  draggedPosition = viewportPixelsToOrbScreenPosition({
250
272
  centerX: left + size / 2,
251
273
  centerY: top + size / 2,
252
- viewportWidth: window.innerWidth,
253
- viewportHeight: window.innerHeight,
274
+ viewportWidth: content.width,
275
+ viewportHeight: content.height,
254
276
  orbDiameter: size,
255
277
  });
256
278
  applyDeployment();
@@ -685,6 +707,7 @@ export function mountOrb(controller, options = {}) {
685
707
  unsubscribe();
686
708
  stopWatch();
687
709
  window.removeEventListener('resize', reposition);
710
+ contentObserver?.disconnect();
688
711
  visualViewport?.removeEventListener('resize', onViewportChange);
689
712
  visualViewport?.removeEventListener('scroll', onViewportChange);
690
713
  clearOverlay();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fiodos/web-core",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Framework-agnostic browser layer for the Fiodos agent: a vanilla AgentController (orchestrator), DOM adapters (navigation/voice/storage), HTTP client and decision engine over @fiodos/core. Shared base for @fiodos/vue, @fiodos/svelte and @fiodos/angular (no framework imports).",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "publishConfig": {