organify-ui 0.3.6 → 0.3.7

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/ai/index.js CHANGED
@@ -1,3 +1,3 @@
1
- export { createAiClient, useAiChat, useAiCommand, useAiSuggest, useAiUsage } from '../chunk-P2ORBJBL.js';
1
+ export { createAiClient, useAiChat, useAiCommand, useAiSuggest, useAiUsage } from '../chunk-NV4RWAQ2.js';
2
2
  //# sourceMappingURL=index.js.map
3
3
  //# sourceMappingURL=index.js.map
@@ -29,7 +29,11 @@ async function request(config, method, path, body, query) {
29
29
  err.statusCode = res.status;
30
30
  throw err;
31
31
  }
32
- return res.json();
32
+ const json = await res.json();
33
+ if (json && typeof json === "object" && "success" in json && "data" in json) {
34
+ return json.data;
35
+ }
36
+ return json;
33
37
  }
34
38
  function createAiClient(config) {
35
39
  return {
@@ -265,5 +269,5 @@ function useAiUsage({ client: clientOrConfig }) {
265
269
  }
266
270
 
267
271
  export { createAiClient, useAiChat, useAiCommand, useAiSuggest, useAiUsage };
268
- //# sourceMappingURL=chunk-P2ORBJBL.js.map
269
- //# sourceMappingURL=chunk-P2ORBJBL.js.map
272
+ //# sourceMappingURL=chunk-NV4RWAQ2.js.map
273
+ //# sourceMappingURL=chunk-NV4RWAQ2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ai/client.ts","../src/ai/hooks.ts"],"names":[],"mappings":"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","file":"chunk-NV4RWAQ2.js","sourcesContent":["// ─────────────────────────────────────────────────────────────────────────────\n// organify-ui — AI Client\n// Typed, fetch-based client for the Organify AI Service (via API Gateway).\n//\n// Usage:\n// import { createAiClient } from 'organify-ui/ai';\n// const ai = createAiClient({ baseUrl: process.env.NEXT_PUBLIC_GATEWAY_URL! });\n// const result = await ai.chat({ message: 'Cria uma tarefa…', workspaceId: 'ws_1' });\n// ─────────────────────────────────────────────────────────────────────────────\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\nexport interface AiAction {\n type: string;\n label: string;\n description?: string;\n applied?: boolean;\n}\n\nexport interface AiChatResponse {\n message: string;\n provider: string;\n tokensUsed: number;\n actions: AiAction[];\n}\n\nexport interface AiCommandResponse {\n message: string;\n provider: string;\n tokensUsed: number;\n actions: AiAction[];\n}\n\nexport interface AiSuggestResponse {\n suggestion: string;\n}\n\nexport interface AiResetResponse {\n message: string;\n}\n\nexport interface AiUsageResponse {\n used: number;\n limit: number;\n remaining: number;\n}\n\nexport interface AiAdminLogsResponse {\n logs: AiAdminLog[];\n total: number;\n}\n\nexport interface AiAdminLog {\n userId: string;\n action: string;\n tokensUsed: number;\n timestamp: string;\n workspaceId?: string;\n projectId?: string;\n}\n\nexport interface AiAdminStatsResponse {\n totalRequests: number;\n totalTokens: number;\n uniqueUsers: number;\n [key: string]: unknown;\n}\n\nexport interface AiAdminUsersResponse {\n users: AiAdminUserStat[];\n}\n\nexport interface AiAdminUserStat {\n userId: string;\n requests: number;\n tokens: number;\n}\n\n// ── Config ───────────────────────────────────────────────────────────────────\n\nexport interface AiClientConfig {\n /**\n * Base URL of the API gateway (e.g. \"https://api.organify.app\" or\n * \"http://localhost:4000\"). Must NOT have a trailing slash.\n */\n baseUrl: string;\n /**\n * Optional – if provided, sends as `Authorization: Bearer <token>`.\n * The gateway will forward it and inject user-identity headers before\n * reaching the AI service.\n */\n getToken?: () => string | null | undefined;\n}\n\n// ── Internal helpers ─────────────────────────────────────────────────────────\n\nasync function request<T>(\n config: AiClientConfig,\n method: 'GET' | 'POST',\n path: string,\n body?: Record<string, unknown>,\n query?: Record<string, string>,\n): Promise<T> {\n const token = config.getToken?.();\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n };\n if (token) headers['Authorization'] = `Bearer ${token}`;\n\n let url = `${config.baseUrl}${path}`;\n if (query && Object.keys(query).length) {\n url += '?' + new URLSearchParams(query).toString();\n }\n\n const res = await fetch(url, {\n method,\n headers,\n credentials: 'include',\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!res.ok) {\n let errorBody: { message?: string } = {};\n try {\n errorBody = await res.json();\n } catch { /* ignore */ }\n const msg = errorBody.message ?? res.statusText ?? 'Request failed';\n const err = new Error(msg) as Error & { statusCode: number };\n err.statusCode = res.status;\n throw err;\n }\n\n const json = await res.json();\n // Unwrap NestJS standard response envelope: { success, data }\n if (json && typeof json === 'object' && 'success' in json && 'data' in json) {\n return json.data as T;\n }\n return json as T;\n}\n\n// ── Client factory ───────────────────────────────────────────────────────────\n\nexport function createAiClient(config: AiClientConfig) {\n return {\n /**\n * Multi-turn conversational chat.\n * The gateway injects `x-user-id` / `x-user-plan` from the JWT.\n */\n chat(params: {\n message: string;\n workspaceId: string;\n projectId?: string;\n }): Promise<AiChatResponse> {\n return request<AiChatResponse>(config, 'POST', '/api/ai/chat', params);\n },\n\n /**\n * Single-shot command (⌘K palette).\n */\n command(params: {\n prompt: string;\n workspaceId: string;\n projectId?: string;\n }): Promise<AiCommandResponse> {\n return request<AiCommandResponse>(config, 'POST', '/api/ai/command', params);\n },\n\n /**\n * Lightweight inline suggestion (no rate-limit increment).\n */\n suggest(params: { prompt: string }): Promise<AiSuggestResponse> {\n return request<AiSuggestResponse>(config, 'POST', '/api/ai/suggest', params);\n },\n\n /**\n * Reset conversation context for a workspace.\n */\n reset(params: { workspaceId: string }): Promise<AiResetResponse> {\n return request<AiResetResponse>(config, 'POST', '/api/ai/reset', params);\n },\n\n /**\n * Get per-user usage stats (requests used today, daily limit, remaining).\n */\n getUsage(): Promise<AiUsageResponse> {\n return request<AiUsageResponse>(config, 'GET', '/api/ai/usage');\n },\n\n // ── Admin ──────────────────────────────────────────────────────────────\n\n /** [Admin] Get request logs. Requires `x-user-role: admin` in JWT. */\n adminLogs(query?: Record<string, string>): Promise<AiAdminLogsResponse> {\n return request<AiAdminLogsResponse>(config, 'GET', '/api/ai/admin/logs', undefined, query);\n },\n\n /** [Admin] Get aggregate stats. Requires `x-user-role: admin` in JWT. */\n adminStats(query?: Record<string, string>): Promise<AiAdminStatsResponse> {\n return request<AiAdminStatsResponse>(config, 'GET', '/api/ai/admin/stats', undefined, query);\n },\n\n /** [Admin] Get per-user usage stats. Requires `x-user-role: admin` in JWT. */\n adminUsers(query?: Record<string, string>): Promise<AiAdminUsersResponse> {\n return request<AiAdminUsersResponse>(config, 'GET', '/api/ai/admin/users', undefined, query);\n },\n };\n}\n\nexport type AiClient = ReturnType<typeof createAiClient>;\n","'use client';\n// ─────────────────────────────────────────────────────────────────────────────\n// organify-ui — AI Hooks\n// React hooks that wrap `AiClient` with local state for easy use with the\n// `AiChatSidebar`, `CommandBar`, and `InlineAiButton` components.\n//\n// All hooks accept an `AiClient` instance (or `AiClientConfig` shorthand so\n// the client is created lazily).\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport * as React from 'react';\nimport { createAiClient } from './client';\nimport type { AiClient, AiClientConfig, AiAction, AiUsageResponse } from './client';\nimport type { AiChatMessage } from '../components/chat/ai-chat-sidebar';\n\n// ── Shared helpers ────────────────────────────────────────────────────────────\n\nfunction useClient(clientOrConfig: AiClient | AiClientConfig): AiClient {\n return React.useMemo(() => {\n // Duck-type: if it has a `chat` function it's already a client\n if (typeof (clientOrConfig as AiClient).chat === 'function') {\n return clientOrConfig as AiClient;\n }\n return createAiClient(clientOrConfig as AiClientConfig);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []); // intentionally stable — client identity doesn't change\n}\n\nfunction makeId() {\n return Math.random().toString(36).slice(2);\n}\n\n// ── useAiChat ────────────────────────────────────────────────────────────────\n\nexport interface UseAiChatOptions {\n client: AiClient | AiClientConfig;\n workspaceId: string;\n projectId?: string;\n}\n\nexport interface UseAiChatResult {\n messages: AiChatMessage[];\n loading: boolean;\n error: string | null;\n send: (message: string) => Promise<void>;\n reset: () => Promise<void>;\n clearMessages: () => void;\n}\n\n/**\n * Manages conversation state for `<AiChatSidebar>`.\n *\n * @example\n * const chat = useAiChat({ client: ai, workspaceId: 'ws_1' });\n * <AiChatSidebar messages={chat.messages} onSend={chat.send} loading={chat.loading} ... />\n */\nexport function useAiChat({\n client: clientOrConfig,\n workspaceId,\n projectId,\n}: UseAiChatOptions): UseAiChatResult {\n const client = useClient(clientOrConfig);\n const [messages, setMessages] = React.useState<AiChatMessage[]>([]);\n const [loading, setLoading] = React.useState(false);\n const [error, setError] = React.useState<string | null>(null);\n\n const send = React.useCallback(\n async (message: string) => {\n // Optimistically append user message\n const userMsg: AiChatMessage = {\n id: makeId(),\n role: 'user',\n content: message,\n timestamp: new Date(),\n };\n const placeholderId = makeId();\n const placeholder: AiChatMessage = {\n id: placeholderId,\n role: 'assistant',\n content: '',\n timestamp: new Date(),\n loading: true,\n };\n setMessages((prev) => [...prev, userMsg, placeholder]);\n setLoading(true);\n setError(null);\n\n try {\n const res = await client.chat({ message, workspaceId, projectId });\n const assistantMsg: AiChatMessage = {\n id: makeId(),\n role: 'assistant',\n content: res.message,\n timestamp: new Date(),\n actions: res.actions as AiAction[],\n };\n // Replace loading placeholder\n setMessages((prev) => prev.map((m) => (m.id === placeholderId ? assistantMsg : m)));\n } catch (err: unknown) {\n const errMsg = err instanceof Error ? err.message : 'Erro ao contactar o assistente IA';\n setError(errMsg);\n const errAssistant: AiChatMessage = {\n id: makeId(),\n role: 'assistant',\n content: `⚠️ ${errMsg}`,\n timestamp: new Date(),\n };\n setMessages((prev) => prev.map((m) => (m.id === placeholderId ? errAssistant : m)));\n } finally {\n setLoading(false);\n }\n },\n [client, workspaceId, projectId],\n );\n\n const reset = React.useCallback(async () => {\n try {\n await client.reset({ workspaceId });\n setMessages([]);\n setError(null);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : 'Erro ao reiniciar contexto';\n setError(msg);\n }\n }, [client, workspaceId]);\n\n const clearMessages = React.useCallback(() => setMessages([]), []);\n\n return { messages, loading, error, send, reset, clearMessages };\n}\n\n// ── useAiCommand ─────────────────────────────────────────────────────────────\n\nexport interface UseAiCommandOptions {\n client: AiClient | AiClientConfig;\n workspaceId: string;\n projectId?: string;\n}\n\nexport interface UseAiCommandResult {\n loading: boolean;\n response: string | null;\n actions: AiAction[];\n error: string | null;\n runCommand: (prompt: string) => Promise<void>;\n clear: () => void;\n}\n\n/**\n * Handles a single ⌘K command prompt for `<CommandBar>`.\n *\n * @example\n * const cmd = useAiCommand({ client: ai, workspaceId: 'ws_1' });\n * <CommandBar onAiPrompt={cmd.runCommand} aiLoading={cmd.loading} aiResponse={cmd.response ?? undefined} ... />\n */\nexport function useAiCommand({\n client: clientOrConfig,\n workspaceId,\n projectId,\n}: UseAiCommandOptions): UseAiCommandResult {\n const client = useClient(clientOrConfig);\n const [loading, setLoading] = React.useState(false);\n const [response, setResponse] = React.useState<string | null>(null);\n const [actions, setActions] = React.useState<AiAction[]>([]);\n const [error, setError] = React.useState<string | null>(null);\n\n const runCommand = React.useCallback(\n async (prompt: string) => {\n setLoading(true);\n setError(null);\n setResponse(null);\n setActions([]);\n try {\n const res = await client.command({ prompt, workspaceId, projectId });\n setResponse(res.message);\n setActions(res.actions ?? []);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : 'Erro ao executar comando';\n setError(msg);\n setResponse(`⚠️ ${msg}`);\n } finally {\n setLoading(false);\n }\n },\n [client, workspaceId, projectId],\n );\n\n const clear = React.useCallback(() => {\n setResponse(null);\n setActions([]);\n setError(null);\n }, []);\n\n return { loading, response, actions, error, runCommand, clear };\n}\n\n// ── useAiSuggest ─────────────────────────────────────────────────────────────\n\nexport interface UseAiSuggestOptions {\n client: AiClient | AiClientConfig;\n /** Debounce delay in ms before firing the API call (default: 400) */\n debounceMs?: number;\n}\n\nexport interface UseAiSuggestResult {\n loading: boolean;\n suggestion: string | null;\n error: string | null;\n getSuggestion: (prompt: string) => void;\n accept: () => string | null;\n dismiss: () => void;\n}\n\n/**\n * Provides debounced inline AI suggestions for `<InlineAiButton>`.\n *\n * @example\n * const inline = useAiSuggest({ client: ai });\n * <InlineAiButton\n * onTrigger={(text) => inline.getSuggestion(text ?? '')}\n * loading={inline.loading}\n * suggestion={inline.suggestion ?? undefined}\n * onAccept={inline.accept}\n * onDismiss={inline.dismiss}\n * />\n */\nexport function useAiSuggest({\n client: clientOrConfig,\n debounceMs = 400,\n}: UseAiSuggestOptions): UseAiSuggestResult {\n const client = useClient(clientOrConfig);\n const [loading, setLoading] = React.useState(false);\n const [suggestion, setSuggestion] = React.useState<string | null>(null);\n const [error, setError] = React.useState<string | null>(null);\n const timerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const getSuggestion = React.useCallback(\n (prompt: string) => {\n if (timerRef.current) clearTimeout(timerRef.current);\n if (!prompt.trim()) {\n setSuggestion(null);\n return;\n }\n timerRef.current = setTimeout(async () => {\n setLoading(true);\n setError(null);\n try {\n const res = await client.suggest({ prompt });\n setSuggestion(res.suggestion);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : 'Erro ao obter sugestão';\n setError(msg);\n } finally {\n setLoading(false);\n }\n }, debounceMs);\n },\n [client, debounceMs],\n );\n\n const accept = React.useCallback(() => {\n const val = suggestion;\n setSuggestion(null);\n return val;\n }, [suggestion]);\n\n const dismiss = React.useCallback(() => {\n setSuggestion(null);\n }, []);\n\n // Clean up timer on unmount\n React.useEffect(() => {\n return () => { if (timerRef.current) clearTimeout(timerRef.current); };\n }, []);\n\n return { loading, suggestion, error, getSuggestion, accept, dismiss };\n}\n\n// ── useAiUsage ───────────────────────────────────────────────────────────────\n\nexport interface UseAiUsageResult {\n usage: AiUsageResponse | null;\n loading: boolean;\n error: string | null;\n refresh: () => Promise<void>;\n}\n\n/**\n * Fetches and caches the current user's AI usage stats.\n *\n * @example\n * const { usage } = useAiUsage({ client: ai });\n * <p>{usage?.remaining} requests left today</p>\n */\nexport function useAiUsage({ client: clientOrConfig }: { client: AiClient | AiClientConfig }): UseAiUsageResult {\n const client = useClient(clientOrConfig);\n const [usage, setUsage] = React.useState<AiUsageResponse | null>(null);\n const [loading, setLoading] = React.useState(false);\n const [error, setError] = React.useState<string | null>(null);\n\n const refresh = React.useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const data = await client.getUsage();\n setUsage(data);\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : 'Erro ao obter uso');\n } finally {\n setLoading(false);\n }\n }, [client]);\n\n React.useEffect(() => {\n refresh();\n }, [refresh]);\n\n return { usage, loading, error, refresh };\n}\n"]}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Popover, PopoverTrigger, PopoverContent } from './chunk-SAYB3NN2.js';
2
2
  export { NotificationBell, NotificationItem, NotificationList, OrganifyNotifications, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PresenceAvatarStack, PresenceIndicator, useNotifications, usePresence } from './chunk-SAYB3NN2.js';
3
- export { createAiClient, useAiChat, useAiCommand, useAiSuggest, useAiUsage } from './chunk-P2ORBJBL.js';
3
+ export { createAiClient, useAiChat, useAiCommand, useAiSuggest, useAiUsage } from './chunk-NV4RWAQ2.js';
4
4
  export { I18nProvider, createTranslator, useI18n } from './chunk-FQA33MF4.js';
5
5
  export { ThemeProvider, useTheme } from './chunk-RFOKENE3.js';
6
6
  import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Drawer, DrawerContent, DrawerHeader, DrawerTitle, DrawerDescription, Separator, ResponsiveDialog, Label, Input, Textarea, Button, Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from './chunk-F7LW2OMM.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "organify-ui",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "description": "Organify Design System — Liquid Glass UI components with glassmorphism, branded icons, i18n, and theme system for React",
5
5
  "license": "MIT",
6
6
  "author": "Organify <dev@organify.studio>",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/ai/client.ts","../src/ai/hooks.ts"],"names":[],"mappings":";;;AAgGA,eAAe,OAAA,CACb,MAAA,EACA,MAAA,EACA,IAAA,EACA,MACA,KAAA,EACY;AACZ,EAAA,MAAM,KAAA,GAAQ,OAAO,QAAA,IAAW;AAChC,EAAA,MAAM,OAAA,GAAkC;AAAA,IACtC,cAAA,EAAgB,kBAAA;AAAA,IAChB,MAAA,EAAQ;AAAA,GACV;AACA,EAAA,IAAI,KAAA,EAAO,OAAA,CAAQ,eAAe,CAAA,GAAI,UAAU,KAAK,CAAA,CAAA;AAErD,EAAA,IAAI,GAAA,GAAM,CAAA,EAAG,MAAA,CAAO,OAAO,GAAG,IAAI,CAAA,CAAA;AAClC,EAAA,IAAI,KAAA,IAAS,MAAA,CAAO,IAAA,CAAK,KAAK,EAAE,MAAA,EAAQ;AACtC,IAAA,GAAA,IAAO,GAAA,GAAM,IAAI,eAAA,CAAgB,KAAK,EAAE,QAAA,EAAS;AAAA,EACnD;AAEA,EAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,IAC3B,MAAA;AAAA,IACA,OAAA;AAAA,IACA,WAAA,EAAa,SAAA;AAAA,IACb,IAAA,EAAM,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA,GAAI;AAAA,GACrC,CAAA;AAED,EAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,IAAA,IAAI,YAAkC,EAAC;AACvC,IAAA,IAAI;AACF,MAAA,SAAA,GAAY,MAAM,IAAI,IAAA,EAAK;AAAA,IAC7B,CAAA,CAAA,MAAQ;AAAA,IAAe;AACvB,IAAA,MAAM,GAAA,GAAM,SAAA,CAAU,OAAA,IAAW,GAAA,CAAI,UAAA,IAAc,gBAAA;AACnD,IAAA,MAAM,GAAA,GAAM,IAAI,KAAA,CAAM,GAAG,CAAA;AACzB,IAAA,GAAA,CAAI,aAAa,GAAA,CAAI,MAAA;AACrB,IAAA,MAAM,GAAA;AAAA,EACR;AAEA,EAAA,OAAO,IAAI,IAAA,EAAK;AAClB;AAIO,SAAS,eAAe,MAAA,EAAwB;AACrD,EAAA,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,IAKL,KAAK,MAAA,EAIuB;AAC1B,MAAA,OAAO,OAAA,CAAwB,MAAA,EAAQ,MAAA,EAAQ,cAAA,EAAgB,MAAM,CAAA;AAAA,IACvE,CAAA;AAAA;AAAA;AAAA;AAAA,IAKA,QAAQ,MAAA,EAIuB;AAC7B,MAAA,OAAO,OAAA,CAA2B,MAAA,EAAQ,MAAA,EAAQ,iBAAA,EAAmB,MAAM,CAAA;AAAA,IAC7E,CAAA;AAAA;AAAA;AAAA;AAAA,IAKA,QAAQ,MAAA,EAAwD;AAC9D,MAAA,OAAO,OAAA,CAA2B,MAAA,EAAQ,MAAA,EAAQ,iBAAA,EAAmB,MAAM,CAAA;AAAA,IAC7E,CAAA;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,MAAA,EAA2D;AAC/D,MAAA,OAAO,OAAA,CAAyB,MAAA,EAAQ,MAAA,EAAQ,eAAA,EAAiB,MAAM,CAAA;AAAA,IACzE,CAAA;AAAA;AAAA;AAAA;AAAA,IAKA,QAAA,GAAqC;AACnC,MAAA,OAAO,OAAA,CAAyB,MAAA,EAAQ,KAAA,EAAO,eAAe,CAAA;AAAA,IAChE,CAAA;AAAA;AAAA;AAAA,IAKA,UAAU,KAAA,EAA8D;AACtE,MAAA,OAAO,OAAA,CAA6B,MAAA,EAAQ,KAAA,EAAO,oBAAA,EAAsB,QAAW,KAAK,CAAA;AAAA,IAC3F,CAAA;AAAA;AAAA,IAGA,WAAW,KAAA,EAA+D;AACxE,MAAA,OAAO,OAAA,CAA8B,MAAA,EAAQ,KAAA,EAAO,qBAAA,EAAuB,QAAW,KAAK,CAAA;AAAA,IAC7F,CAAA;AAAA;AAAA,IAGA,WAAW,KAAA,EAA+D;AACxE,MAAA,OAAO,OAAA,CAA8B,MAAA,EAAQ,KAAA,EAAO,qBAAA,EAAuB,QAAW,KAAK,CAAA;AAAA,IAC7F;AAAA,GACF;AACF;ACxLA,SAAS,UAAU,cAAA,EAAqD;AACtE,EAAA,OAAa,cAAQ,MAAM;AAEzB,IAAA,IAAI,OAAQ,cAAA,CAA4B,IAAA,KAAS,UAAA,EAAY;AAC3D,MAAA,OAAO,cAAA;AAAA,IACT;AACA,IAAA,OAAO,eAAe,cAAgC,CAAA;AAAA,EAExD,CAAA,EAAG,EAAE,CAAA;AACP;AAEA,SAAS,MAAA,GAAS;AAChB,EAAA,OAAO,KAAK,MAAA,EAAO,CAAE,SAAS,EAAE,CAAA,CAAE,MAAM,CAAC,CAAA;AAC3C;AA0BO,SAAS,SAAA,CAAU;AAAA,EACxB,MAAA,EAAQ,cAAA;AAAA,EACR,WAAA;AAAA,EACA;AACF,CAAA,EAAsC;AACpC,EAAA,MAAM,MAAA,GAAS,UAAU,cAAc,CAAA;AACvC,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAU,KAAA,CAAA,QAAA,CAA0B,EAAE,CAAA;AAClE,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAU,eAAS,KAAK,CAAA;AAClD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAU,eAAwB,IAAI,CAAA;AAE5D,EAAA,MAAM,IAAA,GAAa,KAAA,CAAA,WAAA;AAAA,IACjB,OAAO,OAAA,KAAoB;AAEzB,MAAA,MAAM,OAAA,GAAyB;AAAA,QAC7B,IAAI,MAAA,EAAO;AAAA,QACX,IAAA,EAAM,MAAA;AAAA,QACN,OAAA,EAAS,OAAA;AAAA,QACT,SAAA,sBAAe,IAAA;AAAK,OACtB;AACA,MAAA,MAAM,gBAAgB,MAAA,EAAO;AAC7B,MAAA,MAAM,WAAA,GAA6B;AAAA,QACjC,EAAA,EAAI,aAAA;AAAA,QACJ,IAAA,EAAM,WAAA;AAAA,QACN,OAAA,EAAS,EAAA;AAAA,QACT,SAAA,sBAAe,IAAA,EAAK;AAAA,QACpB,OAAA,EAAS;AAAA,OACX;AACA,MAAA,WAAA,CAAY,CAAC,IAAA,KAAS,CAAC,GAAG,IAAA,EAAM,OAAA,EAAS,WAAW,CAAC,CAAA;AACrD,MAAA,UAAA,CAAW,IAAI,CAAA;AACf,MAAA,QAAA,CAAS,IAAI,CAAA;AAEb,MAAA,IAAI;AACF,QAAA,MAAM,GAAA,GAAM,MAAM,MAAA,CAAO,IAAA,CAAK,EAAE,OAAA,EAAS,WAAA,EAAa,WAAW,CAAA;AACjE,QAAA,MAAM,YAAA,GAA8B;AAAA,UAClC,IAAI,MAAA,EAAO;AAAA,UACX,IAAA,EAAM,WAAA;AAAA,UACN,SAAS,GAAA,CAAI,OAAA;AAAA,UACb,SAAA,sBAAe,IAAA,EAAK;AAAA,UACpB,SAAS,GAAA,CAAI;AAAA,SACf;AAEA,QAAA,WAAA,CAAY,CAAC,IAAA,KAAS,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,KAAO,CAAA,CAAE,EAAA,KAAO,aAAA,GAAgB,YAAA,GAAe,CAAE,CAAC,CAAA;AAAA,MACpF,SAAS,GAAA,EAAc;AACrB,QAAA,MAAM,MAAA,GAAS,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,mCAAA;AACpD,QAAA,QAAA,CAAS,MAAM,CAAA;AACf,QAAA,MAAM,YAAA,GAA8B;AAAA,UAClC,IAAI,MAAA,EAAO;AAAA,UACX,IAAA,EAAM,WAAA;AAAA,UACN,OAAA,EAAS,gBAAM,MAAM,CAAA,CAAA;AAAA,UACrB,SAAA,sBAAe,IAAA;AAAK,SACtB;AACA,QAAA,WAAA,CAAY,CAAC,IAAA,KAAS,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,KAAO,CAAA,CAAE,EAAA,KAAO,aAAA,GAAgB,YAAA,GAAe,CAAE,CAAC,CAAA;AAAA,MACpF,CAAA,SAAE;AACA,QAAA,UAAA,CAAW,KAAK,CAAA;AAAA,MAClB;AAAA,IACF,CAAA;AAAA,IACA,CAAC,MAAA,EAAQ,WAAA,EAAa,SAAS;AAAA,GACjC;AAEA,EAAA,MAAM,KAAA,GAAc,kBAAY,YAAY;AAC1C,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,CAAO,KAAA,CAAM,EAAE,WAAA,EAAa,CAAA;AAClC,MAAA,WAAA,CAAY,EAAE,CAAA;AACd,MAAA,QAAA,CAAS,IAAI,CAAA;AAAA,IACf,SAAS,GAAA,EAAc;AACrB,MAAA,MAAM,GAAA,GAAM,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,4BAAA;AACjD,MAAA,QAAA,CAAS,GAAG,CAAA;AAAA,IACd;AAAA,EACF,CAAA,EAAG,CAAC,MAAA,EAAQ,WAAW,CAAC,CAAA;AAExB,EAAA,MAAM,aAAA,GAAsB,kBAAY,MAAM,WAAA,CAAY,EAAE,CAAA,EAAG,EAAE,CAAA;AAEjE,EAAA,OAAO,EAAE,QAAA,EAAU,OAAA,EAAS,KAAA,EAAO,IAAA,EAAM,OAAO,aAAA,EAAc;AAChE;AA0BO,SAAS,YAAA,CAAa;AAAA,EAC3B,MAAA,EAAQ,cAAA;AAAA,EACR,WAAA;AAAA,EACA;AACF,CAAA,EAA4C;AAC1C,EAAA,MAAM,MAAA,GAAS,UAAU,cAAc,CAAA;AACvC,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAU,eAAS,KAAK,CAAA;AAClD,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAU,eAAwB,IAAI,CAAA;AAClE,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAU,KAAA,CAAA,QAAA,CAAqB,EAAE,CAAA;AAC3D,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAU,eAAwB,IAAI,CAAA;AAE5D,EAAA,MAAM,UAAA,GAAmB,KAAA,CAAA,WAAA;AAAA,IACvB,OAAO,MAAA,KAAmB;AACxB,MAAA,UAAA,CAAW,IAAI,CAAA;AACf,MAAA,QAAA,CAAS,IAAI,CAAA;AACb,MAAA,WAAA,CAAY,IAAI,CAAA;AAChB,MAAA,UAAA,CAAW,EAAE,CAAA;AACb,MAAA,IAAI;AACF,QAAA,MAAM,GAAA,GAAM,MAAM,MAAA,CAAO,OAAA,CAAQ,EAAE,MAAA,EAAQ,WAAA,EAAa,WAAW,CAAA;AACnE,QAAA,WAAA,CAAY,IAAI,OAAO,CAAA;AACvB,QAAA,UAAA,CAAW,GAAA,CAAI,OAAA,IAAW,EAAE,CAAA;AAAA,MAC9B,SAAS,GAAA,EAAc;AACrB,QAAA,MAAM,GAAA,GAAM,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,0BAAA;AACjD,QAAA,QAAA,CAAS,GAAG,CAAA;AACZ,QAAA,WAAA,CAAY,CAAA,aAAA,EAAM,GAAG,CAAA,CAAE,CAAA;AAAA,MACzB,CAAA,SAAE;AACA,QAAA,UAAA,CAAW,KAAK,CAAA;AAAA,MAClB;AAAA,IACF,CAAA;AAAA,IACA,CAAC,MAAA,EAAQ,WAAA,EAAa,SAAS;AAAA,GACjC;AAEA,EAAA,MAAM,KAAA,GAAc,kBAAY,MAAM;AACpC,IAAA,WAAA,CAAY,IAAI,CAAA;AAChB,IAAA,UAAA,CAAW,EAAE,CAAA;AACb,IAAA,QAAA,CAAS,IAAI,CAAA;AAAA,EACf,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,OAAO,EAAE,OAAA,EAAS,QAAA,EAAU,OAAA,EAAS,KAAA,EAAO,YAAY,KAAA,EAAM;AAChE;AAgCO,SAAS,YAAA,CAAa;AAAA,EAC3B,MAAA,EAAQ,cAAA;AAAA,EACR,UAAA,GAAa;AACf,CAAA,EAA4C;AAC1C,EAAA,MAAM,MAAA,GAAS,UAAU,cAAc,CAAA;AACvC,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAU,eAAS,KAAK,CAAA;AAClD,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAU,eAAwB,IAAI,CAAA;AACtE,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAU,eAAwB,IAAI,CAAA;AAC5D,EAAA,MAAM,QAAA,GAAiB,aAA6C,IAAI,CAAA;AAExE,EAAA,MAAM,aAAA,GAAsB,KAAA,CAAA,WAAA;AAAA,IAC1B,CAAC,MAAA,KAAmB;AAClB,MAAA,IAAI,QAAA,CAAS,OAAA,EAAS,YAAA,CAAa,QAAA,CAAS,OAAO,CAAA;AACnD,MAAA,IAAI,CAAC,MAAA,CAAO,IAAA,EAAK,EAAG;AAClB,QAAA,aAAA,CAAc,IAAI,CAAA;AAClB,QAAA;AAAA,MACF;AACA,MAAA,QAAA,CAAS,OAAA,GAAU,WAAW,YAAY;AACxC,QAAA,UAAA,CAAW,IAAI,CAAA;AACf,QAAA,QAAA,CAAS,IAAI,CAAA;AACb,QAAA,IAAI;AACF,UAAA,MAAM,MAAM,MAAM,MAAA,CAAO,OAAA,CAAQ,EAAE,QAAQ,CAAA;AAC3C,UAAA,aAAA,CAAc,IAAI,UAAU,CAAA;AAAA,QAC9B,SAAS,GAAA,EAAc;AACrB,UAAA,MAAM,GAAA,GAAM,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,2BAAA;AACjD,UAAA,QAAA,CAAS,GAAG,CAAA;AAAA,QACd,CAAA,SAAE;AACA,UAAA,UAAA,CAAW,KAAK,CAAA;AAAA,QAClB;AAAA,MACF,GAAG,UAAU,CAAA;AAAA,IACf,CAAA;AAAA,IACA,CAAC,QAAQ,UAAU;AAAA,GACrB;AAEA,EAAA,MAAM,MAAA,GAAe,kBAAY,MAAM;AACrC,IAAA,MAAM,GAAA,GAAM,UAAA;AACZ,IAAA,aAAA,CAAc,IAAI,CAAA;AAClB,IAAA,OAAO,GAAA;AAAA,EACT,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAEf,EAAA,MAAM,OAAA,GAAgB,kBAAY,MAAM;AACtC,IAAA,aAAA,CAAc,IAAI,CAAA;AAAA,EACpB,CAAA,EAAG,EAAE,CAAA;AAGL,EAAM,gBAAU,MAAM;AACpB,IAAA,OAAO,MAAM;AAAE,MAAA,IAAI,QAAA,CAAS,OAAA,EAAS,YAAA,CAAa,QAAA,CAAS,OAAO,CAAA;AAAA,IAAG,CAAA;AAAA,EACvE,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,OAAO,EAAE,OAAA,EAAS,UAAA,EAAY,KAAA,EAAO,aAAA,EAAe,QAAQ,OAAA,EAAQ;AACtE;AAkBO,SAAS,UAAA,CAAW,EAAE,MAAA,EAAQ,cAAA,EAAe,EAA4D;AAC9G,EAAA,MAAM,MAAA,GAAS,UAAU,cAAc,CAAA;AACvC,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAU,eAAiC,IAAI,CAAA;AACrE,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAU,eAAS,KAAK,CAAA;AAClD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAU,eAAwB,IAAI,CAAA;AAE5D,EAAA,MAAM,OAAA,GAAgB,kBAAY,YAAY;AAC5C,IAAA,UAAA,CAAW,IAAI,CAAA;AACf,IAAA,QAAA,CAAS,IAAI,CAAA;AACb,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,GAAO,MAAM,MAAA,CAAO,QAAA,EAAS;AACnC,MAAA,QAAA,CAAS,IAAI,CAAA;AAAA,IACf,SAAS,GAAA,EAAc;AACrB,MAAA,QAAA,CAAS,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,mBAAmB,CAAA;AAAA,IACnE,CAAA,SAAE;AACA,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IAClB;AAAA,EACF,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAEX,EAAM,gBAAU,MAAM;AACpB,IAAA,OAAA,EAAQ;AAAA,EACV,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,OAAO,EAAE,KAAA,EAAO,OAAA,EAAS,KAAA,EAAO,OAAA,EAAQ;AAC1C","file":"chunk-P2ORBJBL.js","sourcesContent":["// ─────────────────────────────────────────────────────────────────────────────\n// organify-ui — AI Client\n// Typed, fetch-based client for the Organify AI Service (via API Gateway).\n//\n// Usage:\n// import { createAiClient } from 'organify-ui/ai';\n// const ai = createAiClient({ baseUrl: process.env.NEXT_PUBLIC_GATEWAY_URL! });\n// const result = await ai.chat({ message: 'Cria uma tarefa…', workspaceId: 'ws_1' });\n// ─────────────────────────────────────────────────────────────────────────────\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\nexport interface AiAction {\n type: string;\n label: string;\n description?: string;\n applied?: boolean;\n}\n\nexport interface AiChatResponse {\n message: string;\n provider: string;\n tokensUsed: number;\n actions: AiAction[];\n}\n\nexport interface AiCommandResponse {\n message: string;\n provider: string;\n tokensUsed: number;\n actions: AiAction[];\n}\n\nexport interface AiSuggestResponse {\n suggestion: string;\n}\n\nexport interface AiResetResponse {\n message: string;\n}\n\nexport interface AiUsageResponse {\n used: number;\n limit: number;\n remaining: number;\n}\n\nexport interface AiAdminLogsResponse {\n logs: AiAdminLog[];\n total: number;\n}\n\nexport interface AiAdminLog {\n userId: string;\n action: string;\n tokensUsed: number;\n timestamp: string;\n workspaceId?: string;\n projectId?: string;\n}\n\nexport interface AiAdminStatsResponse {\n totalRequests: number;\n totalTokens: number;\n uniqueUsers: number;\n [key: string]: unknown;\n}\n\nexport interface AiAdminUsersResponse {\n users: AiAdminUserStat[];\n}\n\nexport interface AiAdminUserStat {\n userId: string;\n requests: number;\n tokens: number;\n}\n\n// ── Config ───────────────────────────────────────────────────────────────────\n\nexport interface AiClientConfig {\n /**\n * Base URL of the API gateway (e.g. \"https://api.organify.app\" or\n * \"http://localhost:4000\"). Must NOT have a trailing slash.\n */\n baseUrl: string;\n /**\n * Optional – if provided, sends as `Authorization: Bearer <token>`.\n * The gateway will forward it and inject user-identity headers before\n * reaching the AI service.\n */\n getToken?: () => string | null | undefined;\n}\n\n// ── Internal helpers ─────────────────────────────────────────────────────────\n\nasync function request<T>(\n config: AiClientConfig,\n method: 'GET' | 'POST',\n path: string,\n body?: Record<string, unknown>,\n query?: Record<string, string>,\n): Promise<T> {\n const token = config.getToken?.();\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n };\n if (token) headers['Authorization'] = `Bearer ${token}`;\n\n let url = `${config.baseUrl}${path}`;\n if (query && Object.keys(query).length) {\n url += '?' + new URLSearchParams(query).toString();\n }\n\n const res = await fetch(url, {\n method,\n headers,\n credentials: 'include',\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!res.ok) {\n let errorBody: { message?: string } = {};\n try {\n errorBody = await res.json();\n } catch { /* ignore */ }\n const msg = errorBody.message ?? res.statusText ?? 'Request failed';\n const err = new Error(msg) as Error & { statusCode: number };\n err.statusCode = res.status;\n throw err;\n }\n\n return res.json() as Promise<T>;\n}\n\n// ── Client factory ───────────────────────────────────────────────────────────\n\nexport function createAiClient(config: AiClientConfig) {\n return {\n /**\n * Multi-turn conversational chat.\n * The gateway injects `x-user-id` / `x-user-plan` from the JWT.\n */\n chat(params: {\n message: string;\n workspaceId: string;\n projectId?: string;\n }): Promise<AiChatResponse> {\n return request<AiChatResponse>(config, 'POST', '/api/ai/chat', params);\n },\n\n /**\n * Single-shot command (⌘K palette).\n */\n command(params: {\n prompt: string;\n workspaceId: string;\n projectId?: string;\n }): Promise<AiCommandResponse> {\n return request<AiCommandResponse>(config, 'POST', '/api/ai/command', params);\n },\n\n /**\n * Lightweight inline suggestion (no rate-limit increment).\n */\n suggest(params: { prompt: string }): Promise<AiSuggestResponse> {\n return request<AiSuggestResponse>(config, 'POST', '/api/ai/suggest', params);\n },\n\n /**\n * Reset conversation context for a workspace.\n */\n reset(params: { workspaceId: string }): Promise<AiResetResponse> {\n return request<AiResetResponse>(config, 'POST', '/api/ai/reset', params);\n },\n\n /**\n * Get per-user usage stats (requests used today, daily limit, remaining).\n */\n getUsage(): Promise<AiUsageResponse> {\n return request<AiUsageResponse>(config, 'GET', '/api/ai/usage');\n },\n\n // ── Admin ──────────────────────────────────────────────────────────────\n\n /** [Admin] Get request logs. Requires `x-user-role: admin` in JWT. */\n adminLogs(query?: Record<string, string>): Promise<AiAdminLogsResponse> {\n return request<AiAdminLogsResponse>(config, 'GET', '/api/ai/admin/logs', undefined, query);\n },\n\n /** [Admin] Get aggregate stats. Requires `x-user-role: admin` in JWT. */\n adminStats(query?: Record<string, string>): Promise<AiAdminStatsResponse> {\n return request<AiAdminStatsResponse>(config, 'GET', '/api/ai/admin/stats', undefined, query);\n },\n\n /** [Admin] Get per-user usage stats. Requires `x-user-role: admin` in JWT. */\n adminUsers(query?: Record<string, string>): Promise<AiAdminUsersResponse> {\n return request<AiAdminUsersResponse>(config, 'GET', '/api/ai/admin/users', undefined, query);\n },\n };\n}\n\nexport type AiClient = ReturnType<typeof createAiClient>;\n","'use client';\n// ─────────────────────────────────────────────────────────────────────────────\n// organify-ui — AI Hooks\n// React hooks that wrap `AiClient` with local state for easy use with the\n// `AiChatSidebar`, `CommandBar`, and `InlineAiButton` components.\n//\n// All hooks accept an `AiClient` instance (or `AiClientConfig` shorthand so\n// the client is created lazily).\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport * as React from 'react';\nimport { createAiClient } from './client';\nimport type { AiClient, AiClientConfig, AiAction, AiUsageResponse } from './client';\nimport type { AiChatMessage } from '../components/chat/ai-chat-sidebar';\n\n// ── Shared helpers ────────────────────────────────────────────────────────────\n\nfunction useClient(clientOrConfig: AiClient | AiClientConfig): AiClient {\n return React.useMemo(() => {\n // Duck-type: if it has a `chat` function it's already a client\n if (typeof (clientOrConfig as AiClient).chat === 'function') {\n return clientOrConfig as AiClient;\n }\n return createAiClient(clientOrConfig as AiClientConfig);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []); // intentionally stable — client identity doesn't change\n}\n\nfunction makeId() {\n return Math.random().toString(36).slice(2);\n}\n\n// ── useAiChat ────────────────────────────────────────────────────────────────\n\nexport interface UseAiChatOptions {\n client: AiClient | AiClientConfig;\n workspaceId: string;\n projectId?: string;\n}\n\nexport interface UseAiChatResult {\n messages: AiChatMessage[];\n loading: boolean;\n error: string | null;\n send: (message: string) => Promise<void>;\n reset: () => Promise<void>;\n clearMessages: () => void;\n}\n\n/**\n * Manages conversation state for `<AiChatSidebar>`.\n *\n * @example\n * const chat = useAiChat({ client: ai, workspaceId: 'ws_1' });\n * <AiChatSidebar messages={chat.messages} onSend={chat.send} loading={chat.loading} ... />\n */\nexport function useAiChat({\n client: clientOrConfig,\n workspaceId,\n projectId,\n}: UseAiChatOptions): UseAiChatResult {\n const client = useClient(clientOrConfig);\n const [messages, setMessages] = React.useState<AiChatMessage[]>([]);\n const [loading, setLoading] = React.useState(false);\n const [error, setError] = React.useState<string | null>(null);\n\n const send = React.useCallback(\n async (message: string) => {\n // Optimistically append user message\n const userMsg: AiChatMessage = {\n id: makeId(),\n role: 'user',\n content: message,\n timestamp: new Date(),\n };\n const placeholderId = makeId();\n const placeholder: AiChatMessage = {\n id: placeholderId,\n role: 'assistant',\n content: '',\n timestamp: new Date(),\n loading: true,\n };\n setMessages((prev) => [...prev, userMsg, placeholder]);\n setLoading(true);\n setError(null);\n\n try {\n const res = await client.chat({ message, workspaceId, projectId });\n const assistantMsg: AiChatMessage = {\n id: makeId(),\n role: 'assistant',\n content: res.message,\n timestamp: new Date(),\n actions: res.actions as AiAction[],\n };\n // Replace loading placeholder\n setMessages((prev) => prev.map((m) => (m.id === placeholderId ? assistantMsg : m)));\n } catch (err: unknown) {\n const errMsg = err instanceof Error ? err.message : 'Erro ao contactar o assistente IA';\n setError(errMsg);\n const errAssistant: AiChatMessage = {\n id: makeId(),\n role: 'assistant',\n content: `⚠️ ${errMsg}`,\n timestamp: new Date(),\n };\n setMessages((prev) => prev.map((m) => (m.id === placeholderId ? errAssistant : m)));\n } finally {\n setLoading(false);\n }\n },\n [client, workspaceId, projectId],\n );\n\n const reset = React.useCallback(async () => {\n try {\n await client.reset({ workspaceId });\n setMessages([]);\n setError(null);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : 'Erro ao reiniciar contexto';\n setError(msg);\n }\n }, [client, workspaceId]);\n\n const clearMessages = React.useCallback(() => setMessages([]), []);\n\n return { messages, loading, error, send, reset, clearMessages };\n}\n\n// ── useAiCommand ─────────────────────────────────────────────────────────────\n\nexport interface UseAiCommandOptions {\n client: AiClient | AiClientConfig;\n workspaceId: string;\n projectId?: string;\n}\n\nexport interface UseAiCommandResult {\n loading: boolean;\n response: string | null;\n actions: AiAction[];\n error: string | null;\n runCommand: (prompt: string) => Promise<void>;\n clear: () => void;\n}\n\n/**\n * Handles a single ⌘K command prompt for `<CommandBar>`.\n *\n * @example\n * const cmd = useAiCommand({ client: ai, workspaceId: 'ws_1' });\n * <CommandBar onAiPrompt={cmd.runCommand} aiLoading={cmd.loading} aiResponse={cmd.response ?? undefined} ... />\n */\nexport function useAiCommand({\n client: clientOrConfig,\n workspaceId,\n projectId,\n}: UseAiCommandOptions): UseAiCommandResult {\n const client = useClient(clientOrConfig);\n const [loading, setLoading] = React.useState(false);\n const [response, setResponse] = React.useState<string | null>(null);\n const [actions, setActions] = React.useState<AiAction[]>([]);\n const [error, setError] = React.useState<string | null>(null);\n\n const runCommand = React.useCallback(\n async (prompt: string) => {\n setLoading(true);\n setError(null);\n setResponse(null);\n setActions([]);\n try {\n const res = await client.command({ prompt, workspaceId, projectId });\n setResponse(res.message);\n setActions(res.actions ?? []);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : 'Erro ao executar comando';\n setError(msg);\n setResponse(`⚠️ ${msg}`);\n } finally {\n setLoading(false);\n }\n },\n [client, workspaceId, projectId],\n );\n\n const clear = React.useCallback(() => {\n setResponse(null);\n setActions([]);\n setError(null);\n }, []);\n\n return { loading, response, actions, error, runCommand, clear };\n}\n\n// ── useAiSuggest ─────────────────────────────────────────────────────────────\n\nexport interface UseAiSuggestOptions {\n client: AiClient | AiClientConfig;\n /** Debounce delay in ms before firing the API call (default: 400) */\n debounceMs?: number;\n}\n\nexport interface UseAiSuggestResult {\n loading: boolean;\n suggestion: string | null;\n error: string | null;\n getSuggestion: (prompt: string) => void;\n accept: () => string | null;\n dismiss: () => void;\n}\n\n/**\n * Provides debounced inline AI suggestions for `<InlineAiButton>`.\n *\n * @example\n * const inline = useAiSuggest({ client: ai });\n * <InlineAiButton\n * onTrigger={(text) => inline.getSuggestion(text ?? '')}\n * loading={inline.loading}\n * suggestion={inline.suggestion ?? undefined}\n * onAccept={inline.accept}\n * onDismiss={inline.dismiss}\n * />\n */\nexport function useAiSuggest({\n client: clientOrConfig,\n debounceMs = 400,\n}: UseAiSuggestOptions): UseAiSuggestResult {\n const client = useClient(clientOrConfig);\n const [loading, setLoading] = React.useState(false);\n const [suggestion, setSuggestion] = React.useState<string | null>(null);\n const [error, setError] = React.useState<string | null>(null);\n const timerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const getSuggestion = React.useCallback(\n (prompt: string) => {\n if (timerRef.current) clearTimeout(timerRef.current);\n if (!prompt.trim()) {\n setSuggestion(null);\n return;\n }\n timerRef.current = setTimeout(async () => {\n setLoading(true);\n setError(null);\n try {\n const res = await client.suggest({ prompt });\n setSuggestion(res.suggestion);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : 'Erro ao obter sugestão';\n setError(msg);\n } finally {\n setLoading(false);\n }\n }, debounceMs);\n },\n [client, debounceMs],\n );\n\n const accept = React.useCallback(() => {\n const val = suggestion;\n setSuggestion(null);\n return val;\n }, [suggestion]);\n\n const dismiss = React.useCallback(() => {\n setSuggestion(null);\n }, []);\n\n // Clean up timer on unmount\n React.useEffect(() => {\n return () => { if (timerRef.current) clearTimeout(timerRef.current); };\n }, []);\n\n return { loading, suggestion, error, getSuggestion, accept, dismiss };\n}\n\n// ── useAiUsage ───────────────────────────────────────────────────────────────\n\nexport interface UseAiUsageResult {\n usage: AiUsageResponse | null;\n loading: boolean;\n error: string | null;\n refresh: () => Promise<void>;\n}\n\n/**\n * Fetches and caches the current user's AI usage stats.\n *\n * @example\n * const { usage } = useAiUsage({ client: ai });\n * <p>{usage?.remaining} requests left today</p>\n */\nexport function useAiUsage({ client: clientOrConfig }: { client: AiClient | AiClientConfig }): UseAiUsageResult {\n const client = useClient(clientOrConfig);\n const [usage, setUsage] = React.useState<AiUsageResponse | null>(null);\n const [loading, setLoading] = React.useState(false);\n const [error, setError] = React.useState<string | null>(null);\n\n const refresh = React.useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const data = await client.getUsage();\n setUsage(data);\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : 'Erro ao obter uso');\n } finally {\n setLoading(false);\n }\n }, [client]);\n\n React.useEffect(() => {\n refresh();\n }, [refresh]);\n\n return { usage, loading, error, refresh };\n}\n"]}