@nice2dev/icons 1.3.15 → 1.3.16
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/CHANGELOG.md +9 -0
- package/dist/cjs/NiceDesktopIconExporter.js +688 -599
- package/dist/cjs/NiceFaviconGenerator.js +603 -417
- package/dist/cjs/NiceIconPicker.js +788 -508
- package/dist/cjs/NiceIconPreview.js +246 -188
- package/dist/cjs/NiceIconSetCreator.js +683 -552
- package/dist/cjs/NiceIconStacker.js +547 -171
- package/dist/cjs/NiceIconValue.js +27 -35
- package/dist/cjs/NtdIconBadge.js +384 -360
- package/dist/cjs/NtdIconMorph.js +419 -354
- package/dist/cjs/NtdStateMorph.js +150 -147
- package/dist/cjs/advancedIconSearch.js +373 -510
- package/dist/cjs/animationControls.js +518 -642
- package/dist/cjs/client.js +12 -15
- package/dist/cjs/createIcon.js +349 -251
- package/dist/cjs/design-tokens.js +324 -390
- package/dist/cjs/figmaExport.js +162 -257
- package/dist/cjs/figmaSync.js +234 -316
- package/dist/cjs/headless-ui.js +240 -368
- package/dist/cjs/iconAnalytics.js +337 -393
- package/dist/cjs/iconHtml.js +64 -86
- package/dist/cjs/iconMetadata.js +392 -472
- package/dist/cjs/iconParts.js +120 -90
- package/dist/cjs/iconStack.js +136 -155
- package/dist/cjs/iconStyles.js +1609 -504
- package/dist/cjs/iconValue.js +359 -430
- package/dist/cjs/icons/accessibility.js +1119 -134
- package/dist/cjs/icons/actions.js +1290 -330
- package/dist/cjs/icons/ai.js +1424 -144
- package/dist/cjs/icons/brand.js +1344 -172
- package/dist/cjs/icons/business.js +903 -193
- package/dist/cjs/icons/devtech.js +1656 -227
- package/dist/cjs/icons/ecommerce.js +802 -111
- package/dist/cjs/icons/education.js +1345 -199
- package/dist/cjs/icons/files.js +1597 -328
- package/dist/cjs/icons/fintech.js +793 -104
- package/dist/cjs/icons/food.js +1507 -153
- package/dist/cjs/icons/fun.js +786 -109
- package/dist/cjs/icons/gaming.js +916 -170
- package/dist/cjs/icons/health.js +623 -112
- package/dist/cjs/icons/index.js +32 -35
- package/dist/cjs/icons/input.js +282 -83
- package/dist/cjs/icons/legal.js +1065 -122
- package/dist/cjs/icons/media.js +1061 -267
- package/dist/cjs/icons/navigation.js +387 -122
- package/dist/cjs/icons/omniverk.js +747 -90
- package/dist/cjs/icons/realestate.js +1520 -193
- package/dist/cjs/icons/saas.js +867 -98
- package/dist/cjs/icons/science.js +1451 -202
- package/dist/cjs/icons/shapes.js +1316 -308
- package/dist/cjs/icons/smarthome.js +786 -90
- package/dist/cjs/icons/social.js +591 -59
- package/dist/cjs/icons/sports.js +1225 -133
- package/dist/cjs/icons/sustainability.js +1211 -139
- package/dist/cjs/icons/travel.js +1470 -148
- package/dist/cjs/icons/ui.js +1508 -315
- package/dist/cjs/icons/weather.js +1160 -173
- package/dist/cjs/index.js +566 -568
- package/dist/cjs/lottieIntegration.js +261 -266
- package/dist/cjs/nicetodev-icons.css +1 -1
- package/dist/cjs/particleEffects.js +270 -251
- package/dist/cjs/resolver.js +229 -284
- package/dist/cjs/rsc.js +175 -193
- package/dist/cjs/sharpen.js +73 -130
- package/dist/cjs/smilAnimations.js +371 -165
- package/dist/cjs/stacking.js +87 -122
- package/dist/cjs/tailwind-plugin.js +280 -320
- package/dist/cjs/types.js +46 -72
- package/dist/cjs/utilities.js +163 -324
- package/dist/cjs/variantProps.js +12 -52
- package/dist/esm/NiceDesktopIconExporter.js +671 -598
- package/dist/esm/NiceFaviconGenerator.js +583 -416
- package/dist/esm/NiceIconPicker.js +789 -507
- package/dist/esm/NiceIconPreview.js +247 -187
- package/dist/esm/NiceIconSetCreator.js +685 -551
- package/dist/esm/NiceIconStacker.js +549 -170
- package/dist/esm/NiceIconValue.js +29 -34
- package/dist/esm/NtdIconBadge.js +388 -359
- package/dist/esm/NtdIconMorph.js +423 -353
- package/dist/esm/NtdStateMorph.js +152 -146
- package/dist/esm/advancedIconSearch.js +391 -509
- package/dist/esm/animationControls.js +527 -641
- package/dist/esm/client.js +48 -10
- package/dist/esm/createIcon.js +350 -250
- package/dist/esm/design-tokens.js +334 -387
- package/dist/esm/figmaExport.js +170 -256
- package/dist/esm/figmaSync.js +237 -315
- package/dist/esm/headless-ui.js +254 -365
- package/dist/esm/iconAnalytics.js +342 -392
- package/dist/esm/iconHtml.js +67 -85
- package/dist/esm/iconMetadata.js +404 -471
- package/dist/esm/iconParts.js +125 -89
- package/dist/esm/iconStack.js +145 -154
- package/dist/esm/iconStyles.js +1620 -503
- package/dist/esm/iconValue.js +361 -429
- package/dist/esm/icons/accessibility.js +1133 -133
- package/dist/esm/icons/actions.js +1320 -329
- package/dist/esm/icons/ai.js +1441 -143
- package/dist/esm/icons/brand.js +1351 -171
- package/dist/esm/icons/business.js +931 -201
- package/dist/esm/icons/devtech.js +1687 -226
- package/dist/esm/icons/ecommerce.js +816 -110
- package/dist/esm/icons/education.js +1364 -198
- package/dist/esm/icons/files.js +1628 -327
- package/dist/esm/icons/fintech.js +807 -103
- package/dist/esm/icons/food.js +1524 -152
- package/dist/esm/icons/fun.js +801 -108
- package/dist/esm/icons/gaming.js +933 -169
- package/dist/esm/icons/health.js +636 -111
- package/dist/esm/icons/index.js +596 -32
- package/dist/esm/icons/input.js +326 -82
- package/dist/esm/icons/legal.js +1079 -121
- package/dist/esm/icons/media.js +1086 -266
- package/dist/esm/icons/navigation.js +401 -121
- package/dist/esm/icons/omniverk.js +759 -89
- package/dist/esm/icons/realestate.js +1544 -198
- package/dist/esm/icons/saas.js +880 -97
- package/dist/esm/icons/science.js +1477 -208
- package/dist/esm/icons/shapes.js +1343 -307
- package/dist/esm/icons/smarthome.js +799 -89
- package/dist/esm/icons/social.js +599 -58
- package/dist/esm/icons/sports.js +1240 -132
- package/dist/esm/icons/sustainability.js +1226 -138
- package/dist/esm/icons/travel.js +1486 -147
- package/dist/esm/icons/ui.js +1540 -314
- package/dist/esm/icons/weather.js +1181 -172
- package/dist/esm/index.js +756 -58
- package/dist/esm/lottieIntegration.js +264 -265
- package/dist/esm/nicetodev-icons.css +1 -1
- package/dist/esm/particleEffects.js +271 -250
- package/dist/esm/resolver.js +231 -283
- package/dist/esm/rsc.js +182 -192
- package/dist/esm/sharpen.js +78 -129
- package/dist/esm/smilAnimations.js +376 -164
- package/dist/esm/stacking.js +99 -121
- package/dist/esm/tailwind-plugin.js +282 -319
- package/dist/esm/types.js +50 -71
- package/dist/esm/utilities.js +177 -321
- package/dist/esm/variantProps.js +23 -52
- package/package.json +15 -22
package/dist/esm/figmaSync.js
CHANGED
|
@@ -1,338 +1,260 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file figmaSync.ts
|
|
3
|
-
* @description Bidirectional sync between Figma design files and the codebase.
|
|
4
|
-
*
|
|
5
|
-
* - Sync changes from Figma → codebase (import new/updated icons)
|
|
6
|
-
* - Push code changes → Figma (export updated icons to Figma)
|
|
7
|
-
* - Version tracking (track which version of icon is in Figma vs code)
|
|
8
|
-
* - Conflict detection (detect when both sides changed)
|
|
9
|
-
*
|
|
10
|
-
* This module provides the data structures and logic for syncing.
|
|
11
|
-
* The actual Figma API calls are delegated to callbacks so this
|
|
12
|
-
* module can work with any Figma plugin or REST API implementation.
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```tsx
|
|
16
|
-
* const manager = createFigmaSyncManager({
|
|
17
|
-
* fetchFromFigma: async () => figmaIcons,
|
|
18
|
-
* pushToFigma: async (icons) => { ... },
|
|
19
|
-
* getLocalIcons: () => localIcons,
|
|
20
|
-
* onConflict: (diff) => 'prefer-local',
|
|
21
|
-
* });
|
|
22
|
-
*
|
|
23
|
-
* // Pull changes from Figma
|
|
24
|
-
* const result = await manager.pull();
|
|
25
|
-
*
|
|
26
|
-
* // Push changes to Figma
|
|
27
|
-
* const pushResult = await manager.push();
|
|
28
|
-
*
|
|
29
|
-
* // Full bidirectional sync
|
|
30
|
-
* const syncResult = await manager.sync();
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
/* ────────────────────────────────────────────────────────────────────────────
|
|
34
|
-
HASH UTILITY
|
|
35
|
-
───────────────────────────────────────────────────────────────────────────── */
|
|
36
|
-
/**
|
|
37
|
-
* Simple content hash for change detection.
|
|
38
|
-
* Uses a deterministic djb2-like hash for speed (no crypto needed).
|
|
39
|
-
*/
|
|
40
1
|
function hashContent(content) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
2
|
+
let hash = 5381;
|
|
3
|
+
for (let i = 0; i < content.length; i++) {
|
|
4
|
+
hash = (hash << 5) + hash + content.charCodeAt(i) & 4294967295;
|
|
5
|
+
}
|
|
6
|
+
return (hash >>> 0).toString(36);
|
|
46
7
|
}
|
|
47
|
-
/** Generate a hash for an icon's content. */
|
|
48
8
|
function hashIcon(icon) {
|
|
49
|
-
|
|
50
|
-
return hashContent(icon.svgPaths.join('|') + icon.viewBox + ((_a = icon.variant) !== null && _a !== void 0 ? _a : ''));
|
|
9
|
+
return hashContent(icon.svgPaths.join("|") + icon.viewBox + (icon.variant ?? ""));
|
|
51
10
|
}
|
|
52
|
-
/* ────────────────────────────────────────────────────────────────────────────
|
|
53
|
-
DIFF ENGINE
|
|
54
|
-
───────────────────────────────────────────────────────────────────────────── */
|
|
55
|
-
/**
|
|
56
|
-
* Computes a diff between two sets of tracked icons.
|
|
57
|
-
*
|
|
58
|
-
* @param localIcons - Icons in the codebase
|
|
59
|
-
* @param figmaIcons - Icons in Figma
|
|
60
|
-
* @param syncState - Previously synced hashes (for conflict detection)
|
|
61
|
-
* @returns Array of diffs
|
|
62
|
-
*/
|
|
63
11
|
function diffIconSets(localIcons, figmaIcons, syncState) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
local,
|
|
115
|
-
figma,
|
|
116
|
-
lastSyncedHash: lastHash,
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
121
|
-
// No previous sync state — treat as conflict if different
|
|
122
|
-
diffs.push({ name, status: 'conflict', direction: 'both', local, figma });
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
else if (figma && !local) {
|
|
126
|
-
diffs.push({ name, status: 'added', direction: 'figma-to-code', figma });
|
|
127
|
-
}
|
|
128
|
-
else if (local && !figma) {
|
|
129
|
-
diffs.push({ name, status: 'added', direction: 'code-to-figma', local });
|
|
12
|
+
const diffs = [];
|
|
13
|
+
const localMap = new Map(localIcons.map((i) => [i.name, i]));
|
|
14
|
+
const figmaMap = new Map(figmaIcons.map((i) => [i.name, i]));
|
|
15
|
+
const allNames = /* @__PURE__ */ new Set([...localMap.keys(), ...figmaMap.keys()]);
|
|
16
|
+
for (const name of allNames) {
|
|
17
|
+
const local = localMap.get(name);
|
|
18
|
+
const figma = figmaMap.get(name);
|
|
19
|
+
const lastHash = syncState?.get(name);
|
|
20
|
+
if (local && figma) {
|
|
21
|
+
const localHash = hashIcon(local);
|
|
22
|
+
const figmaHash = hashIcon(figma);
|
|
23
|
+
if (localHash === figmaHash) {
|
|
24
|
+
diffs.push({
|
|
25
|
+
name,
|
|
26
|
+
status: "unchanged",
|
|
27
|
+
direction: "none",
|
|
28
|
+
local,
|
|
29
|
+
figma,
|
|
30
|
+
lastSyncedHash: lastHash
|
|
31
|
+
});
|
|
32
|
+
} else if (lastHash) {
|
|
33
|
+
const localChanged = localHash !== lastHash;
|
|
34
|
+
const figmaChanged = figmaHash !== lastHash;
|
|
35
|
+
if (localChanged && figmaChanged) {
|
|
36
|
+
diffs.push({
|
|
37
|
+
name,
|
|
38
|
+
status: "conflict",
|
|
39
|
+
direction: "both",
|
|
40
|
+
local,
|
|
41
|
+
figma,
|
|
42
|
+
lastSyncedHash: lastHash
|
|
43
|
+
});
|
|
44
|
+
} else if (localChanged) {
|
|
45
|
+
diffs.push({
|
|
46
|
+
name,
|
|
47
|
+
status: "modified",
|
|
48
|
+
direction: "code-to-figma",
|
|
49
|
+
local,
|
|
50
|
+
figma,
|
|
51
|
+
lastSyncedHash: lastHash
|
|
52
|
+
});
|
|
53
|
+
} else {
|
|
54
|
+
diffs.push({
|
|
55
|
+
name,
|
|
56
|
+
status: "modified",
|
|
57
|
+
direction: "figma-to-code",
|
|
58
|
+
local,
|
|
59
|
+
figma,
|
|
60
|
+
lastSyncedHash: lastHash
|
|
61
|
+
});
|
|
130
62
|
}
|
|
63
|
+
} else {
|
|
64
|
+
diffs.push({ name, status: "conflict", direction: "both", local, figma });
|
|
65
|
+
}
|
|
66
|
+
} else if (figma && !local) {
|
|
67
|
+
diffs.push({ name, status: "added", direction: "figma-to-code", figma });
|
|
68
|
+
} else if (local && !figma) {
|
|
69
|
+
diffs.push({ name, status: "added", direction: "code-to-figma", local });
|
|
131
70
|
}
|
|
132
|
-
|
|
71
|
+
}
|
|
72
|
+
return diffs;
|
|
133
73
|
}
|
|
134
|
-
|
|
135
|
-
SYNC MANAGER
|
|
136
|
-
───────────────────────────────────────────────────────────────────────────── */
|
|
137
|
-
const STORAGE_KEY = 'ntd-figma-sync-state';
|
|
138
|
-
/**
|
|
139
|
-
* Creates a FigmaSyncManager for bidirectional icon synchronization.
|
|
140
|
-
*/
|
|
74
|
+
const STORAGE_KEY = "ntd-figma-sync-state";
|
|
141
75
|
function createFigmaSyncManager(config) {
|
|
142
|
-
|
|
76
|
+
return new FigmaSyncManager(config);
|
|
143
77
|
}
|
|
144
78
|
class FigmaSyncManager {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
else {
|
|
173
|
-
// prefer-local — no pull needed
|
|
174
|
-
skipped.push(diff);
|
|
175
|
-
}
|
|
176
|
-
conflicts.push(diff);
|
|
177
|
-
}
|
|
178
|
-
else if (diff.direction === 'figma-to-code') {
|
|
179
|
-
pulled.push(diff);
|
|
180
|
-
if (diff.figma) {
|
|
181
|
-
this.updateSyncHash(diff.name, diff.figma);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
79
|
+
constructor(config) {
|
|
80
|
+
this.config = config;
|
|
81
|
+
this.syncState = this.loadSyncState();
|
|
82
|
+
}
|
|
83
|
+
/** Pull changes from Figma → codebase. */
|
|
84
|
+
async pull() {
|
|
85
|
+
const start = performance.now();
|
|
86
|
+
const figmaIcons = await this.config.fetchFromFigma();
|
|
87
|
+
const localIcons = this.config.getLocalIcons();
|
|
88
|
+
const diffs = diffIconSets(localIcons, figmaIcons, this.syncState);
|
|
89
|
+
const pulled = [];
|
|
90
|
+
const conflicts = [];
|
|
91
|
+
const skipped = [];
|
|
92
|
+
for (const diff of diffs) {
|
|
93
|
+
if (diff.status === "unchanged") {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (diff.status === "conflict") {
|
|
97
|
+
const resolution = this.config.onConflict?.(diff) ?? "skip";
|
|
98
|
+
if (resolution === "prefer-figma") {
|
|
99
|
+
pulled.push(diff);
|
|
100
|
+
this.updateSyncHash(diff.name, diff.figma);
|
|
101
|
+
} else if (resolution === "skip") {
|
|
102
|
+
skipped.push(diff);
|
|
103
|
+
} else {
|
|
104
|
+
skipped.push(diff);
|
|
184
105
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
skipped,
|
|
191
|
-
duration: performance.now() - start,
|
|
192
|
-
timestamp: Date.now(),
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
/** Push changes from codebase → Figma. */
|
|
196
|
-
async push() {
|
|
197
|
-
var _a, _b, _c;
|
|
198
|
-
const start = performance.now();
|
|
199
|
-
const figmaIcons = await this.config.fetchFromFigma();
|
|
200
|
-
const localIcons = this.config.getLocalIcons();
|
|
201
|
-
const diffs = diffIconSets(localIcons, figmaIcons, this.syncState);
|
|
202
|
-
const pushed = [];
|
|
203
|
-
const conflicts = [];
|
|
204
|
-
const skipped = [];
|
|
205
|
-
const toPush = [];
|
|
206
|
-
for (const diff of diffs) {
|
|
207
|
-
if (diff.status === 'unchanged') {
|
|
208
|
-
continue;
|
|
209
|
-
}
|
|
210
|
-
if (diff.status === 'conflict') {
|
|
211
|
-
const resolution = (_c = (_b = (_a = this.config).onConflict) === null || _b === void 0 ? void 0 : _b.call(_a, diff)) !== null && _c !== void 0 ? _c : 'skip';
|
|
212
|
-
if (resolution === 'prefer-local') {
|
|
213
|
-
toPush.push(diff.local);
|
|
214
|
-
pushed.push(diff);
|
|
215
|
-
this.updateSyncHash(diff.name, diff.local);
|
|
216
|
-
}
|
|
217
|
-
else if (resolution === 'skip') {
|
|
218
|
-
skipped.push(diff);
|
|
219
|
-
}
|
|
220
|
-
conflicts.push(diff);
|
|
221
|
-
}
|
|
222
|
-
else if (diff.direction === 'code-to-figma' && diff.local) {
|
|
223
|
-
toPush.push(diff.local);
|
|
224
|
-
pushed.push(diff);
|
|
225
|
-
this.updateSyncHash(diff.name, diff.local);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
if (toPush.length > 0) {
|
|
229
|
-
await this.config.pushToFigma(toPush);
|
|
106
|
+
conflicts.push(diff);
|
|
107
|
+
} else if (diff.direction === "figma-to-code") {
|
|
108
|
+
pulled.push(diff);
|
|
109
|
+
if (diff.figma) {
|
|
110
|
+
this.updateSyncHash(diff.name, diff.figma);
|
|
230
111
|
}
|
|
231
|
-
|
|
232
|
-
return {
|
|
233
|
-
pulled: [],
|
|
234
|
-
pushed,
|
|
235
|
-
conflicts,
|
|
236
|
-
skipped,
|
|
237
|
-
duration: performance.now() - start,
|
|
238
|
-
timestamp: Date.now(),
|
|
239
|
-
};
|
|
112
|
+
}
|
|
240
113
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
else if (diff.direction === 'figma-to-code') {
|
|
274
|
-
pulled.push(diff);
|
|
275
|
-
if (diff.figma) {
|
|
276
|
-
this.updateSyncHash(diff.name, diff.figma);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
else if (diff.direction === 'code-to-figma' && diff.local) {
|
|
280
|
-
toPush.push(diff.local);
|
|
281
|
-
pushed.push(diff);
|
|
282
|
-
this.updateSyncHash(diff.name, diff.local);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
if (toPush.length > 0) {
|
|
286
|
-
await this.config.pushToFigma(toPush);
|
|
114
|
+
this.saveSyncState();
|
|
115
|
+
return {
|
|
116
|
+
pulled,
|
|
117
|
+
pushed: [],
|
|
118
|
+
conflicts,
|
|
119
|
+
skipped,
|
|
120
|
+
duration: performance.now() - start,
|
|
121
|
+
timestamp: Date.now()
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/** Push changes from codebase → Figma. */
|
|
125
|
+
async push() {
|
|
126
|
+
const start = performance.now();
|
|
127
|
+
const figmaIcons = await this.config.fetchFromFigma();
|
|
128
|
+
const localIcons = this.config.getLocalIcons();
|
|
129
|
+
const diffs = diffIconSets(localIcons, figmaIcons, this.syncState);
|
|
130
|
+
const pushed = [];
|
|
131
|
+
const conflicts = [];
|
|
132
|
+
const skipped = [];
|
|
133
|
+
const toPush = [];
|
|
134
|
+
for (const diff of diffs) {
|
|
135
|
+
if (diff.status === "unchanged") {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (diff.status === "conflict") {
|
|
139
|
+
const resolution = this.config.onConflict?.(diff) ?? "skip";
|
|
140
|
+
if (resolution === "prefer-local") {
|
|
141
|
+
toPush.push(diff.local);
|
|
142
|
+
pushed.push(diff);
|
|
143
|
+
this.updateSyncHash(diff.name, diff.local);
|
|
144
|
+
} else if (resolution === "skip") {
|
|
145
|
+
skipped.push(diff);
|
|
287
146
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
duration: performance.now() - start,
|
|
295
|
-
timestamp: Date.now(),
|
|
296
|
-
};
|
|
147
|
+
conflicts.push(diff);
|
|
148
|
+
} else if (diff.direction === "code-to-figma" && diff.local) {
|
|
149
|
+
toPush.push(diff.local);
|
|
150
|
+
pushed.push(diff);
|
|
151
|
+
this.updateSyncHash(diff.name, diff.local);
|
|
152
|
+
}
|
|
297
153
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
const figmaIcons = await this.config.fetchFromFigma();
|
|
301
|
-
const localIcons = this.config.getLocalIcons();
|
|
302
|
-
return diffIconSets(localIcons, figmaIcons, this.syncState);
|
|
154
|
+
if (toPush.length > 0) {
|
|
155
|
+
await this.config.pushToFigma(toPush);
|
|
303
156
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
157
|
+
this.saveSyncState();
|
|
158
|
+
return {
|
|
159
|
+
pulled: [],
|
|
160
|
+
pushed,
|
|
161
|
+
conflicts,
|
|
162
|
+
skipped,
|
|
163
|
+
duration: performance.now() - start,
|
|
164
|
+
timestamp: Date.now()
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/** Full bidirectional sync. */
|
|
168
|
+
async sync() {
|
|
169
|
+
const start = performance.now();
|
|
170
|
+
const figmaIcons = await this.config.fetchFromFigma();
|
|
171
|
+
const localIcons = this.config.getLocalIcons();
|
|
172
|
+
const diffs = diffIconSets(localIcons, figmaIcons, this.syncState);
|
|
173
|
+
const pulled = [];
|
|
174
|
+
const pushed = [];
|
|
175
|
+
const conflicts = [];
|
|
176
|
+
const skipped = [];
|
|
177
|
+
const toPush = [];
|
|
178
|
+
for (const diff of diffs) {
|
|
179
|
+
if (diff.status === "unchanged") {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (diff.status === "conflict") {
|
|
183
|
+
const resolution = this.config.onConflict?.(diff) ?? "skip";
|
|
184
|
+
conflicts.push(diff);
|
|
185
|
+
if (resolution === "prefer-local" && diff.local) {
|
|
186
|
+
toPush.push(diff.local);
|
|
187
|
+
pushed.push(diff);
|
|
188
|
+
this.updateSyncHash(diff.name, diff.local);
|
|
189
|
+
} else if (resolution === "prefer-figma" && diff.figma) {
|
|
190
|
+
pulled.push(diff);
|
|
191
|
+
this.updateSyncHash(diff.name, diff.figma);
|
|
192
|
+
} else {
|
|
193
|
+
skipped.push(diff);
|
|
194
|
+
}
|
|
195
|
+
} else if (diff.direction === "figma-to-code") {
|
|
196
|
+
pulled.push(diff);
|
|
197
|
+
if (diff.figma) {
|
|
198
|
+
this.updateSyncHash(diff.name, diff.figma);
|
|
199
|
+
}
|
|
200
|
+
} else if (diff.direction === "code-to-figma" && diff.local) {
|
|
201
|
+
toPush.push(diff.local);
|
|
202
|
+
pushed.push(diff);
|
|
203
|
+
this.updateSyncHash(diff.name, diff.local);
|
|
204
|
+
}
|
|
307
205
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
this.syncState.clear();
|
|
311
|
-
this.saveSyncState();
|
|
206
|
+
if (toPush.length > 0) {
|
|
207
|
+
await this.config.pushToFigma(toPush);
|
|
312
208
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
209
|
+
this.saveSyncState();
|
|
210
|
+
return {
|
|
211
|
+
pulled,
|
|
212
|
+
pushed,
|
|
213
|
+
conflicts,
|
|
214
|
+
skipped,
|
|
215
|
+
duration: performance.now() - start,
|
|
216
|
+
timestamp: Date.now()
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
/** Get current diff without making changes. */
|
|
220
|
+
async diff() {
|
|
221
|
+
const figmaIcons = await this.config.fetchFromFigma();
|
|
222
|
+
const localIcons = this.config.getLocalIcons();
|
|
223
|
+
return diffIconSets(localIcons, figmaIcons, this.syncState);
|
|
224
|
+
}
|
|
225
|
+
/** Get sync history from storage. */
|
|
226
|
+
getSyncState() {
|
|
227
|
+
return new Map(this.syncState);
|
|
228
|
+
}
|
|
229
|
+
/** Reset sync state (forces full re-sync on next operation). */
|
|
230
|
+
resetSyncState() {
|
|
231
|
+
this.syncState.clear();
|
|
232
|
+
this.saveSyncState();
|
|
233
|
+
}
|
|
234
|
+
// ── Private helpers ──
|
|
235
|
+
updateSyncHash(name, icon) {
|
|
236
|
+
this.syncState.set(name, hashIcon(icon));
|
|
237
|
+
}
|
|
238
|
+
loadSyncState() {
|
|
239
|
+
const raw = this.config.storage?.get(STORAGE_KEY);
|
|
240
|
+
if (!raw) {
|
|
241
|
+
return /* @__PURE__ */ new Map();
|
|
316
242
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
return new Map();
|
|
322
|
-
}
|
|
323
|
-
try {
|
|
324
|
-
return new Map(Object.entries(JSON.parse(raw)));
|
|
325
|
-
}
|
|
326
|
-
catch (_b) {
|
|
327
|
-
return new Map();
|
|
328
|
-
}
|
|
243
|
+
try {
|
|
244
|
+
return new Map(Object.entries(JSON.parse(raw)));
|
|
245
|
+
} catch {
|
|
246
|
+
return /* @__PURE__ */ new Map();
|
|
329
247
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
this.config.storage.set(STORAGE_KEY, JSON.stringify(Object.fromEntries(this.syncState)));
|
|
248
|
+
}
|
|
249
|
+
saveSyncState() {
|
|
250
|
+
if (!this.config.storage) {
|
|
251
|
+
return;
|
|
335
252
|
}
|
|
253
|
+
this.config.storage.set(STORAGE_KEY, JSON.stringify(Object.fromEntries(this.syncState)));
|
|
254
|
+
}
|
|
336
255
|
}
|
|
337
|
-
|
|
338
|
-
|
|
256
|
+
export {
|
|
257
|
+
FigmaSyncManager,
|
|
258
|
+
createFigmaSyncManager,
|
|
259
|
+
diffIconSets
|
|
260
|
+
};
|