AI entry button position:
- The AI button (entry point to the inline prompt) now renders as a
floating pill at the bottom-right corner of the cell, not inside
the native cell toolbar.
- The widget is still created per cell via the Cell toolbar factory
(it's the per-cell lifecycle hook), but its own node is
display:none inside the toolbar (no visible artifact). On
construction, OpenCodeCellActions creates a real <button> with
class .opencode-cell-ai-btn and appends it to cell.node. The
cell.node gets position:relative so the absolute-positioned
button anchors to the cell.
- style/base.css: removed the .opencode-cell-actions / .opencode-btn
rules (the widget's own node is no longer the visible button) and
added .opencode-cell-ai-btn (absolute, bottom:4px, right:4px,
z-index:10, small pill, hover/disabled states).
Clear input after success:
- OpenCodeInlinePrompt gains a public clearInput() method that empties
the textarea.
- OpenCodeCellActions._handleResponse on a successful response now
calls prompt.clearInput() after the history refresh, so the user
can immediately type a follow-up message.
Tests:
- All tests that previously queried the AI button on actions.node now
query cell.node.querySelector('button.opencode-cell-ai-btn') (the
button lives on the cell, not on the widget's own node). The two
tests that constructed the widget only for its side effect now use
the expression statement form to avoid the unused-const lint.
- The orphan-cell test now sets (cell as any).node since the
constructor accesses cell.node to position/append.
- New test: 'clears the input textarea after a successful response'
(types into the textarea, fires _handleResponse success, asserts
textarea.value === '').
Verification: pytest 37/37, jest 35/35, jlpm build OK.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
215 lines
5.6 KiB
CSS
215 lines
5.6 KiB
CSS
/*
|
|
See the JupyterLab Developer Guide for useful CSS Patterns:
|
|
|
|
https://jupyterlab.readthedocs.io/en/stable/developer/css.html
|
|
*/
|
|
|
|
/* AI entry button: rendered as a floating pill at the bottom-right
|
|
corner of the cell. The widget that owns it (opencode-cell-actions)
|
|
is registered in the native cell toolbar but its own node is
|
|
display:none — only this floating button is visible. */
|
|
.opencode-cell-ai-btn {
|
|
position: absolute;
|
|
bottom: 4px;
|
|
right: 4px;
|
|
z-index: 10;
|
|
border: 1px solid var(--jp-border-color1, #999);
|
|
background: var(--jp-layout-color1, #fff);
|
|
color: var(--jp-ui-font-color1, #333);
|
|
padding: 1px 8px;
|
|
border-radius: 10px;
|
|
font-size: var(--jp-ui-font-size0, 11px);
|
|
line-height: 1.3;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.opencode-cell-ai-btn:hover {
|
|
background: var(--jp-layout-color2, #f0f0f0);
|
|
}
|
|
|
|
.opencode-cell-ai-btn:disabled {
|
|
opacity: 0.5;
|
|
cursor: default;
|
|
}
|
|
|
|
/* Inline prompt panel attached to the cell when the AI button is clicked. */
|
|
.opencode-inline-prompt {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
padding: 6px;
|
|
margin: 4px 8px;
|
|
border: 1px solid var(--jp-border-color2, #ddd);
|
|
border-radius: 4px;
|
|
background: var(--jp-layout-color1, #fff);
|
|
}
|
|
|
|
.opencode-inline-prompt .opencode-prompt-providers {
|
|
display: flex;
|
|
gap: 12px;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.opencode-inline-prompt .opencode-prompt-providers label {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
font-size: var(--jp-ui-font-size1);
|
|
color: var(--jp-ui-font-color1, #333);
|
|
}
|
|
|
|
.opencode-inline-prompt .opencode-provider-select,
|
|
.opencode-inline-prompt .opencode-model-select {
|
|
flex: 1;
|
|
min-width: 120px;
|
|
padding: 2px 4px;
|
|
font-size: var(--jp-ui-font-size1);
|
|
font-family: inherit;
|
|
border: 1px solid var(--jp-border-color2, #ccc);
|
|
border-radius: 3px;
|
|
background: var(--jp-layout-color1, #fff);
|
|
}
|
|
|
|
.opencode-inline-prompt .opencode-provider-select:disabled,
|
|
.opencode-inline-prompt .opencode-model-select:disabled {
|
|
opacity: 0.5;
|
|
cursor: default;
|
|
}
|
|
|
|
/* Scrollable history of the current session's messages. The owning
|
|
cell action populates it via setMessages(). max-height keeps the
|
|
inline prompt compact; the user scrolls to see older messages. */
|
|
.opencode-inline-prompt .opencode-inline-history {
|
|
max-height: 320px;
|
|
overflow-y: auto;
|
|
margin: 4px 0;
|
|
padding: 4px 6px;
|
|
border: 1px solid var(--jp-border-color1, #c0c0c0);
|
|
border-radius: 3px;
|
|
background: var(--jp-layout-color2, #f7f7f7);
|
|
font-size: var(--jp-ui-font-size1);
|
|
line-height: 1.45;
|
|
}
|
|
|
|
.opencode-inline-prompt .opencode-msg {
|
|
margin-bottom: 6px;
|
|
}
|
|
|
|
.opencode-inline-prompt .opencode-msg:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.opencode-inline-prompt .opencode-msg-user {
|
|
white-space: pre-wrap;
|
|
padding: 4px 6px;
|
|
border-radius: 3px;
|
|
background: var(--jp-layout-color3, #eaeaea);
|
|
color: var(--jp-ui-font-color1, #333);
|
|
}
|
|
|
|
.opencode-inline-prompt .opencode-msg-assistant {
|
|
padding: 4px 6px;
|
|
color: var(--jp-ui-font-color0, #000);
|
|
}
|
|
|
|
/* Per-code-block toolbar: wraps every rendered <pre> so the Copy /
|
|
Insert / Replace buttons live INSIDE the fenced section (not as a
|
|
message-level header). */
|
|
.opencode-inline-prompt .opencode-code-block {
|
|
margin: 6px 0;
|
|
}
|
|
|
|
.opencode-inline-prompt .opencode-code-toolbar {
|
|
display: flex;
|
|
gap: 4px;
|
|
padding: 2px 6px;
|
|
background: var(--jp-layout-color2, #f0f0f0);
|
|
border: 1px solid var(--jp-border-color2, #ccc);
|
|
border-bottom: none;
|
|
border-top-left-radius: 3px;
|
|
border-top-right-radius: 3px;
|
|
font-size: var(--jp-ui-font-size0, 11px);
|
|
align-items: center;
|
|
}
|
|
|
|
.opencode-inline-prompt .opencode-code-btn {
|
|
border: 1px solid var(--jp-border-color2, #ccc);
|
|
background: var(--jp-layout-color1, #fff);
|
|
padding: 0 6px;
|
|
border-radius: 3px;
|
|
cursor: pointer;
|
|
font-size: var(--jp-ui-font-size0, 11px);
|
|
line-height: 1.4;
|
|
color: var(--jp-ui-font-color1, #333);
|
|
}
|
|
|
|
.opencode-inline-prompt .opencode-code-btn:hover {
|
|
background: var(--jp-layout-color2, #f7f7f7);
|
|
border-color: var(--jp-border-color1, #999);
|
|
}
|
|
|
|
/* The wrapped <pre> connects visually to the toolbar above (no top
|
|
border/radius, no top margin) so the block reads as one unit. */
|
|
.opencode-inline-prompt .opencode-code-block > pre {
|
|
margin-top: 0;
|
|
border-top-left-radius: 0;
|
|
border-top-right-radius: 0;
|
|
}
|
|
|
|
.opencode-inline-prompt .opencode-inline-history pre {
|
|
background: var(--jp-layout-color1, #fff);
|
|
padding: 6px 8px;
|
|
border-radius: 3px;
|
|
overflow-x: auto;
|
|
margin: 4px 0;
|
|
}
|
|
|
|
.opencode-inline-prompt .opencode-inline-history code {
|
|
font-family: var(--jp-code-font-family, monospace);
|
|
font-size: var(--jp-code-font-size, 13px);
|
|
}
|
|
|
|
.opencode-inline-prompt .opencode-inline-history p {
|
|
margin: 2px 0;
|
|
}
|
|
|
|
.opencode-inline-prompt .opencode-inline-history h1,
|
|
.opencode-inline-prompt .opencode-inline-history h2,
|
|
.opencode-inline-prompt .opencode-inline-history h3 {
|
|
margin: 6px 0 4px;
|
|
font-size: var(--jp-ui-font-size2, 14px);
|
|
}
|
|
|
|
.opencode-inline-prompt textarea {
|
|
width: 100%;
|
|
resize: vertical;
|
|
font-family: var(--jp-code-font-family, monospace);
|
|
font-size: var(--jp-code-font-size, 13px);
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.opencode-inline-prompt .opencode-inline-actions {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 4px;
|
|
}
|
|
|
|
.opencode-inline-prompt .opencode-inline-actions button {
|
|
border: 1px solid var(--jp-border-color2, #ccc);
|
|
background: var(--jp-layout-color2, #f7f7f7);
|
|
padding: 2px 8px;
|
|
border-radius: 3px;
|
|
cursor: pointer;
|
|
font-size: var(--jp-ui-font-size1);
|
|
}
|
|
|
|
.opencode-inline-prompt .opencode-inline-actions button:hover:enabled {
|
|
background: var(--jp-layout-color3, #eaeaea);
|
|
}
|
|
|
|
.opencode-inline-prompt .opencode-inline-actions button:disabled {
|
|
opacity: 0.4;
|
|
cursor: default;
|
|
}
|