Skip to content

Commit 6e3740c

Browse files
authored
✨ feat: 添加提示词置顶功能 (#39)
* ✨ feat: 添加提示词置顶功能 * ✨ feat: 更新提示词的国际化支持,添加置顶和取消置顶的翻译
1 parent 227f1c1 commit 6e3740c

File tree

6 files changed

+100
-2
lines changed

6 files changed

+100
-2
lines changed

entrypoints/content/components/PromptSelector.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,16 @@ const PromptSelector: React.FC<PromptSelectorProps> = ({
8383
}
8484

8585
return true;
86+
}).sort((a, b) => {
87+
// 按置顶状态和最后修改时间排序:置顶的在前面,同级别内按最后修改时间降序
88+
// 首先按置顶状态排序,置顶的在前面
89+
if (a.pinned && !b.pinned) return -1;
90+
if (!a.pinned && b.pinned) return 1;
91+
92+
// 如果置顶状态相同,按最后修改时间降序排序(新的在前面)
93+
const aTime = a.lastModified ? new Date(a.lastModified).getTime() : 0;
94+
const bTime = b.lastModified ? new Date(b.lastModified).getTime() : 0;
95+
return bTime - aTime;
8696
});
8797

8898
// 当组件挂载时聚焦搜索框

entrypoints/options/components/PromptList.tsx

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ interface PromptListProps {
1010
searchTerm: string
1111
allPromptsCount: number
1212
onToggleEnabled?: (id: string, enabled: boolean) => void
13+
onTogglePinned?: (id: string, pinned: boolean) => void
1314
selectedCategoryId?: string | null
1415
}
1516

