leglas 1.2.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -39
- package/dist/bin.js +73 -9
- package/dist/index.js +73 -9
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -21,30 +21,36 @@
|
|
|
21
21
|
|
|
22
22
|
<p align="center"><i>Every direction in the rail, the selected one running as your actual app. Arrow keys flip between them.</i></p>
|
|
23
23
|
|
|
24
|
-
Code is becoming the source of truth. Features go from prompt to
|
|
25
|
-
|
|
26
|
-
fastest teams already design in the medium they ship
|
|
27
|
-
for working that way:
|
|
28
|
-
once, live
|
|
29
|
-
|
|
30
|
-
The goal is to help devs and designers try many variations of a component,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
24
|
+
Code is becoming the source of truth. Features go from prompt to working
|
|
25
|
+
code in minutes, while mockups and design files fall behind the product
|
|
26
|
+
they describe. The fastest teams already design in the medium they ship, and
|
|
27
|
+
Leglas is built for working that way: many design directions, explored at
|
|
28
|
+
once, live in your own app.
|
|
29
|
+
|
|
30
|
+
The goal is to help devs and designers try many variations of a component,
|
|
31
|
+
feature, page or user-flow quickly and make coming up with ideas extremely
|
|
32
|
+
easy.
|
|
33
|
+
|
|
34
|
+
Ask your agent for a handful of directions for the landing page, the
|
|
35
|
+
checkout, the empty states or your onboarding flow. Leglas runs every one
|
|
36
|
+
of them as your actual app, in one place, and keeps your notes on each.
|
|
37
|
+
Explore far and wide without losing focus: you see more ideas without
|
|
38
|
+
losing your opinion of any of them.
|
|
39
|
+
|
|
40
|
+
And because every direction is the real product, your judgment is real
|
|
38
41
|
too. Everything behaves the way it will in production, motion and data
|
|
39
|
-
included. Choosing between two directions is choosing between two
|
|
40
|
-
|
|
41
|
-
a picture or design file.
|
|
42
|
+
included. Choosing between two directions is choosing between two things
|
|
43
|
+
that already exist, and the winner never has to be rebuilt from a picture.
|
|
42
44
|
|
|
43
|
-
|
|
44
|
-
dev server
|
|
45
|
-
|
|
45
|
+
Leglas asks nothing of your app: no package, no import, no build step. It
|
|
46
|
+
sits in front of the dev server you already run, and its config file is
|
|
47
|
+
optional. New directions go in `.leglas/`, a folder Leglas adds to your
|
|
48
|
+
`.gitignore`, and keeping a winner clears the rest away. Sessions clean up
|
|
49
|
+
after themselves too: stopping Leglas shuts down everything it started,
|
|
50
|
+
from dev servers and branch checkouts to the share tunnel and any agent
|
|
51
|
+
still at work.
|
|
46
52
|
|
|
47
|
-
## What
|
|
53
|
+
## What Leglas does
|
|
48
54
|
|
|
49
55
|
<p align="center">
|
|
50
56
|
<img src="https://raw.githubusercontent.com/FredAmartey/leglas/main/.github/assets/screenshots/compare-artboards.jpg" width="900" alt="The Leglas interface: the rail on the left, and two directions running side by side as the real app, each labelled with its name and the width it is drawn at." />
|
|
@@ -53,11 +59,12 @@ and sessions that clean up after themselves.
|
|
|
53
59
|
<p align="center"><i>Two directions for the same page, running side by side as the actual app.</i></p>
|
|
54
60
|
|
|
55
61
|
- Put any two directions side by side when it gets hard to choose.
|
|
56
|
-
- Name each direction, drag to reorder
|
|
57
|
-
right. Your opinion of every idea survives a long exploration.
|
|
62
|
+
- Name each direction, drag to reorder and set aside the ones that don't
|
|
63
|
+
feel right. Your opinion of every idea survives a long exploration.
|
|
58
64
|
- Share the rail with someone who has no repo: a client, a cofounder, a
|
|
59
65
|
teammate on another machine. They get the real app, in your order, and
|
|
60
|
-
cannot change anything.
|
|
66
|
+
cannot change anything. Share it from the interface, from a terminal with
|
|
67
|
+
`npx leglas share` or by asking your agent. See [Sharing](https://github.com/FredAmartey/leglas/blob/main/docs/sharing.md).
|
|
61
68
|
- Compare things no design tool can hold: three git branches, a local build
|
|
62
69
|
against production, yesterday's direction against today's.
|
|
63
70
|
- Hand the workflow to your coding agent. `leglas init` teaches it, and
|
|
@@ -67,13 +74,14 @@ and sessions that clean up after themselves.
|
|
|
67
74
|
- Ask for changes without leaving the comparison. Describe what you want on
|
|
68
75
|
the direction you are looking at, or point at it, and Leglas turns that
|
|
69
76
|
into a precise request for your agent, file path and screenshots
|
|
70
|
-
included.
|
|
71
|
-
|
|
72
|
-
- A change makes a variant, not a rewrite. The result
|
|
77
|
+
included. Pick any agent on your machine from beside the send button.
|
|
78
|
+
Your agent, your subscription, no keys.
|
|
79
|
+
- A change makes a variant, not a rewrite. The result appears under the
|
|
73
80
|
direction it came from, with the original still there to compare against.
|
|
74
81
|
- Keep the winner with one command. Leglas moves it into your source tree,
|
|
75
|
-
writes down what the exploration decided and
|
|
76
|
-
- No app yet?
|
|
82
|
+
writes down what the exploration decided and cleans up after itself.
|
|
83
|
+
- No app yet? Leglas compares plain HTML files the same way, with no dev
|
|
84
|
+
server.
|
|
77
85
|
|
|
78
86
|
## Get started
|
|
79
87
|
|
|
@@ -99,16 +107,18 @@ export default {
|
|
|
99
107
|
};
|
|
100
108
|
```
|
|
101
109
|
|
|
102
|
-
Each preview is a URL your dev server already answers.
|
|
103
|
-
|
|
104
|
-
|
|
110
|
+
Each preview is a URL your dev server already answers. [Setting up a
|
|
111
|
+
project](https://github.com/FredAmartey/leglas/blob/main/docs/configuration.md)
|
|
112
|
+
covers every field, plain HTML files with no dev server, git branches side
|
|
113
|
+
by side and how the proxy works.
|
|
105
114
|
|
|
106
115
|
It works with whatever you are building in. Leglas never imports or
|
|
107
116
|
executes your framework, so the target can be Next, Vite, Remix, SvelteKit,
|
|
108
|
-
Astro or a folder of static files. It needs Node 24 or newer
|
|
109
|
-
else
|
|
110
|
-
|
|
111
|
-
|
|
117
|
+
Astro or a folder of static files. It needs Node 24 or newer and nothing
|
|
118
|
+
else; `npx` fetches it the first time you run it. Leglas tells you when a
|
|
119
|
+
newer version is out and can update itself. To pin a version for a
|
|
120
|
+
project, `npm install -D leglas`; to type `leglas` without `npx`,
|
|
121
|
+
`npm install -g leglas`.
|
|
112
122
|
|
|
113
123
|
## With your agent
|
|
114
124
|
|
|
@@ -121,9 +131,9 @@ pricing page" as a Leglas exploration in any project, including ones that
|
|
|
121
131
|
have never seen Leglas. In a project, `npx leglas init` writes the whole
|
|
122
132
|
workflow into `AGENTS.md`, so Claude Code, Cursor, Codex or whatever you
|
|
123
133
|
switch to next opens the project already knowing how to add design
|
|
124
|
-
directions to it.
|
|
125
|
-
|
|
126
|
-
|
|
134
|
+
directions to it. [Working with agents](https://github.com/FredAmartey/leglas/blob/main/docs/agents.md)
|
|
135
|
+
has the one rule that workflow is built around, the commands an agent runs,
|
|
136
|
+
the MCP server and the Agent Plugin.
|
|
127
137
|
|
|
128
138
|
## Documentation
|
|
129
139
|
|
package/dist/bin.js
CHANGED
|
@@ -2427,6 +2427,12 @@ var LOCATOR = `(function (selector, text, tag) {
|
|
|
2427
2427
|
if (r.width === 0 && r.height === 0) return null;
|
|
2428
2428
|
return { x: r.left + window.scrollX, y: r.top + window.scrollY, width: r.width, height: r.height };
|
|
2429
2429
|
})`;
|
|
2430
|
+
var DRAWN_WITH = /* @__PURE__ */ new Set(["Script", "Stylesheet", "Font", "Image"]);
|
|
2431
|
+
var LAID_OUT = `(async () => {
|
|
2432
|
+
await new Promise((next) => requestAnimationFrame(() => requestAnimationFrame(next)));
|
|
2433
|
+
if (document.fonts) await document.fonts.ready;
|
|
2434
|
+
return true;
|
|
2435
|
+
})()`;
|
|
2430
2436
|
function clamp(value, low, high) {
|
|
2431
2437
|
return Math.min(high, Math.max(low, value));
|
|
2432
2438
|
}
|
|
@@ -2508,6 +2514,22 @@ async function render(page, input) {
|
|
|
2508
2514
|
errors.push(message2);
|
|
2509
2515
|
};
|
|
2510
2516
|
let documentStatus = null;
|
|
2517
|
+
const pending = /* @__PURE__ */ new Set();
|
|
2518
|
+
const afterLoadScripts = /* @__PURE__ */ new Set();
|
|
2519
|
+
let loadFired = false;
|
|
2520
|
+
let scriptLanded = null;
|
|
2521
|
+
let asked = 0;
|
|
2522
|
+
let emptied = () => {
|
|
2523
|
+
};
|
|
2524
|
+
const received = (id) => {
|
|
2525
|
+
if (afterLoadScripts.delete(id))
|
|
2526
|
+
scriptLanded = Date.now();
|
|
2527
|
+
if (pending.delete(id) && pending.size === 0)
|
|
2528
|
+
emptied();
|
|
2529
|
+
};
|
|
2530
|
+
const landed = () => pending.size === 0 ? Promise.resolve() : new Promise((resolve5) => {
|
|
2531
|
+
emptied = resolve5;
|
|
2532
|
+
});
|
|
2511
2533
|
const unlisten = [
|
|
2512
2534
|
page.on("Network.responseReceived", (params) => {
|
|
2513
2535
|
if (documentStatus !== null || params?.type !== "Document")
|
|
@@ -2516,6 +2538,25 @@ async function render(page, input) {
|
|
|
2516
2538
|
if (isNumber(status))
|
|
2517
2539
|
documentStatus = status;
|
|
2518
2540
|
}),
|
|
2541
|
+
page.on("Network.requestWillBeSent", (params) => {
|
|
2542
|
+
const id = params?.requestId;
|
|
2543
|
+
if (!DRAWN_WITH.has(params?.type) || !isString(id))
|
|
2544
|
+
return;
|
|
2545
|
+
if (!pending.has(id)) {
|
|
2546
|
+
asked += 1;
|
|
2547
|
+
if (loadFired && params?.type === "Script")
|
|
2548
|
+
afterLoadScripts.add(id);
|
|
2549
|
+
}
|
|
2550
|
+
pending.add(id);
|
|
2551
|
+
}),
|
|
2552
|
+
page.on("Network.loadingFinished", (params) => {
|
|
2553
|
+
if (isString(params?.requestId))
|
|
2554
|
+
received(params.requestId);
|
|
2555
|
+
}),
|
|
2556
|
+
page.on("Network.loadingFailed", (params) => {
|
|
2557
|
+
if (isString(params?.requestId))
|
|
2558
|
+
received(params.requestId);
|
|
2559
|
+
}),
|
|
2519
2560
|
page.on("Runtime.exceptionThrown", (params) => remember(params?.exceptionDetails?.exception?.description ?? params?.exceptionDetails?.text)),
|
|
2520
2561
|
page.on("Runtime.consoleAPICalled", (params) => {
|
|
2521
2562
|
if (params?.type !== "error")
|
|
@@ -2542,7 +2583,10 @@ async function render(page, input) {
|
|
|
2542
2583
|
const load = new Promise((resolve5) => {
|
|
2543
2584
|
loaded = resolve5;
|
|
2544
2585
|
});
|
|
2545
|
-
const stopLoad = page.on("Page.loadEventFired", () =>
|
|
2586
|
+
const stopLoad = page.on("Page.loadEventFired", () => {
|
|
2587
|
+
loadFired = true;
|
|
2588
|
+
loaded();
|
|
2589
|
+
});
|
|
2546
2590
|
unlisten.push(stopLoad);
|
|
2547
2591
|
const navigation = await page.send("Page.navigate", { url: input.url });
|
|
2548
2592
|
if (isString(navigation.errorText) && navigation.errorText !== "") {
|
|
@@ -2553,12 +2597,28 @@ async function render(page, input) {
|
|
|
2553
2597
|
if (documentStatus !== null && documentStatus >= 500) {
|
|
2554
2598
|
throw new Error(`The page did not load: the app answered HTTP ${documentStatus}.`);
|
|
2555
2599
|
}
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2600
|
+
const until = Date.now() + 2e3;
|
|
2601
|
+
let waitedForScript = null;
|
|
2602
|
+
while (Date.now() < until) {
|
|
2603
|
+
await bounded(landed(), until - Date.now(), void 0);
|
|
2604
|
+
const before = asked;
|
|
2605
|
+
const answered = await bounded(page.send("Runtime.evaluate", {
|
|
2606
|
+
expression: LAID_OUT,
|
|
2607
|
+
awaitPromise: true,
|
|
2608
|
+
returnByValue: true
|
|
2609
|
+
}), Math.max(0, until - Date.now()), void 0).then(() => true, () => false);
|
|
2610
|
+
if (!answered)
|
|
2611
|
+
break;
|
|
2612
|
+
if (pending.size !== 0 || asked !== before)
|
|
2613
|
+
continue;
|
|
2614
|
+
const latestScript = scriptLanded;
|
|
2615
|
+
if (latestScript === null || latestScript === waitedForScript)
|
|
2616
|
+
break;
|
|
2617
|
+
const remaining = Math.min(latestScript + 300, until) - Date.now();
|
|
2618
|
+
if (remaining > 0)
|
|
2619
|
+
await new Promise((resolve5) => setTimeout(resolve5, remaining));
|
|
2620
|
+
waitedForScript = latestScript;
|
|
2621
|
+
}
|
|
2562
2622
|
await bounded(page.send("Runtime.evaluate", {
|
|
2563
2623
|
expression: `(async () => {
|
|
2564
2624
|
// The page stops itself before the caller's deadline can. Without
|
|
@@ -9260,8 +9320,10 @@ type SearchParams = Record<string, string | string[] | undefined>;
|
|
|
9260
9320
|
/**
|
|
9261
9321
|
* Production always renders the fallback, whatever the URL says, so a branch
|
|
9262
9322
|
* point that reaches a deployed build cannot expose an unreleased direction.
|
|
9323
|
+
* It is not exported because a file whose exports are all components hot-swaps
|
|
9324
|
+
* in place, while an extra export makes every edit reload the page.
|
|
9263
9325
|
*/
|
|
9264
|
-
|
|
9326
|
+
function resolve${titleCase(slug).replace(/\s/g, "")}(searchParams: SearchParams): Direction {
|
|
9265
9327
|
if (process.env.NODE_ENV === "production") return FALLBACK;
|
|
9266
9328
|
const raw = searchParams["v-${slug}"];
|
|
9267
9329
|
const value = Array.isArray(raw) ? raw[0] : raw;
|
|
@@ -9307,8 +9369,10 @@ function isProduction(): boolean {
|
|
|
9307
9369
|
/**
|
|
9308
9370
|
* Production always renders the fallback, whatever the URL says, so a branch
|
|
9309
9371
|
* point that reaches a deployed build cannot expose an unreleased direction.
|
|
9372
|
+
* It is not exported because a file whose exports are all components hot-swaps
|
|
9373
|
+
* in place, while an extra export makes every edit reload the page.
|
|
9310
9374
|
*/
|
|
9311
|
-
|
|
9375
|
+
function resolve${name}(): Direction {
|
|
9312
9376
|
if (isProduction()) return FALLBACK;
|
|
9313
9377
|
const value = new URLSearchParams(window.location.search).get("v-${slug}");
|
|
9314
9378
|
return value !== null && value in DIRECTIONS ? (value as Direction) : FALLBACK;
|
package/dist/index.js
CHANGED
|
@@ -598,8 +598,10 @@ type SearchParams = Record<string, string | string[] | undefined>;
|
|
|
598
598
|
/**
|
|
599
599
|
* Production always renders the fallback, whatever the URL says, so a branch
|
|
600
600
|
* point that reaches a deployed build cannot expose an unreleased direction.
|
|
601
|
+
* It is not exported because a file whose exports are all components hot-swaps
|
|
602
|
+
* in place, while an extra export makes every edit reload the page.
|
|
601
603
|
*/
|
|
602
|
-
|
|
604
|
+
function resolve${titleCase(slug).replace(/\s/g, "")}(searchParams: SearchParams): Direction {
|
|
603
605
|
if (process.env.NODE_ENV === "production") return FALLBACK;
|
|
604
606
|
const raw = searchParams["v-${slug}"];
|
|
605
607
|
const value = Array.isArray(raw) ? raw[0] : raw;
|
|
@@ -645,8 +647,10 @@ function isProduction(): boolean {
|
|
|
645
647
|
/**
|
|
646
648
|
* Production always renders the fallback, whatever the URL says, so a branch
|
|
647
649
|
* point that reaches a deployed build cannot expose an unreleased direction.
|
|
650
|
+
* It is not exported because a file whose exports are all components hot-swaps
|
|
651
|
+
* in place, while an extra export makes every edit reload the page.
|
|
648
652
|
*/
|
|
649
|
-
|
|
653
|
+
function resolve${name}(): Direction {
|
|
650
654
|
if (isProduction()) return FALLBACK;
|
|
651
655
|
const value = new URLSearchParams(window.location.search).get("v-${slug}");
|
|
652
656
|
return value !== null && value in DIRECTIONS ? (value as Direction) : FALLBACK;
|
|
@@ -2819,6 +2823,12 @@ var LOCATOR = `(function (selector, text, tag) {
|
|
|
2819
2823
|
if (r.width === 0 && r.height === 0) return null;
|
|
2820
2824
|
return { x: r.left + window.scrollX, y: r.top + window.scrollY, width: r.width, height: r.height };
|
|
2821
2825
|
})`;
|
|
2826
|
+
var DRAWN_WITH = /* @__PURE__ */ new Set(["Script", "Stylesheet", "Font", "Image"]);
|
|
2827
|
+
var LAID_OUT = `(async () => {
|
|
2828
|
+
await new Promise((next) => requestAnimationFrame(() => requestAnimationFrame(next)));
|
|
2829
|
+
if (document.fonts) await document.fonts.ready;
|
|
2830
|
+
return true;
|
|
2831
|
+
})()`;
|
|
2822
2832
|
function clamp(value, low, high) {
|
|
2823
2833
|
return Math.min(high, Math.max(low, value));
|
|
2824
2834
|
}
|
|
@@ -2900,6 +2910,22 @@ async function render(page, input) {
|
|
|
2900
2910
|
errors.push(message2);
|
|
2901
2911
|
};
|
|
2902
2912
|
let documentStatus = null;
|
|
2913
|
+
const pending = /* @__PURE__ */ new Set();
|
|
2914
|
+
const afterLoadScripts = /* @__PURE__ */ new Set();
|
|
2915
|
+
let loadFired = false;
|
|
2916
|
+
let scriptLanded = null;
|
|
2917
|
+
let asked = 0;
|
|
2918
|
+
let emptied = () => {
|
|
2919
|
+
};
|
|
2920
|
+
const received = (id) => {
|
|
2921
|
+
if (afterLoadScripts.delete(id))
|
|
2922
|
+
scriptLanded = Date.now();
|
|
2923
|
+
if (pending.delete(id) && pending.size === 0)
|
|
2924
|
+
emptied();
|
|
2925
|
+
};
|
|
2926
|
+
const landed = () => pending.size === 0 ? Promise.resolve() : new Promise((resolve5) => {
|
|
2927
|
+
emptied = resolve5;
|
|
2928
|
+
});
|
|
2903
2929
|
const unlisten = [
|
|
2904
2930
|
page.on("Network.responseReceived", (params) => {
|
|
2905
2931
|
if (documentStatus !== null || params?.type !== "Document")
|
|
@@ -2908,6 +2934,25 @@ async function render(page, input) {
|
|
|
2908
2934
|
if (isNumber(status))
|
|
2909
2935
|
documentStatus = status;
|
|
2910
2936
|
}),
|
|
2937
|
+
page.on("Network.requestWillBeSent", (params) => {
|
|
2938
|
+
const id = params?.requestId;
|
|
2939
|
+
if (!DRAWN_WITH.has(params?.type) || !isString(id))
|
|
2940
|
+
return;
|
|
2941
|
+
if (!pending.has(id)) {
|
|
2942
|
+
asked += 1;
|
|
2943
|
+
if (loadFired && params?.type === "Script")
|
|
2944
|
+
afterLoadScripts.add(id);
|
|
2945
|
+
}
|
|
2946
|
+
pending.add(id);
|
|
2947
|
+
}),
|
|
2948
|
+
page.on("Network.loadingFinished", (params) => {
|
|
2949
|
+
if (isString(params?.requestId))
|
|
2950
|
+
received(params.requestId);
|
|
2951
|
+
}),
|
|
2952
|
+
page.on("Network.loadingFailed", (params) => {
|
|
2953
|
+
if (isString(params?.requestId))
|
|
2954
|
+
received(params.requestId);
|
|
2955
|
+
}),
|
|
2911
2956
|
page.on("Runtime.exceptionThrown", (params) => remember(params?.exceptionDetails?.exception?.description ?? params?.exceptionDetails?.text)),
|
|
2912
2957
|
page.on("Runtime.consoleAPICalled", (params) => {
|
|
2913
2958
|
if (params?.type !== "error")
|
|
@@ -2934,7 +2979,10 @@ async function render(page, input) {
|
|
|
2934
2979
|
const load = new Promise((resolve5) => {
|
|
2935
2980
|
loaded = resolve5;
|
|
2936
2981
|
});
|
|
2937
|
-
const stopLoad = page.on("Page.loadEventFired", () =>
|
|
2982
|
+
const stopLoad = page.on("Page.loadEventFired", () => {
|
|
2983
|
+
loadFired = true;
|
|
2984
|
+
loaded();
|
|
2985
|
+
});
|
|
2938
2986
|
unlisten.push(stopLoad);
|
|
2939
2987
|
const navigation = await page.send("Page.navigate", { url: input.url });
|
|
2940
2988
|
if (isString(navigation.errorText) && navigation.errorText !== "") {
|
|
@@ -2945,12 +2993,28 @@ async function render(page, input) {
|
|
|
2945
2993
|
if (documentStatus !== null && documentStatus >= 500) {
|
|
2946
2994
|
throw new Error(`The page did not load: the app answered HTTP ${documentStatus}.`);
|
|
2947
2995
|
}
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2996
|
+
const until = Date.now() + 2e3;
|
|
2997
|
+
let waitedForScript = null;
|
|
2998
|
+
while (Date.now() < until) {
|
|
2999
|
+
await bounded(landed(), until - Date.now(), void 0);
|
|
3000
|
+
const before = asked;
|
|
3001
|
+
const answered = await bounded(page.send("Runtime.evaluate", {
|
|
3002
|
+
expression: LAID_OUT,
|
|
3003
|
+
awaitPromise: true,
|
|
3004
|
+
returnByValue: true
|
|
3005
|
+
}), Math.max(0, until - Date.now()), void 0).then(() => true, () => false);
|
|
3006
|
+
if (!answered)
|
|
3007
|
+
break;
|
|
3008
|
+
if (pending.size !== 0 || asked !== before)
|
|
3009
|
+
continue;
|
|
3010
|
+
const latestScript = scriptLanded;
|
|
3011
|
+
if (latestScript === null || latestScript === waitedForScript)
|
|
3012
|
+
break;
|
|
3013
|
+
const remaining = Math.min(latestScript + 300, until) - Date.now();
|
|
3014
|
+
if (remaining > 0)
|
|
3015
|
+
await new Promise((resolve5) => setTimeout(resolve5, remaining));
|
|
3016
|
+
waitedForScript = latestScript;
|
|
3017
|
+
}
|
|
2954
3018
|
await bounded(page.send("Runtime.evaluate", {
|
|
2955
3019
|
expression: `(async () => {
|
|
2956
3020
|
// The page stops itself before the caller's deadline can. Without
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "leglas",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Compare design directions inside your own running app.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"types": "./dist/index.d.ts",
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"tsup": "^8.5.0",
|
|
31
|
-
"@leglas/
|
|
32
|
-
"@leglas/
|
|
31
|
+
"@leglas/shell": "0.0.0",
|
|
32
|
+
"@leglas/server": "0.0.0"
|
|
33
33
|
},
|
|
34
34
|
"engines": {
|
|
35
35
|
"node": ">=24"
|