pageship 0.2.0 → 0.4.0
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/dist/cli.mjs +31 -21
- package/dist/skills/pageship/SKILL.md +8 -8
- package/dist/skills/pageship-chart/SKILL.md +18 -2
- package/dist/skills/pageship-code-block/SKILL.md +1 -1
- package/dist/skills/pageship-decision-record/SKILL.md +5 -1
- package/dist/skills/pageship-diagram/SKILL.md +16 -14
- package/dist/skills/pageship-diagram/references/check-diagram.js +3 -2
- package/dist/skills/pageship-docs/SKILL.md +32 -8
- package/dist/skills/pageship-docs/references/axe.js +34 -0
- package/dist/skills/pageship-docs/references/check.md +35 -8
- package/dist/skills/pageship-docs/references/design.md +38 -18
- package/dist/skills/pageship-docs/references/html-validate-rules.mjs +326 -0
- package/dist/skills/pageship-docs/references/html-validate.json +13 -0
- package/dist/skills/pageship-docs/references/palette.mjs +791 -0
- package/dist/skills/pageship-docs/references/plain-default.md +1 -1
- package/dist/skills/pageship-explainer/SKILL.md +13 -5
- package/dist/skills/pageship-interaction/SKILL.md +7 -5
- package/dist/skills/pageship-interaction/references/pointer.md +3 -0
- package/dist/skills/pageship-interaction/references/step-through.md +10 -25
- package/dist/skills/pageship-key-figures/SKILL.md +1 -1
- package/dist/skills/pageship-media/SKILL.md +2 -2
- package/dist/skills/pageship-motion/SKILL.md +31 -28
- package/dist/skills/pageship-motion/references/figure-motion.js +252 -0
- package/dist/skills/pageship-motion/references/figure-motion.md +294 -0
- package/dist/skills/pageship-plan/SKILL.md +3 -3
- package/dist/skills/pageship-report/SKILL.md +5 -1
- package/dist/skills/pageship-review-report/SKILL.md +5 -1
- package/dist/skills/pageship-slide-deck/SKILL.md +5 -3
- package/dist/skills/pageship-slide-deck/references/engine.md +1 -1
- package/dist/skills/pageship-spec/SKILL.md +5 -1
- package/dist/skills/pageship-table/SKILL.md +5 -3
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -705,7 +705,7 @@ async function updateWorkspaceAddress(baseUrl, action, options = {}) {
|
|
|
705
705
|
credentials: options.token ? "omit" : "include",
|
|
706
706
|
headers: options.token ? { Authorization: `Bearer ${options.token}` } : void 0,
|
|
707
707
|
redirect: "error",
|
|
708
|
-
cache: "
|
|
708
|
+
cache: "default",
|
|
709
709
|
throwOnError: false,
|
|
710
710
|
signal: options.signal ? AbortSignal.any([timeout, options.signal]) : timeout
|
|
711
711
|
});
|
|
@@ -724,7 +724,7 @@ function isRevisionId(value) {
|
|
|
724
724
|
return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}(?![\s\S])/iu.test(value);
|
|
725
725
|
}
|
|
726
726
|
/** Same readable normalized Git remote grammar as the Effect contract. */
|
|
727
|
-
function
|
|
727
|
+
function isRepository(value) {
|
|
728
728
|
return typeof value === "string" && value.length <= 512 && /^[a-z0-9._-]+(?::[0-9]{1,5})?(?:\/(?!\.{1,2}(?:\/|$))[^\x00-\x20\x7f/\\?#]+)+(?![\s\S])/u.test(value);
|
|
729
729
|
}
|
|
730
730
|
function isCliVersion(value) {
|
|
@@ -742,6 +742,16 @@ function requestOptions(baseUrl, options) {
|
|
|
742
742
|
throwOnError: false
|
|
743
743
|
};
|
|
744
744
|
}
|
|
745
|
+
/**
|
|
746
|
+
* A change keeps the browser's default cache mode. Chromium skips its preflight cache for a
|
|
747
|
+
* `no-store` request, and no browser caches a change's response anyway.
|
|
748
|
+
*/
|
|
749
|
+
function changeOptions(baseUrl, options) {
|
|
750
|
+
return {
|
|
751
|
+
...requestOptions(baseUrl, options),
|
|
752
|
+
cache: "default"
|
|
753
|
+
};
|
|
754
|
+
}
|
|
745
755
|
function record(value) {
|
|
746
756
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
747
757
|
}
|
|
@@ -757,7 +767,7 @@ function normalizeDocument(value) {
|
|
|
757
767
|
return validDocument(normalized) ? normalized : void 0;
|
|
758
768
|
}
|
|
759
769
|
function validDocument(value) {
|
|
760
|
-
return record(value) && isDocumentId(value.id) && typeof value.slug === "string" && /^[a-z0-9]+(?:-[a-z0-9]+)*(?![\s\S])/u.test(value.slug) && validTitle(value.title) && typeof value.sourcePath === "string" && value.sourcePath.length <= 1024 && /^(?!\/)(?!.*(?:^|\/)\.{1,2}(?:\/|$))(?!.*\/\/)[^\x00-\x1f\x7f\\:]*\.[hH][tT][mM][lL]?(?![\s\S])/u.test(value.sourcePath) && (value.
|
|
770
|
+
return record(value) && isDocumentId(value.id) && typeof value.slug === "string" && /^[a-z0-9]+(?:-[a-z0-9]+)*(?![\s\S])/u.test(value.slug) && validTitle(value.title) && typeof value.sourcePath === "string" && value.sourcePath.length <= 1024 && /^(?!\/)(?!.*(?:^|\/)\.{1,2}(?:\/|$))(?!.*\/\/)[^\x00-\x1f\x7f\\:]*\.[hH][tT][mM][lL]?(?![\s\S])/u.test(value.sourcePath) && (value.repository === null || isRepository(value.repository)) && (value.visibility === "private" || value.visibility === "public") && isRevisionId(value.revisionId) && typeof value.byteLength === "number" && Number.isSafeInteger(value.byteLength) && value.byteLength > 0 && typeof value.createdAt === "number" && Number.isSafeInteger(value.createdAt) && value.createdAt >= 0 && typeof value.publishedAt === "number" && Number.isSafeInteger(value.publishedAt) && value.publishedAt >= 0 && typeof value.url === "string" && new RegExp(`^https://[a-z0-9-]+\\.${workspaceDomainPattern}/d/${value.id}-${value.slug}$`, "u").test(value.url);
|
|
761
771
|
}
|
|
762
772
|
function validRevision(value, documentId) {
|
|
763
773
|
return record(value) && isRevisionId(value.id) && typeof value.createdAt === "number" && Number.isSafeInteger(value.createdAt) && value.createdAt >= 0 && typeof value.byteLength === "number" && Number.isSafeInteger(value.byteLength) && value.byteLength > 0 && typeof value.isCurrent === "boolean" && (value.cliVersion === null || isCliVersion(value.cliVersion)) && typeof value.url === "string" && new RegExp(`^https://[a-z0-9-]+\\.${workspaceDomainPattern}/d/${documentId}-[a-z0-9]+(?:-[a-z0-9]+)*/revisions/${value.id}$`, "u").test(value.url);
|
|
@@ -861,7 +871,7 @@ async function getDocumentContent(baseUrl, id, options = {}) {
|
|
|
861
871
|
throw new Error("Invalid document content response.");
|
|
862
872
|
}
|
|
863
873
|
async function restoreDocumentRevision(baseUrl, id, input, options = {}) {
|
|
864
|
-
const request =
|
|
874
|
+
const request = changeOptions(baseUrl, options);
|
|
865
875
|
const { data: received, response, error } = await documentsRestore({
|
|
866
876
|
...request,
|
|
867
877
|
path: { id },
|
|
@@ -891,7 +901,7 @@ async function restoreDocumentRevision(baseUrl, id, input, options = {}) {
|
|
|
891
901
|
}
|
|
892
902
|
async function updateDocumentVisibility(baseUrl, id, visibility, options = {}) {
|
|
893
903
|
const { data: received, response, error } = await documentsUpdateVisibility({
|
|
894
|
-
...
|
|
904
|
+
...changeOptions(baseUrl, options),
|
|
895
905
|
path: { id },
|
|
896
906
|
body: { visibility }
|
|
897
907
|
});
|
|
@@ -913,7 +923,7 @@ async function publishDocument(baseUrl, input, options = {}) {
|
|
|
913
923
|
if (input.action) {
|
|
914
924
|
if (!(await getDocumentLimits(baseUrl, options)).publicationActions.includes(input.action)) throw new DocumentRequestError("publication_action_unsupported", "This server does not support explicit publication actions. Ask the operator to update the server. No HTML was uploaded.", 400);
|
|
915
925
|
}
|
|
916
|
-
const request =
|
|
926
|
+
const request = changeOptions(baseUrl, options);
|
|
917
927
|
const { data: received, response, error } = await documentsCreate({
|
|
918
928
|
...request,
|
|
919
929
|
headers: {
|
|
@@ -923,7 +933,7 @@ async function publishDocument(baseUrl, input, options = {}) {
|
|
|
923
933
|
body: new Blob([new Uint8Array(input.bytes)], { type: "text/html" }),
|
|
924
934
|
query: {
|
|
925
935
|
sourcePath: input.sourcePath,
|
|
926
|
-
|
|
936
|
+
repository: input.repository,
|
|
927
937
|
cliVersion: input.cliVersion,
|
|
928
938
|
action: input.action,
|
|
929
939
|
documentId: input.documentId,
|
|
@@ -962,7 +972,7 @@ async function getWorkspace(baseUrl, options = {}) {
|
|
|
962
972
|
});
|
|
963
973
|
if (!response?.ok) throw new Error("Could not load your workspace.", { cause: error });
|
|
964
974
|
if (data?.status === "signed_out" || data?.status === "uninvited") return { status: data.status };
|
|
965
|
-
if (data?.status === "admitted" && typeof data.user?.id === "string" && data.user.id.length > 0 && typeof data.user?.name === "string" && (data.user.login === void 0 || typeof data.user.login === "string") && typeof data.workspace?.id === "string" && data.workspace.id.length > 0 && (data.address === void 0 || data.address === null || isWorkspaceAddress(data.address))) return data;
|
|
975
|
+
if (data?.status === "admitted" && typeof data.user?.id === "string" && data.user.id.length > 0 && typeof data.user?.name === "string" && (data.user.login === void 0 || typeof data.user.login === "string") && (data.user.githubAccountId === void 0 || typeof data.user.githubAccountId === "string" && /^[1-9]\d*$/u.test(data.user.githubAccountId)) && typeof data.workspace?.id === "string" && data.workspace.id.length > 0 && (data.address === void 0 || data.address === null || isWorkspaceAddress(data.address))) return data;
|
|
966
976
|
throw new Error("Invalid workspace response.");
|
|
967
977
|
}
|
|
968
978
|
function validateCompatibility(value) {
|
|
@@ -1323,7 +1333,7 @@ const outcomes = {
|
|
|
1323
1333
|
choice_required: "pending",
|
|
1324
1334
|
address_required: "pending",
|
|
1325
1335
|
source_collision: "pending",
|
|
1326
|
-
|
|
1336
|
+
repository_mismatch: "pending",
|
|
1327
1337
|
marker_unavailable: "pending",
|
|
1328
1338
|
revision_conflict: "pending",
|
|
1329
1339
|
cancelled: "pending",
|
|
@@ -1412,7 +1422,7 @@ function commandLine(command, args) {
|
|
|
1412
1422
|
}
|
|
1413
1423
|
//#endregion
|
|
1414
1424
|
//#region package.json
|
|
1415
|
-
var version = "0.
|
|
1425
|
+
var version = "0.4.0";
|
|
1416
1426
|
//#endregion
|
|
1417
1427
|
//#region src/publish.ts
|
|
1418
1428
|
const execute = promisify(execFile);
|
|
@@ -1424,7 +1434,7 @@ async function sourceMetadata(file) {
|
|
|
1424
1434
|
const path = await realpath(resolve(file));
|
|
1425
1435
|
const cwd = await realpath(process.cwd());
|
|
1426
1436
|
let root = contained(cwd, path) ? cwd : dirname(path);
|
|
1427
|
-
let
|
|
1437
|
+
let repository;
|
|
1428
1438
|
try {
|
|
1429
1439
|
const git = async (...args) => (await execute("git", [
|
|
1430
1440
|
"-C",
|
|
@@ -1445,8 +1455,8 @@ async function sourceMetadata(file) {
|
|
|
1445
1455
|
"ssh:",
|
|
1446
1456
|
"git:"
|
|
1447
1457
|
].includes(url.protocol) && url.hostname) {
|
|
1448
|
-
const
|
|
1449
|
-
if (
|
|
1458
|
+
const normalized = `${url.hostname.toLowerCase()}${url.port ? `:${url.port}` : ""}/${url.pathname.replace(/^\/+|\/+$/gu, "").replace(/\.git$/u, "")}`;
|
|
1459
|
+
if (isRepository(normalized)) repository = normalized;
|
|
1450
1460
|
}
|
|
1451
1461
|
}
|
|
1452
1462
|
} catch {}
|
|
@@ -1455,7 +1465,7 @@ async function sourceMetadata(file) {
|
|
|
1455
1465
|
return {
|
|
1456
1466
|
path,
|
|
1457
1467
|
sourcePath,
|
|
1458
|
-
|
|
1468
|
+
repository
|
|
1459
1469
|
};
|
|
1460
1470
|
}
|
|
1461
1471
|
async function fileBytes(path, limit) {
|
|
@@ -1653,7 +1663,7 @@ async function publish(options) {
|
|
|
1653
1663
|
expectedRevisionId: entry.revisionId
|
|
1654
1664
|
};
|
|
1655
1665
|
automatic = "manifest";
|
|
1656
|
-
} else if (target.
|
|
1666
|
+
} else if (target.repository === (source.repository ?? null)) {
|
|
1657
1667
|
action = {
|
|
1658
1668
|
action: "revise",
|
|
1659
1669
|
documentId: target.id,
|
|
@@ -1661,9 +1671,9 @@ async function publish(options) {
|
|
|
1661
1671
|
};
|
|
1662
1672
|
automatic = "marker";
|
|
1663
1673
|
} else return {
|
|
1664
|
-
status: "
|
|
1674
|
+
status: "repository_mismatch",
|
|
1665
1675
|
document: target,
|
|
1666
|
-
message: `This file's marker names ${target.url}, a document from ${target.
|
|
1676
|
+
message: `This file's marker names ${target.url}, a document from ${target.repository ?? "a loose file"}, but this file ${source.repository ? `belongs to ${source.repository}` : "is a loose file"}. No document changed. To revise that document anyway, publish with --revise ${target.id} --expected-revision ${target.revisionId}, or use --new for a separate document. Use --json for complete follow-up arguments.`,
|
|
1667
1677
|
resolutions: resolutions(options.file, contextArgs, [target])
|
|
1668
1678
|
};
|
|
1669
1679
|
} else if (detect) {
|
|
@@ -1691,7 +1701,7 @@ async function publish(options) {
|
|
|
1691
1701
|
const result = await publishDocument(server, {
|
|
1692
1702
|
bytes: uploaded,
|
|
1693
1703
|
sourcePath: source.sourcePath,
|
|
1694
|
-
...source.
|
|
1704
|
+
...source.repository ? { repository: source.repository } : {},
|
|
1695
1705
|
cliVersion: version,
|
|
1696
1706
|
idempotencyKey,
|
|
1697
1707
|
...proposal
|
|
@@ -2191,7 +2201,7 @@ function listDocuments(options) {
|
|
|
2191
2201
|
...next ? { next } : {},
|
|
2192
2202
|
message: [
|
|
2193
2203
|
page.documents.length ? "Documents:" : options.cursor ? "No more documents." : "No documents.",
|
|
2194
|
-
...page.documents.map((document) => `${document.id} ${document.visibility} ${document.title}\n` + (document.
|
|
2204
|
+
...page.documents.map((document) => `${document.id} ${document.visibility} ${document.title}\n` + (document.repository ? ` repository ${document.repository}\n` : "") + ` source ${document.sourcePath}\n current revision ${document.revisionId}\n ${document.url}`),
|
|
2195
2205
|
...next ? [`More documents: run ${commandLine(next.command, next.args)}`] : []
|
|
2196
2206
|
].join("\n")
|
|
2197
2207
|
};
|
|
@@ -2346,7 +2356,7 @@ const isSkillName = (name) => /^pageship(?:-[a-z0-9-]+)?$/u.test(name);
|
|
|
2346
2356
|
const recommendedSkills = ["frontend-design", "design-taste-frontend"];
|
|
2347
2357
|
/** The oldest Node major that runs the authoring skills' scripts. */
|
|
2348
2358
|
const authoringNodeMajor = 20;
|
|
2349
|
-
const setupGuide = "https://pageship.dev/setup-guide";
|
|
2359
|
+
const setupGuide = "https://pageship.dev/setup-guide.md";
|
|
2350
2360
|
const installNext = {
|
|
2351
2361
|
command: "skills",
|
|
2352
2362
|
args: ["install"]
|
|
@@ -2457,7 +2467,7 @@ async function findMissing(environment) {
|
|
|
2457
2467
|
function notFound(missing) {
|
|
2458
2468
|
if (missing.length === 0) return void 0;
|
|
2459
2469
|
const named = missing.map((entry) => entry === "node" ? `Node ${authoringNodeMajor} or later` : entry);
|
|
2460
|
-
return `${named.length > 1 ? `${named.slice(0, -1).join(", ")} and ${named.at(-1)}` : named[0]} ${named.length > 1 ? "were" : "was"} not found. The setup guide
|
|
2470
|
+
return `${named.length > 1 ? `${named.slice(0, -1).join(", ")} and ${named.at(-1)}` : named[0]} ${named.length > 1 ? "were" : "was"} not found. The setup guide covers them: download ${setupGuide} and read all of it.`;
|
|
2461
2471
|
}
|
|
2462
2472
|
/**
|
|
2463
2473
|
* Whether the Pageship skills in ~/.agents/skills are missing, current, or older than this
|
|
@@ -3,7 +3,7 @@ name: pageship
|
|
|
3
3
|
description: Publish an HTML file to Pageship and manage what is published. Use when the user asks to publish, share or get a link to a document, update its published copy, change its visibility, list, inspect history, download or restore published documents, or sign in to Pageship. To write, edit, rename or move a document, load pageship-docs instead.
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.4.0"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Pageship
|
|
@@ -18,7 +18,7 @@ A document marker in a file (`<!-- pageship:v1 document=<id> -->`) records that
|
|
|
18
18
|
|
|
19
19
|
## Write or change a document
|
|
20
20
|
|
|
21
|
-
When the user asks to write, change, rename or move a document, load `pageship-docs` and follow it, even when the same request asks you to publish. When it hands over the file, publish it if the user asked. If `pageship-docs` isn't installed, say so and offer the setup guide
|
|
21
|
+
When the user asks to write, change, rename or move a document, load `pageship-docs` and follow it, even when the same request asks you to publish. When it hands over the file, publish it if the user asked. If `pageship-docs` isn't installed, say so and offer the setup guide, which installs it. Get it as [Install the CLI](#install-the-cli) describes. If the user declines, write the document without the authoring skills, then publish it if the user asked.
|
|
22
22
|
|
|
23
23
|
## Reading a result
|
|
24
24
|
|
|
@@ -38,7 +38,7 @@ A follow-up has a `command` and an `args` array. Run it as `pageship --json <com
|
|
|
38
38
|
|
|
39
39
|
## Install the CLI
|
|
40
40
|
|
|
41
|
-
If the shell can't find `pageship`, tell the user the Pageship CLI isn't installed and offer to install it. Only with their consent, follow the setup guide
|
|
41
|
+
If the shell can't find `pageship`, tell the user the Pageship CLI isn't installed and offer to install it. Only with their consent, follow the setup guide. Download https://pageship.dev/setup-guide.md with curl or a similar command and read all of it, since a fetch tool may hand you only a summary. The guide asks before it changes their environment.
|
|
42
42
|
|
|
43
43
|
## Check readiness
|
|
44
44
|
|
|
@@ -62,7 +62,7 @@ pageship --json skills install
|
|
|
62
62
|
|
|
63
63
|
On `skills_installed`, they are in `~/.agents/skills`, and linked from `~/.claude/skills` when Claude Code is set up. On `skills_modified`, nothing was written: the folders in `folders` were changed after the CLI installed them, or the CLI has no record of them. Show the user those folders. Run `resolutions.force` only if they agree to replace them and lose those changes.
|
|
64
64
|
|
|
65
|
-
Both results, and `status`, list in `missing` what the CLI looked for and did not find: the recommended skills `frontend-design` and `design-taste-frontend`, and `node`, Node 20 or later, which the authoring skills' scripts need. The CLI never installs them, and they never change the outcome. Mention them, and offer the setup guide
|
|
65
|
+
Both results, and `status`, list in `missing` what the CLI looked for and did not find: the recommended skills `frontend-design` and `design-taste-frontend`, and `node`, Node 20 or later, which the authoring skills' scripts need. The CLI never installs them, and they never change the outcome. Mention them, and offer the setup guide. Get it as [Install the CLI](#install-the-cli) describes.
|
|
66
66
|
|
|
67
67
|
## Sign in
|
|
68
68
|
|
|
@@ -88,9 +88,9 @@ Set the shell placeholder `PAGESHIP_FILE` to one nonempty `.html` or `.htm` file
|
|
|
88
88
|
pageship --json publish "$PAGESHIP_FILE"
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
The CLI records each published file's real path in its publish manifest on this machine, so running the same command again for that file revises its document without a choice, and a fresh session needs no remembered document ID. Inside a Git checkout with an origin remote, the file belongs to a
|
|
91
|
+
The CLI records each published file's real path in its publish manifest on this machine, so running the same command again for that file revises its document without a choice, and a fresh session needs no remembered document ID. Inside a Git checkout with an origin remote, the file belongs to a repository. A file in a repository without a manifest entry, such as one in another checkout, is looked up by its repository and repository-relative path on the server. `document.repository` shows the readable remote, and it is `null` for a loose file outside any repository.
|
|
92
92
|
|
|
93
|
-
After a successful upload the CLI adds the document marker to the end of the local file, so the file and the published revision match. The marker lets the file find its document after a rename or a move, and on another machine in a checkout of the same
|
|
93
|
+
After a successful upload the CLI adds the document marker to the end of the local file, so the file and the published revision match. The marker lets the file find its document after a rename or a move, and on another machine in a checkout of the same repository. Keep the marker when you edit or regenerate the file, and do not write or change it yourself. To publish a file exactly as it is, add `--no-marker`.
|
|
94
94
|
|
|
95
95
|
### Report the publish
|
|
96
96
|
|
|
@@ -104,7 +104,7 @@ Keep `idempotencyKey` only while you resolve an uncertain attempt.
|
|
|
104
104
|
|
|
105
105
|
### When the publish stops for a decision
|
|
106
106
|
|
|
107
|
-
On `
|
|
107
|
+
On `repository_mismatch`, the file's marker names a document from another repository, for example a fork. Nothing changed. Show the user the returned `document` and ask whether to revise it with the `resolutions.revise` entry or create a separate document with `resolutions.new`.
|
|
108
108
|
|
|
109
109
|
On `marker_unavailable`, the marker names a document that was not found or is not the user's, and nothing was uploaded. Ask before running the returned `next`, which creates a separate document.
|
|
110
110
|
|
|
@@ -120,7 +120,7 @@ An unmarked loose file is not matched by path, so on another machine, or from an
|
|
|
120
120
|
pageship --json publish "$PAGESHIP_FILE" --revise "$PAGESHIP_DOCUMENT" --expected-revision "$PAGESHIP_REVISION"
|
|
121
121
|
```
|
|
122
122
|
|
|
123
|
-
This works for any document the user owns, even when the file moved or belongs to another
|
|
123
|
+
This works for any document the user owns, even when the file moved or belongs to another repository. Another account's document returns `unavailable`. Later plain publishes of this file revise the named document, and its marker names it. After `--new`, they revise the new one.
|
|
124
124
|
|
|
125
125
|
## Share a document
|
|
126
126
|
|
|
@@ -3,12 +3,12 @@ name: pageship-chart
|
|
|
3
3
|
description: Later step of pageship-docs: for a request to write or edit a document, load pageship-docs first; load this skill first only when the user names it. Pattern for charts in a Pageship HTML document, loaded by a pageship- kind skill before it draws a chart.
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.4.0"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Chart
|
|
10
10
|
|
|
11
|
-
A chart answers one question about the source's numbers at a glance. You draw it as inline SVG on an honest scale, with every value the reader needs printed on the page, never with a chart library or Mermaid.
|
|
11
|
+
A chart answers one question about the source's numbers at a glance. You draw it as inline SVG on an honest scale, with every value the reader needs printed on the page, never with a chart library or Mermaid, and it plays once from its initial state to its values when it scrolls into view.
|
|
12
12
|
|
|
13
13
|
Unless another pageship- skill loaded this one, start as [pageship-docs says](../pageship-docs/SKILL.md#starting-at-a-kind-or-pattern-skill).
|
|
14
14
|
|
|
@@ -85,6 +85,22 @@ Write static SVG markup with the coordinates computed as you write. For each val
|
|
|
85
85
|
|
|
86
86
|
Buttons that highlight or filter series show every series by default.
|
|
87
87
|
|
|
88
|
+
## 5. Play the entrance
|
|
89
|
+
|
|
90
|
+
Load `pageship-motion` before the first chart: [its playback rules](../pageship-motion/SKILL.md#2-build-it-so-the-still-page-tells-the-whole-story) own when a chart plays, its pace, its controls and reduced motion. The markup you wrote in step 4 is the end state; the entrance starts from each mark's initial state and ends on that markup.
|
|
91
|
+
|
|
92
|
+
| Form | Entrance |
|
|
93
|
+
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
94
|
+
| Vertical bars, histogram bins | Bars grow up from the baseline to their height; each printed value fades in with its bar. |
|
|
95
|
+
| Horizontal bars | Bars grow from the left axis to their length, top to bottom. |
|
|
96
|
+
| Line chart | Each line draws from left to right; a point and its value appear as the line reaches it; the direct label appears at the end. |
|
|
97
|
+
| Share bar | The parts fill in from left to right, one after the next, each to its share. |
|
|
98
|
+
| Scatter plot, dot plot | Dots appear in order of the horizontal axis, left to right. |
|
|
99
|
+
| Small multiples | Every small chart plays its own form's entrance at the same time, on the same clock. |
|
|
100
|
+
| Target, forecast and other lines | Appear in full when the series they belong to finishes. |
|
|
101
|
+
|
|
102
|
+
[The reference script](../pageship-motion/references/figure-motion.md) shows bars that grow and a line that draws; the other forms scale each mark's size or opacity the same way.
|
|
103
|
+
|
|
88
104
|
## Colours
|
|
89
105
|
|
|
90
106
|
Colour the chart in the stylesheet, with `fill` and `stroke`, from [the look's colour variables](../pageship-docs/references/design.md#the-looks-colour-variables) and no other colour:
|
|
@@ -3,7 +3,7 @@ name: pageship-code-block
|
|
|
3
3
|
description: Later step of pageship-docs: for a request to write or edit a document, load pageship-docs first; load this skill first only when the user names it. Pattern for code in a Pageship HTML document, such as source code, commands, configuration or logs, loaded by a pageship- kind skill before the first code block.
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.4.0"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Code block
|
|
@@ -3,7 +3,7 @@ name: pageship-decision-record
|
|
|
3
3
|
description: HTML decision records, as the second step of pageship-docs. For any request to record, write, regenerate, edit or move a decision, load pageship-docs first; load this skill first only when the user names it.
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.4.0"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Decision record
|
|
@@ -29,6 +29,10 @@ Build one comparison of the options:
|
|
|
29
29
|
- Every option gets a value on every point: what the source says about that option on that point, in its units and wording. Where the source says nothing, write "Not stated", never a value you would have to infer, such as "low" for an option whose cost the source never gives.
|
|
30
30
|
- Keep the source's order of options, and mark the chosen one in words.
|
|
31
31
|
|
|
32
|
+
## Figures
|
|
33
|
+
|
|
34
|
+
Beyond the comparison table, a decision record's figures come from the numbers behind the context and from the consequences, such as a table of the consequences when most share fields such as standing and who acts. When the source gives a number for every option on one point, such as cost, a chart of that point beside the table is a candidate figure.
|
|
35
|
+
|
|
32
36
|
## Order
|
|
33
37
|
|
|
34
38
|
1. **Title and decision.** An `h1` naming what was decided, then at once the decision in one or two sentences in the source's words, and a line with its status and the date, owner and scope the source gives.
|
|
@@ -3,7 +3,7 @@ name: pageship-diagram
|
|
|
3
3
|
description: Later step of pageship-docs: for a request to write or edit a document, load pageship-docs first; load this skill first only when the user names it. Pattern for diagrams in a Pageship HTML document, such as a process, a timeline, a system map or states, loaded by a pageship- kind skill before it draws a diagram.
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.4.0"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Diagram
|
|
@@ -16,16 +16,16 @@ Unless another pageship- skill loaded this one, start as [pageship-docs says](..
|
|
|
16
16
|
|
|
17
17
|
Write down the reader's question the figure answers, in a few words, and take the row closest to it.
|
|
18
18
|
|
|
19
|
-
| The reader asks | Diagram | Drawn with
|
|
20
|
-
| ----------------------------------------------- | --------------------------------------- |
|
|
21
|
-
| What happens next, and where does it branch? | Flowchart, top to bottom | SVG,
|
|
22
|
-
| Who hands what to whom, in what order? | Swimlane or sequence | SVG,
|
|
23
|
-
| When did each thing happen, and how far apart? | [Timeline](references/svg.md#timelines) | SVG,
|
|
24
|
-
| What states can it be in, and what moves it on? | State diagram | SVG, [computed][layout] when large
|
|
25
|
-
| What connects to what? | System map or dependency graph | SVG, [computed][layout] when large
|
|
26
|
-
| What is part of what? | Tree or nested boxes | SVG
|
|
19
|
+
| The reader asks | Diagram | Drawn with |
|
|
20
|
+
| ----------------------------------------------- | --------------------------------------- | ----------------------------------- |
|
|
21
|
+
| What happens next, and where does it branch? | Flowchart, top to bottom | SVG, played as a [sequence][motion] |
|
|
22
|
+
| Who hands what to whom, in what order? | Swimlane or sequence | SVG, played as a [sequence][motion] |
|
|
23
|
+
| When did each thing happen, and how far apart? | [Timeline](references/svg.md#timelines) | SVG, played as a [sequence][motion] |
|
|
24
|
+
| What states can it be in, and what moves it on? | State diagram | SVG, [computed][layout] when large |
|
|
25
|
+
| What connects to what? | System map or dependency graph | SVG, [computed][layout] when large |
|
|
26
|
+
| What is part of what? | Tree or nested boxes | SVG |
|
|
27
27
|
|
|
28
|
-
A process or a
|
|
28
|
+
Every diagram plays once when it scrolls into view, by `pageship-motion`. A process, a path or events in time play as a [sequence][motion]: a marker travels the path, or the stages or events appear in order, and the reader has the controls. Every other diagram, a computed layout included, is a graphic that builds in: its nodes appear, then its connectors draw. A Mermaid figure, which Mermaid draws at load, holds still.
|
|
29
29
|
|
|
30
30
|
## 2. Fit the limits
|
|
31
31
|
|
|
@@ -52,7 +52,7 @@ Content you merge or move stays in the document, in a figure or the text beside
|
|
|
52
52
|
- With SVG, follow [the SVG rules](references/svg.md).
|
|
53
53
|
- With a computed layout, follow [the computed layout steps][layout].
|
|
54
54
|
- With Mermaid, follow [the Mermaid rules][mermaid].
|
|
55
|
-
-
|
|
55
|
+
- Load `pageship-motion` before the first SVG figure. A process, a path or events in time laid out by hand plays as a [sequence][motion]: each node and connector carries the `data-step` that brings it in, and an ordered list of the steps with their values follows the drawing. Any other SVG figure builds in as a graphic: the nodes appear, then the connectors draw.
|
|
56
56
|
|
|
57
57
|
Every figure, however drawn:
|
|
58
58
|
|
|
@@ -78,8 +78,10 @@ When the source sorts the nodes into two to four named groups, such as the team
|
|
|
78
78
|
|
|
79
79
|
## Check
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
Run the browser steps with [the check's browser tool](../pageship-docs/references/check.md#checks-in-a-browser).
|
|
82
|
+
|
|
83
|
+
- Run [check-diagram.js](references/check-diagram.js) in the page at 1280px and at 390px wide. In `html_preview`, the copy's `load` listener runs `const problems = <the file's text>` and logs `JSON.stringify(problems)`. In agent-browser, run `agent-browser eval "$(cat <this skill's folder>/references/check-diagram.js)"`. It returns a list of problems with every SVG you drew or computed. Fix each one and run it again until the list is empty. A line starting with "note" is a crossing to remove if you can.
|
|
84
|
+
- For every SVG figure, run [the motion check](../pageship-motion/SKILL.md#check).
|
|
83
85
|
- For Mermaid, look at the screenshot at both widths and confirm every node and label is readable, no two lines run together, and no line runs through a node or a label.
|
|
84
86
|
- Without a browser, check your layout plan by arithmetic as [the SVG rules](references/svg.md#plan-the-layout) say.
|
|
85
87
|
|
|
@@ -87,6 +89,6 @@ When the source sorts the nodes into two to four named groups, such as the team
|
|
|
87
89
|
|
|
88
90
|
The structure you introduced includes the form you chose for each figure and why, and any merges, splits or overviews. When a graph over the hand-drawn limits went to Mermaid instead of a computed layout, say why.
|
|
89
91
|
|
|
90
|
-
[
|
|
92
|
+
[motion]: ../pageship-motion/SKILL.md
|
|
91
93
|
[mermaid]: references/mermaid.md
|
|
92
94
|
[layout]: references/computed-layout.md
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Checks every hand-drawn diagram in the open page against the pageship-diagram rules.
|
|
2
|
-
// Run the whole file in the page, for example with `agent-browser eval "$(cat check-diagram.js)"
|
|
3
|
-
// or
|
|
2
|
+
// Run the whole file in the page, for example with `agent-browser eval "$(cat check-diagram.js)"`,
|
|
3
|
+
// or in html_preview as `const problems = <this file>` in a load listener that logs them.
|
|
4
|
+
// It returns a list of problems; an empty list passes.
|
|
4
5
|
// Mermaid diagrams are skipped: Mermaid lays them out itself.
|
|
5
6
|
(() => {
|
|
6
7
|
const problems = [];
|
|
@@ -3,13 +3,15 @@ name: pageship-docs
|
|
|
3
3
|
description: First step for any document people read, such as a report, plan, spec, explainer, review, decision record or slide deck, in HTML or Markdown. Load it before any other pageship- skill. Use when the user asks for one of these documents, asks to regenerate, redesign, edit, rename, move or copy an HTML source file, or asks to record whether they want HTML or Markdown.
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.4.0"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Pageship documents
|
|
10
10
|
|
|
11
11
|
The result is Markdown or one standalone HTML source file. You never publish yourself; you hand the file to the `pageship` skill, which does.
|
|
12
12
|
|
|
13
|
+
When your task says you write the file for `pageship-docs`, you are the subagent of [step 4 in a subagent](#step-4-in-a-subagent): start at step 4 with the format, kind and path it gives. Otherwise, when you can start a subagent, as in Claude Code, Codex and T3 Code, run steps 1 to 3 yourself and send step 4 to one subagent, for a new file and an edit alike. The sources, skill text and check output then stay in its context, not yours. Without subagents, run every step yourself.
|
|
14
|
+
|
|
13
15
|
## 1. Settle the format
|
|
14
16
|
|
|
15
17
|
Take the first rule that applies.
|
|
@@ -35,7 +37,7 @@ For Markdown, go to step 3.
|
|
|
35
37
|
| Decision record | What was decided, the options and the consequences | `pageship-decision-record` |
|
|
36
38
|
| Slide deck | Slides to present from a browser | `pageship-slide-deck` |
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
Pick the kind that fits. Step 4 loads its skill. When none fits exactly, or its skill is not installed, use the closest installed kind and tell the user which.
|
|
39
41
|
|
|
40
42
|
## 3. Choose the path
|
|
41
43
|
|
|
@@ -43,25 +45,47 @@ A new file goes at the path the user names, or else where the project keeps such
|
|
|
43
45
|
|
|
44
46
|
## 4. Write the file
|
|
45
47
|
|
|
48
|
+
When you send this step to a subagent, follow [step 4 in a subagent](#step-4-in-a-subagent).
|
|
49
|
+
|
|
46
50
|
For Markdown, write one file by the [content rules](references/content.md) alone, then hand over.
|
|
47
51
|
|
|
48
|
-
For HTML, the kind skill gives the parts, the order and its own rules, which win over this skeleton:
|
|
52
|
+
For HTML, load the kind skill. It gives the parts, the order and its own rules, which win over this skeleton:
|
|
49
53
|
|
|
50
54
|
1. **Gather.** Read the request, every file it points to and the project's glossary. Where the kind asks for an **inventory**, list every item of the source in its order and words, until an item covers every sentence of the source. Done when you can point to where each fact you plan to write comes from.
|
|
51
55
|
2. **Sort.** Put every statement of the source into exactly one of the kind's parts.
|
|
52
|
-
3. **
|
|
53
|
-
4. **
|
|
56
|
+
3. **Figures.** For each section, ask whether a figure carries its point better than prose: a chart, a table, a diagram or key figures. Wherever one does, use it, in the form [the design rules](references/design.md#the-form-of-each-figure) give. Technical or complex content, such as a mechanism, a set of numbers or a comparison of several items, gets a figure and prose. The prose beside each figure says what it shows and why it matters, as the source puts it. Before drawing the first chart, graphic or sequence diagram, load `pageship-motion`, which plays each one as it scrolls into view. Done when you can name, for each section, the figures it gets, or that prose carries its point better.
|
|
57
|
+
4. **Write.** One file by the [content rules](references/content.md), the [file rules](references/file.md) and the [design rules](references/design.md), in the kind's order, under its headings or the closest glossary terms. A part the source leaves empty keeps its heading and one line saying the source gives none. Done when every sorted statement, or every inventory item, is in the file.
|
|
58
|
+
5. **Check.** Run [the check](references/check.md), with the checks the kind and pattern skills add.
|
|
54
59
|
|
|
55
60
|
Then hand over. Your final message lists, however briefly you otherwise reply:
|
|
56
61
|
|
|
57
62
|
- the file's path;
|
|
58
63
|
- the structure you added that the source didn't have, as [the check lists it](references/check.md#the-structure-you-added);
|
|
59
64
|
- the open questions, marking the ones you added;
|
|
60
|
-
- the checks that ran and the ones you skipped;
|
|
65
|
+
- the checks that ran and the ones you skipped, and which tool ran [the browser checks](references/check.md#checks-in-a-browser), or that none did;
|
|
61
66
|
- what the kind and pattern skills add to this list.
|
|
62
67
|
|
|
63
|
-
|
|
68
|
+
As the subagent, end at the list. Otherwise, for an HTML file, end the hand-over with the next step for publishing. When the user asked in this conversation to publish this document, or accepted an offer to, load `pageship` and publish it. Otherwise, when the `pageship` skill is installed, offer to publish the file through it; for a file with a document marker, offer to publish the change as a new revision of its document.
|
|
69
|
+
|
|
70
|
+
## Step 4 in a subagent
|
|
71
|
+
|
|
72
|
+
Start one subagent and give it:
|
|
73
|
+
|
|
74
|
+
- the user's request, word for word, and every source that lives only in this conversation, such as pasted text or an answer the user gave, quoted in full;
|
|
75
|
+
- each source file by absolute path, the file being edited, renamed, moved or copied included. The subagent reads them;
|
|
76
|
+
- the format and the path from steps 1 to 3 and, for HTML, the kind or lone pattern skill;
|
|
77
|
+
- this instruction: "You write the file for `pageship-docs`. Load it and the skill named here, and follow step 4 and its hand-over. The format, kind and path are settled. Put each question you would ask the user into the open questions. Your final message is the full hand-over list; I handle publishing."
|
|
78
|
+
|
|
79
|
+
Wait for its final message. When it lacks the hand-over list, ask the subagent for the list.
|
|
80
|
+
|
|
81
|
+
When the list has "Fresh reading still to run", the subagent couldn't start a reader of its own:
|
|
82
|
+
|
|
83
|
+
1. Start a new subagent that has not seen this conversation and give it only the reader task the list gives, word for word. Wait for its answer.
|
|
84
|
+
2. Bring its quotes to the writer. When you can continue the writing subagent, send it the quotes in full. Otherwise start a new subagent with the file's path, each source file by absolute path, the kind skill, the quotes in full and this instruction: "You write the file for `pageship-docs`. Load it and the skill named here. The file is written; the quotes below are its fresh reading. Fix each one as the skill's check says, then run the rest of the check again. Your final message is the full hand-over list; I handle publishing."
|
|
85
|
+
3. Its new list replaces the first. Done when the list you relay has no "Fresh reading still to run".
|
|
86
|
+
|
|
87
|
+
Relay the list to the user in full, open questions included, then, for an HTML file, end with the next step for publishing as step 4 says.
|
|
64
88
|
|
|
65
89
|
## Starting at a kind or pattern skill
|
|
66
90
|
|
|
67
|
-
|
|
91
|
+
The subagent always starts at step 4. Otherwise, when the user named a kind skill, or named a pattern skill and asked for just that pattern, write HTML unless the request names another format, and start at step 3. A lone pattern is the file's main content, under an `h1`, with no parts to sort. In every other case, start at step 1.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Runs axe-core's WCAG 2 A and AA rules on the page and logs one line per problem, with the
|
|
2
|
+
// element and how to fix it. Run with `agent-browser eval --stdin < axe.js`, which also prints the
|
|
3
|
+
// lines, or paste into a <script> at the end of <body> for html_preview, which shows the log.
|
|
4
|
+
// The promise stays the script's value, which agent-browser eval waits for and prints.
|
|
5
|
+
// oxlint-disable-next-line typescript/no-floating-promises
|
|
6
|
+
(async () => {
|
|
7
|
+
// Added before the page's load event, the script delays it, so html_preview waits for axe-core.
|
|
8
|
+
if (!window.axe) {
|
|
9
|
+
const script = document.createElement("script");
|
|
10
|
+
script.src = "https://cdn.jsdelivr.net/npm/axe-core@4.14.0/axe.min.js";
|
|
11
|
+
script.integrity = "sha384-lFUXB8tI4BaTE1bqxH9eeAXON2NH1qsdAhs7S5L++nUNP5QOy+hwr0C3TYEJUEl0";
|
|
12
|
+
script.crossOrigin = "anonymous";
|
|
13
|
+
document.head.append(script);
|
|
14
|
+
await new Promise((resolve, reject) => {
|
|
15
|
+
script.onload = resolve;
|
|
16
|
+
script.onerror = () => reject(new Error("axe-core didn't load"));
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
if (document.readyState !== "complete") {
|
|
20
|
+
await new Promise((resolve) => addEventListener("load", resolve, { once: true }));
|
|
21
|
+
}
|
|
22
|
+
const { violations } = await window.axe.run(document, {
|
|
23
|
+
runOnly: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22aa"],
|
|
24
|
+
});
|
|
25
|
+
const problems = violations.flatMap((violation) =>
|
|
26
|
+
violation.nodes.map(
|
|
27
|
+
(node) =>
|
|
28
|
+
`axe ${violation.id}: ${node.target.join(" ")}: ${node.failureSummary.replace(/\s+/g, " ")}`,
|
|
29
|
+
),
|
|
30
|
+
);
|
|
31
|
+
for (const problem of problems) console.log(problem);
|
|
32
|
+
console.log(`axe: ${problems.length} problems`);
|
|
33
|
+
return problems;
|
|
34
|
+
})();
|
|
@@ -2,25 +2,52 @@
|
|
|
2
2
|
|
|
3
3
|
Run these checks on the finished file and fix what they find.
|
|
4
4
|
|
|
5
|
-
The files a check makes, such as screenshots, PDFs and
|
|
5
|
+
The files a check makes, such as screenshots, PDFs, scripts and edited copies of the file, are scratch: save them by absolute path in the system temporary folder, never in the project, and delete them, wherever they landed, once the check is done.
|
|
6
6
|
|
|
7
7
|
## Checks that need no browser
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
Run the linter, html-validate with the config and rules in this folder:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npx --yes html-validate@11.17.0 --config <pageship-docs folder>/references/html-validate.json <file>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
It reports HTML errors, accessibility problems such as a missing `lang` or `alt` or a skipped heading level, and breaks of three rules: [one standalone file](file.md#one-standalone-file) and [trusted libraries only](file.md#trusted-libraries-only) in the file rules, and **Height** in [the design rules](design.md#base-rules-every-file-meets). Fix each problem it reports and run it again. Done when it reports nothing. When `npx` can't run, such as without Node or network, check the file against those three rules by reading it, and say in the hand-over that the linter didn't run.
|
|
16
|
+
|
|
17
|
+
Then:
|
|
18
|
+
|
|
11
19
|
- The file uses nothing on the sandbox list in the file rules.
|
|
12
20
|
- The marker is as the file rules say: the same last line as before, or none at all.
|
|
13
21
|
- Every number, name and date in the file appears in the source. Each gap is an open question.
|
|
14
|
-
- Light and dark each set every variable in [the design rules](design.md#the-looks-colour-variables), each colour clears its floor
|
|
22
|
+
- Light and dark each set every variable in [the design rules](design.md#the-looks-colour-variables), and each colour clears its floor: `node <pageship-docs folder>/references/palette.mjs --check <file>` lists each one that doesn't. Without Node, compare each value with its floor yourself. Both accents colour something on screen.
|
|
15
23
|
|
|
16
24
|
## Checks in a browser
|
|
17
25
|
|
|
18
|
-
|
|
26
|
+
These checks, and the browser steps of the kind and pattern skills, are required whenever you have a browser tool. Use the first one you have:
|
|
27
|
+
|
|
28
|
+
1. **`html_preview`**, in T3 Code. Pass the file's text as `html`, with a `width` and an `appearance`. It returns a screenshot of the whole page and the console output. A file over 512 KB is too large for it: use agent-browser.
|
|
29
|
+
2. **agent-browser**, in every other harness. `agent-browser open file://<the file's absolute path>`, then `set viewport <width> 800`, `set media light` or `set media dark`, `screenshot <path>`, `console` and `errors`. A step's input uses `click`, `press` and `focus`, and a step's script runs with `eval`.
|
|
30
|
+
3. **Neither.** Skip this section and the skills' browser steps, and take the step a skill gives for a missing browser, where it gives one.
|
|
19
31
|
|
|
20
|
-
|
|
21
|
-
|
|
32
|
+
Some states take an edited copy of the file:
|
|
33
|
+
|
|
34
|
+
- **axe-core in `html_preview`**: at the end of `<body>`, add a `<script>` holding the text of `<pageship-docs folder>/references/axe.js`. Every preview of the file uses a copy with it.
|
|
35
|
+
- **Dark in `html_preview`**, which doesn't set the colour scheme: change every `prefers-color-scheme: dark` to `prefers-color-scheme: light`, add `<style>:root { color-scheme: dark }</style>` at the end of `<head>`, and pass `appearance: "dark"`.
|
|
36
|
+
- **Reduced motion in `html_preview`**: swap `prefers-reduced-motion: reduce` and `prefers-reduced-motion: no-preference` everywhere. In agent-browser, `set media light reduced-motion`.
|
|
37
|
+
- **Print, in both tools**, which can't emulate it: swap `print` and `screen` in every media query, in `@media` rules and `matchMedia` calls alike, so `@media print` reads `@media screen`.
|
|
38
|
+
- **Input and scripts in `html_preview`**: at the end of `<body>`, add a `<script>` with a `load` listener that takes the steps and logs each result with `console.log`. Click with `element.click()`, which also stands for Enter or Space on a button; press a key with `element.dispatchEvent(new KeyboardEvent("keydown", { key: "ArrowRight", bubbles: true, cancelable: true }))`; hover with a `pointerover` event the same way. A script can't press Tab: log each control's `tabIndex`, which is `0` for every control in the tab order, then focus each one in document order with `element.focus()` and log `document.activeElement`. The screenshot shows the page after the listener ran.
|
|
39
|
+
- **A fragment in `html_preview`**, such as `#slide-3`: the copy's first script runs `history.replaceState(null, "", "#slide-3")`.
|
|
40
|
+
|
|
41
|
+
`html_preview` takes its screenshot right after the page loads, before timers and animations run their course, so a figure that plays in view at load may show its initial state or part of its playback; `pageship-motion`'s check covers its end state. A step that waits, such as letting playback run, takes the skill's step for a missing browser, or is skipped where the skill gives none.
|
|
42
|
+
|
|
43
|
+
At 1280px and at 390px wide, each in light and in dark:
|
|
44
|
+
|
|
45
|
+
- Confirm the page doesn't scroll sideways: `document.documentElement.scrollWidth` is no larger than `clientWidth`.
|
|
46
|
+
- Look at the screenshot. Fix overlapping, clipped or unreadable content.
|
|
22
47
|
- Read the console. Fix every error, including failed integrity checks.
|
|
23
|
-
-
|
|
48
|
+
- Run axe-core, which checks accessibility in the rendered page, such as the contrast of each piece of text against what is really behind it. In agent-browser, run `agent-browser eval --stdin < <pageship-docs folder>/references/axe.js`; in `html_preview`, read the lines the copy's script logs. Fix each problem it lists.
|
|
49
|
+
|
|
50
|
+
Then use every control by keyboard and confirm it works and shows focus. Done when, at both widths in both colour schemes, the page doesn't scroll sideways, the screenshot shows nothing overlapping, clipped or unreadable, the console shows no error, and axe-core lists no problem, and every control works by keyboard and shows focus.
|
|
24
51
|
|
|
25
52
|
## Claims
|
|
26
53
|
|