mac-human-design 0.1.3 → 0.1.6

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.
@@ -0,0 +1,277 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
6
+ const packageJsonPath = path.join(repoRoot, "node_modules", "@base-ui", "react", "package.json");
7
+ const wrapperPath = path.join(repoRoot, "src", "components", "MacBaseUI.tsx");
8
+ const galleryPath = path.join(repoRoot, "src", "components", "MacBaseUIGallery.tsx");
9
+ const stylesPath = path.join(repoRoot, "src", "styles", "macosBaseUi.css");
10
+ const docsPath = path.join(repoRoot, "docs", "macos-base-ui-coverage.md");
11
+
12
+ const nonComponentExports = new Set([
13
+ ".",
14
+ "esm",
15
+ "merge-props",
16
+ "types",
17
+ "unstable-use-media-query",
18
+ "use-render",
19
+ ]);
20
+
21
+ const acronymParts = new Map([
22
+ ["csp", "CSP"],
23
+ ["otp", "OTP"],
24
+ ]);
25
+
26
+ function readText(filePath) {
27
+ return fs.readFileSync(filePath, "utf8");
28
+ }
29
+
30
+ function toMacExportName(family) {
31
+ return `Mac${family
32
+ .split("-")
33
+ .map((part) => acronymParts.get(part) ?? `${part[0].toUpperCase()}${part.slice(1)}`)
34
+ .join("")}`;
35
+ }
36
+
37
+ function getBaseUiFamilies() {
38
+ const packageJson = JSON.parse(readText(packageJsonPath));
39
+ return Object.keys(packageJson.exports)
40
+ .filter((exportKey) => exportKey.startsWith("./"))
41
+ .map((exportKey) => exportKey.slice(2))
42
+ .filter((exportName) => !exportName.startsWith("internals/"))
43
+ .filter((exportName) => exportName !== "package.json")
44
+ .filter((exportName) => !nonComponentExports.has(exportName))
45
+ .sort();
46
+ }
47
+
48
+ function getMacExports(source) {
49
+ return new Set(
50
+ Array.from(
51
+ source.matchAll(/^export (?:const|function) (Mac[A-Za-z0-9]+)\b/gm),
52
+ (match) => match[1],
53
+ ),
54
+ );
55
+ }
56
+
57
+ function getBaseUiParts(family) {
58
+ const partsPath = path.join(repoRoot, "node_modules", "@base-ui", "react", family, "index.parts.d.ts");
59
+ if (!fs.existsSync(partsPath)) {
60
+ return [];
61
+ }
62
+
63
+ const partsSource = readText(partsPath);
64
+ const parts = new Set();
65
+
66
+ for (const match of partsSource.matchAll(/export\s+\{\s*[A-Za-z0-9_$]+\s+as\s+([A-Z][A-Za-z0-9_$]*)\s*\}/g)) {
67
+ parts.add(match[1]);
68
+ }
69
+
70
+ for (const match of partsSource.matchAll(/export\s+\{\s*([A-Z][A-Za-z0-9_$]*)\s*\}/g)) {
71
+ parts.add(match[1]);
72
+ }
73
+
74
+ return Array.from(parts).sort();
75
+ }
76
+
77
+ function getMacObjectPartKeys(source, macExport) {
78
+ const objectStart = source.match(new RegExp(`export const ${macExport} = \\\\{`));
79
+ if (!objectStart) {
80
+ return null;
81
+ }
82
+
83
+ const startIndex = objectStart.index + objectStart[0].length;
84
+ let depth = 1;
85
+ let index = startIndex;
86
+
87
+ while (index < source.length && depth > 0) {
88
+ const char = source[index];
89
+ if (char === "{") {
90
+ depth += 1;
91
+ } else if (char === "}") {
92
+ depth -= 1;
93
+ }
94
+ index += 1;
95
+ }
96
+
97
+ const objectBody = source.slice(startIndex, index - 1);
98
+ return new Set(
99
+ Array.from(objectBody.matchAll(/^\s*([A-Z][A-Za-z0-9_$]*):/gm), (match) => match[1]),
100
+ );
101
+ }
102
+
103
+ function fail(message, details = []) {
104
+ console.error(message);
105
+ for (const detail of details) {
106
+ console.error(`- ${detail}`);
107
+ }
108
+ process.exitCode = 1;
109
+ }
110
+
111
+ const families = getBaseUiFamilies();
112
+ const wrapperSource = readText(wrapperPath);
113
+ const gallerySource = readText(galleryPath);
114
+ const stylesSource = readText(stylesPath);
115
+ const docsSource = readText(docsPath);
116
+ const macExports = getMacExports(wrapperSource);
117
+
118
+ const missingWrappers = families
119
+ .map((family) => [family, toMacExportName(family)])
120
+ .filter(([, macExport]) => !macExports.has(macExport))
121
+ .map(([family, macExport]) => `${family} -> ${macExport}`);
122
+
123
+ if (missingWrappers.length > 0) {
124
+ fail("Missing macOS wrappers for public @base-ui/react component families.", missingWrappers);
125
+ }
126
+
127
+ const missingPartWrappers = [];
128
+ for (const family of families) {
129
+ const macExport = toMacExportName(family);
130
+ const baseParts = getBaseUiParts(family);
131
+ if (baseParts.length === 0) {
132
+ continue;
133
+ }
134
+
135
+ const macParts = getMacObjectPartKeys(wrapperSource, macExport);
136
+ if (!macParts) {
137
+ continue;
138
+ }
139
+
140
+ for (const part of baseParts) {
141
+ if (!macParts.has(part)) {
142
+ missingPartWrappers.push(`${family}.${part} -> ${macExport}.${part}`);
143
+ }
144
+ }
145
+ }
146
+
147
+ if (missingPartWrappers.length > 0) {
148
+ fail("Missing macOS wrappers for public @base-ui/react namespace parts.", missingPartWrappers);
149
+ }
150
+
151
+ const missingDocsRows = families
152
+ .map((family) => [family, toMacExportName(family)])
153
+ .filter(([family, macExport]) => {
154
+ return !docsSource.includes(`| \`${family}\` | \`${macExport}\` |`);
155
+ })
156
+ .map(([family, macExport]) => `${family} -> ${macExport}`);
157
+
158
+ if (missingDocsRows.length > 0) {
159
+ fail("Missing coverage documentation rows.", missingDocsRows);
160
+ }
161
+
162
+ const requiredVariants = [
163
+ "MacMotionProvider",
164
+ "MacPrimaryButton",
165
+ "MacSecondaryButton",
166
+ "MacDestructiveButton",
167
+ "MacPlainButton",
168
+ "MacIconButton",
169
+ "MacHelpButton",
170
+ "MacTextField",
171
+ "MacSearchField",
172
+ "MacToolbarToggle",
173
+ "MacSegmentedToggleGroup",
174
+ "MacSeparatedSegmentedToggleGroup",
175
+ ];
176
+
177
+ const missingVariants = requiredVariants.filter((variant) => !macExports.has(variant));
178
+ if (missingVariants.length > 0) {
179
+ fail("Missing expected native macOS variant exports.", missingVariants);
180
+ }
181
+
182
+ const requiredNestedVariants = [
183
+ "MacSelect.PopUpButtonTrigger",
184
+ "MacSelect.PullDownButtonTrigger",
185
+ "MacDialog.SheetPopup",
186
+ "MacDrawer.SidebarPopup",
187
+ "MacToolbar.IconButton",
188
+ ];
189
+
190
+ const missingNestedVariants = requiredNestedVariants.filter((variantPath) => {
191
+ const [exportName, partName] = variantPath.split(".");
192
+ return !new RegExp(`export const ${exportName} = [\\s\\S]*?^\\s*${partName}:`, "m").test(
193
+ wrapperSource,
194
+ );
195
+ });
196
+
197
+ if (missingNestedVariants.length > 0) {
198
+ fail("Missing expected native macOS nested variant exports.", missingNestedVariants);
199
+ }
200
+
201
+ const missingGalleryCoverage = Array.from(macExports)
202
+ .sort()
203
+ .filter((macExport) => !gallerySource.includes(macExport));
204
+
205
+ if (missingGalleryCoverage.length > 0) {
206
+ fail("Missing gallery coverage for public macOS wrapper exports.", missingGalleryCoverage);
207
+ }
208
+
209
+ const wrapperClasses = new Set(
210
+ Array.from(wrapperSource.matchAll(/"([^"]*hd-mac[^"]*)"/g))
211
+ .flatMap((match) => match[1].split(/\s+/))
212
+ .filter(Boolean),
213
+ );
214
+
215
+ const missingClassStyles = Array.from(wrapperClasses)
216
+ .sort()
217
+ .filter((className) => {
218
+ const escapedClassName = className.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
219
+ return !new RegExp(`\\.${escapedClassName}(?![a-zA-Z0-9_-])`).test(stylesSource);
220
+ });
221
+
222
+ if (missingClassStyles.length > 0) {
223
+ fail("Missing CSS selectors for macOS wrapper classes.", missingClassStyles);
224
+ }
225
+
226
+ const forbiddenPatterns = [
227
+ /\bfrom\s+["']baseui(?:\/[^"']*)?["']/,
228
+ /\bbaseui\b/,
229
+ /\bstyletron\b/i,
230
+ /Base Web/,
231
+ ];
232
+
233
+ function listFiles(directory, relativePrefix = "") {
234
+ const files = [];
235
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
236
+ const relativePath = path.join(relativePrefix, entry.name);
237
+ const absolutePath = path.join(directory, entry.name);
238
+ if (entry.isDirectory()) {
239
+ files.push(...listFiles(absolutePath, relativePath));
240
+ } else if (entry.isFile()) {
241
+ files.push(relativePath);
242
+ }
243
+ }
244
+ return files;
245
+ }
246
+
247
+ const sourceFiles = [
248
+ "package.json",
249
+ "package-lock.json",
250
+ "README.md",
251
+ "docs/macos-base-ui-coverage.md",
252
+ ...listFiles(path.join(repoRoot, "src")).map((filePath) => path.join("src", filePath)),
253
+ ...listFiles(path.join(repoRoot, "examples")).map((filePath) => path.join("examples", filePath)),
254
+ ];
255
+
256
+ const forbiddenHits = [];
257
+ for (const relativeFile of sourceFiles) {
258
+ const absoluteFile = path.join(repoRoot, relativeFile);
259
+ const text = readText(absoluteFile);
260
+ for (const pattern of forbiddenPatterns) {
261
+ if (pattern.test(text)) {
262
+ forbiddenHits.push(`${relativeFile}: ${pattern}`);
263
+ }
264
+ }
265
+ }
266
+
267
+ if (forbiddenHits.length > 0) {
268
+ fail("Forbidden Base Web or Styletron references found.", forbiddenHits);
269
+ }
270
+
271
+ if (process.exitCode) {
272
+ process.exit();
273
+ }
274
+
275
+ console.log(
276
+ `Verified ${families.length} @base-ui/react component families, namespace parts, ${requiredVariants.length} provider/variant exports, ${requiredNestedVariants.length} nested variants, ${wrapperClasses.size} wrapper classes, gallery coverage, and no Base Web references.`,
277
+ );
@@ -0,0 +1,146 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
6
+ const stylesPath = path.join(repoRoot, "src", "styles", "macosBaseUi.css");
7
+ const packageJsonPath = path.join(repoRoot, "package.json");
8
+ const wrapperPath = path.join(repoRoot, "src", "components", "MacBaseUI.tsx");
9
+
10
+ function readText(filePath) {
11
+ return fs.readFileSync(filePath, "utf8");
12
+ }
13
+
14
+ function listFiles(directory, relativePrefix = "") {
15
+ const files = [];
16
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
17
+ const relativePath = path.join(relativePrefix, entry.name);
18
+ const absolutePath = path.join(directory, entry.name);
19
+ if (entry.isDirectory()) {
20
+ files.push(...listFiles(absolutePath, relativePath));
21
+ } else if (entry.isFile()) {
22
+ files.push(relativePath);
23
+ }
24
+ }
25
+ return files;
26
+ }
27
+
28
+ function fail(message, details = []) {
29
+ console.error(message);
30
+ for (const detail of details) {
31
+ console.error(`- ${detail}`);
32
+ }
33
+ process.exitCode = 1;
34
+ }
35
+
36
+ const sourceFiles = listFiles(path.join(repoRoot, "src"))
37
+ .filter((filePath) => /\.(css|ts|tsx)$/.test(filePath))
38
+ .map((filePath) => path.join("src", filePath));
39
+
40
+ const sources = new Map(
41
+ sourceFiles.map((relativeFile) => [relativeFile, readText(path.join(repoRoot, relativeFile))]),
42
+ );
43
+ const stylesSource = readText(stylesPath);
44
+ const packageJson = JSON.parse(readText(packageJsonPath));
45
+ const wrapperSource = readText(wrapperPath);
46
+
47
+ const requiredStyleFeatures = [
48
+ ["system font stack", /-apple-system,\s*BlinkMacSystemFont/],
49
+ ["dark color-scheme support", /@media\s*\(prefers-color-scheme:\s*dark\)/],
50
+ ["reduced motion support", /@media\s*\(prefers-reduced-motion:\s*reduce\)/],
51
+ ["keyboard focus-visible support", /:focus-visible/],
52
+ ["macOS blue accent token", /--hd-mac-blue:\s*#0a84ff/i],
53
+ ];
54
+
55
+ const missingFeatures = requiredStyleFeatures
56
+ .filter(([, pattern]) => !pattern.test(stylesSource))
57
+ .map(([label]) => label);
58
+
59
+ if (missingFeatures.length > 0) {
60
+ fail("Missing required macOS styling features.", missingFeatures);
61
+ }
62
+
63
+ const missingMotionDependencies = [];
64
+ if (!packageJson.peerDependencies?.motion) {
65
+ missingMotionDependencies.push("peerDependencies.motion");
66
+ }
67
+ if (!packageJson.devDependencies?.motion) {
68
+ missingMotionDependencies.push("devDependencies.motion");
69
+ }
70
+
71
+ if (missingMotionDependencies.length > 0) {
72
+ fail("Missing required Motion dependency declarations.", missingMotionDependencies);
73
+ }
74
+
75
+ const requiredMotionFeatures = [
76
+ ["MotionConfig import", /import\s+\{[\s\S]*MotionConfig[\s\S]*\}\s+from\s+["']motion\/react["']/],
77
+ ["motion import", /import\s+\{[\s\S]*\bmotion\b[\s\S]*\}\s+from\s+["']motion\/react["']/],
78
+ ["MacMotionProvider export", /export function MacMotionProvider\b/],
79
+ ["MotionProps wrapper typing", /React\.ComponentPropsWithoutRef<C> & MotionProps/],
80
+ ["reduced motion user config", /reducedMotion\s*=\s*["']user["']/],
81
+ ["motion.create wrapper", /motion\.create\(BaseWrapped as AnyComponent\)/],
82
+ ["default Motion transition", /const macDefaultTransition:\s*Transition/],
83
+ ];
84
+
85
+ const missingMotionFeatures = requiredMotionFeatures
86
+ .filter(([, pattern]) => !pattern.test(wrapperSource))
87
+ .map(([label]) => label);
88
+
89
+ if (missingMotionFeatures.length > 0) {
90
+ fail("Missing required Motion integration features.", missingMotionFeatures);
91
+ }
92
+
93
+ const motionCreateMatches = Array.from(wrapperSource.matchAll(/motion\.create\(/g));
94
+ const allowedMotionCreatePattern = /^\s*const MotionPart = motion\.create\(BaseWrapped as AnyComponent\);$/m;
95
+ if (motionCreateMatches.length !== 1 || !allowedMotionCreatePattern.test(wrapperSource)) {
96
+ fail("motion.create() must be called exactly once in the module-level macPart wrapper setup.", [
97
+ "src/components/MacBaseUI.tsx",
98
+ ]);
99
+ }
100
+
101
+ const negativeLetterSpacing = [];
102
+ const viewportScaledFonts = [];
103
+
104
+ for (const [relativeFile, source] of sources) {
105
+ for (const match of source.matchAll(/letter-spacing\s*:\s*-[^;,\n}]*/g)) {
106
+ negativeLetterSpacing.push(`${relativeFile}: ${match[0]}`);
107
+ }
108
+
109
+ for (const match of source.matchAll(/font(?:-size)?\s*:\s*[^;,\n}]*(?:vw|vh|vmin|vmax)/g)) {
110
+ viewportScaledFonts.push(`${relativeFile}: ${match[0]}`);
111
+ }
112
+ }
113
+
114
+ if (negativeLetterSpacing.length > 0) {
115
+ fail("Negative letter spacing is not allowed in human-design controls.", negativeLetterSpacing);
116
+ }
117
+
118
+ if (viewportScaledFonts.length > 0) {
119
+ fail("Viewport-scaled font sizing is not allowed in human-design controls.", viewportScaledFonts);
120
+ }
121
+
122
+ const allSourceText = Array.from(sources.values()).join("\n");
123
+ const hdMacClasses = new Set(
124
+ Array.from(allSourceText.matchAll(/["'`]([^"'`]*hd-mac[^"'`]*)["'`]/g))
125
+ .flatMap((match) => match[1].split(/\s+/))
126
+ .filter((className) => className.startsWith("hd-mac")),
127
+ );
128
+
129
+ const missingClassStyles = Array.from(hdMacClasses)
130
+ .sort()
131
+ .filter((className) => {
132
+ const escapedClassName = className.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
133
+ return !new RegExp(`\\.${escapedClassName}(?![a-zA-Z0-9_-])`).test(stylesSource);
134
+ });
135
+
136
+ if (missingClassStyles.length > 0) {
137
+ fail("Missing CSS selectors for hd-mac classes used in source files.", missingClassStyles);
138
+ }
139
+
140
+ if (process.exitCode) {
141
+ process.exit();
142
+ }
143
+
144
+ console.log(
145
+ `Verified macOS design features, typography constraints, and ${hdMacClasses.size} styled hd-mac classes.`,
146
+ );
@@ -1,25 +1,11 @@
1
1
  import { ReactNode } from "react";
2
- import { Block } from "baseui/block";
3
- import { useStyletron } from "baseui";
4
2
 
5
3
  export function AppWindowShell({ children }: { children: ReactNode }) {
6
- const [, theme] = useStyletron();
7
-
8
4
  return (
9
- <Block
10
- width="100%"
11
- $style={{
12
- minHeight: "100vh",
13
- boxSizing: "border-box",
14
- display: "flex",
15
- flexDirection: "column",
16
- backgroundColor: theme.colors.backgroundPrimary,
17
- color: theme.colors.contentPrimary,
18
- }}
19
- >
20
- <Block flex="1" $style={{ minHeight: 0, display: "flex", flexDirection: "column" }}>
5
+ <div className="hd-mac-window">
6
+ <div className="hd-mac-window-content">
21
7
  {children}
22
- </Block>
23
- </Block>
8
+ </div>
9
+ </div>
24
10
  );
25
11
  }