mirror of
https://github.com/basketikun/infinite-canvas.git
synced 2026-08-05 17:04:27 +08:00
feat(prompts): add atlascloud logo SVG and update prompt source handling
This commit is contained in:
@@ -9,12 +9,11 @@ import { exportCanvasNodes } from "@/lib/canvas/canvas-export";
|
||||
import { getNodeDefinition } from "@/lib/canvas/node-registry";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { PromptDetailDialog } from "@/pages/prompts/components/prompt-detail-dialog";
|
||||
import { fetchSourcePrompts, personalPromptToPrompt, type Prompt } from "@/services/api/prompts";
|
||||
import { fetchSourcePrompts, type Prompt } from "@/services/api/prompts";
|
||||
import { uploadMediaFile } from "@/services/file-storage";
|
||||
import { uploadImage } from "@/services/image-storage";
|
||||
import { useAssetStore, type Asset, type AssetKind } from "@/stores/use-asset-store";
|
||||
import { usePromptSourceStore } from "@/stores/use-prompt-source-store";
|
||||
import { usePromptStore } from "@/stores/use-prompt-store";
|
||||
import { CANVAS_SIDE_PANEL_MAX_WIDTH, CANVAS_SIDE_PANEL_MIN_WIDTH, CANVAS_SIDE_PANEL_MOTION_MS, useCanvasSidePanelStore } from "@/stores/use-canvas-side-panel-store";
|
||||
import { useThemeStore } from "@/stores/use-theme-store";
|
||||
import { CanvasNodeType, type CanvasNodeData } from "@/types/canvas";
|
||||
@@ -444,10 +443,9 @@ function AssetCover({ asset }: { asset: Asset }) {
|
||||
const CanvasPromptsTab = memo(function CanvasPromptsTab({ onInsert, theme }: { onInsert: (payload: InsertAssetPayload) => void; theme: CanvasTheme }) {
|
||||
const { message } = App.useApp();
|
||||
const sources = usePromptSourceStore((state) => state.sources);
|
||||
const personalPrompts = usePromptStore((state) => state.prompts);
|
||||
const enabledSources = useMemo(() => sources.filter((source) => source.enabled), [sources]);
|
||||
const [keyword, setKeyword] = useState("");
|
||||
const [expanded, setExpanded] = useState<Record<string, boolean>>({ personal: true });
|
||||
const [expanded, setExpanded] = useState<Record<string, boolean>>({});
|
||||
const [detail, setDetail] = useState<Prompt | null>(null);
|
||||
|
||||
const copyPrompt = async (prompt: string) => {
|
||||
@@ -466,32 +464,19 @@ const CanvasPromptsTab = memo(function CanvasPromptsTab({ onInsert, theme }: { o
|
||||
</div>
|
||||
<div className="min-h-0 flex-1 overflow-y-auto px-2 pb-3">
|
||||
<div className="space-y-1">
|
||||
<PersonalPromptGroup
|
||||
items={personalPrompts.map(personalPromptToPrompt)}
|
||||
keyword={keyword}
|
||||
open={!!expanded.personal}
|
||||
theme={theme}
|
||||
onToggle={() => setExpanded((prev) => ({ ...prev, personal: !prev.personal }))}
|
||||
onInsert={onInsert}
|
||||
onView={setDetail}
|
||||
/>
|
||||
{enabledSources.length ? (
|
||||
<>
|
||||
{enabledSources.map((source) => (
|
||||
<PromptSourceGroup
|
||||
key={source.id}
|
||||
sourceId={source.id}
|
||||
sourceName={source.name}
|
||||
keyword={keyword}
|
||||
open={!!expanded[source.id]}
|
||||
theme={theme}
|
||||
onToggle={() => setExpanded((prev) => ({ ...prev, [source.id]: !prev[source.id] }))}
|
||||
onInsert={onInsert}
|
||||
onView={setDetail}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
) : personalPrompts.length === 0 ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无提示词" className="pt-12" /> : null}
|
||||
{enabledSources.length ? enabledSources.map((source) => (
|
||||
<PromptSourceGroup
|
||||
key={source.id}
|
||||
sourceId={source.id}
|
||||
sourceName={source.name}
|
||||
keyword={keyword}
|
||||
open={!!expanded[source.id]}
|
||||
theme={theme}
|
||||
onToggle={() => setExpanded((prev) => ({ ...prev, [source.id]: !prev[source.id] }))}
|
||||
onInsert={onInsert}
|
||||
onView={setDetail}
|
||||
/>
|
||||
)) : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无提示词" className="pt-12" />}
|
||||
</div>
|
||||
</div>
|
||||
<PromptDetailDialog prompt={detail} onClose={() => setDetail(null)} onCopy={(prompt) => void copyPrompt(prompt)} />
|
||||
@@ -499,31 +484,6 @@ const CanvasPromptsTab = memo(function CanvasPromptsTab({ onInsert, theme }: { o
|
||||
);
|
||||
});
|
||||
|
||||
function PersonalPromptGroup({ items, keyword, open, theme, onToggle, onInsert, onView }: { items: Prompt[]; keyword: string; open: boolean; theme: CanvasTheme; onToggle: () => void; onInsert: (payload: InsertAssetPayload) => void; onView: (prompt: Prompt) => void }) {
|
||||
const showResults = open || !!keyword.trim();
|
||||
const filtered = useMemo(() => {
|
||||
const query = keyword.trim().toLowerCase();
|
||||
return query ? items.filter((item) => [item.title, item.prompt, item.description, ...item.tags].join(" ").toLowerCase().includes(query)) : items;
|
||||
}, [items, keyword]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button type="button" onClick={onToggle} className="flex w-full items-center gap-1.5 rounded-md px-1.5 py-1.5 text-left text-xs font-semibold opacity-75 transition hover:opacity-100">
|
||||
<ChevronRight className={cn("size-3.5 transition-transform", showResults && "rotate-90")} />
|
||||
<BookOpen className="size-3.5" />
|
||||
<span className="min-w-0 flex-1 truncate">我的提示词</span>
|
||||
<span className="opacity-50">{filtered.length}</span>
|
||||
</button>
|
||||
{showResults ? (
|
||||
<div className="space-y-1.5 px-1 pb-2 pt-1">
|
||||
{filtered.map((item) => <PromptRow key={item.id} item={item} theme={theme} onInsert={() => onInsert({ kind: "text", content: item.prompt, title: item.title })} onView={() => onView(item)} />)}
|
||||
{!filtered.length ? <div className="py-4 text-center text-xs opacity-40">{keyword.trim() ? "无匹配提示词" : "还没有保存提示词"}</div> : null}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function PromptSourceGroup({
|
||||
sourceId,
|
||||
sourceName,
|
||||
|
||||
Reference in New Issue
Block a user