Commit Graph
34 Commits
Author SHA1 Message Date
tao.chenandClaude Fable 5 1d7f5da9d4 feat: inline markdown output box — render AI reply with marked, do not replace cell
Flow (v3-final corrected):
  1. Model call succeeds.
  2. Server passes the AI reply through unchanged as 'markdown' (the
     system prompt allows ```language fences + a brief explanation, so
     the response is real markdown that marked can render into code
     blocks, headings, etc.).
  3. Client OpenCodeCellActions._handleResponse calls prompt.setOutput(
     resp.markdown); the inline prompt widget renders it with
     marked.parse and shows it in a new output area (hidden until a
     response arrives). The cell source is NOT replaced.
  4. User can close the output or cancel the whole prompt.

Server:
  - New unified system prompt: '你是代码助手 ... 按指令修改代码,可附简
    短说明' (allows ```fences``` + explanation; no more 'no markdown
    fences' restriction).
  - EditHandler returns {ok, markdown, sessionId, notebookPath} (raw
    text, fences intact). _strip_code_fence kept as a helper for any
    future apply-to-cell path; no longer called.
  - finalSource field dropped (the cell-apply path is gone).

Client:
  - OpenCodeSuccess: markdown: string (finalSource removed).
  - OpenCodeInlinePrompt: new output area, setOutput(md) renders via
    marked.parse, hideOutput() closes it.
  - OpenCodeCellActions._handleResponse: setOutput(markdown) instead of
    sharedModel.setSource + auto-hide. The prompt stays open so the
    user can read the output.
  - Uses marked@17 (already in node_modules via JupyterLab; no new dep).
  - CSS: output area styling (border, max-height 320px scroll, code/pre
    styling, close button).

Tests: pytest 34/34, jest 29/29. marked is mocked in jest.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 18:29:58 +08:00
tao.chenandClaude Fable 5 04f90ab5a0 feat: dual select (provider + model) with default[provider] model default
The inline prompt now renders two <select> boxes:
  - Provider: lists every provider from the cached /config/providers
    response. Default = first provider.
  - Model: lists that provider's model keys (Object.keys of the Record).
    Default = default[providerID] (from the same response) when that
    modelID is present in the provider's models; otherwise the first
    model. Changing the provider select rebuilds the model select with
    the new provider's models and its own default[provider].

On submit the two select values are read separately and passed as
providerId / modelId to the OpenCodeRequest (no more '|' delimiter).
The /opencode-bridge/edit body shape is unchanged (providerId, modelId).

Also fix the index.ts startup console log (p.models is a Record, use
Object.values), and drop the unused _cell field on the prompt widget
(now an unused param).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 18:13:14 +08:00
tao.chenandClaude Fable 5 e7579d3779 fix: Provider.models is a Record keyed by modelID, not an array
Per the OpenCode server docs (GET /config/providers), each Provider's
'models' is { [modelID: string]: Model } (a Record), so model IDs can be
referenced as strings (matching the 'default' map of providerID ->
modelID). Our code treated it as Model[], which:
  1. crashed with 'p.models is not iterable' on real responses, and
  2. after the previous Array.isArray guard, skipped EVERY provider
     (a Record is never an array), so the inline picker showed nothing.

Update types.ts (OpenCodeProvider.models -> Record, add OpenCodeModel,
add optional 'default' map to the response) and rewrite
flattenProviders to iterate Object.keys(p.models). Update test fixture
to the Record shape.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 18:01:16 +08:00
tao.chenandClaude Fable 5 f7099be060 fix: guard flattenProviders against non-array p.models
Some OpenCode Serve /config/providers responses include providers
without a 'models' field (or with a non-array value). The un-guarded
for-of on p.models then throws 'p.models is not iterable' and crashes
the inline prompt construction. Skip such providers instead.

Also re-assert the button label as 'AI' (matches the df2f3df label
simplification, which the 02cedac revert had bundled away).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 17:58:29 +08:00
tao.chen 02cedac138 Revert "fix: attach inline prompt to cell.inputArea (under the editor), not cell.node"
This reverts commit 8ed55c43c7.
2026-07-23 17:56:09 +08:00
tao.chenandClaude Fable 5 8ed55c43c7 fix: attach inline prompt to cell.inputArea (under the editor), not cell.node
Previously the inline prompt was Widget.attach'd to cell.node, which
in JupyterLab places it at the very bottom of the cell (after the
output area and In/Out prompt) — so the user couldn't see it, especially
on taller cells. Attaching to cell.inputArea.node puts the prompt
directly under the editor, which is the natural inline position.
Falls back to cell.node if inputArea is unexpectedly null.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 17:54:07 +08:00
tao.chenandClaude Fable 5 df2f3dff95 style: simplify button labels (drop emoji, keep text)
- 🪄 AI 智能编辑 → AI
- 🚀 发送 → 发送
- ✕ 取消 → 取消

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 17:41:38 +08:00
tao.chenandClaude Fable 5 b6c0273ee0 test: cover auth-on-request and close() in OpenCodeClient
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 17:40:58 +08:00
tao.chenandClaude Fable 5 c86de1d31b fix: set Basic Auth on HTTPRequest, not as fetch kwargs; close client cleanly
tornado rejects fetch(request, **kwargs) when the first arg is already an
HTTPRequest and the kwargs overlap request construction (auth_username /
auth_password). Move auth onto the HTTPRequest. Add OpenCodeClient.close()
to release its AsyncHTTPClient; module-level atexit closes the tornado
singleton so 'jupyter lab' can exit cleanly after SIGINT instead of
hanging on 'received signal 2, stopping'.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 17:40:57 +08:00
tao.chenandClaude Fable 5 ff932d9095 docs: v4 plan for OpenCodeClient auth + close() bugfixes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 17:13:41 +08:00
tao.chen 5306ada0c4 style: polish inline prompt model picker layout 2026-07-23 16:11:25 +08:00
tao.chen 74953d0ab8 feat: fully dynamic model picker in inline prompt 2026-07-23 16:11:20 +08:00
tao.chenandClaude Fable 5 74de18646f docs: amend v3 plan to v3-final (no settings, fully dynamic model picker)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:59:59 +08:00
tao.chenandClaude Fable 5 1e42459560 docs: design.md v3-final (startup env + dynamic model picker, no settings)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:56:07 +08:00
tao.chen b5bcb8f67c build: use node-modules linker so plain jlpm works
Removes the pnpm node-linker so 'jlpm test' / 'jlpm build' run directly
without the YARN_NODE_LINKER=node-modules env override. Re-run 'jlpm
install' to regenerate node_modules in the flat layout (the in-flight
yarn.lock migration in package.json already targets node-modules).
2026-07-23 15:53:30 +08:00
tao.chen f9b93906c6 refactor: move all server config to startup env vars; drop plugin settings 2026-07-23 13:21:31 +08:00
tao.chenandClaude Fable 5 ca2281af3b docs: amend v3 spec to v3-final — remove all plugin settings, fully dynamic model picker
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 13:14:41 +08:00
tao.chenandClaude Fable 5 8b51eda2ff docs: v3 implementation plan for startup env + model selector
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 13:11:18 +08:00
tao.chenandClaude Fable 5 12c0f4c618 docs: v3 spec for startup env config + inline model selector
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 13:11:18 +08:00
tao.chen 24bff6d2eb style: single AI button + inline prompt panel CSS 2026-07-23 12:47:49 +08:00
tao.chen 85e16914b5 feat: single AI action button with inline prompt; replace cell source 2026-07-23 12:47:47 +08:00
tao.chen d03f0fa434 refactor: drop mode from frontend types; react to settings changes 2026-07-23 12:47:44 +08:00
tao.chenandClaude Fable 5 04d011afa1 docs: design.md v2 interaction + contract (single button, no mode)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 12:37:49 +08:00
tao.chen 42ea857313 refactor: drop mode from backend; unify system prompt 2026-07-23 12:31:28 +08:00
tao.chenandClaude Fable 5 b5adf331a6 docs: v2 implementation plan for cell toolbar actions
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 12:29:23 +08:00
tao.chenandClaude Fable 5 50ba847659 docs: v2 spec for single AI button + inline prompt + drop mode
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 12:29:23 +08:00
tao.chenandClaude Fable 5 2bac98d659 docs: update design.md interaction flow for native Cell toolbar
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 10:53:30 +08:00
tao.chenandClaude Fable 5 47a774f237 style: make new files lint-clean (prettier + eqeqeq + remove unused eslint-disables)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 10:52:10 +08:00
tao.chen fe45c80dd2 feat: contribute opencode-cell-actions to Cell toolbar settings + styles 2026-07-23 10:40:33 +08:00
tao.chen 0a05424b29 refactor: register AI actions in native Cell toolbar; remove cell footer chain 2026-07-23 10:30:15 +08:00
tao.chen bb846d64fe feat: add OpenCodeCellActions widget for the native Cell toolbar 2026-07-23 10:22:15 +08:00
tao.chenandClaude Fable 5 714a55653c docs: implementation plan for cell toolbar actions migration
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 10:07:50 +08:00
tao.chenandClaude Fable 5 9e7c36e323 docs: spec for moving AI cell buttons into native Cell toolbar
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 09:54:17 +08:00
tao.chen c919c95842 Initial commit: opencode_bridge JupyterLab extension (Slices 1-3.5)
A JupyterLab extension that bridges the cell UI to a local OpenCode Serve
process. The extension is a dual package: a Python server extension
exposed under /opencode-bridge/*, plus a TypeScript frontend that
registers per-cell toolbars.

Backend (Python, tornado)
- Slice 1: config + auth + OpenCode HTTP client (tornado.httpclient,
  no aiohttp). 4 settings in schema/plugin.json (url, user, password,
  request timeout).
- Slice 2: handlers for /hello, /health, /providers, /edit.
- Slice 2.1 (correction): SessionManager with 1 notebook = 1 session
  mapping, async-safe via per-path locks, 404 recovery via invalidate().
  Two new endpoints: GET /sessions, DELETE /session?notebook=<path>.
- 32 pytest tests pass.

Frontend (TypeScript, JupyterLab 4.6)
- src/types.ts: CellContext, OpenCodeRequest/Response, OpenCodeSettings.
- src/context/cell_context.ts: extract CellContext from a CodeCell +
  its parent NotebookPanel, structured error collection.
- src/api/opencode_client.ts: callOpenCodeEdit, callOpenCodeProviders.
- src/components/opencode_cell_footer.ts: OpenCodeCellFooter Widget
  implementing ICellFooter with 3 buttons (optimize / fix / edit),
  resolved via this.parent instanceof CodeCell. NOT cellToolbar
  (does not exist in JL 4.6) and NOT Widget.findParent (removed in
  @lumino/widgets 2.x).
- src/components/opencode_cell_factory.ts: Cell.ContentFactory
  subclass returning the OpenCodeCellFooter.
- src/components/opencode_installer.ts: installOpenCodeEverywhere
  patches every notebook (existing + new) to use the custom factory.
- src/index.ts: registers the factory, loads settings, fetches
  /providers on activation and logs the list to the console.
- 23 jest tests pass (mocked JupyterLab boundary, pnpm path safe).

Settings
- 6 fields: 3 auth (url/user/password) + 1 timeout + 2 model selection
  (provider/model). Provider list is fetched at startup from
  /opencode-bridge/providers and printed to the browser console so
  users can copy values into Settings Editor.

Docs
- design.md: 6 sections covering architecture, UI flow, API contract,
  TS skeletons, session management (v0.2.1 correction), and
  provider/model selection (v0.2.2 addition).
- CLAUDE.md: agent guidance for working in this repo.
- TODO.md: remaining work for Slices 4-7 + v0.4+ backlog.

CI
- Gitea release workflow at .github/workflows/build.yml.
- Bark notification helper (non-fatal on failure).

Generated artefacts ignored: opencode_bridge/labextension/, _version.py,
*.tsbuildinfo, junit.xml, test.ipynb scratch notebook.
2026-07-22 19:07:58 +08:00