diff --git a/packages/claw-client/src/components/home/HomeView.tsx b/packages/claw-client/src/components/home/HomeView.tsx index 80cbfd3..73718e2 100644 --- a/packages/claw-client/src/components/home/HomeView.tsx +++ b/packages/claw-client/src/components/home/HomeView.tsx @@ -34,6 +34,25 @@ import { SectionHeader } from "./SectionHeader"; import { NotifPanel } from "./notif/NotifPanel"; import type { HomeNotif, NotifType } from "./notif/types"; +// Seeds a prompt into the composer (the home-page composer or, once in a +// thread, the chat composer) — `SessionComposer` listens for this event. +// Mirrors the command-palette priming in `AppOverlays`. Pass `submit: true` +// to also send it immediately (the "Create an example" CTAs do); otherwise +// the text just lands in the input for the user to tweak. +export function primeComposer(text: string, submit = false) { + window.dispatchEvent(new CustomEvent("openclaw-os:prime-composer", { detail: { text, submit } })); +} + +// Prompts seeded by the "Create an example" CTAs in the empty Apps / +// Artifacts home sections (desktop + mobile). Rather than hard-coding one +// app/doc, we let the agent propose something it can actually pull off with +// whatever integrations and credentials are already wired up — lower-effort +// and personalized. +export const APP_EXAMPLE_PROMPT = + "Suggest a few apps you could build for me — dashboards, work trackers, calendars, etc. — using the tools and integrations I already have connected, then build the one I pick."; +export const ARTIFACT_EXAMPLE_PROMPT = + "Suggest a couple of documents or reports you could put together for me based on what you know about my work, then draft the one I pick as an artifact."; + // ──────────────────────────────────────────────────────────────────────────── // Adapters: real app data → homepage view-models // ──────────────────────────────────────────────────────────────────────────── @@ -254,8 +273,12 @@ export function HomeView({
work trackers, marketing calendars, etc.

- ) : ( @@ -294,8 +317,12 @@ export function HomeView({
slides and reports.

- ) : ( diff --git a/packages/claw-client/src/components/mobile/MobileHomeView.tsx b/packages/claw-client/src/components/mobile/MobileHomeView.tsx index f0d91db..b65bca5 100644 --- a/packages/claw-client/src/components/mobile/MobileHomeView.tsx +++ b/packages/claw-client/src/components/mobile/MobileHomeView.tsx @@ -13,7 +13,12 @@ import { useMemo } from "react"; import { AgentCard, type AgentCardData } from "@/components/cards/AgentCard"; import { Greeting } from "@/components/home/Greeting"; -import type { HomeViewProps } from "@/components/home/HomeView"; +import { + APP_EXAMPLE_PROMPT, + ARTIFACT_EXAMPLE_PROMPT, + primeComposer, + type HomeViewProps, +} from "@/components/home/HomeView"; import { SectionHeader } from "@/components/home/SectionHeader"; import { MobileButton } from "@/components/mobile/MobileButton"; import { MobileCronRow } from "@/components/mobile/MobileCronRow"; @@ -131,8 +136,12 @@ export function MobileHomeView({
work trackers, marketing calendars, etc.

- ) : ( @@ -163,8 +172,12 @@ export function MobileHomeView({
slides and reports.

- ) : ( diff --git a/packages/claw-client/src/components/session/SessionComposer.tsx b/packages/claw-client/src/components/session/SessionComposer.tsx index cb2c9ee..ee279a4 100644 --- a/packages/claw-client/src/components/session/SessionComposer.tsx +++ b/packages/claw-client/src/components/session/SessionComposer.tsx @@ -484,6 +484,9 @@ export function SessionComposer({ const [isDragOver, setIsDragOver] = useState(false); const dragDepthRef = useRef(0); const textareaRef = useRef(null); + // Set when a `prime-composer` event asks us to auto-send the seeded text. + // The flush effect below picks it up once the text has landed in state. + const submitAfterPrimeRef = useRef(false); // Rotating-placeholder UX (home composer only — `rotatingPlaceholders` // is undefined for chat). The hook owns the cycle interval, the @@ -554,10 +557,14 @@ export function SessionComposer({ useEffect(() => { const listener = (event: Event) => { - const detail = (event as CustomEvent<{ text?: string }>).detail; + const detail = (event as CustomEvent<{ text?: string; submit?: boolean }>).detail; if (!detail?.text) return; setTextContent(detail.text); - requestAnimationFrame(() => textareaRef.current?.focus()); + if (detail.submit) { + submitAfterPrimeRef.current = true; + } else { + requestAnimationFrame(() => textareaRef.current?.focus()); + } }; window.addEventListener("openclaw-os:prime-composer", listener as EventListener); return () => @@ -653,6 +660,20 @@ export function SessionComposer({ await sendPromise; }; + // Flush a queued auto-submit (from a `prime-composer` event with + // `submit: true`) once the seeded text has actually landed in state — we + // can't call `handleSubmit` straight from the event listener because it + // reads `textContent` from the render closure, which is still empty there. + useEffect(() => { + if (!submitAfterPrimeRef.current || !textContent.trim()) return; + submitAfterPrimeRef.current = false; + void handleSubmit(); + // `handleSubmit` is intentionally not a dep — the effect re-runs on the + // render where `textContent` equals the primed value, so this closure's + // `handleSubmit` already sees that text. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [textContent]); + // Drag-drop & clipboard-paste plumbing. Both route through `onAddFiles`, // which mirrors the file-picker path. We use a depth counter for dragenter/ // dragleave so nested children inside the composer don't toggle the overlay