@design-twin/dtwin 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +959 -2
- package/figma-plugin/code.js +4207 -0
- package/figma-plugin/manifest.json +28 -0
- package/figma-plugin/ui.html +686 -0
- package/layer/cross-check.js +2746 -0
- package/layer/drift-lint.js +1526 -0
- package/layer/get-component.js +553 -0
- package/layer/map-bootstrap.js +1404 -0
- package/layer/map-validate.js +397 -0
- package/package.json +53 -4
- package/src/abort-reason.ts +12 -0
- package/src/asset-compare.ts +175 -0
- package/src/asset-owners.ts +280 -0
- package/src/atomic-write.ts +79 -0
- package/src/bridge-holder.ts +294 -0
- package/src/cli-commands.ts +427 -0
- package/src/client-presence.ts +146 -0
- package/src/clock.ts +45 -0
- package/src/commands.ts +561 -0
- package/src/daemon-auth.ts +174 -0
- package/src/daemon.ts +788 -0
- package/src/delivery.ts +85 -0
- package/src/design-system-layout.ts +232 -0
- package/src/doc-types.ts +1054 -0
- package/src/doctor.ts +602 -0
- package/src/errmsg.ts +14 -0
- package/src/export-reader.ts +409 -0
- package/src/export-verdict.ts +258 -0
- package/src/figma-mcp.ts +942 -0
- package/src/figma-pull.ts +1265 -0
- package/src/file-lock.ts +268 -0
- package/src/file-memo.ts +62 -0
- package/src/hash.ts +6 -0
- package/src/hex-color.ts +39 -0
- package/src/human-ms.ts +7 -0
- package/src/init.ts +424 -0
- package/src/instance-overrides.ts +7 -0
- package/src/json-file.ts +79 -0
- package/src/json-size.ts +108 -0
- package/src/json-util.ts +51 -0
- package/src/library-layout.ts +193 -0
- package/src/node-id.ts +69 -0
- package/src/pages-layout.ts +483 -0
- package/src/pending-requests.ts +187 -0
- package/src/plugin-frames.ts +32 -0
- package/src/png.ts +196 -0
- package/src/ports.ts +25 -0
- package/src/project-layout.ts +189 -0
- package/src/quick-keys.ts +68 -0
- package/src/read-json.ts +67 -0
- package/src/read-opts.ts +125 -0
- package/src/read-plan.ts +105 -0
- package/src/read-run.ts +119 -0
- package/src/route.ts +243 -0
- package/src/seed-components.ts +229 -0
- package/src/served-session.ts +347 -0
- package/src/server-core.ts +1093 -0
- package/src/settings.ts +156 -0
- package/src/source-stamp.ts +16 -0
- package/src/staleness.ts +63 -0
- package/src/stdio.ts +156 -0
- package/src/svg-normalize.ts +205 -0
- package/src/timeouts.ts +76 -0
- package/src/timings.ts +118 -0
- package/src/token-store.ts +261 -0
- package/src/variables-merge.ts +337 -0
- package/src/verbs.ts +140 -0
- package/src/write-out.ts +1255 -0
package/src/doc-types.ts
ADDED
|
@@ -0,0 +1,1054 @@
|
|
|
1
|
+
// doc-types.ts — the PRODUCER-side document contract: the shapes the Figma plugin serializes and the
|
|
2
|
+
// bridge writes to disk (the IR node tree, the pull manifest / screen / layer / index files, the
|
|
3
|
+
// design-system split files, and the collector documents pages-layout.ts / design-system-layout.ts /
|
|
4
|
+
// library-layout.ts split). The design-to-code layer's types.ts re-exports every name here (`export
|
|
5
|
+
// type *`) except ScreenDoc, which it declares narrower — a local declaration shadows the star export, so a
|
|
6
|
+
// name added here that types.ts also declares is silently its local one. Its consumers import from there; the bridge imports from here so its tsc program stays inside
|
|
7
|
+
// its own src/ (the published package ships src/ alone).
|
|
8
|
+
//
|
|
9
|
+
// Types only: no runtime code and NO imports at all (not even `import type`), no Node types, ES2019-safe —
|
|
10
|
+
// the plugin-shared modules (pages-layout, design-system-layout, library-layout) import it, so it is
|
|
11
|
+
// type-checked under figma-plugin/tsconfig.json (lib es2019, no @types/node) as well as the bridge's.
|
|
12
|
+
// The conventions (optional fields, open `(string & {})` vocabularies) are those described in the
|
|
13
|
+
// header of the design-to-code layer's types.ts.
|
|
14
|
+
|
|
15
|
+
// ================================================================ raw JSON
|
|
16
|
+
|
|
17
|
+
export type JsonPrimitive = string | number | boolean | null;
|
|
18
|
+
export type JsonValue = JsonPrimitive | JsonValue[] | JsonObject;
|
|
19
|
+
export interface JsonObject { [key: string]: JsonValue }
|
|
20
|
+
|
|
21
|
+
// ================================================================ the IR (one node of a screen tree)
|
|
22
|
+
|
|
23
|
+
/** Figma SceneNode.type, passed through verbatim by serialize.ts. */
|
|
24
|
+
export type IrNodeType =
|
|
25
|
+
| "FRAME" | "GROUP" | "SECTION" | "COMPONENT" | "COMPONENT_SET" | "INSTANCE"
|
|
26
|
+
| "TEXT" | "TEXT_PATH" | "RECTANGLE" | "ELLIPSE" | "POLYGON" | "STAR" | "LINE" | "VECTOR"
|
|
27
|
+
| "BOOLEAN_OPERATION" | "TABLE" | "SLICE" | "STICKY" | "SHAPE_WITH_TEXT" | "CONNECTOR"
|
|
28
|
+
| (string & {});
|
|
29
|
+
|
|
30
|
+
/** node.tokens / paint.tokens / effect.tokens / run.tokens: boundVariables resolved to token NAMES.
|
|
31
|
+
* A field bound to several variables (e.g. `fills` with several paints) carries an array. */
|
|
32
|
+
export type TokenMap = Record<string, string | string[]>;
|
|
33
|
+
|
|
34
|
+
export interface XY { x: number; y: number }
|
|
35
|
+
|
|
36
|
+
/** `box`: the absoluteBoundingBox, page-space. `x`/`y` only when the parent does not auto-position
|
|
37
|
+
* the node (serialize.ts) — inside an auto-layout parent they are absent on purpose. */
|
|
38
|
+
export interface Box { w: number; h: number; x?: number; y?: number }
|
|
39
|
+
/** `renderBox`: absoluteRenderBounds: larger than `box` with effects, tighter on TEXT; emitted only when it differs from `box`. */
|
|
40
|
+
export interface RenderBox { x: number; y: number; w: number; h: number }
|
|
41
|
+
|
|
42
|
+
export interface RadiusCorners { tl?: number; tr?: number; br?: number; bl?: number }
|
|
43
|
+
|
|
44
|
+
export interface SizeLimits { minWidth?: number; maxWidth?: number; minHeight?: number; maxHeight?: number }
|
|
45
|
+
|
|
46
|
+
/** Lowercased Figma ConstraintType. */
|
|
47
|
+
export type PinMode = "min" | "center" | "max" | "stretch" | "scale";
|
|
48
|
+
export interface Pin { h: PinMode; v: PinMode }
|
|
49
|
+
|
|
50
|
+
// ---- layout (layout.ts). ONE interface, not a discriminated union: every consumer reads it as an
|
|
51
|
+
// optional bag (`node.layout.mode === "absolute"`, `node.layout.padding`, …) without narrowing first.
|
|
52
|
+
// Flex: display:"flex" + flexDirection; grid: display:"grid"; no auto-layout: mode:"absolute" + width/height.
|
|
53
|
+
export type FlexAlign = "flex-start" | "center" | "flex-end" | "space-between" | "space-evenly" | "space-around" | "baseline";
|
|
54
|
+
export interface GridTrack { type?: "flex" | "fixed" | "hug"; value?: number }
|
|
55
|
+
export interface LayoutSpec {
|
|
56
|
+
display?: "flex" | "grid";
|
|
57
|
+
mode?: "absolute";
|
|
58
|
+
/** With mode:"absolute" only — the frame's own size. */
|
|
59
|
+
width?: number;
|
|
60
|
+
height?: number;
|
|
61
|
+
flexDirection?: "row" | "column";
|
|
62
|
+
gap?: number;
|
|
63
|
+
/** [top, right, bottom, left] */
|
|
64
|
+
padding?: number[];
|
|
65
|
+
justifyContent?: FlexAlign;
|
|
66
|
+
alignItems?: FlexAlign;
|
|
67
|
+
flexWrap?: "wrap";
|
|
68
|
+
/** The gap between rows: a grid's row gap, or a wrapping row's track gap (counterAxisSpacing). */
|
|
69
|
+
rowGap?: number;
|
|
70
|
+
alignContent?: FlexAlign;
|
|
71
|
+
/** auto-layout was INFERRED (node.inferredAutoLayout) — not set by the designer. */
|
|
72
|
+
inferred?: true;
|
|
73
|
+
reverseZ?: true;
|
|
74
|
+
columns?: number;
|
|
75
|
+
rows?: number;
|
|
76
|
+
/** The gap between columns: a grid's column gap, or a wrapping column's track gap (counterAxisSpacing). */
|
|
77
|
+
columnGap?: number;
|
|
78
|
+
columnSizes?: GridTrack[];
|
|
79
|
+
rowSizes?: GridTrack[];
|
|
80
|
+
autoFlow?: string;
|
|
81
|
+
autoTracks?: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Per-frame layout grids (layout.ts simplifyGrid). */
|
|
85
|
+
export interface LayoutGridSpec {
|
|
86
|
+
pattern?: "columns" | "rows" | "grid";
|
|
87
|
+
size?: number;
|
|
88
|
+
gutter?: number;
|
|
89
|
+
count?: number;
|
|
90
|
+
offset?: number;
|
|
91
|
+
alignment?: "min" | "max" | "center" | "stretch";
|
|
92
|
+
visible?: false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ---- paints (paint.ts simplifyFills). Discriminated on `type`.
|
|
96
|
+
export interface PaintBase {
|
|
97
|
+
/** Lowercased paint blendMode, omitted when NORMAL. */
|
|
98
|
+
blend?: string;
|
|
99
|
+
/** Non-solid paints only (a SOLID folds opacity into its hex alpha). */
|
|
100
|
+
opacity?: number;
|
|
101
|
+
/** Paint-level variable bindings (e.g. `{ color: "Primary/Primary" }`). */
|
|
102
|
+
tokens?: TokenMap;
|
|
103
|
+
/** Never emitted (invisible paints are filtered out); design-to-code/verify-expect.ts still tests `!== false`. */
|
|
104
|
+
visible?: boolean;
|
|
105
|
+
}
|
|
106
|
+
export interface SolidPaint extends PaintBase { type: "solid"; color: string }
|
|
107
|
+
export interface GradientStop { pos: number; color: string; tokens?: TokenMap }
|
|
108
|
+
export interface GradientPaint extends PaintBase {
|
|
109
|
+
type: "gradient";
|
|
110
|
+
kind: "GRADIENT_LINEAR" | "GRADIENT_RADIAL" | "GRADIENT_ANGULAR" | "GRADIENT_DIAMOND";
|
|
111
|
+
stops?: GradientStop[];
|
|
112
|
+
transform?: number[][];
|
|
113
|
+
}
|
|
114
|
+
export interface ImageFilters {
|
|
115
|
+
exposure?: number; contrast?: number; saturation?: number; temperature?: number; tint?: number; highlights?: number; shadows?: number;
|
|
116
|
+
}
|
|
117
|
+
export interface MediaPaint extends PaintBase {
|
|
118
|
+
type: "image" | "video";
|
|
119
|
+
scaleMode?: "fill" | "fit" | "crop" | "tile";
|
|
120
|
+
/** correlates the fill to an exported source asset */
|
|
121
|
+
hash?: string;
|
|
122
|
+
scale?: number;
|
|
123
|
+
rotation?: number;
|
|
124
|
+
/** crop rect transform */
|
|
125
|
+
transform?: number[][];
|
|
126
|
+
filters?: ImageFilters;
|
|
127
|
+
/** native pixel size of the source image (image paints only) */
|
|
128
|
+
intrinsicSize?: { w: number; h: number };
|
|
129
|
+
}
|
|
130
|
+
export interface PatternPaint extends PaintBase {
|
|
131
|
+
type: "pattern";
|
|
132
|
+
sourceNodeId?: string;
|
|
133
|
+
tileType?: string;
|
|
134
|
+
scale?: number;
|
|
135
|
+
spacing?: XY;
|
|
136
|
+
align?: string;
|
|
137
|
+
}
|
|
138
|
+
/** One shader property assignment (paint.ts putShaderProperties), keyed in `properties` by Figma's opaque
|
|
139
|
+
* property-definition id. Colours are hex (8-digit when alpha < 1); a bare COLOR is wrapped as `{color}`
|
|
140
|
+
* so it can't be mistaken for a TEXT string. A value (or the colour inside a point/gradient) bound to a
|
|
141
|
+
* variable is omitted here and named in the paint/effect `tokens` instead (`defId`, `defId.color`,
|
|
142
|
+
* `defId.stops.N.color`). */
|
|
143
|
+
export type ShaderPropValue =
|
|
144
|
+
| boolean | string | number
|
|
145
|
+
| { color: string }
|
|
146
|
+
| XY
|
|
147
|
+
| { x: number; y: number; x2: number; y2: number }
|
|
148
|
+
| { x: number; y: number; radius: number; angle?: number }
|
|
149
|
+
| { x: number; y: number; color?: string }
|
|
150
|
+
| { stops: Array<{ pos: number; color?: string }> };
|
|
151
|
+
/** Figma's declared shader input type, lowercased (`ShaderPropDef.type`). */
|
|
152
|
+
export type ShaderPropType =
|
|
153
|
+
| "boolean" | "text" | "number" | "image" | "instance_swap" | "slot" | "color" | "point" | "line" | "circle"
|
|
154
|
+
| "circle_point" | "color_point" | "gradient";
|
|
155
|
+
/** One entry of a shader paint/effect's `propertyDefs` (paint.ts putShaderProperties), keyed by the same
|
|
156
|
+
* property-definition id as `properties`. `name` is the input's identifier as the shader source declares
|
|
157
|
+
* it (Figma's properties panel can show a different label, which the Plugin API does not expose); `type`
|
|
158
|
+
* is Figma's declared input type lowercased (`boolean`, `text`, `number`, `image`, `instance_swap`, `slot`, `color`,
|
|
159
|
+
* `point`, `line`, `circle`, `circle_point`, `color_point`, `gradient`); `default` is normalised like a
|
|
160
|
+
* `properties` value (a variable-bound default, or a bound colour inside one, is left out of `default`
|
|
161
|
+
* and named in this definition's `tokens` under `default`, `default.color` or `default.stops.N.color`);
|
|
162
|
+
* `description` is omitted when empty. Definitions are emitted for every input the shader declares,
|
|
163
|
+
* whether or not `properties` assigns it. */
|
|
164
|
+
export interface ShaderPropDef {
|
|
165
|
+
name: string; type: ShaderPropType | (string & {}); default?: ShaderPropValue; description?: string; tokens?: TokenMap;
|
|
166
|
+
}
|
|
167
|
+
export interface ShaderPaint extends PaintBase {
|
|
168
|
+
type: "shader"; shaderId?: string; properties?: Record<string, ShaderPropValue>; propertyDefs?: Record<string, ShaderPropDef>;
|
|
169
|
+
}
|
|
170
|
+
export type Paint = SolidPaint | GradientPaint | MediaPaint | PatternPaint | ShaderPaint;
|
|
171
|
+
|
|
172
|
+
/** `strokes` is an OBJECT (paint.ts simplifyStrokes), never an array of paints. */
|
|
173
|
+
export interface Strokes {
|
|
174
|
+
/** hex of every visible SOLID stroke paint */
|
|
175
|
+
colors?: string[];
|
|
176
|
+
/** every NON-solid stroke paint, in full */
|
|
177
|
+
paints?: Paint[];
|
|
178
|
+
weight?: number;
|
|
179
|
+
/** per-side weights when strokeWeight is mixed */
|
|
180
|
+
weights?: { top?: number; right?: number; bottom?: number; left?: number };
|
|
181
|
+
align?: "inside" | "outside" | "center";
|
|
182
|
+
dash?: number[];
|
|
183
|
+
cap?: string;
|
|
184
|
+
join?: string;
|
|
185
|
+
miter?: number;
|
|
186
|
+
/** Variable-width (tapered) stroke: one of Figma's named presets, or `custom` with its `variableWidthPoints`
|
|
187
|
+
* compacted to `{pos, width}` (points only for `custom`). */
|
|
188
|
+
variableWidth?: { profile: "uniform" | "wedge" | "taper" | "quarter_taper" | "eye" | "mirrored_taper" | "custom"; points?: Array<{ pos: number; width: number }> };
|
|
189
|
+
/** Brush / dynamic stroke (complexStrokeProperties); absent for a plain (BASIC) stroke. */
|
|
190
|
+
complex?: ComplexStroke;
|
|
191
|
+
}
|
|
192
|
+
/** complexStrokeProperties, enums lowercased, numbers rounded. `brushName` is `custom` for a custom brush. */
|
|
193
|
+
export type ComplexStroke =
|
|
194
|
+
| { type: "dynamic"; frequency: number; wiggle: number; smoothen: number }
|
|
195
|
+
| { type: "brush"; brushType: "scatter"; brushName: string; gap: number; wiggle: number; sizeJitter: number; angularJitter: number; rotation: number }
|
|
196
|
+
| { type: "brush"; brushType: "stretch"; brushName: string; direction: "forward" | "backward" };
|
|
197
|
+
|
|
198
|
+
// ---- effects (effects.ts). Discriminated on `type` (lowercased Figma Effect.type).
|
|
199
|
+
export interface EffectBase { tokens?: TokenMap }
|
|
200
|
+
export interface ShadowEffect extends EffectBase {
|
|
201
|
+
type: "drop_shadow" | "inner_shadow";
|
|
202
|
+
color?: string;
|
|
203
|
+
offset?: XY;
|
|
204
|
+
radius?: number;
|
|
205
|
+
spread?: number;
|
|
206
|
+
blendMode?: string;
|
|
207
|
+
behindNode?: true;
|
|
208
|
+
}
|
|
209
|
+
export interface BlurEffect extends EffectBase {
|
|
210
|
+
type: "layer_blur" | "background_blur";
|
|
211
|
+
radius?: number;
|
|
212
|
+
blurType?: "progressive";
|
|
213
|
+
startOffset?: XY;
|
|
214
|
+
endOffset?: XY;
|
|
215
|
+
startRadius?: number;
|
|
216
|
+
}
|
|
217
|
+
export interface NoiseEffect extends EffectBase {
|
|
218
|
+
type: "noise";
|
|
219
|
+
noiseType?: string; color?: string; density?: number; noiseSize?: number; noiseSizeVector?: XY; secondaryColor?: string; opacity?: number; blendMode?: string;
|
|
220
|
+
}
|
|
221
|
+
export interface GlassEffect extends EffectBase {
|
|
222
|
+
type: "glass";
|
|
223
|
+
lightIntensity?: number; lightAngle?: number; refraction?: number; depth?: number; dispersion?: number; radius?: number;
|
|
224
|
+
}
|
|
225
|
+
export interface TextureEffect extends EffectBase { type: "texture"; noiseSize?: number; noiseSizeVector?: XY; radius?: number; clipToShape?: boolean }
|
|
226
|
+
export interface ShaderEffect extends EffectBase {
|
|
227
|
+
type: "shader"; shaderId?: string; properties?: Record<string, ShaderPropValue>; propertyDefs?: Record<string, ShaderPropDef>;
|
|
228
|
+
}
|
|
229
|
+
export type Effect = ShadowEffect | BlurEffect | NoiseEffect | GlassEffect | TextureEffect | ShaderEffect;
|
|
230
|
+
|
|
231
|
+
// ---- text (text.ts)
|
|
232
|
+
/** A Figma length. `{unit:"auto"}` carries no value (line-height AUTO). */
|
|
233
|
+
export interface LengthSpec { value?: number; unit: "px" | "percent" | "auto" }
|
|
234
|
+
export interface FontSpec {
|
|
235
|
+
size?: number | "mixed";
|
|
236
|
+
family?: string;
|
|
237
|
+
/** Figma style name, verbatim: "Regular", "SemiBold", "Bold Italic"… */
|
|
238
|
+
weight?: string;
|
|
239
|
+
/** numeric CSS weight when the API exposes it (node-level only) */
|
|
240
|
+
weightValue?: number;
|
|
241
|
+
/** "italic" when the styled-text segment's `fontStyle` is ITALIC (plugin-api.d.ts 1.139.0 L5496,
|
|
242
|
+
* `type FontStyle = 'REGULAR' | 'ITALIC'` L4000); REGULAR is the default and is omitted. Segment-only
|
|
243
|
+
* in the API: a uniform node that had no readable segment never carries it. */
|
|
244
|
+
fontStyle?: "italic";
|
|
245
|
+
/** FontName.variationSettings: the variable font axis values applied, e.g. {wght: 600, slnt: -10}.
|
|
246
|
+
* Absent for a static font. */
|
|
247
|
+
variationSettings?: Record<string, number>;
|
|
248
|
+
lineHeight?: LengthSpec;
|
|
249
|
+
letterSpacing?: LengthSpec;
|
|
250
|
+
case?: "upper" | "lower" | "title" | "small_caps" | "small_caps_forced";
|
|
251
|
+
decoration?: "underline" | "strikethrough";
|
|
252
|
+
decorationStyle?: string;
|
|
253
|
+
decorationColor?: string;
|
|
254
|
+
decorationThickness?: LengthSpec;
|
|
255
|
+
decorationOffset?: LengthSpec;
|
|
256
|
+
decorationSkipInk?: false;
|
|
257
|
+
openType?: string[];
|
|
258
|
+
/** first visible SOLID fill of the text, as hex */
|
|
259
|
+
color?: string;
|
|
260
|
+
// node-level only (not on runs[].font)
|
|
261
|
+
align?: "left" | "center" | "right" | "justified";
|
|
262
|
+
paragraphSpacing?: number;
|
|
263
|
+
paragraphIndent?: number;
|
|
264
|
+
listSpacing?: number;
|
|
265
|
+
leadingTrim?: string;
|
|
266
|
+
valign?: "center" | "bottom";
|
|
267
|
+
textWrap?: "balance" | "pretty";
|
|
268
|
+
hangingList?: true;
|
|
269
|
+
hangingPunctuation?: true;
|
|
270
|
+
}
|
|
271
|
+
/** One styled segment of mixed-format text. */
|
|
272
|
+
export interface TextRun {
|
|
273
|
+
text: string;
|
|
274
|
+
font: FontSpec;
|
|
275
|
+
href?: string;
|
|
276
|
+
linkNode?: string;
|
|
277
|
+
list?: string;
|
|
278
|
+
indent?: number;
|
|
279
|
+
textStyle?: string;
|
|
280
|
+
fillStyle?: string;
|
|
281
|
+
tokens?: TokenMap;
|
|
282
|
+
/** This run's paragraph wrap style — only when the node-level `textWrapStyle` is mixed (else it is
|
|
283
|
+
* `font.textWrap` on the node); AUTO omitted. */
|
|
284
|
+
textWrap?: "balance" | "pretty";
|
|
285
|
+
/** This run's paragraph spacing / indent (px; StyledTextSegment plugin-api.d.ts 1.139.0 L5560/L5564) —
|
|
286
|
+
* only when the node-level value is mixed (L10027/L10084; else it is `font.paragraphSpacing` /
|
|
287
|
+
* `font.paragraphIndent` on the node); 0 omitted. */
|
|
288
|
+
paragraphSpacing?: number;
|
|
289
|
+
paragraphIndent?: number;
|
|
290
|
+
/** StyledTextSegment.textStyleOverrides (plugin-api.d.ts 1.139.0 L5588, TextStyleOverrideType
|
|
291
|
+
* L5465-5467), lower-cased: "semantic_italic" | "semantic_weight" | "hyperlink" | "text_decoration".
|
|
292
|
+
* Only when non-empty AND the run has a text style (the overrides are "over a text style"). The
|
|
293
|
+
* runtime hands the plugin bare strings, not the typings' `{ type }` objects (live, 2026-09-25;
|
|
294
|
+
* figma-plugin/src/text.ts `styleOverrides` reads both). */
|
|
295
|
+
textStyleOverrides?: string[];
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ---- prototype (prototype.ts)
|
|
299
|
+
/** Lowercased Figma Trigger.type; "unknown" when the trigger had no type. */
|
|
300
|
+
export type ReactionTrigger =
|
|
301
|
+
| "on_click" | "on_hover" | "on_press" | "on_drag" | "after_timeout" | "mouse_enter" | "mouse_leave"
|
|
302
|
+
| "mouse_up" | "mouse_down" | "on_key_down" | "on_media_hit" | "on_media_end" | "unknown" | (string & {});
|
|
303
|
+
export type ActionType =
|
|
304
|
+
| "back" | "close" | "url" | "node" | "set_variable" | "set_variable_mode" | "conditional" | "update_media_runtime" | (string & {});
|
|
305
|
+
export type ActionNavigation = "navigate" | "swap" | "overlay" | "scroll_to" | "change_to" | (string & {});
|
|
306
|
+
/** Control points of a custom cubic-bezier easing — Figma's `easingFunctionCubicBezier`, carried verbatim. */
|
|
307
|
+
export interface CubicBezier { x1: number; y1: number; x2: number; y2: number }
|
|
308
|
+
export interface Transition {
|
|
309
|
+
type?: string;
|
|
310
|
+
direction?: string;
|
|
311
|
+
/** seconds */
|
|
312
|
+
duration?: number;
|
|
313
|
+
matchLayers?: boolean;
|
|
314
|
+
easing?: string;
|
|
315
|
+
cubicBezier?: CubicBezier;
|
|
316
|
+
spring?: unknown;
|
|
317
|
+
}
|
|
318
|
+
export interface Action {
|
|
319
|
+
type: ActionType;
|
|
320
|
+
navigation?: ActionNavigation;
|
|
321
|
+
url?: string;
|
|
322
|
+
openInNewTab?: true;
|
|
323
|
+
destinationId?: string;
|
|
324
|
+
/** the destination node's NAME, resolved */
|
|
325
|
+
destination?: string;
|
|
326
|
+
preserveScroll?: true;
|
|
327
|
+
resetScroll?: true;
|
|
328
|
+
resetVideo?: true;
|
|
329
|
+
resetInteractive?: true;
|
|
330
|
+
overlayOffset?: XY;
|
|
331
|
+
transition?: Transition;
|
|
332
|
+
// SET_VARIABLE / SET_VARIABLE_MODE / UPDATE_MEDIA_RUNTIME / CONDITIONAL
|
|
333
|
+
variable?: string;
|
|
334
|
+
value?: unknown;
|
|
335
|
+
collection?: string;
|
|
336
|
+
mode?: string;
|
|
337
|
+
mediaAction?: string;
|
|
338
|
+
amountToSkip?: number;
|
|
339
|
+
newTimestamp?: number;
|
|
340
|
+
conditionalBlocks?: Array<{ condition?: unknown; actions?: Action[] }>;
|
|
341
|
+
}
|
|
342
|
+
/** The real shape is `reactions[].actions[]` (plural on both) with `trigger` a plain string. */
|
|
343
|
+
export interface Reaction {
|
|
344
|
+
trigger?: ReactionTrigger;
|
|
345
|
+
timeout?: number;
|
|
346
|
+
delay?: number;
|
|
347
|
+
keyCodes?: number[];
|
|
348
|
+
device?: string;
|
|
349
|
+
mediaHitTime?: number;
|
|
350
|
+
actions?: Action[];
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// ---- components (components.ts)
|
|
354
|
+
/** INSTANCE → its main component; the join keys back to the catalog. */
|
|
355
|
+
export interface MainComponentRef {
|
|
356
|
+
name: string;
|
|
357
|
+
id?: string;
|
|
358
|
+
key?: string;
|
|
359
|
+
remote?: true;
|
|
360
|
+
setId?: string;
|
|
361
|
+
setKey?: string;
|
|
362
|
+
setName?: string;
|
|
363
|
+
/** the variant's own name, e.g. "Type=Primary, Status=Default" */
|
|
364
|
+
variant?: string;
|
|
365
|
+
}
|
|
366
|
+
/** INSTANCE.componentProperties → `{ propName: value }` (Figma's "#id" suffix stripped). */
|
|
367
|
+
export type ComponentPropValues = Record<string, string | boolean>;
|
|
368
|
+
|
|
369
|
+
export interface Annotation { label?: string; markdown?: string; categoryId?: string; props?: string[] }
|
|
370
|
+
export interface ExportSetting { format: string; suffix?: string; constraint?: { type: string; value: number } }
|
|
371
|
+
export interface Overlay { position?: string; closeOnClickOutside?: true; background?: string }
|
|
372
|
+
export interface InstanceOverride { id: string; fields: string[] }
|
|
373
|
+
/** exportAsync failed but the node's resolved outlines were recovered (assets.ts geometryOf). */
|
|
374
|
+
export interface Geometry { fills?: string[]; strokes?: string[]; w?: number; h?: number }
|
|
375
|
+
export interface TableCell extends Partial<TextFields> { row: number; col: number; fills?: Paint[] }
|
|
376
|
+
export interface DevResource { name: string; url: string; nodeId?: string; inheritedNodeId?: string }
|
|
377
|
+
/** Node-level style references (variables.ts nodeStyles): style NAMES, by slot. */
|
|
378
|
+
export interface StyleRefs { fill?: string; stroke?: string; effect?: string; text?: string; grid?: string }
|
|
379
|
+
/** `{ collectionName: modeName }` (an unreadable collection keeps its raw ids). */
|
|
380
|
+
export type ModeMap = Record<string, string>;
|
|
381
|
+
|
|
382
|
+
/** The text surface (text.ts serializeText), merged into a TEXT / TEXT_PATH node. */
|
|
383
|
+
export interface TextFields {
|
|
384
|
+
text: string;
|
|
385
|
+
/** mixed-format text: one entry per styled run (font is then the FIRST run's) */
|
|
386
|
+
runs?: TextRun[];
|
|
387
|
+
font?: FontSpec;
|
|
388
|
+
/** uniform text's own per-run bindings */
|
|
389
|
+
textTokens?: TokenMap;
|
|
390
|
+
href?: string;
|
|
391
|
+
linkNode?: string;
|
|
392
|
+
list?: string;
|
|
393
|
+
indent?: number;
|
|
394
|
+
/** uniform text's overrides over its text style (`styles.text`) — same rule as `TextRun.textStyleOverrides` */
|
|
395
|
+
textStyleOverrides?: string[];
|
|
396
|
+
autoResize?: "width_and_height" | "height" | "truncate";
|
|
397
|
+
truncate?: true;
|
|
398
|
+
maxLines?: number;
|
|
399
|
+
missingFont?: true;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/** One keyframe of an opt-in motion track (motion.ts). `value` is already compacted: number, hex colour, {x,y}, …
|
|
403
|
+
* (absent when the keyframe carried no readable value). `easing` is the lower-cased MotionEasing type plus
|
|
404
|
+
* its curve params, or — when the easing is bound to an EASING variable (typings `MotionEasing |
|
|
405
|
+
* VariableAlias`) — the SAME alias shape a top-level variable reference uses: `{aliasOf: <variable
|
|
406
|
+
* name>}`, falling back to `{aliasOf: <id>}` when the name can't be resolved (variables.ts's
|
|
407
|
+
* VariableAlias -> {aliasOf} resolution, via the run's `varName` memo (run.ts) over
|
|
408
|
+
* `figma.variables.getVariableByIdAsync`). */
|
|
409
|
+
export interface MotionKeyframe { t: number; value?: JsonValue; easing?: { type: string; cubicBezier?: CubicBezier; spring?: unknown } | VariableAlias }
|
|
410
|
+
/** One manually-keyframed field (ManualKeyframeBinding): its base value and the keyframes. */
|
|
411
|
+
export interface MotionTrack { base?: JsonValue; keyframes?: MotionKeyframe[] }
|
|
412
|
+
/** One timeline-animated field (KeyframeBinding): base value, the timeline's duration, and its tracks,
|
|
413
|
+
* each with a non-SET keyframe operation and keyframes. */
|
|
414
|
+
export interface MotionAnimation { base?: JsonValue; duration?: number; tracks?: Array<{ op?: "offset" | "scale"; keyframes?: MotionKeyframe[] }> }
|
|
415
|
+
/** A fills[i] / strokes[i] motion entry (PaintManualKeyframeTrack / PaintKeyframeBinding): either ONE binding
|
|
416
|
+
* for the whole paint, or — for a shader paint — one binding per shader property id under `properties`. */
|
|
417
|
+
export type MotionPaintTrack<T> = T | { properties: Record<string, T> };
|
|
418
|
+
/** An effects[i] motion entry (EffectManualKeyframeTracks / EffectKeyframeBindings): one binding per
|
|
419
|
+
* EffectKeyframeFieldName (`RADIUS`, `OFFSET_X`, `COLOR`, …) plus per-shader-property bindings under `properties`. */
|
|
420
|
+
export type MotionEffectTracks<T> = Record<string, T> & { properties?: Record<string, T> };
|
|
421
|
+
/** The indexed collections of a motion map, keyed by paint/effect index as a string ("0", "1", …). */
|
|
422
|
+
export interface MotionIndexedTracks<T> {
|
|
423
|
+
fills?: Record<string, MotionPaintTrack<T>>;
|
|
424
|
+
strokes?: Record<string, MotionPaintTrack<T>>;
|
|
425
|
+
effects?: Record<string, MotionEffectTracks<T>>;
|
|
426
|
+
}
|
|
427
|
+
/** node.manualKeyframeTracks: one MotionTrack per KeyframePropertyFieldName (`TRANSLATION_X`, `OPACITY`, …),
|
|
428
|
+
* plus the indexed fills/strokes/effects collections. */
|
|
429
|
+
export type MotionTracks = Record<string, MotionTrack> & MotionIndexedTracks<MotionTrack>;
|
|
430
|
+
/** node.animations: the same layout as MotionTracks, with MotionAnimation bindings. */
|
|
431
|
+
export type MotionAnimations = Record<string, MotionAnimation> & MotionIndexedTracks<MotionAnimation>;
|
|
432
|
+
/** The opt-in motion read on a node (`--motion`): timelines, per-field tracks, applied animation styles. */
|
|
433
|
+
/** One configured value of an applied animation style's `props` (AnimationStyleConfiguration.props):
|
|
434
|
+
* a plain string/number/boolean, or — for a MotionEasing / VariableAlias value — the same compacted
|
|
435
|
+
* shape as a keyframe's `easing` (lower-cased curve type + params, or `{aliasOf: <name or id>}`). */
|
|
436
|
+
export type MotionStylePropValue = string | number | boolean | NonNullable<MotionKeyframe["easing"]>;
|
|
437
|
+
/** One Figma animation style with its configuration (AppliedFigmaAnimationStyle): the instance id, the
|
|
438
|
+
* style's name and styleId, its description (absent when empty), duration (s), timelineOffset (s, omitted
|
|
439
|
+
* when 0) and per-prop values. The same shape is one entry of a custom animation style
|
|
440
|
+
* (CustomAnimationStyle.animationEntries). */
|
|
441
|
+
export interface MotionStyleEntry {
|
|
442
|
+
id: string;
|
|
443
|
+
name: string;
|
|
444
|
+
styleId: string;
|
|
445
|
+
description?: string;
|
|
446
|
+
duration?: number;
|
|
447
|
+
timelineOffset?: number;
|
|
448
|
+
props?: Record<string, MotionStylePropValue>;
|
|
449
|
+
}
|
|
450
|
+
/** One node.animationStyles row (AppliedAnimationStyle). A Figma animation style is a MotionStyleEntry. */
|
|
451
|
+
export interface MotionStyle extends Omit<MotionStyleEntry, "id" | "name" | "styleId"> {
|
|
452
|
+
/** present on every row except an unknown-kind one with the field absent */
|
|
453
|
+
id?: string;
|
|
454
|
+
name?: string;
|
|
455
|
+
styleId?: string;
|
|
456
|
+
/** a row of an animation style kind this exporter does not know (a kind added to the Plugin API after
|
|
457
|
+
* the typings it uses): the applied row's `type`, lower-cased. Only the ids, name and
|
|
458
|
+
* timelineOffset are exported, each only when present, since the row's shape is not guessed: it
|
|
459
|
+
* carries no duration, props or entries, and a consumer treats it as an unknown animation style.
|
|
460
|
+
* Absent on Figma and custom rows. */
|
|
461
|
+
kind?: string;
|
|
462
|
+
/** a CustomAnimationStyle (applied type "CUSTOM"): the applied row has the ids, name and timelineOffset
|
|
463
|
+
* only (no duration/props); `description` and `entries` come from the style itself, looked up by
|
|
464
|
+
* styleId, and are absent when that lookup finds no custom animation style. */
|
|
465
|
+
custom?: true;
|
|
466
|
+
/** the Figma animations the custom animation style contains, in order. `[]` is a style that was read and
|
|
467
|
+
* has none; absent means the lookup found no custom animation style or its entries could not be read. */
|
|
468
|
+
entries?: MotionStyleEntry[];
|
|
469
|
+
}
|
|
470
|
+
export interface NodeMotion {
|
|
471
|
+
timelines?: Array<{ id: string; duration: number }>;
|
|
472
|
+
manualTracks?: MotionTracks;
|
|
473
|
+
animations?: MotionAnimations;
|
|
474
|
+
/** node.animationStyles (AppliedAnimationStyle[]): the applied instance id, the style's name/styleId,
|
|
475
|
+
* and its configuration (duration, timelineOffset, per-prop values); a custom one is expanded from
|
|
476
|
+
* its CustomAnimationStyle (MotionStyle). */
|
|
477
|
+
styles?: MotionStyle[];
|
|
478
|
+
}
|
|
479
|
+
export interface IrNode extends Partial<TextFields> {
|
|
480
|
+
type: IrNodeType;
|
|
481
|
+
name: string;
|
|
482
|
+
id: string;
|
|
483
|
+
/** The ONE visibility flag (visible-tree.ts): the designer switched this layer off. Descendants inherit it. */
|
|
484
|
+
hidden?: true;
|
|
485
|
+
children?: IrNode[];
|
|
486
|
+
|
|
487
|
+
// instance / component identity
|
|
488
|
+
props?: ComponentPropValues;
|
|
489
|
+
/** a BOOLEAN/TEXT prop whose value is driven by a variable */
|
|
490
|
+
propTokens?: TokenMap;
|
|
491
|
+
/** main component's bare name (INSTANCE) or the node's own name (COMPONENT / COMPONENT_SET) */
|
|
492
|
+
component?: string;
|
|
493
|
+
mainComponent?: MainComponentRef;
|
|
494
|
+
/** which component prop drives this sublayer's visibility / text / swapped instance */
|
|
495
|
+
propRefs?: { visible?: string; characters?: string; mainComponent?: string };
|
|
496
|
+
overrides?: InstanceOverride[];
|
|
497
|
+
exposedInstances?: string[];
|
|
498
|
+
detachedFrom?: { key?: string; componentId?: string };
|
|
499
|
+
|
|
500
|
+
// geometry + layout
|
|
501
|
+
x?: number;
|
|
502
|
+
y?: number;
|
|
503
|
+
box?: Box;
|
|
504
|
+
renderBox?: RenderBox;
|
|
505
|
+
layout?: LayoutSpec;
|
|
506
|
+
layoutGrids?: LayoutGridSpec[];
|
|
507
|
+
absolute?: true;
|
|
508
|
+
/** layoutGrow (Figma emits 0|1; only a truthy value is written) */
|
|
509
|
+
grow?: number;
|
|
510
|
+
alignSelf?: "min" | "center" | "max" | "stretch";
|
|
511
|
+
/** FIXED (the default) is never written. */
|
|
512
|
+
widthMode?: "fill" | "hug";
|
|
513
|
+
heightMode?: "fill" | "hug";
|
|
514
|
+
scroll?: "horizontal" | "vertical" | "both";
|
|
515
|
+
gridColumnSpan?: number;
|
|
516
|
+
gridRowSpan?: number;
|
|
517
|
+
gridColumnStart?: number;
|
|
518
|
+
gridRowStart?: number;
|
|
519
|
+
gridJustifySelf?: "start" | "center" | "end";
|
|
520
|
+
gridAlignSelf?: "start" | "center" | "end";
|
|
521
|
+
sizeLimits?: SizeLimits;
|
|
522
|
+
pin?: Pin;
|
|
523
|
+
fixedChildren?: number;
|
|
524
|
+
clip?: true;
|
|
525
|
+
|
|
526
|
+
fills?: Paint[];
|
|
527
|
+
strokes?: Strokes;
|
|
528
|
+
strokesInLayout?: true;
|
|
529
|
+
effects?: Effect[];
|
|
530
|
+
radius?: number | RadiusCorners;
|
|
531
|
+
/** the corner was Figma's "fully rounded" sentinel; `radius` holds what actually renders */
|
|
532
|
+
radiusFull?: true;
|
|
533
|
+
opacity?: number;
|
|
534
|
+
rotation?: number;
|
|
535
|
+
flipped?: true;
|
|
536
|
+
skew?: number;
|
|
537
|
+
blendMode?: string;
|
|
538
|
+
mask?: true;
|
|
539
|
+
maskType?: string;
|
|
540
|
+
cornerSmoothing?: number;
|
|
541
|
+
aspectRatio?: number;
|
|
542
|
+
arc?: { start?: number; end?: number; innerRadius?: number };
|
|
543
|
+
shape?: { points?: number; innerRadius?: number };
|
|
544
|
+
booleanOp?: "union" | "intersect" | "subtract" | "exclude";
|
|
545
|
+
exportSettings?: ExportSetting[];
|
|
546
|
+
overlay?: Overlay;
|
|
547
|
+
|
|
548
|
+
// tokens / styles / prototype / handoff
|
|
549
|
+
tokens?: TokenMap;
|
|
550
|
+
styles?: StyleRefs;
|
|
551
|
+
reactions?: Reaction[];
|
|
552
|
+
/** modes pinned ON this node */
|
|
553
|
+
variableModes?: ModeMap;
|
|
554
|
+
/** root only: the EFFECTIVE mode per collection, ancestors and page included */
|
|
555
|
+
resolvedModes?: ModeMap;
|
|
556
|
+
annotations?: Annotation[];
|
|
557
|
+
devStatus?: "ready_for_dev" | "completed";
|
|
558
|
+
devStatusNote?: string;
|
|
559
|
+
/** opt-in reads */
|
|
560
|
+
css?: Record<string, string>;
|
|
561
|
+
pluginData?: Record<string, string>;
|
|
562
|
+
sharedData?: Record<string, Record<string, string>>;
|
|
563
|
+
motion?: NodeMotion;
|
|
564
|
+
|
|
565
|
+
// assets (asset nodes are LEAVES — no children)
|
|
566
|
+
asset?: string;
|
|
567
|
+
/** `true` = skipped via --no-assets; `"hidden"` = a graphic that is hidden (itself or an ancestor) — never exported */
|
|
568
|
+
assetSkipped?: true | "hidden";
|
|
569
|
+
/** a hidden node whose `asset` is a visible twin's file (set by the bridge): the twin's node id */
|
|
570
|
+
assetFrom?: string;
|
|
571
|
+
/** on an `asset` leaf only: the rotation/flip/skew Figma already drew into the file — never apply it again */
|
|
572
|
+
sourceTransform?: { rotation?: number; flipped?: true; skew?: number };
|
|
573
|
+
geometry?: Geometry;
|
|
574
|
+
tableCells?: TableCell[][];
|
|
575
|
+
|
|
576
|
+
// root-only enrichment attached inline by collect.ts rootTree() (single-screen pulls)
|
|
577
|
+
reference?: string;
|
|
578
|
+
devResources?: DevResource[];
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// ================================================================ the documents a pull writes
|
|
582
|
+
|
|
583
|
+
/** run.ts manifestOf(): {nodes, skipped, truncated, assetsFailed, warnings[]} + the raw counters
|
|
584
|
+
* (every RunStats counter is always present — a new Run starts them all at zero). */
|
|
585
|
+
export interface Manifest {
|
|
586
|
+
nodes: number;
|
|
587
|
+
/** derived: === truncated */
|
|
588
|
+
skipped: number;
|
|
589
|
+
truncated: number;
|
|
590
|
+
assetsFailed: number;
|
|
591
|
+
/** nodes that would have exported an asset but were skipped via --no-assets */
|
|
592
|
+
assetsSkipped: number;
|
|
593
|
+
/** vector/icon nodes with no visible paint — nothing to render, never attempted, never a failure */
|
|
594
|
+
assetsSkippedInvisible: number;
|
|
595
|
+
/** real export failures RECOVERED as inline path geometry (assets.ts geometryOf) */
|
|
596
|
+
assetsGeometry: number;
|
|
597
|
+
/** hidden graphics (the node or an ancestor hidden) not exported — `assetSkipped:"hidden"`; absent on older exports */
|
|
598
|
+
assetsHidden?: number;
|
|
599
|
+
/** nodes kept as hidden (the node or an ancestor hidden); absent on older exports */
|
|
600
|
+
hiddenNodes?: number;
|
|
601
|
+
/** which opt-in reads ran */
|
|
602
|
+
reads?: string[];
|
|
603
|
+
warnings: string[];
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// ---- assets (figma-plugin/src/run.ts Asset — the ONE definition, used by the plugin's collectors
|
|
607
|
+
// and by the bridge's write-out.ts alike)
|
|
608
|
+
/** What assets.ts registers: an SVG (`text`), a 2x PNG (`base64`), or a source image's own bytes. */
|
|
609
|
+
export type AssetFormat = "svg" | "png" | "jpg" | "webp" | "gif" | "bin";
|
|
610
|
+
/** `reference`: the whole-frame screenshot; `source`: an image fill's original bytes; absent: a rendered node. */
|
|
611
|
+
export type AssetKind = "reference" | "source";
|
|
612
|
+
/** One exported asset. Exactly one of `base64`/`text` carries the bytes (a manifest-only entry has neither). */
|
|
613
|
+
export interface Asset {
|
|
614
|
+
id: string;
|
|
615
|
+
name: string;
|
|
616
|
+
format: AssetFormat;
|
|
617
|
+
/** Basename this asset must be written as. The PRODUCER names the file (assets.ts) and the node
|
|
618
|
+
* tree's `asset` path is built from the same string, so a consumer never re-derives the convention
|
|
619
|
+
* — a mismatch here would silently point every `asset:` path at a file that isn't on disk. */
|
|
620
|
+
file: string;
|
|
621
|
+
base64?: string;
|
|
622
|
+
text?: string;
|
|
623
|
+
kind?: AssetKind;
|
|
624
|
+
/** Content hash of the bytes (assets.ts contentHash). Written into the per-screen asset index so a
|
|
625
|
+
* consumer can see which files are byte-identical without diffing them. */
|
|
626
|
+
hash?: string;
|
|
627
|
+
/** Every node id that resolved to THIS file. Present only when more than one did — i.e. when the
|
|
628
|
+
* same artwork was reached through several instance paths and deduped to one file. */
|
|
629
|
+
from?: string[];
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
export interface Measurement {
|
|
633
|
+
page: string; pageId: string;
|
|
634
|
+
start?: { nodeId?: string; side?: string }; end?: { nodeId?: string; side?: string };
|
|
635
|
+
text?: string;
|
|
636
|
+
/** Figma's MeasurementOffset, verbatim: `{type:"INNER", relative}` or `{type:"OUTER", fixed}`. */
|
|
637
|
+
offset?: { type: "INNER"; relative: number } | { type: "OUTER"; fixed: number };
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/** A `--node` / `--selection` pull: pages/<Page>/<Screen>__<id>.json (collect.ts screenResult → write-out.ts writeScreen). */
|
|
641
|
+
export interface ScreenExport {
|
|
642
|
+
exportedAt?: string;
|
|
643
|
+
/** the human screen label (the layer name, or "selection") */
|
|
644
|
+
screen?: string;
|
|
645
|
+
nodes: IrNode[];
|
|
646
|
+
manifest?: Manifest;
|
|
647
|
+
page?: string;
|
|
648
|
+
pageId?: string;
|
|
649
|
+
nodeId?: string;
|
|
650
|
+
measurements?: Measurement[];
|
|
651
|
+
/** which Figma file the pull talked to (write-out.ts) */
|
|
652
|
+
sourceFile?: string;
|
|
653
|
+
sourceFileKey?: string;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/** A page-walk layer file: pages/<Page>/<Layer>__<id>.json (pages-layout.ts buildPageLayout). */
|
|
657
|
+
export interface LayerFile {
|
|
658
|
+
name: string;
|
|
659
|
+
id: string;
|
|
660
|
+
page?: string;
|
|
661
|
+
pageId?: string;
|
|
662
|
+
sourceFile?: string;
|
|
663
|
+
tree: IrNode;
|
|
664
|
+
reference?: string;
|
|
665
|
+
devResources?: DevResource[];
|
|
666
|
+
manifest?: Manifest;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* ANY document shape a screen reader accepts — the three `rootsOf()` implementations do
|
|
671
|
+
* `Array.isArray(doc.nodes) ? doc.nodes : doc.tree ? [doc.tree] : doc.id || doc.type ? [doc] : []`.
|
|
672
|
+
* One wide optional bag rather than a union, so that duck-typing stays legal without `in` narrowing.
|
|
673
|
+
* ScreenExport, LayerFile and IrNode are each assignable to it.
|
|
674
|
+
*/
|
|
675
|
+
export interface ScreenDoc {
|
|
676
|
+
nodes?: IrNode[];
|
|
677
|
+
tree?: IrNode;
|
|
678
|
+
id?: string;
|
|
679
|
+
type?: string;
|
|
680
|
+
name?: string;
|
|
681
|
+
screen?: string;
|
|
682
|
+
exportedAt?: string;
|
|
683
|
+
manifest?: Manifest;
|
|
684
|
+
page?: string;
|
|
685
|
+
pageId?: string;
|
|
686
|
+
nodeId?: string;
|
|
687
|
+
reference?: string;
|
|
688
|
+
devResources?: DevResource[];
|
|
689
|
+
resolvedModes?: ModeMap;
|
|
690
|
+
sourceFile?: string;
|
|
691
|
+
measurements?: Measurement[];
|
|
692
|
+
children?: IrNode[];
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/** One row of pages/index.json `layers[]` / pages/<Page>/index.json `layers[]` — a page-walk row
|
|
696
|
+
* (name/id/type/page/pageId/nodes/bytes/title/texts/file) or a single-screen row (write-out.ts
|
|
697
|
+
* writeScreen `entry`: + exportedAt/variables/assets/reference/w/h). */
|
|
698
|
+
export interface IndexRow {
|
|
699
|
+
name: string;
|
|
700
|
+
id: string;
|
|
701
|
+
type?: IrNodeType;
|
|
702
|
+
page?: string;
|
|
703
|
+
pageId?: string;
|
|
704
|
+
/** relative to the export root: pages/<dir>/<Layer>__<id>.json */
|
|
705
|
+
file: string;
|
|
706
|
+
nodes?: number;
|
|
707
|
+
bytes?: number;
|
|
708
|
+
/** the visible on-screen title (pages-layout.ts deriveTitle), absent when none could be derived.
|
|
709
|
+
* `| undefined` (also on texts): buildPageLayout writes both after spreading the collector's row, so a
|
|
710
|
+
* layer with no tree must be able to mask a title/texts that row carried — undefined, not absent. */
|
|
711
|
+
title?: string | undefined;
|
|
712
|
+
/** the first distinct TEXT strings, reading order */
|
|
713
|
+
texts?: string[] | undefined;
|
|
714
|
+
exportedAt?: string;
|
|
715
|
+
sourceFile?: string;
|
|
716
|
+
/** the Figma file's key, beside its name (two files can share a name); absent when the client gave none */
|
|
717
|
+
sourceFileKey?: string;
|
|
718
|
+
/** sibling <Screen>.vars.json */
|
|
719
|
+
variables?: string;
|
|
720
|
+
/** sibling <Screen>.assets.json */
|
|
721
|
+
assets?: string;
|
|
722
|
+
reference?: string;
|
|
723
|
+
/** reference PNG px per design px (write-out.ts writeScreen). */
|
|
724
|
+
referenceScale?: number;
|
|
725
|
+
/** where the reference PNG's top-left sits relative to the node's `box` top-left, in design px — the
|
|
726
|
+
* render bounds include shadows/outside strokes, so it is usually <= 0. */
|
|
727
|
+
referenceOffset?: { x: number; y: number };
|
|
728
|
+
w?: number;
|
|
729
|
+
h?: number;
|
|
730
|
+
}
|
|
731
|
+
export interface PageDirEntry { page: string; pageId?: string; dir: string; index: string; layers: number }
|
|
732
|
+
export interface PageIndex { page: string; pageId?: string; layers: IndexRow[] }
|
|
733
|
+
export interface PrototypeFlow { page: string; pageId: string; nodeId: string; name: string }
|
|
734
|
+
export interface PageSettings { page: string; pageId: string; background?: Paint[]; prototypeBackground?: Paint[] }
|
|
735
|
+
/** pages/index.json (pages-layout.ts meta + mergeRootIndex). `layers` is absent on the oldest exports. */
|
|
736
|
+
export interface PagesRootIndex {
|
|
737
|
+
pageDirs: PageDirEntry[];
|
|
738
|
+
layers?: IndexRow[];
|
|
739
|
+
exportedAt?: string;
|
|
740
|
+
scope?: "all-pages" | "page" | "current-page";
|
|
741
|
+
page?: string;
|
|
742
|
+
pages?: string[];
|
|
743
|
+
flows?: PrototypeFlow[];
|
|
744
|
+
pageSettings?: PageSettings[];
|
|
745
|
+
measurements?: Measurement[];
|
|
746
|
+
manifest?: Manifest;
|
|
747
|
+
sourceFile?: string;
|
|
748
|
+
sourceFileKey?: string;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// ================================================================ the design system (split files)
|
|
752
|
+
|
|
753
|
+
/** The stamp every design-system file repeats (design-system-layout.ts). */
|
|
754
|
+
export interface DesignSystemStamp {
|
|
755
|
+
exportedAt?: string;
|
|
756
|
+
file?: string;
|
|
757
|
+
/** Lowercased DocumentNode.documentColorProfile (components.ts): note the UNDERSCORE in "display_p3". */
|
|
758
|
+
colorProfile?: "legacy" | "srgb" | "display_p3" | (string & {});
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/** design-system.json — the slim POINTER manifest (never a payload; catalog-input.ts refuses it). */
|
|
762
|
+
export interface DesignSystemManifest extends DesignSystemStamp {
|
|
763
|
+
files: { tokens: string } & StyleKeyed<string> & {
|
|
764
|
+
componentsLocal: string; componentsLibrary: string; hygiene: string;
|
|
765
|
+
/** only when a COMPONENT_SET detail file was written */
|
|
766
|
+
componentsDir?: string;
|
|
767
|
+
};
|
|
768
|
+
counts: { collections: number; variables: number } & StyleKeyed<number> & { components: number; libraryComponents: number; hygiene: number };
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/** Figma's VariableResolvedDataType, verbatim (EASING/TIMING are the motion variable types). */
|
|
772
|
+
export type VariableType = "COLOR" | "FLOAT" | "STRING" | "BOOLEAN" | "EASING" | "TIMING";
|
|
773
|
+
export interface VariableAlias { aliasOf: string }
|
|
774
|
+
/**
|
|
775
|
+
* Figma's VariableComposedColor (plugin-typings 1.139, Update 139): a COLOR value authored as a colour
|
|
776
|
+
* plus a SEPARATE opacity, where the colour, the opacity, or both are variable aliases. Each alias half
|
|
777
|
+
* is emitted exactly like a top-level alias (`{aliasOf: <target name, or id when unresolvable>}`); a raw
|
|
778
|
+
* colour is a hex string like any COLOR value; a raw opacity is Figma's number VERBATIM — not rescaled —
|
|
779
|
+
* on Figma's 0–100 scale: "An opacity percentage from 0 to 100, or an alias to a FLOAT variable" (REST API
|
|
780
|
+
* variables types, VariableComposedColor.opacity: https://developers.figma.com/docs/rest-api/variables-types/).
|
|
781
|
+
* An aliased opacity names a FLOAT on the same scale. How it combines with a colour whose own alpha is
|
|
782
|
+
* < 1 is not documented; design-to-code multiplies (alpha × opacity/100) — an inference.
|
|
783
|
+
* The two members are Figma's two, so "neither half is an alias" is unrepresentable here too.
|
|
784
|
+
*/
|
|
785
|
+
export type ComposedColor =
|
|
786
|
+
| { color: string; opacity: VariableAlias }
|
|
787
|
+
| { color: VariableAlias; opacity: number | VariableAlias };
|
|
788
|
+
export interface VariableComposedColor { composed: ComposedColor }
|
|
789
|
+
/** A per-mode value: COLOR → hex string (alpha kept); FLOAT → number; STRING/BOOLEAN → as is; alias → {aliasOf};
|
|
790
|
+
* composed colour → {composed: {color, opacity}} (see ComposedColor). */
|
|
791
|
+
export type VariableValue = string | number | boolean | VariableAlias | VariableComposedColor;
|
|
792
|
+
export interface VariableCollection {
|
|
793
|
+
name: string;
|
|
794
|
+
modes: string[];
|
|
795
|
+
/** the base/`:root` mode */
|
|
796
|
+
default?: string;
|
|
797
|
+
theming: boolean;
|
|
798
|
+
extended?: true;
|
|
799
|
+
hiddenFromPublishing?: true;
|
|
800
|
+
key?: string;
|
|
801
|
+
/** library mode only: current | changed | unpublished */
|
|
802
|
+
publish?: string;
|
|
803
|
+
}
|
|
804
|
+
export interface Variable {
|
|
805
|
+
name: string;
|
|
806
|
+
type: VariableType;
|
|
807
|
+
/** Absent when the plugin could not resolve the variable's collection (variables.ts writes `collOf(id)?.name`). */
|
|
808
|
+
collection?: string;
|
|
809
|
+
/** The collection's Figma key — collection NAMES repeat ("Spacing" ×2); absent on old exports */
|
|
810
|
+
collectionKey?: string;
|
|
811
|
+
tier: "primitive" | "semantic";
|
|
812
|
+
/** keyed by MODE NAME */
|
|
813
|
+
values: Record<string, VariableValue>;
|
|
814
|
+
scopes?: string[];
|
|
815
|
+
codeSyntax?: { WEB?: string; ANDROID?: string; iOS?: string };
|
|
816
|
+
description?: string;
|
|
817
|
+
remote?: true;
|
|
818
|
+
hiddenFromPublishing?: true;
|
|
819
|
+
/** durable cross-file identity — a variable IS its key; names are not unique */
|
|
820
|
+
key?: string;
|
|
821
|
+
publish?: string;
|
|
822
|
+
}
|
|
823
|
+
/** One contributing pull recorded in a merged variables.json (variables-merge.ts sliceEntry). */
|
|
824
|
+
export interface SliceEntry {
|
|
825
|
+
screen: string;
|
|
826
|
+
file?: string;
|
|
827
|
+
at: string;
|
|
828
|
+
variables: number;
|
|
829
|
+
collections: number;
|
|
830
|
+
/** every variable key this pull carried */
|
|
831
|
+
keys?: string[];
|
|
832
|
+
}
|
|
833
|
+
export interface ValueConflict {
|
|
834
|
+
kind?: "value";
|
|
835
|
+
name: string; collection?: string; key?: string;
|
|
836
|
+
was?: Record<string, VariableValue>; now?: Record<string, VariableValue>;
|
|
837
|
+
from?: string;
|
|
838
|
+
}
|
|
839
|
+
export interface SameNameConflict {
|
|
840
|
+
kind: "same-name";
|
|
841
|
+
name: string; collection?: string; sameValue: boolean;
|
|
842
|
+
variants: Array<{ key?: string; values?: Record<string, VariableValue>; screens: string[] }>;
|
|
843
|
+
}
|
|
844
|
+
export type VariableConflict = ValueConflict | SameNameConflict;
|
|
845
|
+
/**
|
|
846
|
+
* design-system/tokens.json ({stamp, collections, variables}) AND the merged design/export/variables.json
|
|
847
|
+
* / per-screen <Screen>.vars.json ({collections, variables, hygiene, _slices, _conflicts, _note,
|
|
848
|
+
* exportedAt}) — the same token catalog shape, read by the same emitters (tokens.ts). A merged union has
|
|
849
|
+
* `exportedAt: null` until a slice carries an `at`.
|
|
850
|
+
*/
|
|
851
|
+
export interface TokensDoc extends Omit<DesignSystemStamp, "exportedAt"> {
|
|
852
|
+
exportedAt?: string | null;
|
|
853
|
+
collections?: VariableCollection[];
|
|
854
|
+
variables?: Variable[];
|
|
855
|
+
hygiene?: string[];
|
|
856
|
+
_slices?: SliceEntry[];
|
|
857
|
+
_conflicts?: VariableConflict[];
|
|
858
|
+
_note?: string;
|
|
859
|
+
/** library pulls only. `| undefined`: LibraryStamp and DesignSystemDoc declare theirs as
|
|
860
|
+
* `TokensDoc["source"]`, and an optional property's indexed type includes undefined — without it a
|
|
861
|
+
* library tokens file (TokensDoc & LibraryStamp) is not assignable to TokensDoc. */
|
|
862
|
+
source?: { role: "library"; libraryName?: string; fileKey?: string; collectionKeys?: string[] } | undefined;
|
|
863
|
+
}
|
|
864
|
+
export type VariablesDoc = TokensDoc;
|
|
865
|
+
|
|
866
|
+
/** The catalog meta every style shares (components.ts styleMeta). */
|
|
867
|
+
export interface StyleMeta { key?: string; id?: string; remote?: true; docs?: string[]; publish?: string; description?: string; tokens?: TokenMap }
|
|
868
|
+
export interface PaintStyle extends StyleMeta { name: string; paints?: Paint[] }
|
|
869
|
+
export interface TextStyle extends StyleMeta {
|
|
870
|
+
name: string;
|
|
871
|
+
size?: number;
|
|
872
|
+
/** font family */
|
|
873
|
+
font?: string;
|
|
874
|
+
weight?: string;
|
|
875
|
+
lineHeight?: LengthSpec;
|
|
876
|
+
letterSpacing?: LengthSpec;
|
|
877
|
+
case?: string;
|
|
878
|
+
decoration?: string;
|
|
879
|
+
paragraphSpacing?: number;
|
|
880
|
+
paragraphIndent?: number;
|
|
881
|
+
leadingTrim?: string;
|
|
882
|
+
listSpacing?: number;
|
|
883
|
+
textWrap?: string;
|
|
884
|
+
}
|
|
885
|
+
export interface EffectStyle extends StyleMeta { name: string; effects?: Effect[] }
|
|
886
|
+
export interface GridStyle extends StyleMeta { name: string; grids?: LayoutGridSpec[] }
|
|
887
|
+
/** A local custom animation style (CustomAnimationStyle): the Figma animations it contains, in order.
|
|
888
|
+
* `entries: []` is a style with none; `entries` is absent when the list could not be read. */
|
|
889
|
+
export interface AnimationStyle extends StyleMeta { name: string; entries?: MotionStyleEntry[] }
|
|
890
|
+
/**
|
|
891
|
+
* The style kinds: each kind's name (its list's key in the plugin's `styles` block) and the shape of one style of it.
|
|
892
|
+
* The kinds' files, pointers and counts follow from this map: design-system-layout.ts STYLE_KIND_ROWS needs a row per
|
|
893
|
+
* kind (its file and its manifest key, typed from the kind), and every manifest's `files` and `counts` carry a key per
|
|
894
|
+
* kind (StyleKeyed). Adding a kind is a line here, its row there and the plugin's read.
|
|
895
|
+
*/
|
|
896
|
+
export interface StyleByKind {
|
|
897
|
+
paint: PaintStyle;
|
|
898
|
+
text: TextStyle;
|
|
899
|
+
effect: EffectStyle;
|
|
900
|
+
grid: GridStyle;
|
|
901
|
+
/** local custom animation styles (CustomAnimationStyle) */
|
|
902
|
+
animation: AnimationStyle;
|
|
903
|
+
}
|
|
904
|
+
export type StyleKind = keyof StyleByKind;
|
|
905
|
+
export type AnyStyle = StyleByKind[StyleKind];
|
|
906
|
+
/** A kind's key in a manifest's `files` pointer map and in its `counts`: `stylesPaint`, `stylesText`, … */
|
|
907
|
+
export type StyleKey<K extends StyleKind = StyleKind> = `styles${Capitalize<K>}`;
|
|
908
|
+
/** One value per style kind, under its StyleKey. */
|
|
909
|
+
export type StyleKeyed<V> = { [K in StyleKind as StyleKey<K>]: V };
|
|
910
|
+
/** design-system/styles.<kind>.json (one file per StyleKind) */
|
|
911
|
+
export interface StylesDoc<S = AnyStyle> extends DesignSystemStamp { styles: S[] }
|
|
912
|
+
export type PaintStylesDoc = StylesDoc<PaintStyle>;
|
|
913
|
+
export type TextStylesDoc = StylesDoc<TextStyle>;
|
|
914
|
+
export type EffectStylesDoc = StylesDoc<EffectStyle>;
|
|
915
|
+
export type GridStylesDoc = StylesDoc<GridStyle>;
|
|
916
|
+
export type AnimationStylesDoc = StylesDoc<AnimationStyle>;
|
|
917
|
+
/** design-system/hygiene.json */
|
|
918
|
+
export interface HygieneDoc extends DesignSystemStamp { hygiene: string[] }
|
|
919
|
+
|
|
920
|
+
export type ComponentPropType = "VARIANT" | "BOOLEAN" | "TEXT" | "INSTANCE_SWAP" | "SLOT";
|
|
921
|
+
/** One componentPropertyDefinitions entry (real "#uid" key kept under `key`). */
|
|
922
|
+
export interface ComponentPropDef {
|
|
923
|
+
key: string;
|
|
924
|
+
type: ComponentPropType;
|
|
925
|
+
/** VARIANT only */
|
|
926
|
+
options?: string[];
|
|
927
|
+
/** VARIANT/TEXT: string; BOOLEAN: boolean; INSTANCE_SWAP: a component id (string) */
|
|
928
|
+
default?: string | boolean;
|
|
929
|
+
preferredValues?: Array<{ type: string; key: string }>;
|
|
930
|
+
description?: string;
|
|
931
|
+
/** SLOT only: Figma's SlotSettings as set (a null min/max = no limit, omitted). */
|
|
932
|
+
slotSettings?: { stretchChildOnInsert?: boolean; displayEmptyByDefault?: boolean; minChildren?: number; maxChildren?: number; allowPreferredValuesOnly?: boolean };
|
|
933
|
+
tokens?: TokenMap;
|
|
934
|
+
/** library entries with derivedFrom:"instances" — the values SEEN, not the option list */
|
|
935
|
+
observed?: Array<string | boolean>;
|
|
936
|
+
}
|
|
937
|
+
export interface CatalogVariant { id: string; name: string; key?: string; values?: Record<string, string>; node?: IrNode }
|
|
938
|
+
/**
|
|
939
|
+
* One row of components.local.json (a local COMPONENT / COMPONENT_SET: id, page, pageId, props from
|
|
940
|
+
* definitions, visuals) or components.library.json (a consumed library main recovered from instances
|
|
941
|
+
* — remote:true, source, uses, derivedFrom; no id/page).
|
|
942
|
+
*/
|
|
943
|
+
export interface CatalogComponent {
|
|
944
|
+
name: string;
|
|
945
|
+
type: "COMPONENT" | "COMPONENT_SET";
|
|
946
|
+
key?: string;
|
|
947
|
+
id?: string;
|
|
948
|
+
page?: string;
|
|
949
|
+
pageId?: string;
|
|
950
|
+
description?: string;
|
|
951
|
+
remote?: true;
|
|
952
|
+
docs?: string[];
|
|
953
|
+
props?: Record<string, ComponentPropDef>;
|
|
954
|
+
visuals?: { fills?: Paint[]; strokes?: Strokes; effects?: Effect[]; radius?: number | RadiusCorners; opacity?: number; blendMode?: string };
|
|
955
|
+
publish?: string;
|
|
956
|
+
/** COMPONENT_SET: the variants (their node trees split out to `variantsFile`) */
|
|
957
|
+
variants?: CatalogVariant[];
|
|
958
|
+
variantsFile?: string;
|
|
959
|
+
/** standalone COMPONENT with variantVisuals: its node tree, split out to `nodeFile` */
|
|
960
|
+
node?: IrNode;
|
|
961
|
+
nodeFile?: string;
|
|
962
|
+
// library entries only (libraries.ts)
|
|
963
|
+
source?: string;
|
|
964
|
+
uses?: number;
|
|
965
|
+
variant?: string;
|
|
966
|
+
derivedFrom?: "definitions" | "instances";
|
|
967
|
+
}
|
|
968
|
+
/** design-system/components.local.json / components.library.json */
|
|
969
|
+
export interface ComponentsCatalog extends DesignSystemStamp { components: CatalogComponent[] }
|
|
970
|
+
/** design-system/components/<name>__<id>.json — a COMPONENT_SET's variants with node trees, or a COMPONENT's node. */
|
|
971
|
+
export interface ComponentDetailFile extends DesignSystemStamp {
|
|
972
|
+
name: string;
|
|
973
|
+
setId?: string; setKey?: string; variants?: CatalogVariant[];
|
|
974
|
+
id?: string; key?: string; node?: IrNode;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// ================================================================ the collector documents the bridge splits
|
|
978
|
+
|
|
979
|
+
// ---- pages-layout.ts (buildPageLayout's input)
|
|
980
|
+
/** One top-level layer in the collector's layersDoc (figma-plugin/src/collect.ts collectFull). */
|
|
981
|
+
export interface LayersDocLayer {
|
|
982
|
+
name: string;
|
|
983
|
+
id: string;
|
|
984
|
+
page?: string;
|
|
985
|
+
pageId?: string;
|
|
986
|
+
tree: IrNode;
|
|
987
|
+
reference?: string;
|
|
988
|
+
devResources?: DevResource[];
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
/** The collector's per-layer index row — IndexRow before pages-layout.ts adds title/texts/file. */
|
|
992
|
+
export type LayersDocIndexRow = Omit<IndexRow, "file" | "title" | "texts">;
|
|
993
|
+
|
|
994
|
+
/**
|
|
995
|
+
* The collector's layersDoc: the run-wide manifest fields that become pages/index.json, plus the two
|
|
996
|
+
* parallel arrays pages-layout.ts splits into per-page files. write-out.ts's writeExport also stamps
|
|
997
|
+
* `sourceFile` on it.
|
|
998
|
+
*/
|
|
999
|
+
export interface LayersDoc extends Omit<PagesRootIndex, "pageDirs" | "layers"> {
|
|
1000
|
+
layers?: LayersDocLayer[];
|
|
1001
|
+
index?: LayersDocIndexRow[];
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// ---- design-system-layout.ts / library-layout.ts (the designSystem doc they split)
|
|
1005
|
+
/** The plugin's styles block (components.ts buildDesignSystem), one array per style kind. */
|
|
1006
|
+
export type DesignSystemStyles = { [K in StyleKind]?: Array<StyleByKind[K]> };
|
|
1007
|
+
|
|
1008
|
+
/**
|
|
1009
|
+
* The plugin's designSystem doc (components.ts buildDesignSystem) — the ONE document
|
|
1010
|
+
* design-system-layout.ts and library-layout.ts split. `source` is present on a library-mode export only (collectLibraryFile).
|
|
1011
|
+
*/
|
|
1012
|
+
export interface DesignSystemDoc extends DesignSystemStamp {
|
|
1013
|
+
collections?: VariableCollection[];
|
|
1014
|
+
variables?: Variable[];
|
|
1015
|
+
components?: CatalogComponent[];
|
|
1016
|
+
styles?: DesignSystemStyles;
|
|
1017
|
+
hygiene?: string[];
|
|
1018
|
+
source?: TokensDoc["source"];
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
// ---- library-layout.ts (libraries/<dir>/index.json and libraries/index.json)
|
|
1022
|
+
/** The stamp every library file carries: the design-system stamp plus `source` (library mode). */
|
|
1023
|
+
export interface LibraryStamp extends DesignSystemStamp {
|
|
1024
|
+
source?: TokensDoc["source"];
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
export type LibraryCounts = { collections: number; variables: number } & StyleKeyed<number> & { components: number; hygiene: number };
|
|
1028
|
+
|
|
1029
|
+
/** libraries/<dir>/index.json — the slim manifest: stamp + `source` + `files` pointer map + `counts`
|
|
1030
|
+
* + `publish` (a per-status count, absent when nothing carries a publish status). */
|
|
1031
|
+
export interface LibraryManifest extends LibraryStamp {
|
|
1032
|
+
files: { tokens: string } & StyleKeyed<string> & { components: string; hygiene: string };
|
|
1033
|
+
counts: LibraryCounts;
|
|
1034
|
+
publish?: Record<string, number>;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
/** One row of libraries/index.json. */
|
|
1038
|
+
export interface LibrariesIndexRow {
|
|
1039
|
+
dir: string;
|
|
1040
|
+
libraryName?: string;
|
|
1041
|
+
file?: string;
|
|
1042
|
+
fileKey?: string;
|
|
1043
|
+
collectionKeys?: string[];
|
|
1044
|
+
exportedAt?: string;
|
|
1045
|
+
counts: LibraryCounts;
|
|
1046
|
+
publish?: Record<string, number>;
|
|
1047
|
+
index: string;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
/** libraries/index.json. Rows already on disk are carried through as read (untyped JSON). */
|
|
1051
|
+
export interface LibrariesIndex {
|
|
1052
|
+
libraries: unknown[];
|
|
1053
|
+
generatedAt: string;
|
|
1054
|
+
}
|