diff --git a/CHANGELOG.md b/CHANGELOG.md index d0f6117..324a4c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.2.8] - 2026-04-03 + +### Changed 🎨 +- **Sidebar UX**: Adăugat scrollable container independent și scrollbar minimalist pentru coloana stângă (Workspaces, Tags, Templates), prevenind scroll-ul întregii pagini pe liste lungi. + +## [1.2.7] - 2026-04-03 + +### Fixed 🛠️ +- Adăugat suport pentru extragerea textului `hint` din variabile și utilizarea acestuia ca `placeholder` dinamic în interfața grafică (`VariableInjector`). + +## [1.2.6] - 2026-04-03 + +### Fixed 🛠️ +- Imbunătățit regex-ul din `VariableInjector` și `TemplateManager` pentru a detecta impecabil variabile cu sintaxă complexă (ex: `{{VAR=hint cu caractere speciale}}`). +- Rezolvat conflictele de compilare (Vite 8 Rolldown) și fixat tipabilitățile TypeScript din zona de căutare hibridă FlexSearch. +- Fixat dependențele peer conflictuale la integrarea modulului `vite-plugin-pwa` offline. +- Adăugat suport pentru persistența datelor migratoare `prompt-history` printr-un fix minor de compatibilitate pentru hook-urile React (`useIndexedDB`). + ## [1.2.5] - 2026-03-28 ### Added 🚀 diff --git a/package.json b/package.json index 81ea5fd..4591c39 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "prompt-library", "private": false, "description": "A modern, sleek web application for managing AI prompts. Built with React, TypeScript, and Vite.", - "version": "1.2.5", + "version": "1.2.8", "author": "Ivan Andrei (NaviAndrei)", "license": "MIT", "type": "module", diff --git a/src/components/TemplateManager.tsx b/src/components/TemplateManager.tsx index 71caf03..7661294 100644 --- a/src/components/TemplateManager.tsx +++ b/src/components/TemplateManager.tsx @@ -45,8 +45,8 @@ export function TemplateManager({ onSelectTemplate }: TemplateManagerProps) { return; } - // Simple regex to extract variables from the template body - const variables = Array.from(form.templateBody.matchAll(/\{\{(\w+)(?::\w+)?(?::[^}]+)?\}\}/g)).map(m => m[1]); + // Permissive regex to extract variables from the template body (supporting {{var}}, {{var=hint}}, {{var:type}}) + const variables = Array.from(form.templateBody.matchAll(/\{\{([\w\s-]+)(?:[:=][^}]+)?\}\}/g)).map(m => m[1].trim()); const newTemplate: PromptTemplate = { id: crypto.randomUUID(), diff --git a/src/components/VariableInjector.tsx b/src/components/VariableInjector.tsx index b92c2e2..abc35d3 100644 --- a/src/components/VariableInjector.tsx +++ b/src/components/VariableInjector.tsx @@ -11,6 +11,7 @@ interface VariableInfo { name: string; type: string; raw: string; + hint?: string; } interface VariablePreset { @@ -34,22 +35,36 @@ const TYPE_MAP: Record = { // Detects all unique variables of type {{name:type:options}} from text function extractVariables(text: string): (VariableInfo & { options?: string[] })[] { - const regex = /\{\{(\w+)(?::(\w+))?(?::([^}]+))?\}\}/g; + // Permissive regex to support {{var}}, {{var:type}}, {{var:select:a,b}}, and {{var=hint}} + const regex = /\{\{([\w\s-]+)(?:[:=]([^}]+))?\}\}/g; const seen = new Set(); const variables: (VariableInfo & { options?: string[] })[] = []; let match; while ((match = regex.exec(text)) !== null) { - const name = match[1]; - const typeHint = (match[2] || 'text').toLowerCase(); - const optionsRaw = match[3]; + const name = match[1].trim(); + const fullHint = (match[2] || '').trim(); if (!seen.has(name)) { seen.add(name); + + // Extract type and options if colon syntax is used + let typeHint = 'text'; + let optionsRaw = ''; + + if (fullHint.includes(':')) { + const parts = fullHint.split(':'); + typeHint = parts[0].toLowerCase(); + optionsRaw = parts.slice(1).join(':'); + } else if (fullHint && !fullHint.includes('=') && TYPE_MAP[fullHint.toLowerCase()]) { + // If it's a recognized type (e.g. {{date:date}}), use it + typeHint = fullHint.toLowerCase(); + } const info: VariableInfo & { options?: string[] } = { name, type: TYPE_MAP[typeHint] || 'text', raw: match[0], + hint: fullHint && !TYPE_MAP[fullHint.toLowerCase()] && !fullHint.includes(':') ? fullHint : undefined, }; if (typeHint === 'select' && optionsRaw) { @@ -62,10 +77,13 @@ function extractVariables(text: string): (VariableInfo & { options?: string[] }) return variables; } -// Replaces all occurrences of {{variable}} or {{variable:type}} with the corresponding value function injectVariables(text: string, values: Record): string { - return text.replace(/\{\{(\w+)(?::(\w+))?(?::([^}]+))?\}\}/g, (_, name) => { - return values[name] !== undefined && values[name] !== '' ? values[name] : `{{${name}}}`; + const regex = /\{\{([\w\s-]+)(?:[:=]([^}]+))?\}\}/g; + return text.replace(regex, (fullMatch, name) => { + const trimmedName = name.trim(); + return values[trimmedName] !== undefined && values[trimmedName] !== '' + ? values[trimmedName] + : fullMatch; }); } @@ -187,7 +205,7 @@ export function VariableInjector({ body }: VariableInjectorProps) { handleChange(v.name, e.target.value)} aria-label={`Enter value for variable ${v.name}`} diff --git a/src/index.css b/src/index.css index a85d41b..569b7dd 100644 --- a/src/index.css +++ b/src/index.css @@ -299,7 +299,23 @@ button { display: flex; flex-direction: column; gap: 1.5rem; - min-height: calc(100vh - 4rem); + max-height: calc(100vh - 4rem); + overflow-y: auto; +} + +/* Custom minimal scrollbar for sidebar */ +.sidebar::-webkit-scrollbar { + width: 6px; +} +.sidebar::-webkit-scrollbar-track { + background: transparent; +} +.sidebar::-webkit-scrollbar-thumb { + background-color: var(--border-color); + border-radius: var(--radius-md); +} +.sidebar::-webkit-scrollbar-thumb:hover { + background-color: var(--text-muted); } .sidebar-header {