@plitzi/cli 0.38.8 → 0.38.10
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/CHANGELOG.md +147 -0
- package/README.md +23 -8
- package/dist/index.js +899 -297
- package/package.json +3 -3
- package/skills/plitzi-cli/SKILL.md +10 -4
- package/skills/plitzi-cli/reference/projects.md +1 -1
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import { createHash, randomBytes } from "node:crypto";
|
|
|
11
11
|
import { createServer } from "node:http";
|
|
12
12
|
import tls from "node:tls";
|
|
13
13
|
import readline from "node:readline/promises";
|
|
14
|
-
import { EXPLAIN_KINDS, PAGE_SPEC_FIELDS, PAGE_TEXT_LIMIT, actionSpecFromEntry, actionToSource, authorSpace, blankTemplateFiles, catalogTemplateFiles, closest, comparePictures, compareTexts, darkScheme, dataIssues, elementTriggers, elementTypeNames,
|
|
14
|
+
import { EXPLAIN_KINDS, PAGE_SPEC_FIELDS, PAGE_TEXT_LIMIT, actionSpecFromEntry, actionToSource, authorSpace, blankTemplateFiles, catalogTemplateFiles, closest, comparePictures, compareTexts, darkScheme, dataIssues, elementTriggers, elementTypeNames, emptyTemplateFiles, explain, explainKindOf, explainList, explanationText, failedFlowText, importProbe, importedFiles, inspectDocument, inspectPage, isSuggestionCode, loadImages, locateClasses, locateElements, openPage, pageRegions, pageTexts, planFixes, readDevTools, refusalOf, unrollPage } from "@plitzi/sdk-authoring";
|
|
15
15
|
import { existsSync, readFileSync, readdirSync, realpathSync, statSync, watch } from "node:fs";
|
|
16
16
|
import { parseEnv, promisify } from "node:util";
|
|
17
17
|
import { lookup } from "node:dns/promises";
|
|
@@ -1354,7 +1354,7 @@ var n$3 = [
|
|
|
1354
1354
|
".otf"
|
|
1355
1355
|
];
|
|
1356
1356
|
var r$5 = () => Object.fromEntries(n$3.map((e) => [e, "dataurl"]));
|
|
1357
|
-
var i$
|
|
1357
|
+
var i$4 = /\?(raw|inline)$/;
|
|
1358
1358
|
var a$5 = {
|
|
1359
1359
|
".wasm": "application/wasm",
|
|
1360
1360
|
".js": "text/javascript",
|
|
@@ -1371,8 +1371,8 @@ var a$5 = {
|
|
|
1371
1371
|
var o$4 = () => ({
|
|
1372
1372
|
name: "plitzi-import-queries",
|
|
1373
1373
|
setup: (n) => {
|
|
1374
|
-
n.onResolve({ filter: i$
|
|
1375
|
-
let t = i$
|
|
1374
|
+
n.onResolve({ filter: i$4 }, async (e) => {
|
|
1375
|
+
let t = i$4.exec(e.path)?.[1] ?? "raw", r = await n.resolve(e.path.replace(i$4, ""), {
|
|
1376
1376
|
kind: e.kind,
|
|
1377
1377
|
importer: e.importer,
|
|
1378
1378
|
resolveDir: e.resolveDir
|
|
@@ -2418,7 +2418,7 @@ var projectScripts = (answers) => {
|
|
|
2418
2418
|
* runs what `build` emitted and carries no TypeScript at all.
|
|
2419
2419
|
*/
|
|
2420
2420
|
build: "tsc -p tsconfig.build.json",
|
|
2421
|
-
"start:prod": nodeWithEnv(`${v$3}/main.js`)
|
|
2421
|
+
"start:prod": `NODE_ENV=production ${nodeWithEnv(`${v$3}/main.js`)}`
|
|
2422
2422
|
} : {
|
|
2423
2423
|
start: "vite",
|
|
2424
2424
|
build: "vite build",
|
|
@@ -2512,7 +2512,7 @@ var YARN_IGNORES$1 = "\n.yarn/*\n!.yarn/patches\n!.yarn/plugins\n!.yarn/releases
|
|
|
2512
2512
|
* the CLI records about it, and is committed — a clone without it could not pull, push or upgrade.
|
|
2513
2513
|
*/
|
|
2514
2514
|
var gitignore$1 = ({ mode, packageManager }) => `node_modules\n${v$3}\n.env\ntmp\n${mode === "server" ? `${y$2}\n` : ""}${packageManager === "yarn" ? YARN_IGNORES$1 : ""}`;
|
|
2515
|
-
var startLine = ({ mode, packageManager, source }) => mode === "server" ? `\`${runCommand(packageManager, "start")}\` serves pages on http://127.0.0.1:8080 — or, when something else holds 8080, the next free port, which it prints and writes to \`${x$3}\` (set \`PORT\` to choose one). \`${runCommand(packageManager, "start:dev")}\` restarts on a save to the server's code; a save to a plugin swaps it in the open page${source === "local" ? ", and a save to the space reloads it" : ""}. In production, \`${runCommand(packageManager, "build")}\` once and run \`${runCommand(packageManager, "start:prod")}
|
|
2515
|
+
var startLine = ({ mode, packageManager, source }) => mode === "server" ? `\`${runCommand(packageManager, "start")}\` serves pages on http://127.0.0.1:8080 — or, when something else holds 8080, the next free port, which it prints and writes to \`${x$3}\` (set \`PORT\` to choose one). \`${runCommand(packageManager, "start:dev")}\` restarts on a save to the server's code; a save to a plugin swaps it in the open page${source === "local" ? ", and a save to the space reloads it" : ""}. In production, \`${runCommand(packageManager, "build")}\` once and run \`${runCommand(packageManager, "start:prod")}\`, which sets \`NODE_ENV=production\` itself: the compiled server, with no TypeScript in the process.` : `\`${runCommand(packageManager, "start")}\` runs Vite on http://127.0.0.1:5173, with hot module replacement.`;
|
|
2516
2516
|
var spaceSection = (answers) => {
|
|
2517
2517
|
if (answers.source === "cloud") return `## Where the space comes from
|
|
2518
2518
|
|
|
@@ -2634,7 +2634,7 @@ ${commands.join("\n")}
|
|
|
2634
2634
|
- **Settings are ${code(".env")}**, never committed; ${code(".env.example")} names them, committed — a new one goes in both. ${settingsNote}
|
|
2635
2635
|
${dataNote}
|
|
2636
2636
|
- **${code("public/")} is on the internet.** Every file in it is served to anyone who asks for it, as it is, the moment the project is deployed — no sign-in, no check. Never put in it a secret, a key, a ${code(".env")}, a private document, a database dump, or data only some visitors may read: that goes through a server action or a provider that checks who is asking.
|
|
2637
|
-
${serverNotes}- **Check a page in text first:** ${code(`${runCommand(answers.packageManager, "check")} -- / --width 1440,390`)} says whether every element is on screen, nothing overflows and the console is clean — and with ${code("--click <id>")}, what clicking one element changed, or that nothing did (${code("--fill <id>=<value>")} first, for a form) — a picture only when it says something is wrong: ${code(`${runCommand(answers.packageManager, "shot")} -- / --width 390`)} (add ${code("--scheme dark")}; ${code("--frames 4")} to see what moves; ${code("--compare <url>")} against another site: by section, and each text measured). ${run("visual")} runs the checks as tests.
|
|
2637
|
+
${serverNotes}- **Check a page in text first:** ${code(`${runCommand(answers.packageManager, "check")} -- / --width 1440,390`)} says whether every element is on screen, nothing overflows and the console is clean — and with ${code("--click <id>")}, what clicking one element changed, or that nothing did (${code("--fill <id>=<value>")} first, for a form) — a picture only when it says something is wrong: ${code(`${runCommand(answers.packageManager, "shot")} -- / --width 390`)} (add ${code("--scheme dark")}; ${code("--frames 4")} to see what moves, ${code("--click <id|selector> --sheet")} for an interaction; ${code("--compare <url>")} against another site: by section, and each text measured). ${run("visual")} runs the checks as tests.
|
|
2638
2638
|
- **What the page holds, in text:** ${code(`${runCommand(answers.packageManager, "check")} -- /products --state --element <id>`)} adds its state, every source by name and one element (what it reads, its own state, whether it is on screen); every check already lists the flows that failed. Read it instead of guessing from classes in the DOM.
|
|
2639
2639
|
|
|
2640
2640
|
## Do not read
|
|
@@ -2656,11 +2656,11 @@ ${code(".claude/skills/plitzi-cli/SKILL.md")} first: ${code("plitzi plugin add")
|
|
|
2656
2656
|
|
|
2657
2657
|
What you leave behind is the next reader's problem — the user's, or the next agent's. Before calling a change done:
|
|
2658
2658
|
|
|
2659
|
-
- **Nothing unused.** Delete what you made and no longer use — a file, a page, a component, a class, a token, a data file, an import, a plugin folder. No commented-out code, no ${code("console.log")} left from debugging, no copy of a file kept "just in case": git keeps the history.
|
|
2659
|
+
- **Nothing unused.** Delete what you made and no longer use — a file, a page, a component, a class, a token, a data file, an import, a plugin folder. A folder's ${code(".gitkeep")} keeps it in git while it is empty; once the folder holds files it does nothing, and ${code("doctor")} and ${code("upgrade")} ask for it no more. No commented-out code, no ${code("console.log")} left from debugging, no copy of a file kept "just in case": git keeps the history.
|
|
2660
2660
|
- **Scratch goes in ${code(`tmp/`)}, or nowhere.** A one-off script, a dump, a picture to look at — never at the root or beside the source, where it reads as part of the project.
|
|
2661
2661
|
- **One of everything.** A look used twice is a class; a value used twice is a token; a block used twice is a component, and rows of data are one list. Change it where it is defined, and rename everywhere when you rename.
|
|
2662
2662
|
- **Files a reader can find.** One part per file, named after what it is, in the folder of its kind — the shape ${code("src/space/")} already has. Do not start a parallel layout of your own.
|
|
2663
|
-
- **Leave it passing.** ${run("verify")} runs it all and prints only what fails: ${local ? `${run("author")} with zero warnings, ${run("lint:space")} clean, ` : ""}${run("typecheck")}, ${run("lint")} and the format clean, and every page whole (${run("check")}, with the server up).
|
|
2663
|
+
- **Leave it passing.** ${run("verify")} runs it all and prints only what fails: ${local ? `${run("author")} with zero warnings, ${run("lint:space")} clean, ` : ""}${run("typecheck")}, ${run("lint")} and the format clean, and every page whole (${run("check")}, with the server up) — those for signed-in visitors too, once ${code(".env")} names an account to check them as: ${code("PLITZI_CHECK_USER")} and ${code("PLITZI_CHECK_PASSWORD")}.
|
|
2664
2664
|
|
|
2665
2665
|
## The rules that go wrong most
|
|
2666
2666
|
|
|
@@ -2908,6 +2908,8 @@ var serverOptionsModule = (local) => `import type { ProjectServerOptions } from
|
|
|
2908
2908
|
*
|
|
2909
2909
|
* - \`images: { domains: ['images.example.com'] }\` — pictures from those hosts resized here, with \`sharp\` installed.
|
|
2910
2910
|
${local ? ACTION_OPTIONS_DOC : ""} * - \`rsc: { elementTimeoutMs }\` — how long a section resolved on the server is waited for: 5 s by default.
|
|
2911
|
+
* - \`devMode\` — the development build, the dev tools, a public action's trace: on unless \`NODE_ENV\` is \`production\`.
|
|
2912
|
+
* \`devMode: false\` keeps them off wherever the server is started, \`NODE_ENV\` or not. \`devReload\` follows it.
|
|
2911
2913
|
*/
|
|
2912
2914
|
export const serverOptions: ProjectServerOptions = {};
|
|
2913
2915
|
`;
|
|
@@ -3226,7 +3228,10 @@ var spaceFiles = (answers) => {
|
|
|
3226
3228
|
[r$6]: authorScript()
|
|
3227
3229
|
};
|
|
3228
3230
|
if (answers.template === "blank") return {
|
|
3229
|
-
|
|
3231
|
+
...emptyTemplateFiles({
|
|
3232
|
+
name: answers.name,
|
|
3233
|
+
dir: i$6
|
|
3234
|
+
}),
|
|
3230
3235
|
[r$6]: authorScript(),
|
|
3231
3236
|
...answers.mode === "client" ? { "public/data/.gitkeep": "" } : {}
|
|
3232
3237
|
};
|
|
@@ -4422,27 +4427,27 @@ var __exportAll = (all, no_symbols) => {
|
|
|
4422
4427
|
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
4423
4428
|
return target;
|
|
4424
4429
|
};
|
|
4425
|
-
var e$
|
|
4426
|
-
var t$63 = `(?:${e$
|
|
4427
|
-
var n$47 = `\\[\\s*(?:\\d+|'[^']*'|"[^"]*"|${e$
|
|
4428
|
-
var r$
|
|
4429
|
-
var i$
|
|
4430
|
-
var a$24 = `(?:${`\\{\\{\\{\\s*${r$
|
|
4430
|
+
var e$43 = "[a-zA-Z_][a-zA-Z0-9_]*(?:-[a-zA-Z0-9_]+)*";
|
|
4431
|
+
var t$63 = `(?:${e$43}|\\d+)`;
|
|
4432
|
+
var n$47 = `\\[\\s*(?:\\d+|'[^']*'|"[^"]*"|${e$43}(?:\\.${t$63})*)\\s*\\]`;
|
|
4433
|
+
var r$36 = `${`${e$43}(?:${n$47})*(?:\\??\\.${t$63}(?:${n$47})*)*`}(?:\\s*\\?\\?\\s*[^|{}]+?)?(?:\\s*\\|\\s*[a-zA-Z_]+(?:\\([^)]*\\))?)*`;
|
|
4434
|
+
var i$29 = `\\{\\{\\s*${r$36}\\s*\\}\\}`;
|
|
4435
|
+
var a$24 = `(?:${`\\{\\{\\{\\s*${r$36}\\s*\\}\\}\\}`}|${i$29})`;
|
|
4431
4436
|
new RegExp(a$24, "g");
|
|
4432
4437
|
RegExp(`^${a$24}$`);
|
|
4433
|
-
var e$
|
|
4438
|
+
var e$42 = ["smooth", "auto"];
|
|
4434
4439
|
var t$61 = [
|
|
4435
4440
|
"start",
|
|
4436
4441
|
"center",
|
|
4437
4442
|
"end",
|
|
4438
4443
|
"nearest"
|
|
4439
4444
|
];
|
|
4440
|
-
var s$
|
|
4441
|
-
var r$
|
|
4445
|
+
var s$22 = "onScroll";
|
|
4446
|
+
var r$34 = {
|
|
4442
4447
|
type: "select",
|
|
4443
4448
|
description: "How it moves: \"smooth\" glides there, \"auto\" jumps.",
|
|
4444
4449
|
default: "smooth",
|
|
4445
|
-
options: [...e$
|
|
4450
|
+
options: [...e$42]
|
|
4446
4451
|
};
|
|
4447
4452
|
var a$22 = {
|
|
4448
4453
|
setState: {
|
|
@@ -4516,7 +4521,7 @@ var a$22 = {
|
|
|
4516
4521
|
description: "How far down, the same way; negative goes up.",
|
|
4517
4522
|
default: ""
|
|
4518
4523
|
},
|
|
4519
|
-
behavior: r$
|
|
4524
|
+
behavior: r$34
|
|
4520
4525
|
}
|
|
4521
4526
|
},
|
|
4522
4527
|
scrollTo: {
|
|
@@ -4534,7 +4539,7 @@ var a$22 = {
|
|
|
4534
4539
|
description: "Where down, the same way. Empty stays.",
|
|
4535
4540
|
default: ""
|
|
4536
4541
|
},
|
|
4537
|
-
behavior: r$
|
|
4542
|
+
behavior: r$34
|
|
4538
4543
|
}
|
|
4539
4544
|
},
|
|
4540
4545
|
scrollIntoView: {
|
|
@@ -4554,11 +4559,11 @@ var a$22 = {
|
|
|
4554
4559
|
default: "nearest",
|
|
4555
4560
|
options: [...t$61]
|
|
4556
4561
|
},
|
|
4557
|
-
behavior: r$
|
|
4562
|
+
behavior: r$34
|
|
4558
4563
|
}
|
|
4559
4564
|
}
|
|
4560
4565
|
};
|
|
4561
|
-
var e$
|
|
4566
|
+
var e$41 = {
|
|
4562
4567
|
onLoad: {
|
|
4563
4568
|
action: "onLoad",
|
|
4564
4569
|
title: "On Load",
|
|
@@ -4728,7 +4733,7 @@ var e$40 = {
|
|
|
4728
4733
|
params: {}
|
|
4729
4734
|
}
|
|
4730
4735
|
};
|
|
4731
|
-
var e$
|
|
4736
|
+
var e$40 = () => (e) => e;
|
|
4732
4737
|
var t$60 = () => (e) => e;
|
|
4733
4738
|
var t$59 = t$60()({
|
|
4734
4739
|
type: "blockHtml",
|
|
@@ -4958,16 +4963,16 @@ var n$44 = {
|
|
|
4958
4963
|
"border-bottom-left-radius": "4px",
|
|
4959
4964
|
"border-bottom-right-radius": "4px"
|
|
4960
4965
|
};
|
|
4961
|
-
var r$
|
|
4966
|
+
var r$33 = t$60()({
|
|
4962
4967
|
type: "button",
|
|
4963
4968
|
attributeValues: {
|
|
4964
|
-
subType: e$
|
|
4969
|
+
subType: e$40()([
|
|
4965
4970
|
"button",
|
|
4966
4971
|
"submit",
|
|
4967
4972
|
"reset"
|
|
4968
4973
|
]),
|
|
4969
|
-
contentPlacement: e$
|
|
4970
|
-
iconPlacement: e$
|
|
4974
|
+
contentPlacement: e$40()(["before", "after"]),
|
|
4975
|
+
iconPlacement: e$40()(["before", "after"])
|
|
4971
4976
|
},
|
|
4972
4977
|
content: {
|
|
4973
4978
|
attributes: {
|
|
@@ -5035,7 +5040,7 @@ var r$34 = t$60()({
|
|
|
5035
5040
|
});
|
|
5036
5041
|
var n$43 = t$60()({
|
|
5037
5042
|
type: "dropdown",
|
|
5038
|
-
attributeValues: { popupPlacement: e$
|
|
5043
|
+
attributeValues: { popupPlacement: e$40()([
|
|
5039
5044
|
"left",
|
|
5040
5045
|
"right",
|
|
5041
5046
|
"top",
|
|
@@ -5166,7 +5171,7 @@ var t$54 = t$60()({
|
|
|
5166
5171
|
});
|
|
5167
5172
|
var n$42 = t$60()({
|
|
5168
5173
|
type: "heading",
|
|
5169
|
-
attributeValues: { subType: e$
|
|
5174
|
+
attributeValues: { subType: e$40()([
|
|
5170
5175
|
"h1",
|
|
5171
5176
|
"h2",
|
|
5172
5177
|
"h3",
|
|
@@ -5288,20 +5293,20 @@ var n$42 = t$60()({
|
|
|
5288
5293
|
var n$41 = t$60()({
|
|
5289
5294
|
type: "link",
|
|
5290
5295
|
attributeValues: {
|
|
5291
|
-
mode: e$
|
|
5296
|
+
mode: e$40()([
|
|
5292
5297
|
"page",
|
|
5293
5298
|
"internal",
|
|
5294
5299
|
"external"
|
|
5295
5300
|
]),
|
|
5296
|
-
target: e$
|
|
5301
|
+
target: e$40()([
|
|
5297
5302
|
"self",
|
|
5298
5303
|
"blank",
|
|
5299
5304
|
"parent",
|
|
5300
5305
|
"top"
|
|
5301
5306
|
]),
|
|
5302
|
-
contentPlacement: e$
|
|
5303
|
-
iconPlacement: e$
|
|
5304
|
-
current: e$
|
|
5307
|
+
contentPlacement: e$40()(["before", "after"]),
|
|
5308
|
+
iconPlacement: e$40()(["before", "after"]),
|
|
5309
|
+
current: e$40()(["page", "section"])
|
|
5305
5310
|
},
|
|
5306
5311
|
content: {
|
|
5307
5312
|
attributes: {
|
|
@@ -5473,7 +5478,7 @@ var t$52 = t$60()({
|
|
|
5473
5478
|
});
|
|
5474
5479
|
var n$40 = t$60()({
|
|
5475
5480
|
type: "richText",
|
|
5476
|
-
attributeValues: { format: e$
|
|
5481
|
+
attributeValues: { format: e$40()([
|
|
5477
5482
|
"html",
|
|
5478
5483
|
"markdown",
|
|
5479
5484
|
"text"
|
|
@@ -5608,9 +5613,9 @@ var n$39 = {
|
|
|
5608
5613
|
"letter-spacing": "inherit",
|
|
5609
5614
|
cursor: "pointer"
|
|
5610
5615
|
};
|
|
5611
|
-
var r$
|
|
5616
|
+
var r$32 = t$60()({
|
|
5612
5617
|
type: "themeToggle",
|
|
5613
|
-
attributeValues: { subType: e$
|
|
5618
|
+
attributeValues: { subType: e$40()(["switch", "segmented"]) },
|
|
5614
5619
|
triggers: { onThemeChange: {
|
|
5615
5620
|
action: "onThemeChange",
|
|
5616
5621
|
title: "On Theme Change",
|
|
@@ -5676,7 +5681,7 @@ var r$33 = t$60()({
|
|
|
5676
5681
|
});
|
|
5677
5682
|
var n$38 = t$60()({
|
|
5678
5683
|
type: "form",
|
|
5679
|
-
attributeValues: { method: e$
|
|
5684
|
+
attributeValues: { method: e$40()(["get", "post"]) },
|
|
5680
5685
|
sourceType: "apiContainer",
|
|
5681
5686
|
triggers: { onSubmit: {
|
|
5682
5687
|
action: "onSubmit",
|
|
@@ -5806,11 +5811,11 @@ var n$37 = {
|
|
|
5806
5811
|
outline: "none",
|
|
5807
5812
|
position: "relative"
|
|
5808
5813
|
};
|
|
5809
|
-
var r$
|
|
5814
|
+
var r$31 = {
|
|
5810
5815
|
...n$37,
|
|
5811
5816
|
"line-height": "20px"
|
|
5812
5817
|
};
|
|
5813
|
-
var i$
|
|
5818
|
+
var i$26 = {
|
|
5814
5819
|
display: "flex",
|
|
5815
5820
|
cursor: "pointer",
|
|
5816
5821
|
"font-weight": "600",
|
|
@@ -5820,7 +5825,7 @@ var i$25 = {
|
|
|
5820
5825
|
"user-select": "none"
|
|
5821
5826
|
};
|
|
5822
5827
|
var a$21 = {
|
|
5823
|
-
...i$
|
|
5828
|
+
...i$26,
|
|
5824
5829
|
"margin-bottom": "4px"
|
|
5825
5830
|
};
|
|
5826
5831
|
var o$23 = {
|
|
@@ -5828,7 +5833,7 @@ var o$23 = {
|
|
|
5828
5833
|
color: "red",
|
|
5829
5834
|
"margin-top": "4px"
|
|
5830
5835
|
};
|
|
5831
|
-
var s$
|
|
5836
|
+
var s$20 = {
|
|
5832
5837
|
color: "red",
|
|
5833
5838
|
"margin-left": "4px"
|
|
5834
5839
|
};
|
|
@@ -5847,14 +5852,14 @@ var l$13 = {
|
|
|
5847
5852
|
"padding-left": "0px",
|
|
5848
5853
|
display: "block",
|
|
5849
5854
|
...c$18,
|
|
5850
|
-
"background-color": "
|
|
5855
|
+
"background-color": "transparent",
|
|
5851
5856
|
color: "inherit",
|
|
5852
5857
|
"line-height": "inherit",
|
|
5853
5858
|
"font-family": "inherit",
|
|
5854
5859
|
"font-size": "inherit",
|
|
5855
5860
|
"box-shadow": "none"
|
|
5856
5861
|
};
|
|
5857
|
-
var u$
|
|
5862
|
+
var u$14 = {
|
|
5858
5863
|
height: "100%",
|
|
5859
5864
|
width: "100%",
|
|
5860
5865
|
display: "block",
|
|
@@ -5868,7 +5873,7 @@ var u$13 = {
|
|
|
5868
5873
|
cursor: "inherit",
|
|
5869
5874
|
"box-shadow": "none"
|
|
5870
5875
|
};
|
|
5871
|
-
var d$
|
|
5876
|
+
var d$12 = {
|
|
5872
5877
|
height: "100%",
|
|
5873
5878
|
"margin-top": "0px",
|
|
5874
5879
|
"margin-right": "0px",
|
|
@@ -5888,11 +5893,11 @@ var d$13 = {
|
|
|
5888
5893
|
"font-weight": "inherit",
|
|
5889
5894
|
cursor: "pointer"
|
|
5890
5895
|
};
|
|
5891
|
-
var f$
|
|
5896
|
+
var f$10 = {
|
|
5892
5897
|
...n$37,
|
|
5893
5898
|
cursor: "pointer"
|
|
5894
5899
|
};
|
|
5895
|
-
var p$
|
|
5900
|
+
var p$11 = {
|
|
5896
5901
|
height: "20px",
|
|
5897
5902
|
width: "36px",
|
|
5898
5903
|
"margin-top": "0px",
|
|
@@ -5922,7 +5927,7 @@ var p$10 = {
|
|
|
5922
5927
|
appearance: "none",
|
|
5923
5928
|
cursor: "pointer"
|
|
5924
5929
|
};
|
|
5925
|
-
var m$
|
|
5930
|
+
var m$7 = (e, t, { field: n, icon: r } = {}) => ({
|
|
5926
5931
|
name: e,
|
|
5927
5932
|
displayMode: "desktop",
|
|
5928
5933
|
style: {
|
|
@@ -5931,14 +5936,14 @@ var m$6 = (e, t, { field: n, icon: r } = {}) => ({
|
|
|
5931
5936
|
...n ? { field: { default: n } } : {},
|
|
5932
5937
|
...r ? { icon: { default: r } } : {},
|
|
5933
5938
|
label: { default: a$21 },
|
|
5934
|
-
requiredMark: { default: s$
|
|
5939
|
+
requiredMark: { default: s$20 },
|
|
5935
5940
|
error: { default: o$23 }
|
|
5936
5941
|
}
|
|
5937
5942
|
});
|
|
5938
5943
|
var h$4 = { field: l$13 };
|
|
5939
5944
|
var g$4 = t$60()({
|
|
5940
5945
|
type: "formControl",
|
|
5941
|
-
attributeValues: { subType: e$
|
|
5946
|
+
attributeValues: { subType: e$40()([
|
|
5942
5947
|
"text",
|
|
5943
5948
|
"number",
|
|
5944
5949
|
"email",
|
|
@@ -5977,7 +5982,7 @@ var g$4 = t$60()({
|
|
|
5977
5982
|
autoFocus: !1,
|
|
5978
5983
|
disabled: !1,
|
|
5979
5984
|
options: [],
|
|
5980
|
-
required: !
|
|
5985
|
+
required: !1,
|
|
5981
5986
|
requiredMessage: "",
|
|
5982
5987
|
minLength: 0,
|
|
5983
5988
|
minLengthMessage: "",
|
|
@@ -5993,7 +5998,7 @@ var g$4 = t$60()({
|
|
|
5993
5998
|
definition: {
|
|
5994
5999
|
label: "Form Control",
|
|
5995
6000
|
type: "formControl",
|
|
5996
|
-
description: "A single labelled input (text/select/checkbox/… per its subType) inside a form; captures one field of user input. A select offers its `options`: `[{ label, value }, …]` — the text shown, and what the field takes. A `switch` is a checkbox announced and drawn as an on/off switch (`role=\"switch\"`), for a setting that applies at once; both hold `true`/`false`, and a `defaultValue` of `\"true\"` starts them on. Its slots: `label`, `requiredMark` (the `*` that ends the label of a required field), `input` (the box a typed field or a select is drawn in — a textarea, checkbox or switch is its own box), `field` (the `<input>` or `<select>` inside that box), `icon` (the show/hide button of a password) and `error`. The thumb of a switch is no element a class reaches: the class on its `input` slot sets `--plitzi-switch-thumb` (off), `--plitzi-switch-thumb-checked` (on, white by default) and `--plitzi-switch-thumb-shadow`.",
|
|
6001
|
+
description: "A single labelled input (text/select/checkbox/… per its subType) inside a form; captures one field of user input. Optional unless `required: true`, as an HTML field is: a `requiredMessage` alone requires nothing. A select offers its `options`: `[{ label, value }, …]` — the text shown, and what the field takes. A `switch` is a checkbox announced and drawn as an on/off switch (`role=\"switch\"`), for a setting that applies at once; both hold `true`/`false`, and a `defaultValue` of `\"true\"` starts them on. Its slots: `label`, `requiredMark` (the `*` that ends the label of a required field), `input` (the box a typed field or a select is drawn in — a textarea, checkbox or switch is its own box), `field` (the `<input>` or `<select>` inside that box), `icon` (the show/hide button of a password) and `error`. The box is ringed (`2px solid currentColor`) while the field inside has keyboard focus: restyle it with a `focus-within` state on the `input` slot — the box never takes `focus` itself. The thumb of a switch is no element a class reaches: the class on its `input` slot sets `--plitzi-switch-thumb` (off), `--plitzi-switch-thumb-checked` (on, white by default) and `--plitzi-switch-thumb-shadow`.",
|
|
5997
6002
|
bindings: {},
|
|
5998
6003
|
styleSelectors: {
|
|
5999
6004
|
base: "",
|
|
@@ -6029,26 +6034,26 @@ var g$4 = t$60()({
|
|
|
6029
6034
|
displayMode: "desktop",
|
|
6030
6035
|
style: { base: { default: {} } },
|
|
6031
6036
|
subTypes: {
|
|
6032
|
-
hidden: m$
|
|
6033
|
-
text: m$
|
|
6034
|
-
number: m$
|
|
6035
|
-
email: m$
|
|
6036
|
-
password: m$
|
|
6037
|
+
hidden: m$7("Form Control Hidden", {}),
|
|
6038
|
+
text: m$7("Form Control Text", r$31, h$4),
|
|
6039
|
+
number: m$7("Form Control Number", r$31, h$4),
|
|
6040
|
+
email: m$7("Form Control Email", r$31, h$4),
|
|
6041
|
+
password: m$7("Form Control Password", r$31, {
|
|
6037
6042
|
...h$4,
|
|
6038
|
-
icon: d$
|
|
6043
|
+
icon: d$12
|
|
6039
6044
|
}),
|
|
6040
|
-
search: m$
|
|
6041
|
-
url: m$
|
|
6042
|
-
tel: m$
|
|
6043
|
-
date: m$
|
|
6044
|
-
time: m$
|
|
6045
|
-
color: m$
|
|
6046
|
-
textarea: m$
|
|
6047
|
-
...r$
|
|
6045
|
+
search: m$7("Form Control Search", r$31, h$4),
|
|
6046
|
+
url: m$7("Form Control URL", r$31, h$4),
|
|
6047
|
+
tel: m$7("Form Control Phone", r$31, h$4),
|
|
6048
|
+
date: m$7("Form Control Date", r$31, h$4),
|
|
6049
|
+
time: m$7("Form Control Time", r$31, h$4),
|
|
6050
|
+
color: m$7("Form Control Color", r$31, h$4),
|
|
6051
|
+
textarea: m$7("Form Control Textarea", {
|
|
6052
|
+
...r$31,
|
|
6048
6053
|
color: "inherit",
|
|
6049
6054
|
resize: "vertical"
|
|
6050
6055
|
}),
|
|
6051
|
-
select: m$
|
|
6056
|
+
select: m$7("Form Control Select", f$10, { field: u$14 }),
|
|
6052
6057
|
checkbox: {
|
|
6053
6058
|
name: "Form Control Checkbox",
|
|
6054
6059
|
displayMode: "desktop",
|
|
@@ -6060,8 +6065,8 @@ var g$4 = t$60()({
|
|
|
6060
6065
|
"margin-left": "0px",
|
|
6061
6066
|
"margin-right": "4px"
|
|
6062
6067
|
} },
|
|
6063
|
-
label: { default: i$
|
|
6064
|
-
requiredMark: { default: s$
|
|
6068
|
+
label: { default: i$26 },
|
|
6069
|
+
requiredMark: { default: s$20 },
|
|
6065
6070
|
error: { default: o$23 }
|
|
6066
6071
|
}
|
|
6067
6072
|
},
|
|
@@ -6070,12 +6075,12 @@ var g$4 = t$60()({
|
|
|
6070
6075
|
displayMode: "desktop",
|
|
6071
6076
|
style: {
|
|
6072
6077
|
base: { default: {} },
|
|
6073
|
-
input: { default: p$
|
|
6078
|
+
input: { default: p$11 },
|
|
6074
6079
|
label: { default: {
|
|
6075
|
-
...i$
|
|
6080
|
+
...i$26,
|
|
6076
6081
|
"align-items": "center"
|
|
6077
6082
|
} },
|
|
6078
|
-
requiredMark: { default: s$
|
|
6083
|
+
requiredMark: { default: s$20 },
|
|
6079
6084
|
error: { default: o$23 }
|
|
6080
6085
|
}
|
|
6081
6086
|
}
|
|
@@ -6086,7 +6091,7 @@ var g$4 = t$60()({
|
|
|
6086
6091
|
});
|
|
6087
6092
|
var n$36 = t$60()({
|
|
6088
6093
|
type: "layoutContainer",
|
|
6089
|
-
attributeValues: { subType: e$
|
|
6094
|
+
attributeValues: { subType: e$40()([
|
|
6090
6095
|
"div",
|
|
6091
6096
|
"header",
|
|
6092
6097
|
"footer",
|
|
@@ -6218,6 +6223,11 @@ var t$49 = t$60()({
|
|
|
6218
6223
|
});
|
|
6219
6224
|
var t$48 = t$60()({
|
|
6220
6225
|
type: "page",
|
|
6226
|
+
serverTemplates: [
|
|
6227
|
+
"seoPageTitle",
|
|
6228
|
+
"seoPageDescription",
|
|
6229
|
+
"notFound"
|
|
6230
|
+
],
|
|
6221
6231
|
triggers: { onPageLoad: {
|
|
6222
6232
|
action: "onPageLoad",
|
|
6223
6233
|
title: "On Page Load",
|
|
@@ -6385,12 +6395,12 @@ var t$46 = t$60()({
|
|
|
6385
6395
|
var n$35 = t$60()({
|
|
6386
6396
|
type: "image",
|
|
6387
6397
|
attributeValues: {
|
|
6388
|
-
fetchPriority: e$
|
|
6398
|
+
fetchPriority: e$40()([
|
|
6389
6399
|
"high",
|
|
6390
6400
|
"low",
|
|
6391
6401
|
"auto"
|
|
6392
6402
|
]),
|
|
6393
|
-
loadMode: e$
|
|
6403
|
+
loadMode: e$40()([
|
|
6394
6404
|
"auto",
|
|
6395
6405
|
"eager",
|
|
6396
6406
|
"lazy"
|
|
@@ -6539,7 +6549,7 @@ var t$44 = t$60()({
|
|
|
6539
6549
|
var n$34 = t$60()({
|
|
6540
6550
|
type: "apiContainer",
|
|
6541
6551
|
attributeValues: {
|
|
6542
|
-
subType: e$
|
|
6552
|
+
subType: e$40()([
|
|
6543
6553
|
"",
|
|
6544
6554
|
"div",
|
|
6545
6555
|
"header",
|
|
@@ -6552,25 +6562,26 @@ var n$34 = t$60()({
|
|
|
6552
6562
|
"address",
|
|
6553
6563
|
"figure"
|
|
6554
6564
|
]),
|
|
6555
|
-
method: e$
|
|
6565
|
+
method: e$40()([
|
|
6556
6566
|
"get",
|
|
6557
6567
|
"post",
|
|
6558
6568
|
"put",
|
|
6559
6569
|
"delete",
|
|
6560
6570
|
"patch"
|
|
6561
6571
|
]),
|
|
6562
|
-
credentials: e$
|
|
6572
|
+
credentials: e$40()([
|
|
6563
6573
|
"include",
|
|
6564
6574
|
"omit",
|
|
6565
6575
|
"same-origin"
|
|
6566
6576
|
]),
|
|
6567
|
-
pagination: e$
|
|
6577
|
+
pagination: e$40()([
|
|
6568
6578
|
"none",
|
|
6569
6579
|
"url",
|
|
6570
6580
|
"append"
|
|
6571
6581
|
])
|
|
6572
6582
|
},
|
|
6573
6583
|
sourceType: "apiContainer",
|
|
6584
|
+
serverTemplates: ["notFound"],
|
|
6574
6585
|
triggers: {
|
|
6575
6586
|
onApiError: {
|
|
6576
6587
|
action: "onApiError",
|
|
@@ -6677,7 +6688,7 @@ var n$34 = t$60()({
|
|
|
6677
6688
|
definition: {
|
|
6678
6689
|
label: "Api Container",
|
|
6679
6690
|
type: "apiContainer",
|
|
6680
|
-
description: "Fetches data from a backend HTTP API (its `query`/`method`/`credentials`) and exposes the response as a data source ITS DESCENDANTS bind to (source `apiContainer_<id>.data`; only elements inside it can consume it). This is how you get backend data into the frontend. Its `mockData` prop is builder-only sample data — the published runtime fetches the real `query`, so always set a real query for production. With `cache: true` a browser request is kept for `staleTime` seconds (default 30) and shared with every provider asking the same thing; it is off unless set. A flow refreshes it with `performQuery` or the global `invalidateQueries` step, and `refreshSeconds` makes it ask again on its own every N seconds (either runtime; paused while the tab is hidden) — the way to keep a queue, feed or status board current without a plugin. Its `subType` (container tag) is empty by default, and then it renders NO element of its own: its children lay out directly in its parent, and any class, variant or style binding on it applies to nothing. To style the provider itself give it a tag (`subType: \"div\"`, `section`, …); otherwise style its parent or a child.",
|
|
6691
|
+
description: "Fetches data from a backend HTTP API (its `query`/`method`/`credentials`) and exposes the response as a data source ITS DESCENDANTS bind to (source `apiContainer_<id>.data`; only elements inside it can consume it). This is how you get backend data into the frontend. Its `mockData` prop is builder-only sample data — the published runtime fetches the real `query`, so always set a real query for production. With `cache: true` a browser request is kept for `staleTime` seconds (default 30) and shared with every provider asking the same thing; it is off unless set. A flow refreshes it with `performQuery` or the global `invalidateQueries` step, and `refreshSeconds` makes it ask again on its own every N seconds (either runtime; paused while the tab is hidden) — the way to keep a queue, feed or status board current without a plugin. A server provider (`runtime: \"server\"`) whose answer means the address shows nothing — a record not found — says so in `notFound`, a template against its answer (`{{ source.found == false }}`): the page is sent with status 404, rendered as written. Its `subType` (container tag) is empty by default, and then it renders NO element of its own: its children lay out directly in its parent, and any class, variant or style binding on it applies to nothing. To style the provider itself give it a tag (`subType: \"div\"`, `section`, …); otherwise style its parent or a child.",
|
|
6681
6692
|
items: [],
|
|
6682
6693
|
bindings: {},
|
|
6683
6694
|
styleSelectors: { base: "" },
|
|
@@ -6712,7 +6723,7 @@ var n$34 = t$60()({
|
|
|
6712
6723
|
});
|
|
6713
6724
|
var n$33 = t$60()({
|
|
6714
6725
|
type: "channel",
|
|
6715
|
-
attributeValues: { subType: e$
|
|
6726
|
+
attributeValues: { subType: e$40()([
|
|
6716
6727
|
"",
|
|
6717
6728
|
"div",
|
|
6718
6729
|
"section",
|
|
@@ -6832,6 +6843,49 @@ var n$33 = t$60()({
|
|
|
6832
6843
|
}
|
|
6833
6844
|
});
|
|
6834
6845
|
var t$43 = t$60()({
|
|
6846
|
+
type: "carouselTrack",
|
|
6847
|
+
ancestorType: "carousel",
|
|
6848
|
+
content: {
|
|
6849
|
+
attributes: {},
|
|
6850
|
+
definition: {
|
|
6851
|
+
label: "Carousel Track",
|
|
6852
|
+
type: "carouselTrack",
|
|
6853
|
+
description: "Where a carousel shows its slides: what is inside is one slide, rendered once per item.",
|
|
6854
|
+
items: [],
|
|
6855
|
+
bindings: {},
|
|
6856
|
+
styleSelectors: { base: "" },
|
|
6857
|
+
initialState: { visibility: !0 }
|
|
6858
|
+
},
|
|
6859
|
+
builder: {
|
|
6860
|
+
canDelete: !0,
|
|
6861
|
+
canSelect: !0,
|
|
6862
|
+
canDragDrop: !0,
|
|
6863
|
+
canMove: !0,
|
|
6864
|
+
canSnippet: !1,
|
|
6865
|
+
itemsAllowed: [],
|
|
6866
|
+
itemsNotAllowed: []
|
|
6867
|
+
},
|
|
6868
|
+
market: {
|
|
6869
|
+
category: "structure",
|
|
6870
|
+
owner: "Plitzi",
|
|
6871
|
+
verified: !0,
|
|
6872
|
+
license: "MIT",
|
|
6873
|
+
website: "https://plitzi.com",
|
|
6874
|
+
backgroundColor: "#4422ee",
|
|
6875
|
+
icon: "fa-solid fa-film"
|
|
6876
|
+
},
|
|
6877
|
+
defaultStyle: {
|
|
6878
|
+
name: "Carousel Track",
|
|
6879
|
+
displayMode: "desktop",
|
|
6880
|
+
style: { base: { default: {
|
|
6881
|
+
position: "relative",
|
|
6882
|
+
overflow: "hidden"
|
|
6883
|
+
} } }
|
|
6884
|
+
},
|
|
6885
|
+
settings: {}
|
|
6886
|
+
}
|
|
6887
|
+
});
|
|
6888
|
+
var t$42 = t$60()({
|
|
6835
6889
|
type: "carousel",
|
|
6836
6890
|
sourceType: "carousel",
|
|
6837
6891
|
triggers: { onChange: {
|
|
@@ -6933,53 +6987,10 @@ var t$43 = t$60()({
|
|
|
6933
6987
|
},
|
|
6934
6988
|
initialItems: ["carouselTrack"]
|
|
6935
6989
|
});
|
|
6936
|
-
var
|
|
6937
|
-
type: "carouselTrack",
|
|
6938
|
-
ancestorType: "carousel",
|
|
6939
|
-
content: {
|
|
6940
|
-
attributes: {},
|
|
6941
|
-
definition: {
|
|
6942
|
-
label: "Carousel Track",
|
|
6943
|
-
type: "carouselTrack",
|
|
6944
|
-
description: "Where a carousel shows its slides: what is inside is one slide, rendered once per item.",
|
|
6945
|
-
items: [],
|
|
6946
|
-
bindings: {},
|
|
6947
|
-
styleSelectors: { base: "" },
|
|
6948
|
-
initialState: { visibility: !0 }
|
|
6949
|
-
},
|
|
6950
|
-
builder: {
|
|
6951
|
-
canDelete: !0,
|
|
6952
|
-
canSelect: !0,
|
|
6953
|
-
canDragDrop: !0,
|
|
6954
|
-
canMove: !0,
|
|
6955
|
-
canSnippet: !1,
|
|
6956
|
-
itemsAllowed: [],
|
|
6957
|
-
itemsNotAllowed: []
|
|
6958
|
-
},
|
|
6959
|
-
market: {
|
|
6960
|
-
category: "structure",
|
|
6961
|
-
owner: "Plitzi",
|
|
6962
|
-
verified: !0,
|
|
6963
|
-
license: "MIT",
|
|
6964
|
-
website: "https://plitzi.com",
|
|
6965
|
-
backgroundColor: "#4422ee",
|
|
6966
|
-
icon: "fa-solid fa-film"
|
|
6967
|
-
},
|
|
6968
|
-
defaultStyle: {
|
|
6969
|
-
name: "Carousel Track",
|
|
6970
|
-
displayMode: "desktop",
|
|
6971
|
-
style: { base: { default: {
|
|
6972
|
-
position: "relative",
|
|
6973
|
-
overflow: "hidden"
|
|
6974
|
-
} } }
|
|
6975
|
-
},
|
|
6976
|
-
settings: {}
|
|
6977
|
-
}
|
|
6978
|
-
});
|
|
6979
|
-
var r$31 = t$60()({
|
|
6990
|
+
var r$30 = t$60()({
|
|
6980
6991
|
type: "container",
|
|
6981
6992
|
attributeValues: {
|
|
6982
|
-
subType: e$
|
|
6993
|
+
subType: e$40()([
|
|
6983
6994
|
"div",
|
|
6984
6995
|
"header",
|
|
6985
6996
|
"footer",
|
|
@@ -7003,7 +7014,7 @@ var r$31 = t$60()({
|
|
|
7003
7014
|
"p",
|
|
7004
7015
|
"span"
|
|
7005
7016
|
]),
|
|
7006
|
-
live: e$
|
|
7017
|
+
live: e$40()([
|
|
7007
7018
|
"",
|
|
7008
7019
|
"polite",
|
|
7009
7020
|
"assertive"
|
|
@@ -7093,11 +7104,11 @@ var B$2 = {
|
|
|
7093
7104
|
ApiContainer: n$34,
|
|
7094
7105
|
BlockHtml: t$59,
|
|
7095
7106
|
BlockJsx: t$58,
|
|
7096
|
-
Button: r$
|
|
7097
|
-
Carousel: t$
|
|
7098
|
-
CarouselTrack: t$
|
|
7107
|
+
Button: r$33,
|
|
7108
|
+
Carousel: t$42,
|
|
7109
|
+
CarouselTrack: t$43,
|
|
7099
7110
|
Channel: n$33,
|
|
7100
|
-
Container: r$
|
|
7111
|
+
Container: r$30,
|
|
7101
7112
|
Custom: t$57,
|
|
7102
7113
|
DialogContainer: t$60()({
|
|
7103
7114
|
type: "dialogContainer",
|
|
@@ -7228,8 +7239,7 @@ var B$2 = {
|
|
|
7228
7239
|
top: "0",
|
|
7229
7240
|
position: "absolute",
|
|
7230
7241
|
"z-index": "210",
|
|
7231
|
-
"background-color": "
|
|
7232
|
-
opacity: "0.5"
|
|
7242
|
+
"background-color": "rgb(0 0 0 / 50%)"
|
|
7233
7243
|
} },
|
|
7234
7244
|
rootContainer: { default: {
|
|
7235
7245
|
display: "flex",
|
|
@@ -7300,8 +7310,8 @@ var B$2 = {
|
|
|
7300
7310
|
List: t$60()({
|
|
7301
7311
|
type: "list",
|
|
7302
7312
|
attributeValues: {
|
|
7303
|
-
subType: e$
|
|
7304
|
-
source: e$
|
|
7313
|
+
subType: e$40()(["ul", "ol"]),
|
|
7314
|
+
source: e$40()(["none", "controlled"])
|
|
7305
7315
|
},
|
|
7306
7316
|
sourceType: "list",
|
|
7307
7317
|
content: {
|
|
@@ -7464,7 +7474,7 @@ var B$2 = {
|
|
|
7464
7474
|
definition: {
|
|
7465
7475
|
label: "Modal Container",
|
|
7466
7476
|
type: "modalContainer",
|
|
7467
|
-
description: "A modal overlay container opened and closed through interactions; use for dialogs over the page.",
|
|
7477
|
+
description: "A modal overlay container opened and closed through interactions; use for dialogs over the page. The element itself is fixed over the whole viewport, and holds two layers side by side — neither inside the other: `backgroundContainer`, the dim layer, `rgb(0 0 0 / 50%)` (its `background-color` is the whole dim), and `rootContainer`, the dialog, centred by `top: 50%`, `left: 50%` and `transform: translate3d(-50%, -50%, 0)` — to sit it near the top, set `top` and `transform: translateX(-50%)`; flex alignment on the dim layer moves nothing. Inside the dialog, top to bottom: `headerContainer` (with `headerTitle` and `headerCloseButton`) and `bodyContainer`.",
|
|
7468
7478
|
items: [],
|
|
7469
7479
|
bindings: {},
|
|
7470
7480
|
loadStrategy: "lazy",
|
|
@@ -7516,8 +7526,7 @@ var B$2 = {
|
|
|
7516
7526
|
top: "0",
|
|
7517
7527
|
position: "absolute",
|
|
7518
7528
|
"z-index": "210",
|
|
7519
|
-
"background-color": "
|
|
7520
|
-
opacity: "0.5"
|
|
7529
|
+
"background-color": "rgb(0 0 0 / 50%)"
|
|
7521
7530
|
} },
|
|
7522
7531
|
rootContainer: { default: {
|
|
7523
7532
|
display: "flex",
|
|
@@ -7601,8 +7610,8 @@ var B$2 = {
|
|
|
7601
7610
|
Pagination: t$60()({
|
|
7602
7611
|
type: "pagination",
|
|
7603
7612
|
attributeValues: {
|
|
7604
|
-
mode: e$
|
|
7605
|
-
target: e$
|
|
7613
|
+
mode: e$40()(["pages", "loadMore"]),
|
|
7614
|
+
target: e$40()(["url", "interaction"])
|
|
7606
7615
|
},
|
|
7607
7616
|
triggers: { onPageChange: {
|
|
7608
7617
|
action: "onPageChange",
|
|
@@ -7851,9 +7860,10 @@ var B$2 = {
|
|
|
7851
7860
|
}
|
|
7852
7861
|
}),
|
|
7853
7862
|
Text: t$51,
|
|
7854
|
-
ThemeToggle: r$
|
|
7863
|
+
ThemeToggle: r$32,
|
|
7855
7864
|
Video: t$44
|
|
7856
7865
|
};
|
|
7866
|
+
Object.fromEntries(Object.values(B$2).flatMap((e) => "serverTemplates" in e ? [[e.type, e.serverTemplates]] : []));
|
|
7857
7867
|
/** Every declaration, read through the part of its shape this catalog uses. */
|
|
7858
7868
|
var declarations$1 = B$2;
|
|
7859
7869
|
/** Keyed by the schema `type`, which is what a document and every catalog address a type by. */
|
|
@@ -7864,7 +7874,7 @@ var elementCatalog = Object.fromEntries(Object.values(declarations$1).map((decla
|
|
|
7864
7874
|
}]));
|
|
7865
7875
|
Object.keys(elementCatalog);
|
|
7866
7876
|
Object.fromEntries(Object.values(declarations$1).filter((declaration) => declaration.sourceType).map((declaration) => [declaration.type, declaration.sourceType]));
|
|
7867
|
-
Object.fromEntries(Object.values(declarations$1).map((declaration) => [declaration.type, [...Object.keys(e$
|
|
7877
|
+
Object.fromEntries(Object.values(declarations$1).map((declaration) => [declaration.type, [...Object.keys(e$41), ...Object.keys(declaration.triggers ?? {})]]));
|
|
7868
7878
|
/** The triggers only some types fire, by action name — for a step builder that wants the title the builder shows. */
|
|
7869
7879
|
var typeTriggerDefinitions = Object.fromEntries(Object.values(declarations$1).flatMap((declaration) => Object.entries(declaration.triggers ?? {})));
|
|
7870
7880
|
Object.fromEntries(Object.values(declarations$1).flatMap((declaration) => Object.entries(declaration.callbacks ?? {})));
|
|
@@ -7965,6 +7975,11 @@ var AUTHORING_CODES = {
|
|
|
7965
7975
|
means: "a page that sends visitors it is not for to a page the space does not have",
|
|
7966
7976
|
fix: "a page's id or slug (`''` is the home page), or a full URL"
|
|
7967
7977
|
},
|
|
7978
|
+
"page-template": {
|
|
7979
|
+
kind: "refused",
|
|
7980
|
+
means: "a template in a page's `seoTitle`, `seoDescription` or `notFound` that reads what is not there when the server answers — anything but the page's `runtime: 'server'` providers and `navigation` — or cannot be read",
|
|
7981
|
+
fix: "read the record from a provider with `runtime: 'server'` on the page or its layout — `'{{ apiContainer_post.title }} — Blog'` — or words of its own"
|
|
7982
|
+
},
|
|
7968
7983
|
"folder-undeclared": {
|
|
7969
7984
|
kind: "refused",
|
|
7970
7985
|
means: "a page, a layout or a folder filed in a folder the space does not declare",
|
|
@@ -8140,6 +8155,11 @@ var AUTHORING_CODES = {
|
|
|
8140
8155
|
means: "an ancestor condition keyed by something that is neither a class name nor `'>'` (the parent)",
|
|
8141
8156
|
fix: "`[card.name]` for a `styles()` declaration, or `'>'` for the parent whatever it wears"
|
|
8142
8157
|
},
|
|
8158
|
+
"focus-on-field-box": {
|
|
8159
|
+
kind: "warned",
|
|
8160
|
+
means: "a `focus` or `focus-visible` state on a text field's or a select's `input` slot — the box the field is drawn in, which never takes focus, so no ring is ever shown",
|
|
8161
|
+
fix: "`'focus-within'` on the same class, or the state on the `field` slot"
|
|
8162
|
+
},
|
|
8143
8163
|
"heading-level-overridden": {
|
|
8144
8164
|
kind: "warned",
|
|
8145
8165
|
means: "a markdown's or richText's `heading` slot and a level's (`heading3`) both setting one property, the general class written later in the stylesheet — the level's value is lost",
|
|
@@ -8370,6 +8390,16 @@ var AUTHORING_CODES = {
|
|
|
8370
8390
|
means: "a `runtime: 'server'` element inside a component: the page server resolves a page's and its layouts', never a component's",
|
|
8371
8391
|
fix: "put the provider on the page, around the instance, and hand the component its rows as a prop"
|
|
8372
8392
|
},
|
|
8393
|
+
"not-found-in-browser": {
|
|
8394
|
+
kind: "refused",
|
|
8395
|
+
means: "`notFound` on a provider asked from the browser — its answer arrives after the page was sent with its status",
|
|
8396
|
+
fix: "`runtime: 'server'` on the provider, or `visible` on the page's \"not found\" part instead"
|
|
8397
|
+
},
|
|
8398
|
+
"not-found-not-a-template": {
|
|
8399
|
+
kind: "refused",
|
|
8400
|
+
means: "`notFound`, a provider's or a page's, that is not one `{{ expression }}` — never `true`, so never a 404",
|
|
8401
|
+
fix: "`notFound: '{{ source.found == false }}'`"
|
|
8402
|
+
},
|
|
8373
8403
|
"server-data-in-browser": {
|
|
8374
8404
|
kind: "refused",
|
|
8375
8405
|
means: "a provider asking for the project's own data (`/data/…`, read by its server from `src/data/` and never served) from the browser",
|
|
@@ -8390,6 +8420,11 @@ var AUTHORING_CODES = {
|
|
|
8390
8420
|
means: "a control in a form with no `name` — its value never reaches `values`",
|
|
8391
8421
|
fix: "`formControl({ name: 'email', … })`"
|
|
8392
8422
|
},
|
|
8423
|
+
"required-message-unused": {
|
|
8424
|
+
kind: "warned",
|
|
8425
|
+
means: "a `requiredMessage` on a field nothing requires — fields are optional unless `required: true`, so it is never shown and an empty answer is sent",
|
|
8426
|
+
fix: "`required: true` on the field, or no message"
|
|
8427
|
+
},
|
|
8393
8428
|
"form-control-name-taken": {
|
|
8394
8429
|
kind: "warned",
|
|
8395
8430
|
means: "two controls in one form with one name — one overwrites the other",
|
|
@@ -8855,6 +8890,11 @@ var AUTHORING_CODES = {
|
|
|
8855
8890
|
means: "a colour written out in a class where the space has a token of that value — it stays put in the dark scheme",
|
|
8856
8891
|
fix: "`var(--token)` when it should follow the scheme; a token of one value of its own when it must stay the same in both"
|
|
8857
8892
|
},
|
|
8893
|
+
"not-found-page": {
|
|
8894
|
+
kind: "suggested",
|
|
8895
|
+
means: "a space with no page of its own for an address no other page answers — a plain one was added, in the home page’s layout, sent with status 404",
|
|
8896
|
+
fix: "a page whose slug is `'*'`: `{ id: 'not-found', name: 'Not found', slug: '*', layout: …, body: [...] }`"
|
|
8897
|
+
},
|
|
8858
8898
|
"unused-component": {
|
|
8859
8899
|
kind: "suggested",
|
|
8860
8900
|
means: "a component no page, layout or other component places",
|
|
@@ -8965,7 +9005,7 @@ var sourceRow = (listSource, items) => {
|
|
|
8965
9005
|
index: pathTo(`${listSource}.index`, 0)
|
|
8966
9006
|
};
|
|
8967
9007
|
};
|
|
8968
|
-
var e$
|
|
9008
|
+
var e$39 = [
|
|
8969
9009
|
"variables",
|
|
8970
9010
|
"navigation",
|
|
8971
9011
|
"auth",
|
|
@@ -8975,7 +9015,7 @@ var e$38 = [
|
|
|
8975
9015
|
"flags",
|
|
8976
9016
|
"computed"
|
|
8977
9017
|
];
|
|
8978
|
-
e$
|
|
9018
|
+
e$39.filter((e) => e !== "state" && e !== "computed");
|
|
8979
9019
|
var t$30 = {
|
|
8980
9020
|
DISPLAY: "display",
|
|
8981
9021
|
FLEX_WRAP: "flex-wrap",
|
|
@@ -9154,6 +9194,8 @@ var t$30 = {
|
|
|
9154
9194
|
TEXT_INDENT: "text-indent",
|
|
9155
9195
|
TEXT_TRANSFORM: "text-transform",
|
|
9156
9196
|
DIRECTION: "direction",
|
|
9197
|
+
WRITING_MODE: "writing-mode",
|
|
9198
|
+
TEXT_ORIENTATION: "text-orientation",
|
|
9157
9199
|
WHITE_SPACE: "white-space",
|
|
9158
9200
|
TEXT_WRAP: "text-wrap",
|
|
9159
9201
|
WORD_BREAK: "word-break",
|
|
@@ -9178,8 +9220,8 @@ Object.values(t$30).map((e) => ({
|
|
|
9178
9220
|
path: e,
|
|
9179
9221
|
label: e
|
|
9180
9222
|
}));
|
|
9181
|
-
t$30.DISPLAY, t$30.FLEX_WRAP, t$30.FLEX_DIRECTION, t$30.ALIGN_ITEMS, t$30.JUSTIFY_ITEMS, t$30.JUSTIFY_CONTENT, t$30.ALIGN_CONTENT, t$30.ROW_GAP, t$30.COLUMN_GAP, t$30.GRID_ROW_GAP, t$30.GRID_COLUMN_GAP, t$30.GRID_TEMPLATE_AREAS, t$30.GRID_TEMPLATE_COLUMNS, t$30.GRID_TEMPLATE_ROWS, t$30.GRID_AUTO_FLOW, t$30.GRID_AUTO_ROWS, t$30.GRID_AUTO_COLUMNS, t$30.GRID_ROW_START, t$30.GRID_COLUMN_START, t$30.GRID_ROW_END, t$30.GRID_COLUMN_END, t$30.ALIGN_SELF, t$30.JUSTIFY_SELF, t$30.ORDER, t$30.FLEX_GROW, t$30.FLEX_SHRINK, t$30.FLEX_BASIS, t$30.LIST_STYLE, t$30.LIST_STYLE_TYPE, t$30.LIST_STYLE_POSITION, t$30.LIST_STYLE_IMAGE, t$30.BACKGROUND_COLOR, t$30.BACKGROUND_IMAGE, t$30.BACKGROUND_POSITION, t$30.BACKGROUND_SIZE, t$30.BACKGROUND_CLIP, t$30.BACKGROUND_ORIGIN, t$30.BACKGROUND_REPEAT, t$30.BACKGROUND_ATTACHMENT, t$30.BACKGROUND_BLEND_MODE, t$30.MASK_IMAGE, t$30.MASK_SIZE, t$30.MASK_POSITION, t$30.MASK_REPEAT, t$30.MASK_COMPOSITE, t$30.WEBKIT_MASK_IMAGE, t$30.BORDER_TOP_STYLE, t$30.BORDER_TOP_WIDTH, t$30.BORDER_TOP_COLOR, t$30.BORDER_BOTTOM_STYLE, t$30.BORDER_BOTTOM_WIDTH, t$30.BORDER_BOTTOM_COLOR, t$30.BORDER_LEFT_STYLE, t$30.BORDER_LEFT_WIDTH, t$30.BORDER_LEFT_COLOR, t$30.BORDER_RIGHT_STYLE, t$30.BORDER_RIGHT_WIDTH, t$30.BORDER_RIGHT_COLOR, t$30.BORDER_TOP_LEFT_RADIUS, t$30.BORDER_TOP_RIGHT_RADIUS, t$30.BORDER_BOTTOM_LEFT_RADIUS, t$30.BORDER_BOTTOM_RIGHT_RADIUS, t$30.BORDER_COLLAPSE, t$30.BORDER_SPACING, t$30.TABLE_LAYOUT, t$30.OPACITY, t$30.VISIBILITY, t$30.CURSOR, t$30.TRANSITION, t$30.TRANSITION_PROPERTY, t$30.TRANSITION_DURATION, t$30.TRANSITION_TIMING_FUNCTION, t$30.TRANSITION_DELAY, t$30.TRANSITION_BEHAVIOR, t$30.ANIMATION, t$30.ANIMATION_NAME, t$30.ANIMATION_DURATION, t$30.ANIMATION_TIMING_FUNCTION, t$30.ANIMATION_DELAY, t$30.ANIMATION_ITERATION_COUNT, t$30.ANIMATION_DIRECTION, t$30.ANIMATION_FILL_MODE, t$30.ANIMATION_PLAY_STATE, t$30.ANIMATION_TIMELINE, t$30.ANIMATION_RANGE, t$30.BOX_SHADOW, t$30.FILTER, t$30.BACKDROP_FILTER, t$30.MIX_BLEND_MODE, t$30.ISOLATION, t$30.CLIP_PATH, t$30.TRANSFORM, t$30.TRANSFORM_ORIGIN, t$30.TRANSLATE, t$30.ROTATE, t$30.SCALE, t$30.TRANSFORM_STYLE, t$30.PERSPECTIVE, t$30.PERSPECTIVE_ORIGIN, t$30.BACKFACE_VISIBILITY, t$30.WILL_CHANGE, t$30.OUTLINE_WIDTH, t$30.OUTLINE_STYLE, t$30.OUTLINE_COLOR, t$30.OUTLINE_OFFSET, t$30.POINTER_EVENTS, t$30.USER_SELECT, t$30.TOUCH_ACTION, t$30.RESIZE, t$30.APPEARANCE, t$30.SCROLL_BEHAVIOR, t$30.OVERSCROLL_BEHAVIOR, t$30.SCROLL_SNAP_TYPE, t$30.SCROLL_SNAP_ALIGN, t$30.SCROLL_SNAP_STOP, t$30.SCROLL_PADDING_TOP, t$30.SCROLL_MARGIN_TOP, t$30.ACCENT_COLOR, t$30.CARET_COLOR, t$30.COLOR_SCHEME, t$30.POSITION, t$30.FLOAT, t$30.CLEAR, t$30.Z_INDEX, t$30.TOP, t$30.BOTTOM, t$30.LEFT, t$30.RIGHT, t$30.BOX_SIZING, t$30.ASPECT_RATIO, t$30.WIDTH, t$30.HEIGHT, t$30.MIN_WIDTH, t$30.MIN_HEIGHT, t$30.MAX_WIDTH, t$30.MAX_HEIGHT, t$30.OVERFLOW, t$30.OVERFLOW_X, t$30.OVERFLOW_Y, t$30.SCROLLBAR_WIDTH, t$30.SCROLLBAR_COLOR, t$30.CONTAINER_TYPE, t$30.CONTENT, t$30.CONTAINER_NAME, t$30.OBJECT_FIT, t$30.OBJECT_POSITION, t$30.COLUMN_WIDTH, t$30.COLUMN_COUNT, t$30.MARGIN_TOP, t$30.MARGIN_BOTTOM, t$30.MARGIN_LEFT, t$30.MARGIN_RIGHT, t$30.PADDING_TOP, t$30.PADDING_BOTTOM, t$30.PADDING_LEFT, t$30.PADDING_RIGHT, t$30.FONT_FAMILY, t$30.FONT_WEIGHT, t$30.FONT_SIZE, t$30.LINE_HEIGHT, t$30.COLOR, t$30.TEXT_ALIGN, t$30.FONT_STYLE, t$30.FONT_VARIANT, t$30.FONT_VARIANT_NUMERIC, t$30.FONT_FEATURE_SETTINGS, t$30.TEXT_DECORATION, t$30.TEXT_DECORATION_LINE, t$30.TEXT_DECORATION_COLOR, t$30.TEXT_DECORATION_STYLE, t$30.TEXT_DECORATION_THICKNESS, t$30.TEXT_UNDERLINE_OFFSET, t$30.LETTER_SPACING, t$30.WORD_SPACING, t$30.TEXT_INDENT, t$30.TEXT_TRANSFORM, t$30.DIRECTION, t$30.WHITE_SPACE, t$30.TEXT_WRAP, t$30.WORD_BREAK, t$30.OVERFLOW_WRAP, t$30.HYPHENS, t$30.VERTICAL_ALIGN, t$30.TEXT_OVERFLOW, t$30.TEXT_SHADOW, t$30.LINE_CLAMP, t$30.WEBKIT_LINE_CLAMP, t$30.WEBKIT_BOX_ORIENT, t$30.WEBKIT_TEXT_FILL_COLOR, t$30.WEBKIT_BACKGROUND_CLIP, t$30.BOX_DECORATION_BREAK, t$30.WEBKIT_BOX_DECORATION_BREAK, t$30.FILL, t$30.STROKE, t$30.STROKE_WIDTH;
|
|
9182
|
-
[...e$
|
|
9223
|
+
t$30.DISPLAY, t$30.FLEX_WRAP, t$30.FLEX_DIRECTION, t$30.ALIGN_ITEMS, t$30.JUSTIFY_ITEMS, t$30.JUSTIFY_CONTENT, t$30.ALIGN_CONTENT, t$30.ROW_GAP, t$30.COLUMN_GAP, t$30.GRID_ROW_GAP, t$30.GRID_COLUMN_GAP, t$30.GRID_TEMPLATE_AREAS, t$30.GRID_TEMPLATE_COLUMNS, t$30.GRID_TEMPLATE_ROWS, t$30.GRID_AUTO_FLOW, t$30.GRID_AUTO_ROWS, t$30.GRID_AUTO_COLUMNS, t$30.GRID_ROW_START, t$30.GRID_COLUMN_START, t$30.GRID_ROW_END, t$30.GRID_COLUMN_END, t$30.ALIGN_SELF, t$30.JUSTIFY_SELF, t$30.ORDER, t$30.FLEX_GROW, t$30.FLEX_SHRINK, t$30.FLEX_BASIS, t$30.LIST_STYLE, t$30.LIST_STYLE_TYPE, t$30.LIST_STYLE_POSITION, t$30.LIST_STYLE_IMAGE, t$30.BACKGROUND_COLOR, t$30.BACKGROUND_IMAGE, t$30.BACKGROUND_POSITION, t$30.BACKGROUND_SIZE, t$30.BACKGROUND_CLIP, t$30.BACKGROUND_ORIGIN, t$30.BACKGROUND_REPEAT, t$30.BACKGROUND_ATTACHMENT, t$30.BACKGROUND_BLEND_MODE, t$30.MASK_IMAGE, t$30.MASK_SIZE, t$30.MASK_POSITION, t$30.MASK_REPEAT, t$30.MASK_COMPOSITE, t$30.WEBKIT_MASK_IMAGE, t$30.BORDER_TOP_STYLE, t$30.BORDER_TOP_WIDTH, t$30.BORDER_TOP_COLOR, t$30.BORDER_BOTTOM_STYLE, t$30.BORDER_BOTTOM_WIDTH, t$30.BORDER_BOTTOM_COLOR, t$30.BORDER_LEFT_STYLE, t$30.BORDER_LEFT_WIDTH, t$30.BORDER_LEFT_COLOR, t$30.BORDER_RIGHT_STYLE, t$30.BORDER_RIGHT_WIDTH, t$30.BORDER_RIGHT_COLOR, t$30.BORDER_TOP_LEFT_RADIUS, t$30.BORDER_TOP_RIGHT_RADIUS, t$30.BORDER_BOTTOM_LEFT_RADIUS, t$30.BORDER_BOTTOM_RIGHT_RADIUS, t$30.BORDER_COLLAPSE, t$30.BORDER_SPACING, t$30.TABLE_LAYOUT, t$30.OPACITY, t$30.VISIBILITY, t$30.CURSOR, t$30.TRANSITION, t$30.TRANSITION_PROPERTY, t$30.TRANSITION_DURATION, t$30.TRANSITION_TIMING_FUNCTION, t$30.TRANSITION_DELAY, t$30.TRANSITION_BEHAVIOR, t$30.ANIMATION, t$30.ANIMATION_NAME, t$30.ANIMATION_DURATION, t$30.ANIMATION_TIMING_FUNCTION, t$30.ANIMATION_DELAY, t$30.ANIMATION_ITERATION_COUNT, t$30.ANIMATION_DIRECTION, t$30.ANIMATION_FILL_MODE, t$30.ANIMATION_PLAY_STATE, t$30.ANIMATION_TIMELINE, t$30.ANIMATION_RANGE, t$30.BOX_SHADOW, t$30.FILTER, t$30.BACKDROP_FILTER, t$30.MIX_BLEND_MODE, t$30.ISOLATION, t$30.CLIP_PATH, t$30.TRANSFORM, t$30.TRANSFORM_ORIGIN, t$30.TRANSLATE, t$30.ROTATE, t$30.SCALE, t$30.TRANSFORM_STYLE, t$30.PERSPECTIVE, t$30.PERSPECTIVE_ORIGIN, t$30.BACKFACE_VISIBILITY, t$30.WILL_CHANGE, t$30.OUTLINE_WIDTH, t$30.OUTLINE_STYLE, t$30.OUTLINE_COLOR, t$30.OUTLINE_OFFSET, t$30.POINTER_EVENTS, t$30.USER_SELECT, t$30.TOUCH_ACTION, t$30.RESIZE, t$30.APPEARANCE, t$30.SCROLL_BEHAVIOR, t$30.OVERSCROLL_BEHAVIOR, t$30.SCROLL_SNAP_TYPE, t$30.SCROLL_SNAP_ALIGN, t$30.SCROLL_SNAP_STOP, t$30.SCROLL_PADDING_TOP, t$30.SCROLL_MARGIN_TOP, t$30.ACCENT_COLOR, t$30.CARET_COLOR, t$30.COLOR_SCHEME, t$30.POSITION, t$30.FLOAT, t$30.CLEAR, t$30.Z_INDEX, t$30.TOP, t$30.BOTTOM, t$30.LEFT, t$30.RIGHT, t$30.BOX_SIZING, t$30.ASPECT_RATIO, t$30.WIDTH, t$30.HEIGHT, t$30.MIN_WIDTH, t$30.MIN_HEIGHT, t$30.MAX_WIDTH, t$30.MAX_HEIGHT, t$30.OVERFLOW, t$30.OVERFLOW_X, t$30.OVERFLOW_Y, t$30.SCROLLBAR_WIDTH, t$30.SCROLLBAR_COLOR, t$30.CONTAINER_TYPE, t$30.CONTENT, t$30.CONTAINER_NAME, t$30.OBJECT_FIT, t$30.OBJECT_POSITION, t$30.COLUMN_WIDTH, t$30.COLUMN_COUNT, t$30.MARGIN_TOP, t$30.MARGIN_BOTTOM, t$30.MARGIN_LEFT, t$30.MARGIN_RIGHT, t$30.PADDING_TOP, t$30.PADDING_BOTTOM, t$30.PADDING_LEFT, t$30.PADDING_RIGHT, t$30.FONT_FAMILY, t$30.FONT_WEIGHT, t$30.FONT_SIZE, t$30.LINE_HEIGHT, t$30.COLOR, t$30.TEXT_ALIGN, t$30.FONT_STYLE, t$30.FONT_VARIANT, t$30.FONT_VARIANT_NUMERIC, t$30.FONT_FEATURE_SETTINGS, t$30.TEXT_DECORATION, t$30.TEXT_DECORATION_LINE, t$30.TEXT_DECORATION_COLOR, t$30.TEXT_DECORATION_STYLE, t$30.TEXT_DECORATION_THICKNESS, t$30.TEXT_UNDERLINE_OFFSET, t$30.LETTER_SPACING, t$30.WORD_SPACING, t$30.TEXT_INDENT, t$30.TEXT_TRANSFORM, t$30.DIRECTION, t$30.WRITING_MODE, t$30.TEXT_ORIENTATION, t$30.WHITE_SPACE, t$30.TEXT_WRAP, t$30.WORD_BREAK, t$30.OVERFLOW_WRAP, t$30.HYPHENS, t$30.VERTICAL_ALIGN, t$30.TEXT_OVERFLOW, t$30.TEXT_SHADOW, t$30.LINE_CLAMP, t$30.WEBKIT_LINE_CLAMP, t$30.WEBKIT_BOX_ORIENT, t$30.WEBKIT_TEXT_FILL_COLOR, t$30.WEBKIT_BACKGROUND_CLIP, t$30.BOX_DECORATION_BREAK, t$30.WEBKIT_BOX_DECORATION_BREAK, t$30.FILL, t$30.STROKE, t$30.STROKE_WIDTH;
|
|
9224
|
+
[...e$39];
|
|
9183
9225
|
/** Both binding forms as one list. The map form targets attributes, which is what nearly every binding does. */
|
|
9184
9226
|
var toBindingSpecs = (bind) => Array.isArray(bind) ? bind : Object.entries(bind).map(([to, source]) => ({
|
|
9185
9227
|
to,
|
|
@@ -9491,20 +9533,20 @@ var tabContainerItem = defineElement(B$2.TabContainerItem);
|
|
|
9491
9533
|
var text$1 = defineElement(B$2.Text);
|
|
9492
9534
|
var themeToggle = defineElement(B$2.ThemeToggle);
|
|
9493
9535
|
var video = defineElement(B$2.Video);
|
|
9494
|
-
var e$
|
|
9536
|
+
var e$33 = (e) => typeof e == "object" && !!e && !Array.isArray(e);
|
|
9495
9537
|
var STEP_TYPES = [
|
|
9496
9538
|
"trigger",
|
|
9497
9539
|
"globalCallback",
|
|
9498
9540
|
"callback",
|
|
9499
9541
|
"utility"
|
|
9500
9542
|
];
|
|
9501
|
-
var e$
|
|
9543
|
+
var e$31 = {
|
|
9502
9544
|
desktop: 1024,
|
|
9503
9545
|
tablet: 768,
|
|
9504
9546
|
mobile: 0
|
|
9505
9547
|
};
|
|
9506
|
-
var t$26 = (t) => e$
|
|
9507
|
-
var e$
|
|
9548
|
+
var t$26 = (t) => e$31[t] === 0 ? "0" : `${String(e$31[t] / 16)}rem`;
|
|
9549
|
+
var e$30 = [
|
|
9508
9550
|
"desktop",
|
|
9509
9551
|
"tablet",
|
|
9510
9552
|
"mobile"
|
|
@@ -9512,16 +9554,16 @@ var e$29 = [
|
|
|
9512
9554
|
Object.values(/* @__PURE__ */ function(e) {
|
|
9513
9555
|
return e.COLOR = "color", e.SPACING = "spacing", e.SHADOW = "shadow", e.CUSTOM = "custom", e;
|
|
9514
9556
|
}({}));
|
|
9515
|
-
var e$
|
|
9557
|
+
var e$28 = {
|
|
9516
9558
|
"motion-reduce": "@media (prefers-reduced-motion: reduce)",
|
|
9517
9559
|
"motion-safe": "@media (prefers-reduced-motion: no-preference)"
|
|
9518
9560
|
};
|
|
9519
|
-
var t$23 = Object.keys(e$
|
|
9520
|
-
var r$
|
|
9521
|
-
var i$
|
|
9522
|
-
var a$15 = String.raw`\((?:min|max)-width:\s*${i$
|
|
9561
|
+
var t$23 = Object.keys(e$28);
|
|
9562
|
+
var r$18 = (t) => Object.hasOwn(e$28, t);
|
|
9563
|
+
var i$18 = String.raw`\d+(?:\.\d+)?(?:px|rem|em|ch)`;
|
|
9564
|
+
var a$15 = String.raw`\((?:min|max)-width:\s*${i$18}\)`;
|
|
9523
9565
|
var o$17 = new RegExp(String.raw`^container(?:\s+(?<name>-?[_a-zA-Z][\w-]*))?\s+(?<query>${a$15}(?:\s+and\s+${a$15})?)$`);
|
|
9524
|
-
var s$
|
|
9566
|
+
var s$15 = (e) => {
|
|
9525
9567
|
let t = o$17.exec(e.trim());
|
|
9526
9568
|
if (t?.groups) return {
|
|
9527
9569
|
name: t.groups.name,
|
|
@@ -9529,11 +9571,11 @@ var s$14 = (e) => {
|
|
|
9529
9571
|
};
|
|
9530
9572
|
};
|
|
9531
9573
|
var c$12 = (e) => {
|
|
9532
|
-
if (r$
|
|
9533
|
-
let t = s$
|
|
9574
|
+
if (r$18(e)) return e;
|
|
9575
|
+
let t = s$15(e);
|
|
9534
9576
|
if (t) return t.name ? `container ${t.name} ${t.query}` : `container ${t.query}`;
|
|
9535
9577
|
};
|
|
9536
|
-
var e$
|
|
9578
|
+
var e$27 = [
|
|
9537
9579
|
"before",
|
|
9538
9580
|
"after",
|
|
9539
9581
|
"marker",
|
|
@@ -9552,7 +9594,7 @@ var t$22 = {
|
|
|
9552
9594
|
selection: "::selection"
|
|
9553
9595
|
};
|
|
9554
9596
|
var n$19 = (e) => Object.hasOwn(t$22, e);
|
|
9555
|
-
var i$
|
|
9597
|
+
var i$17 = [
|
|
9556
9598
|
"before",
|
|
9557
9599
|
"after",
|
|
9558
9600
|
"marker"
|
|
@@ -9573,7 +9615,7 @@ var o$16 = [
|
|
|
9573
9615
|
"line-height",
|
|
9574
9616
|
"vertical-align"
|
|
9575
9617
|
];
|
|
9576
|
-
var s$
|
|
9618
|
+
var s$14 = [
|
|
9577
9619
|
"font-family",
|
|
9578
9620
|
"font-size",
|
|
9579
9621
|
"font-style",
|
|
@@ -9600,24 +9642,24 @@ var l$8 = {
|
|
|
9600
9642
|
"color",
|
|
9601
9643
|
"content",
|
|
9602
9644
|
"direction",
|
|
9603
|
-
...s$
|
|
9645
|
+
...s$14
|
|
9604
9646
|
],
|
|
9605
9647
|
placeholder: [
|
|
9606
9648
|
...o$16,
|
|
9607
|
-
...s$
|
|
9649
|
+
...s$14,
|
|
9608
9650
|
...c$11
|
|
9609
9651
|
],
|
|
9610
9652
|
"first-line": [
|
|
9611
9653
|
...o$16,
|
|
9612
|
-
...s$
|
|
9654
|
+
...s$14,
|
|
9613
9655
|
...c$11
|
|
9614
9656
|
]
|
|
9615
9657
|
};
|
|
9616
|
-
var u$
|
|
9658
|
+
var u$8 = (e, t) => {
|
|
9617
9659
|
let n = l$8[e];
|
|
9618
9660
|
return !n || t.startsWith("--") || n.includes(t);
|
|
9619
9661
|
};
|
|
9620
|
-
var d$
|
|
9662
|
+
var d$7 = /* @__PURE__ */ new Set([
|
|
9621
9663
|
"none",
|
|
9622
9664
|
"normal",
|
|
9623
9665
|
"open-quote",
|
|
@@ -9629,8 +9671,8 @@ var d$8 = /* @__PURE__ */ new Set([
|
|
|
9629
9671
|
"unset",
|
|
9630
9672
|
"revert"
|
|
9631
9673
|
]);
|
|
9632
|
-
var f$
|
|
9633
|
-
var p$
|
|
9674
|
+
var f$5 = /^(?:counter|counters|attr|url|var|image-set|linear-gradient|radial-gradient|conic-gradient)\(/i;
|
|
9675
|
+
var p$8 = (e) => {
|
|
9634
9676
|
let t = [], n = "", r = "", i = 0;
|
|
9635
9677
|
for (let a of e) {
|
|
9636
9678
|
if (r) {
|
|
@@ -9648,11 +9690,11 @@ var p$7 = (e) => {
|
|
|
9648
9690
|
}
|
|
9649
9691
|
return n && t.push(n), r || i !== 0 ? void 0 : t;
|
|
9650
9692
|
};
|
|
9651
|
-
var m$
|
|
9652
|
-
let t = p$
|
|
9653
|
-
return t?.length ? t.every((e) => /^(["']).*\1$/s.test(e) || d$
|
|
9693
|
+
var m$4 = (e) => {
|
|
9694
|
+
let t = p$8(e.trim());
|
|
9695
|
+
return t?.length ? t.every((e) => /^(["']).*\1$/s.test(e) || d$7.has(e.toLowerCase()) || f$5.test(e) && e.endsWith(")")) : !1;
|
|
9654
9696
|
};
|
|
9655
|
-
var e$
|
|
9697
|
+
var e$26 = [
|
|
9656
9698
|
"hover",
|
|
9657
9699
|
"focus",
|
|
9658
9700
|
"focus-visible",
|
|
@@ -9670,7 +9712,7 @@ var e$25 = [
|
|
|
9670
9712
|
"even"
|
|
9671
9713
|
];
|
|
9672
9714
|
var t$21 = "plitzi-component--hidden";
|
|
9673
|
-
var r$
|
|
9715
|
+
var r$16 = {
|
|
9674
9716
|
odd: 0,
|
|
9675
9717
|
even: 1,
|
|
9676
9718
|
first: 2,
|
|
@@ -9687,7 +9729,7 @@ var r$17 = {
|
|
|
9687
9729
|
disabled: 13,
|
|
9688
9730
|
hidden: 14
|
|
9689
9731
|
};
|
|
9690
|
-
var i$
|
|
9732
|
+
var i$16 = (e) => Object.hasOwn(r$16, e);
|
|
9691
9733
|
var l$7 = {
|
|
9692
9734
|
current: ":is([aria-current]:not([aria-current=\"false\"]),[aria-pressed=\"true\"],[aria-selected=\"true\"],[role=\"tabpanel\"]:not([hidden]))",
|
|
9693
9735
|
expanded: "[aria-expanded=\"true\"]",
|
|
@@ -9697,8 +9739,8 @@ var l$7 = {
|
|
|
9697
9739
|
odd: ":nth-child(odd)",
|
|
9698
9740
|
even: ":nth-child(even)"
|
|
9699
9741
|
};
|
|
9700
|
-
var u$
|
|
9701
|
-
var p$
|
|
9742
|
+
var u$7 = (e) => (i$16(e) ? l$7[e] : void 0) ?? `:${e}`;
|
|
9743
|
+
var p$7 = (e) => e === ">";
|
|
9702
9744
|
var n$16 = (e) => {
|
|
9703
9745
|
let t = typeof e == "string" && e.trim() !== "" ? Number(e) : e;
|
|
9704
9746
|
return typeof t == "number" && Number.isInteger(t) && t >= 250 ? t : void 0;
|
|
@@ -9733,7 +9775,7 @@ var r$13 = {
|
|
|
9733
9775
|
del: "delete",
|
|
9734
9776
|
return: "enter"
|
|
9735
9777
|
};
|
|
9736
|
-
var i$
|
|
9778
|
+
var i$13 = /* @__PURE__ */ new Set([
|
|
9737
9779
|
"escape",
|
|
9738
9780
|
"enter",
|
|
9739
9781
|
"tab",
|
|
@@ -9766,7 +9808,7 @@ var o$11 = (e, t) => {
|
|
|
9766
9808
|
}
|
|
9767
9809
|
if (u.length !== 1) return { problem: `"${e.trim()}" names ${u.length ? `${u.length} keys (${u.join(", ")})` : "no key, only modifiers"}: a shortcut is modifiers and ONE key, like "shift+f"` };
|
|
9768
9810
|
let [d] = u;
|
|
9769
|
-
return d.length !== 1 && !i$
|
|
9811
|
+
return d.length !== 1 && !i$13.has(d) ? { problem: `"${d}" in "${e.trim()}" is not a key: use one character ("f", "?", "+") or a key's name (${[...i$13].slice(0, 15).join(", ")}, f1…f12)` } : { combo: a$10(l, d) };
|
|
9770
9812
|
};
|
|
9771
9813
|
var s$9 = (e, t = !1) => {
|
|
9772
9814
|
let n = e.split(",").map((e) => o$11(e, t));
|
|
@@ -9819,7 +9861,7 @@ var steps_exports$1 = /* @__PURE__ */ __exportAll({
|
|
|
9819
9861
|
* the builders useless for exactly the flows that matter most.
|
|
9820
9862
|
*/
|
|
9821
9863
|
var builtinTrigger = (trigger) => {
|
|
9822
|
-
if (Object.hasOwn(e$
|
|
9864
|
+
if (Object.hasOwn(e$41, trigger)) return e$41[trigger];
|
|
9823
9865
|
return Object.hasOwn(typeTriggerDefinitions, trigger) ? typeTriggerDefinitions[trigger] : void 0;
|
|
9824
9866
|
};
|
|
9825
9867
|
var on = (trigger, params = {}) => {
|
|
@@ -9880,7 +9922,7 @@ var onInterval = (ms) => {
|
|
|
9880
9922
|
* and hide the arrow at the end already reached: `[named('row', onScroll()), setState({ key: 'atEnd', type:
|
|
9881
9923
|
* 'boolean', value: '{{ row.atEnd }}' })]`.
|
|
9882
9924
|
*/
|
|
9883
|
-
var onScroll = () => on(s$
|
|
9925
|
+
var onScroll = () => on(s$22);
|
|
9884
9926
|
var onPageLoad = () => on("onPageLoad");
|
|
9885
9927
|
/**
|
|
9886
9928
|
* The end of a server action this element started — the trigger a `detached` run needs, because that step returns
|
|
@@ -10130,7 +10172,7 @@ var carouselGoTo = (target, index) => carouselStep(target, "goTo", "Go To Slide"
|
|
|
10130
10172
|
var carouselPlay = (target) => carouselStep(target, "play", "Play");
|
|
10131
10173
|
/** The carousel `target` stops moving by itself until it is told to play. */
|
|
10132
10174
|
var carouselPause = (target) => carouselStep(target, "pause", "Pause");
|
|
10133
|
-
var e$
|
|
10175
|
+
var e$22 = {
|
|
10134
10176
|
origin: "Requests to the same site",
|
|
10135
10177
|
all: "All cached requests",
|
|
10136
10178
|
elements: "Only these containers",
|
|
@@ -10142,7 +10184,7 @@ var t$16 = (t, n = () => !0) => ({
|
|
|
10142
10184
|
description: "What to refresh once the write succeeded, so the page shows the new data: the requests to the same site, every provider on the page, only the api containers named in `invalidateElements`, or none for a step that only reads. Server-driven containers are refreshed too, asking the server past its cache. Containers on screen ask again at once; the rest when they are shown.",
|
|
10143
10185
|
default: t[0],
|
|
10144
10186
|
options: [...t],
|
|
10145
|
-
optionLabels: Object.fromEntries(t.map((t) => [t, e$
|
|
10187
|
+
optionLabels: Object.fromEntries(t.map((t) => [t, e$22[t]])),
|
|
10146
10188
|
label: "Refresh cached requests",
|
|
10147
10189
|
canBind: !1,
|
|
10148
10190
|
when: n
|
|
@@ -10222,7 +10264,7 @@ var t$15 = {
|
|
|
10222
10264
|
preview: { cancelled: "" }
|
|
10223
10265
|
}
|
|
10224
10266
|
};
|
|
10225
|
-
var e$
|
|
10267
|
+
var e$21 = {
|
|
10226
10268
|
login: {
|
|
10227
10269
|
source: "auth",
|
|
10228
10270
|
title: "Auth Login",
|
|
@@ -10308,7 +10350,7 @@ var e$20 = {
|
|
|
10308
10350
|
params: {}
|
|
10309
10351
|
}
|
|
10310
10352
|
};
|
|
10311
|
-
var e$
|
|
10353
|
+
var e$20 = { hostAction: {
|
|
10312
10354
|
source: "host",
|
|
10313
10355
|
title: "Host Action",
|
|
10314
10356
|
strictParams: !0,
|
|
@@ -10325,7 +10367,7 @@ var e$19 = { hostAction: {
|
|
|
10325
10367
|
}
|
|
10326
10368
|
}
|
|
10327
10369
|
} };
|
|
10328
|
-
var e$
|
|
10370
|
+
var e$19 = { navigate: {
|
|
10329
10371
|
source: "navigation",
|
|
10330
10372
|
title: "Navigate",
|
|
10331
10373
|
strictParams: !0,
|
|
@@ -10352,7 +10394,7 @@ var e$18 = { navigate: {
|
|
|
10352
10394
|
}
|
|
10353
10395
|
}
|
|
10354
10396
|
} };
|
|
10355
|
-
var e$
|
|
10397
|
+
var e$18 = { invalidateQueries: {
|
|
10356
10398
|
source: "queries",
|
|
10357
10399
|
title: "Invalidate Queries",
|
|
10358
10400
|
strictParams: !0,
|
|
@@ -10585,11 +10627,11 @@ var BUILTIN_GLOBAL_CALLBACKS = {
|
|
|
10585
10627
|
default: ""
|
|
10586
10628
|
} }
|
|
10587
10629
|
},
|
|
10588
|
-
...e$19,
|
|
10589
|
-
...e$18,
|
|
10590
10630
|
...e$20,
|
|
10631
|
+
...e$19,
|
|
10632
|
+
...e$21,
|
|
10591
10633
|
...t$15,
|
|
10592
|
-
...e$
|
|
10634
|
+
...e$18
|
|
10593
10635
|
};
|
|
10594
10636
|
var steps_exports = /* @__PURE__ */ __exportAll({
|
|
10595
10637
|
SET_STATE_TYPES: () => SET_STATE_TYPES,
|
|
@@ -10828,10 +10870,11 @@ var t$14 = (e) => {
|
|
|
10828
10870
|
var t$13 = "[A-Za-z_][A-Za-z0-9_]*";
|
|
10829
10871
|
RegExp(`^${t$13}$`);
|
|
10830
10872
|
RegExp(`^!?${t$13}$`);
|
|
10831
|
-
var e$
|
|
10832
|
-
var e$
|
|
10833
|
-
var i$
|
|
10834
|
-
i$
|
|
10873
|
+
var e$9 = "/data/";
|
|
10874
|
+
var e$7 = (e) => typeof e == "string" && /\{\{[\s\S]*?\}\}|\{%[\s\S]*?%\}/.test(e);
|
|
10875
|
+
var i$5 = (e) => `value|round(0, '${e}')`;
|
|
10876
|
+
i$5("floor"), i$5("ceil");
|
|
10877
|
+
new Set(Object.entries(g$4.content.defaultStyle.subTypes).filter(([, variant]) => "field" in variant.style).map(([subType]) => subType));
|
|
10835
10878
|
/** A container with no tag of its own named: the row `list-row-not-li` makes an `<li>` without asking. */
|
|
10836
10879
|
var isPlainContainer = (element) => element.definition.type === "container" && (element.attributes.subType === void 0 || element.attributes.subType === "div");
|
|
10837
10880
|
new Set(STEP_TYPES);
|
|
@@ -10990,7 +11033,7 @@ Array.from({ length: 9 }, (e, t) => ({
|
|
|
10990
11033
|
Object.entries(AUTHORING_CODES).filter(([, entry]) => entry.kind === "suggested").map(([code]) => code);
|
|
10991
11034
|
var URL_SCHEME = /^[a-z][a-z0-9+.-]*:/i;
|
|
10992
11035
|
/** A page target that is really a URL: the one reading of `mailto:`/`https:` a page-mode link can never mean. */
|
|
10993
|
-
var isUrlTarget = (target) => typeof target === "string" && !e$
|
|
11036
|
+
var isUrlTarget = (target) => typeof target === "string" && !e$7(target) && URL_SCHEME.test(target);
|
|
10994
11037
|
var isRoot = (element) => !element.definition.parentId;
|
|
10995
11038
|
var stepsOf$1 = (element) => Object.values(element.definition.interactions ?? {});
|
|
10996
11039
|
/**
|
|
@@ -11001,7 +11044,7 @@ var settleUnknownKeys = (record, known, what, on, report, defaults = {}) => {
|
|
|
11001
11044
|
for (const key of Object.keys(record)) {
|
|
11002
11045
|
if (known.includes(key)) continue;
|
|
11003
11046
|
const wrapped = record[key];
|
|
11004
|
-
if (e$
|
|
11047
|
+
if (e$33(wrapped) && Object.keys(wrapped).length > 0 && Object.keys(wrapped).every((inner) => known.includes(inner))) {
|
|
11005
11048
|
const inners = Object.keys(wrapped);
|
|
11006
11049
|
if (inners.every((inner) => record[inner] === void 0 || record[inner] === defaults[inner])) {
|
|
11007
11050
|
Object.assign(record, wrapped);
|
|
@@ -11282,7 +11325,7 @@ t$4.map((e) => {
|
|
|
11282
11325
|
fieldOf: (e) => t.get(e)
|
|
11283
11326
|
};
|
|
11284
11327
|
});
|
|
11285
|
-
Object.fromEntries(e$
|
|
11328
|
+
Object.fromEntries(e$30.map((e) => [e, t$26(e)]));
|
|
11286
11329
|
/** Every kebab-case CSS property Plitzi's style engine understands. Values written to a definition must use these
|
|
11287
11330
|
* exact keys — camelCase or unknown keys are rejected. */
|
|
11288
11331
|
var cssProperties = Array.from(new Set(Object.values(t$30))).sort();
|
|
@@ -12067,7 +12110,7 @@ var byProperty = (rules) => {
|
|
|
12067
12110
|
return responsive;
|
|
12068
12111
|
};
|
|
12069
12112
|
/** The states a selector can react to — the closed list the style editor offers, read from where it is declared. */
|
|
12070
|
-
var STYLE_STATES = e$
|
|
12113
|
+
var STYLE_STATES = e$26;
|
|
12071
12114
|
var STYLE_STATE_SET = new Set(STYLE_STATES);
|
|
12072
12115
|
var RULE_SET_KEYS = /* @__PURE__ */ new Set([
|
|
12073
12116
|
"css",
|
|
@@ -12094,7 +12137,7 @@ var toStates = (states) => {
|
|
|
12094
12137
|
};
|
|
12095
12138
|
var CLASS_NAME = /^-?[_a-zA-Z][\w-]*$/;
|
|
12096
12139
|
var toAncestors = (ancestors) => Object.entries(ancestors ?? {}).map(([name, ancestor]) => {
|
|
12097
|
-
if (!p$
|
|
12140
|
+
if (!p$7(name) && !CLASS_NAME.test(name)) throw new AuthoringError("ancestor-not-class", `The ancestor "${name}" is not a class name. An ancestor condition is keyed by a class that ancestor wears — \`[card.name]\` for a \`styles()\` declaration — or by \`'>'\` for the parent, whatever it wears.`);
|
|
12098
12141
|
return [name, toBlocks({
|
|
12099
12142
|
css: ancestor.css,
|
|
12100
12143
|
states: ancestor.states,
|
|
@@ -12131,11 +12174,11 @@ var assertPseudo = (pseudo, part) => {
|
|
|
12131
12174
|
const ruleSets = partRuleSets(part);
|
|
12132
12175
|
for (const rules of ruleSets) for (const [property, value] of Object.entries(rules)) {
|
|
12133
12176
|
if (property === "content") {
|
|
12134
|
-
if (!i$
|
|
12135
|
-
if (!m$
|
|
12177
|
+
if (!i$17.includes(pseudo)) throw new AuthoringError("style-pseudo-content", `\`content\` on \`::${pseudo}\` draws nothing: only ${i$17.map((name) => `\`::${name}\``).join(", ")} take one.`);
|
|
12178
|
+
if (!m$4(String(value))) throw new AuthoringError("style-pseudo-content", `\`content: ${String(value)}\` on \`::${pseudo}\` is not text CSS can read, so nothing is drawn. Text goes in quotes inside the string — \`content: '"${String(value).replace(/"/g, "\\\"")}"'\` — and an empty box is \`'""'\`; \`counter()\`, \`attr()\` and \`url()\` are written as they are.`);
|
|
12136
12179
|
continue;
|
|
12137
12180
|
}
|
|
12138
|
-
if (!u$
|
|
12181
|
+
if (!u$8(pseudo, property)) throw new AuthoringError("style-pseudo-property", `\`${property}\` on \`::${pseudo}\` is dropped by every browser — \`::${pseudo}\` honours ${(l$8[pseudo] ?? []).map((name) => `\`${name}\``).join(", ")}. Style the element itself for the rest.`);
|
|
12139
12182
|
}
|
|
12140
12183
|
if (a$14.includes(pseudo) && !ruleSets.some((rules) => Object.hasOwn(rules, "content"))) throw new AuthoringError("style-pseudo-content", `\`::${pseudo}\` has no \`content\`, so the browser draws nothing for it. Give it one — \`content: '""'\` for an empty box — in its rules or in a state's.`);
|
|
12141
12184
|
};
|
|
@@ -12143,7 +12186,7 @@ var toPseudos = (pseudos) => {
|
|
|
12143
12186
|
const parts = /* @__PURE__ */ new Map();
|
|
12144
12187
|
for (const [name, spec] of Object.entries(pseudos ?? {})) {
|
|
12145
12188
|
const bare = name.replace(/^:+/, "");
|
|
12146
|
-
if (!n$19(name)) throw new AuthoringError("style-pseudo-unknown", `Unknown pseudo-element "${name}"${n$19(bare) ? ` — write it without the colons, "${bare}"` : didYouMean(bare, e$
|
|
12189
|
+
if (!n$19(name)) throw new AuthoringError("style-pseudo-unknown", `Unknown pseudo-element "${name}"${n$19(bare) ? ` — write it without the colons, "${bare}"` : didYouMean(bare, e$27)}. A class dresses ${e$27.join(", ")}.`);
|
|
12147
12190
|
const part = partOf(spec);
|
|
12148
12191
|
const rules = {
|
|
12149
12192
|
base: toResponsive(part.css),
|
|
@@ -13769,7 +13812,7 @@ var createTw = ({ colors = {} } = {}) => (classes) => {
|
|
|
13769
13812
|
return rules;
|
|
13770
13813
|
};
|
|
13771
13814
|
createTw();
|
|
13772
|
-
[...new Set(STYLE_STATES.filter((state) => state !== "hidden"))].filter((state) => u$
|
|
13815
|
+
[...new Set(STYLE_STATES.filter((state) => state !== "hidden"))].filter((state) => u$7(state) !== `:${state}`);
|
|
13773
13816
|
new Set("break case catch class const continue debugger default delete do else enum export extends false finally for function if import in instanceof new null return super switch this throw true try typeof var void while with yield let static implements interface package private protected public await element styles".split(" "));
|
|
13774
13817
|
new Set(STYLE_STATES);
|
|
13775
13818
|
new Set([
|
|
@@ -14305,6 +14348,33 @@ var footerRow = styles("footer-row", { css: {
|
|
|
14305
14348
|
gap: "8px"
|
|
14306
14349
|
}
|
|
14307
14350
|
} });
|
|
14351
|
+
container({
|
|
14352
|
+
id: "not-found-main",
|
|
14353
|
+
subType: "main",
|
|
14354
|
+
class: [shell$1, heroBand],
|
|
14355
|
+
children: [
|
|
14356
|
+
text$1("404", {
|
|
14357
|
+
id: "not-found-kicker",
|
|
14358
|
+
class: kicker
|
|
14359
|
+
}),
|
|
14360
|
+
heading("Page not found", {
|
|
14361
|
+
id: "not-found-title",
|
|
14362
|
+
subType: "h1",
|
|
14363
|
+
class: sectionTitle
|
|
14364
|
+
}),
|
|
14365
|
+
paragraph({
|
|
14366
|
+
id: "not-found-lede",
|
|
14367
|
+
class: bodyText,
|
|
14368
|
+
content: "There is nothing at this address — it may have moved, or the link has a typo."
|
|
14369
|
+
}),
|
|
14370
|
+
link({
|
|
14371
|
+
id: "not-found-home",
|
|
14372
|
+
href: "/",
|
|
14373
|
+
class: button,
|
|
14374
|
+
content: "Back to the home page"
|
|
14375
|
+
})
|
|
14376
|
+
]
|
|
14377
|
+
});
|
|
14308
14378
|
/**
|
|
14309
14379
|
* The space, declared: this file is the page, and it assembles the rest — the palette in `tokens.ts`, the classes in
|
|
14310
14380
|
* `theme.ts`, what the page says in `content.ts`. Everything else — element ids, style selector names, the
|
|
@@ -14614,6 +14684,17 @@ container({
|
|
|
14614
14684
|
})]
|
|
14615
14685
|
})]
|
|
14616
14686
|
});
|
|
14687
|
+
heading("Page not found", {
|
|
14688
|
+
id: "not-found-title",
|
|
14689
|
+
subType: "h1"
|
|
14690
|
+
}), paragraph({
|
|
14691
|
+
id: "not-found-lede",
|
|
14692
|
+
content: "There is nothing at this address."
|
|
14693
|
+
}), link({
|
|
14694
|
+
id: "not-found-home",
|
|
14695
|
+
href: "/",
|
|
14696
|
+
content: "Back to the home page"
|
|
14697
|
+
});
|
|
14617
14698
|
/**
|
|
14618
14699
|
* The space, declared — and nothing in it yet but what every space starts from: tokens for both themes, a layout the
|
|
14619
14700
|
* pages render in, and one page.
|
|
@@ -14914,7 +14995,7 @@ var V$1 = (e) => {
|
|
|
14914
14995
|
for (let n of e.filter((e) => x$2.test(e))) t.set(M$1(n), [...t.get(M$1(n)) ?? [], n]);
|
|
14915
14996
|
return [...t].flatMap(([e, t]) => t.slice(1).map((n) => y$1("plugin-type-taken", `${o$1$1}/${n}/`, `${o$1$1}/${t[0]}/ and ${o$1$1}/${n}/ are both the plugin ${e}: the server registers a folder by its name with a small first letter, and keeps one.`, `Put them in one folder, or rename one: git mv ${o$1$1}/${n} ${o$1$1}/<AnotherName>`)));
|
|
14916
14997
|
};
|
|
14917
|
-
var H$1 = (t) => e$
|
|
14998
|
+
var H$1 = (t) => e$33(t.assets) ? Object.values(t.assets).flatMap((t) => e$33(t) && typeof t.src == "string" && typeof t.type == "string" ? [{
|
|
14918
14999
|
src: t.src,
|
|
14919
15000
|
type: t.type,
|
|
14920
15001
|
isMain: t.isMain === !0
|
|
@@ -14928,7 +15009,7 @@ var U$1 = (t, n) => {
|
|
|
14928
15009
|
} catch (e) {
|
|
14929
15010
|
return { problem: y$1("vendor-manifest-invalid", i, `${i} is not JSON (${e instanceof Error ? e.message : String(e)}), so ${n} does not run.`, a) };
|
|
14930
15011
|
}
|
|
14931
|
-
if (!e$
|
|
15012
|
+
if (!e$33(o)) return { problem: y$1("vendor-manifest-invalid", i, `${i} is not a manifest, so ${n} does not run.`, a) };
|
|
14932
15013
|
let s = H$1(o), l = s.find((e) => e.type === "script" && e.isMain) ?? s.find((e) => e.type === "script");
|
|
14933
15014
|
if (!l) return { problem: y$1("vendor-script-missing", i, `${i} names no script to run, so ${n} does not run.`, a) };
|
|
14934
15015
|
if (!C$1(t, path.posix.join(r, l.src), "file")) return { problem: y$1("vendor-script-missing", i, `${i} names ${l.src} as the script to run, and ${r}/${l.src} is not there, so ${n} does not run.`, a) };
|
|
@@ -15052,7 +15133,7 @@ var ce$1 = (e, t) => {
|
|
|
15052
15133
|
var Z$1 = (t) => {
|
|
15053
15134
|
try {
|
|
15054
15135
|
let n = JSON.parse(readFileSync(path.join(t, "package.json"), "utf-8"));
|
|
15055
|
-
return e$
|
|
15136
|
+
return e$33(n) && [n.dependencies, n.devDependencies].some((t) => e$33(t) && "@plitzi/sdk-server" in t);
|
|
15056
15137
|
} catch {
|
|
15057
15138
|
return !1;
|
|
15058
15139
|
}
|
|
@@ -15175,7 +15256,7 @@ var publicData = (folder) => fileReader(folder, "/");
|
|
|
15175
15256
|
* never serves (`dataDir`) — for `authorSpace`'s `serverData`: bindings are held to the file, and a provider reading
|
|
15176
15257
|
* one from the browser is refused.
|
|
15177
15258
|
*/
|
|
15178
|
-
var projectData = (folder) => fileReader(folder, e$
|
|
15259
|
+
var projectData = (folder) => fileReader(folder, e$9);
|
|
15179
15260
|
/**
|
|
15180
15261
|
* The declaration of every plugin under `folder` — `src/plugins/<Name>/declaration.ts`, its default export — for
|
|
15181
15262
|
* `authorSpace`'s `plugins`: what each fires, answers and reads. Found by folder, as the server finds the plugins
|
|
@@ -15197,7 +15278,7 @@ var pluginDeclarations = async (folder) => {
|
|
|
15197
15278
|
var manifestOf$1 = (file) => {
|
|
15198
15279
|
try {
|
|
15199
15280
|
const manifest = JSON.parse(readFileSync(file, "utf-8"));
|
|
15200
|
-
return e$
|
|
15281
|
+
return e$33(manifest) ? manifest : {};
|
|
15201
15282
|
} catch {
|
|
15202
15283
|
return {};
|
|
15203
15284
|
}
|
|
@@ -15208,9 +15289,17 @@ var manifestOf$1 = (file) => {
|
|
|
15208
15289
|
*/
|
|
15209
15290
|
var builtPluginTypes = (folder) => existsSync(folder) ? readdirSync(folder, { withFileTypes: true }).filter((entry) => entry.isDirectory()).sort((a, b) => a.name.localeCompare(b.name)).flatMap((entry) => {
|
|
15210
15291
|
const { pluginSchema } = manifestOf$1(path.join(folder, entry.name, d$1));
|
|
15211
|
-
return [entry.name, ...e$
|
|
15292
|
+
return [entry.name, ...e$33(pluginSchema) ? Object.keys(pluginSchema) : []];
|
|
15212
15293
|
}) : [];
|
|
15213
15294
|
/**
|
|
15295
|
+
* The stylesheets of the plugins under `folder` — every `.css` in a plugin's folder, at any depth — as their text, for
|
|
15296
|
+
* `authorSpace`'s `stylesheets`: the tokens a plugin's own CSS reads are tokens the space uses.
|
|
15297
|
+
*/
|
|
15298
|
+
var pluginStylesheets = (folder) => existsSync(folder) ? readdirSync(folder, {
|
|
15299
|
+
recursive: true,
|
|
15300
|
+
withFileTypes: true
|
|
15301
|
+
}).filter((entry) => entry.isFile() && entry.name.endsWith(".css")).map((entry) => path.join(entry.parentPath, entry.name)).sort().map((file) => readFileSync(file, "utf-8")) : [];
|
|
15302
|
+
/**
|
|
15214
15303
|
* `projectAuthoring` of the project at `root`, held to the same check: for a tool that works on a project from another
|
|
15215
15304
|
* folder — the CLI, run anywhere inside one. A project's own code calls `projectAuthoring()`.
|
|
15216
15305
|
*/
|
|
@@ -15221,6 +15310,7 @@ var projectAuthoringAt = async (root) => {
|
|
|
15221
15310
|
return {
|
|
15222
15311
|
plugins: await pluginDeclarations(path.join(at, o$1$1)),
|
|
15223
15312
|
pluginTypes: builtPluginTypes(path.join(at, u$2)),
|
|
15313
|
+
stylesheets: pluginStylesheets(path.join(at, o$1$1)),
|
|
15224
15314
|
...existsSync(serverData) ? { serverData: projectData(serverData) } : {},
|
|
15225
15315
|
data: publicData(path.join(at, p$1))
|
|
15226
15316
|
};
|
|
@@ -15268,7 +15358,7 @@ var importProjectModule = async (file, root) => {
|
|
|
15268
15358
|
* What the module exports as `space`, taken as a declaration when it has the shape of one. `authorSpace` checks the
|
|
15269
15359
|
* rest of it, field by field, and says what is wrong — so this only has to tell a space from anything else.
|
|
15270
15360
|
*/
|
|
15271
|
-
var isSpaceSpec = (value) => e$
|
|
15361
|
+
var isSpaceSpec = (value) => e$33(value) && typeof value.name === "string" && typeof value.permanentUrl === "string" && Array.isArray(value.pages);
|
|
15272
15362
|
/**
|
|
15273
15363
|
* `projectSpace` of the project at `root`, held to the same checks: for a tool that works on a project from another
|
|
15274
15364
|
* folder — the CLI, run anywhere inside one. A project's own code calls `projectSpace()` or `authorProjectSpace()`.
|
|
@@ -15276,7 +15366,7 @@ var isSpaceSpec = (value) => e$32(value) && typeof value.name === "string" && ty
|
|
|
15276
15366
|
var projectSpaceAt = async (root) => {
|
|
15277
15367
|
const authoring = await projectAuthoringAt(root);
|
|
15278
15368
|
const module = await importProjectModule(l$2(root, a$1, process.argv[1]), root);
|
|
15279
|
-
const space = e$
|
|
15369
|
+
const space = e$33(module) ? module.space : void 0;
|
|
15280
15370
|
if (!isSpaceSpec(space)) throw new ProjectSpaceError(a$1);
|
|
15281
15371
|
return {
|
|
15282
15372
|
space,
|
|
@@ -16227,7 +16317,8 @@ var unifiedDiff = (file, before, after, context = 2) => {
|
|
|
16227
16317
|
* is left to the author, said with the command that would take the registry's.
|
|
16228
16318
|
* - `skills`: `.claude/skills/plitzi-*` from the packages installed, each replaced whole.
|
|
16229
16319
|
* - `renames`: a name a version renamed, still written in the source (`canTemplate`), at its file and line — and with
|
|
16230
|
-
* `--write`, renamed.
|
|
16320
|
+
* `--write`, renamed. With it, what a version changed that the source may count on — a `formControl` written
|
|
16321
|
+
* before fields became optional — at its file and line, for a project last upgraded before: said, never written.
|
|
16231
16322
|
*
|
|
16232
16323
|
* Shown by default; `--write` makes it. What the space is — its pages, plugins, data — is the project's, never named.
|
|
16233
16324
|
*
|
|
@@ -16288,6 +16379,42 @@ var renamesIn = (file, text) => RENAMES.filter((rename) => !rename.from || impor
|
|
|
16288
16379
|
since: rename.since,
|
|
16289
16380
|
what: rename.what
|
|
16290
16381
|
}] : []));
|
|
16382
|
+
/** Before this version a `formControl` was required unless it said `required: false`; from it, optional unless `true`. */
|
|
16383
|
+
var OPTIONAL_FIELDS_SINCE = "0.38.9";
|
|
16384
|
+
/** The text of the call whose `(` is at `open`, up to its `)` — strings skipped, so a `)` inside one closes nothing. */
|
|
16385
|
+
var callArguments = (text, open) => {
|
|
16386
|
+
let depth = 0;
|
|
16387
|
+
let quote;
|
|
16388
|
+
for (let at = open; at < text.length; at++) {
|
|
16389
|
+
const char = text[at];
|
|
16390
|
+
if (quote && char === "\\") {
|
|
16391
|
+
at++;
|
|
16392
|
+
continue;
|
|
16393
|
+
}
|
|
16394
|
+
if (quote) {
|
|
16395
|
+
quote = char === quote ? void 0 : quote;
|
|
16396
|
+
continue;
|
|
16397
|
+
}
|
|
16398
|
+
if (/['"`]/.test(char)) quote = char;
|
|
16399
|
+
else if (char === "(") depth++;
|
|
16400
|
+
else if (char === ")" && --depth === 0) return text.slice(open + 1, at);
|
|
16401
|
+
}
|
|
16402
|
+
return text.slice(open + 1);
|
|
16403
|
+
};
|
|
16404
|
+
/**
|
|
16405
|
+
* A `formControl(…)` that writes no `required`: one that, written before fields became optional, was required, and now
|
|
16406
|
+
* takes an empty answer. Said, never written — whether a field must be answered is the space's to say. Found as written
|
|
16407
|
+
* in the call: one built by a helper of the project's own is the helper's to look at.
|
|
16408
|
+
*/
|
|
16409
|
+
var optionalFieldsIn = (file, text) => [...text.matchAll(/\bformControl\(/g)].flatMap((match) => {
|
|
16410
|
+
const open = match.index + match[0].length - 1;
|
|
16411
|
+
return /\brequired\s*:/.test(callArguments(text, open)) ? [] : [{
|
|
16412
|
+
file,
|
|
16413
|
+
line: text.slice(0, match.index).split("\n").length,
|
|
16414
|
+
since: OPTIONAL_FIELDS_SINCE,
|
|
16415
|
+
what: "a formControl with no `required` is optional now — write `required: true` if it must be answered"
|
|
16416
|
+
}];
|
|
16417
|
+
});
|
|
16291
16418
|
/** The lowest version a range allows, as numbers: `^0.38.2` → [0, 38, 2]. */
|
|
16292
16419
|
var floorOf$1 = (range) => {
|
|
16293
16420
|
const match = /(\d+)\.(\d+)\.(\d+)/.exec(range);
|
|
@@ -16384,8 +16511,20 @@ var readText$1 = async (file) => {
|
|
|
16384
16511
|
return;
|
|
16385
16512
|
}
|
|
16386
16513
|
};
|
|
16514
|
+
var holdsFiles = async (folder) => {
|
|
16515
|
+
try {
|
|
16516
|
+
return (await fs.readdir(folder)).length > 0;
|
|
16517
|
+
} catch {
|
|
16518
|
+
return false;
|
|
16519
|
+
}
|
|
16520
|
+
};
|
|
16387
16521
|
var planFiles = async (root, files, recorded, take) => Promise.all(Object.entries(files).map(async ([file, ours]) => {
|
|
16388
16522
|
const yours = await readText$1(path.join(root, file));
|
|
16523
|
+
if (yours === void 0 && path.basename(file) === ".gitkeep" && await holdsFiles(path.join(root, file, ".."))) return {
|
|
16524
|
+
file,
|
|
16525
|
+
status: "current",
|
|
16526
|
+
ours
|
|
16527
|
+
};
|
|
16389
16528
|
if (yours === void 0) return {
|
|
16390
16529
|
file,
|
|
16391
16530
|
status: "added",
|
|
@@ -16544,6 +16683,10 @@ var reportText$3 = (report, install) => {
|
|
|
16544
16683
|
lines.push(report.renames.length === 0 ? chalk.green("renames: none written") : "renames:");
|
|
16545
16684
|
lines.push(...report.renames.map((found) => chalk.yellow(` ${report.write ? "~" : "!"} ${found.file}:${String(found.line)} ${found.name} → ${found.to} (${found.since}: ${found.what})`)));
|
|
16546
16685
|
}
|
|
16686
|
+
if (report.changes && report.changes.length > 0) {
|
|
16687
|
+
lines.push("changes the source may count on (look, then decide — never written):");
|
|
16688
|
+
lines.push(...report.changes.map((found) => chalk.yellow(` ! ${found.file}:${String(found.line)} (${found.since}) ${found.what}`)));
|
|
16689
|
+
}
|
|
16547
16690
|
return lines.join("\n");
|
|
16548
16691
|
};
|
|
16549
16692
|
var upgrade = async (parts, options) => {
|
|
@@ -16640,8 +16783,11 @@ var upgrade = async (parts, options) => {
|
|
|
16640
16783
|
}
|
|
16641
16784
|
if (wanted.has("renames")) {
|
|
16642
16785
|
const found = [];
|
|
16786
|
+
const changes = [];
|
|
16787
|
+
const before = record === void 0 || isBelow(record.cli, OPTIONAL_FIELDS_SINCE);
|
|
16643
16788
|
for (const file of await sourceFiles(root)) {
|
|
16644
16789
|
const text = await readText$1(path.join(root, file));
|
|
16790
|
+
if (before && text !== void 0) changes.push(...optionalFieldsIn(file, text));
|
|
16645
16791
|
const inFile = text === void 0 ? [] : renamesIn(file, text);
|
|
16646
16792
|
if (write && text !== void 0 && inFile.length > 0) {
|
|
16647
16793
|
const renamed = [...new Set(inFile.map((entry) => entry.name))].reduce((result, name) => result.replace(wordOf(name), RENAMES.find((rename) => rename.name === name)?.to ?? name), text);
|
|
@@ -16650,6 +16796,7 @@ var upgrade = async (parts, options) => {
|
|
|
16650
16796
|
found.push(...inFile);
|
|
16651
16797
|
}
|
|
16652
16798
|
report.renames = found;
|
|
16799
|
+
report.changes = changes;
|
|
16653
16800
|
}
|
|
16654
16801
|
console.log(options.json ? JSON.stringify(report) : reportText$3(report, installCommand(manager)));
|
|
16655
16802
|
if (report.packages?.install === "failed") {
|
|
@@ -18285,20 +18432,69 @@ var move$1 = async (elementId, options) => {
|
|
|
18285
18432
|
].join("\n"));
|
|
18286
18433
|
};
|
|
18287
18434
|
//#endregion
|
|
18435
|
+
//#region src/commands/projectSettings.ts
|
|
18436
|
+
/**
|
|
18437
|
+
* What the project's processes start with: what its `.env` sets, under what the environment already has — as its
|
|
18438
|
+
* scripts read it (`--env-file-if-exists=.env`). Read, never loaded into this process's own environment.
|
|
18439
|
+
*/
|
|
18440
|
+
var projectSettings = async (root) => {
|
|
18441
|
+
const text = await fs.readFile(path.join(root, ".env"), "utf-8").catch(() => void 0);
|
|
18442
|
+
const parsed = text === void 0 ? {} : parseEnv(text);
|
|
18443
|
+
return {
|
|
18444
|
+
file: text !== void 0,
|
|
18445
|
+
value: (name) => process.env[name] ?? parsed[name] ?? ""
|
|
18446
|
+
};
|
|
18447
|
+
};
|
|
18448
|
+
//#endregion
|
|
18288
18449
|
//#region src/commands/check.ts
|
|
18289
|
-
/**
|
|
18450
|
+
/** The account `verify` signs in as for the pages that send a visitor to sign in: the project's, in `.env`. */
|
|
18451
|
+
var CHECK_USER_ENV = "PLITZI_CHECK_USER";
|
|
18452
|
+
/** Where the account's password is read: `.env` or the environment, never the command line, which a shell keeps. */
|
|
18290
18453
|
var CHECK_PASSWORD_ENV = "PLITZI_CHECK_PASSWORD";
|
|
18291
18454
|
/** The project's sign-in route: where `createServer({ auth })` mounts its flows unless told otherwise. */
|
|
18292
18455
|
var SIGN_IN_PATH = "/auth/login";
|
|
18456
|
+
/**
|
|
18457
|
+
* The account a check signs in as: the one named, or the project's own (`PLITZI_CHECK_USER`) — `undefined` when
|
|
18458
|
+
* neither is — with its password from `PLITZI_CHECK_PASSWORD`, each read from the environment over `.env`, as the
|
|
18459
|
+
* project's scripts read them.
|
|
18460
|
+
*/
|
|
18461
|
+
var checkAccount = async (root, username) => {
|
|
18462
|
+
const settings = await projectSettings(root);
|
|
18463
|
+
const named = username ?? settings.value("PLITZI_CHECK_USER");
|
|
18464
|
+
if (!named) return;
|
|
18465
|
+
const password = settings.value(CHECK_PASSWORD_ENV);
|
|
18466
|
+
if (!password) return { problem: `${named} signs in with the password in ${CHECK_PASSWORD_ENV}: set it in .env — never on the command line, which a shell keeps` };
|
|
18467
|
+
return {
|
|
18468
|
+
username: named,
|
|
18469
|
+
password
|
|
18470
|
+
};
|
|
18471
|
+
};
|
|
18472
|
+
/** Signs the page's browser in as the account, through the server's `/auth` routes: what went wrong, or nothing. */
|
|
18473
|
+
var signInAs = async (page, origin, account) => {
|
|
18474
|
+
const answer = await page.request.post(`${origin}${SIGN_IN_PATH}`, { data: {
|
|
18475
|
+
username: account.username,
|
|
18476
|
+
password: account.password
|
|
18477
|
+
} });
|
|
18478
|
+
if (answer.ok()) return;
|
|
18479
|
+
return answer.status() === 404 ? `nothing answers at ${SIGN_IN_PATH}: --as signs in through the routes createServer({ auth }) mounts there` : `${account.username} was not signed in (${String(answer.status())}): ${(await answer.text()).slice(0, 200)} — check ${CHECK_PASSWORD_ENV} in .env`;
|
|
18480
|
+
};
|
|
18481
|
+
var withoutTrailingSlash = (path) => path.length > 1 ? path.replace(/\/+$/, "") : path;
|
|
18482
|
+
/** Where the page landed, when that is not the path it was opened at — sent to the sign-in, most often. */
|
|
18483
|
+
var sentElsewhere = (page, pathname) => {
|
|
18484
|
+
const landed = new URL(page.url()).pathname;
|
|
18485
|
+
const asked = new URL(pathname, page.url()).pathname;
|
|
18486
|
+
return withoutTrailingSlash(landed) === withoutTrailingSlash(asked) ? void 0 : landed;
|
|
18487
|
+
};
|
|
18293
18488
|
/** The space the project declares, authored. Only for one written here. */
|
|
18294
18489
|
var projectSpace = async (root) => {
|
|
18295
18490
|
const project = await loadProjectSpace(root);
|
|
18296
18491
|
if ("problem" in project) return { problem: `${project.problem} There is nothing to check the page against.` };
|
|
18297
18492
|
try {
|
|
18298
|
-
const { handles, schema } = authorSpace(project.space, project.authoring);
|
|
18493
|
+
const { handles, schema, style } = authorSpace(project.space, project.authoring);
|
|
18299
18494
|
return {
|
|
18300
18495
|
handles,
|
|
18301
|
-
schema
|
|
18496
|
+
schema,
|
|
18497
|
+
schemes: SCHEMES.filter((scheme) => style.theme.schemes.includes(scheme))
|
|
18302
18498
|
};
|
|
18303
18499
|
} catch (error) {
|
|
18304
18500
|
return { problem: `The space does not author — npm run author says why: ${error instanceof Error ? error.message.split("\n")[0] : String(error)}` };
|
|
@@ -18310,12 +18506,25 @@ var projectSpace = async (root) => {
|
|
|
18310
18506
|
* (`/p/:slug`), so the one meant is read off the list rather than guessed from letters.
|
|
18311
18507
|
*/
|
|
18312
18508
|
var noPageMessage = (handles, pathname) => `no page of the space answers at ${pathname}: its pages are ${Object.values(handles.pages).map((page) => page.path).join(", ")}`;
|
|
18509
|
+
/**
|
|
18510
|
+
* The space's "not found" page: a page whose slug is `*`, the page of every address under its folder that no other
|
|
18511
|
+
* page answers. Not every path ending in `*` — `update/*` is a page of its own that answers a whole subtree.
|
|
18512
|
+
*/
|
|
18513
|
+
var isNotFoundPage = (page) => page.slug === "*";
|
|
18514
|
+
/** A path whose last segment is `*`: it answers every address under it. */
|
|
18515
|
+
var isSplat = (path) => path.endsWith("/*");
|
|
18516
|
+
/** Where `verify` looks at a page for an address nothing answers: an address under its folder no page has. */
|
|
18517
|
+
var NOT_FOUND_PROBE = "plitzi-check-not-found";
|
|
18518
|
+
/** The page an address is answered by — as the router picks: every page with a path of its own first, then the "not found" pages, the deepest folder's before the space's. */
|
|
18313
18519
|
var pageFor = (handles, pathname) => {
|
|
18314
18520
|
const wanted = pathname.replace(/\/$/, "").split("/");
|
|
18315
|
-
|
|
18316
|
-
const
|
|
18317
|
-
|
|
18318
|
-
|
|
18521
|
+
const answers = (path) => {
|
|
18522
|
+
const splat = isSplat(path);
|
|
18523
|
+
const segments = (splat ? path.slice(0, -2) : path.replace(/\/$/, "")).split("/");
|
|
18524
|
+
return (splat ? wanted.length > segments.length : segments.length === wanted.length) && segments.every((segment, index) => segment.startsWith(":") || /^\{\{.*\}\}$/.test(segment) || segment === wanted[index]);
|
|
18525
|
+
};
|
|
18526
|
+
const pages = Object.values(handles.pages);
|
|
18527
|
+
return [...pages.filter((page) => !isNotFoundPage(page)), ...pages.filter(isNotFoundPage).sort((a, b) => b.path.length - a.path.length)].find((page) => answers(page.path))?.id;
|
|
18319
18528
|
};
|
|
18320
18529
|
/** The elements a piece of HTML carries, by the id each is marked with. */
|
|
18321
18530
|
var markedIn = (html) => new Set([...html.matchAll(/data-plitzi-el="([^"]+)"/g)].map(([, id]) => id));
|
|
@@ -18701,24 +18910,25 @@ var checkAt = async (browser, origin, pathname, width, scheme, authored, asked,
|
|
|
18701
18910
|
devTools: false,
|
|
18702
18911
|
lists: {}
|
|
18703
18912
|
});
|
|
18704
|
-
|
|
18705
|
-
|
|
18706
|
-
|
|
18707
|
-
|
|
18708
|
-
|
|
18709
|
-
|
|
18710
|
-
code: "not-signed-in",
|
|
18711
|
-
message: signedIn.status() === 404 ? `nothing answers at ${SIGN_IN_PATH}: --as signs in through the routes createServer({ auth }) mounts there` : `${account.username} was not signed in (${String(signedIn.status())}): ${(await signedIn.text()).slice(0, 200)} — check ${CHECK_PASSWORD_ENV}`,
|
|
18712
|
-
width
|
|
18713
|
-
});
|
|
18714
|
-
}
|
|
18913
|
+
const notSignedIn = account && await signInAs(page, origin, account);
|
|
18914
|
+
if (notSignedIn) return unchecked({
|
|
18915
|
+
code: "not-signed-in",
|
|
18916
|
+
message: notSignedIn,
|
|
18917
|
+
width
|
|
18918
|
+
});
|
|
18715
18919
|
const consoleErrors = [];
|
|
18716
18920
|
const failedRequests = [];
|
|
18717
18921
|
page.on("pageerror", (error) => consoleErrors.push(error.message));
|
|
18922
|
+
const address = `${origin}${pathname}`;
|
|
18718
18923
|
page.on("console", (message) => {
|
|
18719
|
-
if (message.type() === "error") consoleErrors.push(message.text());
|
|
18924
|
+
if (message.type() === "error" && message.location().url !== address) consoleErrors.push(message.text());
|
|
18720
18925
|
});
|
|
18926
|
+
let sentWith;
|
|
18721
18927
|
page.on("response", (response) => {
|
|
18928
|
+
if (sentWith === void 0 && response.url() === address) {
|
|
18929
|
+
sentWith = response.status();
|
|
18930
|
+
return;
|
|
18931
|
+
}
|
|
18722
18932
|
if (response.status() >= 400 && response.url().startsWith(origin)) failedRequests.push(`${String(response.status())} ${response.url().slice(origin.length)}`);
|
|
18723
18933
|
});
|
|
18724
18934
|
if (handles && !pageFor(handles, pathname)) return {
|
|
@@ -18739,11 +18949,11 @@ var checkAt = async (browser, origin, pathname, width, scheme, authored, asked,
|
|
|
18739
18949
|
consoleErrors,
|
|
18740
18950
|
failedRequests
|
|
18741
18951
|
};
|
|
18742
|
-
const landed =
|
|
18743
|
-
if (
|
|
18952
|
+
const landed = sentElsewhere(page, pathname);
|
|
18953
|
+
if (landed) return {
|
|
18744
18954
|
...unchecked({
|
|
18745
18955
|
code: "redirected",
|
|
18746
|
-
message: account ? `${pathname} sent the browser to ${landed}, signed in as ${account.username}: that account may not see it` : `${pathname} sent the browser to ${landed} — what a page for signed-in visitors does: \`plitzi page check ${pathname} --as <username>\` signs in first, the password in ${CHECK_PASSWORD_ENV}`,
|
|
18956
|
+
message: account ? `${pathname} sent the browser to ${landed}, signed in as ${account.username}: that account may not see it` : `${pathname} sent the browser to ${landed} — what a page for signed-in visitors does: \`plitzi page check ${pathname} --as <username>\` signs in first, the password in ${CHECK_PASSWORD_ENV} in .env`,
|
|
18747
18957
|
width
|
|
18748
18958
|
}),
|
|
18749
18959
|
consoleErrors,
|
|
@@ -18773,8 +18983,18 @@ var checkAt = async (browser, origin, pathname, width, scheme, authored, asked,
|
|
|
18773
18983
|
...flow.on === void 0 ? {} : { elementId: flow.on },
|
|
18774
18984
|
width
|
|
18775
18985
|
}));
|
|
18986
|
+
const statusIssues = (handles && pageId ? isNotFoundPage(handles.pages[pageId]) : false) && sentWith !== 404 ? [{
|
|
18987
|
+
code: "page-status",
|
|
18988
|
+
message: `${pathname} shows the page for an address nothing answers, sent with status ${String(sentWith)}: a crawler reads it as a page that exists — the server should send it with 404`,
|
|
18989
|
+
width
|
|
18990
|
+
}] : sentWith !== void 0 && sentWith >= 400 && sentWith !== 404 ? [{
|
|
18991
|
+
code: "page-status",
|
|
18992
|
+
message: `${pathname} was sent with status ${String(sentWith)}`,
|
|
18993
|
+
width
|
|
18994
|
+
}] : [];
|
|
18776
18995
|
const issues = [
|
|
18777
18996
|
...noPage,
|
|
18997
|
+
...statusIssues,
|
|
18778
18998
|
...report.issues.map((issue) => ({
|
|
18779
18999
|
...issue,
|
|
18780
19000
|
width
|
|
@@ -18803,7 +19023,8 @@ var checkAt = async (browser, origin, pathname, width, scheme, authored, asked,
|
|
|
18803
19023
|
...!asked.state || devTools.sources === void 0 ? {} : { sources: devTools.sources },
|
|
18804
19024
|
...devTools.element === void 0 ? {} : { element: devTools.element },
|
|
18805
19025
|
...styled === void 0 ? {} : { styles: styled },
|
|
18806
|
-
...clicked === void 0 ? {} : { clicked }
|
|
19026
|
+
...clicked === void 0 ? {} : { clicked },
|
|
19027
|
+
...sentWith === 404 ? { notFound: true } : {}
|
|
18807
19028
|
};
|
|
18808
19029
|
};
|
|
18809
19030
|
/** What the page holds, when it was asked for: one compact line each, the shape of a source rather than its rows. */
|
|
@@ -18857,8 +19078,9 @@ var clickedText = (clicked) => {
|
|
|
18857
19078
|
];
|
|
18858
19079
|
};
|
|
18859
19080
|
var reportText$2 = (report, asked) => {
|
|
18860
|
-
const head = `${report.path} at ${String(report.width)} px`;
|
|
19081
|
+
const head = `${report.path} at ${String(report.width)} px${report.scheme ? `, ${report.scheme}` : ""}`;
|
|
18861
19082
|
const held = [
|
|
19083
|
+
...report.notFound ? [" · sent with status 404: the page for an address that shows nothing"] : [],
|
|
18862
19084
|
...listsText(report.lists),
|
|
18863
19085
|
...browserOnlyText(report),
|
|
18864
19086
|
...heldText(report, asked),
|
|
@@ -18878,28 +19100,27 @@ var reportText$2 = (report, asked) => {
|
|
|
18878
19100
|
...held
|
|
18879
19101
|
].join("\n");
|
|
18880
19102
|
};
|
|
18881
|
-
var
|
|
18882
|
-
var check = async (route, options) => {
|
|
19103
|
+
var check = async (routes, options) => {
|
|
18883
19104
|
const project = await projectHere("whose page to check");
|
|
18884
19105
|
if (!project) return;
|
|
18885
|
-
const
|
|
18886
|
-
if (
|
|
18887
|
-
fail(`--as ${
|
|
19106
|
+
const account = options.as === void 0 ? void 0 : await checkAccount(project.root, options.as);
|
|
19107
|
+
if (account && "problem" in account) {
|
|
19108
|
+
fail(`--as ${account.problem}.`);
|
|
18888
19109
|
return;
|
|
18889
19110
|
}
|
|
18890
19111
|
if (options.fill && !options.click) {
|
|
18891
19112
|
fail("--fill fills fields for a --click: with nothing clicked, nothing they hold is sent anywhere.");
|
|
18892
19113
|
return;
|
|
18893
19114
|
}
|
|
18894
|
-
|
|
18895
|
-
|
|
18896
|
-
|
|
18897
|
-
}
|
|
19115
|
+
if (options.click && routes.length > 1) {
|
|
19116
|
+
fail(`--click clicks one element of one page: ${String(routes.length)} were named.`);
|
|
19117
|
+
return;
|
|
19118
|
+
}
|
|
18898
19119
|
const asked = {
|
|
18899
19120
|
state: Boolean(options.state),
|
|
18900
19121
|
...options.element ? { element: options.element } : {}
|
|
18901
19122
|
};
|
|
18902
|
-
const reports = await checkRoutes(project,
|
|
19123
|
+
const reports = await checkRoutes(project, routes.length > 0 ? routes : ["/"], {
|
|
18903
19124
|
widths: options.width ?? [1440, 390],
|
|
18904
19125
|
asked,
|
|
18905
19126
|
ssr: Boolean(options.ssr),
|
|
@@ -18932,17 +19153,26 @@ var checkRoutes = async (project, routes, options) => {
|
|
|
18932
19153
|
if ("problem" in browser) return browser;
|
|
18933
19154
|
try {
|
|
18934
19155
|
const reports = [];
|
|
18935
|
-
for (const route of routes) for (const width of options.widths)
|
|
19156
|
+
for (const route of routes) for (const width of options.widths) {
|
|
19157
|
+
const report = await checkAt(browser, where.origin, route, width, options.scheme, authored, options.asked, options.ssr, options.account, options.click);
|
|
19158
|
+
reports.push({
|
|
19159
|
+
...report,
|
|
19160
|
+
...options.scheme ? { scheme: options.scheme } : {}
|
|
19161
|
+
});
|
|
19162
|
+
}
|
|
18936
19163
|
return reports;
|
|
18937
19164
|
} finally {
|
|
18938
19165
|
await browser.close();
|
|
18939
19166
|
}
|
|
18940
19167
|
};
|
|
18941
|
-
/**
|
|
19168
|
+
/** What a whole-project check opens: every page with no parameter in its path, in every theme the space can show. */
|
|
18942
19169
|
var staticPaths = async (root) => {
|
|
18943
19170
|
const authored = await projectSpace(root);
|
|
18944
19171
|
if ("problem" in authored) return authored;
|
|
18945
|
-
return
|
|
19172
|
+
return {
|
|
19173
|
+
paths: Object.values(authored.handles.pages).map((page) => isNotFoundPage(page) ? `${page.path.slice(0, -1)}${NOT_FOUND_PROBE}` : page.path).filter((pathname) => pathname.split("/").every((segment) => !segment.startsWith(":") && !/\{\{.*\}\}/.test(segment))),
|
|
19174
|
+
schemes: authored.schemes
|
|
19175
|
+
};
|
|
18946
19176
|
};
|
|
18947
19177
|
//#endregion
|
|
18948
19178
|
//#region src/commands/functions.ts
|
|
@@ -21056,12 +21286,12 @@ var measure = async (browser, url, width, scheme, read = []) => {
|
|
|
21056
21286
|
screen: await page.screenshot({ fullPage: true })
|
|
21057
21287
|
};
|
|
21058
21288
|
};
|
|
21059
|
-
var count$
|
|
21289
|
+
var count$3 = (amount, one, many = `${one}s`) => `${String(amount)} ${amount === 1 ? one : many}`;
|
|
21060
21290
|
var summaryText = (summary, out, stale) => [
|
|
21061
21291
|
chalk.green(`Imported ${summary.url} into ${out}`),
|
|
21062
|
-
` tokens ${count$
|
|
21063
|
-
` outline ${count$
|
|
21064
|
-
...summary.lists.length > 0 ? [` lists ${summary.lists.map((list) => `${list.file} (${count$
|
|
21292
|
+
` tokens ${count$3(summary.colours, "colour")} (${summary.dark ? "light and dark" : "no dark scheme: dark repeats light"}), ${count$3(summary.shadows, "shadow")}, ${count$3(summary.radii, "radius", "radii")}${summary.fonts.length > 0 ? `, fonts ${summary.fonts.join(", ")}` : ""}`,
|
|
21293
|
+
` outline ${count$3(summary.blocks, "block")} at ${summary.widths.join(", ")} px`,
|
|
21294
|
+
...summary.lists.length > 0 ? [` lists ${summary.lists.map((list) => `${list.file} (${count$3(list.rows, "row")})`).join(", ")}`] : [],
|
|
21065
21295
|
` pictures ${String(summary.pictures)} in assets.json`,
|
|
21066
21296
|
...stale.length > 0 ? [chalk.yellow(` earlier ${stale.join(", ")} ${stale.length === 1 ? "was" : "were"} there before and not written this time: delete what is stale`)] : [],
|
|
21067
21297
|
` next ${path.join(out, "IMPORT.md")} says what was not carried over`
|
|
@@ -22279,7 +22509,7 @@ var projectConnectors = async (root) => {
|
|
|
22279
22509
|
manifest: value.manifest
|
|
22280
22510
|
}] : []);
|
|
22281
22511
|
};
|
|
22282
|
-
var count$
|
|
22512
|
+
var count$2 = (entries, noun) => entries ? `${String(entries.length)} ${noun}${entries.length === 1 ? "" : "s"}` : `the space’s ${noun}s as they are`;
|
|
22283
22513
|
/**
|
|
22284
22514
|
* What of the space would not reach Plitzi with it, said before it is sent — never left to be found on a page that
|
|
22285
22515
|
* renders empty there:
|
|
@@ -22364,7 +22594,7 @@ var pushSpaceOf = async (root, connection, space, { base, force, downloads }) =>
|
|
|
22364
22594
|
outcome: "unchanged",
|
|
22365
22595
|
draft: data.draft
|
|
22366
22596
|
};
|
|
22367
|
-
console.log(chalk.green(`The space is ${space.name}’s draft now — its pages and styles, ${count$
|
|
22597
|
+
console.log(chalk.green(`The space is ${space.name}’s draft now — its pages and styles, ${count$2(sent.actions, "action")}, ${count$2(sent.connectors, "connector")}.`) + chalk.dim(" Any builder open on it shows it now."));
|
|
22368
22598
|
return {
|
|
22369
22599
|
outcome: "pushed",
|
|
22370
22600
|
draft: data.draft
|
|
@@ -23392,7 +23622,107 @@ var _ = `[data-motion-enter], [data-motion-stagger] > *, [data-motion-loop] { an
|
|
|
23392
23622
|
"[data-motion-on=\"scroll\"][data-motion-enter], [data-motion-on=\"scroll\"][data-motion-stagger] > * { animation-timeline: auto, auto !important; }"
|
|
23393
23623
|
].join("\n");
|
|
23394
23624
|
//#endregion
|
|
23625
|
+
//#region src/commands/shotSteps.ts
|
|
23626
|
+
var STEP_NAMES = [
|
|
23627
|
+
"click",
|
|
23628
|
+
"type",
|
|
23629
|
+
"press",
|
|
23630
|
+
"wait",
|
|
23631
|
+
"wait-for",
|
|
23632
|
+
"shot",
|
|
23633
|
+
"frames"
|
|
23634
|
+
];
|
|
23635
|
+
/** The longest a script may wait in one step: a page that needs more is waiting for something else. */
|
|
23636
|
+
var MAX_WAIT_MS = 3e4;
|
|
23637
|
+
var unquoted = (text) => text.replace(/^(["'])(.*)\1$/s, "$2");
|
|
23638
|
+
var count$1 = (value, at, max) => {
|
|
23639
|
+
const parsed = Number(value);
|
|
23640
|
+
return Number.isInteger(parsed) && parsed > 0 && parsed <= max ? parsed : { problem: `${at}: "${value ?? ""}" is not a whole number from 1 to ${String(max)}` };
|
|
23641
|
+
};
|
|
23642
|
+
/** A script, read into its steps — or what is wrong with it, said by its step. */
|
|
23643
|
+
var parseSteps = (script, every) => {
|
|
23644
|
+
const lines = script.split(/[;\n]/).map((line) => line.trim()).filter((line) => line !== "" && !line.startsWith("#"));
|
|
23645
|
+
if (lines.length === 0) return { problem: "--steps has no step: click, type, press, wait, wait-for, shot or frames, separated by \";\"" };
|
|
23646
|
+
const steps = [];
|
|
23647
|
+
for (const [index, line] of lines.entries()) {
|
|
23648
|
+
const at = `step ${String(index + 1)} ("${line}")`;
|
|
23649
|
+
const [name, ...rest] = line.split(/\s+/);
|
|
23650
|
+
const argument = unquoted(rest.join(" "));
|
|
23651
|
+
switch (name) {
|
|
23652
|
+
case "click":
|
|
23653
|
+
case "wait-for":
|
|
23654
|
+
if (!argument) return { problem: `${at}: ${name} needs an element — its name or a CSS selector` };
|
|
23655
|
+
steps.push({
|
|
23656
|
+
kind: name,
|
|
23657
|
+
target: argument
|
|
23658
|
+
});
|
|
23659
|
+
break;
|
|
23660
|
+
case "type": {
|
|
23661
|
+
const [target, ...words] = rest;
|
|
23662
|
+
if (!target || words.length === 0) return { problem: `${at}: type needs an element and the text — type <element> <text>` };
|
|
23663
|
+
steps.push({
|
|
23664
|
+
kind: "type",
|
|
23665
|
+
target,
|
|
23666
|
+
text: unquoted(words.join(" "))
|
|
23667
|
+
});
|
|
23668
|
+
break;
|
|
23669
|
+
}
|
|
23670
|
+
case "press":
|
|
23671
|
+
if (!argument) return { problem: `${at}: press needs a key — Enter, Escape, ArrowDown…` };
|
|
23672
|
+
steps.push({
|
|
23673
|
+
kind: "press",
|
|
23674
|
+
key: argument
|
|
23675
|
+
});
|
|
23676
|
+
break;
|
|
23677
|
+
case "wait": {
|
|
23678
|
+
const ms = count$1(rest[0], at, MAX_WAIT_MS);
|
|
23679
|
+
if (typeof ms !== "number") return ms;
|
|
23680
|
+
steps.push({
|
|
23681
|
+
kind: "wait",
|
|
23682
|
+
ms
|
|
23683
|
+
});
|
|
23684
|
+
break;
|
|
23685
|
+
}
|
|
23686
|
+
case "shot":
|
|
23687
|
+
steps.push({
|
|
23688
|
+
kind: "shot",
|
|
23689
|
+
...argument ? { label: argument } : {}
|
|
23690
|
+
});
|
|
23691
|
+
break;
|
|
23692
|
+
case "frames": {
|
|
23693
|
+
const frames = count$1(rest[0], at, 60);
|
|
23694
|
+
if (typeof frames !== "number") return frames;
|
|
23695
|
+
const apart = rest.length < 2 ? every : count$1(rest[1], at, MAX_WAIT_MS);
|
|
23696
|
+
if (typeof apart !== "number") return apart;
|
|
23697
|
+
steps.push({
|
|
23698
|
+
kind: "frames",
|
|
23699
|
+
count: frames,
|
|
23700
|
+
every: apart
|
|
23701
|
+
});
|
|
23702
|
+
break;
|
|
23703
|
+
}
|
|
23704
|
+
default: return { problem: `${at}: "${name}" is not a step — the steps are ${STEP_NAMES.join(", ")}` };
|
|
23705
|
+
}
|
|
23706
|
+
}
|
|
23707
|
+
return steps;
|
|
23708
|
+
};
|
|
23709
|
+
//#endregion
|
|
23395
23710
|
//#region src/commands/shot.ts
|
|
23711
|
+
/**
|
|
23712
|
+
* `plitzi page shot`: a picture of one page of the project — and, asked, the numbers that say what a picture would.
|
|
23713
|
+
*
|
|
23714
|
+
* plitzi page shot /about --width 390 --scheme dark
|
|
23715
|
+
* plitzi page shot / --compare https://example.com --width 1440 # beside another site: what differs, and how
|
|
23716
|
+
* plitzi page shot / --frames 4 --every 500 # what moves: a marquee, an autoplay
|
|
23717
|
+
* plitzi page shot / --clip pricing-table # one element, scrolled to wherever it is
|
|
23718
|
+
* plitzi page shot / --scroll-to faq --viewport --frames 6 # the screen at that section, as it animates
|
|
23719
|
+
*
|
|
23720
|
+
* The project's server has to be running (`npm start`). `--json` answers in one object, for a tool or an agent. The
|
|
23721
|
+
* dev tools' badge is never in the picture; a picture of the whole page has every lazy image loaded and shows every
|
|
23722
|
+
* arrival that waits for the scroll as it ends — taken without scrolling, they would be holes where the sections are.
|
|
23723
|
+
*/
|
|
23724
|
+
/** When a run's first picture is taken: once the page settles, or as soon as its HTML is in. */
|
|
23725
|
+
var SHOT_MOMENTS = ["settled", "load"];
|
|
23396
23726
|
/** A region moves when more than this share of it changed between two frames: below is anti-aliasing and carets. */
|
|
23397
23727
|
var MOVING_PERCENT = .5;
|
|
23398
23728
|
/** How long a whole page waits for its pictures before it is taken anyway. */
|
|
@@ -23403,8 +23733,8 @@ var TEXTS_SHOWN = 12;
|
|
|
23403
23733
|
var DRIFT_PX = 2;
|
|
23404
23734
|
/** An arrival that waits for the scroll and has not had it: one waiting to be seen, or one tied to the scroll. */
|
|
23405
23735
|
var WAITING_FOR_SCROLL = "[data-motion-on=\"view\"]:not([data-motion-seen]), [data-motion-on=\"scroll\"]";
|
|
23406
|
-
/** A selector, or
|
|
23407
|
-
var selectorOf = (target) => /^[#.[]/.test(target) ? target : `[data-plitzi-el="${target}"]`;
|
|
23736
|
+
/** A selector — CSS, or Playwright's own (`text=Saved`, `role=button[name="Publish"]`) — or an element's name. */
|
|
23737
|
+
var selectorOf = (target) => /^([#.[]|(text|role|css|xpath)=)/.test(target) ? target : `[data-plitzi-el="${target}"]`;
|
|
23408
23738
|
var framing = (view) => {
|
|
23409
23739
|
if (view.clip) return {
|
|
23410
23740
|
kind: "element",
|
|
@@ -23429,7 +23759,10 @@ var pictureOf = async (browser, url, view, origin) => {
|
|
|
23429
23759
|
colorScheme: view.scheme ?? "light",
|
|
23430
23760
|
reducedMotion: view.reducedMotion ? "reduce" : "no-preference"
|
|
23431
23761
|
});
|
|
23432
|
-
|
|
23762
|
+
const notSignedIn = origin && view.account && await signInAs(page, origin, view.account);
|
|
23763
|
+
if (notSignedIn) return { problem: `--as ${notSignedIn}.` };
|
|
23764
|
+
if (!(view.fromLoad ? await page.goto(url, { waitUntil: "domcontentloaded" }).catch(() => null) : await openPage(page, url))) return { problem: `Nothing answers at ${url}.${origin ? " Start the project first: npm start" : ""}` };
|
|
23765
|
+
const sentTo = origin ? sentElsewhere(page, new URL(url).pathname) : void 0;
|
|
23433
23766
|
if (view.waitFor) {
|
|
23434
23767
|
if (!await page.waitForSelector(selectorOf(view.waitFor), { timeout: 1e4 }).catch(() => null)) return { problem: `${view.waitFor} did not appear on ${url} within 10 s.` };
|
|
23435
23768
|
}
|
|
@@ -23446,7 +23779,8 @@ var pictureOf = async (browser, url, view, origin) => {
|
|
|
23446
23779
|
png: await capture(page, framing(view)),
|
|
23447
23780
|
scheme: await paintedScheme(page, view.scheme ?? "light"),
|
|
23448
23781
|
settled,
|
|
23449
|
-
unloaded: pictures?.missing ?? 0
|
|
23782
|
+
unloaded: pictures?.missing ?? 0,
|
|
23783
|
+
...sentTo ? { sentTo } : {}
|
|
23450
23784
|
};
|
|
23451
23785
|
};
|
|
23452
23786
|
var shiftText = (shift) => shift !== void 0 && Math.abs(shift) > DRIFT_PX ? ` (${shift > 0 ? "+" : ""}${String(shift)}px there)` : "";
|
|
@@ -23510,6 +23844,141 @@ var compareWith = async (browser, taken, target, view, regions, base) => {
|
|
|
23510
23844
|
diff: `${base}-diff.png`
|
|
23511
23845
|
};
|
|
23512
23846
|
};
|
|
23847
|
+
/**
|
|
23848
|
+
* The pictures on one sheet, each under what it shows, four to a row and each 360 px wide: runs in the page, so it is
|
|
23849
|
+
* self-contained — no import, nothing it closes over.
|
|
23850
|
+
*/
|
|
23851
|
+
function contactSheet(cells) {
|
|
23852
|
+
const CELL_WIDTH = 360;
|
|
23853
|
+
const LABEL = 24;
|
|
23854
|
+
const GAP = 12;
|
|
23855
|
+
const load = (src) => new Promise((resolve, reject) => {
|
|
23856
|
+
const image = new Image();
|
|
23857
|
+
image.onload = () => resolve(image);
|
|
23858
|
+
image.onerror = () => reject(/* @__PURE__ */ new Error("a picture of the sheet did not load"));
|
|
23859
|
+
image.src = src;
|
|
23860
|
+
});
|
|
23861
|
+
return Promise.all(cells.map((cell) => load(cell.src))).then((images) => {
|
|
23862
|
+
const columns = Math.min(4, images.length);
|
|
23863
|
+
const heightOf = (image) => Math.round(image.naturalHeight * CELL_WIDTH / image.naturalWidth);
|
|
23864
|
+
const cellHeight = Math.max(...images.map(heightOf));
|
|
23865
|
+
const rows = Math.ceil(images.length / columns);
|
|
23866
|
+
const canvas = document.createElement("canvas");
|
|
23867
|
+
canvas.width = GAP + columns * 372;
|
|
23868
|
+
canvas.height = GAP + rows * (LABEL + cellHeight + GAP);
|
|
23869
|
+
const context = canvas.getContext("2d");
|
|
23870
|
+
if (!context) throw new Error("no canvas to draw the sheet on");
|
|
23871
|
+
context.fillStyle = "#ffffff";
|
|
23872
|
+
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
23873
|
+
context.font = "13px ui-monospace, monospace";
|
|
23874
|
+
images.forEach((image, index) => {
|
|
23875
|
+
const x = GAP + index % columns * 372;
|
|
23876
|
+
const y = GAP + Math.floor(index / columns) * (LABEL + cellHeight + GAP);
|
|
23877
|
+
context.fillStyle = "#16181d";
|
|
23878
|
+
context.fillText(cells[index].label, x, y + 16);
|
|
23879
|
+
context.drawImage(image, x, y + LABEL, CELL_WIDTH, heightOf(image));
|
|
23880
|
+
context.strokeStyle = "#d0d4dc";
|
|
23881
|
+
context.strokeRect(x + .5, y + LABEL + .5, 359, heightOf(image) - 1);
|
|
23882
|
+
});
|
|
23883
|
+
return canvas.toDataURL("image/png");
|
|
23884
|
+
});
|
|
23885
|
+
}
|
|
23886
|
+
/**
|
|
23887
|
+
* Each element clicked in turn, and the page pictured after it — `frames` pictures `every` ms apart, or one. What a
|
|
23888
|
+
* click changed is measured against the picture before it: nothing at all is said, as a click that did nothing.
|
|
23889
|
+
*/
|
|
23890
|
+
var clickThrough = async (taken, how, clicks, { frames, every }) => {
|
|
23891
|
+
const moments = [{
|
|
23892
|
+
label: "before",
|
|
23893
|
+
png: taken.png
|
|
23894
|
+
}];
|
|
23895
|
+
const clicked = [];
|
|
23896
|
+
for (const target of clicks) {
|
|
23897
|
+
const element = taken.page.locator(selectorOf(target)).first();
|
|
23898
|
+
if (await element.count() === 0) return { problem: `${target} is not on the page: --click takes an element's name or a CSS selector.` };
|
|
23899
|
+
const before = moments[moments.length - 1].png;
|
|
23900
|
+
try {
|
|
23901
|
+
await element.click({ timeout: 3e3 });
|
|
23902
|
+
} catch (error) {
|
|
23903
|
+
return { problem: `${target} could not be clicked — hidden, covered or disabled: ${error instanceof Error ? error.message.split("\n")[0] : String(error)}` };
|
|
23904
|
+
}
|
|
23905
|
+
for (let frame = 1; frame <= Math.max(1, frames); frame += 1) {
|
|
23906
|
+
await taken.page.waitForTimeout(every);
|
|
23907
|
+
moments.push({
|
|
23908
|
+
label: `${target} +${String(frame * every)} ms`,
|
|
23909
|
+
png: await capture(taken.page, how)
|
|
23910
|
+
});
|
|
23911
|
+
}
|
|
23912
|
+
const after = moments[moments.length - 1].png;
|
|
23913
|
+
clicked.push({
|
|
23914
|
+
target,
|
|
23915
|
+
changed: (await comparePictures(taken.page, dataUrl(before), dataUrl(after))).changed
|
|
23916
|
+
});
|
|
23917
|
+
}
|
|
23918
|
+
return {
|
|
23919
|
+
moments,
|
|
23920
|
+
clicked
|
|
23921
|
+
};
|
|
23922
|
+
};
|
|
23923
|
+
/** What a step is, as its label says it. */
|
|
23924
|
+
var stepText = (step) => {
|
|
23925
|
+
switch (step.kind) {
|
|
23926
|
+
case "click":
|
|
23927
|
+
case "wait-for": return `${step.kind} ${step.target}`;
|
|
23928
|
+
case "type": return `type ${step.target}`;
|
|
23929
|
+
case "press": return `press ${step.key}`;
|
|
23930
|
+
case "wait": return `wait ${String(step.ms)} ms`;
|
|
23931
|
+
case "shot": return step.label ?? "shot";
|
|
23932
|
+
case "frames": return `frames ${String(step.count)}`;
|
|
23933
|
+
}
|
|
23934
|
+
};
|
|
23935
|
+
/**
|
|
23936
|
+
* A `--steps` script played on the page: what each step does, and every picture the script asks for — each labelled with
|
|
23937
|
+
* its step and the time since the first, and measured against the one before it, so "nothing changed" is said.
|
|
23938
|
+
*/
|
|
23939
|
+
var playSteps = async (taken, how, steps) => {
|
|
23940
|
+
const { page } = taken;
|
|
23941
|
+
const started = Date.now();
|
|
23942
|
+
const moments = [{
|
|
23943
|
+
label: "before",
|
|
23944
|
+
png: taken.png
|
|
23945
|
+
}];
|
|
23946
|
+
const changes = [];
|
|
23947
|
+
const picture = async (label) => {
|
|
23948
|
+
const before = moments[moments.length - 1].png;
|
|
23949
|
+
const png = await capture(page, how);
|
|
23950
|
+
const at = `${label} · +${String(Date.now() - started)} ms`;
|
|
23951
|
+
moments.push({
|
|
23952
|
+
label: at,
|
|
23953
|
+
png
|
|
23954
|
+
});
|
|
23955
|
+
changes.push({
|
|
23956
|
+
label: at,
|
|
23957
|
+
changed: (await comparePictures(page, dataUrl(before), dataUrl(png))).changed
|
|
23958
|
+
});
|
|
23959
|
+
};
|
|
23960
|
+
for (const [index, step] of steps.entries()) try {
|
|
23961
|
+
if (step.kind === "click" || step.kind === "type") {
|
|
23962
|
+
const element = page.locator(selectorOf(step.target)).first();
|
|
23963
|
+
if (await element.count() === 0) return { problem: `Step ${String(index + 1)}: ${step.target} is not on the page — its name or a CSS selector.` };
|
|
23964
|
+
await element.click({ timeout: 3e3 });
|
|
23965
|
+
if (step.kind === "type") await page.keyboard.type(step.text);
|
|
23966
|
+
} else if (step.kind === "press") await page.keyboard.press(step.key);
|
|
23967
|
+
else if (step.kind === "wait") await page.waitForTimeout(step.ms);
|
|
23968
|
+
else if (step.kind === "wait-for") await page.waitForSelector(selectorOf(step.target), { timeout: 1e4 });
|
|
23969
|
+
else if (step.kind === "shot") await picture(step.label ?? `step ${String(index + 1)}`);
|
|
23970
|
+
else for (let frame = 1; frame <= step.count; frame += 1) {
|
|
23971
|
+
if (frame > 1) await page.waitForTimeout(step.every);
|
|
23972
|
+
await picture(`frame ${String(frame)}/${String(step.count)}`);
|
|
23973
|
+
}
|
|
23974
|
+
} catch (error) {
|
|
23975
|
+
return { problem: `Step ${String(index + 1)} (${stepText(step)}) did not happen: ${error instanceof Error ? error.message.split("\n")[0] : String(error)}` };
|
|
23976
|
+
}
|
|
23977
|
+
return {
|
|
23978
|
+
moments,
|
|
23979
|
+
changes
|
|
23980
|
+
};
|
|
23981
|
+
};
|
|
23513
23982
|
var shot = async (route, options) => {
|
|
23514
23983
|
const project = await projectHere("whose page to picture");
|
|
23515
23984
|
if (!project) return;
|
|
@@ -23518,11 +23987,55 @@ var shot = async (route, options) => {
|
|
|
23518
23987
|
fail(where.problem);
|
|
23519
23988
|
return;
|
|
23520
23989
|
}
|
|
23990
|
+
const account = options.as === void 0 ? void 0 : await checkAccount(project.root, options.as);
|
|
23991
|
+
if (account && "problem" in account) {
|
|
23992
|
+
fail(`--as ${account.problem}.`);
|
|
23993
|
+
return;
|
|
23994
|
+
}
|
|
23521
23995
|
const browser = await launchBrowser(project.root);
|
|
23522
23996
|
if ("problem" in browser) {
|
|
23523
23997
|
fail(browser.problem);
|
|
23524
23998
|
return;
|
|
23525
23999
|
}
|
|
24000
|
+
const clicks = options.click ?? [];
|
|
24001
|
+
const script = options.steps?.startsWith("@") ? await readFile(path.resolve(options.steps.slice(1)), "utf-8").catch(() => void 0) : options.steps;
|
|
24002
|
+
if (options.steps !== void 0 && script === void 0) {
|
|
24003
|
+
await browser.close();
|
|
24004
|
+
fail(`${options.steps.slice(1)} cannot be read: --steps @<file> names a file of steps.`);
|
|
24005
|
+
return;
|
|
24006
|
+
}
|
|
24007
|
+
const steps = script === void 0 ? void 0 : parseSteps(script, options.every ?? 500);
|
|
24008
|
+
if (steps && "problem" in steps) {
|
|
24009
|
+
await browser.close();
|
|
24010
|
+
fail(`--steps: ${steps.problem}.`);
|
|
24011
|
+
return;
|
|
24012
|
+
}
|
|
24013
|
+
if (steps && (clicks.length > 0 || options.compare || (options.frames ?? 0) > 1)) {
|
|
24014
|
+
await browser.close();
|
|
24015
|
+
fail("`--steps` is the whole interaction — its own clicks and frames: leave out --click, --frames and --compare.");
|
|
24016
|
+
return;
|
|
24017
|
+
}
|
|
24018
|
+
if (options.compare && clicks.length > 0) {
|
|
24019
|
+
await browser.close();
|
|
24020
|
+
fail("`--compare` sets the page beside another site as it loads: leave out --click, or picture the click on its own.");
|
|
24021
|
+
return;
|
|
24022
|
+
}
|
|
24023
|
+
if (options.sheet && clicks.length === 0 && (options.frames ?? 0) <= 1 && options.steps === void 0) {
|
|
24024
|
+
await browser.close();
|
|
24025
|
+
fail("`--sheet` lays out the pictures a --click, --frames or --steps takes: with none of them, there is one picture and no sheet.");
|
|
24026
|
+
return;
|
|
24027
|
+
}
|
|
24028
|
+
const fromLoad = options.from === "load";
|
|
24029
|
+
if (fromLoad && ((options.frames ?? 0) <= 1 || clicks.length > 0 || options.compare)) {
|
|
24030
|
+
await browser.close();
|
|
24031
|
+
fail("`--from load` starts --frames as the HTML arrives, for what plays while the page loads: give it --frames, and leave out --click and --compare.");
|
|
24032
|
+
return;
|
|
24033
|
+
}
|
|
24034
|
+
if (fromLoad && !options.clip && !options.scrollTo && !options.viewport) {
|
|
24035
|
+
await browser.close();
|
|
24036
|
+
fail("`--from load` pictures the screen as the page arrives, never the whole page — unrolling it waits for it to settle: add --viewport or --clip.");
|
|
24037
|
+
return;
|
|
24038
|
+
}
|
|
23526
24039
|
if (options.compare && (options.clip || options.scrollTo || options.viewport)) {
|
|
23527
24040
|
await browser.close();
|
|
23528
24041
|
fail("`--compare` sets the whole page beside the other site, section by section: leave out --clip, --scroll-to and --viewport.");
|
|
@@ -23539,12 +24052,14 @@ var shot = async (route, options) => {
|
|
|
23539
24052
|
...options.clip ? { clip: options.clip } : {},
|
|
23540
24053
|
...options.scrollTo ? { scrollTo: options.scrollTo } : {},
|
|
23541
24054
|
...options.viewport ? { viewport: true } : {},
|
|
23542
|
-
|
|
24055
|
+
...account ? { account } : {},
|
|
24056
|
+
...fromLoad ? { fromLoad } : {},
|
|
24057
|
+
still: !options.clip && !options.scrollTo && !options.viewport && (options.frames ?? 0) <= 1 && clicks.length === 0
|
|
23543
24058
|
};
|
|
23544
24059
|
const how = framing(view);
|
|
23545
24060
|
const taken = await pictureOf(browser, `${where.origin}${pathname}`, view, where.origin);
|
|
23546
24061
|
if ("problem" in taken) {
|
|
23547
|
-
fail(
|
|
24062
|
+
fail(taken.problem);
|
|
23548
24063
|
return;
|
|
23549
24064
|
}
|
|
23550
24065
|
const name = pathname === "/" ? "home" : pathname.replace(/[^a-z0-9]+/gi, "-").replace(/^-|-$/g, "");
|
|
@@ -23559,7 +24074,8 @@ var shot = async (route, options) => {
|
|
|
23559
24074
|
scheme: taken.scheme,
|
|
23560
24075
|
out,
|
|
23561
24076
|
...taken.settled > 0 ? { settled: taken.settled } : {},
|
|
23562
|
-
...taken.unloaded > 0 ? { unloaded: taken.unloaded } : {}
|
|
24077
|
+
...taken.unloaded > 0 ? { unloaded: taken.unloaded } : {},
|
|
24078
|
+
...taken.sentTo ? { sentTo: taken.sentTo } : {}
|
|
23563
24079
|
};
|
|
23564
24080
|
if (options.compare) {
|
|
23565
24081
|
const other = new URL(options.compare);
|
|
@@ -23571,13 +24087,37 @@ var shot = async (route, options) => {
|
|
|
23571
24087
|
report.compare = compared;
|
|
23572
24088
|
}
|
|
23573
24089
|
const frames = options.frames ?? 0;
|
|
23574
|
-
|
|
23575
|
-
|
|
24090
|
+
const every = options.every ?? 500;
|
|
24091
|
+
let moments = [];
|
|
24092
|
+
if (steps) {
|
|
24093
|
+
const played = await playSteps(taken, how, steps);
|
|
24094
|
+
if ("problem" in played) {
|
|
24095
|
+
fail(played.problem);
|
|
24096
|
+
return;
|
|
24097
|
+
}
|
|
24098
|
+
moments = played.moments;
|
|
24099
|
+
report.steps = played.changes;
|
|
24100
|
+
} else if (clicks.length > 0) {
|
|
24101
|
+
const played = await clickThrough(taken, how, clicks, {
|
|
24102
|
+
frames,
|
|
24103
|
+
every
|
|
24104
|
+
});
|
|
24105
|
+
if ("problem" in played) {
|
|
24106
|
+
fail(played.problem);
|
|
24107
|
+
return;
|
|
24108
|
+
}
|
|
24109
|
+
moments = played.moments;
|
|
24110
|
+
report.clicked = played.clicked;
|
|
24111
|
+
} else if (frames > 1) {
|
|
23576
24112
|
const pictures = [taken.png];
|
|
23577
24113
|
for (let frame = 1; frame < frames; frame += 1) {
|
|
23578
24114
|
await taken.page.waitForTimeout(every);
|
|
23579
24115
|
pictures.push(await capture(taken.page, how));
|
|
23580
24116
|
}
|
|
24117
|
+
moments = pictures.map((png, frame) => ({
|
|
24118
|
+
label: frame === 0 ? fromLoad ? "HTML in" : "before" : `+${String(frame * every)} ms`,
|
|
24119
|
+
png
|
|
24120
|
+
}));
|
|
23581
24121
|
const diffs = [];
|
|
23582
24122
|
for (let frame = 1; frame < pictures.length; frame += 1) diffs.push(await comparePictures(taken.page, dataUrl(pictures[frame - 1]), dataUrl(pictures[frame]), { regions }));
|
|
23583
24123
|
const whole = how.kind === "element" ? options.clip ?? "element" : "viewport";
|
|
@@ -23592,14 +24132,37 @@ var shot = async (route, options) => {
|
|
|
23592
24132
|
still: moved.filter((region) => region.changed <= MOVING_PERCENT).map((region) => region.name)
|
|
23593
24133
|
};
|
|
23594
24134
|
}
|
|
24135
|
+
if (moments.length > 1 && options.sheet) {
|
|
24136
|
+
const sheet = await taken.page.evaluate(contactSheet, moments.map((moment) => ({
|
|
24137
|
+
label: moment.label,
|
|
24138
|
+
src: dataUrl(moment.png)
|
|
24139
|
+
})));
|
|
24140
|
+
report.sheet = `${base}-sheet.png`;
|
|
24141
|
+
await writeFile(report.sheet, Buffer.from(sheet.replace(/^data:image\/png;base64,/, ""), "base64"));
|
|
24142
|
+
} else if (moments.length > 1 && (clicks.length > 0 || steps)) {
|
|
24143
|
+
report.pictures = [];
|
|
24144
|
+
for (const [index, moment] of moments.slice(1).entries()) {
|
|
24145
|
+
const file = `${base}-${String(index + 1)}.png`;
|
|
24146
|
+
await writeFile(file, moment.png);
|
|
24147
|
+
report.pictures.push({
|
|
24148
|
+
label: moment.label,
|
|
24149
|
+
out: file
|
|
24150
|
+
});
|
|
24151
|
+
}
|
|
24152
|
+
}
|
|
23595
24153
|
if (options.json) {
|
|
23596
24154
|
console.log(JSON.stringify(report));
|
|
23597
24155
|
return;
|
|
23598
24156
|
}
|
|
23599
24157
|
console.log(out);
|
|
24158
|
+
if (report.sentTo) console.log(chalk.yellow(account ? `${pathname} sent the browser to ${report.sentTo}, signed in as ${account.username}: the picture is of ${report.sentTo} — that account may not see ${pathname}` : `${pathname} sent the browser to ${report.sentTo}: the picture is of ${report.sentTo}. A page for signed-in visitors? \`--as <username>\` signs in first, the password in ${CHECK_PASSWORD_ENV} in .env`));
|
|
23600
24159
|
if (report.settled) console.log(chalk.dim(`${String(report.settled)} arrival${report.settled === 1 ? "" : "s"} waiting for the scroll shown as they end — --scroll-to <id> --viewport --frames 4 shows one arriving`));
|
|
23601
24160
|
if (report.unloaded) console.log(chalk.yellow(`${String(report.unloaded)} picture(s) had not loaded when the page was taken`));
|
|
23602
24161
|
if (report.compare) console.log(compareText(report.compare).join("\n"));
|
|
24162
|
+
for (const { label, changed } of report.steps ?? []) console.log(changed > 0 ? `${label}: ${String(changed)}% of the picture changed` : chalk.yellow(`${label}: nothing changed since the picture before`));
|
|
24163
|
+
for (const { target, changed } of report.clicked ?? []) console.log(changed > 0 ? `clicked ${target}: ${String(changed)}% of the picture changed` : chalk.yellow(`clicked ${target}: nothing changed — the click did nothing to see`));
|
|
24164
|
+
if (report.sheet) console.log(`sheet: ${report.sheet}`);
|
|
24165
|
+
for (const picture of report.pictures ?? []) console.log(`${picture.label}: ${picture.out}`);
|
|
23603
24166
|
if (report.frames) console.log(report.frames.moving.length > 0 ? `moving over ${String(report.frames.count)} frames: ${regionsText(report.frames.moving)}` : `nothing moved over ${String(report.frames.count)} frames, ${String(report.frames.every)} ms apart`);
|
|
23604
24167
|
} finally {
|
|
23605
24168
|
await browser.close();
|
|
@@ -23664,8 +24227,10 @@ var scriptsOf = async (root) => {
|
|
|
23664
24227
|
const scripts = e$3(parsed) && e$3(parsed.scripts) ? parsed.scripts : {};
|
|
23665
24228
|
return Object.fromEntries(Object.entries(scripts).filter((entry) => typeof entry[1] === "string"));
|
|
23666
24229
|
};
|
|
24230
|
+
/** A page the browser was sent away from — one for signed-in visitors, sent to sign in. */
|
|
24231
|
+
var redirected = (report) => report.issues.some((issue) => issue.code === "redirected");
|
|
23667
24232
|
/** A page the browser was sent away from, or that nothing answered: not looked at, so neither passing nor failing. */
|
|
23668
|
-
var unchecked = (report) => report.issues.some((issue) => issue.code === "
|
|
24233
|
+
var unchecked = (report) => redirected(report) || report.issues.some((issue) => issue.code === "no-answer");
|
|
23669
24234
|
var verify = async (options) => {
|
|
23670
24235
|
const project = await projectHere("to verify");
|
|
23671
24236
|
if (!project) return;
|
|
@@ -23703,29 +24268,62 @@ var verify = async (options) => {
|
|
|
23703
24268
|
} : pages));
|
|
23704
24269
|
if (failed) process.exitCode = 1;
|
|
23705
24270
|
};
|
|
24271
|
+
var NOT_STARTED = "start it (npm start) and verify again, or --no-pages to leave them";
|
|
23706
24272
|
var pagesChecked = async (project) => {
|
|
23707
|
-
const
|
|
23708
|
-
if ("problem" in
|
|
23709
|
-
const
|
|
23710
|
-
|
|
23711
|
-
|
|
23712
|
-
|
|
23713
|
-
|
|
23714
|
-
|
|
23715
|
-
|
|
23716
|
-
|
|
23717
|
-
|
|
23718
|
-
|
|
24273
|
+
const found = await staticPaths(project.root);
|
|
24274
|
+
if ("problem" in found) return found;
|
|
24275
|
+
const { paths, schemes } = found;
|
|
24276
|
+
const inEveryTheme = async (routes, account) => {
|
|
24277
|
+
const all = [];
|
|
24278
|
+
for (const scheme of schemes) {
|
|
24279
|
+
const reports = await checkRoutes(project, routes, {
|
|
24280
|
+
widths: WIDTHS,
|
|
24281
|
+
asked: { state: false },
|
|
24282
|
+
ssr: false,
|
|
24283
|
+
scheme,
|
|
24284
|
+
...account ? { account } : {}
|
|
24285
|
+
});
|
|
24286
|
+
if ("problem" in reports) return { problem: `${reports.problem} — ${NOT_STARTED}` };
|
|
24287
|
+
all.push(...reports);
|
|
24288
|
+
}
|
|
24289
|
+
return all;
|
|
23719
24290
|
};
|
|
24291
|
+
const reports = await inEveryTheme(paths);
|
|
24292
|
+
if ("problem" in reports) return reports;
|
|
24293
|
+
const sentAway = [...new Set(reports.filter(redirected).map((report) => report.path))];
|
|
24294
|
+
if (sentAway.length === 0) return summary(schemes, reports);
|
|
24295
|
+
const account = await checkAccount(project.root);
|
|
24296
|
+
if (!account || "problem" in account) return summary(schemes, reports, account ?? { none: true });
|
|
24297
|
+
const signedIn = await inEveryTheme(sentAway, account);
|
|
24298
|
+
if ("problem" in signedIn) return signedIn;
|
|
24299
|
+
return summary(schemes, [...reports.filter((report) => !sentAway.includes(report.path)), ...signedIn], {
|
|
24300
|
+
as: account.username,
|
|
24301
|
+
checks: signedIn.filter((report) => !unchecked(report)).length
|
|
24302
|
+
});
|
|
24303
|
+
};
|
|
24304
|
+
var summary = (schemes, reports, signedIn) => ({
|
|
24305
|
+
schemes,
|
|
24306
|
+
passing: reports.filter((report) => report.ok).length,
|
|
24307
|
+
failing: reports.filter((report) => !report.ok && !unchecked(report)),
|
|
24308
|
+
unchecked: [...new Set(reports.filter(unchecked).map((report) => report.path))],
|
|
24309
|
+
...signedIn ? { signedIn } : {}
|
|
24310
|
+
});
|
|
24311
|
+
/** Why the pages left are not checked, and what checks them. */
|
|
24312
|
+
var uncheckedText = (pages) => {
|
|
24313
|
+
const list = pages.unchecked.join(", ");
|
|
24314
|
+
const signedIn = pages.signedIn;
|
|
24315
|
+
if (!signedIn || "none" in signedIn) return `- not checked — sent elsewhere (a page for signed-in visitors) or not answered: ${list} — name an account of the project in .env, ${CHECK_USER_ENV} and ${CHECK_PASSWORD_ENV}, and verify signs in for them`;
|
|
24316
|
+
if ("problem" in signedIn) return `- not checked — sent elsewhere (a page for signed-in visitors) or not answered: ${list} — ${signedIn.problem}`;
|
|
24317
|
+
return `- not checked, even signed in as ${signedIn.as}: ${list} — that account may not see them, or nothing answered`;
|
|
23720
24318
|
};
|
|
23721
24319
|
var verifyText = (results, pages) => [...results.flatMap((result) => result.ok ? [chalk.green(`✓ ${result.name}`)] : [
|
|
23722
24320
|
chalk.red(`✗ ${result.name}`),
|
|
23723
24321
|
...(result.said ?? []).map((line) => ` ${line}`),
|
|
23724
24322
|
...result.fix ? [` → ${result.fix}`] : []
|
|
23725
24323
|
]), ...pages === void 0 ? [chalk.yellow("- pages not checked: --no-pages")] : "waiting" in pages ? [chalk.yellow(`- not run until ${pages.after} passes: ${pages.waiting.join(", ")} (--keep-going runs them anyway)`)] : "problem" in pages ? [chalk.red(`✗ pages — ${pages.problem}`)] : [
|
|
23726
|
-
pages.failing.length === 0 ? chalk.green(`✓ pages — ${String(pages.passing)} checks at ${WIDTHS.join(" and ")} px`) : chalk.red(`✗ pages — ${String(pages.failing.length)} of ${String(pages.passing + pages.failing.length)} checks`),
|
|
24324
|
+
pages.failing.length === 0 ? chalk.green(`✓ pages — ${String(pages.passing)} checks at ${WIDTHS.join(" and ")} px, ${pages.schemes.join(" and ")}${pages.signedIn && "as" in pages.signedIn ? `, ${String(pages.signedIn.checks)} of them signed in as ${pages.signedIn.as}` : ""}`) : chalk.red(`✗ pages — ${String(pages.failing.length)} of ${String(pages.passing + pages.failing.length)} checks`),
|
|
23727
24325
|
...pages.failing.map((report) => reportText$2(report, { state: false })),
|
|
23728
|
-
...pages.unchecked.length > 0 ? [chalk.yellow(
|
|
24326
|
+
...pages.unchecked.length > 0 ? [chalk.yellow(uncheckedText(pages))] : []
|
|
23729
24327
|
]].join("\n");
|
|
23730
24328
|
//#endregion
|
|
23731
24329
|
//#region src/doctor/projectModules.ts
|
|
@@ -24066,17 +24664,8 @@ var minimumSecret = async (root) => {
|
|
|
24066
24664
|
const minimum = e$3(actions) ? actions.MIN_SIGNING_SECRET_LENGTH : void 0;
|
|
24067
24665
|
return typeof minimum === "number" ? minimum : void 0;
|
|
24068
24666
|
};
|
|
24069
|
-
/** What the process starts with: what `.env` sets, under what the environment already has — as the server reads it. */
|
|
24070
|
-
var settingsOf = async (root) => {
|
|
24071
|
-
const text = await readOptional(path.join(root, ".env"));
|
|
24072
|
-
const parsed = text === void 0 ? {} : parseEnv(text);
|
|
24073
|
-
return {
|
|
24074
|
-
file: text !== void 0,
|
|
24075
|
-
value: (name) => process.env[name] ?? parsed[name] ?? ""
|
|
24076
|
-
};
|
|
24077
|
-
};
|
|
24078
24667
|
var envChecks = async ({ root, answers }) => {
|
|
24079
|
-
const settings = await
|
|
24668
|
+
const settings = await projectSettings(root);
|
|
24080
24669
|
const where = settings.file ? ".env" : "the environment (there is no .env)";
|
|
24081
24670
|
const findings = [];
|
|
24082
24671
|
if (answers.mode === "client") {
|
|
@@ -24682,6 +25271,13 @@ var NODE_SCRIPTS = [
|
|
|
24682
25271
|
"start:dev",
|
|
24683
25272
|
"author"
|
|
24684
25273
|
];
|
|
25274
|
+
/** The variables a script sets before its command — `NODE_ENV=production node …` — by name. */
|
|
25275
|
+
var assignmentsOf = (command) => Object.fromEntries([...(/^((?:\w+=\S*\s+)*)/.exec(command)?.[1] ?? "").matchAll(/(\w+)=(\S*)/g)].map(([, name, value]) => [name, value]));
|
|
25276
|
+
/** A script's words when its command runs Node — after any variable it sets first — and none when it runs anything else. */
|
|
25277
|
+
var nodeWords = (command) => {
|
|
25278
|
+
const words = (command ?? "").replace(/^(?:\w+=\S*\s+)*/, "").split(/\s+/);
|
|
25279
|
+
return words[0] === "node" ? words : [];
|
|
25280
|
+
};
|
|
24685
25281
|
/**
|
|
24686
25282
|
* What a script that runs Node needs on disk: the file it starts, and every folder `--watch-path` names — Node stops at
|
|
24687
25283
|
* start when one of them is not there, which is why each watched folder keeps a `.gitkeep`.
|
|
@@ -24691,7 +25287,7 @@ var scriptTargetChecks = async ({ root, manifest }) => {
|
|
|
24691
25287
|
const findings = [];
|
|
24692
25288
|
for (const name of NODE_SCRIPTS) {
|
|
24693
25289
|
const command = scripts[name];
|
|
24694
|
-
const words =
|
|
25290
|
+
const words = nodeWords(command).slice(1);
|
|
24695
25291
|
const watched = words.flatMap((word) => /^--watch-path=(.+)$/.exec(word)?.[1] ?? []);
|
|
24696
25292
|
const started = words.filter((word) => !word.startsWith("-")).at(-1);
|
|
24697
25293
|
for (const folder of watched) if (!await fs.stat(path.join(root, folder)).then(() => true, () => false)) findings.push(say$3.error("watch-path-missing", `The script ${name} watches ${folder}, which is not there: Node stops before it starts.`, {
|
|
@@ -24727,8 +25323,12 @@ var scriptEnvChecks = ({ answers, manifest }) => {
|
|
|
24727
25323
|
if (answers.mode !== "server") return [];
|
|
24728
25324
|
const scripts = stringsOf(manifest.scripts);
|
|
24729
25325
|
return SERVER_SCRIPTS.flatMap((name) => {
|
|
24730
|
-
const words =
|
|
25326
|
+
const words = nodeWords(scripts[name]);
|
|
24731
25327
|
if (words.length === 0) return [];
|
|
25328
|
+
if (name === "start:prod" && assignmentsOf(scripts[name]).NODE_ENV !== "production") return [say$3.warning("start-prod-not-production", "The script start:prod does not set NODE_ENV=production: wherever it runs without it, the server is a development one — its dev tools on, and the trace of every action in what a visitor is answered.", {
|
|
25329
|
+
file: "package.json",
|
|
25330
|
+
fix: `NODE_ENV=production before node in start:prod — ${UPGRADE_PACKAGES} rewrites a script the CLI wrote.`
|
|
25331
|
+
})];
|
|
24732
25332
|
const flagged = words.some((word) => ENV_FLAG.test(word));
|
|
24733
25333
|
const preloaded = words.some((word, index) => word === `--import=${ENV_PRELOAD}` || word === "--import" && words[index + 1] === ENV_PRELOAD);
|
|
24734
25334
|
const watched = words.some((word) => word.startsWith("--watch"));
|
|
@@ -25452,7 +26052,9 @@ var previousLines = (previous) => previous.length ? [
|
|
|
25452
26052
|
...previous.map((url) => ` - ${url}`),
|
|
25453
26053
|
"Read each (a claude.ai artifact: open it with your artifact tool, never by fetching the URL). Number the new",
|
|
25454
26054
|
"findings after the highest id there, and fill `changed` with every earlier finding this project touched:",
|
|
25455
|
-
"fixed, partly or still open — and how you saw it, on the versions above."
|
|
26055
|
+
"fixed, partly or still open — and how you saw it, on the versions above.",
|
|
26056
|
+
"A link can stop opening — an artifact deleted, or no longer shared. Say which one did not, ask the person for",
|
|
26057
|
+
"the highest id it had, and leave `changed` to the reports you could read."
|
|
25456
26058
|
] : ["No earlier report: number the findings from PZ-1."];
|
|
25457
26059
|
var feedbackBrief = (facts, file, previous) => [
|
|
25458
26060
|
"A report to Plitzi, for the developer to send as a link.",
|
|
@@ -26749,7 +27351,7 @@ var reportText = (report) => {
|
|
|
26749
27351
|
}
|
|
26750
27352
|
for (const [title, findings] of groups) lines.push(...fileText(title, findings));
|
|
26751
27353
|
if (report.findings.length > 0) lines.push(chalk.dim(`What each means and what to write instead: the docs of each code — ${docsOf(report)}.`));
|
|
26752
|
-
const suggested = [...new Set(report.findings.filter((each) => each.origin === "authoring").map((each) => each.code))];
|
|
27354
|
+
const suggested = [...new Set(report.findings.filter((each) => each.origin === "authoring" && each.file !== void 0).map((each) => each.code))];
|
|
26753
27355
|
if (suggested.length > 0) lines.push(chalk.dim(`Authoring's suggestions (${suggested.join(", ")}) are quieted on the element they are about — \`quiet: ['${suggested[0]}']\` — not by a \`plitzi-lint-disable\` comment, which silences only lint's own codes.`));
|
|
26754
27356
|
const summary = [
|
|
26755
27357
|
plural(counts.error, "error"),
|
|
@@ -26901,14 +27503,14 @@ program.command("login").description("Sign in, in your browser. The session is k
|
|
|
26901
27503
|
program.command("logout").description("Sign out: the session is revoked on the platform and forgotten here").action(() => logout());
|
|
26902
27504
|
program.command("whoami").description("Who the CLI is signed in as, and the space it works in").option("--json", "One object: { api, user, space }, for a tool or an agent").option(...API_OPTION).action((options) => whoami(options));
|
|
26903
27505
|
spaceGroup.command("use").description("Choose the space to work in, in your browser. One at a time: it replaces the one chosen before").option(...API_OPTION).action((options) => space$2(options));
|
|
26904
|
-
pageGroup.command("check").argument("[
|
|
27506
|
+
pageGroup.command("check").argument("[paths...]", "The pages: /about /writers, in one browser. The home page when left out").description("Whether a page of the running project is whole, in text: every element on screen, no broken image, no console error, no failed flow").addOption(widthsOption("The widths to check it at, separated by commas", [1440, 390])).addOption(schemeOption()).option("--state", "Also what the page holds: its state, and every source by name").option("--element <id>", "Also one element: what it reads, its own state, whether it is on screen").option("--ssr", "Also the HTML the server sent against the hydrated page: what a server provider sent late").option("--click <id>", "Click one element once the page settles, and say what it changed — or that nothing did").addOption(new Option("--fill <id=value>", "Fill a field before the click, as a visitor types in it — once per field; a box takes true or false").argParser(fill)).option("--as <username>", "Sign in first as this account, through the server’s /auth routes — the password from PLITZI_CHECK_PASSWORD, in .env").option("--json", "One object per page and width, for a tool or an agent").action((paths, options) => check(paths, options));
|
|
26905
27507
|
spaceGroup.command("fix").description("What authoring would fix in the space, as edits to your source: shown as a diff; with --write written, formatted and checked").option("--write", "Write the edits — kept only when the space authors with each fix gone and no problem added").option("--json", "The plan as one object, for a tool or an agent").action((options) => fix(options));
|
|
26906
27508
|
pageGroup.command("import").argument("<url>", "The page to start from: https://example.com/pricing").description("A page of a site you verified, as a place to start writing from: its tokens, the outline of its blocks per breakpoint, its lists as JSON — never its words").option("-o, --out <dir>", "Where it is written, inside the project", "src/imported").addOption(widthsOption("The widths it is measured at: the widest is desktop, then tablet and mobile", [
|
|
26907
27509
|
1440,
|
|
26908
27510
|
768,
|
|
26909
27511
|
390
|
|
26910
27512
|
])).option("-f, --force", "Write over what --out already holds").option("--account", "A site not served from this machine: ask your Plitzi account whether one of your spaces verified its domain (signs in)").option("--json", "One object, for a tool or an agent").option(...API_OPTION).option(...DRY_RUN_OPTION).action((url, options) => importPage(url, options));
|
|
26911
|
-
pageGroup.command("shot").argument("[path]", "The page: /about. The home page when left out").description("A picture of one page of the running project — and, asked, how it differs from another or what moves").addOption(new Option("--width <px>", "The viewport width").argParser(width).default(1280)).addOption(new Option("--height <px>", "The viewport height (the picture is the whole page unless --viewport, --scroll-to or --clip)").argParser(positiveInteger).default(800)).addOption(schemeOption()).option("-o, --out <file>", "Where the picture goes. tmp/shots/<page>-<width>-<scheme>.png by default").option("--compare <url>", "Another site: the same page there, side by side, and how much differs by section").addOption(new Option("--frames <n>", "Pictures taken one after another: which sections move (a marquee, an autoplay)").argParser(positiveInteger)).addOption(new Option("--every <ms>", "How far apart --frames are taken").argParser(positiveInteger).default(500)).option("--wait-for <element>", "An element to wait for first: its name (data-plitzi-el) or a CSS selector").option("--clip <element>", "Only this element — its name or a CSS selector — scrolled to wherever it is").option("--scroll-to <element>", "The page scrolled until this element is in view; the picture is the screen there").option("--viewport", "Only what fits the viewport, not the whole page").option("--reduced-motion", "As a visitor who asked for less motion").option("--json", "One object, for a tool or an agent").action((path, options) => shot(path, options));
|
|
27513
|
+
pageGroup.command("shot").argument("[path]", "The page: /about. The home page when left out").description("A picture of one page of the running project — and, asked, how it differs from another or what moves").addOption(new Option("--width <px>", "The viewport width").argParser(width).default(1280)).addOption(new Option("--height <px>", "The viewport height (the picture is the whole page unless --viewport, --scroll-to or --clip)").argParser(positiveInteger).default(800)).addOption(schemeOption()).option("-o, --out <file>", "Where the picture goes. tmp/shots/<page>-<width>-<scheme>.png by default").option("--compare <url>", "Another site: the same page there, side by side, and how much differs by section").addOption(new Option("--frames <n>", "Pictures taken one after another: which sections move (a marquee, an autoplay)").argParser(positiveInteger)).addOption(new Option("--every <ms>", "How far apart --frames are taken").argParser(positiveInteger).default(500)).addOption(new Option("--from <moment>", "When --frames start: once the page settles, or as soon as its HTML is in (load) — what plays as it arrives").choices([...SHOT_MOMENTS]).default("settled")).option("--wait-for <element>", "An element to wait for first: its name (data-plitzi-el) or a CSS selector").option("--clip <element>", "Only this element — its name or a CSS selector — scrolled to wherever it is").option("--scroll-to <element>", "The page scrolled until this element is in view; the picture is the screen there").option("--viewport", "Only what fits the viewport, not the whole page").option("--click <element...>", "Click these in order once the page settles — each its name or a CSS selector, inside a plugin too — picturing what each changed (--frames pictures --every ms apart after each)").option("--sheet", "Every picture taken — before, after each click, each frame — on one contact sheet").option("--steps <script>", "An interaction, step by step: \"click <el>; type <el> <text>; press <key>; wait <ms>; wait-for <el>; shot [label]; frames <n> [ms]\" — or @file with one per line").option("--reduced-motion", "As a visitor who asked for less motion").option("--as <username>", "Sign in first as this account, through the server’s /auth routes — the password from PLITZI_CHECK_PASSWORD, in .env").option("--json", "One object, for a tool or an agent").action((path, options) => shot(path, options));
|
|
26912
27514
|
program.command("verify").description("Whether the project is left passing: author, lints, types, format and every page, only the problems said").option("--no-pages", "Leave the pages out — said as such").option("--keep-going", "Run every check even after one fails — by default the first failure ends the run").option("--json", "One object, for a tool or an agent").action((options) => verify(options));
|
|
26913
27515
|
elementGroup.command("where").argument("<query>", "An element’s id, a class it wears, or words it shows: hero-cta, navLink, \"Get started\"").description("Where the project’s code writes an element: the file, the line and the call itself").addOption(new Option("--by <reading>", "Read the query one way only, not the first of id, class and words that matches").choices(WHERE_READINGS)).option("--json", "One object, for a tool or an agent").action((query, options) => where(query, options));
|
|
26914
27516
|
elementGroup.command("edit").argument("<id>", "The element, by its id: hero-cta").description("Change an element’s attributes in the call that writes it, then check the space still authors").option("--set <key=value...>", "An attribute to write: content=\"Start free\", required=true").option("--remove <key...>", "An attribute to take out").option("--every", "The call writes other elements too, and the change is for every one").option("--json", "One object, for a tool or an agent").action((id, options) => edit(id, options));
|