@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/README.md CHANGED
@@ -1,3 +1,207 @@
1
- # Temporary Holding Version
1
+ # Figma
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A local Daydream plugin that puts what is open in Figma desktop beside the
4
+ real page. The design stays in Figma; Daydream takes an image of it, or
5
+ asks your agent to transcribe it as a page, and lets any agent connected to
6
+ Daydream read Figma through four tools. There is no plugin inside Figma.
7
+ Enable **Figma** in Daydream's Plugins page (⌥⌘P).
8
+
9
+ From this repository, install the plugin with:
10
+
11
+ ```sh
12
+ pnpm install
13
+ pnpm ship plugins/daydream.figma --enable
14
+ ```
15
+
16
+ The build produces `plugins/daydream.figma/dist/index.js`. The install command
17
+ copies the plugin into the user's Daydream plugins folder and records its trust.
18
+
19
+ ## Requirements
20
+
21
+ - Figma desktop, open on this machine, with the file you want to read open
22
+ in it.
23
+ - Figma's Dev Mode MCP server turned on in Figma's preferences. The plugin
24
+ reads it at `http://127.0.0.1:3845/mcp`, on this machine only; nothing
25
+ leaves it.
26
+ - A Figma seat that Figma gives the Dev Mode MCP server to (a paid plan, as
27
+ Figma requires).
28
+
29
+ The panel's first line says whether Figma answers, probed when the panel
30
+ opens, when a project is opened and when the window comes to the front.
31
+ Figma closed, or the server off, is a normal state: the line says what to
32
+ do, and each gesture answers the same sentence.
33
+
34
+ ## The two gestures
35
+
36
+ Both take what is selected in Figma, and nothing else: one frame, or
37
+ several, up to 12 at once (past that, each gesture refuses in one sentence
38
+ naming the cap). There is nothing to type; select in Figma, press here.
39
+ Each begins by reading the selection, and says what it found in its own
40
+ words: nothing selected, Figma not there (or refusing, in the server's own
41
+ sentence), or Figma too slow to answer ("Figma took too long to answer;
42
+ try again"). A frame's id and name are the selection's listing's, so a
43
+ frame whose own metadata is slow to come is still that frame. A layer
44
+ inside a component instance — which Figma lists with an id of its own
45
+ shape and refuses to read by it — is left out before anything is asked,
46
+ and the error line says so: "2 layers inside instances were left out;
47
+ select their frames."
48
+
49
+ 1. **Add as image** takes the frame's screenshot by its id, copies it into
50
+ the project's `assets/` (named by its bytes, so the same frame twice is
51
+ one file), and lands it on the canvas as a `daydream.figma.reference`
52
+ item at free space, selected and brought into view, as one undo step.
53
+ Its title bar carries the frame's name, as the selection listed it, and
54
+ renames it; nothing is drawn under the image. With nothing selected, the
55
+ page's screenshot lands, unnamed. A screenshot wider than 1200 px lands at that width; a resize
56
+ keeps the image's proportions, and a double-click on a handle resets it.
57
+ The payload is `{src, name, nodeId}`: `src` a file of the project's own
58
+ (`assets/<file>`), `name` the frame's, `nodeId` Figma's id or null. With
59
+ several frames selected, one screenshot is taken per frame by id and
60
+ every image lands in one row from free space, each 40 px right of the
61
+ last, side by side to compare, all selected, as one undo step; a frame
62
+ whose screenshot Figma refuses is left out and named in the error line
63
+ while the others land. (An earlier build kept a `link` the user had
64
+ typed on the payload; a canvas saved by it still renders, the `link`
65
+ let through and never read.)
66
+ 2. **Add as page** asks the project's agent to transcribe the frame as a page
67
+ of the project (Daydream's decision #96: the button is the prompt, and
68
+ nothing starts without it). The prompt names the frame — its id, its
69
+ name and its width — and asks for a
70
+ transcription through the tools below and the dream-author workflow: a
71
+ draft whose HTML and CSS are the project's own, finalized as a new page
72
+ titled after the frame. The plugin holds the project's conversation for
73
+ its own rounds (`contributes.conversation`), so the agent starts whether
74
+ the Agent plugin is on or not, and the panel shows the round it asked
75
+ for under the buttons: a status line — asking the agent, the agent
76
+ working, done with the page it wrote when a tool call named one, or the
77
+ agent's own note of why it stopped — each tool call as one line with a
78
+ mark for its state, the last paragraph of what the agent said, the
79
+ permissions the agent asks during the round with its own options as
80
+ buttons (a minimized panel carries a dot while one waits), and Cancel
81
+ while the turn runs. Every ask is yours, those about the plugin's own
82
+ four tools included: the plugin answers none. Nothing shows before the
83
+ first round; the last round stays when its turn ends. The prompt is a
84
+ turn of the project's one conversation, so the Agent panel shows the
85
+ same turn when it is on, and a turn asked there is not shown here. It
86
+ needs a frame — with nothing selected in Figma, the error line says to
87
+ select one first, and the agent is not started — and an agent
88
+ installed; the error line says which is missing. With several frames
89
+ selected, one prompt names every frame by name and id and asks for one
90
+ page per frame, titled after each, each compared against its own
91
+ screenshot; the panel shows that one round. The one-frame prompt
92
+ carries the frame's width when its metadata answers within 3 s, and
93
+ goes without it otherwise.
94
+
95
+ Under the two buttons sits a settings row, the same the Impeccable box
96
+ shows, hidden on a host that runs no agents. **Agent** is a select of
97
+ the agents Daydream knows, the one the Agent panel would show chosen
98
+ first — the project's conversation's, else the one used last, else the
99
+ first installed — with its state after it in a word or two ("starts on
100
+ Add as page" until a conversation with it is open; starting, ready,
101
+ working, needs a sign-in, stopped). Choosing another starts nothing by
102
+ itself: the next Add as page starts it, as a new conversation when it
103
+ is not the one the project's conversation is with (Daydream's
104
+ `intent: "new"`, the user's one switch). Once a conversation with the
105
+ chosen agent is open, the row adds the agent's **Mode** (unless the
106
+ agent offers its mode among its settings, which then stands for it) and
107
+ each of its settings — a select, or a checkbox — set through Daydream
108
+ as the Agent panel sets them, so the two never disagree. The agent's
109
+ permission behaviour lives there (Claude Code's is its mode), and is
110
+ your choice: pick a mode that asks for each tool, or one that does
111
+ not, before pressing Add as page a second time, or in the row as the
112
+ round runs. Before the first Add as page the row shows the agent alone,
113
+ since modes and settings are a live conversation's.
114
+
115
+ Why the agent and not a converter: Figma's Dev Mode server emits React and
116
+ Tailwind for `get_design_context`, in every configuration it was asked in
117
+ (`clientLanguages` of `html,css`, `clientFrameworks` of `unknown`, `none`,
118
+ `vanilla` and `html`, and `forceCode`), and its own notes say the code must
119
+ be converted. A mechanical rewrite of Tailwind classes into CSS would ship a
120
+ page that looks like the frame only by accident; the transcription is the
121
+ agent's work, with the frame's tree, code and screenshot in front of it.
122
+
123
+ ## The agent tools
124
+
125
+ The host part registers four MCP tools, so any agent connected to Daydream
126
+ reads Figma through it with no Figma setup of its own. Each takes an
127
+ optional `node` (an id, or a Figma link with a `node-id`; none for Figma's
128
+ selection — the panel never names one, an agent does), reads Figma only,
129
+ and answers one sentence as an error result when Figma does not answer. `figma_metadata` and `figma_variables` are marked read-only;
130
+ `figma_screenshot` and `figma_design_context` are not, since they write
131
+ the screenshot into the project.
132
+
133
+ | Tool | Answers |
134
+ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
135
+ | `figma_metadata` | the node's tree as `get_metadata` gives it: XML of ids, layer types, names, positions and sizes, the server's notes after it; with nothing selected, the document's pages |
136
+ | `figma_design_context` | the node as code (`get_design_context`: React and Tailwind, with `data-node-id` attributes) and the notes; with `screenshot: true`, its PNG is written too |
137
+ | `figma_screenshot` | the node's PNG (`get_screenshot`), written into the open project's `.daydream/figma/<node>.png` (`selection.png` for the selection) and answered as that path |
138
+ | `figma_variables` | the variables the node uses (`get_variable_defs`), as the server spells them |
139
+
140
+ A tool's result carries text alone on this plugin API, so a screenshot is a
141
+ file in the project's `.daydream/` scratch, never through a link, for the
142
+ agent to read with its own file tools. The images a design context names
143
+ are served at `http://localhost:3845/assets/…` only while Figma runs; a
144
+ page that uses one copies it into the project's `assets/`.
145
+
146
+ ## What is not done
147
+
148
+ - No plugin inside Figma: Daydream reads Figma through Figma's own server
149
+ and adds nothing to Figma.
150
+ - No write-back: nothing changes in Figma, ever.
151
+ - No `.fig` files: only the file open in Figma desktop is read, through its
152
+ server, never a file on disk or Figma's web API.
153
+ - No converter of Figma's code into HTML and CSS (above).
154
+
155
+ ## Limits
156
+
157
+ - One file at a time: the one open in Figma desktop. The server reads the
158
+ current page's nodes and the selection, and never takes or tells a file
159
+ key, so a `node` an agent gives a tool as a link is read against the
160
+ open file whichever file the link names: a Share link from another file
161
+ reads that id in the open file, or is refused when the id is not there,
162
+ and nothing on this side can tell. Open the linked file in Figma first.
163
+ - A node that is not on the open page, or an id of a shape the server does
164
+ not take, is Figma's refusal, said in the error line.
165
+ - The image's name and id come from the selection's listing; the
166
+ `figma_screenshot` tool, which takes a node from an agent, names it from
167
+ the frame's metadata, asked under 3 s of its own (a name is a courtesy),
168
+ and names the node the agent gave when that does not answer in time, its
169
+ file named after it. A screenshot of the page itself (nothing selected)
170
+ lands as "Figma image", unnamed, to rename in its title bar.
171
+ - The selection's listing is read under 5 s of its own: past that the
172
+ gesture says Figma was too slow, and nothing is asked further.
173
+ - A streamed answer is buffered to 32 MB at most; a server that streams
174
+ past that without answering is dropped with one sentence.
175
+ - `get_variable_defs` answers a JSON object of the variables the node
176
+ uses, name to value, every value a string
177
+ (`{"Size":"42","White":"#ffffff","Name":"John"}`, seen live; `{}` for
178
+ none; an alias resolved to the value it points at), passed through as
179
+ text, unread.
180
+ - The server's own bounds apply: a very large frame may be answered as
181
+ metadata alone, and one call of the client — its handshake, a session
182
+ opened again and the retry included — is given 45 s in all, under the
183
+ 50 s the kernel allows a host-part call, so what the panel hears is the
184
+ client's sentence and never the kernel's.
185
+
186
+ ## Validation
187
+
188
+ `pnpm test` runs the host part over a fake client and a fake host (the
189
+ channels, the four tools — the one list the manifest declares — the
190
+ screenshot file under the project, Figma not answering as a sentence), the
191
+ gestures over a fake `dd` (the selection's kinds, a slow metadata read, the
192
+ layers inside instances left out, several frames) and the pure parts (the
193
+ payload rules, a saved `link` let through, the frame read from the metadata
194
+ XML, the prompt). The round reader's three folds carry `mirrors-adapted`
195
+ markers naming the agent panel's thread state in the kernel, so
196
+ `test:kernel` reports when the kernel's changes.
197
+ `pnpm test:kernel <daydream-checkout> plugins/daydream.figma` mounts the
198
+ real shell with host-part replies in memory: the state line, an image
199
+ landed and selected with its file copied, three frames in a row, a saved
200
+ item with its `link` still drawn, nothing selected, the unreachable
201
+ sentence, Add as page with no agent, and — over a scripted `dd.agent` — the round block as the
202
+ agent takes the prompt, calls tools, asks a permission and ends, Cancel,
203
+ a turn asked from the agent panel left alone, and the settings row: the
204
+ project's agent chosen, the mode and settings of the open conversation
205
+ set through the host, and another agent chosen started as a new
206
+ conversation on the next Add as page. The plugin requires a Daydream build reporting API 3.0. See the
207
+ repository’s kernel pin and release gate before shipping.