@marimo-team/islands 0.15.1 → 0.15.3

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.
Files changed (189) hide show
  1. package/dist/{ConnectedDataExplorerComponent-DIy_POzQ.js → ConnectedDataExplorerComponent-DfvW3rBn.js} +323 -328
  2. package/dist/{ImageComparisonComponent-4ehfWTqN.js → ImageComparisonComponent-XaJshw7d.js} +13 -13
  3. package/dist/{_baseUniq-Duh9chhg.js → _baseUniq-dN9WKF9m.js} +67 -67
  4. package/dist/any-language-editor-CpFniVi-.js +27 -0
  5. package/dist/{arc-BXrety1g.js → arc-BOhn-m2C.js} +1 -1
  6. package/dist/{architectureDiagram-KFL7JDKH-3xVup6Z2.js → architectureDiagram-W76B3OCA-Bpg85ZKv.js} +144 -144
  7. package/dist/assets/{worker-COGufAQn.js → worker-Y-Q4G-N2.js} +30 -26
  8. package/dist/asterisk-DS281yxp.js +271 -0
  9. package/dist/{blockDiagram-ZYB65J3Q-eEZ6LTIH.js → blockDiagram-QIGZ2CNN-DS1kOHlW.js} +10 -10
  10. package/dist/{c4Diagram-AAMF2YG6-BqjOo55d.js → c4Diagram-FPNF74CW-CyRVKssw.js} +8 -8
  11. package/dist/{channel-BkhHQVue.js → channel-BilGXox7.js} +1 -1
  12. package/dist/{chunk-ANTBXLJU-BkLYh6ot.js → chunk-4BX2VUAB-CZR39zCO.js} +1 -1
  13. package/dist/{chunk-WVR4S24B-DpdvazVp.js → chunk-55IACEB6-BIH-MYov.js} +1 -1
  14. package/dist/{chunk-GLLZNHP4-BGaltx6Q.js → chunk-FMBD7UC4-4PZXFZE8.js} +1 -1
  15. package/dist/{chunk-JBRWN2VN-BZ3JgjiU.js → chunk-K7UQS3LO-CEvWKznk.js} +117 -117
  16. package/dist/{chunk-NRVI72HA-XO3jKXGr.js → chunk-QN33PNHL-D5LO5Jq_.js} +1 -1
  17. package/dist/{chunk-FHKO5MBM-BqvR49qe.js → chunk-QZHKN3VN-6gwUonWI.js} +1 -1
  18. package/dist/{chunk-LXBSTHXV-CJgOmuuc.js → chunk-TVAH2DTR-3gm06QdU.js} +7 -7
  19. package/dist/{chunk-OMD6QJNC-1rwXYaVn.js → chunk-TZMSLE5B-Cm8Iy9bO.js} +1 -1
  20. package/dist/{classDiagram-v2-QTMF73CY-_BeLc0pY.js → classDiagram-KNZD7YFC-DC529O_z.js} +2 -2
  21. package/dist/{classDiagram-3BZAVTQC-_BeLc0pY.js → classDiagram-v2-RKCZMP56-DC529O_z.js} +2 -2
  22. package/dist/{clone-DhXjmfNH.js → clone-CLoRX3j6.js} +1 -1
  23. package/dist/cose-bilkent-S5V4N54A-qf5DlS6Y.js +2609 -0
  24. package/dist/{cytoscape.esm-BYnVVhJX.js → cytoscape.esm-DfdJODL8.js} +34 -34
  25. package/dist/{dagre-2BBEFEWP-DoebA2zc.js → dagre-5GWH7T2D-Ceocls0m.js} +6 -6
  26. package/dist/{data-grid-overlay-editor-DYmc5v9I.js → data-grid-overlay-editor-AqDS_UKe.js} +11 -11
  27. package/dist/{diagram-4IRLE6MV-CxnJ439S.js → diagram-N5W7TBWH-CP66oSiv.js} +59 -60
  28. package/dist/{diagram-RP2FKANI-C-v51IXB.js → diagram-QEK2KX5R-_YD4kxxi.js} +15 -15
  29. package/dist/{diagram-GUPCWM2R-B7MLfHE7.js → diagram-S2PKOQOG-Cnj8T-OP.js} +10 -10
  30. package/dist/dockerfile-Cm8cRYCN.js +194 -0
  31. package/dist/ebnf-DUPDuY4r.js +78 -0
  32. package/dist/{erDiagram-HZWUO2LU-Cv1Slpwd.js → erDiagram-AWTI2OKA-CGnvoHx6.js} +8 -8
  33. package/dist/fcl-CPC2WYrI.js +103 -0
  34. package/dist/{flowDiagram-THRYKUMA-CnjAkcKg.js → flowDiagram-PVAE7QVJ-DG-pr9R9.js} +9 -9
  35. package/dist/{ganttDiagram-WV7ZQ7D5-CRQQ5cqP.js → ganttDiagram-OWAHRB6G-JmChtxvn.js} +34 -34
  36. package/dist/{gitGraphDiagram-OJR772UL-a7Wl_pP8.js → gitGraphDiagram-NY62KEGX-D8wLfOPd.js} +4 -4
  37. package/dist/{glide-data-editor-DrSTYUHP.js → glide-data-editor-9nC3iCIZ.js} +33 -33
  38. package/dist/{graph-Dq4lx1WI.js → graph-CoRe7vAN.js} +3 -3
  39. package/dist/http-D9LttvKF.js +44 -0
  40. package/dist/{index-B_t2cVfJ.js → index-6qYeHHjQ.js} +33090 -32892
  41. package/dist/{index-BhnWrXVh.js → index-BpzLh4Qe.js} +7711 -7711
  42. package/dist/{index-DSAnCCyV.js → index-BthgsgYX.js} +6 -6
  43. package/dist/{index-pBmAzQJl.js → index-MCx5v1x0.js} +2 -2
  44. package/dist/index-jkm77Jrz.js +98 -0
  45. package/dist/{infoDiagram-6WOFNB3A-B4pr_cke.js → infoDiagram-STP46IZ2-BlXxvOrR.js} +2 -2
  46. package/dist/{journeyDiagram-FFXJYRFH-CF8H9tcV.js → journeyDiagram-BIP6EPQ6-CNRYs_Fc.js} +24 -26
  47. package/dist/{kanban-definition-KOZQBZVT-BYfBCo4W.js → kanban-definition-6OIFK2YF-B9HeMAuP.js} +14 -14
  48. package/dist/{layout-D8_YOjKz.js → layout-m2vOUiW1.js} +81 -81
  49. package/dist/{linear-Be5SurQB.js → linear-DU6Q5CX3.js} +35 -35
  50. package/dist/{main-8IC_V2L6.js → main-BD2KGFpU.js} +74594 -68034
  51. package/dist/main.js +1 -1
  52. package/dist/{mermaid-B4v-qVXz.js → mermaid-HVCtvbyx.js} +6160 -7493
  53. package/dist/min-DcGMA4e_.js +80 -0
  54. package/dist/mindmap-definition-Q6HEUPPD-BW8UmIDQ.js +785 -0
  55. package/dist/nginx-zDPm3Z74.js +89 -0
  56. package/dist/{number-overlay-editor-XASk1ikZ.js → number-overlay-editor-D8Hl0Syo.js} +19 -19
  57. package/dist/{pieDiagram-DBDJKBY4-DhPX65dy.js → pieDiagram-ADFJNKIX-Bg-3zg5U.js} +17 -17
  58. package/dist/{quadrantDiagram-YPSRARAO-BjPa8bg5.js → quadrantDiagram-LMRXKWRM-BO4IG6Yz.js} +6 -6
  59. package/dist/{react-plotly-C_6SNkG9.js → react-plotly-dkvHVuRb.js} +3577 -3577
  60. package/dist/{requirementDiagram-EGVEC5DT-CtN_U9bI.js → requirementDiagram-4UW4RH46-5sdTguSM.js} +7 -7
  61. package/dist/{sankeyDiagram-HRAUVNP4-DVi73SZP.js → sankeyDiagram-GR3RE2ED-Buhlv9OI.js} +5 -5
  62. package/dist/sequenceDiagram-C3RYC4MD-C3qsM2UP.js +2519 -0
  63. package/dist/{slides-component-ML7vxRcD.js → slides-component-D209A0-s.js} +66 -66
  64. package/dist/solr-BNlsLglM.js +41 -0
  65. package/dist/spreadsheet-C-cy4P5N.js +49 -0
  66. package/dist/{stateDiagram-UUKSUZ4H-qZ62tNIz.js → stateDiagram-KXAO66HF-CopJ7G6P.js} +5 -5
  67. package/dist/{stateDiagram-v2-EYPG3UTE-BZEyTMgl.js → stateDiagram-v2-UMBNRL4Z-CejL8AKf.js} +2 -2
  68. package/dist/style.css +1 -1
  69. package/dist/tiddlywiki-5wqsXtSk.js +155 -0
  70. package/dist/tiki-__Kn3CeS.js +181 -0
  71. package/dist/{time-CZ2vTSO2.js → time-BwSBitlN.js} +58 -58
  72. package/dist/{timeline-definition-3HZDQTIS-DbWMwd1x.js → timeline-definition-XQNQX7LJ-DzMNTX-C.js} +10 -12
  73. package/dist/{timer-BYwnU4DF.js → timer-B0-z63CM.js} +16 -16
  74. package/dist/{treemap-75Q7IDZK-CocR-oAZ.js → treemap-75Q7IDZK-zeJG07dk.js} +14 -14
  75. package/dist/{vega-component-BrYek_kz.js → vega-component-CUkiTayd.js} +30 -30
  76. package/dist/{xychartDiagram-FDP5SA34-CYiUdiy5.js → xychartDiagram-6GGTOJPD-DiENNXMS.js} +7 -7
  77. package/package.json +39 -39
  78. package/src/__mocks__/notebook.ts +3 -0
  79. package/src/__mocks__/requests.ts +3 -0
  80. package/src/__tests__/__snapshots__/CellStatus.test.tsx.snap +12 -12
  81. package/src/__tests__/chat-utils.test.ts +26 -211
  82. package/src/components/ai/ai-model-dropdown.tsx +25 -9
  83. package/src/components/app-config/ai-config.tsx +7 -0
  84. package/src/components/chat/chat-components.tsx +71 -0
  85. package/src/components/chat/chat-panel.tsx +481 -291
  86. package/src/components/chat/chat-utils.ts +50 -0
  87. package/src/components/chat/markdown-renderer.tsx +3 -7
  88. package/src/components/chat/tool-call-accordion.tsx +5 -5
  89. package/src/components/datasources/__tests__/utils.test.ts +6 -0
  90. package/src/components/datasources/column-preview.tsx +1 -3
  91. package/src/components/editor/actions/useNotebookActions.tsx +1 -1
  92. package/src/components/editor/ai/add-cell-with-ai.tsx +20 -15
  93. package/src/components/editor/ai/ai-completion-editor.tsx +22 -3
  94. package/src/components/editor/ai/completion-handlers.tsx +2 -4
  95. package/src/components/editor/ai/completion-utils.ts +85 -11
  96. package/src/components/editor/alerts/startup-logs-alert.tsx +72 -0
  97. package/src/components/editor/chrome/panels/datasources-panel.tsx +3 -1
  98. package/src/components/editor/chrome/panels/dependency-graph-panel.tsx +3 -1
  99. package/src/components/editor/chrome/panels/documentation-panel.tsx +3 -1
  100. package/src/components/editor/chrome/panels/error-panel.tsx +3 -1
  101. package/src/components/editor/chrome/panels/file-explorer-panel.tsx +3 -1
  102. package/src/components/editor/chrome/panels/logs-panel.tsx +3 -1
  103. package/src/components/editor/chrome/panels/outline-panel.tsx +3 -1
  104. package/src/components/editor/chrome/panels/packages-panel.tsx +4 -2
  105. package/src/components/editor/chrome/panels/scratchpad-panel.tsx +3 -1
  106. package/src/components/editor/chrome/panels/secrets-panel.tsx +3 -1
  107. package/src/components/editor/chrome/panels/snippets-panel.tsx +3 -1
  108. package/src/components/editor/chrome/panels/tracing-panel.tsx +3 -1
  109. package/src/components/editor/chrome/panels/variable-panel.tsx +3 -1
  110. package/src/components/editor/chrome/wrapper/app-chrome.tsx +38 -28
  111. package/src/components/editor/controls/command-palette-button.tsx +1 -1
  112. package/src/components/editor/controls/command-palette.tsx +5 -4
  113. package/src/components/editor/controls/state.ts +4 -0
  114. package/src/components/editor/package-alert.tsx +108 -58
  115. package/src/components/editor/renderers/CellArray.tsx +2 -0
  116. package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +0 -1
  117. package/src/components/pages/edit-page.tsx +7 -3
  118. package/src/core/ai/chat-utils.ts +26 -43
  119. package/src/core/ai/config.ts +1 -1
  120. package/src/core/ai/context/__tests__/registry.test.ts +277 -3
  121. package/src/core/ai/context/context.ts +11 -1
  122. package/src/core/ai/context/providers/__tests__/cell-output.test.ts +378 -0
  123. package/src/core/ai/context/providers/__tests__/error.test.ts +3 -2
  124. package/src/core/ai/context/providers/__tests__/file.test.ts +119 -0
  125. package/src/core/ai/context/providers/cell-output.ts +349 -0
  126. package/src/core/ai/context/providers/common.ts +5 -1
  127. package/src/core/ai/context/providers/file.ts +287 -0
  128. package/src/core/ai/context/registry.ts +79 -0
  129. package/src/core/ai/state.ts +22 -5
  130. package/src/core/alerts/state.ts +71 -3
  131. package/src/core/cells/cell.ts +2 -2
  132. package/src/core/cells/cells.ts +1 -1
  133. package/src/core/cells/logs.ts +1 -1
  134. package/src/core/cells/runs.ts +6 -5
  135. package/src/core/codemirror/ai/resources.ts +47 -5
  136. package/src/core/codemirror/ai/state.ts +12 -0
  137. package/src/core/codemirror/language/__tests__/sql.test.ts +45 -0
  138. package/src/core/codemirror/markdown/__tests__/commands.test.ts +1 -0
  139. package/src/core/codemirror/theme/dark.ts +1 -1
  140. package/src/core/config/capabilities.ts +1 -1
  141. package/src/core/datasets/__tests__/data-source.test.ts +24 -0
  142. package/src/core/errors/__tests__/errors.test.ts +2 -0
  143. package/src/core/islands/bridge.ts +1 -0
  144. package/src/core/islands/main.ts +1 -0
  145. package/src/core/kernel/messages.ts +12 -6
  146. package/src/core/layout/layout.ts +3 -3
  147. package/src/core/network/requests-network.ts +8 -0
  148. package/src/core/network/requests-static.ts +1 -0
  149. package/src/core/network/requests-toasting.ts +1 -0
  150. package/src/core/network/types.ts +4 -1
  151. package/src/core/wasm/bridge.ts +18 -2
  152. package/src/core/wasm/worker/bootstrap.ts +3 -1
  153. package/src/core/wasm/worker/getMarimoWheel.ts +3 -8
  154. package/src/core/wasm/worker/types.ts +3 -0
  155. package/src/core/websocket/useMarimoWebSocket.tsx +7 -1
  156. package/src/css/app/Cell.css +42 -21
  157. package/src/css/app/codemirror.css +5 -1
  158. package/src/css/globals.css +3 -0
  159. package/src/css/md.css +1 -1
  160. package/src/plugins/impl/MicrophonePlugin.tsx +2 -2
  161. package/src/plugins/impl/chat/ChatPlugin.tsx +2 -9
  162. package/src/plugins/impl/chat/chat-ui.tsx +129 -110
  163. package/src/plugins/impl/chat/types.ts +5 -8
  164. package/src/plugins/impl/code/__tests__/language.test.ts +15 -0
  165. package/src/plugins/impl/code/any-language-editor.tsx +11 -8
  166. package/src/plugins/layout/MimeRenderPlugin.tsx +3 -6
  167. package/src/stories/cell.stories.tsx +6 -0
  168. package/src/stories/layout/vertical/one-column.stories.tsx +215 -0
  169. package/src/theme/useTheme.ts +11 -6
  170. package/src/utils/__tests__/blob.test.ts +37 -0
  171. package/src/utils/arrays.ts +13 -0
  172. package/src/utils/fileToBase64.ts +21 -6
  173. package/src/utils/json/base64.ts +5 -2
  174. package/src/utils/numbers.ts +9 -7
  175. package/dist/any-language-editor-Cvakxnzg.js +0 -45
  176. package/dist/asn1-jKiBa2Ya.js +0 -95
  177. package/dist/clojure-CCKyeQKf.js +0 -800
  178. package/dist/css-BkF-NPzE.js +0 -1553
  179. package/dist/index-CfrpOVe5.js +0 -341
  180. package/dist/index-DIFrY2JV.js +0 -288
  181. package/dist/javascript-C2yteZeJ.js +0 -691
  182. package/dist/min-Q4QXsXWR.js +0 -80
  183. package/dist/mindmap-definition-LNHGMQRG-CebaDS3t.js +0 -3234
  184. package/dist/mllike-BSnXJBGA.js +0 -272
  185. package/dist/pug-CwAQJzGR.js +0 -248
  186. package/dist/python-BkR3uSy8.js +0 -313
  187. package/dist/rpm-IznJm2Xc.js +0 -57
  188. package/dist/sequenceDiagram-WFGC7UMF-C8jImRwI.js +0 -2284
  189. package/dist/ttcn-cfg-Bac_acMi.js +0 -88
