Skip to content

Commit a66c73a

Browse files
author
Tim Sinaeve
committed
Refactor RichTextEditor, fix slider styling, and adjust theme tones
- Refactored Icons.tsx to use map-based lookup. - Extracted RichTextEditor sub-components to components/rich-text/. - Fixed slider styling in index.html. - Adjusted Warm and Cool light theme palettes.
1 parent fb73cec commit a66c73a

15 files changed

Lines changed: 2438 additions & 2871 deletions

components/Icons.tsx

Lines changed: 85 additions & 532 deletions
Large diffs are not rendered by default.

components/PromptTreeItem.tsx

Lines changed: 203 additions & 205 deletions
Large diffs are not rendered by default.

components/RichTextEditor.tsx

Lines changed: 166 additions & 2069 deletions
Large diffs are not rendered by default.

components/TemplateList.tsx

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const TemplateList: React.FC<TemplateListProps> = ({ templates, activeTemplateId
4242
renameInputRef.current?.select();
4343
}
4444
}, [renamingId]);
45-
45+
4646
const handleDelete = (e: React.MouseEvent, id: string) => {
4747
e.stopPropagation();
4848
onDeleteTemplate(id, e.shiftKey);
@@ -54,53 +54,53 @@ const TemplateList: React.FC<TemplateListProps> = ({ templates, activeTemplateId
5454
// Fix: Use template_id instead of id
5555
const isFocused = focusedItemId === template.template_id;
5656
return (
57-
// Fix: Use template_id for key and data-item-id
58-
<li key={template.template_id} data-item-id={template.template_id}>
57+
// Fix: Use template_id for key and data-item-id
58+
<li key={template.template_id} data-item-id={template.template_id}>
5959
{renamingId === template.template_id ? (
60-
<div className="p-1 flex items-center gap-2">
60+
<div className="p-1 flex items-center gap-2">
6161
<DocumentDuplicateIcon className="w-3.5 h-3.5 flex-shrink-0" />
6262
<input
63-
ref={renameInputRef}
64-
type="text"
65-
value={renameValue}
66-
onChange={(e) => setRenameValue(e.target.value)}
67-
onBlur={handleRenameSubmit}
68-
onKeyDown={handleRenameKeyDown}
69-
className="w-full text-left text-xs px-1.5 py-1 rounded-md bg-background text-text-main border border-border-color focus:outline-none focus:ring-1 focus:ring-primary"
63+
ref={renameInputRef}
64+
type="text"
65+
value={renameValue}
66+
onChange={(e) => setRenameValue(e.target.value)}
67+
onBlur={handleRenameSubmit}
68+
onKeyDown={handleRenameKeyDown}
69+
className="w-full text-left text-xs px-1.5 py-1 rounded-md bg-background text-text-main border border-border-color focus:outline-none focus:ring-1 focus:ring-primary"
7070
/>
71-
</div>
71+
</div>
7272
) : (
73-
<button
73+
<button
7474
// Fix: Use template_id
7575
onClick={() => onSelectTemplate(template.template_id)}
7676
onDoubleClick={(e) => handleRenameStart(e, template)}
77-
className={`w-full text-left p-1 rounded-md group flex justify-between items-center transition-colors duration-150 text-xs relative focus:outline-none ${
78-
// Fix: Use template_id
79-
activeTemplateId === template.template_id
80-
? 'bg-background text-text-main'
81-
: 'hover:bg-border-color/30 text-text-secondary hover:text-text-main'
82-
} ${isFocused ? 'ring-2 ring-primary ring-offset-[-2px] ring-offset-secondary' : ''}`}
83-
>
84-
<div className="flex items-center gap-1.5 flex-1 truncate">
85-
<DocumentDuplicateIcon className="w-3.5 h-3.5 flex-shrink-0" />
86-
<span className="truncate flex-1 px-1">{template.title}</span>
77+
className={`w-full text-left pr-1 flex justify-between items-center transition-colors duration-0 text-[13px] relative focus:outline-none h-[22px] min-h-[22px] cursor-default ${
78+
// Fix: Use template_id
79+
activeTemplateId === template.template_id
80+
? 'bg-tree-selected/20 text-text-main font-medium'
81+
: 'hover:bg-tree-selected/10 text-text-secondary hover:text-text-main'
82+
} ${isFocused ? 'ring-1 ring-inset ring-primary' : ''}`}
83+
>
84+
<div className="flex items-center gap-1 flex-1 truncate pl-[2px]">
85+
<DocumentDuplicateIcon className={`w-3.5 h-3.5 flex-shrink-0 ${activeTemplateId === template.template_id ? 'text-text-main' : 'text-text-secondary'}`} />
86+
<span className="truncate flex-1 px-1">{template.title}</span>
8787
</div>
8888
{/* Fix: Use template_id */}
8989
<div className={`transition-opacity pr-1 flex items-center ${activeTemplateId === template.template_id ? 'opacity-100' : 'opacity-0 group-hover:opacity-100'}`}>
90-
{/* Fix: Use template_id */}
91-
<IconButton onClick={(e) => handleDelete(e, template.template_id)} tooltip="Delete" size="xs" variant="destructive">
90+
{/* Fix: Use template_id */}
91+
<IconButton onClick={(e) => handleDelete(e, template.template_id)} tooltip="Delete" size="xs" variant="destructive">
9292
<TrashIcon className="w-3.5 h-3.5" />
93-
</IconButton>
93+
</IconButton>
9494
</div>
95-
</button>
95+
</button>
9696
)}
97-
</li>
97+
</li>
9898
)
9999
})}
100-
{templates.length === 0 && (
101-
<li className="text-center text-text-secondary p-4 text-xs">
102-
No templates yet.
103-
</li>
100+
{templates.length === 0 && (
101+
<li className="text-center text-text-secondary p-4 text-xs">
102+
No templates yet.
103+
</li>
104104
)}
105105
</ul>
106106
);
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import React, { useCallback, useEffect, useRef, useState } from 'react';
2+
import { ChevronDownIcon } from '../Icons';
3+
4+
interface FontDropDownProps {
5+
value: string;
6+
options: { label: string; value: string }[];
7+
onChange: (value: string) => void;
8+
disabled?: boolean;
9+
className?: string;
10+
placeholder?: string;
11+
}
12+
13+
const FontDropDown: React.FC<FontDropDownProps> = ({
14+
value,
15+
options,
16+
onChange,
17+
disabled = false,
18+
className = '',
19+
placeholder = 'Select...',
20+
}) => {
21+
const [isOpen, setIsOpen] = useState(false);
22+
const containerRef = useRef<HTMLDivElement>(null);
23+
24+
const toggleOpen = useCallback(() => {
25+
if (!disabled) {
26+
setIsOpen((prev) => !prev);
27+
}
28+
}, [disabled]);
29+
30+
const close = useCallback(() => {
31+
setIsOpen(false);
32+
}, []);
33+
34+
useEffect(() => {
35+
if (!isOpen) return;
36+
37+
const handleClickOutside = (event: MouseEvent) => {
38+
if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
39+
close();
40+
}
41+
};
42+
43+
document.addEventListener('mousedown', handleClickOutside);
44+
return () => document.removeEventListener('mousedown', handleClickOutside);
45+
}, [isOpen, close]);
46+
47+
const handleSelect = (optionValue: string) => {
48+
onChange(optionValue);
49+
close();
50+
};
51+
52+
const selectedOption = options.find((opt) => opt.value === value);
53+
54+
return (
55+
<div ref={containerRef} className={`relative inline-block text-left ${className}`}>
56+
<button
57+
type="button"
58+
onClick={toggleOpen}
59+
disabled={disabled}
60+
className={`flex items-center justify-between w-full rounded-md border border-border-color/50 bg-secondary/30 px-2 py-1 text-xs font-medium text-text-main hover:bg-secondary-hover focus:outline-none focus:ring-1 focus:ring-primary/50 ${disabled ? 'opacity-50 cursor-not-allowed' : ''}`}
61+
>
62+
<span className="block truncate max-w-[100px]">{selectedOption ? selectedOption.label : placeholder}</span>
63+
<ChevronDownIcon className="ml-1 h-3 w-3 text-text-secondary" />
64+
</button>
65+
66+
{isOpen && (
67+
<div className="absolute left-0 z-50 mt-1 w-40 origin-top-left rounded-md bg-secondary shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none max-h-60 overflow-auto">
68+
<div className="py-1">
69+
{options.map((option) => (
70+
<button
71+
key={option.value}
72+
onClick={() => handleSelect(option.value)}
73+
className={`block w-full px-4 py-2 text-left text-xs ${option.value === value
74+
? 'bg-primary/10 text-primary'
75+
: 'text-text-main hover:bg-secondary-hover'
76+
}`}
77+
>
78+
<span style={{ fontFamily: option.value }}>{option.label}</span>
79+
</button>
80+
))}
81+
</div>
82+
</div>
83+
)}
84+
</div>
85+
);
86+
};
87+
88+
export default FontDropDown;

