mlola-ui 1.0.1 → 1.0.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 +1 -1
- package/package.json +1 -1
- package/registry/assets.json +578 -0
- package/registry/index.json +65 -65
- package/registry/source/packages/components/alert/alert.tsx +1 -1
- package/registry/source/packages/components/badge/badge.tsx +2 -2
- package/registry/source/packages/components/breadcrumb/breadcrumb.tsx +1 -1
- package/registry/source/packages/components/carousel/carousel.tsx +1 -0
- package/registry/source/packages/components/checkbox/checkbox.tsx +3 -2
- package/registry/source/packages/components/color-picker/color-picker.tsx +15 -13
- package/registry/source/packages/components/color-picker/color.ts +1 -1
- package/registry/source/packages/components/combobox/combobox.tsx +33 -6
- package/registry/source/packages/components/date-picker/date-picker.tsx +5 -3
- package/registry/source/packages/components/dropzone/dropzone.tsx +1 -1
- package/registry/source/packages/components/empty-state/empty-state.tsx +1 -1
- package/registry/source/packages/components/hover-card/hover-card.tsx +1 -1
- package/registry/source/packages/components/number-input/number-input.tsx +6 -5
- package/registry/source/packages/components/otp-input/otp-input.tsx +6 -4
- package/registry/source/packages/components/progress/progress.tsx +1 -1
- package/registry/source/packages/components/segmented-control/segmented-control.tsx +5 -3
- package/registry/source/packages/components/select/select.tsx +6 -5
- package/registry/source/packages/components/tag-input/tag-input.tsx +7 -6
- package/registry/source/packages/components/textarea/textarea.tsx +1 -1
- package/registry/source/packages/components/time-picker/time-picker.tsx +8 -5
- package/registry/source/packages/components/timeline/timeline.tsx +1 -1
- package/registry/source/packages/components/toast/toast.tsx +1 -1
- package/registry/source/packages/components/toggle/toggle.tsx +1 -0
- package/registry/source/packages/components/tour/tour.tsx +3 -3
- package/registry/source-manifest.json +24 -24
- package/src/assets.js +90 -0
- package/src/cli.js +39 -5
- package/src/config.js +1 -0
- package/src/pro.js +1 -1
|
@@ -8,43 +8,43 @@
|
|
|
8
8
|
"packages/components/accordion/accordion.tsx": "sha256-0ALaLJd1fvj8Vbx86HLBKm3qUItrtwbrGJTykVfNSMg=",
|
|
9
9
|
"packages/components/activity-heatmap/activity-heatmap.tsx": "sha256-fMGDlXKpOfIOy0hmbpR8hKrbbN3fC386tXK27A21ZwM=",
|
|
10
10
|
"packages/components/activity-heatmap/calendar.ts": "sha256-NB+AjXY5Xc+KzR8MMTRBgZxdzOeoHhkW0KQOW7WJPuQ=",
|
|
11
|
-
"packages/components/alert/alert.tsx": "sha256-
|
|
11
|
+
"packages/components/alert/alert.tsx": "sha256-j680akXvCIh4MTVnUo79acvVrv51XyQ3UBJ5FBAy8Dw=",
|
|
12
12
|
"packages/components/app-shell/app-shell.tsx": "sha256-XAq82XOTH3ZR+hxTkYFu8P7Ih2aI6ERy/TpXc2VMUwM=",
|
|
13
13
|
"packages/components/avatar/avatar.tsx": "sha256-hvZUGhatN95ZyX6p6NBhTRf6h1/rrJ+bJrqBMtIspq0=",
|
|
14
|
-
"packages/components/badge/badge.tsx": "sha256
|
|
15
|
-
"packages/components/breadcrumb/breadcrumb.tsx": "sha256-
|
|
14
|
+
"packages/components/badge/badge.tsx": "sha256-flEH8S2jZ/64n3Y/ROhC9+9rvF7wXaN0xLcMMwqNOVQ=",
|
|
15
|
+
"packages/components/breadcrumb/breadcrumb.tsx": "sha256-Mt9mvmwwDgdp5/+mj5byIrh8+hicsp7R6UcvUlK0yog=",
|
|
16
16
|
"packages/components/button/button.tsx": "sha256-khqYxcV3hlZ+Q+cYNTRXEpAfIijDFSvkuDJXMPtlk7g=",
|
|
17
17
|
"packages/components/calendar/calendar.tsx": "sha256-uE22LI4jpU8ErR/oBmAkeKiu+Rp0bz3IXZVCurycpwc=",
|
|
18
18
|
"packages/components/calendar/dates.ts": "sha256-mygYkoC70av2ZQ0vkxf885trwW97Wd10I4dptYQe+4A=",
|
|
19
19
|
"packages/components/card/card.tsx": "sha256-aMKbv5DWYYWq7GZAFR9O1fuYE0H61WCjkvEbRMOd894=",
|
|
20
|
-
"packages/components/carousel/carousel.tsx": "sha256-
|
|
21
|
-
"packages/components/checkbox/checkbox.tsx": "sha256-
|
|
22
|
-
"packages/components/color-picker/color-picker.tsx": "sha256-
|
|
23
|
-
"packages/components/color-picker/color.ts": "sha256-
|
|
24
|
-
"packages/components/combobox/combobox.tsx": "sha256-
|
|
20
|
+
"packages/components/carousel/carousel.tsx": "sha256-pamugpB6Ujt9Ylxd37jNF/ZwSb8iRkW/MoYe12nY3i4=",
|
|
21
|
+
"packages/components/checkbox/checkbox.tsx": "sha256-hz4YbGwOxv/aJpJHrLXR4MKSWJGyjzoJEIGvdhyML9M=",
|
|
22
|
+
"packages/components/color-picker/color-picker.tsx": "sha256-E+QB6MbSxg+3AZX3A7bOkst4arxgpmFL2ZmochoWCl8=",
|
|
23
|
+
"packages/components/color-picker/color.ts": "sha256-YqSgYjzn7XfPWAHuyzbwOhbg/ScaSfB7V73e9w+OjJs=",
|
|
24
|
+
"packages/components/combobox/combobox.tsx": "sha256-CvuYc/hJoS7yw6QM9vv1SIkwflx0Kwd95UjchtC+AOs=",
|
|
25
25
|
"packages/components/command-menu/command-menu.tsx": "sha256-mF1z92yrPY609pjx5Dt1l5EhGHl9EWV8F8Sq0hyJRw4=",
|
|
26
26
|
"packages/components/context-menu/context-menu.tsx": "sha256-YgYsIzvLGFcJG1XX5zFJwrid5Z1Kn0lyWVzmLKmbMVU=",
|
|
27
|
-
"packages/components/date-picker/date-picker.tsx": "sha256-
|
|
27
|
+
"packages/components/date-picker/date-picker.tsx": "sha256-rsmDweD3RVfKTXR2i+/pnTb/MsrDAZXmHAYBvcUGxig=",
|
|
28
28
|
"packages/components/dropdown-menu/dropdown-menu.tsx": "sha256-xgybCu9HBxIieafu5CjAtY4aCOQoaAj/VUCH6D6vLP8=",
|
|
29
|
-
"packages/components/dropzone/dropzone.tsx": "sha256-
|
|
30
|
-
"packages/components/empty-state/empty-state.tsx": "sha256-
|
|
31
|
-
"packages/components/hover-card/hover-card.tsx": "sha256-
|
|
29
|
+
"packages/components/dropzone/dropzone.tsx": "sha256-tHCCpEWE6839UeLOszTo+R2vAL4Sp9UxuRdLkFLfnq4=",
|
|
30
|
+
"packages/components/empty-state/empty-state.tsx": "sha256-uCKqYpr9vkozXZHpdl4GFtdX9ijcGOriCHvgyarBqRw=",
|
|
31
|
+
"packages/components/hover-card/hover-card.tsx": "sha256-AyRbxfqDOYBgGlgjgrtQZm5DipArLEzQn6XnzjPd0ro=",
|
|
32
32
|
"packages/components/input/input.tsx": "sha256-lbbheo+ydQTzyujTf0W7DD96TogOjifCyqj9c3h3W0g=",
|
|
33
33
|
"packages/components/kbd/kbd.tsx": "sha256-wrPqzn1e82zJYoKzO7u94Bgq4+nPc0DXLc8GJ/4/3nY=",
|
|
34
34
|
"packages/components/modal/modal.tsx": "sha256-vybxgBGYCrMYvJRnO1wDAc2VFQylu+mQIf5OZIcabX4=",
|
|
35
|
-
"packages/components/number-input/number-input.tsx": "sha256-
|
|
35
|
+
"packages/components/number-input/number-input.tsx": "sha256-BDOyvCNtMULD/HK7uWHsYbGg79lrRDrVqtGQQrYHpmM=",
|
|
36
36
|
"packages/components/number-input/number.ts": "sha256-IrHCQrEhI5ein9ifMFuSViSoxQUBoT3tNmmCu/paGGM=",
|
|
37
|
-
"packages/components/otp-input/otp-input.tsx": "sha256-
|
|
37
|
+
"packages/components/otp-input/otp-input.tsx": "sha256-QURn8g5HqCQXBTdeENPbVxoPlqs3ZtoAPLRlFjQDxmI=",
|
|
38
38
|
"packages/components/pagination/pagination.tsx": "sha256-FBwk8tq4JcsK2D0dlWEYiDSRx3owgBmGzEIlVRaNBoE=",
|
|
39
39
|
"packages/components/popover/popover.tsx": "sha256-/kTiAuFklU4IuP97XaiCUGdEpKtNyMPLPS359mfzPHw=",
|
|
40
40
|
"packages/components/priority-icon/labels.ts": "sha256-zhmJIb7/gS16wU+xCJ7hkReBi9q5RI3SMwdjkJvvvKU=",
|
|
41
41
|
"packages/components/priority-icon/priority-icon.tsx": "sha256-3D6hFRpht3YuWouLhakE2DFdldavmJx0B/povEDwrtQ=",
|
|
42
|
-
"packages/components/progress/progress.tsx": "sha256-
|
|
42
|
+
"packages/components/progress/progress.tsx": "sha256-9OjecRNPWn7Bfe+C5IyXaKsW1NJae6X5gco1o0X5XkE=",
|
|
43
43
|
"packages/components/radio-group/radio-group.tsx": "sha256-jhnSEBLkdpe8dv5us/Q1sMsQF4sKGRgDXWGKm2JSFew=",
|
|
44
44
|
"packages/components/rating/rating.tsx": "sha256-zgPOQXzAiRE4qGNPQ8qat3SP1XSI/7567kA5VaT51P0=",
|
|
45
45
|
"packages/components/resizable/resizable.tsx": "sha256-40sK2UyIR9Ek72KAu5nvbr44tKEY6JhC5Ftb++Wf6cI=",
|
|
46
|
-
"packages/components/segmented-control/segmented-control.tsx": "sha256-
|
|
47
|
-
"packages/components/select/select.tsx": "sha256-
|
|
46
|
+
"packages/components/segmented-control/segmented-control.tsx": "sha256-MCBG5XHFWCwbACctsYKbqe8mvSSR6pf60tyRgdCDKDA=",
|
|
47
|
+
"packages/components/select/select.tsx": "sha256-m8QmKUGC4386XcklosSntal0XwUAFjKosTBQSF0dduQ=",
|
|
48
48
|
"packages/components/sheet/sheet.tsx": "sha256-EIn3OeRDmXGHbYf/uihRh8aw3LmzarIFiyC9DUsL1jg=",
|
|
49
49
|
"packages/components/skeleton/skeleton.tsx": "sha256-2Fk72NbLr3+CrXH7jGbHNsc3t+vskMqSyySe/UwbGCs=",
|
|
50
50
|
"packages/components/slider/slider.tsx": "sha256-l+VrBqPYHIIcemiVixxwDxNDB0HEMmYRZekx/H5HroQ=",
|
|
@@ -53,15 +53,15 @@
|
|
|
53
53
|
"packages/components/stepper/stepper.tsx": "sha256-uI/dRmkbijlixHiGUceWcpaDzpEixrzQ1UTmoPbGZAY=",
|
|
54
54
|
"packages/components/table/table.tsx": "sha256-ed1Qn2sIpxRvGdPDAb5ih5AcXr5PmHTHdlrJLYDYRp8=",
|
|
55
55
|
"packages/components/tabs/tabs.tsx": "sha256-kWAQjE9mshqZL7V3NJTUiurp6M5Nu3vx5O1LsIgdV5Y=",
|
|
56
|
-
"packages/components/tag-input/tag-input.tsx": "sha256
|
|
57
|
-
"packages/components/textarea/textarea.tsx": "sha256-
|
|
58
|
-
"packages/components/time-picker/time-picker.tsx": "sha256-
|
|
56
|
+
"packages/components/tag-input/tag-input.tsx": "sha256-EK7xJTe3Y/S5Kn3vS0zBQg9lC+RVoGvky6HOAt+DnAw=",
|
|
57
|
+
"packages/components/textarea/textarea.tsx": "sha256-C9jMYZ63w+m6bgdLvHjvULRuoCe26q5zbgS4+ahWzr8=",
|
|
58
|
+
"packages/components/time-picker/time-picker.tsx": "sha256-og8Cbf+e8oFYewfagUgjRzLHSIIlAZh+1OpAn9yNq70=",
|
|
59
59
|
"packages/components/time-picker/times.ts": "sha256-V/4uqFZPn9gVnyx8cB9vwp/RocmTYaPmLxjfy8DPQOI=",
|
|
60
|
-
"packages/components/timeline/timeline.tsx": "sha256-
|
|
61
|
-
"packages/components/toast/toast.tsx": "sha256-
|
|
62
|
-
"packages/components/toggle/toggle.tsx": "sha256-
|
|
60
|
+
"packages/components/timeline/timeline.tsx": "sha256-nVg7lBcAUAzqEb0YrHagJXSaHE2aWWE9bUbQMWQY6PU=",
|
|
61
|
+
"packages/components/toast/toast.tsx": "sha256-FvZnwN6Lk/QEHmcNxo7sVlEvR3Lx54aT5cH9lwrthhE=",
|
|
62
|
+
"packages/components/toggle/toggle.tsx": "sha256-8qPHeSliHNdzQS7OLJ47BrvRR75MYyUGJlK4s21XS/U=",
|
|
63
63
|
"packages/components/tooltip/tooltip.tsx": "sha256-NbaOUmlUbBlUIdovchrWwH2QqzuvXB97DjnJnrHf4Gc=",
|
|
64
|
-
"packages/components/tour/tour.tsx": "sha256-
|
|
64
|
+
"packages/components/tour/tour.tsx": "sha256-mrAE8aVDBVIibGvQah9Gh3mzIiWDFxB6aObzqFo5xXU=",
|
|
65
65
|
"packages/components/virtual-list/virtual-list.tsx": "sha256-raGEFn4HlAOIohqZcy0DgptsnUIPS/6bWLYeTe+M9E8=",
|
|
66
66
|
"packages/components/virtual-list/virtual.ts": "sha256-PyDIj3C2Vwb1i4ZssxSlYjgKmZPBIFLZFNq8YCW2Z78="
|
|
67
67
|
}
|
package/src/assets.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { targetRoot } from "./config.js";
|
|
5
|
+
import { BUNDLED_REGISTRY_ROOT, readJson } from "./registry.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 2D and 3D assets.
|
|
9
|
+
*
|
|
10
|
+
* Their files are not bundled with the CLI: a project wants three of them,
|
|
11
|
+
* not every model. The bundled index lists each file with its integrity; the
|
|
12
|
+
* files download from the site and are checked before anything is written.
|
|
13
|
+
*
|
|
14
|
+
* Static files (SVG, GLB, posters) land in the `assets` target, public/mlola
|
|
15
|
+
* by default, so a web app serves them as /mlola/2d/… and /mlola/3d/…. A 2D
|
|
16
|
+
* asset's React component lands beside your components, in illustrations/.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export function loadAssetIndex() {
|
|
20
|
+
const filename = path.join(BUNDLED_REGISTRY_ROOT, "assets.json");
|
|
21
|
+
if (!fs.existsSync(filename)) return { items: [] };
|
|
22
|
+
return readJson(filename);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const integrityOf = (bytes) => `sha256-${createHash("sha256").update(bytes).digest("base64")}`;
|
|
26
|
+
|
|
27
|
+
/** Where each of an asset's files goes in the project. */
|
|
28
|
+
export function assetTarget(config, item, file) {
|
|
29
|
+
if (file.role === "react") return path.join(targetRoot(config, "components"), "illustrations", file.path);
|
|
30
|
+
return path.join(targetRoot(config, "assets"), item.kind, file.path);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function addAssets(cwd, config, ids, { source, fetcher, overwrite = false }) {
|
|
34
|
+
const index = loadAssetIndex();
|
|
35
|
+
const known = new Map(index.items.map((item) => [item.id, item]));
|
|
36
|
+
const unknown = ids.filter((id) => !known.has(id));
|
|
37
|
+
if (unknown.length) {
|
|
38
|
+
throw new Error(`No asset named ${unknown.map((id) => `"${id}"`).join(", ")}. See them all with "npx mlola-ui list --kind asset".`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Download and verify everything first, so a failure writes nothing.
|
|
42
|
+
const planned = [];
|
|
43
|
+
for (const id of ids) {
|
|
44
|
+
const item = known.get(id);
|
|
45
|
+
for (const file of item.files) {
|
|
46
|
+
const url = `${source}/${item.kind}/${item.id}/${file.path}`;
|
|
47
|
+
let response;
|
|
48
|
+
try {
|
|
49
|
+
response = await fetcher(url);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
throw new Error(`Could not download ${url}: ${error.message}`);
|
|
52
|
+
}
|
|
53
|
+
if (!response.ok) throw new Error(`Could not download ${url}: the server answered ${response.status}.`);
|
|
54
|
+
const bytes = Buffer.from(await response.arrayBuffer());
|
|
55
|
+
if (integrityOf(bytes) !== file.integrity) throw new Error(`${item.id}/${file.path} failed integrity validation; nothing was written.`);
|
|
56
|
+
planned.push({ item, file, bytes, relative: assetTarget(config, item, file) });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const result = { written: [], unchanged: [], conflicts: [], items: ids.map((id) => known.get(id)) };
|
|
61
|
+
for (const { bytes, relative } of planned) {
|
|
62
|
+
const filename = path.join(cwd, relative);
|
|
63
|
+
if (fs.existsSync(filename)) {
|
|
64
|
+
if (fs.readFileSync(filename).equals(bytes)) {
|
|
65
|
+
result.unchanged.push(relative);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (!overwrite) {
|
|
69
|
+
result.conflicts.push(relative);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
fs.mkdirSync(path.dirname(filename), { recursive: true });
|
|
74
|
+
fs.writeFileSync(filename, bytes);
|
|
75
|
+
result.written.push(relative);
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** A line on how to use each added asset. */
|
|
81
|
+
export function assetHint(config, item) {
|
|
82
|
+
const pascal = item.id.replace(/(^|-)([a-z0-9])/g, (_, __, letter) => letter.toUpperCase());
|
|
83
|
+
const assets = targetRoot(config, "assets");
|
|
84
|
+
const publicPath = assets.startsWith("public/") ? `/${assets.slice("public/".length)}` : `/${assets}`;
|
|
85
|
+
if (item.kind === "2d") {
|
|
86
|
+
const alias = (config.aliases?.components ?? "@/components/ui").replace(/\/$/, "");
|
|
87
|
+
return `${item.id}: import { ${pascal}Illustration } from "${alias}/illustrations/${item.id}"; it follows your theme. As a plain image: ${publicPath}/2d/${item.id}.svg`;
|
|
88
|
+
}
|
|
89
|
+
return `${item.id}: ${publicPath}/3d/${item.id}.glb, with its poster at ${publicPath}/3d/${item.id}-poster.webp. Materials are named primary, surface, accent, ink, neutral and metal, so a viewer can paint them from your theme.`;
|
|
90
|
+
}
|
package/src/cli.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
targetRoot,
|
|
8
8
|
writeDefaultConfig,
|
|
9
9
|
} from "./config.js";
|
|
10
|
+
import { addAssets, assetHint, loadAssetIndex } from "./assets.js";
|
|
10
11
|
import { runDoctor } from "./doctor.js";
|
|
11
12
|
import { collectEngineDependencies, installItems } from "./installer.js";
|
|
12
13
|
import { loadCatalogNames, loadRegistry, resolveItems } from "./registry.js";
|
|
@@ -18,9 +19,10 @@ const HELP = `Mlola UI — source-copy components with Theme
|
|
|
18
19
|
Usage:
|
|
19
20
|
mlola-ui init [--force]
|
|
20
21
|
mlola-ui add <name...> [--overwrite] [--install|--yes]
|
|
22
|
+
mlola-ui add asset <id...> [--overwrite]
|
|
21
23
|
mlola-ui login <token> [--host <url>]
|
|
22
24
|
mlola-ui logout
|
|
23
|
-
mlola-ui list [--kind component|block|template] [--json]
|
|
25
|
+
mlola-ui list [--kind component|block|template|asset] [--json]
|
|
24
26
|
mlola-ui doctor
|
|
25
27
|
mlola-ui theme pull <theme-id | url> [--host <url>] [--overwrite]
|
|
26
28
|
|
|
@@ -29,6 +31,7 @@ Examples:
|
|
|
29
31
|
npx mlola-ui add button card
|
|
30
32
|
npx mlola-ui login mlp_… (Mlola Pro: a token from /account)
|
|
31
33
|
npx mlola-ui add conversation bot
|
|
34
|
+
npx mlola-ui add asset empty-inbox orb
|
|
32
35
|
npx mlola-ui list --kind block
|
|
33
36
|
npx mlola-ui theme pull https://ui.mlola.com/t/th-4k2x9qf7wz3m
|
|
34
37
|
`;
|
|
@@ -116,8 +119,22 @@ export async function run(argv, options = {}) {
|
|
|
116
119
|
if (command === "list") {
|
|
117
120
|
const registry = loadRegistry();
|
|
118
121
|
const kind = optionValue(args, "--kind");
|
|
119
|
-
if (kind && !["component", "block", "template"].includes(kind)) {
|
|
120
|
-
throw new Error(`--kind must be component, block, or
|
|
122
|
+
if (kind && !["component", "block", "template", "asset"].includes(kind)) {
|
|
123
|
+
throw new Error(`--kind must be component, block, template or asset (received "${kind}")`);
|
|
124
|
+
}
|
|
125
|
+
if (kind === "asset") {
|
|
126
|
+
const assets = loadAssetIndex().items;
|
|
127
|
+
if (hasFlag(args, "--json")) {
|
|
128
|
+
output.log(JSON.stringify(assets, null, 2));
|
|
129
|
+
return 0;
|
|
130
|
+
}
|
|
131
|
+
for (const group of ["2d", "3d"]) {
|
|
132
|
+
const grouped = assets.filter((item) => item.kind === group);
|
|
133
|
+
if (!grouped.length) continue;
|
|
134
|
+
output.log(`${group === "2d" ? "Illustrations" : "3D models"} (${grouped.length})`);
|
|
135
|
+
for (const item of grouped) output.log(` ${item.id.padEnd(18)} ${item.summary}`);
|
|
136
|
+
}
|
|
137
|
+
return 0;
|
|
121
138
|
}
|
|
122
139
|
const items = kind
|
|
123
140
|
? registry.items.filter((item) => item.type === {
|
|
@@ -150,7 +167,7 @@ export async function run(argv, options = {}) {
|
|
|
150
167
|
if (!token) throw new Error(`Usage: mlola-ui login <token>. Create a token at ${host}/account.`);
|
|
151
168
|
const { licence } = await verifyToken(host, token, fetcher);
|
|
152
169
|
const filename = saveCredentials(env, token, host === hostFrom(env) ? undefined : host);
|
|
153
|
-
output.log(`✓ Logged in to Mlola Pro (${licence.plan}
|
|
170
|
+
output.log(`✓ Logged in to Mlola Pro (${licence.plan} license). Token saved to ${filename}.`);
|
|
154
171
|
return 0;
|
|
155
172
|
}
|
|
156
173
|
|
|
@@ -159,6 +176,23 @@ export async function run(argv, options = {}) {
|
|
|
159
176
|
return 0;
|
|
160
177
|
}
|
|
161
178
|
|
|
179
|
+
if (command === "add" && args[0] === "asset") {
|
|
180
|
+
const host = optionValue(args, "--host");
|
|
181
|
+
const ids = args.slice(1).filter((argument) => !argument.startsWith("-") && argument !== host);
|
|
182
|
+
if (!ids.length) throw new Error('Usage: mlola-ui add asset <id...>. See them with "npx mlola-ui list --kind asset".');
|
|
183
|
+
const config = loadConfig(cwd);
|
|
184
|
+
const source = env.MLOLA_ASSETS_URL?.replace(/\/$/, "") ?? `${hostFrom(env, host)}/asset-files`;
|
|
185
|
+
const result = await addAssets(cwd, config, ids, { source, fetcher, overwrite: hasFlag(args, "--overwrite") });
|
|
186
|
+
for (const filename of result.written) output.log(`✓ Added ${filename}`);
|
|
187
|
+
if (result.unchanged.length) output.log(`• ${result.unchanged.length} file(s) already up to date`);
|
|
188
|
+
if (result.conflicts.length) {
|
|
189
|
+
output.error(`✗ Refused to replace ${result.conflicts.length} modified file(s):\n ${result.conflicts.join("\n ")}\nRun again with --overwrite to replace them.`);
|
|
190
|
+
return 1;
|
|
191
|
+
}
|
|
192
|
+
for (const item of result.items) output.log(assetHint(config, item));
|
|
193
|
+
return 0;
|
|
194
|
+
}
|
|
195
|
+
|
|
162
196
|
if (command === "add") {
|
|
163
197
|
const names = args.filter((argument) => !argument.startsWith("-"));
|
|
164
198
|
if (!names.length) throw new Error("Usage: mlola-ui add <name...>");
|
|
@@ -178,7 +212,7 @@ export async function run(argv, options = {}) {
|
|
|
178
212
|
if (!credentials) {
|
|
179
213
|
throw new Error(
|
|
180
214
|
`${proNames.map((name) => `"${name}"`).join(", ")} ${proNames.length === 1 ? "is" : "are"} part of Mlola Pro. ` +
|
|
181
|
-
`With a
|
|
215
|
+
`With a license, create a token at ${host}/account and run "npx mlola-ui login <token>". See ${host}/pricing to get Pro.`,
|
|
182
216
|
);
|
|
183
217
|
}
|
|
184
218
|
const delivered = await fetchProItems(host, credentials.token, proNames, fetcher);
|
package/src/config.js
CHANGED
package/src/pro.js
CHANGED
|
@@ -10,7 +10,7 @@ import { sha256 } from "./registry.js";
|
|
|
10
10
|
* Pro source is never bundled. `mlola-ui login <token>` stores a token made at
|
|
11
11
|
* /account (readable by the current user only); `add` sends it to the Pro
|
|
12
12
|
* endpoint, which returns the items and their Pro dependencies stamped with
|
|
13
|
-
* the
|
|
13
|
+
* the license. In CI, `MLOLA_PRO_TOKEN` takes the place of a login.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
export const DEFAULT_HOST = "https://ui.mlola.com";
|