@@ -0,0 +1,50 @@
1
+ /* Copyright 2024 Marimo. All rights reserved. */
2
+
3
+ import type { FileUIPart, UIMessage } from "ai";
4
+ import { blobToString } from "@/utils/fileToBase64";
5
+
6
+ export function generateChatTitle(message: string): string {
7
+ return message.length > 50 ? `${message.slice(0, 50)}...` : message;
8
+ }
9
+
10
+ export async function convertToFileUIPart(
11
+ files: File[],
12
+ ): Promise<FileUIPart[]> {
13
+ const fileUIParts = await Promise.all(
14
+ files.map(async (file) => {
15
+ const part: FileUIPart = {
16
+ type: "file" as const,
17
+ mediaType: file.type,
18
+ filename: file.name,
19
+ url: await blobToString(file, "dataUrl"),
20
+ };
21
+ return part;
22
+ }),
23
+ );
24
+
25
+ return fileUIParts;
26
+ }
27
+
28
+ export function isLastMessageReasoning(messages: UIMessage[]): boolean {
29
+ if (messages.length === 0) {
30
+ return false;
31
+ }
32
+
33
+ const lastMessage = messages.at(-1);
34
+ if (!lastMessage) {
35
+ return false;
36
+ }
37
+
38
+ if (lastMessage.role !== "assistant" || !lastMessage.parts) {
39
+ return false;
40
+ }
41
+
42
+ const parts = lastMessage.parts;
43
+ if (parts.length === 0) {
44
+ return false;
45
+ }
46
+
47
+ // Check if the last part is reasoning
48
+ const lastPart = parts[parts.length - 1];
49
+ return lastPart.type === "reasoning";
50
+ }
@@ -172,21 +172,17 @@ const CopyButton: React.FC<ButtonProps> = ({ onClick, ...props }) => {
172
172
  };
