@lastbrain/ai-ui-react 1.0.3
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/README.md +84 -0
- package/dist/components/AiChipLabel.d.ts +8 -0
- package/dist/components/AiChipLabel.d.ts.map +1 -0
- package/dist/components/AiChipLabel.js +39 -0
- package/dist/components/AiImageButton.d.ts +8 -0
- package/dist/components/AiImageButton.d.ts.map +1 -0
- package/dist/components/AiImageButton.js +36 -0
- package/dist/components/AiInput.d.ts +7 -0
- package/dist/components/AiInput.d.ts.map +1 -0
- package/dist/components/AiInput.js +77 -0
- package/dist/components/AiModelSelect.d.ts +10 -0
- package/dist/components/AiModelSelect.d.ts.map +1 -0
- package/dist/components/AiModelSelect.js +5 -0
- package/dist/components/AiPromptPanel.d.ts +22 -0
- package/dist/components/AiPromptPanel.d.ts.map +1 -0
- package/dist/components/AiPromptPanel.js +30 -0
- package/dist/components/AiSelect.d.ts +8 -0
- package/dist/components/AiSelect.d.ts.map +1 -0
- package/dist/components/AiSelect.js +38 -0
- package/dist/components/AiSettingsButton.d.ts +11 -0
- package/dist/components/AiSettingsButton.d.ts.map +1 -0
- package/dist/components/AiSettingsButton.js +16 -0
- package/dist/components/AiTextarea.d.ts +7 -0
- package/dist/components/AiTextarea.d.ts.map +1 -0
- package/dist/components/AiTextarea.js +79 -0
- package/dist/context/AiProvider.d.ts +18 -0
- package/dist/context/AiProvider.d.ts.map +1 -0
- package/dist/context/AiProvider.js +20 -0
- package/dist/hooks/useAiCallImage.d.ts +12 -0
- package/dist/hooks/useAiCallImage.d.ts.map +1 -0
- package/dist/hooks/useAiCallImage.js +29 -0
- package/dist/hooks/useAiCallText.d.ts +12 -0
- package/dist/hooks/useAiCallText.d.ts.map +1 -0
- package/dist/hooks/useAiCallText.js +29 -0
- package/dist/hooks/useAiClient.d.ts +12 -0
- package/dist/hooks/useAiClient.d.ts.map +1 -0
- package/dist/hooks/useAiClient.js +13 -0
- package/dist/hooks/useAiModels.d.ts +13 -0
- package/dist/hooks/useAiModels.d.ts.map +1 -0
- package/dist/hooks/useAiModels.js +32 -0
- package/dist/hooks/useAiStatus.d.ts +13 -0
- package/dist/hooks/useAiStatus.d.ts.map +1 -0
- package/dist/hooks/useAiStatus.js +32 -0
- package/dist/hooks/usePrompts.d.ts +35 -0
- package/dist/hooks/usePrompts.d.ts.map +1 -0
- package/dist/hooks/usePrompts.js +125 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/types.d.ts +20 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +60 -0
- package/src/components/AiChipLabel.tsx +88 -0
- package/src/components/AiImageButton.tsx +87 -0
- package/src/components/AiInput.tsx +150 -0
- package/src/components/AiModelSelect.tsx +37 -0
- package/src/components/AiPromptPanel.tsx +120 -0
- package/src/components/AiSelect.tsx +91 -0
- package/src/components/AiSettingsButton.tsx +111 -0
- package/src/components/AiTextarea.tsx +152 -0
- package/src/context/AiProvider.tsx +44 -0
- package/src/hooks/useAiCallImage.ts +49 -0
- package/src/hooks/useAiCallText.ts +49 -0
- package/src/hooks/useAiClient.ts +23 -0
- package/src/hooks/useAiModels.ts +50 -0
- package/src/hooks/useAiStatus.ts +50 -0
- package/src/hooks/usePrompts.ts +187 -0
- package/src/index.ts +23 -0
- package/src/types.ts +20 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AiTextRequest, AiTextResponse } from "@lastbrain/ai-ui-core";
|
|
2
|
+
export interface UseAiCallTextOptions {
|
|
3
|
+
baseUrl?: string;
|
|
4
|
+
apiKeyId?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface UseAiCallTextResult {
|
|
7
|
+
generateText: (request: AiTextRequest) => Promise<AiTextResponse>;
|
|
8
|
+
loading: boolean;
|
|
9
|
+
error: Error | null;
|
|
10
|
+
}
|
|
11
|
+
export declare function useAiCallText(options?: UseAiCallTextOptions): UseAiCallTextResult;
|
|
12
|
+
//# sourceMappingURL=useAiCallText.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAiCallText.d.ts","sourceRoot":"","sources":["../../src/hooks/useAiCallText.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG3E,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED,wBAAgB,aAAa,CAC3B,OAAO,CAAC,EAAE,oBAAoB,GAC7B,mBAAmB,CA6BrB"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState, useCallback } from "react";
|
|
3
|
+
import { useAiClient } from "./useAiClient";
|
|
4
|
+
export function useAiCallText(options) {
|
|
5
|
+
const client = useAiClient(options);
|
|
6
|
+
const [loading, setLoading] = useState(false);
|
|
7
|
+
const [error, setError] = useState(null);
|
|
8
|
+
const generateText = useCallback(async (request) => {
|
|
9
|
+
setLoading(true);
|
|
10
|
+
setError(null);
|
|
11
|
+
try {
|
|
12
|
+
const result = await client.generateText(request);
|
|
13
|
+
return result;
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
const error = err instanceof Error ? err : new Error("Failed to generate text");
|
|
17
|
+
setError(error);
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
finally {
|
|
21
|
+
setLoading(false);
|
|
22
|
+
}
|
|
23
|
+
}, [client]);
|
|
24
|
+
return {
|
|
25
|
+
generateText,
|
|
26
|
+
loading,
|
|
27
|
+
error,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface UseAiClientOptions {
|
|
2
|
+
baseUrl?: string;
|
|
3
|
+
apiKeyId?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function useAiClient(options?: UseAiClientOptions): {
|
|
6
|
+
getModels: () => Promise<import("@lastbrain/ai-ui-core").ModelRef[]>;
|
|
7
|
+
generateText: (req: import("@lastbrain/ai-ui-core").AiTextRequest) => Promise<import("@lastbrain/ai-ui-core").AiTextResponse>;
|
|
8
|
+
generateImage: (req: import("@lastbrain/ai-ui-core").AiImageRequest) => Promise<import("@lastbrain/ai-ui-core").AiImageResponse>;
|
|
9
|
+
embed: (req: import("@lastbrain/ai-ui-core").AiEmbedRequest) => Promise<import("@lastbrain/ai-ui-core").AiEmbedResponse>;
|
|
10
|
+
getStatus: () => Promise<import("@lastbrain/ai-ui-core").AiStatus>;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=useAiClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAiClient.d.ts","sourceRoot":"","sources":["../../src/hooks/useAiClient.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB;;;;;;EAWvD"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { createClient } from "@lastbrain/ai-ui-core";
|
|
4
|
+
import { useAiContext } from "../context/AiProvider";
|
|
5
|
+
export function useAiClient(options) {
|
|
6
|
+
const context = useAiContext();
|
|
7
|
+
const client = useMemo(() => {
|
|
8
|
+
const baseUrl = options?.baseUrl || context.baseUrl;
|
|
9
|
+
const apiKeyId = options?.apiKeyId || context.apiKeyId;
|
|
10
|
+
return createClient({ baseUrl, apiKeyId });
|
|
11
|
+
}, [options?.baseUrl, options?.apiKeyId, context.baseUrl, context.apiKeyId]);
|
|
12
|
+
return client;
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ModelRef } from "@lastbrain/ai-ui-core";
|
|
2
|
+
export interface UseAiModelsOptions {
|
|
3
|
+
baseUrl?: string;
|
|
4
|
+
apiKeyId?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface UseAiModelsResult {
|
|
7
|
+
models: ModelRef[] | null;
|
|
8
|
+
loading: boolean;
|
|
9
|
+
error: Error | null;
|
|
10
|
+
refetch: () => void;
|
|
11
|
+
}
|
|
12
|
+
export declare function useAiModels(options?: UseAiModelsOptions): UseAiModelsResult;
|
|
13
|
+
//# sourceMappingURL=useAiModels.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAiModels.d.ts","sourceRoot":"","sources":["../../src/hooks/useAiModels.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGtD,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,iBAAiB,CA+B3E"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState, useEffect, useCallback } from "react";
|
|
3
|
+
import { useAiClient } from "./useAiClient";
|
|
4
|
+
export function useAiModels(options) {
|
|
5
|
+
const client = useAiClient(options);
|
|
6
|
+
const [models, setModels] = useState(null);
|
|
7
|
+
const [loading, setLoading] = useState(false);
|
|
8
|
+
const [error, setError] = useState(null);
|
|
9
|
+
const fetchModels = useCallback(async () => {
|
|
10
|
+
setLoading(true);
|
|
11
|
+
setError(null);
|
|
12
|
+
try {
|
|
13
|
+
const result = await client.getModels();
|
|
14
|
+
setModels(result);
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
setError(err instanceof Error ? err : new Error("Failed to fetch models"));
|
|
18
|
+
}
|
|
19
|
+
finally {
|
|
20
|
+
setLoading(false);
|
|
21
|
+
}
|
|
22
|
+
}, [client]);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
fetchModels();
|
|
25
|
+
}, [fetchModels]);
|
|
26
|
+
return {
|
|
27
|
+
models,
|
|
28
|
+
loading,
|
|
29
|
+
error,
|
|
30
|
+
refetch: fetchModels,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AiStatus } from "@lastbrain/ai-ui-core";
|
|
2
|
+
export interface UseAiStatusOptions {
|
|
3
|
+
baseUrl?: string;
|
|
4
|
+
apiKeyId?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface UseAiStatusResult {
|
|
7
|
+
status: AiStatus | null;
|
|
8
|
+
loading: boolean;
|
|
9
|
+
error: Error | null;
|
|
10
|
+
refetch: () => void;
|
|
11
|
+
}
|
|
12
|
+
export declare function useAiStatus(options?: UseAiStatusOptions): UseAiStatusResult;
|
|
13
|
+
//# sourceMappingURL=useAiStatus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAiStatus.d.ts","sourceRoot":"","sources":["../../src/hooks/useAiStatus.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGtD,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,iBAAiB,CA+B3E"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState, useEffect, useCallback } from "react";
|
|
3
|
+
import { useAiClient } from "./useAiClient";
|
|
4
|
+
export function useAiStatus(options) {
|
|
5
|
+
const client = useAiClient(options);
|
|
6
|
+
const [status, setStatus] = useState(null);
|
|
7
|
+
const [loading, setLoading] = useState(false);
|
|
8
|
+
const [error, setError] = useState(null);
|
|
9
|
+
const fetchStatus = useCallback(async () => {
|
|
10
|
+
setLoading(true);
|
|
11
|
+
setError(null);
|
|
12
|
+
try {
|
|
13
|
+
const result = await client.getStatus();
|
|
14
|
+
setStatus(result);
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
setError(err instanceof Error ? err : new Error("Failed to fetch status"));
|
|
18
|
+
}
|
|
19
|
+
finally {
|
|
20
|
+
setLoading(false);
|
|
21
|
+
}
|
|
22
|
+
}, [client]);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
fetchStatus();
|
|
25
|
+
}, [fetchStatus]);
|
|
26
|
+
return {
|
|
27
|
+
status,
|
|
28
|
+
loading,
|
|
29
|
+
error,
|
|
30
|
+
refetch: fetchStatus,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface Prompt {
|
|
2
|
+
id: string;
|
|
3
|
+
slug?: string;
|
|
4
|
+
title: string;
|
|
5
|
+
content: string;
|
|
6
|
+
type: "text" | "image";
|
|
7
|
+
is_public: boolean;
|
|
8
|
+
favorite: boolean;
|
|
9
|
+
tags: string[];
|
|
10
|
+
created_at: string;
|
|
11
|
+
updated_at?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface PublicPrompt extends Prompt {
|
|
14
|
+
slug: string;
|
|
15
|
+
views: number;
|
|
16
|
+
used_count: number;
|
|
17
|
+
picked_count: number;
|
|
18
|
+
}
|
|
19
|
+
export interface UsePromptsOptions {
|
|
20
|
+
type?: "text" | "image";
|
|
21
|
+
favorite?: boolean;
|
|
22
|
+
public?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface UsePromptsReturn {
|
|
25
|
+
prompts: Prompt[] | PublicPrompt[];
|
|
26
|
+
loading: boolean;
|
|
27
|
+
error: string | null;
|
|
28
|
+
fetchPrompts: (options?: UsePromptsOptions) => Promise<void>;
|
|
29
|
+
createPrompt: (data: Omit<Prompt, "id" | "created_at" | "updated_at">) => Promise<Prompt | null>;
|
|
30
|
+
updatePrompt: (id: string, data: Partial<Prompt>) => Promise<Prompt | null>;
|
|
31
|
+
deletePrompt: (id: string) => Promise<boolean>;
|
|
32
|
+
incrementStat: (promptId: string, statType: "views" | "used" | "picked") => Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
export declare function usePrompts(): UsePromptsReturn;
|
|
35
|
+
//# sourceMappingURL=usePrompts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePrompts.d.ts","sourceRoot":"","sources":["../../src/hooks/usePrompts.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAa,SAAQ,MAAM;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,EAAE,GAAG,YAAY,EAAE,CAAC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,YAAY,EAAE,CAAC,OAAO,CAAC,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,YAAY,EAAE,CACZ,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,GAAG,YAAY,GAAG,YAAY,CAAC,KACnD,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5B,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5E,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,aAAa,EAAE,CACb,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,KAClC,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB;AAED,wBAAgB,UAAU,IAAI,gBAAgB,CA2I7C"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState, useCallback } from "react";
|
|
3
|
+
import { useAiContext } from "../context/AiProvider";
|
|
4
|
+
export function usePrompts() {
|
|
5
|
+
const { apiKeyId } = useAiContext();
|
|
6
|
+
const [prompts, setPrompts] = useState([]);
|
|
7
|
+
const [loading, setLoading] = useState(false);
|
|
8
|
+
const [error, setError] = useState(null);
|
|
9
|
+
const fetchPrompts = useCallback(async (options) => {
|
|
10
|
+
try {
|
|
11
|
+
setLoading(true);
|
|
12
|
+
setError(null);
|
|
13
|
+
const params = new URLSearchParams();
|
|
14
|
+
if (options?.type)
|
|
15
|
+
params.append("type", options.type);
|
|
16
|
+
if (options?.favorite !== undefined)
|
|
17
|
+
params.append("favorite", String(options.favorite));
|
|
18
|
+
// Use public route without auth if public=true, otherwise use auth route
|
|
19
|
+
const endpoint = options?.public
|
|
20
|
+
? `/api/ai/public/prompts?${params}`
|
|
21
|
+
: `/api/ai/auth/prompts?${params}`;
|
|
22
|
+
const response = await fetch(endpoint);
|
|
23
|
+
const data = await response.json();
|
|
24
|
+
if (response.ok) {
|
|
25
|
+
setPrompts(data.prompts || []);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
setError(data.error || "Failed to fetch prompts");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
setError(err instanceof Error ? err.message : "Unknown error");
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
setLoading(false);
|
|
36
|
+
}
|
|
37
|
+
}, []);
|
|
38
|
+
const createPrompt = useCallback(async (data) => {
|
|
39
|
+
try {
|
|
40
|
+
setError(null);
|
|
41
|
+
const response = await fetch("/api/ai/auth/prompts", {
|
|
42
|
+
method: "POST",
|
|
43
|
+
headers: { "Content-Type": "application/json" },
|
|
44
|
+
body: JSON.stringify(data),
|
|
45
|
+
});
|
|
46
|
+
const result = await response.json();
|
|
47
|
+
if (response.ok) {
|
|
48
|
+
return result.prompt;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
setError(result.error || "Failed to create prompt");
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
setError(err instanceof Error ? err.message : "Unknown error");
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}, []);
|
|
60
|
+
const updatePrompt = useCallback(async (id, data) => {
|
|
61
|
+
try {
|
|
62
|
+
setError(null);
|
|
63
|
+
const response = await fetch("/api/ai/auth/prompts", {
|
|
64
|
+
method: "PUT",
|
|
65
|
+
headers: { "Content-Type": "application/json" },
|
|
66
|
+
body: JSON.stringify({ id, ...data }),
|
|
67
|
+
});
|
|
68
|
+
const result = await response.json();
|
|
69
|
+
if (response.ok) {
|
|
70
|
+
return result.prompt;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
setError(result.error || "Failed to update prompt");
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
setError(err instanceof Error ? err.message : "Unknown error");
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}, []);
|
|
82
|
+
const deletePrompt = useCallback(async (id) => {
|
|
83
|
+
try {
|
|
84
|
+
setError(null);
|
|
85
|
+
const response = await fetch(`/api/ai/auth/prompts?id=${id}`, {
|
|
86
|
+
method: "DELETE",
|
|
87
|
+
});
|
|
88
|
+
const result = await response.json();
|
|
89
|
+
if (response.ok) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
setError(result.error || "Failed to delete prompt");
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
setError(err instanceof Error ? err.message : "Unknown error");
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}, []);
|
|
102
|
+
const incrementStat = useCallback(async (promptId, statType) => {
|
|
103
|
+
try {
|
|
104
|
+
await fetch("/api/ai/auth/prompts/stats", {
|
|
105
|
+
method: "POST",
|
|
106
|
+
headers: { "Content-Type": "application/json" },
|
|
107
|
+
body: JSON.stringify({ prompt_id: promptId, stat_type: statType }),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
// Silent fail for stats
|
|
112
|
+
console.error("Failed to increment stat:", err);
|
|
113
|
+
}
|
|
114
|
+
}, []);
|
|
115
|
+
return {
|
|
116
|
+
prompts,
|
|
117
|
+
loading,
|
|
118
|
+
error,
|
|
119
|
+
fetchPrompts,
|
|
120
|
+
createPrompt,
|
|
121
|
+
updatePrompt,
|
|
122
|
+
deletePrompt,
|
|
123
|
+
incrementStat,
|
|
124
|
+
};
|
|
125
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export * from "./types";
|
|
2
|
+
export * from "./context/AiProvider";
|
|
3
|
+
export * from "./hooks/useAiClient";
|
|
4
|
+
export * from "./hooks/useAiModels";
|
|
5
|
+
export * from "./hooks/useAiStatus";
|
|
6
|
+
export * from "./hooks/useAiCallText";
|
|
7
|
+
export * from "./hooks/useAiCallImage";
|
|
8
|
+
export * from "./hooks/usePrompts";
|
|
9
|
+
export * from "./components/AiPromptPanel";
|
|
10
|
+
export * from "./components/AiModelSelect";
|
|
11
|
+
export * from "./components/AiInput";
|
|
12
|
+
export * from "./components/AiTextarea";
|
|
13
|
+
export * from "./components/AiSelect";
|
|
14
|
+
export * from "./components/AiChipLabel";
|
|
15
|
+
export * from "./components/AiImageButton";
|
|
16
|
+
export * from "./components/AiSettingsButton";
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;AAGxB,cAAc,sBAAsB,CAAC;AAGrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,+BAA+B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
export * from "./types";
|
|
3
|
+
// Context
|
|
4
|
+
export * from "./context/AiProvider";
|
|
5
|
+
// Hooks
|
|
6
|
+
export * from "./hooks/useAiClient";
|
|
7
|
+
export * from "./hooks/useAiModels";
|
|
8
|
+
export * from "./hooks/useAiStatus";
|
|
9
|
+
export * from "./hooks/useAiCallText";
|
|
10
|
+
export * from "./hooks/useAiCallImage";
|
|
11
|
+
export * from "./hooks/usePrompts";
|
|
12
|
+
// Components
|
|
13
|
+
export * from "./components/AiPromptPanel";
|
|
14
|
+
export * from "./components/AiModelSelect";
|
|
15
|
+
export * from "./components/AiInput";
|
|
16
|
+
export * from "./components/AiTextarea";
|
|
17
|
+
export * from "./components/AiSelect";
|
|
18
|
+
export * from "./components/AiChipLabel";
|
|
19
|
+
export * from "./components/AiImageButton";
|
|
20
|
+
export * from "./components/AiSettingsButton";
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type ToastType = {
|
|
2
|
+
type: "success" | "error";
|
|
3
|
+
message: string;
|
|
4
|
+
code?: string;
|
|
5
|
+
};
|
|
6
|
+
export type UiMode = "modal" | "drawer";
|
|
7
|
+
export interface BaseAiProps {
|
|
8
|
+
baseUrl?: string;
|
|
9
|
+
apiKeyId?: string;
|
|
10
|
+
type?: UiMode;
|
|
11
|
+
context?: string | null;
|
|
12
|
+
model?: string | null;
|
|
13
|
+
prompt?: string | null;
|
|
14
|
+
editMode?: boolean;
|
|
15
|
+
onValue?: (value: string) => void;
|
|
16
|
+
onToast?: (toast: ToastType) => void;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
className?: string;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AACF,MAAM,MAAM,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAExC,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lastbrain/ai-ui-react",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "Headless React components for LastBrain AI UI Kit",
|
|
5
|
+
"private": false,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"release": {
|
|
18
|
+
"type": "public"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"ai",
|
|
22
|
+
"lastbrain",
|
|
23
|
+
"react",
|
|
24
|
+
"headless",
|
|
25
|
+
"components",
|
|
26
|
+
"ui-kit"
|
|
27
|
+
],
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/lastpublication/starter.git",
|
|
31
|
+
"directory": "packages/ai-ui-react"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"src"
|
|
39
|
+
],
|
|
40
|
+
"sideEffects": false,
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"react": ">=18.0.0",
|
|
43
|
+
"react-dom": ">=18.0.0"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@lastbrain/ai-ui-core": "^1.0.3"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/react": "^19.2.0",
|
|
50
|
+
"@types/react-dom": "^19.2.0",
|
|
51
|
+
"react": "^19.2.1",
|
|
52
|
+
"react-dom": "^19.2.1",
|
|
53
|
+
"typescript": "^5.4.0"
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"dev": "tsc -p tsconfig.json --watch",
|
|
57
|
+
"build": "tsc -p tsconfig.json",
|
|
58
|
+
"lint": "eslint ."
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState, type HTMLAttributes } from "react";
|
|
4
|
+
import type { BaseAiProps } from "../types";
|
|
5
|
+
import { useAiCallText } from "../hooks/useAiCallText";
|
|
6
|
+
import { useAiModels } from "../hooks/useAiModels";
|
|
7
|
+
import { AiPromptPanel } from "./AiPromptPanel";
|
|
8
|
+
|
|
9
|
+
export interface AiChipLabelProps
|
|
10
|
+
extends Omit<BaseAiProps, "type">, HTMLAttributes<HTMLDivElement> {
|
|
11
|
+
label?: string;
|
|
12
|
+
uiMode?: "modal" | "drawer";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function AiChipLabel({
|
|
16
|
+
baseUrl,
|
|
17
|
+
apiKeyId,
|
|
18
|
+
uiMode = "modal",
|
|
19
|
+
context,
|
|
20
|
+
model,
|
|
21
|
+
prompt,
|
|
22
|
+
onValue,
|
|
23
|
+
onToast,
|
|
24
|
+
disabled,
|
|
25
|
+
className,
|
|
26
|
+
label,
|
|
27
|
+
...divProps
|
|
28
|
+
}: AiChipLabelProps) {
|
|
29
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
30
|
+
const [chipLabel, setChipLabel] = useState(label || "");
|
|
31
|
+
|
|
32
|
+
const { models } = useAiModels({ baseUrl, apiKeyId });
|
|
33
|
+
const { generateText, loading } = useAiCallText({ baseUrl, apiKeyId });
|
|
34
|
+
|
|
35
|
+
const handleOpenPanel = () => {
|
|
36
|
+
setIsOpen(true);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const handleClosePanel = () => {
|
|
40
|
+
setIsOpen(false);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const handleSubmit = async (
|
|
44
|
+
selectedModel: string,
|
|
45
|
+
selectedPrompt: string
|
|
46
|
+
) => {
|
|
47
|
+
try {
|
|
48
|
+
const result = await generateText({
|
|
49
|
+
model: selectedModel,
|
|
50
|
+
prompt: selectedPrompt,
|
|
51
|
+
context: context || chipLabel || undefined,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (result.text) {
|
|
55
|
+
setChipLabel(result.text);
|
|
56
|
+
onValue?.(result.text);
|
|
57
|
+
onToast?.({ type: "success", message: "Label generated successfully" });
|
|
58
|
+
}
|
|
59
|
+
} catch (error) {
|
|
60
|
+
onToast?.({ type: "error", message: "Failed to generate label" });
|
|
61
|
+
} finally {
|
|
62
|
+
setIsOpen(false);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<div data-ai-chip-wrapper className={className} {...divProps}>
|
|
68
|
+
<div data-ai-chip-label>{chipLabel || "Generate label"}</div>
|
|
69
|
+
<button
|
|
70
|
+
onClick={handleOpenPanel}
|
|
71
|
+
disabled={disabled || loading}
|
|
72
|
+
data-ai-chip-button
|
|
73
|
+
type="button"
|
|
74
|
+
>
|
|
75
|
+
{loading ? "..." : "✨"}
|
|
76
|
+
</button>
|
|
77
|
+
{isOpen && (
|
|
78
|
+
<AiPromptPanel
|
|
79
|
+
isOpen={isOpen}
|
|
80
|
+
onClose={handleClosePanel}
|
|
81
|
+
onSubmit={handleSubmit}
|
|
82
|
+
uiMode={uiMode}
|
|
83
|
+
models={models || []}
|
|
84
|
+
/>
|
|
85
|
+
)}
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState, type ButtonHTMLAttributes } from "react";
|
|
4
|
+
import type { BaseAiProps } from "../types";
|
|
5
|
+
import { useAiCallImage } from "../hooks/useAiCallImage";
|
|
6
|
+
import { useAiModels } from "../hooks/useAiModels";
|
|
7
|
+
import { AiPromptPanel } from "./AiPromptPanel";
|
|
8
|
+
|
|
9
|
+
export interface AiImageButtonProps
|
|
10
|
+
extends
|
|
11
|
+
Omit<BaseAiProps, "onValue" | "type">,
|
|
12
|
+
ButtonHTMLAttributes<HTMLButtonElement> {
|
|
13
|
+
onImage?: (imageUrl: string) => void;
|
|
14
|
+
uiMode?: "modal" | "drawer";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function AiImageButton({
|
|
18
|
+
baseUrl,
|
|
19
|
+
apiKeyId,
|
|
20
|
+
uiMode = "modal",
|
|
21
|
+
context,
|
|
22
|
+
model,
|
|
23
|
+
prompt,
|
|
24
|
+
onImage,
|
|
25
|
+
onToast,
|
|
26
|
+
disabled,
|
|
27
|
+
className,
|
|
28
|
+
children,
|
|
29
|
+
...buttonProps
|
|
30
|
+
}: AiImageButtonProps) {
|
|
31
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
32
|
+
|
|
33
|
+
const { models } = useAiModels({ baseUrl, apiKeyId });
|
|
34
|
+
const { generateImage, loading } = useAiCallImage({ baseUrl, apiKeyId });
|
|
35
|
+
|
|
36
|
+
const handleOpenPanel = () => {
|
|
37
|
+
setIsOpen(true);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const handleClosePanel = () => {
|
|
41
|
+
setIsOpen(false);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const handleSubmit = async (
|
|
45
|
+
selectedModel: string,
|
|
46
|
+
selectedPrompt: string
|
|
47
|
+
) => {
|
|
48
|
+
try {
|
|
49
|
+
const result = await generateImage({
|
|
50
|
+
model: selectedModel,
|
|
51
|
+
prompt: selectedPrompt,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (result.url) {
|
|
55
|
+
onImage?.(result.url);
|
|
56
|
+
onToast?.({ type: "success", message: "Image generated successfully" });
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
onToast?.({ type: "error", message: "Failed to generate image" });
|
|
60
|
+
} finally {
|
|
61
|
+
setIsOpen(false);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<>
|
|
67
|
+
<button
|
|
68
|
+
{...buttonProps}
|
|
69
|
+
onClick={handleOpenPanel}
|
|
70
|
+
disabled={disabled || loading}
|
|
71
|
+
className={className}
|
|
72
|
+
data-ai-image-button
|
|
73
|
+
>
|
|
74
|
+
{loading ? "Generating..." : children || "Generate Image"}
|
|
75
|
+
</button>
|
|
76
|
+
{isOpen && (
|
|
77
|
+
<AiPromptPanel
|
|
78
|
+
isOpen={isOpen}
|
|
79
|
+
onClose={handleClosePanel}
|
|
80
|
+
onSubmit={handleSubmit}
|
|
81
|
+
uiMode={uiMode}
|
|
82
|
+
models={models?.filter((m) => m.type === "image") || []}
|
|
83
|
+
/>
|
|
84
|
+
)}
|
|
85
|
+
</>
|
|
86
|
+
);
|
|
87
|
+
}
|