react-beauty-link 1.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Andre Desbiens
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,220 @@
1
+ # react-beauty-link
2
+
3
+ A React hook that automatically converts URLs in text into beautiful, clickable links with page titles, favicons, and file type icons.
4
+
5
+ ## Features
6
+
7
+ - 🔗 **Automatic URL Detection** - Finds and converts HTTPS URLs in text
8
+ - 🎨 **Beautiful Link Previews** - Shows page titles instead of raw URLs
9
+ - 🌐 **Favicon Display** - Fetches and displays website favicons
10
+ - 📄 **File Type Icons** - Shows Nerd Font icons for 60+ file types (PDF, DOC, images, code, etc.)
11
+ - ⚙️ **Configurable** - Control how links open (new tab, new window, or same tab)
12
+ - 📏 **Smart Truncation** - Limits title length to 60 characters
13
+ - 🎯 **TypeScript Support** - Full type safety included
14
+ - 🚀 **Lightweight** - No heavy dependencies
15
+ - ⚡ **Fast** - Skips metadata fetching for file URLs
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install react-beauty-link
21
+ ```
22
+
23
+ Or with yarn:
24
+
25
+ ```bash
26
+ yarn add react-beauty-link
27
+ ```
28
+
29
+ Or with pnpm:
30
+
31
+ ```bash
32
+ pnpm add react-beauty-link
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ ### Basic Example
38
+
39
+ ```tsx
40
+ import { useBeautyLink } from 'react-beauty-link';
41
+
42
+ function App() {
43
+ const text = "Check out https://react.dev for React docs!";
44
+ const linkedContent = useBeautyLink(text);
45
+
46
+ return <div>{linkedContent}</div>;
47
+ }
48
+ ```
49
+
50
+ ### With File Links
51
+
52
+ The hook automatically detects file extensions and shows appropriate icons:
53
+
54
+ ```tsx
55
+ import { useBeautyLink } from 'react-beauty-link';
56
+
57
+ function App() {
58
+ const text = `
59
+ Download report: https://example.com/quarterly-report.pdf
60
+ View code: https://github.com/user/repo/main.tsx
61
+ Get package: https://example.com/app.zip
62
+ `;
63
+ const linkedContent = useBeautyLink(text);
64
+
65
+ return <div>{linkedContent}</div>;
66
+ }
67
+ ```
68
+
69
+ Result:
70
+ - 📕 `quarterly-report.pdf` (with red PDF icon)
71
+ - ⚛️ `main.tsx` (with React icon)
72
+ - 📦 `app.zip` (with archive icon)
73
+
74
+ ### With Custom Target
75
+
76
+ Control how links open:
77
+
78
+ ```tsx
79
+ import { useBeautyLink } from 'react-beauty-link';
80
+
81
+ function App() {
82
+ const text = "Visit https://github.com and https://npmjs.com";
83
+
84
+ // Open in new tab (default)
85
+ const newTabLinks = useBeautyLink(text, 'new-tab');
86
+
87
+ // Open in new window
88
+ const newWindowLinks = useBeautyLink(text, 'new-window');
89
+
90
+ // Open in same tab
91
+ const sameTabLinks = useBeautyLink(text, 'self');
92
+
93
+ return (
94
+ <div>
95
+ <p>{newTabLinks}</p>
96
+ <p>{newWindowLinks}</p>
97
+ <p>{sameTabLinks}</p>
98
+ </div>
99
+ );
100
+ }
101
+ ```
102
+
103
+ ### TypeScript
104
+
105
+ ```tsx
106
+ import { useBeautyLink, LinkTarget } from 'react-beauty-link';
107
+
108
+ function App() {
109
+ const text = "Check out https://typescript.org";
110
+ const target: LinkTarget = 'new-tab'; // 'new-tab' | 'new-window' | 'self'
111
+ const linkedContent = useBeautyLink(text, target);
112
+
113
+ return <div>{linkedContent}</div>;
114
+ }
115
+ ```
116
+
117
+ ## API
118
+
119
+ ### `useBeautyLink(text: string, target?: LinkTarget): ReactNode[]`
120
+
121
+ Converts URLs in text to clickable links with titles and favicons.
122
+
123
+ #### Parameters
124
+
125
+ - **`text`** (string, required): The text containing URLs to linkify
126
+ - **`target`** (LinkTarget, optional): How links should open
127
+ - `'new-tab'` (default): Opens in a new browser tab
128
+ - `'new-window'`: Opens in a new browser window (800x600)
129
+ - `'self'`: Opens in the same tab
130
+
131
+ #### Returns
132
+
133
+ - Array of React nodes containing text and link elements
134
+
135
+ ### Types
136
+
137
+ ```typescript
138
+ type LinkTarget = 'new-tab' | 'new-window' | 'self';
139
+ ```
140
+
141
+ ## How It Works
142
+
143
+ 1. **URL Detection**: Scans text for HTTPS URLs (matches until first space)
144
+ 2. **File Type Check**: If URL has a file extension, shows Nerd Font icon and filename
145
+ 3. **Metadata Fetching**: For regular URLs, retrieves page title and favicon via CORS proxies
146
+ 4. **Fallback Handling**: Uses Google's favicon service if fetching fails
147
+ 5. **Rendering**: Creates beautiful links with icons and truncated titles
148
+
149
+ ## Supported File Types
150
+
151
+ The hook supports 60+ file types with Nerd Font icons:
152
+
153
+ - **Documents**: PDF, DOC, DOCX, XLS, XLSX, PPT, PPTX, TXT
154
+ - **Code**: JS, TS, JSX, TSX, PY, JAVA, PHP, RB, GO, RS, HTML, CSS, JSON, MD, SQL
155
+ - **Media**: JPG, PNG, GIF, SVG, MP4, AVI, MOV, MP3, WAV, FLAC
156
+ - **Archives**: ZIP, RAR, 7Z, TAR, GZ
157
+
158
+ See [FILE_TYPE_SUPPORT.md](FILE_TYPE_SUPPORT.md) for the complete list with colors.
159
+
160
+ ## Examples
161
+
162
+ ### Before
163
+ ```
164
+ Check out https://react.dev for React documentation
165
+ ```
166
+
167
+ ### After
168
+ ```
169
+ Check out [🌐 React] for React documentation
170
+ ```
171
+ (Where [🌐 React] is a clickable link with the actual favicon and page title)
172
+
173
+ ## Styling
174
+
175
+ Links are rendered with the following default styles:
176
+ - Color: `#646cff`
177
+ - Text decoration: `underline`
178
+ - Display: `inline-flex` with icons and text aligned
179
+ - Icon size: `16x16px`
180
+
181
+ You can override these styles using CSS:
182
+
183
+ ```css
184
+ /* Target all linkified links */
185
+ a[target="_blank"] {
186
+ color: #your-color;
187
+ text-decoration: none;
188
+ }
189
+ ```
190
+
191
+ ## Browser Support
192
+
193
+ Works in all modern browsers that support:
194
+ - ES2020
195
+ - React 18+
196
+ - Fetch API
197
+ - DOMParser
198
+
199
+ ## Notes
200
+
201
+ - Only detects HTTPS URLs (not HTTP)
202
+ - URLs are matched until the first space character
203
+ - Fetching metadata requires CORS proxies (included)
204
+ - Google Favicon Service is used as a reliable fallback
205
+
206
+ ## License
207
+
208
+ MIT
209
+
210
+ ## Contributing
211
+
212
+ Contributions are welcome! Please feel free to submit a Pull Request.
213
+
214
+ ## Issues
215
+
216
+ If you find a bug or have a feature request, please open an issue on [GitHub](https://github.com/yourusername/react-beauty-link/issues).
217
+
218
+ ## Author
219
+
220
+ Your Name - [your.email@example.com](mailto:your.email@example.com)
@@ -0,0 +1,11 @@
1
+ import type { ReactNode } from 'react';
2
+ export type LinkTarget = 'new-tab' | 'new-window' | 'self';
3
+ /**
4
+ * Custom hook that converts HTTPS URLs in a string into clickable links
5
+ * with page titles and favicons
6
+ * @param text - The input string containing potential URLs
7
+ * @param target - How to open links: 'new-tab' (default), 'new-window', or 'self'
8
+ * @returns An array of React nodes with text and links
9
+ */
10
+ export declare const useBeautyLink: (text: string, target?: LinkTarget, customColor?: string) => ReactNode[];
11
+ //# sourceMappingURL=useBeautyLink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBeautyLink.d.ts","sourceRoot":"","sources":["../../src/hooks/useBeautyLink.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAQvC,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,YAAY,GAAG,MAAM,CAAC;AAe3D;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,EAAE,SAAQ,UAAsB,EAAE,cAAc,MAAM,KAAG,SAAS,EAkK3G,CAAC"}
@@ -0,0 +1,203 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect } from 'react';
3
+ import { FILE_TYPE_ICONS } from '../utils/fileIcons';
4
+ function getFileExtension(url) {
5
+ try {
6
+ const urlObj = new URL(url);
7
+ const pathname = urlObj.pathname;
8
+ const lastDot = pathname.lastIndexOf('.');
9
+ if (lastDot === -1 || lastDot === pathname.length - 1)
10
+ return null;
11
+ return pathname.substring(lastDot + 1).toLowerCase();
12
+ }
13
+ catch {
14
+ return null;
15
+ }
16
+ }
17
+ /**
18
+ * Custom hook that converts HTTPS URLs in a string into clickable links
19
+ * with page titles and favicons
20
+ * @param text - The input string containing potential URLs
21
+ * @param target - How to open links: 'new-tab' (default), 'new-window', or 'self'
22
+ * @returns An array of React nodes with text and links
23
+ */
24
+ export const useBeautyLink = (text, target = 'new-tab', customColor) => {
25
+ const urlRegex = /(https:\/\/[^\s]+)/g;
26
+ const [linkMetadata, setLinkMetadata] = useState({});
27
+ const urls = Array.from(text.matchAll(urlRegex)).map(match => match[0]);
28
+ useEffect(() => {
29
+ const fetchAllMetadata = async () => {
30
+ for (const url of urls) {
31
+ if (!linkMetadata[url]) {
32
+ const extension = getFileExtension(url);
33
+ // If it's a file URL, skip metadata fetching and use filename
34
+ if (extension && FILE_TYPE_ICONS[extension]) {
35
+ const filename = url.split('/').pop() || url;
36
+ setLinkMetadata(prev => ({
37
+ ...prev,
38
+ [url]: {
39
+ title: decodeURIComponent(filename),
40
+ favicon: null
41
+ }
42
+ }));
43
+ continue;
44
+ }
45
+ try {
46
+ console.log('Fetching metadata for:', url);
47
+ const metadata = await fetchLinkMetadata(url);
48
+ console.log('Metadata received:', metadata);
49
+ setLinkMetadata(prev => ({
50
+ ...prev,
51
+ [url]: metadata
52
+ }));
53
+ }
54
+ catch (error) {
55
+ console.error('Failed to fetch metadata for', url, error);
56
+ setLinkMetadata(prev => ({
57
+ ...prev,
58
+ [url]: {
59
+ title: url,
60
+ favicon: null
61
+ }
62
+ }));
63
+ }
64
+ }
65
+ }
66
+ };
67
+ fetchAllMetadata();
68
+ }, [text, JSON.stringify(urls)]);
69
+ const parts = [];
70
+ let lastIndex = 0;
71
+ let match;
72
+ urlRegex.lastIndex = 0;
73
+ while ((match = urlRegex.exec(text)) !== null) {
74
+ const url = match[0];
75
+ const startIndex = match.index;
76
+ if (startIndex > lastIndex) {
77
+ parts.push(text.substring(lastIndex, startIndex));
78
+ }
79
+ const metadata = linkMetadata[url];
80
+ const displayTitle = metadata?.title
81
+ ? (metadata.title.length > 60 ? metadata.title.substring(0, 60) + '...' : metadata.title)
82
+ : url;
83
+ const faviconUrl = metadata?.favicon;
84
+ const extension = getFileExtension(url);
85
+ const fileIcon = extension ? FILE_TYPE_ICONS[extension] : null;
86
+ const getTargetAttributes = () => {
87
+ switch (target) {
88
+ case 'new-window':
89
+ return {
90
+ target: '_blank',
91
+ rel: 'noopener noreferrer',
92
+ onClick: (e) => {
93
+ e.preventDefault();
94
+ window.open(url, '_blank', 'noopener,noreferrer,width=800,height=600');
95
+ }
96
+ };
97
+ case 'self':
98
+ return {
99
+ target: '_self'
100
+ };
101
+ case 'new-tab':
102
+ default:
103
+ return {
104
+ target: '_blank',
105
+ rel: 'noopener noreferrer'
106
+ };
107
+ }
108
+ };
109
+ parts.push(_jsxs("a", { href: url, ...getTargetAttributes(), style: {
110
+ color: customColor || '#646cff',
111
+ textDecoration: 'underline',
112
+ display: 'inline-flex',
113
+ alignItems: 'center',
114
+ gap: '6px'
115
+ }, children: [fileIcon ? (_jsx("span", { style: {
116
+ fontFamily: '"Symbols Nerd Font Mono", "Symbols Nerd Font", "Nerd Font", "FiraCode Nerd Font", monospace',
117
+ fontSize: '16px',
118
+ color: fileIcon.color,
119
+ lineHeight: 1,
120
+ display: 'inline-block',
121
+ width: '16px',
122
+ textAlign: 'center',
123
+ fontWeight: 'normal'
124
+ }, "aria-hidden": "true", children: fileIcon.icon })) : faviconUrl ? (_jsx("img", { src: faviconUrl, alt: "", style: { width: '16px', height: '16px' }, onError: (e) => {
125
+ e.target.src = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"%3E%3Cpath d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/%3E%3Cpath d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/%3E%3C/svg%3E';
126
+ } })) : (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }), _jsx("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })] })), _jsx("span", { children: displayTitle })] }, `link-${startIndex}`));
127
+ lastIndex = startIndex + url.length;
128
+ }
129
+ if (lastIndex < text.length) {
130
+ parts.push(text.substring(lastIndex));
131
+ }
132
+ return parts.length > 0 ? parts : [text];
133
+ };
134
+ /**
135
+ * Fetches metadata (title and favicon) for a given URL
136
+ */
137
+ async function fetchLinkMetadata(url) {
138
+ try {
139
+ const urlObj = new URL(url);
140
+ const origin = urlObj.origin;
141
+ const proxies = [
142
+ `https://api.allorigins.win/get?url=${encodeURIComponent(url)}`,
143
+ `https://corsproxy.io/?${encodeURIComponent(url)}`,
144
+ ];
145
+ let html = '';
146
+ let success = false;
147
+ for (const proxyUrl of proxies) {
148
+ try {
149
+ const response = await fetch(proxyUrl, {
150
+ signal: AbortSignal.timeout(10000) // 10 second timeout
151
+ });
152
+ if (!response.ok)
153
+ continue;
154
+ const data = await response.json();
155
+ html = data.contents || data;
156
+ success = true;
157
+ break;
158
+ }
159
+ catch (err) {
160
+ console.warn('Proxy failed:', proxyUrl, err);
161
+ continue;
162
+ }
163
+ }
164
+ if (!success || !html) {
165
+ throw new Error('All proxies failed');
166
+ }
167
+ const parser = new DOMParser();
168
+ const doc = parser.parseFromString(html, 'text/html');
169
+ let title = doc.querySelector('meta[property="og:title"]')?.getAttribute('content') ||
170
+ doc.querySelector('meta[name="twitter:title"]')?.getAttribute('content') ||
171
+ doc.querySelector('title')?.textContent ||
172
+ url;
173
+ title = title.trim();
174
+ let favicon = `https://www.google.com/s2/favicons?domain=${urlObj.hostname}&sz=32`;
175
+ const faviconLink = doc.querySelector('link[rel="icon"]')?.getAttribute('href') ||
176
+ doc.querySelector('link[rel="shortcut icon"]')?.getAttribute('href') ||
177
+ doc.querySelector('link[rel="apple-touch-icon"]')?.getAttribute('href');
178
+ if (faviconLink) {
179
+ if (faviconLink.startsWith('http')) {
180
+ favicon = faviconLink;
181
+ }
182
+ else if (faviconLink.startsWith('//')) {
183
+ favicon = 'https:' + faviconLink;
184
+ }
185
+ else if (faviconLink.startsWith('/')) {
186
+ favicon = origin + faviconLink;
187
+ }
188
+ else {
189
+ favicon = origin + '/' + faviconLink;
190
+ }
191
+ }
192
+ return { title, favicon };
193
+ }
194
+ catch (error) {
195
+ console.error('Error fetching metadata:', error);
196
+ const urlObj = new URL(url);
197
+ return {
198
+ title: urlObj.hostname,
199
+ favicon: `https://www.google.com/s2/favicons?domain=${urlObj.hostname}&sz=32`
200
+ };
201
+ }
202
+ }
203
+ //# sourceMappingURL=useBeautyLink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBeautyLink.js","sourceRoot":"","sources":["../../src/hooks/useBeautyLink.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { ReactNode } from 'react';
2
+ export type LinkTarget = 'new-tab' | 'new-window' | 'self';
3
+ /**
4
+ * Custom hook that converts HTTPS URLs in a string into clickable links
5
+ * with page titles and favicons
6
+ * @param text - The input string containing potential URLs
7
+ * @param target - How to open links: 'new-tab' (default), 'new-window', or 'self'
8
+ * @returns An array of React nodes with text and links
9
+ */
10
+ export declare const useLinkify: (text: string, target?: LinkTarget) => ReactNode[];
11
+ //# sourceMappingURL=useLinkify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLinkify.d.ts","sourceRoot":"","sources":["../../src/hooks/useLinkify.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAuB,MAAM,OAAO,CAAC;AAOvD,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,YAAY,GAAG,MAAM,CAAC;AAiF3D;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,EAAE,SAAQ,UAAsB,KAAG,SAAS,EAgKlF,CAAC"}