173
173
 
174
174
  const COMPONENTS: Components = {
175
- code: ({ children, className, key }) => {
175
+ code: ({ children, className }) => {
176
176
  const language = className?.replace("language-", "");
177
177
  if (language && typeof children === "string") {
178
178
  return (
179
- <div key={key}>
179
+ <div>
180
180
  <div className="text-xs text-muted-foreground pl-1">{language}</div>
181
181
  <CodeBlock code={children.trim()} language={language} />
182
182
  </div>
183
183
  );
184
184
  }
185
- return (
186
- <code key={key} className={className}>
187
- {children}
188
- </code>
189
- );
185
+ return <code className={className}>{children}</code>;
190
186
  },
191
187
  };
192
188
 
@@ -1,5 +1,6 @@
1
1
  /* Copyright 2024 Marimo. All rights reserved. */
2
2
 
3
+ import type { ToolUIPart } from "ai";
3
4
  import {
4
5
  CheckCircleIcon,
5
6
  Loader2,
@@ -17,10 +18,10 @@ import { cn } from "@/utils/cn";
17
18
 
18
19
  interface ToolCallAccordionProps {
19
20
  toolName: string;
20
- result?: string | null;
21
+ result: unknown;
21
22
  error?: string;
22
23
  index?: number;
23
- state?: "partial-call" | "call" | "result";
24
+ state?: ToolUIPart["state"];
24
25
  }
25
26
 
26
27
  export const ToolCallAccordion: React.FC<ToolCallAccordionProps> = ({
@@ -30,7 +31,7 @@ export const ToolCallAccordion: React.FC<ToolCallAccordionProps> = ({
30
31
  index = 0,
31
32
  state,
32
33
  }) => {
33
- const hasResult = state === "result" && (result || error);
34
+ const hasResult = state === "output-available" && (result || error);
34
35
  const status = error ? "error" : hasResult ? "success" : "loading";
35
36
 
36
37
  const getStatusIcon = () => {
@@ -84,8 +85,7 @@ export const ToolCallAccordion: React.FC<ToolCallAccordionProps> = ({
84
85
  {/* Only show content when tool is complete */}
85
86
  {hasResult && (
86
87
  <div className="space-y-3">
87
- {/* Result */}
88
- {result && (
88
+ {result !== undefined && result !== null && (
89
89
  <div>
90
90
  <div className="text-xs font-medium text-muted-foreground mt-2 mb-1">
91
91
  Result:
@@ -13,6 +13,12 @@ describe("sqlCode", () => {
13
13
  source: "local",
14
14
  source_type: "local",
15
15
  type: "table",
16
+ engine: null,
17
+ indexes: null,
18
+ num_columns: null,
19
+ num_rows: null,
20
+ variable_name: null,
21
+ primary_keys: null,
16
22
  };
17
23
  const mockColumn = {
18
24
  name: "email" as const,
@@ -173,9 +173,7 @@ export function renderPreviewError({
173
173
  }
174
174
 
175
175
  export function renderStats(
176
- stats: Partial<
177
- Record<ColumnHeaderStatsKey, string | number | boolean | null | undefined>
178
- >,
176
+ stats: Partial<Record<ColumnHeaderStatsKey, unknown>>,
179
177
  dataType: DataType,
180
178
  ) {
181
179
  return (
@@ -73,8 +73,8 @@ import { newNotebookURL } from "@/utils/urls";
73
73
  import { useRunAllCells } from "../cell/useRunCells";
74
74
  import { useChromeActions, useChromeState } from "../chrome/state";
75
75
  import { PANELS } from "../chrome/types";
76
- import { commandPaletteAtom } from "../controls/command-palette";
77
76
  import { keyboardShortcutsAtom } from "../controls/keyboard-shortcuts";
77
+ import { commandPaletteAtom } from "../controls/state";
78
78
  import { AddDatabaseDialogContent } from "../database/add-database-form";
79
79
  import { displayLayoutName, getLayoutIcon } from "../renderers/layout-select";
80
80
  import { LAYOUT_TYPES } from "../renderers/types";
@@ -1,5 +1,6 @@
1
1
  /* Copyright 2024 Marimo. All rights reserved. */
2
2
 
3
+ import { useCompletion } from "@ai-sdk/react";
3
4
  import {
4
5
  autocompletion,
5
6
  type Completion,
@@ -16,8 +17,7 @@ import ReactCodeMirror, {
16
17
  minimalSetup,
17
18
  type ReactCodeMirrorRef,
18
19
  } from "@uiw/react-codemirror";
19
- import { useCompletion } from "ai/react";
20
- import { useAtom, useAtomValue, useStore } from "jotai";
20
+ import { useAtom, useStore } from "jotai";
21
21
  import { atomWithStorage } from "jotai/utils";
22
22
  import {
23
23
  ChevronsUpDown,
@@ -40,12 +40,9 @@ import {
40
40
  DropdownMenuTrigger,
41
41
  } from "@/components/ui/dropdown-menu";
42
42
  import { toast } from "@/components/ui/use-toast";
43
- import { useModelChange } from "@/core/ai/config";
44
43
  import { resourceExtension } from "@/core/codemirror/ai/resources";
45
44
  import { customPythonLanguageSupport } from "@/core/codemirror/language/languages/python";
46
45
  import { SQLLanguageAdapter } from "@/core/codemirror/language/languages/sql/sql";
47
- import { aiAtom } from "@/core/config/config";
48
- import { DEFAULT_AI_MODEL } from "@/core/config/config-schema";
49
46
  import { useRuntimeManager } from "@/core/runtime/config";
50
47
  import { useTheme } from "@/theme/useTheme";
51
48
  import { cn } from "@/utils/cn";
@@ -58,6 +55,7 @@ import {
58
55
  createAiCompletionOnKeydown,
59
56
  } from "./completion-handlers";
60
57
  import {
58
+ CONTEXT_TRIGGER,
61
59
  getAICompletionBody,
62
60
  mentionsCompletionSource,
63
61
  } from "./completion-utils";
@@ -90,9 +88,6 @@ export const AddCellWithAI: React.FC<{
90
88
  const { theme } = useTheme();
91
89
  const runtimeManager = useRuntimeManager();
92
90
 
93
- const ai = useAtomValue(aiAtom);
94
- const editModel = ai?.models?.edit_model || DEFAULT_AI_MODEL;
95
- const { saveModelChange } = useModelChange();
96
91
  const inputRef = useRef<ReactCodeMirrorRef>(null);
97
92
 
98
93
  const {
@@ -261,10 +256,6 @@ export const AddCellWithAI: React.FC<{
261
256
  <div className="ml-auto flex items-center gap-1">
262
257
  {languageDropdown}
263
258
  <AIModelDropdown
264
- value={editModel}
265
- onSelect={(model) => {
266
- saveModelChange(model, "edit");
267
- }}
268
259
  triggerClassName="h-7 text-xs max-w-64"
269
260
  iconSize="small"
270
261
  forRole="edit"
@@ -301,6 +292,7 @@ interface PromptInputProps {
301
292
  onSubmit: (e: KeyboardEvent | undefined, value: string) => void;
302
293
  additionalCompletions?: AdditionalCompletions;
303
294
  maxHeight?: string;
295
+ onAddFiles?: (files: File[]) => void;
304
296
  }
305
297
 
306
298
  /**
@@ -318,6 +310,7 @@ export const PromptInput = ({
318
310
  onSubmit,
319
311
  onKeyDown,
320
312
  onClose,
313
+ onAddFiles,
321
314
  additionalCompletions,
322
315
  maxHeight,
323
316
  }: PromptInputProps) => {
@@ -346,7 +339,11 @@ export const PromptInput = ({
346
339
  return [
347
340
  autocompletion({}),
348
341
  markdownLanguage,
349
- resourceExtension(markdownLanguage.language, store),
342
+ resourceExtension({
343
+ language: markdownLanguage.language,
344
+ store,
345
+ onAddFiles,
346
+ }),
350
347
  markdownLanguage.language.data.of({
351
348
  autocomplete: additionalCompletionsSource,
352
349
  }),
@@ -411,7 +408,13 @@ export const PromptInput = ({
411
408
  },
412
409
  ]),
413
410
  ];
414
- }, [store, additionalCompletionsSource, handleSubmit, handleEscape]);
411
+ }, [
412
+ store,
413
+ onAddFiles,
414
+ additionalCompletionsSource,
415
+ handleSubmit,
416
+ handleEscape,
417
+ ]);
415
418
 
416
419
  return (
417
420
  <ReactCodeMirror
@@ -425,7 +428,9 @@ export const PromptInput = ({
425
428
  onChange={onChange}
426
429
  onKeyDown={onKeyDown}
427
430
  theme={theme === "dark" ? "dark" : "light"}
428
- placeholder={placeholder || "Generate with AI"}
431
+ placeholder={
432
+ placeholder || `Generate with AI, ${CONTEXT_TRIGGER} to include context`
433
+ }
429
434
  />
430
435
  );
431
436
  };
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { useCompletion } from "@ai-sdk/react";
4
4
  import { EditorView } from "@codemirror/view";
5
- import { Loader2Icon, SparklesIcon, XIcon } from "lucide-react";
5
+ import { AtSignIcon, Loader2Icon, SparklesIcon, XIcon } from "lucide-react";
6
6
  import React, { useEffect, useId, useState } from "react";
7
7
  import CodeMirrorMerge from "react-codemirror-merge";
8
8
  import { Button } from "@/components/ui/button";
@@ -12,6 +12,7 @@ import "./merge-editor.css";
12
12
  import { storePrompt } from "@marimo-team/codemirror-ai";
13
13
  import type { ReactCodeMirrorRef } from "@uiw/react-codemirror";
14
14
  import { useAtom } from "jotai";
15
+ import { AIModelDropdown } from "@/components/ai/ai-model-dropdown";
15
16
  import { Checkbox } from "@/components/ui/checkbox";
16
17
  import { Label } from "@/components/ui/label";
17
18
  import { Tooltip } from "@/components/ui/tooltip";
@@ -30,7 +31,7 @@ import {
30
31
  CompletionActions,
31
32
  createAiCompletionOnKeydown,
32
33
  } from "./completion-handlers";
33
- import { getAICompletionBody } from "./completion-utils";
34
+ import { addContextCompletion, getAICompletionBody } from "./completion-utils";
34
35
 
35
36
  const Original = CodeMirrorMerge.Original;
36
37
  const Modified = CodeMirrorMerge.Modified;
@@ -164,7 +165,7 @@ export const AiCompletionEditor: React.FC<Props> = ({
164
165
  <SparklesIcon className="text-(--blue-10) shrink-0" size={16} />
165
166
  <PromptInput
166
167
  inputRef={inputRef}
167
- className="h-full my-0 py-1.5"
168
+ className="h-full my-0 py-2"
168
169
  onClose={() => {
169
170
  declineChange();
170
171
  setCompletion("");
@@ -209,6 +210,24 @@ export const AiCompletionEditor: React.FC<Props> = ({
209
210
  size="xs"
210
211
  />
211
212
  )}
213
+ <div className="flex flex-row items-center gap-0.5 -ml-1.5 -mr-2">
214
+ <Tooltip content="Add context">
215
+ <Button
216
+ variant="text"
217
+ size="icon"
218
+ onClick={() => addContextCompletion(inputRef)}
219
+ >
220
+ <AtSignIcon className="h-3 w-3" />
221
+ </Button>
222
+ </Tooltip>
223
+ <AIModelDropdown
224
+ triggerClassName="h-7"
225
+ iconSize="small"
226
+ displayIconOnly={true}
227
+ forRole="edit"
228
+ />
229
+ </div>
230
+
212
231
  <div className="h-full w-px bg-border mx-2" />
213
232
  <Tooltip content="Include code from other cells">
214
233
  <div className="flex flex-row items-start gap-1 overflow-hidden">
@@ -25,10 +25,8 @@ export const createAiCompletionOnKeydown = (opts: {
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") {
29
- if (!isLoading && completion) {
30
- handleAcceptCompletion();
31
- }
28
+ if (metaKey && e.key === "Enter" && !isLoading && completion) {
29
+ handleAcceptCompletion();
32
30
  }
33
31
 
34
32
  // Mod+Shift+Delete should decline the completion
@@ -1,28 +1,41 @@
1
1
  /* Copyright 2024 Marimo. All rights reserved. */
2
2
 
3
- import type {
4
- Completion,
5
- CompletionContext,
6
- CompletionSource,
3
+ import {
4
+ type Completion,
5
+ type CompletionContext,
6
+ type CompletionSource,
7
+ startCompletion,
7
8
  } from "@codemirror/autocomplete";
9
+ import type { ReactCodeMirrorRef } from "@uiw/react-codemirror";
10
+ import type { FileUIPart } from "ai";
8
11
  import { getAIContextRegistry } from "@/core/ai/context/context";
9
12
  import { getCodes } from "@/core/codemirror/copilot/getCodes";
10
13
  import type { AiCompletionRequest } from "@/core/network/types";
11
14
  import { store } from "@/core/state/jotai";
12
15
  import { Logger } from "@/utils/Logger";
13
16
 
17
+ export const CONTEXT_TRIGGER = "@";
18
+
19
+ interface Opts {
20
+ input: string;
21
+ }
22
+
23
+ interface AICompletionBodyWithAttachments {
24
+ body: Omit<AiCompletionRequest, "language" | "prompt" | "code">;
25
+ attachments: FileUIPart[];
26
+ }
27
+
14
28
  /**
15
29
  * Gets the request body for the AI completion API.
16
30
  */
17
31
  export function getAICompletionBody({
18
32
  input,
19
- }: {
20
- input: string;
21
- }): Omit<AiCompletionRequest, "language" | "prompt" | "code"> {
33
+ }: Opts): Omit<AiCompletionRequest, "language" | "prompt" | "code"> {
22
34
  let contextString = "";
35
+
23
36
  // Skip if no '@' in the input
24
37
  if (input.includes("@")) {
25
- contextString = extractDatasetsAndVariables(input);
38
+ contextString = extractTaggedContext(input);
26
39
  Logger.debug("Included context", contextString);
27
40
  }
28
41
 
@@ -37,11 +50,49 @@ export function getAICompletionBody({
37
50
  }
38
51
 
39
52
  /**
40
- * Extracts datasets and variables from the input.
53
+ * Gets the request body and attachments for the AI completion API.
54
+ */
55
+ export async function getAICompletionBodyWithAttachments({
56
+ input,
57
+ }: Opts): Promise<AICompletionBodyWithAttachments> {
58
+ let contextString = "";
59
+ let attachments: FileUIPart[] = [];
60
+
61
+ // Skip if no '@' in the input
62
+ if (input.includes("@")) {
63
+ const registry = getAIContextRegistry(store);
64
+ const contextIds = registry.parseAllContextIds(input);
65
+
66
+ // Get context string
67
+ contextString = registry.formatContextForAI(contextIds);
68
+
69
+ // Get attachments
70
+ try {
71
+ attachments = await registry.getAttachmentsForContext(contextIds);
72
+ Logger.debug("Included attachments", attachments.length);
73
+ } catch (error) {
74
+ Logger.error("Error getting attachments:", error);
75
+ }
76
+ }
77
+
78
+ return {
79
+ body: {
80
+ includeOtherCode: getCodes(""),
81
+ context: {
82
+ plainText: contextString,
83
+ schema: [],
84
+ variables: [],
85
+ },
86
+ },
87
+ attachments,
88
+ };
89
+ }
90
+
91
+ /**
92
+ * Extracts datasets, variables and other context from the input.
41
93
  * References are with @<name> in the input.
42
- * Prioritizes datasets over variables if there's a name conflict.
43
94
  */
44
- function extractDatasetsAndVariables(input: string): string {
95
+ function extractTaggedContext(input: string): string {
45
96
  const registry = getAIContextRegistry(store);
46
97
  const contextIds = registry.parseAllContextIds(input);
47
98
  return registry.formatContextForAI(contextIds);
@@ -71,3 +122,26 @@ export function mentionsCompletionSource(
71
122
  };
72
123
  };
73
124
  }
125
+
126
+ export function addContextCompletion(
127
+ inputRef: React.RefObject<ReactCodeMirrorRef | null>,
128
+ ) {
129
+ if (inputRef.current?.view) {
130
+ const pos = inputRef.current.view.state.selection.main.from;
131
+ // Insert @ at the cursor position
132
+ inputRef.current.view.dispatch({
133
+ changes: {
134
+ from: pos,
135
+ to: pos,
136
+ insert: CONTEXT_TRIGGER,
137
+ },
138
+ selection: {
139
+ anchor: pos + 1,
140
+ head: pos + 1,
141
+ },
142
+ });
143
+ inputRef.current.view.focus();
144
+ // Trigger completion
145
+ startCompletion(inputRef.current.view);
146
+ }
147
+ }
@@ -0,0 +1,72 @@
1
+ /* Copyright 2024 Marimo. All rights reserved. */
2
+ import { BoxIcon, XIcon } from "lucide-react";
3
+ import { useEffect, useState } from "react";
4
+ import { useEvent } from "react-use-event-hook";
5
+ import { useAlertActions, useAlerts } from "@/core/alerts/state";
6
+ import { Banner } from "@/plugins/impl/common/error-banner";
7
+ import { Button } from "../../ui/button";
8
+
9
+ export const StartupLogsAlert: React.FC = () => {
10
+ const { startupLogsAlert } = useAlerts();
11
+ const { clearStartupLogsAlert } = useAlertActions();
12
+ const [hasCleared, setHasCleared] = useState(false);
13
+ const status = startupLogsAlert?.status;
14
+
15
+ const handleClear = useEvent(() => {
16
+ clearStartupLogsAlert();
17
+ setHasCleared(true);
18
+ });
19
+
20
+ const isDone = status === "done";
21
+ const isRunning = status === "start" || status === "append";
22
+
23
+ useEffect(() => {
24
+ let timeout: number | undefined;
25
+
26
+ if (isDone) {
27
+ // Dismiss after 5 seconds
28
+ timeout = window.setTimeout(() => handleClear(), 5000);
29
+ }
30
+
31
+ return () => {
32
+ if (timeout) {
33
+ window.clearTimeout(timeout);
34
+ }
35
+ };
36
+ }, [isDone]);
37
+
38
+ if (startupLogsAlert === null || hasCleared) {
39
+ return null;
40
+ }
41
+
42
+ return (
43
+ <div className="flex flex-col gap-4 mb-5 fixed top-5 left-12 min-w-[400px] max-w-[600px] z-200 opacity-95">
44
+ <Banner
45
+ kind="info"
46
+ className="flex flex-col rounded py-3 px-5 animate-in slide-in-from-left"
47
+ >
48
+ <div className="flex justify-between">
49
+ <span className="font-bold text-lg flex items-center mb-2">
50
+ <BoxIcon className="w-5 h-5 inline-block mr-2" />
51
+ {isRunning ? "Initializing..." : "Initialized"}
52
+ </span>
53
+ <Button
54
+ variant="text"
55
+ data-testid="remove-startup-logs-button"
56
+ size="icon"
57
+ onClick={handleClear}
58
+ >
59
+ <XIcon className="w-5 h-5" />
60
+ </Button>
61
+ </div>
62
+ <div className="flex flex-col gap-4 justify-between items-start text-muted-foreground text-base">
63
+ <div className="w-full">
64
+ <pre className="bg-muted p-3 rounded text-sm font-mono overflow-auto max-h-64 whitespace-pre-wrap">
65
+ {startupLogsAlert.content || "Starting startup script..."}
66
+ </pre>
67
+ </div>
68
+ </div>
69
+ </Banner>
70
+ </div>
71
+ );
72
+ };
@@ -1,6 +1,8 @@
1
1
  /* Copyright 2024 Marimo. All rights reserved. */
2
2
  import { DataSources } from "@/components/datasources/datasources";
3
3
 
4
- export const DataSourcesPanel: React.FC = () => {
4
+ const DataSourcesPanel: React.FC = () => {
5
5
  return <DataSources />;
6
6
  };
7
+
8
+ export default DataSourcesPanel;
@@ -6,7 +6,7 @@ import { useVariables } from "@/core/variables/state";
6
6
  import { cn } from "@/utils/cn";
7
7
  import { DependencyGraph } from "../../../dependency-graph/dependency-graph";
8
8
 
9
- export const DependencyGraphPanel: React.FC = () => {
9
+ const DependencyGraphPanel: React.FC = () => {
10
10
  const variables = useVariables();
11
11
  const cellIds = useCellIds();
12
12
  const [cells] = useCellDataAtoms();
@@ -21,3 +21,5 @@ export const DependencyGraphPanel: React.FC = () => {
21
21
  </div>
22
22
  );
23
23
  };
24
+
25
+ export default DependencyGraphPanel;
@@ -8,7 +8,7 @@ import "../../documentation.css";
8
8
  import { TextSearchIcon } from "lucide-react";
9
9
  import { PanelEmptyState } from "./empty-state";
10
10
 
11
- export const DocumentationPanel: React.FC = () => {
11
+ const DocumentationPanel: React.FC = () => {
12
12
  const { documentation } = useAtomValue(documentationAtom);
13
13
 
14
14
  if (!documentation) {
@@ -27,3 +27,5 @@ export const DocumentationPanel: React.FC = () => {
27
27
  </div>
28
28
  );
29
29
  };
30
+
31
+ export default DocumentationPanel;
@@ -7,7 +7,7 @@ import { useCellErrors } from "../../../../core/cells/cells";
7
7
  import { MarimoErrorOutput } from "../../output/MarimoErrorOutput";
8
8
  import { PanelEmptyState } from "./empty-state";
9
9
 
10
- export const ErrorsPanel: React.FC = () => {
10
+ const ErrorsPanel: React.FC = () => {
11
11
  const errors = useCellErrors();
12
12
 
13
13
  if (errors.length === 0) {
@@ -33,3 +33,5 @@ export const ErrorsPanel: React.FC = () => {
33
33
  </div>
34
34
  );
35
35
  };
36
+
37
+ export default ErrorsPanel;
@@ -6,7 +6,7 @@ import { cn } from "@/utils/cn";
6
6
  import { FileExplorer } from "../../file-tree/file-explorer";
7
7
  import { useFileExplorerUpload } from "../../file-tree/upload";
8
8
 
9
- export const FileExplorerPanel: React.FC = () => {
9
+ const FileExplorerPanel: React.FC = () => {
10
10
  const { ref, height = 1 } = useResizeObserver<HTMLDivElement>();
11
11
  const { getRootProps, getInputProps, isDragActive } = useFileExplorerUpload({
12
12
  noClick: true,
@@ -31,3 +31,5 @@ export const FileExplorerPanel: React.FC = () => {
31
31
  </div>
32
32
  );
33
33
  };
34
+
35
+ export default FileExplorerPanel;
@@ -14,7 +14,7 @@ interface Props {
14
14
  logs: CellLog[];
15
15
  }
16
16
 
17
- export const LogsPanel: React.FC = () => {
17
+ const LogsPanel: React.FC = () => {
18
18
  const logs = useCellLogs();
19
19
  const { clearLogs } = useCellActions();
20
20
 
@@ -46,6 +46,8 @@ export const LogsPanel: React.FC = () => {
46
46
  );
47
47
  };
48
48
 
49
+ export default LogsPanel;
50
+
49
51
  export const LogViewer: React.FC<Props> = ({ logs, className }) => {
50
52
  const hover = "opacity-70 group-hover:bg-(--gray-3) group-hover:opacity-100";
51
53
  return (
@@ -13,7 +13,7 @@ import {
13
13
  useActiveOutline,
14
14
  } from "./outline/useActiveOutline";
15
15
 
16
- export const OutlinePanel: React.FC = () => {
16
+ const OutlinePanel: React.FC = () => {
17
17
  const { items } = useAtomValue(notebookOutline);
18
18
  const headerElements = useMemo(() => findOutlineElements(items), [items]);
19
19
  const { activeHeaderId, activeOccurrences } =
@@ -37,3 +37,5 @@ export const OutlinePanel: React.FC = () => {
37
37
  />
38
38
  );
39
39
  };
40
+
41
+ export default OutlinePanel;
@@ -65,7 +65,7 @@ const PackageActionButton: React.FC<{
65
65
  );
66
66
  };
67
67
 
68
- export const PackagesPanel: React.FC = () => {
68
+ const PackagesPanel: React.FC = () => {
69
69
  const [config] = useResolvedMarimoConfig();
70
70
  const packageManager = config.package_management.manager;
71
71
  const { getDependencyTree, getPackageList } = useRequestClient();
@@ -162,6 +162,8 @@ export const PackagesPanel: React.FC = () => {
162
162
  );
163
163
  };
164
164
 
165
+ export default PackagesPanel;
166
+
165
167
  const InstallPackageForm: React.FC<{
166
168
  packageManager: string;
167
169
  onSuccess: () => void;
@@ -411,7 +413,7 @@ const RemoveButton: React.FC<{
411
413
  };
412
414
 
413
415
  const DependencyTree: React.FC<{
414
- tree?: DependencyTreeNode;
416
+ tree: DependencyTreeNode | null;
415
417
  error?: Error | null;
416
418
  onSuccess: () => void;
417
419
  }> = ({ tree, error, onSuccess }) => {