@marimo-team/islands 0.23.7-dev42 → 0.23.7-dev44

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/islands",
3
- "version": "0.23.7-dev42",
3
+ "version": "0.23.7-dev44",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -18,14 +18,16 @@ import { assertNever } from "@/utils/assertNever";
18
18
  import { asRemoteURL, useRuntimeManager } from "@/core/runtime/config";
19
19
  import { API } from "@/core/network/api";
20
20
 
21
- type AgentTab = "claude" | "codex" | "opencode";
21
+ type AgentTab = "claude" | "codex" | "opencode" | "prompt";
22
+
23
+ const TERMINAL_TABS = ["claude", "codex", "opencode"] as const;
22
24
 
23
25
  function getMarimoCommand(): string {
24
26
  return import.meta.env.DEV ? "uv run marimo" : "uvx marimo@latest";
25
27
  }
26
28
 
27
- function getPromptCommand(
28
- agent: AgentTab,
29
+ function getTerminalCommand(
30
+ agent: Exclude<AgentTab, "prompt">,
29
31
  url: string,
30
32
  withToken: boolean,
31
33
  ): string {
@@ -43,6 +45,21 @@ function getPromptCommand(
43
45
  }
44
46
  }
45
47
 
48
+ function getRawPrompt(url: string, token: string | null): string {
49
+ const tokenHint = token
50
+ ? `\n\nUse this auth token when calling \`execute-code.sh\`: \`execute-code.sh --url '${url}' --token '${token}'\`.`
51
+ : "";
52
+ return [
53
+ "Use the /marimo-pair skill to pair-program on a running marimo notebook.",
54
+ "",
55
+ `Connect to the notebook at: ${url}`,
56
+ "",
57
+ `Use \`execute-code.sh --url ${url}\` from the marimo-pair skill to execute code in the notebook.${tokenHint}`,
58
+ "",
59
+ "Once you are connected, send a fun toast (mo.status.toast(...)) to the user inside marimo letting them know you're ready to pair.",
60
+ ].join("\n");
61
+ }
62
+
46
63
  function maskToken(token: string): string {
47
64
  if (token.length <= 4) {
48
65
  return "****";
@@ -52,6 +69,13 @@ function maskToken(token: string): string {
52
69
 
53
70
  const SKILL_INSTALL = "npx skills add marimo-team/marimo-pair";
54
71
 
72
+ const AGENT_LABELS: Record<AgentTab, string> = {
73
+ claude: "Claude",
74
+ codex: "Codex",
75
+ opencode: "OpenCode",
76
+ prompt: "Prompt",
77
+ };
78
+
55
79
  function useAuthToken(): string | null {
56
80
  const [token, setToken] = useState<string | null>(null);
57
81
  useEffect(() => {
@@ -75,10 +99,9 @@ export const PairWithAgentModal: React.FC<{
75
99
  const authToken = useAuthToken();
76
100
  const hasToken = Boolean(authToken);
77
101
  const remoteUrl = runtimeManager.httpURL.toString();
78
- const promptCommand = getPromptCommand(activeTab, remoteUrl, hasToken);
79
102
 
80
103
  return (
81
- <DialogContent className="sm:max-w-lg">
104
+ <DialogContent className="sm:max-w-2xl">
82
105
  <DialogHeader>
83
106
  <DialogTitle>Pair with an agent</DialogTitle>
84
107
  <DialogDescription>
@@ -96,49 +119,75 @@ export const PairWithAgentModal: React.FC<{
96
119
  </DialogHeader>
97
120
 
98
121
  <div className="flex flex-col gap-4 py-2">
99
- <div className="flex flex-col gap-2">
100
- <span className="text-sm font-medium">1. Install the skill</span>
101
- <CommandBlock command={SKILL_INSTALL} />
102
- </div>
103
-
104
- <div className="flex flex-col gap-2">
105
- <span className="text-sm font-medium">2. Run in your terminal</span>
106
- <Tabs
107
- value={activeTab}
108
- onValueChange={(v) => setActiveTab(v as AgentTab)}
109
- >
110
- <TabsList className="w-full">
111
- <TabsTrigger value="claude" className="flex-1">
112
- Claude
113
- </TabsTrigger>
114
- <TabsTrigger value="codex" className="flex-1">
115
- Codex
122
+ <Tabs
123
+ value={activeTab}
124
+ onValueChange={(v) => setActiveTab(v as AgentTab)}
125
+ >
126
+ <TabsList className="w-full">
127
+ {(["claude", "codex", "opencode", "prompt"] as const).map((tab) => (
128
+ <TabsTrigger key={tab} value={tab} className="flex-1">
129
+ {AGENT_LABELS[tab]}
116
130
  </TabsTrigger>
117
- <TabsTrigger value="opencode" className="flex-1">
118
- OpenCode
119
- </TabsTrigger>
120
- </TabsList>
131
+ ))}
132
+ </TabsList>
121
133
 
122
- <TabsContent value="claude" className="mt-3">
123
- <CommandBlock command={promptCommand} />
124
- </TabsContent>
125
- <TabsContent value="codex" className="mt-3">
126
- <CommandBlock command={promptCommand} />
134
+ {TERMINAL_TABS.map((tab) => (
135
+ <TabsContent
136
+ key={tab}
137
+ value={tab}
138
+ className="mt-4 flex flex-col gap-4"
139
+ >
140
+ <Step
141
+ index={1}
142
+ title="Install the skill"
143
+ hint="Run once per machine."
144
+ >
145
+ <CommandBlock command={SKILL_INSTALL} />
146
+ </Step>
147
+ <Step index={2} title="Run in your terminal">
148
+ <CommandBlock
149
+ command={getTerminalCommand(tab, remoteUrl, hasToken)}
150
+ />
151
+ </Step>
152
+ {hasToken && authToken && (
153
+ <Step index={3} title="Paste when prompted for a token">
154
+ <CommandBlock
155
+ command={authToken}
156
+ display={maskToken(authToken)}
157
+ />
158
+ </Step>
159
+ )}
127
160
  </TabsContent>
128
- <TabsContent value="opencode" className="mt-3">
129
- <CommandBlock command={promptCommand} />
130
- </TabsContent>
131
- </Tabs>
132
- </div>
133
-
134
- {hasToken && authToken && (
135
- <div className="flex flex-col gap-2">
136
- <span className="text-sm font-medium">
137
- 3. Paste when prompted for token
138
- </span>
139
- <CommandBlock command={authToken} display={maskToken(authToken)} />
140
- </div>
141
- )}
161
+ ))}
162
+
163
+ <TabsContent value="prompt" className="mt-4 flex flex-col gap-4">
164
+ <Step
165
+ index={1}
166
+ title="Make sure the marimo-pair skill is available to your agent"
167
+ hint="Skip if your agent already has it."
168
+ >
169
+ <CommandBlock command={SKILL_INSTALL} />
170
+ </Step>
171
+ <Step
172
+ index={2}
173
+ title="Copy this prompt into your agent"
174
+ hint={
175
+ hasToken
176
+ ? "Includes your auth token — keep it private."
177
+ : undefined
178
+ }
179
+ >
180
+ <CommandBlock
181
+ command={getRawPrompt(remoteUrl, authToken)}
182
+ display={getRawPrompt(
183
+ remoteUrl,
184
+ authToken ? maskToken(authToken) : null,
185
+ )}
186
+ multiline={true}
187
+ />
188
+ </Step>
189
+ </TabsContent>
190
+ </Tabs>
142
191
  </div>
143
192
 
144
193
  <DialogFooter>
@@ -150,10 +199,28 @@ export const PairWithAgentModal: React.FC<{
150
199
  );
151
200
  };
152
201
 
153
- const CommandBlock: React.FC<{ command: string; display?: string }> = ({
154
- command,
155
- display,
156
- }) => {
202
+ const Step: React.FC<{
203
+ index: number;
204
+ title: string;
205
+ hint?: string;
206
+ children: React.ReactNode;
207
+ }> = ({ index, title, hint, children }) => (
208
+ <div className="flex flex-col gap-2">
209
+ <div className="flex items-baseline gap-2">
210
+ <span className="text-sm font-medium">
211
+ {index}. {title}
212
+ </span>
213
+ {hint && <span className="text-xs text-muted-foreground">{hint}</span>}
214
+ </div>
215
+ {children}
216
+ </div>
217
+ );
218
+
219
+ const CommandBlock: React.FC<{
220
+ command: string;
221
+ display?: string;
222
+ multiline?: boolean;
223
+ }> = ({ command, display, multiline = false }) => {
157
224
  const [copied, setCopied] = useState(false);
158
225
 
159
226
  const copy = Events.stopPropagation(async (e) => {
@@ -163,6 +230,30 @@ const CommandBlock: React.FC<{ command: string; display?: string }> = ({
163
230
  setTimeout(() => setCopied(false), 2000);
164
231
  });
165
232
 
233
+ if (multiline) {
234
+ return (
235
+ <div className="relative rounded-md bg-muted">
236
+ <pre className="max-h-64 overflow-auto whitespace-pre-wrap break-words px-3 py-2 pr-10 font-mono text-xs select-all">
237
+ {display ?? command}
238
+ </pre>
239
+ <Tooltip content="Copied!" open={copied}>
240
+ <Button
241
+ onClick={copy}
242
+ size="xs"
243
+ variant="ghost"
244
+ className="absolute right-1 top-1"
245
+ >
246
+ {copied ? (
247
+ <CheckIcon size={14} strokeWidth={1.5} />
248
+ ) : (
249
+ <CopyIcon size={14} strokeWidth={1.5} />
250
+ )}
251
+ </Button>
252
+ </Tooltip>
253
+ </div>
254
+ );
255
+ }
256
+
166
257
  return (
167
258
  <div className="flex items-center gap-2 rounded-md bg-muted px-3 py-2 font-mono text-xs">
168
259
  <code className="flex-1 select-all break-words">