@tablestore/tablestore-agent-cli 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dashboard-runtime/dashboard/.next/BUILD_ID +1 -1
- package/dist/dashboard-runtime/dashboard/.next/build-manifest.json +3 -3
- package/dist/dashboard-runtime/dashboard/.next/prerender-manifest.json +3 -3
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/chat/page.js.nft.json +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/chat/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/[name]/documents/page.js.nft.json +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/[name]/documents/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/[name]/page.js.nft.json +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/[name]/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/[name]/qa/page.js.nft.json +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/[name]/qa/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/[name]/retrieval/page.js.nft.json +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/[name]/retrieval/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/[name]/settings/page.js.nft.json +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/[name]/settings/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/create/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/page.js.nft.json +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/llm-providers/[id]/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/llm-providers/new/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/llm-providers/page.js.nft.json +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/llm-providers/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/messages/page.js.nft.json +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/messages/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/page.js.nft.json +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/requests/page.js.nft.json +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/requests/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/search/page.js.nft.json +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/search/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/settings/page.js.nft.json +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/settings/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/units/page.js.nft.json +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/units/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/create/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/page.js.nft.json +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/projects/page.js.nft.json +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/projects/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/_global-error.html +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/_global-error.rsc +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/_not-found.html +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/_not-found.rsc +2 -2
- package/dist/dashboard-runtime/dashboard/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/dist/dashboard-runtime/dashboard/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/dist/dashboard-runtime/dashboard/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/dist/dashboard-runtime/dashboard/.next/server/app/api/v1/knowledge-bases/[name]/documents/upload/route.js.nft.json +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/index.html +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/index.rsc +2 -2
- package/dist/dashboard-runtime/dashboard/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/index.segments/_full.segment.rsc +2 -2
- package/dist/dashboard-runtime/dashboard/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/dist/dashboard-runtime/dashboard/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/dist/dashboard-runtime/dashboard/.next/server/app/onboarding/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/onboarding.html +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/onboarding.rsc +3 -3
- package/dist/dashboard-runtime/dashboard/.next/server/app/onboarding.segments/_full.segment.rsc +3 -3
- package/dist/dashboard-runtime/dashboard/.next/server/app/onboarding.segments/_head.segment.rsc +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/onboarding.segments/_index.segment.rsc +2 -2
- package/dist/dashboard-runtime/dashboard/.next/server/app/onboarding.segments/_tree.segment.rsc +2 -2
- package/dist/dashboard-runtime/dashboard/.next/server/app/onboarding.segments/onboarding/__PAGE__.segment.rsc +2 -2
- package/dist/dashboard-runtime/dashboard/.next/server/app/onboarding.segments/onboarding.segment.rsc +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/0b1r_app_(dashboard)_knowledge-base_[name]_documents_documents-client_tsx_0g2uxmt._.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/[root-of-the-server]__09_30hi._.js +3 -0
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/[root-of-the-server]__0kaiok7._.js +3 -0
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/[root-of-the-server]__0l937k4._.js +3 -0
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_0-dlo4s._.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_01pp~xx._.js +3 -0
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/{_0476v7r._.js → _08j6p-z._.js} +2 -2
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/{_0ct0n55._.js → _09eom0_._.js} +2 -2
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/{_0mzxmfh._.js → _0f-may2._.js} +2 -2
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_0fp_nnb._.js +3 -0
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_0l6jz-5._.js +3 -0
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/{_0fg6iap._.js → _0ntv66s._.js} +2 -2
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_0sc0tpz._.js +3 -0
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_0tyxwr3._.js +3 -0
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_0u9md36._.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/dashboard_src_065m1.j._.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/dashboard_src_app_(dashboard)_knowledge-base_[name]_layout-client_tsx_05momc5._.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/middleware-build-manifest.js +3 -3
- package/dist/dashboard-runtime/dashboard/.next/server/pages/404.html +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/pages/500.html +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/server-reference-manifest.js +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/server-reference-manifest.json +1 -1
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/0-xqk6wfme5fu.js +1 -0
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/{0jeb9kxtd_jq..js → 00.fyg_t3xjc~.js} +1 -1
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/01k-.wkndubwy.js +138 -0
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/07r22x5uhjxjb.js +1 -0
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/0cbu3udngtx-v.js +4 -0
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/0h7d69g2fdgs6.js +138 -0
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/0iv0g360z0ja5.js +1 -0
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/0jf4ytf.izmzh.js +1 -0
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/{17nx_vdk_qemm.js → 0m42mmyxmgn-i.js} +1 -1
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/0odz1whf-ozqn.css +1 -0
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/0ouxv_2qvf6.9.js +1 -0
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/0rbkgj._oqsb3.js +1 -0
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/0u6u.649ou67b.js +1 -0
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/0vf7~eysvh9bq.js +1 -0
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/{09qsmnfvk8dm3.js → 0wmaq.ay~r2sn.js} +1 -1
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/{0i5aqiffpm9jr.js → 0xb0_9t-vmo6e.js} +1 -1
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/0yp_2ea75d19v.js +1 -0
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/{0s3mvx9_dr13c.js → 11cizim1aug~b.js} +1 -1
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/13-2ei295hl0-.js +1 -0
- package/dist/dashboard-runtime/dashboard/src/app/(dashboard)/knowledge-base/[name]/qa/page.tsx +23 -1
- package/dist/dashboard-runtime/dashboard/src/components/documents/doc-upload-dialog.tsx +370 -91
- package/dist/dashboard-runtime/dashboard/src/components/knowledge-base/kb-card.tsx +8 -5
- package/dist/dashboard-runtime/dashboard/src/components/knowledge-base/kb-header.tsx +18 -10
- package/dist/dashboard-runtime/dashboard/src/components/memory/memory-header.tsx +8 -12
- package/dist/dashboard-runtime/dashboard/src/components/qa/qa-chat-panel.tsx +45 -4
- package/dist/dashboard-runtime/dashboard/src/components/qa/qa-message.tsx +636 -36
- package/dist/dashboard-runtime/dashboard/src/components/shared/confirm-dialog.tsx +8 -1
- package/dist/dashboard-runtime/dashboard/src/components/shared/refresh-button.tsx +102 -86
- package/dist/dashboard-runtime/dashboard/src/hooks/use-qa-chat.ts +261 -58
- package/dist/dashboard-runtime/dashboard/src/lib/chat-message-ops.ts +76 -0
- package/dist/dashboard-runtime/dashboard/src/lib/dev-flags.ts +24 -0
- package/dist/dashboard-runtime/dashboard/src/types/llm-chat.ts +19 -0
- package/package.json +1 -1
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/[root-of-the-server]__0i0y9.r._.js +0 -3
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/[root-of-the-server]__0r_6wpx._.js +0 -3
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/[root-of-the-server]__0y8rrcu._.js +0 -3
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_02e0c00._.js +0 -3
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_0doczxg._.js +0 -3
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_0e9y.xc._.js +0 -3
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_0ti~om_._.js +0 -3
- package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_0~s~e-0._.js +0 -3
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/055_bo6_3nt-s.js +0 -1
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/07l4dodn5t6l4.js +0 -4
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/0a5ass8u78k90.js +0 -1
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/0j~63ld9lbb25.js +0 -1
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/0l6znnjqw.4yw.js +0 -1
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/0o-c2g8y5pw-c.js +0 -1
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/0o0i2ke2jbdvq.js +0 -138
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/0u2lo2lzizxy_.js +0 -1
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/0v.hno.6q74nn.css +0 -1
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/100.ax5hitxko.js +0 -1
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/14ti2gx~1fiub.js +0 -1
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/14vqmdm7sosj2.js +0 -1
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/17h~onnkksf44.js +0 -138
- package/dist/dashboard-runtime/dashboard/.next/static/chunks/186jubvt5xyuj.js +0 -1
- /package/dist/dashboard-runtime/dashboard/.next/static/{wWmSSYUu4w4mIF1R_7UoM → BGnMIk4OPjFx_qvLFMTBc}/_buildManifest.js +0 -0
- /package/dist/dashboard-runtime/dashboard/.next/static/{wWmSSYUu4w4mIF1R_7UoM → BGnMIk4OPjFx_qvLFMTBc}/_clientMiddlewareManifest.js +0 -0
- /package/dist/dashboard-runtime/dashboard/.next/static/{wWmSSYUu4w4mIF1R_7UoM → BGnMIk4OPjFx_qvLFMTBc}/_ssgManifest.js +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
"use client";
|
|
3
3
|
|
|
4
|
-
import { useState, useCallback } from "react";
|
|
4
|
+
import { useState, useCallback, type ReactNode } from "react";
|
|
5
5
|
import {
|
|
6
6
|
Dialog,
|
|
7
7
|
DialogContent,
|
|
@@ -23,6 +23,10 @@ interface ConfirmDialogProps {
|
|
|
23
23
|
variant?: "default" | "danger";
|
|
24
24
|
onConfirm: () => void;
|
|
25
25
|
confirmInput?: string;
|
|
26
|
+
/** Optional body slot rendered between the description and the footer.
|
|
27
|
+
* Used for inline form elements (e.g. the chat-message delete dialog's
|
|
28
|
+
* 「同时删除下方的 AI 回复」 checkbox). */
|
|
29
|
+
children?: ReactNode;
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
export function ConfirmDialog({
|
|
@@ -35,6 +39,7 @@ export function ConfirmDialog({
|
|
|
35
39
|
variant = "default",
|
|
36
40
|
onConfirm,
|
|
37
41
|
confirmInput,
|
|
42
|
+
children,
|
|
38
43
|
}: ConfirmDialogProps) {
|
|
39
44
|
const [inputValue, setInputValue] = useState("");
|
|
40
45
|
|
|
@@ -81,6 +86,8 @@ export function ConfirmDialog({
|
|
|
81
86
|
</div>
|
|
82
87
|
)}
|
|
83
88
|
|
|
89
|
+
{children && <div className="py-1">{children}</div>}
|
|
90
|
+
|
|
84
91
|
<DialogFooter>
|
|
85
92
|
<Button variant="outline" onClick={() => handleOpenChange(false)}>
|
|
86
93
|
{cancelText}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useEffect, useRef
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
4
|
import { RefreshCw } from "lucide-react";
|
|
5
5
|
import { Button } from "@/components/ui/button";
|
|
6
6
|
import { cn } from "@/lib/utils";
|
|
@@ -22,32 +22,29 @@ interface RefreshButtonProps {
|
|
|
22
22
|
variant?: "outline" | "ghost";
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
const
|
|
29
|
-
/** Minimum on-screen spin time to avoid a flash on sub-100ms refetches. */
|
|
30
|
-
const MIN_SPIN_MS = 300;
|
|
31
|
-
/** Material `ease-standard`: smooth ease-in-out that looks natural on both
|
|
32
|
-
* the first turn and the final settling turn. */
|
|
33
|
-
const EASING = "cubic-bezier(0.4, 0, 0.2, 1)";
|
|
25
|
+
/** Duration of one full revolution. 800ms is the GitHub / Vercel / Slack
|
|
26
|
+
* band — fast enough to feel responsive, slow enough to read as motion
|
|
27
|
+
* rather than a blur. */
|
|
28
|
+
const SPIN_PERIOD_MS = 800;
|
|
34
29
|
|
|
35
30
|
/**
|
|
36
|
-
* Refresh button with
|
|
31
|
+
* Refresh button with **atomic chained rotations**.
|
|
37
32
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
33
|
+
* Industry pattern (GitHub, Linear, Raycast, ChatGPT regenerate): a refresh
|
|
34
|
+
* spin is not a single "infinite" CSS animation that gets stopped mid-flight
|
|
35
|
+
* — it's a chain of complete 360° cycles. Each cycle is one Animation with
|
|
36
|
+
* `iterations: 1, easing: 'linear'`. On `finish`, if loading is still in
|
|
37
|
+
* flight we start another cycle; otherwise we stop. This way:
|
|
42
38
|
*
|
|
43
|
-
* -
|
|
44
|
-
*
|
|
45
|
-
* the
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
39
|
+
* - The icon always lands cleanly at 0° (== 360°), no partial-angle settle.
|
|
40
|
+
* - Speed is constant the entire time — no "slow down to a stop" that looks
|
|
41
|
+
* like the spinner hit a wall, which is the most common cause of refresh
|
|
42
|
+
* animations feeling unnatural.
|
|
43
|
+
* - The transition between "loading" and "idle" never produces a snap-back,
|
|
44
|
+
* even if the request finishes inside a single millisecond.
|
|
45
|
+
*
|
|
46
|
+
* A minimum of one full revolution plays after a click, so sub-100ms refetches
|
|
47
|
+
* still register as "I did something" instead of flickering.
|
|
51
48
|
*
|
|
52
49
|
* Drive `loading` from TanStack Query's `isRefetching` (not `isFetching`) so
|
|
53
50
|
* background auto-refetches on mount do not animate without user intent.
|
|
@@ -59,66 +56,80 @@ export function RefreshButton({
|
|
|
59
56
|
className,
|
|
60
57
|
size = "icon",
|
|
61
58
|
label,
|
|
62
|
-
variant = "
|
|
59
|
+
variant = "ghost",
|
|
63
60
|
}: RefreshButtonProps) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
71
|
-
const stopTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
72
|
-
const startedAtRef = useRef(0);
|
|
61
|
+
const iconRef = useRef<HTMLSpanElement | null>(null);
|
|
62
|
+
/** Currently-running cycle; null when idle. */
|
|
63
|
+
const animRef = useRef<Animation | null>(null);
|
|
64
|
+
/** Mirror of the `loading` prop accessible from inside the `finish`
|
|
65
|
+
* listener, which closes over a stale value if we read the prop directly. */
|
|
66
|
+
const loadingRef = useRef(loading);
|
|
73
67
|
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
if (stopTimerRef.current) {
|
|
80
|
-
clearTimeout(stopTimerRef.current);
|
|
81
|
-
stopTimerRef.current = null;
|
|
82
|
-
}
|
|
83
|
-
};
|
|
68
|
+
const startCycle = () => {
|
|
69
|
+
const el = iconRef.current;
|
|
70
|
+
if (!el) return;
|
|
71
|
+
if (animRef.current) return;
|
|
84
72
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
73
|
+
const anim = el.animate(
|
|
74
|
+
[{ transform: "rotate(0deg)" }, { transform: "rotate(360deg)" }],
|
|
75
|
+
{
|
|
76
|
+
duration: SPIN_PERIOD_MS,
|
|
77
|
+
easing: "linear",
|
|
78
|
+
iterations: 1,
|
|
79
|
+
fill: "none",
|
|
80
|
+
},
|
|
81
|
+
);
|
|
82
|
+
animRef.current = anim;
|
|
83
|
+
|
|
84
|
+
anim.addEventListener("finish", () => {
|
|
85
|
+
animRef.current = null;
|
|
86
|
+
// Chain the next cycle if the request is still in flight. The chain
|
|
87
|
+
// is implicit, not explicit: each cycle only knows about itself.
|
|
88
|
+
if (loadingRef.current) {
|
|
89
|
+
startCycle();
|
|
90
|
+
}
|
|
91
|
+
});
|
|
95
92
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
93
|
+
// `cancel` shouldn't normally fire — we never call .cancel() ourselves —
|
|
94
|
+
// but defensively clear the ref so a fresh click can start a new cycle.
|
|
95
|
+
anim.addEventListener("cancel", () => {
|
|
96
|
+
animRef.current = null;
|
|
97
|
+
});
|
|
101
98
|
};
|
|
102
99
|
|
|
100
|
+
// Keep the ref in sync so the `finish` listener sees the latest value.
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
loadingRef.current = loading;
|
|
103
|
+
}, [loading]);
|
|
104
|
+
|
|
105
|
+
// Start a cycle when loading begins. We don't have to do anything when
|
|
106
|
+
// loading ends — the in-flight cycle finishes on its own and the chain
|
|
107
|
+
// doesn't continue because `loadingRef.current` will be false.
|
|
103
108
|
useEffect(() => {
|
|
104
109
|
if (loading) {
|
|
105
|
-
|
|
106
|
-
return;
|
|
110
|
+
startCycle();
|
|
107
111
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (remaining <= 0) {
|
|
112
|
-
stopSpinning();
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
stopTimerRef.current = setTimeout(stopSpinning, remaining);
|
|
112
|
+
// startCycle is a stable closure over refs; including it would force a
|
|
113
|
+
// re-run on every render with no behaviour change.
|
|
114
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
116
115
|
}, [loading]);
|
|
117
116
|
|
|
118
|
-
useEffect(
|
|
117
|
+
useEffect(
|
|
118
|
+
() => () => {
|
|
119
|
+
animRef.current?.cancel();
|
|
120
|
+
animRef.current = null;
|
|
121
|
+
},
|
|
122
|
+
[],
|
|
123
|
+
);
|
|
119
124
|
|
|
120
125
|
const handleClick = () => {
|
|
121
126
|
if (loading) return;
|
|
127
|
+
// Start a cycle eagerly so the user gets visual confirmation even when
|
|
128
|
+
// the request resolves in <100ms (cache hit). Cycles are atomic: once
|
|
129
|
+
// started, they always play to completion regardless of `loading` —
|
|
130
|
+
// that's what makes the "minimum one full revolution" guarantee
|
|
131
|
+
// structural rather than a separate timer.
|
|
132
|
+
startCycle();
|
|
122
133
|
onClick();
|
|
123
134
|
};
|
|
124
135
|
|
|
@@ -127,33 +138,38 @@ export function RefreshButton({
|
|
|
127
138
|
|
|
128
139
|
return (
|
|
129
140
|
<Button
|
|
141
|
+
type="button"
|
|
130
142
|
variant={variant}
|
|
131
143
|
size={size}
|
|
132
144
|
onClick={handleClick}
|
|
133
|
-
|
|
145
|
+
// We don't disable while loading: the icon is its own affordance, and
|
|
146
|
+
// disabling produces a "dead" hit state that feels worse than letting
|
|
147
|
+
// a redundant click no-op silently.
|
|
134
148
|
title={title}
|
|
135
149
|
aria-label={label ?? title}
|
|
136
|
-
|
|
150
|
+
aria-busy={loading || undefined}
|
|
151
|
+
className={cn(
|
|
152
|
+
"group text-muted-foreground transition-colors duration-150",
|
|
153
|
+
"hover:text-foreground active:scale-[0.96]",
|
|
154
|
+
// Soft hover wash for the default ghost variant; outlined stays as-is.
|
|
155
|
+
variant === "ghost" && "hover:bg-[hsl(var(--accent))]",
|
|
156
|
+
className,
|
|
157
|
+
)}
|
|
137
158
|
>
|
|
138
|
-
{/*
|
|
139
|
-
|
|
140
|
-
|
|
159
|
+
{/*
|
|
160
|
+
The icon lives inside its own span which is what we animate. We
|
|
161
|
+
intentionally do NOT add a hover-tilt to this span — a hover transform
|
|
162
|
+
and the rotate animation share the same `transform` property, and
|
|
163
|
+
switching from "tilted" to "rotating-from-0" produces a visible snap
|
|
164
|
+
back to 0deg the moment the user clicks. Hover state is conveyed via
|
|
165
|
+
text + background colour shifts on the parent button instead.
|
|
166
|
+
*/}
|
|
141
167
|
<span
|
|
168
|
+
ref={iconRef}
|
|
142
169
|
aria-hidden
|
|
143
170
|
className={cn("inline-flex transform-gpu", showLabel && "mr-1.5")}
|
|
144
|
-
style={{
|
|
145
|
-
transform: `rotate(${turns * 360}deg)`,
|
|
146
|
-
transition: `transform ${TURN_MS}ms ${EASING}`,
|
|
147
|
-
}}
|
|
148
171
|
>
|
|
149
|
-
{
|
|
150
|
-
the outer rotation so the two transforms compose without fighting. */}
|
|
151
|
-
<RefreshCw
|
|
152
|
-
className={cn(
|
|
153
|
-
iconSize,
|
|
154
|
-
"transition-transform duration-200 group-hover:-rotate-[35deg]",
|
|
155
|
-
)}
|
|
156
|
-
/>
|
|
172
|
+
<RefreshCw className={iconSize} />
|
|
157
173
|
</span>
|
|
158
174
|
{showLabel && label}
|
|
159
175
|
</Button>
|