@wenathlan/devthink 1.1.15 → 2.0.2
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 +1299 -0
- package/README.md +6 -0
- package/bridge.d.ts +698 -0
- package/bridge.d.ts.map +1 -0
- package/bridge.js +1877 -0
- package/bun.d.ts +8 -0
- package/bun.d.ts.map +1 -0
- package/bun.js +52329 -0
- package/caps/background.json +712 -0
- package/caps/cli.json +360 -0
- package/caps/library.json +3611 -0
- package/caps/mcp.json +126 -0
- package/caps/pagebridge.json +377 -0
- package/caps/popup.json +51 -0
- package/caps/sidepanel.json +522 -0
- package/checksums.txt +142 -0
- package/cli.d.ts +187 -0
- package/cli.d.ts.map +1 -0
- package/cli.js +18197 -0
- package/companion.d.ts +19 -0
- package/companion.d.ts.map +1 -0
- package/companion.js +389 -0
- package/crossbrowser.d.ts +277 -0
- package/crossbrowser.d.ts.map +1 -0
- package/crossbrowser.js +1191 -0
- package/dashdone.d.ts +273 -0
- package/dashdone.d.ts.map +1 -0
- package/dashdone.js +368 -0
- package/deno.d.ts +11 -0
- package/deno.d.ts.map +1 -0
- package/deno.js +52325 -0
- package/devthink.umd.js +52346 -0
- package/fixtures/bun.ts +7 -0
- package/fixtures/cjs.js +6 -0
- package/fixtures/deno.ts +6 -0
- package/fixtures/example-org-pagestate.json +22 -0
- package/fixtures/importers/automa-workflow.json +18 -0
- package/fixtures/importers/selenium-side.json +20 -0
- package/fixtures/importers/tabular-plan.csv +7 -0
- package/fixtures/importers/uivision-macro.json +10 -0
- package/fixtures/importers/v1-plan.json +13 -0
- package/fixtures/mcp-client.mjs +14 -0
- package/fixtures/pages/checkout.html +192 -0
- package/fixtures/pages/dashboard.html +110 -0
- package/fixtures/pages/feed.html +113 -0
- package/fixtures/pages/product-grid.html +150 -0
- package/fixtures/plans/form-inventory-plan.json +11 -0
- package/fixtures/plans/release-notes-plan.json +11 -0
- package/fixtures/recipes/agentscompete.json +11 -0
- package/fixtures/recipes/agentsforms.json +11 -0
- package/fixtures/recipes/agentsmonitor.json +11 -0
- package/fixtures/recipes/agentsreview.json +29 -0
- package/fixtures/recipes/agentswarm.json +11 -0
- package/fixtures/recipes/fillcalendar.json +12 -0
- package/fixtures/recipes/fillcheckout.json +13 -0
- package/fixtures/recipes/filldependentdropdowns.json +12 -0
- package/fixtures/recipes/fillfromcsv.json +13 -0
- package/fixtures/recipes/fillloginform.json +12 -0
- package/fixtures/recipes/fillmultistepwizard.json +13 -0
- package/fixtures/recipes/fillregistration.json +12 -0
- package/fixtures/recipes/fillsearchfilters.json +12 -0
- package/fixtures/recipes/filltypeahead.json +11 -0
- package/fixtures/recipes/monitoralerts.json +12 -0
- package/fixtures/recipes/monitoravailability.json +11 -0
- package/fixtures/recipes/monitornetworkquiet.json +12 -0
- package/fixtures/recipes/monitorpagechanges.json +11 -0
- package/fixtures/recipes/monitorpricechange.json +11 -0
- package/fixtures/recipes/monitorschedule.json +12 -0
- package/fixtures/recipes/scrapedynamicprices.json +13 -0
- package/fixtures/recipes/scrapeimagegallery.json +12 -0
- package/fixtures/recipes/scrapeinfinitefeed.json +13 -0
- package/fixtures/recipes/scrapenestedlists.json +12 -0
- package/fixtures/recipes/scrapeproductgrid.json +12 -0
- package/fixtures/recipes/scrapereviews.json +12 -0
- package/fixtures/recipes/scrapesearchresults.json +12 -0
- package/fixtures/recipes/scrapetablepagination.json +12 -0
- package/fixtures/recipes/testaccessibility.json +34 -0
- package/fixtures/recipes/testconsoleerrors.json +12 -0
- package/fixtures/recipes/testdeeplinks.json +12 -0
- package/fixtures/recipes/testformvalidation.json +13 -0
- package/fixtures/recipes/testlayoutshift.json +12 -0
- package/fixtures/recipes/testlinkcheck.json +12 -0
- package/fixtures/recipes/testloadtiming.json +12 -0
- package/fixtures/recipes/testvisualdiff.json +12 -0
- package/fixtures/workflows/changelog-digest-workflow.json +13 -0
- package/fixtures/workflows/form-survey-workflow.json +13 -0
- package/gallery.json +706 -0
- package/hardening.d.ts +81 -0
- package/hardening.d.ts.map +1 -0
- package/hardening.js +188 -0
- package/headless.d.ts +114 -0
- package/headless.d.ts.map +1 -0
- package/headless.js +11522 -0
- package/http.d.ts +636 -0
- package/http.d.ts.map +1 -0
- package/http.js +2165 -0
- package/index.cjs +54682 -0
- package/index.d.ts +40 -0
- package/index.d.ts.map +1 -0
- package/index.js +52310 -0
- package/index.neutral.js +46343 -0
- package/manifest.json +199 -0
- package/mcp.d.ts +501 -0
- package/mcp.d.ts.map +1 -0
- package/mcp.js +20515 -0
- package/memory.d.ts +2397 -0
- package/memory.d.ts.map +1 -0
- package/memory.js +5782 -0
- package/nativehost.template.json +10 -0
- package/node.cjs +54686 -0
- package/node.d.ts +8 -0
- package/node.d.ts.map +1 -0
- package/pack.d.ts +211 -0
- package/pack.d.ts.map +1 -0
- package/pack.js +805 -0
- package/package.json +101 -51
- package/policy.d.ts +1854 -0
- package/policy.d.ts.map +1 -0
- package/policy.js +12580 -0
- package/progress.d.ts +390 -0
- package/progress.d.ts.map +1 -0
- package/progress.js +645 -0
- package/protocol.d.ts +2336 -0
- package/protocol.d.ts.map +1 -0
- package/protocol.js +8334 -0
- package/schemas/audit.schema.json +226 -0
- package/schemas/capability.schema.json +113 -0
- package/schemas/envelope.schema.json +162 -0
- package/schemas/memory.schema.json +51 -0
- package/schemas/observation.schema.json +113 -0
- package/schemas/plan.schema.json +208 -0
- package/schemas/progress.schema.json +126 -0
- package/schemas/proposal.schema.json +68 -0
- package/schemas/session.schema.json +82 -0
- package/schemas/tool.schema.json +94 -0
- package/server.d.ts +435 -0
- package/server.d.ts.map +1 -0
- package/server.js +28872 -0
- package/umd-example.html +18 -0
- package/umd.d.ts +6 -0
- package/umd.d.ts.map +1 -0
- package/build.ts +0 -41
- package/check-secrets.ts +0 -52
- package/compatibility.ts +0 -37
- package/config.ts +0 -246
- package/devthink.ts +0 -397
- package/identity.ts +0 -156
- package/ids.ts +0 -22
- package/memory.ts +0 -95
- package/modes.ts +0 -43
- package/plugin.ts +0 -68
- package/providers.ts +0 -179
- package/server.ts +0 -264
- package/session.ts +0 -155
- package/storage.ts +0 -82
- package/stream.ts +0 -130
- package/sync.ts +0 -27
- package/terminal-ui.tsx +0 -155
- package/ui.ts +0 -143
- package/workspace.ts +0 -20
package/fixtures/bun.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** One bun script that imports the library through the bun runtime: the same consent-first library loads with the node default adapters. */
|
|
2
|
+
import { bundlestamp, parseproposal } from "@wenathlan/extension";
|
|
3
|
+
|
|
4
|
+
const stamp = bundlestamp();
|
|
5
|
+
console.log(`devthink ${stamp.version} in ${stamp.mode} mode under bun ${Bun.version}`);
|
|
6
|
+
const proposal = parseproposal(JSON.stringify({ version: stamp.version, plan: { id: "plan1", state: "awaitingapproval", objective: "Read the release notes", steps: [{ id: "s1", kind: "observe", summary: "Observe the page." }] } }), "https://example.org");
|
|
7
|
+
console.log(`the plan parses with ${proposal.plan.steps.length} step${proposal.plan.steps.length === 1 ? "" : "s"} under the same review the extension runs`);
|
package/fixtures/cjs.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** One plain node script that requires the library in cjs mode: the stable getter object exposes the same named exports as the esm build. */
|
|
2
|
+
const devthink = require("@wenathlan/extension");
|
|
3
|
+
|
|
4
|
+
const stamp = devthink.bundlestamp();
|
|
5
|
+
console.log(`devthink ${stamp.version} in ${stamp.mode} mode`);
|
|
6
|
+
console.log(`policy exports ${Object.keys(require("@wenathlan/extension/policy")).length} functions`);
|
package/fixtures/deno.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** One deno script that imports the library through npm specifiers: the deno adapter reads its configuration and the same consent gates hold. */
|
|
2
|
+
import { bundlestamp, platformtargets } from "@wenathlan/extension";
|
|
3
|
+
|
|
4
|
+
const stamp = bundlestamp();
|
|
5
|
+
console.log(`devthink ${stamp.version} in ${stamp.mode} mode under deno ${Deno.version.deno}`);
|
|
6
|
+
console.log(`the platform matrix covers ${platformtargets().length} runtime targets`);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "example-org-pagestate",
|
|
3
|
+
"origin": "https://example.org",
|
|
4
|
+
"recordedat": 1800000000000,
|
|
5
|
+
"grants": ["observe", "readtext", "readforms", "inspect", "countelements"],
|
|
6
|
+
"observation": {
|
|
7
|
+
"schemaversion": 1,
|
|
8
|
+
"url": "https://example.org/releases",
|
|
9
|
+
"title": "Example releases",
|
|
10
|
+
"textpreview": "Release notes of the example project: the latest release ships the cli expansion.",
|
|
11
|
+
"textlength": 512,
|
|
12
|
+
"forms": [
|
|
13
|
+
{ "label": "email", "type": "email", "name": "email" },
|
|
14
|
+
{ "label": "message", "type": "textarea", "name": "message" }
|
|
15
|
+
],
|
|
16
|
+
"interactive": [
|
|
17
|
+
{ "selector": "main a", "role": "link", "label": "latest release" },
|
|
18
|
+
{ "selector": "form button", "role": "button", "label": "send" }
|
|
19
|
+
],
|
|
20
|
+
"capturedat": 1800000000000
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "contact survey sweep",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"blocks": {
|
|
5
|
+
"trigger": { "id": "trigger", "type": "trigger", "data": { "type": "manual" } },
|
|
6
|
+
"tab": { "id": "tab", "type": "newtab", "data": { "url": "https://example.org/contact", "active": true } },
|
|
7
|
+
"survey": {
|
|
8
|
+
"id": "survey",
|
|
9
|
+
"type": "forms",
|
|
10
|
+
"data": { "fields": [
|
|
11
|
+
{ "name": "email", "value": "survey@example.org", "selector": "input[name=email]" },
|
|
12
|
+
{ "name": "message", "value": "hello from the sweep", "selector": "textarea" }
|
|
13
|
+
] }
|
|
14
|
+
},
|
|
15
|
+
"send": { "id": "send", "type": "click-element", "data": { "selector": "button.primary" } },
|
|
16
|
+
"hold": { "id": "hold", "type": "wait", "data": { "ms": 1500 } }
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "side-example",
|
|
3
|
+
"version": "2.0",
|
|
4
|
+
"name": "example contact survey",
|
|
5
|
+
"url": "https://example.org",
|
|
6
|
+
"tests": [
|
|
7
|
+
{
|
|
8
|
+
"id": "survey",
|
|
9
|
+
"name": "survey",
|
|
10
|
+
"commands": [
|
|
11
|
+
{ "id": "open", "command": "open", "target": "/contact", "value": "" },
|
|
12
|
+
{ "id": "email", "command": "type", "target": "css=input[name=email]", "value": "survey@example.org" },
|
|
13
|
+
{ "id": "keys", "command": "sendKeys", "target": "css=input[name=email]", "value": " hello" },
|
|
14
|
+
{ "id": "pick", "command": "select", "target": "id=topic", "value": "general" },
|
|
15
|
+
{ "id": "send", "command": "click", "target": "css=button.primary", "value": "" },
|
|
16
|
+
{ "id": "hold", "command": "pause", "target": "2000", "value": "" }
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Name": "example releases read",
|
|
3
|
+
"CreationDate": "2024-05-01",
|
|
4
|
+
"Commands": [
|
|
5
|
+
{ "Command": "open", "Target": "https://example.org/releases", "Value": "" },
|
|
6
|
+
{ "Command": "click", "Target": "css=nav a.releases", "Value": "" },
|
|
7
|
+
{ "Command": "type", "Target": "css=input[name=filter]", "Value": "release notes" },
|
|
8
|
+
{ "Command": "verifyText", "Target": "css=main h1", "Value": "Releases" }
|
|
9
|
+
]
|
|
10
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1",
|
|
3
|
+
"goal": "Survey the contact form of the example project",
|
|
4
|
+
"origin": "https://example.org",
|
|
5
|
+
"grants": ["observe", "readforms", "inspect", "type", "delay"],
|
|
6
|
+
"steps": [
|
|
7
|
+
{ "id": "observe", "action": "observe", "label": "Observe the contact page" },
|
|
8
|
+
{ "id": "read", "action": "readforms", "label": "Read the contact form controls" },
|
|
9
|
+
{ "id": "inspect", "action": "inspect", "label": "Inspect the form fields", "selector": "form input" },
|
|
10
|
+
{ "id": "answer", "action": "type", "label": "Type the survey answer", "selector": "input[name=answer]", "value": "yes" },
|
|
11
|
+
{ "id": "pause", "action": "delay", "label": "Wait for the quiet form", "milliseconds": 3000 }
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** One minimal mcp client script that lists the devthink tools over stdio: run `node dist/fixtures/mcp-client.mjs` with the serve command of the extension cli in your path. */
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
|
|
4
|
+
const child = spawn("node", ["dist/cli.js", "serve", "--stdio"], { stdio: ["pipe", "pipe", "inherit"] });
|
|
5
|
+
const send = (id, method, params) => child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}\n`);
|
|
6
|
+
child.stdout.on("data", chunk => {
|
|
7
|
+
for (const line of String(chunk).trim().split("\n")) {
|
|
8
|
+
if (line === "") continue;
|
|
9
|
+
const frame = JSON.parse(line);
|
|
10
|
+
if (frame.id === 1) { console.log(`server: ${frame.result.serverInfo.name} ${frame.result.serverInfo.version}`); send(2, "tools/list", {}); }
|
|
11
|
+
if (frame.id === 2) { console.log(`${frame.result.tools.length} tools:`); for (const tool of frame.result.tools) console.log(` ${tool.name} (${tool.metadata?.riskclass ?? "read"})`); child.kill(); }
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
send(1, "initialize", { protocolVersion: "2025-06-18", capabilities: {}, clientInfo: { name: "example client", version: "1" } });
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>Gallery fixture checkout</title>
|
|
7
|
+
<meta name="description" content="The checkout fixture page of the devthink example gallery: a login form, a checkout wizard, a registration form, dependent dropdowns, a delivery calendar and inline validation errors.">
|
|
8
|
+
<!--
|
|
9
|
+
The checkout fixture page of the example gallery (local origin https://checkout.test).
|
|
10
|
+
Self contained static html with inline styles and inline script: no remote resource loads,
|
|
11
|
+
no payment call, no storage. The page exists so the form recipes have one reviewable local
|
|
12
|
+
target that mirrors a checkout flow with its validation behavior.
|
|
13
|
+
-->
|
|
14
|
+
<style>
|
|
15
|
+
:root { color-scheme: light; font-family: system-ui, sans-serif; }
|
|
16
|
+
body { margin: 0 auto; max-width: 640px; padding: 16px; color: #17202e; }
|
|
17
|
+
header h1 { font-size: 22px; margin: 4px 0; }
|
|
18
|
+
header p { color: #56637a; margin: 4px 0 18px; }
|
|
19
|
+
form { display: grid; gap: 8px; border: 1px solid #d5dce8; border-radius: 10px; padding: 14px; margin: 14px 0 22px; }
|
|
20
|
+
form h2 { font-size: 16px; margin: 0 0 6px; }
|
|
21
|
+
label { font-size: 13px; color: #56637a; }
|
|
22
|
+
input, select { padding: 7px 9px; border: 1px solid #c6cedd; border-radius: 6px; font-size: 14px; }
|
|
23
|
+
button { padding: 8px 14px; border: 1px solid #17202e; background: #17202e; color: #fff; border-radius: 6px; cursor: pointer; justify-self: start; }
|
|
24
|
+
ol.steps { display: flex; gap: 8px; list-style: none; padding: 0; margin: 0 0 10px; font-size: 13px; }
|
|
25
|
+
ol.steps li { padding: 4px 10px; border: 1px solid #c6cedd; border-radius: 999px; color: #56637a; }
|
|
26
|
+
ol.steps li[aria-current="true"] { background: #17202e; color: #fff; border-color: #17202e; }
|
|
27
|
+
fieldset { border: 1px solid #d5dce8; border-radius: 8px; margin: 6px 0; }
|
|
28
|
+
.form-error { display: none; margin: 8px 0 0; padding: 8px 12px; border-left: 3px solid #ff7b8e; background: #fff2f4; color: #8a2233; font-size: 13px; }
|
|
29
|
+
.form-error[data-visible="true"] { display: block; }
|
|
30
|
+
footer p { color: #56637a; font-size: 12px; }
|
|
31
|
+
</style>
|
|
32
|
+
</head>
|
|
33
|
+
<body>
|
|
34
|
+
<header>
|
|
35
|
+
<h1>Fixture checkout</h1>
|
|
36
|
+
<p>The local gallery fixture of the devthink form recipes: a login form, a checkout wizard, a registration form, dependent dropdowns and a delivery calendar.</p>
|
|
37
|
+
</header>
|
|
38
|
+
<main>
|
|
39
|
+
<form class="login-form" aria-label="Account login">
|
|
40
|
+
<h2>Account login</h2>
|
|
41
|
+
<label for="loginemail">Email</label>
|
|
42
|
+
<input id="loginemail" name="email" type="email" autocomplete="username">
|
|
43
|
+
<label for="loginpassword">Password</label>
|
|
44
|
+
<input id="loginpassword" name="password" type="password" autocomplete="current-password">
|
|
45
|
+
<button type="button" id="loginsubmit">Log in</button>
|
|
46
|
+
<p class="form-error" id="loginerror" role="alert">The email field needs a valid address before the login continues.</p>
|
|
47
|
+
</form>
|
|
48
|
+
<form class="checkout-form" aria-label="Checkout wizard">
|
|
49
|
+
<h2>Checkout</h2>
|
|
50
|
+
<ol class="steps" id="checkoutsteps">
|
|
51
|
+
<li aria-current="true">Address</li>
|
|
52
|
+
<li>Shipping</li>
|
|
53
|
+
<li>Review</li>
|
|
54
|
+
</ol>
|
|
55
|
+
<fieldset>
|
|
56
|
+
<legend>Address</legend>
|
|
57
|
+
<label for="checkoutname">Full name</label>
|
|
58
|
+
<input id="checkoutname" name="name">
|
|
59
|
+
<label for="checkoutaddress">Street address</label>
|
|
60
|
+
<input id="checkoutaddress" name="address">
|
|
61
|
+
<label for="checkoutcity">City</label>
|
|
62
|
+
<input id="checkoutcity" name="city">
|
|
63
|
+
</fieldset>
|
|
64
|
+
<fieldset id="shippingfields" hidden>
|
|
65
|
+
<legend>Shipping</legend>
|
|
66
|
+
<label for="shipping">Shipping method</label>
|
|
67
|
+
<select id="shipping" name="shipping"><option value="standard">Standard</option><option value="express">Express</option></select>
|
|
68
|
+
</fieldset>
|
|
69
|
+
<fieldset id="reviewfields" hidden>
|
|
70
|
+
<legend>Review</legend>
|
|
71
|
+
<label for="cardnumber">Card number</label>
|
|
72
|
+
<input id="cardnumber" name="cardnumber" inputmode="numeric" placeholder="0000 0000 0000 0000">
|
|
73
|
+
</fieldset>
|
|
74
|
+
<button type="button" id="checkoutsubmit">Place the order</button>
|
|
75
|
+
<p class="form-error" id="checkouterror" role="alert">The card number field stays empty; the review step refuses the order.</p>
|
|
76
|
+
</form>
|
|
77
|
+
<form class="registration-form" aria-label="Account registration">
|
|
78
|
+
<h2>Registration</h2>
|
|
79
|
+
<label for="fullname">Full name</label>
|
|
80
|
+
<input id="fullname" name="fullname">
|
|
81
|
+
<label for="regemail">Email</label>
|
|
82
|
+
<input id="regemail" name="email" type="email">
|
|
83
|
+
<label for="phone">Phone</label>
|
|
84
|
+
<input id="phone" name="phone" type="tel">
|
|
85
|
+
<button type="button" id="registersubmit">Create the account</button>
|
|
86
|
+
<p class="form-error" id="registererror" role="alert">The registration needs a name before the account is created.</p>
|
|
87
|
+
</form>
|
|
88
|
+
<form class="wizard-form" aria-label="Delivery wizard">
|
|
89
|
+
<h2>Delivery wizard</h2>
|
|
90
|
+
<ol class="steps" id="wizardsteps">
|
|
91
|
+
<li aria-current="true">Address</li>
|
|
92
|
+
<li>Delivery</li>
|
|
93
|
+
<li>Confirm</li>
|
|
94
|
+
</ol>
|
|
95
|
+
<label for="wizardname">Name</label>
|
|
96
|
+
<input id="wizardname" name="name">
|
|
97
|
+
<label for="wizardstreet">Street</label>
|
|
98
|
+
<input id="wizardstreet" name="street">
|
|
99
|
+
<label for="wizardcity">City</label>
|
|
100
|
+
<input id="wizardcity" name="city">
|
|
101
|
+
<button type="button" id="wizardsubmit">Confirm the delivery</button>
|
|
102
|
+
<p class="form-error" id="wizarderror" role="alert">The delivery wizard needs every address field before it confirms.</p>
|
|
103
|
+
</form>
|
|
104
|
+
<form class="dropdown-form" aria-label="Region and city">
|
|
105
|
+
<h2>Region and city</h2>
|
|
106
|
+
<label for="region">Region</label>
|
|
107
|
+
<select id="region" name="region">
|
|
108
|
+
<option value="">Choose a region</option>
|
|
109
|
+
<option value="north">North</option>
|
|
110
|
+
<option value="south">South</option>
|
|
111
|
+
</select>
|
|
112
|
+
<label for="city">City</label>
|
|
113
|
+
<select id="city" name="city" disabled>
|
|
114
|
+
<option value="">Choose a city</option>
|
|
115
|
+
</select>
|
|
116
|
+
<button type="button" id="dropdownsubmit">Save the region</button>
|
|
117
|
+
</form>
|
|
118
|
+
<form class="calendar-form" aria-label="Delivery date">
|
|
119
|
+
<h2>Delivery date</h2>
|
|
120
|
+
<label for="delivery">Delivery date</label>
|
|
121
|
+
<input id="delivery" name="delivery" type="date">
|
|
122
|
+
<button type="button" id="calendarsubmit">Choose the date</button>
|
|
123
|
+
</form>
|
|
124
|
+
</main>
|
|
125
|
+
<footer>
|
|
126
|
+
<p>Fixture page of the devthink example gallery — local origin https://checkout.test, no remote resource, no payment call, no storage.</p>
|
|
127
|
+
</footer>
|
|
128
|
+
<script>
|
|
129
|
+
/* The inline behavior of the fixture page: the dependent dropdown fills its city options from
|
|
130
|
+
the chosen region, the wizard and checkout step lists advance their step markers, and every
|
|
131
|
+
submit button reveals the inline validation error the validation recipes read. */
|
|
132
|
+
(function () {
|
|
133
|
+
"use strict";
|
|
134
|
+
var cities = { north: ["harbor", "fjord"], south: ["cove", "atlas"] };
|
|
135
|
+
var region = document.getElementById("region");
|
|
136
|
+
var city = document.getElementById("city");
|
|
137
|
+
function showerror(id) {
|
|
138
|
+
var node = document.getElementById(id);
|
|
139
|
+
if (node) node.setAttribute("data-visible", "true");
|
|
140
|
+
}
|
|
141
|
+
var errors = [
|
|
142
|
+
["loginsubmit", "loginerror"],
|
|
143
|
+
["checkoutsubmit", "checkouterror"],
|
|
144
|
+
["registersubmit", "registererror"],
|
|
145
|
+
["wizardsubmit", "wizarderror"],
|
|
146
|
+
["dropdownsubmit", null],
|
|
147
|
+
["calendarsubmit", null]
|
|
148
|
+
];
|
|
149
|
+
for (var index = 0; index < errors.length; index += 1) {
|
|
150
|
+
var button = document.getElementById(errors[index][0]);
|
|
151
|
+
var errorid = errors[index][1];
|
|
152
|
+
if (button) button.addEventListener("click", function (errorvalue) {
|
|
153
|
+
return function () { if (errorvalue !== null) showerror(errorvalue); };
|
|
154
|
+
}(errorid));
|
|
155
|
+
}
|
|
156
|
+
if (region && city) region.addEventListener("change", function () {
|
|
157
|
+
var chosen = region.value;
|
|
158
|
+
while (city.options.length > 1) city.remove(1);
|
|
159
|
+
var list = cities[chosen] || [];
|
|
160
|
+
for (var option = 0; option < list.length; option += 1) {
|
|
161
|
+
var item = document.createElement("option");
|
|
162
|
+
item.value = list[option];
|
|
163
|
+
item.textContent = list[option];
|
|
164
|
+
city.append(item);
|
|
165
|
+
}
|
|
166
|
+
city.disabled = chosen === "";
|
|
167
|
+
});
|
|
168
|
+
function advance(listid) {
|
|
169
|
+
var list = document.getElementById(listid);
|
|
170
|
+
if (!list) return;
|
|
171
|
+
var steps = list.children;
|
|
172
|
+
for (var step = 0; step < steps.length; step += 1) {
|
|
173
|
+
if (steps[step].getAttribute("aria-current") === "true") {
|
|
174
|
+
steps[step].removeAttribute("aria-current");
|
|
175
|
+
if (steps[step + 1]) steps[step + 1].setAttribute("aria-current", "true");
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
var shippingfields = document.getElementById("shippingfields");
|
|
181
|
+
var reviewfields = document.getElementById("reviewfields");
|
|
182
|
+
var checkoutsubmit = document.getElementById("checkoutsubmit");
|
|
183
|
+
if (checkoutsubmit) checkoutsubmit.addEventListener("click", function () {
|
|
184
|
+
if (shippingfields && shippingfields.hidden) { shippingfields.hidden = false; advance("checkoutsteps"); return; }
|
|
185
|
+
if (reviewfields && reviewfields.hidden) { reviewfields.hidden = false; advance("checkoutsteps"); }
|
|
186
|
+
});
|
|
187
|
+
var wizardsubmit = document.getElementById("wizardsubmit");
|
|
188
|
+
if (wizardsubmit) wizardsubmit.addEventListener("click", function () { advance("wizardsteps"); });
|
|
189
|
+
})();
|
|
190
|
+
</script>
|
|
191
|
+
</body>
|
|
192
|
+
</html>
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>Gallery fixture dashboard</title>
|
|
7
|
+
<meta name="description" content="The dashboard fixture page of the devthink example gallery: a price ticker on a timer, toasts, console noise, an availability badge, deep links and network quiet.">
|
|
8
|
+
<!--
|
|
9
|
+
The dashboard fixture page of the example gallery (local origin https://dashboard.test).
|
|
10
|
+
Self contained static html with inline styles and inline script: the timers tick locally,
|
|
11
|
+
the console noise writes to the devtools console only, the network quiet is a local activity
|
|
12
|
+
counter that decays over time — no remote resource, no fetch, no storage, no cookie.
|
|
13
|
+
-->
|
|
14
|
+
<style>
|
|
15
|
+
:root { color-scheme: dark; font-family: system-ui, sans-serif; }
|
|
16
|
+
body { margin: 0 auto; max-width: 720px; padding: 16px; background: #090d16; color: #eff6ff; }
|
|
17
|
+
header h1 { font-size: 22px; margin: 4px 0; }
|
|
18
|
+
header p { color: #9aa8bd; margin: 4px 0 18px; }
|
|
19
|
+
.price-ticker { display: flex; gap: 14px; flex-wrap: wrap; border: 1px solid #26334b; border-radius: 10px; padding: 12px; margin: 12px 0 18px; font-variant-numeric: tabular-nums; }
|
|
20
|
+
.price-ticker span { font-size: 15px; }
|
|
21
|
+
.price-ticker span[data-direction="up"] { color: #9ae6b4; }
|
|
22
|
+
.price-ticker span[data-direction="down"] { color: #ff7b8e; }
|
|
23
|
+
.availability { display: inline-block; border-radius: 999px; border: 1px solid #56cc9b; color: #56cc9b; font-size: 12px; font-weight: 700; letter-spacing: .06em; padding: 3px 10px; text-transform: uppercase; }
|
|
24
|
+
.toasts { display: grid; gap: 8px; margin: 12px 0 18px; }
|
|
25
|
+
.toast { border: 1px solid #26334b; border-radius: 8px; background: #11192a; padding: 8px 12px; font-size: 13px; color: #9aa8bd; }
|
|
26
|
+
nav.deeplinks { display: flex; gap: 10px; flex-wrap: wrap; margin: 12px 0 22px; }
|
|
27
|
+
nav.deeplinks a { color: #74e8c1; font-size: 14px; }
|
|
28
|
+
.meter { border: 1px solid #26334b; border-radius: 8px; padding: 10px; font-size: 13px; color: #9aa8bd; margin: 12px 0 22px; }
|
|
29
|
+
footer p { color: #9aa8bd; font-size: 12px; }
|
|
30
|
+
</style>
|
|
31
|
+
</head>
|
|
32
|
+
<body>
|
|
33
|
+
<header>
|
|
34
|
+
<h1>Fixture dashboard</h1>
|
|
35
|
+
<p>The local gallery fixture of the devthink monitoring and testing recipes: a live price ticker, timers, toasts, console noise, an availability badge, deep links and network quiet.</p>
|
|
36
|
+
</header>
|
|
37
|
+
<main>
|
|
38
|
+
<section aria-label="Price ticker">
|
|
39
|
+
<h2>Price ticker</h2>
|
|
40
|
+
<p><span class="availability" id="availabilitybadge">All systems available</span></p>
|
|
41
|
+
<div class="price-ticker" id="ticker" aria-live="off">
|
|
42
|
+
<span data-symbol="LUMEN" data-direction="up">$89.00</span>
|
|
43
|
+
<span data-symbol="HARBOR" data-direction="down">$45.00</span>
|
|
44
|
+
<span data-symbol="NORTH" data-direction="up">$120.00</span>
|
|
45
|
+
</div>
|
|
46
|
+
</section>
|
|
47
|
+
<section aria-label="Activity">
|
|
48
|
+
<h2>Activity</h2>
|
|
49
|
+
<div class="toasts" id="toasts">
|
|
50
|
+
<div class="toast" role="status">The nightly digest finished at 03:00.</div>
|
|
51
|
+
<div class="toast" role="status">The inventory sync queued 6 rows.</div>
|
|
52
|
+
</div>
|
|
53
|
+
<div class="meter" id="networkmeter" role="status" aria-live="polite">Network activity: 3 pending events.</div>
|
|
54
|
+
</section>
|
|
55
|
+
<nav class="deeplinks" aria-label="Dashboard deep links">
|
|
56
|
+
<a href="#ticker">Open the ticker</a>
|
|
57
|
+
<a href="#toasts">Open the toasts</a>
|
|
58
|
+
<a href="#networkmeter">Open the network meter</a>
|
|
59
|
+
</nav>
|
|
60
|
+
</main>
|
|
61
|
+
<footer>
|
|
62
|
+
<p>Fixture page of the devthink example gallery — local origin https://dashboard.test, no remote resource, no fetch, no storage, no cookie.</p>
|
|
63
|
+
</footer>
|
|
64
|
+
<script>
|
|
65
|
+
/* The inline behavior of the fixture page: the ticker timer nudges one price per tick, the
|
|
66
|
+
toast timer appends one line per tick, the console noise writes one log line and one error
|
|
67
|
+
line on load, and the network meter decays its local activity counter toward quiet. */
|
|
68
|
+
(function () {
|
|
69
|
+
"use strict";
|
|
70
|
+
var ticker = document.getElementById("ticker");
|
|
71
|
+
var toasts = document.getElementById("toasts");
|
|
72
|
+
var meter = document.getElementById("networkmeter");
|
|
73
|
+
var symbols = ticker ? ticker.querySelectorAll("span") : [];
|
|
74
|
+
var prices = [];
|
|
75
|
+
for (var index = 0; index < symbols.length; index += 1) {
|
|
76
|
+
prices.push(Number.parseFloat(symbols[index].textContent.replace("$", "")) || 0);
|
|
77
|
+
}
|
|
78
|
+
var tick = 0;
|
|
79
|
+
var timer = window.setInterval(function () {
|
|
80
|
+
tick += 1;
|
|
81
|
+
if (symbols.length > 0) {
|
|
82
|
+
var slot = tick % symbols.length;
|
|
83
|
+
var next = prices[slot] + (tick % 2 === 0 ? 0.5 : -0.5);
|
|
84
|
+
prices[slot] = next;
|
|
85
|
+
symbols[slot].setAttribute("data-direction", next >= 0 ? "up" : "down");
|
|
86
|
+
symbols[slot].textContent = "$" + next.toFixed(2);
|
|
87
|
+
}
|
|
88
|
+
if (toasts) {
|
|
89
|
+
var toast = document.createElement("div");
|
|
90
|
+
toast.className = "toast";
|
|
91
|
+
toast.setAttribute("role", "status");
|
|
92
|
+
toast.textContent = "Tick " + tick + ": the local fixture timer fired.";
|
|
93
|
+
toasts.append(toast);
|
|
94
|
+
if (toasts.children.length > 6) toasts.firstElementChild.remove();
|
|
95
|
+
}
|
|
96
|
+
}, 1200);
|
|
97
|
+
if (meter) {
|
|
98
|
+
var pending = 3;
|
|
99
|
+
var quiet = window.setInterval(function () {
|
|
100
|
+
pending = Math.max(0, pending - 1);
|
|
101
|
+
meter.textContent = "Network activity: " + pending + " pending event" + (pending === 1 ? "" : "s") + (pending === 0 ? " — quiet." : ".");
|
|
102
|
+
}, 900);
|
|
103
|
+
window.addEventListener("beforeunload", function () { window.clearInterval(timer); window.clearInterval(quiet); });
|
|
104
|
+
}
|
|
105
|
+
console.log("The fixture dashboard booted with 3 pending network events.");
|
|
106
|
+
console.error("The fixture dashboard records one deliberate console error for the console recipes.");
|
|
107
|
+
})();
|
|
108
|
+
</script>
|
|
109
|
+
</body>
|
|
110
|
+
</html>
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>Gallery fixture feed</title>
|
|
7
|
+
<meta name="description" content="The feed fixture page of the devthink example gallery: an infinite scroll feed with a sentinel, delayed card loads, search results and a nested comment tree.">
|
|
8
|
+
<!--
|
|
9
|
+
The feed fixture page of the example gallery (local origin https://feed.test).
|
|
10
|
+
Self contained static html with inline styles and inline script: the infinite scroll appends
|
|
11
|
+
local cards at the sentinel, the delayed loads reveal their cards on timers, and the search
|
|
12
|
+
results and the comment tree are static markup — no remote resource, no fetch, no storage.
|
|
13
|
+
-->
|
|
14
|
+
<style>
|
|
15
|
+
:root { color-scheme: light; font-family: system-ui, sans-serif; }
|
|
16
|
+
body { margin: 0 auto; max-width: 640px; padding: 16px; color: #17202e; }
|
|
17
|
+
header h1 { font-size: 22px; margin: 4px 0; }
|
|
18
|
+
header p { color: #56637a; margin: 4px 0 18px; }
|
|
19
|
+
.feed { display: grid; gap: 10px; margin: 12px 0 18px; }
|
|
20
|
+
.feed article { border: 1px solid #d5dce8; border-radius: 10px; padding: 12px; }
|
|
21
|
+
.feed article h3 { font-size: 15px; margin: 0 0 4px; }
|
|
22
|
+
.feed article p { margin: 2px 0; color: #56637a; font-size: 13px; }
|
|
23
|
+
.feed article[hidden] { display: none; }
|
|
24
|
+
.sentinel { height: 2px; background: #c6cedd; border-radius: 999px; margin: 18px 0 26px; }
|
|
25
|
+
.searchresults { border: 1px solid #d5dce8; border-radius: 10px; padding: 12px; margin: 12px 0 22px; }
|
|
26
|
+
.searchresults h2 { font-size: 16px; margin: 0 0 8px; }
|
|
27
|
+
.searchresults a { color: #0c6b5a; font-size: 14px; display: block; margin: 4px 0; }
|
|
28
|
+
.searchresults p { color: #56637a; font-size: 13px; margin: 2px 0; }
|
|
29
|
+
.comment-tree { margin: 10px 0 22px; padding-left: 0; list-style: none; font-size: 14px; }
|
|
30
|
+
.comment-tree ul { list-style: none; padding-left: 18px; }
|
|
31
|
+
.comment-tree li { border-left: 2px solid #d5dce8; padding: 4px 0 4px 10px; margin: 4px 0; }
|
|
32
|
+
footer p { color: #56637a; font-size: 12px; }
|
|
33
|
+
</style>
|
|
34
|
+
</head>
|
|
35
|
+
<body>
|
|
36
|
+
<header>
|
|
37
|
+
<h1>Fixture feed</h1>
|
|
38
|
+
<p>The local gallery fixture of the devthink scraping and monitoring recipes: an infinite feed, delayed loads, search results and a nested comment tree.</p>
|
|
39
|
+
</header>
|
|
40
|
+
<main>
|
|
41
|
+
<section aria-label="Infinite feed">
|
|
42
|
+
<h2>Feed</h2>
|
|
43
|
+
<div class="feed" id="feed">
|
|
44
|
+
<article><h3>Feed card 1</h3><p>The first fixture feed card loads with the page.</p></article>
|
|
45
|
+
<article><h3>Feed card 2</h3><p>The second fixture feed card loads with the page.</p></article>
|
|
46
|
+
<article data-delayed="true" hidden><h3>Feed card 3</h3><p>The third fixture feed card loads on its delay timer.</p></article>
|
|
47
|
+
<article data-delayed="true" hidden><h3>Feed card 4</h3><p>The fourth fixture feed card loads on its delay timer.</p></article>
|
|
48
|
+
</div>
|
|
49
|
+
<div class="sentinel" id="sentinel" aria-hidden="true"></div>
|
|
50
|
+
</section>
|
|
51
|
+
<section class="searchresults" aria-label="Search results">
|
|
52
|
+
<h2>Search results</h2>
|
|
53
|
+
<a href="#feed">Lumen desk lamp — fixture result</a>
|
|
54
|
+
<p>The result excerpts stay local: no query leaves the page.</p>
|
|
55
|
+
<a href="#commenttree">Harbor shelf — fixture result</a>
|
|
56
|
+
<p>Every result anchor resolves inside the same fixture document.</p>
|
|
57
|
+
</section>
|
|
58
|
+
<section aria-label="Comments">
|
|
59
|
+
<h2 id="commenttree">Comments</h2>
|
|
60
|
+
<ul class="comment-tree">
|
|
61
|
+
<li>Top comment one
|
|
62
|
+
<ul>
|
|
63
|
+
<li>Reply one
|
|
64
|
+
<ul><li>Deep reply</li></ul>
|
|
65
|
+
</li>
|
|
66
|
+
<li>Reply two</li>
|
|
67
|
+
</ul>
|
|
68
|
+
</li>
|
|
69
|
+
<li>Top comment two
|
|
70
|
+
<ul><li>Reply three</li></ul>
|
|
71
|
+
</li>
|
|
72
|
+
</ul>
|
|
73
|
+
</section>
|
|
74
|
+
</main>
|
|
75
|
+
<footer>
|
|
76
|
+
<p>Fixture page of the devthink example gallery — local origin https://feed.test, no remote resource, no fetch, no storage, no cookie.</p>
|
|
77
|
+
</footer>
|
|
78
|
+
<script>
|
|
79
|
+
/* The inline behavior of the fixture page: the delay timers reveal the delayed cards, and the
|
|
80
|
+
intersection of the sentinel with the viewport appends one new feed card per arrival, capped
|
|
81
|
+
at the reviewed card count so the page never grows unbounded. */
|
|
82
|
+
(function () {
|
|
83
|
+
"use strict";
|
|
84
|
+
var feed = document.getElementById("feed");
|
|
85
|
+
var sentinel = document.getElementById("sentinel");
|
|
86
|
+
var delayed = document.querySelectorAll("[data-delayed='true']");
|
|
87
|
+
for (var index = 0; index < delayed.length; index += 1) {
|
|
88
|
+
(function (card, delay) {
|
|
89
|
+
window.setTimeout(function () { card.hidden = false; }, delay);
|
|
90
|
+
}(delayed[index], 600 + index * 500));
|
|
91
|
+
}
|
|
92
|
+
var appended = 0;
|
|
93
|
+
var cap = 6;
|
|
94
|
+
if (sentinel && "IntersectionObserver" in window) {
|
|
95
|
+
var observer = new IntersectionObserver(function (entries) {
|
|
96
|
+
for (var entry = 0; entry < entries.length; entry += 1) {
|
|
97
|
+
if (!entries[entry].isIntersecting || appended >= cap) continue;
|
|
98
|
+
appended += 1;
|
|
99
|
+
var card = document.createElement("article");
|
|
100
|
+
var heading = document.createElement("h3");
|
|
101
|
+
heading.textContent = "Appended feed card " + appended;
|
|
102
|
+
var line = document.createElement("p");
|
|
103
|
+
line.textContent = "The infinite scroll fixture appended this card locally.";
|
|
104
|
+
card.append(heading, line);
|
|
105
|
+
feed.append(card);
|
|
106
|
+
}
|
|
107
|
+
}, { rootMargin: "64px" });
|
|
108
|
+
observer.observe(sentinel);
|
|
109
|
+
}
|
|
110
|
+
})();
|
|
111
|
+
</script>
|
|
112
|
+
</body>
|
|
113
|
+
</html>
|