components/rich-text/LinkModal.tsx

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import React, { useRef, useState, useEffect } from 'react';
2+
import Modal from '../Modal';
3+
import Button from '../Button';
4+
5+
interface LinkModalProps {
6+
isOpen: boolean;
7+
initialUrl: string;
8+
onSubmit: (url: string) => void;
9+
onRemove: () => void;
10+
onClose: () => void;
11+
}
12+
13+
export const LinkModal: React.FC<LinkModalProps> = ({ isOpen, initialUrl, onSubmit, onRemove, onClose }) => {
14+
const inputRef = useRef<HTMLInputElement>(null);
15+
const [url, setUrl] = useState(initialUrl);
16+
17+
useEffect(() => {
18+
setUrl(initialUrl);
19+
}, [initialUrl]);
20+
21+
const handleSubmit = (event: React.FormEvent) => {
22+
event.preventDefault();
23+
onSubmit(url);
24+
};
25+
26+
if (!isOpen) {
27+
return null;
28+
}
29+
30+
return (
31+
<Modal onClose={onClose} title="Insert link" initialFocusRef={inputRef}>
32+
<form onSubmit={handleSubmit}>
33+
<div className="p-6 space-y-3">
34+
<label className="block text-sm font-semibold text-text-main" htmlFor="link-url-input">
35+
Link URL
36+
</label>
37+
<input
38+
id="link-url-input"
39+
ref={inputRef}
40+
type="text"
41+
inputMode="url"
42+
autoComplete="url"
43+
required
44+
value={url}
45+
onChange={event => setUrl(event.target.value)}
46+
className="w-full rounded-md border border-border-color bg-background px-3 py-2 text-sm text-text-main focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/30"
47+
placeholder="https://example.com"
48+
/>
49+
<p className="text-xs text-text-secondary">
50+
Enter a valid URL. If you omit the protocol, https:// will be added automatically.
51+
</p>
52+
</div>
53+
<div className="flex justify-end gap-3 px-6 py-4 bg-background/50 border-t border-border-color rounded-b-lg">
54+
<Button type="button" variant="secondary" onClick={onClose}>
55+
Cancel
56+
</Button>
57+
<Button type="button" variant="secondary" onClick={onRemove}>
58+
Remove link
59+
</Button>
60+
<Button type="submit">Save link</Button>
61+
</div>
62+
</form>
63+
</Modal>
64+
);
65+
};

0 commit comments

Comments
 (0)