Add in-app micro-feedback prompts for new features

Show a one-time toast asking how a feature is working after its third
successful use, with an optional comment sent anonymously to the Yaak
API (feature key, text, app version, and OS only — nothing identifying,
and nothing is sent unless the user clicks Send).

- New cmd_send_feedback Tauri command posts fire-and-forget via the
  shared API client (localhost in dev)
- Feature keys registry (cookie-editor, response-history, sse-summary,
  git-sync) with per-feature use counting in the key-value store
- "Never ask for feedback" setting to disable prompts entirely
- Toast gains dynamicHeight and hideDismiss props for richer content
- Fix missing vertical padding on xs/2xs multiline inputs
- Fix unused-import and dead-code warnings in yaak-system-appearance
  on non-Linux builds

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Gregory Schier
2026-07-04 15:06:21 -07:00
parent e52853cc2d
commit b1f1363502
18 changed files with 283 additions and 13 deletions
+34 -10
View File
@@ -15,6 +15,12 @@ export interface ToastProps {
action?: (args: { hide: () => void }) => ReactNode;
icon?: ShowToastRequest["icon"] | null;
color?: ShowToastRequest["color"];
// Grow with the content (up to the viewport) instead of scrolling internally
// past the default max height
dynamicHeight?: boolean;
// Hide the close button, for toasts that render their own dismiss action.
// Escape still closes the toast
hideDismiss?: boolean;
}
const ICONS: Record<NonNullable<ToastProps["color"] | "custom">, IconProps["icon"] | null> = {
@@ -28,7 +34,17 @@ const ICONS: Record<NonNullable<ToastProps["color"] | "custom">, IconProps["icon
warning: "alert_triangle",
};
export function Toast({ children, open, onClose, timeout, action, icon, color }: ToastProps) {
export function Toast({
children,
open,
onClose,
timeout,
action,
icon,
color,
dynamicHeight,
hideDismiss,
}: ToastProps) {
useKey(
"Escape",
() => {
@@ -57,7 +73,13 @@ export function Toast({ children, open, onClose, timeout, action, icon, color }:
"border border-border shadow-lg w-100",
)}
>
<div className="pl-3 py-3 pr-10 flex items-start gap-2 w-full max-h-44 overflow-auto">
<div
className={classNames(
"pl-3 py-3 flex items-start gap-2 w-full overflow-auto",
hideDismiss ? "pr-3" : "pr-10",
dynamicHeight ? "max-h-[80vh]" : "max-h-44",
)}
>
{toastIcon && <Icon icon={toastIcon} color={color} className="mt-1 shrink-0" />}
<VStack space={2} className="w-full min-w-0">
<div className="select-auto">{children}</div>
@@ -65,14 +87,16 @@ export function Toast({ children, open, onClose, timeout, action, icon, color }:
</VStack>
</div>
<IconButton
color={color}
variant="border"
className="opacity-60 border-0 absolute! top-2 right-2"
title="Dismiss"
icon="x"
onClick={onClose}
/>
{!hideDismiss && (
<IconButton
color={color}
variant="border"
className="opacity-60 border-0 absolute! top-2 right-2"
title="Dismiss"
icon="x"
onClick={onClose}
/>
)}
{timeout != null && (
<div className="w-full absolute bottom-0 left-0 right-0">