reachat 3.2.2 → 3.3.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.
Files changed (42) hide show
  1. package/dist/AppBar/AppBar.d.ts +1 -0
  2. package/dist/Chat.d.ts +3 -0
  3. package/dist/ChatInput/ChatInput.d.ts +10 -3
  4. package/dist/ChatInput/RichTextInput.d.ts +9 -3
  5. package/dist/Markdown/CodeHighlighter.d.ts +2 -0
  6. package/dist/Markdown/Markdown.d.ts +1 -0
  7. package/dist/SessionMessages/SessionMessage/MessageFile/renderers/index.d.ts +4 -4
  8. package/dist/SessionMessages/SessionMessagePanel.d.ts +4 -0
  9. package/dist/SessionMessages/SessionMessages.d.ts +4 -0
  10. package/dist/SessionsList/NewSessionButton.d.ts +1 -0
  11. package/dist/SessionsList/SessionListItem.d.ts +4 -0
  12. package/dist/index.css +267 -97
  13. package/dist/index.js +3792 -76
  14. package/dist/index.js.map +1 -1
  15. package/dist/stories/AgUi.stories.tsx +1 -1
  16. package/dist/stories/Charts.stories.tsx +1 -1
  17. package/dist/stories/Chat.stories.tsx +1 -1
  18. package/dist/stories/ChatBubble.stories.tsx +1 -1
  19. package/dist/stories/ChatSuggestions.stories.tsx +1 -1
  20. package/dist/stories/Companion.stories.tsx +3 -6
  21. package/dist/stories/ComponentCatalog.stories.tsx +1 -1
  22. package/dist/stories/ComponentError.stories.tsx +1 -1
  23. package/dist/stories/Console.stories.tsx +1 -1
  24. package/dist/stories/EnhancedInput.stories.tsx +4 -2
  25. package/dist/stories/MessageStatus.stories.tsx +1 -1
  26. package/dist/stories/Redact.stories.tsx +1 -1
  27. package/dist/stories/RichTextInput.stories.tsx +1 -1
  28. package/dist/stories/examples.ts +1 -1
  29. package/package.json +50 -61
  30. package/dist/CSVFileRenderer-CQkyw8il.js +0 -141
  31. package/dist/CSVFileRenderer-CQkyw8il.js.map +0 -1
  32. package/dist/DefaultFileRenderer-B0ZTdkz2.js +0 -15
  33. package/dist/DefaultFileRenderer-B0ZTdkz2.js.map +0 -1
  34. package/dist/ImageFileRenderer-C8tVW3I8.js +0 -6
  35. package/dist/ImageFileRenderer-C8tVW3I8.js.map +0 -1
  36. package/dist/PDFFileRenderer-DQdFS2l6.js +0 -9
  37. package/dist/PDFFileRenderer-DQdFS2l6.js.map +0 -1
  38. package/dist/docs.json +0 -3545
  39. package/dist/index-BiStNXJ4.js +0 -4014
  40. package/dist/index-BiStNXJ4.js.map +0 -1
  41. package/dist/index.umd.cjs +0 -4177
  42. package/dist/index.umd.cjs.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { FC, ReactNode, useState } from 'react';
2
- import { Meta } from '@storybook/react';
2
+ import { Meta } from '@storybook/react-vite';
3
3
  import {
4
4
  Chat,
5
5
  SessionsList,
@@ -1,4 +1,4 @@
1
- import { Meta } from '@storybook/react';
1
+ import { Meta } from '@storybook/react-vite';
2
2
  import { subHours } from 'date-fns';
3
3
  import {
4
4
  Chat,
@@ -1,4 +1,4 @@
1
- import { Meta } from '@storybook/react';
1
+ import { Meta } from '@storybook/react-vite';
2
2
  import {
3
3
  Chat,
4
4
  SessionMessages,
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import { ChatBubble } from 'reachat';
3
3
  import {
4
4
  Chat,
@@ -1,5 +1,5 @@
1
1
  import { useState } from 'react';
2
- import { Meta } from '@storybook/react';
2
+ import type { Meta } from '@storybook/react-vite';
3
3
  import {
4
4
  Chat,
5
5
  SessionsList,
@@ -1,4 +1,4 @@
1
- import { Meta } from '@storybook/react';
1
+ import { Meta } from '@storybook/react-vite';
2
2
  import {
3
3
  Chat,
4
4
  SessionsList,
@@ -100,13 +100,10 @@ export const Empty = () => {
100
100
  <Chat
101
101
  sessions={[]}
102
102
  viewType="companion"
103
+ className="flex flex-col"
103
104
  onDeleteSession={() => alert('delete!')}
104
105
  >
105
- <SessionsList>
106
- <NewSessionButton />
107
- <SessionGroups />
108
- </SessionsList>
109
- <div className="flex-1 h-full flex flex-col">
106
+ <div className="flex-1 min-h-0 flex flex-col">
110
107
  <SessionMessages
111
108
  newSessionContent={
112
109
  <div className="flex flex-col gap-2 items-center justify-center h-full">
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { Meta } from '@storybook/react';
2
+ import { Meta } from '@storybook/react-vite';
3
3
  import { subHours } from 'date-fns';
4
4
  import { z } from 'zod';
5
5
  import {
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { Meta } from '@storybook/react';
2
+ import { Meta } from '@storybook/react-vite';
3
3
  import {
4
4
  Chat,
5
5
  ComponentError,
@@ -1,6 +1,6 @@
1
1
  import { useState, useRef, FC, useContext, useEffect } from 'react';
2
2
  import AttachIcon from './assets/paperclip.svg?react';
3
- import { Meta } from '@storybook/react';
3
+ import { Meta } from '@storybook/react-vite';
4
4
  import {
5
5
  Chat,
6
6
  Session,
@@ -1,4 +1,4 @@
1
- import { Meta, StoryFn } from '@storybook/react';
1
+ import { Meta, StoryFn } from '@storybook/react-vite';
2
2
  import { useState, useCallback } from 'react';
3
3
  import {
4
4
  Chat,
@@ -252,7 +252,9 @@ export const WithAsyncSearch: StoryFn = () => {
252
252
  mentions={{
253
253
  items: sampleUsers,
254
254
  onSearch: searchUsers,
255
- renderEmpty: query => <span>No users found matching "{query}"</span>
255
+ renderEmpty: query => (
256
+ <span>No users found matching &quot;{query}&quot;</span>
257
+ )
256
258
  }}
257
259
  />
258
260
  </SessionMessagePanel>
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import { useEffect, useState } from 'react';
3
3
  import {
4
4
  MessageStatus,
@@ -1,4 +1,4 @@
1
- import { Meta } from '@storybook/react';
1
+ import { Meta } from '@storybook/react-vite';
2
2
  import {
3
3
  Chat,
4
4
  SessionMessages,
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import { useState } from 'react';
3
3
  import { RichTextInput, MentionItem, SlashCommandItem } from 'reachat';
4
4
  import { ChatContext } from 'reachat';
@@ -1,5 +1,5 @@
1
1
  import { Dispatch, SetStateAction } from 'react';
2
- import { Session } from '@/types';
2
+ import { Session } from 'reachat';
3
3
  import { subHours } from 'date-fns';
4
4
 
5
5
  /**
package/package.json CHANGED
@@ -1,15 +1,14 @@
1
1
  {
2
2
  "name": "reachat",
3
- "version": "3.2.2",
3
+ "version": "3.3.0",
4
4
  "description": "Chat UI for Building LLMs",
5
5
  "scripts": {
6
6
  "build-storybook": "storybook build",
7
- "build": "npm run build:js && npm run build:styles && npm run build:docs && npm run rewrite:stories",
7
+ "build": "npm run build:js && npm run build:styles && npm run rewrite:stories",
8
8
  "build:js": "vite build --mode library",
9
- "build:docs": "node scripts/docs.js",
10
9
  "build:styles": "npx @tailwindcss/cli -i ./src/index.css -o ./dist/index.css",
11
- "lint": "eslint --ext js,ts,tsx",
12
- "lint:fix": "eslint --ext js,ts,tsx --fix src",
10
+ "lint": "eslint src stories",
11
+ "lint:fix": "eslint src stories --fix",
13
12
  "lint:prettier": "prettier --loglevel warn --write 'src/**/*.{ts,tsx,js,jsx}'",
14
13
  "prettier": "prettier --loglevel warn --write 'src/**/*.{ts,tsx,js,jsx}'",
15
14
  "rewrite:stories": "node scripts/stories.cjs",
@@ -18,7 +17,7 @@
18
17
  "test:coverage": "vitest run --coverage --passWithNoTests",
19
18
  "test:visual": "test-storybook --url http://localhost:9009",
20
19
  "test:visual:update": "test-storybook --url http://localhost:9009 --updateSnapshot",
21
- "prepare": "husky install"
20
+ "prepare": "husky"
22
21
  },
23
22
  "license": "Apache-2.0",
24
23
  "files": [
@@ -26,16 +25,15 @@
26
25
  ],
27
26
  "type": "module",
28
27
  "types": "dist/index.d.ts",
29
- "main": "./dist/index.umd.cjs",
28
+ "main": "./dist/index.js",
30
29
  "module": "./dist/index.js",
31
30
  "source": "src/index.ts",
32
31
  "exports": {
33
32
  ".": {
34
33
  "types": "./dist/index.d.ts",
35
34
  "import": "./dist/index.js",
36
- "require": "./dist/index.umd.cjs"
35
+ "default": "./dist/index.js"
37
36
  },
38
- "./docs.json": "./dist/docs.json",
39
37
  "./stories/*": "./dist/stories/*",
40
38
  "./index.css": "./dist/index.css"
41
39
  },
@@ -45,23 +43,23 @@
45
43
  "@floating-ui/dom": "^1.6.0",
46
44
  "@floating-ui/react": "^0.27.6",
47
45
  "@radix-ui/react-slot": "^1.1.0",
48
- "@tiptap/core": "^3.15.3",
49
- "@tiptap/extension-document": "^3.15.3",
50
- "@tiptap/extension-hard-break": "^3.15.3",
51
- "@tiptap/extension-mention": "^3.15.3",
52
- "@tiptap/extension-paragraph": "^3.15.3",
53
- "@tiptap/extension-placeholder": "^3.15.3",
54
- "@tiptap/extension-text": "^3.15.3",
55
- "@tiptap/extensions": "^3.20.4",
56
- "@tiptap/pm": "^3.15.3",
57
- "@tiptap/react": "^3.15.3",
58
- "@tiptap/suggestion": "^3.20.4",
46
+ "@tiptap/core": "^3.25.0",
47
+ "@tiptap/extension-document": "^3.25.0",
48
+ "@tiptap/extension-hard-break": "^3.25.0",
49
+ "@tiptap/extension-mention": "^3.25.0",
50
+ "@tiptap/extension-paragraph": "^3.25.0",
51
+ "@tiptap/extension-placeholder": "^3.25.0",
52
+ "@tiptap/extension-text": "^3.25.0",
53
+ "@tiptap/extensions": "^3.25.0",
54
+ "@tiptap/pm": "^3.25.0",
55
+ "@tiptap/react": "^3.25.0",
56
+ "@tiptap/suggestion": "^3.25.0",
59
57
  "date-fns": "^4.1.0",
60
58
  "katex": "^0.16.11",
61
59
  "lodash": "^4.17.21",
62
60
  "mdast-util-find-and-replace": "^3.0.1",
63
61
  "motion": "^12.4.2",
64
- "reablocks": "^10.0.0",
62
+ "reablocks": "^10.2.0",
65
63
  "react-markdown": "^10.0.0",
66
64
  "react-syntax-highlighter": "^16.1.0",
67
65
  "reakeys": "^2.0.3",
@@ -85,56 +83,53 @@
85
83
  }
86
84
  },
87
85
  "devDependencies": {
88
- "@storybook/addon-docs": "^10.0.0",
89
- "@storybook/addon-themes": "^10.0.0",
90
- "@storybook/react": "^10.0.0",
91
- "@storybook/react-vite": "^10.0.0",
86
+ "@eslint/js": "^9.39.4",
87
+ "@storybook/addon-docs": "^10.4.2",
88
+ "@storybook/addon-themes": "^10.4.2",
89
+ "@storybook/react": "^10.4.2",
90
+ "@storybook/react-vite": "^10.4.2",
92
91
  "@storybook/test-runner": "^0.24.0",
93
- "@tailwindcss/cli": "^4.0.3",
94
- "@tailwindcss/postcss": "^4.0.1",
95
- "@tailwindcss/vite": "^4.0.6",
92
+ "@tailwindcss/cli": "^4.3.0",
93
+ "@tailwindcss/postcss": "^4.3.0",
94
+ "@tailwindcss/vite": "^4.3.0",
96
95
  "@types/lodash": "^4.17.21",
97
96
  "@types/mdast": "^4.0.4",
98
97
  "@types/react": "^19.0.0",
99
98
  "@types/react-dom": "^19.0.0",
100
- "@typescript-eslint/eslint-plugin": "^7.6.0",
101
- "@typescript-eslint/parser": "^7.6.0",
102
- "@vitejs/plugin-react": "^5.0.0",
103
- "@vitest/coverage-v8": "^3.2.0",
104
- "autoprefixer": "^10.4.19",
105
- "eslint": "^8.57.0",
106
- "eslint-config-prettier": "^9.1.0",
99
+ "@typescript-eslint/eslint-plugin": "^8.60.1",
100
+ "@typescript-eslint/parser": "^8.60.1",
101
+ "@vitejs/plugin-react": "^6.0.2",
102
+ "@vitest/coverage-v8": "^4.1.8",
103
+ "eslint": "^9.39.4",
104
+ "eslint-config-prettier": "^10.1.8",
107
105
  "eslint-plugin-no-relative-import-paths": "^1.5.4",
108
106
  "eslint-plugin-react": "^7.34.1",
109
- "eslint-plugin-react-hooks": "^4.3.0",
110
- "eslint-plugin-storybook": "^0.12.0",
107
+ "eslint-plugin-react-hooks": "^7.1.1",
108
+ "eslint-plugin-storybook": "^10.4.2",
111
109
  "fast-glob": "^3.3.2",
110
+ "globals": "^17.6.0",
112
111
  "husky": "^9.0.11",
113
112
  "jest-image-snapshot": "^6.4.0",
114
- "jsdom": "^24.0.0",
115
- "lint-staged": "^15.2.2",
113
+ "jiti": "^2.7.0",
114
+ "jsdom": "^29.1.1",
115
+ "lint-staged": "^16.4.0",
116
116
  "postcss": "^8.4.39",
117
- "postcss-nested": "^6.0.1",
118
- "postcss-preset-env": "^9.5.2",
119
117
  "prettier": "^3.2.5",
120
118
  "react": "^19.0.0",
121
- "react-docgen-typescript": "^2.2.2",
122
119
  "react-dom": "^19.0.0",
123
120
  "reaviz": "^16.1.1",
124
121
  "rollup-plugin-peer-deps-external": "2.2.4",
125
- "storybook": "^10.0.0",
126
- "tailwindcss": "^4.0.0",
127
- "tw-colors": "^3.3.1",
128
- "typescript": "^5.9.3",
122
+ "storybook": "^10.4.2",
123
+ "tailwindcss": "^4.3.0",
124
+ "typescript": "^6.0.3",
129
125
  "typescript-rewrite-paths": "^1.3.1",
130
- "vite": "^7.0.0",
131
- "vite-plugin-checker": "^0.12.0",
132
- "vite-plugin-css-injected-by-js": "^3.5.0",
133
- "vite-plugin-dts": "^4.5.0",
134
- "vite-plugin-static-copy": "^3.1.1",
135
- "vite-plugin-svgr": "^4.2.0",
136
- "vite-tsconfig-paths": "^4.3.2",
137
- "vitest": "^3.2.0"
126
+ "vite": "^8.0.16",
127
+ "vite-plugin-checker": "^0.14.1",
128
+ "vite-plugin-css-injected-by-js": "^5.0.1",
129
+ "vite-plugin-dts": "^5.0.2",
130
+ "vite-plugin-static-copy": "^4.1.0",
131
+ "vite-plugin-svgr": "^5.2.0",
132
+ "vitest": "^4.1.8"
138
133
  },
139
134
  "prettier": {
140
135
  "semi": true,
@@ -146,13 +141,7 @@
146
141
  },
147
142
  "lint-staged": {
148
143
  "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [
149
- "prettier --write",
150
- "git add"
144
+ "prettier --write"
151
145
  ]
152
- },
153
- "husky": {
154
- "hooks": {
155
- "pre-commit": "lint-staged"
156
- }
157
146
  }
158
147
  }
@@ -1,141 +0,0 @@
1
- import { jsxs, jsx } from "react/jsx-runtime";
2
- import * as React from "react";
3
- import { useState, useRef, useEffect } from "react";
4
- import { AnimatePresence, motion } from "motion/react";
5
- import { a as SvgCopy } from "./index-BiStNXJ4.js";
6
- import { IconButton } from "reablocks";
7
- const sanitizeSVGCell = (cell) => {
8
- const trimmed = cell.trim();
9
- const escaped = trimmed.replace(/"/g, '""');
10
- const prefix = /^[=+\-@]/.test(trimmed) ? "'" : "";
11
- const needsQuotes = /[",\n\r]/.test(escaped) || prefix;
12
- return needsQuotes ? `"${prefix}${escaped}"` : escaped;
13
- };
14
- const parseCSV = (csvString) => {
15
- try {
16
- const rows = csvString.split("\n");
17
- return rows.map((row) => row.split(",").map((cell) => sanitizeSVGCell(cell)));
18
- } catch (error) {
19
- console.error("Error parsing CSV:", error);
20
- throw new Error("Failed to parse CSV file.");
21
- }
22
- };
23
- const SvgDownload = (props) => /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: 24, height: 24, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1, strokeLinecap: "round", strokeLinejoin: "round", className: "lucide lucide-cloud-download", ...props }, /* @__PURE__ */ React.createElement("path", { d: "M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242" }), /* @__PURE__ */ React.createElement("path", { d: "M12 12v9" }), /* @__PURE__ */ React.createElement("path", { d: "m8 17 4 4 4-4" }));
24
- const CSVFileRenderer = ({ name, url, fileIcon }) => {
25
- const [isLoading, setIsLoading] = useState(true);
26
- const [csvData, setCsvData] = useState([]);
27
- const [error, setError] = useState(null);
28
- const [isModalOpen, setIsModalOpen] = useState(false);
29
- const modalRef = useRef(null);
30
- useEffect(() => {
31
- const fetchCsvData = async () => {
32
- try {
33
- setIsLoading(true);
34
- const response = await fetch(url);
35
- const data = parseCSV(await response.text());
36
- setCsvData(data);
37
- } catch {
38
- setError("Failed to load CSV file.");
39
- } finally {
40
- setIsLoading(false);
41
- }
42
- };
43
- fetchCsvData();
44
- }, [url]);
45
- const toggleModal = () => {
46
- setIsModalOpen((prev) => !prev);
47
- };
48
- const handleClickOutside = (event) => {
49
- if (modalRef.current && !modalRef.current.contains(event.target)) {
50
- setIsModalOpen(false);
51
- }
52
- };
53
- useEffect(() => {
54
- if (isModalOpen) {
55
- document.addEventListener("mousedown", handleClickOutside);
56
- } else {
57
- document.removeEventListener("mousedown", handleClickOutside);
58
- }
59
- return () => {
60
- document.removeEventListener("mousedown", handleClickOutside);
61
- };
62
- }, [isModalOpen]);
63
- const downloadCSV = () => {
64
- if (csvData.length === 0) return;
65
- const csvContent = csvData.map((row) => row.join(",")).join("\n");
66
- const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
67
- const url2 = URL.createObjectURL(blob);
68
- const link = document.createElement("a");
69
- link.href = url2;
70
- link.setAttribute("download", `${name || "data"}`);
71
- document.body.appendChild(link);
72
- link.click();
73
- document.body.removeChild(link);
74
- };
75
- const renderTable = (data, maxRows) => /* @__PURE__ */ jsxs(
76
- motion.table,
77
- {
78
- layout: true,
79
- className: "w-full",
80
- transition: { type: "spring", stiffness: 100, damping: 20 },
81
- children: [
82
- /* @__PURE__ */ jsx("thead", { className: "sticky top-0 bg-gray-200 dark:bg-gray-800 z-10", children: /* @__PURE__ */ jsxs("tr", { children: [
83
- /* @__PURE__ */ jsx("th", { className: "py-4 px-6", children: "#" }),
84
- data[0].map((header, index) => /* @__PURE__ */ jsx("th", { className: "py-4 px-6", children: header }, `header-${index}`))
85
- ] }) }),
86
- /* @__PURE__ */ jsx("tbody", { children: data.slice(1, maxRows).map((row, rowIndex) => /* @__PURE__ */ jsxs(
87
- "tr",
88
- {
89
- className: "border-b border-panel-accent light:border-gray-700 hover:bg-panel-accent hover:light:bg-gray-700/40 transition-colors text-base",
90
- children: [
91
- /* @__PURE__ */ jsx("td", { className: "py-4 px-6", children: rowIndex + 1 }),
92
- row.map((cell, cellIndex) => /* @__PURE__ */ jsx("td", { className: "py-4 px-6", children: cell }, `cell-${rowIndex}-${cellIndex}`))
93
- ]
94
- },
95
- `row-${rowIndex}`
96
- )) })
97
- ]
98
- }
99
- );
100
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
101
- /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center gap-4", children: [
102
- /* @__PURE__ */ jsxs("div", { className: "csv-icon flex items-center", children: [
103
- fileIcon,
104
- name && /* @__PURE__ */ jsx("figcaption", { className: "ml-1", children: name })
105
- ] }),
106
- /* @__PURE__ */ jsxs("div", { className: "csv-icon flex items-center gap-6", children: [
107
- /* @__PURE__ */ jsx(IconButton, { size: "small", variant: "text", onClick: downloadCSV, children: /* @__PURE__ */ jsx(SvgDownload, {}) }),
108
- /* @__PURE__ */ jsx(IconButton, { size: "small", variant: "text", onClick: toggleModal, children: /* @__PURE__ */ jsx(SvgCopy, {}) })
109
- ] })
110
- ] }),
111
- error && /* @__PURE__ */ jsx("div", { className: "error-message", children: error }),
112
- isLoading && !csvData && /* @__PURE__ */ jsx("div", { className: "text-text-secondary", children: "Loading..." }),
113
- /* @__PURE__ */ jsx("div", { className: "flex justify-between", children: !error && csvData.length > 0 && renderTable(csvData, 6) }),
114
- /* @__PURE__ */ jsx(AnimatePresence, { children: isModalOpen && /* @__PURE__ */ jsx(
115
- motion.div,
116
- {
117
- className: "fixed inset-0 bg-black/70 flex justify-center items-center z-50",
118
- initial: { opacity: 0 },
119
- animate: { opacity: 1 },
120
- exit: { opacity: 0 },
121
- transition: { duration: 0.3 },
122
- children: /* @__PURE__ */ jsx(
123
- motion.div,
124
- {
125
- ref: modalRef,
126
- className: "bg-white dark:bg-gray-900 rounded-md w-11/12 h-5/6 overflow-auto",
127
- initial: { scale: 0.8 },
128
- animate: { scale: 1 },
129
- exit: { scale: 0.8 },
130
- transition: { duration: 0.3 },
131
- children: !error && csvData.length > 0 && renderTable(csvData)
132
- }
133
- )
134
- }
135
- ) })
136
- ] });
137
- };
138
- export {
139
- CSVFileRenderer as default
140
- };
141
- //# sourceMappingURL=CSVFileRenderer-CQkyw8il.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"CSVFileRenderer-CQkyw8il.js","sources":["../src/utils/sanitize.ts","../src/utils/parseCSV.ts","../src/assets/download.svg?react","../src/SessionMessages/SessionMessage/MessageFile/renderers/CSVFileRenderer.tsx"],"sourcesContent":["\n/**\n * Sanitizes cell content to prevent CSV injection and other potential vulnerabilities.\n * Based on the documentation of OWASP for CSV Injection\n * https://owasp.org/www-community/attacks/CSV_Injection\n * @param cell The cell content to sanitize.\n * @returns The sanitized cell content.\n */\nexport const sanitizeSVGCell = (cell: string): string => {\n const trimmed = cell.trim();\n // Escape double quotes by doubling them\n const escaped = trimmed.replace(/\"/g, '\"\"');\n // Add single quote prefix only for potentially dangerous content\n const prefix = /^[=+\\-@]/.test(trimmed) ? '\\'' : '';\n // Only wrap in quotes if the content contains special characters\n const needsQuotes = /[\",\\n\\r]/.test(escaped) || prefix;\n\n return needsQuotes ? `\"${prefix}${escaped}\"` : escaped;\n};\n","import { sanitizeSVGCell } from './sanitize';\n\n/**\n * Parses a CSV string from a local file and returns an array of rows.\n * Sanitizes cell data to prevent injection attacks.\n * @param csvString The raw CSV string content to parse.\n * @returns The parsed CSV data as a 2D array of strings.\n */\nexport const parseCSV = (csvString: string): string[][] => {\n try {\n const rows = csvString.split('\\n');\n return rows.map((row) => row.split(',').map((cell) => sanitizeSVGCell(cell)));\n } catch (error) {\n console.error('Error parsing CSV:', error);\n throw new Error('Failed to parse CSV file.');\n }\n};\n","import * as React from \"react\";\nconst SvgDownload = (props) => /* @__PURE__ */ React.createElement(\"svg\", { xmlns: \"http://www.w3.org/2000/svg\", width: 24, height: 24, viewBox: \"0 0 24 24\", fill: \"none\", stroke: \"currentColor\", strokeWidth: 1, strokeLinecap: \"round\", strokeLinejoin: \"round\", className: \"lucide lucide-cloud-download\", ...props }, /* @__PURE__ */ React.createElement(\"path\", { d: \"M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242\" }), /* @__PURE__ */ React.createElement(\"path\", { d: \"M12 12v9\" }), /* @__PURE__ */ React.createElement(\"path\", { d: \"m8 17 4 4 4-4\" }));\nexport default SvgDownload;\n","import { FC, useEffect, useState, ReactElement, useRef } from 'react';\nimport { motion, AnimatePresence } from 'motion/react';\nimport { parseCSV } from '@/utils/parseCSV';\nimport DownloadIcon from '@/assets/download.svg?react';\nimport PlaceholderIcon from '@/assets/copy.svg?react';\nimport { IconButton } from 'reablocks';\n\ninterface CSVFileRendererProps {\n /**\n * Name of the file.\n */\n name?: string;\n\n /**\n * URL of the file.\n */\n url: string;\n\n /**\n * Icon to for file type.\n */\n fileIcon?: ReactElement;\n}\n\n/**\n * Renderer for CSV files that fetches and displays a snippet of the file data.\n */\nconst CSVFileRenderer: FC<CSVFileRendererProps> = ({ name, url, fileIcon }) => {\n const [isLoading, setIsLoading] = useState<boolean>(true);\n const [csvData, setCsvData] = useState<string[][]>([]);\n const [error, setError] = useState<string | null>(null);\n const [isModalOpen, setIsModalOpen] = useState(false);\n const modalRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const fetchCsvData = async () => {\n try {\n setIsLoading(true);\n const response = await fetch(url);\n const data = parseCSV(await response.text());\n setCsvData(data);\n } catch {\n setError('Failed to load CSV file.');\n } finally {\n setIsLoading(false);\n }\n };\n\n fetchCsvData();\n }, [url]);\n\n const toggleModal = () => {\n setIsModalOpen(prev => !prev);\n };\n\n const handleClickOutside = (event: MouseEvent) => {\n if (modalRef.current && !modalRef.current.contains(event.target as Node)) {\n setIsModalOpen(false);\n }\n };\n\n useEffect(() => {\n if (isModalOpen) {\n document.addEventListener('mousedown', handleClickOutside);\n } else {\n document.removeEventListener('mousedown', handleClickOutside);\n }\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n };\n }, [isModalOpen]);\n\n const downloadCSV = () => {\n if (csvData.length === 0) return;\n\n const csvContent = csvData.map(row => row.join(',')).join('\\n');\n const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.setAttribute('download', `${name || 'data'}`);\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n };\n\n const renderTable = (data: string[][], maxRows?: number) => (\n <motion.table\n layout\n className=\"w-full\"\n transition={{ type: 'spring', stiffness: 100, damping: 20 }}\n >\n <thead className=\"sticky top-0 bg-gray-200 dark:bg-gray-800 z-10\">\n <tr>\n <th className=\"py-4 px-6\">#</th>\n {data[0].map((header, index) => (\n <th key={`header-${index}`} className=\"py-4 px-6\">\n {header}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {data.slice(1, maxRows).map((row, rowIndex) => (\n <tr\n key={`row-${rowIndex}`}\n className=\"border-b border-panel-accent light:border-gray-700 hover:bg-panel-accent hover:light:bg-gray-700/40 transition-colors text-base\"\n >\n <td className=\"py-4 px-6\">{rowIndex + 1}</td>\n {row.map((cell, cellIndex) => (\n <td key={`cell-${rowIndex}-${cellIndex}`} className=\"py-4 px-6\">\n {cell}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </motion.table>\n );\n\n return (\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex justify-between items-center gap-4\">\n <div className=\"csv-icon flex items-center\">\n {fileIcon}\n {name && <figcaption className=\"ml-1\">{name}</figcaption>}\n </div>\n <div className=\"csv-icon flex items-center gap-6\">\n <IconButton size=\"small\" variant=\"text\" onClick={downloadCSV}>\n <DownloadIcon />\n </IconButton>\n <IconButton size=\"small\" variant=\"text\" onClick={toggleModal}>\n <PlaceholderIcon />\n </IconButton>\n </div>\n </div>\n\n {error && <div className=\"error-message\">{error}</div>}\n\n {isLoading && !csvData && (\n <div className=\"text-text-secondary\">Loading...</div>\n )}\n\n <div className=\"flex justify-between\">\n {!error && csvData.length > 0 && renderTable(csvData, 6)}\n </div>\n\n <AnimatePresence>\n {isModalOpen && (\n <motion.div\n className=\"fixed inset-0 bg-black/70 flex justify-center items-center z-50\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.3 }}\n >\n <motion.div\n ref={modalRef}\n className=\"bg-white dark:bg-gray-900 rounded-md w-11/12 h-5/6 overflow-auto\"\n initial={{ scale: 0.8 }}\n animate={{ scale: 1 }}\n exit={{ scale: 0.8 }}\n transition={{ duration: 0.3 }}\n >\n {!error && csvData.length > 0 && renderTable(csvData)}\n </motion.div>\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n );\n};\n\nexport default CSVFileRenderer;\n"],"names":["url","DownloadIcon","PlaceholderIcon"],"mappings":"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"}
@@ -1,15 +0,0 @@
1
- import { jsxs, jsx } from "react/jsx-runtime";
2
- import { S as SvgFile } from "./index-BiStNXJ4.js";
3
- import { Ellipsis, cn } from "reablocks";
4
- const DefaultFileRenderer = ({
5
- name,
6
- limit = 100,
7
- fileIcon = /* @__PURE__ */ jsx(SvgFile, {})
8
- }) => /* @__PURE__ */ jsxs("figure", { className: "flex items-center gap-2", children: [
9
- fileIcon,
10
- name && /* @__PURE__ */ jsx("figcaption", { className: cn("file-name-class"), children: /* @__PURE__ */ jsx(Ellipsis, { value: name, limit }) })
11
- ] });
12
- export {
13
- DefaultFileRenderer as default
14
- };
15
- //# sourceMappingURL=DefaultFileRenderer-B0ZTdkz2.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"DefaultFileRenderer-B0ZTdkz2.js","sources":["../src/SessionMessages/SessionMessage/MessageFile/renderers/DefaultFileRenderer.tsx"],"sourcesContent":["import { FC, ReactElement } from 'react';\nimport FileIcon from '@/assets/file.svg?react';\nimport { Ellipsis, cn } from 'reablocks';\n\ninterface DefaultFileRendererProps {\n /**\n * Limit for the name.\n */\n limit?: number;\n\n /**\n * Name of the file.\n */\n name?: string;\n\n /**\n * URL of the file.\n */\n url: string;\n\n /**\n * Icon to for file type.\n */\n fileIcon?: ReactElement;\n}\n\n/**\n * Default renderer for unspecified file types.\n */\nconst DefaultFileRenderer: FC<DefaultFileRendererProps> = ({\n name,\n limit = 100,\n fileIcon = <FileIcon />,\n}) => (\n <figure className=\"flex items-center gap-2\">\n {fileIcon}\n {name && (\n <figcaption className={cn('file-name-class')}>\n <Ellipsis value={name} limit={limit} />\n </figcaption>\n )}\n </figure>\n);\n\nexport default DefaultFileRenderer;\n"],"names":["FileIcon"],"mappings":";;;AA6BA,MAAM,sBAAoD,CAAC;AAAA,EACzD;AAAA,EACA,QAAQ;AAAA,EACR,+BAAYA,SAAA,CAAA,CAAS;AACvB,MACE,qBAAC,UAAA,EAAO,WAAU,2BACf,UAAA;AAAA,EAAA;AAAA,EACA,QACC,oBAAC,cAAA,EAAW,WAAW,GAAG,iBAAiB,GACzC,UAAA,oBAAC,UAAA,EAAS,OAAO,MAAM,MAAA,CAAc,EAAA,CACvC;AAAA,EAAA,CAEJ;"}
@@ -1,6 +0,0 @@
1
- import { jsx } from "react/jsx-runtime";
2
- const ImageFileRenderer = ({ url }) => /* @__PURE__ */ jsx("img", { src: url, alt: "Image", className: "size-10" });
3
- export {
4
- ImageFileRenderer as default
5
- };
6
- //# sourceMappingURL=ImageFileRenderer-C8tVW3I8.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ImageFileRenderer-C8tVW3I8.js","sources":["../src/SessionMessages/SessionMessage/MessageFile/renderers/ImageFileRenderer.tsx"],"sourcesContent":["import { FC } from 'react';\n\ninterface ImageFileRendererProps {\n /**\n * Name of the file.\n */\n name?: string;\n\n /**\n * URL of the file.\n */\n url: string;\n}\n\n/**\n * Renderer for image files.\n */\nconst ImageFileRenderer: FC<ImageFileRendererProps> = ({ url }) => (\n <img src={url} alt=\"Image\" className=\"size-10\" />\n);\n\nexport default ImageFileRenderer;\n"],"names":[],"mappings":";AAiBA,MAAM,oBAAgD,CAAC,EAAE,IAAA,MACvD,oBAAC,OAAA,EAAI,KAAK,KAAK,KAAI,SAAQ,WAAU,UAAA,CAAU;"}
@@ -1,9 +0,0 @@
1
- import { jsxs, jsx } from "react/jsx-runtime";
2
- const PDFFileRenderer = ({ name, url, fileIcon }) => /* @__PURE__ */ jsxs("figure", { className: "csv-icon flex items-center gap-2", onClick: () => window.open(url, "_blank"), children: [
3
- fileIcon,
4
- name && /* @__PURE__ */ jsx("figcaption", { className: "file-name", children: name })
5
- ] });
6
- export {
7
- PDFFileRenderer as default
8
- };
9
- //# sourceMappingURL=PDFFileRenderer-DQdFS2l6.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"PDFFileRenderer-DQdFS2l6.js","sources":["../src/SessionMessages/SessionMessage/MessageFile/renderers/PDFFileRenderer.tsx"],"sourcesContent":["import { FC, ReactElement } from 'react';\n\ninterface PDFFileRendererProps {\n /**\n * Name of the file.\n */\n name?: string;\n\n /**\n * URL of the file.\n */\n url: string;\n\n /**\n * Icon to for file type.\n */\n fileIcon?: ReactElement;\n}\n\n/**\n * Renderer for PDF files.\n */\nconst PDFFileRenderer: FC<PDFFileRendererProps> = ({ name, url, fileIcon }) => (\n <figure className=\"csv-icon flex items-center gap-2\" onClick={() => window.open(url, '_blank')}>\n {fileIcon}\n {name && <figcaption className=\"file-name\">{name}</figcaption>}\n </figure>\n);\n\nexport default PDFFileRenderer;\n"],"names":[],"mappings":";AAsBA,MAAM,kBAA4C,CAAC,EAAE,MAAM,KAAK,eAC9D,qBAAC,UAAA,EAAO,WAAU,oCAAmC,SAAS,MAAM,OAAO,KAAK,KAAK,QAAQ,GAC1F,UAAA;AAAA,EAAA;AAAA,EACA,QAAQ,oBAAC,cAAA,EAAW,WAAU,aAAa,UAAA,KAAA,CAAK;AAAA,EAAA,CACnD;"}