@@ -20,6 +21,7 @@ const PromptList = ({
2021
searchTerm,
2122
allPromptsCount,
2223
onToggleEnabled,
24+
onTogglePinned,
2325
selectedCategoryId,
2426
}: PromptListProps) => {
2527
const [categories, setCategories] = useState<Category[]>([])
@@ -165,9 +167,21 @@ const PromptList = ({
165167
className='bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-xl overflow-hidden shadow-sm hover:shadow-md transition-all duration-200 flex flex-col'
166168
>
167169
{/* Card Header */}
168-
<div className='px-5 py-4 border-b border-gray-100 dark:border-gray-700 bg-gradient-to-r from-gray-50 to-white dark:from-gray-800 dark:to-gray-700'>
170+
<div className={`px-5 py-4 border-b border-gray-100 dark:border-gray-700 ${
171+
prompt.pinned
172+
? 'bg-gradient-to-r from-amber-50 to-orange-50 dark:from-amber-900/20 dark:to-orange-900/20'
173+
: 'bg-gradient-to-r from-gray-50 to-white dark:from-gray-800 dark:to-gray-700'
174+
}`}>
169175
<div className='flex items-center justify-between'>
170-
<h3 className='text-lg font-semibold text-gray-800 dark:text-gray-200 truncate flex-1'>{prompt.title}</h3>
176+
<div className='flex items-center flex-1 min-w-0'>
177+
{/* 置顶图标 */}
178+
{prompt.pinned && (
179+
<svg className='w-4 h-4 text-amber-600 dark:text-amber-400 mr-2 flex-shrink-0' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
180+
<path strokeLinecap='round' strokeLinejoin='round' strokeWidth='2' d='M5 15l7-7 7 7'/>
181+
</svg>
182+
)}
183+
<h3 className='text-lg font-semibold text-gray-800 dark:text-gray-200 truncate'>{prompt.title}</h3>
184+
</div>
171185
{/* 分类标识 */}
172186
{category && (
173187
<div className='ml-2 flex items-center'>
@@ -254,6 +268,34 @@ const PromptList = ({
254268

255269
{/* Card Footer / Actions */}
256270
<div className='px-5 py-3 bg-gray-50 dark:bg-gray-700 border-t border-gray-100 dark:border-gray-600 flex justify-end space-x-2'>
271+
{/* 置顶按钮 */}
272+
{onTogglePinned && (
273+
<button
274+
onClick={() => onTogglePinned(prompt.id, !prompt.pinned)}
275+
className={`px-3 py-1.5 text-sm rounded-md border transition-colors duration-200 ${
276+
prompt.pinned
277+
? 'bg-amber-100 dark:bg-amber-900/50 border-amber-300 dark:border-amber-600 text-amber-700 dark:text-amber-400 hover:bg-amber-200 dark:hover:bg-amber-900/70'
278+
: 'bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700'
279+
} focus:outline-none focus:ring-2 focus:ring-offset-1 focus:ring-amber-500`}
280+
title={prompt.pinned ? t('unpinPrompt') : t('pinPrompt')}
281+
>
282+
<span className='flex items-center'>
283+
{prompt.pinned ? (
284+
// 已置顶状态:显示向下箭头,表示可以取消置顶
285+
<svg className='w-4 h-4 mr-1.5' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
286+
<path strokeLinecap='round' strokeLinejoin='round' strokeWidth='2' d='M19 9l-7 7-7-7'/>
287+
</svg>
288+
) : (
289+
// 未置顶状态:显示向上箭头,表示可以置顶
290+
<svg className='w-4 h-4 mr-1.5' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
291+
<path strokeLinecap='round' strokeLinejoin='round' strokeWidth='2' d='M5 15l7-7 7 7'/>
292+
</svg>
293+
)}
294+
{prompt.pinned ? t('unpin') : t('pin')}
295+
</span>
296+
</button>
297+
)}
298+
257299
<button
258300
onClick={() => handleCopy(prompt.content, prompt.id)}
259301
className={`px-3 py-1.5 text-sm rounded-md border transition-colors duration-200 ${

entrypoints/options/components/PromptManager.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,18 @@ const PromptManager = () => {
102102
});
103103
}
104104

105+
// 按置顶状态和最后修改时间排序:置顶的在前面,同级别内按最后修改时间降序
106+
filtered.sort((a, b) => {
107+
// 首先按置顶状态排序,置顶的在前面
108+
if (a.pinned && !b.pinned) return -1;
109+
if (!a.pinned && b.pinned) return 1;
110+
111+
// 如果置顶状态相同,按最后修改时间降序排序(新的在前面)
112+
const aTime = a.lastModified ? new Date(a.lastModified).getTime() : 0;
113+
const bTime = b.lastModified ? new Date(b.lastModified).getTime() : 0;
114+
return bTime - aTime;
115+
});
116+
105117
setFilteredPrompts(filtered);
106118
}, [searchTerm, prompts, selectedCategoryId]);
107119

@@ -219,6 +231,14 @@ const PromptManager = () => {
219231
await savePrompts(newPrompts);
220232
};
221233

234+
// 添加切换置顶状态的函数
235+
const togglePromptPinned = async (id: string, pinned: boolean) => {
236+
const newPrompts = prompts.map((p) =>
237+
p.id === id ? { ...p, pinned } : p
238+
);
239+
await savePrompts(newPrompts);
240+
};
241+
222242
// 导出提示词
223243
const exportPrompts = () => {
224244
if (prompts.length === 0) {
@@ -712,6 +732,7 @@ const PromptManager = () => {
712732
searchTerm={searchTerm}
713733
allPromptsCount={prompts.length}
714734
onToggleEnabled={togglePromptEnabled}
735+
onTogglePinned={togglePromptPinned}
715736
selectedCategoryId={selectedCategoryId}
716737
/>
717738

public/_locales/en/messages.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,5 +1317,17 @@
13171317
},
13181318
"importCategoriesInstructionsDetail": {
13191319
"message": "Enter the URL link containing valid category JSON data, and the system will automatically fetch and import the data."
1320+
},
1321+
"pin": {
1322+
"message": "Pin"
1323+
},
1324+
"unpin": {
1325+
"message": "Unpin"
1326+
},
1327+
"pinPrompt": {
1328+
"message": "Pin prompt"
1329+
},
1330+
"unpinPrompt": {
1331+
"message": "Unpin prompt"
13201332
}
13211333
}

public/_locales/zh/messages.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,5 +1317,17 @@
13171317
},
13181318
"importCategoriesInstructionsDetail": {
13191319
"message": "输入包含有效分类 JSON 数据的 URL 链接,系统将自动获取并导入数据。"
1320+
},
1321+
"pin": {
1322+
"message": "置顶"
1323+
},
1324+
"unpin": {
1325+
"message": "取消置顶"
1326+
},
1327+
"pinPrompt": {
1328+
"message": "置顶提示词"
1329+
},
1330+
"unpinPrompt": {
1331+
"message": "取消置顶提示词"
13201332
}
13211333
}

utils/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export interface PromptItem {
2121
tags: string[];
2222
enabled: boolean;
2323
categoryId: string;
24+
pinned?: boolean; // 置顶字段
2425
notionPageId?: string;
2526
notes?: string; // 备注字段
2627
lastModified?: string; // 最后修改时间(ISO 字符串)

0 commit comments

Comments
 (0)