con un clic
writing-typescript-code
Provides TypeScript and React coding standards for this repository. Use when writing or modifying TypeScript code, creating React components, implementing MSAL authentication, or working with the frontend.
Menú
Provides TypeScript and React coding standards for this repository. Use when writing or modifying TypeScript code, creating React components, implementing MSAL authentication, or working with the frontend.
Provides deployment commands and troubleshooting for Azure Container Apps. Use when running azd commands, deploying containers, debugging deployment failures, or updating infrastructure in this repository.
Provides research patterns for Azure AI Foundry Agent Service SDK. Use when implementing agent features, looking up SDK methods, finding code samples, or troubleshooting Azure.AI.Projects API usage.
Provides Playwright MCP testing workflow for the web application. Use when testing UI changes, verifying chat functionality, debugging frontend issues, or validating state transitions in the browser.
Provides authentication troubleshooting for MSAL, JWT, and Entra ID. Use when debugging 401 errors, token issues, MSAL configuration problems, or credential failures in this repository.
Provides step-by-step procedures for validating UI features - theme toggle, new chat, cancel stream, markdown rendering, and token usage info.
Provides C# and ASP.NET Core coding standards for this repository. Use when writing or modifying C# code, implementing API endpoints, configuring middleware, or working with authentication in the backend. Use when writing or modifying C# code, implementing API endpoints,
| name | writing-typescript-code |
| description | Provides TypeScript and React coding standards for this repository. Use when writing or modifying TypeScript code, creating React components, implementing MSAL authentication, or working with the frontend. |
Goal: Write type-safe React components with proper MSAL integration
The frontend runs with Vite HMR. When you edit TypeScript/React code:
VS Code Tasks (use Run Task command or check terminal panel):
Frontend: React Vite - Runs npm run dev with HMR enabledNo restart needed - Just edit, save, and see changes instantly in the browser.
Testing changes: Use Playwright browser tools to:
Enable strict mode + explicit types (avoid any):
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true
}
}
Use functional components + hooks + typed props:
interface MessageProps {
message: string;
sender: 'user' | 'agent';
}
function Message({ message, sender }: MessageProps) {
return <div className={`msg-${sender}`}>{message}</div>;
}
Always: Try silent first, fallback to popup:
try {
const { accessToken } = await instance.acquireTokenSilent({
...tokenRequest,
account: accounts[0]
});
return accessToken;
} catch {
const { accessToken } = await instance.acquireTokenPopup(tokenRequest);
return accessToken;
}
CRITICAL: Access at module level only (build-time replacement):
// ✅ Correct - module level
const clientId = import.meta.env.VITE_ENTRA_SPA_CLIENT_ID;
// ❌ Wrong - inside function (won't work after build)
function getClientId() {
return import.meta.env.VITE_ENTRA_SPA_CLIENT_ID;
}
Available variables:
VITE_ENTRA_SPA_CLIENT_ID - Entra app client IDVITE_ENTRA_TENANT_ID - Azure tenant IDUse useState (local) or Context API (shared):
const [messages, setMessages] = useState<Message[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
Use useMemo and useCallback for expensive computations and stable references:
// Memoize computed values
const isAuthenticated = useMemo(
() => accounts.length > 0,
[accounts.length]
);
// Memoize callbacks to prevent child re-renders
const getAccessToken = useCallback(async () => {
// ... token acquisition logic
}, [instance, accounts]);
// Return memoized object for stable reference
return useMemo(
() => ({ getAccessToken, isAuthenticated, user }),
[getAccessToken, isAuthenticated, user]
);
Include Authorization header + use async/await:
const response = await fetch('/api/endpoint', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
if (!response.ok) throw new Error(`API error: ${response.status}`);
React 19: Use --legacy-peer-deps flag:
npm install --legacy-peer-deps
IMPORTANT: --legacy-peer-deps skips automatic peer dependency installation. If a package requires peer dependencies, you must add them explicitly to package.json.
Example: @fluentui-copilot/react-copilot → @lexical/react → @lexical/yjs requires yjs as a peer dependency. Since peer deps aren't auto-installed, yjs must be in package.json directly.
Before committing package changes, always verify with:
npm ci # Fails if lock file is out of sync with package.json
If npm ci fails with "Missing: from lock file", add the missing package explicitly to package.json and run npm install --legacy-peer-deps again.
import.meta.env.* in functionsany typenpm install without --legacy-peer-depsuseMemo| Concern | Implementation |
|---|---|
| State Management | Centralized Context + useReducer (AppContext) with discriminated action union |
| Authentication | MSAL redirect flow; silent token refresh; useAuth hook |
| Chat Streaming | SSE in ChatService with abort controllers for cancellation |
| Accessibility | Live region (aria-live), aria labels, focus management |
| Logging | Dev-only diff-based logger |
| Component | Purpose |
|---|---|
AgentPreview.tsx | Container wiring chat state to controlled ChatInterface |
ChatInterface.tsx | Stateless controlled UI; renders messages, input, errors, BuiltWithBadge |
chat/AssistantMessage.tsx | Memoized assistant message with streaming + citation footnotes |
chat/UserMessage.tsx | Memoized user message with image thumbnail previews |
chat/ChatInput.tsx | File uploads, character counter, cancel streaming button |
chat/CitationMarker.tsx | Inline superscript citation badge with tooltip + click handler |
core/Markdown.tsx | Renders markdown with inline citation markers via ContentWithCitations |
core/BuiltWithBadge.tsx | "Built with Azure AI Foundry" link badge (centered under input) |
Parser: frontend/src/utils/citationParser.ts
Handles Azure AI Agent citation formats:
【4:0†source】, 【13†myfile.pdf】[doc1], [doc2]Flow:
parseContentWithCitations() replaces placeholders with [N] markersMarkdown.tsx renders CitationMarker components for each [N]AssistantMessage.tsx renders footnote list with icons by type (URI/file/document)Key Types (frontend/src/types/chat.ts):
IAnnotation - Citation metadata (type, label, url, fileId, quote, textToReplace)IndexedCitation - Parsed citation with display indexLimits: 5MB per file, max 5 files total
See: frontend/src/utils/fileAttachments.ts for validateImageFile() and validateFileCount()
File: frontend/src/services/chatService.ts
Key patterns:
Dispatch<AppAction> for state updatesAbortController for stream cancellation (cancelStream())retryWithBackoff() for resilient API calls (3 retries, 1s initial delay)parseSseLine() and splitSseBuffer() utilitiesMethods:
| Method | Purpose |
|---|---|
sendMessage() | Orchestrates auth, file conversion, streaming |
cancelStream() | Aborts active stream, dispatches CHAT_CANCEL_STREAM |
clearChat() | Resets conversation state |
clearError() | Clears error without affecting chat |
AppAction union in frontend/src/types/appState.tsfrontend/src/reducers/appReducer.ts (keep pure, no side effects)ChatService if network interaction neededAgentPreview.tsx to dispatch actionsaria-busy attribute on messages container during streamingaria-label when icon-onlyaria-describedby