mirror of
https://github.com/nexu-io/open-design.git
synced 2026-06-01 03:14:35 +07:00
A design system imported from a SwiftUI repo could never be published. The import's file scorer was web-only, so every .swift file scored 0 while the repo's config dotfiles (.zenflow, .vscode, .zed) scored on the generic text bonus and won the top-N selection. Even when a source file was selected, the snapshot writer's text-file allowlist didn't include .swift, so it was dropped. The result: snapshots were all config dotfiles, which the project files API hides, so the publish gate saw zero evidence snapshots and stayed blocked no matter how many times the intake ran. Three layers fixed in tools-connectors-cli.ts: - scoreDesignFile now scores native/design-token source (Swift, Kotlin, Go, Rust, etc.), with a high boost for token files like ColorSystem.swift, Typography.swift, Spacing.swift. - shouldSkipRepoPath skips editor/CI/agent tooling dirs (.vscode, .zed, .idea, .zenflow, .github, .husky, ...) so their files stop crowding out real source. - isTextSnapshotPath recognizes native source extensions so selected files are actually written. Also teach the design-system swatch extraction to read SwiftUI colors: swift-colors.ts parses Color(red:green:blue:), Color(hue:saturation:brightness:) (HSB), and Color(white:), evaluating decimal, hex-byte, and division component expressions (0xF4 / 255, 220 / 360), and design-systems.ts uses it as a new swatch form. scoreDesignFile, shouldSkipRepoPath, and isTextSnapshotPath are exported for unit tests. Verified against a real SwiftUI repo: the intake now captures ColorSystem, TypographySystem, SpacingSystem and the view/model source instead of config dotfiles.
58 lines
2 KiB
TypeScript
58 lines
2 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
|
|
import {
|
|
evalSwiftNumber,
|
|
extractSwiftColors,
|
|
hsbToHex,
|
|
rgbUnitToHex,
|
|
} from '../src/swift-colors.js';
|
|
|
|
describe('evalSwiftNumber', () => {
|
|
it('reads decimals, ints, hex bytes, and single divisions', () => {
|
|
expect(evalSwiftNumber('0.99')).toBeCloseTo(0.99, 5);
|
|
expect(evalSwiftNumber('255')).toBe(255);
|
|
expect(evalSwiftNumber('0xF4')).toBe(244);
|
|
expect(evalSwiftNumber('0xF4 / 255')).toBeCloseTo(244 / 255, 5);
|
|
expect(evalSwiftNumber('220 / 360')).toBeCloseTo(220 / 360, 5);
|
|
});
|
|
|
|
it('returns null for anything it cannot evaluate safely', () => {
|
|
expect(evalSwiftNumber('someVar')).toBeNull();
|
|
expect(evalSwiftNumber('1 / 0')).toBeNull();
|
|
expect(evalSwiftNumber('1 + 2 + 3')).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('hsbToHex / rgbUnitToHex', () => {
|
|
it('converts the SwiftUI HSB grey50 token to hex', () => {
|
|
// static let grey50 = Color(hue: 220 / 360, saturation: 0.02, brightness: 0.99)
|
|
expect(hsbToHex(220 / 360, 0.02, 0.99)).toBe('#f7f9fc');
|
|
});
|
|
|
|
it('converts unit RGB to hex', () => {
|
|
expect(rgbUnitToHex(0xf4 / 255, 0xf4 / 255, 0xf4 / 255)).toBe('#f4f4f4');
|
|
expect(rgbUnitToHex(0, 0, 0)).toBe('#000000');
|
|
expect(rgbUnitToHex(1, 1, 1)).toBe('#ffffff');
|
|
});
|
|
});
|
|
|
|
describe('extractSwiftColors', () => {
|
|
it('parses HSB and RGB Color declarations with names', () => {
|
|
const src = [
|
|
'enum ColorSystem {',
|
|
' static let grey50 = Color(hue: 220 / 360, saturation: 0.02, brightness: 0.99)',
|
|
' static let appShellLight = Color(red: 0xF4 / 255, green: 0xF4 / 255, blue: 0xF4 / 255)',
|
|
' static let scrim = Color(white: 0.0, opacity: 0.4)',
|
|
'}',
|
|
].join('\n');
|
|
expect(extractSwiftColors(src)).toEqual([
|
|
{ name: 'grey50', hex: '#f7f9fc' },
|
|
{ name: 'appShellLight', hex: '#f4f4f4' },
|
|
{ name: 'scrim', hex: '#000000' },
|
|
]);
|
|
});
|
|
|
|
it('skips named asset colors that carry no component values', () => {
|
|
expect(extractSwiftColors('let accent = Color("AccentColor")')).toEqual([]);
|
|
});
|
|
});
|