@fiodos/web-core 0.1.12 → 0.1.15
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/cjs/adapters/webCredentialAutofillAdapter.d.ts +16 -0
- package/dist/cjs/adapters/webCredentialAutofillAdapter.js +47 -0
- package/dist/cjs/api/backendClient.d.ts +10 -3
- package/dist/cjs/api/backendClient.js +31 -9
- package/dist/cjs/config/types.d.ts +22 -2
- package/dist/cjs/controller/AgentController.d.ts +14 -0
- package/dist/cjs/controller/AgentController.js +65 -2
- package/dist/cjs/dropin/createFiodosAgent.d.ts +27 -1
- package/dist/cjs/dropin/createFiodosAgent.js +48 -4
- package/dist/cjs/embed/dynamics.d.ts +61 -0
- package/dist/cjs/embed/dynamics.js +173 -0
- package/dist/cjs/embed/global.d.ts +45 -0
- package/dist/cjs/embed/global.js +48 -0
- package/dist/cjs/index.d.ts +3 -0
- package/dist/cjs/index.js +8 -1
- package/dist/cjs/orb/mountOrb.js +172 -28
- package/dist/cjs/orb/orbView.js +42 -8
- package/dist/cjs/orb/publishedConfig.d.ts +4 -2
- package/dist/cjs/orb/publishedConfig.js +56 -7
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/embed/fiodos-embed.js +66 -0
- package/dist/esm/adapters/webCredentialAutofillAdapter.d.ts +16 -0
- package/dist/esm/adapters/webCredentialAutofillAdapter.js +44 -0
- package/dist/esm/api/backendClient.d.ts +10 -3
- package/dist/esm/api/backendClient.js +32 -10
- package/dist/esm/config/types.d.ts +22 -2
- package/dist/esm/controller/AgentController.d.ts +14 -0
- package/dist/esm/controller/AgentController.js +65 -2
- package/dist/esm/dropin/createFiodosAgent.d.ts +27 -1
- package/dist/esm/dropin/createFiodosAgent.js +48 -4
- package/dist/esm/embed/dynamics.d.ts +61 -0
- package/dist/esm/embed/dynamics.js +168 -0
- package/dist/esm/embed/global.d.ts +45 -0
- package/dist/esm/embed/global.js +46 -0
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/orb/mountOrb.js +173 -29
- package/dist/esm/orb/orbView.js +43 -9
- package/dist/esm/orb/publishedConfig.d.ts +4 -2
- package/dist/esm/orb/publishedConfig.js +56 -7
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +5 -3
package/dist/esm/index.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export { createWebNavigationAdapter, BACK_ROUTE, } from './adapters/webNavigatio
|
|
|
26
26
|
export type { WebNavigationAdapter, WebNavigationAdapterOptions, } from './adapters/webNavigationAdapter.js';
|
|
27
27
|
export { createWebVoiceAdapter } from './adapters/webVoiceAdapter.js';
|
|
28
28
|
export type { WebVoiceAdapterOptions } from './adapters/webVoiceAdapter.js';
|
|
29
|
+
export { createWebCredentialAutofillAdapter } from './adapters/webCredentialAutofillAdapter.js';
|
|
29
30
|
export { createFiodosBackendClient, buildManifestPayload } from './api/backendClient.js';
|
|
30
31
|
export type { FiodosBackendClientOptions } from './api/backendClient.js';
|
|
31
32
|
export { createFiodosTelemetry } from './api/backendTelemetry.js';
|
|
@@ -36,6 +37,8 @@ export { createBridge } from './bridge/createBridge.js';
|
|
|
36
37
|
export type { Bridge } from './bridge/createBridge.js';
|
|
37
38
|
export { createScreenContextStore } from './context/screenContextStore.js';
|
|
38
39
|
export type { ScreenContextStore, AgentScreenSnapshot } from './context/screenContextStore.js';
|
|
40
|
+
export { connectDynamicsFormContext, dynamicsFormSnapshot, normalizeDynamicsId, } from './embed/dynamics.js';
|
|
41
|
+
export type { DynamicsFormContextLike, DynamicsSnapshotOptions, DynamicsContextBridge, ScreenContextSink, } from './embed/dynamics.js';
|
|
39
42
|
export { createSpeechSession } from './speech/speechSession.js';
|
|
40
43
|
export type { SpeechSession, SpeechSessionOptions, SpeechSessionState, SpeechSessionSnapshot, } from './speech/speechSession.js';
|
|
41
44
|
export { DEFAULT_AGENT_TIMINGS, DEFAULT_AGENT_CHAINING } from './config/types.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -23,6 +23,7 @@ export { watchPublishedConfig } from './orb/publishedConfig.js';
|
|
|
23
23
|
export { createWebStorageAdapter } from './adapters/webStorageAdapter.js';
|
|
24
24
|
export { createWebNavigationAdapter, BACK_ROUTE, } from './adapters/webNavigationAdapter.js';
|
|
25
25
|
export { createWebVoiceAdapter } from './adapters/webVoiceAdapter.js';
|
|
26
|
+
export { createWebCredentialAutofillAdapter } from './adapters/webCredentialAutofillAdapter.js';
|
|
26
27
|
// ── Backend + telemetry (same wire contract as every platform) ────────────────
|
|
27
28
|
export { createFiodosBackendClient, buildManifestPayload } from './api/backendClient.js';
|
|
28
29
|
export { createFiodosTelemetry } from './api/backendTelemetry.js';
|
|
@@ -31,6 +32,8 @@ export { AgentApiError, isAgentApiError } from './api/errors.js';
|
|
|
31
32
|
export { createBridge } from './bridge/createBridge.js';
|
|
32
33
|
// ── Screen context ────────────────────────────────────────────────────────────
|
|
33
34
|
export { createScreenContextStore } from './context/screenContextStore.js';
|
|
35
|
+
// ── Closed-host context adapters (embed mode) ─────────────────────────────────
|
|
36
|
+
export { connectDynamicsFormContext, dynamicsFormSnapshot, normalizeDynamicsId, } from './embed/dynamics.js';
|
|
34
37
|
// ── Speech session ──────────────────────────────────────────────────────────────
|
|
35
38
|
export { createSpeechSession } from './speech/speechSession.js';
|
|
36
39
|
// ── Config / i18n ─────────────────────────────────────────────────────────────
|
package/dist/esm/orb/mountOrb.js
CHANGED
|
@@ -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, matchRouteIntent, resolveBubblePrompt, bubblePromptToAffirmative, resolveBubbleTheme, resolveBubblePadding, bubbleShapeToCss, resolveOrbAnchorPx, resolveOrbDeployment, snapOrbScreenPositionToSide, viewportPixelsToOrbScreenPosition, fiodosInputInkColor, fiodosInputMutedInkColor, shouldHideInternalOrb, } from '@fiodos/core';
|
|
19
|
+
import { DEFAULT_ORB_SCREEN_POSITION, createWebOrbHaptics, matchRouteIntent, resolveBubblePrompt, bubblePromptToAffirmative, resolveBubbleTheme, resolveBubblePadding, bubbleShapeToCss, applyBubbleShapeCss, resolveOrbAnchorPx, resolveOrbDeployment, snapOrbScreenPositionToSide, viewportPixelsToOrbScreenPosition, fiodosInputInkColor, fiodosInputMutedInkColor, shouldHideInternalOrb, } from '@fiodos/core';
|
|
20
20
|
import { buildKeyboardChip, createOrbVisual, DEFAULT_ORB_APPEARANCE, } from './orbView.js';
|
|
21
21
|
import { watchPublishedConfig } from './publishedConfig.js';
|
|
22
22
|
import { createBubbleDictation, scrollTextInputToEnd } from '../speech/bubbleDictation.js';
|
|
@@ -27,8 +27,23 @@ const CSS = `
|
|
|
27
27
|
.fyodos-orb-btn img{pointer-events:none;-webkit-user-drag:none;user-select:none;}
|
|
28
28
|
.fyodos-orb-btn:focus-visible{outline:2px solid #8ab6ff;outline-offset:4px;border-radius:50%;}
|
|
29
29
|
.fyodos-orb-anchor{position:relative;display:inline-flex;}
|
|
30
|
-
.fyodos-bubble{position:absolute;display:flex;flex-direction:column;width:min(320px,calc(100vw - 48px));max-height:340px;overflow:hidden;border-radius:16px;box-shadow:0 16px 40px rgba(0,0,0,0.28);border:1px solid rgba(160,180,220,0.22);animation:fyodos-fade-in .14s ease-out;}
|
|
31
|
-
.fyodos-bubble
|
|
30
|
+
.fyodos-bubble{position:absolute;display:flex;flex-direction:column;width:min(320px,calc(100vw - 48px));max-height:340px;overflow:hidden;border-radius:16px;box-shadow:0 16px 40px rgba(0,0,0,0.28);border:1px solid rgba(160,180,220,0.22);animation:fyodos-fade-in .14s ease-out;transition:width .25s cubic-bezier(0.22,1,0.36,1),max-height .25s cubic-bezier(0.22,1,0.36,1);cursor:zoom-in;}
|
|
31
|
+
.fyodos-bubble--expanded{width:min(560px,calc(100vw - 32px));max-height:min(620px,75vh);cursor:auto;}
|
|
32
|
+
.fyodos-bubble-user,.fyodos-bubble-reply{cursor:text;}
|
|
33
|
+
.fyodos-bubble-row{cursor:default;}
|
|
34
|
+
.fyodos-bubble--expanded .fyodos-bubble-scroll{padding:22px 24px 8px;gap:10px;}
|
|
35
|
+
.fyodos-bubble--expanded .fyodos-bubble-exchanges{gap:14px;}
|
|
36
|
+
.fyodos-bubble--expanded .fyodos-bubble-exchange{gap:9px;}
|
|
37
|
+
.fyodos-bubble--expanded .fyodos-bubble-exchange:first-child .fyodos-bubble-user{padding-right:0;}
|
|
38
|
+
.fyodos-bubble--expanded .fyodos-bubble-user{font-size:14.5px;line-height:21px;}
|
|
39
|
+
.fyodos-bubble--expanded .fyodos-bubble-reply{font-size:17px;line-height:25px;}
|
|
40
|
+
.fyodos-bubble--expanded .fyodos-bubble-row{padding:12px 16px;gap:10px;}
|
|
41
|
+
.fyodos-bubble--expanded .fyodos-bubble-busy{padding:8px 24px;}
|
|
42
|
+
.fyodos-bubble--expanded .fyodos-bubble-row input{padding:12px 14px;font-size:16px;}
|
|
43
|
+
.fyodos-bubble--expanded .fyodos-bubble-mic,.fyodos-bubble--expanded .fyodos-bubble-row button{width:38px;height:38px;min-width:38px;font-size:19px;}
|
|
44
|
+
.fyodos-bubble-close,.fyodos-bubble-unexpand{position:absolute;top:4px;z-index:1;display:flex;align-items:center;justify-content:center;border:none;background:transparent;font-size:18px;line-height:18px;width:28px;height:28px;cursor:pointer;padding:0;opacity:.75;}
|
|
45
|
+
.fyodos-bubble-close{right:6px;}
|
|
46
|
+
.fyodos-bubble-unexpand{right:38px;display:none;}
|
|
32
47
|
.fyodos-bubble-user-row{display:flex;flex-direction:row;align-items:flex-start;gap:6px;}
|
|
33
48
|
.fyodos-bubble-scroll{overflow-y:auto;padding:12px 14px 4px;display:flex;flex-direction:column;gap:6px;min-height:18px;scrollbar-width:none;-ms-overflow-style:none;}
|
|
34
49
|
.fyodos-bubble-scroll::-webkit-scrollbar{display:none;width:0;height:0;}
|
|
@@ -195,12 +210,13 @@ export function mountOrb(controller, options = {}) {
|
|
|
195
210
|
const prompt = resolveCurrentBubblePrompt();
|
|
196
211
|
openTextPanel(prompt
|
|
197
212
|
? bubblePromptToAffirmative(prompt, {
|
|
198
|
-
locale:
|
|
213
|
+
locale: currentBubbleLocale(),
|
|
199
214
|
messages: controller.messages,
|
|
200
215
|
})
|
|
201
216
|
: undefined);
|
|
202
217
|
return;
|
|
203
218
|
}
|
|
219
|
+
userTriedVoice = true;
|
|
204
220
|
void controller.toggleListening();
|
|
205
221
|
});
|
|
206
222
|
thoughtBubbleEl.addEventListener('keydown', (event) => {
|
|
@@ -370,10 +386,22 @@ export function mountOrb(controller, options = {}) {
|
|
|
370
386
|
btn.addEventListener('lostpointercapture', onPointerUp);
|
|
371
387
|
// ── Text bubble ─────────────────────────────────────────────────────────────
|
|
372
388
|
let bubbleOpen = false;
|
|
389
|
+
// Reduced (default) size stays exactly as before. Tapping any EMPTY area of
|
|
390
|
+
// the panel (not a message, not the input row, not a button — so text stays
|
|
391
|
+
// selectable/copyable) grows it to the expanded layout; shrinking back is an
|
|
392
|
+
// explicit tap on `unexpandBtn` next to the close button.
|
|
393
|
+
let bubbleExpanded = false;
|
|
373
394
|
// When the mic is denied/unavailable, tapping the orb still puts it in the
|
|
374
395
|
// SAME "active/listening" visual state and reveals the keyboard chip — the
|
|
375
396
|
// user is never blocked, text is always an open avenue (Change 1).
|
|
376
397
|
let micFallbackActive = false;
|
|
398
|
+
// True once the user actually ATTEMPTED voice (tapped the orb/bubble to
|
|
399
|
+
// listen). The voiceBlocked→keyboard fallback in render() must only fire for
|
|
400
|
+
// a mic that failed MID-ATTEMPT: the upfront Permissions-API detection also
|
|
401
|
+
// sets voiceBlocked at mount (e.g. the site has the mic permission denied),
|
|
402
|
+
// and without this guard the conversation panel would pop open on page load
|
|
403
|
+
// with zero interaction.
|
|
404
|
+
let userTriedVoice = false;
|
|
377
405
|
const bubble = document.createElement('div');
|
|
378
406
|
bubble.className = 'fyodos-bubble';
|
|
379
407
|
bubble.style.display = 'none';
|
|
@@ -406,6 +434,19 @@ export function mountOrb(controller, options = {}) {
|
|
|
406
434
|
closeBtn.textContent = '×';
|
|
407
435
|
closeBtn.setAttribute('aria-label', 'Close');
|
|
408
436
|
closeBtn.addEventListener('click', () => setBubble(false));
|
|
437
|
+
// Collapse button — only shown while expanded, right next to the close
|
|
438
|
+
// button (same minimalist style). Tapping an empty area of the panel is
|
|
439
|
+
// what EXPANDS it; shrinking back is always an explicit, discoverable tap.
|
|
440
|
+
const unexpandBtn = document.createElement('button');
|
|
441
|
+
unexpandBtn.type = 'button';
|
|
442
|
+
unexpandBtn.className = 'fyodos-bubble-unexpand';
|
|
443
|
+
unexpandBtn.innerHTML =
|
|
444
|
+
'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 14h6v6M20 10h-6V4M14 10l7-7M3 21l7-7"></path></svg>';
|
|
445
|
+
unexpandBtn.setAttribute('aria-label', 'Collapse');
|
|
446
|
+
unexpandBtn.addEventListener('click', (e) => {
|
|
447
|
+
e.stopPropagation();
|
|
448
|
+
setBubbleExpanded(false);
|
|
449
|
+
});
|
|
409
450
|
// Loading row shown at the top while an older page of the session history
|
|
410
451
|
// streams in (scroll-to-top pagination).
|
|
411
452
|
const loaderRow = document.createElement('div');
|
|
@@ -429,6 +470,12 @@ export function mountOrb(controller, options = {}) {
|
|
|
429
470
|
signature.endsWith(renderedSignature);
|
|
430
471
|
const fromBottom = exchangeScroll.scrollHeight - exchangeScroll.scrollTop;
|
|
431
472
|
renderedSignature = signature;
|
|
473
|
+
// Theme colors INLINE at node creation: the embed lives inside arbitrary
|
|
474
|
+
// host pages (e.g. Shopify themes) whose CSS sets `color` on everything,
|
|
475
|
+
// so a freshly-rendered message must never wait for the next
|
|
476
|
+
// applyBubbleTheme() pass to become readable (it would inherit the host's
|
|
477
|
+
// ink — black on our dark panel — until the next config poll).
|
|
478
|
+
const t = { ...DEFAULT_MODAL_THEME, ...(modalTheme ?? {}) };
|
|
432
479
|
exchangeList.replaceChildren();
|
|
433
480
|
for (const ex of exchanges) {
|
|
434
481
|
const block = document.createElement('div');
|
|
@@ -436,11 +483,13 @@ export function mountOrb(controller, options = {}) {
|
|
|
436
483
|
const user = document.createElement('div');
|
|
437
484
|
user.className = 'fyodos-bubble-user';
|
|
438
485
|
user.textContent = ex.userText;
|
|
486
|
+
user.style.color = t.bodyColor;
|
|
439
487
|
block.append(user);
|
|
440
488
|
if (ex.replyText) {
|
|
441
489
|
const reply = document.createElement('div');
|
|
442
490
|
reply.className = 'fyodos-bubble-reply';
|
|
443
491
|
reply.textContent = ex.replyText;
|
|
492
|
+
reply.style.color = t.titleColor;
|
|
444
493
|
block.append(reply);
|
|
445
494
|
}
|
|
446
495
|
exchangeList.append(block);
|
|
@@ -547,7 +596,8 @@ export function mountOrb(controller, options = {}) {
|
|
|
547
596
|
if (active) {
|
|
548
597
|
micBtn.style.background = accent;
|
|
549
598
|
micBtn.style.border = 'none';
|
|
550
|
-
|
|
599
|
+
// Ink readable on the (customizable) accent — dark accents need light ink.
|
|
600
|
+
micBtn.style.color = fiodosInputInkColor(accent);
|
|
551
601
|
micBtn.style.borderRadius = sendRadius;
|
|
552
602
|
}
|
|
553
603
|
else {
|
|
@@ -559,8 +609,71 @@ export function mountOrb(controller, options = {}) {
|
|
|
559
609
|
micBtn.setAttribute('aria-label', active ? ui.micStopLabel : ui.micLabel);
|
|
560
610
|
micBtn.title = active ? ui.micStopLabel : ui.micLabel;
|
|
561
611
|
}
|
|
562
|
-
bubble.append(closeBtn, exchangeScroll, busyRow, inputRow);
|
|
612
|
+
bubble.append(closeBtn, unexpandBtn, exchangeScroll, busyRow, inputRow);
|
|
563
613
|
anchor.appendChild(bubble);
|
|
614
|
+
function setBubbleExpanded(expanded) {
|
|
615
|
+
if (bubbleExpanded === expanded)
|
|
616
|
+
return;
|
|
617
|
+
bubbleExpanded = expanded;
|
|
618
|
+
bubble.classList.toggle('fyodos-bubble--expanded', expanded);
|
|
619
|
+
unexpandBtn.style.display = expanded ? 'flex' : 'none';
|
|
620
|
+
positionBubble();
|
|
621
|
+
}
|
|
622
|
+
// Tap-to-expand: a plain click on the panel grows it. Text selection is
|
|
623
|
+
// never broken: a drag-selection is a move (threshold guard), a double/
|
|
624
|
+
// triple-click word/paragraph selection cancels the pending expand (detail
|
|
625
|
+
// guard + delay), and any active selection blocks it. Controls (input row,
|
|
626
|
+
// buttons) never expand.
|
|
627
|
+
let bubbleTapX = 0;
|
|
628
|
+
let bubbleTapY = 0;
|
|
629
|
+
let pendingExpandTimer = null;
|
|
630
|
+
const TAP_MOVE_THRESHOLD = 8;
|
|
631
|
+
const hasActiveSelection = () => {
|
|
632
|
+
const selection = typeof window !== 'undefined' ? window.getSelection?.() : null;
|
|
633
|
+
return !!selection && !selection.isCollapsed && selection.toString().length > 0;
|
|
634
|
+
};
|
|
635
|
+
const cancelPendingExpand = () => {
|
|
636
|
+
if (pendingExpandTimer != null) {
|
|
637
|
+
window.clearTimeout(pendingExpandTimer);
|
|
638
|
+
pendingExpandTimer = null;
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
bubble.addEventListener('pointerdown', (e) => {
|
|
642
|
+
bubbleTapX = e.clientX;
|
|
643
|
+
bubbleTapY = e.clientY;
|
|
644
|
+
});
|
|
645
|
+
bubble.addEventListener('click', (e) => {
|
|
646
|
+
if (bubbleExpanded)
|
|
647
|
+
return;
|
|
648
|
+
const target = e.target;
|
|
649
|
+
if (target?.closest('.fyodos-bubble-row, .fyodos-bubble-close, .fyodos-bubble-unexpand, button, input, a')) {
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
const moved = Math.hypot(e.clientX - bubbleTapX, e.clientY - bubbleTapY);
|
|
653
|
+
if (moved > TAP_MOVE_THRESHOLD)
|
|
654
|
+
return;
|
|
655
|
+
// Double/triple click is a word/paragraph selection gesture, never expand
|
|
656
|
+
// (it also cancels the pending expand from its own first click).
|
|
657
|
+
if (e.detail > 1) {
|
|
658
|
+
cancelPendingExpand();
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
cancelPendingExpand();
|
|
662
|
+
const overText = !!target?.closest('.fyodos-bubble-user, .fyodos-bubble-reply');
|
|
663
|
+
if (!overText && !hasActiveSelection()) {
|
|
664
|
+
// Genuinely empty area, nothing selected — expand immediately.
|
|
665
|
+
setBubbleExpanded(true);
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
// Over message text, or a selection may still be collapsing: expand after
|
|
669
|
+
// a short delay, re-checking the selection right before — a double-click's
|
|
670
|
+
// second click or a real selection cancels it.
|
|
671
|
+
pendingExpandTimer = window.setTimeout(() => {
|
|
672
|
+
pendingExpandTimer = null;
|
|
673
|
+
if (!hasActiveSelection())
|
|
674
|
+
setBubbleExpanded(true);
|
|
675
|
+
}, 280);
|
|
676
|
+
});
|
|
564
677
|
// Initial placement (after the bubble exists, so applyDeployment can lay it out).
|
|
565
678
|
reposition();
|
|
566
679
|
function applyBubbleTheme() {
|
|
@@ -571,6 +684,7 @@ export function mountOrb(controller, options = {}) {
|
|
|
571
684
|
if (modalTheme?.borderRadius != null)
|
|
572
685
|
bubble.style.borderRadius = `${modalTheme.borderRadius}px`;
|
|
573
686
|
closeBtn.style.color = t.bodyColor;
|
|
687
|
+
unexpandBtn.style.color = t.bodyColor;
|
|
574
688
|
exchangeList.querySelectorAll('.fyodos-bubble-user').forEach((node) => {
|
|
575
689
|
node.style.color = t.bodyColor;
|
|
576
690
|
});
|
|
@@ -582,6 +696,9 @@ export function mountOrb(controller, options = {}) {
|
|
|
582
696
|
textInput.style.color = fiodosInputInkColor(inputBg);
|
|
583
697
|
textInput.style.setProperty('--fyodos-input-placeholder', fiodosInputMutedInkColor(inputBg));
|
|
584
698
|
sendBtn.style.background = accent;
|
|
699
|
+
// The glyph (↑ / ◼) must stay readable on ANY published accent: dark ink
|
|
700
|
+
// on light accents, light ink on dark ones (never the hardcoded black).
|
|
701
|
+
sendBtn.style.color = fiodosInputInkColor(accent);
|
|
585
702
|
sendBtn.style.borderRadius = `${modalTheme?.sendButtonRadius ?? 10}px`;
|
|
586
703
|
syncMicButton();
|
|
587
704
|
busyRow.querySelectorAll('span').forEach((s) => {
|
|
@@ -605,8 +722,18 @@ export function mountOrb(controller, options = {}) {
|
|
|
605
722
|
controller.resetBubbleWindow();
|
|
606
723
|
// Closing the panel returns the orb to idle (also clears mic fallback).
|
|
607
724
|
micFallbackActive = false;
|
|
725
|
+
// Always re-open at the reduced size.
|
|
726
|
+
cancelPendingExpand();
|
|
727
|
+
setBubbleExpanded(false);
|
|
608
728
|
}
|
|
609
729
|
syncChip(controller.getState());
|
|
730
|
+
// Re-resolve the thought bubble NOW: closing the panel must bring the
|
|
731
|
+
// screen-aware CTA back immediately. Nothing else is guaranteed to run
|
|
732
|
+
// right after close (controller state does not change on close, the route
|
|
733
|
+
// poll only fires on changes, the config poll every ~12s) — without this
|
|
734
|
+
// the orb sat "naked" until some unrelated event. Opening hides it (the
|
|
735
|
+
// resolver returns null while the panel is open), so one call covers both.
|
|
736
|
+
syncThoughtBubble(controller.getState());
|
|
610
737
|
}
|
|
611
738
|
function submitText() {
|
|
612
739
|
// While a turn is in flight the button is a STOP button; never start a new
|
|
@@ -671,8 +798,22 @@ export function mountOrb(controller, options = {}) {
|
|
|
671
798
|
applyDeployment();
|
|
672
799
|
}
|
|
673
800
|
// ── Thought bubble (screen-aware CTA beside the orb) ─────────────────────────
|
|
801
|
+
// LIVE locale: re-read the host app's active language on every resolution so
|
|
802
|
+
// the bubble text (analyzer `bubblePrompts` translations) follows the app's
|
|
803
|
+
// language switcher without remounting. Falls back to the mount-time locale.
|
|
804
|
+
function currentBubbleLocale() {
|
|
805
|
+
try {
|
|
806
|
+
const live = controller.config.getLocale?.();
|
|
807
|
+
if (typeof live === 'string' && live.trim())
|
|
808
|
+
return live.trim();
|
|
809
|
+
}
|
|
810
|
+
catch {
|
|
811
|
+
/* live locale read is best-effort */
|
|
812
|
+
}
|
|
813
|
+
return controller.config.locale;
|
|
814
|
+
}
|
|
674
815
|
function resolveCurrentBubblePrompt() {
|
|
675
|
-
return resolveBubblePrompt(matchRouteIntent(currentRoute, controller.config.manifest.routes), controller.messages, { locale:
|
|
816
|
+
return resolveBubblePrompt(matchRouteIntent(currentRoute, controller.config.manifest.routes), controller.messages, { locale: currentBubbleLocale(), actions: controller.config.manifest.actions });
|
|
676
817
|
}
|
|
677
818
|
// Render the CTA on ONE line at its NATURAL size. When the phrase is too long
|
|
678
819
|
// to fit the max width, scroll it (marquee, right-to-left, looping) instead of
|
|
@@ -707,29 +848,17 @@ export function mountOrb(controller, options = {}) {
|
|
|
707
848
|
track.appendChild(second);
|
|
708
849
|
thoughtBubbleEl.appendChild(track);
|
|
709
850
|
}
|
|
710
|
-
function applyThoughtBubbleTheme() {
|
|
851
|
+
function applyThoughtBubbleTheme(widthPx) {
|
|
711
852
|
const t = bubbleTheme;
|
|
712
853
|
const { fontPx, padH, approxHeight } = resolveBubblePadding(t);
|
|
713
|
-
const shapeCss = bubbleShapeToCss(t, approxHeight);
|
|
854
|
+
const shapeCss = bubbleShapeToCss(t, approxHeight, 1, widthPx);
|
|
714
855
|
thoughtBubbleEl.style.fontSize = `${fontPx}px`;
|
|
715
856
|
thoughtBubbleEl.style.height = `${approxHeight}px`;
|
|
716
857
|
thoughtBubbleEl.style.padding = `0 ${padH}px`;
|
|
717
858
|
thoughtBubbleEl.style.color = t.textColor;
|
|
718
859
|
thoughtBubbleEl.style.background = t.backgroundColor;
|
|
719
860
|
thoughtBubbleEl.style.border = `${t.borderWidth}px solid ${t.borderColor}`;
|
|
720
|
-
|
|
721
|
-
if (!value) {
|
|
722
|
-
thoughtBubbleEl.style.removeProperty(prop);
|
|
723
|
-
return;
|
|
724
|
-
}
|
|
725
|
-
thoughtBubbleEl.style.setProperty(prop, value, 'important');
|
|
726
|
-
};
|
|
727
|
-
setImportant('border-radius', shapeCss.borderRadius);
|
|
728
|
-
setImportant('border-top-left-radius', shapeCss.borderTopLeftRadius);
|
|
729
|
-
setImportant('border-top-right-radius', shapeCss.borderTopRightRadius);
|
|
730
|
-
setImportant('border-bottom-left-radius', shapeCss.borderBottomLeftRadius);
|
|
731
|
-
setImportant('border-bottom-right-radius', shapeCss.borderBottomRightRadius);
|
|
732
|
-
setImportant('clip-path', shapeCss.clipPath);
|
|
861
|
+
applyBubbleShapeCss(thoughtBubbleEl, shapeCss);
|
|
733
862
|
}
|
|
734
863
|
function positionThoughtBubble() {
|
|
735
864
|
const dep = resolveOrbDeployment(effectivePosition());
|
|
@@ -764,6 +893,11 @@ export function mountOrb(controller, options = {}) {
|
|
|
764
893
|
thoughtBubbleEl.style.display = 'flex';
|
|
765
894
|
applyThoughtBubbleTheme();
|
|
766
895
|
layoutThoughtContent(prompt);
|
|
896
|
+
if (bubbleTheme.shape === 'cloud') {
|
|
897
|
+
const widthPx = thoughtBubbleEl.offsetWidth;
|
|
898
|
+
if (widthPx > 0)
|
|
899
|
+
applyThoughtBubbleTheme(widthPx);
|
|
900
|
+
}
|
|
767
901
|
positionThoughtBubble();
|
|
768
902
|
}
|
|
769
903
|
// ── Overlays (confirmation + consent) ────────────────────────────────────────
|
|
@@ -894,6 +1028,7 @@ export function mountOrb(controller, options = {}) {
|
|
|
894
1028
|
// denied/blocked, in which case we skip voice and go straight to the keyboard.
|
|
895
1029
|
if (controller.voiceAvailable && voiceInputEnabled) {
|
|
896
1030
|
micFallbackActive = false;
|
|
1031
|
+
userTriedVoice = true;
|
|
897
1032
|
void controller.toggleListening();
|
|
898
1033
|
return;
|
|
899
1034
|
}
|
|
@@ -905,6 +1040,7 @@ export function mountOrb(controller, options = {}) {
|
|
|
905
1040
|
openTextPanel();
|
|
906
1041
|
return;
|
|
907
1042
|
}
|
|
1043
|
+
userTriedVoice = true;
|
|
908
1044
|
void controller.toggleListening();
|
|
909
1045
|
};
|
|
910
1046
|
sendBtn.onclick = () => {
|
|
@@ -943,11 +1079,15 @@ export function mountOrb(controller, options = {}) {
|
|
|
943
1079
|
// Track phase transitions to fire haptics: start/stop listening + response.
|
|
944
1080
|
let lastPhase = null;
|
|
945
1081
|
function render(state) {
|
|
946
|
-
// Mic just became unusable
|
|
947
|
-
// keyboard so the user always has a working channel.
|
|
1082
|
+
// Mic just became unusable MID-ATTEMPT (no Permissions API): fall back to
|
|
1083
|
+
// the keyboard so the user always has a working channel. Guarded by
|
|
1084
|
+
// userTriedVoice: the upfront permission detection also sets voiceBlocked
|
|
1085
|
+
// right at mount (site-level mic denial), and that must NEVER auto-open
|
|
1086
|
+
// the conversation panel on page load — the user did nothing yet; their
|
|
1087
|
+
// first orb tap will route to the keyboard via the voiceAvailable check.
|
|
948
1088
|
if (state.voiceBlocked && !lastVoiceBlocked) {
|
|
949
1089
|
lastVoiceBlocked = true;
|
|
950
|
-
if (enableTextInput && !bubbleOpen && !state.isBusy) {
|
|
1090
|
+
if (userTriedVoice && enableTextInput && !bubbleOpen && !state.isBusy) {
|
|
951
1091
|
openTextPanel();
|
|
952
1092
|
return;
|
|
953
1093
|
}
|
|
@@ -1016,9 +1156,11 @@ export function mountOrb(controller, options = {}) {
|
|
|
1016
1156
|
}
|
|
1017
1157
|
const unsubscribe = controller.subscribe(render);
|
|
1018
1158
|
render(controller.getState());
|
|
1019
|
-
// Track the current route
|
|
1020
|
-
//
|
|
1021
|
-
// popstate / visibility
|
|
1159
|
+
// Track the current route AND the app's live locale so the thought bubble
|
|
1160
|
+
// re-resolves its CTA on navigation and on language switches. No universal
|
|
1161
|
+
// navigation/i18n event exists, so poll + listen to popstate / visibility
|
|
1162
|
+
// for snappier updates.
|
|
1163
|
+
let lastBubbleLocale = currentBubbleLocale();
|
|
1022
1164
|
const readRoute = () => {
|
|
1023
1165
|
let next = null;
|
|
1024
1166
|
try {
|
|
@@ -1027,8 +1169,10 @@ export function mountOrb(controller, options = {}) {
|
|
|
1027
1169
|
catch {
|
|
1028
1170
|
/* navigation read is best-effort */
|
|
1029
1171
|
}
|
|
1030
|
-
|
|
1172
|
+
const nextLocale = currentBubbleLocale();
|
|
1173
|
+
if (next !== currentRoute || nextLocale !== lastBubbleLocale) {
|
|
1031
1174
|
currentRoute = next;
|
|
1175
|
+
lastBubbleLocale = nextLocale;
|
|
1032
1176
|
syncThoughtBubble(controller.getState());
|
|
1033
1177
|
}
|
|
1034
1178
|
};
|
package/dist/esm/orb/orbView.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* pixel-for-pixel — no "basic circle" divergence. Vue / Svelte / Angular mount
|
|
9
9
|
* THIS orb through createFiodosAgent → mountOrb.
|
|
10
10
|
*/
|
|
11
|
-
import { chipBorderRadius, chipIconBaseSizePx, chipIconCenterOffset, chipIconSpec, computeOutwardBorderLayout, normalizeChipIconScale, normalizeKeyboardChipIcon, orbFillDepthBoxShadow, orbOuterGlowBoxShadow, resolveChipIconColor,
|
|
11
|
+
import { chipBorderRadius, chipIconBaseSizePx, chipIconCenterOffset, chipIconSpec, computeOutwardBorderLayout, normalizeChipIconScale, normalizeKeyboardChipIcon, orbFillDepthBoxShadow, orbOuterGlowBoxShadow, resolveChipIconColor, fiodosOrbMarkIdleHalfHeight, resolveFiodosOrbMarkAnimatedBars, resolveInnerBorderColor, resolveOrbBackgroundColor, } from '@fiodos/core';
|
|
12
12
|
const SVG_NS = 'http://www.w3.org/2000/svg';
|
|
13
13
|
export const DEFAULT_ORB_APPEARANCE = {
|
|
14
14
|
accentColor: '#4f8cff',
|
|
@@ -73,9 +73,15 @@ function buildDoubleBorder(opts) {
|
|
|
73
73
|
root.appendChild(mid);
|
|
74
74
|
return { root, fill };
|
|
75
75
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Fiodos brand mark (idle, no logo). Breathes very gently on web — a
|
|
78
|
+
* direct port of the landing hero orbs' waveform easing (see
|
|
79
|
+
* `fiodosOrbMarkIdleHalfHeight` in @fiodos/core) — deliberately subtler than
|
|
80
|
+
* and distinct from the volume-reactive waveform shown while listening.
|
|
81
|
+
* Tinted with the same "audio color" (waveformColor) as that waveform.
|
|
82
|
+
*/
|
|
83
|
+
function buildOrbMark(orbSize, markColor) {
|
|
84
|
+
const { bars, color, radius } = resolveFiodosOrbMarkAnimatedBars(orbSize, markColor);
|
|
79
85
|
const svg = svgEl('svg', {
|
|
80
86
|
width: orbSize,
|
|
81
87
|
height: orbSize,
|
|
@@ -83,10 +89,31 @@ function buildOrbMark(orbSize) {
|
|
|
83
89
|
'aria-hidden': 'true',
|
|
84
90
|
});
|
|
85
91
|
svg.style.display = 'block';
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
92
|
+
const rects = bars.map((b) => {
|
|
93
|
+
const rect = svgEl('rect', { x: b.x, y: b.y, width: b.width, height: b.height, rx: radius, ry: radius, fill: color });
|
|
94
|
+
svg.appendChild(rect);
|
|
95
|
+
return rect;
|
|
96
|
+
});
|
|
97
|
+
let raf = 0;
|
|
98
|
+
return {
|
|
99
|
+
el: svg,
|
|
100
|
+
start() {
|
|
101
|
+
const t0 = performance.now();
|
|
102
|
+
const loop = (now) => {
|
|
103
|
+
raf = requestAnimationFrame(loop);
|
|
104
|
+
const t = (now - t0) / 1000;
|
|
105
|
+
bars.forEach((b, i) => {
|
|
106
|
+
const half = fiodosOrbMarkIdleHalfHeight(t, i, b.baseHalfHeight, b.maxHalfHeight);
|
|
107
|
+
rects[i].setAttribute('y', (b.centerY - half).toFixed(2));
|
|
108
|
+
rects[i].setAttribute('height', (2 * half).toFixed(2));
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
raf = requestAnimationFrame(loop);
|
|
112
|
+
},
|
|
113
|
+
stop() {
|
|
114
|
+
cancelAnimationFrame(raf);
|
|
115
|
+
},
|
|
116
|
+
};
|
|
90
117
|
}
|
|
91
118
|
// ── Waveform (port of VoiceWaveform: 9 bars, bell profile, rAF) ───────────────
|
|
92
119
|
const WAVE_BARS = 9;
|
|
@@ -377,6 +404,7 @@ export function createOrbVisual(initial) {
|
|
|
377
404
|
let volume = 0;
|
|
378
405
|
let fillEl = null;
|
|
379
406
|
let waveform = null;
|
|
407
|
+
let markAnim = null;
|
|
380
408
|
function rebuild() {
|
|
381
409
|
const size = appearance.size ?? DEFAULT_ORB_APPEARANCE.size;
|
|
382
410
|
const fill = resolveOrbBackgroundColor({
|
|
@@ -388,6 +416,8 @@ export function createOrbVisual(initial) {
|
|
|
388
416
|
const interiorColor = resolveInnerBorderColor(appearance.innerBorderColor);
|
|
389
417
|
waveform?.stop();
|
|
390
418
|
waveform = null;
|
|
419
|
+
markAnim?.stop();
|
|
420
|
+
markAnim = null;
|
|
391
421
|
wrapper.replaceChildren();
|
|
392
422
|
const { root, fill: fillNode } = buildDoubleBorder({
|
|
393
423
|
fillDiameter: size,
|
|
@@ -409,6 +439,8 @@ export function createOrbVisual(initial) {
|
|
|
409
439
|
const waveColor = appearance.waveformColor ?? '#ffffff';
|
|
410
440
|
waveform?.stop();
|
|
411
441
|
waveform = null;
|
|
442
|
+
markAnim?.stop();
|
|
443
|
+
markAnim = null;
|
|
412
444
|
fillEl.replaceChildren();
|
|
413
445
|
// The "speaking" feedback now lives INSIDE the fill (a pulsing logo, or the
|
|
414
446
|
// rings + dot when there's no photo) to mirror the dashboard editor, so the
|
|
@@ -453,7 +485,9 @@ export function createOrbVisual(initial) {
|
|
|
453
485
|
return;
|
|
454
486
|
}
|
|
455
487
|
if (state === 'idle') {
|
|
456
|
-
|
|
488
|
+
markAnim = buildOrbMark(size, appearance.waveformColor);
|
|
489
|
+
fillEl.appendChild(markAnim.el);
|
|
490
|
+
markAnim.start();
|
|
457
491
|
}
|
|
458
492
|
}
|
|
459
493
|
rebuild();
|
|
@@ -60,7 +60,9 @@ export interface WatchPublishedConfigOptions {
|
|
|
60
60
|
}
|
|
61
61
|
/**
|
|
62
62
|
* Starts watching the published config. Returns an unsubscribe function.
|
|
63
|
-
* Best-effort: a failed fetch
|
|
64
|
-
*
|
|
63
|
+
* Best-effort: a failed fetch is SILENTLY skipped — the orb keeps its
|
|
64
|
+
* last-known-good (or cached) look instead of reverting to the default — and
|
|
65
|
+
* never throws into the host app. `onChange(null)` is reported ONLY when the
|
|
66
|
+
* backend confirms nothing is published.
|
|
65
67
|
*/
|
|
66
68
|
export declare function watchPublishedConfig(controller: AgentController, options: WatchPublishedConfigOptions): () => void;
|
|
@@ -11,6 +11,38 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { mapPublishedConfigToOrbProps, resolveBubbleTheme, } from '@fiodos/core';
|
|
13
13
|
import { DEFAULT_ORB_APPEARANCE } from './orbView.js';
|
|
14
|
+
/**
|
|
15
|
+
* Last-known-good published config, persisted so the customized orb survives
|
|
16
|
+
* page reloads during a backend outage and shows instantly on the next visit.
|
|
17
|
+
* Best-effort: storage failures (private mode, quota) are ignored.
|
|
18
|
+
*/
|
|
19
|
+
const PUBLISHED_CONFIG_CACHE_KEY = 'fyodos:published-config:v1';
|
|
20
|
+
function readCachedPublishedConfig() {
|
|
21
|
+
try {
|
|
22
|
+
if (typeof window === 'undefined' || !window.localStorage)
|
|
23
|
+
return null;
|
|
24
|
+
const raw = window.localStorage.getItem(PUBLISHED_CONFIG_CACHE_KEY);
|
|
25
|
+
return raw ? JSON.parse(raw) : null;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function writeCachedPublishedConfig(published) {
|
|
32
|
+
try {
|
|
33
|
+
if (typeof window === 'undefined' || !window.localStorage)
|
|
34
|
+
return;
|
|
35
|
+
if (published) {
|
|
36
|
+
window.localStorage.setItem(PUBLISHED_CONFIG_CACHE_KEY, JSON.stringify(published));
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
window.localStorage.removeItem(PUBLISHED_CONFIG_CACHE_KEY);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
/* best-effort cache */
|
|
44
|
+
}
|
|
45
|
+
}
|
|
14
46
|
function toResolved(props, published) {
|
|
15
47
|
const enabled = published.orbEnabled !== false;
|
|
16
48
|
const theme = props.theme;
|
|
@@ -45,8 +77,10 @@ function toResolved(props, published) {
|
|
|
45
77
|
}
|
|
46
78
|
/**
|
|
47
79
|
* Starts watching the published config. Returns an unsubscribe function.
|
|
48
|
-
* Best-effort: a failed fetch
|
|
49
|
-
*
|
|
80
|
+
* Best-effort: a failed fetch is SILENTLY skipped — the orb keeps its
|
|
81
|
+
* last-known-good (or cached) look instead of reverting to the default — and
|
|
82
|
+
* never throws into the host app. `onChange(null)` is reported ONLY when the
|
|
83
|
+
* backend confirms nothing is published.
|
|
50
84
|
*/
|
|
51
85
|
export function watchPublishedConfig(controller, options) {
|
|
52
86
|
const fetcher = controller.config.backend.fetchPublishedConfig;
|
|
@@ -55,18 +89,33 @@ export function watchPublishedConfig(controller, options) {
|
|
|
55
89
|
return () => { };
|
|
56
90
|
}
|
|
57
91
|
let cancelled = false;
|
|
92
|
+
// Hydrate from the local cache so the customized orb shows instantly (and
|
|
93
|
+
// survives page loads while the backend is unreachable). The first network
|
|
94
|
+
// response below overwrites it as the source of truth.
|
|
95
|
+
const cached = readCachedPublishedConfig();
|
|
96
|
+
if (cached) {
|
|
97
|
+
options.onChange(toResolved(mapPublishedConfigToOrbProps(cached), cached));
|
|
98
|
+
}
|
|
58
99
|
const load = async () => {
|
|
59
100
|
try {
|
|
60
101
|
const res = await fetcher.call(controller.config.backend);
|
|
61
102
|
if (cancelled)
|
|
62
103
|
return;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
104
|
+
if (res.published) {
|
|
105
|
+
options.onChange(toResolved(mapPublishedConfigToOrbProps(res.published), res.published));
|
|
106
|
+
writeCachedPublishedConfig(res.published);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
// A SUCCESSFUL response with nothing published: the project truly has
|
|
110
|
+
// no custom design, so the defaults are correct.
|
|
111
|
+
options.onChange(null);
|
|
112
|
+
writeCachedPublishedConfig(null);
|
|
113
|
+
}
|
|
66
114
|
}
|
|
67
115
|
catch {
|
|
68
|
-
|
|
69
|
-
|
|
116
|
+
// Transient failure (timeout, network blip after hours open, backend
|
|
117
|
+
// redeploy…): keep the current appearance. The next successful poll
|
|
118
|
+
// refreshes it.
|
|
70
119
|
}
|
|
71
120
|
};
|
|
72
121
|
void load();
|
package/dist/esm/version.d.ts
CHANGED
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
* Auto-generated by scripts/sync-sdk-version.mjs from package.json. Do not edit
|
|
6
6
|
* by hand — change package.json `version` and re-run the sync/release script.
|
|
7
7
|
*/
|
|
8
|
-
export declare const SDK_VERSION = "0.1.
|
|
8
|
+
export declare const SDK_VERSION = "0.1.15";
|
|
9
9
|
export declare const SDK_PACKAGE = "@fiodos/web-core";
|
package/dist/esm/version.js
CHANGED
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
* Auto-generated by scripts/sync-sdk-version.mjs from package.json. Do not edit
|
|
6
6
|
* by hand — change package.json `version` and re-run the sync/release script.
|
|
7
7
|
*/
|
|
8
|
-
export const SDK_VERSION = '0.1.
|
|
8
|
+
export const SDK_VERSION = '0.1.15';
|
|
9
9
|
export const SDK_PACKAGE = '@fiodos/web-core';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fiodos/web-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
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": {
|
|
@@ -23,16 +23,18 @@
|
|
|
23
23
|
"LICENSE"
|
|
24
24
|
],
|
|
25
25
|
"scripts": {
|
|
26
|
-
"build": "tsc -p tsconfig.cjs.json && tsc -p tsconfig.esm.json && node ../../scripts/fix-esm-extensions.mjs dist/esm && node ../../scripts/mark-dual.mjs",
|
|
26
|
+
"build": "tsc -p tsconfig.cjs.json && tsc -p tsconfig.esm.json && node ../../scripts/fix-esm-extensions.mjs dist/esm && node ../../scripts/mark-dual.mjs && npm run build:embed",
|
|
27
|
+
"build:embed": "esbuild src/embed/global.ts --bundle --format=iife --target=es2019 --minify --outfile=dist/embed/fiodos-embed.js",
|
|
27
28
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
28
29
|
"test": "node --import tsx --test test/*.test.ts",
|
|
29
30
|
"prepublishOnly": "node ../../scripts/sync-sdk-version.mjs && npm run build"
|
|
30
31
|
},
|
|
31
32
|
"dependencies": {
|
|
32
|
-
"@fiodos/core": "^0.1.
|
|
33
|
+
"@fiodos/core": "^0.1.10"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
36
|
"@types/node": "^22.0.0",
|
|
37
|
+
"esbuild": "^0.28.1",
|
|
36
38
|
"tsx": "^4.19.0",
|
|
37
39
|
"typescript": "^5.6.0"
|
|
38
40
|
}
|