@smart-cloud/ai-kit-ui 1.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/.gitattributes +8 -0
- package/dist/ai-kit-ui.css +201 -0
- package/dist/index.cjs +14 -0
- package/dist/index.d.cts +87 -0
- package/dist/index.d.ts +87 -0
- package/dist/index.js +14 -0
- package/eslint.config.js +18 -0
- package/package.json +75 -0
- package/src/ShadowBoundary.tsx +266 -0
- package/src/ai-feature/AiFeature.tsx +1824 -0
- package/src/ai-feature/AiFeatureBorder.tsx +22 -0
- package/src/ai-feature/ProofreadDiff.tsx +118 -0
- package/src/ai-feature/index.tsx +2 -0
- package/src/ai-feature/utils.tsx +20 -0
- package/src/i18n/ar.ts +156 -0
- package/src/i18n/de.ts +157 -0
- package/src/i18n/en.ts +156 -0
- package/src/i18n/es.ts +157 -0
- package/src/i18n/fr.ts +158 -0
- package/src/i18n/he.ts +156 -0
- package/src/i18n/hi.ts +157 -0
- package/src/i18n/hu.ts +156 -0
- package/src/i18n/id.ts +157 -0
- package/src/i18n/index.ts +47 -0
- package/src/i18n/it.ts +159 -0
- package/src/i18n/ja.ts +157 -0
- package/src/i18n/ko.ts +155 -0
- package/src/i18n/nb.ts +156 -0
- package/src/i18n/nl.ts +157 -0
- package/src/i18n/pl.ts +158 -0
- package/src/i18n/pt.ts +155 -0
- package/src/i18n/ru.ts +157 -0
- package/src/i18n/sv.ts +156 -0
- package/src/i18n/th.ts +154 -0
- package/src/i18n/tr.ts +158 -0
- package/src/i18n/ua.ts +159 -0
- package/src/i18n/zh.ts +151 -0
- package/src/index.tsx +4 -0
- package/src/styles/ai-kit-ui.css +201 -0
- package/src/useAiRun.ts +177 -0
- package/src/withAiKitShell.tsx +208 -0
- package/tsconfig.json +32 -0
- package/tsconfig.node.json +13 -0
- package/tsup.config.ts +21 -0
package/eslint.config.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import jseslint from "@eslint/js";
|
|
2
|
+
import { defineConfig } from 'eslint/config';
|
|
3
|
+
import globals from "globals";
|
|
4
|
+
import tseslint from 'typescript-eslint';
|
|
5
|
+
|
|
6
|
+
export default defineConfig(
|
|
7
|
+
{ ignores: ['**/build/**', '**/dist/**', '**/webpack.config.cjs'] },
|
|
8
|
+
jseslint.configs.recommended,
|
|
9
|
+
...tseslint.configs.recommended,
|
|
10
|
+
{
|
|
11
|
+
files: ["**/*.tsx", "**/*.ts"],
|
|
12
|
+
languageOptions: {
|
|
13
|
+
parser: tseslint.parser,
|
|
14
|
+
ecmaVersion: 2020,
|
|
15
|
+
globals: globals.browser,
|
|
16
|
+
},
|
|
17
|
+
}
|
|
18
|
+
);
|
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@smart-cloud/ai-kit-ui",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"license": "ISC",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsup --minify",
|
|
11
|
+
"lint": "eslint 'src/**/*.{ts,tsx}' --report-unused-disable-directives --max-warnings 0",
|
|
12
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
13
|
+
"publish": "WPSUITE_PREMIUM=true tsup --minify && npm publish --access=public"
|
|
14
|
+
},
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"registry": "https://registry.npmjs.org/",
|
|
17
|
+
"scope": "@smart-cloud/ai-kit-ui"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@smart-cloud/ai-kit-core": "^1.0.0",
|
|
21
|
+
"@smart-cloud/wpsuite-core": "^2.0.5",
|
|
22
|
+
"@tabler/icons-react": "^3.36.1",
|
|
23
|
+
"react-markdown": "^10.1.0",
|
|
24
|
+
"rehype-sanitize": "^6.0.0",
|
|
25
|
+
"rehype-stringify": "^10.0.1",
|
|
26
|
+
"remark-gfm": "^4.0.1",
|
|
27
|
+
"remark-parse": "^11.0.0",
|
|
28
|
+
"remark-rehype": "^11.1.2",
|
|
29
|
+
"unified": "^11.0.5"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@emotion/cache": "^11.14.0",
|
|
33
|
+
"@emotion/react": "^11.14.0",
|
|
34
|
+
"@mantine/core": "^8.3.12",
|
|
35
|
+
"@mantine/hooks": "^8.3.12",
|
|
36
|
+
"@mantine/modals": "^8.3.12",
|
|
37
|
+
"@wordpress/data": "^10.37.0",
|
|
38
|
+
"aws-amplify": "^6.15.9",
|
|
39
|
+
"react": "^18.3.1",
|
|
40
|
+
"react-dom": "^18.3.1"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@emotion/cache": "^11.14.0",
|
|
44
|
+
"@emotion/react": "^11.14.0",
|
|
45
|
+
"@eslint/js": "^9.39.2",
|
|
46
|
+
"@mantine/core": "^8.3.12",
|
|
47
|
+
"@mantine/hooks": "^8.3.12",
|
|
48
|
+
"@mantine/modals": "^8.3.12",
|
|
49
|
+
"@types/dom-chromium-ai": "^0.0.13",
|
|
50
|
+
"@types/jquery": "^3.5.33",
|
|
51
|
+
"@types/react": "^18.3.23",
|
|
52
|
+
"@types/react-dom": "^18.3.7",
|
|
53
|
+
"@typescript-eslint/eslint-plugin": "^8.52.0",
|
|
54
|
+
"@typescript-eslint/parser": "^8.52.0",
|
|
55
|
+
"@wordpress/data": "^10.37.0",
|
|
56
|
+
"ajv": "^8.17.1",
|
|
57
|
+
"aws-amplify": "^6.15.9",
|
|
58
|
+
"eslint": "^9.39.2",
|
|
59
|
+
"globals": "^17.0.0",
|
|
60
|
+
"jquery": "^3.7.1",
|
|
61
|
+
"react": "^18.3.1",
|
|
62
|
+
"react-dom": "^18.3.1",
|
|
63
|
+
"tsup": "^8.5.1",
|
|
64
|
+
"typescript": "^5.9.3",
|
|
65
|
+
"typescript-eslint": "^8.52.0"
|
|
66
|
+
},
|
|
67
|
+
"exports": {
|
|
68
|
+
".": {
|
|
69
|
+
"types": "./dist/index.d.ts",
|
|
70
|
+
"import": "./dist/index.js",
|
|
71
|
+
"require": "./dist/index.cjs"
|
|
72
|
+
},
|
|
73
|
+
"./styles.css": "./dist/ai-kit-ui.css"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import createCache from "@emotion/cache";
|
|
2
|
+
import { CacheProvider } from "@emotion/react";
|
|
3
|
+
import { useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { createPortal } from "react-dom";
|
|
5
|
+
|
|
6
|
+
export type ShadowBoundaryMode = "local" | "overlay";
|
|
7
|
+
|
|
8
|
+
export type ShadowBoundaryProps = {
|
|
9
|
+
/** Stylesheets to inject into the shadow root (as <link rel="stylesheet">). */
|
|
10
|
+
stylesheets: string[];
|
|
11
|
+
|
|
12
|
+
/** Optional class name applied to the host element. */
|
|
13
|
+
className?: string;
|
|
14
|
+
|
|
15
|
+
/** Optional raw CSS text injected into the shadow root (as <style>). */
|
|
16
|
+
styleText?: string;
|
|
17
|
+
|
|
18
|
+
/** ID of the element inside the shadow root used as the portal target. */
|
|
19
|
+
rootElementId: string;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Where to create the shadow root:
|
|
23
|
+
* - "local": attach shadow to this component's host element (keeps layout positioning).
|
|
24
|
+
* - "overlay": attach shadow to a singleton element in top (or self) document (always on top).
|
|
25
|
+
*/
|
|
26
|
+
mode?: ShadowBoundaryMode;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* For mode="overlay": host id in the top (or self) document.
|
|
30
|
+
* Same id everywhere => singleton overlay host.
|
|
31
|
+
*/
|
|
32
|
+
overlayRootId?: string;
|
|
33
|
+
|
|
34
|
+
children: (api: {
|
|
35
|
+
/** Portal target element inside the shadow root. */
|
|
36
|
+
rootElement: HTMLDivElement;
|
|
37
|
+
/** Shadow root instance. */
|
|
38
|
+
shadowRoot: ShadowRoot;
|
|
39
|
+
}) => React.ReactNode;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
function getTopDocumentSafe(): Document {
|
|
43
|
+
try {
|
|
44
|
+
return window.top?.document ?? window.document;
|
|
45
|
+
} catch {
|
|
46
|
+
return window.document;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function ensureStylesheets(
|
|
51
|
+
doc: Document,
|
|
52
|
+
container: HTMLElement,
|
|
53
|
+
shadow: ShadowRoot,
|
|
54
|
+
hrefs: string[],
|
|
55
|
+
) {
|
|
56
|
+
for (const href of hrefs) {
|
|
57
|
+
const id = `ai-kit-style-${btoa(href).replace(/=+$/g, "")}`;
|
|
58
|
+
if (shadow.getElementById(id)) continue;
|
|
59
|
+
const link = doc.createElement("link");
|
|
60
|
+
link.id = id;
|
|
61
|
+
link.rel = "stylesheet";
|
|
62
|
+
link.href = href;
|
|
63
|
+
container.appendChild(link);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const REGISTRY_ID = "ai-kit-property-registry";
|
|
68
|
+
|
|
69
|
+
function installAiKitPropertyRegistry() {
|
|
70
|
+
const doc = getTopDocumentSafe();
|
|
71
|
+
|
|
72
|
+
// simple dedupe
|
|
73
|
+
if (doc.getElementById(REGISTRY_ID)) return;
|
|
74
|
+
|
|
75
|
+
const style = doc.createElement("style");
|
|
76
|
+
style.id = REGISTRY_ID;
|
|
77
|
+
|
|
78
|
+
// ONLY @property registrations (no global resets!)
|
|
79
|
+
style.textContent = `
|
|
80
|
+
@property --ai-kit-border-angle {
|
|
81
|
+
syntax: "<angle>";
|
|
82
|
+
inherits: true;
|
|
83
|
+
initial-value: 0deg;
|
|
84
|
+
}
|
|
85
|
+
`;
|
|
86
|
+
|
|
87
|
+
doc.head.appendChild(style);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// tiny stable hash to detect styleText changes without forcing huge deps churn
|
|
91
|
+
function hashStringDjb2(str: string): string {
|
|
92
|
+
let hash = 5381;
|
|
93
|
+
for (let i = 0; i < str.length; i++) {
|
|
94
|
+
hash = ((hash << 5) + hash) ^ str.charCodeAt(i);
|
|
95
|
+
}
|
|
96
|
+
// unsigned + base36
|
|
97
|
+
return (hash >>> 0).toString(36);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const STYLE_TEXT_ID = "ai-kit-style-text";
|
|
101
|
+
|
|
102
|
+
export function ShadowBoundary({
|
|
103
|
+
stylesheets,
|
|
104
|
+
className,
|
|
105
|
+
styleText,
|
|
106
|
+
children,
|
|
107
|
+
rootElementId,
|
|
108
|
+
mode = "local",
|
|
109
|
+
overlayRootId = "ai-kit-overlay-root",
|
|
110
|
+
}: ShadowBoundaryProps) {
|
|
111
|
+
const hostRef = useRef<HTMLDivElement | null>(null);
|
|
112
|
+
|
|
113
|
+
const [shadowRoot, setShadowRoot] = useState<ShadowRoot | null>(null);
|
|
114
|
+
const [portalTarget, setPortalTarget] = useState<HTMLDivElement | null>(null);
|
|
115
|
+
|
|
116
|
+
// Combine built-in + external stylesheets; stable key so callers don't need to memoize arrays.
|
|
117
|
+
const stylesKey = useMemo(() => {
|
|
118
|
+
const all = [...stylesheets];
|
|
119
|
+
return all.join("|");
|
|
120
|
+
}, [stylesheets]);
|
|
121
|
+
|
|
122
|
+
const styleTextHash = useMemo(() => {
|
|
123
|
+
return styleText ? hashStringDjb2(styleText) : "";
|
|
124
|
+
}, [styleText]);
|
|
125
|
+
|
|
126
|
+
useLayoutEffect(() => {
|
|
127
|
+
if (!hostRef.current) return;
|
|
128
|
+
|
|
129
|
+
const doc =
|
|
130
|
+
mode === "overlay" ? getTopDocumentSafe() : hostRef.current.ownerDocument;
|
|
131
|
+
|
|
132
|
+
// 1) Decide overlay host vs local host
|
|
133
|
+
let host: HTMLElement;
|
|
134
|
+
if (mode === "overlay") {
|
|
135
|
+
let overlayHost = doc.getElementById(
|
|
136
|
+
overlayRootId,
|
|
137
|
+
) as HTMLDivElement | null;
|
|
138
|
+
if (!overlayHost) {
|
|
139
|
+
overlayHost = doc.createElement("div");
|
|
140
|
+
overlayHost.id = overlayRootId;
|
|
141
|
+
|
|
142
|
+
// Do not affect layout; allow overlays to sit above everything.
|
|
143
|
+
overlayHost.style.position = "fixed";
|
|
144
|
+
overlayHost.style.inset = "0";
|
|
145
|
+
overlayHost.style.width = "0";
|
|
146
|
+
overlayHost.style.height = "0";
|
|
147
|
+
overlayHost.style.zIndex = "2147483647"; // max z-index
|
|
148
|
+
overlayHost.style.pointerEvents = "none";
|
|
149
|
+
|
|
150
|
+
doc.body.appendChild(overlayHost);
|
|
151
|
+
}
|
|
152
|
+
host = overlayHost;
|
|
153
|
+
} else {
|
|
154
|
+
host = hostRef.current;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// 2) Ensure shadow root
|
|
158
|
+
const shadow = host.shadowRoot ?? host.attachShadow({ mode: "open" });
|
|
159
|
+
|
|
160
|
+
// 3) Ensure portal target div
|
|
161
|
+
let rootEl = shadow.querySelector(
|
|
162
|
+
`#${CSS.escape(rootElementId)}`,
|
|
163
|
+
) as HTMLDivElement | null;
|
|
164
|
+
if (!rootEl) {
|
|
165
|
+
rootEl = doc.createElement("div");
|
|
166
|
+
rootEl.id = rootElementId;
|
|
167
|
+
rootEl.style.margin = "0";
|
|
168
|
+
|
|
169
|
+
// Overlay host itself has pointerEvents:none; allow content to receive events.
|
|
170
|
+
if (mode === "overlay") {
|
|
171
|
+
rootEl.style.pointerEvents = "auto";
|
|
172
|
+
}
|
|
173
|
+
shadow.appendChild(rootEl);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const readScheme = () => rootEl.getAttribute("data-mantine-color-scheme");
|
|
177
|
+
const readVariation = () => rootEl.getAttribute("data-ai-kit-variation");
|
|
178
|
+
|
|
179
|
+
const applyScheme = () => {
|
|
180
|
+
host.setAttribute("data-ai-kit-variation", readVariation() || "default");
|
|
181
|
+
host.setAttribute("data-mantine-color-scheme", readScheme() || "auto");
|
|
182
|
+
if (className) {
|
|
183
|
+
host.className = className;
|
|
184
|
+
}
|
|
185
|
+
host.style.setProperty("outline", "none");
|
|
186
|
+
host.style.setProperty("box-shadow", "none");
|
|
187
|
+
host.style.setProperty("--mantine-color-body", "transparent");
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
applyScheme();
|
|
191
|
+
|
|
192
|
+
// Kövesd, ha a host dokumentumban változik a séma
|
|
193
|
+
const mo = new MutationObserver(applyScheme);
|
|
194
|
+
mo.observe(rootEl, {
|
|
195
|
+
attributes: true,
|
|
196
|
+
attributeFilter: ["data-mantine-color-scheme"],
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const mq = window.matchMedia?.("(prefers-color-scheme: dark)");
|
|
200
|
+
const onMq = () => applyScheme();
|
|
201
|
+
mq?.addEventListener?.("change", onMq);
|
|
202
|
+
|
|
203
|
+
installAiKitPropertyRegistry();
|
|
204
|
+
|
|
205
|
+
// 5) Inject styles into shadow body (dedup per shadow root)
|
|
206
|
+
ensureStylesheets(
|
|
207
|
+
doc,
|
|
208
|
+
rootEl,
|
|
209
|
+
shadow,
|
|
210
|
+
stylesKey ? stylesKey.split("|") : [],
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
// 6) Optional: inject raw style text into the shadow root
|
|
214
|
+
const existingStyle = shadow.getElementById(
|
|
215
|
+
STYLE_TEXT_ID,
|
|
216
|
+
) as HTMLStyleElement | null;
|
|
217
|
+
|
|
218
|
+
if (styleText) {
|
|
219
|
+
if (!existingStyle) {
|
|
220
|
+
const s = doc.createElement("style");
|
|
221
|
+
s.id = STYLE_TEXT_ID;
|
|
222
|
+
s.setAttribute("data-hash", styleTextHash);
|
|
223
|
+
s.textContent = styleText;
|
|
224
|
+
rootEl.appendChild(s);
|
|
225
|
+
} else {
|
|
226
|
+
const prevHash = existingStyle.getAttribute("data-hash") || "";
|
|
227
|
+
if (prevHash !== styleTextHash) {
|
|
228
|
+
existingStyle.setAttribute("data-hash", styleTextHash);
|
|
229
|
+
existingStyle.textContent = styleText;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
} else if (existingStyle) {
|
|
233
|
+
existingStyle.remove();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
setShadowRoot(shadow);
|
|
237
|
+
setPortalTarget(rootEl);
|
|
238
|
+
|
|
239
|
+
return () => {
|
|
240
|
+
mo.disconnect();
|
|
241
|
+
mq?.removeEventListener?.("change", onMq);
|
|
242
|
+
};
|
|
243
|
+
}, [mode, overlayRootId, rootElementId, stylesKey, styleText, styleTextHash]);
|
|
244
|
+
|
|
245
|
+
const emotionCache = useMemo(() => {
|
|
246
|
+
if (!portalTarget) return null;
|
|
247
|
+
// IMPORTANT: container must be an HTMLElement inside the shadow tree.
|
|
248
|
+
return createCache({
|
|
249
|
+
key: mode === "overlay" ? "ai-kit-ov" : "ai-kit-local",
|
|
250
|
+
container: portalTarget,
|
|
251
|
+
});
|
|
252
|
+
}, [portalTarget, mode]);
|
|
253
|
+
|
|
254
|
+
return (
|
|
255
|
+
<div ref={hostRef}>
|
|
256
|
+
{portalTarget && shadowRoot && emotionCache
|
|
257
|
+
? createPortal(
|
|
258
|
+
<CacheProvider value={emotionCache}>
|
|
259
|
+
{children({ rootElement: portalTarget, shadowRoot })}
|
|
260
|
+
</CacheProvider>,
|
|
261
|
+
portalTarget,
|
|
262
|
+
)
|
|
263
|
+
: null}
|
|
264
|
+
</div>
|
|
265
|
+
);
|
|
266
|
+
}
|