@useatlas/react 0.0.1
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 +95 -0
- package/dist/chunk-2WFDP7G5.js +231 -0
- package/dist/chunk-2WFDP7G5.js.map +1 -0
- package/dist/chunk-44HBZYKP.js +224 -0
- package/dist/chunk-44HBZYKP.js.map +1 -0
- package/dist/chunk-5SEVKHS5.cjs +229 -0
- package/dist/chunk-5SEVKHS5.cjs.map +1 -0
- package/dist/chunk-UIRB6L36.cjs +249 -0
- package/dist/chunk-UIRB6L36.cjs.map +1 -0
- package/dist/hooks.cjs +251 -0
- package/dist/hooks.cjs.map +1 -0
- package/dist/hooks.d.cts +132 -0
- package/dist/hooks.d.ts +132 -0
- package/dist/hooks.js +237 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.cjs +2976 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +69 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.js +2926 -0
- package/dist/index.js.map +1 -0
- package/dist/result-chart-NFAJ4IQ5.js +398 -0
- package/dist/result-chart-NFAJ4IQ5.js.map +1 -0
- package/dist/result-chart-YLCKBNV4.cjs +400 -0
- package/dist/result-chart-YLCKBNV4.cjs.map +1 -0
- package/dist/styles.css +59 -0
- package/dist/use-dark-mode-rFxawUv1.d.cts +123 -0
- package/dist/use-dark-mode-rFxawUv1.d.ts +123 -0
- package/dist/widget.css +2 -0
- package/dist/widget.js +445 -0
- package/package.json +113 -0
- package/src/components/__tests__/tool-renderers.test.tsx +239 -0
- package/src/components/actions/action-approval-card.tsx +296 -0
- package/src/components/actions/action-status-badge.tsx +50 -0
- package/src/components/admin/change-password-dialog.tsx +128 -0
- package/src/components/atlas-chat.tsx +656 -0
- package/src/components/chart/chart-detection.ts +318 -0
- package/src/components/chart/result-chart.tsx +590 -0
- package/src/components/chat/api-key-bar.tsx +66 -0
- package/src/components/chat/copy-button.tsx +25 -0
- package/src/components/chat/data-table.tsx +104 -0
- package/src/components/chat/error-banner.tsx +32 -0
- package/src/components/chat/explore-card.tsx +41 -0
- package/src/components/chat/follow-up-chips.tsx +29 -0
- package/src/components/chat/loading-card.tsx +10 -0
- package/src/components/chat/managed-auth-card.tsx +116 -0
- package/src/components/chat/markdown.tsx +146 -0
- package/src/components/chat/python-result-card.tsx +245 -0
- package/src/components/chat/sql-block.tsx +54 -0
- package/src/components/chat/sql-result-card.tsx +163 -0
- package/src/components/chat/starter-prompts.ts +6 -0
- package/src/components/chat/tool-part.tsx +106 -0
- package/src/components/chat/typing-indicator.tsx +22 -0
- package/src/components/conversations/conversation-item.tsx +135 -0
- package/src/components/conversations/conversation-list.tsx +69 -0
- package/src/components/conversations/conversation-sidebar.tsx +113 -0
- package/src/components/conversations/delete-confirmation.tsx +27 -0
- package/src/components/schema-explorer/schema-explorer.tsx +517 -0
- package/src/components/ui/alert-dialog.tsx +196 -0
- package/src/components/ui/badge.tsx +48 -0
- package/src/components/ui/button.tsx +64 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/dialog.tsx +158 -0
- package/src/components/ui/dropdown-menu.tsx +257 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/scroll-area.tsx +62 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/sheet.tsx +143 -0
- package/src/components/ui/table.tsx +116 -0
- package/src/components/ui/toggle-group.tsx +83 -0
- package/src/components/ui/toggle.tsx +47 -0
- package/src/context.tsx +85 -0
- package/src/env.d.ts +9 -0
- package/src/hooks/__tests__/provider.test.tsx +83 -0
- package/src/hooks/__tests__/use-atlas-auth.test.tsx +283 -0
- package/src/hooks/__tests__/use-atlas-chat.test.tsx +157 -0
- package/src/hooks/__tests__/use-atlas-conversations.test.tsx +159 -0
- package/src/hooks/__tests__/use-atlas-theme.test.tsx +56 -0
- package/src/hooks/index.ts +47 -0
- package/src/hooks/provider.tsx +77 -0
- package/src/hooks/theme-init-script.ts +17 -0
- package/src/hooks/use-atlas-auth.ts +131 -0
- package/src/hooks/use-atlas-chat.ts +102 -0
- package/src/hooks/use-atlas-conversations.ts +61 -0
- package/src/hooks/use-atlas-theme.ts +34 -0
- package/src/hooks/use-conversations.ts +189 -0
- package/src/hooks/use-dark-mode.ts +150 -0
- package/src/index.ts +36 -0
- package/src/lib/action-types.ts +11 -0
- package/src/lib/helpers.ts +198 -0
- package/src/lib/tool-renderer-types.ts +76 -0
- package/src/lib/types.ts +29 -0
- package/src/lib/utils.ts +6 -0
- package/src/styles.css +59 -0
- package/src/test-setup.ts +55 -0
- package/src/widget-entry.ts +20 -0
- package/src/widget.css +12 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/** Extract tool invocation input from a ToolUIPart. Returns empty object if unavailable. */
|
|
2
|
+
export function getToolArgs(part: unknown): Record<string, unknown> {
|
|
3
|
+
if (part == null || typeof part !== "object") return {};
|
|
4
|
+
const input = (part as Record<string, unknown>).input;
|
|
5
|
+
if (input == null || typeof input !== "object") return {};
|
|
6
|
+
return input as Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Extract tool output from a ToolUIPart. Returns null if not yet available. */
|
|
10
|
+
export function getToolResult(part: unknown): unknown {
|
|
11
|
+
if (part == null || typeof part !== "object") return null;
|
|
12
|
+
return (part as Record<string, unknown>).output ?? null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** True when the tool invocation has finished successfully (state is "output-available"). */
|
|
16
|
+
export function isToolComplete(part: unknown): boolean {
|
|
17
|
+
if (part == null || typeof part !== "object") return false;
|
|
18
|
+
return (part as Record<string, unknown>).state === "output-available";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Parse a CSV string into headers + rows. Handles basic quoting and escaped quotes (""). */
|
|
22
|
+
export function parseCSV(csv: string): { headers: string[]; rows: string[][] } {
|
|
23
|
+
if (!csv || !csv.trim()) return { headers: [], rows: [] };
|
|
24
|
+
|
|
25
|
+
const lines = csv.trim().split("\n");
|
|
26
|
+
if (lines.length === 0) return { headers: [], rows: [] };
|
|
27
|
+
|
|
28
|
+
function parseLine(line: string): string[] {
|
|
29
|
+
const result: string[] = [];
|
|
30
|
+
let current = "";
|
|
31
|
+
let inQuotes = false;
|
|
32
|
+
for (let k = 0; k < line.length; k++) {
|
|
33
|
+
const char = line[k];
|
|
34
|
+
if (char === '"') {
|
|
35
|
+
if (inQuotes && line[k + 1] === '"') {
|
|
36
|
+
current += '"';
|
|
37
|
+
k++;
|
|
38
|
+
} else {
|
|
39
|
+
inQuotes = !inQuotes;
|
|
40
|
+
}
|
|
41
|
+
} else if (char === "," && !inQuotes) {
|
|
42
|
+
result.push(current.trim());
|
|
43
|
+
current = "";
|
|
44
|
+
} else {
|
|
45
|
+
current += char;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
result.push(current.trim());
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
headers: parseLine(lines[0]),
|
|
54
|
+
rows: lines
|
|
55
|
+
.slice(1)
|
|
56
|
+
.filter((l) => l.trim())
|
|
57
|
+
.map(parseLine),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Serialize columns + rows to a CSV string. Handles commas, quotes, and newlines in values. */
|
|
62
|
+
export function toCsvString(columns: string[], rows: Record<string, unknown>[]): string {
|
|
63
|
+
const escape = (v: unknown) => {
|
|
64
|
+
const s = v == null ? "" : String(v);
|
|
65
|
+
return s.includes(",") || s.includes('"') || s.includes("\n")
|
|
66
|
+
? `"${s.replace(/"/g, '""')}"`
|
|
67
|
+
: s;
|
|
68
|
+
};
|
|
69
|
+
const header = columns.map(escape).join(",");
|
|
70
|
+
const body = rows.map((row) => columns.map((col) => escape(row[col])).join(","));
|
|
71
|
+
return [header, ...body].join("\n");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Trigger a CSV download in the browser. */
|
|
75
|
+
export function downloadCSV(csv: string, filename = "atlas-results.csv") {
|
|
76
|
+
let url: string | null = null;
|
|
77
|
+
try {
|
|
78
|
+
const blob = new Blob([csv], { type: "text/csv" });
|
|
79
|
+
url = URL.createObjectURL(blob);
|
|
80
|
+
const a = document.createElement("a");
|
|
81
|
+
a.href = url;
|
|
82
|
+
a.download = filename;
|
|
83
|
+
a.click();
|
|
84
|
+
} catch (err) {
|
|
85
|
+
console.error("CSV download failed:", err);
|
|
86
|
+
window.alert("CSV download failed");
|
|
87
|
+
} finally {
|
|
88
|
+
if (url) {
|
|
89
|
+
const blobUrl = url;
|
|
90
|
+
setTimeout(() => URL.revokeObjectURL(blobUrl), 10_000);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Strict ISO date pattern: YYYY-MM-DD with optional time component. */
|
|
96
|
+
const ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})?)?$/;
|
|
97
|
+
|
|
98
|
+
/** Coerce a cell value to a typed Excel cell: numbers/booleans pass through, ISO dates become Date objects, null becomes empty string. Exported for testing. */
|
|
99
|
+
export function coerceExcelCell(v: unknown): unknown {
|
|
100
|
+
if (v == null) return "";
|
|
101
|
+
if (typeof v === "number" || typeof v === "boolean") return v;
|
|
102
|
+
if (typeof v === "string" && ISO_DATE_RE.test(v) && !isNaN(Date.parse(v))) {
|
|
103
|
+
return new Date(v);
|
|
104
|
+
}
|
|
105
|
+
return String(v);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Trigger an Excel (.xlsx) download in the browser. Dynamically imports xlsx to avoid bundle bloat. */
|
|
109
|
+
export async function downloadExcel(
|
|
110
|
+
columns: string[],
|
|
111
|
+
rows: Record<string, unknown>[],
|
|
112
|
+
filename = "atlas-results.xlsx",
|
|
113
|
+
) {
|
|
114
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
115
|
+
let XLSX: any;
|
|
116
|
+
try {
|
|
117
|
+
XLSX = await import("xlsx" /* webpackIgnore: true */);
|
|
118
|
+
} catch (err) {
|
|
119
|
+
console.error("Failed to load xlsx library:", err);
|
|
120
|
+
window.alert("Excel export is unavailable. The spreadsheet library failed to load.");
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
let url: string | null = null;
|
|
125
|
+
try {
|
|
126
|
+
const data = rows.map((row) => {
|
|
127
|
+
const obj: Record<string, unknown> = {};
|
|
128
|
+
for (const col of columns) {
|
|
129
|
+
obj[col] = coerceExcelCell(row[col]);
|
|
130
|
+
}
|
|
131
|
+
return obj;
|
|
132
|
+
});
|
|
133
|
+
const ws = XLSX.utils.json_to_sheet(data, { header: columns });
|
|
134
|
+
const wb = XLSX.utils.book_new();
|
|
135
|
+
XLSX.utils.book_append_sheet(wb, ws, "Results");
|
|
136
|
+
const wbOut = XLSX.write(wb, { bookType: "xlsx", type: "array" });
|
|
137
|
+
const blob = new Blob([wbOut], {
|
|
138
|
+
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
139
|
+
});
|
|
140
|
+
url = URL.createObjectURL(blob);
|
|
141
|
+
const a = document.createElement("a");
|
|
142
|
+
a.href = url;
|
|
143
|
+
a.download = filename;
|
|
144
|
+
a.click();
|
|
145
|
+
} catch (err) {
|
|
146
|
+
console.error("Excel download failed:", err);
|
|
147
|
+
const detail = err instanceof Error ? err.message : "Unknown error";
|
|
148
|
+
window.alert(`Excel download failed: ${detail}\n\nYou can try the CSV download as an alternative.`);
|
|
149
|
+
} finally {
|
|
150
|
+
if (url) {
|
|
151
|
+
const blobUrl = url;
|
|
152
|
+
setTimeout(() => URL.revokeObjectURL(blobUrl), 10_000);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const SUGGESTIONS_RE = /<suggestions>\s*([\s\S]*?)\s*<\/suggestions>/g;
|
|
158
|
+
|
|
159
|
+
/** Extract follow-up suggestions from assistant message content and return the cleaned text + suggestions.
|
|
160
|
+
* All `<suggestions>` blocks are stripped; suggestions from all blocks are merged. */
|
|
161
|
+
export function parseSuggestions(content: string): { text: string; suggestions: string[] } {
|
|
162
|
+
const suggestions: string[] = [];
|
|
163
|
+
let match;
|
|
164
|
+
while ((match = SUGGESTIONS_RE.exec(content)) !== null) {
|
|
165
|
+
const lines = match[1].split("\n").map((l) => l.trim()).filter(Boolean);
|
|
166
|
+
suggestions.push(...lines);
|
|
167
|
+
}
|
|
168
|
+
SUGGESTIONS_RE.lastIndex = 0;
|
|
169
|
+
if (suggestions.length === 0) return { text: content, suggestions: [] };
|
|
170
|
+
const text = content.replace(SUGGESTIONS_RE, "").trimEnd();
|
|
171
|
+
SUGGESTIONS_RE.lastIndex = 0;
|
|
172
|
+
return { text, suggestions: suggestions.slice(0, 5) };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Normalize a record-or-array field from the API into a flat array.
|
|
177
|
+
* When the data is a Record, the key is merged in under `keyName`.
|
|
178
|
+
* Used by both admin entity-detail and schema explorer components.
|
|
179
|
+
*/
|
|
180
|
+
export function normalizeList<T>(
|
|
181
|
+
data: Record<string, T> | T[] | undefined,
|
|
182
|
+
keyName: string,
|
|
183
|
+
): (T & Record<string, unknown>)[] {
|
|
184
|
+
if (!data) return [];
|
|
185
|
+
if (Array.isArray(data)) return data as (T & Record<string, unknown>)[];
|
|
186
|
+
return Object.entries(data).map(([key, value]) => ({ ...value, [keyName]: key }));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Format a cell value: null as em-dash, numbers with locale formatting, else stringified. */
|
|
190
|
+
export function formatCell(value: unknown): string {
|
|
191
|
+
if (value == null) return "\u2014";
|
|
192
|
+
if (typeof value === "number") {
|
|
193
|
+
return Number.isInteger(value)
|
|
194
|
+
? value.toLocaleString()
|
|
195
|
+
: value.toLocaleString(undefined, { maximumFractionDigits: 2 });
|
|
196
|
+
}
|
|
197
|
+
return String(value);
|
|
198
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { ComponentType } from "react";
|
|
2
|
+
|
|
3
|
+
/** Props passed to custom tool renderers. */
|
|
4
|
+
export interface ToolRendererProps<T = unknown> {
|
|
5
|
+
/** Name of the tool being rendered. */
|
|
6
|
+
toolName: string;
|
|
7
|
+
/** Input arguments passed to the tool invocation. */
|
|
8
|
+
args: Record<string, unknown>;
|
|
9
|
+
/** Tool output. For built-in tools, `null` while the tool is still running. */
|
|
10
|
+
result: T;
|
|
11
|
+
/** Whether the tool invocation is still in progress. */
|
|
12
|
+
isLoading: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/* ------------------------------------------------------------------ */
|
|
16
|
+
/* Known tool result types */
|
|
17
|
+
/* ------------------------------------------------------------------ */
|
|
18
|
+
|
|
19
|
+
/** Result shape from the executeSQL tool. Subset covering fields most useful for rendering. */
|
|
20
|
+
export type SQLToolResult =
|
|
21
|
+
| {
|
|
22
|
+
success: true;
|
|
23
|
+
columns: string[];
|
|
24
|
+
rows: Record<string, unknown>[];
|
|
25
|
+
truncated?: boolean;
|
|
26
|
+
explanation?: string;
|
|
27
|
+
row_count?: number;
|
|
28
|
+
}
|
|
29
|
+
| {
|
|
30
|
+
success: false;
|
|
31
|
+
error: string;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/** Result shape from the explore tool (semantic layer exploration output). */
|
|
35
|
+
export type ExploreToolResult = string;
|
|
36
|
+
|
|
37
|
+
/** Result shape from the executePython tool. */
|
|
38
|
+
export type PythonToolResult =
|
|
39
|
+
| {
|
|
40
|
+
success: true;
|
|
41
|
+
output?: string;
|
|
42
|
+
explanation?: string;
|
|
43
|
+
table?: { columns: string[]; rows: unknown[][] };
|
|
44
|
+
charts?: { base64: string; mimeType: "image/png" }[];
|
|
45
|
+
rechartsCharts?: {
|
|
46
|
+
type: "line" | "bar" | "pie";
|
|
47
|
+
data: Record<string, unknown>[];
|
|
48
|
+
categoryKey: string;
|
|
49
|
+
valueKeys: string[];
|
|
50
|
+
}[];
|
|
51
|
+
}
|
|
52
|
+
| {
|
|
53
|
+
success: false;
|
|
54
|
+
error: string;
|
|
55
|
+
output?: string;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/* ------------------------------------------------------------------ */
|
|
59
|
+
/* Tool renderers map */
|
|
60
|
+
/* ------------------------------------------------------------------ */
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Map of tool names to custom renderer components.
|
|
64
|
+
*
|
|
65
|
+
* Known tool names (executeSQL, explore, executePython) get typed result generics.
|
|
66
|
+
* Arbitrary tool names are also supported via the index signature with `unknown` result type.
|
|
67
|
+
*
|
|
68
|
+
* Custom renderers take precedence over built-in defaults, including the action approval UI.
|
|
69
|
+
*/
|
|
70
|
+
export type ToolRenderers = {
|
|
71
|
+
executeSQL?: ComponentType<ToolRendererProps<SQLToolResult | null>>;
|
|
72
|
+
explore?: ComponentType<ToolRendererProps<ExploreToolResult | null>>;
|
|
73
|
+
executePython?: ComponentType<ToolRendererProps<PythonToolResult | null>>;
|
|
74
|
+
} & {
|
|
75
|
+
[toolName: string]: ComponentType<ToolRendererProps> | undefined;
|
|
76
|
+
};
|
package/src/lib/types.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for Atlas UI.
|
|
3
|
+
*
|
|
4
|
+
* All types are canonical from @useatlas/types — no local duplication.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { AUTH_MODES, DB_TYPES, CHAT_ERROR_CODES } from "@useatlas/types";
|
|
8
|
+
export type {
|
|
9
|
+
AuthMode,
|
|
10
|
+
MessageRole,
|
|
11
|
+
Surface,
|
|
12
|
+
Conversation,
|
|
13
|
+
Message,
|
|
14
|
+
ConversationWithMessages,
|
|
15
|
+
DBType,
|
|
16
|
+
HealthStatus,
|
|
17
|
+
ConnectionHealth,
|
|
18
|
+
ConnectionInfo,
|
|
19
|
+
ChatErrorCode,
|
|
20
|
+
ChatErrorInfo,
|
|
21
|
+
Dimension,
|
|
22
|
+
Join,
|
|
23
|
+
Measure,
|
|
24
|
+
QueryPattern,
|
|
25
|
+
SemanticEntitySummary,
|
|
26
|
+
SemanticEntityDetail,
|
|
27
|
+
EntityData,
|
|
28
|
+
} from "@useatlas/types";
|
|
29
|
+
export { authErrorMessage, parseChatError } from "@useatlas/types/errors";
|
package/src/lib/utils.ts
ADDED
package/src/styles.css
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @useatlas/react — Base styles
|
|
3
|
+
*
|
|
4
|
+
* Import this CSS in your app:
|
|
5
|
+
* import "@useatlas/react/styles.css";
|
|
6
|
+
*
|
|
7
|
+
* Also configure Tailwind CSS 4 to scan this package:
|
|
8
|
+
* @source "../node_modules/@useatlas/react/dist";
|
|
9
|
+
*
|
|
10
|
+
* These CSS custom properties define the Atlas design tokens.
|
|
11
|
+
* They match shadcn/ui's "neutral" base with "new-york" style.
|
|
12
|
+
* Scoped to .atlas-root to avoid clobbering host app styles.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
.atlas-root {
|
|
16
|
+
--radius: 0.625rem;
|
|
17
|
+
--background: oklch(1 0 0);
|
|
18
|
+
--foreground: oklch(0.145 0 0);
|
|
19
|
+
--card: oklch(1 0 0);
|
|
20
|
+
--card-foreground: oklch(0.145 0 0);
|
|
21
|
+
--popover: oklch(1 0 0);
|
|
22
|
+
--popover-foreground: oklch(0.145 0 0);
|
|
23
|
+
--primary: oklch(0.205 0 0);
|
|
24
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
25
|
+
--secondary: oklch(0.97 0 0);
|
|
26
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
27
|
+
--muted: oklch(0.97 0 0);
|
|
28
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
29
|
+
--accent: oklch(0.97 0 0);
|
|
30
|
+
--accent-foreground: oklch(0.205 0 0);
|
|
31
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
32
|
+
--destructive-foreground: oklch(0.577 0.245 27.325);
|
|
33
|
+
--border: oklch(0.922 0 0);
|
|
34
|
+
--input: oklch(0.922 0 0);
|
|
35
|
+
--ring: oklch(0.708 0 0);
|
|
36
|
+
--atlas-brand: oklch(0.759 0.148 167.71);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.dark .atlas-root {
|
|
40
|
+
--background: oklch(0.145 0 0);
|
|
41
|
+
--foreground: oklch(0.985 0 0);
|
|
42
|
+
--card: oklch(0.145 0 0);
|
|
43
|
+
--card-foreground: oklch(0.985 0 0);
|
|
44
|
+
--popover: oklch(0.145 0 0);
|
|
45
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
46
|
+
--primary: oklch(0.985 0 0);
|
|
47
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
48
|
+
--secondary: oklch(0.269 0 0);
|
|
49
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
50
|
+
--muted: oklch(0.269 0 0);
|
|
51
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
52
|
+
--accent: oklch(0.269 0 0);
|
|
53
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
54
|
+
--destructive: oklch(0.396 0.141 25.723);
|
|
55
|
+
--destructive-foreground: oklch(0.637 0.237 25.331);
|
|
56
|
+
--border: oklch(0.269 0 0);
|
|
57
|
+
--input: oklch(0.269 0 0);
|
|
58
|
+
--ring: oklch(0.439 0 0);
|
|
59
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Window } from "happy-dom";
|
|
2
|
+
|
|
3
|
+
const win = new Window({ url: "http://localhost:3000" });
|
|
4
|
+
|
|
5
|
+
const DOM_GLOBALS = [
|
|
6
|
+
"document", "navigator", "location", "history", "screen",
|
|
7
|
+
"HTMLElement", "HTMLInputElement", "HTMLTextAreaElement", "HTMLButtonElement",
|
|
8
|
+
"HTMLFormElement", "HTMLAnchorElement", "HTMLImageElement", "HTMLDivElement",
|
|
9
|
+
"HTMLSpanElement", "HTMLTableElement", "HTMLPreElement", "HTMLHeadingElement",
|
|
10
|
+
"HTMLParagraphElement", "HTMLUListElement", "HTMLOListElement", "HTMLLIElement",
|
|
11
|
+
"HTMLQuoteElement", "HTMLTableRowElement", "HTMLTableCellElement",
|
|
12
|
+
"HTMLTableSectionElement", "HTMLBRElement", "HTMLHRElement",
|
|
13
|
+
"HTMLSelectElement", "HTMLOptionElement", "HTMLCanvasElement",
|
|
14
|
+
"HTMLBodyElement", "HTMLHtmlElement", "HTMLStyleElement", "HTMLScriptElement",
|
|
15
|
+
"HTMLLinkElement", "HTMLMetaElement", "HTMLLabelElement",
|
|
16
|
+
"Element", "Node", "Text", "Comment", "Document", "DocumentFragment",
|
|
17
|
+
"NodeList", "HTMLCollection", "NamedNodeMap",
|
|
18
|
+
"DOMParser", "XMLSerializer", "Range", "Selection",
|
|
19
|
+
"TreeWalker", "NodeIterator", "NodeFilter",
|
|
20
|
+
"CSSStyleDeclaration", "CSSStyleSheet", "StyleSheet", "MediaQueryList",
|
|
21
|
+
"DOMTokenList", "DOMRect", "DOMRectReadOnly",
|
|
22
|
+
"Event", "MouseEvent", "KeyboardEvent", "FocusEvent", "InputEvent", "CustomEvent",
|
|
23
|
+
"PointerEvent", "WheelEvent", "UIEvent", "ErrorEvent", "ProgressEvent",
|
|
24
|
+
"AnimationEvent", "TransitionEvent", "ClipboardEvent",
|
|
25
|
+
"MutationObserver", "MutationRecord", "IntersectionObserver", "ResizeObserver",
|
|
26
|
+
"Headers", "Request", "Response", "URL", "URLSearchParams",
|
|
27
|
+
"Blob", "File", "FileReader", "FormData", "FileList",
|
|
28
|
+
"AbortController", "AbortSignal",
|
|
29
|
+
"SVGElement", "SVGSVGElement",
|
|
30
|
+
"Image", "getComputedStyle", "matchMedia",
|
|
31
|
+
"DOMException",
|
|
32
|
+
"XMLHttpRequest", "WebSocket",
|
|
33
|
+
"Storage", "localStorage", "sessionStorage",
|
|
34
|
+
] as const;
|
|
35
|
+
|
|
36
|
+
for (const key of DOM_GLOBALS) {
|
|
37
|
+
const val = (win as unknown as Record<string, unknown>)[key];
|
|
38
|
+
if (val !== undefined) {
|
|
39
|
+
try {
|
|
40
|
+
const descriptor = Object.getOwnPropertyDescriptor(globalThis, key);
|
|
41
|
+
if (descriptor && !descriptor.configurable) continue;
|
|
42
|
+
(globalThis as Record<string, unknown>)[key] = val;
|
|
43
|
+
} catch {
|
|
44
|
+
// skip non-configurable globals
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try { (globalThis as Record<string, unknown>).window = win; }
|
|
50
|
+
catch { /* skip */ }
|
|
51
|
+
try { (globalThis as Record<string, unknown>).self = win; }
|
|
52
|
+
catch { /* skip */ }
|
|
53
|
+
(globalThis as Record<string, unknown>).requestAnimationFrame = (cb: FrameRequestCallback) =>
|
|
54
|
+
setTimeout(cb, 0) as unknown as number;
|
|
55
|
+
(globalThis as Record<string, unknown>).cancelAnimationFrame = (id: number) => clearTimeout(id);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Widget bundle entry point.
|
|
3
|
+
*
|
|
4
|
+
* Produces a self-contained ESM file that exposes React, ReactDOM/client,
|
|
5
|
+
* and the AtlasChat component on `globalThis.AtlasWidget` so the widget
|
|
6
|
+
* host HTML can render without any external CDN dependencies.
|
|
7
|
+
*
|
|
8
|
+
* Built by `tsup` — see tsup.config.ts for the widget-specific config.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { createElement, Component } from "react";
|
|
12
|
+
import { createRoot } from "react-dom/client";
|
|
13
|
+
import { AtlasChat } from "./components/atlas-chat";
|
|
14
|
+
import { setTheme } from "./hooks/use-dark-mode";
|
|
15
|
+
|
|
16
|
+
const AtlasWidget = { createElement, Component, createRoot, AtlasChat, setTheme };
|
|
17
|
+
|
|
18
|
+
Object.assign(globalThis, { AtlasWidget });
|
|
19
|
+
|
|
20
|
+
export { createElement, Component, createRoot, AtlasChat, setTheme };
|
package/src/widget.css
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Widget Tailwind CSS source.
|
|
3
|
+
*
|
|
4
|
+
* Compiled by @tailwindcss/cli during `bun run build` to produce
|
|
5
|
+
* dist/widget.css — a static CSS file containing all Tailwind
|
|
6
|
+
* utility classes used by @useatlas/react components.
|
|
7
|
+
*
|
|
8
|
+
* Pre-compiled at build time to avoid runtime CSS generation overhead.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
@import "tailwindcss";
|
|
12
|
+
@source "./";
|