@foisit/angular-wrapper 2.5.1 → 3.0.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/README.md +43 -3
- package/fesm2022/foisit-angular-wrapper.mjs +119 -55
- package/fesm2022/foisit-angular-wrapper.mjs.map +1 -1
- package/index.d.ts +1 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ Transform your Angular app into an intelligent, voice-ready platform. Foisit pro
|
|
|
33
33
|
- **Programmatic UI Triggers** - Direct command execution via `runCommand()`
|
|
34
34
|
- **Rich Markdown Rendering** - Enhanced response formatting with headings, code, and links
|
|
35
35
|
- **Advanced File Validations** - Comprehensive client-side file validation with size, type, and dimension checks
|
|
36
|
-
- **Premium UI** -
|
|
36
|
+
- **Premium UI** - Glass or solid theme with dark/light mode support
|
|
37
37
|
- **Zero Backend Required** - Secure proxy architecture keeps API keys server-side
|
|
38
38
|
- **Angular Native** - Uses Dependency Injection, Signals, and RxJS
|
|
39
39
|
- **Type-Safe** - Full TypeScript support with comprehensive types
|
|
@@ -51,8 +51,8 @@ npm install @foisit/angular-wrapper
|
|
|
51
51
|
|
|
52
52
|
```json
|
|
53
53
|
{
|
|
54
|
-
"@angular/core": "^
|
|
55
|
-
"@angular/common": "^
|
|
54
|
+
"@angular/core": "^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0",
|
|
55
|
+
"@angular/common": "^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0"
|
|
56
56
|
}
|
|
57
57
|
```
|
|
58
58
|
|
|
@@ -471,9 +471,49 @@ interface AssistantConfig {
|
|
|
471
471
|
customHtml?: string;
|
|
472
472
|
position?: { bottom: string; right: string };
|
|
473
473
|
};
|
|
474
|
+
|
|
475
|
+
// Theme mode: 'glass' (default) or 'solid'
|
|
476
|
+
theme?: 'glass' | 'solid';
|
|
477
|
+
|
|
478
|
+
// Custom colors for solid theme (ignored in glass mode)
|
|
479
|
+
themeColors?: ThemeColors;
|
|
480
|
+
}
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### `ThemeColors`
|
|
484
|
+
|
|
485
|
+
Custom colors for solid theme mode:
|
|
486
|
+
|
|
487
|
+
```typescript
|
|
488
|
+
interface ThemeColors {
|
|
489
|
+
background?: string; // Background color (e.g., '#1e1e2e')
|
|
490
|
+
text?: string; // Primary text color (e.g., '#ffffff')
|
|
491
|
+
accent?: string; // Accent color for highlights (e.g., '#89b4fa' or a gradient)
|
|
492
|
+
userBubbleBg?: string; // User message bubble background
|
|
493
|
+
systemBubbleBg?: string; // System message bubble background
|
|
494
|
+
border?: string; // Border color
|
|
474
495
|
}
|
|
475
496
|
```
|
|
476
497
|
|
|
498
|
+
### Theme Customization Example
|
|
499
|
+
|
|
500
|
+
```typescript
|
|
501
|
+
AssistantModule.forRoot({
|
|
502
|
+
commands: [...],
|
|
503
|
+
// Use solid theme with custom colors
|
|
504
|
+
theme: 'solid',
|
|
505
|
+
themeColors: {
|
|
506
|
+
background: '#1e1e2e',
|
|
507
|
+
text: '#cdd6f4',
|
|
508
|
+
accent: '#89b4fa',
|
|
509
|
+
userBubbleBg: 'rgba(137, 180, 250, 0.2)',
|
|
510
|
+
systemBubbleBg: 'rgba(255, 255, 255, 0.05)',
|
|
511
|
+
},
|
|
512
|
+
})
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
> **Note**: Glass theme (default) uses glassmorphism with blur effects and adapts to light/dark mode via `prefers-color-scheme`. Solid theme ignores system preferences and uses configured colors.
|
|
516
|
+
|
|
477
517
|
---
|
|
478
518
|
|
|
479
519
|
## Advanced Usage
|
|
@@ -1136,7 +1136,7 @@ class OverlayManager {
|
|
|
1136
1136
|
async runCommand(options) {
|
|
1137
1137
|
if (!options || !options.commandId)
|
|
1138
1138
|
throw new Error('runCommand requires a commandId');
|
|
1139
|
-
const { commandId, params, openOverlay = true, showInvocation = true } = options;
|
|
1139
|
+
const { commandId, params, openOverlay = true, showInvocation = true, } = options;
|
|
1140
1140
|
if (openOverlay && !this.isOpen)
|
|
1141
1141
|
this.toggle();
|
|
1142
1142
|
const handler = this.commandHandlers.get(commandId);
|
|
@@ -1210,7 +1210,8 @@ class OverlayManager {
|
|
|
1210
1210
|
this.chatWindow = existing.querySelector('.foisit-chat');
|
|
1211
1211
|
this.messagesContainer = existing.querySelector('.foisit-messages');
|
|
1212
1212
|
this.input = existing.querySelector('input.foisit-input');
|
|
1213
|
-
if (this.config.floatingButton?.visible !== false &&
|
|
1213
|
+
if (this.config.floatingButton?.visible !== false &&
|
|
1214
|
+
!existing.querySelector('.foisit-floating-btn')) {
|
|
1214
1215
|
this.renderFloatingButton();
|
|
1215
1216
|
}
|
|
1216
1217
|
if (!this.chatWindow) {
|
|
@@ -1376,7 +1377,8 @@ class OverlayManager {
|
|
|
1376
1377
|
else {
|
|
1377
1378
|
msg.textContent = text;
|
|
1378
1379
|
}
|
|
1379
|
-
msg.className =
|
|
1380
|
+
msg.className =
|
|
1381
|
+
type === 'user' ? 'foisit-bubble user' : 'foisit-bubble system';
|
|
1380
1382
|
// Entrance animation: fade + slight upward motion. Duration scales so
|
|
1381
1383
|
// short messages animate a bit slower, long messages appear faster.
|
|
1382
1384
|
const length = (text || '').length || 0;
|
|
@@ -1411,7 +1413,9 @@ class OverlayManager {
|
|
|
1411
1413
|
return;
|
|
1412
1414
|
}
|
|
1413
1415
|
// Otherwise fall back to value or label string
|
|
1414
|
-
const value =
|
|
1416
|
+
const value = opt && typeof opt.value === 'string' && opt.value.trim()
|
|
1417
|
+
? opt.value
|
|
1418
|
+
: opt.label;
|
|
1415
1419
|
if (this.onSubmit)
|
|
1416
1420
|
this.onSubmit(value);
|
|
1417
1421
|
};
|
|
@@ -1437,7 +1441,8 @@ class OverlayManager {
|
|
|
1437
1441
|
const createLabel = (text, required) => {
|
|
1438
1442
|
const label = document.createElement('div');
|
|
1439
1443
|
label.className = 'foisit-form-label';
|
|
1440
|
-
label.innerHTML =
|
|
1444
|
+
label.innerHTML =
|
|
1445
|
+
text + (required ? ' <span class="foisit-req-star">*</span>' : '');
|
|
1441
1446
|
return label;
|
|
1442
1447
|
};
|
|
1443
1448
|
const createError = () => {
|
|
@@ -1530,7 +1535,7 @@ class OverlayManager {
|
|
|
1530
1535
|
}
|
|
1531
1536
|
const maxSize = ffield.maxSizeBytes ?? Infinity;
|
|
1532
1537
|
const total = files.reduce((s, f) => s + f.size, 0);
|
|
1533
|
-
if (files.some(f => f.size > maxSize)) {
|
|
1538
|
+
if (files.some((f) => f.size > maxSize)) {
|
|
1534
1539
|
errEl.textContent = `One or more files exceed the maximum size of ${Math.round(maxSize / 1024)} KB.`;
|
|
1535
1540
|
errEl.style.display = 'block';
|
|
1536
1541
|
return;
|
|
@@ -1547,7 +1552,10 @@ class OverlayManager {
|
|
|
1547
1552
|
const ok = files.every((file) => {
|
|
1548
1553
|
if (!file.type)
|
|
1549
1554
|
return true; // can't tell
|
|
1550
|
-
return accepts.some(a => a.startsWith('.')
|
|
1555
|
+
return accepts.some((a) => a.startsWith('.')
|
|
1556
|
+
? file.name.toLowerCase().endsWith(a.toLowerCase())
|
|
1557
|
+
: file.type === a ||
|
|
1558
|
+
file.type.startsWith(a.split('/')[0] + '/'));
|
|
1551
1559
|
});
|
|
1552
1560
|
if (!ok) {
|
|
1553
1561
|
errEl.textContent = 'One or more files have an unsupported type.';
|
|
@@ -1617,11 +1625,11 @@ class OverlayManager {
|
|
|
1617
1625
|
const data = {};
|
|
1618
1626
|
let hasError = false;
|
|
1619
1627
|
// Clear previous errors
|
|
1620
|
-
form.querySelectorAll('.foisit-form-error').forEach(el => {
|
|
1628
|
+
form.querySelectorAll('.foisit-form-error').forEach((el) => {
|
|
1621
1629
|
el.style.display = 'none';
|
|
1622
1630
|
el.textContent = '';
|
|
1623
1631
|
});
|
|
1624
|
-
form.querySelectorAll('.foisit-form-input').forEach(el => {
|
|
1632
|
+
form.querySelectorAll('.foisit-form-input').forEach((el) => {
|
|
1625
1633
|
el.classList.remove('foisit-error-border');
|
|
1626
1634
|
});
|
|
1627
1635
|
for (const c of controls) {
|
|
@@ -1907,7 +1915,10 @@ class OverlayManager {
|
|
|
1907
1915
|
const url = URL.createObjectURL(file);
|
|
1908
1916
|
const img = new Image();
|
|
1909
1917
|
img.onload = () => {
|
|
1910
|
-
const dims = {
|
|
1918
|
+
const dims = {
|
|
1919
|
+
width: img.naturalWidth || img.width,
|
|
1920
|
+
height: img.naturalHeight || img.height,
|
|
1921
|
+
};
|
|
1911
1922
|
URL.revokeObjectURL(url);
|
|
1912
1923
|
resolve(dims);
|
|
1913
1924
|
};
|
|
@@ -1926,7 +1937,9 @@ class OverlayManager {
|
|
|
1926
1937
|
return new Promise((resolve) => {
|
|
1927
1938
|
try {
|
|
1928
1939
|
const url = URL.createObjectURL(file);
|
|
1929
|
-
const el = file.type.startsWith('audio')
|
|
1940
|
+
const el = file.type.startsWith('audio')
|
|
1941
|
+
? document.createElement('audio')
|
|
1942
|
+
: document.createElement('video');
|
|
1930
1943
|
let settled = false;
|
|
1931
1944
|
const timeout = setTimeout(() => {
|
|
1932
1945
|
if (!settled) {
|
|
@@ -1966,57 +1979,105 @@ class OverlayManager {
|
|
|
1966
1979
|
return;
|
|
1967
1980
|
const style = document.createElement('style');
|
|
1968
1981
|
style.id = 'foisit-overlay-styles';
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
/* Bubbles */
|
|
1983
|
-
--foisit-bubble-user-bg: rgba(0, 0, 0, 0.04);
|
|
1984
|
-
--foisit-bubble-user-text: #333;
|
|
1985
|
-
|
|
1986
|
-
--foisit-bubble-sys-bg: rgba(255, 255, 255, 0.45);
|
|
1987
|
-
--foisit-bubble-sys-text: #333;
|
|
1988
|
-
|
|
1989
|
-
/* Form Colors */
|
|
1990
|
-
--foisit-req-star: #ef4444; /* Red asterisk */
|
|
1991
|
-
--foisit-error-text: #dc2626;
|
|
1992
|
-
--foisit-error-border: #fca5a5;
|
|
1993
|
-
}
|
|
1994
|
-
|
|
1995
|
-
@media (prefers-color-scheme: dark) {
|
|
1982
|
+
// Determine theme mode (default: glass)
|
|
1983
|
+
const theme = this.config.theme || 'glass';
|
|
1984
|
+
const colors = this.config.themeColors || {};
|
|
1985
|
+
if (theme === 'solid') {
|
|
1986
|
+
// SOLID THEME: Opaque backgrounds with customizable colors
|
|
1987
|
+
// Defaults: dark navy bg, white text, purple-blue gradient accent
|
|
1988
|
+
const bg = colors.background || '#1a1a2e';
|
|
1989
|
+
const text = colors.text || '#ffffff';
|
|
1990
|
+
const accent = colors.accent || 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
|
|
1991
|
+
const userBubbleBg = colors.userBubbleBg || 'rgba(102, 126, 234, 0.2)';
|
|
1992
|
+
const systemBubbleBg = colors.systemBubbleBg || 'rgba(255, 255, 255, 0.08)';
|
|
1993
|
+
const border = colors.border || 'rgba(255, 255, 255, 0.1)';
|
|
1994
|
+
style.textContent = `
|
|
1996
1995
|
:root {
|
|
1997
|
-
/*
|
|
1998
|
-
--foisit-bg:
|
|
1999
|
-
--foisit-border: 1px solid
|
|
2000
|
-
--foisit-shadow: 0 16px 48px rgba(0, 0, 0, 0.
|
|
2001
|
-
--foisit-text:
|
|
1996
|
+
/* SOLID THEME - Custom Colors */
|
|
1997
|
+
--foisit-bg: ${bg};
|
|
1998
|
+
--foisit-border: 1px solid ${border};
|
|
1999
|
+
--foisit-shadow: 0 16px 48px rgba(0, 0, 0, 0.4);
|
|
2000
|
+
--foisit-text: ${text};
|
|
2001
|
+
--foisit-accent: ${accent};
|
|
2002
|
+
--foisit-backdrop: none;
|
|
2002
2003
|
|
|
2003
2004
|
/* Input */
|
|
2004
|
-
--foisit-input-color:
|
|
2005
|
-
--foisit-input-placeholder:
|
|
2005
|
+
--foisit-input-color: ${text};
|
|
2006
|
+
--foisit-input-placeholder: ${text}99;
|
|
2006
2007
|
|
|
2007
2008
|
/* Bubbles */
|
|
2008
|
-
--foisit-bubble-user-bg:
|
|
2009
|
-
--foisit-bubble-user-text:
|
|
2009
|
+
--foisit-bubble-user-bg: ${userBubbleBg};
|
|
2010
|
+
--foisit-bubble-user-text: ${text};
|
|
2010
2011
|
|
|
2011
|
-
--foisit-bubble-sys-bg:
|
|
2012
|
-
--foisit-bubble-sys-text:
|
|
2012
|
+
--foisit-bubble-sys-bg: ${systemBubbleBg};
|
|
2013
|
+
--foisit-bubble-sys-text: ${text}ee;
|
|
2013
2014
|
|
|
2014
2015
|
/* Form Colors */
|
|
2015
2016
|
--foisit-req-star: #f87171;
|
|
2016
2017
|
--foisit-error-text: #fca5a5;
|
|
2017
2018
|
--foisit-error-border: #f87171;
|
|
2018
2019
|
}
|
|
2019
|
-
|
|
2020
|
+
`;
|
|
2021
|
+
}
|
|
2022
|
+
else {
|
|
2023
|
+
// GLASS THEME (default): Glassmorphism with blur effects
|
|
2024
|
+
style.textContent = `
|
|
2025
|
+
:root {
|
|
2026
|
+
/* LIGHT MODE (Default) - Smoother gradient */
|
|
2027
|
+
/* Changed: Softer, right-focused radial highlight to avoid a heavy white bottom */
|
|
2028
|
+
--foisit-bg: radial-gradient(ellipse at 75% 30%, rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0.03));
|
|
2029
|
+
--foisit-border: 1px solid rgba(255, 255, 255, 0.25);
|
|
2030
|
+
--foisit-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
|
|
2031
|
+
--foisit-text: #333;
|
|
2032
|
+
--foisit-backdrop: blur(20px);
|
|
2033
|
+
|
|
2034
|
+
/* Input */
|
|
2035
|
+
--foisit-input-color: #333;
|
|
2036
|
+
--foisit-input-placeholder: rgba(60, 60, 67, 0.6);
|
|
2037
|
+
|
|
2038
|
+
/* Bubbles */
|
|
2039
|
+
--foisit-bubble-user-bg: rgba(0, 0, 0, 0.04);
|
|
2040
|
+
--foisit-bubble-user-text: #333;
|
|
2041
|
+
|
|
2042
|
+
--foisit-bubble-sys-bg: rgba(255, 255, 255, 0.45);
|
|
2043
|
+
--foisit-bubble-sys-text: #333;
|
|
2044
|
+
|
|
2045
|
+
/* Form Colors */
|
|
2046
|
+
--foisit-req-star: #ef4444; /* Red asterisk */
|
|
2047
|
+
--foisit-error-text: #dc2626;
|
|
2048
|
+
--foisit-error-border: #fca5a5;
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
@media (prefers-color-scheme: dark) {
|
|
2052
|
+
:root {
|
|
2053
|
+
/* DARK MODE */
|
|
2054
|
+
--foisit-bg: linear-gradient(135deg, rgba(40, 40, 40, 0.65), rgba(40, 40, 40, 0.25));
|
|
2055
|
+
--foisit-border: 1px solid rgba(255, 255, 255, 0.1);
|
|
2056
|
+
--foisit-shadow: 0 16px 48px rgba(0, 0, 0, 0.5);
|
|
2057
|
+
--foisit-text: #fff;
|
|
2058
|
+
--foisit-backdrop: blur(20px);
|
|
2059
|
+
|
|
2060
|
+
/* Input */
|
|
2061
|
+
--foisit-input-color: white;
|
|
2062
|
+
--foisit-input-placeholder: rgba(235, 235, 245, 0.5);
|
|
2063
|
+
|
|
2064
|
+
/* Bubbles */
|
|
2065
|
+
--foisit-bubble-user-bg: rgba(255, 255, 255, 0.1);
|
|
2066
|
+
--foisit-bubble-user-text: white;
|
|
2067
|
+
|
|
2068
|
+
--foisit-bubble-sys-bg: rgba(255, 255, 255, 0.05);
|
|
2069
|
+
--foisit-bubble-sys-text: rgba(255, 255, 255, 0.9);
|
|
2070
|
+
|
|
2071
|
+
/* Form Colors */
|
|
2072
|
+
--foisit-req-star: #f87171;
|
|
2073
|
+
--foisit-error-text: #fca5a5;
|
|
2074
|
+
--foisit-error-border: #f87171;
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
`;
|
|
2078
|
+
}
|
|
2079
|
+
// Add common styles shared between both themes (animations, layouts, etc.)
|
|
2080
|
+
style.textContent += `
|
|
2020
2081
|
|
|
2021
2082
|
@keyframes foisitPulse {
|
|
2022
2083
|
0%, 100% { transform: scale(0.8); opacity: 0.5; }
|
|
@@ -2064,8 +2125,8 @@ class OverlayManager {
|
|
|
2064
2125
|
border: var(--foisit-border);
|
|
2065
2126
|
box-shadow: var(--foisit-shadow);
|
|
2066
2127
|
|
|
2067
|
-
backdrop-filter:
|
|
2068
|
-
-webkit-backdrop-filter:
|
|
2128
|
+
backdrop-filter: var(--foisit-backdrop);
|
|
2129
|
+
-webkit-backdrop-filter: var(--foisit-backdrop);
|
|
2069
2130
|
|
|
2070
2131
|
border-radius: 18px;
|
|
2071
2132
|
display: none;
|
|
@@ -2274,8 +2335,8 @@ class OverlayManager {
|
|
|
2274
2335
|
border: 1px solid rgba(255,255,255,0.2);
|
|
2275
2336
|
background: var(--foisit-bg);
|
|
2276
2337
|
color: var(--foisit-text);
|
|
2277
|
-
backdrop-filter:
|
|
2278
|
-
-webkit-backdrop-filter:
|
|
2338
|
+
backdrop-filter: var(--foisit-backdrop);
|
|
2339
|
+
-webkit-backdrop-filter: var(--foisit-backdrop);
|
|
2279
2340
|
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
2280
2341
|
cursor: pointer;
|
|
2281
2342
|
pointer-events: auto;
|
|
@@ -2407,6 +2468,8 @@ class AssistantService {
|
|
|
2407
2468
|
floatingButton: this.config.floatingButton,
|
|
2408
2469
|
inputPlaceholder: this.config.inputPlaceholder,
|
|
2409
2470
|
enableGestureActivation: this.config.enableGestureActivation,
|
|
2471
|
+
theme: this.config.theme,
|
|
2472
|
+
themeColors: this.config.themeColors,
|
|
2410
2473
|
});
|
|
2411
2474
|
// Let the overlay delegate command execution to our CommandHandler when
|
|
2412
2475
|
// a programmatic handler isn't registered on the overlay.
|
|
@@ -2572,7 +2635,8 @@ class AssistantService {
|
|
|
2572
2635
|
});
|
|
2573
2636
|
return;
|
|
2574
2637
|
}
|
|
2575
|
-
if ((response.type === 'ambiguous' || response.type === 'confirm') &&
|
|
2638
|
+
if ((response.type === 'ambiguous' || response.type === 'confirm') &&
|
|
2639
|
+
response.options) {
|
|
2576
2640
|
if (response.message) {
|
|
2577
2641
|
this.overlayManager.addMessage(response.message, 'system');
|
|
2578
2642
|
}
|