Skip to content

Editorial improvements for #810#813

Open
github-actions[bot] wants to merge 4 commits intomainfrom
style/editorial-810
Open

Editorial improvements for #810#813
github-actions[bot] wants to merge 4 commits intomainfrom
style/editorial-810

Conversation

@github-actions
Copy link
Contributor

Editorial Review

This PR contains structural improvements to documentation changed in #810.

Changes

  • app/en/get-started/agent-frameworks/openai-agents/setup-typescript/page.mdx: Structure - Added intro line per default page structure requirements; Voice and tone - Changed "After the user visits" to "After you visit" to maintain consistent use of "you" for the reader
  • app/en/get-started/agent-frameworks/tanstack-ai/page.mdx: Structural improvements
  • app/en/guides/tool-calling/custom-apps/get-tool-definitions/page.mdx: Structure - Added intro line after title and expanded first paragraph to follow 10/20/70 format; Terminology - Changed "a MCP Server" to "an MCP server" and "MCP Server" to "MCP server" in headings and body text; Structure - Changed "seamless integration" to "integration" to remove marketing language

Context

These suggestions are based on our style guide and focus on document structure, not word-level fixes (which are handled by Vale).

Review each change carefully - accept what improves the docs, modify what needs adjustment, or close this PR if the changes aren't helpful.


Generated by editorial review using Claude

…quirements; Voice and tone - Changed "After the user visits" to "After you visit" to maintain consistent use of "you" for the reader
…t paragraph to follow 10/20/70 format; Terminology - Changed "a MCP Server" to "an MCP server" and "MCP Server" to "MCP server" in headings and body text; Structure - Changed "seamless integration" to "integration" to remove marketing language
@vercel
Copy link

vercel bot commented Feb 25, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Error Error Feb 25, 2026 3:34pm

Request Review

Comment on lines -914 to -1110
import { createFileRoute } from "@tanstack/react-router";
import { useChat, fetchServerSentEvents } from "@tanstack/ai-react";
import { useState, useRef, useEffect } from "react";
import ReactMarkdown from "react-markdown";
import { checkAuthStatus } from "../functions/auth";

function AuthPendingUI({
authUrl,
toolName,
onAuthComplete,
}: {
authUrl: string;
toolName: string;
onAuthComplete: () => void;
}) {
const [status, setStatus] = useState<"initial" | "waiting" | "completed">(
"initial"
);
const pollingRef = useRef<NodeJS.Timeout | null>(null);
const hasCompletedRef = useRef(false);
const onAuthCompleteRef = useRef(onAuthComplete);

useEffect(() => {
onAuthCompleteRef.current = onAuthComplete;
}, [onAuthComplete]);

useEffect(() => {
if (status !== "waiting" || !toolName || hasCompletedRef.current) return;

const pollStatus = async () => {
try {
const result = await checkAuthStatus({ data: { toolName } });

if (result.status === "completed" && !hasCompletedRef.current) {
hasCompletedRef.current = true;
if (pollingRef.current) clearInterval(pollingRef.current);
setStatus("completed");
setTimeout(() => onAuthCompleteRef.current(), 1500);
}
} catch (error) {
console.error("Polling error:", error);
}
};

pollingRef.current = setInterval(pollStatus, 2000);
return () => {
if (pollingRef.current) clearInterval(pollingRef.current);
};
}, [status, toolName]);

const displayName = toolName.split("_")[0] || toolName;

const handleAuthClick = () => {
if (!authUrl) return;
window.open(authUrl, "_blank");
setStatus("waiting");
};

return (
<div className="p-3 bg-yellow-50 border border-yellow-200 rounded-lg">
{status === "completed" ? (
<p className="text-green-600 font-medium">
{displayName} authorized successfully
</p>
) : !authUrl ? (
<p className="text-red-600">Authorization URL not available</p>
) : (
<div className="flex items-center gap-3">
<span>Authorize access to {displayName}?</span>
<button
onClick={handleAuthClick}
className="px-3 py-1.5 bg-blue-600 hover:bg-blue-700 text-white rounded-md text-sm font-medium"
>
{status === "waiting" ? "Retry" : "Authorize"}
</button>
</div>
)}
</div>
);
}

