@courseecho/ai-widget-react 1.0.22 → 1.0.23

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.
@@ -1,6 +1,12 @@
1
1
  /**
2
2
  * ShadowWrapper — renders children inside a Shadow DOM so the widget's CSS
3
3
  * is completely isolated from the host application's styles.
4
+ *
5
+ * Key isolation techniques used:
6
+ * 1. :host { all: initial } — kills every inherited CSS property at the boundary
7
+ * 2. Full element reset inside the shadow — counters any UA-sheet or inherited font changes
8
+ * 3. Google Fonts injected into document <head> (fonts are shared across shadow trees)
9
+ * 4. React portal into a container that lives inside the shadow root
4
10
  */
5
11
  import React from 'react';
6
12
  export declare const ShadowWrapper: React.FC<{
@@ -2,12 +2,59 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
2
2
  /**
3
3
  * ShadowWrapper — renders children inside a Shadow DOM so the widget's CSS
4
4
  * is completely isolated from the host application's styles.
5
+ *
6
+ * Key isolation techniques used:
7
+ * 1. :host { all: initial } — kills every inherited CSS property at the boundary
8
+ * 2. Full element reset inside the shadow — counters any UA-sheet or inherited font changes
9
+ * 3. Google Fonts injected into document <head> (fonts are shared across shadow trees)
10
+ * 4. React portal into a container that lives inside the shadow root
5
11
  */
6
12
  import { useRef, useEffect, useState } from 'react';
7
13
  import { createPortal } from 'react-dom';
8
- // All widget CSS inlined here so it can be injected into the shadow root.
9
- // The @import is replaced with a <link> element appended to the shadow root.
14
+ // Inject Inter font into the document <head> once (fonts ARE shared across shadow DOM boundaries)
15
+ function ensureFont() {
16
+ if (typeof document === 'undefined')
17
+ return;
18
+ if (document.getElementById('aiwg-inter-font'))
19
+ return;
20
+ const link = document.createElement('link');
21
+ link.id = 'aiwg-inter-font';
22
+ link.rel = 'stylesheet';
23
+ link.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap';
24
+ document.head.appendChild(link);
25
+ }
10
26
  const WIDGET_CSS = `
27
+ /* ── Total isolation from host app ───────────────────────────────────────── */
28
+ :host {
29
+ all: initial !important;
30
+ display: block !important;
31
+ }
32
+
33
+ /* Guarantee every element inside the shadow starts from a known baseline */
34
+ *, *::before, *::after {
35
+ box-sizing: border-box !important;
36
+ margin: 0 !important;
37
+ padding: 0 !important;
38
+ border: 0 !important;
39
+ font-size: 100% !important;
40
+ font: inherit !important;
41
+ vertical-align: baseline !important;
42
+ line-height: inherit !important;
43
+ color: inherit !important;
44
+ background: transparent !important;
45
+ text-decoration: none !important;
46
+ list-style: none !important;
47
+ }
48
+
49
+ /* Restore sensible block/inline defaults that the above stripped */
50
+ div, section, article, header, footer, main, nav, aside { display: block !important; }
51
+ span, em, strong, small, code, kbd { display: inline !important; }
52
+ button { display: inline-flex !important; cursor: pointer !important; }
53
+ textarea, input { display: block !important; }
54
+ ul, ol { display: flex !important; flex-direction: column !important; }
55
+ a { cursor: pointer !important; }
56
+
57
+ /* ── Widget component styles ─────────────────────────────────────────────── */
11
58
  .aiwg-root *, .aiwg-root *::before, .aiwg-root *::after { box-sizing: border-box; margin: 0; padding: 0; }
12
59
 
13
60
  .aiwg-root {
@@ -146,15 +193,11 @@ export const ShadowWrapper = ({ children }) => {
146
193
  const hostRef = useRef(null);
147
194
  const [portalTarget, setPortalTarget] = useState(null);
148
195
  useEffect(() => {
196
+ ensureFont(); // inject Inter into <head> — fonts are shared across shadow boundaries
149
197
  const host = hostRef.current;
150
198
  if (!host || host.shadowRoot)
151
199
  return;
152
200
  const shadow = host.attachShadow({ mode: 'open' });
153
- // Load Inter font (Google Fonts can't be @imported inside shadow DOM)
154
- const link = document.createElement('link');
155
- link.rel = 'stylesheet';
156
- link.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap';
157
- shadow.appendChild(link);
158
201
  // Inject all widget CSS into the shadow root
159
202
  const style = document.createElement('style');
160
203
  style.textContent = WIDGET_CSS;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@courseecho/ai-widget-react",
3
- "version": "1.0.22",
3
+ "version": "1.0.23",
4
4
  "description": "React AI chat widget component for CourseEcho.",
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": ["dist"],
17
+ "files": [
18
+ "dist"
19
+ ],
18
20
  "scripts": {
19
21
  "build": "tsc && xcopy /Y src\\*.css dist\\",
20
22
  "dev": "tsc --watch"
@@ -24,7 +26,13 @@
24
26
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
25
27
  "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
26
28
  },
27
- "keywords": ["ai", "courseecho", "chatbot", "react", "widget"],
29
+ "keywords": [
30
+ "ai",
31
+ "courseecho",
32
+ "chatbot",
33
+ "react",
34
+ "widget"
35
+ ],
28
36
  "devDependencies": {
29
37
  "@courseecho/ai-core-sdk": "file:../core",
30
38
  "@types/react": "^18.0.0",
@@ -33,4 +41,4 @@
33
41
  "react-dom": "^18.0.0",
34
42
  "typescript": "^5.3.0"
35
43
  }
36
- }
44
+ }