mirror of
https://gitee.com/wanwujie/deer-flow
synced 2026-04-19 04:14:46 +08:00
feat: add surprise-me
This commit is contained in:
@@ -51,6 +51,7 @@
|
|||||||
"ai": "^6.0.33",
|
"ai": "^6.0.33",
|
||||||
"best-effort-json-parser": "^1.2.1",
|
"best-effort-json-parser": "^1.2.1",
|
||||||
"better-auth": "^1.3",
|
"better-auth": "^1.3",
|
||||||
|
"canvas-confetti": "^1.9.4",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
|
|||||||
8
frontend/pnpm-lock.yaml
generated
8
frontend/pnpm-lock.yaml
generated
@@ -116,6 +116,9 @@ importers:
|
|||||||
better-auth:
|
better-auth:
|
||||||
specifier: ^1.3
|
specifier: ^1.3
|
||||||
version: 1.4.12(next@16.1.4(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.26(typescript@5.9.3))
|
version: 1.4.12(next@16.1.4(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.26(typescript@5.9.3))
|
||||||
|
canvas-confetti:
|
||||||
|
specifier: ^1.9.4
|
||||||
|
version: 1.9.4
|
||||||
class-variance-authority:
|
class-variance-authority:
|
||||||
specifier: ^0.7.1
|
specifier: ^0.7.1
|
||||||
version: 0.7.1
|
version: 0.7.1
|
||||||
@@ -2577,6 +2580,9 @@ packages:
|
|||||||
caniuse-lite@1.0.30001764:
|
caniuse-lite@1.0.30001764:
|
||||||
resolution: {integrity: sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==}
|
resolution: {integrity: sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==}
|
||||||
|
|
||||||
|
canvas-confetti@1.9.4:
|
||||||
|
resolution: {integrity: sha512-yxQbJkAVrFXWNbTUjPqjF7G+g6pDotOUHGbkZq2NELZUMDpiJ85rIEazVb8GTaAptNW2miJAXbs1BtioA251Pw==}
|
||||||
|
|
||||||
ccount@2.0.1:
|
ccount@2.0.1:
|
||||||
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
|
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
|
||||||
|
|
||||||
@@ -7513,6 +7519,8 @@ snapshots:
|
|||||||
|
|
||||||
caniuse-lite@1.0.30001764: {}
|
caniuse-lite@1.0.30001764: {}
|
||||||
|
|
||||||
|
canvas-confetti@1.9.4: {}
|
||||||
|
|
||||||
ccount@2.0.1: {}
|
ccount@2.0.1: {}
|
||||||
|
|
||||||
chalk@4.1.2:
|
chalk@4.1.2:
|
||||||
|
|||||||
49
frontend/src/components/ui/confetti-button.tsx
Normal file
49
frontend/src/components/ui/confetti-button.tsx
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, { type MouseEventHandler } from "react";
|
||||||
|
import confetti from "canvas-confetti";
|
||||||
|
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
interface ConfettiButtonProps extends React.ComponentProps<typeof Button> {
|
||||||
|
angle?: number;
|
||||||
|
particleCount?: number;
|
||||||
|
startVelocity?: number;
|
||||||
|
spread?: number;
|
||||||
|
onClick?: MouseEventHandler<HTMLButtonElement>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ConfettiButton({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
angle = 90,
|
||||||
|
particleCount = 75,
|
||||||
|
startVelocity = 35,
|
||||||
|
spread = 70,
|
||||||
|
onClick,
|
||||||
|
...props
|
||||||
|
}: ConfettiButtonProps) {
|
||||||
|
const handleClick: MouseEventHandler<HTMLButtonElement> = (event) => {
|
||||||
|
const target = event.currentTarget;
|
||||||
|
if (target) {
|
||||||
|
const rect = target.getBoundingClientRect();
|
||||||
|
confetti({
|
||||||
|
particleCount,
|
||||||
|
startVelocity,
|
||||||
|
angle,
|
||||||
|
spread,
|
||||||
|
origin: {
|
||||||
|
x: (rect.left + rect.width / 2) / window.innerWidth,
|
||||||
|
y: (rect.top + rect.height / 2) / window.innerHeight,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
onClick?.(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button onClick={handleClick} className={className} {...props}>
|
||||||
|
{children}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
LightbulbIcon,
|
LightbulbIcon,
|
||||||
PaperclipIcon,
|
PaperclipIcon,
|
||||||
PlusIcon,
|
PlusIcon,
|
||||||
|
SparklesIcon,
|
||||||
ZapIcon,
|
ZapIcon,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useSearchParams } from "next/navigation";
|
import { useSearchParams } from "next/navigation";
|
||||||
@@ -30,6 +31,7 @@ import {
|
|||||||
usePromptInputController,
|
usePromptInputController,
|
||||||
type PromptInputMessage,
|
type PromptInputMessage,
|
||||||
} from "@/components/ai-elements/prompt-input";
|
} from "@/components/ai-elements/prompt-input";
|
||||||
|
import { ConfettiButton } from "@/components/ui/confetti-button";
|
||||||
import {
|
import {
|
||||||
DropdownMenuGroup,
|
DropdownMenuGroup,
|
||||||
DropdownMenuLabel,
|
DropdownMenuLabel,
|
||||||
@@ -386,6 +388,14 @@ function SuggestionList() {
|
|||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<Suggestions className="w-fit">
|
<Suggestions className="w-fit">
|
||||||
|
<ConfettiButton
|
||||||
|
className="text-muted-foreground cursor-pointer rounded-full px-4 text-xs font-normal"
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => handleSuggestionClick("Surprise me")}
|
||||||
|
>
|
||||||
|
<SparklesIcon className="size-4" /> Surprise
|
||||||
|
</ConfettiButton>
|
||||||
{t.inputBox.suggestions.map((suggestion) => (
|
{t.inputBox.suggestions.map((suggestion) => (
|
||||||
<Suggestion
|
<Suggestion
|
||||||
key={suggestion.suggestion}
|
key={suggestion.suggestion}
|
||||||
|
|||||||
54
skills/public/surprise-me/SKILL.md
Normal file
54
skills/public/surprise-me/SKILL.md
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
name: surprise-me
|
||||||
|
description: >
|
||||||
|
Create a delightful, unexpected "wow" experience for the user by dynamically discovering and creatively combining other enabled skills. Triggers when the user says "surprise me" or any request expressing a desire for an unexpected creative showcase. Also triggers when the user is bored, wants inspiration, or asks Claude to "do something interesting". This skill does NOT hardcode which skills exist — it discovers them at runtime.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Surprise Me
|
||||||
|
|
||||||
|
Deliver an unexpected, delightful experience by dynamically discovering available skills and combining them creatively.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
### Step 1: Discover Available Skills
|
||||||
|
|
||||||
|
Read all the skills listed in the <available_skills>.
|
||||||
|
|
||||||
|
### Step 2: Plan the Surprise
|
||||||
|
|
||||||
|
Select **1 to 3** skills and design a creative mashup. The goal is a single cohesive deliverable, not separate demos.
|
||||||
|
|
||||||
|
**Creative combination principles:**
|
||||||
|
- Juxtapose skills in unexpected ways (e.g., a presentation about algorithmic art, a research report turned into a slide deck, a styled doc with canvas-designed illustrations)
|
||||||
|
- Incorporate the user's known interests/context from memory if available
|
||||||
|
- Prioritize visual impact and emotional delight over information density
|
||||||
|
- The output should feel like a gift — polished, surprising, and fun
|
||||||
|
|
||||||
|
**Theme ideas (pick or remix):**
|
||||||
|
- Something tied to today's date, season, or trending news
|
||||||
|
- A mini creative project the user never asked for but would love
|
||||||
|
- A playful "what if" concept
|
||||||
|
- An aesthetic artifact combining data + design
|
||||||
|
- A fun interactive HTML/React experience
|
||||||
|
|
||||||
|
### Step 3: Fallback — No Other Skills Available
|
||||||
|
|
||||||
|
If no other skills are discovered (only surprise-me exists), use one of these fallbacks:
|
||||||
|
|
||||||
|
1. **News-based surprise**: Search today's news for a fascinating story, then create a beautifully designed HTML artifact presenting it in a visually striking way
|
||||||
|
2. **Interactive HTML experience**: Build a creative single-page web experience — generative art, a mini-game, a visual poem, an animated infographic, or an interactive story
|
||||||
|
3. **Personalized artifact**: Use known user context to create something personal and delightful
|
||||||
|
|
||||||
|
### Step 4: Execute
|
||||||
|
|
||||||
|
1. Read the full SKILL.md body of each selected skill
|
||||||
|
2. Follow each skill's instructions for technical execution
|
||||||
|
3. Combine outputs into one cohesive deliverable
|
||||||
|
4. Present the result with minimal preamble — let the work speak for itself
|
||||||
|
|
||||||
|
### Step 5: Reveal
|
||||||
|
|
||||||
|
Present the surprise with minimal spoilers. A short teaser line, then the artifact.
|
||||||
|
|
||||||
|
- **Good reveal:** "I made you something ✨" + [the artifact]
|
||||||
|
- **Bad reveal:** "I decided to combine the pptx skill with the canvas-design skill to create a presentation about..." (kills the surprise)
|
||||||
Reference in New Issue
Block a user