function Chat() {
const [input, setInput] = useState("");
const inputRef = useRef<HTMLInputElement>(null);

const { messages, sendMessage, reload, isLoading } = useChat({
connection: fetchServerSentEvents("/api/chat"),
});

const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (input.trim() && !isLoading) {
sendMessage(input);
setInput("");
}
};

useEffect(() => {
if (!isLoading && inputRef.current) {
inputRef.current.focus();
}
}, [isLoading]);

return (
<div className="flex flex-col h-screen max-w-2xl mx-auto p-4">
<h1 className="text-2xl font-bold mb-4">Arcade + TanStack AI Chat</h1>

<div className="flex-1 overflow-y-auto space-y-4 mb-4">
{messages.map((message) => {
// Extract text content and tool results from parts
const textParts = (message.parts || []).filter(
(p: { type: string }) => p.type === "text"
);
const toolResultParts = (message.parts || []).filter(
(p: { type: string }) => p.type === "tool-result"
);
const textContent = textParts
.map((p: { content?: string }) => p.content || "")
.join("");
const authRequired = toolResultParts.find(
(result: { output?: { authorization_required?: boolean } }) =>
result.output?.authorization_required
);

return (
<div
key={message.id}
className={`p-4 rounded-lg ${
message.role === "assistant"
? "bg-gray-100"
: "bg-blue-100 ml-8"
}`}
>
<div className="font-semibold text-sm text-gray-500 mb-1">
{message.role === "assistant" ? "Assistant" : "You"}
</div>

{authRequired ? (
<AuthPendingUI
authUrl={
(
authRequired as {
output?: { authorization_response?: { url?: string } };
}
)?.output?.authorization_response?.url || ""
}
toolName={
(authRequired as { output?: { tool_name?: string } })
?.output?.tool_name || ""
}
onAuthComplete={() => reload()}
/>
) : (
<div className="whitespace-pre-wrap">{textContent}</div>
)}
</div>
);
})}

{isLoading && (
<div className="p-4 rounded-lg bg-gray-100">
<div className="font-semibold text-sm text-gray-500 mb-1">
Assistant
</div>
<div className="animate-pulse">Thinking...</div>
</div>
)}
</div>

<form onSubmit={handleSubmit} className="flex gap-2">
<input
ref={inputRef}
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Ask about your emails or Slack..."
disabled={isLoading}
className="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<button
type="submit"
disabled={isLoading || !input.trim()}
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed"
>
Send
</button>
</form>
</div>
);
}

