بنقرة واحدة
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.
// 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.
| 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}`);
frontend/.npmrc sets legacy-peer-deps=true automatically — no flag needed when running from frontend/.
Gotcha: legacy-peer-deps skips automatic peer dependency installation. If a package requires peer deps, add them explicitly to package.json.
Example: @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, verify with:
npm ci # Fails if lock file is out of sync with package.json
import.meta.env.* in functionsany typenpm install outside frontend/ (misses .npmrc config)useMemo| 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 |
|---|---|
AgentChat.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 Microsoft 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 neededAgentChat.tsx to dispatch actionsaria-busy attribute on messages container during streamingaria-label when icon-onlyaria-describedbyProvides 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 SSE streaming patterns for the chat API and frontend. Use when implementing or modifying chat streaming, handling SSE events, or troubleshooting message flow between frontend and backend.
Provides research patterns for Foundry Agent Service SDK. Use when implementing agent features, looking up SDK methods, finding code samples, or troubleshooting Azure.AI.Projects API usage.
Provides architecture overview with state machines, SSE event flow, and file mappings. Use when understanding system design, debugging state issues, or maintaining ARCHITECTURE-FLOW.md.
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.
Diagnose and fix incomplete local development setup. Use when dev servers fail to start, env vars are missing, authentication errors occur, or before running any dev commands for the first time.