conductor-figma 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 +193 -0
- package/bin/conductor.js +58 -0
- package/package.json +37 -0
- package/src/design/exporter.js +72 -0
- package/src/design/intelligence.js +341 -0
- package/src/index.js +29 -0
- package/src/server.js +115 -0
- package/src/tools/handlers.js +423 -0
- package/src/tools/registry.js +292 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 0xDragoon
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# CONDUCTOR
|
|
2
|
+
|
|
3
|
+
> Experimental software. DYOR. Use at your own risk.
|
|
4
|
+
|
|
5
|
+
Design-intelligent MCP server for Figma. 61 tools across 10 categories. It doesn't place shapes — it designs. 8px grid, type scale ratios, auto-layout, component reuse, WCAG accessibility, and a self-auditing design score. Every decision follows the rules a senior designer applies instinctively.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
npx conductor-figma
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## What it does
|
|
12
|
+
|
|
13
|
+
Other MCP servers translate your words into shapes. CONDUCTOR translates them into design decisions.
|
|
14
|
+
|
|
15
|
+
Say "create a pricing page." A typical MCP creates rectangles with absolute positioning, random font sizes, and arbitrary spacing. CONDUCTOR creates a page with auto-layout frames, a type scale following a major third ratio (1.25), spacing on an 8px grid, and proper visual hierarchy — headings are dominant, body text recedes, CTAs are prominent.
|
|
16
|
+
|
|
17
|
+
## Setup
|
|
18
|
+
|
|
19
|
+
Install the CONDUCTOR plugin from the Figma Community. Then add to your editor:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
// ~/.cursor/mcp.json
|
|
23
|
+
{
|
|
24
|
+
"mcpServers": {
|
|
25
|
+
"conductor": {
|
|
26
|
+
"command": "npx",
|
|
27
|
+
"args": ["-y", "conductor-figma"]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Open your Figma file, run the plugin, then chat:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
"Create a SaaS landing page with hero, features, and pricing"
|
|
37
|
+
"Audit this frame — is my spacing consistent?"
|
|
38
|
+
"Generate a dark mode variant of this page"
|
|
39
|
+
"Convert these absolute-positioned layers to auto-layout"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## 61 Tools · 10 Categories
|
|
43
|
+
|
|
44
|
+
### Create (8)
|
|
45
|
+
| Tool | Description |
|
|
46
|
+
|------|-------------|
|
|
47
|
+
| `create_frame` | Auto-layout frame with grid-aligned spacing |
|
|
48
|
+
| `create_page` | Full page from intent: landing, pricing, dashboard |
|
|
49
|
+
| `create_section` | Hero, features, testimonials, FAQ, CTA — pattern-aware |
|
|
50
|
+
| `create_card` | Card with proper padding, radius, shadow, hierarchy |
|
|
51
|
+
| `create_form` | Form with labels, inputs, validation, submit flow |
|
|
52
|
+
| `create_table` | Data table with header, rows, sorting, pagination |
|
|
53
|
+
| `create_modal` | Modal with overlay, sizing, and action buttons |
|
|
54
|
+
| `create_nav` | Topbar, sidebar, breadcrumbs, tabs — with states |
|
|
55
|
+
|
|
56
|
+
### Layout (7)
|
|
57
|
+
| Tool | Description |
|
|
58
|
+
|------|-------------|
|
|
59
|
+
| `layout_auto` | Convert any frame to auto-layout |
|
|
60
|
+
| `layout_grid` | Apply column grid: 12-col, 8-col, custom |
|
|
61
|
+
| `layout_stack` | Stack children with consistent spacing |
|
|
62
|
+
| `layout_wrap` | Wrap-layout for tags, pills, flexible grids |
|
|
63
|
+
| `layout_constrain` | Set fill, hug, fixed, min/max constraints |
|
|
64
|
+
| `layout_align` | Align and distribute frames on the grid |
|
|
65
|
+
| `layout_nest` | Restructure flat layers into auto-layout tree |
|
|
66
|
+
|
|
67
|
+
### Typography (6)
|
|
68
|
+
| Tool | Description |
|
|
69
|
+
|------|-------------|
|
|
70
|
+
| `type_scale` | Generate or detect type scale ratios |
|
|
71
|
+
| `type_hierarchy` | Set heading levels with proper ratios |
|
|
72
|
+
| `type_pair` | Font pairing suggestions |
|
|
73
|
+
| `type_measure` | Check line length and readability |
|
|
74
|
+
| `type_apply` | Bulk-apply text styles |
|
|
75
|
+
| `type_audit` | Find off-scale sizes and inconsistencies |
|
|
76
|
+
|
|
77
|
+
### Color & Style (7)
|
|
78
|
+
| Tool | Description |
|
|
79
|
+
|------|-------------|
|
|
80
|
+
| `color_palette` | Generate 50-950 shades from one color |
|
|
81
|
+
| `color_semantic` | Create semantic tokens |
|
|
82
|
+
| `color_darkmode` | Generate dark mode with preserved contrast |
|
|
83
|
+
| `color_contrast` | WCAG AA/AAA contrast checking |
|
|
84
|
+
| `color_apply` | Apply colors by semantic role |
|
|
85
|
+
| `style_shadow` | Generate elevation system |
|
|
86
|
+
| `style_radius` | Generate radius scale |
|
|
87
|
+
|
|
88
|
+
### Components (6)
|
|
89
|
+
| Tool | Description |
|
|
90
|
+
|------|-------------|
|
|
91
|
+
| `component_list` | List components in file or library |
|
|
92
|
+
| `component_use` | Instantiate with variant props |
|
|
93
|
+
| `component_create` | Create component from frame |
|
|
94
|
+
| `component_swap` | Swap instances across a frame |
|
|
95
|
+
| `component_detach` | Detach instances to editable frames |
|
|
96
|
+
| `component_audit` | Find missing/unused/detached components |
|
|
97
|
+
|
|
98
|
+
### Spacing & Grid (6)
|
|
99
|
+
| Tool | Description |
|
|
100
|
+
|------|-------------|
|
|
101
|
+
| `spacing_scale` | Generate spacing scale from base unit |
|
|
102
|
+
| `spacing_fix` | Snap off-grid values to nearest grid |
|
|
103
|
+
| `spacing_audit` | Report all spacing values, flag issues |
|
|
104
|
+
| `spacing_rhythm` | Check vertical rhythm consistency |
|
|
105
|
+
| `grid_apply` | Apply layout grid to a frame |
|
|
106
|
+
| `grid_check` | Verify children align to grid |
|
|
107
|
+
|
|
108
|
+
### Audit & Critique (6)
|
|
109
|
+
| Tool | Description |
|
|
110
|
+
|------|-------------|
|
|
111
|
+
| `audit_full` | Full design audit across all categories |
|
|
112
|
+
| `audit_hierarchy` | Check visual hierarchy |
|
|
113
|
+
| `audit_consistency` | Find elements with inconsistent styles |
|
|
114
|
+
| `audit_alignment` | Flag misaligned elements |
|
|
115
|
+
| `audit_density` | Check information density |
|
|
116
|
+
| `audit_score` | 0-100 design health score |
|
|
117
|
+
|
|
118
|
+
### Accessibility (5)
|
|
119
|
+
| Tool | Description |
|
|
120
|
+
|------|-------------|
|
|
121
|
+
| `a11y_contrast` | WCAG AA/AAA contrast checking |
|
|
122
|
+
| `a11y_touch` | 44x44px touch target verification |
|
|
123
|
+
| `a11y_focus` | Generate focus ring styles |
|
|
124
|
+
| `a11y_labels` | Check for missing labels |
|
|
125
|
+
| `a11y_fix` | Auto-fix: contrast, targets, focus |
|
|
126
|
+
|
|
127
|
+
### Responsive (4)
|
|
128
|
+
| Tool | Description |
|
|
129
|
+
|------|-------------|
|
|
130
|
+
| `responsive_variant` | Generate mobile/tablet/desktop variants |
|
|
131
|
+
| `responsive_reflow` | Reflow desktop to mobile |
|
|
132
|
+
| `responsive_breakpoints` | Set up breakpoint frames |
|
|
133
|
+
| `responsive_check` | Verify overflow and targets |
|
|
134
|
+
|
|
135
|
+
### Export & Handoff (6)
|
|
136
|
+
| Tool | Description |
|
|
137
|
+
|------|-------------|
|
|
138
|
+
| `export_tokens_css` | CSS custom properties |
|
|
139
|
+
| `export_tokens_tailwind` | Tailwind config extension |
|
|
140
|
+
| `export_tokens_json` | W3C Design Tokens format |
|
|
141
|
+
| `export_tokens_scss` | SCSS variables |
|
|
142
|
+
| `export_spec` | Design spec with measurements |
|
|
143
|
+
| `export_changelog` | Diff two frames |
|
|
144
|
+
|
|
145
|
+
## Design Intelligence
|
|
146
|
+
|
|
147
|
+
Every tool is backed by real design logic, not arbitrary values.
|
|
148
|
+
|
|
149
|
+
**8px Grid**: All spacing values snap to the nearest multiple of 8. Padding, margins, gaps — no 13px, no 27px, no arbitrary numbers.
|
|
150
|
+
|
|
151
|
+
**Type Scale Ratios**: Font sizes follow mathematical ratios. Minor third (1.2), major third (1.25), perfect fourth (1.333), golden ratio (1.618). Headings, body, captions all relate to each other.
|
|
152
|
+
|
|
153
|
+
**Auto-Layout**: Every frame uses Figma auto-layout with proper direction, gap, padding, and alignment. Zero absolute positioning.
|
|
154
|
+
|
|
155
|
+
**WCAG Accessibility**: Contrast checking against AA (4.5:1) and AAA (7:1). Touch target verification (44x44px minimum). Focus state generation.
|
|
156
|
+
|
|
157
|
+
**Design Score**: 0-100 health score weighted across spacing (25%), typography (20%), color (15%), components (15%), accessibility (15%), hierarchy (10%).
|
|
158
|
+
|
|
159
|
+
## Programmatic API
|
|
160
|
+
|
|
161
|
+
```js
|
|
162
|
+
import { generateTypeScale, checkContrast, auditSpacing, snapToGrid } from 'conductor-figma'
|
|
163
|
+
|
|
164
|
+
const scale = generateTypeScale(16, 'major-third')
|
|
165
|
+
// → { scale: 'Major Third', ratio: 1.25, sizes: [...] }
|
|
166
|
+
|
|
167
|
+
const contrast = checkContrast('#333333', '#ffffff')
|
|
168
|
+
// → { ratio: 12.63, aa: true, aaa: true }
|
|
169
|
+
|
|
170
|
+
const audit = auditSpacing([8, 13, 16, 22, 32], 8)
|
|
171
|
+
// → { adherence: 0.6, fixes: [{ from: 13, to: 16 }, { from: 22, to: 24 }] }
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## CLI
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
conductor-figma --list # List all 61 tools
|
|
178
|
+
conductor-figma --categories # Show categories
|
|
179
|
+
conductor-figma --help # Help
|
|
180
|
+
conductor-figma # Start MCP server (stdio)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Zero Dependencies
|
|
184
|
+
|
|
185
|
+
CONDUCTOR has no runtime dependencies. Pure JavaScript. The design intelligence engine runs locally — no API calls, no external services.
|
|
186
|
+
|
|
187
|
+
## Disclaimer
|
|
188
|
+
|
|
189
|
+
Experimental, open-source software. Provided as-is with no warranties. DYOR. Use at your own risk. The author assumes zero liability.
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
MIT. Built by 0xDragoon.
|
package/bin/conductor.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { startServer } from '../src/server.js';
|
|
4
|
+
import { TOOLS, CATEGORIES } from '../src/tools/registry.js';
|
|
5
|
+
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
|
|
8
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
9
|
+
console.log(`
|
|
10
|
+
⊞ CONDUCTOR — Design-intelligent MCP server for Figma
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
conductor-figma Start MCP server (stdio)
|
|
14
|
+
conductor-figma --list List all ${TOOLS.length} tools
|
|
15
|
+
conductor-figma --categories Show tool categories
|
|
16
|
+
conductor-figma --help Show this help
|
|
17
|
+
|
|
18
|
+
MCP Setup (Cursor):
|
|
19
|
+
Add to ~/.cursor/mcp.json:
|
|
20
|
+
|
|
21
|
+
{
|
|
22
|
+
"mcpServers": {
|
|
23
|
+
"conductor": {
|
|
24
|
+
"command": "npx",
|
|
25
|
+
"args": ["-y", "conductor-figma"]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
${TOOLS.length} tools · ${Object.keys(CATEGORIES).length} categories · Zero dependencies
|
|
31
|
+
Built by 0xDragoon · MIT License
|
|
32
|
+
`);
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (args.includes('--list')) {
|
|
37
|
+
for (const [catKey, cat] of Object.entries(CATEGORIES)) {
|
|
38
|
+
const tools = TOOLS.filter(t => t.category === catKey);
|
|
39
|
+
console.log(`\n ${cat.icon} ${cat.label} (${tools.length})`);
|
|
40
|
+
for (const t of tools) {
|
|
41
|
+
console.log(` ${t.name.padEnd(28)} ${t.description.slice(0, 70)}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
console.log(`\n ${TOOLS.length} tools total\n`);
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (args.includes('--categories')) {
|
|
49
|
+
for (const [key, cat] of Object.entries(CATEGORIES)) {
|
|
50
|
+
const tools = TOOLS.filter(t => t.category === key);
|
|
51
|
+
console.log(` ${cat.icon} ${cat.label.padEnd(18)} ${tools.length} tools`);
|
|
52
|
+
}
|
|
53
|
+
console.log(`\n ${TOOLS.length} tools total`);
|
|
54
|
+
process.exit(0);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Default: start MCP server
|
|
58
|
+
startServer();
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "conductor-figma",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Design-intelligent MCP server for Figma. 61 tools across 10 categories. 8px grid, type scale ratios, auto-layout, component reuse, accessibility — real design intelligence, not shape proxying.",
|
|
5
|
+
"author": "0xDragoon",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./src/index.js",
|
|
9
|
+
"bin": {
|
|
10
|
+
"conductor-figma": "./bin/conductor.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"bin",
|
|
14
|
+
"src",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"start": "node bin/conductor.js",
|
|
20
|
+
"test": "node test/run.js"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"mcp", "figma", "design", "ai", "cursor", "claude-code",
|
|
24
|
+
"auto-layout", "design-system", "tokens", "components",
|
|
25
|
+
"accessibility", "wcag", "type-scale", "spacing", "grid",
|
|
26
|
+
"model-context-protocol", "copilot", "devtools"
|
|
27
|
+
],
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/dragoon0x/conductor"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://dragoon0x.github.io/conductor/",
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18.0.0"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {}
|
|
37
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// CONDUCTOR — Token Exporter
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
|
|
5
|
+
export function exportCSS(tokens) {
|
|
6
|
+
let out = '/* Generated by CONDUCTOR */\n:root {\n';
|
|
7
|
+
if (tokens.colors) for (const [k, v] of Object.entries(tokens.colors)) out += ` --color-${k}: ${v};\n`;
|
|
8
|
+
if (tokens.spacing) tokens.spacing.forEach((v, i) => { out += ` --space-${i + 1}: ${v}px;\n`; });
|
|
9
|
+
if (tokens.fontSizes) {
|
|
10
|
+
const names = ['xs','sm','base','md','lg','xl','2xl','3xl','4xl','5xl','6xl'];
|
|
11
|
+
tokens.fontSizes.forEach((v, i) => { out += ` --text-${names[i] || i}: ${v.size || v}px;\n`; });
|
|
12
|
+
}
|
|
13
|
+
if (tokens.radii) tokens.radii.forEach(r => { out += ` --radius-${r.name}: ${r.value >= 9999 ? '9999px' : r.value + 'px'};\n`; });
|
|
14
|
+
if (tokens.shadows) tokens.shadows.forEach(s => { out += ` --shadow-${s.step}: ${s.css};\n`; });
|
|
15
|
+
out += '}\n';
|
|
16
|
+
return out;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function exportTailwind(tokens) {
|
|
20
|
+
const config = { theme: { extend: {} } };
|
|
21
|
+
if (tokens.colors) config.theme.extend.colors = { ...tokens.colors };
|
|
22
|
+
if (tokens.spacing) {
|
|
23
|
+
config.theme.extend.spacing = {};
|
|
24
|
+
tokens.spacing.forEach((v, i) => { config.theme.extend.spacing[String(i + 1)] = `${v}px`; });
|
|
25
|
+
}
|
|
26
|
+
if (tokens.fontSizes) {
|
|
27
|
+
config.theme.extend.fontSize = {};
|
|
28
|
+
const names = ['xs','sm','base','md','lg','xl','2xl','3xl','4xl','5xl','6xl'];
|
|
29
|
+
tokens.fontSizes.forEach((v, i) => { config.theme.extend.fontSize[names[i] || i] = `${v.size || v}px`; });
|
|
30
|
+
}
|
|
31
|
+
if (tokens.radii) {
|
|
32
|
+
config.theme.extend.borderRadius = {};
|
|
33
|
+
tokens.radii.forEach(r => { config.theme.extend.borderRadius[r.name] = r.value >= 9999 ? '9999px' : `${r.value}px`; });
|
|
34
|
+
}
|
|
35
|
+
return `// tailwind.config.js — Generated by CONDUCTOR\nmodule.exports = ${JSON.stringify(config, null, 2)}\n`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function exportSCSS(tokens) {
|
|
39
|
+
let out = '// Generated by CONDUCTOR\n\n';
|
|
40
|
+
if (tokens.colors) for (const [k, v] of Object.entries(tokens.colors)) out += `$color-${k}: ${v};\n`;
|
|
41
|
+
if (tokens.spacing) { out += '\n'; tokens.spacing.forEach((v, i) => { out += `$space-${i + 1}: ${v}px;\n`; }); }
|
|
42
|
+
if (tokens.fontSizes) {
|
|
43
|
+
out += '\n';
|
|
44
|
+
const names = ['xs','sm','base','md','lg','xl','2xl','3xl','4xl','5xl','6xl'];
|
|
45
|
+
tokens.fontSizes.forEach((v, i) => { out += `$text-${names[i] || i}: ${v.size || v}px;\n`; });
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function exportJSON(tokens) {
|
|
51
|
+
return JSON.stringify(tokens, null, 2);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function exportW3CTokens(tokens) {
|
|
55
|
+
const w3c = {};
|
|
56
|
+
if (tokens.colors) {
|
|
57
|
+
w3c.color = {};
|
|
58
|
+
for (const [k, v] of Object.entries(tokens.colors)) {
|
|
59
|
+
w3c.color[k] = { $value: v, $type: 'color' };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (tokens.spacing) {
|
|
63
|
+
w3c.spacing = {};
|
|
64
|
+
tokens.spacing.forEach((v, i) => { w3c.spacing[`space-${i + 1}`] = { $value: `${v}px`, $type: 'dimension' }; });
|
|
65
|
+
}
|
|
66
|
+
if (tokens.fontSizes) {
|
|
67
|
+
w3c.fontSize = {};
|
|
68
|
+
const names = ['xs','sm','base','md','lg','xl','2xl','3xl','4xl','5xl','6xl'];
|
|
69
|
+
tokens.fontSizes.forEach((v, i) => { w3c.fontSize[names[i] || i] = { $value: `${v.size || v}px`, $type: 'dimension' }; });
|
|
70
|
+
}
|
|
71
|
+
return JSON.stringify(w3c, null, 2);
|
|
72
|
+
}
|