Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
304 changes: 153 additions & 151 deletions src/features/prompts/components/PromptPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -402,179 +402,181 @@ export function PromptPanel({
{hasPrompts ? `${totalCount} prompt${totalCount === 1 ? "" : "s"}` : "No prompts"}
</div>
</div>
{editor && (
<div className="prompt-editor">
<div className="prompt-editor-row">
<div className="file-tree-search">
<Search className="file-tree-search-icon" aria-hidden />
<input
className="file-tree-search-input"
type="search"
placeholder="Filter prompts"
value={query}
onChange={(event) => setQuery(event.target.value)}
aria-label="Filter prompts"
/>
</div>
<div className="prompt-panel-scroll">
{editor && (
<div className="prompt-editor">
<div className="prompt-editor-row">
<label className="prompt-editor-label">
Name
<input
className="prompt-args-input"
type="text"
value={editor.name}
onChange={(event) => updateEditor({ name: event.target.value })}
placeholder="Prompt name"
/>
</label>
<label className="prompt-editor-label">
Scope
<select
className="prompt-scope-select"
value={editor.scope}
onChange={(event) =>
updateEditor({
scope: event.target.value as PromptEditorState["scope"],
})
}
disabled={editor.mode === "edit"}
>
<option value="workspace">Workspace</option>
<option value="global">General</option>
</select>
</label>
</div>
<div className="prompt-editor-row">
<label className="prompt-editor-label">
Description
<input
className="prompt-args-input"
type="text"
value={editor.description}
onChange={(event) => updateEditor({ description: event.target.value })}
placeholder="Optional description"
/>
</label>
<label className="prompt-editor-label">
Argument hint
<input
className="prompt-args-input"
type="text"
value={editor.argumentHint}
onChange={(event) => updateEditor({ argumentHint: event.target.value })}
placeholder="Optional argument hint"
/>
</label>
</div>
<label className="prompt-editor-label">
Name
<input
className="prompt-args-input"
type="text"
value={editor.name}
onChange={(event) => updateEditor({ name: event.target.value })}
placeholder="Prompt name"
Content
<textarea
className="prompt-editor-textarea"
value={editor.content}
onChange={(event) => updateEditor({ content: event.target.value })}
placeholder="Prompt content"
rows={6}
/>
</label>
<label className="prompt-editor-label">
Scope
<select
className="prompt-scope-select"
value={editor.scope}
onChange={(event) =>
updateEditor({
scope: event.target.value as PromptEditorState["scope"],
})
}
disabled={editor.mode === "edit"}
{editorError && <div className="prompt-editor-error">{editorError}</div>}
<div className="prompt-editor-actions">
<button
type="button"
className="ghost prompt-action"
onClick={() => setEditor(null)}
disabled={isSaving}
>
<option value="workspace">Workspace</option>
<option value="global">General</option>
</select>
</label>
</div>
<div className="prompt-editor-row">
<label className="prompt-editor-label">
Description
<input
className="prompt-args-input"
type="text"
value={editor.description}
onChange={(event) => updateEditor({ description: event.target.value })}
placeholder="Optional description"
/>
</label>
<label className="prompt-editor-label">
Argument hint
<input
className="prompt-args-input"
type="text"
value={editor.argumentHint}
onChange={(event) => updateEditor({ argumentHint: event.target.value })}
placeholder="Optional argument hint"
/>
</label>
Cancel
</button>
<button
type="button"
className="ghost prompt-action"
onClick={() => void handleSave()}
disabled={isSaving}
>
{editor.mode === "create" ? "Create" : "Save"}
</button>
</div>
</div>
<label className="prompt-editor-label">
Content
<textarea
className="prompt-editor-textarea"
value={editor.content}
onChange={(event) => updateEditor({ content: event.target.value })}
placeholder="Prompt content"
rows={6}
/>
</label>
{editorError && <div className="prompt-editor-error">{editorError}</div>}
<div className="prompt-editor-actions">
)}
<div className="prompt-section">
<div className="prompt-section-header">
<div className="prompt-section-title">Workspace prompts</div>
<button
type="button"
className="ghost prompt-action"
onClick={() => setEditor(null)}
disabled={isSaving}
className="ghost icon-button prompt-section-add"
onClick={() => startCreate("workspace")}
aria-label="Add workspace prompt"
title="Add workspace prompt"
>
Cancel
<Plus aria-hidden />
</button>
</div>
{workspacePrompts.length > 0 ? (
<div className="prompt-list">
{workspacePrompts.map((prompt) => renderPromptRow(prompt))}
</div>
) : (
<div className="prompt-empty-card">
<ScrollText className="prompt-empty-icon" aria-hidden />
<div className="prompt-empty-text">
<div className="prompt-empty-title">No workspace prompts yet</div>
<div className="prompt-empty-subtitle">
Create one here or drop a .md file into the{" "}
{workspacePath ? (
<button
type="button"
className="prompt-empty-link"
onClick={() => void onRevealWorkspacePrompts()}
>
workspace prompts folder
</button>
) : (
<span className="prompt-empty-link is-disabled">
workspace prompts folder
</span>
)}
.
</div>
</div>
</div>
)}
</div>
<div className="prompt-section">
<div className="prompt-section-header">
<div className="prompt-section-title">General prompts</div>
<button
type="button"
className="ghost prompt-action"
onClick={() => void handleSave()}
disabled={isSaving}
className="ghost icon-button prompt-section-add"
onClick={() => startCreate("global")}
aria-label="Add general prompt"
title="Add general prompt"
>
{editor.mode === "create" ? "Create" : "Save"}
<Plus aria-hidden />
</button>
</div>
</div>
)}
<div className="file-tree-search">
<Search className="file-tree-search-icon" aria-hidden />
<input
className="file-tree-search-input"
type="search"
placeholder="Filter prompts"
value={query}
onChange={(event) => setQuery(event.target.value)}
aria-label="Filter prompts"
/>
</div>
<div className="prompt-section">
<div className="prompt-section-header">
<div className="prompt-section-title">Workspace prompts</div>
<button
type="button"
className="ghost icon-button prompt-section-add"
onClick={() => startCreate("workspace")}
aria-label="Add workspace prompt"
title="Add workspace prompt"
>
<Plus aria-hidden />
</button>
</div>
{workspacePrompts.length > 0 ? (
<div className="prompt-list">
{workspacePrompts.map((prompt) => renderPromptRow(prompt))}
</div>
) : (
<div className="prompt-empty-card">
<ScrollText className="prompt-empty-icon" aria-hidden />
<div className="prompt-empty-text">
<div className="prompt-empty-title">No workspace prompts yet</div>
<div className="prompt-empty-subtitle">
Create one here or drop a .md file into the{" "}
{workspacePath ? (
{globalPrompts.length > 0 ? (
<div className="prompt-list">
{globalPrompts.map((prompt) => renderPromptRow(prompt))}
</div>
) : (
<div className="prompt-empty-card">
<ScrollText className="prompt-empty-icon" aria-hidden />
<div className="prompt-empty-text">
<div className="prompt-empty-title">No general prompts yet</div>
<div className="prompt-empty-subtitle">
Create one here or drop a .md file into{" "}
<button
type="button"
className="prompt-empty-link"
onClick={() => void onRevealWorkspacePrompts()}
onClick={() => void onRevealGeneralPrompts()}
>
workspace prompts folder
~/.codex/prompts
</button>
) : (
<span className="prompt-empty-link is-disabled">
workspace prompts folder
</span>
)}
.
.
</div>
</div>
</div>
</div>
)}
</div>
<div className="prompt-section">
<div className="prompt-section-header">
<div className="prompt-section-title">General prompts</div>
<button
type="button"
className="ghost icon-button prompt-section-add"
onClick={() => startCreate("global")}
aria-label="Add general prompt"
title="Add general prompt"
>
<Plus aria-hidden />
</button>
)}
</div>
{globalPrompts.length > 0 ? (
<div className="prompt-list">
{globalPrompts.map((prompt) => renderPromptRow(prompt))}
</div>
) : (
<div className="prompt-empty-card">
<ScrollText className="prompt-empty-icon" aria-hidden />
<div className="prompt-empty-text">
<div className="prompt-empty-title">No general prompts yet</div>
<div className="prompt-empty-subtitle">
Create one here or drop a .md file into{" "}
<button
type="button"
className="prompt-empty-link"
onClick={() => void onRevealGeneralPrompts()}
>
~/.codex/prompts
</button>
.
</div>
</div>
</div>
)}
</div>
</aside>
);
Expand Down
11 changes: 11 additions & 0 deletions src/styles/prompts.css
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,17 @@
}
}

.prompt-panel-scroll {
display: flex;
flex-direction: column;
gap: 12px;
flex: 1;
min-height: 0;
overflow-y: auto;
padding-right: 2px;
padding-bottom: 12px;
}

@media (prefers-reduced-motion: reduce) {
.prompt-row,
.prompt-empty-card,
Expand Down