feat: Implement Prompt Builder functionality in Popup and Options
Some checks failed
CI — Test & Build / Test & Build (push) Failing after 39s
Some checks failed
CI — Test & Build / Test & Build (push) Failing after 39s
- Added a new "Prompt Builder" tab in the Popup for generating AI prompts with customizable parameters. - Introduced new state variables for managing prompt styles, personas, formats, and models. - Enhanced the Options page to fetch and display models based on the provided API key. - Updated the actions and types to include the new 'prompt' action and its associated parameters. - Implemented migration logic for legacy plaintext API keys to encrypted storage. - Updated the getSystemPrompt function to incorporate prompt parameters for better instruction generation. - Added tests for the new functionality, including context menu entries and prompt generation logic.
This commit is contained in:
@@ -318,54 +318,7 @@ function OptionsPage() {
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Model — fetched live from the selected provider */}
|
||||
<div style={styles.formGroup}>
|
||||
<label style={styles.label}>Model</label>
|
||||
<div style={{ display: 'flex', gap: '8px' }}>
|
||||
<select
|
||||
style={{ ...styles.select, flex: 1, opacity: models.length === 0 ? 0.6 : 1 }}
|
||||
value={model}
|
||||
onChange={(e) => setModel(e.target.value)}
|
||||
disabled={modelsStatus === 'loading' || models.length === 0}
|
||||
>
|
||||
{modelsStatus === 'loading' && <option value="">Loading models…</option>}
|
||||
{modelsStatus !== 'loading' && models.length === 0 && (
|
||||
<option value="">
|
||||
{modelsStatus === 'error' ? 'Failed to load — see below' : 'Load models to choose'}
|
||||
</option>
|
||||
)}
|
||||
{models.length > 0 && <option value="">— Select a model —</option>}
|
||||
{models.map((m) => (
|
||||
<option key={m} value={m}>{m}</option>
|
||||
))}
|
||||
</select>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => loadModels()}
|
||||
disabled={modelsStatus === 'loading'}
|
||||
title="Fetch the current model list from your provider"
|
||||
style={{
|
||||
background: 'rgba(137,180,250,0.15)',
|
||||
color: '#89b4fa',
|
||||
border: '1px solid rgba(137,180,250,0.3)',
|
||||
borderRadius: '9px',
|
||||
padding: '0 12px',
|
||||
fontSize: '13px',
|
||||
fontWeight: 600,
|
||||
cursor: modelsStatus === 'loading' ? 'not-allowed' : 'pointer',
|
||||
whiteSpace: 'nowrap',
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
{modelsStatus === 'loading' ? '⏳' : '↻ Load'}
|
||||
</button>
|
||||
</div>
|
||||
{modelsStatus === 'error' && modelsError && (
|
||||
<div style={{ ...styles.hint, color: '#f38ba8' }}>⚠ {modelsError}</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* API Key */}
|
||||
{/* API Key — comes before Model: the live model list is fetched with this key */}
|
||||
<div style={styles.formGroup}>
|
||||
<label style={styles.label}>
|
||||
API Key{' '}
|
||||
@@ -424,6 +377,53 @@ function OptionsPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Model — fetched live from the selected provider using the key above */}
|
||||
<div style={styles.formGroup}>
|
||||
<label style={styles.label}>Model</label>
|
||||
<div style={{ display: 'flex', gap: '8px' }}>
|
||||
<select
|
||||
style={{ ...styles.select, flex: 1, opacity: models.length === 0 ? 0.6 : 1 }}
|
||||
value={model}
|
||||
onChange={(e) => setModel(e.target.value)}
|
||||
disabled={modelsStatus === 'loading' || models.length === 0}
|
||||
>
|
||||
{modelsStatus === 'loading' && <option value="">Loading models…</option>}
|
||||
{modelsStatus !== 'loading' && models.length === 0 && (
|
||||
<option value="">
|
||||
{modelsStatus === 'error' ? 'Failed to load — see below' : 'Load models to choose'}
|
||||
</option>
|
||||
)}
|
||||
{models.length > 0 && <option value="">— Select a model —</option>}
|
||||
{models.map((m) => (
|
||||
<option key={m} value={m}>{m}</option>
|
||||
))}
|
||||
</select>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => loadModels()}
|
||||
disabled={modelsStatus === 'loading'}
|
||||
title="Fetch the current model list from your provider"
|
||||
style={{
|
||||
background: 'rgba(137,180,250,0.15)',
|
||||
color: '#89b4fa',
|
||||
border: '1px solid rgba(137,180,250,0.3)',
|
||||
borderRadius: '9px',
|
||||
padding: '0 12px',
|
||||
fontSize: '13px',
|
||||
fontWeight: 600,
|
||||
cursor: modelsStatus === 'loading' ? 'not-allowed' : 'pointer',
|
||||
whiteSpace: 'nowrap',
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
{modelsStatus === 'loading' ? '⏳' : '↻ Load'}
|
||||
</button>
|
||||
</div>
|
||||
{modelsStatus === 'error' && modelsError && (
|
||||
<div style={{ ...styles.hint, color: '#f38ba8' }}>⚠ {modelsError}</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Save */}
|
||||
<button style={saveBtnStyle} onClick={handleSave} disabled={saveStatus === 'saving'}>
|
||||
{saveStatus === 'saving'
|
||||
@@ -442,7 +442,7 @@ function OptionsPage() {
|
||||
<strong style={{ color: '#a6adc8' }}>How to use LexAI:</strong>
|
||||
<ol style={{ margin: '8px 0 0 16px', padding: 0 }}>
|
||||
<li>Select any text on a webpage</li>
|
||||
<li>Click Fix, Rephrase, Shorten, Expand, or Explain</li>
|
||||
<li>Click Fix, Rephrase, Shorten, Expand, Explain, or Prompt (turns your text into an engineered AI prompt)</li>
|
||||
<li>Accept or replace the suggestion</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user