@marimo-team/frontend 0.24.1-dev58 → 0.24.1-dev59
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/add-cell-with-ai-DVGZNNUv.js +76 -0
- package/dist/assets/{agent-panel-D7mo8YLQ.js → agent-panel-BTY3oeHD.js} +3 -3
- package/dist/assets/cell-editor-D3YsOG7a.js +21 -0
- package/dist/assets/{chat-display-Cgg0xLir.js → chat-display-NTNQQyoE.js} +1 -1
- package/dist/assets/chat-panel-Bow0QKrA.js +4 -0
- package/dist/assets/{chat-ui-DYLpA8l4.js → chat-ui-Cg69zAqt.js} +4 -4
- package/dist/assets/{command-palette-DiT0E_K7.js → command-palette-QFmIvDcT.js} +1 -1
- package/dist/assets/{edit-page-DAC4cNKe.js → edit-page-ds4edlkF.js} +6 -6
- package/dist/assets/{index-W5CUNEV9.js → index-B36dhEV6.js} +3 -3
- package/dist/assets/index-ecw9ngZu.css +2 -0
- package/dist/assets/{layout-D3zwsTvR.js → layout-iNJ-1JI2.js} +2 -2
- package/dist/assets/{panels-ChFSBlsU.js → panels-BbkuRtEi.js} +1 -1
- package/dist/assets/{reveal-component-BFRmOcQr.js → reveal-component-C4pF9OE7.js} +1 -1
- package/dist/assets/{run-page-MZCCEXct.js → run-page-Bo57bjbp.js} +1 -1
- package/dist/assets/{scratchpad-panel-BqYT2vj3.js → scratchpad-panel-vBIq9BfA.js} +1 -1
- package/dist/assets/{skeleton-gmxAkHfa.js → skeleton-B5iAa5n_.js} +1 -1
- package/dist/assets/{useNotebookActions-BoFJmccb.js → useNotebookActions-DGuDG0Px.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/components/editor/ai/__tests__/completion-utils.test.ts +0 -178
- package/src/components/editor/ai/__tests__/staged-cell-submission.test.ts +106 -0
- package/src/components/editor/ai/add-cell-with-ai.tsx +83 -62
- package/src/components/editor/ai/ai-completion-editor.tsx +71 -37
- package/src/components/editor/ai/completion-handlers.tsx +4 -0
- package/src/components/editor/ai/completion-utils.ts +0 -92
- package/src/components/editor/ai/staged-cell-submission.ts +46 -0
- package/src/components/editor/cell/StagedAICell.tsx +4 -2
- package/src/components/editor/cell/__tests__/StagedAICell.test.tsx +64 -0
- package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +77 -0
- package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +8 -2
- package/src/core/ai/__tests__/staged-cells.test.ts +442 -125
- package/src/core/ai/__tests__/stream-completion-text.test.ts +74 -0
- package/src/core/ai/completion-output.ts +56 -0
- package/src/core/ai/staged-cells.ts +245 -164
- package/src/core/ai/stream-completion-text.ts +23 -6
- package/dist/assets/add-cell-with-ai-0dqk0f7O.js +0 -77
- package/dist/assets/cell-editor-e7_o4mEh.js +0 -24
- package/dist/assets/chat-panel-BaG-5LiN.js +0 -4
- package/dist/assets/index-DIUt_9oR.css +0 -2
- package/src/core/ai/__tests__/strip-wrapping-backticks.test.ts +0 -133
- package/src/core/ai/strip-wrapping-backticks.ts +0 -88
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
|
|
3
|
-
import { describe, expect, it } from "vitest";
|
|
4
|
-
import { stripWrappingBackticks } from "../strip-wrapping-backticks";
|
|
5
|
-
|
|
6
|
-
// Cases aligned with `without_wrapping_backticks` on a complete string (single chunk).
|
|
7
|
-
const CASES: [string[], string][] = [
|
|
8
|
-
[["Hello", " world", "!"], "Hello world!"],
|
|
9
|
-
[["```", "print('hello')", "print('world')"], "print('hello')print('world')"],
|
|
10
|
-
[
|
|
11
|
-
["print('hello')", "print('world')", "```"],
|
|
12
|
-
"print('hello')print('world')```",
|
|
13
|
-
],
|
|
14
|
-
[["```", "print('hello')", "```"], "print('hello')"],
|
|
15
|
-
[["Hello", " ``` ", "world"], "Hello ``` world"],
|
|
16
|
-
[["``", "`print('hello')", "``", "`"], "print('hello')"],
|
|
17
|
-
[["``", "`", "\n", "print('hello')", "\n", "``", "`"], "print('hello')"],
|
|
18
|
-
[
|
|
19
|
-
["```\n", "print('hello')", "print('world')", "\n```"],
|
|
20
|
-
"print('hello')print('world')",
|
|
21
|
-
],
|
|
22
|
-
[
|
|
23
|
-
["```\nprint('hello')\n", "print('world')\n```"],
|
|
24
|
-
"print('hello')\nprint('world')",
|
|
25
|
-
],
|
|
26
|
-
[
|
|
27
|
-
["```\n", "def test():\n ", "return True\n```"],
|
|
28
|
-
"def test():\n return True",
|
|
29
|
-
],
|
|
30
|
-
[[], ""],
|
|
31
|
-
[["```idk```"], "idk"],
|
|
32
|
-
[["Hello world"], "Hello world"],
|
|
33
|
-
[
|
|
34
|
-
["```python\n", "def hello():\n ", "print('world')\n```"],
|
|
35
|
-
"def hello():\n print('world')",
|
|
36
|
-
],
|
|
37
|
-
[
|
|
38
|
-
["```python", "\ndef hello():\n ", "print('world')\n```"],
|
|
39
|
-
"def hello():\n print('world')",
|
|
40
|
-
],
|
|
41
|
-
[
|
|
42
|
-
["```sql", "SELECT * FROM table", " WHERE id = 1", "```"],
|
|
43
|
-
"SELECT * FROM table WHERE id = 1",
|
|
44
|
-
],
|
|
45
|
-
[
|
|
46
|
-
["```sql\n", "SELECT * FROM table\n", "WHERE id = 1\n```"],
|
|
47
|
-
"SELECT * FROM table\nWHERE id = 1",
|
|
48
|
-
],
|
|
49
|
-
[["```", "print('hello')", "``` "], "print('hello') "],
|
|
50
|
-
[["```python\n", "print('hello')", "\n``` "], "print('hello') "],
|
|
51
|
-
[["```", "code", "```\t\n"], "code\t\n"],
|
|
52
|
-
[[" ```", "code", "```"], "code"],
|
|
53
|
-
[[" ```python\n", "code", "```"], "code"],
|
|
54
|
-
[["\t```", "code", "```"], "code"],
|
|
55
|
-
[["\n", "\n", "```\n", "code", "\n```\n"], "code\n"],
|
|
56
|
-
[["\n", "\n", "```python\n", "code", "\n```\n"], "code\n"],
|
|
57
|
-
[["\n``", "`python\n", "code", "\n```\n"], "code\n"],
|
|
58
|
-
[["\n`", "`", "`python\n", "code", "\n```\n"], "code\n"],
|
|
59
|
-
[["```python ", "code", "```"], " code"],
|
|
60
|
-
[["```python\t", "code", "```"], "\tcode"],
|
|
61
|
-
[["```\n", "```"], ""],
|
|
62
|
-
[["```python\n", "```"], ""],
|
|
63
|
-
[["```\n", "code"], "code"],
|
|
64
|
-
[["```python\n", "code"], "code"],
|
|
65
|
-
[["code", "\n```"], "code\n```"],
|
|
66
|
-
[
|
|
67
|
-
["```\n", "x = 1\n", "```\n", "```\n", "y = 2\n", "```"],
|
|
68
|
-
"x = 1\n```\n```\ny = 2",
|
|
69
|
-
],
|
|
70
|
-
[["```python\n", "s = 'use `backticks`'\n", "```"], "s = 'use `backticks`'"],
|
|
71
|
-
[["``", "`\n", "code\n", "``", "`"], "code"],
|
|
72
|
-
[["```", "python\n", "code\n", "```"], "code"],
|
|
73
|
-
[["```", "code", "```"], "code"],
|
|
74
|
-
[["prefix ", "```\n", "code\n", "```"], "prefix ```\ncode\n```"],
|
|
75
|
-
[["```\n", "code\n", "```", " suffix"], "code\n``` suffix"],
|
|
76
|
-
[["```\n\n", "code\n\n", "```"], "\ncode\n"],
|
|
77
|
-
[["```\n", " code \n", "```"], " code "],
|
|
78
|
-
[["```\n", "\tcode\t\n", "```"], "\tcode\t"],
|
|
79
|
-
[
|
|
80
|
-
["```\n", "x\n", "```\n", "```python\n", "y\n", "```"],
|
|
81
|
-
"x\n```\n```python\ny",
|
|
82
|
-
],
|
|
83
|
-
[["```javascript\n", "console.log()\n", "```"], "javascript\nconsole.log()"],
|
|
84
|
-
[["```markdown\n", "# Title\n", "```"], "# Title"],
|
|
85
|
-
];
|
|
86
|
-
|
|
87
|
-
describe("stripWrappingBackticks", () => {
|
|
88
|
-
it.each(CASES)("strips fences for %j", (chunks, expected) => {
|
|
89
|
-
expect(stripWrappingBackticks(chunks.join(""))).toBe(expected);
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
// In streaming mode, the opening fence is stripped as soon as it is
|
|
94
|
-
// unambiguous, but the closing fence is left untouched (it may not have
|
|
95
|
-
// arrived yet, or a trailing "```" may be content).
|
|
96
|
-
const STREAMING_CASES: [string, string][] = [
|
|
97
|
-
// No fence: passthrough.
|
|
98
|
-
["import pandas as pd", "import pandas as pd"],
|
|
99
|
-
// Plain opening fence stripped once the first line is terminated.
|
|
100
|
-
["```\n", ""],
|
|
101
|
-
["```\ncode", "code"],
|
|
102
|
-
["```\ncode\nmore", "code\nmore"],
|
|
103
|
-
// Opening fence stripped, closing fence intentionally kept while streaming.
|
|
104
|
-
["```\ncode\n```", "code\n```"],
|
|
105
|
-
["```python\ncode\n```", "code\n```"],
|
|
106
|
-
// Language fences stripped as soon as the full identifier is present.
|
|
107
|
-
["```python", ""],
|
|
108
|
-
["```python\n", ""],
|
|
109
|
-
["```python\ncode", "code"],
|
|
110
|
-
["```sql\nSELECT 1", "SELECT 1"],
|
|
111
|
-
["```markdown\n# Title", "# Title"],
|
|
112
|
-
// Leading whitespace before the fence.
|
|
113
|
-
[" ```python\ncode", "code"],
|
|
114
|
-
// Partial language identifiers are left intact until they resolve.
|
|
115
|
-
["```", "```"],
|
|
116
|
-
["```p", "```p"],
|
|
117
|
-
["```py", "```py"],
|
|
118
|
-
["```pyth", "```pyth"],
|
|
119
|
-
["```s", "```s"],
|
|
120
|
-
["```mark", "```mark"],
|
|
121
|
-
// First-line content that is not a known language prefix is stripped as a
|
|
122
|
-
// plain fence even before a newline arrives.
|
|
123
|
-
["```x", "x"],
|
|
124
|
-
["```x = 1", "x = 1"],
|
|
125
|
-
// Unsupported language identifiers are kept as content (matches final mode).
|
|
126
|
-
["```json\ncode", "json\ncode"],
|
|
127
|
-
];
|
|
128
|
-
|
|
129
|
-
describe("stripWrappingBackticks (streaming)", () => {
|
|
130
|
-
it.each(STREAMING_CASES)("strips opening fence for %j", (text, expected) => {
|
|
131
|
-
expect(stripWrappingBackticks(text, { streaming: true })).toBe(expected);
|
|
132
|
-
});
|
|
133
|
-
});
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
|
|
3
|
-
const LANGS = ["python", "sql", "markdown"] as const;
|
|
4
|
-
|
|
5
|
-
interface StripOptions {
|
|
6
|
-
/**
|
|
7
|
-
* When true, the text is treated as a partial stream:
|
|
8
|
-
* - the opening fence is only stripped once we can be sure of its language
|
|
9
|
-
* identifier (e.g. "```py" is left untouched since it may still become
|
|
10
|
-
* "```python"), and
|
|
11
|
-
* - the closing fence is left in place, since it may not have arrived yet (or
|
|
12
|
-
* a trailing "```" may be part of the content).
|
|
13
|
-
*/
|
|
14
|
-
streaming?: boolean;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Removes wrapping markdown code fences from a completion string.
|
|
19
|
-
*/
|
|
20
|
-
export function stripWrappingBackticks(
|
|
21
|
-
text: string,
|
|
22
|
-
opts: StripOptions = {},
|
|
23
|
-
): string {
|
|
24
|
-
const { streaming = false } = opts;
|
|
25
|
-
const leadingWhitespace = text.match(/^\s*/)?.[0] ?? "";
|
|
26
|
-
const rest = text.slice(leadingWhitespace.length);
|
|
27
|
-
|
|
28
|
-
let body: string | null = null;
|
|
29
|
-
let strippedOpening = false;
|
|
30
|
-
|
|
31
|
-
for (const lang of LANGS) {
|
|
32
|
-
if (rest.startsWith(`\`\`\`${lang}`)) {
|
|
33
|
-
strippedOpening = true;
|
|
34
|
-
body = rest.slice(3 + lang.length);
|
|
35
|
-
if (body.startsWith("\n")) {
|
|
36
|
-
body = body.slice(1);
|
|
37
|
-
}
|
|
38
|
-
break;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (!strippedOpening && rest.startsWith("```")) {
|
|
43
|
-
const afterFence = rest.slice(3);
|
|
44
|
-
if (streaming && isPartialLanguageFence(afterFence)) {
|
|
45
|
-
return text;
|
|
46
|
-
}
|
|
47
|
-
strippedOpening = true;
|
|
48
|
-
body = afterFence;
|
|
49
|
-
if (body.startsWith("\n")) {
|
|
50
|
-
body = body.slice(1);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (!strippedOpening || body === null) {
|
|
55
|
-
return text;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// While streaming, the closing fence may not have arrived yet, so leave the
|
|
59
|
-
// body as-is once the opening fence is removed.
|
|
60
|
-
if (streaming) {
|
|
61
|
-
return body;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const strippedEnd = body.trimEnd();
|
|
65
|
-
const trailingSpace = body.slice(strippedEnd.length);
|
|
66
|
-
|
|
67
|
-
if (strippedEnd.endsWith("\n```")) {
|
|
68
|
-
return strippedEnd.slice(0, -4) + trailingSpace;
|
|
69
|
-
}
|
|
70
|
-
if (strippedEnd.endsWith("```")) {
|
|
71
|
-
return strippedEnd.slice(0, -3) + trailingSpace;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return body;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Returns true if the text after an opening "```" has no terminating newline
|
|
79
|
-
* yet and could still become a known language identifier.
|
|
80
|
-
*/
|
|
81
|
-
function isPartialLanguageFence(afterFence: string): boolean {
|
|
82
|
-
// Once the first line is terminated, we already know it is not a known
|
|
83
|
-
// language fence (those are handled above), so treat it as a plain fence.
|
|
84
|
-
if (afterFence.includes("\n")) {
|
|
85
|
-
return false;
|
|
86
|
-
}
|
|
87
|
-
return LANGS.some((lang) => lang.startsWith(afterFence));
|
|
88
|
-
}
|