@marimo-team/frontend 0.15.5 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{ConnectedDataExplorerComponent-Cn5-l2X1.js → ConnectedDataExplorerComponent-BErMbWvG.js} +1 -1
- package/dist/assets/{ImageComparisonComponent-CEXMKKA4.js → ImageComparisonComponent-fTHv1Ih0.js} +1 -1
- package/dist/assets/{VegaLite-Bt14Ds9k.js → VegaLite-Bdi-TyfY.js} +6 -6
- package/dist/assets/_baseEach-CNBxBxvS.js +1 -0
- package/dist/assets/_baseMap-D1WHjKrd.js +1 -0
- package/dist/assets/_baseUniq-CCgDNtZb.js +1 -0
- package/dist/assets/_createAggregator-DcD0kTA5.js +1 -0
- package/dist/assets/agent-panel-Crv430aI.js +268 -0
- package/dist/assets/agent-panel-D92Mfy1i.css +1 -0
- package/dist/assets/{any-language-editor-DiwNT6zp.js → any-language-editor-CQh552Wu.js} +1 -1
- package/dist/assets/architectureDiagram-W76B3OCA-BAJeBxzt.js +36 -0
- package/dist/assets/{between-horizontal-start-FyewyCGn.js → between-horizontal-start-Boxgxbt_.js} +1 -1
- package/dist/assets/{blockDiagram-QIGZ2CNN-BrOkAf_c.js → blockDiagram-QIGZ2CNN-CL-1svEK.js} +1 -1
- package/dist/assets/{c4Diagram-FPNF74CW-BHPzDxE2.js → c4Diagram-FPNF74CW-BbEqbCTl.js} +5 -5
- package/dist/assets/channel-_2eNSz0n.js +1 -0
- package/dist/assets/chat-panel-CXh5Wl6C.js +3 -0
- package/dist/assets/{chunk-4BX2VUAB-DLxaCNYh.js → chunk-4BX2VUAB-C--8TXeE.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-DdzvO3HR.js → chunk-55IACEB6-Bj00HDqq.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-R5o-nSiG.js → chunk-FMBD7UC4-C-lhB6hN.js} +1 -1
- package/dist/assets/{chunk-K7UQS3LO-DxaMrGgG.js → chunk-K7UQS3LO-B-pGTXPt.js} +1 -1
- package/dist/assets/{chunk-QN33PNHL-DqS9-FYm.js → chunk-QN33PNHL-DqUzGhvm.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-BZ-TzajS.js → chunk-QZHKN3VN-TntJHfSk.js} +1 -1
- package/dist/assets/{chunk-TVAH2DTR-BsgP2dyv.js → chunk-TVAH2DTR-HUJb1psV.js} +1 -1
- package/dist/assets/{chunk-TZMSLE5B-D-h3ahXI.js → chunk-TZMSLE5B-BK3C__t3.js} +1 -1
- package/dist/assets/{circle-play-CQtRZ-rT.js → circle-play-DBLOv1Yu.js} +1 -1
- package/dist/assets/classDiagram-KNZD7YFC-BGmh9POF.js +1 -0
- package/dist/assets/classDiagram-v2-RKCZMP56-BGmh9POF.js +1 -0
- package/dist/assets/{clear-button-BY6Z_ViL.js → clear-button-BeoFbEKH.js} +1 -1
- package/dist/assets/clone-BFDSPAj3.js +1 -0
- package/dist/assets/command-palette-CXZiSv0I.js +1 -0
- package/dist/assets/common-C7oJcmCT.js +1 -0
- package/dist/assets/{compile-Ct_jzdKr.js → compile-7L0MwhyI.js} +1 -1
- package/dist/assets/cose-bilkent-S5V4N54A-BMkGLcVC.js +1 -0
- package/dist/assets/dagre-5GWH7T2D-BJtRienS.js +4 -0
- package/dist/assets/{data-grid-overlay-editor-BN_wulc3.js → data-grid-overlay-editor-DBkmGtNs.js} +1 -1
- package/dist/assets/datasources-panel-B7FbYLiy.js +1 -0
- package/dist/assets/{dependency-graph-panel-BOmSCZf7.js → dependency-graph-panel-DEdOxp2X.js} +4 -4
- package/dist/assets/diagram-N5W7TBWH-CmECY3nb.js +24 -0
- package/dist/assets/diagram-QEK2KX5R-DMOVSNKD.js +43 -0
- package/dist/assets/diagram-S2PKOQOG-BiJ96PNQ.js +24 -0
- package/dist/assets/{documentation-panel-BxjJO_Gw.js → documentation-panel-xULhaEv3.js} +1 -1
- package/dist/assets/edit-page-BrYda9VE.js +129 -0
- package/dist/assets/{ellipsis-vertical-UHbmjI2n.js → ellipsis-vertical-BBqXIlc2.js} +1 -1
- package/dist/assets/{empty-state-BIBXzY_0.js → empty-state-B3dA3G5P.js} +1 -1
- package/dist/assets/{erDiagram-AWTI2OKA-E84mAle_.js → erDiagram-AWTI2OKA-MP1DiFRo.js} +1 -1
- package/dist/assets/{error-panel-MEvQ6K7h.js → error-panel-Cc1sv-Ag.js} +1 -1
- package/dist/assets/file-explorer-panel-Bw59Kva1.js +1 -0
- package/dist/assets/{flowDiagram-PVAE7QVJ-DfbIRSAW.js → flowDiagram-PVAE7QVJ-BX7caPp7.js} +1 -1
- package/dist/assets/{ganttDiagram-OWAHRB6G-DR4HZ1z_.js → ganttDiagram-OWAHRB6G-B462g4Yf.js} +3 -3
- package/dist/assets/gitGraphDiagram-NY62KEGX-CGgvZ9-9.js +65 -0
- package/dist/assets/{glide-data-editor-nNmo1lPq.js → glide-data-editor-C0gUFZON.js} +4 -4
- package/dist/assets/graph-CHRVBzY5.js +1 -0
- package/dist/assets/{home-page-9eW6qida.js → home-page-Fb2osjys.js} +3 -3
- package/dist/assets/{index-DMomwMcN.js → index-BVgAenPd.js} +1 -1
- package/dist/assets/{index-B8llrTSo.js → index-BY93Ejhl.js} +1 -1
- package/dist/assets/{index-BFSnz7iM.js → index-C-8WADat.js} +1 -1
- package/dist/assets/{index-CPN7TRA1.js → index-C-GhZ7ti.js} +1 -1
- package/dist/assets/{index-DyLSuOH1.js → index-C1v_Z9et.js} +1 -1
- package/dist/assets/{index-VPWqq2Pg.js → index-C4Tn5NvJ.js} +1 -1
- package/dist/assets/{index-BAH034Ue.js → index-C77h_TXN.js} +1 -1
- package/dist/assets/{index-Dt9UWeWn.js → index-CQDrxQ0j.js} +1 -1
- package/dist/assets/{index-DWOaniGT.js → index-CWMgowgL.js} +1 -1
- package/dist/assets/{index-B1_GXGaP.js → index-Clbi_Yaq.js} +1 -1
- package/dist/assets/{index-B7yXbrLa.js → index-CpTPJo4k.js} +1 -1
- package/dist/assets/{index-CknhX2Vy.css → index-Cx0bsY1w.css} +1 -1
- package/dist/assets/{index-DqzMPAC8.js → index-D1vmG6DS.js} +2 -2
- package/dist/assets/{index-c6If577Q.js → index-D9UKkrr2.js} +1 -1
- package/dist/assets/{index-CB2pnVQG.js → index-DEQvTChO.js} +1 -1
- package/dist/assets/{index-OC46250R.js → index-DKEudB02.js} +205 -197
- package/dist/assets/{index-CSgxTUzD.js → index-DRMm6SNo.js} +1 -1
- package/dist/assets/{index-Bq516OmX.js → index-DoRmcrKM.js} +1 -1
- package/dist/assets/{index-DSU75csX.js → index-lYa_leQE.js} +1 -1
- package/dist/assets/{index-BLu5CX6z.js → index-vmICa5KN.js} +1 -1
- package/dist/assets/{index-uacyUula.js → index-z9bohSQJ.js} +1 -1
- package/dist/assets/infoDiagram-STP46IZ2-CVyrdLc8.js +2 -0
- package/dist/assets/isEmpty-DU_ogP_D.js +1 -0
- package/dist/assets/{journeyDiagram-BIP6EPQ6-BBiFyygf.js → journeyDiagram-BIP6EPQ6-C6EgLP_Q.js} +1 -1
- package/dist/assets/{kanban-definition-6OIFK2YF-DhgA6Nt6.js → kanban-definition-6OIFK2YF-BXzYO1yj.js} +4 -4
- package/dist/assets/layout-jihVw5-i.js +1 -0
- package/dist/assets/linear-C4blANlC.js +1 -0
- package/dist/assets/{links-CbvGxbsJ.js → links-D59GIweI.js} +3 -3
- package/dist/assets/{logs-panel-B9SmTZAW.js → logs-panel-D401qzZh.js} +1 -1
- package/dist/assets/markdown-renderer-Cd9eYyaL.js +263 -0
- package/dist/assets/{agent-panel-DpQ6muj-.css → markdown-renderer-ClyzDMmG.css} +1 -1
- package/dist/assets/mermaid-BEVuRz_O.js +1 -0
- package/dist/assets/{mermaid.core-4nVOEVX3.js → mermaid.core-CaSnaLH0.js} +41 -41
- package/dist/assets/min-DUMu_zeK.js +1 -0
- package/dist/assets/{mindmap-definition-Q6HEUPPD-CVLQNn1q.js → mindmap-definition-Q6HEUPPD-BXUM5MT2.js} +2 -2
- package/dist/assets/{number-overlay-editor-CzRzXLcd.js → number-overlay-editor-4uWXGlPG.js} +1 -1
- package/dist/assets/{outline-panel-uvsS-YEQ.js → outline-panel-DIzkvm2I.js} +1 -1
- package/dist/assets/packages-panel-CJL0MVlj.js +1 -0
- package/dist/assets/{pieDiagram-ADFJNKIX-C5IQ5DBZ.js → pieDiagram-ADFJNKIX-Dxt5PVNo.js} +3 -3
- package/dist/assets/{quadrantDiagram-LMRXKWRM-CFXFnQxx.js → quadrantDiagram-LMRXKWRM-D4pUaA31.js} +1 -1
- package/dist/assets/{react-plotly-mzdv02_Y.js → react-plotly-cJZ0VWBq.js} +1 -1
- package/dist/assets/{requirementDiagram-4UW4RH46-D9bPC89T.js → requirementDiagram-4UW4RH46-DVRTjgas.js} +1 -1
- package/dist/assets/run-page-BUEnMC9w.js +1 -0
- package/dist/assets/sankeyDiagram-GR3RE2ED-CVFnD9C-.js +10 -0
- package/dist/assets/scratchpad-panel-BIgRENkI.js +1 -0
- package/dist/assets/secrets-panel-xY5-V_BD.js +1 -0
- package/dist/assets/{sequenceDiagram-C3RYC4MD-6N7_hY4k.js → sequenceDiagram-C3RYC4MD-_lY4ZN_S.js} +4 -4
- package/dist/assets/{slides-component-EcjC8sDK.js → slides-component-Xjymwj7X.js} +1 -1
- package/dist/assets/snippets-panel-CTPYW41n.js +1 -0
- package/dist/assets/sortBy-BNZKwiq_.js +1 -0
- package/dist/assets/state-C4NiC9tO.js +1 -0
- package/dist/assets/stateDiagram-KXAO66HF-Da0JQWCn.js +1 -0
- package/dist/assets/stateDiagram-v2-UMBNRL4Z-D5lYZOOt.js +1 -0
- package/dist/assets/storage-CMdLzB_c.js +26 -0
- package/dist/assets/terminal-BPwTkXae.js +10 -0
- package/dist/assets/time-Dv5_Ouz_.js +1 -0
- package/dist/assets/{timeline-definition-XQNQX7LJ-BEaynAiY.js → timeline-definition-XQNQX7LJ-Dxh5Zu2e.js} +1 -1
- package/dist/assets/tracing-BCIurUfa.js +2 -0
- package/dist/assets/{tracing-panel-BmuHLPrY.js → tracing-panel-DAzrzNmm.js} +2 -2
- package/dist/assets/{trash-UBqfK4mR.js → trash-Dc6DSjz_.js} +1 -1
- package/dist/assets/{tree-XiEycetl.js → tree-jheoerAX.js} +1 -1
- package/dist/assets/{treemap-75Q7IDZK-CnuVFbBG.js → treemap-75Q7IDZK-IgpxeGaf.js} +21 -21
- package/dist/assets/{ts-tags-CloPe9IY.js → ts-tags-DxCDHihD.js} +1 -1
- package/dist/assets/variable-panel-DYAiLBmF.js +1 -0
- package/dist/assets/{vega-component-DsTH4tuX.js → vega-component-BpfpiPKI.js} +1 -1
- package/dist/assets/{xychartDiagram-6GGTOJPD-Dcz3O-A3.js → xychartDiagram-6GGTOJPD-CmNigJ31.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +8 -4
- package/src/__tests__/mocks.ts +43 -0
- package/src/components/app-config/user-config-form.tsx +32 -0
- package/src/components/chat/acp/__tests__/__snapshots__/prompt.test.ts.snap +55 -23
- package/src/components/chat/acp/__tests__/context-utils.test.ts +222 -0
- package/src/components/chat/acp/__tests__/prompt.test.ts +1 -1
- package/src/components/chat/acp/__tests__/state.test.ts +2 -6
- package/src/components/chat/acp/agent-docs.tsx +33 -6
- package/src/components/chat/acp/agent-panel.css +0 -18
- package/src/components/chat/acp/agent-panel.tsx +397 -72
- package/src/components/chat/acp/agent-selector.tsx +7 -1
- package/src/components/chat/acp/blocks.tsx +40 -10
- package/src/components/chat/acp/common.tsx +10 -2
- package/src/components/chat/acp/context-utils.ts +127 -0
- package/src/components/chat/acp/prompt.ts +34 -10
- package/src/components/chat/acp/state.ts +1 -1
- package/src/components/chat/acp/types.ts +8 -0
- package/src/components/chat/chat-panel.tsx +23 -88
- package/src/components/chat/chat-utils.ts +127 -1
- package/src/components/chat/markdown-renderer.css +39 -0
- package/src/components/chat/markdown-renderer.tsx +7 -38
- package/src/components/chat/tool-call-accordion.tsx +113 -23
- package/src/components/editor/Cell.tsx +6 -0
- package/src/components/editor/actions/name-cell-input.tsx +6 -1
- package/src/components/editor/actions/useCellActionButton.tsx +3 -1
- package/src/components/editor/ai/__tests__/completion-utils.test.ts +178 -1
- package/src/components/editor/ai/add-cell-with-ai.tsx +68 -66
- package/src/components/editor/ai/ai-completion-editor.tsx +29 -26
- package/src/components/editor/ai/completion-handlers.tsx +44 -6
- package/src/components/editor/ai/completion-utils.ts +92 -0
- package/src/components/editor/ai/transport/chat-transport.tsx +36 -0
- package/src/components/editor/cell/StagedAICell.tsx +51 -0
- package/src/components/editor/cell/cell-actions.tsx +2 -1
- package/src/components/terminal/__tests__/state.test.ts +207 -0
- package/src/components/terminal/hooks.ts +41 -0
- package/src/components/terminal/state.ts +75 -0
- package/src/components/terminal/terminal.tsx +334 -13
- package/src/components/terminal/theme.tsx +56 -0
- package/src/core/ai/__tests__/staged-cells.test.ts +356 -0
- package/src/core/ai/staged-cells.ts +208 -0
- package/src/core/cells/cells.ts +1 -1
- package/src/core/codemirror/lsp/federated-lsp.ts +1 -1
- package/src/core/islands/main.ts +2 -2
- package/src/core/kernel/messages.ts +8 -12
- package/src/core/saving/__tests__/filename.test.ts +37 -0
- package/src/core/static/__tests__/download-html.test.ts +43 -1
- package/src/core/websocket/useMarimoWebSocket.tsx +2 -2
- package/src/css/app/Cell.css +11 -0
- package/src/plugins/core/RenderHTML.tsx +36 -2
- package/src/plugins/core/__test__/RenderHTML.test.ts +72 -0
- package/src/plugins/core/registerReactComponent.tsx +28 -0
- package/src/plugins/impl/FileBrowserPlugin.tsx +8 -2
- package/src/stories/cell.stories.tsx +1 -1
- package/src/stories/layout/vertical/one-column.stories.tsx +1 -1
- package/src/utils/__tests__/cell-urls.test.ts +29 -0
- package/src/utils/__tests__/filenames.test.ts +18 -0
- package/src/utils/__tests__/path.test.ts +38 -0
- package/src/utils/__tests__/urls.test.ts +56 -1
- package/src/utils/errors.ts +9 -0
- package/dist/assets/_baseEach-C1FLm7WW.js +0 -1
- package/dist/assets/_baseMap-DBVArUYD.js +0 -1
- package/dist/assets/_baseUniq-Dk7ZPJ3N.js +0 -1
- package/dist/assets/_createAggregator-Bn38fDd3.js +0 -1
- package/dist/assets/agent-panel-COUYnuIK.js +0 -475
- package/dist/assets/architectureDiagram-W76B3OCA-DBzWQKKu.js +0 -36
- package/dist/assets/channel-CjhbjOv4.js +0 -1
- package/dist/assets/chat-panel-BPXKoTnZ.js +0 -7
- package/dist/assets/chat-panel-Brrs_eeH.css +0 -1
- package/dist/assets/classDiagram-KNZD7YFC-DHs5cFzy.js +0 -1
- package/dist/assets/classDiagram-v2-RKCZMP56-DHs5cFzy.js +0 -1
- package/dist/assets/clone-DM1YNjEn.js +0 -1
- package/dist/assets/command-palette-S0bzQp7v.js +0 -1
- package/dist/assets/common-B8U9k2Ly.js +0 -1
- package/dist/assets/cose-bilkent-S5V4N54A-wz1Sfx7j.js +0 -1
- package/dist/assets/dagre-5GWH7T2D-BfpcVBgq.js +0 -4
- package/dist/assets/datasources-panel-DfuURLJw.js +0 -1
- package/dist/assets/diagram-N5W7TBWH-Bf0oqqQh.js +0 -24
- package/dist/assets/diagram-QEK2KX5R-ZTc3qikh.js +0 -43
- package/dist/assets/diagram-S2PKOQOG-tLScBy7Z.js +0 -24
- package/dist/assets/edit-page-DJ8kJZ9w.js +0 -129
- package/dist/assets/file-explorer-panel-CzNUJ63G.js +0 -1
- package/dist/assets/gitGraphDiagram-NY62KEGX-C1t6QtVa.js +0 -65
- package/dist/assets/graph-CssCVWIq.js +0 -1
- package/dist/assets/index-DcCIe7np.js +0 -28
- package/dist/assets/infoDiagram-STP46IZ2-CwiAoz9f.js +0 -2
- package/dist/assets/layout-DpQrxGW-.js +0 -1
- package/dist/assets/linear-NsreOeBF.js +0 -1
- package/dist/assets/mermaid-DSt0r6IQ.js +0 -1
- package/dist/assets/min-D259kI3t.js +0 -1
- package/dist/assets/packages-panel-xMz9W2hW.js +0 -1
- package/dist/assets/run-page-Bb68qdhQ.js +0 -1
- package/dist/assets/sankeyDiagram-GR3RE2ED-BSJOau8E.js +0 -10
- package/dist/assets/scratchpad-panel-BF4BO-U4.js +0 -1
- package/dist/assets/secrets-panel-CdIX44dQ.js +0 -1
- package/dist/assets/snippets-panel-Dco9h0rb.js +0 -1
- package/dist/assets/sortBy-aLGA-PGK.js +0 -1
- package/dist/assets/stateDiagram-KXAO66HF-Bd68WT3b.js +0 -1
- package/dist/assets/stateDiagram-v2-UMBNRL4Z-BXz_GSwb.js +0 -1
- package/dist/assets/storage-CGlP4lCF.js +0 -26
- package/dist/assets/terminal-CxkHubcu.js +0 -9
- package/dist/assets/time-D2nr1UgQ.js +0 -1
- package/dist/assets/tracing-kTqHxa7q.js +0 -2
- package/dist/assets/variable-panel-noTnH-AQ.js +0 -1
|
@@ -8,7 +8,7 @@ import type { DatasetsState } from "@/core/datasets/types";
|
|
|
8
8
|
import { store } from "@/core/state/jotai";
|
|
9
9
|
import { variablesAtom } from "@/core/variables/state";
|
|
10
10
|
import type { Variable, VariableName } from "@/core/variables/types";
|
|
11
|
-
import { getAICompletionBody } from "../completion-utils";
|
|
11
|
+
import { codeToCells, getAICompletionBody } from "../completion-utils";
|
|
12
12
|
|
|
13
13
|
// Mock getCodes function
|
|
14
14
|
vi.mock("@/core/codemirror/copilot/getCodes", () => ({
|
|
@@ -357,3 +357,180 @@ describe("getAICompletionBody", () => {
|
|
|
357
357
|
`);
|
|
358
358
|
});
|
|
359
359
|
});
|
|
360
|
+
|
|
361
|
+
describe("codeToCells", () => {
|
|
362
|
+
it("should return empty array for empty string", () => {
|
|
363
|
+
const code = "";
|
|
364
|
+
const result = codeToCells(code);
|
|
365
|
+
expect(result).toEqual([]);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it("should return empty array for whitespace only", () => {
|
|
369
|
+
const code = " \n\t ";
|
|
370
|
+
const result = codeToCells(code);
|
|
371
|
+
expect(result).toEqual([]);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it("should convert code without backticks to single python cell", () => {
|
|
375
|
+
const code = "print('Hello, world!')";
|
|
376
|
+
const result = codeToCells(code);
|
|
377
|
+
expect(result).toEqual([
|
|
378
|
+
{ language: "python", code: "print('Hello, world!')" },
|
|
379
|
+
]);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it("should convert code with single closed backticks to cells", () => {
|
|
383
|
+
const code = "```python\nprint('Hello, world!')\n```";
|
|
384
|
+
const result = codeToCells(code);
|
|
385
|
+
expect(result).toEqual([
|
|
386
|
+
{ language: "python", code: "print('Hello, world!')" },
|
|
387
|
+
]);
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it("should convert code with unclosed backticks to cells", () => {
|
|
391
|
+
const code = "```python\nprint('Hello, world!')\n";
|
|
392
|
+
const result = codeToCells(code);
|
|
393
|
+
expect(result).toEqual([
|
|
394
|
+
{ language: "python", code: "print('Hello, world!')" },
|
|
395
|
+
]);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
it("should convert code with multiple closed cells", () => {
|
|
399
|
+
const code =
|
|
400
|
+
"```python\nprint('Hello, world!')\n```\n```sql\nSELECT * FROM users\n```";
|
|
401
|
+
const result = codeToCells(code);
|
|
402
|
+
expect(result).toEqual([
|
|
403
|
+
{ language: "python", code: "print('Hello, world!')" },
|
|
404
|
+
{ language: "sql", code: "SELECT * FROM users" },
|
|
405
|
+
]);
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
it("should handle code with no language identifier", () => {
|
|
409
|
+
const code = "```\nprint('Hello, world!')\n```";
|
|
410
|
+
const result = codeToCells(code);
|
|
411
|
+
expect(result).toEqual([
|
|
412
|
+
{ language: "python", code: "print('Hello, world!')" },
|
|
413
|
+
]);
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it("should handle unclosed code with no language identifier", () => {
|
|
417
|
+
const code = "```\nprint('Hello, world!')\n";
|
|
418
|
+
const result = codeToCells(code);
|
|
419
|
+
expect(result).toEqual([
|
|
420
|
+
{ language: "python", code: "print('Hello, world!')" },
|
|
421
|
+
]);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it("should handle markdown language", () => {
|
|
425
|
+
const code = "```markdown\n# Hello, world!\n```";
|
|
426
|
+
const result = codeToCells(code);
|
|
427
|
+
expect(result).toEqual([{ language: "markdown", code: "# Hello, world!" }]);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it("should handle sql language", () => {
|
|
431
|
+
const code = "```sql\nSELECT * FROM users\n```";
|
|
432
|
+
const result = codeToCells(code);
|
|
433
|
+
expect(result).toEqual([{ language: "sql", code: "SELECT * FROM users" }]);
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
it("should handle unclosed markdown cell", () => {
|
|
437
|
+
const code = "```markdown\n# Hello, world!\n";
|
|
438
|
+
const result = codeToCells(code);
|
|
439
|
+
expect(result).toEqual([{ language: "markdown", code: "# Hello, world!" }]);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
it("should handle unclosed sql cell", () => {
|
|
443
|
+
const code = "```sql\nSELECT * FROM users\n";
|
|
444
|
+
const result = codeToCells(code);
|
|
445
|
+
expect(result).toEqual([{ language: "sql", code: "SELECT * FROM users" }]);
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
it("should handle empty cells and skip them", () => {
|
|
449
|
+
const code = "```python\n\n```\n```sql\nSELECT * FROM users\n```";
|
|
450
|
+
const result = codeToCells(code);
|
|
451
|
+
expect(result).toEqual([{ language: "sql", code: "SELECT * FROM users" }]);
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it("should handle cells with only whitespace and skip them", () => {
|
|
455
|
+
const code = "```python\n \n```\n```sql\nSELECT * FROM users\n```";
|
|
456
|
+
const result = codeToCells(code);
|
|
457
|
+
expect(result).toEqual([{ language: "sql", code: "SELECT * FROM users" }]);
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
it("should handle code with trailing newlines", () => {
|
|
461
|
+
const code = "```python\nprint('Hello, world!')\n\n\n```";
|
|
462
|
+
const result = codeToCells(code);
|
|
463
|
+
expect(result).toEqual([
|
|
464
|
+
{ language: "python", code: "print('Hello, world!')" },
|
|
465
|
+
]);
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
it("should handle unclosed code with trailing newlines", () => {
|
|
469
|
+
const code = "```python\nprint('Hello, world!')\n\n\n";
|
|
470
|
+
const result = codeToCells(code);
|
|
471
|
+
expect(result).toEqual([
|
|
472
|
+
{ language: "python", code: "print('Hello, world!')" },
|
|
473
|
+
]);
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
it("should handle multiple cells with different languages", () => {
|
|
477
|
+
const code =
|
|
478
|
+
"```python\nprint('Hello, world!')\n```\n```sql\nSELECT * FROM users\n```\n```markdown\n# Title\nThis is markdown\n```";
|
|
479
|
+
|
|
480
|
+
const result = codeToCells(code);
|
|
481
|
+
expect(result).toEqual([
|
|
482
|
+
{ language: "python", code: "print('Hello, world!')" },
|
|
483
|
+
{ language: "sql", code: "SELECT * FROM users" },
|
|
484
|
+
{ language: "markdown", code: "# Title\nThis is markdown" },
|
|
485
|
+
]);
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
it("should handle complex multiline code", () => {
|
|
489
|
+
const code =
|
|
490
|
+
'```python\ndef hello():\n print("Hello, world!")\n return "success"\n\nhello()\n```\n```sql\nSELECT \n id,\n name,\n email\nFROM users\nWHERE active = true\nORDER BY name;\n```';
|
|
491
|
+
|
|
492
|
+
const result = codeToCells(code);
|
|
493
|
+
expect(result).toEqual([
|
|
494
|
+
{
|
|
495
|
+
language: "python",
|
|
496
|
+
code: 'def hello():\n print("Hello, world!")\n return "success"\n\nhello()',
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
language: "sql",
|
|
500
|
+
code: "SELECT \n id,\n name,\n email\nFROM users\nWHERE active = true\nORDER BY name;",
|
|
501
|
+
},
|
|
502
|
+
]);
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
it("should handle code with backticks in the content", () => {
|
|
506
|
+
const code = "```python\nprint('```')\n```";
|
|
507
|
+
const result = codeToCells(code);
|
|
508
|
+
expect(result).toEqual([{ language: "python", code: "print('" }]);
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
it("should handle code with no backticks in the last cell", () => {
|
|
512
|
+
const code =
|
|
513
|
+
"```python\nprint('Hello, world!')\n```\n```python\nprint('Hello, world!')";
|
|
514
|
+
const result = codeToCells(code);
|
|
515
|
+
expect(result).toEqual([
|
|
516
|
+
{ language: "python", code: "print('Hello, world!')" },
|
|
517
|
+
{ language: "python", code: "print('Hello, world!')" },
|
|
518
|
+
]);
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
it("should handle case insensitive language detection", () => {
|
|
522
|
+
const code = "```PYTHON\nprint('Hello, world!')\n```";
|
|
523
|
+
const result = codeToCells(code);
|
|
524
|
+
expect(result).toEqual([
|
|
525
|
+
{ language: "python", code: "print('Hello, world!')" },
|
|
526
|
+
]);
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
it("should handle unknown language", { fails: true }, () => {
|
|
530
|
+
const code = "```javascript\nconsole.log('Hello, world!')\n```";
|
|
531
|
+
const result = codeToCells(code);
|
|
532
|
+
expect(result).toEqual([
|
|
533
|
+
{ language: "javascript", code: "console.log('Hello, world!')" },
|
|
534
|
+
]);
|
|
535
|
+
});
|
|
536
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { useChat } from "@ai-sdk/react";
|
|
4
4
|
import {
|
|
5
5
|
autocompletion,
|
|
6
6
|
type Completion,
|
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
type CompletionSource,
|
|
9
9
|
} from "@codemirror/autocomplete";
|
|
10
10
|
import { markdown } from "@codemirror/lang-markdown";
|
|
11
|
-
import { sql } from "@codemirror/lang-sql";
|
|
12
11
|
import { Prec } from "@codemirror/state";
|
|
13
12
|
import { promptHistory, storePrompt } from "@marimo-team/codemirror-ai";
|
|
14
13
|
import ReactCodeMirror, {
|
|
@@ -17,7 +16,7 @@ import ReactCodeMirror, {
|
|
|
17
16
|
minimalSetup,
|
|
18
17
|
type ReactCodeMirrorRef,
|
|
19
18
|
} from "@uiw/react-codemirror";
|
|
20
|
-
import { useAtom, useStore } from "jotai";
|
|
19
|
+
import { useAtom, useAtomValue, useStore } from "jotai";
|
|
21
20
|
import { atomWithStorage } from "jotai/utils";
|
|
22
21
|
import {
|
|
23
22
|
ChevronsUpDown,
|
|
@@ -31,6 +30,10 @@ import { useMemo, useRef, useState } from "react";
|
|
|
31
30
|
import useEvent from "react-use-event-hook";
|
|
32
31
|
import { z } from "zod";
|
|
33
32
|
import { AIModelDropdown } from "@/components/ai/ai-model-dropdown";
|
|
33
|
+
import {
|
|
34
|
+
buildCompletionRequestBody,
|
|
35
|
+
handleToolCall,
|
|
36
|
+
} from "@/components/chat/chat-utils";
|
|
34
37
|
import { Button } from "@/components/ui/button";
|
|
35
38
|
import {
|
|
36
39
|
DropdownMenu,
|
|
@@ -40,31 +43,22 @@ import {
|
|
|
40
43
|
DropdownMenuTrigger,
|
|
41
44
|
} from "@/components/ui/dropdown-menu";
|
|
42
45
|
import { toast } from "@/components/ui/use-toast";
|
|
46
|
+
import { stagedAICellsAtom, useStagedCells } from "@/core/ai/staged-cells";
|
|
43
47
|
import { resourceExtension } from "@/core/codemirror/ai/resources";
|
|
44
|
-
import {
|
|
45
|
-
import {
|
|
48
|
+
import { useRequestClient } from "@/core/network/requests";
|
|
49
|
+
import type { AiCompletionRequest } from "@/core/network/types";
|
|
46
50
|
import { useRuntimeManager } from "@/core/runtime/config";
|
|
47
51
|
import { useTheme } from "@/theme/useTheme";
|
|
48
52
|
import { cn } from "@/utils/cn";
|
|
49
53
|
import { prettyError } from "@/utils/errors";
|
|
50
54
|
import { ZodLocalStorage } from "@/utils/localStorage";
|
|
51
|
-
import { useCellActions } from "../../../core/cells/cells";
|
|
52
55
|
import { PythonIcon } from "../cell/code/icons";
|
|
53
56
|
import {
|
|
54
57
|
CompletionActions,
|
|
55
58
|
createAiCompletionOnKeydown,
|
|
56
59
|
} from "./completion-handlers";
|
|
57
|
-
import {
|
|
58
|
-
|
|
59
|
-
getAICompletionBody,
|
|
60
|
-
mentionsCompletionSource,
|
|
61
|
-
} from "./completion-utils";
|
|
62
|
-
|
|
63
|
-
const pythonExtensions = [
|
|
64
|
-
customPythonLanguageSupport(),
|
|
65
|
-
EditorView.lineWrapping,
|
|
66
|
-
];
|
|
67
|
-
const sqlExtensions = [sql(), EditorView.lineWrapping];
|
|
60
|
+
import { CONTEXT_TRIGGER, mentionsCompletionSource } from "./completion-utils";
|
|
61
|
+
import { StreamingChunkTransport } from "./transport/chat-transport";
|
|
68
62
|
|
|
69
63
|
// Persist across sessions
|
|
70
64
|
const languageAtom = atomWithStorage<"python" | "sql">(
|
|
@@ -82,32 +76,56 @@ const promptHistoryStorage = new ZodLocalStorage(z.array(z.string()), () => []);
|
|
|
82
76
|
export const AddCellWithAI: React.FC<{
|
|
83
77
|
onClose: () => void;
|
|
84
78
|
}> = ({ onClose }) => {
|
|
85
|
-
const
|
|
86
|
-
const [
|
|
79
|
+
const store = useStore();
|
|
80
|
+
const [input, setInput] = useState("");
|
|
81
|
+
|
|
82
|
+
const { deleteAllStagedCells, clearStagedCells, onStream } =
|
|
83
|
+
useStagedCells(store);
|
|
87
84
|
const [language, setLanguage] = useAtom(languageAtom);
|
|
88
|
-
const { theme } = useTheme();
|
|
89
85
|
const runtimeManager = useRuntimeManager();
|
|
86
|
+
const { invokeAiTool } = useRequestClient();
|
|
90
87
|
|
|
88
|
+
const stagedAICells = useAtomValue(stagedAICellsAtom);
|
|
91
89
|
const inputRef = useRef<ReactCodeMirrorRef>(null);
|
|
92
90
|
|
|
93
|
-
const {
|
|
94
|
-
completion,
|
|
95
|
-
input,
|
|
96
|
-
stop,
|
|
97
|
-
isLoading,
|
|
98
|
-
setCompletion,
|
|
99
|
-
setInput,
|
|
100
|
-
handleSubmit,
|
|
101
|
-
} = useCompletion({
|
|
102
|
-
api: runtimeManager.getAiURL("completion").toString(),
|
|
103
|
-
headers: runtimeManager.headers(),
|
|
104
|
-
streamProtocol: "text",
|
|
91
|
+
const { sendMessage, stop, status, addToolResult } = useChat({
|
|
105
92
|
// Throttle the messages and data updates to 100ms
|
|
106
93
|
experimental_throttle: 100,
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
94
|
+
transport: new StreamingChunkTransport(
|
|
95
|
+
{
|
|
96
|
+
api: runtimeManager.getAiURL("completion").toString(),
|
|
97
|
+
headers: runtimeManager.headers(),
|
|
98
|
+
prepareSendMessagesRequest: async (options) => {
|
|
99
|
+
const completionBody = await buildCompletionRequestBody(
|
|
100
|
+
options.messages,
|
|
101
|
+
);
|
|
102
|
+
const body: AiCompletionRequest = {
|
|
103
|
+
...options,
|
|
104
|
+
...completionBody,
|
|
105
|
+
code: "",
|
|
106
|
+
prompt: "", // Don't need prompt since we are using messages
|
|
107
|
+
language: language,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
body: body,
|
|
112
|
+
};
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
(chunk) => {
|
|
116
|
+
onStream(chunk);
|
|
117
|
+
},
|
|
118
|
+
),
|
|
119
|
+
onToolCall: async ({ toolCall }) => {
|
|
120
|
+
await handleToolCall({
|
|
121
|
+
invokeAiTool,
|
|
122
|
+
addToolResult,
|
|
123
|
+
toolCall: {
|
|
124
|
+
toolName: toolCall.toolName,
|
|
125
|
+
toolCallId: toolCall.toolCallId,
|
|
126
|
+
input: toolCall.input as Record<string, never>,
|
|
127
|
+
},
|
|
128
|
+
});
|
|
111
129
|
},
|
|
112
130
|
onError: (error) => {
|
|
113
131
|
toast({
|
|
@@ -115,18 +133,20 @@ export const AddCellWithAI: React.FC<{
|
|
|
115
133
|
description: prettyError(error),
|
|
116
134
|
});
|
|
117
135
|
},
|
|
118
|
-
onFinish: (_prompt, completion) => {
|
|
119
|
-
// Remove trailing new lines
|
|
120
|
-
setCompletion(completion.trimEnd());
|
|
121
|
-
},
|
|
122
136
|
});
|
|
123
137
|
|
|
138
|
+
const isLoading = status === "streaming" || status === "submitted";
|
|
139
|
+
const hasCompletion = stagedAICells.size > 0;
|
|
140
|
+
const multipleCompletions = stagedAICells.size > 1;
|
|
141
|
+
|
|
124
142
|
const submit = () => {
|
|
125
143
|
if (!isLoading) {
|
|
126
144
|
if (inputRef.current?.view) {
|
|
127
145
|
storePrompt(inputRef.current.view);
|
|
128
146
|
}
|
|
129
|
-
|
|
147
|
+
// TODO: When we have conversations, don't delete existing cells
|
|
148
|
+
deleteAllStagedCells();
|
|
149
|
+
sendMessage({ text: input });
|
|
130
150
|
}
|
|
131
151
|
};
|
|
132
152
|
|
|
@@ -171,20 +191,12 @@ export const AddCellWithAI: React.FC<{
|
|
|
171
191
|
);
|
|
172
192
|
|
|
173
193
|
const handleAcceptCompletion = () => {
|
|
174
|
-
|
|
175
|
-
cellId: "__end__",
|
|
176
|
-
before: false,
|
|
177
|
-
code:
|
|
178
|
-
language === "python"
|
|
179
|
-
? completion
|
|
180
|
-
: SQLLanguageAdapter.fromQuery(completion),
|
|
181
|
-
});
|
|
182
|
-
setCompletion("");
|
|
194
|
+
clearStagedCells();
|
|
183
195
|
onClose();
|
|
184
196
|
};
|
|
185
197
|
|
|
186
198
|
const handleDeclineCompletion = () => {
|
|
187
|
-
|
|
199
|
+
deleteAllStagedCells();
|
|
188
200
|
};
|
|
189
201
|
|
|
190
202
|
const inputComponent = (
|
|
@@ -193,20 +205,19 @@ export const AddCellWithAI: React.FC<{
|
|
|
193
205
|
<PromptInput
|
|
194
206
|
inputRef={inputRef}
|
|
195
207
|
onClose={() => {
|
|
196
|
-
|
|
208
|
+
deleteAllStagedCells();
|
|
197
209
|
onClose();
|
|
198
210
|
}}
|
|
199
211
|
value={input}
|
|
200
212
|
onChange={(newValue) => {
|
|
201
213
|
setInput(newValue);
|
|
202
|
-
setCompletionBody(getAICompletionBody({ input: newValue }));
|
|
203
214
|
}}
|
|
204
215
|
onSubmit={submit}
|
|
205
216
|
onKeyDown={createAiCompletionOnKeydown({
|
|
206
217
|
handleAcceptCompletion,
|
|
207
218
|
handleDeclineCompletion,
|
|
208
219
|
isLoading,
|
|
209
|
-
|
|
220
|
+
hasCompletion,
|
|
210
221
|
})}
|
|
211
222
|
/>
|
|
212
223
|
{isLoading && (
|
|
@@ -234,7 +245,7 @@ export const AddCellWithAI: React.FC<{
|
|
|
234
245
|
<div className={cn("flex flex-col w-full gap-2 py-2")}>
|
|
235
246
|
{inputComponent}
|
|
236
247
|
<div className="flex flex-row justify-between -mt-1 ml-1 mr-3">
|
|
237
|
-
{!
|
|
248
|
+
{!hasCompletion && (
|
|
238
249
|
<span className="text-xs text-muted-foreground px-3 flex flex-col gap-1">
|
|
239
250
|
<span>
|
|
240
251
|
You can mention{" "}
|
|
@@ -245,12 +256,13 @@ export const AddCellWithAI: React.FC<{
|
|
|
245
256
|
<span>Code from other cells is automatically included.</span>
|
|
246
257
|
</span>
|
|
247
258
|
)}
|
|
248
|
-
{
|
|
259
|
+
{hasCompletion && (
|
|
249
260
|
<CompletionActions
|
|
250
261
|
isLoading={isLoading}
|
|
251
262
|
onAccept={handleAcceptCompletion}
|
|
252
263
|
onDecline={handleDeclineCompletion}
|
|
253
264
|
size="sm"
|
|
265
|
+
multipleCompletions={multipleCompletions}
|
|
254
266
|
/>
|
|
255
267
|
)}
|
|
256
268
|
<div className="ml-auto flex items-center gap-1">
|
|
@@ -262,16 +274,6 @@ export const AddCellWithAI: React.FC<{
|
|
|
262
274
|
/>
|
|
263
275
|
</div>
|
|
264
276
|
</div>
|
|
265
|
-
|
|
266
|
-
{completion && (
|
|
267
|
-
<ReactCodeMirror
|
|
268
|
-
value={completion}
|
|
269
|
-
className="cm border-t"
|
|
270
|
-
onChange={setCompletion}
|
|
271
|
-
theme={theme === "dark" ? "dark" : "light"}
|
|
272
|
-
extensions={language === "python" ? pythonExtensions : sqlExtensions}
|
|
273
|
-
/>
|
|
274
|
-
)}
|
|
275
277
|
</div>
|
|
276
278
|
);
|
|
277
279
|
};
|
|
@@ -165,7 +165,7 @@ export const AiCompletionEditor: React.FC<Props> = ({
|
|
|
165
165
|
<SparklesIcon className="text-(--blue-10) shrink-0" size={16} />
|
|
166
166
|
<PromptInput
|
|
167
167
|
inputRef={inputRef}
|
|
168
|
-
className="h-full my-0 py-2"
|
|
168
|
+
className="h-full my-0 py-2 flex items-center"
|
|
169
169
|
onClose={() => {
|
|
170
170
|
declineChange();
|
|
171
171
|
setCompletion("");
|
|
@@ -187,7 +187,7 @@ export const AiCompletionEditor: React.FC<Props> = ({
|
|
|
187
187
|
handleAcceptCompletion,
|
|
188
188
|
handleDeclineCompletion,
|
|
189
189
|
isLoading,
|
|
190
|
-
completion,
|
|
190
|
+
hasCompletion: completion.trim().length > 0,
|
|
191
191
|
})}
|
|
192
192
|
/>
|
|
193
193
|
{isLoading && (
|
|
@@ -202,30 +202,33 @@ export const AiCompletionEditor: React.FC<Props> = ({
|
|
|
202
202
|
Stop
|
|
203
203
|
</Button>
|
|
204
204
|
)}
|
|
205
|
-
|
|
206
|
-
<
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
205
|
+
<div className="-mr-1.5 py-1.5">
|
|
206
|
+
<div className="flex flex-row items-center justify-end gap-0.5">
|
|
207
|
+
<Tooltip content="Add context">
|
|
208
|
+
<Button
|
|
209
|
+
variant="text"
|
|
210
|
+
size="icon"
|
|
211
|
+
onClick={() => addContextCompletion(inputRef)}
|
|
212
|
+
>
|
|
213
|
+
<AtSignIcon className="h-3 w-3" />
|
|
214
|
+
</Button>
|
|
215
|
+
</Tooltip>
|
|
216
|
+
<AIModelDropdown
|
|
217
|
+
triggerClassName="h-7 text-xs w-24"
|
|
218
|
+
iconSize="small"
|
|
219
|
+
forRole="edit"
|
|
220
|
+
/>
|
|
221
|
+
</div>
|
|
222
|
+
{completion && (
|
|
223
|
+
<div className="-mb-1.5">
|
|
224
|
+
<CompletionActions
|
|
225
|
+
isLoading={isLoading}
|
|
226
|
+
onAccept={handleAcceptCompletion}
|
|
227
|
+
onDecline={handleDeclineCompletion}
|
|
228
|
+
size="xs"
|
|
229
|
+
/>
|
|
230
|
+
</div>
|
|
231
|
+
)}
|
|
229
232
|
</div>
|
|
230
233
|
|
|
231
234
|
<div className="h-full w-px bg-border mx-2" />
|
|
@@ -12,20 +12,20 @@ export const createAiCompletionOnKeydown = (opts: {
|
|
|
12
12
|
handleAcceptCompletion: () => void;
|
|
13
13
|
handleDeclineCompletion: () => void;
|
|
14
14
|
isLoading: boolean;
|
|
15
|
-
|
|
15
|
+
hasCompletion: boolean;
|
|
16
16
|
}) => {
|
|
17
17
|
const {
|
|
18
18
|
handleAcceptCompletion,
|
|
19
19
|
handleDeclineCompletion,
|
|
20
20
|
isLoading,
|
|
21
|
-
|
|
21
|
+
hasCompletion,
|
|
22
22
|
} = opts;
|
|
23
23
|
|
|
24
24
|
return (e: React.KeyboardEvent<HTMLDivElement>) => {
|
|
25
25
|
const metaKey = isPlatformMac() ? e.metaKey : e.ctrlKey;
|
|
26
26
|
|
|
27
27
|
// Mod+Enter should accept the completion, if there is one
|
|
28
|
-
if (metaKey && e.key === "Enter" && !isLoading &&
|
|
28
|
+
if (metaKey && e.key === "Enter" && !isLoading && hasCompletion) {
|
|
29
29
|
handleAcceptCompletion();
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -49,6 +49,7 @@ export const CompletionActions: React.FC<{
|
|
|
49
49
|
size?: "xs" | "sm";
|
|
50
50
|
acceptShortcut?: string;
|
|
51
51
|
declineShortcut?: string;
|
|
52
|
+
multipleCompletions?: boolean;
|
|
52
53
|
}> = ({
|
|
53
54
|
isLoading,
|
|
54
55
|
onAccept,
|
|
@@ -56,6 +57,7 @@ export const CompletionActions: React.FC<{
|
|
|
56
57
|
size = "sm",
|
|
57
58
|
acceptShortcut = "Mod-↵",
|
|
58
59
|
declineShortcut = "Shift-Mod-Delete",
|
|
60
|
+
multipleCompletions = false,
|
|
59
61
|
}) => {
|
|
60
62
|
return (
|
|
61
63
|
<>
|
|
@@ -68,7 +70,7 @@ export const CompletionActions: React.FC<{
|
|
|
68
70
|
onClick={onAccept}
|
|
69
71
|
>
|
|
70
72
|
<span className="text-(--grass-11) opacity-100">
|
|
71
|
-
Accept{" "}
|
|
73
|
+
Accept{multipleCompletions && " all"}{" "}
|
|
72
74
|
<MinimalHotkeys className="ml-1 inline" shortcut={acceptShortcut} />
|
|
73
75
|
</span>
|
|
74
76
|
</Button>
|
|
@@ -76,14 +78,50 @@ export const CompletionActions: React.FC<{
|
|
|
76
78
|
data-testid="decline-completion-button"
|
|
77
79
|
variant="text"
|
|
78
80
|
size={size}
|
|
79
|
-
className="mb-0 pl-1"
|
|
81
|
+
className="mb-0 pl-1 pr-0"
|
|
80
82
|
onClick={onDecline}
|
|
81
83
|
>
|
|
82
84
|
<span className="text-(--red-10)">
|
|
83
|
-
Reject{" "}
|
|
85
|
+
Reject{multipleCompletions && " all"}{" "}
|
|
84
86
|
<MinimalHotkeys className="ml-1 inline" shortcut={declineShortcut} />
|
|
85
87
|
</span>
|
|
86
88
|
</Button>
|
|
87
89
|
</>
|
|
88
90
|
);
|
|
89
91
|
};
|
|
92
|
+
|
|
93
|
+
export const CompletionActionsCellFooter: React.FC<{
|
|
94
|
+
isLoading: boolean;
|
|
95
|
+
onAccept: () => void;
|
|
96
|
+
onDecline: () => void;
|
|
97
|
+
size?: "xs" | "sm";
|
|
98
|
+
multipleCompletions?: boolean;
|
|
99
|
+
}> = ({ isLoading, onAccept, onDecline }) => {
|
|
100
|
+
return (
|
|
101
|
+
<>
|
|
102
|
+
<Button
|
|
103
|
+
variant="text"
|
|
104
|
+
size="xs"
|
|
105
|
+
disabled={isLoading}
|
|
106
|
+
onClick={onAccept}
|
|
107
|
+
className="h-6 text-(--grass-11) border-(--grass-7) bg-(--grass-3)/60
|
|
108
|
+
hover:bg-(--grass-3) dark:bg-(--grass-4)/80 dark:hover:bg-(--grass-3) rounded px-3 font-semibold
|
|
109
|
+
active:bg-(--grass-5) dark:active:bg-(--grass-4)
|
|
110
|
+
border-(--green-6) border hover:shadow-xs"
|
|
111
|
+
>
|
|
112
|
+
Accept
|
|
113
|
+
</Button>
|
|
114
|
+
<Button
|
|
115
|
+
variant="text"
|
|
116
|
+
size="xs"
|
|
117
|
+
onClick={onDecline}
|
|
118
|
+
className="h-6 text-(--red-10) border-(--red-7) bg-(--red-3)/60 hover:bg-(--red-3)
|
|
119
|
+
dark:bg-(--red-4)/80 dark:hover:bg-(--red-3) rounded px-3 font-semibold
|
|
120
|
+
active:bg-(--red-5) dark:active:bg-(--red-4)
|
|
121
|
+
border-(--red-6) border hover:shadow-xs"
|
|
122
|
+
>
|
|
123
|
+
Reject
|
|
124
|
+
</Button>
|
|
125
|
+
</>
|
|
126
|
+
);
|
|
127
|
+
};
|