mac-human-design 0.1.30-preview.1 → 0.1.30-preview.3
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/README.md +2 -2
- package/changelog.md +17 -0
- package/docs/macos-base-ui-coverage.md +67 -67
- package/examples/macos-base-ui-gallery/src/main.tsx +6 -6
- package/package.json +11 -2
- package/scripts/verify-base-ui-coverage.mjs +96 -109
- package/scripts/verify-macos-design-components.mjs +224 -0
- package/scripts/verify-macos-design-icons.mjs +86 -0
- package/scripts/verify-macos-design.mjs +109 -327
- package/src/components/AppWindowShell.tsx +3 -5
- package/src/components/MacBaseUI.tsx +3 -736
- package/src/components/MacBaseUIControls.tsx +202 -0
- package/src/components/MacBaseUIGallery.tsx +14 -456
- package/src/components/MacBaseUIGalleryAdvanced.tsx +294 -0
- package/src/components/MacBaseUIGalleryControls.tsx +196 -0
- package/src/components/MacBaseUIGalleryShared.tsx +21 -0
- package/src/components/MacBaseUIInternals.tsx +297 -0
- package/src/components/MacBaseUINavigation.tsx +233 -0
- package/src/components/MacDataTable.tsx +95 -53
- package/src/components/MacSegmentedControl.tsx +22 -26
- package/src/components/MacWindowToolbar.tsx +17 -17
- package/src/components/StatusMessage.tsx +5 -5
- package/src/components/SymbolIconButton.tsx +30 -30
- package/src/components/ViewDragRegion.tsx +3 -3
- package/src/components/index.ts +9 -9
- package/src/index.ts +14 -14
- package/src/styles/macosBaseUi.css +67 -29
- package/src/styles/macosTauri.css +2 -4
- package/src/symbols/SystemSymbolImage.tsx +20 -20
- package/src/symbols/index.ts +3 -3
- package/src/symbols/systemSymbolService.ts +71 -89
- package/src/symbols/useSystemSymbol.ts +27 -32
- package/src/tauri/index.ts +2 -4
- package/src/tauri/macosWindowConfig.ts +3 -3
- package/src/theme/index.ts +1 -1
- package/src/theme/macosTheme.ts +9 -9
- package/src/types/css.d.ts +1 -1
- package/src/utils/index.ts +1 -1
- package/src/utils/isDraggableAreaTarget.ts +4 -7
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ Consumer apps should also include the macOS Base UI stylesheet once near their
|
|
|
31
31
|
app root:
|
|
32
32
|
|
|
33
33
|
```ts
|
|
34
|
-
import "mac-human-design/styles/macos-base-ui.css"
|
|
34
|
+
import "mac-human-design/styles/macos-base-ui.css"
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
In the Tauri Rust side, add the local path dependency while developing:
|
|
@@ -172,5 +172,5 @@ import {
|
|
|
172
172
|
SymbolIconButton,
|
|
173
173
|
ViewDragRegion,
|
|
174
174
|
isDraggableAreaTarget,
|
|
175
|
-
} from "mac-human-design"
|
|
175
|
+
} from "mac-human-design"
|
|
176
176
|
```
|
package/changelog.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
This file tracks changes between published npm versions of `mac-human-design`.
|
|
4
4
|
|
|
5
|
+
## 0.1.30-preview.2
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added `MacDataTable` arrow, Home, and End keyboard navigation for interactive
|
|
10
|
+
rows.
|
|
11
|
+
- Added a layered `MacDataTable` row hover and selection treatment so row
|
|
12
|
+
feedback animates under cell content without shifting text or layout.
|
|
13
|
+
- Added verifier coverage for data-table keyboard navigation and row state
|
|
14
|
+
layer invariants.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- Improved Finder-style table interaction polish for object-browser style
|
|
19
|
+
screens while preserving row geometry and selected-row focus rings.
|
|
20
|
+
- Bumped the package to `0.1.30-preview.2` under the `preview` dist-tag.
|
|
21
|
+
|
|
5
22
|
## 0.1.30-preview.1
|
|
6
23
|
|
|
7
24
|
### Added
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
1.5.0 with macOS-oriented class names. Import the CSS once:
|
|
5
5
|
|
|
6
6
|
```ts
|
|
7
|
-
import "mac-human-design/styles/macos-base-ui.css"
|
|
7
|
+
import "mac-human-design/styles/macos-base-ui.css"
|
|
8
8
|
```
|
|
9
9
|
|
|
10
10
|
Consumer apps install `@base-ui/react`, `motion`, `react`, and `react-dom` as
|
|
@@ -14,48 +14,48 @@ preference through `MacMotionProvider`.
|
|
|
14
14
|
|
|
15
15
|
## Component Families
|
|
16
16
|
|
|
17
|
-
| Base UI family
|
|
18
|
-
|
|
|
19
|
-
| `accordion`
|
|
20
|
-
| `alert-dialog`
|
|
21
|
-
| `autocomplete`
|
|
22
|
-
| `avatar`
|
|
23
|
-
| `button`
|
|
24
|
-
| `checkbox`
|
|
25
|
-
| `checkbox-group`
|
|
26
|
-
| `collapsible`
|
|
27
|
-
| `combobox`
|
|
28
|
-
| `context-menu`
|
|
29
|
-
| `csp-provider`
|
|
30
|
-
| `dialog`
|
|
17
|
+
| Base UI family | macOS export |
|
|
18
|
+
| -------------------- | ---------------------- |
|
|
19
|
+
| `accordion` | `MacAccordion` |
|
|
20
|
+
| `alert-dialog` | `MacAlertDialog` |
|
|
21
|
+
| `autocomplete` | `MacAutocomplete` |
|
|
22
|
+
| `avatar` | `MacAvatar` |
|
|
23
|
+
| `button` | `MacButton` |
|
|
24
|
+
| `checkbox` | `MacCheckbox` |
|
|
25
|
+
| `checkbox-group` | `MacCheckboxGroup` |
|
|
26
|
+
| `collapsible` | `MacCollapsible` |
|
|
27
|
+
| `combobox` | `MacCombobox` |
|
|
28
|
+
| `context-menu` | `MacContextMenu` |
|
|
29
|
+
| `csp-provider` | `MacCSPProvider` |
|
|
30
|
+
| `dialog` | `MacDialog` |
|
|
31
31
|
| `direction-provider` | `MacDirectionProvider` |
|
|
32
|
-
| `drawer`
|
|
33
|
-
| `field`
|
|
34
|
-
| `fieldset`
|
|
35
|
-
| `form`
|
|
36
|
-
| `input`
|
|
37
|
-
| `menu`
|
|
38
|
-
| `menubar`
|
|
39
|
-
| `meter`
|
|
40
|
-
| `navigation-menu`
|
|
41
|
-
| `number-field`
|
|
42
|
-
| `otp-field`
|
|
43
|
-
| `popover`
|
|
44
|
-
| `preview-card`
|
|
45
|
-
| `progress`
|
|
46
|
-
| `radio`
|
|
47
|
-
| `radio-group`
|
|
48
|
-
| `scroll-area`
|
|
49
|
-
| `select`
|
|
50
|
-
| `separator`
|
|
51
|
-
| `slider`
|
|
52
|
-
| `switch`
|
|
53
|
-
| `tabs`
|
|
54
|
-
| `toast`
|
|
55
|
-
| `toggle`
|
|
56
|
-
| `toggle-group`
|
|
57
|
-
| `toolbar`
|
|
58
|
-
| `tooltip`
|
|
32
|
+
| `drawer` | `MacDrawer` |
|
|
33
|
+
| `field` | `MacField` |
|
|
34
|
+
| `fieldset` | `MacFieldset` |
|
|
35
|
+
| `form` | `MacForm` |
|
|
36
|
+
| `input` | `MacInput` |
|
|
37
|
+
| `menu` | `MacMenu` |
|
|
38
|
+
| `menubar` | `MacMenubar` |
|
|
39
|
+
| `meter` | `MacMeter` |
|
|
40
|
+
| `navigation-menu` | `MacNavigationMenu` |
|
|
41
|
+
| `number-field` | `MacNumberField` |
|
|
42
|
+
| `otp-field` | `MacOTPField` |
|
|
43
|
+
| `popover` | `MacPopover` |
|
|
44
|
+
| `preview-card` | `MacPreviewCard` |
|
|
45
|
+
| `progress` | `MacProgress` |
|
|
46
|
+
| `radio` | `MacRadio` |
|
|
47
|
+
| `radio-group` | `MacRadioGroup` |
|
|
48
|
+
| `scroll-area` | `MacScrollArea` |
|
|
49
|
+
| `select` | `MacSelect` |
|
|
50
|
+
| `separator` | `MacSeparator` |
|
|
51
|
+
| `slider` | `MacSlider` |
|
|
52
|
+
| `switch` | `MacSwitch` |
|
|
53
|
+
| `tabs` | `MacTabs` |
|
|
54
|
+
| `toast` | `MacToast` |
|
|
55
|
+
| `toggle` | `MacToggle` |
|
|
56
|
+
| `toggle-group` | `MacToggleGroup` |
|
|
57
|
+
| `toolbar` | `MacToolbar` |
|
|
58
|
+
| `tooltip` | `MacTooltip` |
|
|
59
59
|
|
|
60
60
|
Support exports such as `merge-props`, `use-render`, and
|
|
61
61
|
`unstable-use-media-query` are intentionally not wrapped because they do not
|
|
@@ -63,38 +63,38 @@ render UI.
|
|
|
63
63
|
|
|
64
64
|
## Native Variants
|
|
65
65
|
|
|
66
|
-
| Native role
|
|
67
|
-
|
|
|
68
|
-
| Default push button
|
|
69
|
-
| Blue default button
|
|
70
|
-
| Secondary push button
|
|
71
|
-
| Destructive button
|
|
72
|
-
| Borderless button
|
|
73
|
-
| Icon button
|
|
74
|
-
| Help button
|
|
75
|
-
| Motion defaults provider
|
|
76
|
-
| Text field
|
|
77
|
-
| Search field
|
|
78
|
-
| Pop-up button
|
|
79
|
-
| Pull-down button
|
|
80
|
-
| Segmented control
|
|
66
|
+
| Native role | Export |
|
|
67
|
+
| --------------------------- | ---------------------------------- |
|
|
68
|
+
| Default push button | `MacButton` |
|
|
69
|
+
| Blue default button | `MacPrimaryButton` |
|
|
70
|
+
| Secondary push button | `MacSecondaryButton` |
|
|
71
|
+
| Destructive button | `MacDestructiveButton` |
|
|
72
|
+
| Borderless button | `MacPlainButton` |
|
|
73
|
+
| Icon button | `MacIconButton` |
|
|
74
|
+
| Help button | `MacHelpButton` |
|
|
75
|
+
| Motion defaults provider | `MacMotionProvider` |
|
|
76
|
+
| Text field | `MacTextField` / `MacInput` |
|
|
77
|
+
| Search field | `MacSearchField` |
|
|
78
|
+
| Pop-up button | `MacSelect.PopUpButtonTrigger` |
|
|
79
|
+
| Pull-down button | `MacSelect.PullDownButtonTrigger` |
|
|
80
|
+
| Segmented control | `MacSegmentedToggleGroup` |
|
|
81
81
|
| Separated segmented control | `MacSeparatedSegmentedToggleGroup` |
|
|
82
|
-
| Toolbar toggle
|
|
83
|
-
| Toolbar icon button
|
|
84
|
-
| Window sheet
|
|
85
|
-
| Sidebar drawer
|
|
82
|
+
| Toolbar toggle | `MacToolbarToggle` |
|
|
83
|
+
| Toolbar icon button | `MacToolbar.IconButton` |
|
|
84
|
+
| Window sheet | `MacDialog.SheetPopup` |
|
|
85
|
+
| Sidebar drawer | `MacDrawer.SidebarPopup` |
|
|
86
86
|
|
|
87
87
|
## App-Level Components
|
|
88
88
|
|
|
89
89
|
These shared components cover reusable macOS/Tauri app patterns that should not
|
|
90
90
|
be reimplemented in consuming apps:
|
|
91
91
|
|
|
92
|
-
| App pattern
|
|
93
|
-
|
|
|
94
|
-
| Finder-style grid/table primitive | `MacDataTable`
|
|
95
|
-
| Tauri window toolbar
|
|
96
|
-
| Segmented control
|
|
97
|
-
| Centered SF Symbols icon button
|
|
92
|
+
| App pattern | Export | Contract |
|
|
93
|
+
| --------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
94
|
+
| Finder-style grid/table primitive | `MacDataTable` | Uses `table` semantics for static rows, upgrades to `grid` semantics for interactive rows, exposes row/column metadata, busy state, keyboard activation, arrow/Home/End row focus navigation, selected row state, and layered hover/selection styling. |
|
|
95
|
+
| Tauri window toolbar | `MacWindowToolbar` | Provides a labelled `toolbar` landmark, labelled leading/content/trailing control groups, a root drag region, no-drag regions around interactive controls, and stable wrapped-control sizing for dense icon toolbars. |
|
|
96
|
+
| Segmented control | `MacSegmentedControl` | Wraps the shared macOS segmented toggle styling with accessible labels, stable class names, full-width layout support, and class/style pass-through. |
|
|
97
|
+
| Centered SF Symbols icon button | `SymbolIconButton` | Renders through `MacIconButton`, defaults to `type="button"`, preserves fixed geometry while allowing style/class pass-through, supports tooltip offsets and per-symbol optical offsets, and exposes loading state with `aria-busy`. |
|
|
98
98
|
|
|
99
99
|
## Icon Button Alignment Guarantees
|
|
100
100
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { StrictMode } from "react"
|
|
2
|
-
import { createRoot } from "react-dom/client"
|
|
3
|
-
import { AppWindowShell, MacBaseUIGallery } from "../../../src"
|
|
4
|
-
import "../../../src/styles/macosBaseUi.css"
|
|
5
|
-
import "../../../src/styles/macosTauri.css"
|
|
1
|
+
import { StrictMode } from "react"
|
|
2
|
+
import { createRoot } from "react-dom/client"
|
|
3
|
+
import { AppWindowShell, MacBaseUIGallery } from "../../../src"
|
|
4
|
+
import "../../../src/styles/macosBaseUi.css"
|
|
5
|
+
import "../../../src/styles/macosTauri.css"
|
|
6
6
|
|
|
7
7
|
createRoot(document.getElementById("root") as HTMLElement).render(
|
|
8
8
|
<StrictMode>
|
|
@@ -10,4 +10,4 @@ createRoot(document.getElementById("root") as HTMLElement).render(
|
|
|
10
10
|
<MacBaseUIGallery />
|
|
11
11
|
</AppWindowShell>
|
|
12
12
|
</StrictMode>,
|
|
13
|
-
)
|
|
13
|
+
)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mac-human-design",
|
|
3
|
-
"version": "0.1.30-preview.
|
|
3
|
+
"version": "0.1.30-preview.3",
|
|
4
4
|
"description": "Reusable macOS-oriented UI primitives and SF Symbols bridge for Tauri apps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -36,8 +36,11 @@
|
|
|
36
36
|
"components"
|
|
37
37
|
],
|
|
38
38
|
"scripts": {
|
|
39
|
-
"check": "npm run typecheck && npm run verify:base-ui && npm run verify:design && npm run verify:gallery",
|
|
39
|
+
"check": "npm run lint && npm run format:check && npm run typecheck && npm run verify:base-ui && npm run verify:design && npm run verify:gallery",
|
|
40
40
|
"dev:gallery": "vite --host 127.0.0.1 examples/macos-base-ui-gallery",
|
|
41
|
+
"format": "prettier --write .",
|
|
42
|
+
"format:check": "prettier --check .",
|
|
43
|
+
"lint": "eslint . --max-warnings=0",
|
|
41
44
|
"publish": "npm publish --access public --ignore-scripts",
|
|
42
45
|
"publish:preview": "npm publish --access public --ignore-scripts --tag preview",
|
|
43
46
|
"typecheck": "tsc --noEmit",
|
|
@@ -61,14 +64,20 @@
|
|
|
61
64
|
},
|
|
62
65
|
"devDependencies": {
|
|
63
66
|
"@base-ui/react": "^1.5.0",
|
|
67
|
+
"@eslint/js": "^10.0.1",
|
|
64
68
|
"@tauri-apps/api": "^2.11.0",
|
|
65
69
|
"@types/react": "^19.2.17",
|
|
66
70
|
"@types/react-dom": "^19.2.3",
|
|
67
71
|
"@vitejs/plugin-react": "^6.0.2",
|
|
72
|
+
"eslint": "^10.4.1",
|
|
73
|
+
"eslint-plugin-react-hooks": "^7.1.1",
|
|
74
|
+
"globals": "^17.6.0",
|
|
68
75
|
"motion": "^12.40.0",
|
|
76
|
+
"prettier": "^3.8.4",
|
|
69
77
|
"react": "^19.2.7",
|
|
70
78
|
"react-dom": "^19.2.7",
|
|
71
79
|
"typescript": "^6.0.3",
|
|
80
|
+
"typescript-eslint": "^8.61.0",
|
|
72
81
|
"vite": "^8.0.16"
|
|
73
82
|
}
|
|
74
83
|
}
|
|
@@ -1,162 +1,156 @@
|
|
|
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
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"unstable-use-media-query",
|
|
18
|
-
"use-render",
|
|
19
|
-
]);
|
|
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 wrapperPaths = [
|
|
8
|
+
path.join(repoRoot, "src", "components", "MacBaseUI.tsx"),
|
|
9
|
+
path.join(repoRoot, "src", "components", "MacBaseUIInternals.tsx"),
|
|
10
|
+
path.join(repoRoot, "src", "components", "MacBaseUIControls.tsx"),
|
|
11
|
+
path.join(repoRoot, "src", "components", "MacBaseUINavigation.tsx"),
|
|
12
|
+
]
|
|
13
|
+
const stylesPath = path.join(repoRoot, "src", "styles", "macosBaseUi.css")
|
|
14
|
+
const docsPath = path.join(repoRoot, "docs", "macos-base-ui-coverage.md")
|
|
15
|
+
|
|
16
|
+
const nonComponentExports = new Set([".", "esm", "merge-props", "types", "unstable-use-media-query", "use-render"])
|
|
20
17
|
|
|
21
18
|
const acronymParts = new Map([
|
|
22
19
|
["csp", "CSP"],
|
|
23
20
|
["otp", "OTP"],
|
|
24
|
-
])
|
|
21
|
+
])
|
|
25
22
|
|
|
26
23
|
function readText(filePath) {
|
|
27
|
-
return fs.readFileSync(filePath, "utf8")
|
|
24
|
+
return fs.readFileSync(filePath, "utf8")
|
|
28
25
|
}
|
|
29
26
|
|
|
30
27
|
function toMacExportName(family) {
|
|
31
28
|
return `Mac${family
|
|
32
29
|
.split("-")
|
|
33
30
|
.map((part) => acronymParts.get(part) ?? `${part[0].toUpperCase()}${part.slice(1)}`)
|
|
34
|
-
.join("")}
|
|
31
|
+
.join("")}`
|
|
35
32
|
}
|
|
36
33
|
|
|
37
34
|
function getBaseUiFamilies() {
|
|
38
|
-
const packageJson = JSON.parse(readText(packageJsonPath))
|
|
35
|
+
const packageJson = JSON.parse(readText(packageJsonPath))
|
|
39
36
|
return Object.keys(packageJson.exports)
|
|
40
37
|
.filter((exportKey) => exportKey.startsWith("./"))
|
|
41
38
|
.map((exportKey) => exportKey.slice(2))
|
|
42
39
|
.filter((exportName) => !exportName.startsWith("internals/"))
|
|
43
40
|
.filter((exportName) => exportName !== "package.json")
|
|
44
41
|
.filter((exportName) => !nonComponentExports.has(exportName))
|
|
45
|
-
.sort()
|
|
42
|
+
.sort()
|
|
46
43
|
}
|
|
47
44
|
|
|
48
45
|
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
|
-
);
|
|
46
|
+
return new Set(Array.from(source.matchAll(/^export (?:const|function) (Mac[A-Za-z0-9]+)\b/gm), (match) => match[1]))
|
|
55
47
|
}
|
|
56
48
|
|
|
57
49
|
function getBaseUiParts(family) {
|
|
58
|
-
const partsPath = path.join(repoRoot, "node_modules", "@base-ui", "react", family, "index.parts.d.ts")
|
|
50
|
+
const partsPath = path.join(repoRoot, "node_modules", "@base-ui", "react", family, "index.parts.d.ts")
|
|
59
51
|
if (!fs.existsSync(partsPath)) {
|
|
60
|
-
return []
|
|
52
|
+
return []
|
|
61
53
|
}
|
|
62
54
|
|
|
63
|
-
const partsSource = readText(partsPath)
|
|
64
|
-
const parts = new Set()
|
|
55
|
+
const partsSource = readText(partsPath)
|
|
56
|
+
const parts = new Set()
|
|
65
57
|
|
|
66
58
|
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])
|
|
59
|
+
parts.add(match[1])
|
|
68
60
|
}
|
|
69
61
|
|
|
70
62
|
for (const match of partsSource.matchAll(/export\s+\{\s*([A-Z][A-Za-z0-9_$]*)\s*\}/g)) {
|
|
71
|
-
parts.add(match[1])
|
|
63
|
+
parts.add(match[1])
|
|
72
64
|
}
|
|
73
65
|
|
|
74
|
-
return Array.from(parts).sort()
|
|
66
|
+
return Array.from(parts).sort()
|
|
75
67
|
}
|
|
76
68
|
|
|
77
69
|
function getMacObjectPartKeys(source, macExport) {
|
|
78
|
-
const objectStart = source.match(new RegExp(`export const ${macExport} = \\\\{`))
|
|
70
|
+
const objectStart = source.match(new RegExp(`export const ${macExport} = \\\\{`))
|
|
79
71
|
if (!objectStart) {
|
|
80
|
-
return null
|
|
72
|
+
return null
|
|
81
73
|
}
|
|
82
74
|
|
|
83
|
-
const startIndex = objectStart.index + objectStart[0].length
|
|
84
|
-
let depth = 1
|
|
85
|
-
let index = startIndex
|
|
75
|
+
const startIndex = objectStart.index + objectStart[0].length
|
|
76
|
+
let depth = 1
|
|
77
|
+
let index = startIndex
|
|
86
78
|
|
|
87
79
|
while (index < source.length && depth > 0) {
|
|
88
|
-
const char = source[index]
|
|
80
|
+
const char = source[index]
|
|
89
81
|
if (char === "{") {
|
|
90
|
-
depth += 1
|
|
82
|
+
depth += 1
|
|
91
83
|
} else if (char === "}") {
|
|
92
|
-
depth -= 1
|
|
84
|
+
depth -= 1
|
|
93
85
|
}
|
|
94
|
-
index += 1
|
|
86
|
+
index += 1
|
|
95
87
|
}
|
|
96
88
|
|
|
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
|
-
);
|
|
89
|
+
const objectBody = source.slice(startIndex, index - 1)
|
|
90
|
+
return new Set(Array.from(objectBody.matchAll(/^\s*([A-Z][A-Za-z0-9_$]*):/gm), (match) => match[1]))
|
|
101
91
|
}
|
|
102
92
|
|
|
103
93
|
function fail(message, details = []) {
|
|
104
|
-
console.error(message)
|
|
94
|
+
console.error(message)
|
|
105
95
|
for (const detail of details) {
|
|
106
|
-
console.error(`- ${detail}`)
|
|
96
|
+
console.error(`- ${detail}`)
|
|
107
97
|
}
|
|
108
|
-
process.exitCode = 1
|
|
98
|
+
process.exitCode = 1
|
|
109
99
|
}
|
|
110
100
|
|
|
111
|
-
const families = getBaseUiFamilies()
|
|
112
|
-
const wrapperSource = readText(
|
|
113
|
-
const gallerySource =
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
101
|
+
const families = getBaseUiFamilies()
|
|
102
|
+
const wrapperSource = wrapperPaths.map((filePath) => readText(filePath)).join("\n")
|
|
103
|
+
const gallerySource = listFiles(path.join(repoRoot, "src", "components"))
|
|
104
|
+
.filter((filePath) => /^MacBaseUIGallery.*\.tsx$/.test(filePath))
|
|
105
|
+
.map((filePath) => readText(path.join(repoRoot, "src", "components", filePath)))
|
|
106
|
+
.join("\n")
|
|
107
|
+
const stylesSource = readText(stylesPath)
|
|
108
|
+
const docsSource = readText(docsPath)
|
|
109
|
+
const macExports = getMacExports(wrapperSource)
|
|
117
110
|
|
|
118
111
|
const missingWrappers = families
|
|
119
112
|
.map((family) => [family, toMacExportName(family)])
|
|
120
113
|
.filter(([, macExport]) => !macExports.has(macExport))
|
|
121
|
-
.map(([family, macExport]) => `${family} -> ${macExport}`)
|
|
114
|
+
.map(([family, macExport]) => `${family} -> ${macExport}`)
|
|
122
115
|
|
|
123
116
|
if (missingWrappers.length > 0) {
|
|
124
|
-
fail("Missing macOS wrappers for public @base-ui/react component families.", missingWrappers)
|
|
117
|
+
fail("Missing macOS wrappers for public @base-ui/react component families.", missingWrappers)
|
|
125
118
|
}
|
|
126
119
|
|
|
127
|
-
const missingPartWrappers = []
|
|
120
|
+
const missingPartWrappers = []
|
|
128
121
|
for (const family of families) {
|
|
129
|
-
const macExport = toMacExportName(family)
|
|
130
|
-
const baseParts = getBaseUiParts(family)
|
|
122
|
+
const macExport = toMacExportName(family)
|
|
123
|
+
const baseParts = getBaseUiParts(family)
|
|
131
124
|
if (baseParts.length === 0) {
|
|
132
|
-
continue
|
|
125
|
+
continue
|
|
133
126
|
}
|
|
134
127
|
|
|
135
|
-
const macParts = getMacObjectPartKeys(wrapperSource, macExport)
|
|
128
|
+
const macParts = getMacObjectPartKeys(wrapperSource, macExport)
|
|
136
129
|
if (!macParts) {
|
|
137
|
-
continue
|
|
130
|
+
continue
|
|
138
131
|
}
|
|
139
132
|
|
|
140
133
|
for (const part of baseParts) {
|
|
141
134
|
if (!macParts.has(part)) {
|
|
142
|
-
missingPartWrappers.push(`${family}.${part} -> ${macExport}.${part}`)
|
|
135
|
+
missingPartWrappers.push(`${family}.${part} -> ${macExport}.${part}`)
|
|
143
136
|
}
|
|
144
137
|
}
|
|
145
138
|
}
|
|
146
139
|
|
|
147
140
|
if (missingPartWrappers.length > 0) {
|
|
148
|
-
fail("Missing macOS wrappers for public @base-ui/react namespace parts.", missingPartWrappers)
|
|
141
|
+
fail("Missing macOS wrappers for public @base-ui/react namespace parts.", missingPartWrappers)
|
|
149
142
|
}
|
|
150
143
|
|
|
151
144
|
const missingDocsRows = families
|
|
152
145
|
.map((family) => [family, toMacExportName(family)])
|
|
153
146
|
.filter(([family, macExport]) => {
|
|
154
|
-
|
|
147
|
+
const rowPattern = new RegExp(`\\|\\s*\\\`${family}\\\`\\s*\\|\\s*\\\`${macExport}\\\`\\s*\\|`)
|
|
148
|
+
return !rowPattern.test(docsSource)
|
|
155
149
|
})
|
|
156
|
-
.map(([family, macExport]) => `${family} -> ${macExport}`)
|
|
150
|
+
.map(([family, macExport]) => `${family} -> ${macExport}`)
|
|
157
151
|
|
|
158
152
|
if (missingDocsRows.length > 0) {
|
|
159
|
-
fail("Missing coverage documentation rows.", missingDocsRows)
|
|
153
|
+
fail("Missing coverage documentation rows.", missingDocsRows)
|
|
160
154
|
}
|
|
161
155
|
|
|
162
156
|
const requiredVariants = [
|
|
@@ -172,11 +166,11 @@ const requiredVariants = [
|
|
|
172
166
|
"MacToolbarToggle",
|
|
173
167
|
"MacSegmentedToggleGroup",
|
|
174
168
|
"MacSeparatedSegmentedToggleGroup",
|
|
175
|
-
]
|
|
169
|
+
]
|
|
176
170
|
|
|
177
|
-
const missingVariants = requiredVariants.filter((variant) => !macExports.has(variant))
|
|
171
|
+
const missingVariants = requiredVariants.filter((variant) => !macExports.has(variant))
|
|
178
172
|
if (missingVariants.length > 0) {
|
|
179
|
-
fail("Missing expected native macOS variant exports.", missingVariants)
|
|
173
|
+
fail("Missing expected native macOS variant exports.", missingVariants)
|
|
180
174
|
}
|
|
181
175
|
|
|
182
176
|
const requiredNestedVariants = [
|
|
@@ -185,63 +179,56 @@ const requiredNestedVariants = [
|
|
|
185
179
|
"MacDialog.SheetPopup",
|
|
186
180
|
"MacDrawer.SidebarPopup",
|
|
187
181
|
"MacToolbar.IconButton",
|
|
188
|
-
]
|
|
182
|
+
]
|
|
189
183
|
|
|
190
184
|
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
|
-
|
|
194
|
-
);
|
|
195
|
-
});
|
|
185
|
+
const [exportName, partName] = variantPath.split(".")
|
|
186
|
+
return !new RegExp(`export const ${exportName} = [\\s\\S]*?^\\s*${partName}:`, "m").test(wrapperSource)
|
|
187
|
+
})
|
|
196
188
|
|
|
197
189
|
if (missingNestedVariants.length > 0) {
|
|
198
|
-
fail("Missing expected native macOS nested variant exports.", missingNestedVariants)
|
|
190
|
+
fail("Missing expected native macOS nested variant exports.", missingNestedVariants)
|
|
199
191
|
}
|
|
200
192
|
|
|
201
193
|
const missingGalleryCoverage = Array.from(macExports)
|
|
202
194
|
.sort()
|
|
203
|
-
.filter((macExport) => !gallerySource.includes(macExport))
|
|
195
|
+
.filter((macExport) => !gallerySource.includes(macExport))
|
|
204
196
|
|
|
205
197
|
if (missingGalleryCoverage.length > 0) {
|
|
206
|
-
fail("Missing gallery coverage for public macOS wrapper exports.", missingGalleryCoverage)
|
|
198
|
+
fail("Missing gallery coverage for public macOS wrapper exports.", missingGalleryCoverage)
|
|
207
199
|
}
|
|
208
200
|
|
|
209
201
|
const wrapperClasses = new Set(
|
|
210
202
|
Array.from(wrapperSource.matchAll(/"([^"]*hd-mac[^"]*)"/g))
|
|
211
203
|
.flatMap((match) => match[1].split(/\s+/))
|
|
212
204
|
.filter(Boolean),
|
|
213
|
-
)
|
|
205
|
+
)
|
|
214
206
|
|
|
215
207
|
const missingClassStyles = Array.from(wrapperClasses)
|
|
216
208
|
.sort()
|
|
217
209
|
.filter((className) => {
|
|
218
|
-
const escapedClassName = className.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
219
|
-
return !new RegExp(`\\.${escapedClassName}(?![a-zA-Z0-9_-])`).test(stylesSource)
|
|
220
|
-
})
|
|
210
|
+
const escapedClassName = className.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
211
|
+
return !new RegExp(`\\.${escapedClassName}(?![a-zA-Z0-9_-])`).test(stylesSource)
|
|
212
|
+
})
|
|
221
213
|
|
|
222
214
|
if (missingClassStyles.length > 0) {
|
|
223
|
-
fail("Missing CSS selectors for macOS wrapper classes.", missingClassStyles)
|
|
215
|
+
fail("Missing CSS selectors for macOS wrapper classes.", missingClassStyles)
|
|
224
216
|
}
|
|
225
217
|
|
|
226
|
-
const forbiddenPatterns = [
|
|
227
|
-
/\bfrom\s+["']baseui(?:\/[^"']*)?["']/,
|
|
228
|
-
/\bbaseui\b/,
|
|
229
|
-
/\bstyletron\b/i,
|
|
230
|
-
/Base Web/,
|
|
231
|
-
];
|
|
218
|
+
const forbiddenPatterns = [/\bfrom\s+["']baseui(?:\/[^"']*)?["']/, /\bbaseui\b/, /\bstyletron\b/i, /Base Web/]
|
|
232
219
|
|
|
233
220
|
function listFiles(directory, relativePrefix = "") {
|
|
234
|
-
const files = []
|
|
221
|
+
const files = []
|
|
235
222
|
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)
|
|
223
|
+
const relativePath = path.join(relativePrefix, entry.name)
|
|
224
|
+
const absolutePath = path.join(directory, entry.name)
|
|
238
225
|
if (entry.isDirectory()) {
|
|
239
|
-
files.push(...listFiles(absolutePath, relativePath))
|
|
226
|
+
files.push(...listFiles(absolutePath, relativePath))
|
|
240
227
|
} else if (entry.isFile()) {
|
|
241
|
-
files.push(relativePath)
|
|
228
|
+
files.push(relativePath)
|
|
242
229
|
}
|
|
243
230
|
}
|
|
244
|
-
return files
|
|
231
|
+
return files
|
|
245
232
|
}
|
|
246
233
|
|
|
247
234
|
const sourceFiles = [
|
|
@@ -251,27 +238,27 @@ const sourceFiles = [
|
|
|
251
238
|
"docs/macos-base-ui-coverage.md",
|
|
252
239
|
...listFiles(path.join(repoRoot, "src")).map((filePath) => path.join("src", filePath)),
|
|
253
240
|
...listFiles(path.join(repoRoot, "examples")).map((filePath) => path.join("examples", filePath)),
|
|
254
|
-
]
|
|
241
|
+
]
|
|
255
242
|
|
|
256
|
-
const forbiddenHits = []
|
|
243
|
+
const forbiddenHits = []
|
|
257
244
|
for (const relativeFile of sourceFiles) {
|
|
258
|
-
const absoluteFile = path.join(repoRoot, relativeFile)
|
|
259
|
-
const text = readText(absoluteFile)
|
|
245
|
+
const absoluteFile = path.join(repoRoot, relativeFile)
|
|
246
|
+
const text = readText(absoluteFile)
|
|
260
247
|
for (const pattern of forbiddenPatterns) {
|
|
261
248
|
if (pattern.test(text)) {
|
|
262
|
-
forbiddenHits.push(`${relativeFile}: ${pattern}`)
|
|
249
|
+
forbiddenHits.push(`${relativeFile}: ${pattern}`)
|
|
263
250
|
}
|
|
264
251
|
}
|
|
265
252
|
}
|
|
266
253
|
|
|
267
254
|
if (forbiddenHits.length > 0) {
|
|
268
|
-
fail("Forbidden Base Web or Styletron references found.", forbiddenHits)
|
|
255
|
+
fail("Forbidden Base Web or Styletron references found.", forbiddenHits)
|
|
269
256
|
}
|
|
270
257
|
|
|
271
258
|
if (process.exitCode) {
|
|
272
|
-
process.exit()
|
|
259
|
+
process.exit()
|
|
273
260
|
}
|
|
274
261
|
|
|
275
262
|
console.log(
|
|
276
263
|
`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
|
-
)
|
|
264
|
+
)
|