@courseecho/ai-core-sdk 1.0.20 → 1.0.22
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/icons.d.ts +45 -0
- package/dist/icons.js +81 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/widget-css.d.ts +14 -0
- package/dist/widget-css.js +398 -0
- package/package.json +13 -4
package/dist/icons.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared SVG icons for AI Widget — single source of truth.
|
|
3
|
+
*
|
|
4
|
+
* Usage in React (JSX):
|
|
5
|
+
* <div className="aiwg-avatar" dangerouslySetInnerHTML={{ __html: ICONS.BOT }} />
|
|
6
|
+
*
|
|
7
|
+
* Usage in jQuery / plain HTML template literals:
|
|
8
|
+
* `<div class="aiwg-avatar">${ICONS.BOT}</div>`
|
|
9
|
+
*/
|
|
10
|
+
export declare const ICONS: {
|
|
11
|
+
readonly BOT: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" fill=\"currentColor\" aria-hidden=\"true\">\n <!-- head -->\n <rect x=\"5\" y=\"8\" width=\"14\" height=\"11\" rx=\"2.5\"/>\n <!-- eyes -->\n <rect x=\"8.5\" y=\"11.5\" width=\"2\" height=\"2.5\" rx=\"1\" fill=\"white\"/>\n <rect x=\"13.5\" y=\"11.5\" width=\"2\" height=\"2.5\" rx=\"1\" fill=\"white\"/>\n <!-- mouth -->\n <rect x=\"9\" y=\"15\" width=\"6\" height=\"1.2\" rx=\"0.6\" fill=\"white\" opacity=\"0.85\"/>\n <!-- antenna stem -->\n <rect x=\"11.25\" y=\"4.5\" width=\"1.5\" height=\"3.5\" rx=\"0.75\"/>\n <!-- antenna ball -->\n <circle cx=\"12\" cy=\"4\" r=\"1.5\"/>\n <!-- left arm -->\n <rect x=\"2.5\" y=\"11\" width=\"2.5\" height=\"5\" rx=\"1.25\"/>\n <!-- right arm -->\n <rect x=\"19\" y=\"11\" width=\"2.5\" height=\"5\" rx=\"1.25\"/>\n</svg>";
|
|
12
|
+
readonly USER: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"8\" r=\"4\"/>\n <path d=\"M4 20c0-4.418 3.582-7 8-7s8 2.582 8 7\" fill=\"currentColor\"/>\n</svg>";
|
|
13
|
+
readonly WAVE: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"32\" height=\"32\" fill=\"currentColor\" aria-hidden=\"true\">\n <!-- palm -->\n <path d=\"M11 4.5a1 1 0 0 1 2 0v6.5a1 1 0 0 0 2 0V6a1 1 0 0 1 2 0v5a1 1 0 0 0 2 0V9a1 1 0 0 1 2 0v5c0 4-3 7-7 7H12c-3.86 0-7-3.14-7-7V9.97c0-.52.42-.97.93-1 .56-.02 1.07.44 1.07 1V12a1 1 0 0 0 2 0V4.5z\"/>\n</svg>";
|
|
14
|
+
readonly SEND: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"20\" height=\"20\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/>\n <polygon points=\"22 2 15 22 11 13 2 9 22 2\"/>\n</svg>";
|
|
15
|
+
readonly CLOSE: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n</svg>";
|
|
16
|
+
};
|
|
17
|
+
/** 🎓 */ export declare const EMOJI_GRADUATION = "\uD83C\uDF93";
|
|
18
|
+
/** 📋 */ export declare const EMOJI_CLIPBOARD = "\uD83D\uDCCB";
|
|
19
|
+
/** 🏆 */ export declare const EMOJI_TROPHY = "\uD83C\uDFC6";
|
|
20
|
+
/** ⬇️ */ export declare const EMOJI_DOWN_ARROW = "\u2B07\uFE0F";
|
|
21
|
+
/** 📈 */ export declare const EMOJI_CHART_UP = "\uD83D\uDCC8";
|
|
22
|
+
/** 🙋 */ export declare const EMOJI_HAND_RAISE = "\uD83D\uDE4B";
|
|
23
|
+
/** 💡 */ export declare const EMOJI_BULB = "\uD83D\uDCA1";
|
|
24
|
+
/** 📝 */ export declare const EMOJI_MEMO = "\uD83D\uDCDD";
|
|
25
|
+
/** ➡️ */ export declare const EMOJI_RIGHT_ARROW = "\u27A1\uFE0F";
|
|
26
|
+
/** 🆘 */ export declare const EMOJI_SOS = "\uD83C\uDD98";
|
|
27
|
+
/** 🏋️ */ export declare const EMOJI_LIFT = "\uD83C\uDFCB\uFE0F";
|
|
28
|
+
/** 🔍 */ export declare const EMOJI_SEARCH = "\uD83D\uDD0D";
|
|
29
|
+
/** 📚 */ export declare const EMOJI_BOOKS = "\uD83D\uDCDA";
|
|
30
|
+
/** 📦 */ export declare const EMOJI_PACKAGE = "\uD83D\uDCE6";
|
|
31
|
+
/** 💳 */ export declare const EMOJI_CARD = "\uD83D\uDCB3";
|
|
32
|
+
/** 🛒 */ export declare const EMOJI_CART = "\uD83D\uDED2";
|
|
33
|
+
/** 🆓 */ export declare const EMOJI_FREE = "\uD83C\uDD93";
|
|
34
|
+
/** 💰 */ export declare const EMOJI_MONEY = "\uD83D\uDCB0";
|
|
35
|
+
/** 🔒 */ export declare const EMOJI_LOCK = "\uD83D\uDD12";
|
|
36
|
+
/** 🎟️ */ export declare const EMOJI_TICKET = "\uD83C\uDF9F\uFE0F";
|
|
37
|
+
/** 👤 */ export declare const EMOJI_USER = "\uD83D\uDC64";
|
|
38
|
+
/** 💻 */ export declare const EMOJI_LAPTOP = "\uD83D\uDCBB";
|
|
39
|
+
/** 🐛 */ export declare const EMOJI_BUG = "\uD83D\uDC1B";
|
|
40
|
+
/** 🔧 */ export declare const EMOJI_WRENCH = "\uD83D\uDD27";
|
|
41
|
+
/** 💬 */ export declare const EMOJI_CHAT = "\uD83D\uDCAC";
|
|
42
|
+
/** 🚀 */ export declare const EMOJI_ROCKET = "\uD83D\uDE80";
|
|
43
|
+
/** 📖 */ export declare const EMOJI_BOOK = "\uD83D\uDCD6";
|
|
44
|
+
/** 🛠️ */ export declare const EMOJI_TOOLS = "\uD83D\uDEE0\uFE0F";
|
|
45
|
+
/** ⚠️ */ export declare const EMOJI_WARNING = "\u26A0\uFE0F";
|
package/dist/icons.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared SVG icons for AI Widget — single source of truth.
|
|
3
|
+
*
|
|
4
|
+
* Usage in React (JSX):
|
|
5
|
+
* <div className="aiwg-avatar" dangerouslySetInnerHTML={{ __html: ICONS.BOT }} />
|
|
6
|
+
*
|
|
7
|
+
* Usage in jQuery / plain HTML template literals:
|
|
8
|
+
* `<div class="aiwg-avatar">${ICONS.BOT}</div>`
|
|
9
|
+
*/
|
|
10
|
+
/** Bot / AI assistant avatar */
|
|
11
|
+
const BOT = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="22" height="22" fill="currentColor" aria-hidden="true">
|
|
12
|
+
<!-- head -->
|
|
13
|
+
<rect x="5" y="8" width="14" height="11" rx="2.5"/>
|
|
14
|
+
<!-- eyes -->
|
|
15
|
+
<rect x="8.5" y="11.5" width="2" height="2.5" rx="1" fill="white"/>
|
|
16
|
+
<rect x="13.5" y="11.5" width="2" height="2.5" rx="1" fill="white"/>
|
|
17
|
+
<!-- mouth -->
|
|
18
|
+
<rect x="9" y="15" width="6" height="1.2" rx="0.6" fill="white" opacity="0.85"/>
|
|
19
|
+
<!-- antenna stem -->
|
|
20
|
+
<rect x="11.25" y="4.5" width="1.5" height="3.5" rx="0.75"/>
|
|
21
|
+
<!-- antenna ball -->
|
|
22
|
+
<circle cx="12" cy="4" r="1.5"/>
|
|
23
|
+
<!-- left arm -->
|
|
24
|
+
<rect x="2.5" y="11" width="2.5" height="5" rx="1.25"/>
|
|
25
|
+
<!-- right arm -->
|
|
26
|
+
<rect x="19" y="11" width="2.5" height="5" rx="1.25"/>
|
|
27
|
+
</svg>`;
|
|
28
|
+
/** Human / user avatar */
|
|
29
|
+
const USER = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="22" height="22" fill="currentColor" aria-hidden="true">
|
|
30
|
+
<circle cx="12" cy="8" r="4"/>
|
|
31
|
+
<path d="M4 20c0-4.418 3.582-7 8-7s8 2.582 8 7" fill="currentColor"/>
|
|
32
|
+
</svg>`;
|
|
33
|
+
/** Waving hand — welcome screen */
|
|
34
|
+
const WAVE = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="32" height="32" fill="currentColor" aria-hidden="true">
|
|
35
|
+
<!-- palm -->
|
|
36
|
+
<path d="M11 4.5a1 1 0 0 1 2 0v6.5a1 1 0 0 0 2 0V6a1 1 0 0 1 2 0v5a1 1 0 0 0 2 0V9a1 1 0 0 1 2 0v5c0 4-3 7-7 7H12c-3.86 0-7-3.14-7-7V9.97c0-.52.42-.97.93-1 .56-.02 1.07.44 1.07 1V12a1 1 0 0 0 2 0V4.5z"/>
|
|
37
|
+
</svg>`;
|
|
38
|
+
/** Paper-plane send button */
|
|
39
|
+
const SEND = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
40
|
+
<line x1="22" y1="2" x2="11" y2="13"/>
|
|
41
|
+
<polygon points="22 2 15 22 11 13 2 9 22 2"/>
|
|
42
|
+
</svg>`;
|
|
43
|
+
/** Close / minimise (×) */
|
|
44
|
+
const CLOSE = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true">
|
|
45
|
+
<line x1="18" y1="6" x2="6" y2="18"/>
|
|
46
|
+
<line x1="6" y1="6" x2="18" y2="18"/>
|
|
47
|
+
</svg>`;
|
|
48
|
+
export const ICONS = { BOT, USER, WAVE, SEND, CLOSE };
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Chip / suggestion template emoji strings
|
|
51
|
+
// Using Unicode escapes so this file is encoding-safe regardless of editor.
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
/** 🎓 */ export const EMOJI_GRADUATION = '\u{1F393}';
|
|
54
|
+
/** 📋 */ export const EMOJI_CLIPBOARD = '\u{1F4CB}';
|
|
55
|
+
/** 🏆 */ export const EMOJI_TROPHY = '\u{1F3C6}';
|
|
56
|
+
/** ⬇️ */ export const EMOJI_DOWN_ARROW = '\u2B07\uFE0F';
|
|
57
|
+
/** 📈 */ export const EMOJI_CHART_UP = '\u{1F4C8}';
|
|
58
|
+
/** 🙋 */ export const EMOJI_HAND_RAISE = '\u{1F64B}';
|
|
59
|
+
/** 💡 */ export const EMOJI_BULB = '\u{1F4A1}';
|
|
60
|
+
/** 📝 */ export const EMOJI_MEMO = '\u{1F4DD}';
|
|
61
|
+
/** ➡️ */ export const EMOJI_RIGHT_ARROW = '\u27A1\uFE0F';
|
|
62
|
+
/** 🆘 */ export const EMOJI_SOS = '\u{1F198}';
|
|
63
|
+
/** 🏋️ */ export const EMOJI_LIFT = '\u{1F3CB}\uFE0F';
|
|
64
|
+
/** 🔍 */ export const EMOJI_SEARCH = '\u{1F50D}';
|
|
65
|
+
/** 📚 */ export const EMOJI_BOOKS = '\u{1F4DA}';
|
|
66
|
+
/** 📦 */ export const EMOJI_PACKAGE = '\u{1F4E6}';
|
|
67
|
+
/** 💳 */ export const EMOJI_CARD = '\u{1F4B3}';
|
|
68
|
+
/** 🛒 */ export const EMOJI_CART = '\u{1F6D2}';
|
|
69
|
+
/** 🆓 */ export const EMOJI_FREE = '\u{1F193}';
|
|
70
|
+
/** 💰 */ export const EMOJI_MONEY = '\u{1F4B0}';
|
|
71
|
+
/** 🔒 */ export const EMOJI_LOCK = '\u{1F512}';
|
|
72
|
+
/** 🎟️ */ export const EMOJI_TICKET = '\u{1F39F}\uFE0F';
|
|
73
|
+
/** 👤 */ export const EMOJI_USER = '\u{1F464}';
|
|
74
|
+
/** 💻 */ export const EMOJI_LAPTOP = '\u{1F4BB}';
|
|
75
|
+
/** 🐛 */ export const EMOJI_BUG = '\u{1F41B}';
|
|
76
|
+
/** 🔧 */ export const EMOJI_WRENCH = '\u{1F527}';
|
|
77
|
+
/** 💬 */ export const EMOJI_CHAT = '\u{1F4AC}';
|
|
78
|
+
/** 🚀 */ export const EMOJI_ROCKET = '\u{1F680}';
|
|
79
|
+
/** 📖 */ export const EMOJI_BOOK = '\u{1F4D6}';
|
|
80
|
+
/** 🛠️ */ export const EMOJI_TOOLS = '\u{1F6E0}\uFE0F';
|
|
81
|
+
/** ⚠️ */ export const EMOJI_WARNING = '\u26A0\uFE0F';
|
package/dist/index.d.ts
CHANGED
|
@@ -8,3 +8,5 @@ export { ChatStateManager, ContextStateManager, ChartManager, SuggestionManager,
|
|
|
8
8
|
export { PageContentScanner, AiSuggestionGenerator, type PageContent, } from './auto-suggestion-generator';
|
|
9
9
|
export { AiWidgetSDK } from './sdk';
|
|
10
10
|
export type { StateListener } from './state-manager';
|
|
11
|
+
export { WIDGET_CSS } from './widget-css';
|
|
12
|
+
export { ICONS, EMOJI_GRADUATION, EMOJI_CLIPBOARD, EMOJI_TROPHY, EMOJI_DOWN_ARROW, EMOJI_CHART_UP, EMOJI_HAND_RAISE, EMOJI_BULB, EMOJI_MEMO, EMOJI_RIGHT_ARROW, EMOJI_SOS, EMOJI_LIFT, EMOJI_SEARCH, EMOJI_BOOKS, EMOJI_PACKAGE, EMOJI_CARD, EMOJI_CART, EMOJI_FREE, EMOJI_MONEY, EMOJI_LOCK, EMOJI_TICKET, EMOJI_USER, EMOJI_LAPTOP, EMOJI_BUG, EMOJI_WRENCH, EMOJI_CHAT, EMOJI_ROCKET, EMOJI_BOOK, EMOJI_TOOLS, EMOJI_WARNING, } from './icons';
|
package/dist/index.js
CHANGED
|
@@ -11,3 +11,7 @@ export { ChatStateManager, ContextStateManager, ChartManager, SuggestionManager,
|
|
|
11
11
|
export { PageContentScanner, AiSuggestionGenerator, } from './auto-suggestion-generator';
|
|
12
12
|
// Main SDK
|
|
13
13
|
export { AiWidgetSDK } from './sdk';
|
|
14
|
+
// Canonical widget CSS (used by all framework packages)
|
|
15
|
+
export { WIDGET_CSS } from './widget-css';
|
|
16
|
+
// Shared SVG icons + encoding-safe emoji strings
|
|
17
|
+
export { ICONS, EMOJI_GRADUATION, EMOJI_CLIPBOARD, EMOJI_TROPHY, EMOJI_DOWN_ARROW, EMOJI_CHART_UP, EMOJI_HAND_RAISE, EMOJI_BULB, EMOJI_MEMO, EMOJI_RIGHT_ARROW, EMOJI_SOS, EMOJI_LIFT, EMOJI_SEARCH, EMOJI_BOOKS, EMOJI_PACKAGE, EMOJI_CARD, EMOJI_CART, EMOJI_FREE, EMOJI_MONEY, EMOJI_LOCK, EMOJI_TICKET, EMOJI_USER, EMOJI_LAPTOP, EMOJI_BUG, EMOJI_WRENCH, EMOJI_CHAT, EMOJI_ROCKET, EMOJI_BOOK, EMOJI_TOOLS, EMOJI_WARNING, } from './icons';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical widget CSS — single source of truth for all framework packages.
|
|
3
|
+
*
|
|
4
|
+
* Isolation strategy:
|
|
5
|
+
* - React : injected into a Shadow DOM via ShadowWrapper (createPortal).
|
|
6
|
+
* :host { all: initial } resets inherited properties at the boundary.
|
|
7
|
+
* - jQuery : injected into attachShadow() root. Same :host reset applies.
|
|
8
|
+
* - Angular: ViewEncapsulation.ShadowDom — Angular handles the shadow root.
|
|
9
|
+
*
|
|
10
|
+
* NOTE: `@import` for Google Fonts does NOT work inside shadow-DOM <style> tags.
|
|
11
|
+
* Each package is responsible for loading the Inter font into document <head>
|
|
12
|
+
* separately (React: ensureFont(), jQuery: injectFontLink()).
|
|
13
|
+
*/
|
|
14
|
+
export declare const WIDGET_CSS = "\n/* \u2500\u2500 Shadow-DOM host isolation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n:host {\n all: initial !important;\n display: block !important;\n}\n\n/* Scoped box-model reset \u2014 only touches widget elements, not all of shadow DOM */\n.aiwg-root *, .aiwg-root *::before, .aiwg-root *::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\n\n/* \u2500\u2500 Container \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-root {\n --aiwg-primary: #6366f1;\n --aiwg-primary-dark: #8b5cf6;\n --aiwg-fg-color: #ffffff;\n --aiwg-bg-color: #ffffff;\n --aiwg-chat-bg: #f1f5f9;\n\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n font-size: 14px;\n line-height: 1.5;\n color: #1a1a2e;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n border-radius: 16px;\n background: var(--aiwg-bg-color);\n position: fixed !important;\n bottom: 24px;\n right: 24px;\n width: 380px !important;\n max-width: calc(100vw - 48px) !important;\n z-index: 99999;\n transition: height 0.25s ease;\n box-shadow: 0 24px 64px rgba(0,0,0,0.18);\n}\n\n/* \u2500\u2500 Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-header {\n background: linear-gradient(135deg, var(--aiwg-primary) 0%, var(--aiwg-primary-dark) 100%);\n padding: 16px 20px;\n display: flex;\n align-items: center;\n gap: 12px;\n flex-shrink: 0;\n position: relative;\n overflow: hidden;\n}\n.aiwg-header::before {\n content: '';\n position: absolute;\n width: 140px; height: 140px;\n background: rgba(255,255,255,0.08);\n border-radius: 50%;\n top: -50px; right: -30px;\n}\n.aiwg-avatar {\n width: 40px; height: 40px;\n background: rgba(255,255,255,0.25);\n border-radius: 50%;\n display: flex; align-items: center; justify-content: center;\n font-size: 18px;\n flex-shrink: 0;\n border: 2px solid rgba(255,255,255,0.4);\n color: #fff;\n}\n.aiwg-avatar svg { display: block; width: 22px; height: 22px; }\n.aiwg-header-info { flex: 1; }\n.aiwg-title { color: #fff; font-weight: 600; font-size: 15px; }\n.aiwg-subtitle {\n color: rgba(255,255,255,0.75);\n font-size: 12px;\n display: flex; align-items: center; gap: 5px;\n}\n.aiwg-online-dot {\n width: 7px; height: 7px;\n background: #4ade80;\n border-radius: 50%;\n display: inline-block;\n animation: aiwg-pulse 2s ease-in-out infinite;\n}\n@keyframes aiwg-pulse {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.6; transform: scale(0.85); }\n}\n.aiwg-minimize-btn {\n width: 36px; height: 36px;\n background: rgba(255,255,255,0.18);\n border: 1.5px solid rgba(255,255,255,0.3);\n border-radius: 50%;\n color: #fff; cursor: pointer;\n display: flex; align-items: center; justify-content: center;\n font-size: 18px; line-height: 1;\n transition: background 0.15s, transform 0.15s;\n flex-shrink: 0;\n position: relative; z-index: 2;\n}\n.aiwg-minimize-btn * { cursor: pointer !important; pointer-events: none; }\n.aiwg-minimize-btn svg { display: block; width: 16px; height: 16px; }\n.aiwg-minimize-btn:hover { background: rgba(255,255,255,0.32); transform: scale(1.08); }\n\n/* \u2500\u2500 Messages area \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-messages {\n flex: 1;\n overflow-y: auto;\n padding: 20px 16px 8px;\n scroll-behavior: smooth;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.aiwg-messages::-webkit-scrollbar { width: 4px; }\n.aiwg-messages::-webkit-scrollbar-thumb { background: #e2e8f0; border-radius: 99px; }\n\n/* \u2500\u2500 Welcome card \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-welcome {\n text-align: center;\n padding: 24px 16px;\n display: flex; flex-direction: column; align-items: center; gap: 8px;\n}\n.aiwg-welcome-icon {\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 4px;\n color: var(--aiwg-primary);\n}\n.aiwg-welcome-icon svg { width: 36px; height: 36px; }\n.aiwg-welcome h4 { font-size: 15px; font-weight: 600; color: #1a1a2e; }\n.aiwg-welcome p { font-size: 13px; color: #64748b; }\n\n/* \u2500\u2500 Message bubbles \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-msg {\n display: flex;\n gap: 8px;\n align-items: flex-end;\n animation: aiwg-fade-up 0.2s ease both;\n}\n@keyframes aiwg-fade-up {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.aiwg-msg--user { flex-direction: row-reverse; }\n.aiwg-msg-avatar {\n width: 28px; height: 28px; border-radius: 50%;\n display: flex; align-items: center; justify-content: center;\n font-size: 13px; flex-shrink: 0;\n margin-bottom: 2px;\n}\n.aiwg-msg-avatar svg { display: block; width: 16px; height: 16px; }\n.aiwg-msg--bot .aiwg-msg-avatar { background: linear-gradient(135deg, var(--aiwg-primary), var(--aiwg-primary-dark)); color: var(--aiwg-fg-color); }\n.aiwg-msg--user .aiwg-msg-avatar { background: #e2e8f0; color: #64748b; }\n.aiwg-msg-body { max-width: 78%; }\n.aiwg-msg-bubble {\n padding: 10px 14px;\n border-radius: 18px;\n font-size: 14px;\n line-height: 1.55;\n word-break: break-word;\n}\n.aiwg-msg--bot .aiwg-msg-bubble { background: var(--aiwg-chat-bg); color: #1a1a2e; border-bottom-left-radius: 4px; }\n.aiwg-msg--user .aiwg-msg-bubble { background: linear-gradient(135deg, var(--aiwg-primary), var(--aiwg-primary-dark)); color: var(--aiwg-fg-color); border-bottom-right-radius: 4px; }\n.aiwg-msg-time { font-size: 10px; color: #94a3b8; margin-top: 4px; text-align: right; padding: 0 4px; }\n.aiwg-msg--bot .aiwg-msg-time { text-align: left; }\n\n/* \u2500\u2500 Typing indicator \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-typing { padding: 10px 14px; }\n.aiwg-typing-dots { display: flex; gap: 4px; align-items: center; }\n.aiwg-typing-dots span {\n width: 7px; height: 7px;\n background: #94a3b8; border-radius: 50%;\n animation: aiwg-bounce 1.2s ease-in-out infinite;\n}\n.aiwg-typing-dots span:nth-child(2) { animation-delay: 0.2s; }\n.aiwg-typing-dots span:nth-child(3) { animation-delay: 0.4s; }\n@keyframes aiwg-bounce {\n 0%, 80%, 100% { transform: translateY(0); }\n 40% { transform: translateY(-6px); }\n}\n\n/* \u2500\u2500 Quick-reply chips \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-chip-row {\n padding: 6px 16px 2px;\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n flex-shrink: 0;\n}\n.aiwg-chip {\n background: #f1f5f9;\n border: 1px solid #e2e8f0;\n border-radius: 99px;\n padding: 4px 12px;\n font-size: 12px;\n color: #4f46e5;\n cursor: pointer;\n transition: all 0.15s;\n white-space: nowrap;\n font-weight: 500;\n}\n.aiwg-chip:hover { background: #ede9fe; border-color: #a5b4fc; }\n.aiwg-chip-shimmer {\n background: linear-gradient(90deg, #e2e8f0 25%, #f1f5f9 50%, #e2e8f0 75%);\n background-size: 200% 100%;\n border: 1px solid transparent;\n border-radius: 99px;\n padding: 4px 34px;\n animation: aiwg-shimmer 1.4s ease-in-out infinite;\n cursor: default;\n pointer-events: none;\n}\n.aiwg-chip-ai-badge {\n display: inline-block;\n font-size: 9px; font-weight: 700;\n background: linear-gradient(135deg, var(--aiwg-primary), var(--aiwg-primary-dark));\n color: #fff;\n padding: 1px 5px;\n border-radius: 99px;\n margin-left: 5px;\n vertical-align: middle;\n letter-spacing: 0.04em;\n}\n@keyframes aiwg-shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n\n/* \u2500\u2500 Input area \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-input-area {\n padding: 12px 16px 16px;\n flex-shrink: 0;\n background: #fff;\n border-top: 1px solid #f1f5f9;\n position: relative;\n}\n.aiwg-input-row { display: flex; gap: 8px; align-items: flex-end; }\n.aiwg-input {\n flex: 1;\n border: 1.5px solid #e2e8f0;\n border-radius: 12px;\n padding: 10px 14px;\n font-size: 14px;\n font-family: inherit;\n outline: none;\n resize: none;\n background: #f8fafc;\n color: #1a1a2e;\n transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;\n line-height: 1.4;\n max-height: 120px;\n overflow-y: auto;\n scrollbar-width: none;\n -ms-overflow-style: none;\n}\n.aiwg-input::-webkit-scrollbar { display: none; }\n.aiwg-input::placeholder { color: #94a3b8; }\n.aiwg-input:focus {\n border-color: var(--aiwg-primary-dark);\n background: #fff;\n box-shadow: 0 0 0 3px rgba(139,92,246,0.1);\n}\n.aiwg-send-btn {\n width: 42px; height: 42px;\n background: linear-gradient(135deg, var(--aiwg-primary), var(--aiwg-primary-dark));\n border: none; border-radius: 12px;\n color: var(--aiwg-fg-color); cursor: pointer;\n display: flex; align-items: center; justify-content: center;\n flex-shrink: 0;\n transition: transform 0.15s, box-shadow 0.15s, opacity 0.15s;\n box-shadow: 0 4px 12px rgba(99,102,241,0.35);\n}\n.aiwg-send-btn svg { width: 18px; height: 18px; }\n.aiwg-send-btn:hover { transform: translateY(-1px); box-shadow: 0 6px 16px rgba(99,102,241,0.4); }\n.aiwg-send-btn:active { transform: translateY(0); }\n.aiwg-send-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }\n\n/* \u2500\u2500 Autocomplete suggestions dropdown \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-suggestions {\n position: absolute;\n bottom: calc(100% + 4px);\n left: 16px; right: 16px;\n background: #fff;\n border: 1px solid #e2e8f0;\n border-radius: 12px;\n box-shadow: 0 8px 24px rgba(0,0,0,0.12);\n overflow: hidden;\n z-index: 100;\n animation: aiwg-fade-up 0.15s ease both;\n}\n.aiwg-suggestions-header {\n padding: 8px 14px 4px;\n font-size: 11px; font-weight: 600;\n color: #94a3b8;\n text-transform: uppercase; letter-spacing: 0.06em;\n border-bottom: 1px solid #f1f5f9;\n}\n.aiwg-suggestion-item {\n display: flex; align-items: center; gap: 10px;\n padding: 10px 14px;\n cursor: pointer;\n transition: background 0.1s;\n border-bottom: 1px solid #f8fafc;\n}\n.aiwg-suggestion-item:last-child { border-bottom: none; }\n.aiwg-suggestion-item:hover,\n.aiwg-suggestion-item.aiwg-active { background: #f5f3ff; }\n.aiwg-suggestion-icon { font-size: 16px; flex-shrink: 0; }\n.aiwg-suggestion-main { flex: 1; min-width: 0; }\n.aiwg-suggestion-text { font-size: 13px; font-weight: 500; color: #1a1a2e; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.aiwg-suggestion-text mark { background: none; color: #6366f1; font-weight: 600; }\n.aiwg-suggestion-desc { font-size: 11px; color: #94a3b8; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.aiwg-suggestion-badge { font-size: 10px; font-weight: 600; padding: 2px 8px; border-radius: 99px; background: #ede9fe; color: #6366f1; flex-shrink: 0; }\n.aiwg-kbd-hint {\n padding: 6px 14px;\n font-size: 11px; color: #cbd5e1; background: #f8fafc;\n display: flex; justify-content: flex-end; gap: 10px;\n}\n.aiwg-kbd-hint kbd { font-family: inherit; background: #e2e8f0; border-radius: 4px; padding: 1px 5px; font-size: 10px; color: #64748b; }\n\n/* \u2500\u2500 Error toast \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-error {\n margin: 0 16px 8px;\n padding: 8px 12px;\n background: #fef2f2;\n border: 1px solid #fecaca;\n border-radius: 8px;\n font-size: 12px;\n color: #dc2626;\n}\n\n/* \u2500\u2500 Minimized state \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-root.aiwg-minimized .aiwg-messages,\n.aiwg-root.aiwg-minimized .aiwg-chip-row,\n.aiwg-root.aiwg-minimized .aiwg-error,\n.aiwg-root.aiwg-minimized .aiwg-input-area,\n.aiwg-root.aiwg-minimized .aiwg-powered { display: none !important; }\n.aiwg-root.aiwg-minimized .aiwg-minimize-btn { display: none !important; }\n.aiwg-root.aiwg-minimized { cursor: pointer; box-shadow: 0 8px 32px rgba(0,0,0,0.28); }\n.aiwg-root.aiwg-minimized .aiwg-header { cursor: pointer !important; }\n\n/* \u2500\u2500 Powered-by footer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-powered {\n text-align: center;\n padding: 6px 16px 10px;\n font-size: 11px; color: #94a3b8;\n flex-shrink: 0;\n border-top: 1px solid #f1f5f9;\n background: #fff;\n}\n.aiwg-powered a { color: #6366f1; text-decoration: none; font-weight: 500; }\n.aiwg-powered a:hover { text-decoration: underline; }\n\n/* \u2500\u2500 Markdown rendering \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-msg-bubble strong { font-weight: 600; }\n.aiwg-msg-bubble em { font-style: italic; }\n.aiwg-msg-bubble code { background: rgba(0,0,0,0.08); padding: 1px 5px; border-radius: 4px; font-family: 'Fira Code','Consolas',monospace; font-size: 0.9em; }\n.aiwg-msg--user .aiwg-msg-bubble code { background: rgba(255,255,255,0.2); }\n.aiwg-msg-bubble ol, .aiwg-msg-bubble ul { padding-left: 18px; margin: 6px 0; display: flex; flex-direction: column; gap: 3px; }\n.aiwg-msg-bubble ol { list-style: decimal; }\n.aiwg-msg-bubble ul { list-style: disc; }\n.aiwg-msg-bubble li { line-height: 1.5; }\n.aiwg-msg-bubble h3, .aiwg-msg-bubble h4 { font-weight: 600; margin: 8px 0 4px; }\n.aiwg-msg-bubble p { margin: 2px 0; }\n\n/* \u2500\u2500 Dark theme \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-root.aiwg-dark { background: #0f172a; color: #e2e8f0; }\n.aiwg-dark .aiwg-msg--bot .aiwg-msg-bubble { background: #1e293b; color: #e2e8f0; }\n.aiwg-dark .aiwg-input-area { background: #0f172a; border-top-color: #1e293b; }\n.aiwg-dark .aiwg-input { background: #1e293b; border-color: #334155; color: #e2e8f0; }\n.aiwg-dark .aiwg-input:focus { border-color: #8b5cf6; background: #1e293b; box-shadow: 0 0 0 3px rgba(139,92,246,0.2); }\n.aiwg-dark .aiwg-suggestions { background: #1e293b; border-color: #334155; }\n.aiwg-dark .aiwg-suggestions-header { color: #64748b; border-bottom-color: #334155; }\n.aiwg-dark .aiwg-suggestion-item { border-bottom-color: #0f172a; }\n.aiwg-dark .aiwg-suggestion-item:hover,\n.aiwg-dark .aiwg-suggestion-item.aiwg-active { background: #2d1b69; }\n.aiwg-dark .aiwg-suggestion-text { color: #e2e8f0; }\n.aiwg-dark .aiwg-chip { background: #1e293b; border-color: #334155; }\n.aiwg-dark .aiwg-chip:hover { background: #2d1b69; }\n.aiwg-dark .aiwg-chip-shimmer { background: linear-gradient(90deg, #1e293b 25%, #334155 50%, #1e293b 75%); background-size: 200% 100%; }\n.aiwg-dark .aiwg-kbd-hint { background: #1e293b; }\n.aiwg-dark .aiwg-welcome h4 { color: #e2e8f0; }\n.aiwg-dark .aiwg-powered { background: #0f172a; border-top-color: #1e293b; }\n";
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical widget CSS — single source of truth for all framework packages.
|
|
3
|
+
*
|
|
4
|
+
* Isolation strategy:
|
|
5
|
+
* - React : injected into a Shadow DOM via ShadowWrapper (createPortal).
|
|
6
|
+
* :host { all: initial } resets inherited properties at the boundary.
|
|
7
|
+
* - jQuery : injected into attachShadow() root. Same :host reset applies.
|
|
8
|
+
* - Angular: ViewEncapsulation.ShadowDom — Angular handles the shadow root.
|
|
9
|
+
*
|
|
10
|
+
* NOTE: `@import` for Google Fonts does NOT work inside shadow-DOM <style> tags.
|
|
11
|
+
* Each package is responsible for loading the Inter font into document <head>
|
|
12
|
+
* separately (React: ensureFont(), jQuery: injectFontLink()).
|
|
13
|
+
*/
|
|
14
|
+
export const WIDGET_CSS = `
|
|
15
|
+
/* ── Shadow-DOM host isolation ────────────────────────────────────────────── */
|
|
16
|
+
:host {
|
|
17
|
+
all: initial !important;
|
|
18
|
+
display: block !important;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* Scoped box-model reset — only touches widget elements, not all of shadow DOM */
|
|
22
|
+
.aiwg-root *, .aiwg-root *::before, .aiwg-root *::after {
|
|
23
|
+
box-sizing: border-box;
|
|
24
|
+
margin: 0;
|
|
25
|
+
padding: 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* ── Container ────────────────────────────────────────────────────────────── */
|
|
29
|
+
.aiwg-root {
|
|
30
|
+
--aiwg-primary: #6366f1;
|
|
31
|
+
--aiwg-primary-dark: #8b5cf6;
|
|
32
|
+
--aiwg-fg-color: #ffffff;
|
|
33
|
+
--aiwg-bg-color: #ffffff;
|
|
34
|
+
--aiwg-chat-bg: #f1f5f9;
|
|
35
|
+
|
|
36
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
37
|
+
font-size: 14px;
|
|
38
|
+
line-height: 1.5;
|
|
39
|
+
color: #1a1a2e;
|
|
40
|
+
display: flex;
|
|
41
|
+
flex-direction: column;
|
|
42
|
+
overflow: hidden;
|
|
43
|
+
border-radius: 16px;
|
|
44
|
+
background: var(--aiwg-bg-color);
|
|
45
|
+
position: fixed !important;
|
|
46
|
+
bottom: 24px;
|
|
47
|
+
right: 24px;
|
|
48
|
+
width: 380px !important;
|
|
49
|
+
max-width: calc(100vw - 48px) !important;
|
|
50
|
+
z-index: 99999;
|
|
51
|
+
transition: height 0.25s ease;
|
|
52
|
+
box-shadow: 0 24px 64px rgba(0,0,0,0.18);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* ── Header ───────────────────────────────────────────────────────────────── */
|
|
56
|
+
.aiwg-header {
|
|
57
|
+
background: linear-gradient(135deg, var(--aiwg-primary) 0%, var(--aiwg-primary-dark) 100%);
|
|
58
|
+
padding: 16px 20px;
|
|
59
|
+
display: flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
gap: 12px;
|
|
62
|
+
flex-shrink: 0;
|
|
63
|
+
position: relative;
|
|
64
|
+
overflow: hidden;
|
|
65
|
+
}
|
|
66
|
+
.aiwg-header::before {
|
|
67
|
+
content: '';
|
|
68
|
+
position: absolute;
|
|
69
|
+
width: 140px; height: 140px;
|
|
70
|
+
background: rgba(255,255,255,0.08);
|
|
71
|
+
border-radius: 50%;
|
|
72
|
+
top: -50px; right: -30px;
|
|
73
|
+
}
|
|
74
|
+
.aiwg-avatar {
|
|
75
|
+
width: 40px; height: 40px;
|
|
76
|
+
background: rgba(255,255,255,0.25);
|
|
77
|
+
border-radius: 50%;
|
|
78
|
+
display: flex; align-items: center; justify-content: center;
|
|
79
|
+
font-size: 18px;
|
|
80
|
+
flex-shrink: 0;
|
|
81
|
+
border: 2px solid rgba(255,255,255,0.4);
|
|
82
|
+
color: #fff;
|
|
83
|
+
}
|
|
84
|
+
.aiwg-avatar svg { display: block; width: 22px; height: 22px; }
|
|
85
|
+
.aiwg-header-info { flex: 1; }
|
|
86
|
+
.aiwg-title { color: #fff; font-weight: 600; font-size: 15px; }
|
|
87
|
+
.aiwg-subtitle {
|
|
88
|
+
color: rgba(255,255,255,0.75);
|
|
89
|
+
font-size: 12px;
|
|
90
|
+
display: flex; align-items: center; gap: 5px;
|
|
91
|
+
}
|
|
92
|
+
.aiwg-online-dot {
|
|
93
|
+
width: 7px; height: 7px;
|
|
94
|
+
background: #4ade80;
|
|
95
|
+
border-radius: 50%;
|
|
96
|
+
display: inline-block;
|
|
97
|
+
animation: aiwg-pulse 2s ease-in-out infinite;
|
|
98
|
+
}
|
|
99
|
+
@keyframes aiwg-pulse {
|
|
100
|
+
0%, 100% { opacity: 1; transform: scale(1); }
|
|
101
|
+
50% { opacity: 0.6; transform: scale(0.85); }
|
|
102
|
+
}
|
|
103
|
+
.aiwg-minimize-btn {
|
|
104
|
+
width: 36px; height: 36px;
|
|
105
|
+
background: rgba(255,255,255,0.18);
|
|
106
|
+
border: 1.5px solid rgba(255,255,255,0.3);
|
|
107
|
+
border-radius: 50%;
|
|
108
|
+
color: #fff; cursor: pointer;
|
|
109
|
+
display: flex; align-items: center; justify-content: center;
|
|
110
|
+
font-size: 18px; line-height: 1;
|
|
111
|
+
transition: background 0.15s, transform 0.15s;
|
|
112
|
+
flex-shrink: 0;
|
|
113
|
+
position: relative; z-index: 2;
|
|
114
|
+
}
|
|
115
|
+
.aiwg-minimize-btn * { cursor: pointer !important; pointer-events: none; }
|
|
116
|
+
.aiwg-minimize-btn svg { display: block; width: 16px; height: 16px; }
|
|
117
|
+
.aiwg-minimize-btn:hover { background: rgba(255,255,255,0.32); transform: scale(1.08); }
|
|
118
|
+
|
|
119
|
+
/* ── Messages area ────────────────────────────────────────────────────────── */
|
|
120
|
+
.aiwg-messages {
|
|
121
|
+
flex: 1;
|
|
122
|
+
overflow-y: auto;
|
|
123
|
+
padding: 20px 16px 8px;
|
|
124
|
+
scroll-behavior: smooth;
|
|
125
|
+
display: flex;
|
|
126
|
+
flex-direction: column;
|
|
127
|
+
gap: 12px;
|
|
128
|
+
}
|
|
129
|
+
.aiwg-messages::-webkit-scrollbar { width: 4px; }
|
|
130
|
+
.aiwg-messages::-webkit-scrollbar-thumb { background: #e2e8f0; border-radius: 99px; }
|
|
131
|
+
|
|
132
|
+
/* ── Welcome card ─────────────────────────────────────────────────────────── */
|
|
133
|
+
.aiwg-welcome {
|
|
134
|
+
text-align: center;
|
|
135
|
+
padding: 24px 16px;
|
|
136
|
+
display: flex; flex-direction: column; align-items: center; gap: 8px;
|
|
137
|
+
}
|
|
138
|
+
.aiwg-welcome-icon {
|
|
139
|
+
display: flex; align-items: center; justify-content: center;
|
|
140
|
+
margin-bottom: 4px;
|
|
141
|
+
color: var(--aiwg-primary);
|
|
142
|
+
}
|
|
143
|
+
.aiwg-welcome-icon svg { width: 36px; height: 36px; }
|
|
144
|
+
.aiwg-welcome h4 { font-size: 15px; font-weight: 600; color: #1a1a2e; }
|
|
145
|
+
.aiwg-welcome p { font-size: 13px; color: #64748b; }
|
|
146
|
+
|
|
147
|
+
/* ── Message bubbles ──────────────────────────────────────────────────────── */
|
|
148
|
+
.aiwg-msg {
|
|
149
|
+
display: flex;
|
|
150
|
+
gap: 8px;
|
|
151
|
+
align-items: flex-end;
|
|
152
|
+
animation: aiwg-fade-up 0.2s ease both;
|
|
153
|
+
}
|
|
154
|
+
@keyframes aiwg-fade-up {
|
|
155
|
+
from { opacity: 0; transform: translateY(8px); }
|
|
156
|
+
to { opacity: 1; transform: translateY(0); }
|
|
157
|
+
}
|
|
158
|
+
.aiwg-msg--user { flex-direction: row-reverse; }
|
|
159
|
+
.aiwg-msg-avatar {
|
|
160
|
+
width: 28px; height: 28px; border-radius: 50%;
|
|
161
|
+
display: flex; align-items: center; justify-content: center;
|
|
162
|
+
font-size: 13px; flex-shrink: 0;
|
|
163
|
+
margin-bottom: 2px;
|
|
164
|
+
}
|
|
165
|
+
.aiwg-msg-avatar svg { display: block; width: 16px; height: 16px; }
|
|
166
|
+
.aiwg-msg--bot .aiwg-msg-avatar { background: linear-gradient(135deg, var(--aiwg-primary), var(--aiwg-primary-dark)); color: var(--aiwg-fg-color); }
|
|
167
|
+
.aiwg-msg--user .aiwg-msg-avatar { background: #e2e8f0; color: #64748b; }
|
|
168
|
+
.aiwg-msg-body { max-width: 78%; }
|
|
169
|
+
.aiwg-msg-bubble {
|
|
170
|
+
padding: 10px 14px;
|
|
171
|
+
border-radius: 18px;
|
|
172
|
+
font-size: 14px;
|
|
173
|
+
line-height: 1.55;
|
|
174
|
+
word-break: break-word;
|
|
175
|
+
}
|
|
176
|
+
.aiwg-msg--bot .aiwg-msg-bubble { background: var(--aiwg-chat-bg); color: #1a1a2e; border-bottom-left-radius: 4px; }
|
|
177
|
+
.aiwg-msg--user .aiwg-msg-bubble { background: linear-gradient(135deg, var(--aiwg-primary), var(--aiwg-primary-dark)); color: var(--aiwg-fg-color); border-bottom-right-radius: 4px; }
|
|
178
|
+
.aiwg-msg-time { font-size: 10px; color: #94a3b8; margin-top: 4px; text-align: right; padding: 0 4px; }
|
|
179
|
+
.aiwg-msg--bot .aiwg-msg-time { text-align: left; }
|
|
180
|
+
|
|
181
|
+
/* ── Typing indicator ─────────────────────────────────────────────────────── */
|
|
182
|
+
.aiwg-typing { padding: 10px 14px; }
|
|
183
|
+
.aiwg-typing-dots { display: flex; gap: 4px; align-items: center; }
|
|
184
|
+
.aiwg-typing-dots span {
|
|
185
|
+
width: 7px; height: 7px;
|
|
186
|
+
background: #94a3b8; border-radius: 50%;
|
|
187
|
+
animation: aiwg-bounce 1.2s ease-in-out infinite;
|
|
188
|
+
}
|
|
189
|
+
.aiwg-typing-dots span:nth-child(2) { animation-delay: 0.2s; }
|
|
190
|
+
.aiwg-typing-dots span:nth-child(3) { animation-delay: 0.4s; }
|
|
191
|
+
@keyframes aiwg-bounce {
|
|
192
|
+
0%, 80%, 100% { transform: translateY(0); }
|
|
193
|
+
40% { transform: translateY(-6px); }
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* ── Quick-reply chips ────────────────────────────────────────────────────── */
|
|
197
|
+
.aiwg-chip-row {
|
|
198
|
+
padding: 6px 16px 2px;
|
|
199
|
+
display: flex;
|
|
200
|
+
flex-wrap: wrap;
|
|
201
|
+
gap: 6px;
|
|
202
|
+
flex-shrink: 0;
|
|
203
|
+
}
|
|
204
|
+
.aiwg-chip {
|
|
205
|
+
background: #f1f5f9;
|
|
206
|
+
border: 1px solid #e2e8f0;
|
|
207
|
+
border-radius: 99px;
|
|
208
|
+
padding: 4px 12px;
|
|
209
|
+
font-size: 12px;
|
|
210
|
+
color: #4f46e5;
|
|
211
|
+
cursor: pointer;
|
|
212
|
+
transition: all 0.15s;
|
|
213
|
+
white-space: nowrap;
|
|
214
|
+
font-weight: 500;
|
|
215
|
+
}
|
|
216
|
+
.aiwg-chip:hover { background: #ede9fe; border-color: #a5b4fc; }
|
|
217
|
+
.aiwg-chip-shimmer {
|
|
218
|
+
background: linear-gradient(90deg, #e2e8f0 25%, #f1f5f9 50%, #e2e8f0 75%);
|
|
219
|
+
background-size: 200% 100%;
|
|
220
|
+
border: 1px solid transparent;
|
|
221
|
+
border-radius: 99px;
|
|
222
|
+
padding: 4px 34px;
|
|
223
|
+
animation: aiwg-shimmer 1.4s ease-in-out infinite;
|
|
224
|
+
cursor: default;
|
|
225
|
+
pointer-events: none;
|
|
226
|
+
}
|
|
227
|
+
.aiwg-chip-ai-badge {
|
|
228
|
+
display: inline-block;
|
|
229
|
+
font-size: 9px; font-weight: 700;
|
|
230
|
+
background: linear-gradient(135deg, var(--aiwg-primary), var(--aiwg-primary-dark));
|
|
231
|
+
color: #fff;
|
|
232
|
+
padding: 1px 5px;
|
|
233
|
+
border-radius: 99px;
|
|
234
|
+
margin-left: 5px;
|
|
235
|
+
vertical-align: middle;
|
|
236
|
+
letter-spacing: 0.04em;
|
|
237
|
+
}
|
|
238
|
+
@keyframes aiwg-shimmer {
|
|
239
|
+
0% { background-position: 200% 0; }
|
|
240
|
+
100% { background-position: -200% 0; }
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/* ── Input area ───────────────────────────────────────────────────────────── */
|
|
244
|
+
.aiwg-input-area {
|
|
245
|
+
padding: 12px 16px 16px;
|
|
246
|
+
flex-shrink: 0;
|
|
247
|
+
background: #fff;
|
|
248
|
+
border-top: 1px solid #f1f5f9;
|
|
249
|
+
position: relative;
|
|
250
|
+
}
|
|
251
|
+
.aiwg-input-row { display: flex; gap: 8px; align-items: flex-end; }
|
|
252
|
+
.aiwg-input {
|
|
253
|
+
flex: 1;
|
|
254
|
+
border: 1.5px solid #e2e8f0;
|
|
255
|
+
border-radius: 12px;
|
|
256
|
+
padding: 10px 14px;
|
|
257
|
+
font-size: 14px;
|
|
258
|
+
font-family: inherit;
|
|
259
|
+
outline: none;
|
|
260
|
+
resize: none;
|
|
261
|
+
background: #f8fafc;
|
|
262
|
+
color: #1a1a2e;
|
|
263
|
+
transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
|
|
264
|
+
line-height: 1.4;
|
|
265
|
+
max-height: 120px;
|
|
266
|
+
overflow-y: auto;
|
|
267
|
+
scrollbar-width: none;
|
|
268
|
+
-ms-overflow-style: none;
|
|
269
|
+
}
|
|
270
|
+
.aiwg-input::-webkit-scrollbar { display: none; }
|
|
271
|
+
.aiwg-input::placeholder { color: #94a3b8; }
|
|
272
|
+
.aiwg-input:focus {
|
|
273
|
+
border-color: var(--aiwg-primary-dark);
|
|
274
|
+
background: #fff;
|
|
275
|
+
box-shadow: 0 0 0 3px rgba(139,92,246,0.1);
|
|
276
|
+
}
|
|
277
|
+
.aiwg-send-btn {
|
|
278
|
+
width: 42px; height: 42px;
|
|
279
|
+
background: linear-gradient(135deg, var(--aiwg-primary), var(--aiwg-primary-dark));
|
|
280
|
+
border: none; border-radius: 12px;
|
|
281
|
+
color: var(--aiwg-fg-color); cursor: pointer;
|
|
282
|
+
display: flex; align-items: center; justify-content: center;
|
|
283
|
+
flex-shrink: 0;
|
|
284
|
+
transition: transform 0.15s, box-shadow 0.15s, opacity 0.15s;
|
|
285
|
+
box-shadow: 0 4px 12px rgba(99,102,241,0.35);
|
|
286
|
+
}
|
|
287
|
+
.aiwg-send-btn svg { width: 18px; height: 18px; }
|
|
288
|
+
.aiwg-send-btn:hover { transform: translateY(-1px); box-shadow: 0 6px 16px rgba(99,102,241,0.4); }
|
|
289
|
+
.aiwg-send-btn:active { transform: translateY(0); }
|
|
290
|
+
.aiwg-send-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
|
|
291
|
+
|
|
292
|
+
/* ── Autocomplete suggestions dropdown ────────────────────────────────────── */
|
|
293
|
+
.aiwg-suggestions {
|
|
294
|
+
position: absolute;
|
|
295
|
+
bottom: calc(100% + 4px);
|
|
296
|
+
left: 16px; right: 16px;
|
|
297
|
+
background: #fff;
|
|
298
|
+
border: 1px solid #e2e8f0;
|
|
299
|
+
border-radius: 12px;
|
|
300
|
+
box-shadow: 0 8px 24px rgba(0,0,0,0.12);
|
|
301
|
+
overflow: hidden;
|
|
302
|
+
z-index: 100;
|
|
303
|
+
animation: aiwg-fade-up 0.15s ease both;
|
|
304
|
+
}
|
|
305
|
+
.aiwg-suggestions-header {
|
|
306
|
+
padding: 8px 14px 4px;
|
|
307
|
+
font-size: 11px; font-weight: 600;
|
|
308
|
+
color: #94a3b8;
|
|
309
|
+
text-transform: uppercase; letter-spacing: 0.06em;
|
|
310
|
+
border-bottom: 1px solid #f1f5f9;
|
|
311
|
+
}
|
|
312
|
+
.aiwg-suggestion-item {
|
|
313
|
+
display: flex; align-items: center; gap: 10px;
|
|
314
|
+
padding: 10px 14px;
|
|
315
|
+
cursor: pointer;
|
|
316
|
+
transition: background 0.1s;
|
|
317
|
+
border-bottom: 1px solid #f8fafc;
|
|
318
|
+
}
|
|
319
|
+
.aiwg-suggestion-item:last-child { border-bottom: none; }
|
|
320
|
+
.aiwg-suggestion-item:hover,
|
|
321
|
+
.aiwg-suggestion-item.aiwg-active { background: #f5f3ff; }
|
|
322
|
+
.aiwg-suggestion-icon { font-size: 16px; flex-shrink: 0; }
|
|
323
|
+
.aiwg-suggestion-main { flex: 1; min-width: 0; }
|
|
324
|
+
.aiwg-suggestion-text { font-size: 13px; font-weight: 500; color: #1a1a2e; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
325
|
+
.aiwg-suggestion-text mark { background: none; color: #6366f1; font-weight: 600; }
|
|
326
|
+
.aiwg-suggestion-desc { font-size: 11px; color: #94a3b8; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
327
|
+
.aiwg-suggestion-badge { font-size: 10px; font-weight: 600; padding: 2px 8px; border-radius: 99px; background: #ede9fe; color: #6366f1; flex-shrink: 0; }
|
|
328
|
+
.aiwg-kbd-hint {
|
|
329
|
+
padding: 6px 14px;
|
|
330
|
+
font-size: 11px; color: #cbd5e1; background: #f8fafc;
|
|
331
|
+
display: flex; justify-content: flex-end; gap: 10px;
|
|
332
|
+
}
|
|
333
|
+
.aiwg-kbd-hint kbd { font-family: inherit; background: #e2e8f0; border-radius: 4px; padding: 1px 5px; font-size: 10px; color: #64748b; }
|
|
334
|
+
|
|
335
|
+
/* ── Error toast ──────────────────────────────────────────────────────────── */
|
|
336
|
+
.aiwg-error {
|
|
337
|
+
margin: 0 16px 8px;
|
|
338
|
+
padding: 8px 12px;
|
|
339
|
+
background: #fef2f2;
|
|
340
|
+
border: 1px solid #fecaca;
|
|
341
|
+
border-radius: 8px;
|
|
342
|
+
font-size: 12px;
|
|
343
|
+
color: #dc2626;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/* ── Minimized state ──────────────────────────────────────────────────────── */
|
|
347
|
+
.aiwg-root.aiwg-minimized .aiwg-messages,
|
|
348
|
+
.aiwg-root.aiwg-minimized .aiwg-chip-row,
|
|
349
|
+
.aiwg-root.aiwg-minimized .aiwg-error,
|
|
350
|
+
.aiwg-root.aiwg-minimized .aiwg-input-area,
|
|
351
|
+
.aiwg-root.aiwg-minimized .aiwg-powered { display: none !important; }
|
|
352
|
+
.aiwg-root.aiwg-minimized .aiwg-minimize-btn { display: none !important; }
|
|
353
|
+
.aiwg-root.aiwg-minimized { cursor: pointer; box-shadow: 0 8px 32px rgba(0,0,0,0.28); }
|
|
354
|
+
.aiwg-root.aiwg-minimized .aiwg-header { cursor: pointer !important; }
|
|
355
|
+
|
|
356
|
+
/* ── Powered-by footer ────────────────────────────────────────────────────── */
|
|
357
|
+
.aiwg-powered {
|
|
358
|
+
text-align: center;
|
|
359
|
+
padding: 6px 16px 10px;
|
|
360
|
+
font-size: 11px; color: #94a3b8;
|
|
361
|
+
flex-shrink: 0;
|
|
362
|
+
border-top: 1px solid #f1f5f9;
|
|
363
|
+
background: #fff;
|
|
364
|
+
}
|
|
365
|
+
.aiwg-powered a { color: #6366f1; text-decoration: none; font-weight: 500; }
|
|
366
|
+
.aiwg-powered a:hover { text-decoration: underline; }
|
|
367
|
+
|
|
368
|
+
/* ── Markdown rendering ───────────────────────────────────────────────────── */
|
|
369
|
+
.aiwg-msg-bubble strong { font-weight: 600; }
|
|
370
|
+
.aiwg-msg-bubble em { font-style: italic; }
|
|
371
|
+
.aiwg-msg-bubble code { background: rgba(0,0,0,0.08); padding: 1px 5px; border-radius: 4px; font-family: 'Fira Code','Consolas',monospace; font-size: 0.9em; }
|
|
372
|
+
.aiwg-msg--user .aiwg-msg-bubble code { background: rgba(255,255,255,0.2); }
|
|
373
|
+
.aiwg-msg-bubble ol, .aiwg-msg-bubble ul { padding-left: 18px; margin: 6px 0; display: flex; flex-direction: column; gap: 3px; }
|
|
374
|
+
.aiwg-msg-bubble ol { list-style: decimal; }
|
|
375
|
+
.aiwg-msg-bubble ul { list-style: disc; }
|
|
376
|
+
.aiwg-msg-bubble li { line-height: 1.5; }
|
|
377
|
+
.aiwg-msg-bubble h3, .aiwg-msg-bubble h4 { font-weight: 600; margin: 8px 0 4px; }
|
|
378
|
+
.aiwg-msg-bubble p { margin: 2px 0; }
|
|
379
|
+
|
|
380
|
+
/* ── Dark theme ───────────────────────────────────────────────────────────── */
|
|
381
|
+
.aiwg-root.aiwg-dark { background: #0f172a; color: #e2e8f0; }
|
|
382
|
+
.aiwg-dark .aiwg-msg--bot .aiwg-msg-bubble { background: #1e293b; color: #e2e8f0; }
|
|
383
|
+
.aiwg-dark .aiwg-input-area { background: #0f172a; border-top-color: #1e293b; }
|
|
384
|
+
.aiwg-dark .aiwg-input { background: #1e293b; border-color: #334155; color: #e2e8f0; }
|
|
385
|
+
.aiwg-dark .aiwg-input:focus { border-color: #8b5cf6; background: #1e293b; box-shadow: 0 0 0 3px rgba(139,92,246,0.2); }
|
|
386
|
+
.aiwg-dark .aiwg-suggestions { background: #1e293b; border-color: #334155; }
|
|
387
|
+
.aiwg-dark .aiwg-suggestions-header { color: #64748b; border-bottom-color: #334155; }
|
|
388
|
+
.aiwg-dark .aiwg-suggestion-item { border-bottom-color: #0f172a; }
|
|
389
|
+
.aiwg-dark .aiwg-suggestion-item:hover,
|
|
390
|
+
.aiwg-dark .aiwg-suggestion-item.aiwg-active { background: #2d1b69; }
|
|
391
|
+
.aiwg-dark .aiwg-suggestion-text { color: #e2e8f0; }
|
|
392
|
+
.aiwg-dark .aiwg-chip { background: #1e293b; border-color: #334155; }
|
|
393
|
+
.aiwg-dark .aiwg-chip:hover { background: #2d1b69; }
|
|
394
|
+
.aiwg-dark .aiwg-chip-shimmer { background: linear-gradient(90deg, #1e293b 25%, #334155 50%, #1e293b 75%); background-size: 200% 100%; }
|
|
395
|
+
.aiwg-dark .aiwg-kbd-hint { background: #1e293b; }
|
|
396
|
+
.aiwg-dark .aiwg-welcome h4 { color: #e2e8f0; }
|
|
397
|
+
.aiwg-dark .aiwg-powered { background: #0f172a; border-top-color: #1e293b; }
|
|
398
|
+
`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@courseecho/ai-core-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.22",
|
|
4
4
|
"description": "Framework-agnostic core AI chat SDK. Shared logic for all framework wrappers.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "CourseEcho",
|
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
"types": "./dist/index.d.ts"
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
|
-
"files": [
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
18
20
|
"scripts": {
|
|
19
21
|
"build": "tsc",
|
|
20
22
|
"dev": "tsc --watch"
|
|
@@ -22,9 +24,16 @@
|
|
|
22
24
|
"dependencies": {
|
|
23
25
|
"rxjs": "^7.8.0"
|
|
24
26
|
},
|
|
25
|
-
"keywords": [
|
|
27
|
+
"keywords": [
|
|
28
|
+
"ai",
|
|
29
|
+
"courseecho",
|
|
30
|
+
"chatbot",
|
|
31
|
+
"sdk",
|
|
32
|
+
"widget",
|
|
33
|
+
"chat"
|
|
34
|
+
],
|
|
26
35
|
"devDependencies": {
|
|
27
36
|
"typescript": "^5.3.0",
|
|
28
37
|
"@types/node": "^20.0.0"
|
|
29
38
|
}
|
|
30
|
-
}
|
|
39
|
+
}
|