feat: fully dynamic model picker in inline prompt
This commit is contained in:
@@ -1,22 +1,30 @@
|
||||
/**
|
||||
* Inline prompt widget attached to a cell's DOM when the AI button is clicked.
|
||||
* Renders a textarea + send/cancel buttons. The owning cell is passed so
|
||||
* we can resolve the cell context at submit time. submit/cancel are
|
||||
* callbacks owned by OpenCodeCellActions.
|
||||
* Renders a model selector, a textarea, and send/cancel buttons. The owning
|
||||
* cell is passed so we can resolve the cell context at submit time. submit
|
||||
* and cancel are callbacks owned by OpenCodeCellActions.
|
||||
*
|
||||
* v3-final: the model selector is fully dynamic — it lists every
|
||||
* (provider, model) pair from the providers cache and defaults to the first
|
||||
* option. No settings-based default.
|
||||
*/
|
||||
import { CodeCell } from '@jupyterlab/cells';
|
||||
import { Widget } from '@lumino/widgets';
|
||||
|
||||
import type { OpenCodeProvidersResponse } from '../types';
|
||||
|
||||
export interface IOpenCodeInlinePromptOptions {
|
||||
onSubmit: (text: string) => void;
|
||||
onSubmit: (text: string, providerId?: string, modelId?: string) => void;
|
||||
onCancel: () => void;
|
||||
disabled: boolean;
|
||||
providers: OpenCodeProvidersResponse | null;
|
||||
}
|
||||
|
||||
export class OpenCodeInlinePrompt extends Widget {
|
||||
private _textarea: HTMLTextAreaElement;
|
||||
private _sendBtn: HTMLButtonElement;
|
||||
private _cancelBtn: HTMLButtonElement;
|
||||
private _select: HTMLSelectElement | null = null;
|
||||
|
||||
constructor(
|
||||
_cell: CodeCell,
|
||||
@@ -24,6 +32,21 @@ export class OpenCodeInlinePrompt extends Widget {
|
||||
) {
|
||||
super();
|
||||
this.addClass('opencode-inline-prompt');
|
||||
|
||||
const flat = flattenProviders(options.providers);
|
||||
if (flat.length > 0) {
|
||||
this._select = document.createElement('select');
|
||||
this._select.className = 'opencode-model-select';
|
||||
for (const opt of flat) {
|
||||
const o = document.createElement('option');
|
||||
o.value = `${opt.providerId}|${opt.modelId}`;
|
||||
o.textContent = `${opt.providerId} / ${opt.modelId}`;
|
||||
this._select.appendChild(o);
|
||||
}
|
||||
// No settings-based default — pick the first available option.
|
||||
this._select.value = this._select.options[0].value;
|
||||
}
|
||||
|
||||
this._textarea = document.createElement('textarea');
|
||||
this._textarea.placeholder = '向 AI 描述你想要的修改…';
|
||||
this._textarea.rows = 3;
|
||||
@@ -33,9 +56,11 @@ export class OpenCodeInlinePrompt extends Widget {
|
||||
this._sendBtn.disabled = options.disabled;
|
||||
this._sendBtn.addEventListener('click', () => {
|
||||
const text = this._textarea.value;
|
||||
if (text.trim()) {
|
||||
options.onSubmit(text);
|
||||
if (!text.trim()) {
|
||||
return;
|
||||
}
|
||||
const [providerId, modelId] = this._readSelection();
|
||||
options.onSubmit(text, providerId, modelId);
|
||||
});
|
||||
this._cancelBtn = document.createElement('button');
|
||||
this._cancelBtn.className = 'opencode-btn-cancel';
|
||||
@@ -49,11 +74,49 @@ export class OpenCodeInlinePrompt extends Widget {
|
||||
actions.appendChild(this._sendBtn);
|
||||
actions.appendChild(this._cancelBtn);
|
||||
|
||||
if (this._select) {
|
||||
const label = document.createElement('label');
|
||||
label.className = 'opencode-model-label';
|
||||
const span = document.createElement('span');
|
||||
span.textContent = '模型:';
|
||||
label.appendChild(span);
|
||||
label.appendChild(this._select);
|
||||
this.node.appendChild(label);
|
||||
}
|
||||
this.node.appendChild(this._textarea);
|
||||
this.node.appendChild(actions);
|
||||
}
|
||||
|
||||
setDisabled(disabled: boolean): void {
|
||||
this._sendBtn.disabled = disabled;
|
||||
if (this._select) {
|
||||
this._select.disabled = disabled;
|
||||
}
|
||||
}
|
||||
|
||||
private _readSelection(): [string | undefined, string | undefined] {
|
||||
if (!this._select) {
|
||||
return [undefined, undefined];
|
||||
}
|
||||
const parts = this._select.value.split('|', 2);
|
||||
if (parts.length !== 2) {
|
||||
return [undefined, undefined];
|
||||
}
|
||||
return [parts[0], parts[1]];
|
||||
}
|
||||
}
|
||||
|
||||
function flattenProviders(
|
||||
providers: OpenCodeProvidersResponse | null
|
||||
): { providerId: string; modelId: string }[] {
|
||||
if (!providers || !providers.providers) {
|
||||
return [];
|
||||
}
|
||||
const out: { providerId: string; modelId: string }[] = [];
|
||||
for (const p of providers.providers) {
|
||||
for (const m of p.models) {
|
||||
out.push({ providerId: p.id, modelId: m.id });
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user