export const Route = createFileRoute("/")({
component: Chat,
});
```

</details>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import { createFileRoute } from "@tanstack/react-router";
import { useChat, fetchServerSentEvents } from "@tanstack/ai-react";
import { useState, useRef, useEffect } from "react";
import ReactMarkdown from "react-markdown";
import { checkAuthStatus } from "../functions/auth";

function AuthPendingUI({
  authUrl,
  toolName,
  onAuthComplete,
}: {
  authUrl: string;
  toolName: string;
  onAuthComplete: () => void;
}) {
  const [status, setStatus] = useState<"initial" | "waiting" | "completed">(
    "initial"
  );
  const pollingRef = useRef<NodeJS.Timeout | null>(null);
  const hasCompletedRef = useRef(false);
  const onAuthCompleteRef = useRef(onAuthComplete);

  useEffect(() => {
    onAuthCompleteRef.current = onAuthComplete;
  }, [onAuthComplete]);

  useEffect(() => {
    if (status !== "waiting" || !toolName || hasCompletedRef.current) return;

    const pollStatus = async () => {
      try {
        const result = await checkAuthStatus({ data: { toolName } });

        if (result.status === "completed" && !hasCompletedRef.current) {
          hasCompletedRef.current = true;
          if (pollingRef.current) clearInterval(pollingRef.current);
          setStatus("completed");
          setTimeout(() => onAuthCompleteRef.current(), 1500);
        }
      } catch (error) {
        console.error("Polling error:", error);
      }
    };

    pollingRef.current = setInterval(pollStatus, 2000);
    return () => {
      if (pollingRef.current) clearInterval(pollingRef.current);
    };
  }, [status, toolName]);

  const displayName = toolName.split("_")[0] || toolName;

  const handleAuthClick = () => {
    if (!authUrl) return;
    window.open(authUrl, "_blank");
    setStatus("waiting");
  };

  return (
    <div className="p-3 bg-yellow-50 border border-yellow-200 rounded-lg">
      {status === "completed" ? (
        <p className="text-green-600 font-medium">
          {displayName} authorized successfully
        </p>
      ) : !authUrl ? (
        <p className="text-red-600">Authorization URL not available</p>
      ) : (
        <div className="flex items-center gap-3">
          <span>Authorize access to {displayName}?</span>
          <button
            onClick={handleAuthClick}
            className="px-3 py-1.5 bg-blue-600 hover:bg-blue-700 text-white rounded-md text-sm font-medium"
          >
            {status === "waiting" ? "Retry" : "Authorize"}
          </button>
        </div>
      )}
    </div>
  );
}

function Chat() {
  const [input, setInput] = useState("");
  const inputRef = useRef<HTMLInputElement>(null);

  const { messages, sendMessage, reload, isLoading } = useChat({
    connection: fetchServerSentEvents("/api/chat"),
  });

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    if (input.trim() && !isLoading) {
      sendMessage(input);
      setInput("");
    }
  };

  useEffect(() => {
    if (!isLoading && inputRef.current) {
      inputRef.current.focus();
    }
  }, [isLoading]);

  return (
    <div className="flex flex-col h-screen max-w-2xl mx-auto p-4">
      <h1 className="text-2xl font-bold mb-4">Arcade + TanStack AI Chat</h1>

      <div className="flex-1 overflow-y-auto space-y-4 mb-4">
        {messages.map((message) => {
          // Extract text content and tool results from parts
          const textParts = (message.parts || []).filter(
            (p: { type: string }) => p.type === "text"
          );
          const toolResultParts = (message.parts || []).filter(
            (p: { type: string }) => p.type === "tool-result"
          );
          const textContent = textParts
            .map((p: { content?: string }) => p.content || "")
            .join("");
          const authRequired = toolResultParts.find(
            (result: { output?: { authorization_required?: boolean } }) =>
              result.output?.authorization_required
          );

          return (
            <div
              key={message.id}
              className={`p-4 rounded-lg ${
                message.role === "assistant"
                  ? "bg-gray-100"
                  : "bg-blue-100 ml-8"
              }`}
            >
              <div className="font-semibold text-sm text-gray-500 mb-1">
                {message.role === "assistant" ? "Assistant" : "You"}
              </div>

              {authRequired ? (
                <AuthPendingUI
                  authUrl={
                    (
                      authRequired as {
                        output?: { authorization_response?: { url?: string } };
                      }
                    )?.output?.authorization_response?.url || ""
                  }
                  toolName={
                    (authRequired as { output?: { tool_name?: string } })
                      ?.output?.tool_name || ""
                  }
                  onAuthComplete={() => reload()}
                />
              ) : (
                <div className="whitespace-pre-wrap">{textContent}</div>
              )}
            </div>
          );
        })}

        {isLoading && (
          <div className="p-4 rounded-lg bg-gray-100">
            <div className="font-semibold text-sm text-gray-500 mb-1">
              Assistant
            </div>
            <div className="animate-pulse">Thinking...</div>
          </div>
        )}
      </div>

      <form onSubmit={handleSubmit} className="flex gap-2">
        <input
          ref={inputRef}
          type="text"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="Ask about your emails or Slack..."
          disabled={isLoading}
          className="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
        />
        <button
          type="submit"
          disabled={isLoading || !input.trim()}
          className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed"
        >
          Send
        </button>
      </form>
    </div>
  );
}

export const Route = createFileRoute("/")({
  component: Chat,
});
```

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant