@daydreamui/figma 0.0.0-stage → 0.3.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/index.js ADDED
@@ -0,0 +1,1545 @@
1
+ import { className, createComponent, delegateEvents, effect, insert, memo, readShallow, ref, setAttribute, style, template } from "@solidjs/web";
2
+ import { For, Show, createMemo, createSignal, onCleanup, onSettled, untrack } from "solid-js";
3
+ function chosenAgent(conversationAgent, listed) {
4
+ const list = listed?.agents ?? [];
5
+ const pick = (id) => id == null ? void 0 : list.find((agent) => agent.id === id);
6
+ return pick(conversationAgent) ?? pick(listed?.project) ?? pick(listed?.last) ?? list.find((agent) => agent.installed) ?? null;
7
+ }
8
+ function isSwitch(agentId, conversationAgent, listed) {
9
+ const current = conversationAgent ?? listed?.project ?? null;
10
+ return current !== null && current !== agentId;
11
+ }
12
+ var said = (error) => error instanceof Error ? error.message : String(error);
13
+ var isBusy = (status) => status?.phase === "ready" && status.busy;
14
+ function createAgentSession(dd, projection) {
15
+ const [state, setState] = createSignal(null);
16
+ const [agents, setAgents] = createSignal(null);
17
+ const [available, setAvailable] = createSignal(null);
18
+ const [problem, setProblemSignal] = createSignal(null);
19
+ let stateNow = null;
20
+ let agentsNow = null;
21
+ let availableNow = null;
22
+ let problemNow = null;
23
+ let heard = 0;
24
+ const listeners = [];
25
+ const setProblem = (why) => {
26
+ problemNow = why;
27
+ setProblemSignal(why);
28
+ };
29
+ const ask = async (work) => {
30
+ setProblem(null);
31
+ try {
32
+ return {
33
+ ok: true,
34
+ value: await work()
35
+ };
36
+ } catch (error) {
37
+ const why = said(error);
38
+ setProblem(why);
39
+ return {
40
+ ok: false,
41
+ why
42
+ };
43
+ }
44
+ };
45
+ const list = async () => {
46
+ const answer = await ask(() => dd.agent.agents());
47
+ if (!answer.ok) return false;
48
+ agentsNow = answer.value;
49
+ setAgents(answer.value);
50
+ return true;
51
+ };
52
+ const start = async (agentId, options = {}, retry = false) => {
53
+ const answer = await ask(() => dd.agent.start(agentId, options));
54
+ if (retry || answer.ok && answer.value.phase === "failed" && answer.value.install !== null) await list();
55
+ return answer;
56
+ };
57
+ let read;
58
+ const firstRead = new Promise((resolve) => {
59
+ read = resolve;
60
+ });
61
+ const changed = (change) => {
62
+ const before = stateNow;
63
+ const next = change.type === "thread" ? projection.fromThread(change.thread) : before === null ? null : projection.applyChange(before, change);
64
+ if (next === null) return;
65
+ stateNow = next;
66
+ setState(() => next);
67
+ heard++;
68
+ read();
69
+ for (const listener of listeners) listener(change, next, before);
70
+ };
71
+ let following = false;
72
+ const follow = async (options = {}) => {
73
+ const runs = await dd.agent.available().catch(() => false);
74
+ availableNow = runs;
75
+ setAvailable(runs);
76
+ if (!runs) return false;
77
+ if (!following) {
78
+ if (options.beforeFollow !== void 0 && !await options.beforeFollow()) return false;
79
+ following = true;
80
+ dd.agent.follow(changed);
81
+ }
82
+ if (options.waitForThread !== false) await firstRead;
83
+ return true;
84
+ };
85
+ const statusNow = () => stateNow?.status;
86
+ return {
87
+ state,
88
+ agents,
89
+ available,
90
+ problem,
91
+ ask,
92
+ list,
93
+ start,
94
+ follow,
95
+ setProblem,
96
+ chosen: () => chosenAgent(state()?.status.agentId, agents()),
97
+ now: {
98
+ state: () => stateNow,
99
+ agents: () => agentsNow,
100
+ status: statusNow,
101
+ available: () => availableNow,
102
+ problem: () => problemNow,
103
+ heard: () => heard,
104
+ chosen: () => chosenAgent(statusNow()?.agentId, agentsNow)
105
+ },
106
+ onChange(listener) {
107
+ listeners.push(listener);
108
+ }
109
+ };
110
+ }
111
+ function sameMessage(before, next) {
112
+ return before === null || next === null || before === next;
113
+ }
114
+ function createRoundFold(applyUpdate) {
115
+ const lastUserAt = (entries) => {
116
+ for (let at = entries.length - 1; at >= 0; at--) if (entries[at].kind === "user") return at;
117
+ return 0;
118
+ };
119
+ return {
120
+ fromThread(thread) {
121
+ const entries = thread.updates.reduce(applyUpdate, []);
122
+ return {
123
+ status: thread.status,
124
+ entries,
125
+ permissions: thread.permissions,
126
+ turnStart: isBusy(thread.status) ? lastUserAt(entries) : null
127
+ };
128
+ },
129
+ applyChange(state, change) {
130
+ switch (change.type) {
131
+ case "status": return {
132
+ ...state,
133
+ status: change.status,
134
+ turnStart: !isBusy(change.status) ? null : isBusy(state.status) ? state.turnStart : state.entries.length
135
+ };
136
+ case "update": return {
137
+ ...state,
138
+ entries: applyUpdate(state.entries, change.update)
139
+ };
140
+ case "permission": return {
141
+ ...state,
142
+ permissions: [...state.permissions, change.ask]
143
+ };
144
+ case "resolved": return {
145
+ ...state,
146
+ permissions: state.permissions.filter((ask) => ask.requestId !== change.requestId)
147
+ };
148
+ case "cleared": return {
149
+ ...state,
150
+ entries: [],
151
+ permissions: [],
152
+ turnStart: isBusy(state.status) ? 0 : null
153
+ };
154
+ }
155
+ }
156
+ };
157
+ }
158
+ var NODE_ID = /^\d+[:-]\d+$/;
159
+ function nodeIdFromUrl(urlOrId) {
160
+ const given = urlOrId.trim();
161
+ if (NODE_ID.test(given)) return given.replace("-", ":");
162
+ let url;
163
+ try {
164
+ url = new URL(given);
165
+ } catch {
166
+ return null;
167
+ }
168
+ const id = url.searchParams.get("node-id");
169
+ return id !== null && NODE_ID.test(id) ? id.replace("-", ":") : null;
170
+ }
171
+ var NOT_REACHABLE = "Figma is not reachable: open Figma desktop and enable the Dev Mode MCP server in its preferences";
172
+ function createProbe({ ask, answer, target, debounceMs = 400 }) {
173
+ let running = null;
174
+ let again = false;
175
+ let timer;
176
+ let gone = false;
177
+ const run = async () => {
178
+ let reachable;
179
+ try {
180
+ reachable = await ask() === true;
181
+ } catch {
182
+ reachable = false;
183
+ }
184
+ if (!gone) answer(reachable);
185
+ };
186
+ const probe = (soon = false) => {
187
+ if (gone) return;
188
+ if (soon) {
189
+ clearTimeout(timer);
190
+ timer = setTimeout(() => probe(), debounceMs);
191
+ return;
192
+ }
193
+ if (running !== null) {
194
+ again = true;
195
+ return;
196
+ }
197
+ running = run().finally(() => {
198
+ running = null;
199
+ if (again) {
200
+ again = false;
201
+ probe();
202
+ }
203
+ });
204
+ };
205
+ const focused = () => probe(true);
206
+ target?.addEventListener("focus", focused);
207
+ return {
208
+ probe,
209
+ async settled() {
210
+ while (running !== null) await running;
211
+ },
212
+ dispose() {
213
+ gone = true;
214
+ clearTimeout(timer);
215
+ target?.removeEventListener("focus", focused);
216
+ }
217
+ };
218
+ }
219
+ var REFERENCE_KIND = "daydream.figma.reference";
220
+ var LEGACY_REFERENCE_KIND = "mrbavio.figma.reference";
221
+ var MAX_REFERENCE_WIDTH = 1200;
222
+ var tooManySelected = (count) => `${count} frames are selected in Figma; a gesture takes at most 12 at once. Select fewer.`;
223
+ function askable(frames) {
224
+ const kept = frames.filter((frame) => frame.id !== null && nodeIdFromUrl(frame.id) !== null);
225
+ return {
226
+ frames: kept,
227
+ leftOut: frames.length - kept.length
228
+ };
229
+ }
230
+ function leftOutSentence(leftOut) {
231
+ if (leftOut === 0) return null;
232
+ return leftOut === 1 ? "1 layer inside an instance was left out; select its frame." : `${leftOut} layers inside instances were left out; select their frames.`;
233
+ }
234
+ var messageOf = (error) => error instanceof Error ? error.message : String(error);
235
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
236
+ var nullOrString = (value) => value === null || typeof value === "string";
237
+ var nullOrNumber = (value) => value === null || typeof value === "number" && Number.isFinite(value);
238
+ function isScreenshotAnswer(value) {
239
+ return isRecord(value) && typeof value["mimeType"] === "string" && typeof value["base64"] === "string" && nullOrString(value["name"]) && nullOrString(value["nodeId"]);
240
+ }
241
+ function isFrameAnswer(value) {
242
+ return isRecord(value) && nullOrString(value["id"]) && nullOrString(value["name"]) && nullOrNumber(value["width"]) && nullOrNumber(value["height"]);
243
+ }
244
+ function isSelectionAnswer(value) {
245
+ if (!isRecord(value)) return false;
246
+ switch (value["kind"]) {
247
+ case "frames": return Array.isArray(value["frames"]) && value["frames"].every((frame) => isFrameAnswer(frame) && frame.id !== null);
248
+ case "none":
249
+ case "late": return true;
250
+ case "unreachable": return typeof value["said"] === "string";
251
+ default: return false;
252
+ }
253
+ }
254
+ var PROJECT_FILE = /^assets\/(?:[A-Za-z0-9_-]{1,80}\/){0,7}[A-Za-z0-9_-]{1,80}\.[A-Za-z0-9]{1,8}$/;
255
+ var FIELDS = [
256
+ "src",
257
+ "name",
258
+ "nodeId",
259
+ "link"
260
+ ];
261
+ function referencePayloadProblem(raw, where) {
262
+ if (!isRecord(raw)) return `${where} is not a Figma image payload`;
263
+ if (Object.keys(raw).find((key) => !FIELDS.includes(key)) !== void 0) return `${where} may only contain src, name and nodeId`;
264
+ if (typeof raw["src"] !== "string" || !PROJECT_FILE.test(raw["src"])) return `${where}.src must be a file of the project's own, assets/<file>`;
265
+ if (typeof raw["name"] !== "string" || raw["name"].trim() === "") return `${where}.name must be a non-empty string`;
266
+ if (raw["nodeId"] !== null && (typeof raw["nodeId"] !== "string" || raw["nodeId"] === "")) return `${where}.nodeId must be a non-empty string or null`;
267
+ if ("link" in raw && !nullOrString(raw["link"])) return `${where}.link, kept by an earlier build, must be a string or null`;
268
+ return null;
269
+ }
270
+ function describeReference(payload) {
271
+ return `Figma image · ${payload.name} · ${payload.nodeId ?? "no node id"}`;
272
+ }
273
+ var PROMPT_PREFIX = `Figma: transcribe `;
274
+ var named = (frame) => frame.name === null ? "the frame" : JSON.stringify(frame.name);
275
+ function pagePrompt(frames) {
276
+ const several = frames.length > 1;
277
+ const one = frames[0] ?? {
278
+ nodeId: "",
279
+ name: null,
280
+ width: null
281
+ };
282
+ const node = JSON.stringify(one.nodeId);
283
+ const width = one.width === null ? "" : ` The frame is ${Math.round(one.width)} px wide; make the page that wide.`;
284
+ return [
285
+ several ? `${PROMPT_PREFIX}${frames.length} frames as pages of this project.` : `${PROMPT_PREFIX}the frame${one.name === null ? "" : ` ${named(one)}`} (node ${node}) as a page of this project.`,
286
+ ...several ? [`The frames, selected in Figma: ${frames.map((frame) => `${named(frame)} (node ${JSON.stringify(frame.nodeId)})`).join(", ")}.`] : [],
287
+ several ? `Figma desktop is open on this machine and Daydream's figma_* tools read it. For each frame, with its node: call figma_metadata for the frame's tree (ids, names, boxes; the root's width is the page's), figma_design_context for Figma's rendering of it as code — React and Tailwind, to transcribe into plain HTML and CSS, never to ship as it is — and figma_screenshot for how it looks (the PNG is written under .daydream/figma/). The images its code names are served at http://localhost:3845/assets/… only while Figma runs: copy each one a page uses into the project's assets/ and link it from there.` : `Figma desktop is open on this machine and Daydream's figma_* tools read it. Call figma_metadata with node ${node} for the frame's tree (ids, names, boxes), figma_design_context with node ${node} for Figma's rendering of it as code — React and Tailwind, to transcribe into plain HTML and CSS, never to ship as it is — and figma_screenshot with node ${node} for how it looks (the PNG is written under .daydream/figma/). The images its code names are served at http://localhost:3845/assets/… only while Figma runs: copy each one the page uses into the project's assets/ and link it from there.`,
288
+ `Build ${several ? "one page per frame" : "the page"} with the dream-author workflow: a draft whose HTML and CSS are the project's own — real elements, no framework, no utility classes — finalized as a new page titled ${several ? "after its frame, as wide as the frame" : one.name === null ? "after the frame" : named(one)}.${width} Match ${several ? "each" : "the"} frame's layout, type, colours and spacing; what the design leaves out stays out. Write the CSS as a person writes it — one rule per block, one declaration per line, named classes — and lay the page out in flow with flex and grid, as Figma's auto layout maps; position absolutely only what the design arranges freely, never a whole frame of boxes at coordinates.`,
289
+ several ? `Before you finish, open each screenshot figma_screenshot wrote (the path it answered, under .daydream/figma/) with your image-reading tool and compare that page as rendered against it — layout, type, colours, spacing — and fix what differs; then say in one line per page what still differs, or that nothing does. Do not end the turn without having looked at every one.` : `Before you finish, open the screenshot figma_screenshot wrote (the path it answered, under .daydream/figma/) with your image-reading tool and compare the page as rendered against it — layout, type, colours, spacing — and fix what differs; then say in one line what still differs, or that nothing does. Do not end the turn without having looked at it.`
290
+ ].join("\n");
291
+ }
292
+ var isPagePrompt = (text) => text.startsWith(PROMPT_PREFIX);
293
+ function textOf(block) {
294
+ return block.type === "text" ? block.text : "";
295
+ }
296
+ var HTML = /\.html?$/i;
297
+ function pagesOf(call) {
298
+ const paths = [...(call.content ?? []).flatMap((c) => c.type === "diff" ? [c.path] : []), ...(call.locations ?? []).map((l) => l.path)];
299
+ return paths.filter((path, at) => HTML.test(path) && paths.indexOf(path) === at);
300
+ }
301
+ function replaceLast(entries, entry) {
302
+ return [...entries.slice(0, -1), entry];
303
+ }
304
+ function upsertStep(entries, id, made) {
305
+ const at = entries.findIndex((e) => e.kind === "tool" && e.step.id === id);
306
+ if (at === -1) return [...entries, {
307
+ kind: "tool",
308
+ step: made(null)
309
+ }];
310
+ const next = [...entries];
311
+ next[at] = {
312
+ kind: "tool",
313
+ step: made(entries[at].step)
314
+ };
315
+ return next;
316
+ }
317
+ function applyUpdate(entries, update) {
318
+ const last = entries.at(-1);
319
+ switch (update.sessionUpdate) {
320
+ case "agent_message_chunk": {
321
+ const text = textOf(update.content);
322
+ const id = update.messageId ?? null;
323
+ return last?.kind === "agent" && sameMessage(last.id, id) ? replaceLast(entries, {
324
+ kind: "agent",
325
+ text: last.text + text,
326
+ id: last.id ?? id
327
+ }) : [...entries, {
328
+ kind: "agent",
329
+ text,
330
+ id
331
+ }];
332
+ }
333
+ case "agent_thought_chunk": return last?.kind === "thought" ? entries : [...entries, { kind: "thought" }];
334
+ case "user_message_chunk":
335
+ if (isPagePrompt(textOf(update.content))) return [...entries, {
336
+ kind: "user",
337
+ prompt: true
338
+ }];
339
+ return last?.kind === "user" && !last.prompt ? entries : [...entries, {
340
+ kind: "user",
341
+ prompt: false
342
+ }];
343
+ case "tool_call": return upsertStep(entries, update.toolCallId, () => ({
344
+ id: update.toolCallId,
345
+ title: update.title,
346
+ status: update.status ?? null,
347
+ pages: pagesOf(update)
348
+ }));
349
+ case "tool_call_update": return upsertStep(entries, update.toolCallId, (before) => {
350
+ const step = before ?? {
351
+ id: update.toolCallId,
352
+ title: "Tool call",
353
+ status: null,
354
+ pages: []
355
+ };
356
+ const pages = pagesOf(update);
357
+ return {
358
+ ...step,
359
+ ...update.title == null ? {} : { title: update.title },
360
+ ...update.status == null ? {} : { status: update.status },
361
+ ...pages.length === 0 ? {} : { pages: [...step.pages, ...pages.filter((p) => !step.pages.includes(p))] }
362
+ };
363
+ });
364
+ default: return entries;
365
+ }
366
+ }
367
+ var { fromThread, applyChange } = createRoundFold(applyUpdate);
368
+ function lastRound(entries) {
369
+ let round = null;
370
+ for (const [at, entry] of entries.entries()) if (entry.kind === "user") {
371
+ if (round !== null) round.closed = true;
372
+ round = entry.prompt ? {
373
+ at,
374
+ steps: [],
375
+ message: "",
376
+ pages: [],
377
+ closed: false
378
+ } : round;
379
+ } else if (round !== null && !round.closed && entry.kind === "agent") round.message = entry.text;
380
+ else if (round !== null && !round.closed && entry.kind === "tool") {
381
+ round.steps.push(entry.step);
382
+ for (const page of entry.step.pages) if (!round.pages.includes(page)) round.pages.push(page);
383
+ }
384
+ return round;
385
+ }
386
+ function isRunning(state, round) {
387
+ return !round.closed && state.turnStart !== null && isBusy(state.status) && round.at >= state.turnStart;
388
+ }
389
+ function lastParagraph(text) {
390
+ return (text.split(/\n[ \t]*\n/).map((p) => p.trim()).filter((p) => p !== "").at(-1) ?? "").replace(/`([^`\n]*)`/g, "$1");
391
+ }
392
+ function stepTitle(title) {
393
+ return title.replace(/^mcp__.+?__/, "");
394
+ }
395
+ var sessionOf = (status) => status?.phase === "ready" ? status.sessionId : null;
396
+ function createConversation(dd) {
397
+ const session = createAgentSession(dd, {
398
+ fromThread,
399
+ applyChange
400
+ });
401
+ const { available, state: followed, agents, problem } = session;
402
+ const [chosenId, setChosenId] = createSignal(null);
403
+ const [cancelledAt, setCancelledAt] = createSignal(null);
404
+ const status = () => followed()?.status;
405
+ const round = createMemo(() => {
406
+ const state = followed();
407
+ return state === null ? null : lastRound(state.entries);
408
+ });
409
+ const running = () => {
410
+ const state = followed();
411
+ const r = round();
412
+ return state !== null && r !== null && isRunning(state, r);
413
+ };
414
+ session.onChange((change) => {
415
+ if (change.type === "cleared") setCancelledAt(null);
416
+ });
417
+ const ask = async (work) => (await session.ask(work)).ok;
418
+ let following = null;
419
+ let listing = null;
420
+ const follow = async () => {
421
+ if (!await (following ??= session.follow({ waitForThread: false }))) return;
422
+ if (session.now.agents() === null) {
423
+ listing ??= session.list().then((ok) => {
424
+ if (!ok) {
425
+ listing = null;
426
+ session.setProblem(null);
427
+ }
428
+ return ok;
429
+ });
430
+ await listing;
431
+ }
432
+ };
433
+ return {
434
+ available,
435
+ followed,
436
+ status,
437
+ agents,
438
+ chosen: () => {
439
+ const listed = agents();
440
+ const picked = chosenId();
441
+ return (picked === null ? void 0 : listed?.agents.find((agent) => agent.id === picked)) ?? chosenAgent(status()?.agentId, listed);
442
+ },
443
+ chosenId,
444
+ round,
445
+ running,
446
+ asks: () => running() ? followed()?.permissions ?? [] : [],
447
+ agentName: () => {
448
+ const id = followed()?.status.agentId ?? null;
449
+ return agents()?.agents.find((agent) => agent.id === id)?.name ?? "the agent";
450
+ },
451
+ cancelled: () => {
452
+ const cancelled = cancelledAt();
453
+ return cancelled !== null && round()?.at === cancelled.at && sessionOf(status()) === cancelled.session;
454
+ },
455
+ problem,
456
+ follow,
457
+ choose: setChosenId,
458
+ answer: async (requestId, optionId) => {
459
+ await ask(() => dd.agent.answer(requestId, optionId));
460
+ },
461
+ cancel: async () => {
462
+ const at = round()?.at ?? null;
463
+ const session = sessionOf(status());
464
+ if (await ask(() => dd.agent.cancel())) setCancelledAt(at === null ? null : {
465
+ at,
466
+ session
467
+ });
468
+ },
469
+ setMode: async (modeId) => {
470
+ await ask(() => dd.agent.setMode(modeId));
471
+ },
472
+ setConfig: async (configId, value) => {
473
+ await ask(() => dd.agent.setConfig(configId, value));
474
+ }
475
+ };
476
+ }
477
+ var NO_AGENT_HOST = "This Daydream runs no agent; an agent of your own can read Figma through the figma_* tools.";
478
+ var NO_AGENT_INSTALLED = "No agent is installed on this machine; the agent panel says how to install one.";
479
+ var NO_FRAME = "Select a frame in Figma first.";
480
+ var TOO_SLOW = "Figma took too long to answer; try again.";
481
+ var errorLine = (sentences) => {
482
+ const said = sentences.filter((s) => s !== null);
483
+ return said.length === 0 ? null : said.join(" ");
484
+ };
485
+ var stillHere = (dd) => {
486
+ const version = dd.loadVersion();
487
+ return () => dd.loadVersion() === version;
488
+ };
489
+ function imageFile(answer) {
490
+ const binary = atob(answer.base64);
491
+ const bytes = new Uint8Array(binary.length);
492
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
493
+ const extension = answer.mimeType === "image/png" ? "png" : answer.mimeType.split("/")[1] ?? "png";
494
+ const stem = answer.nodeId === null ? "selection" : answer.nodeId.replace(/[^A-Za-z0-9_-]+/g, "-");
495
+ return new File([bytes], `figma-${stem}.${extension}`, { type: answer.mimeType });
496
+ }
497
+ async function intrinsicWidth(file) {
498
+ try {
499
+ const bitmap = await createImageBitmap(file);
500
+ const width = bitmap.width;
501
+ bitmap.close();
502
+ return width;
503
+ } catch {
504
+ return;
505
+ }
506
+ }
507
+ async function selectedFrames(dd) {
508
+ const answer = await dd.hostPart.call("figma-selection");
509
+ if (!isSelectionAnswer(answer)) throw new Error("the host part answered no selection");
510
+ switch (answer.kind) {
511
+ case "none": return {
512
+ frames: [],
513
+ leftOut: null
514
+ };
515
+ case "unreachable": throw new Error(answer.said);
516
+ case "late": throw new Error(TOO_SLOW);
517
+ case "frames": {
518
+ const { frames, leftOut } = askable(answer.frames);
519
+ if (frames.length > 12) throw new Error(tooManySelected(frames.length));
520
+ const said = leftOutSentence(leftOut);
521
+ if (frames.length === 0 && said !== null) throw new Error(`${NO_FRAME} ${said}`);
522
+ return {
523
+ frames,
524
+ leftOut: said
525
+ };
526
+ }
527
+ }
528
+ }
529
+ async function vendored(dd, frame, here) {
530
+ const answer = await dd.hostPart.call("figma-screenshot", { node: frame?.id ?? void 0 });
531
+ if (!isScreenshotAnswer(answer)) throw new Error("the host part answered no screenshot");
532
+ if (!here()) return null;
533
+ const file = imageFile({
534
+ ...answer,
535
+ nodeId: frame?.id ?? answer.nodeId
536
+ });
537
+ const [{ pageSrc }, width] = await Promise.all([dd.vendorFile(file), intrinsicWidth(file)]);
538
+ return {
539
+ pageSrc,
540
+ width,
541
+ name: frame?.name ?? answer.name ?? "Figma image",
542
+ nodeId: frame?.id ?? answer.nodeId
543
+ };
544
+ }
545
+ function referenceItem(dd, shot, position) {
546
+ const payload = {
547
+ src: shot.pageSrc,
548
+ name: shot.name,
549
+ nodeId: shot.nodeId
550
+ };
551
+ return {
552
+ id: dd.core.generateId(),
553
+ kind: REFERENCE_KIND,
554
+ position,
555
+ ...shot.width !== void 0 && shot.width > 1200 ? { frame: { width: MAX_REFERENCE_WIDTH } } : {},
556
+ payload
557
+ };
558
+ }
559
+ var landedWidth = (shot) => shot.width === void 0 ? MAX_REFERENCE_WIDTH : Math.min(shot.width, MAX_REFERENCE_WIDTH);
560
+ function land(dd, items) {
561
+ dd.mutateItems((draft) => {
562
+ draft.push(...items);
563
+ });
564
+ const ids = items.map((item) => item.id);
565
+ dd.selectItems(ids);
566
+ dd.canvas.reveal(ids);
567
+ }
568
+ async function inBatches(items, width, work) {
569
+ const answers = new Array(items.length);
570
+ let next = 0;
571
+ const worker = async () => {
572
+ for (;;) {
573
+ const i = next++;
574
+ if (i >= items.length) return;
575
+ answers[i] = await work(items[i]);
576
+ }
577
+ };
578
+ await Promise.all(Array.from({ length: Math.min(width, items.length) }, worker));
579
+ return answers;
580
+ }
581
+ async function addReference(dd) {
582
+ const here = stillHere(dd);
583
+ const selected = await selectedFrames(dd);
584
+ if (!here()) return null;
585
+ if (selected.frames.length === 0) {
586
+ const shot = await vendored(dd, null, here);
587
+ if (shot === null || !here()) return null;
588
+ land(dd, [referenceItem(dd, shot, dd.core.freeSpaceAnchor(dd.items()))]);
589
+ return {
590
+ status: `Added ${shot.name} as an image.`,
591
+ error: null
592
+ };
593
+ }
594
+ const reads = await inBatches(selected.frames, 4, async (frame) => {
595
+ try {
596
+ return { shot: await vendored(dd, frame, here) };
597
+ } catch (error) {
598
+ return { error: messageOf(error) };
599
+ }
600
+ });
601
+ if (!here()) return null;
602
+ const shots = [];
603
+ const failures = [];
604
+ const reasons = new Set();
605
+ for (const [i, read] of reads.entries()) if ("error" in read) {
606
+ reasons.add(read.error);
607
+ failures.push(`${selected.frames[i].name ?? selected.frames[i].id} could not be read: ${read.error}`);
608
+ } else if (read.shot === null) return null;
609
+ else shots.push(read.shot);
610
+ if (shots.length === 0) throw new Error(reasons.size === 1 ? [...reasons][0] : failures.join(" "));
611
+ const anchor = dd.core.freeSpaceAnchor(dd.items());
612
+ let x = anchor.x;
613
+ land(dd, shots.map((shot) => {
614
+ const item = referenceItem(dd, shot, {
615
+ x,
616
+ y: anchor.y
617
+ });
618
+ x += landedWidth(shot) + 40;
619
+ return item;
620
+ }));
621
+ return {
622
+ status: shots.length === 1 ? `Added ${shots[0].name} as an image.` : `Added ${shots.length} images: ${shots.map((shot) => shot.name).join(", ")}.`,
623
+ error: errorLine([...failures, selected.leftOut])
624
+ };
625
+ }
626
+ var NO_CHOICE = {
627
+ agentId: null,
628
+ conversationAgent: null
629
+ };
630
+ async function widthOf(dd, frame) {
631
+ const answer = await dd.hostPart.call("figma-frame", { node: frame.id }).catch(() => null);
632
+ return isFrameAnswer(answer) ? answer.width : null;
633
+ }
634
+ async function addAsPage(dd, choice = NO_CHOICE) {
635
+ const here = stillHere(dd);
636
+ const selected = await selectedFrames(dd);
637
+ if (!here()) return null;
638
+ if (selected.frames.length === 0) throw new Error(NO_FRAME);
639
+ const [one] = selected.frames;
640
+ const width = selected.frames.length === 1 ? await widthOf(dd, one) : null;
641
+ if (!here()) return null;
642
+ const prompt = pagePrompt(selected.frames.map((frame) => ({
643
+ nodeId: frame.id,
644
+ name: frame.name,
645
+ width
646
+ })));
647
+ const named = selected.frames.length === 1 ? one.name ?? "the frame" : `${selected.frames.length} frames`;
648
+ if (!await dd.agent.available()) throw new Error(NO_AGENT_HOST);
649
+ const listed = await dd.agent.agents();
650
+ const agent = (choice.agentId === null ? void 0 : listed.agents.find((a) => a.id === choice.agentId)) ?? chosenAgent(choice.conversationAgent, listed);
651
+ if (agent === null) throw new Error(NO_AGENT_INSTALLED);
652
+ if (!here()) return null;
653
+ const status = await dd.agent.start(agent.id, isSwitch(agent.id, choice.conversationAgent, listed) ? { intent: "new" } : {});
654
+ if (status.phase === "failed" || status.phase === "sign-in") throw new Error(status.note ?? `${agent.name} did not start`);
655
+ if (!here()) return null;
656
+ await dd.agent.prompt([{
657
+ type: "text",
658
+ text: prompt
659
+ }]);
660
+ return {
661
+ status: selected.frames.length === 1 ? `Asked ${agent.name} to transcribe ${named} as a page.` : `Asked ${agent.name} to transcribe ${named} as pages.`,
662
+ error: selected.leftOut
663
+ };
664
+ }
665
+ var _tmpl$$3 = template(`<li> earlier`);
666
+ var _tmpl$2$2 = template(`<ul><!><!>`);
667
+ var _tmpl$3$1 = template(`<p>`);
668
+ var _tmpl$4$1 = template(`<div><button type=button>Cancel`);
669
+ var _tmpl$5$1 = template(`<div><p></p><!><!><!><!><!>`);
670
+ var _tmpl$6$1 = template(`<li><span aria-hidden=true>`);
671
+ var _tmpl$7$1 = template(`<div><p></p><div>`);
672
+ var _tmpl$8$1 = template(`<button type=button>`);
673
+ var DONE_ON_CANVAS = "Done, see the canvas";
674
+ var CANCELLED = "Cancelled";
675
+ var basename = (path) => path.split("/").pop() ?? path;
676
+ var mark = (status) => status === "completed" ? "✓" : status === "failed" ? "✕" : "·";
677
+ function statusLine(round, state) {
678
+ if (state.asking) return `${state.agent} needs an answer`;
679
+ if (state.running) return round.steps.length === 0 && round.message === "" ? `Asking ${state.agent}…` : `${state.agent} is working…`;
680
+ if (state.cancelled) return CANCELLED;
681
+ if (state.failure !== null) return state.failure;
682
+ return round.pages.length === 0 ? DONE_ON_CANVAS : `Done: ${round.pages.map(basename).join(", ")}`;
683
+ }
684
+ function RoundBlock(props) {
685
+ const { conversation, prefix: p } = untrack(() => ({ ...props }));
686
+ const failure = () => {
687
+ const status = conversation.followed()?.status;
688
+ if (status === void 0) return null;
689
+ if (status.phase === "failed") return status.note;
690
+ if (status.phase === "ready") return status.note;
691
+ return null;
692
+ };
693
+ const line = (round) => statusLine(round, {
694
+ running: conversation.running(),
695
+ asking: conversation.asks().length > 0,
696
+ cancelled: conversation.cancelled(),
697
+ agent: conversation.agentName(),
698
+ failure: round.closed ? null : failure()
699
+ });
700
+ const state = () => conversation.asks().length > 0 ? "asking" : conversation.running() ? "running" : "done";
701
+ return createComponent(Show, {
702
+ get when() {
703
+ return conversation.round();
704
+ },
705
+ children: (round) => {
706
+ const steps = () => round().steps.slice(-5);
707
+ const earlier = () => Math.max(0, round().steps.length - 5);
708
+ const word = () => lastParagraph(round().message);
709
+ var _el$ = _tmpl$5$1();
710
+ var _el$2 = _el$.firstChild;
711
+ var _el$8 = _el$2.nextSibling;
712
+ var _el$10 = _el$8.nextSibling;
713
+ var _el$11 = _el$10.nextSibling;
714
+ var _el$12 = _el$11.nextSibling;
715
+ var _el$15 = _el$12.nextSibling;
716
+ insert(_el$2, () => {
717
+ return line(round());
718
+ });
719
+ insert(_el$, createComponent(Show, {
720
+ get when() {
721
+ return steps().length > 0;
722
+ },
723
+ get children() {
724
+ var _el$3 = _tmpl$2$2();
725
+ var _el$6 = _el$3.firstChild;
726
+ var _el$7 = _el$6.nextSibling;
727
+ insert(_el$3, createComponent(Show, {
728
+ get when() {
729
+ return earlier() > 0;
730
+ },
731
+ get children() {
732
+ var _el$4 = _tmpl$$3();
733
+ var _el$5 = _el$4.firstChild;
734
+ insert(_el$4, earlier, _el$5);
735
+ effect(() => `${p}-earlier`, (_v$, _$p) => {
736
+ className(_el$4, _v$, _$p);
737
+ });
738
+ return _el$4;
739
+ }
740
+ }), _el$6);
741
+ insert(_el$3, createComponent(For, {
742
+ get each() {
743
+ return steps();
744
+ },
745
+ children: (step) => (() => {
746
+ var _el$16 = _tmpl$6$1();
747
+ var _el$17 = _el$16.firstChild;
748
+ insert(_el$17, () => {
749
+ return mark(step.status);
750
+ });
751
+ insert(_el$16, () => {
752
+ return stepTitle(step.title);
753
+ }, null);
754
+ effect(() => {
755
+ return {
756
+ e: step.status ?? "",
757
+ t: `${p}-mark`
758
+ };
759
+ }, ({ e, t }, _p$) => {
760
+ e !== _p$?.e && setAttribute(_el$16, "data-step-status", e);
761
+ className(_el$17, t, _p$?.t);
762
+ });
763
+ return _el$16;
764
+ })()
765
+ }), _el$7);
766
+ effect(() => `${p}-steps`, (_v$, _$p) => {
767
+ className(_el$3, _v$, _$p);
768
+ });
769
+ return _el$3;
770
+ }
771
+ }), _el$8);
772
+ insert(_el$, createComponent(Show, {
773
+ get when() {
774
+ return word() !== "";
775
+ },
776
+ get children() {
777
+ var _el$9 = _tmpl$3$1();
778
+ insert(_el$9, word);
779
+ effect(() => `${p}-word`, (_v$, _$p) => {
780
+ className(_el$9, _v$, _$p);
781
+ });
782
+ return _el$9;
783
+ }
784
+ }), _el$10);
785
+ insert(_el$, createComponent(For, {
786
+ get each() {
787
+ return conversation.asks();
788
+ },
789
+ children: (ask) => (() => {
790
+ var _el$18 = _tmpl$7$1();
791
+ var _el$19 = _el$18.firstChild;
792
+ var _el$20 = _el$19.nextSibling;
793
+ insert(_el$19, (() => {
794
+ var _c$ = memo(() => {
795
+ return ask.toolCall.title == null;
796
+ });
797
+ return () => {
798
+ return _c$() ? "The agent asks:" : `${stepTitle(ask.toolCall.title)}?`;
799
+ };
800
+ })());
801
+ insert(_el$20, createComponent(For, {
802
+ get each() {
803
+ return ask.options;
804
+ },
805
+ children: (option) => (() => {
806
+ var _el$21 = _tmpl$8$1();
807
+ _el$21._$$click = () => void conversation.answer(ask.requestId, option.optionId);
808
+ insert(_el$21, () => {
809
+ return option.name;
810
+ });
811
+ effect(() => {
812
+ return {
813
+ e: `${p}-button`,
814
+ t: option.kind
815
+ };
816
+ }, ({ e, t }, _p$) => {
817
+ className(_el$21, e, _p$?.e);
818
+ t !== _p$?.t && setAttribute(_el$21, "data-option-kind", t);
819
+ });
820
+ return _el$21;
821
+ })()
822
+ }));
823
+ effect(() => {
824
+ return {
825
+ e: `${p}-ask`,
826
+ t: `${p}-actions`
827
+ };
828
+ }, ({ e, t }, _p$) => {
829
+ className(_el$18, e, _p$?.e);
830
+ className(_el$20, t, _p$?.t);
831
+ });
832
+ return _el$18;
833
+ })()
834
+ }), _el$11);
835
+ insert(_el$, createComponent(Show, {
836
+ get when() {
837
+ return conversation.problem();
838
+ },
839
+ children: (problem) => (() => {
840
+ var _el$22 = _tmpl$3$1();
841
+ insert(_el$22, problem);
842
+ effect(() => `${p}-round-problem`, (_v$, _$p) => {
843
+ className(_el$22, _v$, _$p);
844
+ });
845
+ return _el$22;
846
+ })()
847
+ }), _el$12);
848
+ insert(_el$, createComponent(Show, {
849
+ get when() {
850
+ return conversation.running();
851
+ },
852
+ get children() {
853
+ var _el$13 = _tmpl$4$1();
854
+ var _el$14 = _el$13.firstChild;
855
+ _el$14._$$click = () => void conversation.cancel();
856
+ effect(() => {
857
+ return {
858
+ e: `${p}-actions`,
859
+ t: `${p}-button`
860
+ };
861
+ }, ({ e, t }, _p$) => {
862
+ className(_el$13, e, _p$?.e);
863
+ className(_el$14, t, _p$?.t);
864
+ });
865
+ return _el$13;
866
+ }
867
+ }), _el$15);
868
+ effect(() => {
869
+ return {
870
+ e: `${p}-round`,
871
+ t: state(),
872
+ a: `${p}-round-status`
873
+ };
874
+ }, ({ e, t, a }, _p$) => {
875
+ className(_el$, e, _p$?.e);
876
+ t !== _p$?.t && setAttribute(_el$, "data-round-state", t);
877
+ className(_el$2, a, _p$?.a);
878
+ });
879
+ return _el$;
880
+ }
881
+ });
882
+ }
883
+ delegateEvents(["click"]);
884
+ [...new Set([
885
+ "bypassPermissions",
886
+ "dontAsk",
887
+ "yolo",
888
+ "full-access"
889
+ ])];
890
+ var shownName = (name) => name.replace(/\s*\(recommended\)\s*$/i, "") || name;
891
+ function choicesOf(option) {
892
+ return option.options.flatMap((entry) => "group" in entry ? entry.options : [entry]).map((choice) => ({
893
+ ...choice,
894
+ name: shownName(choice.name)
895
+ }));
896
+ }
897
+ var STARTS_ON_PAGE = "starts on Add as page";
898
+ function statusWord(status, agentId) {
899
+ if (status === void 0 || status.phase === "idle" || status.agentId !== agentId) return STARTS_ON_PAGE;
900
+ switch (status.phase) {
901
+ case "starting": return "starting";
902
+ case "ready": return status.busy ? "working" : "ready";
903
+ case "sign-in": return "needs a sign-in";
904
+ case "failed": return "stopped";
905
+ }
906
+ }
907
+ var _tmpl$$2 = template(`<select aria-label=Agent>`);
908
+ var _tmpl$2$1 = template(`<span>`);
909
+ var _tmpl$3 = template(`<div><span>Agent</span><span></span><!><!>`);
910
+ var _tmpl$4 = template(`<option>`);
911
+ var _tmpl$5 = template(`<span>Mode`);
912
+ var _tmpl$6 = template(`<span><select aria-label=Mode>`);
913
+ var _tmpl$7 = template(`<select>`);
914
+ var _tmpl$8 = template(`<input type=checkbox>`);
915
+ function Settings(props) {
916
+ const { conversation, prefix: p } = untrack(() => ({ ...props }));
917
+ const ready = () => {
918
+ const now = conversation.status();
919
+ return now?.phase === "ready" ? now : null;
920
+ };
921
+ const own = () => {
922
+ const now = ready();
923
+ return now !== null && now.agentId === conversation.chosen()?.id ? now : null;
924
+ };
925
+ return createComponent(Show, {
926
+ get when() {
927
+ return conversation.available() === true;
928
+ },
929
+ get children() {
930
+ var _el$ = _tmpl$3();
931
+ var _el$2 = _el$.firstChild;
932
+ var _el$3 = _el$2.nextSibling;
933
+ var _el$6 = _el$3.nextSibling;
934
+ var _el$7 = _el$6.nextSibling;
935
+ insert(_el$3, createComponent(Show, {
936
+ get when() {
937
+ return (conversation.agents()?.agents.length ?? 0) > 0;
938
+ },
939
+ get fallback() {
940
+ var _el$8 = _tmpl$2$1();
941
+ insert(_el$8, () => {
942
+ return conversation.agents() === null ? "looking for agents" : "none known";
943
+ });
944
+ effect(() => `${p}-phase`, (_v$, _$p) => {
945
+ className(_el$8, _v$, _$p);
946
+ });
947
+ return _el$8;
948
+ },
949
+ get children() {
950
+ return [(() => {
951
+ var _el$4 = _tmpl$$2();
952
+ _el$4.addEventListener("change", (event) => conversation.choose(event.currentTarget.value));
953
+ insert(_el$4, createComponent(For, {
954
+ get each() {
955
+ return conversation.agents()?.agents ?? [];
956
+ },
957
+ children: (agent) => (() => {
958
+ var _el$9 = _tmpl$4();
959
+ insert(_el$9, (() => {
960
+ var _c$ = memo(() => {
961
+ return !!agent.installed;
962
+ });
963
+ return () => {
964
+ return _c$() ? agent.name : `${agent.name} (not installed)`;
965
+ };
966
+ })());
967
+ effect(() => {
968
+ return {
969
+ e: agent.id,
970
+ t: !agent.installed
971
+ };
972
+ }, ({ e, t }, _p$) => {
973
+ _el$9.value = e;
974
+ t !== _p$?.t && setAttribute(_el$9, "disabled", t);
975
+ });
976
+ return _el$9;
977
+ })()
978
+ }));
979
+ effect(() => {
980
+ return {
981
+ e: `${p}-select`,
982
+ t: conversation.chosen()?.id ?? ""
983
+ };
984
+ }, ({ e, t }, _p$) => {
985
+ className(_el$4, e, _p$?.e);
986
+ queueMicrotask(() => {
987
+ return _el$4.value = t;
988
+ }) || (_el$4.value = t);
989
+ });
990
+ return _el$4;
991
+ })(), (() => {
992
+ var _el$5 = _tmpl$2$1();
993
+ insert(_el$5, () => {
994
+ return statusWord(conversation.status(), conversation.chosen()?.id ?? null);
995
+ });
996
+ effect(() => {
997
+ return {
998
+ e: `${p}-phase`,
999
+ t: conversation.status()?.phase ?? "idle"
1000
+ };
1001
+ }, ({ e, t }, _p$) => {
1002
+ className(_el$5, e, _p$?.e);
1003
+ t !== _p$?.t && setAttribute(_el$5, "data-agent-phase", t);
1004
+ });
1005
+ return _el$5;
1006
+ })()];
1007
+ }
1008
+ }));
1009
+ insert(_el$, createComponent(Show, {
1010
+ get when() {
1011
+ return memo(() => {
1012
+ return !!own()?.configOptions?.some((option) => option.category === "mode");
1013
+ })() ? null : own()?.modes;
1014
+ },
1015
+ children: (modes) => [(() => {
1016
+ var _el$10 = _tmpl$5();
1017
+ effect(() => `${p}-key`, (_v$, _$p) => {
1018
+ className(_el$10, _v$, _$p);
1019
+ });
1020
+ return _el$10;
1021
+ })(), (() => {
1022
+ var _el$11 = _tmpl$6();
1023
+ var _el$12 = _el$11.firstChild;
1024
+ _el$12.addEventListener("change", (event) => void conversation.setMode(event.currentTarget.value));
1025
+ insert(_el$12, createComponent(For, {
1026
+ get each() {
1027
+ return modes().availableModes;
1028
+ },
1029
+ children: (mode) => (() => {
1030
+ var _el$13 = _tmpl$4();
1031
+ insert(_el$13, () => {
1032
+ return mode.name;
1033
+ });
1034
+ effect(() => mode.id, (_v$) => {
1035
+ _el$13.value = _v$;
1036
+ });
1037
+ return _el$13;
1038
+ })()
1039
+ }));
1040
+ effect(() => {
1041
+ return {
1042
+ e: `${p}-value`,
1043
+ t: `${p}-select`,
1044
+ a: modes().currentModeId
1045
+ };
1046
+ }, ({ e, t, a }, _p$) => {
1047
+ className(_el$11, e, _p$?.e);
1048
+ className(_el$12, t, _p$?.t);
1049
+ queueMicrotask(() => {
1050
+ return _el$12.value = a;
1051
+ }) || (_el$12.value = a);
1052
+ });
1053
+ return _el$11;
1054
+ })()]
1055
+ }), _el$6);
1056
+ insert(_el$, createComponent(For, {
1057
+ get each() {
1058
+ return own()?.configOptions ?? [];
1059
+ },
1060
+ children: (option) => [(() => {
1061
+ var _el$14 = _tmpl$2$1();
1062
+ insert(_el$14, () => {
1063
+ return option.name;
1064
+ });
1065
+ effect(() => {
1066
+ return {
1067
+ e: `${p}-key`,
1068
+ t: option.description ?? ""
1069
+ };
1070
+ }, ({ e, t }, _p$) => {
1071
+ className(_el$14, e, _p$?.e);
1072
+ t !== _p$?.t && setAttribute(_el$14, "title", t);
1073
+ });
1074
+ return _el$14;
1075
+ })(), (() => {
1076
+ var _el$15 = _tmpl$2$1();
1077
+ insert(_el$15, (() => {
1078
+ var _c$2 = memo(() => {
1079
+ return option.type === "select";
1080
+ });
1081
+ return () => {
1082
+ return _c$2() ? (() => {
1083
+ var _el$16 = _tmpl$7();
1084
+ _el$16.addEventListener("change", (event) => void conversation.setConfig(option.id, event.currentTarget.value));
1085
+ insert(_el$16, createComponent(For, {
1086
+ get each() {
1087
+ return choicesOf(option);
1088
+ },
1089
+ children: (choice) => (() => {
1090
+ var _el$18 = _tmpl$4();
1091
+ insert(_el$18, () => {
1092
+ return choice.name;
1093
+ });
1094
+ effect(() => choice.value, (_v$) => {
1095
+ _el$18.value = _v$;
1096
+ });
1097
+ return _el$18;
1098
+ })()
1099
+ }));
1100
+ effect(() => {
1101
+ return {
1102
+ e: `${p}-select`,
1103
+ t: option.name,
1104
+ a: option.currentValue
1105
+ };
1106
+ }, ({ e, t, a }, _p$) => {
1107
+ className(_el$16, e, _p$?.e);
1108
+ t !== _p$?.t && setAttribute(_el$16, "aria-label", t);
1109
+ queueMicrotask(() => {
1110
+ return _el$16.value = a;
1111
+ }) || (_el$16.value = a);
1112
+ });
1113
+ return _el$16;
1114
+ })() : (() => {
1115
+ var _el$17 = _tmpl$8();
1116
+ _el$17.addEventListener("change", (event) => void conversation.setConfig(option.id, event.currentTarget.checked));
1117
+ effect(() => {
1118
+ return {
1119
+ e: option.name,
1120
+ t: option.currentValue
1121
+ };
1122
+ }, ({ e, t }, _p$) => {
1123
+ e !== _p$?.e && setAttribute(_el$17, "aria-label", e);
1124
+ _el$17.checked = t;
1125
+ });
1126
+ return _el$17;
1127
+ })();
1128
+ };
1129
+ })());
1130
+ effect(() => `${p}-value`, (_v$, _$p) => {
1131
+ className(_el$15, _v$, _$p);
1132
+ });
1133
+ return _el$15;
1134
+ })()]
1135
+ }), _el$7);
1136
+ effect(() => {
1137
+ return {
1138
+ e: `${p}-settings`,
1139
+ t: `${p}-key`,
1140
+ a: `${p}-value`
1141
+ };
1142
+ }, ({ e, t, a }, _p$) => {
1143
+ className(_el$, e, _p$?.e);
1144
+ className(_el$2, t, _p$?.t);
1145
+ className(_el$3, a, _p$?.a);
1146
+ });
1147
+ return _el$;
1148
+ }
1149
+ });
1150
+ }
1151
+ var _tmpl$$1 = template(`<p>`);
1152
+ var _tmpl$2 = template(`<div><div><button type=button></button><button type=button></button></div><p role=status></p><p role=alert></p><!>`);
1153
+ var PROBING = "Looking for Figma desktop…";
1154
+ function createFigmaPanel(dd, state, conversation, p) {
1155
+ const [status, setStatus] = createSignal("");
1156
+ const [error, setError] = createSignal("");
1157
+ const [busy, setBusy] = createSignal(null);
1158
+ let alive = true;
1159
+ onCleanup(() => {
1160
+ alive = false;
1161
+ });
1162
+ state.probe();
1163
+ onSettled(() => {
1164
+ conversation.follow();
1165
+ });
1166
+ const act = async (which, work) => {
1167
+ if (busy() !== null) return;
1168
+ setError("");
1169
+ setStatus("");
1170
+ setBusy(which);
1171
+ try {
1172
+ const said = await work();
1173
+ if (alive && said !== null) {
1174
+ setStatus(said.status);
1175
+ if (said.error !== null) setError(said.error);
1176
+ }
1177
+ } catch (cause) {
1178
+ if (alive) setError(messageOf(cause));
1179
+ } finally {
1180
+ if (alive) setBusy(null);
1181
+ }
1182
+ state.probe();
1183
+ };
1184
+ const line = () => state.reachable() === null ? PROBING : NOT_REACHABLE;
1185
+ var _el$ = _tmpl$2();
1186
+ var _el$3 = _el$.firstChild;
1187
+ var _el$4 = _el$3.firstChild;
1188
+ var _el$5 = _el$4.nextSibling;
1189
+ var _el$6 = _el$3.nextSibling;
1190
+ var _el$7 = _el$6.nextSibling;
1191
+ var _el$8 = _el$7.nextSibling;
1192
+ insert(_el$, createComponent(Show, {
1193
+ get when() {
1194
+ return state.reachable() !== true;
1195
+ },
1196
+ get children() {
1197
+ var _el$2 = _tmpl$$1();
1198
+ insert(_el$2, line);
1199
+ effect(() => `${p}-state`, (_v$, _$p) => {
1200
+ className(_el$2, _v$, _$p);
1201
+ });
1202
+ return _el$2;
1203
+ }
1204
+ }), _el$3);
1205
+ _el$4._$$click = () => void act("image", () => addReference(dd));
1206
+ insert(_el$4, () => {
1207
+ return busy() === "image" ? "Adding…" : "Add as image";
1208
+ });
1209
+ _el$5._$$click = () => void act("page", () => addAsPage(dd, {
1210
+ agentId: conversation.chosenId(),
1211
+ conversationAgent: conversation.status()?.agentId ?? null
1212
+ }));
1213
+ insert(_el$5, () => {
1214
+ return busy() === "page" ? "Asking…" : "Add as page";
1215
+ });
1216
+ insert(_el$, createComponent(Settings, {
1217
+ conversation,
1218
+ prefix: p
1219
+ }), _el$6);
1220
+ insert(_el$6, status);
1221
+ insert(_el$7, error);
1222
+ insert(_el$, createComponent(RoundBlock, {
1223
+ conversation,
1224
+ prefix: p
1225
+ }), _el$8);
1226
+ effect(() => {
1227
+ return {
1228
+ e: `${p}-panel`,
1229
+ t: `${p}-actions`,
1230
+ a: `${p}-button`,
1231
+ o: busy() !== null,
1232
+ i: `${p}-button`,
1233
+ n: busy() !== null,
1234
+ s: `${p}-status`,
1235
+ h: `${p}-error`
1236
+ };
1237
+ }, ({ e, t, a, o, i, n, s, h }, _p$) => {
1238
+ className(_el$, e, _p$?.e);
1239
+ className(_el$3, t, _p$?.t);
1240
+ className(_el$4, a, _p$?.a);
1241
+ o !== _p$?.o && setAttribute(_el$4, "disabled", o);
1242
+ className(_el$5, i, _p$?.i);
1243
+ n !== _p$?.n && setAttribute(_el$5, "disabled", n);
1244
+ className(_el$6, s, _p$?.s);
1245
+ className(_el$7, h, _p$?.h);
1246
+ });
1247
+ return _el$;
1248
+ }
1249
+ delegateEvents(["click"]);
1250
+ var _tmpl$ = template(`<figure><img>`);
1251
+ function ReferenceItemView(props) {
1252
+ const id = untrack(() => props.item.id);
1253
+ const p = untrack(() => props.prefix);
1254
+ let binding;
1255
+ onSettled(() => () => binding?.dispose());
1256
+ const style$1 = () => ({
1257
+ left: `${props.item.position.x}px`,
1258
+ top: `${props.item.position.y}px`,
1259
+ ...props.item.frame === void 0 ? {} : { width: `${props.item.frame.width}px` },
1260
+ ...props.item.frame?.height === void 0 ? {} : { height: `${props.item.frame.height}px` }
1261
+ });
1262
+ var _el$ = _tmpl$();
1263
+ var _el$2 = _el$.firstChild;
1264
+ ref(() => {
1265
+ return (node) => {
1266
+ binding = props.dd.canvas.bindItemNode(id, node);
1267
+ };
1268
+ }, _el$);
1269
+ effect(() => {
1270
+ return {
1271
+ e: `${p}-reference`,
1272
+ t: readShallow(style$1()),
1273
+ a: props.item.frame === void 0 ? void 0 : "",
1274
+ o: props.item.frame?.height === void 0 ? void 0 : "",
1275
+ i: props.dd.assetUrl(props.item.payload.src),
1276
+ n: props.item.payload.name
1277
+ };
1278
+ }, ({ e, t, a, o, i, n }, _p$) => {
1279
+ className(_el$, e, _p$?.e);
1280
+ style(_el$, t, _p$?.t);
1281
+ a !== _p$?.a && setAttribute(_el$, "data-framed", a);
1282
+ o !== _p$?.o && setAttribute(_el$, "data-sized", o);
1283
+ i !== _p$?.i && setAttribute(_el$2, "src", i);
1284
+ n !== _p$?.n && setAttribute(_el$2, "alt", n);
1285
+ });
1286
+ return _el$;
1287
+ }
1288
+ function createFigmaState(dd, target = typeof window === "undefined" ? null : window) {
1289
+ const [reachable, setReachable] = createSignal(null);
1290
+ const probe = createProbe({
1291
+ ask: async () => await dd.hostPart.call("reachable") === true,
1292
+ answer: (known) => setReachable(known),
1293
+ target
1294
+ });
1295
+ dd.on("project", () => probe.probe());
1296
+ onCleanup(() => probe.dispose());
1297
+ return {
1298
+ reachable,
1299
+ probe: probe.probe,
1300
+ settled: probe.settled
1301
+ };
1302
+ }
1303
+ var classPrefix = (pluginId) => pluginId.replace(/[^A-Za-z0-9_-]/g, "-");
1304
+ var panelCss = (p) => `
1305
+ .${p}-panel {
1306
+ --panel-text: var(--dream-app-text, #b8b8be);
1307
+ --panel-bright: var(--dream-app-bright, #c8c8ce);
1308
+ --panel-dim: var(--dream-app-dim, #9a9aa0);
1309
+ --panel-muted: var(--dream-app-muted, #85858c);
1310
+ --panel-chrome: var(--dream-app-line, #232328);
1311
+
1312
+ display: flex;
1313
+ flex-direction: column;
1314
+ gap: 8px;
1315
+ min-height: 0;
1316
+ padding: 6px 12px 12px;
1317
+ font-size: 12px;
1318
+ line-height: 1.4;
1319
+ color: var(--panel-text);
1320
+ }
1321
+
1322
+ .${p}-state {
1323
+ margin: 0;
1324
+ font-size: 11px;
1325
+ color: var(--panel-dim);
1326
+ overflow-wrap: anywhere;
1327
+ }
1328
+
1329
+ .${p}-button:focus-visible {
1330
+ outline: 1px solid var(--panel-bright);
1331
+ outline-offset: -1px;
1332
+ }
1333
+
1334
+ .${p}-actions {
1335
+ display: flex;
1336
+ flex-wrap: wrap;
1337
+ align-items: center;
1338
+ gap: 6px;
1339
+ }
1340
+
1341
+ .${p}-button {
1342
+ padding: 4px 10px;
1343
+ font: inherit;
1344
+ color: var(--panel-bright);
1345
+ background: var(--panel-chrome);
1346
+ border: 0;
1347
+ border-radius: 4px;
1348
+ cursor: pointer;
1349
+ }
1350
+
1351
+ .${p}-button:disabled {
1352
+ color: var(--panel-muted);
1353
+ cursor: default;
1354
+ }
1355
+
1356
+ /* The agent Add as page asks, and its settings: a key-value grid under
1357
+ the buttons, hidden on a host that runs no agents. */
1358
+ .${p}-settings {
1359
+ display: grid;
1360
+ grid-template-columns: max-content minmax(0, 1fr);
1361
+ gap: 4px 10px;
1362
+ align-items: center;
1363
+ font-size: 11px;
1364
+ }
1365
+
1366
+ .${p}-key {
1367
+ color: var(--panel-muted);
1368
+ }
1369
+
1370
+ .${p}-value {
1371
+ display: flex;
1372
+ flex-wrap: wrap;
1373
+ align-items: center;
1374
+ gap: 6px;
1375
+ min-width: 0;
1376
+ }
1377
+
1378
+ .${p}-select {
1379
+ min-width: 0;
1380
+ max-width: 12em;
1381
+ font: inherit;
1382
+ color: var(--panel-dim);
1383
+ background: transparent;
1384
+ border: 1px solid var(--panel-chrome);
1385
+ border-radius: 3px;
1386
+ padding: 1px 2px;
1387
+ }
1388
+
1389
+ .${p}-select:focus-visible {
1390
+ outline: 1px solid var(--panel-bright);
1391
+ outline-offset: -1px;
1392
+ }
1393
+
1394
+ .${p}-phase {
1395
+ color: var(--panel-muted);
1396
+ }
1397
+
1398
+ .${p}-status,
1399
+ .${p}-error {
1400
+ margin: 0;
1401
+ font-size: 11px;
1402
+ color: var(--panel-muted);
1403
+ overflow-wrap: anywhere;
1404
+ }
1405
+
1406
+ .${p}-status:empty,
1407
+ .${p}-error:empty {
1408
+ display: none;
1409
+ }
1410
+
1411
+ .${p}-error {
1412
+ color: var(--dream-app-danger, #d08a8a);
1413
+ }
1414
+
1415
+ /* The round Add as page asked for: a quiet block under the buttons,
1416
+ absent until the first. */
1417
+ .${p}-round {
1418
+ --panel-amber: var(--dream-app-amber, #a8842c);
1419
+ --panel-faint: var(--dream-app-line, #3a3a40);
1420
+
1421
+ display: flex;
1422
+ flex-direction: column;
1423
+ gap: 4px;
1424
+ padding-top: 6px;
1425
+ border-top: 1px solid var(--panel-chrome);
1426
+ font-size: 11px;
1427
+ color: var(--panel-muted);
1428
+ }
1429
+
1430
+ .${p}-round-status {
1431
+ margin: 0;
1432
+ color: var(--panel-dim);
1433
+ overflow-wrap: anywhere;
1434
+ }
1435
+
1436
+ .${p}-round[data-round-state="running"] .${p}-round-status,
1437
+ .${p}-round[data-round-state="asking"] .${p}-round-status {
1438
+ color: var(--panel-amber);
1439
+ }
1440
+
1441
+ .${p}-steps {
1442
+ margin: 0;
1443
+ padding: 0;
1444
+ list-style: none;
1445
+ }
1446
+
1447
+ .${p}-steps li {
1448
+ overflow: hidden;
1449
+ text-overflow: ellipsis;
1450
+ white-space: nowrap;
1451
+ }
1452
+
1453
+ .${p}-steps li[data-step-status="failed"] {
1454
+ color: var(--dream-app-danger, #d08a8a);
1455
+ }
1456
+
1457
+ .${p}-mark {
1458
+ display: inline-block;
1459
+ width: 1.2em;
1460
+ }
1461
+
1462
+ .${p}-steps li[data-step-status="pending"] .${p}-mark,
1463
+ .${p}-steps li[data-step-status="in_progress"] .${p}-mark {
1464
+ color: var(--panel-amber);
1465
+ }
1466
+
1467
+ .${p}-word {
1468
+ margin: 0;
1469
+ color: var(--panel-text);
1470
+ overflow-wrap: anywhere;
1471
+ }
1472
+
1473
+ .${p}-ask {
1474
+ padding: 6px 8px;
1475
+ border: 1px solid var(--panel-faint);
1476
+ border-left: 2px solid var(--panel-amber);
1477
+ border-radius: 4px;
1478
+ color: var(--panel-dim);
1479
+ }
1480
+
1481
+ .${p}-ask p {
1482
+ margin: 0 0 6px;
1483
+ overflow-wrap: anywhere;
1484
+ }
1485
+
1486
+ .${p}-round-problem {
1487
+ margin: 0;
1488
+ color: var(--dream-app-danger, #d08a8a);
1489
+ overflow-wrap: anywhere;
1490
+ }
1491
+ `;
1492
+ var itemCss = (p) => `
1493
+ .${p}-reference {
1494
+ position: absolute;
1495
+ margin: 0;
1496
+ }
1497
+
1498
+ /* With no frame the image is its own size, as a Media image lands; a
1499
+ frame's width is the image's, its height too when a resize set one. */
1500
+ .${p}-reference img {
1501
+ display: block;
1502
+ object-fit: contain;
1503
+ object-position: top left;
1504
+ }
1505
+
1506
+ .${p}-reference[data-framed] img {
1507
+ width: 100%;
1508
+ height: auto;
1509
+ }
1510
+
1511
+ .${p}-reference[data-sized] img {
1512
+ height: 100%;
1513
+ }
1514
+ `;
1515
+ function activate(dd) {
1516
+ const p = classPrefix(dd.plugin.id);
1517
+ const state = createFigmaState(dd);
1518
+ const conversation = createConversation(dd);
1519
+ for (const kind of [REFERENCE_KIND, LEGACY_REFERENCE_KIND]) dd.registerItemKind({
1520
+ kind,
1521
+ payloadProblem: referencePayloadProblem,
1522
+ styles: itemCss(p),
1523
+ render: (props) => createComponent(ReferenceItemView, {
1524
+ dd,
1525
+ get item() {
1526
+ return props.item;
1527
+ },
1528
+ prefix: p
1529
+ }),
1530
+ name: (item) => item.payload.name,
1531
+ rename: (item, name) => {
1532
+ item.payload.name = name;
1533
+ },
1534
+ describe: (item) => describeReference(item.payload),
1535
+ resize: { reset: true }
1536
+ });
1537
+ dd.registerPanel({
1538
+ id: "figma",
1539
+ title: "Figma",
1540
+ styles: panelCss(p),
1541
+ attention: () => conversation.asks().length > 0,
1542
+ render: () => createFigmaPanel(dd, state, conversation, p)
1543
+ });
1544
+ }
1545
+ export { activate as default };