mlola-ui 1.0.8 → 1.0.9
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 +21 -10
- package/package.json +1 -1
- package/registry/assets.json +168 -12
- package/registry/index.json +44 -44
- package/src/cli.js +40 -28
- package/src/config.js +51 -2
- package/src/doctor.js +19 -32
- package/src/installer.js +10 -4
- package/src/mcp.js +5 -3
package/README.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# mlola-ui
|
|
2
2
|
|
|
3
3
|
The Mlola UI source-copy CLI. It writes components, blocks, pages, and templates
|
|
4
|
-
into your project, rewrites their imports to
|
|
5
|
-
|
|
6
|
-
HTML, and React sources use only
|
|
4
|
+
into your project, rewrites their imports to fit it, and installs the Mlola
|
|
5
|
+
packages they import with your package manager. Framework-free: the copied
|
|
6
|
+
markup works in any language that can emit HTML, and React sources use only
|
|
7
|
+
React.
|
|
7
8
|
|
|
8
9
|
The library's contract is generated from its own stylesheet, so a model reading
|
|
9
10
|
it cannot invent a class or attribute that does not exist.
|
|
@@ -17,20 +18,30 @@ npx mlola-ui doctor
|
|
|
17
18
|
```
|
|
18
19
|
|
|
19
20
|
`init` writes `mlola.config.json`, the engine stylesheet, and the instructions
|
|
20
|
-
your coding agents read (see below)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
your coding agents read (see below), and installs `@mlola-ui/engine`. It fits
|
|
22
|
+
the project it finds: files go where the `@/` alias in `tsconfig.json` points,
|
|
23
|
+
or into `src/` when there is one. Without an alias, as in a new Vite app, it
|
|
24
|
+
sets `"imports": "relative"` and the copied files import each other by
|
|
25
|
+
relative path, so nothing needs setting up first.
|
|
26
|
+
|
|
27
|
+
`add` resolves the dependency graph (a page pulls its blocks, a block pulls its
|
|
28
|
+
components), copies only what you asked for, and installs the packages the
|
|
29
|
+
copied code imports that `package.json` does not declare yet. Pass
|
|
30
|
+
`--no-install` to only print the command. `doctor` checks the project against
|
|
31
|
+
the registry: packages the installed items import, the engine import, the
|
|
32
|
+
theme attribute, and files changed since they were added.
|
|
33
|
+
|
|
34
|
+
Tailwind can stay in the project. The engine places Mlola's layers above
|
|
35
|
+
Tailwind's preflight in either import order.
|
|
25
36
|
|
|
26
37
|
## Commands
|
|
27
38
|
|
|
28
39
|
| Command | What it does |
|
|
29
40
|
| --- | --- |
|
|
30
|
-
| `init [--no-agents]` | create `mlola.config.json`, the stylesheet entry and the agent instructions |
|
|
41
|
+
| `init [--no-agents] [--no-install]` | create `mlola.config.json`, the stylesheet entry and the agent instructions, and install the engine |
|
|
31
42
|
| `agents` | write or refresh the agent instructions in an existing project |
|
|
32
43
|
| `mcp` | run the Mlola MCP server over stdio, for coding agents |
|
|
33
|
-
| `add <items
|
|
44
|
+
| `add <items…> [--no-install]` | copy items and their dependencies into the project, and install the packages they import |
|
|
34
45
|
| `list [--json]` | print every installable item |
|
|
35
46
|
| `doctor` | report project and registry problems |
|
|
36
47
|
| `login <token>` | save a Mlola Pro token (from /account) for this user |
|
package/package.json
CHANGED
package/registry/assets.json
CHANGED
|
@@ -382,13 +382,13 @@
|
|
|
382
382
|
"path": "download.svg",
|
|
383
383
|
"role": "source",
|
|
384
384
|
"bytes": 3444,
|
|
385
|
-
"integrity": "sha256-
|
|
385
|
+
"integrity": "sha256-BUz1gF+cjzSGX80cAXkBEbk2B/QfikfDVvMkXf9aetY="
|
|
386
386
|
},
|
|
387
387
|
{
|
|
388
388
|
"path": "download.tsx",
|
|
389
389
|
"role": "react",
|
|
390
390
|
"bytes": 4359,
|
|
391
|
-
"integrity": "sha256-
|
|
391
|
+
"integrity": "sha256-HqMG8B7wBduafDrBZ3k0PSxWpPZPWArZsJy3YXyv+Ew="
|
|
392
392
|
}
|
|
393
393
|
]
|
|
394
394
|
},
|
|
@@ -656,7 +656,7 @@
|
|
|
656
656
|
"id": "link",
|
|
657
657
|
"kind": "2d",
|
|
658
658
|
"title": "Connected",
|
|
659
|
-
"summary": "Two
|
|
659
|
+
"summary": "Two open chain links joined by a clear central bar with a restrained signal accent.",
|
|
660
660
|
"tags": [
|
|
661
661
|
"link",
|
|
662
662
|
"connection",
|
|
@@ -667,14 +667,14 @@
|
|
|
667
667
|
{
|
|
668
668
|
"path": "link.svg",
|
|
669
669
|
"role": "source",
|
|
670
|
-
"bytes":
|
|
671
|
-
"integrity": "sha256
|
|
670
|
+
"bytes": 2504,
|
|
671
|
+
"integrity": "sha256-+gKXIfVp1G3qEUWS1veswBBwNwRcs5c04F1y3NKB7hs="
|
|
672
672
|
},
|
|
673
673
|
{
|
|
674
674
|
"path": "link.tsx",
|
|
675
675
|
"role": "react",
|
|
676
|
-
"bytes":
|
|
677
|
-
"integrity": "sha256-
|
|
676
|
+
"bytes": 3357,
|
|
677
|
+
"integrity": "sha256-MGE+g1e9BmbGHP1XMzC/YKBCNqp3LO+b5Jb2DiN7wNU="
|
|
678
678
|
}
|
|
679
679
|
]
|
|
680
680
|
},
|
|
@@ -812,7 +812,7 @@
|
|
|
812
812
|
"id": "notifications",
|
|
813
813
|
"kind": "2d",
|
|
814
814
|
"title": "Notifications",
|
|
815
|
-
"summary": "A
|
|
815
|
+
"summary": "A softly outlined notification bell with a small clapper and one fresh message card.",
|
|
816
816
|
"tags": [
|
|
817
817
|
"notifications",
|
|
818
818
|
"alerts",
|
|
@@ -823,14 +823,14 @@
|
|
|
823
823
|
{
|
|
824
824
|
"path": "notifications.svg",
|
|
825
825
|
"role": "source",
|
|
826
|
-
"bytes":
|
|
827
|
-
"integrity": "sha256
|
|
826
|
+
"bytes": 3485,
|
|
827
|
+
"integrity": "sha256-ihNfIqnU8XYhAQnwgl9t2ZOjN0m8XIC8HJmRsec7+EM="
|
|
828
828
|
},
|
|
829
829
|
{
|
|
830
830
|
"path": "notifications.tsx",
|
|
831
831
|
"role": "react",
|
|
832
|
-
"bytes":
|
|
833
|
-
"integrity": "sha256-
|
|
832
|
+
"bytes": 4431,
|
|
833
|
+
"integrity": "sha256-8epIRgSRCUZLmlIjw/P2MZbmkYNXeIz4l5IiX6s22pc="
|
|
834
834
|
}
|
|
835
835
|
]
|
|
836
836
|
},
|
|
@@ -912,6 +912,58 @@
|
|
|
912
912
|
}
|
|
913
913
|
]
|
|
914
914
|
},
|
|
915
|
+
{
|
|
916
|
+
"id": "precise-layout",
|
|
917
|
+
"kind": "2d",
|
|
918
|
+
"title": "Precise layout",
|
|
919
|
+
"summary": "A measured layout sheet and an extended tape, with aligned tick marks and a folded end hook.",
|
|
920
|
+
"tags": [
|
|
921
|
+
"measure",
|
|
922
|
+
"layout",
|
|
923
|
+
"design"
|
|
924
|
+
],
|
|
925
|
+
"themeable": true,
|
|
926
|
+
"files": [
|
|
927
|
+
{
|
|
928
|
+
"path": "precise-layout.svg",
|
|
929
|
+
"role": "source",
|
|
930
|
+
"bytes": 4986,
|
|
931
|
+
"integrity": "sha256-19uhMUCRTWI2J0Po8hbC3cyBmOhv2dxjMqLMsjHaFAo="
|
|
932
|
+
},
|
|
933
|
+
{
|
|
934
|
+
"path": "precise-layout.tsx",
|
|
935
|
+
"role": "react",
|
|
936
|
+
"bytes": 6002,
|
|
937
|
+
"integrity": "sha256-xUeKXphcaq5uFvfkci0bXbK3jzhh1QkRSSukH/BlG3k="
|
|
938
|
+
}
|
|
939
|
+
]
|
|
940
|
+
},
|
|
941
|
+
{
|
|
942
|
+
"id": "print-ready",
|
|
943
|
+
"kind": "2d",
|
|
944
|
+
"title": "Print ready",
|
|
945
|
+
"summary": "A compact desktop printer with a fresh sheet feeding through a clearly defined output slot.",
|
|
946
|
+
"tags": [
|
|
947
|
+
"print",
|
|
948
|
+
"documents",
|
|
949
|
+
"export"
|
|
950
|
+
],
|
|
951
|
+
"themeable": true,
|
|
952
|
+
"files": [
|
|
953
|
+
{
|
|
954
|
+
"path": "print-ready.svg",
|
|
955
|
+
"role": "source",
|
|
956
|
+
"bytes": 4240,
|
|
957
|
+
"integrity": "sha256-Z5LuX5KA6LA/slehxyi5lJi2sMSmiVw5oGnzKqKxNAw="
|
|
958
|
+
},
|
|
959
|
+
{
|
|
960
|
+
"path": "print-ready.tsx",
|
|
961
|
+
"role": "react",
|
|
962
|
+
"bytes": 5222,
|
|
963
|
+
"integrity": "sha256-WI61gBtD0xRJYGOQLOxyo5utgw8obTyYad4Rjbvqunc="
|
|
964
|
+
}
|
|
965
|
+
]
|
|
966
|
+
},
|
|
915
967
|
{
|
|
916
968
|
"id": "quick-calculation",
|
|
917
969
|
"kind": "2d",
|
|
@@ -938,6 +990,32 @@
|
|
|
938
990
|
}
|
|
939
991
|
]
|
|
940
992
|
},
|
|
993
|
+
{
|
|
994
|
+
"id": "quiet-workspace",
|
|
995
|
+
"kind": "2d",
|
|
996
|
+
"title": "Quiet workspace",
|
|
997
|
+
"summary": "An articulated task lamp beside an open notebook, giving a single piece of work room to breathe.",
|
|
998
|
+
"tags": [
|
|
999
|
+
"workspace",
|
|
1000
|
+
"focus",
|
|
1001
|
+
"notes"
|
|
1002
|
+
],
|
|
1003
|
+
"themeable": true,
|
|
1004
|
+
"files": [
|
|
1005
|
+
{
|
|
1006
|
+
"path": "quiet-workspace.svg",
|
|
1007
|
+
"role": "source",
|
|
1008
|
+
"bytes": 4306,
|
|
1009
|
+
"integrity": "sha256-nQ8z7c5LiW6GkN6gPJqFP6fdjf7ZtgTL0KdLJzi5FAo="
|
|
1010
|
+
},
|
|
1011
|
+
{
|
|
1012
|
+
"path": "quiet-workspace.tsx",
|
|
1013
|
+
"role": "react",
|
|
1014
|
+
"bytes": 5307,
|
|
1015
|
+
"integrity": "sha256-18G80gjKTkOyudwlynL4CgJz2IhMjRl9H2s7w5yeOno="
|
|
1016
|
+
}
|
|
1017
|
+
]
|
|
1018
|
+
},
|
|
941
1019
|
{
|
|
942
1020
|
"id": "responsive-layout",
|
|
943
1021
|
"kind": "2d",
|
|
@@ -1614,6 +1692,32 @@
|
|
|
1614
1692
|
}
|
|
1615
1693
|
]
|
|
1616
1694
|
},
|
|
1695
|
+
{
|
|
1696
|
+
"id": "measuring-tape",
|
|
1697
|
+
"kind": "3d",
|
|
1698
|
+
"title": "Measuring tape",
|
|
1699
|
+
"summary": "An enamel tape measure with a circular porcelain inset, evenly spaced marks and a folded metal end hook.",
|
|
1700
|
+
"tags": [
|
|
1701
|
+
"measure",
|
|
1702
|
+
"precision",
|
|
1703
|
+
"design"
|
|
1704
|
+
],
|
|
1705
|
+
"themeable": true,
|
|
1706
|
+
"files": [
|
|
1707
|
+
{
|
|
1708
|
+
"path": "measuring-tape.glb",
|
|
1709
|
+
"role": "source",
|
|
1710
|
+
"bytes": 592628,
|
|
1711
|
+
"integrity": "sha256-gjp8OVIjpEbmo0zeegc7p9W+gkYGeNqwETxuBfE43Js="
|
|
1712
|
+
},
|
|
1713
|
+
{
|
|
1714
|
+
"path": "measuring-tape-poster.webp",
|
|
1715
|
+
"role": "poster",
|
|
1716
|
+
"bytes": 16086,
|
|
1717
|
+
"integrity": "sha256-Ygzmwy/ttH5//dWsBUTsR+4XGQlD/YpodjI6y9caXuo="
|
|
1718
|
+
}
|
|
1719
|
+
]
|
|
1720
|
+
},
|
|
1617
1721
|
{
|
|
1618
1722
|
"id": "notebook",
|
|
1619
1723
|
"kind": "3d",
|
|
@@ -1718,6 +1822,32 @@
|
|
|
1718
1822
|
}
|
|
1719
1823
|
]
|
|
1720
1824
|
},
|
|
1825
|
+
{
|
|
1826
|
+
"id": "paper-printer",
|
|
1827
|
+
"kind": "3d",
|
|
1828
|
+
"title": "Paper printer",
|
|
1829
|
+
"summary": "A compact porcelain printer with a rear paper feed, inset output slot and a neatly printed sheet.",
|
|
1830
|
+
"tags": [
|
|
1831
|
+
"print",
|
|
1832
|
+
"documents",
|
|
1833
|
+
"workspace"
|
|
1834
|
+
],
|
|
1835
|
+
"themeable": true,
|
|
1836
|
+
"files": [
|
|
1837
|
+
{
|
|
1838
|
+
"path": "paper-printer.glb",
|
|
1839
|
+
"role": "source",
|
|
1840
|
+
"bytes": 457012,
|
|
1841
|
+
"integrity": "sha256-9sK7cA4R5ZYekDnTBrsZlUnYY4E/SUddQ/dCJGTtO9c="
|
|
1842
|
+
},
|
|
1843
|
+
{
|
|
1844
|
+
"path": "paper-printer-poster.webp",
|
|
1845
|
+
"role": "poster",
|
|
1846
|
+
"bytes": 16926,
|
|
1847
|
+
"integrity": "sha256-lRC0WaPykkZxEhtgEi4f0N93LFGoJ2uE0kfWbQOPR7Y="
|
|
1848
|
+
}
|
|
1849
|
+
]
|
|
1850
|
+
},
|
|
1721
1851
|
{
|
|
1722
1852
|
"id": "rocket",
|
|
1723
1853
|
"kind": "3d",
|
|
@@ -1796,6 +1926,32 @@
|
|
|
1796
1926
|
}
|
|
1797
1927
|
]
|
|
1798
1928
|
},
|
|
1929
|
+
{
|
|
1930
|
+
"id": "task-lamp",
|
|
1931
|
+
"kind": "3d",
|
|
1932
|
+
"title": "Task lamp",
|
|
1933
|
+
"summary": "An articulated desk lamp with aligned metal joints, an enamel light bar and a porcelain diffuser.",
|
|
1934
|
+
"tags": [
|
|
1935
|
+
"workspace",
|
|
1936
|
+
"focus",
|
|
1937
|
+
"lighting"
|
|
1938
|
+
],
|
|
1939
|
+
"themeable": true,
|
|
1940
|
+
"files": [
|
|
1941
|
+
{
|
|
1942
|
+
"path": "task-lamp.glb",
|
|
1943
|
+
"role": "source",
|
|
1944
|
+
"bytes": 283168,
|
|
1945
|
+
"integrity": "sha256-hAolZPXrmhkMW0DjUx/aB4iXTEBm3YFDnoTzOqsMtMc="
|
|
1946
|
+
},
|
|
1947
|
+
{
|
|
1948
|
+
"path": "task-lamp-poster.webp",
|
|
1949
|
+
"role": "poster",
|
|
1950
|
+
"bytes": 16676,
|
|
1951
|
+
"integrity": "sha256-KczARj6DGDO6U9YrYZ5LLTKCHK922mbXLvJtcmeMEfQ="
|
|
1952
|
+
}
|
|
1953
|
+
]
|
|
1954
|
+
},
|
|
1799
1955
|
{
|
|
1800
1956
|
"id": "usb-stick",
|
|
1801
1957
|
"kind": "3d",
|
package/registry/index.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "./schema/registry-index.schema.json",
|
|
3
3
|
"schemaVersion": 2,
|
|
4
4
|
"name": "mlola-ui",
|
|
5
|
-
"version": "1.0.
|
|
5
|
+
"version": "1.0.9",
|
|
6
6
|
"homepage": "https://ui.mlola.com",
|
|
7
7
|
"themeContract": {
|
|
8
8
|
"attribute": "data-theme",
|
|
@@ -177,8 +177,8 @@
|
|
|
177
177
|
},
|
|
178
178
|
"registryDependencies": [],
|
|
179
179
|
"engineDependencies": {
|
|
180
|
-
"@mlola-ui/behavior": "^1.0.
|
|
181
|
-
"@mlola-ui/icons": "^1.0.
|
|
180
|
+
"@mlola-ui/behavior": "^1.0.9",
|
|
181
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
182
182
|
},
|
|
183
183
|
"options": [
|
|
184
184
|
{
|
|
@@ -239,7 +239,7 @@
|
|
|
239
239
|
},
|
|
240
240
|
"registryDependencies": [],
|
|
241
241
|
"engineDependencies": {
|
|
242
|
-
"@mlola-ui/icons": "^1.0.
|
|
242
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
243
243
|
},
|
|
244
244
|
"options": [
|
|
245
245
|
{
|
|
@@ -393,7 +393,7 @@
|
|
|
393
393
|
},
|
|
394
394
|
"registryDependencies": [],
|
|
395
395
|
"engineDependencies": {
|
|
396
|
-
"@mlola-ui/icons": "^1.0.
|
|
396
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
397
397
|
},
|
|
398
398
|
"options": [
|
|
399
399
|
{
|
|
@@ -485,7 +485,7 @@
|
|
|
485
485
|
},
|
|
486
486
|
"registryDependencies": [],
|
|
487
487
|
"engineDependencies": {
|
|
488
|
-
"@mlola-ui/icons": "^1.0.
|
|
488
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
489
489
|
},
|
|
490
490
|
"options": [],
|
|
491
491
|
"variants": [],
|
|
@@ -538,7 +538,7 @@
|
|
|
538
538
|
"spinner"
|
|
539
539
|
],
|
|
540
540
|
"engineDependencies": {
|
|
541
|
-
"@mlola-ui/motion": "^1.0.
|
|
541
|
+
"@mlola-ui/motion": "^1.0.9"
|
|
542
542
|
},
|
|
543
543
|
"options": [
|
|
544
544
|
{
|
|
@@ -693,8 +693,8 @@
|
|
|
693
693
|
},
|
|
694
694
|
"registryDependencies": [],
|
|
695
695
|
"engineDependencies": {
|
|
696
|
-
"@mlola-ui/behavior": "^1.0.
|
|
697
|
-
"@mlola-ui/icons": "^1.0.
|
|
696
|
+
"@mlola-ui/behavior": "^1.0.9",
|
|
697
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
698
698
|
},
|
|
699
699
|
"options": [
|
|
700
700
|
{
|
|
@@ -756,7 +756,7 @@
|
|
|
756
756
|
},
|
|
757
757
|
"registryDependencies": [],
|
|
758
758
|
"engineDependencies": {
|
|
759
|
-
"@mlola-ui/icons": "^1.0.
|
|
759
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
760
760
|
},
|
|
761
761
|
"options": [],
|
|
762
762
|
"variants": [],
|
|
@@ -811,7 +811,7 @@
|
|
|
811
811
|
"modal"
|
|
812
812
|
],
|
|
813
813
|
"engineDependencies": {
|
|
814
|
-
"@mlola-ui/icons": "^1.0.
|
|
814
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
815
815
|
},
|
|
816
816
|
"options": [],
|
|
817
817
|
"variants": [],
|
|
@@ -874,8 +874,8 @@
|
|
|
874
874
|
},
|
|
875
875
|
"registryDependencies": [],
|
|
876
876
|
"engineDependencies": {
|
|
877
|
-
"@mlola-ui/behavior": "^1.0.
|
|
878
|
-
"@mlola-ui/icons": "^1.0.
|
|
877
|
+
"@mlola-ui/behavior": "^1.0.9",
|
|
878
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
879
879
|
},
|
|
880
880
|
"options": [
|
|
881
881
|
{
|
|
@@ -939,7 +939,7 @@
|
|
|
939
939
|
"input"
|
|
940
940
|
],
|
|
941
941
|
"engineDependencies": {
|
|
942
|
-
"@mlola-ui/icons": "^1.0.
|
|
942
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
943
943
|
},
|
|
944
944
|
"options": [],
|
|
945
945
|
"variants": [],
|
|
@@ -1163,8 +1163,8 @@
|
|
|
1163
1163
|
},
|
|
1164
1164
|
"registryDependencies": [],
|
|
1165
1165
|
"engineDependencies": {
|
|
1166
|
-
"@mlola-ui/behavior": "^1.0.
|
|
1167
|
-
"@mlola-ui/icons": "^1.0.
|
|
1166
|
+
"@mlola-ui/behavior": "^1.0.9",
|
|
1167
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
1168
1168
|
},
|
|
1169
1169
|
"options": [
|
|
1170
1170
|
{
|
|
@@ -1302,7 +1302,7 @@
|
|
|
1302
1302
|
},
|
|
1303
1303
|
"registryDependencies": [],
|
|
1304
1304
|
"engineDependencies": {
|
|
1305
|
-
"@mlola-ui/icons": "^1.0.
|
|
1305
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
1306
1306
|
},
|
|
1307
1307
|
"options": [],
|
|
1308
1308
|
"variants": [],
|
|
@@ -1353,7 +1353,7 @@
|
|
|
1353
1353
|
},
|
|
1354
1354
|
"registryDependencies": [],
|
|
1355
1355
|
"engineDependencies": {
|
|
1356
|
-
"@mlola-ui/behavior": "^1.0.
|
|
1356
|
+
"@mlola-ui/behavior": "^1.0.9"
|
|
1357
1357
|
},
|
|
1358
1358
|
"options": [
|
|
1359
1359
|
{
|
|
@@ -1722,7 +1722,7 @@
|
|
|
1722
1722
|
},
|
|
1723
1723
|
"registryDependencies": [],
|
|
1724
1724
|
"engineDependencies": {
|
|
1725
|
-
"@mlola-ui/behavior": "^1.0.
|
|
1725
|
+
"@mlola-ui/behavior": "^1.0.9"
|
|
1726
1726
|
},
|
|
1727
1727
|
"options": [],
|
|
1728
1728
|
"variants": [],
|
|
@@ -1773,8 +1773,8 @@
|
|
|
1773
1773
|
},
|
|
1774
1774
|
"registryDependencies": [],
|
|
1775
1775
|
"engineDependencies": {
|
|
1776
|
-
"@mlola-ui/behavior": "^1.0.
|
|
1777
|
-
"@mlola-ui/icons": "^1.0.
|
|
1776
|
+
"@mlola-ui/behavior": "^1.0.9",
|
|
1777
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
1778
1778
|
},
|
|
1779
1779
|
"options": [
|
|
1780
1780
|
{
|
|
@@ -1851,8 +1851,8 @@
|
|
|
1851
1851
|
},
|
|
1852
1852
|
"registryDependencies": [],
|
|
1853
1853
|
"engineDependencies": {
|
|
1854
|
-
"@mlola-ui/behavior": "^1.0.
|
|
1855
|
-
"@mlola-ui/icons": "^1.0.
|
|
1854
|
+
"@mlola-ui/behavior": "^1.0.9",
|
|
1855
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
1856
1856
|
},
|
|
1857
1857
|
"options": [
|
|
1858
1858
|
{
|
|
@@ -1984,7 +1984,7 @@
|
|
|
1984
1984
|
},
|
|
1985
1985
|
"registryDependencies": [],
|
|
1986
1986
|
"engineDependencies": {
|
|
1987
|
-
"@mlola-ui/behavior": "^1.0.
|
|
1987
|
+
"@mlola-ui/behavior": "^1.0.9"
|
|
1988
1988
|
},
|
|
1989
1989
|
"options": [
|
|
1990
1990
|
{
|
|
@@ -2129,7 +2129,7 @@
|
|
|
2129
2129
|
},
|
|
2130
2130
|
"registryDependencies": [],
|
|
2131
2131
|
"engineDependencies": {
|
|
2132
|
-
"@mlola-ui/behavior": "^1.0.
|
|
2132
|
+
"@mlola-ui/behavior": "^1.0.9"
|
|
2133
2133
|
},
|
|
2134
2134
|
"options": [
|
|
2135
2135
|
{
|
|
@@ -2255,7 +2255,7 @@
|
|
|
2255
2255
|
},
|
|
2256
2256
|
"registryDependencies": [],
|
|
2257
2257
|
"engineDependencies": {
|
|
2258
|
-
"@mlola-ui/icons": "^1.0.
|
|
2258
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
2259
2259
|
},
|
|
2260
2260
|
"options": [
|
|
2261
2261
|
{
|
|
@@ -2341,7 +2341,7 @@
|
|
|
2341
2341
|
"spinner"
|
|
2342
2342
|
],
|
|
2343
2343
|
"engineDependencies": {
|
|
2344
|
-
"@mlola-ui/icons": "^1.0.
|
|
2344
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
2345
2345
|
},
|
|
2346
2346
|
"options": [
|
|
2347
2347
|
{
|
|
@@ -2615,7 +2615,7 @@
|
|
|
2615
2615
|
},
|
|
2616
2616
|
"registryDependencies": [],
|
|
2617
2617
|
"engineDependencies": {
|
|
2618
|
-
"@mlola-ui/icons": "^1.0.
|
|
2618
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
2619
2619
|
},
|
|
2620
2620
|
"options": [
|
|
2621
2621
|
{
|
|
@@ -2687,7 +2687,7 @@
|
|
|
2687
2687
|
"popover"
|
|
2688
2688
|
],
|
|
2689
2689
|
"engineDependencies": {
|
|
2690
|
-
"@mlola-ui/icons": "^1.0.
|
|
2690
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
2691
2691
|
},
|
|
2692
2692
|
"options": [
|
|
2693
2693
|
{
|
|
@@ -2757,7 +2757,7 @@
|
|
|
2757
2757
|
"popover"
|
|
2758
2758
|
],
|
|
2759
2759
|
"engineDependencies": {
|
|
2760
|
-
"@mlola-ui/icons": "^1.0.
|
|
2760
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
2761
2761
|
},
|
|
2762
2762
|
"options": [],
|
|
2763
2763
|
"variants": [],
|
|
@@ -2955,7 +2955,7 @@
|
|
|
2955
2955
|
"input"
|
|
2956
2956
|
],
|
|
2957
2957
|
"engineDependencies": {
|
|
2958
|
-
"@mlola-ui/icons": "^1.0.
|
|
2958
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
2959
2959
|
},
|
|
2960
2960
|
"options": [
|
|
2961
2961
|
{
|
|
@@ -3032,7 +3032,7 @@
|
|
|
3032
3032
|
"input"
|
|
3033
3033
|
],
|
|
3034
3034
|
"engineDependencies": {
|
|
3035
|
-
"@mlola-ui/icons": "^1.0.
|
|
3035
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
3036
3036
|
},
|
|
3037
3037
|
"options": [],
|
|
3038
3038
|
"variants": [],
|
|
@@ -3087,7 +3087,7 @@
|
|
|
3087
3087
|
"input"
|
|
3088
3088
|
],
|
|
3089
3089
|
"engineDependencies": {
|
|
3090
|
-
"@mlola-ui/icons": "^1.0.
|
|
3090
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
3091
3091
|
},
|
|
3092
3092
|
"options": [],
|
|
3093
3093
|
"variants": [],
|
|
@@ -3150,7 +3150,7 @@
|
|
|
3150
3150
|
"popover"
|
|
3151
3151
|
],
|
|
3152
3152
|
"engineDependencies": {
|
|
3153
|
-
"@mlola-ui/icons": "^1.0.
|
|
3153
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
3154
3154
|
},
|
|
3155
3155
|
"options": [
|
|
3156
3156
|
{
|
|
@@ -3220,7 +3220,7 @@
|
|
|
3220
3220
|
},
|
|
3221
3221
|
"registryDependencies": [],
|
|
3222
3222
|
"engineDependencies": {
|
|
3223
|
-
"@mlola-ui/icons": "^1.0.
|
|
3223
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
3224
3224
|
},
|
|
3225
3225
|
"options": [
|
|
3226
3226
|
{
|
|
@@ -3339,8 +3339,8 @@
|
|
|
3339
3339
|
},
|
|
3340
3340
|
"registryDependencies": [],
|
|
3341
3341
|
"engineDependencies": {
|
|
3342
|
-
"@mlola-ui/behavior": "^1.0.
|
|
3343
|
-
"@mlola-ui/icons": "^1.0.
|
|
3342
|
+
"@mlola-ui/behavior": "^1.0.9",
|
|
3343
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
3344
3344
|
},
|
|
3345
3345
|
"options": [],
|
|
3346
3346
|
"variants": [],
|
|
@@ -3404,7 +3404,7 @@
|
|
|
3404
3404
|
},
|
|
3405
3405
|
"registryDependencies": [],
|
|
3406
3406
|
"engineDependencies": {
|
|
3407
|
-
"@mlola-ui/behavior": "^1.0.
|
|
3407
|
+
"@mlola-ui/behavior": "^1.0.9"
|
|
3408
3408
|
},
|
|
3409
3409
|
"options": [
|
|
3410
3410
|
{
|
|
@@ -3491,8 +3491,8 @@
|
|
|
3491
3491
|
},
|
|
3492
3492
|
"registryDependencies": [],
|
|
3493
3493
|
"engineDependencies": {
|
|
3494
|
-
"@mlola-ui/behavior": "^1.0.
|
|
3495
|
-
"@mlola-ui/icons": "^1.0.
|
|
3494
|
+
"@mlola-ui/behavior": "^1.0.9",
|
|
3495
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
3496
3496
|
},
|
|
3497
3497
|
"options": [],
|
|
3498
3498
|
"variants": [],
|
|
@@ -3560,7 +3560,7 @@
|
|
|
3560
3560
|
"sheet"
|
|
3561
3561
|
],
|
|
3562
3562
|
"engineDependencies": {
|
|
3563
|
-
"@mlola-ui/icons": "^1.0.
|
|
3563
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
3564
3564
|
},
|
|
3565
3565
|
"options": [],
|
|
3566
3566
|
"variants": [],
|
|
@@ -3677,7 +3677,7 @@
|
|
|
3677
3677
|
"button"
|
|
3678
3678
|
],
|
|
3679
3679
|
"engineDependencies": {
|
|
3680
|
-
"@mlola-ui/icons": "^1.0.
|
|
3680
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
3681
3681
|
},
|
|
3682
3682
|
"options": [
|
|
3683
3683
|
{
|
|
@@ -3772,8 +3772,8 @@
|
|
|
3772
3772
|
"input"
|
|
3773
3773
|
],
|
|
3774
3774
|
"engineDependencies": {
|
|
3775
|
-
"@mlola-ui/behavior": "^1.0.
|
|
3776
|
-
"@mlola-ui/icons": "^1.0.
|
|
3775
|
+
"@mlola-ui/behavior": "^1.0.9",
|
|
3776
|
+
"@mlola-ui/icons": "^1.0.9"
|
|
3777
3777
|
},
|
|
3778
3778
|
"options": [
|
|
3779
3779
|
{
|
package/src/cli.js
CHANGED
|
@@ -2,7 +2,6 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
4
|
import {
|
|
5
|
-
DEFAULT_CONFIG,
|
|
6
5
|
loadConfig,
|
|
7
6
|
targetRoot,
|
|
8
7
|
writeDefaultConfig,
|
|
@@ -19,10 +18,10 @@ import { pullTheme } from "./theme-pull.js";
|
|
|
19
18
|
const HELP = `Mlola UI — source-copy components with Theme
|
|
20
19
|
|
|
21
20
|
Usage:
|
|
22
|
-
mlola-ui init [--force] [--no-agents]
|
|
21
|
+
mlola-ui init [--force] [--no-agents] [--no-install]
|
|
23
22
|
mlola-ui agents
|
|
24
23
|
mlola-ui mcp
|
|
25
|
-
mlola-ui add <name...> [--overwrite] [--install
|
|
24
|
+
mlola-ui add <name...> [--overwrite] [--no-install]
|
|
26
25
|
mlola-ui add asset <id...> [--overwrite]
|
|
27
26
|
mlola-ui login <token> [--host <url>]
|
|
28
27
|
mlola-ui logout
|
|
@@ -80,17 +79,34 @@ function packageManager(cwd) {
|
|
|
80
79
|
return ["npm", ["install"]];
|
|
81
80
|
}
|
|
82
81
|
|
|
83
|
-
function
|
|
84
|
-
|
|
82
|
+
function declaredPackages(cwd) {
|
|
83
|
+
const filename = path.join(cwd, "package.json");
|
|
84
|
+
if (!fs.existsSync(filename)) return null;
|
|
85
|
+
const manifest = JSON.parse(fs.readFileSync(filename, "utf8"));
|
|
86
|
+
return new Set(Object.keys({ ...manifest.dependencies, ...manifest.devDependencies, ...manifest.peerDependencies }));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Installs the packages the copied code imports and package.json does not
|
|
90
|
+
// declare yet, with the project's own package manager. Without a package.json
|
|
91
|
+
// (a plain HTML site) or with --no-install it only names them.
|
|
92
|
+
function ensurePackages(cwd, dependencies, { skip, output, installer }) {
|
|
93
|
+
const declared = declaredPackages(cwd);
|
|
94
|
+
const missing = [...dependencies].filter(([name]) => !declared?.has(name)).map(([name, range]) => `${name}@${range}`);
|
|
95
|
+
if (!missing.length) return 0;
|
|
85
96
|
const [command, baseArgs] = packageManager(cwd);
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
97
|
+
const line = `${command} ${[...baseArgs, ...missing].join(" ")}`;
|
|
98
|
+
if (skip || !declared) {
|
|
99
|
+
output.log(`Install what this needs: ${line}`);
|
|
100
|
+
return 0;
|
|
101
|
+
}
|
|
102
|
+
output.log(`Installing ${missing.join(", ")} with ${command}...`);
|
|
103
|
+
const result = installer(command, [...baseArgs, ...missing], cwd);
|
|
104
|
+
if (result.error || result.status !== 0) {
|
|
105
|
+
output.error(`✗ ${command} could not install them${result.error ? ` (${result.error.message})` : ""}. Run it yourself: ${line}`);
|
|
91
106
|
return 1;
|
|
92
107
|
}
|
|
93
|
-
|
|
108
|
+
output.log(`✓ Installed ${missing.join(", ")}`);
|
|
109
|
+
return 0;
|
|
94
110
|
}
|
|
95
111
|
|
|
96
112
|
export async function run(argv, options = {}) {
|
|
@@ -99,6 +115,14 @@ export async function run(argv, options = {}) {
|
|
|
99
115
|
const env = options.env ?? process.env;
|
|
100
116
|
const fetcher = options.fetch ?? fetch;
|
|
101
117
|
const [command = "help", ...args] = argv;
|
|
118
|
+
const install = {
|
|
119
|
+
skip: argv.includes("--no-install"),
|
|
120
|
+
output,
|
|
121
|
+
installer:
|
|
122
|
+
options.installer ??
|
|
123
|
+
((name, installArgs, directory) =>
|
|
124
|
+
spawnSync(name, installArgs, { cwd: directory, stdio: options.installStdio ?? "inherit", shell: process.platform === "win32" })),
|
|
125
|
+
};
|
|
102
126
|
|
|
103
127
|
try {
|
|
104
128
|
if (command === "help" || command === "--help" || command === "-h") {
|
|
@@ -109,7 +133,7 @@ export async function run(argv, options = {}) {
|
|
|
109
133
|
if (command === "init") {
|
|
110
134
|
const force = hasFlag(args, "--force");
|
|
111
135
|
const configResult = writeDefaultConfig(cwd, { force });
|
|
112
|
-
const config = configResult.created ?
|
|
136
|
+
const config = configResult.created ? configResult.config : loadConfig(cwd);
|
|
113
137
|
const stylesResult = writeStyles(cwd, config, force);
|
|
114
138
|
output.log(
|
|
115
139
|
configResult.created
|
|
@@ -124,11 +148,8 @@ export async function run(argv, options = {}) {
|
|
|
124
148
|
output.log(
|
|
125
149
|
`Set data-theme="${config.theme}" on your document root and import ${stylesResult.relative}.`,
|
|
126
150
|
);
|
|
127
|
-
output.log(
|
|
128
|
-
`Install the engines you use, starting with: npm install ${config.engine.engine ?? "@mlola-ui/engine"} ${config.engine.motion}`,
|
|
129
|
-
);
|
|
130
151
|
if (!hasFlag(args, "--no-agents")) reportAgents(writeAgentFiles(cwd), output);
|
|
131
|
-
return
|
|
152
|
+
return ensurePackages(cwd, new Map([[config.engine.engine ?? "@mlola-ui/engine", `^${CLI_VERSION}`]]), install);
|
|
132
153
|
}
|
|
133
154
|
|
|
134
155
|
if (command === "agents") {
|
|
@@ -273,18 +294,9 @@ export async function run(argv, options = {}) {
|
|
|
273
294
|
if (styleResult.index) output.log(`Pro styles are gathered in ${styleResult.index}; import it once, after the engine stylesheet.`);
|
|
274
295
|
if (guideResult.status === "written") output.log(`Coding agents: point your AGENTS.md at ${GUIDE_FILENAME}, which lists every Pro class and attribute.`);
|
|
275
296
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
280
|
-
if (engines.size) {
|
|
281
|
-
output.log(
|
|
282
|
-
`Engine dependencies: ${[...engines]
|
|
283
|
-
.map(([name, range]) => `${name}@${range}`)
|
|
284
|
-
.join(" ")}`,
|
|
285
|
-
);
|
|
286
|
-
}
|
|
287
|
-
return 0;
|
|
297
|
+
// The stylesheet from init imports the engine, so it is always needed.
|
|
298
|
+
const engines = new Map([[config.engine.engine ?? "@mlola-ui/engine", `^${CLI_VERSION}`], ...collectEngineDependencies(items)]);
|
|
299
|
+
return ensurePackages(cwd, engines, install);
|
|
288
300
|
}
|
|
289
301
|
|
|
290
302
|
if (command === "theme") {
|
package/src/config.js
CHANGED
|
@@ -51,6 +51,9 @@ export function validateConfig(config) {
|
|
|
51
51
|
errors.push(`aliases.${key} must be a non-empty string`);
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
+
if (config.imports !== undefined && !["alias", "relative"].includes(config.imports)) {
|
|
55
|
+
errors.push(`imports must be "alias" or "relative" (received ${JSON.stringify(config.imports)})`);
|
|
56
|
+
}
|
|
54
57
|
if (!config.engine || typeof config.engine !== "object") {
|
|
55
58
|
errors.push("engine mappings are required");
|
|
56
59
|
}
|
|
@@ -105,11 +108,57 @@ export function interpolateTarget(template, config, mode = "filesystem") {
|
|
|
105
108
|
return `${base.replace(/\/$/, "")}/${remainder}`;
|
|
106
109
|
}
|
|
107
110
|
|
|
111
|
+
/** Reads a tsconfig or jsconfig, which allow comments and trailing commas. */
|
|
112
|
+
function readJsonc(filename) {
|
|
113
|
+
const text = fs
|
|
114
|
+
.readFileSync(filename, "utf8")
|
|
115
|
+
.replace(/("(?:\\.|[^"\\])*")|\/\/[^\n]*|\/\*[\s\S]*?\*\//g, (match, string) => string ?? "")
|
|
116
|
+
.replace(/,(\s*[}\]])/g, "$1");
|
|
117
|
+
return JSON.parse(text);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** The folder "@/" points at in the project's TypeScript or JavaScript config, or null. */
|
|
121
|
+
function aliasRoot(cwd) {
|
|
122
|
+
for (const name of ["tsconfig.json", "tsconfig.app.json", "jsconfig.json"]) {
|
|
123
|
+
const filename = path.join(cwd, name);
|
|
124
|
+
if (!fs.existsSync(filename)) continue;
|
|
125
|
+
let options;
|
|
126
|
+
try {
|
|
127
|
+
options = readJsonc(filename).compilerOptions;
|
|
128
|
+
} catch {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const target = options?.paths?.["@/*"]?.[0];
|
|
132
|
+
if (typeof target !== "string" || !target.endsWith("/*")) continue;
|
|
133
|
+
const root = path.posix.normalize(path.posix.join(options.baseUrl ?? ".", target.slice(0, -2)));
|
|
134
|
+
if (root.startsWith("..")) continue;
|
|
135
|
+
return root === "." ? "" : root;
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* A configuration that fits the project as it is: files go where "@/" points,
|
|
142
|
+
* or into src/ when there is one, and without an "@/" alias (a new Vite app)
|
|
143
|
+
* the copied files import each other by relative path, so nothing needs
|
|
144
|
+
* setting up first.
|
|
145
|
+
*/
|
|
146
|
+
export function detectConfig(cwd) {
|
|
147
|
+
const root = aliasRoot(cwd);
|
|
148
|
+
const base = root ?? (fs.existsSync(path.join(cwd, "src")) ? "src" : "");
|
|
149
|
+
const targets = Object.fromEntries(
|
|
150
|
+
Object.entries(DEFAULT_CONFIG.targets).map(([key, value]) => [key, key === "assets" || !base ? value : `${base}/${value}`]),
|
|
151
|
+
);
|
|
152
|
+
const { $schema, version, theme, aliases, engine } = DEFAULT_CONFIG;
|
|
153
|
+
return { $schema, version, theme, ...(root === null ? { imports: "relative" } : {}), aliases, targets, engine };
|
|
154
|
+
}
|
|
155
|
+
|
|
108
156
|
export function writeDefaultConfig(cwd, { force = false } = {}) {
|
|
109
157
|
const filename = configPath(cwd);
|
|
110
158
|
if (fs.existsSync(filename) && !force) {
|
|
111
159
|
return { filename, created: false };
|
|
112
160
|
}
|
|
113
|
-
|
|
114
|
-
|
|
161
|
+
const config = detectConfig(cwd);
|
|
162
|
+
fs.writeFileSync(filename, `${JSON.stringify(config, null, 2)}\n`);
|
|
163
|
+
return { filename, created: true, config };
|
|
115
164
|
}
|
package/src/doctor.js
CHANGED
|
@@ -4,14 +4,6 @@ import { interpolateTarget, loadConfig, targetRoot } from "./config.js";
|
|
|
4
4
|
import { createTransformContext, expectedFile } from "./installer.js";
|
|
5
5
|
import { loadRegistry, readJson, sha256, sourcePath } from "./registry.js";
|
|
6
6
|
|
|
7
|
-
const FORBIDDEN_DEPENDENCIES = [
|
|
8
|
-
"clsx",
|
|
9
|
-
"tailwind-merge",
|
|
10
|
-
"tailwindcss",
|
|
11
|
-
"@tailwindcss/postcss",
|
|
12
|
-
"lucide-react",
|
|
13
|
-
];
|
|
14
|
-
|
|
15
7
|
function finding(level, message) {
|
|
16
8
|
return { level, message };
|
|
17
9
|
}
|
|
@@ -28,6 +20,9 @@ function projectDependencies(cwd) {
|
|
|
28
20
|
}
|
|
29
21
|
|
|
30
22
|
function projectContains(cwd, pattern) {
|
|
23
|
+
// A Vite app sets the theme in index.html at the project root.
|
|
24
|
+
const page = path.join(cwd, "index.html");
|
|
25
|
+
if (fs.existsSync(page) && pattern.test(fs.readFileSync(page, "utf8"))) return true;
|
|
31
26
|
const roots = ["app", "src", "pages"]
|
|
32
27
|
.map((directory) => path.join(cwd, directory))
|
|
33
28
|
.filter((directory) => fs.existsSync(directory));
|
|
@@ -80,24 +75,25 @@ export function runDoctor(cwd) {
|
|
|
80
75
|
}
|
|
81
76
|
|
|
82
77
|
const dependencies = projectDependencies(cwd);
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
78
|
+
const context = createTransformContext(registry, config);
|
|
79
|
+
const installed = registry.items.filter((item) => {
|
|
80
|
+
const primary = item.files.find((file) => file.path.endsWith(`/${item.name}/${item.name}.tsx`));
|
|
81
|
+
return primary && fs.existsSync(path.join(cwd, interpolateTarget(primary.target, config)));
|
|
82
|
+
});
|
|
89
83
|
|
|
90
|
-
|
|
91
|
-
|
|
84
|
+
// What the stylesheet and the installed items import, not the whole registry.
|
|
85
|
+
const requiredEngines = new Set([config.engine.engine ?? "@mlola-ui/engine"]);
|
|
86
|
+
for (const item of installed) {
|
|
92
87
|
for (const name of Object.keys(item.engineDependencies)) requiredEngines.add(name);
|
|
93
88
|
}
|
|
94
89
|
const missingEngines = [...requiredEngines].filter((name) => !dependencies[name]);
|
|
95
90
|
if (missingEngines.length) {
|
|
96
|
-
findings.push(
|
|
97
|
-
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
|
|
91
|
+
findings.push(finding("warn", `packages the installed items import are not declared: ${missingEngines.join(", ")}`));
|
|
92
|
+
} else {
|
|
93
|
+
findings.push(finding("pass", "the packages the installed items import are declared"));
|
|
94
|
+
}
|
|
95
|
+
if (dependencies.tailwindcss) {
|
|
96
|
+
findings.push(finding("pass", "Tailwind is declared; Mlola's layers sit above its reset, so the two work together"));
|
|
101
97
|
}
|
|
102
98
|
if (!dependencies.react) findings.push(finding("warn", "React is not declared in package.json"));
|
|
103
99
|
if (!dependencies["react-dom"]) findings.push(finding("warn", "ReactDOM is not declared in package.json"));
|
|
@@ -124,17 +120,8 @@ export function runDoctor(cwd) {
|
|
|
124
120
|
);
|
|
125
121
|
}
|
|
126
122
|
|
|
127
|
-
const context = createTransformContext(registry, config);
|
|
128
|
-
let installedItems = 0;
|
|
129
123
|
let modifiedFiles = 0;
|
|
130
|
-
for (const item of
|
|
131
|
-
const primary = item.files.find((file) =>
|
|
132
|
-
file.path.endsWith(`/${item.name}/${item.name}.tsx`),
|
|
133
|
-
);
|
|
134
|
-
if (!primary) continue;
|
|
135
|
-
const primaryDestination = path.join(cwd, interpolateTarget(primary.target, config));
|
|
136
|
-
if (!fs.existsSync(primaryDestination)) continue;
|
|
137
|
-
installedItems += 1;
|
|
124
|
+
for (const item of installed) {
|
|
138
125
|
for (const file of item.files) {
|
|
139
126
|
const destination = path.join(cwd, interpolateTarget(file.target, config));
|
|
140
127
|
if (!fs.existsSync(destination) || fs.readFileSync(destination, "utf8") !== expectedFile(file, context)) {
|
|
@@ -150,7 +137,7 @@ export function runDoctor(cwd) {
|
|
|
150
137
|
),
|
|
151
138
|
);
|
|
152
139
|
} else {
|
|
153
|
-
findings.push(finding("pass", `${
|
|
140
|
+
findings.push(finding("pass", `${installed.length} installed item(s) match the registry snapshot`));
|
|
154
141
|
}
|
|
155
142
|
return findings;
|
|
156
143
|
}
|
package/src/installer.js
CHANGED
|
@@ -26,6 +26,11 @@ function withoutSourceExtension(value) {
|
|
|
26
26
|
return clean.endsWith("/index") ? clean.slice(0, -"/index".length) : clean;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
function relativeImport(from, to) {
|
|
30
|
+
const relative = path.posix.relative(path.posix.dirname(from), to);
|
|
31
|
+
return relative.startsWith(".") ? relative : `./${relative}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
29
34
|
export function createTransformContext(registry, config) {
|
|
30
35
|
const filesBySource = new Map();
|
|
31
36
|
for (const item of registry.items) {
|
|
@@ -43,10 +48,11 @@ export function transformSource(content, sourceFile, context) {
|
|
|
43
48
|
);
|
|
44
49
|
if (importedSource) {
|
|
45
50
|
const importedFile = context.filesBySource.get(importedSource);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
const target =
|
|
52
|
+
context.config.imports === "relative"
|
|
53
|
+
? relativeImport(interpolateTarget(sourceFile.target, context.config), interpolateTarget(importedFile.target, context.config))
|
|
54
|
+
: interpolateTarget(importedFile.target, context.config, "import");
|
|
55
|
+
replacements.set(specifier, withoutSourceExtension(target));
|
|
50
56
|
}
|
|
51
57
|
continue;
|
|
52
58
|
}
|
package/src/mcp.js
CHANGED
|
@@ -29,7 +29,9 @@ function tools({ cwd, run }) {
|
|
|
29
29
|
const capture = async (argv) => {
|
|
30
30
|
const lines = [];
|
|
31
31
|
const output = { log: (...parts) => lines.push(parts.join(" ")), error: (...parts) => lines.push(parts.join(" ")) };
|
|
32
|
-
|
|
32
|
+
// stdout is this server's protocol channel: a package manager started by
|
|
33
|
+
// init or add writes to stderr and reads nothing.
|
|
34
|
+
const code = await run(argv, { cwd, output, installStdio: ["ignore", 2, 2] });
|
|
33
35
|
return { code, output: lines.join("\n") };
|
|
34
36
|
};
|
|
35
37
|
return [
|
|
@@ -101,7 +103,7 @@ function tools({ cwd, run }) {
|
|
|
101
103
|
{
|
|
102
104
|
name: "add_components",
|
|
103
105
|
title: "Install Mlola components",
|
|
104
|
-
description: "Copies components into the project with the Mlola CLI (npx mlola-ui add), with their dependencies and styles. Pro items need a login first.",
|
|
106
|
+
description: "Copies components into the project with the Mlola CLI (npx mlola-ui add), with their dependencies and styles, and installs the npm packages they import. Pro items need a login first.",
|
|
105
107
|
inputSchema: {
|
|
106
108
|
type: "object",
|
|
107
109
|
properties: { names: { type: "array", items: { type: "string" }, minItems: 1 }, overwrite: { type: "boolean", default: false } },
|
|
@@ -117,7 +119,7 @@ function tools({ cwd, run }) {
|
|
|
117
119
|
{
|
|
118
120
|
name: "init_project",
|
|
119
121
|
title: "Set a project up for Mlola",
|
|
120
|
-
description: "Runs npx mlola-ui init: writes mlola.config.json, the stylesheet entry and the agent instructions (AGENTS.md, the design guide, MCP config).",
|
|
122
|
+
description: "Runs npx mlola-ui init: writes mlola.config.json, the stylesheet entry and the agent instructions (AGENTS.md, the design guide, MCP config), and installs @mlola-ui/engine.",
|
|
121
123
|
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
122
124
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true },
|
|
123
125
|
handler: async () => {
|