@marimo-team/islands 0.22.1-dev37 → 0.22.1-dev38
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/package.json
CHANGED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { useAtomValue } from "jotai";
|
|
4
|
+
import { CheckIcon, CopyIcon } from "lucide-react";
|
|
5
|
+
import React, { useState } from "react";
|
|
6
|
+
import { Button } from "@/components/ui/button";
|
|
7
|
+
import {
|
|
8
|
+
DialogContent,
|
|
9
|
+
DialogDescription,
|
|
10
|
+
DialogFooter,
|
|
11
|
+
DialogHeader,
|
|
12
|
+
DialogTitle,
|
|
13
|
+
} from "@/components/ui/dialog";
|
|
14
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
15
|
+
import { runtimeConfigAtom } from "@/core/runtime/config";
|
|
16
|
+
import { copyToClipboard } from "@/utils/copy";
|
|
17
|
+
import { Events } from "@/utils/events";
|
|
18
|
+
import { Tooltip } from "@/components/ui/tooltip";
|
|
19
|
+
import type { RuntimeConfig } from "@/core/runtime/types";
|
|
20
|
+
|
|
21
|
+
type AgentTab = "claude" | "codex" | "opencode";
|
|
22
|
+
|
|
23
|
+
function buildRemoteUrl(config: RuntimeConfig) {
|
|
24
|
+
const url = new URL(config.url);
|
|
25
|
+
if (config.authToken) {
|
|
26
|
+
url.searchParams.set("auth", config.authToken);
|
|
27
|
+
}
|
|
28
|
+
return url.toString();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getPromptCommand(agent: AgentTab, remoteUrl: string): string {
|
|
32
|
+
const command = import.meta.env.DEV ? "uv run marimo" : "uvx marimo@latest";
|
|
33
|
+
const base = `${command} pair prompt --url '${remoteUrl}'`;
|
|
34
|
+
switch (agent) {
|
|
35
|
+
case "claude":
|
|
36
|
+
return `claude "$(${base} --claude)"`;
|
|
37
|
+
case "codex":
|
|
38
|
+
return `codex "$(${base} --codex)"`;
|
|
39
|
+
case "opencode":
|
|
40
|
+
return `opencode "$(${base} --opencode)"`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const SKILL_INSTALL = "npx skills add marimo-team/marimo-pair";
|
|
45
|
+
|
|
46
|
+
export const PairWithAgentModal: React.FC<{
|
|
47
|
+
onClose: () => void;
|
|
48
|
+
}> = ({ onClose }) => {
|
|
49
|
+
const [activeTab, setActiveTab] = useState<AgentTab>("claude");
|
|
50
|
+
const runtimeConfig = useAtomValue(runtimeConfigAtom);
|
|
51
|
+
const remoteUrl = buildRemoteUrl(runtimeConfig);
|
|
52
|
+
const promptCommand = getPromptCommand(activeTab, remoteUrl);
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<DialogContent className="sm:max-w-lg">
|
|
56
|
+
<DialogHeader>
|
|
57
|
+
<DialogTitle>Pair with an agent</DialogTitle>
|
|
58
|
+
<DialogDescription>
|
|
59
|
+
Use an AI coding agent to pair-program on this notebook.{" "}
|
|
60
|
+
<a
|
|
61
|
+
href="https://links.marimo.app/marimo-pair"
|
|
62
|
+
target="_blank"
|
|
63
|
+
rel="noopener noreferrer"
|
|
64
|
+
className="underline"
|
|
65
|
+
>
|
|
66
|
+
Learn more
|
|
67
|
+
</a>
|
|
68
|
+
.
|
|
69
|
+
</DialogDescription>
|
|
70
|
+
</DialogHeader>
|
|
71
|
+
|
|
72
|
+
<div className="flex flex-col gap-4 py-2">
|
|
73
|
+
<div className="flex flex-col gap-2">
|
|
74
|
+
<span className="text-sm font-medium">1. Install the skill</span>
|
|
75
|
+
<CommandBlock command={SKILL_INSTALL} />
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<div className="flex flex-col gap-2">
|
|
79
|
+
<span className="text-sm font-medium">2. Run in your terminal</span>
|
|
80
|
+
<Tabs
|
|
81
|
+
value={activeTab}
|
|
82
|
+
onValueChange={(v) => setActiveTab(v as AgentTab)}
|
|
83
|
+
>
|
|
84
|
+
<TabsList className="w-full">
|
|
85
|
+
<TabsTrigger value="claude" className="flex-1">
|
|
86
|
+
Claude
|
|
87
|
+
</TabsTrigger>
|
|
88
|
+
<TabsTrigger value="codex" className="flex-1">
|
|
89
|
+
Codex
|
|
90
|
+
</TabsTrigger>
|
|
91
|
+
<TabsTrigger value="opencode" className="flex-1">
|
|
92
|
+
OpenCode
|
|
93
|
+
</TabsTrigger>
|
|
94
|
+
</TabsList>
|
|
95
|
+
|
|
96
|
+
<TabsContent value="claude" className="mt-3">
|
|
97
|
+
<CommandBlock command={promptCommand} />
|
|
98
|
+
</TabsContent>
|
|
99
|
+
<TabsContent value="codex" className="mt-3">
|
|
100
|
+
<CommandBlock command={promptCommand} />
|
|
101
|
+
</TabsContent>
|
|
102
|
+
<TabsContent value="opencode" className="mt-3">
|
|
103
|
+
<CommandBlock command={promptCommand} />
|
|
104
|
+
</TabsContent>
|
|
105
|
+
</Tabs>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<DialogFooter>
|
|
110
|
+
<Button variant="secondary" onClick={onClose}>
|
|
111
|
+
Close
|
|
112
|
+
</Button>
|
|
113
|
+
</DialogFooter>
|
|
114
|
+
</DialogContent>
|
|
115
|
+
);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const CommandBlock: React.FC<{ command: string }> = ({ command }) => {
|
|
119
|
+
const [copied, setCopied] = useState(false);
|
|
120
|
+
|
|
121
|
+
const copy = Events.stopPropagation(async (e) => {
|
|
122
|
+
e.preventDefault();
|
|
123
|
+
await copyToClipboard(command);
|
|
124
|
+
setCopied(true);
|
|
125
|
+
setTimeout(() => setCopied(false), 2000);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<div className="flex items-center gap-2 rounded-md bg-muted px-3 py-2 font-mono text-xs">
|
|
130
|
+
<code className="flex-1 select-all break-words">{command}</code>
|
|
131
|
+
<Tooltip content="Copied!" open={copied}>
|
|
132
|
+
<Button onClick={copy} size="xs" variant="ghost">
|
|
133
|
+
{copied ? (
|
|
134
|
+
<CheckIcon size={14} strokeWidth={1.5} />
|
|
135
|
+
) : (
|
|
136
|
+
<CopyIcon size={14} strokeWidth={1.5} />
|
|
137
|
+
)}
|
|
138
|
+
</Button>
|
|
139
|
+
</Tooltip>
|
|
140
|
+
</div>
|
|
141
|
+
);
|
|
142
|
+
};
|
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
PresentationIcon,
|
|
37
37
|
SettingsIcon,
|
|
38
38
|
Share2Icon,
|
|
39
|
+
SparklesIcon,
|
|
39
40
|
Undo2Icon,
|
|
40
41
|
XCircleIcon,
|
|
41
42
|
YoutubeIcon,
|
|
@@ -45,6 +46,7 @@ import { settingDialogAtom } from "@/components/app-config/state";
|
|
|
45
46
|
import { MarkdownIcon } from "@/components/editor/cell/code/icons";
|
|
46
47
|
import { useImperativeModal } from "@/components/modal/ImperativeModal";
|
|
47
48
|
import { renderShortcut } from "@/components/shortcuts/renderShortcut";
|
|
49
|
+
import { PairWithAgentModal } from "@/components/editor/actions/pair-with-agent-modal";
|
|
48
50
|
import { ShareStaticNotebookModal } from "@/components/static-html/share-modal";
|
|
49
51
|
import { toast } from "@/components/ui/use-toast";
|
|
50
52
|
import {
|
|
@@ -343,6 +345,14 @@ export function useNotebookActions() {
|
|
|
343
345
|
],
|
|
344
346
|
},
|
|
345
347
|
|
|
348
|
+
{
|
|
349
|
+
icon: <SparklesIcon size={14} strokeWidth={1.5} />,
|
|
350
|
+
label: "Pair with an agent",
|
|
351
|
+
handle: async () => {
|
|
352
|
+
openModal(<PairWithAgentModal onClose={closeModal} />);
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
|
|
346
356
|
{
|
|
347
357
|
icon: <Share2Icon size={14} strokeWidth={1.5} />,
|
|
348
358
|
label: "Share",
|