@retrivora-ai/rag-engine 2.2.5 → 2.2.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/{index-BbJGyNmW.d.mts → LicenseValidator-B3xpJaVf.d.mts} +42 -2
- package/dist/{index-BCbeeh74.d.ts → LicenseValidator-D4I4pbSL.d.ts} +42 -2
- package/dist/handlers/index.js +9 -4
- package/dist/handlers/index.mjs +9 -4
- package/dist/index.css +119 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +413 -204
- package/dist/index.mjs +412 -205
- package/dist/server.d.mts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +127 -4
- package/dist/server.mjs +124 -4
- package/package.json +1 -1
- package/src/components/ChatWidget.tsx +53 -7
- package/src/components/ChatWindow.tsx +76 -13
- package/src/core/LicenseValidator.ts +146 -0
- package/src/exceptions/index.ts +25 -5
- package/src/handlers/index.ts +11 -1
- package/src/hooks/useRagChat.ts +18 -2
- package/src/index.css +119 -0
- package/src/index.ts +4 -0
- package/src/server.ts +6 -0
package/dist/server.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { V as VectorDBConfig, L as LLMConfig, d as EmbeddingConfig, i as RagConfig, n as UniversalRagConfig, o as UpsertDocument, q as VectorMatch, G as GraphDBConfig, r as GraphNode, s as Edge, t as GraphSearchResult, I as ILLMProvider, p as VectorDBProvider, g as LLMProvider, e as EmbeddingProvider, R as RAGConfig, m as UIConfig, C as ChatMessage, b as ChatOptions, E as EmbedOptions } from './ILLMProvider-BWa68XX5.mjs';
|
|
2
2
|
export { B as BarChartData, u as CarouselProduct, c as ChatResponse, v as IRetriever, f as IngestDocument, h as LatencyBreakdown, w as LineChartDataPoint, M as MessageRole, x as MetricCardData, O as ObservabilityTrace, P as PieChartData, y as Product, j as RagMessage, k as RetrievalConfig, z as RetrievalResult, l as RetrievedChunk, A as RetrivoraChunkMetadata, D as RetrivoraIngestedDocument, S as ScatterPlotDataPoint, F as SuggestionsResponse, H as TableData, T as TokenUsage, J as UITransformationResponse, U as UseRagChatOptions, a as UseRagChatReturn, K as VisualizationType, W as WorkflowConfig } from './ILLMProvider-BWa68XX5.mjs';
|
|
3
|
-
import { I as IRenderRule, i as DecisionContext, k as IntentCategory,
|
|
4
|
-
export {
|
|
3
|
+
import { I as IRenderRule, i as DecisionContext, k as IntentCategory, q as RenderDecision } from './LicenseValidator-B3xpJaVf.mjs';
|
|
4
|
+
export { F as ArchitectureCardProps, A as AuthenticationException, G as CarouselRendererStrategy, H as ChartRendererStrategy, e as ChartType, J as ChatViewportSize, C as ChatWidgetProps, a as ChatWindowProps, f as Chunk, g as ChunkOptions, c as ClientConfig, b as ConfigProviderProps, h as ConfigurationException, K as DocumentChunker, D as DocumentUploadProps, E as EmbeddingFailedException, j as IRendererStrategy, l as IntentClassifier, L as LicenseValidationError, m as LicenseValidationRequest, n as LicenseValidationResponse, o as LicenseValidator, M as MessageBubbleProps, N as MixedRendererStrategy, O as Pipeline, Q as PipelineStep, P as ProductCardProps, d as ProductCarouselProps, p as ProviderNotFoundException, T as ProviderPill, R as RateLimitException, U as RenderSectionDecision, r as RenderType, s as RendererRegistry, t as RetrievalException, u as Retrivora, v as RetrivoraError, w as RetrivoraErrorCode, x as RuleEngine, y as SDKVersionUnsupportedError, z as SDK_VERSION, W as Snippet, S as SourceCardProps, X as TableRendererStrategy, Y as TextRendererStrategy, V as VisualizationDecisionEngine, B as decideVisualization, Z as wrapError } from './LicenseValidator-B3xpJaVf.mjs';
|
|
5
5
|
import { H as HealthCheckResult, I as IProviderValidator, a as IProviderHealthChecker } from './index-DvbtNz7m.mjs';
|
|
6
6
|
export { C as ConfigValidator, V as ValidationError, b as VectorPlugin, c as createChatHandler, d as createFeedbackHandler, e as createHealthHandler, f as createHistoryHandler, g as createIngestHandler, h as createRagHandler, i as createSessionsHandler, j as createStreamHandler, k as createUploadHandler, s as sseErrorFrame, l as sseFrame, m as sseMetaFrame, n as sseTextFrame } from './index-DvbtNz7m.mjs';
|
|
7
7
|
import 'react';
|
package/dist/server.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { V as VectorDBConfig, L as LLMConfig, d as EmbeddingConfig, i as RagConfig, n as UniversalRagConfig, o as UpsertDocument, q as VectorMatch, G as GraphDBConfig, r as GraphNode, s as Edge, t as GraphSearchResult, I as ILLMProvider, p as VectorDBProvider, g as LLMProvider, e as EmbeddingProvider, R as RAGConfig, m as UIConfig, C as ChatMessage, b as ChatOptions, E as EmbedOptions } from './ILLMProvider-BWa68XX5.js';
|
|
2
2
|
export { B as BarChartData, u as CarouselProduct, c as ChatResponse, v as IRetriever, f as IngestDocument, h as LatencyBreakdown, w as LineChartDataPoint, M as MessageRole, x as MetricCardData, O as ObservabilityTrace, P as PieChartData, y as Product, j as RagMessage, k as RetrievalConfig, z as RetrievalResult, l as RetrievedChunk, A as RetrivoraChunkMetadata, D as RetrivoraIngestedDocument, S as ScatterPlotDataPoint, F as SuggestionsResponse, H as TableData, T as TokenUsage, J as UITransformationResponse, U as UseRagChatOptions, a as UseRagChatReturn, K as VisualizationType, W as WorkflowConfig } from './ILLMProvider-BWa68XX5.js';
|
|
3
|
-
import { I as IRenderRule, i as DecisionContext, k as IntentCategory,
|
|
4
|
-
export {
|
|
3
|
+
import { I as IRenderRule, i as DecisionContext, k as IntentCategory, q as RenderDecision } from './LicenseValidator-D4I4pbSL.js';
|
|
4
|
+
export { F as ArchitectureCardProps, A as AuthenticationException, G as CarouselRendererStrategy, H as ChartRendererStrategy, e as ChartType, J as ChatViewportSize, C as ChatWidgetProps, a as ChatWindowProps, f as Chunk, g as ChunkOptions, c as ClientConfig, b as ConfigProviderProps, h as ConfigurationException, K as DocumentChunker, D as DocumentUploadProps, E as EmbeddingFailedException, j as IRendererStrategy, l as IntentClassifier, L as LicenseValidationError, m as LicenseValidationRequest, n as LicenseValidationResponse, o as LicenseValidator, M as MessageBubbleProps, N as MixedRendererStrategy, O as Pipeline, Q as PipelineStep, P as ProductCardProps, d as ProductCarouselProps, p as ProviderNotFoundException, T as ProviderPill, R as RateLimitException, U as RenderSectionDecision, r as RenderType, s as RendererRegistry, t as RetrievalException, u as Retrivora, v as RetrivoraError, w as RetrivoraErrorCode, x as RuleEngine, y as SDKVersionUnsupportedError, z as SDK_VERSION, W as Snippet, S as SourceCardProps, X as TableRendererStrategy, Y as TextRendererStrategy, V as VisualizationDecisionEngine, B as decideVisualization, Z as wrapError } from './LicenseValidator-D4I4pbSL.js';
|
|
5
5
|
import { H as HealthCheckResult, I as IProviderValidator, a as IProviderHealthChecker } from './index-B5TTZWkx.js';
|
|
6
6
|
export { C as ConfigValidator, V as ValidationError, b as VectorPlugin, c as createChatHandler, d as createFeedbackHandler, e as createHealthHandler, f as createHistoryHandler, g as createIngestHandler, h as createRagHandler, i as createSessionsHandler, j as createStreamHandler, k as createUploadHandler, s as sseErrorFrame, l as sseFrame, m as sseMetaFrame, n as sseTextFrame } from './index-B5TTZWkx.js';
|
|
7
7
|
import 'react';
|
package/dist/server.js
CHANGED
|
@@ -2224,6 +2224,8 @@ __export(server_exports, {
|
|
|
2224
2224
|
IntentClassifier: () => IntentClassifier,
|
|
2225
2225
|
LLMFactory: () => LLMFactory,
|
|
2226
2226
|
LLM_PROFILES: () => LLM_PROFILES,
|
|
2227
|
+
LicenseValidationError: () => LicenseValidationError,
|
|
2228
|
+
LicenseValidator: () => LicenseValidator,
|
|
2227
2229
|
LicenseVerifier: () => LicenseVerifier,
|
|
2228
2230
|
MilvusProvider: () => MilvusProvider,
|
|
2229
2231
|
MixedRendererStrategy: () => MixedRendererStrategy,
|
|
@@ -2254,6 +2256,7 @@ __export(server_exports, {
|
|
|
2254
2256
|
Rule6SmallResultSetRule: () => Rule6SmallResultSetRule,
|
|
2255
2257
|
Rule7LargeTableRule: () => Rule7LargeTableRule,
|
|
2256
2258
|
RuleEngine: () => RuleEngine,
|
|
2259
|
+
SDKVersionUnsupportedError: () => SDKVersionUnsupportedError,
|
|
2257
2260
|
SDK_VERSION: () => SDK_VERSION,
|
|
2258
2261
|
TableRendererStrategy: () => TableRendererStrategy,
|
|
2259
2262
|
TextRendererStrategy: () => TextRendererStrategy,
|
|
@@ -2328,6 +2331,16 @@ var AuthenticationException = class extends RetrivoraError {
|
|
|
2328
2331
|
super(message, "AUTHENTICATION_ERROR", details);
|
|
2329
2332
|
}
|
|
2330
2333
|
};
|
|
2334
|
+
var SDKVersionUnsupportedError = class extends RetrivoraError {
|
|
2335
|
+
constructor(message = "This SDK version is no longer supported.", details) {
|
|
2336
|
+
super(message, "SDK_VERSION_UNSUPPORTED", details);
|
|
2337
|
+
}
|
|
2338
|
+
};
|
|
2339
|
+
var LicenseValidationError = class extends RetrivoraError {
|
|
2340
|
+
constructor(message = "License validation failed.", details) {
|
|
2341
|
+
super(message, "LICENSE_VALIDATION_ERROR", details);
|
|
2342
|
+
}
|
|
2343
|
+
};
|
|
2331
2344
|
function wrapError(err, defaultCode, defaultMessage) {
|
|
2332
2345
|
var _a2;
|
|
2333
2346
|
if (err instanceof RetrivoraError) {
|
|
@@ -2340,8 +2353,10 @@ function wrapError(err, defaultCode, defaultMessage) {
|
|
|
2340
2353
|
if (status === 429 || /rate[- ]?limit/i.test(message) || code === "RATE_LIMIT_EXCEEDED") {
|
|
2341
2354
|
return new RateLimitException(message, err);
|
|
2342
2355
|
}
|
|
2343
|
-
if (status === 401 || status === 403 || /unauthorized|auth|api[- ]?key/i.test(message) || code === "INVALID_API_KEY") {
|
|
2344
|
-
|
|
2356
|
+
if (status === 401 || status === 403 || /unauthorized|auth|license|revoked|terminated|api[- ]?key/i.test(message) || code === "INVALID_API_KEY" || code === "LICENSE_DELETED" || code === "LICENSE_INACTIVE") {
|
|
2357
|
+
const isLicenseError = /403|license|revoked|terminated/i.test(message) || status === 403;
|
|
2358
|
+
const cleanMsg = isLicenseError ? "Your Retrivora license key has been terminated, suspended, or revoked. Access denied." : message;
|
|
2359
|
+
return new AuthenticationException(cleanMsg, err);
|
|
2345
2360
|
}
|
|
2346
2361
|
switch (defaultCode) {
|
|
2347
2362
|
case "PROVIDER_NOT_FOUND":
|
|
@@ -4808,7 +4823,7 @@ var ConfigValidator = class {
|
|
|
4808
4823
|
// package.json
|
|
4809
4824
|
var package_default = {
|
|
4810
4825
|
name: "@retrivora-ai/rag-engine",
|
|
4811
|
-
version: "2.2.
|
|
4826
|
+
version: "2.2.7",
|
|
4812
4827
|
description: "Retrivora AI is a plug-and-play AI engine for RAG chat experiences \u2014 generic vector DB + LLM provider, embeddable or standalone.",
|
|
4813
4828
|
author: "Abhinav Alkuchi",
|
|
4814
4829
|
license: "UNLICENSED",
|
|
@@ -10924,7 +10939,10 @@ function createStreamHandler(configOrPlugin, options) {
|
|
|
10924
10939
|
}
|
|
10925
10940
|
} catch (streamError) {
|
|
10926
10941
|
if (isActive) {
|
|
10927
|
-
|
|
10942
|
+
let errorMessage = streamError instanceof Error ? streamError.message : String(streamError);
|
|
10943
|
+
if (errorMessage.includes("403") || errorMessage.toLowerCase().includes("license") || errorMessage.toLowerCase().includes("revoked") || errorMessage.toLowerCase().includes("terminated")) {
|
|
10944
|
+
errorMessage = "Your Retrivora license key has been terminated, suspended, or revoked. Access denied.";
|
|
10945
|
+
}
|
|
10928
10946
|
console.error("[createStreamHandler] Stream error:", streamError);
|
|
10929
10947
|
reportTelemetry(req, plugin, "QUERY_GENERATION", "error", errorMessage);
|
|
10930
10948
|
try {
|
|
@@ -11325,6 +11343,108 @@ function createRagHandler(configOrPlugin, options) {
|
|
|
11325
11343
|
}
|
|
11326
11344
|
};
|
|
11327
11345
|
}
|
|
11346
|
+
|
|
11347
|
+
// src/core/LicenseValidator.ts
|
|
11348
|
+
var _LicenseValidator = class _LicenseValidator {
|
|
11349
|
+
// 5-minute TTL for successful validation only
|
|
11350
|
+
constructor() {
|
|
11351
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
11352
|
+
}
|
|
11353
|
+
static getInstance() {
|
|
11354
|
+
if (!_LicenseValidator.instance) {
|
|
11355
|
+
_LicenseValidator.instance = new _LicenseValidator();
|
|
11356
|
+
}
|
|
11357
|
+
return _LicenseValidator.instance;
|
|
11358
|
+
}
|
|
11359
|
+
/**
|
|
11360
|
+
* Invalidate local validation cache for a license key
|
|
11361
|
+
*/
|
|
11362
|
+
purgeCache(licenseKey) {
|
|
11363
|
+
if (licenseKey) {
|
|
11364
|
+
this.cache.delete(licenseKey);
|
|
11365
|
+
} else {
|
|
11366
|
+
this.cache.clear();
|
|
11367
|
+
}
|
|
11368
|
+
}
|
|
11369
|
+
/**
|
|
11370
|
+
* Validate license status and SDK version against Retrivora License Service
|
|
11371
|
+
*/
|
|
11372
|
+
async validate(request) {
|
|
11373
|
+
const {
|
|
11374
|
+
licenseKey,
|
|
11375
|
+
projectId,
|
|
11376
|
+
sdkVersion = SDK_VERSION,
|
|
11377
|
+
sdk = "@retrivora-ai/rag-engine",
|
|
11378
|
+
platform = typeof window !== "undefined" ? "browser" : "node",
|
|
11379
|
+
retrivoraApiBase,
|
|
11380
|
+
headers: customHeaders
|
|
11381
|
+
} = request;
|
|
11382
|
+
if (!licenseKey) {
|
|
11383
|
+
this.purgeCache(licenseKey);
|
|
11384
|
+
throw new LicenseValidationError("Missing RETRIVORA_LICENSE_KEY in request.");
|
|
11385
|
+
}
|
|
11386
|
+
const cached = this.cache.get(licenseKey);
|
|
11387
|
+
if (cached) {
|
|
11388
|
+
const isFresh = Date.now() - cached.timestamp < _LicenseValidator.SUCCESS_CACHE_TTL_MS;
|
|
11389
|
+
if (isFresh && cached.response.valid && cached.response.licenseStatus === "ACTIVE" && !cached.response.forceUpgrade) {
|
|
11390
|
+
return cached.response;
|
|
11391
|
+
} else {
|
|
11392
|
+
this.cache.delete(licenseKey);
|
|
11393
|
+
}
|
|
11394
|
+
}
|
|
11395
|
+
const base = retrivoraApiBase || (typeof process !== "undefined" ? process.env.NEXT_PUBLIC_RETRIVORA_API_BASE || process.env.RETRIVORA_API_BASE : "") || "/api/retrivora";
|
|
11396
|
+
const validateUrl = `${base.replace(/\/$/, "")}/v1/license/validate`;
|
|
11397
|
+
let res;
|
|
11398
|
+
try {
|
|
11399
|
+
res = await fetch(validateUrl, {
|
|
11400
|
+
method: "POST",
|
|
11401
|
+
cache: "no-store",
|
|
11402
|
+
headers: __spreadValues({
|
|
11403
|
+
"Content-Type": "application/json",
|
|
11404
|
+
"x-license-key": licenseKey,
|
|
11405
|
+
"x-sdk-version": sdkVersion
|
|
11406
|
+
}, customHeaders || {}),
|
|
11407
|
+
body: JSON.stringify({
|
|
11408
|
+
licenseKey,
|
|
11409
|
+
projectId,
|
|
11410
|
+
sdkVersion,
|
|
11411
|
+
sdk,
|
|
11412
|
+
platform
|
|
11413
|
+
})
|
|
11414
|
+
});
|
|
11415
|
+
} catch (err) {
|
|
11416
|
+
this.purgeCache(licenseKey);
|
|
11417
|
+
throw new LicenseValidationError(`License validation request failed: ${(err == null ? void 0 : err.message) || "Network error"}`);
|
|
11418
|
+
}
|
|
11419
|
+
const data = await res.json().catch(() => ({
|
|
11420
|
+
valid: false,
|
|
11421
|
+
licenseStatus: "REVOKED",
|
|
11422
|
+
minimumSupportedVersion: "2.1.0",
|
|
11423
|
+
latestVersion: SDK_VERSION,
|
|
11424
|
+
forceUpgrade: false,
|
|
11425
|
+
expiresAt: null,
|
|
11426
|
+
message: "Failed to parse license validation response."
|
|
11427
|
+
}));
|
|
11428
|
+
if (!res.ok || !data.valid || data.licenseStatus !== "ACTIVE" || data.forceUpgrade) {
|
|
11429
|
+
this.purgeCache(licenseKey);
|
|
11430
|
+
if (data.forceUpgrade || res.status === 426) {
|
|
11431
|
+
throw new SDKVersionUnsupportedError(
|
|
11432
|
+
data.message || `This SDK version (${sdkVersion}) is no longer supported. Upgrade to version ${data.latestVersion || "2.2.6"} or later.`,
|
|
11433
|
+
data
|
|
11434
|
+
);
|
|
11435
|
+
}
|
|
11436
|
+
const errMsg = data.message || `License validation failed with status: ${data.licenseStatus}. Access denied.`;
|
|
11437
|
+
throw new LicenseValidationError(errMsg, data);
|
|
11438
|
+
}
|
|
11439
|
+
this.cache.set(licenseKey, {
|
|
11440
|
+
response: data,
|
|
11441
|
+
timestamp: Date.now()
|
|
11442
|
+
});
|
|
11443
|
+
return data;
|
|
11444
|
+
}
|
|
11445
|
+
};
|
|
11446
|
+
_LicenseValidator.SUCCESS_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
11447
|
+
var LicenseValidator = _LicenseValidator;
|
|
11328
11448
|
// Annotate the CommonJS export names for ESM import in node:
|
|
11329
11449
|
0 && (module.exports = {
|
|
11330
11450
|
AnthropicProvider,
|
|
@@ -11347,6 +11467,8 @@ function createRagHandler(configOrPlugin, options) {
|
|
|
11347
11467
|
IntentClassifier,
|
|
11348
11468
|
LLMFactory,
|
|
11349
11469
|
LLM_PROFILES,
|
|
11470
|
+
LicenseValidationError,
|
|
11471
|
+
LicenseValidator,
|
|
11350
11472
|
LicenseVerifier,
|
|
11351
11473
|
MilvusProvider,
|
|
11352
11474
|
MixedRendererStrategy,
|
|
@@ -11377,6 +11499,7 @@ function createRagHandler(configOrPlugin, options) {
|
|
|
11377
11499
|
Rule6SmallResultSetRule,
|
|
11378
11500
|
Rule7LargeTableRule,
|
|
11379
11501
|
RuleEngine,
|
|
11502
|
+
SDKVersionUnsupportedError,
|
|
11380
11503
|
SDK_VERSION,
|
|
11381
11504
|
TableRendererStrategy,
|
|
11382
11505
|
TextRendererStrategy,
|
package/dist/server.mjs
CHANGED
|
@@ -2231,6 +2231,16 @@ var AuthenticationException = class extends RetrivoraError {
|
|
|
2231
2231
|
super(message, "AUTHENTICATION_ERROR", details);
|
|
2232
2232
|
}
|
|
2233
2233
|
};
|
|
2234
|
+
var SDKVersionUnsupportedError = class extends RetrivoraError {
|
|
2235
|
+
constructor(message = "This SDK version is no longer supported.", details) {
|
|
2236
|
+
super(message, "SDK_VERSION_UNSUPPORTED", details);
|
|
2237
|
+
}
|
|
2238
|
+
};
|
|
2239
|
+
var LicenseValidationError = class extends RetrivoraError {
|
|
2240
|
+
constructor(message = "License validation failed.", details) {
|
|
2241
|
+
super(message, "LICENSE_VALIDATION_ERROR", details);
|
|
2242
|
+
}
|
|
2243
|
+
};
|
|
2234
2244
|
function wrapError(err, defaultCode, defaultMessage) {
|
|
2235
2245
|
var _a2;
|
|
2236
2246
|
if (err instanceof RetrivoraError) {
|
|
@@ -2243,8 +2253,10 @@ function wrapError(err, defaultCode, defaultMessage) {
|
|
|
2243
2253
|
if (status === 429 || /rate[- ]?limit/i.test(message) || code === "RATE_LIMIT_EXCEEDED") {
|
|
2244
2254
|
return new RateLimitException(message, err);
|
|
2245
2255
|
}
|
|
2246
|
-
if (status === 401 || status === 403 || /unauthorized|auth|api[- ]?key/i.test(message) || code === "INVALID_API_KEY") {
|
|
2247
|
-
|
|
2256
|
+
if (status === 401 || status === 403 || /unauthorized|auth|license|revoked|terminated|api[- ]?key/i.test(message) || code === "INVALID_API_KEY" || code === "LICENSE_DELETED" || code === "LICENSE_INACTIVE") {
|
|
2257
|
+
const isLicenseError = /403|license|revoked|terminated/i.test(message) || status === 403;
|
|
2258
|
+
const cleanMsg = isLicenseError ? "Your Retrivora license key has been terminated, suspended, or revoked. Access denied." : message;
|
|
2259
|
+
return new AuthenticationException(cleanMsg, err);
|
|
2248
2260
|
}
|
|
2249
2261
|
switch (defaultCode) {
|
|
2250
2262
|
case "PROVIDER_NOT_FOUND":
|
|
@@ -4711,7 +4723,7 @@ var ConfigValidator = class {
|
|
|
4711
4723
|
// package.json
|
|
4712
4724
|
var package_default = {
|
|
4713
4725
|
name: "@retrivora-ai/rag-engine",
|
|
4714
|
-
version: "2.2.
|
|
4726
|
+
version: "2.2.7",
|
|
4715
4727
|
description: "Retrivora AI is a plug-and-play AI engine for RAG chat experiences \u2014 generic vector DB + LLM provider, embeddable or standalone.",
|
|
4716
4728
|
author: "Abhinav Alkuchi",
|
|
4717
4729
|
license: "UNLICENSED",
|
|
@@ -10827,7 +10839,10 @@ function createStreamHandler(configOrPlugin, options) {
|
|
|
10827
10839
|
}
|
|
10828
10840
|
} catch (streamError) {
|
|
10829
10841
|
if (isActive) {
|
|
10830
|
-
|
|
10842
|
+
let errorMessage = streamError instanceof Error ? streamError.message : String(streamError);
|
|
10843
|
+
if (errorMessage.includes("403") || errorMessage.toLowerCase().includes("license") || errorMessage.toLowerCase().includes("revoked") || errorMessage.toLowerCase().includes("terminated")) {
|
|
10844
|
+
errorMessage = "Your Retrivora license key has been terminated, suspended, or revoked. Access denied.";
|
|
10845
|
+
}
|
|
10831
10846
|
console.error("[createStreamHandler] Stream error:", streamError);
|
|
10832
10847
|
reportTelemetry(req, plugin, "QUERY_GENERATION", "error", errorMessage);
|
|
10833
10848
|
try {
|
|
@@ -11228,6 +11243,108 @@ function createRagHandler(configOrPlugin, options) {
|
|
|
11228
11243
|
}
|
|
11229
11244
|
};
|
|
11230
11245
|
}
|
|
11246
|
+
|
|
11247
|
+
// src/core/LicenseValidator.ts
|
|
11248
|
+
var _LicenseValidator = class _LicenseValidator {
|
|
11249
|
+
// 5-minute TTL for successful validation only
|
|
11250
|
+
constructor() {
|
|
11251
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
11252
|
+
}
|
|
11253
|
+
static getInstance() {
|
|
11254
|
+
if (!_LicenseValidator.instance) {
|
|
11255
|
+
_LicenseValidator.instance = new _LicenseValidator();
|
|
11256
|
+
}
|
|
11257
|
+
return _LicenseValidator.instance;
|
|
11258
|
+
}
|
|
11259
|
+
/**
|
|
11260
|
+
* Invalidate local validation cache for a license key
|
|
11261
|
+
*/
|
|
11262
|
+
purgeCache(licenseKey) {
|
|
11263
|
+
if (licenseKey) {
|
|
11264
|
+
this.cache.delete(licenseKey);
|
|
11265
|
+
} else {
|
|
11266
|
+
this.cache.clear();
|
|
11267
|
+
}
|
|
11268
|
+
}
|
|
11269
|
+
/**
|
|
11270
|
+
* Validate license status and SDK version against Retrivora License Service
|
|
11271
|
+
*/
|
|
11272
|
+
async validate(request) {
|
|
11273
|
+
const {
|
|
11274
|
+
licenseKey,
|
|
11275
|
+
projectId,
|
|
11276
|
+
sdkVersion = SDK_VERSION,
|
|
11277
|
+
sdk = "@retrivora-ai/rag-engine",
|
|
11278
|
+
platform = typeof window !== "undefined" ? "browser" : "node",
|
|
11279
|
+
retrivoraApiBase,
|
|
11280
|
+
headers: customHeaders
|
|
11281
|
+
} = request;
|
|
11282
|
+
if (!licenseKey) {
|
|
11283
|
+
this.purgeCache(licenseKey);
|
|
11284
|
+
throw new LicenseValidationError("Missing RETRIVORA_LICENSE_KEY in request.");
|
|
11285
|
+
}
|
|
11286
|
+
const cached = this.cache.get(licenseKey);
|
|
11287
|
+
if (cached) {
|
|
11288
|
+
const isFresh = Date.now() - cached.timestamp < _LicenseValidator.SUCCESS_CACHE_TTL_MS;
|
|
11289
|
+
if (isFresh && cached.response.valid && cached.response.licenseStatus === "ACTIVE" && !cached.response.forceUpgrade) {
|
|
11290
|
+
return cached.response;
|
|
11291
|
+
} else {
|
|
11292
|
+
this.cache.delete(licenseKey);
|
|
11293
|
+
}
|
|
11294
|
+
}
|
|
11295
|
+
const base = retrivoraApiBase || (typeof process !== "undefined" ? process.env.NEXT_PUBLIC_RETRIVORA_API_BASE || process.env.RETRIVORA_API_BASE : "") || "/api/retrivora";
|
|
11296
|
+
const validateUrl = `${base.replace(/\/$/, "")}/v1/license/validate`;
|
|
11297
|
+
let res;
|
|
11298
|
+
try {
|
|
11299
|
+
res = await fetch(validateUrl, {
|
|
11300
|
+
method: "POST",
|
|
11301
|
+
cache: "no-store",
|
|
11302
|
+
headers: __spreadValues({
|
|
11303
|
+
"Content-Type": "application/json",
|
|
11304
|
+
"x-license-key": licenseKey,
|
|
11305
|
+
"x-sdk-version": sdkVersion
|
|
11306
|
+
}, customHeaders || {}),
|
|
11307
|
+
body: JSON.stringify({
|
|
11308
|
+
licenseKey,
|
|
11309
|
+
projectId,
|
|
11310
|
+
sdkVersion,
|
|
11311
|
+
sdk,
|
|
11312
|
+
platform
|
|
11313
|
+
})
|
|
11314
|
+
});
|
|
11315
|
+
} catch (err) {
|
|
11316
|
+
this.purgeCache(licenseKey);
|
|
11317
|
+
throw new LicenseValidationError(`License validation request failed: ${(err == null ? void 0 : err.message) || "Network error"}`);
|
|
11318
|
+
}
|
|
11319
|
+
const data = await res.json().catch(() => ({
|
|
11320
|
+
valid: false,
|
|
11321
|
+
licenseStatus: "REVOKED",
|
|
11322
|
+
minimumSupportedVersion: "2.1.0",
|
|
11323
|
+
latestVersion: SDK_VERSION,
|
|
11324
|
+
forceUpgrade: false,
|
|
11325
|
+
expiresAt: null,
|
|
11326
|
+
message: "Failed to parse license validation response."
|
|
11327
|
+
}));
|
|
11328
|
+
if (!res.ok || !data.valid || data.licenseStatus !== "ACTIVE" || data.forceUpgrade) {
|
|
11329
|
+
this.purgeCache(licenseKey);
|
|
11330
|
+
if (data.forceUpgrade || res.status === 426) {
|
|
11331
|
+
throw new SDKVersionUnsupportedError(
|
|
11332
|
+
data.message || `This SDK version (${sdkVersion}) is no longer supported. Upgrade to version ${data.latestVersion || "2.2.6"} or later.`,
|
|
11333
|
+
data
|
|
11334
|
+
);
|
|
11335
|
+
}
|
|
11336
|
+
const errMsg = data.message || `License validation failed with status: ${data.licenseStatus}. Access denied.`;
|
|
11337
|
+
throw new LicenseValidationError(errMsg, data);
|
|
11338
|
+
}
|
|
11339
|
+
this.cache.set(licenseKey, {
|
|
11340
|
+
response: data,
|
|
11341
|
+
timestamp: Date.now()
|
|
11342
|
+
});
|
|
11343
|
+
return data;
|
|
11344
|
+
}
|
|
11345
|
+
};
|
|
11346
|
+
_LicenseValidator.SUCCESS_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
11347
|
+
var LicenseValidator = _LicenseValidator;
|
|
11231
11348
|
export {
|
|
11232
11349
|
AnthropicProvider,
|
|
11233
11350
|
AuthenticationException,
|
|
@@ -11249,6 +11366,8 @@ export {
|
|
|
11249
11366
|
IntentClassifier,
|
|
11250
11367
|
LLMFactory,
|
|
11251
11368
|
LLM_PROFILES,
|
|
11369
|
+
LicenseValidationError,
|
|
11370
|
+
LicenseValidator,
|
|
11252
11371
|
LicenseVerifier,
|
|
11253
11372
|
MilvusProvider,
|
|
11254
11373
|
MixedRendererStrategy,
|
|
@@ -11279,6 +11398,7 @@ export {
|
|
|
11279
11398
|
Rule6SmallResultSetRule,
|
|
11280
11399
|
Rule7LargeTableRule,
|
|
11281
11400
|
RuleEngine,
|
|
11401
|
+
SDKVersionUnsupportedError,
|
|
11282
11402
|
SDK_VERSION,
|
|
11283
11403
|
TableRendererStrategy,
|
|
11284
11404
|
TextRendererStrategy,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retrivora-ai/rag-engine",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.7",
|
|
4
4
|
"description": "Retrivora AI is a plug-and-play AI engine for RAG chat experiences — generic vector DB + LLM provider, embeddable or standalone.",
|
|
5
5
|
"author": "Abhinav Alkuchi",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
1
|
import React, { useState } from 'react';
|
|
4
|
-
import { MessageSquare, X } from 'lucide-react';
|
|
2
|
+
import { MessageSquare, X, AlertTriangle } from 'lucide-react';
|
|
5
3
|
import { ChatWindow } from './ChatWindow';
|
|
6
4
|
import { useConfig } from './ConfigProvider';
|
|
7
5
|
import { ChatWidgetProps } from '../types';
|
|
6
|
+
import { SDK_VERSION } from '../version';
|
|
7
|
+
import { LicenseValidator } from '../core/LicenseValidator';
|
|
8
8
|
|
|
9
9
|
const DEFAULT_DIMENSIONS = { width: 380, height: 600 };
|
|
10
10
|
|
|
@@ -47,13 +47,15 @@ const GEMINI_STYLES = `
|
|
|
47
47
|
`;
|
|
48
48
|
|
|
49
49
|
export function ChatWidget({ position = 'bottom-right', onAddToCart, apiUrl, retrivoraApiBase, headers }: ChatWidgetProps) {
|
|
50
|
-
const { ui } = useConfig();
|
|
50
|
+
const { ui, projectId } = useConfig();
|
|
51
51
|
const [isOpen, setIsOpen] = useState(false);
|
|
52
52
|
const [hasUnread, setHasUnread] = useState(false);
|
|
53
53
|
const [dimensions, setDimensions] = useState(DEFAULT_DIMENSIONS);
|
|
54
54
|
const [isResizing, setIsResizing] = useState(false);
|
|
55
55
|
const [isMaximized, setIsMaximized] = useState(false);
|
|
56
56
|
const [prevDimensions, setPrevDimensions] = useState(DEFAULT_DIMENSIONS);
|
|
57
|
+
const [widgetError, setWidgetError] = useState<string | null>(null);
|
|
58
|
+
const [isValidating, setIsValidating] = useState(false);
|
|
57
59
|
|
|
58
60
|
if (ui.showWidget === false) return null;
|
|
59
61
|
|
|
@@ -67,9 +69,26 @@ export function ChatWidget({ position = 'bottom-right', onAddToCart, apiUrl, ret
|
|
|
67
69
|
? 'bottom-20 left-6'
|
|
68
70
|
: 'bottom-20 right-6';
|
|
69
71
|
|
|
70
|
-
const handleOpen = () => {
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
const handleOpen = async () => {
|
|
73
|
+
setIsValidating(true);
|
|
74
|
+
try {
|
|
75
|
+
const licenseKey = headers?.['x-license-key'] || headers?.['authorization']?.replace(/^Bearer\s+/i, '') || '';
|
|
76
|
+
await LicenseValidator.getInstance().validate({
|
|
77
|
+
licenseKey,
|
|
78
|
+
projectId,
|
|
79
|
+
retrivoraApiBase,
|
|
80
|
+
headers,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
setWidgetError(null);
|
|
84
|
+
setIsOpen(true);
|
|
85
|
+
setHasUnread(false);
|
|
86
|
+
} catch (err: any) {
|
|
87
|
+
setWidgetError(err?.message || 'Access denied.');
|
|
88
|
+
setIsOpen(false); // DO NOT OPEN CHAT WIDGET
|
|
89
|
+
} finally {
|
|
90
|
+
setIsValidating(false);
|
|
91
|
+
}
|
|
73
92
|
};
|
|
74
93
|
|
|
75
94
|
const handleResizeStart = (e: React.MouseEvent) => {
|
|
@@ -157,6 +176,33 @@ export function ChatWidget({ position = 'bottom-right', onAddToCart, apiUrl, ret
|
|
|
157
176
|
/>
|
|
158
177
|
</div>
|
|
159
178
|
|
|
179
|
+
{/* ── Widget Error Toast/Pop-over (when opening is blocked) ── */}
|
|
180
|
+
{widgetError && !isOpen && (
|
|
181
|
+
<div
|
|
182
|
+
className={`fixed z-[9999] max-w-sm p-4 rounded-2xl shadow-2xl border border-rose-200 dark:border-rose-900/60 bg-white/95 dark:bg-[#0f0f1a]/95 backdrop-blur-xl transition-all duration-300 animate-in fade-in slide-in-from-bottom-3 ${
|
|
183
|
+
position === 'bottom-left' ? 'bottom-24 left-6' : 'bottom-24 right-6'
|
|
184
|
+
}`}
|
|
185
|
+
>
|
|
186
|
+
<div className="flex items-start gap-3">
|
|
187
|
+
<div className="w-8 h-8 rounded-xl bg-rose-100 dark:bg-rose-900/40 text-rose-600 dark:text-rose-400 flex items-center justify-center shrink-0 mt-0.5">
|
|
188
|
+
<AlertTriangle className="w-4 h-4" />
|
|
189
|
+
</div>
|
|
190
|
+
<div className="flex-1 space-y-1">
|
|
191
|
+
<h5 className="text-xs font-bold text-slate-900 dark:text-white">Access Blocked</h5>
|
|
192
|
+
<p className="text-[11px] text-slate-600 dark:text-slate-300 leading-relaxed font-medium">
|
|
193
|
+
{widgetError}
|
|
194
|
+
</p>
|
|
195
|
+
</div>
|
|
196
|
+
<button
|
|
197
|
+
onClick={() => setWidgetError(null)}
|
|
198
|
+
className="text-slate-400 hover:text-slate-600 dark:hover:text-white transition-colors p-1"
|
|
199
|
+
>
|
|
200
|
+
<X className="w-3.5 h-3.5" />
|
|
201
|
+
</button>
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
)}
|
|
205
|
+
|
|
160
206
|
{/* ── Floating Action Button ── */}
|
|
161
207
|
<button
|
|
162
208
|
onClick={isOpen ? () => setIsOpen(false) : handleOpen}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
X,
|
|
8
8
|
Sparkles,
|
|
9
9
|
ArrowUp,
|
|
10
|
+
ArrowUpCircle,
|
|
10
11
|
TriangleAlert,
|
|
11
12
|
RotateCcw,
|
|
12
13
|
Maximize2,
|
|
@@ -21,6 +22,8 @@ import { useConfig } from './ConfigProvider';
|
|
|
21
22
|
import { useRagChat } from '../hooks/useRagChat';
|
|
22
23
|
import { BORDER_RADIUS_MAP, CHAT_SUGGESTIONS } from '../config/uiConstants';
|
|
23
24
|
import { ChatWindowProps } from '../types';
|
|
25
|
+
import { SDK_VERSION } from '../version';
|
|
26
|
+
import { LicenseValidator } from '../core/LicenseValidator';
|
|
24
27
|
|
|
25
28
|
interface SpeechRecognitionEvent {
|
|
26
29
|
resultIndex: number;
|
|
@@ -86,8 +89,42 @@ export function ChatWindow({
|
|
|
86
89
|
|
|
87
90
|
const [isListening, setIsListening] = useState(false);
|
|
88
91
|
const [isUploadModalOpen, setIsUploadModalOpen] = useState(false);
|
|
92
|
+
const [licenseError, setLicenseError] = useState<string | null>(null);
|
|
93
|
+
const [versionError, setVersionError] = useState<string | null>(null);
|
|
89
94
|
const recognitionRef = useRef<ISpeechRecognition | null>(null);
|
|
90
95
|
|
|
96
|
+
// Validate server license and SDK version on ChatWindow session initialization
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
let isMounted = true;
|
|
99
|
+
async function validateSessionLicense() {
|
|
100
|
+
try {
|
|
101
|
+
const licenseKey = headers?.['x-license-key'] || headers?.['authorization']?.replace(/^Bearer\s+/i, '') || '';
|
|
102
|
+
await LicenseValidator.getInstance().validate({
|
|
103
|
+
licenseKey,
|
|
104
|
+
projectId,
|
|
105
|
+
retrivoraApiBase,
|
|
106
|
+
headers,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
if (isMounted) {
|
|
110
|
+
setLicenseError(null);
|
|
111
|
+
setVersionError(null);
|
|
112
|
+
}
|
|
113
|
+
} catch (err: any) {
|
|
114
|
+
if (isMounted) {
|
|
115
|
+
if (err?.code === 'SDK_VERSION_UNSUPPORTED') {
|
|
116
|
+
setVersionError(err?.message || 'SDK version unsupported.');
|
|
117
|
+
} else {
|
|
118
|
+
setLicenseError(err?.message || 'License validation failed.');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
validateSessionLicense();
|
|
125
|
+
return () => { isMounted = false; };
|
|
126
|
+
}, [retrivoraApiBase, headers, projectId]);
|
|
127
|
+
|
|
91
128
|
// Initialize SpeechRecognition
|
|
92
129
|
useEffect(() => {
|
|
93
130
|
if (typeof window !== 'undefined') {
|
|
@@ -345,7 +382,36 @@ export function ChatWindow({
|
|
|
345
382
|
</div>
|
|
346
383
|
|
|
347
384
|
<div className="flex-1 overflow-y-auto px-4 py-4 space-y-5 scrollbar-thin scrollbar-thumb-slate-200 dark:scrollbar-thumb-white/10 scrollbar-track-transparent min-h-0 bg-slate-50 dark:bg-transparent">
|
|
348
|
-
{
|
|
385
|
+
{versionError ? (
|
|
386
|
+
<div className="h-full flex flex-col items-center justify-center text-center p-6 space-y-4 bg-amber-50/70 dark:bg-amber-950/20 rounded-2xl border border-amber-200/80 dark:border-amber-800/40 my-auto">
|
|
387
|
+
<div className="w-14 h-14 rounded-2xl bg-amber-100 dark:bg-amber-900/40 text-amber-600 dark:text-amber-400 flex items-center justify-center shadow-sm shrink-0">
|
|
388
|
+
<ArrowUpCircle className="w-7 h-7 animate-bounce" />
|
|
389
|
+
</div>
|
|
390
|
+
<div className="space-y-2 max-w-xs">
|
|
391
|
+
<h4 className="text-base font-extrabold text-slate-900 dark:text-white tracking-tight">Package Update Required</h4>
|
|
392
|
+
<p className="text-xs text-slate-600 dark:text-slate-300 leading-relaxed font-medium">
|
|
393
|
+
{versionError}
|
|
394
|
+
</p>
|
|
395
|
+
<div className="pt-2">
|
|
396
|
+
<code className="block bg-slate-900 text-amber-300 text-[10px] font-mono py-2 px-3 rounded-xl select-all border border-slate-800">
|
|
397
|
+
npm i @retrivora-ai/rag-engine@latest
|
|
398
|
+
</code>
|
|
399
|
+
</div>
|
|
400
|
+
</div>
|
|
401
|
+
</div>
|
|
402
|
+
) : licenseError ? (
|
|
403
|
+
<div className="h-full flex flex-col items-center justify-center text-center p-6 space-y-4 bg-rose-50/50 dark:bg-rose-950/20 rounded-2xl border border-rose-200/60 dark:border-rose-800/40 my-auto">
|
|
404
|
+
<div className="w-14 h-14 rounded-2xl bg-rose-100 dark:bg-rose-900/40 text-rose-600 dark:text-rose-400 flex items-center justify-center shadow-sm shrink-0">
|
|
405
|
+
<TriangleAlert className="w-7 h-7" />
|
|
406
|
+
</div>
|
|
407
|
+
<div className="space-y-2 max-w-xs">
|
|
408
|
+
<h4 className="text-base font-extrabold text-slate-900 dark:text-white tracking-tight">License Invalid</h4>
|
|
409
|
+
<p className="text-xs text-slate-600 dark:text-slate-300 leading-relaxed font-medium">
|
|
410
|
+
{licenseError}
|
|
411
|
+
</p>
|
|
412
|
+
</div>
|
|
413
|
+
</div>
|
|
414
|
+
) : !mounted || isEmpty ? (
|
|
349
415
|
/* Welcome state */
|
|
350
416
|
<div className="h-full flex flex-col items-center justify-center text-center gap-4 px-6 py-4">
|
|
351
417
|
<div
|
|
@@ -408,7 +474,7 @@ export function ChatWindow({
|
|
|
408
474
|
)}
|
|
409
475
|
|
|
410
476
|
{/* Error */}
|
|
411
|
-
{error && (
|
|
477
|
+
{error && !licenseError && (
|
|
412
478
|
<div className="flex items-center gap-2 text-rose-500 dark:text-rose-400 text-sm bg-rose-50 dark:bg-rose-400/10 border border-rose-200 dark:border-rose-400/20 rounded-xl px-4 py-3">
|
|
413
479
|
<TriangleAlert className="w-4 h-4 flex-shrink-0" />
|
|
414
480
|
<span>{error}</span>
|
|
@@ -479,7 +545,7 @@ export function ChatWindow({
|
|
|
479
545
|
onKeyDown={handleKeyDown}
|
|
480
546
|
placeholder={ui.placeholder}
|
|
481
547
|
rows={1}
|
|
482
|
-
disabled={isLoading}
|
|
548
|
+
disabled={isLoading || !!licenseError || !!versionError}
|
|
483
549
|
className="flex-1 bg-transparent text-slate-900 dark:text-white/90 placeholder-slate-400 dark:placeholder-white/30 text-sm resize-none outline-none py-1.5 px-2 max-h-32 leading-relaxed disabled:opacity-50"
|
|
484
550
|
style={{ scrollbarWidth: 'none' }}
|
|
485
551
|
/>
|
|
@@ -488,7 +554,7 @@ export function ChatWindow({
|
|
|
488
554
|
<button
|
|
489
555
|
type="button"
|
|
490
556
|
onClick={toggleListening}
|
|
491
|
-
disabled={isLoading}
|
|
557
|
+
disabled={isLoading || !!licenseError || !!versionError}
|
|
492
558
|
className={`flex-shrink-0 w-9 h-9 rounded-xl flex items-center justify-center transition-all hover:scale-105 active:scale-95 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100 disabled:active:scale-100 ${isListening
|
|
493
559
|
? 'bg-rose-500 text-white animate-pulse shadow-md shadow-rose-500/20'
|
|
494
560
|
: 'text-slate-400 dark:text-white/40 hover:bg-slate-200 dark:hover:bg-white/10'
|
|
@@ -503,7 +569,8 @@ export function ChatWindow({
|
|
|
503
569
|
<button
|
|
504
570
|
type="button"
|
|
505
571
|
onClick={() => setIsUploadModalOpen(true)}
|
|
506
|
-
|
|
572
|
+
disabled={isLoading || !!licenseError || !!versionError}
|
|
573
|
+
className="flex-shrink-0 w-9 h-9 rounded-xl flex items-center justify-center transition-all hover:scale-105 active:scale-95 text-slate-400 dark:text-white/40 hover:bg-slate-200 dark:hover:bg-white/10 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
507
574
|
title="Upload Document"
|
|
508
575
|
>
|
|
509
576
|
<Paperclip className="w-4 h-4" />
|
|
@@ -521,14 +588,10 @@ export function ChatWindow({
|
|
|
521
588
|
) : (
|
|
522
589
|
<button
|
|
523
590
|
onClick={sendMessage}
|
|
524
|
-
disabled={!input.trim()}
|
|
525
|
-
className=
|
|
526
|
-
style={{
|
|
527
|
-
|
|
528
|
-
input.trim()
|
|
529
|
-
? `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})`
|
|
530
|
-
: 'rgba(148, 163, 184, 0.2)', // slate-400/20 for light mode disabled
|
|
531
|
-
}}
|
|
591
|
+
disabled={!input.trim() || !!licenseError || !!versionError}
|
|
592
|
+
className={`flex-shrink-0 w-9 h-9 flex items-center justify-center transition-all shadow-md active:scale-95 cursor-pointer disabled:opacity-30 disabled:cursor-not-allowed disabled:active:scale-100 ${ui.borderRadius === 'full' ? 'rounded-full' : 'rounded-xl'}`}
|
|
593
|
+
style={{ background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` }}
|
|
594
|
+
title="Send message"
|
|
532
595
|
>
|
|
533
596
|
<ArrowUp className="w-4 h-4 text-white" />
|
|
534
597
|
</button>
|