dsh-mobile-plugin 0.5.27 → 0.5.29
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/extensions/catalog.json +10 -1
- package/extensions/magpie/agentcompact.js +104 -0
- package/extensions/magpie/app.css +4902 -0
- package/extensions/magpie/app.js +24474 -0
- package/extensions/magpie/autobackup.js +185 -0
- package/extensions/magpie/autoresetown.js +151 -0
- package/extensions/magpie/balfound.js +38 -0
- package/extensions/magpie/boot.js +23 -0
- package/extensions/magpie/cfgblocked.js +39 -0
- package/extensions/magpie/checkinnow.js +111 -0
- package/extensions/magpie/claudemode.js +92 -0
- package/extensions/magpie/compat.js +41 -0
- package/extensions/magpie/context.css +433 -0
- package/extensions/magpie/context.js +1576 -0
- package/extensions/magpie/ctxtext.js +223 -0
- package/extensions/magpie/fonts.js +42 -0
- package/extensions/magpie/i18n.js +18942 -0
- package/extensions/magpie/icons/ai21.svg +1 -0
- package/extensions/magpie/icons/ai302-color.svg +1 -0
- package/extensions/magpie/icons/ai360-color.svg +1 -0
- package/extensions/magpie/icons/aihubmix-color.svg +1 -0
- package/extensions/magpie/icons/aionui.svg +1 -0
- package/extensions/magpie/icons/air.svg +4 -0
- package/extensions/magpie/icons/alibaba-color.svg +1 -0
- package/extensions/magpie/icons/alibabacloud-color.svg +1 -0
- package/extensions/magpie/icons/alma.png +0 -0
- package/extensions/magpie/icons/anthropic.svg +1 -0
- package/extensions/magpie/icons/antigravity-color.svg +1 -0
- package/extensions/magpie/icons/anyscale-color.svg +1 -0
- package/extensions/magpie/icons/app-alma.png +0 -0
- package/extensions/magpie/icons/app-cc-switch.png +0 -0
- package/extensions/magpie/icons/apple-touch-icon.png +0 -0
- package/extensions/magpie/icons/aside.svg +28 -0
- package/extensions/magpie/icons/astrbot.svg +1 -0
- package/extensions/magpie/icons/atomcode.png +0 -0
- package/extensions/magpie/icons/aws.svg +1 -0
- package/extensions/magpie/icons/azure-color.svg +1 -0
- package/extensions/magpie/icons/azureai-color.svg +1 -0
- package/extensions/magpie/icons/baichuan-color.svg +1 -0
- package/extensions/magpie/icons/baidu-color.svg +1 -0
- package/extensions/magpie/icons/baiducloud-color.svg +1 -0
- package/extensions/magpie/icons/bailian-color.svg +1 -0
- package/extensions/magpie/icons/baseten.svg +1 -0
- package/extensions/magpie/icons/bedrock-color.svg +1 -0
- package/extensions/magpie/icons/burncloud-color.svg +1 -0
- package/extensions/magpie/icons/bytedance-color.svg +1 -0
- package/extensions/magpie/icons/cerebras.svg +1 -0
- package/extensions/magpie/icons/chatglm-color.svg +1 -0
- package/extensions/magpie/icons/cherryin-color.svg +1 -0
- package/extensions/magpie/icons/chutes.svg +1 -0
- package/extensions/magpie/icons/cindy.png +0 -0
- package/extensions/magpie/icons/claude-color.svg +1 -0
- package/extensions/magpie/icons/claudecode-color.svg +1 -0
- package/extensions/magpie/icons/cline.svg +1 -0
- package/extensions/magpie/icons/cloudflare-color.svg +1 -0
- package/extensions/magpie/icons/codebuddy-color.svg +1 -0
- package/extensions/magpie/icons/codex-color.svg +1 -0
- package/extensions/magpie/icons/cohere-color.svg +1 -0
- package/extensions/magpie/icons/cometapi-color.svg +1 -0
- package/extensions/magpie/icons/commandcode.svg +1 -0
- package/extensions/magpie/icons/copilot-color.svg +1 -0
- package/extensions/magpie/icons/coze.svg +1 -0
- package/extensions/magpie/icons/crush.png +0 -0
- package/extensions/magpie/icons/curl.svg +1 -0
- package/extensions/magpie/icons/cursor.svg +1 -0
- package/extensions/magpie/icons/deepinfra-color.svg +1 -0
- package/extensions/magpie/icons/deepmind-color.svg +1 -0
- package/extensions/magpie/icons/deepseek-color.svg +1 -0
- package/extensions/magpie/icons/deveco-color.svg +12 -0
- package/extensions/magpie/icons/devin.svg +1 -0
- package/extensions/magpie/icons/doubao-color.svg +1 -0
- package/extensions/magpie/icons/empryo-color.svg +1 -0
- package/extensions/magpie/icons/factory.svg +1 -0
- package/extensions/magpie/icons/favicon.svg +1 -0
- package/extensions/magpie/icons/featherless-color.svg +1 -0
- package/extensions/magpie/icons/fireworks-color.svg +1 -0
- package/extensions/magpie/icons/friendli.svg +1 -0
- package/extensions/magpie/icons/fx.svg +1 -0
- package/extensions/magpie/icons/gemini-color.svg +1 -0
- package/extensions/magpie/icons/geminicli-color.svg +1 -0
- package/extensions/magpie/icons/gemma-color.svg +1 -0
- package/extensions/magpie/icons/giteeai.svg +1 -0
- package/extensions/magpie/icons/github.svg +1 -0
- package/extensions/magpie/icons/githubcopilot.svg +1 -0
- package/extensions/magpie/icons/glama.svg +1 -0
- package/extensions/magpie/icons/gmicloud.svg +1 -0
- package/extensions/magpie/icons/google-color.svg +1 -0
- package/extensions/magpie/icons/googlecloud-color.svg +1 -0
- package/extensions/magpie/icons/goose.svg +1 -0
- package/extensions/magpie/icons/grok.svg +1 -0
- package/extensions/magpie/icons/groq.svg +1 -0
- package/extensions/magpie/icons/hanako.png +0 -0
- package/extensions/magpie/icons/hermes.svg +1 -0
- package/extensions/magpie/icons/huawei-color.svg +1 -0
- package/extensions/magpie/icons/huaweicloud-color.svg +1 -0
- package/extensions/magpie/icons/huggingface-color.svg +1 -0
- package/extensions/magpie/icons/hunyuan-color.svg +1 -0
- package/extensions/magpie/icons/hyperbolic-color.svg +1 -0
- package/extensions/magpie/icons/icon-512.png +0 -0
- package/extensions/magpie/icons/iflytekcloud-color.svg +1 -0
- package/extensions/magpie/icons/inference.svg +1 -0
- package/extensions/magpie/icons/infinigence-color.svg +1 -0
- package/extensions/magpie/icons/internlm-color.svg +1 -0
- package/extensions/magpie/icons/jcode.svg +1 -0
- package/extensions/magpie/icons/jina.svg +1 -0
- package/extensions/magpie/icons/kilo.svg +1 -0
- package/extensions/magpie/icons/kilocode.svg +1 -0
- package/extensions/magpie/icons/kimi-code-desktop.png +0 -0
- package/extensions/magpie/icons/kimi.svg +1 -0
- package/extensions/magpie/icons/kiro-color.svg +1 -0
- package/extensions/magpie/icons/kling-color.svg +1 -0
- package/extensions/magpie/icons/lambda.svg +1 -0
- package/extensions/magpie/icons/leptonai-color.svg +1 -0
- package/extensions/magpie/icons/liquid.svg +1 -0
- package/extensions/magpie/icons/liveagent-color.svg +1 -0
- package/extensions/magpie/icons/llmapi.svg +1 -0
- package/extensions/magpie/icons/lmstudio.svg +1 -0
- package/extensions/magpie/icons/lody.png +0 -0
- package/extensions/magpie/icons/longcat.svg +1 -0
- package/extensions/magpie/icons/magpie.svg +1 -0
- package/extensions/magpie/icons/meta-color.svg +1 -0
- package/extensions/magpie/icons/metaai-color.svg +1 -0
- package/extensions/magpie/icons/microsoft-color.svg +1 -0
- package/extensions/magpie/icons/mimocode.svg +1 -0
- package/extensions/magpie/icons/minimax-color.svg +1 -0
- package/extensions/magpie/icons/mistermorph-color.svg +1 -0
- package/extensions/magpie/icons/mistral-color.svg +1 -0
- package/extensions/magpie/icons/mlx-serve.png +0 -0
- package/extensions/magpie/icons/modelscope-color.svg +1 -0
- package/extensions/magpie/icons/monica-color.svg +1 -0
- package/extensions/magpie/icons/moonshot.svg +1 -0
- package/extensions/magpie/icons/morph-color.svg +1 -0
- package/extensions/magpie/icons/mothx.png +0 -0
- package/extensions/magpie/icons/nebius.svg +1 -0
- package/extensions/magpie/icons/newapi-color.svg +1 -0
- package/extensions/magpie/icons/nousresearch.svg +1 -0
- package/extensions/magpie/icons/nova-color.svg +1 -0
- package/extensions/magpie/icons/novita-color.svg +1 -0
- package/extensions/magpie/icons/nvidia-color.svg +1 -0
- package/extensions/magpie/icons/ollama.svg +1 -0
- package/extensions/magpie/icons/omlx.svg +1 -0
- package/extensions/magpie/icons/omo.svg +1 -0
- package/extensions/magpie/icons/omp.svg +1 -0
- package/extensions/magpie/icons/openai.svg +1 -0
- package/extensions/magpie/icons/openchamber.svg +11 -0
- package/extensions/magpie/icons/openclaw.svg +1 -0
- package/extensions/magpie/icons/opencode.svg +1 -0
- package/extensions/magpie/icons/openmausbot.svg +1 -0
- package/extensions/magpie/icons/openrouter.svg +1 -0
- package/extensions/magpie/icons/openwebui.svg +1 -0
- package/extensions/magpie/icons/parasail.svg +1 -0
- package/extensions/magpie/icons/penguin-color.svg +1 -0
- package/extensions/magpie/icons/perplexity-color.svg +1 -0
- package/extensions/magpie/icons/phala-color.svg +1 -0
- package/extensions/magpie/icons/pi.svg +1 -0
- package/extensions/magpie/icons/pipellm-color.svg +5 -0
- package/extensions/magpie/icons/poe.svg +1 -0
- package/extensions/magpie/icons/ppio-color.svg +1 -0
- package/extensions/magpie/icons/prime-agent.svg +1 -0
- package/extensions/magpie/icons/qiniu-color.svg +1 -0
- package/extensions/magpie/icons/qoder.svg +1 -0
- package/extensions/magpie/icons/qwen-color.svg +1 -0
- package/extensions/magpie/icons/qwenpaw.svg +1 -0
- package/extensions/magpie/icons/raycast.svg +1 -0
- package/extensions/magpie/icons/reasonix-color.svg +48 -0
- package/extensions/magpie/icons/reka.svg +1 -0
- package/extensions/magpie/icons/replicate.svg +1 -0
- package/extensions/magpie/icons/roocode.svg +1 -0
- package/extensions/magpie/icons/rtk.png +0 -0
- package/extensions/magpie/icons/sambanova-color.svg +1 -0
- package/extensions/magpie/icons/sensenova-color.svg +1 -0
- package/extensions/magpie/icons/siliconcloud-color.svg +1 -0
- package/extensions/magpie/icons/snow.png +0 -0
- package/extensions/magpie/icons/snowflake-color.svg +1 -0
- package/extensions/magpie/icons/sophnet-color.svg +1 -0
- package/extensions/magpie/icons/spark-color.svg +1 -0
- package/extensions/magpie/icons/stepfun-color.svg +1 -0
- package/extensions/magpie/icons/straico-color.svg +1 -0
- package/extensions/magpie/icons/submodel-color.svg +1 -0
- package/extensions/magpie/icons/t3code.svg +4 -0
- package/extensions/magpie/icons/targon-color.svg +1 -0
- package/extensions/magpie/icons/tencent-color.svg +1 -0
- package/extensions/magpie/icons/tencentcloud-color.svg +1 -0
- package/extensions/magpie/icons/tiangong-color.svg +1 -0
- package/extensions/magpie/icons/together-color.svg +1 -0
- package/extensions/magpie/icons/trae.svg +1 -0
- package/extensions/magpie/icons/typesafe.png +0 -0
- package/extensions/magpie/icons/upstage-color.svg +1 -0
- package/extensions/magpie/icons/venice-color.svg +1 -0
- package/extensions/magpie/icons/vercel.svg +1 -0
- package/extensions/magpie/icons/vertexai-color.svg +1 -0
- package/extensions/magpie/icons/vllm-color.svg +1 -0
- package/extensions/magpie/icons/volcengine-color.svg +1 -0
- package/extensions/magpie/icons/voyage-color.svg +1 -0
- package/extensions/magpie/icons/vscode.svg +1 -0
- package/extensions/magpie/icons/wenxin-color.svg +1 -0
- package/extensions/magpie/icons/windsurf.svg +1 -0
- package/extensions/magpie/icons/workbuddy-color.svg +1 -0
- package/extensions/magpie/icons/workersai-color.svg +1 -0
- package/extensions/magpie/icons/xai.svg +1 -0
- package/extensions/magpie/icons/xiaomimimo.svg +1 -0
- package/extensions/magpie/icons/xinference-color.svg +1 -0
- package/extensions/magpie/icons/yi.svg +1 -0
- package/extensions/magpie/icons/yuanbao-color.svg +1 -0
- package/extensions/magpie/icons/yylx.svg +5 -0
- package/extensions/magpie/icons/zai.svg +1 -0
- package/extensions/magpie/icons/zcode.png +0 -0
- package/extensions/magpie/icons/zed.svg +1 -0
- package/extensions/magpie/icons/zenmux.svg +1 -0
- package/extensions/magpie/icons/zeroone.svg +1 -0
- package/extensions/magpie/icons/zhipu-color.svg +1 -0
- package/extensions/magpie/icons/zoo-code.png +0 -0
- package/extensions/magpie/index.html +572 -0
- package/extensions/magpie/kernel.js +274 -0
- package/extensions/magpie/keyspend.js +63 -0
- package/extensions/magpie/layout.js +26 -0
- package/extensions/magpie/manifest.json +37 -0
- package/extensions/magpie/navlamp.js +47 -0
- package/extensions/magpie/omarchy.css +72 -0
- package/extensions/magpie/omarchy.js +46 -0
- package/extensions/magpie/passerrors.js +21 -0
- package/extensions/magpie/pluginpages.js +405 -0
- package/extensions/magpie/plugins/gateway/client.js +1194 -0
- package/extensions/magpie/plugins/gateway/keys.js +972 -0
- package/extensions/magpie/plugins/gateway/settings.js +459 -0
- package/extensions/magpie/plugins/library/client.js +6788 -0
- package/extensions/magpie/plugins/library/i18n.js +3228 -0
- package/extensions/magpie/plugins/library/library.css +848 -0
- package/extensions/magpie/plugins/otel/i18n.js +48 -0
- package/extensions/magpie/plugins/otel/page.js +189 -0
- package/extensions/magpie/plugins/redact/client.js +46 -0
- package/extensions/magpie/plugins/redact/demo.js +461 -0
- package/extensions/magpie/plugins/redact/i18n.js +432 -0
- package/extensions/magpie/plugins/redact/page.js +985 -0
- package/extensions/magpie/plugins/redact/redact.css +385 -0
- package/extensions/magpie/plugins/redact/settings.js +248 -0
- package/extensions/magpie/plugins/redact/xq_dark.jpg +0 -0
- package/extensions/magpie/plugins/redact/xq_dark.webm +0 -0
- package/extensions/magpie/plugins/redact/xq_light.jpg +0 -0
- package/extensions/magpie/plugins/redact/xq_light.webm +0 -0
- package/extensions/magpie/plugins/routing/client.js +8593 -0
- package/extensions/magpie/plugins/routing/i18n.js +2696 -0
- package/extensions/magpie/plugins/routing/routing.css +1111 -0
- package/extensions/magpie/plugins/search/settings.js +460 -0
- package/extensions/magpie/plugins/sessions/client.js +1622 -0
- package/extensions/magpie/plugins/sessions/i18n.js +360 -0
- package/extensions/magpie/plugins/sessions/sessions.css +181 -0
- package/extensions/magpie/plugins/sessions/settings.js +100 -0
- package/extensions/magpie/plugins/usage/client.js +150 -0
- package/extensions/magpie/plugins/usage/i18n.js +984 -0
- package/extensions/magpie/plugins/usage/page.js +4307 -0
- package/extensions/magpie/plugins.css +319 -0
- package/extensions/magpie/plugins.js +1926 -0
- package/extensions/magpie/profilerename.js +27 -0
- package/extensions/magpie/purposes.js +2 -0
- package/extensions/magpie/quitask.js +79 -0
- package/extensions/magpie/quotaall.js +142 -0
- package/extensions/magpie/ratewindows.js +123 -0
- package/extensions/magpie/registry.js +87 -0
- package/extensions/magpie/resetask.js +137 -0
- package/extensions/magpie/ruleedit.js +53 -0
- package/extensions/magpie/scrollends.js +91 -0
- package/extensions/magpie/settingshints.js +75 -0
- package/extensions/magpie/signsheets.js +86 -0
- package/extensions/magpie/start.js +26 -0
- package/extensions/magpie/syncfiles.js +216 -0
- package/extensions/magpie/theme.js +29 -0
- package/extensions/magpie/traywindows.js +41 -0
- package/extensions/magpie/webfonts.js +130 -0
- package/extensions/magpie/whatsnew.js +252 -0
- package/extensions/magpie.zip +0 -0
- package/knowledge/apps/baijiahao.md +30 -0
- package/knowledge/apps/bilibili.md +1 -0
- package/knowledge/apps/dedao.md +33 -0
- package/knowledge/apps/feishu.md +35 -0
- package/knowledge/apps/gmail.md +33 -0
- package/knowledge/apps/jike.md +31 -0
- package/knowledge/apps/kuaishou.md +34 -0
- package/knowledge/apps/linkedin.md +33 -0
- package/knowledge/apps/qqmail.md +30 -0
- package/knowledge/apps/telegram.md +27 -0
- package/knowledge/apps/toutiao.md +32 -0
- package/knowledge/apps/uber-eats.md +26 -0
- package/knowledge/apps/uber.md +27 -0
- package/knowledge/apps/whatsapp.md +27 -0
- package/knowledge/apps/x.md +32 -0
- package/knowledge/apps/xiaohongshu.md +1 -0
- package/knowledge/apps/zhihu.md +1 -0
- package/knowledge/sites/bilibili.com/SITE.md +50 -0
- package/knowledge/sites/bilibili.com/references/view-fields.md +17 -0
- package/knowledge/sites/github.com/SITE.md +51 -0
- package/knowledge/sites/github.com/references/repo-fields.md +13 -0
- package/knowledge/sites/x.com/SITE.md +47 -0
- package/knowledge/sites/xiaohongshu.com/SITE.md +48 -0
- package/knowledge/sites/xiaohongshu.com/references/note-fields.md +18 -0
- package/knowledge/sites/youtube.com/SITE.md +49 -0
- package/knowledge/sites/youtube.com/references/innertube-fields.md +14 -0
- package/knowledge/sites/zhihu.com/SITE.md +47 -0
- package/knowledge/sites/zhihu.com/references/api-fields.md +21 -0
- package/lib/mobile/extension-catalog.js +41 -1
- package/lib/mobile/extension-data.js +224 -0
- package/lib/mobile/extension-dev.js +3 -1
- package/lib/mobile/file-ops.js +252 -0
- package/lib/mobile/history-derive.js +80 -4
- package/lib/mobile/host-bridge/endpoints.js +48 -6
- package/lib/mobile/host-bridge/event-synthesis.js +33 -6
- package/lib/mobile/jobs-control.js +118 -0
- package/lib/mobile/memory.js +635 -0
- package/lib/mobile/model-badges.js +77 -0
- package/lib/mobile/pet-tools.js +50 -4
- package/lib/mobile/pets.js +214 -18
- package/lib/mobile/phone/access.js +85 -0
- package/lib/mobile/phone/evidence.js +228 -0
- package/lib/mobile/phone/gate.js +290 -0
- package/lib/mobile/phone/grounding.js +314 -0
- package/lib/mobile/phone/guard.js +465 -0
- package/lib/mobile/phone/irreversible.js +88 -8
- package/lib/mobile/phone/knowledge-tools.js +665 -0
- package/lib/mobile/phone/learn.js +329 -0
- package/lib/mobile/phone/redact.js +237 -0
- package/lib/mobile/phone/risk.js +95 -0
- package/lib/mobile/phone/routes.js +28 -1
- package/lib/mobile/phone/service.js +23 -0
- package/lib/mobile/phone/site-commands.js +271 -0
- package/lib/mobile/phone/step-summary.js +201 -0
- package/lib/mobile/phone/stop-summary.js +235 -0
- package/lib/mobile/phone/tool-groups.js +125 -0
- package/lib/mobile/phone/tools.js +589 -0
- package/lib/mobile/phone/untrusted.js +65 -0
- package/lib/mobile/phone-knowledge.js +20 -4
- package/lib/mobile/schedule-edit.js +500 -0
- package/lib/mobile/schedule.js +5 -0
- package/lib/mobile/send-continuation.js +52 -0
- package/lib/mobile/session-models.js +27 -31
- package/lib/mobile/session-send.js +12 -12
- package/lib/mobile/skill-import.js +338 -0
- package/lib/mobile/skill-store.js +524 -0
- package/lib/routes/mobile-api.js +42 -0
- package/package.json +7 -6
- package/skills/skill-authoring/SKILL.md +46 -0
- package/skills/skill-authoring/references/examples.md +82 -0
- package/skills/skill-authoring/references/frontmatter-and-fields.md +46 -0
- package/skills/skill-authoring/scripts/check_skill.py +167 -0
- package/skills/skill-authoring/scripts/manifest.json +3 -0
- package/knowledge/sites/bilibili.com.md +0 -27
- package/knowledge/sites/github.com.md +0 -27
- package/knowledge/sites/x.com.md +0 -28
- package/knowledge/sites/xiaohongshu.com.md +0 -27
- package/knowledge/sites/youtube.com.md +0 -28
- package/knowledge/sites/zhihu.com.md +0 -27
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
// Insights › Masking's demo (client.js has its row): 小鹊 takes one request
|
|
2
|
+
// through masking. The letter comes to her before it leaves this computer,
|
|
3
|
+
// she tears the password and the email out into her notebook and sticks
|
|
4
|
+
// placeholders in their place, the provider answers with placeholders, and
|
|
5
|
+
// she puts the values back before the browser on this computer signs in.
|
|
6
|
+
//
|
|
7
|
+
// She is a video, one take per theme, drawn on the card's own colour; the
|
|
8
|
+
// sheets, the notebook and the boxes are the page's, moved over her. The
|
|
9
|
+
// video is fetched when the row is first opened, never before. It plays
|
|
10
|
+
// while the row is open and on screen, once through, and rests on its last
|
|
11
|
+
// step; with reduced motion it starts there. Folded, or with the page left,
|
|
12
|
+
// the video lets its source go, so a folded demo holds no decoder or
|
|
13
|
+
// buffered video. Where the app's webview would crash on a video
|
|
14
|
+
// (bootPrefs.noVideo: Linux without gst-plugins-good), she is her poster.
|
|
15
|
+
|
|
16
|
+
const XQD_END = 27.5;
|
|
17
|
+
const XQD_STEPS = [
|
|
18
|
+
[0, "You send a request", "You ask the agent to sign in to github.com with a password. The letter goes to Little Magpie first and hasn't left your computer yet."],
|
|
19
|
+
[5.4, "She reads it through", "Little Magpie opens the letter and reads it line by line for what shouldn't be sent: emails, passwords."],
|
|
20
|
+
[7.8, "Tearing out what's private", "The real values are torn out and written in her notebook. The notebook stays on your computer."],
|
|
21
|
+
[10.6, "Sticking on placeholders", "Placeholders fill the gaps. A value always gets the same one, so the model still understands."],
|
|
22
|
+
[12.8, "Off to the provider", "The letter is sealed and sent. The provider gets only placeholders."],
|
|
23
|
+
[15.6, "The answer calls the browser", "The model decides to call browser.login, and its arguments are placeholders too."],
|
|
24
|
+
[18.8, "Putting the values back", "When the answer comes, Little Magpie opens her notebook and turns each placeholder back into its value."],
|
|
25
|
+
[22.2, "Signing in on your computer", "The tool signs in with the real password on your computer. The password never left it."],
|
|
26
|
+
];
|
|
27
|
+
const XQD_MAIL = "yetone@example.com", XQD_PASS = "hunter2-prod";
|
|
28
|
+
const XQD_PM = "{{EMAIL_r3tvb2ce}}", XQD_PP = "{{PASSWORD_k7q2m4xa}}";
|
|
29
|
+
|
|
30
|
+
let xqd = null; // the demo, once its row has been opened
|
|
31
|
+
|
|
32
|
+
document.addEventListener("click", (e) => {
|
|
33
|
+
if (e.target.closest?.("#mdRow")) maskDemoToggle();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
function maskDemoToggle() {
|
|
37
|
+
const row = $("#mdRow"), body = $("#mdBody");
|
|
38
|
+
if (!row || !body) return;
|
|
39
|
+
const open = row.getAttribute("aria-expanded") !== "true";
|
|
40
|
+
row.setAttribute("aria-expanded", String(open));
|
|
41
|
+
body.hidden = !open;
|
|
42
|
+
// the kernel can draw the page again, and an old demo goes with it
|
|
43
|
+
if (open && !xqd?.root.isConnected) xqd = maskDemoBuild(body);
|
|
44
|
+
xqd?.wake();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// What a label says: data-t, so a change of language says it again.
|
|
48
|
+
const xqdSay = (cls, text) => { const s = el("span", cls, t(text)); s.dataset.t = ""; s.dataset.en = text; return s; };
|
|
49
|
+
const xqdIcon = {
|
|
50
|
+
you: '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><circle cx="12" cy="8" r="4"/><path d="M4 21c1.5-4 4.5-6 8-6s6.5 2 8 6"/></svg>',
|
|
51
|
+
book: '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 4h11a3 3 0 0 1 3 3v13H8a3 3 0 0 1-3-3z"/><path d="M5 17a3 3 0 0 1 3-3h11"/></svg>',
|
|
52
|
+
lock: '<svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round"><rect x="5" y="11" width="14" height="10" rx="2"/><path d="M8 11V8a4 4 0 0 1 8 0v3"/></svg>',
|
|
53
|
+
check: (n) => `<svg width="${n}" height="${n}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>`,
|
|
54
|
+
server: '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="7" rx="2"/><rect x="4" y="13" width="16" height="7" rx="2"/><path d="M8 7.5h.01M8 16.5h.01"/></svg>',
|
|
55
|
+
env: (seal) => `<svg class="xq-env xq-mv" viewBox="-2 -1 82 58" aria-hidden="true"><rect x="1.5" y="4" width="76" height="50" rx="6" fill="var(--xq-env-shadow)"/><rect x="1" y="1" width="76" height="50" rx="5" fill="var(--xq-paper)" stroke="var(--faint)" stroke-width="1.2"/><path d="M2 3l37 26L76 3" fill="none" stroke="var(--faint)" stroke-width="1.2"/><circle cx="39" cy="28" r="8" fill="${seal}"/><circle cx="39" cy="28" r="4.5" fill="none" stroke="#fff" stroke-opacity=".5" stroke-width="1.3"/></svg>`,
|
|
56
|
+
pause: '<svg width="12" height="12" viewBox="0 0 12 12"><rect x="2" y="1.5" width="2.6" height="9" rx="1" fill="currentColor"/><rect x="7.4" y="1.5" width="2.6" height="9" rx="1" fill="currentColor"/></svg>',
|
|
57
|
+
play: '<svg width="12" height="12" viewBox="0 0 12 12"><path d="M3 1.8v8.4a.6.6 0 0 0 .9.5l7-4.2a.6.6 0 0 0 0-1L3.9 1.3a.6.6 0 0 0-.9.5z" fill="currentColor"/></svg>',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// The stage is drawn at 1000×420 and scaled to the card's width.
|
|
61
|
+
function maskDemoStage() {
|
|
62
|
+
const stage = el("div", "xq-stage");
|
|
63
|
+
const at = (node, k) => { node.dataset.k = k; return node; };
|
|
64
|
+
const box = (k, icon, title, sub) => {
|
|
65
|
+
const b = at(el("div", "xq-box"), k), h = el("div", "xq-bh"), ic = el("div", "xq-ic" + (icon === "book" ? " xq-acc" : ""));
|
|
66
|
+
ic.innerHTML = xqdIcon[icon];
|
|
67
|
+
const words = el("div");
|
|
68
|
+
words.append(xqdSay("xq-h", title));
|
|
69
|
+
if (sub) words.append(xqdSay("xq-sub", sub));
|
|
70
|
+
h.append(ic, words);
|
|
71
|
+
b.append(h);
|
|
72
|
+
return b;
|
|
73
|
+
};
|
|
74
|
+
const lane = (x, far, text) => { const l = el("div", "xq-lane"); l.style.left = x + "px"; l.append(el("span", "xq-dot" + (far ? " far" : "")), xqdSay("", text)); return l; };
|
|
75
|
+
const divider = el("div", "xq-divider");
|
|
76
|
+
divider.append(xqdSay("", "Over the network"));
|
|
77
|
+
|
|
78
|
+
const req = box("req", "you", "You tell the agent");
|
|
79
|
+
req.append(at(el("div", "xq-msg"), "reqmsg"));
|
|
80
|
+
|
|
81
|
+
const ledger = box("ledger", "book", "Little Magpie's notebook", "Kept on your computer only");
|
|
82
|
+
ledger.append(at(xqdSay("xq-lempty", "Nothing in it yet"), "lempty"));
|
|
83
|
+
// the entries follow the header, so a taller font moves them down with it
|
|
84
|
+
const entries = el("div", "xq-llist");
|
|
85
|
+
[[XQD_PM, XQD_MAIL], [XQD_PP, XQD_PASS]].forEach(([code, real], i) => {
|
|
86
|
+
const r = at(el("div", "xq-lrow xq-mv"), "row" + i);
|
|
87
|
+
r.append(el("span", "xq-lc xq-pillc", code), el("span", "xq-lv", real));
|
|
88
|
+
entries.append(r);
|
|
89
|
+
});
|
|
90
|
+
ledger.append(entries);
|
|
91
|
+
|
|
92
|
+
const browser = at(el("div", "xq-box"), "browser"), bar = el("div", "xq-bbar");
|
|
93
|
+
bar.innerHTML = `<i></i><i></i><i></i><div class="xq-url">${xqdIcon.lock}github.com/login</div>`;
|
|
94
|
+
const form = el("div", "xq-bform"), fu = at(el("div", "xq-field"), "fu"), fp = at(el("div", "xq-field"), "fp"), btn = el("div", "xq-bbtn");
|
|
95
|
+
btn.append(xqdSay("", "Sign in"));
|
|
96
|
+
form.append(fu, fp, btn);
|
|
97
|
+
const ok = at(el("div", "xq-bok xq-mv"), "bok"), chk = el("div", "xq-chk");
|
|
98
|
+
chk.innerHTML = xqdIcon.check(16);
|
|
99
|
+
ok.append(chk, xqdSay("", "Signed in to github.com"), xqdSay("xq-sub", "The tool runs on your computer"));
|
|
100
|
+
browser.append(bar, form, ok);
|
|
101
|
+
|
|
102
|
+
const xq = at(el("div", "xq-her"), "her");
|
|
103
|
+
xq.setAttribute("aria-hidden", "true");
|
|
104
|
+
const vid = el("video");
|
|
105
|
+
vid.muted = true; vid.playsInline = true; vid.preload = "auto";
|
|
106
|
+
vid.setAttribute("muted", ""); vid.setAttribute("playsinline", "");
|
|
107
|
+
xq.append(vid);
|
|
108
|
+
|
|
109
|
+
const slot = (k, real, code, hole, ok) => {
|
|
110
|
+
const s = at(el("span", "xq-slot"), k);
|
|
111
|
+
if (hole) s.append(el("span", "xq-hole"));
|
|
112
|
+
s.append(el("span", "xq-real xq-mv" + (ok ? " ok" : ""), real), el("span", "xq-code xq-mv", code));
|
|
113
|
+
return s;
|
|
114
|
+
};
|
|
115
|
+
const line = (label, ...rest) => { const l = el("div", "xq-ln"); if (label) l.append(xqdSay("xq-k", label)); l.append(...rest); return l; };
|
|
116
|
+
const paper = (k, head, side) => { const p = at(el("div", "xq-paper xq-mv"), k), ph = el("div", "xq-ph"); ph.append(xqdSay("", head), xqdSay("", side)); p.append(ph); return p; };
|
|
117
|
+
const letter = paper("letter", "Letter to the provider", "Agent → model");
|
|
118
|
+
letter.append(line("The job", xqdSay("", "Sign in to github.com")), line("Account", slot("s0", XQD_MAIL, XQD_PM, true)), line("Password", slot("s1", XQD_PASS, XQD_PP, true)));
|
|
119
|
+
const reply = paper("reply", "The provider's answer", "Calls a tool"), fn = el("div", "xq-ln");
|
|
120
|
+
fn.append(el("span", "xq-fn", "browser.login"), el("span", "xq-arg", "url: github.com"));
|
|
121
|
+
reply.append(fn, line("", el("span", "xq-k", "username"), slot("r0", XQD_MAIL, XQD_PM, false, true)), line("", el("span", "xq-k", "password"), slot("r1", XQD_PASS, XQD_PP, false, true)));
|
|
122
|
+
|
|
123
|
+
const prov = box("prov", "server", "Model service", "Remote, not on your computer");
|
|
124
|
+
const pin = at(el("div", "xq-psec xq-mv"), "pin");
|
|
125
|
+
const pline = (...n) => { const p = el("div", "xq-pline"); p.append(...n); return p; };
|
|
126
|
+
pin.append(xqdSay("xq-cap", "Letter it got"), pline(xqdSay("", "Sign in to github.com")), pline(xqdSay("", "Account"), el("span", "xq-pillc", XQD_PM)), pline(xqdSay("", "Password"), el("span", "xq-pillc", XQD_PP)));
|
|
127
|
+
const pout = at(el("div", "xq-psec xq-mv"), "pout"), think = at(el("div", "xq-think"), "think");
|
|
128
|
+
think.append(el("i", "xq-mv"), el("i", "xq-mv"), el("i", "xq-mv"));
|
|
129
|
+
const pcode = at(el("div", "xq-pcode xq-mv"), "pcode");
|
|
130
|
+
pcode.append("browser.login(", el("div", "xq-ind", "username:"), el("span", "xq-pillc", XQD_PM), el("div", "xq-ind", "password:"), el("span", "xq-pillc", XQD_PP), el("div", "", ")"));
|
|
131
|
+
pout.append(xqdSay("xq-cap", "Reply it wrote"), think, pcode);
|
|
132
|
+
const pnote = at(el("div", "xq-pnote xq-mv"), "pnote");
|
|
133
|
+
pnote.innerHTML = xqdIcon.check(12);
|
|
134
|
+
pnote.append(xqdSay("", "It saw only placeholders, never your password or email."));
|
|
135
|
+
prov.append(pin, pout, pnote);
|
|
136
|
+
|
|
137
|
+
stage.append(lane(18, false, "Your computer"), lane(802, true, "Provider"), divider, req, ledger, browser, xq, letter, reply, prov);
|
|
138
|
+
stage.insertAdjacentHTML("beforeend", xqdIcon.env("#3c63d8") + xqdIcon.env("#d6a03c"));
|
|
139
|
+
const [envA, envB] = stage.querySelectorAll(".xq-env");
|
|
140
|
+
at(envA, "envA"); at(envB, "envB");
|
|
141
|
+
const fly = (k, cls, text) => { const f = at(el("span", "xq-flyer xq-mv"), k); f.append(el("span", "xq-code " + cls, text)); return f; };
|
|
142
|
+
stage.append(fly("f0", "torn", XQD_MAIL), fly("f1", "torn", XQD_PASS), fly("g0", "back", XQD_MAIL), fly("g1", "back", XQD_PASS));
|
|
143
|
+
for (let i = 0; i < 6; i++) stage.append(at(el("i", "xq-trail xq-mv"), "t" + i));
|
|
144
|
+
return stage;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function maskDemoBuild(body) {
|
|
148
|
+
const root = el("div", "xqd"), vp = el("div", "xq-vp"), stage = maskDemoStage();
|
|
149
|
+
vp.append(stage);
|
|
150
|
+
const foot = el("div", "xq-foot"), capT = el("div", "xq-cap-t"), capB = el("div", "xq-cap-b");
|
|
151
|
+
foot.append(capT, capB);
|
|
152
|
+
const ctrl = el("div", "xq-ctrl"), play = el("button", "xq-play"), scrub = el("input", "xq-scrub");
|
|
153
|
+
play.type = "button";
|
|
154
|
+
scrub.type = "range"; scrub.min = "0"; scrub.max = "1000"; scrub.value = "0";
|
|
155
|
+
ctrl.append(play, scrub);
|
|
156
|
+
const steps = el("div", "xq-steps");
|
|
157
|
+
root.append(vp, foot, ctrl, steps);
|
|
158
|
+
body.append(root);
|
|
159
|
+
|
|
160
|
+
const q = (k) => stage.querySelector(`[data-k="${k}"]`);
|
|
161
|
+
const clamp = (v, a = 0, b = 1) => Math.min(b, Math.max(a, v));
|
|
162
|
+
const seg = (t, a, b) => clamp((t - a) / (b - a));
|
|
163
|
+
const lerp = (a, b, k) => a + (b - a) * k;
|
|
164
|
+
const eo = (k) => 1 - Math.pow(1 - k, 3);
|
|
165
|
+
const eio = (k) => k < .5 ? 4 * k * k * k : 1 - Math.pow(-2 * k + 2, 3) / 2;
|
|
166
|
+
const f2 = (v) => Math.round(v * 100) / 100;
|
|
167
|
+
// style writes are skipped when the value is the same, so a frame
|
|
168
|
+
// touches only what moved, and nothing per frame reads layout
|
|
169
|
+
const css = (e, prop, v) => { const c = e._c || (e._c = {}); if (c[prop] === v) return; c[prop] = v; e.style[prop] = v; };
|
|
170
|
+
const cls = (e, name, on) => { const c = e._c || (e._c = {}), k = "c:" + name; if (c[k] === on) return; c[k] = on; e.classList.toggle(name, on); };
|
|
171
|
+
const op = (e, v) => css(e, "opacity", String(f2(v)));
|
|
172
|
+
|
|
173
|
+
// her hands rest on the desk here in the video: sheets are handed to and
|
|
174
|
+
// taken from these points
|
|
175
|
+
const DESK = { L: [478, 262], R: [542, 262] };
|
|
176
|
+
const held = (side, tilt) => [DESK[side][0], DESK[side][1], tilt];
|
|
177
|
+
const ENV = .82, REQ = [126, 96], PROV = [893, 110], PROV_OUT = [860, 250], BROWSER = [126, 340], LC = [510, 333];
|
|
178
|
+
const OPEN = [510, 256, 0], SEND = held("R", -14), CATCH2 = held("R", -16);
|
|
179
|
+
|
|
180
|
+
const slots = {};
|
|
181
|
+
for (const k of ["s0", "s1", "r0", "r1"]) {
|
|
182
|
+
const s = q(k);
|
|
183
|
+
slots[k] = { el: s, real: s.querySelector(".xq-real"), code: s.querySelector(".xq-code"), hole: s.querySelector(".xq-hole") };
|
|
184
|
+
}
|
|
185
|
+
const flyers = ["f0", "f1", "g0", "g1"].map(q), rows = [q("row0"), q("row1")], rowCodes = rows.map((r) => r.querySelector(".xq-lc"));
|
|
186
|
+
const trails = [0, 1, 2, 3, 4, 5].map((i) => q("t" + i)), thinkDots = [...q("think").children];
|
|
187
|
+
const e = {};
|
|
188
|
+
for (const k of ["envA", "envB", "letter", "reply", "reqmsg", "lempty", "pin", "pout", "think", "pcode", "pnote", "fu", "fp", "bok"]) e[k] = q(k);
|
|
189
|
+
|
|
190
|
+
// stage-space box from offsets, which ignore transforms, so a sheet is
|
|
191
|
+
// measured where it lies on the desk whatever frame it is drawn in
|
|
192
|
+
const at = (n0) => { let x = 0, y = 0, n = n0; while (n && n !== stage) { x += n.offsetLeft; y += n.offsetTop; n = n.offsetParent; } return { x, y, w: n0.offsetWidth, h: n0.offsetHeight }; };
|
|
193
|
+
const LV = [];
|
|
194
|
+
const measure = () => {
|
|
195
|
+
for (const s of Object.values(slots)) {
|
|
196
|
+
s.rw = s.real.offsetWidth; s.cw = s.code.offsetWidth;
|
|
197
|
+
const b = at(s.el); s.x = b.x; s.y = b.y + b.h / 2;
|
|
198
|
+
if (s.el._c) delete s.el._c.width;
|
|
199
|
+
}
|
|
200
|
+
flyers.forEach((f) => { f._w = f.offsetWidth; f._h = f.offsetHeight; if (f._c) delete f._c.transform; });
|
|
201
|
+
rows.forEach((r, i) => { const b = at(r.querySelector(".xq-lv")); LV[i] = [b.x + Math.min(b.w, 130) / 2 + 6, b.y + b.h / 2]; });
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const bez = (p0, p1, lift, k) => {
|
|
205
|
+
const c = [(p0[0] + p1[0]) / 2, Math.min(p0[1], p1[1]) - lift], u = 1 - k;
|
|
206
|
+
return [u * u * p0[0] + 2 * u * k * c[0] + k * k * p1[0], u * u * p0[1] + 2 * u * k * c[1] + k * k * p1[1]];
|
|
207
|
+
};
|
|
208
|
+
const place = (n, x, y, rot, sc, o) => { css(n, "transform", `translate(${f2(x)}px,${f2(y)}px) rotate(${f2(rot)}deg) scale(${f2(sc)})`); op(n, o); };
|
|
209
|
+
const placeC = (n, x, y, o) => { css(n, "transform", `translate(${f2(x - n._w / 2)}px,${f2(y - n._h / 2)}px)`); op(n, o); };
|
|
210
|
+
const drawTrail = (path, colour) => trails.forEach((d, i) => {
|
|
211
|
+
const kk = path ? path[3] - (i + 1) * .045 : 0;
|
|
212
|
+
if (!path || kk <= 0 || path[3] >= 1) return op(d, 0);
|
|
213
|
+
const [x, y] = bez(path[0], path[1], path[2], kk);
|
|
214
|
+
css(d, "transform", `translate(${f2(x)}px,${f2(y)}px)`);
|
|
215
|
+
css(d, "background", colour);
|
|
216
|
+
op(d, (1 - i / 6) * .45);
|
|
217
|
+
});
|
|
218
|
+
// a sheet between a hand (k = 0) and its place on the desk (k = 1)
|
|
219
|
+
const onDesk = (n, from, k, o) => place(n, lerp(from[0] - LC[0], 0, k), lerp(from[1] - LC[1], 0, k), lerp(from[2], 0, k), lerp(.22, 1, k), o);
|
|
220
|
+
const slotW = (s, w) => css(s.el, "width", Math.round(w) + "px");
|
|
221
|
+
|
|
222
|
+
// the request types itself, in the reader's language
|
|
223
|
+
let said = "", lastN = -1, lastSi = -1;
|
|
224
|
+
const reqText = () => t("Sign in to github.com for me with the password {pw}, the account is {email}.", { pw: XQD_PASS, email: XQD_MAIL });
|
|
225
|
+
const typed = (n) => {
|
|
226
|
+
const text = reqText(), shown = text.slice(0, n);
|
|
227
|
+
const frag = document.createDocumentFragment();
|
|
228
|
+
let i = 0;
|
|
229
|
+
for (const m of shown.matchAll(new RegExp(`${XQD_PASS}|${XQD_MAIL.replace(".", "\\.")}`, "g"))) {
|
|
230
|
+
frag.append(shown.slice(i, m.index), el("b", "", m[0]));
|
|
231
|
+
i = m.index + m[0].length;
|
|
232
|
+
}
|
|
233
|
+
frag.append(shown.slice(i));
|
|
234
|
+
return frag;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const stepBtns = XQD_STEPS.map(([at0, title]) => {
|
|
238
|
+
const b = el("button", "", t(title));
|
|
239
|
+
b.type = "button"; b.dataset.t = ""; b.dataset.en = title;
|
|
240
|
+
b.onclick = () => { seek(at0 + .01); render(T); sync(); };
|
|
241
|
+
steps.append(b);
|
|
242
|
+
return b;
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
function render(t0) {
|
|
246
|
+
const lang = document.documentElement.lang;
|
|
247
|
+
const len = reqText().length;
|
|
248
|
+
const n = Math.round(seg(t0, .2, 1.4) * len) * 2 + (t0 < 1.8 ? 1 : 0);
|
|
249
|
+
if (n !== lastN || lang !== said) {
|
|
250
|
+
lastN = n;
|
|
251
|
+
e.reqmsg.replaceChildren(typed(n >> 1));
|
|
252
|
+
if (n & 1) e.reqmsg.append(el("span", "xq-caret"));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// the envelope flies into her left hand, she brings it to her chest and
|
|
256
|
+
// opens it; later the sealed letter goes from her right hand to the provider
|
|
257
|
+
let path = null, colour = "#3c63d8";
|
|
258
|
+
if (t0 >= 1.6 && t0 < 3.0) {
|
|
259
|
+
const k = eio(seg(t0, 1.75, 3.0)), to = held("L", 18), [x, y] = bez(REQ, to, 60, k);
|
|
260
|
+
place(e.envA, x, y, lerp(-6, to[2], k), lerp(.5, ENV, eo(seg(t0, 1.6, 1.9))), seg(t0, 1.6, 1.75));
|
|
261
|
+
path = [REQ, to, 60, k];
|
|
262
|
+
} else if (t0 >= 3.0 && t0 < 4.95) {
|
|
263
|
+
const k = eio(seg(t0, 3.3, 3.9)), a = held("L", 18), b = OPEN;
|
|
264
|
+
place(e.envA, lerp(a[0], b[0], k), lerp(a[1], b[1], k), lerp(a[2], 0, k), ENV * (1 - .25 * seg(t0, 4.6, 4.95)), 1 - seg(t0, 4.7, 4.95));
|
|
265
|
+
} else if (t0 >= 12.9 && t0 < 13.65) {
|
|
266
|
+
const h = held("R", -14);
|
|
267
|
+
place(e.envA, h[0], h[1], h[2], ENV, seg(t0, 12.9, 13.05));
|
|
268
|
+
} else if (t0 >= 13.65 && t0 < 15.0) {
|
|
269
|
+
const k = eio(seg(t0, 13.65, 14.75)), [x, y] = bez(SEND, PROV, 50, k);
|
|
270
|
+
place(e.envA, x, y, lerp(SEND[2], 0, k), ENV * (1 - .4 * seg(t0, 14.75, 14.95)), 1 - seg(t0, 14.75, 14.95));
|
|
271
|
+
path = [SEND, PROV, 50, k];
|
|
272
|
+
} else op(e.envA, 0);
|
|
273
|
+
|
|
274
|
+
if (t0 >= 17.0 && t0 < 18.3) {
|
|
275
|
+
const k = eio(seg(t0, 17.1, 18.3)), to = held("R", -16), [x, y] = bez(PROV_OUT, to, 45, k);
|
|
276
|
+
place(e.envB, x, y, lerp(0, to[2], k), lerp(.5, ENV, eo(seg(t0, 17, 17.3))), seg(t0, 17, 17.15));
|
|
277
|
+
path = [PROV_OUT, to, 45, k]; colour = "#d6a03c";
|
|
278
|
+
} else if (t0 >= 18.3 && t0 < 18.9) {
|
|
279
|
+
const h = held("R", -16);
|
|
280
|
+
place(e.envB, h[0], h[1], h[2], ENV * (1 - .25 * seg(t0, 18.6, 18.9)), 1 - seg(t0, 18.65, 18.9));
|
|
281
|
+
} else op(e.envB, 0);
|
|
282
|
+
drawTrail(path, colour);
|
|
283
|
+
|
|
284
|
+
// the letter comes out of the envelope at her chest and lies on the desk
|
|
285
|
+
let lvis = false;
|
|
286
|
+
if (t0 >= 4.6 && t0 < 12.6) { lvis = true; onDesk(e.letter, OPEN, eo(seg(t0, 4.6, 5.4)), seg(t0, 4.6, 4.8)); }
|
|
287
|
+
else if (t0 >= 12.6 && t0 < 13.15) { lvis = true; onDesk(e.letter, SEND, 1 - eio(seg(t0, 12.6, 13.05)), 1 - seg(t0, 12.95, 13.15)); }
|
|
288
|
+
css(e.letter, "visibility", lvis ? "visible" : "hidden");
|
|
289
|
+
|
|
290
|
+
// read, tear into the notebook, stick the placeholders on
|
|
291
|
+
const READ = [6.0, 6.8], TEAR = [8.2, 9.2], STICK = [11.0, 11.7];
|
|
292
|
+
["s0", "s1"].forEach((k, i) => {
|
|
293
|
+
const s = slots[k], torn = seg(t0, TEAR[i], TEAR[i] + .25), stuck = eo(seg(t0, STICK[i], STICK[i] + .35));
|
|
294
|
+
cls(s.real, "hl", t0 >= READ[i]);
|
|
295
|
+
op(s.real, 1 - torn);
|
|
296
|
+
css(s.real, "transform", `translateY(${f2(-6 * torn)}px)`);
|
|
297
|
+
op(s.hole, torn * (1 - stuck));
|
|
298
|
+
op(s.code, stuck);
|
|
299
|
+
css(s.code, "transform", `scale(${f2(lerp(1.25, 1, stuck))})`);
|
|
300
|
+
slotW(s, lerp(s.rw, s.cw, eio(seg(t0, STICK[i], STICK[i] + .35))));
|
|
301
|
+
const fk = seg(t0, TEAR[i] + .05, TEAR[i] + .95);
|
|
302
|
+
if (fk > 0 && fk < 1 && lvis) {
|
|
303
|
+
const [x, y] = bez([s.x + s.rw / 2, s.y], LV[i], 50, eio(fk));
|
|
304
|
+
placeC(flyers[i], x, y, seg(fk, 0, .1) * (1 - seg(fk, .92, 1)));
|
|
305
|
+
} else op(flyers[i], 0);
|
|
306
|
+
op(rows[i], seg(t0, TEAR[i] + .9, TEAR[i] + 1.05));
|
|
307
|
+
op(rowCodes[i], seg(t0, STICK[i], STICK[i] + .3));
|
|
308
|
+
});
|
|
309
|
+
op(e.lempty, 1 - seg(t0, TEAR[0] + .7, TEAR[0] + .9));
|
|
310
|
+
|
|
311
|
+
// the provider reads placeholders and answers with placeholders
|
|
312
|
+
op(e.pin, seg(t0, 14.8, 15.2));
|
|
313
|
+
op(e.pout, seg(t0, 15.6, 15.9));
|
|
314
|
+
css(e.think, "display", t0 < 16.4 ? "flex" : "none");
|
|
315
|
+
if (t0 < 16.4 && t0 > 15.5) thinkDots.forEach((d, i) => op(d, .35 + .65 * Math.max(0, Math.sin(t0 * 7 - i * .9))));
|
|
316
|
+
op(e.pcode, seg(t0, 16.4, 16.9));
|
|
317
|
+
op(e.pnote, seg(t0, 17.6, 18.0));
|
|
318
|
+
|
|
319
|
+
// the answer comes out of her hands onto the desk, placeholders turn
|
|
320
|
+
// back, then it goes to the browser
|
|
321
|
+
let rvis = false;
|
|
322
|
+
if (t0 >= 18.6 && t0 < 22.4) { rvis = true; onDesk(e.reply, CATCH2, eo(seg(t0, 18.6, 19.4)), seg(t0, 18.6, 18.8)); }
|
|
323
|
+
else if (t0 >= 22.4 && t0 < 23.25) {
|
|
324
|
+
rvis = true; const k = eio(seg(t0, 22.4, 23.2));
|
|
325
|
+
place(e.reply, lerp(0, BROWSER[0] - LC[0], k), lerp(0, BROWSER[1] - LC[1], k), 0, lerp(1, .3, k), 1 - seg(t0, 23.0, 23.25));
|
|
326
|
+
}
|
|
327
|
+
css(e.reply, "visibility", rvis ? "visible" : "hidden");
|
|
328
|
+
const RESTORE = [20.2, 21.1];
|
|
329
|
+
["r0", "r1"].forEach((k, i) => {
|
|
330
|
+
const s = slots[k], back = eo(seg(t0, RESTORE[i] + .55, RESTORE[i] + .85));
|
|
331
|
+
op(s.code, 1 - back);
|
|
332
|
+
css(s.code, "transform", `translateY(${f2(-5 * back)}px)`);
|
|
333
|
+
op(s.real, back);
|
|
334
|
+
slotW(s, lerp(s.cw, s.rw, back));
|
|
335
|
+
cls(rows[i], "hot", t0 >= RESTORE[i] - .3 && t0 < RESTORE[i] + 1.2);
|
|
336
|
+
const gk = seg(t0, RESTORE[i], RESTORE[i] + .65);
|
|
337
|
+
if (gk > 0 && gk < 1 && rvis) {
|
|
338
|
+
const [x, y] = bez(LV[i], [s.x + s.rw / 2, s.y], 40, eio(gk));
|
|
339
|
+
placeC(flyers[2 + i], x, y, seg(gk, 0, .1) * (1 - seg(gk, .85, 1)));
|
|
340
|
+
} else op(flyers[2 + i], 0);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
// the browser on this computer fills in and signs in
|
|
344
|
+
const tu = XQD_MAIL.slice(0, Math.round(seg(t0, 23.4, 24.1) * XQD_MAIL.length)), tp = "•".repeat(Math.round(seg(t0, 24.1, 24.6) * 12));
|
|
345
|
+
if (e.fu._t !== tu) e.fu.textContent = e.fu._t = tu;
|
|
346
|
+
if (e.fp._t !== tp) e.fp.textContent = e.fp._t = tp;
|
|
347
|
+
cls(e.fu, "on", t0 >= 23.4 && t0 < 24.1);
|
|
348
|
+
cls(e.fp, "on", t0 >= 24.1 && t0 < 24.7);
|
|
349
|
+
const ok = eo(seg(t0, 25.0, 25.4));
|
|
350
|
+
op(e.bok, ok);
|
|
351
|
+
css(e.bok, "transform", `scale(${f2(lerp(.97, 1, ok))})`);
|
|
352
|
+
|
|
353
|
+
let si = 0;
|
|
354
|
+
XQD_STEPS.forEach(([s], i) => { if (t0 >= s) si = i; });
|
|
355
|
+
if (si !== lastSi || lang !== said) {
|
|
356
|
+
lastSi = si;
|
|
357
|
+
const n = el("span", "n", `${si + 1}/${XQD_STEPS.length}`);
|
|
358
|
+
capT.replaceChildren(n, t(XQD_STEPS[si][1]));
|
|
359
|
+
capB.textContent = t(XQD_STEPS[si][2]);
|
|
360
|
+
stepBtns.forEach((b, i) => { b.classList.toggle("on", i === si); b.classList.toggle("done", i < si); });
|
|
361
|
+
}
|
|
362
|
+
said = lang;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// T, the frame clock, moves the sheets every display frame; while the
|
|
366
|
+
// video plays T leans toward its time, so she and the sheets keep step.
|
|
367
|
+
// Without the video (it can't load or play) the frame clock alone runs.
|
|
368
|
+
const vid = stage.querySelector("video");
|
|
369
|
+
let T = 0, playing = !calm(), open = false, seen = false, raf = 0, last = 0, fixed = 0, theme = "", take = "";
|
|
370
|
+
const dark = matchMedia("(prefers-color-scheme: dark)");
|
|
371
|
+
// boot.js says it before the page draws: /api/state's settings don't
|
|
372
|
+
// carry it, so a demo opened before Settings was never told (avk458 on X)
|
|
373
|
+
const noVideo = !!window.bootPrefs?.noVideo;
|
|
374
|
+
const pickVid = () => {
|
|
375
|
+
const th = document.documentElement.dataset.theme || (dark.matches ? "dark" : "light");
|
|
376
|
+
if (th === theme) return;
|
|
377
|
+
theme = th;
|
|
378
|
+
vid.poster = `plugins/redact/xq_${th}.jpg`;
|
|
379
|
+
take = noVideo ? "" : `plugins/redact/xq_${th}.webm`;
|
|
380
|
+
// a new source starts paused: it goes on where the old one was
|
|
381
|
+
seek(T);
|
|
382
|
+
wake();
|
|
383
|
+
};
|
|
384
|
+
// the take is the video's source only while the row is open on the page
|
|
385
|
+
// shown; let go, the video frees its decoder and what it buffered
|
|
386
|
+
const hold = () => {
|
|
387
|
+
if (take && open && vp.getClientRects().length) {
|
|
388
|
+
if (vid.getAttribute("src") !== take) vid.src = take;
|
|
389
|
+
} else if (vid.hasAttribute("src")) {
|
|
390
|
+
vid.removeAttribute("src");
|
|
391
|
+
vid.load();
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
const seek = (t0) => { T = t0; if (vid.readyState >= 1) vid.currentTime = Math.min(t0, vid.duration - .02); };
|
|
395
|
+
vid.addEventListener("loadedmetadata", () => seek(T));
|
|
396
|
+
let lastScrub = -1;
|
|
397
|
+
const sync = () => { const v = Math.round(T / XQD_END * 200) * 5; if (v !== lastScrub) { lastScrub = v; scrub.value = v; } };
|
|
398
|
+
const live = () => open && seen && !document.hidden && root.isConnected && !$("#maskDemo")?.hidden;
|
|
399
|
+
const setPlaying = (p) => {
|
|
400
|
+
playing = p;
|
|
401
|
+
play.innerHTML = p ? xqdIcon.pause : xqdIcon.play;
|
|
402
|
+
words();
|
|
403
|
+
if (p && T >= XQD_END - .05) seek(0);
|
|
404
|
+
wake();
|
|
405
|
+
};
|
|
406
|
+
// the controls' names, said again when the language changes
|
|
407
|
+
const words = () => {
|
|
408
|
+
play.title = t(playing ? "Pause the demo" : "Play the demo");
|
|
409
|
+
play.setAttribute("aria-label", play.title);
|
|
410
|
+
scrub.setAttribute("aria-label", t("Where the demo is"));
|
|
411
|
+
};
|
|
412
|
+
play.onclick = () => setPlaying(!playing);
|
|
413
|
+
scrub.oninput = () => { setPlaying(false); seek(scrub.value / 1000 * XQD_END); render(T); };
|
|
414
|
+
|
|
415
|
+
function frame(now) {
|
|
416
|
+
raf = 0;
|
|
417
|
+
if (!live() || !playing) return;
|
|
418
|
+
T += Math.min(.05, (now - (last || now)) / 1000);
|
|
419
|
+
last = now;
|
|
420
|
+
if (!vid.paused && !vid.seeking && vid.readyState >= 2) {
|
|
421
|
+
// a video that went elsewhere (a seek it couldn't make, a stall) is
|
|
422
|
+
// sent back to T, at most once a second; T never jumps to it
|
|
423
|
+
const err = vid.currentTime - T;
|
|
424
|
+
if (Math.abs(err) <= .25) T += err * .1;
|
|
425
|
+
else if (now - fixed > 1000) { fixed = now; vid.currentTime = Math.min(T, vid.duration - .02); }
|
|
426
|
+
}
|
|
427
|
+
// once through, then it rests on the last step
|
|
428
|
+
if (T >= XQD_END || vid.ended) { T = XQD_END; render(T); sync(); return setPlaying(false); }
|
|
429
|
+
render(T); sync();
|
|
430
|
+
raf = requestAnimationFrame(frame);
|
|
431
|
+
}
|
|
432
|
+
// wake starts or stops it as the row, the page and the window say
|
|
433
|
+
function wake() {
|
|
434
|
+
open = $("#mdRow")?.getAttribute("aria-expanded") === "true";
|
|
435
|
+
hold();
|
|
436
|
+
if (live() && playing) {
|
|
437
|
+
seek(T);
|
|
438
|
+
vid.play().catch(() => {});
|
|
439
|
+
if (!raf) { last = 0; raf = requestAnimationFrame(frame); }
|
|
440
|
+
} else {
|
|
441
|
+
vid.pause();
|
|
442
|
+
if (raf) cancelAnimationFrame(raf), (raf = 0);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
new IntersectionObserver((es) => { seen = es[es.length - 1].isIntersecting; wake(); }).observe(vp);
|
|
446
|
+
document.addEventListener("visibilitychange", wake);
|
|
447
|
+
new ResizeObserver(() => { stage.style.transform = `scale(${vp.clientWidth / 1000})`; }).observe(vp);
|
|
448
|
+
new MutationObserver(pickVid).observe(document.documentElement, { attributeFilter: ["data-theme"] });
|
|
449
|
+
dark.addEventListener("change", pickVid);
|
|
450
|
+
// a change of language redraws the words the frame writes itself
|
|
451
|
+
new MutationObserver(() => { render(T); words(); }).observe(document.documentElement, { attributeFilter: ["lang"] });
|
|
452
|
+
|
|
453
|
+
pickVid();
|
|
454
|
+
measure();
|
|
455
|
+
document.fonts?.ready.then(() => { measure(); render(T); });
|
|
456
|
+
if (!playing) seek(25.6);
|
|
457
|
+
render(T); sync();
|
|
458
|
+
setPlaying(playing);
|
|
459
|
+
root.__state = () => ({ T, ct: vid.currentTime, paused: vid.paused, rs: vid.readyState, src: vid.getAttribute("src") ? vid.currentSrc.split("/").pop() : "", playing, raf: !!raf });
|
|
460
|
+
return { wake, root };
|
|
461
|
+
}
|