feat: Implement Prompt Builder functionality in Popup and Options
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:
john kevin asprec
2026-07-15 15:27:41 +08:00
parent 0fef9848cb
commit acea99d7ad
40 changed files with 1971 additions and 177 deletions

View File

@@ -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>