@toghen/figma 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +18 -2
- package/cli/bin.mjs +19 -0
- package/cli/bridge.ts +243 -0
- package/cli/check.ts +62 -0
- package/cli/figcraft.ts +764 -0
- package/cli/figcraftApply.ts +1067 -0
- package/cli/figcraftFile.ts +476 -0
- package/cli/figcraftLibrary.ts +147 -0
- package/cli/figcraftRecord.ts +121 -0
- package/cli/figcraftScene.ts +661 -0
- package/cli/main.ts +218 -0
- package/cli/members.ts +65 -0
- package/cli/run.ts +756 -0
- package/component-files.md +665 -0
- package/dist/code.js +24161 -0
- package/dist/manifest.json +23 -0
- package/dist/ui.html +6918 -0
- package/guide.md +985 -0
- package/main/code.ts +50 -0
- package/main/components/apply.ts +704 -0
- package/main/components/endOfRun.ts +168 -0
- package/main/components/errors.ts +17 -0
- package/main/components/fieldWriter.ts +497 -0
- package/main/components/file.ts +263 -0
- package/main/components/receipt.ts +228 -0
- package/main/components/resolve.ts +1193 -0
- package/main/components/run.ts +273 -0
- package/main/components/snapshot.ts +341 -0
- package/main/components/stamp.ts +312 -0
- package/main/components/update.ts +465 -0
- package/main/dispatcher.ts +33 -0
- package/main/engines/batchRunner.ts +200 -0
- package/main/engines/ioAdapter.ts +132 -0
- package/main/engines/operationExecutor.ts +512 -0
- package/main/engines/propertyMapper.ts +221 -0
- package/main/engines/queryEngine.ts +45 -0
- package/main/engines/styleSyncEngine.ts +444 -0
- package/main/engines/variableSyncEngine.ts +561 -0
- package/main/handlers/auditSelection.ts +60 -0
- package/main/handlers/boundLayers.ts +51 -0
- package/main/handlers/cancelOperation.ts +18 -0
- package/main/handlers/executeApplyBindings.ts +69 -0
- package/main/handlers/executeScaffold.ts +59 -0
- package/main/handlers/executeStyleSync.ts +121 -0
- package/main/handlers/executeVariableSync.ts +82 -0
- package/main/handlers/exportDocument.ts +114 -0
- package/main/handlers/getSelection.ts +43 -0
- package/main/handlers/index.ts +30 -0
- package/main/handlers/inspectCorrespondence.ts +172 -0
- package/main/handlers/loadConventions.ts +86 -0
- package/main/handlers/loadDocument.ts +253 -0
- package/main/handlers/lock.ts +270 -0
- package/main/handlers/planApply.ts +157 -0
- package/main/handlers/planApplyBindings.ts +79 -0
- package/main/handlers/planScaffold.ts +84 -0
- package/main/handlers/planStyleSync.ts +49 -0
- package/main/handlers/planVariableSync.ts +49 -0
- package/main/handlers/readSelectionBindings.ts +160 -0
- package/main/handlers/resizeUi.ts +105 -0
- package/main/handlers/runComponent.ts +65 -0
- package/main/handlers/templates.ts +462 -0
- package/main/handlers/tokenGraph.ts +122 -0
- package/main/runtimeState.ts +66 -0
- package/main/utils/abortController.ts +11 -0
- package/main/utils/boundVariables.ts +84 -0
- package/main/utils/buildPayloads.ts +85 -0
- package/main/utils/correspondence.ts +658 -0
- package/main/utils/designSource.ts +122 -0
- package/main/utils/figmaCollectionIndex.ts +120 -0
- package/main/utils/figmaStyleAdapter.ts +268 -0
- package/main/utils/figmaTokenAdapter.ts +255 -0
- package/main/utils/flattenDesignSystem.ts +38 -0
- package/main/utils/folds.ts +203 -0
- package/main/utils/fontStack.ts +57 -0
- package/main/utils/lists.ts +4 -0
- package/main/utils/lockStore.ts +72 -0
- package/main/utils/moves.ts +67 -0
- package/main/utils/pageRoots.ts +58 -0
- package/main/utils/planSymbols.ts +96 -0
- package/main/utils/purpose.ts +351 -0
- package/main/utils/recentStore.ts +166 -0
- package/main/utils/resolverModes.ts +271 -0
- package/main/utils/rootRecord.ts +137 -0
- package/main/utils/shardedData.ts +142 -0
- package/main/utils/standing.ts +106 -0
- package/main/utils/styleTarget.ts +51 -0
- package/main/utils/sum.ts +612 -0
- package/main/utils/symbolicIdRegistry.ts +26 -0
- package/main/utils/syncState.ts +133 -0
- package/main/utils/templateStore.ts +73 -0
- package/main/utils/tokenPath.ts +28 -0
- package/main/utils/tokenStamps.ts +91 -0
- package/manifest.json +15 -0
- package/package.json +64 -4
- package/reference.md +1609 -0
- package/shared/components/artboard.ts +126 -0
- package/shared/components/axes.ts +420 -0
- package/shared/components/bindings.ts +64 -0
- package/shared/components/blueprint.ts +248 -0
- package/shared/components/check.ts +705 -0
- package/shared/components/diff.ts +668 -0
- package/shared/components/fields.ts +404 -0
- package/shared/components/groups.ts +306 -0
- package/shared/components/index.ts +140 -0
- package/shared/components/members.ts +280 -0
- package/shared/components/model.ts +273 -0
- package/shared/components/order.ts +53 -0
- package/shared/components/read.ts +480 -0
- package/shared/components/stack.ts +124 -0
- package/shared/components/steps.ts +186 -0
- package/shared/components/validate.ts +565 -0
- package/shared/conventions.ts +28 -0
- package/shared/inputs.ts +40 -0
- package/shared/operations.ts +233 -0
- package/shared/pendingNames.ts +39 -0
- package/shared/protocol.ts +588 -0
- package/shared/tokenTypeMap.ts +21 -0
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
import type { LoadResult } from '@toghen/lib';
|
|
2
|
+
import type { CompactReport, RunOptions } from './components/steps.js';
|
|
3
|
+
import type { BlueprintTokens } from './components/blueprint.js';
|
|
4
|
+
import type { Operation, OperationSummary, SkipCounts } from './operations.js';
|
|
5
|
+
|
|
6
|
+
export type QueryScope = 'selection' | 'page' | 'file';
|
|
7
|
+
/** `load` is no plan: it is the rows a load reports (its collisions, the overrides map's unused keys), in a plan's shape so the result surface draws them. Nothing executes it. */
|
|
8
|
+
export type PlanType = 'variable_sync' | 'style_sync' | 'scaffold' | 'apply_bindings' | 'load' | 'discovery' | 'apply' | 'relink';
|
|
9
|
+
|
|
10
|
+
/** The layers Apply writes: frames (`FRAME`, `SLOT`), instances (`INSTANCE`: the write is an override), component definitions (`COMPONENT`, set members included). */
|
|
11
|
+
export type ApplyKind = 'frames' | 'instances' | 'components';
|
|
12
|
+
|
|
13
|
+
/** One shape, used twice: files in Load, Figma objects in Build. A name as written → what to use instead. */
|
|
14
|
+
export type Overrides = Record<string, string>;
|
|
15
|
+
|
|
16
|
+
/** What a load read, and how: the standing picture of where the document came from. */
|
|
17
|
+
export interface Composition {
|
|
18
|
+
/** A resolver document, or token files wrapped in one. */
|
|
19
|
+
form: 'resolver' | 'tokens';
|
|
20
|
+
/** As the load was given it: one path, or several in order. */
|
|
21
|
+
entry: string | string[];
|
|
22
|
+
/** Every reference whose resolution read a file: the `$ref` as written, its holder, the file read, and whether the map sent it there. */
|
|
23
|
+
reads: LoadResult['reads'];
|
|
24
|
+
/** Present only when a map was given: the map, its hash, and the keys no reference matched. */
|
|
25
|
+
overrides?: { map: Overrides; hash: string; unused: string[] };
|
|
26
|
+
/** Loaded again from a recent's stored bundle, not from the files: `reads` are those of the load that stored it. */
|
|
27
|
+
fromBundle?: true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type ErrorCode =
|
|
31
|
+
| 'INVALID_DOCUMENT'
|
|
32
|
+
| 'INVALID_CONVENTIONS'
|
|
33
|
+
| 'NO_RESOLVER'
|
|
34
|
+
| 'NO_PLAN'
|
|
35
|
+
| 'FIGMA_API_ERROR'
|
|
36
|
+
| 'COMPONENT_RUN_FAILED'
|
|
37
|
+
| 'INVALID_TEMPLATE'
|
|
38
|
+
| 'INVALID_PICK'
|
|
39
|
+
| 'OPERATION_CANCELLED'
|
|
40
|
+
/** Apply's query names nothing it can write: no token or group, or a field for a group. */
|
|
41
|
+
| 'INVALID_QUERY';
|
|
42
|
+
|
|
43
|
+
export interface ProgressPayload {
|
|
44
|
+
completed: number;
|
|
45
|
+
total: number;
|
|
46
|
+
currentLabel: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface ErrorPayload {
|
|
50
|
+
message: string;
|
|
51
|
+
code: ErrorCode;
|
|
52
|
+
commandType: string;
|
|
53
|
+
/** Every reason, one per entry, when `code` is `COMPONENT_RUN_FAILED` or `INVALID_TEMPLATE`. */
|
|
54
|
+
problems?: string[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface DocumentStatePayload {
|
|
58
|
+
/**
|
|
59
|
+
* Whether there is a document at all.
|
|
60
|
+
*
|
|
61
|
+
* `GET_DOCUMENT_STATE` answers even when nothing is loaded, and the UI read
|
|
62
|
+
* any `DOCUMENT_STATE` as proof of a document — so opening the plugin into an
|
|
63
|
+
* empty session showed the loaded pane, named "Untitled", with zero tokens
|
|
64
|
+
* and live buttons. The question has to be answerable rather than inferred
|
|
65
|
+
* from a placeholder name.
|
|
66
|
+
*/
|
|
67
|
+
loaded: boolean;
|
|
68
|
+
name: string;
|
|
69
|
+
/** The document's own `description`, which the Resolver spec allows at root. */
|
|
70
|
+
description?: string;
|
|
71
|
+
tokenCount: number;
|
|
72
|
+
modifiers: Record<string, string[]>;
|
|
73
|
+
/** The sum's modes: the defaults, then each other context on its own. Never the product of every combination. */
|
|
74
|
+
modes: string[];
|
|
75
|
+
/** The default context of each modifier, which export preselects: one context per modifier is chosen, and the choice is shown (R10). */
|
|
76
|
+
modifierDefaults: Record<string, string>;
|
|
77
|
+
/** The mode the document resolves to when no context is chosen: the first of `modes`. */
|
|
78
|
+
defaultMode: string;
|
|
79
|
+
/** The version the system declares for itself (`Resolver.getDeclaredVersion()`), if any. */
|
|
80
|
+
version?: string;
|
|
81
|
+
/** The file the document was read from, when the load named one. */
|
|
82
|
+
fileName?: string;
|
|
83
|
+
/** What the load read and how. Present whenever `loaded` is. */
|
|
84
|
+
composition?: Composition;
|
|
85
|
+
/** Tokens the conversion map sends to a Figma style rather than a variable. */
|
|
86
|
+
styleCount: number;
|
|
87
|
+
/**
|
|
88
|
+
* The collections a variable sync builds: the base first, then one per
|
|
89
|
+
* modifier, then one per folded part axis, each with its modes and how many variables would live in it.
|
|
90
|
+
* Absent when the sum cannot be built; the plan is what says why.
|
|
91
|
+
*/
|
|
92
|
+
collections?: DocumentCollectionSummary[];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface DocumentCollectionSummary {
|
|
96
|
+
name: string;
|
|
97
|
+
/** The modifier it stands for; absent for the base collection. */
|
|
98
|
+
modifier?: string;
|
|
99
|
+
/** Set for the collection of a folded part axis, named after the axis. */
|
|
100
|
+
axis?: true;
|
|
101
|
+
modes: string[];
|
|
102
|
+
variableCount: number;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* One entry in the recents list — what the UI draws, without the document.
|
|
107
|
+
*
|
|
108
|
+
* A filename says nothing: `toghen.resolver.json` is what every one of them is
|
|
109
|
+
* called. The design system's own name and version are what tell two of them
|
|
110
|
+
* apart.
|
|
111
|
+
*/
|
|
112
|
+
export interface RecentDocumentSummary {
|
|
113
|
+
/**
|
|
114
|
+
* Identity, for replacing an entry rather than growing a second one.
|
|
115
|
+
*
|
|
116
|
+
* The document's `name`, falling back to the filename when it declares none.
|
|
117
|
+
* Loading an edited document is a normal working loop, not a new system.
|
|
118
|
+
*/
|
|
119
|
+
key: string;
|
|
120
|
+
name: string;
|
|
121
|
+
/**
|
|
122
|
+
* The version the design system declares for itself, if it declares one.
|
|
123
|
+
*
|
|
124
|
+
* **Not** the document's `version` field, which the Resolver spec fixes at
|
|
125
|
+
* `"2025-11-01"` and reserves for breaking changes to the format — identical
|
|
126
|
+
* in every conforming document, and useless for telling two apart. This comes
|
|
127
|
+
* from `$extensions` on a set or modifier, the only places §4.3 allows them.
|
|
128
|
+
* See `Resolver.getDeclaredVersion()`.
|
|
129
|
+
*/
|
|
130
|
+
version?: string;
|
|
131
|
+
description?: string;
|
|
132
|
+
tokenCount: number;
|
|
133
|
+
/** Mode names, so the shape of the system is visible without loading it. */
|
|
134
|
+
modes: string[];
|
|
135
|
+
/** Modifier names. Absent in entries kept before they were stored. */
|
|
136
|
+
modifiers?: string[];
|
|
137
|
+
/** The file it came from, kept only as a fallback label. */
|
|
138
|
+
fileName?: string;
|
|
139
|
+
/** ISO timestamp of the load this entry records. */
|
|
140
|
+
loadedAt: string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* What the **Figma file** records about having been synced — as opposed to
|
|
145
|
+
* `DocumentStatePayload`, which describes the JSON the user loaded.
|
|
146
|
+
*
|
|
147
|
+
* Kept apart from document state for three reasons: it is meaningful with no
|
|
148
|
+
* document loaded at all (a file synced last week still has an answer), it
|
|
149
|
+
* changes at different moments (after every execute, not on load), and folding
|
|
150
|
+
* it in would make every `GET_DOCUMENT_STATE` read every variable's and style's
|
|
151
|
+
* stamp.
|
|
152
|
+
*/
|
|
153
|
+
export interface SyncStatePayload {
|
|
154
|
+
/** Some variable or style in this Figma file carries a Toghen stamp. */
|
|
155
|
+
hasRecord: boolean;
|
|
156
|
+
/**
|
|
157
|
+
* Variables and styles stamped for a token, including tokens the loaded
|
|
158
|
+
* document does not have. Those belong to some other document synced into the
|
|
159
|
+
* same file — the gap between this and `coveredCount` is information, not an
|
|
160
|
+
* error.
|
|
161
|
+
*/
|
|
162
|
+
recordedCount: number;
|
|
163
|
+
|
|
164
|
+
/** Null when no document is loaded — there is nothing to measure against. */
|
|
165
|
+
documentTokenCount: number | null;
|
|
166
|
+
/** Document tokens stamped in every mode they hold a value for. */
|
|
167
|
+
coveredCount: number | null;
|
|
168
|
+
/** Document tokens covered in some of their modes but not all. */
|
|
169
|
+
partialCount: number | null;
|
|
170
|
+
/**
|
|
171
|
+
* Whose stamps they are: the leading segment of every token they name, each
|
|
172
|
+
* once. A lookup path starts with the document's name, so this names the
|
|
173
|
+
* documents that were synced into the file, answerable with none loaded.
|
|
174
|
+
*/
|
|
175
|
+
recordNames: string[];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Where one token stands between the document and this Figma file.
|
|
180
|
+
*
|
|
181
|
+
* Every name here describes a **condition**, not an intention. `outdated` says
|
|
182
|
+
* the document has changed since the token was last written; it does not say
|
|
183
|
+
* what a sync would do about it. The plan is the only thing that speaks in the
|
|
184
|
+
* future tense, and two regions of one screen each predicting it is how they
|
|
185
|
+
* come to disagree (smell #22).
|
|
186
|
+
*
|
|
187
|
+
* - `current` — Figma holds the document's value in every mode it declares.
|
|
188
|
+
* - `drifted` — they differ, and the variable's stamp proves *Figma* moved
|
|
189
|
+
* since Toghen wrote it, so the change was made in Figma, whether or not the
|
|
190
|
+
* document moved too.
|
|
191
|
+
* - `outdated` — they differ, and Figma still holds what Toghen wrote: the
|
|
192
|
+
* *document* has moved.
|
|
193
|
+
* - `absent` — no Figma variable holds this token.
|
|
194
|
+
* - `adopted` — a variable of this name and type exists that Toghen did not
|
|
195
|
+
* write. Syncing writes into an object with a history nothing here records.
|
|
196
|
+
* - `blocked` — the link cannot exist: a type that varies by mode, a type no
|
|
197
|
+
* variable can hold, or a name taken by a variable of the wrong type.
|
|
198
|
+
* - `not_inspected` — backed by a Figma style, which this round does not
|
|
199
|
+
* compare. Shown rather than omitted, so the counts still reconcile.
|
|
200
|
+
*/
|
|
201
|
+
export type CorrespondenceState = 'current' | 'drifted' | 'outdated' | 'absent' | 'adopted' | 'blocked' | 'not_inspected';
|
|
202
|
+
|
|
203
|
+
/** One mode's row inside an opened token. */
|
|
204
|
+
export interface ModeReport {
|
|
205
|
+
mode: string;
|
|
206
|
+
/** False when the document declares no value for this token in this mode. */
|
|
207
|
+
present: boolean;
|
|
208
|
+
/** Both sides are rendered from Figma's representation, so they compare. */
|
|
209
|
+
documentValue: string | null;
|
|
210
|
+
figmaValue: string | null;
|
|
211
|
+
/** Null when there is nothing to compare against — not the same as "differs". */
|
|
212
|
+
agrees: boolean | null;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export interface TokenReport {
|
|
216
|
+
path: string;
|
|
217
|
+
/** The document's `$type`, or null when the modes disagree about it. */
|
|
218
|
+
type: string | null;
|
|
219
|
+
state: CorrespondenceState;
|
|
220
|
+
note?: string;
|
|
221
|
+
variableName?: string;
|
|
222
|
+
modes: ModeReport[];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* A picture of where every token stands, as of the moment it was taken.
|
|
227
|
+
*
|
|
228
|
+
* `checkedAt` is not decoration. Figma can change under the plugin with no event
|
|
229
|
+
* this codebase watches, so the picture is only true for the instant it was
|
|
230
|
+
* made — and a standing claim with no timestamp is exactly the badge that was
|
|
231
|
+
* caught lying in smell #22.
|
|
232
|
+
*/
|
|
233
|
+
export interface InspectionPayload {
|
|
234
|
+
checkedAt: string;
|
|
235
|
+
collectionName: string;
|
|
236
|
+
tokens: TokenReport[];
|
|
237
|
+
counts: Record<CorrespondenceState, number>;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** One token of the document, as the Tokens pane's browser needs it. See `reference.md`, *The token graph*. */
|
|
241
|
+
export interface TokenGraphEntry {
|
|
242
|
+
path: string;
|
|
243
|
+
type: string | null;
|
|
244
|
+
/** The path without its last segment. */
|
|
245
|
+
group: string;
|
|
246
|
+
/** Per mode of the sum, written for reading; null where the mode has no value. */
|
|
247
|
+
valueByMode: Record<string, string | null>;
|
|
248
|
+
/** Lookup paths this token's value points to, in any mode; the first hop. */
|
|
249
|
+
references: string[];
|
|
250
|
+
/** Lookup paths whose values point to this token. */
|
|
251
|
+
referencedBy: string[];
|
|
252
|
+
/** The CSS custom property lib names it by. */
|
|
253
|
+
codeSyntax: string;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export interface TokenGraphPayload {
|
|
257
|
+
/** The modes `valueByMode` is keyed by, the default first. */
|
|
258
|
+
modes: string[];
|
|
259
|
+
tokens: TokenGraphEntry[];
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export interface PlanPayload {
|
|
263
|
+
planType: PlanType;
|
|
264
|
+
operations: Operation[];
|
|
265
|
+
summary?: PlanCounts;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** A plan's counts, by the names each plan gives them. The variable and style plans add `skipsByCause`, whose sum is their `skips`. */
|
|
269
|
+
export interface PlanCounts {
|
|
270
|
+
[count: string]: number | SkipCounts | undefined;
|
|
271
|
+
skipsByCause?: SkipCounts;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export interface SelectionPayload {
|
|
275
|
+
ids: string[];
|
|
276
|
+
names: string[];
|
|
277
|
+
count: number;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export interface AuditResultPayload {
|
|
281
|
+
nodes: unknown[];
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/** One bindable property of one layer. See `reference.md`, *What the selection is bound to*. */
|
|
285
|
+
export interface PropertyBinding {
|
|
286
|
+
property: string;
|
|
287
|
+
bound: { variableName: string; tokenPath: string | null } | null;
|
|
288
|
+
literal: string | null;
|
|
289
|
+
suggestion: string | null;
|
|
290
|
+
declaredBy: string | null;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* One field of the field table on one layer: the variable it is bound to (on a paint, the first bound paint's), or the
|
|
295
|
+
* style it takes (`textStyle`, `effectStyle`), by name, with the token its stamp names; `null` when it is plain.
|
|
296
|
+
*/
|
|
297
|
+
export interface FieldBinding {
|
|
298
|
+
field: string;
|
|
299
|
+
bound: { name: string; tokenPath: string | null } | null;
|
|
300
|
+
/**
|
|
301
|
+
* What a plain field holds, written for reading (a number, a string, a solid first paint as hex), `null` where it is
|
|
302
|
+
* no one value; absent on a bound field and a style field. A value Apply wrote is plain too: no stamp records it.
|
|
303
|
+
*/
|
|
304
|
+
holds?: string | null;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export interface SelectionBindingsPayload {
|
|
308
|
+
scope: QueryScope;
|
|
309
|
+
readAt: string;
|
|
310
|
+
nodes: Array<{
|
|
311
|
+
id: string;
|
|
312
|
+
name: string;
|
|
313
|
+
kind: string;
|
|
314
|
+
builtFrom: { template: string; fileName: string; kept: string } | null;
|
|
315
|
+
properties: PropertyBinding[];
|
|
316
|
+
/** A row for every field the field table lists, with a token column, for the layer's kind (`applyKindOf`): bound, or plain. */
|
|
317
|
+
fields: FieldBinding[];
|
|
318
|
+
}>;
|
|
319
|
+
/** Layers in scope past the most one answer carries. */
|
|
320
|
+
more?: number;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export interface BoundLayersPayload {
|
|
324
|
+
variableName: string;
|
|
325
|
+
ids: string[];
|
|
326
|
+
pageName: string;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export interface TemplateSummary {
|
|
330
|
+
name: string;
|
|
331
|
+
fileName: string;
|
|
332
|
+
kept: string;
|
|
333
|
+
characters: number;
|
|
334
|
+
members: number;
|
|
335
|
+
axes: Array<{ name: string; values: string[] }>;
|
|
336
|
+
properties: Array<{ name: string; type: string }>;
|
|
337
|
+
tokenGroups: string[];
|
|
338
|
+
builtInto: { page: string; setId: string } | null;
|
|
339
|
+
/** Always null for now: nothing compares a set with its file. */
|
|
340
|
+
differs: boolean | null;
|
|
341
|
+
problems: string[];
|
|
342
|
+
/** A blueprint's needs, as it declares them, before anything looked them up (`LOOK_UP_NEEDS`). Absent for a component file. */
|
|
343
|
+
needs?: {
|
|
344
|
+
tokens?: BlueprintTokens;
|
|
345
|
+
libraries: string[];
|
|
346
|
+
/** Each component the bodies name, by name or as `(key)"…"`; `satisfied` when a body of the blueprint defines it. */
|
|
347
|
+
components: Array<{ name: string; satisfied: boolean }>;
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* What a look found of one kept blueprint's needs (`LOOK_UP_NEEDS`): a standing picture, dated, drawn in the Build pane.
|
|
353
|
+
* Found by the locator's dry pass, which imports nothing. See `reference.md`, *The Build pane's look*.
|
|
354
|
+
*/
|
|
355
|
+
export interface NeedsPayload {
|
|
356
|
+
/** The blueprint, by the name it is kept under. */
|
|
357
|
+
name: string;
|
|
358
|
+
lookedAt: string;
|
|
359
|
+
/** Held against what Load loaded: the entry's file names, and whether a map was loaded with it. */
|
|
360
|
+
tokens?: BlueprintTokens & { state: 'loaded' | 'differs' | 'not loaded'; detail?: string };
|
|
361
|
+
libraries: Array<{ name: string; state: 'in the lock' | 'missing'; by?: LockedLibrary['by']; readAs?: string }>;
|
|
362
|
+
components: Array<{
|
|
363
|
+
name: string;
|
|
364
|
+
/** `would swap`: found, and a build would swap a stamped instance to it (a relink). `by key`: a key, not checked without importing it. */
|
|
365
|
+
state: 'satisfied' | 'found' | 'from a library' | 'by key' | 'would swap' | 'missing' | 'problem';
|
|
366
|
+
/** The declared library it comes from, when it does. */
|
|
367
|
+
library?: string;
|
|
368
|
+
detail?: string;
|
|
369
|
+
}>;
|
|
370
|
+
/** What the look could not answer: a body whose dry pass stopped, so its swaps are not known. */
|
|
371
|
+
notes: string[];
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export interface TemplatesPayload {
|
|
375
|
+
lookedAt: string;
|
|
376
|
+
templates: TemplateSummary[];
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
export interface FigmaCollectionPayload {
|
|
380
|
+
id: string;
|
|
381
|
+
name: string;
|
|
382
|
+
modes: Array<{ modeId: string; name: string }>;
|
|
383
|
+
/** How many local variables the collection holds. */
|
|
384
|
+
variableCount: number;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/** The lock: what each name a file uses from a library resolved to. See `reference.md`, *The lock*. */
|
|
388
|
+
export interface Lock {
|
|
389
|
+
/** Keyed by the library's name, as Figma names it. */
|
|
390
|
+
libraries: Record<string, LockedLibrary>;
|
|
391
|
+
/** Build's map: a name as written → the name to use instead, or a key written `(key)"…"`. */
|
|
392
|
+
overrides?: Overrides;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
export interface LockedLibrary {
|
|
396
|
+
/** The library file's key, only where a run could read it; never guessed. */
|
|
397
|
+
fileKey?: string;
|
|
398
|
+
/** FigCraft: the library's `.figcraft` file, where the run last found it, and the id the file keeps, which a move keeps. */
|
|
399
|
+
path?: string;
|
|
400
|
+
id?: string;
|
|
401
|
+
/** Who wrote the entry last. */
|
|
402
|
+
by: 'discovery' | 'build' | 'command line' | 'person';
|
|
403
|
+
/** A component's name as files write it → its key, and whether the key is a component set's. */
|
|
404
|
+
components: Record<string, { key: string; set: boolean }>;
|
|
405
|
+
/** A token path → the key of the style it names. */
|
|
406
|
+
styles: Record<string, { key: string; kind: 'TEXT' | 'EFFECT' | 'PAINT' }>;
|
|
407
|
+
/** `<collection>/<variable name>` → the variable's key. */
|
|
408
|
+
variables: Record<string, string>;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/** What `getPublishStatusAsync()` answers for a local component, style or variable. */
|
|
412
|
+
export type PublishState = 'UNPUBLISHED' | 'CURRENT' | 'CHANGED';
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* What a library file says of its own keys (`LIBRARY_KEYS`), each with its publish status: the lock's entry for it,
|
|
416
|
+
* before the command line picks who wrote it. See `reference.md`, *The lock*.
|
|
417
|
+
*/
|
|
418
|
+
export interface LibraryKeys {
|
|
419
|
+
/** `figma.fileKey`, where it reads: a private plugin, or a development one with `enablePrivatePluginApi`. */
|
|
420
|
+
fileKey?: string;
|
|
421
|
+
/** Each component set a stamp names (and a component outside a set, if a stamp names one), by the name files write. */
|
|
422
|
+
components: Record<string, { key: string; set: boolean; status: PublishState }>;
|
|
423
|
+
/** Each text, effect and paint style the style sync stamped, by the token path its name spells. */
|
|
424
|
+
styles: Record<string, { key: string; kind: 'TEXT' | 'EFFECT' | 'PAINT'; status: PublishState | undefined }>;
|
|
425
|
+
/** Every local variable, by `<collection>/<variable name>`. */
|
|
426
|
+
variables: Record<string, { key: string; status: PublishState }>;
|
|
427
|
+
/** Lines for the run's report: what the library file could not say (a style's publish status, where Figma gave none). */
|
|
428
|
+
warnings?: string[];
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export type Command =
|
|
432
|
+
/** With `dryRun`, for a run that writes nothing: an older build's root record is turned into stamps in memory only. */
|
|
433
|
+
| {
|
|
434
|
+
type: 'LOAD_DOCUMENT';
|
|
435
|
+
payload: {
|
|
436
|
+
/** Every file picked, by its path relative to the pick. The core reads only these. */
|
|
437
|
+
files: { path: string; text: string }[];
|
|
438
|
+
/** A resolver document, one token file, or several token files in order (the last wins). */
|
|
439
|
+
entry: string | string[];
|
|
440
|
+
/** Token files only: the collection's name. */
|
|
441
|
+
name?: string;
|
|
442
|
+
/** A `$ref` as written → the location to read instead. */
|
|
443
|
+
overrides?: Overrides;
|
|
444
|
+
dryRun?: boolean;
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
| { type: 'GET_RECENTS'; payload: Record<string, never> }
|
|
448
|
+
/** Load a document the user loaded before, from its stored copy. */
|
|
449
|
+
| { type: 'LOAD_RECENT'; payload: { key: string } }
|
|
450
|
+
| { type: 'FORGET_RECENT'; payload: { key: string } }
|
|
451
|
+
| { type: 'EXPORT_DOCUMENT'; payload: { includeExtensions: boolean; contexts?: Record<string, string> } }
|
|
452
|
+
| { type: 'GET_DOCUMENT_STATE'; payload: Record<string, never> }
|
|
453
|
+
| { type: 'GET_SYNC_STATE'; payload: Record<string, never> }
|
|
454
|
+
/**
|
|
455
|
+
* Read Figma back and report where every token stands.
|
|
456
|
+
*
|
|
457
|
+
* Asked for, rather than sent on load: the answer is only true for the moment
|
|
458
|
+
* it was taken, and paying for a full read of the file's variables when the
|
|
459
|
+
* user may never open the Inspector is a cost with no reader.
|
|
460
|
+
*/
|
|
461
|
+
| { type: 'INSPECT_CORRESPONDENCE'; payload: Record<string, never> }
|
|
462
|
+
/** The document's tokens as a graph; answered with `TOKEN_GRAPH`. */
|
|
463
|
+
| { type: 'GET_TOKEN_GRAPH'; payload: Record<string, never> }
|
|
464
|
+
| { type: 'LOAD_CONVENTIONS'; payload: { json: string } }
|
|
465
|
+
| { type: 'GET_CONVENTIONS'; payload: Record<string, never> }
|
|
466
|
+
/**
|
|
467
|
+
* `allowBreaking` lets the plan carry the steps that lose something in Figma (the deletion of a mode its
|
|
468
|
+
* modifier no longer has). Left out, they are listed and not carried: the plugin's own screen never sets it.
|
|
469
|
+
* `overwriteDrift` writes the document's value over a mode Figma holds another value for than the last sync wrote,
|
|
470
|
+
* instead of leaving it as a local edit. The screen never sets it either.
|
|
471
|
+
*/
|
|
472
|
+
| { type: 'PLAN_VARIABLE_SYNC'; payload: { collectionName?: string; allowBreaking?: boolean; overwriteDrift?: boolean } }
|
|
473
|
+
| { type: 'EXECUTE_VARIABLE_SYNC'; payload: Record<string, never> }
|
|
474
|
+
/** `overwriteDrift` writes the document's value over a style changed in Figma since the last sync wrote it, as for a variable. */
|
|
475
|
+
| { type: 'PLAN_STYLE_SYNC'; payload: { collectionName?: string; overwriteDrift?: boolean } }
|
|
476
|
+
| { type: 'EXECUTE_STYLE_SYNC'; payload: Record<string, never> }
|
|
477
|
+
| { type: 'AUDIT_SELECTION'; payload: { scope: QueryScope } }
|
|
478
|
+
| { type: 'PLAN_SCAFFOLD'; payload: { query: string; scope: QueryScope } }
|
|
479
|
+
| { type: 'EXECUTE_SCAFFOLD'; payload: Record<string, never> }
|
|
480
|
+
/** With `nodeId`, only that node; with `property` too, only that property. */
|
|
481
|
+
| { type: 'PLAN_APPLY_BINDINGS'; payload: { query: string; scope: QueryScope; nodeId?: string; property?: string } }
|
|
482
|
+
| { type: 'EXECUTE_APPLY_BINDINGS'; payload: Record<string, never> }
|
|
483
|
+
/**
|
|
484
|
+
* Apply: a token, or a group's tokens, on the layers of `kinds` in `scope`. `tokens` is a token's or a group's path,
|
|
485
|
+
* braces optional. A group's tokens reach fields by their last keys; one token takes `field`, or reaches one by its last key.
|
|
486
|
+
*/
|
|
487
|
+
| { type: 'PLAN_APPLY'; payload: { tokens: string; field?: string; scope: QueryScope; kinds: ApplyKind[] } }
|
|
488
|
+
| { type: 'EXECUTE_APPLY'; payload: Record<string, never> }
|
|
489
|
+
| { type: 'READ_SELECTION_BINDINGS'; payload: { scope: QueryScope } }
|
|
490
|
+
| { type: 'SELECT_NODES'; payload: { ids: string[] } }
|
|
491
|
+
| { type: 'COUNT_BOUND_LAYERS'; payload: { variableName: string } }
|
|
492
|
+
| { type: 'GET_TEMPLATES'; payload: Record<string, never> }
|
|
493
|
+
| { type: 'KEEP_TEMPLATE'; payload: { fileName: string; text: string; replaces?: string } }
|
|
494
|
+
| { type: 'FORGET_TEMPLATE'; payload: { name: string } }
|
|
495
|
+
| { type: 'PLAN_BUILD'; payload: { name: string } }
|
|
496
|
+
| { type: 'EXECUTE_BUILD'; payload: { name: string; allowBreaking?: boolean } }
|
|
497
|
+
| { type: 'GET_FIGMA_COLLECTIONS'; payload: Record<string, never> }
|
|
498
|
+
/** The lock, for the Build pane. */
|
|
499
|
+
| { type: 'GET_LOCK'; payload: Record<string, never> }
|
|
500
|
+
/** Match the variables the kept files bind against the enabled libraries' variables, and write the keys found. */
|
|
501
|
+
| { type: 'DISCOVER_VARIABLES'; payload: Record<string, never> }
|
|
502
|
+
/** Write the selected instance's component, and the selected layer's library styles, under `library`. */
|
|
503
|
+
| { type: 'LOCK_PICK'; payload: { library: string } }
|
|
504
|
+
| { type: 'LOCK_FORGET'; payload: { library: string } }
|
|
505
|
+
| { type: 'SET_BUILD_OVERRIDES'; payload: { overrides: Overrides } }
|
|
506
|
+
/** Look up one kept blueprint's needs, by a dry pass of the locator that imports nothing. Answered with `NEEDS`. */
|
|
507
|
+
| { type: 'LOOK_UP_NEEDS'; payload: { name: string } }
|
|
508
|
+
/** A dry build of every kept file, reporting only the swaps a relink would make and what they need published. Answered with `PLAN_READY`, `relink`. */
|
|
509
|
+
| { type: 'PLAN_RELINK'; payload: Record<string, never> }
|
|
510
|
+
/** In a library file: its own keys and their publish status. Answered with `LIBRARY_KEYS_READ`. */
|
|
511
|
+
| { type: 'LIBRARY_KEYS'; payload: Record<string, never> }
|
|
512
|
+
/** In a target file: write one library's entry, replacing the entry whole. Answered with `LOCK`. */
|
|
513
|
+
| { type: 'LOCK_WRITE'; payload: { library: string; entry: LockedLibrary } }
|
|
514
|
+
| { type: 'GET_SELECTION'; payload: Record<string, never> }
|
|
515
|
+
/**
|
|
516
|
+
* Make Figma agree with one component file, given as its text and its name. The plugin reads,
|
|
517
|
+
* evaluates, validates, plans and applies; the Templates pane's build goes through the same entry.
|
|
518
|
+
* `inputs` is the data an artboard file reads with `(input)`: the tokens of the document the command line
|
|
519
|
+
* resolved, nested by path, each leaf its resolved `$value`. A component file ignores it.
|
|
520
|
+
*/
|
|
521
|
+
| { type: 'RUN_COMPONENT'; payload: { text: string; fileName: string; options: RunOptions; inputs?: Record<string, unknown> } }
|
|
522
|
+
/**
|
|
523
|
+
* Read every file and say every problem of every file, writing nothing. `names` also checks the
|
|
524
|
+
* names each writes against the loaded design system, which a run does not: Figma resolves them.
|
|
525
|
+
*/
|
|
526
|
+
| {
|
|
527
|
+
type: 'CHECK_COMPONENTS';
|
|
528
|
+
payload: {
|
|
529
|
+
files: Array<{ fileName: string; text: string }>;
|
|
530
|
+
names?: boolean;
|
|
531
|
+
inputs?: Record<string, unknown>;
|
|
532
|
+
dryRun?: boolean;
|
|
533
|
+
allowBreaking?: boolean;
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* The end of a run, once, after its last component or artboard step: removes the strays when `allowBreaking`, and stacks
|
|
538
|
+
* Toghen's sets and artboards again on each page the run changed one on. With `dryRun` it only says what it would restack.
|
|
539
|
+
*/
|
|
540
|
+
| { type: 'FINISH_COMPONENTS'; payload: { dryRun?: boolean; allowBreaking?: boolean } }
|
|
541
|
+
| { type: 'CANCEL_OPERATION'; payload: Record<string, never> }
|
|
542
|
+
/**
|
|
543
|
+
* Resize the plugin window.
|
|
544
|
+
*
|
|
545
|
+
* A Figma plugin window cannot be resized by the user — the plugin has to
|
|
546
|
+
* offer the affordance and call `figma.ui.resize()` itself. `remember: true`
|
|
547
|
+
* marks the end of a drag, so the size is persisted once rather than on every
|
|
548
|
+
* intermediate frame.
|
|
549
|
+
*/
|
|
550
|
+
| { type: 'RESIZE_UI'; payload: { width: number; height: number; remember?: boolean } | { mode: WindowMode } };
|
|
551
|
+
|
|
552
|
+
/** The window's two remembered sizes, and the switch between them. */
|
|
553
|
+
export type WindowMode = 'narrow' | 'wide';
|
|
554
|
+
|
|
555
|
+
export type Response =
|
|
556
|
+
/** `report`: the load's own rows, one per collision and per unused key of the map; absent when there are none. */
|
|
557
|
+
| { type: 'DOCUMENT_LOADED'; payload: DocumentStatePayload & { report?: PlanPayload } }
|
|
558
|
+
| { type: 'DOCUMENT_EXPORTED'; payload: { json: string; filename: string } }
|
|
559
|
+
| { type: 'DOCUMENT_STATE'; payload: DocumentStatePayload }
|
|
560
|
+
| { type: 'SYNC_STATE'; payload: SyncStatePayload }
|
|
561
|
+
| { type: 'CORRESPONDENCE'; payload: InspectionPayload }
|
|
562
|
+
| { type: 'TOKEN_GRAPH'; payload: TokenGraphPayload }
|
|
563
|
+
| { type: 'RECENTS'; payload: RecentDocumentSummary[] }
|
|
564
|
+
| { type: 'CONVENTIONS_LOADED'; payload: { count: number } }
|
|
565
|
+
| { type: 'PLAN_READY'; payload: PlanPayload }
|
|
566
|
+
| { type: 'OPERATION_PROGRESS'; payload: ProgressPayload }
|
|
567
|
+
| { type: 'OPERATION_COMPLETE'; payload: OperationSummary }
|
|
568
|
+
| { type: 'AUDIT_RESULT'; payload: AuditResultPayload }
|
|
569
|
+
| { type: 'SELECTION_BINDINGS'; payload: SelectionBindingsPayload }
|
|
570
|
+
| { type: 'BOUND_LAYERS'; payload: BoundLayersPayload }
|
|
571
|
+
| { type: 'TEMPLATES'; payload: TemplatesPayload }
|
|
572
|
+
| { type: 'WINDOW_MODE'; payload: { mode: WindowMode } }
|
|
573
|
+
| { type: 'FIGMA_COLLECTIONS'; payload: FigmaCollectionPayload[] }
|
|
574
|
+
| { type: 'LOCK'; payload: Lock }
|
|
575
|
+
| { type: 'NEEDS'; payload: NeedsPayload }
|
|
576
|
+
| { type: 'LIBRARY_KEYS_READ'; payload: LibraryKeys }
|
|
577
|
+
| { type: 'SELECTION_CHANGED'; payload: SelectionPayload }
|
|
578
|
+
/** Answer to `RUN_COMPONENT`: the engine's report, steps summarised. A run that stopped arrives as `ERROR` with `problems`. */
|
|
579
|
+
| { type: 'COMPONENT_RUN'; payload: { component: string; report: CompactReport } }
|
|
580
|
+
/**
|
|
581
|
+
* Answer to `CHECK_COMPONENTS` when no file has a problem. Otherwise an `ERROR` with `problems`.
|
|
582
|
+
* `strays` are the members outside any set, one breaking line each: present only when there are some, and, unless the
|
|
583
|
+
* check was for a dry run or `allowBreaking`, they are `problems` instead.
|
|
584
|
+
*/
|
|
585
|
+
| { type: 'COMPONENTS_CHECKED'; payload: { files: number; strays?: string[] } }
|
|
586
|
+
/** Answer to `FINISH_COMPONENTS`. */
|
|
587
|
+
| { type: 'COMPONENTS_FINISHED'; payload: { removed: string[]; moved: string[]; notes: string[] } }
|
|
588
|
+
| { type: 'ERROR'; payload: ErrorPayload };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface TokenTypeRoute {
|
|
2
|
+
target: 'variable' | 'style' | 'skip';
|
|
3
|
+
variableType?: VariableResolvedDataType;
|
|
4
|
+
styleType?: 'TEXT' | 'EFFECT' | 'PAINT';
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type TokenTypeConversionMap = Record<string, TokenTypeRoute>;
|
|
8
|
+
|
|
9
|
+
export const defaultTokenTypeMap: TokenTypeConversionMap = {
|
|
10
|
+
color: { target: 'variable', variableType: 'COLOR' },
|
|
11
|
+
dimension: { target: 'variable', variableType: 'FLOAT' },
|
|
12
|
+
letterSpacing: { target: 'variable', variableType: 'FLOAT' },
|
|
13
|
+
number: { target: 'variable', variableType: 'FLOAT' },
|
|
14
|
+
fontFamily: { target: 'variable', variableType: 'STRING' },
|
|
15
|
+
fontWeight: { target: 'variable', variableType: 'FLOAT' },
|
|
16
|
+
string: { target: 'variable', variableType: 'STRING' },
|
|
17
|
+
boolean: { target: 'variable', variableType: 'BOOLEAN' },
|
|
18
|
+
typography: { target: 'style', styleType: 'TEXT' },
|
|
19
|
+
shadow: { target: 'style', styleType: 'EFFECT' },
|
|
20
|
+
gradient: { target: 'style', styleType: 'PAINT' },
|
|
21
|
+
};
|