feat(desktop): persist onboarding state to file system
- Add AppState module in core for managing app state persistence - Add app-state IPC handlers for reading/writing onboarding state - Hydrate onboarding state from file system on app startup - Prevent flash by showing blank screen during hydration - Update onboarding store to sync with file system - Improve MulticaIcon with enhanced animation states - Minor UI fixes in chat and device list components Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
cf3ad1db91
commit
eb4e1f57b1
20 changed files with 360 additions and 67 deletions
|
|
@ -19,10 +19,12 @@ interface ChatInputProps {
|
|||
onSubmit?: (value: string) => void;
|
||||
disabled?: boolean;
|
||||
placeholder?: string;
|
||||
/** Initial value to pre-fill the input */
|
||||
defaultValue?: string;
|
||||
}
|
||||
|
||||
export const ChatInput = forwardRef<ChatInputRef, ChatInputProps>(
|
||||
function ChatInput({ onSubmit, disabled, placeholder = "Type a message..." }, ref) {
|
||||
function ChatInput({ onSubmit, disabled, placeholder = "Type a message...", defaultValue }, ref) {
|
||||
// Use ref to avoid stale closure in Tiptap keydown handler
|
||||
const onSubmitRef = useRef(onSubmit);
|
||||
onSubmitRef.current = onSubmit;
|
||||
|
|
@ -45,6 +47,7 @@ export const ChatInput = forwardRef<ChatInputRef, ChatInputProps>(
|
|||
}),
|
||||
Placeholder.configure({ placeholder }),
|
||||
],
|
||||
content: defaultValue ? `<p>${defaultValue}</p>` : "",
|
||||
immediatelyRender: false,
|
||||
// Scroll cursor into view on every content change (e.g., Shift+Enter newlines)
|
||||
onUpdate({ editor }) {
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ export interface ChatViewProps {
|
|||
onDisconnect?: () => void;
|
||||
/** Optional action button in the error banner (e.g. "Configure Provider") */
|
||||
errorAction?: { label: string; onClick: () => void };
|
||||
/** Initial prompt to pre-fill the input (e.g., from onboarding) */
|
||||
initialPrompt?: string;
|
||||
}
|
||||
|
||||
export function ChatView({
|
||||
|
|
@ -57,6 +59,7 @@ export function ChatView({
|
|||
resolveApproval,
|
||||
onDisconnect,
|
||||
errorAction,
|
||||
initialPrompt,
|
||||
}: ChatViewProps) {
|
||||
const mainRef = useRef<HTMLElement>(null);
|
||||
const sentinelRef = useRef<HTMLDivElement>(null);
|
||||
|
|
@ -259,6 +262,7 @@ export function ChatView({
|
|||
onSubmit={sendMessage}
|
||||
disabled={isLoading || (!!error && error.code !== 'AGENT_ERROR')}
|
||||
placeholder={error && error.code !== 'AGENT_ERROR' ? "Connection error" : "Ask your Agent..."}
|
||||
defaultValue={initialPrompt}
|
||||
/>
|
||||
</footer>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,14 @@ interface MulticaIconProps extends React.ComponentProps<"span"> {
|
|||
* If true, play a one-time entrance spin animation.
|
||||
*/
|
||||
animate?: boolean;
|
||||
/**
|
||||
* If true, disable hover spin animation.
|
||||
*/
|
||||
noSpin?: boolean;
|
||||
/**
|
||||
* If true, show a border around the icon.
|
||||
*/
|
||||
bordered?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -16,6 +24,8 @@ interface MulticaIconProps extends React.ComponentProps<"span"> {
|
|||
export function MulticaIcon({
|
||||
className,
|
||||
animate = false,
|
||||
noSpin = false,
|
||||
bordered = false,
|
||||
...props
|
||||
}: MulticaIconProps) {
|
||||
const [entranceDone, setEntranceDone] = useState(!animate);
|
||||
|
|
@ -26,12 +36,47 @@ export function MulticaIcon({
|
|||
return () => clearTimeout(timer);
|
||||
}, [animate]);
|
||||
|
||||
if (bordered) {
|
||||
return (
|
||||
<span
|
||||
className={cn(
|
||||
"inline-flex items-center justify-center p-1.5 border border-border rounded-md",
|
||||
className
|
||||
)}
|
||||
aria-hidden="true"
|
||||
{...props}
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
"block size-3.5",
|
||||
!entranceDone && "animate-entrance-spin",
|
||||
entranceDone && !noSpin && "hover:animate-spin"
|
||||
)}
|
||||
>
|
||||
<span
|
||||
className="block size-full bg-current"
|
||||
style={{
|
||||
clipPath: `polygon(
|
||||
45% 62.1%, 45% 100%, 55% 100%, 55% 62.1%,
|
||||
81.8% 88.9%, 88.9% 81.8%, 62.1% 55%, 100% 55%,
|
||||
100% 45%, 62.1% 45%, 88.9% 18.2%, 81.8% 11.1%,
|
||||
55% 37.9%, 55% 0%, 45% 0%, 45% 37.9%,
|
||||
18.2% 11.1%, 11.1% 18.2%, 37.9% 45%, 0% 45%,
|
||||
0% 55%, 37.9% 55%, 11.1% 81.8%, 18.2% 88.9%
|
||||
)`,
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
className={cn(
|
||||
"inline-block size-[1em]",
|
||||
!entranceDone && "animate-entrance-spin",
|
||||
entranceDone && "hover:animate-spin",
|
||||
entranceDone && !noSpin && "hover:animate-spin",
|
||||
className
|
||||
)}
|
||||
aria-hidden="true"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue