@hegemonart/get-design-done 1.33.6 → 1.34.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +6 -3
- package/.claude-plugin/plugin.json +5 -2
- package/CHANGELOG.md +24 -0
- package/README.md +10 -0
- package/agents/compose-executor.md +142 -0
- package/agents/design-context-builder.md +35 -1
- package/agents/design-verifier.md +14 -18
- package/agents/flutter-executor.md +147 -0
- package/agents/swift-executor.md +226 -0
- package/connections/android-emulator.md +107 -0
- package/connections/connections.md +4 -0
- package/connections/xcode-simulator.md +108 -0
- package/package.json +1 -1
- package/reference/native-platforms.md +273 -0
- package/reference/registry.json +7 -0
- package/scripts/lib/design-tokens/_native-shared.cjs +206 -0
- package/scripts/lib/design-tokens/compose.cjs +150 -0
- package/scripts/lib/design-tokens/flutter.cjs +128 -0
- package/scripts/lib/design-tokens/index.cjs +13 -0
- package/scripts/lib/design-tokens/swift.cjs +122 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* design-tokens/flutter.cjs — Flutter (Dart) theme emitter + re-extractor
|
|
3
|
+
* (Phase 34.1 Plan 01).
|
|
4
|
+
*
|
|
5
|
+
* Consumes the canonical Phase-23 token map ({ tokens: Record<string,string> })
|
|
6
|
+
* and emits a deterministic Dart theme source string: a `GDDTheme` constants
|
|
7
|
+
* class plus a `themeData()` builder wiring `ColorScheme` + `TextTheme` into a
|
|
8
|
+
* `ThemeData` — per the PRECISION CONTRACT in reference/native-platforms.md.
|
|
9
|
+
*
|
|
10
|
+
* Precision contract:
|
|
11
|
+
* COLOR hex -> Color(0xAARRGGBB). 8-bit channels EXACT; #RGB expands;
|
|
12
|
+
* alpha 0xFF when absent.
|
|
13
|
+
* DIMENSION Npx -> logical-px double (`16` -> `16.0`); NOT rounded.
|
|
14
|
+
* TYPOGRAPHY strings -> Dart string literal, pass-through.
|
|
15
|
+
* NON-MAPPABLE var()/calc()/gradient/rem/em -> verbatim comment, EXCLUDED.
|
|
16
|
+
*
|
|
17
|
+
* Each emitted token line carries a `// token: <original-key>` marker so the
|
|
18
|
+
* symmetric `reextractFlutter(source)` recovers the exact canonical key+value.
|
|
19
|
+
* Pure: no fs, no network, no Date, no process.env, no child_process (D-10).
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
'use strict';
|
|
23
|
+
|
|
24
|
+
const {
|
|
25
|
+
TOKEN_MARKER,
|
|
26
|
+
NONMAPPABLE_MARKER,
|
|
27
|
+
readTokens,
|
|
28
|
+
sortedEntries,
|
|
29
|
+
classify,
|
|
30
|
+
parseHexChannels,
|
|
31
|
+
channelsToArgbLiteral,
|
|
32
|
+
argbLiteralToChannels,
|
|
33
|
+
channelsToHex,
|
|
34
|
+
pxToDouble,
|
|
35
|
+
swiftIdent,
|
|
36
|
+
} = require('./_native-shared.cjs');
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Emit a Flutter / Dart theme source string from a token set.
|
|
40
|
+
*
|
|
41
|
+
* @param {{tokens: Record<string,string>}|Record<string,string>} tokenSet
|
|
42
|
+
* @returns {string}
|
|
43
|
+
*/
|
|
44
|
+
function emitFlutter(tokenSet) {
|
|
45
|
+
const tokens = readTokens(tokenSet);
|
|
46
|
+
const entries = sortedEntries(tokens);
|
|
47
|
+
const lines = [];
|
|
48
|
+
lines.push('// Generated by get-design-done — Flutter theme tokens.');
|
|
49
|
+
lines.push('// See reference/native-platforms.md (token-bridge precision contract).');
|
|
50
|
+
lines.push("import 'package:flutter/material.dart';");
|
|
51
|
+
lines.push('');
|
|
52
|
+
lines.push('class GDDTheme {');
|
|
53
|
+
|
|
54
|
+
for (const [key, value] of entries) {
|
|
55
|
+
const kind = classify(key, value);
|
|
56
|
+
const ident = swiftIdent(key);
|
|
57
|
+
if (kind === 'color') {
|
|
58
|
+
const { r, g, b, a, hadAlpha } = parseHexChannels(value);
|
|
59
|
+
lines.push(
|
|
60
|
+
` static const ${ident} = Color(${channelsToArgbLiteral({ r, g, b, a })}); ${TOKEN_MARKER}${key} hadAlpha=${hadAlpha ? 1 : 0}`,
|
|
61
|
+
);
|
|
62
|
+
} else if (kind === 'dimension') {
|
|
63
|
+
lines.push(
|
|
64
|
+
` static const ${ident} = ${pxToDouble(value)}; ${TOKEN_MARKER}${key}`,
|
|
65
|
+
);
|
|
66
|
+
} else if (kind === 'typography') {
|
|
67
|
+
lines.push(
|
|
68
|
+
` static const ${ident} = ${JSON.stringify(value)}; ${TOKEN_MARKER}${key}`,
|
|
69
|
+
);
|
|
70
|
+
} else {
|
|
71
|
+
lines.push(` ${NONMAPPABLE_MARKER}${key} = ${value}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ThemeData builder wiring ColorScheme + TextTheme (MaterialTheme analog).
|
|
76
|
+
lines.push('');
|
|
77
|
+
lines.push(' static ThemeData themeData() => ThemeData(');
|
|
78
|
+
lines.push(' colorScheme: ColorScheme.fromSeed(seedColor: GDDTheme.colorPrimary),');
|
|
79
|
+
lines.push(' textTheme: const TextTheme(),');
|
|
80
|
+
lines.push(' );');
|
|
81
|
+
lines.push('}');
|
|
82
|
+
lines.push('');
|
|
83
|
+
return lines.join('\n');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const FLUTTER_COLOR_RE = new RegExp(
|
|
87
|
+
String.raw`static const \w+ = Color\((0x[0-9a-fA-F]{8})\);\s*` +
|
|
88
|
+
TOKEN_MARKER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +
|
|
89
|
+
String.raw`(\S+)\s+hadAlpha=([01])`,
|
|
90
|
+
);
|
|
91
|
+
const FLUTTER_DIM_RE = new RegExp(
|
|
92
|
+
String.raw`static const \w+ = (\d+(?:\.\d+)?);\s*` +
|
|
93
|
+
TOKEN_MARKER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +
|
|
94
|
+
String.raw`(\S+)`,
|
|
95
|
+
);
|
|
96
|
+
const FLUTTER_STR_RE = new RegExp(
|
|
97
|
+
String.raw`static const \w+ = ("(?:[^"\\]|\\.)*");\s*` +
|
|
98
|
+
TOKEN_MARKER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +
|
|
99
|
+
String.raw`(\S+)`,
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Recover the canonical token map from an emitted Flutter source string.
|
|
104
|
+
* Non-mappable lines + the ThemeData builder lines are NOT recovered.
|
|
105
|
+
*
|
|
106
|
+
* @param {string} source
|
|
107
|
+
* @returns {{tokens: Record<string,string>}}
|
|
108
|
+
*/
|
|
109
|
+
function reextractFlutter(source) {
|
|
110
|
+
/** @type {Record<string,string>} */
|
|
111
|
+
const tokens = {};
|
|
112
|
+
for (const line of String(source).split(/\r?\n/)) {
|
|
113
|
+
if (line.includes(NONMAPPABLE_MARKER)) continue;
|
|
114
|
+
let m;
|
|
115
|
+
if ((m = FLUTTER_COLOR_RE.exec(line))) {
|
|
116
|
+
const ch = argbLiteralToChannels(m[1]);
|
|
117
|
+
tokens[m[2]] = channelsToHex(ch.r, ch.g, ch.b, ch.a, m[3] === '1');
|
|
118
|
+
} else if ((m = FLUTTER_DIM_RE.exec(line))) {
|
|
119
|
+
// Recover canonical Npx: a logical-px double `16.0` -> `16px`.
|
|
120
|
+
tokens[m[2]] = `${parseFloat(m[1])}px`;
|
|
121
|
+
} else if ((m = FLUTTER_STR_RE.exec(line))) {
|
|
122
|
+
tokens[m[2]] = JSON.parse(m[1]);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return { tokens };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
module.exports = { emitFlutter, reextractFlutter };
|
|
@@ -17,6 +17,12 @@ const { readJsConst } = require('./js-const.cjs');
|
|
|
17
17
|
const { readTailwind } = require('./tailwind.cjs');
|
|
18
18
|
const { readFigma } = require('./figma.cjs');
|
|
19
19
|
|
|
20
|
+
// Phase 34.1 — native theme emitters (token-bridge, D-02: extend the engine,
|
|
21
|
+
// do not fork a native IR). See reference/native-platforms.md.
|
|
22
|
+
const { emitSwift, reextractSwift } = require('./swift.cjs');
|
|
23
|
+
const { emitCompose, reextractCompose } = require('./compose.cjs');
|
|
24
|
+
const { emitFlutter, reextractFlutter } = require('./flutter.cjs');
|
|
25
|
+
|
|
20
26
|
/**
|
|
21
27
|
* @typedef {Object} TokenSet
|
|
22
28
|
* @property {Object<string, string>} tokens
|
|
@@ -97,4 +103,11 @@ module.exports = {
|
|
|
97
103
|
readJsConst,
|
|
98
104
|
readTailwind,
|
|
99
105
|
readFigma,
|
|
106
|
+
// Phase 34.1 native token-bridge emitters + symmetric re-extractors.
|
|
107
|
+
emitSwift,
|
|
108
|
+
emitCompose,
|
|
109
|
+
emitFlutter,
|
|
110
|
+
reextractSwift,
|
|
111
|
+
reextractCompose,
|
|
112
|
+
reextractFlutter,
|
|
100
113
|
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* design-tokens/swift.cjs — SwiftUI theme emitter + re-extractor
|
|
3
|
+
* (Phase 34.1 Plan 01).
|
|
4
|
+
*
|
|
5
|
+
* Consumes the canonical Phase-23 token map ({ tokens: Record<string,string> })
|
|
6
|
+
* and emits a deterministic SwiftUI theme source string: an `enum GDDTheme`
|
|
7
|
+
* exposing static `Color` / `CGFloat` (pt) / `String` constants per the
|
|
8
|
+
* PRECISION CONTRACT in reference/native-platforms.md.
|
|
9
|
+
*
|
|
10
|
+
* Precision contract (the round-trip's definition of "identity preserved"):
|
|
11
|
+
* COLOR hex #RGB/#RRGGBB/#RRGGBBAA -> Color(red:G/255 ...). 8-bit
|
|
12
|
+
* channels EXACT (numerator-over-255 form avoids float drift);
|
|
13
|
+
* #RGB expands to #RRGGBB; alpha opaque (255) when absent.
|
|
14
|
+
* DIMENSION Npx -> CGFloat integer points (round-half-up).
|
|
15
|
+
* TYPOGRAPHY font-family / strings -> String literal, pass-through.
|
|
16
|
+
* NON-MAPPABLE var()/calc()/gradient/rem/em -> verbatim trailing comment,
|
|
17
|
+
* EXCLUDED from the round-trip identity set.
|
|
18
|
+
*
|
|
19
|
+
* Each emitted token line carries a `// token: <original-key>` marker so the
|
|
20
|
+
* symmetric `reextractSwift(source)` recovers the exact canonical key + value.
|
|
21
|
+
* Pure: no fs, no network, no Date, no process.env, no child_process (D-10).
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
'use strict';
|
|
25
|
+
|
|
26
|
+
const {
|
|
27
|
+
TOKEN_MARKER,
|
|
28
|
+
NONMAPPABLE_MARKER,
|
|
29
|
+
readTokens,
|
|
30
|
+
sortedEntries,
|
|
31
|
+
classify,
|
|
32
|
+
parseHexChannels,
|
|
33
|
+
channelsToHex,
|
|
34
|
+
pxToInt,
|
|
35
|
+
swiftIdent,
|
|
36
|
+
} = require('./_native-shared.cjs');
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Emit a SwiftUI theme source string from a token set.
|
|
40
|
+
*
|
|
41
|
+
* @param {{tokens: Record<string,string>}|Record<string,string>} tokenSet
|
|
42
|
+
* @returns {string}
|
|
43
|
+
*/
|
|
44
|
+
function emitSwift(tokenSet) {
|
|
45
|
+
const tokens = readTokens(tokenSet);
|
|
46
|
+
const lines = [];
|
|
47
|
+
lines.push('// Generated by get-design-done — SwiftUI theme tokens.');
|
|
48
|
+
lines.push('// See reference/native-platforms.md (token-bridge precision contract).');
|
|
49
|
+
lines.push('import SwiftUI');
|
|
50
|
+
lines.push('');
|
|
51
|
+
lines.push('enum GDDTheme {');
|
|
52
|
+
for (const [key, value] of sortedEntries(tokens)) {
|
|
53
|
+
const ident = swiftIdent(key);
|
|
54
|
+
const kind = classify(key, value);
|
|
55
|
+
if (kind === 'color') {
|
|
56
|
+
const { r, g, b, a, hadAlpha } = parseHexChannels(value);
|
|
57
|
+
const alphaSlot = hadAlpha
|
|
58
|
+
? `, opacity: ${a}.0/255.0`
|
|
59
|
+
: `, opacity: 255.0/255.0`;
|
|
60
|
+
lines.push(
|
|
61
|
+
` static let ${ident} = Color(red: ${r}.0/255.0, green: ${g}.0/255.0, blue: ${b}.0/255.0${alphaSlot}) ${TOKEN_MARKER}${key} hadAlpha=${hadAlpha ? 1 : 0}`,
|
|
62
|
+
);
|
|
63
|
+
} else if (kind === 'dimension') {
|
|
64
|
+
lines.push(
|
|
65
|
+
` static let ${ident}: CGFloat = ${pxToInt(value)} ${TOKEN_MARKER}${key}`,
|
|
66
|
+
);
|
|
67
|
+
} else if (kind === 'typography') {
|
|
68
|
+
lines.push(
|
|
69
|
+
` static let ${ident} = ${JSON.stringify(value)} ${TOKEN_MARKER}${key}`,
|
|
70
|
+
);
|
|
71
|
+
} else {
|
|
72
|
+
// non-mappable — verbatim, excluded from identity set
|
|
73
|
+
lines.push(` ${NONMAPPABLE_MARKER}${key} = ${value}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
lines.push('}');
|
|
77
|
+
lines.push('');
|
|
78
|
+
return lines.join('\n');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const SWIFT_COLOR_RE = new RegExp(
|
|
82
|
+
String.raw`Color\(red:\s*(\d+)\.0/255\.0,\s*green:\s*(\d+)\.0/255\.0,\s*blue:\s*(\d+)\.0/255\.0,\s*opacity:\s*(\d+)\.0/255\.0\)\s*` +
|
|
83
|
+
TOKEN_MARKER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +
|
|
84
|
+
String.raw`(\S+)\s+hadAlpha=([01])`,
|
|
85
|
+
);
|
|
86
|
+
const SWIFT_DIM_RE = new RegExp(
|
|
87
|
+
String.raw`:\s*CGFloat\s*=\s*(\d+)\s*` +
|
|
88
|
+
TOKEN_MARKER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +
|
|
89
|
+
String.raw`(\S+)`,
|
|
90
|
+
);
|
|
91
|
+
const SWIFT_STR_RE = new RegExp(
|
|
92
|
+
String.raw`static let \w+ = ("(?:[^"\\]|\\.)*")\s*` +
|
|
93
|
+
TOKEN_MARKER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +
|
|
94
|
+
String.raw`(\S+)`,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Recover the canonical token map from an emitted SwiftUI source string.
|
|
99
|
+
* Non-mappable lines (NONMAPPABLE_MARKER) are intentionally NOT recovered.
|
|
100
|
+
*
|
|
101
|
+
* @param {string} source
|
|
102
|
+
* @returns {{tokens: Record<string,string>}}
|
|
103
|
+
*/
|
|
104
|
+
function reextractSwift(source) {
|
|
105
|
+
/** @type {Record<string,string>} */
|
|
106
|
+
const tokens = {};
|
|
107
|
+
for (const line of String(source).split(/\r?\n/)) {
|
|
108
|
+
if (line.includes(NONMAPPABLE_MARKER)) continue;
|
|
109
|
+
let m;
|
|
110
|
+
if ((m = SWIFT_COLOR_RE.exec(line))) {
|
|
111
|
+
const [, r, g, b, a, key, hadAlpha] = m;
|
|
112
|
+
tokens[key] = channelsToHex(+r, +g, +b, +a, hadAlpha === '1');
|
|
113
|
+
} else if ((m = SWIFT_DIM_RE.exec(line))) {
|
|
114
|
+
tokens[m[2]] = `${m[1]}px`;
|
|
115
|
+
} else if ((m = SWIFT_STR_RE.exec(line))) {
|
|
116
|
+
tokens[m[2]] = JSON.parse(m[1]);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return { tokens };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
module.exports = { emitSwift, reextractSwift };
|