UNPKG

ai-palette

Version:

AI-powered color palette generator for React applications

855 lines (834 loc) 39 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var React = require('react'); var ColorThief = require('colorthief'); var axios = require('axios'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var React__default = /*#__PURE__*/_interopDefaultLegacy(React); var ColorThief__default = /*#__PURE__*/_interopDefaultLegacy(ColorThief); var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios); /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; var rgbToHex$1 = function (r, g, b) { var toHex = function (n) { var hex = n.toString(16); return hex.length === 1 ? "0" + hex : hex; }; return "#".concat(toHex(r)).concat(toHex(g)).concat(toHex(b)); }; var rgbToHsl = function (r, g, b) { r /= 255; g /= 255; b /= 255; var max = Math.max(r, g, b); var min = Math.min(r, g, b); var h = 0; var s = 0; var l = (max + min) / 2; if (max !== min) { var d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); switch (max) { case r: h = (g - b) / d + (g < b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; case b: h = (r - g) / d + 4; break; } h /= 6; } return { h: Math.round(h * 360), s: Math.round(s * 100), l: Math.round(l * 100), }; }; // Önceden tanımlanmış tema renkleri var themeColors = { // Doğa sunset: { h: 25, s: 85, l: 55 }, sunrise: { h: 45, s: 90, l: 60 }, ocean: { h: 200, s: 85, l: 45 }, forest: { h: 120, s: 70, l: 35 }, sky: { h: 210, s: 80, l: 70 }, grass: { h: 95, s: 75, l: 45 }, sand: { h: 45, s: 50, l: 85 }, // Mevsimler spring: { h: 95, s: 80, l: 60 }, summer: { h: 45, s: 95, l: 60 }, autumn: { h: 25, s: 90, l: 45 }, winter: { h: 210, s: 50, l: 85 }, // Duygular happy: { h: 45, s: 95, l: 65 }, calm: { h: 180, s: 50, l: 75 }, energetic: { h: 0, s: 90, l: 60 }, peaceful: { h: 150, s: 40, l: 80 }, romantic: { h: 340, s: 80, l: 70 }, // Zaman morning: { h: 45, s: 85, l: 70 }, noon: { h: 200, s: 80, l: 70 }, evening: { h: 25, s: 80, l: 50 }, night: { h: 240, s: 70, l: 25 }, // Mekanlar beach: { h: 35, s: 85, l: 80 }, mountain: { h: 200, s: 30, l: 40 }, desert: { h: 35, s: 70, l: 60 }, tropical: { h: 150, s: 90, l: 45 }, urban: { h: 220, s: 30, l: 45 }, // Sıcaklık warm: { h: 25, s: 90, l: 55 }, cold: { h: 200, s: 85, l: 60 }, hot: { h: 0, s: 95, l: 50 }, cool: { h: 180, s: 60, l: 75 }, // Serin turkuaz }; // HSL renk uzayında başlangıç rengi oluştur var generateBaseColor = function (keyword) { // Keyword'u küçük harfe çevir ve boşlukları kaldır var cleanKeyword = keyword.toLowerCase().trim(); // Önceden tanımlı temalardan eşleşen var mı diye kontrol et for (var _i = 0, _a = Object.entries(themeColors); _i < _a.length; _i++) { var _b = _a[_i], theme = _b[0], color = _b[1]; if (cleanKeyword.includes(theme)) { return color; } } // Eşleşme yoksa varsayılan bir renk üret var hash = cleanKeyword.split("").reduce(function (acc, char) { return char.charCodeAt(0) + ((acc << 5) - acc); }, 0); return { h: Math.abs(hash % 360), s: 70 + (hash % 20), l: 45 + (hash % 20), }; }; // ChatGPT'den renk önerileri al var getColorSuggestionsFromGPT = function (keyword, count, apiKey) { return __awaiter(void 0, void 0, void 0, function () { var response, suggestions, error_1; var _a, _b, _c, _d; return __generator(this, function (_e) { switch (_e.label) { case 0: _e.trys.push([0, 2, , 3]); if (!apiKey) { throw new Error("OpenAI API key is required for AI mode. Please provide an API key in the PaletteProvider config or switch to basic mode"); } return [4 /*yield*/, axios__default["default"].post("https://api.openai.com/v1/chat/completions", { model: "gpt-3.5-turbo", messages: [ { role: "system", content: "You are a color palette generator. Generate colors that match the given theme or keyword. Return only a JSON array of colors with hex codes and descriptions.", }, { role: "user", content: "Generate ".concat(count, " colors that represent \"").concat(keyword, "\". For each color, provide a hex code and a brief description of why it fits the theme. Return as JSON array like this: [{\"hex\": \"#XXXXXX\", \"description\": \"explanation\"}]"), }, ], temperature: 0.7, }, { headers: { "Content-Type": "application/json", Authorization: "Bearer ".concat(apiKey), }, })]; case 1: response = _e.sent(); suggestions = JSON.parse(response.data.choices[0].message.content); return [2 /*return*/, suggestions]; case 2: error_1 = _e.sent(); console.error("Error getting color suggestions:", ((_a = error_1.response) === null || _a === void 0 ? void 0 : _a.data) || error_1.message); throw new Error(((_d = (_c = (_b = error_1.response) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.error) === null || _d === void 0 ? void 0 : _d.message) || "Failed to get color suggestions from GPT"); case 3: return [2 /*return*/]; } }); }); }; // HSL'den RGB'ye dönüşüm var hslToRgb = function (h, s, l) { s /= 100; l /= 100; var k = function (n) { return (n + h / 30) % 12; }; var a = s * Math.min(l, 1 - l); var f = function (n) { return l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1))); }; return { r: Math.round(255 * f(0)), g: Math.round(255 * f(8)), b: Math.round(255 * f(4)), }; }; // Hex'ten HSL'ye dönüşüm var hexToHsl = function (hex) { // Hex'i RGB'ye çevir var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); if (!result) { throw new Error("Invalid hex color: ".concat(hex)); } var r = parseInt(result[1], 16) / 255; var g = parseInt(result[2], 16) / 255; var b = parseInt(result[3], 16) / 255; var max = Math.max(r, g, b); var min = Math.min(r, g, b); var h = 0; var s = 0; var l = (max + min) / 2; if (max !== min) { var d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); switch (max) { case r: h = (g - b) / d + (g < b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; case b: h = (r - g) / d + 4; break; } h /= 6; } return { h: Math.round(h * 360), s: Math.round(s * 100), l: Math.round(l * 100), }; }; // Renk harmonisi oluştur var generateHarmony = function (baseHsl, type, count) { var colors = []; var h = baseHsl.h, s = baseHsl.s, l = baseHsl.l; // Renk çarkında daha uyumlu geçişler için yardımcı fonksiyon var adjustHarmonyColor = function (hue, saturation, lightness) { // Doygunluğu ana renge yakın tut var adjustedS = Math.max(30, Math.min(90, saturation)); // Parlaklığı dengeli tut var adjustedL = Math.max(20, Math.min(80, lightness)); return { h: ((hue % 360) + 360) % 360, s: adjustedS, l: adjustedL, }; }; switch (type) { case "analogous": // Ana rengin her iki yanından yakın renkler var analogStep = 30; // 30 derece aralıklarla for (var i = 0; i < count; i++) { var step = (i - Math.floor(count / 2)) * analogStep; colors.push(adjustHarmonyColor(h + step, s - Math.abs(step) / 10, // Merkeze yakın renkler daha doygun l + (i % 2 === 0 ? 5 : -5) // Alternatif olarak biraz daha açık/koyu )); } break; case "complementary": // Ana renk ve karşıt renk, artı ara geçiş renkleri colors.push(adjustHarmonyColor(h, s, l)); // Ana renk if (count >= 2) { colors.push(adjustHarmonyColor(h + 180, s - 10, l)); // Karşıt renk } // Ara geçiş renkleri var compStep = 180 / (count - 1); for (var i = 2; i < count; i++) { var step = (i - 1) * compStep; colors.push(adjustHarmonyColor(h + step, s - 15, // Ara renkler biraz daha soft l + (i % 2 === 0 ? 10 : -10))); } break; case "triadic": // 120 derece aralıklarla üç ana renk ve ara tonlar var triadicBase = [0, 120, 240]; for (var i = 0; i < count; i++) { var baseIndex = Math.floor(i / Math.ceil(count / 3)); var hue = h + triadicBase[baseIndex % 3]; colors.push(adjustHarmonyColor(hue, s - baseIndex * 5, // Her grup biraz daha soft l + ((i % 2) * 10 - 5) // Alternatif açık/koyu )); } break; case "tetradic": // 90 derece aralıklarla dört ana renk ve ara tonlar var tetradicBase = [0, 90, 180, 270]; for (var i = 0; i < count; i++) { var baseIndex = Math.floor(i / Math.ceil(count / 4)); var hue = h + tetradicBase[baseIndex % 4]; colors.push(adjustHarmonyColor(hue, s - baseIndex * 5, l + ((i % 2) * 10 - 5))); } break; case "monochromatic": // Aynı rengin farklı ton ve doygunlukları for (var i = 0; i < count; i++) { var step = i / (count - 1); // 0 ile 1 arası colors.push({ h: h, s: Math.max(30, Math.min(90, s - step * 20)), l: 25 + step * 50, // Koyudan açığa doğru (25-75 arası) }); } break; default: // Varsayılan olarak dengeli bir renk dağılımı var defaultStep = 360 / count; for (var i = 0; i < count; i++) { colors.push(adjustHarmonyColor(h + i * defaultStep, s - Math.abs(i - count / 2) * 5, // Ortadaki renkler daha doygun l + (i % 2 === 0 ? 5 : -5))); } } // Son kontrol ve yuvarlama return colors.map(function (color) { return ({ h: Math.round(color.h), s: Math.round(color.s), l: Math.round(color.l), }); }); }; // Basic modda renk önerileri oluştur var getBasicColorSuggestions = function (keyword, count, type) { if (type === void 0) { type = "default"; } var baseColor = generateBaseColor(keyword); var hslColors = generateHarmony(baseColor, type, count); return hslColors.map(function (hsl, index) { var rgb = hslToRgb(hsl.h, hsl.s, hsl.l); var hex = rgbToHex(rgb.r, rgb.g, rgb.b); var description = ""; var colorTemperature = hsl.h >= 0 && hsl.h < 60 ? "warm" : hsl.h >= 60 && hsl.h < 180 ? "cool" : hsl.h >= 180 && hsl.h < 300 ? "cool" : "warm"; var intensity = hsl.s > 70 ? "vibrant" : hsl.s > 40 ? "balanced" : "subtle"; var brightness = hsl.l > 70 ? "light" : hsl.l > 30 ? "medium" : "dark"; switch (type) { case "analogous": description = index === 0 ? "A ".concat(intensity, " ").concat(colorTemperature, " ").concat(brightness, " tone that serves as the foundation of the ").concat(keyword, " theme") : "A harmonious ".concat(intensity, " ").concat(colorTemperature, " ").concat(brightness, " shade that complements the base color, creating a smooth transition in the ").concat(keyword, " palette"); break; case "complementary": description = index === 0 ? "The primary ".concat(intensity, " ").concat(colorTemperature, " ").concat(brightness, " color that embodies the essence of ").concat(keyword) : "A contrasting ".concat(intensity, " ").concat(colorTemperature, " ").concat(brightness, " tone that creates a striking balance with the primary color"); break; case "monochromatic": description = "A ".concat(brightness, " ").concat(intensity, " variation that explores the depth of the ").concat(keyword, " theme through subtle changes in brightness and saturation"); break; case "triadic": description = "A ".concat(intensity, " ").concat(colorTemperature, " ").concat(brightness, " hue that forms part of a balanced three-color harmony, adding dynamic energy to the ").concat(keyword, " palette"); break; case "tetradic": description = "A ".concat(intensity, " ").concat(colorTemperature, " ").concat(brightness, " tone that contributes to a rich and complex four-color harmony in the ").concat(keyword, " theme"); break; default: description = "A ".concat(intensity, " ").concat(colorTemperature, " ").concat(brightness, " color that captures the essence of ").concat(keyword); } return { hex: hex, description: description }; }); }; var generatePaletteFromKeyword = function (keyword, options, apiKey) { if (options === void 0) { options = {}; } return __awaiter(void 0, void 0, void 0, function () { var count, mode, type, suggestions, _a, colors, error_2; return __generator(this, function (_b) { switch (_b.label) { case 0: count = options.count || 5; mode = options.mode || "basic"; type = options.type || "default"; _b.label = 1; case 1: _b.trys.push([1, 5, , 6]); if (!(mode === "ai")) return [3 /*break*/, 3]; return [4 /*yield*/, getColorSuggestionsFromGPT(keyword, count, apiKey)]; case 2: _a = _b.sent(); return [3 /*break*/, 4]; case 3: _a = getBasicColorSuggestions(keyword, count, type); _b.label = 4; case 4: suggestions = _a; colors = suggestions.map(function (suggestion) { var hsl = hexToHsl(suggestion.hex); var rgb = hslToRgb(hsl.h, hsl.s, hsl.l); return { hex: suggestion.hex, rgb: rgb, hsl: hsl, description: suggestion.description, }; }); return [2 /*return*/, { colors: colors, name: "".concat(keyword, " palette"), description: "".concat(mode === "ai" ? "AI" : "Basic", " generated color palette based on \"").concat(keyword, "\"").concat(type !== "default" ? " with ".concat(type, " harmony") : ""), }]; case 5: error_2 = _b.sent(); console.error("Error generating palette:", error_2); throw error_2; case 6: return [2 /*return*/]; } }); }); }; var rgbToHex = function (r, g, b) { var toHex = function (n) { var hex = n.toString(16); return hex.length === 1 ? "0" + hex : hex; }; return "#".concat(toHex(r)).concat(toHex(g)).concat(toHex(b)); }; var ValidationError = /** @class */ (function (_super) { __extends(ValidationError, _super); function ValidationError(message) { var _this = _super.call(this, message) || this; _this.name = "ValidationError"; return _this; } return ValidationError; }(Error)); var validateProps = function (props) { if (props.count !== undefined) { if (props.count < 1 || props.count > 10) { throw new ValidationError("Color count must be between 1 and 10"); } if (!Number.isInteger(props.count)) { throw new ValidationError("Color count must be an integer"); } } if (props.keyword !== undefined) { if (typeof props.keyword !== "string") { throw new ValidationError("Keyword must be a string"); } if (props.keyword.trim().length === 0) { throw new ValidationError("Keyword cannot be empty"); } if (props.keyword.length > 100) { throw new ValidationError("Keyword is too long (max 100 characters)"); } } if (props.image !== undefined) { if (props.image instanceof File) { if (!props.image.type.startsWith("image/")) { throw new ValidationError("File must be an image"); } if (props.image.size > 5 * 1024 * 1024) { // 5MB limit throw new ValidationError("Image size must be less than 5MB"); } } else if (typeof props.image === "string") { try { new URL(props.image); } catch (_a) { throw new ValidationError("Invalid image URL"); } } else { throw new ValidationError("Image must be a File object or URL string"); } } if (props.mode !== undefined && !["ai", "basic"].includes(props.mode)) { throw new ValidationError("Invalid generation mode"); } if (props.format !== undefined && !["hex", "rgb", "hsl"].includes(props.format)) { throw new ValidationError("Invalid color format"); } var validTypes = [ "analogous", "complementary", "triadic", "tetradic", "monochromatic", ]; if (props.type !== undefined && !validTypes.includes(props.type)) { throw new ValidationError("Invalid harmony type"); } }; var defaultConfig = { maxColors: 10, defaultFormat: "hex", defaultHarmony: "complementary", cacheEnabled: true, cacheDuration: 5 * 60 * 1000, // 5 minutes }; var PaletteContext = React.createContext({ config: defaultConfig, }); var PaletteProvider = function (_a) { var _b = _a.config, config = _b === void 0 ? {} : _b, children = _a.children; var mergedConfig = __assign(__assign({}, defaultConfig), config); return (React__default["default"].createElement(PaletteContext.Provider, { value: { config: mergedConfig } }, children)); }; var usePaletteConfig = function () { var context = React.useContext(PaletteContext); if (!context) { throw new Error("usePaletteConfig must be used within a PaletteProvider"); } return context.config; }; var CACHE_DURATION = 5 * 60 * 1000; // 5 minutes var cache = new Map(); var useAIPalette = function (options) { if (options === void 0) { options = {}; } var apiKey = usePaletteConfig().apiKey; var _a = React.useState(null), palette = _a[0], setPalette = _a[1]; var _b = React.useState(false), loading = _b[0], setLoading = _b[1]; var _c = React.useState(null), error = _c[0], setError = _c[1]; var abortControllerRef = React.useRef(null); var getCacheKey = React.useCallback(function (opts) { return JSON.stringify({ keyword: opts.keyword, count: opts.count, format: opts.format, type: opts.type, mode: opts.mode, }); }, []); var clearCache = React.useCallback(function () { var now = Date.now(); for (var _i = 0, _a = Array.from(cache.entries()); _i < _a.length; _i++) { var _b = _a[_i], key = _b[0], entry = _b[1]; if (now - entry.timestamp > CACHE_DURATION) { cache.delete(key); } } }, []); var generateFromImage = React.useCallback(function (imageFile) { return __awaiter(void 0, void 0, void 0, function () { var colorThief, image_1, colorPalette, colors, err_1; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); setLoading(true); setError(null); colorThief = new ColorThief__default["default"](); image_1 = new Image(); if (typeof imageFile === "string") { image_1.src = imageFile; } else { image_1.src = URL.createObjectURL(imageFile); } return [4 /*yield*/, new Promise(function (resolve) { image_1.onload = resolve; })]; case 1: _a.sent(); colorPalette = colorThief.getPalette(image_1, options.count || 5); colors = colorPalette.map(function (rgb) { var r = rgb[0], g = rgb[1], b = rgb[2]; var hex = rgbToHex$1(r, g, b); var hsl = rgbToHsl(r, g, b); return { hex: hex, rgb: { r: r, g: g, b: b }, hsl: hsl }; }); setPalette({ colors: colors, name: "Image-based palette", description: "Color palette generated from uploaded image", }); setLoading(false); return [3 /*break*/, 3]; case 2: err_1 = _a.sent(); setError(err_1 instanceof Error ? err_1 : new Error("Failed to generate palette from image")); setLoading(false); return [3 /*break*/, 3]; case 3: return [2 /*return*/]; } }); }); }, [options.count]); var generateFromKeyword = React.useCallback(function (keyword) { return __awaiter(void 0, void 0, void 0, function () { var cacheKey, cachedEntry, newPalette, err_2; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, 3, 4]); setError(null); setLoading(true); // Validate props validateProps(__assign({ keyword: keyword }, options)); cacheKey = getCacheKey(__assign({ keyword: keyword }, options)); cachedEntry = cache.get(cacheKey); if (cachedEntry && Date.now() - cachedEntry.timestamp < CACHE_DURATION) { setPalette(cachedEntry.palette); return [2 /*return*/]; } // Cancel previous request if exists if (abortControllerRef.current) { abortControllerRef.current.abort(); } // Create new abort controller abortControllerRef.current = new AbortController(); return [4 /*yield*/, generatePaletteFromKeyword(keyword, { count: options.count, format: options.format, type: options.type, mode: options.mode, }, options.mode === "ai" ? apiKey : undefined)]; case 1: newPalette = _a.sent(); // Cache the result cache.set(cacheKey, { palette: newPalette, timestamp: Date.now(), options: cacheKey, }); setPalette(newPalette); clearCache(); // Clean old cache entries return [3 /*break*/, 4]; case 2: err_2 = _a.sent(); if (err_2 instanceof ValidationError) { setError(err_2); } else if (err_2 instanceof Error) { setError(new Error("Failed to generate palette: ".concat(err_2.message))); } else { setError(new Error("An unknown error occurred")); } setPalette(null); return [3 /*break*/, 4]; case 3: setLoading(false); abortControllerRef.current = null; return [7 /*endfinally*/]; case 4: return [2 /*return*/]; } }); }); }, [options, apiKey, getCacheKey, clearCache]); React.useEffect(function () { if (options.keyword && options.autoGenerate) { generateFromKeyword(options.keyword); } return function () { if (abortControllerRef.current) { abortControllerRef.current.abort(); } }; }, [options.keyword, options.autoGenerate, generateFromKeyword]); return { palette: palette, loading: loading, error: error, generateFromImage: generateFromImage, generateFromKeyword: generateFromKeyword, }; }; var AIPalette = function (_a) { var keyword = _a.keyword; _a.image; var _b = _a.count, count = _b === void 0 ? 5 : _b, _c = _a.format, format = _c === void 0 ? "hex" : _c, type = _a.type, _d = _a.mode, mode = _d === void 0 ? "basic" : _d, onGenerate = _a.onGenerate, onError = _a.onError; var _e = useAIPalette({ count: count, format: format, type: type, mode: mode, }), palette = _e.palette, loading = _e.loading, error = _e.error, generateFromImage = _e.generateFromImage, generateFromKeyword = _e.generateFromKeyword; var fileInputRef = React.useRef(null); var _f = React.useState(null), copiedColor = _f[0], setCopiedColor = _f[1]; var handleImageUpload = React.useCallback(function (event) { var _a; var file = (_a = event.target.files) === null || _a === void 0 ? void 0 : _a[0]; if (file) { generateFromImage(file); } }, [generateFromImage]); var handleKeywordSubmit = React.useCallback(function (event) { event.preventDefault(); if (keyword) { generateFromKeyword(keyword); } }, [keyword, generateFromKeyword]); var copyToClipboard = function (text) { navigator.clipboard.writeText(text).then(function () { setCopiedColor(text); setTimeout(function () { return setCopiedColor(null); }, 2000); // Reset after 2 seconds }); }; React__default["default"].useEffect(function () { if (error && onError) { onError(error); } }, [error, onError]); React__default["default"].useEffect(function () { if (palette && onGenerate) { onGenerate(palette); } }, [palette, onGenerate]); var renderColor = function (color, index) { var getColorString = function () { switch (format) { case "rgb": return "RGB(".concat(color.rgb.r, ", ").concat(color.rgb.g, ", ").concat(color.rgb.b, ")"); case "hsl": return "HSL(".concat(color.hsl.h, ", ").concat(color.hsl.s, "%, ").concat(color.hsl.l, "%)"); case "hex": default: return color.hex; } }; var colorString = getColorString(); var isCopied = copiedColor === colorString; return (React__default["default"].createElement("div", { key: index, className: "color-item", style: { backgroundColor: color.hex, width: "200px", minHeight: "200px", height: "auto", margin: "5px", borderRadius: "8px", display: "flex", flexDirection: "column", justifyContent: "space-between", alignItems: "center", color: color.hsl.l > 50 ? "#000" : "#fff", padding: "16px", textAlign: "center", cursor: "pointer", position: "relative", transition: "all 0.2s ease", overflow: "visible", }, onClick: function () { return copyToClipboard(colorString); }, title: "Click to copy color code" }, isCopied && (React__default["default"].createElement("div", { style: { position: "absolute", top: "50%", left: "50%", transform: "translate(-50%, -50%)", background: "rgba(0,0,0,0.7)", color: "#fff", padding: "8px 16px", borderRadius: "4px", fontSize: "0.9em", fontWeight: "500", zIndex: 2, animation: "fadeIn 0.2s ease", } }, "Copied!")), React__default["default"].createElement("span", { style: { fontFamily: "monospace", fontSize: "0.9em", fontWeight: "500", padding: "4px 8px", background: "rgba(0,0,0,0.1)", borderRadius: "4px", marginBottom: "16px", width: "auto", display: "inline-block", } }, colorString), color.description && (React__default["default"].createElement("span", { style: { fontSize: "0.85em", lineHeight: "1.4", opacity: 0.9, padding: "8px 12px", background: "rgba(0,0,0,0.1)", borderRadius: "4px", width: "85%", wordBreak: "normal", whiteSpace: "normal", marginTop: "auto", } }, color.description)))); }; return (React__default["default"].createElement("div", { className: "ai-palette" }, React__default["default"].createElement("style", null, "\n @keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n .color-item:hover {\n transform: translateY(-2px);\n box-shadow: 0 4px 12px rgba(0,0,0,0.1);\n }\n .palette {\n display: flex;\n flex-wrap: wrap;\n gap: 16px;\n justify-content: center;\n padding: 16px;\n }\n "), React__default["default"].createElement("div", { className: "controls" }, keyword && (React__default["default"].createElement("form", { onSubmit: handleKeywordSubmit }, React__default["default"].createElement("button", { type: "submit", disabled: loading }, "Generate from \"", keyword, "\""))), React__default["default"].createElement("input", { type: "file", accept: "image/*", onChange: handleImageUpload, ref: fileInputRef, style: { display: "none" } }), React__default["default"].createElement("button", { onClick: function () { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, disabled: loading }, "Upload Image")), loading && React__default["default"].createElement("div", { className: "loading" }, "Generating palette..."), error && React__default["default"].createElement("div", { className: "error" }, error.message), palette && (React__default["default"].createElement("div", { className: "palette", style: { display: "flex", flexWrap: "wrap", gap: "10px" } }, palette.colors.map(function (color, index) { return renderColor(color, index); }))))); }; exports.AIPalette = AIPalette; exports.PaletteProvider = PaletteProvider; //# sourceMappingURL=index.js.map