Files
notebook-ai-extension/src/index.ts
T
tao.chen c0eba7e0e6 feat: replace per-cell AI button with global floating panel
The per-cell toolbar AI button (OpenCodeCellActions) is removed. A
single round button is mounted on document.body at the bottom-right of
the JupyterLab shell; clicking it opens a floating panel containing
the same OpenCodeInlinePrompt widget. Clicking again closes it.

The panel resolves the active notebook from app.shell.currentWidget
at open time and re-resolves it whenever currentChanged fires while
the panel is open. The active cell (used by the 📋 插入单元格内容,
↪ Insert, and ⟳ Replace cell actions) is resolved via a new
getActiveCell callback in OpenCodeInlinePrompt, decoupling the prompt
itself from any specific CodeCell instance.

Files:
- src/components/opencode_floating_panel.ts (new) — global panel
- src/components/opencode_cell_actions.ts (deleted) — logic moved up
- src/components/opencode_inline_prompt.ts — getActiveCell option
- src/index.ts — wire floating panel, drop toolbar factory
- style/base.css — .opencode-floating-* styles
- src/__tests__/opencode_floating_panel.spec.ts — new tests (renamed)

All existing functionality preserved: multi-session UI, SSE
streaming, permission/question interaction, history reload.
2026-07-28 19:34:52 +08:00

74 lines
2.5 KiB
TypeScript

import {
JupyterFrontEnd,
JupyterFrontEndPlugin
} from '@jupyterlab/application';
import { Widget } from '@lumino/widgets';
import { OpenCodeFloatingPanel } from './components/opencode_floating_panel';
import { requestAPI } from './request';
import { callOpenCodeProviders } from './api/opencode_client';
/**
* Initialization data for the opencode_bridge extension.
*
* v5-floating: the per-cell AI button is gone. A single
* OpenCodeFloatingPanel is attached to document.body and operates
* against the JupyterLab shell's currently-active notebook. The model
* is picked dynamically in the inline prompt.
*/
const plugin: JupyterFrontEndPlugin<void> = {
id: 'opencode_bridge:plugin',
description: 'A JupyterLab extension bridging the UI to OpenCode Serve.',
autoStart: true,
activate: (app: JupyterFrontEnd) => {
console.log('JupyterLab extension opencode_bridge is activated!');
// Mount the floating AI button on the body. The panel itself is
// hidden until the user clicks the button.
const floating = new OpenCodeFloatingPanel({
app,
serverSettings: app.serviceManager.serverSettings,
providers: null
});
Widget.attach(floating, document.body);
// Fetch available providers once at activation and cache them for
// the inline prompt's model picker. Failure is non-fatal (the
// picker hides). Also push the payload into the floating panel so
// the prompt can render selects without an extra round trip.
void callOpenCodeProviders(app.serviceManager.serverSettings)
.then(data => {
floating.setProviders(data);
const lines: string[] = [
'[opencode_bridge] Available OpenCode providers:'
];
for (const p of data.providers) {
const models = Object.values(p.models)
.map(m => m.id)
.join(', ');
lines.push(` - ${p.id}: ${models || '(no models)'}`);
}
console.log(lines.join('\n'));
})
.catch(reason => {
console.warn(
'[opencode_bridge] Could not fetch providers (is the opencode-bridge server extension enabled and opencode serve running?):',
reason
);
});
requestAPI<unknown>('hello', app.serviceManager.serverSettings)
.then(data => {
console.log('hello endpoint:', data);
})
.catch(reason => {
console.error(
`The opencode_bridge server extension appears to be missing.\n${reason}`
);
});
}
};
export default plugin;