Skip to content

Commit 8eb5cf4

Browse files
Copilotmikebarkmin
andcommitted
Replace font size dropdown with four square buttons and support bare URLs in captions
- Change font size selector from dropdown to 4 square buttons (S, M, L, XL) - Add support for bare URLs (https://example.com) in image captions alongside markdown links - Update regex parser to handle both [text](url) and bare URL formats - Buttons highlight selected size with blue border and background Co-authored-by: mikebarkmin <2592379+mikebarkmin@users.noreply.github.com>
1 parent 93a8ec0 commit 8eb5cf4

2 files changed

Lines changed: 58 additions & 29 deletions

File tree

packages/learningmap/src/EditorDrawerTaskContent.tsx

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -96,18 +96,31 @@ export function EditorDrawerTaskContent({
9696
</div>
9797
<div className="form-group">
9898
<label>{t.fontSize}</label>
99-
<select
100-
value={getFontSizeOption(localNode.data.fontSize)}
101-
onChange={(e) => {
102-
const selectedSize = e.target.value as FontSizeOption;
103-
handleFieldChange("fontSize", FONT_SIZE_VALUES[selectedSize]);
104-
}}
105-
>
106-
<option value="S">{t.fontSizeSmall} ({FONT_SIZE_VALUES.S}px)</option>
107-
<option value="M">{t.fontSizeMedium} ({FONT_SIZE_VALUES.M}px)</option>
108-
<option value="L">{t.fontSizeLarge} ({FONT_SIZE_VALUES.L}px)</option>
109-
<option value="XL">{t.fontSizeXLarge} ({FONT_SIZE_VALUES.XL}px)</option>
110-
</select>
99+
<div style={{ display: "flex", gap: 8, marginTop: 8 }}>
100+
{(["S", "M", "L", "XL"] as FontSizeOption[]).map((size) => {
101+
const isSelected = getFontSizeOption(localNode.data.fontSize) === size;
102+
return (
103+
<button
104+
key={size}
105+
type="button"
106+
onClick={() => handleFieldChange("fontSize", FONT_SIZE_VALUES[size])}
107+
style={{
108+
width: 40,
109+
height: 40,
110+
borderRadius: 6,
111+
border: isSelected ? "2px solid #3b82f6" : "1px solid #d1d5db",
112+
backgroundColor: isSelected ? "#eff6ff" : "#ffffff",
113+
color: isSelected ? "#3b82f6" : "#374151",
114+
cursor: "pointer",
115+
fontWeight: isSelected ? "bold" : "normal",
116+
fontSize: "14px",
117+
}}
118+
>
119+
{size}
120+
</button>
121+
);
122+
})}
123+
</div>
111124
</div>
112125
<div className="form-group">
113126
<label>{t.summary}</label>

packages/learningmap/src/nodes/ImageNode.tsx

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ function isValidUrl(url: string): boolean {
1212
}
1313
}
1414

15-
// Simple markdown link parser for captions
15+
// Simple markdown link parser for captions - supports [text](url) and bare URLs
1616
function parseMarkdownLinks(text: string): React.ReactNode[] {
1717
const parts: React.ReactNode[] = [];
18-
const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
1918

20-
// Use matchAll instead of exec to avoid potential infinite loops
21-
const matches = Array.from(text.matchAll(linkRegex));
19+
// Combined regex for markdown links [text](url) and bare URLs
20+
// Matches markdown links first, then bare URLs (starting with http:// or https://)
21+
const combinedRegex = /\[([^\]]+)\]\(([^)]+)\)|(https?:\/\/[^\s<>"{}|\\^`\[\]]+)/g;
22+
23+
const matches = Array.from(text.matchAll(combinedRegex));
2224
let lastIndex = 0;
2325

2426
matches.forEach((match, index) => {
@@ -27,19 +29,33 @@ function parseMarkdownLinks(text: string): React.ReactNode[] {
2729
parts.push(text.substring(lastIndex, match.index));
2830
}
2931

30-
// Add the link only if URL is valid
31-
const linkText = match[1];
32-
const linkUrl = match[2];
33-
34-
if (isValidUrl(linkUrl)) {
35-
parts.push(
36-
<a key={index} href={linkUrl} target="_blank" rel="noopener noreferrer" style={{ color: "#3b82f6", textDecoration: "underline" }}>
37-
{linkText}
38-
</a>
39-
);
40-
} else {
41-
// If URL is invalid, just show the text
42-
parts.push(`[${linkText}](${linkUrl})`);
32+
if (match[1] && match[2]) {
33+
// Markdown link [text](url)
34+
const linkText = match[1];
35+
const linkUrl = match[2];
36+
37+
if (isValidUrl(linkUrl)) {
38+
parts.push(
39+
<a key={index} href={linkUrl} target="_blank" rel="noopener noreferrer" style={{ color: "#3b82f6", textDecoration: "underline" }}>
40+
{linkText}
41+
</a>
42+
);
43+
} else {
44+
// If URL is invalid, just show the text
45+
parts.push(`[${linkText}](${linkUrl})`);
46+
}
47+
} else if (match[3]) {
48+
// Bare URL
49+
const url = match[3];
50+
if (isValidUrl(url)) {
51+
parts.push(
52+
<a key={index} href={url} target="_blank" rel="noopener noreferrer" style={{ color: "#3b82f6", textDecoration: "underline" }}>
53+
{url}
54+
</a>
55+
);
56+
} else {
57+
parts.push(url);
58+
}
4359
}
4460

4561
if (match.index !== undefined) {

0 commit comments

Comments
 (0)