pi-namespace-patch 1.0.0-namespace.2 → 1.0.2-namespace.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 +54 -0
- package/README.md +22 -4
- package/dist/bundle/chunks/{anthropic-messages-UID2PALB.js → anthropic-messages-MVG2FL56.js} +3 -3
- package/dist/bundle/chunks/azure-openai-responses-7AMFUIWJ.js +2 -0
- package/dist/bundle/chunks/bedrock-converse-stream.js +2 -2
- package/dist/bundle/chunks/chunk-2KAZSACP.js +2 -0
- package/dist/bundle/chunks/{chunk-2KTBZM5G.js → chunk-6FX7UEPL.js} +5 -5
- package/dist/bundle/chunks/{chunk-2VNGUWBO.js → chunk-BYB6ZAAQ.js} +1 -1
- package/dist/bundle/chunks/chunk-DN3QEIXS.js +2 -0
- package/dist/bundle/chunks/chunk-F5ZABL6Z.js +20 -0
- package/dist/bundle/chunks/{chunk-3LH3ZQKY.js → chunk-G7EDXK4D.js} +1 -1
- package/dist/bundle/chunks/chunk-IKZJX7ZT.js +2 -0
- package/dist/bundle/chunks/{chunk-4N5K4RKC.js → chunk-OLBCNJCL.js} +31 -32
- package/dist/bundle/chunks/{chunk-PRD6WYBT.js → chunk-OYBSLV7Y.js} +1 -1
- package/dist/bundle/chunks/{chunk-IBCPYVFK.js → chunk-PDFMCAOZ.js} +4 -4
- package/dist/bundle/chunks/{chunk-LTXEMVMA.js → chunk-SYF2TF6W.js} +2 -2
- package/dist/bundle/chunks/chunk-WCQZGZ6I.js +82 -0
- package/dist/bundle/chunks/{chunk-KDN6RCEQ.js → chunk-ZSI3BL5Q.js} +2 -2
- package/dist/bundle/chunks/{cli-QOMC26IJ.js → cli-KILDDGW3.js} +2 -2
- package/dist/bundle/chunks/{cloudflare-workers-ai-system-one-NPQN263E.js → cloudflare-workers-ai-system-one-X5PVWWYJ.js} +1 -1
- package/dist/bundle/chunks/codemode-worker.js +24 -3
- package/dist/bundle/chunks/easter-egg-3d-T5FFWIBR.js +2 -0
- package/dist/bundle/chunks/{execute-GVRSEGHL.js → execute-RYVK27GT.js} +25 -4
- package/dist/bundle/chunks/{google-generative-ai-APTPZOQL.js → google-generative-ai-ZOK6CV7D.js} +1 -1
- package/dist/bundle/chunks/{google-vertex-MXCOSOY3.js → google-vertex-VKWZQ3AY.js} +1 -1
- package/dist/bundle/chunks/{mistral-conversations-DKVFNQL6.js → mistral-conversations-7DAUEE4N.js} +1 -1
- package/dist/bundle/chunks/node-N5BSDOA5.js +16 -0
- package/dist/bundle/chunks/openai-chatgpt.js +1 -1
- package/dist/bundle/chunks/{openai-codex-responses-GK67DQQJ.js → openai-codex-responses-TC3OK7B4.js} +1 -1
- package/dist/bundle/chunks/openai-completions-GWHGMQ3T.js +7 -0
- package/dist/bundle/chunks/openai-responses-EZ7FJ2KL.js +3 -0
- package/dist/bundle/chunks/{runtime-2SO2JDWM.js → runtime-D5I2NTIF.js} +1 -1
- package/dist/bundle/chunks/virtual-modules-J2M3WAR2.js +2 -0
- package/dist/bundle/cli-runtime.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +4 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/export-html/tool-renderer.d.ts +3 -3
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
- package/dist/core/export-html/tool-renderer.js +3 -3
- package/dist/core/export-html/tool-renderer.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +5 -0
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +3 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +6 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +9 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/mcp-servers.d.ts +6 -0
- package/dist/core/mcp-servers.d.ts.map +1 -1
- package/dist/core/mcp-servers.js +11 -0
- package/dist/core/mcp-servers.js.map +1 -1
- package/dist/core/model-config.d.ts +36 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +14 -2
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-resolver.js +1 -1
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/provider-composer.d.ts +2 -1
- package/dist/core/provider-composer.d.ts.map +1 -1
- package/dist/core/provider-composer.js +13 -0
- package/dist/core/provider-composer.js.map +1 -1
- package/dist/core/tools/renderers/index.d.ts +2 -2
- package/dist/core/tools/renderers/index.d.ts.map +1 -1
- package/dist/core/tools/renderers/index.js.map +1 -1
- package/dist/extensions/mcp/cli.d.ts.map +1 -1
- package/dist/extensions/mcp/cli.js +3 -0
- package/dist/extensions/mcp/cli.js.map +1 -1
- package/dist/extensions/mcp/config.d.ts +15 -3
- package/dist/extensions/mcp/config.d.ts.map +1 -1
- package/dist/extensions/mcp/config.js +46 -10
- package/dist/extensions/mcp/config.js.map +1 -1
- package/dist/extensions/mcp/index.d.ts +4 -1
- package/dist/extensions/mcp/index.d.ts.map +1 -1
- package/dist/extensions/mcp/index.js +80 -45
- package/dist/extensions/mcp/index.js.map +1 -1
- package/dist/extensions/mcp/oauth.d.ts +4 -1
- package/dist/extensions/mcp/oauth.d.ts.map +1 -1
- package/dist/extensions/mcp/oauth.js +54 -10
- package/dist/extensions/mcp/oauth.js.map +1 -1
- package/dist/extensions/mcp/runtime.d.ts.map +1 -1
- package/dist/extensions/mcp/runtime.js +1 -0
- package/dist/extensions/mcp/runtime.js.map +1 -1
- package/dist/extensions/mcp/tools.d.ts +3 -1
- package/dist/extensions/mcp/tools.d.ts.map +1 -1
- package/dist/extensions/mcp/tools.js +19 -13
- package/dist/extensions/mcp/tools.js.map +1 -1
- package/dist/extensions/mcp/ui.d.ts.map +1 -1
- package/dist/extensions/mcp/ui.js +8 -4
- package/dist/extensions/mcp/ui.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/armin.d.ts +3 -0
- package/dist/modes/interactive/components/armin.d.ts.map +1 -1
- package/dist/modes/interactive/components/armin.js +7 -5
- package/dist/modes/interactive/components/armin.js.map +1 -1
- package/dist/modes/interactive/components/auth-url.d.ts +14 -0
- package/dist/modes/interactive/components/auth-url.d.ts.map +1 -0
- package/dist/modes/interactive/components/auth-url.js +37 -0
- package/dist/modes/interactive/components/auth-url.js.map +1 -0
- package/dist/modes/interactive/components/easter-egg-3d.d.ts +104 -0
- package/dist/modes/interactive/components/easter-egg-3d.d.ts.map +1 -0
- package/dist/modes/interactive/components/{pi-logo-animation.js → easter-egg-3d.js} +127 -74
- package/dist/modes/interactive/components/easter-egg-3d.js.map +1 -0
- package/dist/modes/interactive/components/easter-egg-3d.lazy.d.ts +6 -0
- package/dist/modes/interactive/components/easter-egg-3d.lazy.d.ts.map +1 -0
- package/dist/modes/interactive/components/easter-egg-3d.lazy.js +23 -0
- package/dist/modes/interactive/components/easter-egg-3d.lazy.js.map +1 -0
- package/dist/modes/interactive/components/index.d.ts +0 -1
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +0 -1
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts +2 -0
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +11 -5
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +6 -18
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +22 -45
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +2 -2
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +15 -17
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +12 -0
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/utils/image-convert.d.ts +12 -0
- package/dist/utils/image-convert.d.ts.map +1 -1
- package/dist/utils/image-convert.js +45 -6
- package/dist/utils/image-convert.js.map +1 -1
- package/docs/cli.md +2 -0
- package/docs/codemode.md +1 -1
- package/docs/extensions.md +4 -0
- package/docs/keybindings.md +1 -1
- package/docs/mcp.md +23 -1
- package/docs/models.md +28 -2
- package/docs/quickstart.md +16 -2
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +13 -14
- package/dist/bundle/chunks/azure-openai-responses-AFMVM6NP.js +0 -2
- package/dist/bundle/chunks/chunk-6D7LHLKG.js +0 -2
- package/dist/bundle/chunks/chunk-7FH3BDRG.js +0 -82
- package/dist/bundle/chunks/chunk-DTD7JQ7Y.js +0 -42
- package/dist/bundle/chunks/chunk-MJOHEBV7.js +0 -2
- package/dist/bundle/chunks/node-O3RIBJRH.js +0 -16
- package/dist/bundle/chunks/openai-completions-JXDDPZ23.js +0 -7
- package/dist/bundle/chunks/openai-responses-CQDDLDQF.js +0 -3
- package/dist/bundle/chunks/pi-logo-animation-MPKZE4EK.js +0 -2
- package/dist/bundle/chunks/virtual-modules-3THEGRQQ.js +0 -2
- package/dist/modes/interactive/components/daxnuts.d.ts +0 -23
- package/dist/modes/interactive/components/daxnuts.d.ts.map +0 -1
- package/dist/modes/interactive/components/daxnuts.js +0 -140
- package/dist/modes/interactive/components/daxnuts.js.map +0 -1
- package/dist/modes/interactive/components/pi-logo-animation.d.ts +0 -68
- package/dist/modes/interactive/components/pi-logo-animation.d.ts.map +0 -1
- package/dist/modes/interactive/components/pi-logo-animation.js.map +0 -1
- package/dist/modes/interactive/components/pi-logo-animation.lazy.d.ts +0 -7
- package/dist/modes/interactive/components/pi-logo-animation.lazy.d.ts.map +0 -1
- package/dist/modes/interactive/components/pi-logo-animation.lazy.js +0 -12
- package/dist/modes/interactive/components/pi-logo-animation.lazy.js.map +0 -1
- package/npm-shrinkwrap.json +0 -1952
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Container, type TUI } from "@earendil-works/pi-tui";
|
|
2
|
+
/**
|
|
3
|
+
* A sign-in URL with a click hint and a copy hint. Hosts call `copy()` when `app.message.copy` is
|
|
4
|
+
* pressed, since a long URL wraps and often cannot be selected or clicked as a whole (SSH, tmux).
|
|
5
|
+
*/
|
|
6
|
+
export declare class AuthUrlComponent extends Container {
|
|
7
|
+
readonly url: string;
|
|
8
|
+
private readonly tui;
|
|
9
|
+
private readonly hint;
|
|
10
|
+
constructor(tui: TUI, url: string);
|
|
11
|
+
private setHint;
|
|
12
|
+
copy(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=auth-url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-url.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/auth-url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAmB,KAAK,GAAG,EAAE,MAAM,wBAAwB,CAAC;AAK9E;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,SAAS;IAC9C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAM;IAC1B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAO;IAE5B,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAQhC;IAED,OAAO,CAAC,OAAO;IAMT,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAO1B;CACD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Container, hyperlink, Text } from "@earendil-works/pi-tui";
|
|
2
|
+
import { copyToClipboard } from "../../../utils/clipboard.js";
|
|
3
|
+
import { theme } from "../theme/theme.js";
|
|
4
|
+
import { keyHint } from "./keybinding-hints.js";
|
|
5
|
+
/**
|
|
6
|
+
* A sign-in URL with a click hint and a copy hint. Hosts call `copy()` when `app.message.copy` is
|
|
7
|
+
* pressed, since a long URL wraps and often cannot be selected or clicked as a whole (SSH, tmux).
|
|
8
|
+
*/
|
|
9
|
+
export class AuthUrlComponent extends Container {
|
|
10
|
+
url;
|
|
11
|
+
tui;
|
|
12
|
+
hint;
|
|
13
|
+
constructor(tui, url) {
|
|
14
|
+
super();
|
|
15
|
+
this.tui = tui;
|
|
16
|
+
this.url = url;
|
|
17
|
+
this.addChild(new Text(theme.fg("accent", hyperlink(url, url)), 1, 0));
|
|
18
|
+
this.hint = new Text("", 1, 0);
|
|
19
|
+
this.addChild(this.hint);
|
|
20
|
+
this.setHint(keyHint("app.message.copy", "to copy"));
|
|
21
|
+
}
|
|
22
|
+
setHint(suffix) {
|
|
23
|
+
const clickHint = process.platform === "darwin" ? "Cmd+click to open" : "Ctrl+click to open";
|
|
24
|
+
this.hint.setText(`${theme.fg("dim", hyperlink(clickHint, this.url))} ${theme.fg("dim", "•")} ${suffix}`);
|
|
25
|
+
this.tui.requestRender();
|
|
26
|
+
}
|
|
27
|
+
async copy() {
|
|
28
|
+
try {
|
|
29
|
+
await copyToClipboard(this.url);
|
|
30
|
+
this.setHint(theme.fg("success", "Copied URL to clipboard"));
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
this.setHint(theme.fg("error", error instanceof Error ? error.message : String(error)));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=auth-url.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-url.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/auth-url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAY,MAAM,wBAAwB,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD;;;GAGG;AACH,MAAM,OAAO,gBAAiB,SAAQ,SAAS;IACrC,GAAG,CAAS;IACJ,GAAG,CAAM;IACT,IAAI,CAAO;IAE5B,YAAY,GAAQ,EAAE,GAAW;QAChC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACvE,IAAI,CAAC,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC/B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzB,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC,CAAC;IACtD,CAAC;IAEO,OAAO,CAAC,MAAc;QAC7B,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,oBAAoB,CAAC;QAC7F,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC;QAC1G,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,IAAI;QACT,IAAI,CAAC;YACJ,MAAM,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAChC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,yBAAyB,CAAC,CAAC,CAAC;QAC9D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACzF,CAAC;IACF,CAAC;CACD","sourcesContent":["import { Container, hyperlink, Text, type TUI } from \"@earendil-works/pi-tui\";\nimport { copyToClipboard } from \"../../../utils/clipboard.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { keyHint } from \"./keybinding-hints.ts\";\n\n/**\n * A sign-in URL with a click hint and a copy hint. Hosts call `copy()` when `app.message.copy` is\n * pressed, since a long URL wraps and often cannot be selected or clicked as a whole (SSH, tmux).\n */\nexport class AuthUrlComponent extends Container {\n\treadonly url: string;\n\tprivate readonly tui: TUI;\n\tprivate readonly hint: Text;\n\n\tconstructor(tui: TUI, url: string) {\n\t\tsuper();\n\t\tthis.tui = tui;\n\t\tthis.url = url;\n\t\tthis.addChild(new Text(theme.fg(\"accent\", hyperlink(url, url)), 1, 0));\n\t\tthis.hint = new Text(\"\", 1, 0);\n\t\tthis.addChild(this.hint);\n\t\tthis.setHint(keyHint(\"app.message.copy\", \"to copy\"));\n\t}\n\n\tprivate setHint(suffix: string): void {\n\t\tconst clickHint = process.platform === \"darwin\" ? \"Cmd+click to open\" : \"Ctrl+click to open\";\n\t\tthis.hint.setText(`${theme.fg(\"dim\", hyperlink(clickHint, this.url))} ${theme.fg(\"dim\", \"•\")} ${suffix}`);\n\t\tthis.tui.requestRender();\n\t}\n\n\tasync copy(): Promise<void> {\n\t\ttry {\n\t\t\tawait copyToClipboard(this.url);\n\t\t\tthis.setHint(theme.fg(\"success\", \"Copied URL to clipboard\"));\n\t\t} catch (error) {\n\t\t\tthis.setHint(theme.fg(\"error\", error instanceof Error ? error.message : String(error)));\n\t\t}\n\t}\n}\n"]}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { type Component, type TUI, type TuiMouseEvent, type TuiMouseEventResult } from "@earendil-works/pi-tui";
|
|
2
|
+
/**
|
|
3
|
+
* Fullscreen 3D easter eggs: the pi logo (header logo click) and Armin (/arminsayshi). Both are bitmaps built from
|
|
4
|
+
* one block per pixel. The current screen dissolves into braille dust while the model spins in the center and its
|
|
5
|
+
* blocks play a sliding puzzle. Leaving plays the same timeline backwards, so the screen reassembles.
|
|
6
|
+
*
|
|
7
|
+
* The pi logo lifts off the header, flies to the center, and grows; the dust spreads out from the header logo.
|
|
8
|
+
* Armin grows out of a speck at the center; the dust spreads out from the center.
|
|
9
|
+
*
|
|
10
|
+
* The blocks are ray cast per braille dot. A braille cell holds 2x4 roughly square dots, so one pixel of a
|
|
11
|
+
* half-block bitmap is exactly 2x2 dots, and the header logo (4x2 cells) is 8x8 dots when it lifts off.
|
|
12
|
+
*/
|
|
13
|
+
type Rgb = readonly [number, number, number];
|
|
14
|
+
/** Grid position of a block: column and row in the bitmap, and layer (-1, 0, 1) in depth. */
|
|
15
|
+
type Cell3 = readonly [number, number, number];
|
|
16
|
+
/** A bitmap built from one block per foreground pixel. Each is a block that the puzzle slides around. */
|
|
17
|
+
interface Model {
|
|
18
|
+
/** Bitmap size in pixels. */
|
|
19
|
+
columns: number;
|
|
20
|
+
rows: number;
|
|
21
|
+
blocks: Array<{
|
|
22
|
+
home: Cell3;
|
|
23
|
+
color: Rgb;
|
|
24
|
+
}>;
|
|
25
|
+
/** Camera distance from the model's center, in pixels. */
|
|
26
|
+
cameraDistance: number;
|
|
27
|
+
/** Farthest distance of any block corner from the center, with blocks on the outer depth layers. */
|
|
28
|
+
radius: number;
|
|
29
|
+
/** Largest share of the screen width the spinning model covers. */
|
|
30
|
+
widthShare: number;
|
|
31
|
+
/** Number of blocks the puzzle moves per step, given a random number from 0 to 1. */
|
|
32
|
+
puzzleMoves: (random: number) => number;
|
|
33
|
+
/**
|
|
34
|
+
* Top-left cell of the model's half-block rendering on screen. The model lifts off from there and replaces it.
|
|
35
|
+
* Undefined to grow out of the center.
|
|
36
|
+
*/
|
|
37
|
+
origin: {
|
|
38
|
+
column: number;
|
|
39
|
+
row: number;
|
|
40
|
+
} | undefined;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Which easter egg to play. The pi logo lifts off the header logo, whose top-left cell is at `column`, `row`.
|
|
44
|
+
*/
|
|
45
|
+
export type EasterEgg3d = {
|
|
46
|
+
kind: "pi-logo";
|
|
47
|
+
column: number;
|
|
48
|
+
row: number;
|
|
49
|
+
} | {
|
|
50
|
+
kind: "armin";
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Show the animation as a fullscreen overlay until it is dismissed. The overlay takes focus and mouse input and
|
|
54
|
+
* returns focus when hidden, so the rest of the UI keeps running underneath untouched.
|
|
55
|
+
*/
|
|
56
|
+
export declare function playEasterEgg3d(tui: TUI, screen: readonly string[], egg: EasterEgg3d): Promise<void>;
|
|
57
|
+
export declare class EasterEgg3dAnimation implements Component {
|
|
58
|
+
private readonly tui;
|
|
59
|
+
/** The screen to dissolve, as rendered lines. */
|
|
60
|
+
private readonly screen;
|
|
61
|
+
private readonly model;
|
|
62
|
+
private readonly foreground;
|
|
63
|
+
private readonly background;
|
|
64
|
+
private readonly onDone;
|
|
65
|
+
private readonly startTime;
|
|
66
|
+
private lastRender;
|
|
67
|
+
private timer;
|
|
68
|
+
private exit;
|
|
69
|
+
private shuffle;
|
|
70
|
+
private screenWidth;
|
|
71
|
+
private screenHeight;
|
|
72
|
+
/** Star per cell index, or undefined. */
|
|
73
|
+
private stars;
|
|
74
|
+
private cells;
|
|
75
|
+
private readonly ansiCache;
|
|
76
|
+
private readonly raster;
|
|
77
|
+
constructor(tui: TUI, screen: readonly string[], model: Model, colors: {
|
|
78
|
+
foreground: Rgb;
|
|
79
|
+
background: Rgb;
|
|
80
|
+
}, onDone: () => void);
|
|
81
|
+
/** Play the exit animation. A second call skips it. */
|
|
82
|
+
close(): void;
|
|
83
|
+
handleInput(data: string): void;
|
|
84
|
+
handleMouse(event: TuiMouseEvent): TuiMouseEventResult;
|
|
85
|
+
invalidate(): void;
|
|
86
|
+
render(width: number): string[];
|
|
87
|
+
private elapsed;
|
|
88
|
+
private exitProgress;
|
|
89
|
+
private finish;
|
|
90
|
+
/** Each block's displacement from its place in the model at `time`, in grid units. */
|
|
91
|
+
private blockOffsets;
|
|
92
|
+
private flyProgress;
|
|
93
|
+
private starAlpha;
|
|
94
|
+
private hintAlpha;
|
|
95
|
+
private pose;
|
|
96
|
+
private hint;
|
|
97
|
+
/** A sparse, deterministic starfield: one braille dot in about STAR_DENSITY of all cells. */
|
|
98
|
+
private prepareStars;
|
|
99
|
+
private prepareCells;
|
|
100
|
+
private pack;
|
|
101
|
+
private ansi;
|
|
102
|
+
}
|
|
103
|
+
export {};
|
|
104
|
+
//# sourceMappingURL=easter-egg-3d.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"easter-egg-3d.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/easter-egg-3d.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,23 +1,56 @@
|
|
|
1
1
|
import { backgroundAnsi, colorToRgb, foregroundAnsi, getKeybindings, indexedColor, rgbColor, visibleWidth, } from "@earendil-works/pi-tui";
|
|
2
2
|
import { theme } from "../theme/theme.js";
|
|
3
|
+
import { ARMIN_HEIGHT, ARMIN_WIDTH, isArminPixel } from "./armin.js";
|
|
3
4
|
import { formatKeyText } from "./keybinding-hints.js";
|
|
4
|
-
const CORAL = [228, 138, 122];
|
|
5
|
-
const BLUE = [79, 142, 179];
|
|
6
|
-
const YELLOW = [234, 182, 93];
|
|
7
5
|
const DEPTH = 0.7;
|
|
8
|
-
const CAMERA_DISTANCE = 10;
|
|
9
6
|
const FLY_START = 0.1;
|
|
10
7
|
const FLY_DURATION = 1.3;
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
8
|
+
/** Braille dots per pixel when a model without an origin appears, growing from a speck at the center. */
|
|
9
|
+
const START_SCALE = 0.1;
|
|
10
|
+
function createModel(columns, rows, pixel, options) {
|
|
11
|
+
const blocks = [];
|
|
12
|
+
for (let row = 0; row < rows; row++) {
|
|
13
|
+
for (let column = 0; column < columns; column++) {
|
|
14
|
+
const color = pixel(column, row);
|
|
15
|
+
if (color)
|
|
16
|
+
blocks.push({ home: [column, row, 0], color });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
columns,
|
|
21
|
+
rows,
|
|
22
|
+
blocks,
|
|
23
|
+
cameraDistance: options.cameraDistance,
|
|
24
|
+
radius: Math.hypot(columns / 2, rows / 2, 1 + DEPTH / 2),
|
|
25
|
+
widthShare: options.widthShare,
|
|
26
|
+
puzzleMoves: (random) => options.puzzleMoves(random, blocks.length),
|
|
27
|
+
origin: options.origin,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const CORAL = [228, 138, 122];
|
|
31
|
+
const BLUE = [79, 142, 179];
|
|
32
|
+
const YELLOW = [234, 182, 93];
|
|
33
|
+
const PI_LOGO_PIXELS = ["ccc.", "b.c.", "bb.y", "b..y"];
|
|
34
|
+
const PI_LOGO_COLORS = { c: CORAL, b: BLUE, y: YELLOW };
|
|
35
|
+
function piLogoModel(origin) {
|
|
36
|
+
return createModel(4, 4, (column, row) => PI_LOGO_COLORS[PI_LOGO_PIXELS[row][column]], {
|
|
37
|
+
cameraDistance: 10,
|
|
38
|
+
widthShare: 0.35,
|
|
39
|
+
puzzleMoves: (random) => (random < 0.4 ? 2 : 1),
|
|
40
|
+
origin,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function arminModel(color) {
|
|
44
|
+
return createModel(ARMIN_WIDTH, ARMIN_HEIGHT, (column, row) => (isArminPixel(column, row) ? color : undefined), {
|
|
45
|
+
// Far enough that the perspective stays mild for a figure about 36 blocks tall.
|
|
46
|
+
cameraDistance: 80,
|
|
47
|
+
widthShare: 0.45,
|
|
48
|
+
puzzleMoves: (random, blockCount) => Math.round(blockCount * (0.03 + random * 0.04)),
|
|
49
|
+
origin: undefined,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
// The sliding puzzle: after the model spun for a while, blocks slide into free neighboring cells, a few at a time.
|
|
53
|
+
// Each cycle shuffles, flies every block back home, and holds the model briefly.
|
|
21
54
|
const PUZZLE_START = FLY_START + FLY_DURATION + 3;
|
|
22
55
|
const PUZZLE_STEP = 0.3;
|
|
23
56
|
const PUZZLE_STEPS = 12;
|
|
@@ -37,18 +70,19 @@ const PUZZLE_MOVES = [
|
|
|
37
70
|
[0, 0, -1],
|
|
38
71
|
];
|
|
39
72
|
/** Block positions after each step of one shuffle cycle, starting at home. Deterministic per cycle. */
|
|
40
|
-
function shuffleSteps(cycle) {
|
|
41
|
-
|
|
73
|
+
function shuffleSteps(model, cycle) {
|
|
74
|
+
const { columns, rows } = model;
|
|
75
|
+
let positions = model.blocks.map((block) => block.home);
|
|
42
76
|
const steps = [positions];
|
|
43
77
|
const lastMoved = new Set();
|
|
44
78
|
let random = cycle * 7_919 + 1;
|
|
45
79
|
const next = () => hash(random++);
|
|
46
|
-
const key = ([x, y, z]) => (z + 1) *
|
|
80
|
+
const key = ([x, y, z]) => ((z + 1) * rows + y) * columns + x;
|
|
47
81
|
for (let step = 0; step < PUZZLE_STEPS; step++) {
|
|
48
82
|
const occupied = new Set(positions.map(key));
|
|
49
83
|
const nextPositions = [...positions];
|
|
50
84
|
const moved = new Set();
|
|
51
|
-
const moveCount = next()
|
|
85
|
+
const moveCount = model.puzzleMoves(next());
|
|
52
86
|
for (let move = 0; move < moveCount; move++) {
|
|
53
87
|
const candidates = [];
|
|
54
88
|
for (let block = 0; block < positions.length; block++) {
|
|
@@ -57,7 +91,7 @@ function shuffleSteps(cycle) {
|
|
|
57
91
|
const [x, y, z] = positions[block];
|
|
58
92
|
for (const [dx, dy, dz] of PUZZLE_MOVES) {
|
|
59
93
|
const target = [x + dx, y + dy, z + dz];
|
|
60
|
-
if (target[0] < 0 || target[0]
|
|
94
|
+
if (target[0] < 0 || target[0] >= columns || target[1] < 0 || target[1] >= rows)
|
|
61
95
|
continue;
|
|
62
96
|
if (target[2] < -1 || target[2] > 1 || occupied.has(key(target)))
|
|
63
97
|
continue;
|
|
@@ -335,11 +369,11 @@ function spinAngle(time) {
|
|
|
335
369
|
return phase - SPIN_LINGER * Math.sin(phase);
|
|
336
370
|
}
|
|
337
371
|
/**
|
|
338
|
-
* Renders the
|
|
372
|
+
* Renders the model's blocks into braille cells. Only faces that point at the camera and are not covered by a
|
|
339
373
|
* touching block are drawn. Each face is rasterized over its projected bounds by intersecting each dot's ray
|
|
340
374
|
* with the face's plane, with a depth buffer resolving overlaps. Buffers are reused between frames.
|
|
341
375
|
*/
|
|
342
|
-
class
|
|
376
|
+
class BlockRaster {
|
|
343
377
|
/** Braille dot bits per cell. */
|
|
344
378
|
bits = new Uint8Array(0);
|
|
345
379
|
/** Lit dots per cell. */
|
|
@@ -355,11 +389,15 @@ class LogoRaster {
|
|
|
355
389
|
/** Ray parameter of the nearest hit per dot; smaller is nearer. */
|
|
356
390
|
depth = new Float32Array(0);
|
|
357
391
|
/** Face index + 1 of the nearest hit per dot, 0 for none. */
|
|
358
|
-
faceIds = new
|
|
392
|
+
faceIds = new Uint16Array(0);
|
|
359
393
|
/** Cells written by the previous frame, cleared before the next one. */
|
|
360
394
|
dirty;
|
|
361
395
|
/** Cells with a halo from the previous frame, cleared before the next one. */
|
|
362
396
|
haloDirty;
|
|
397
|
+
cameraDistance;
|
|
398
|
+
constructor(cameraDistance) {
|
|
399
|
+
this.cameraDistance = cameraDistance;
|
|
400
|
+
}
|
|
363
401
|
render(width, height, pose, boxes, background, haloStrength) {
|
|
364
402
|
const dotWidth = width * 2;
|
|
365
403
|
const dotHeight = height * 4;
|
|
@@ -373,7 +411,7 @@ class LogoRaster {
|
|
|
373
411
|
this.haloRgb = new Float32Array(width * height * 3);
|
|
374
412
|
this.haloDirty = undefined;
|
|
375
413
|
this.depth = new Float32Array(dotWidth * dotHeight).fill(Infinity);
|
|
376
|
-
this.faceIds = new
|
|
414
|
+
this.faceIds = new Uint16Array(dotWidth * dotHeight);
|
|
377
415
|
this.dirty = undefined;
|
|
378
416
|
}
|
|
379
417
|
else if (this.dirty) {
|
|
@@ -395,8 +433,9 @@ class LogoRaster {
|
|
|
395
433
|
}
|
|
396
434
|
const m = rotation(pose.yaw, pose.pitch, pose.roll);
|
|
397
435
|
const { centerX, centerY, scale } = pose;
|
|
398
|
-
|
|
399
|
-
|
|
436
|
+
const cameraDistance = this.cameraDistance;
|
|
437
|
+
// The camera sits at (0, 0, cameraDistance) in camera space; object space is the transpose rotation.
|
|
438
|
+
const origin = [m[6] * cameraDistance, m[7] * cameraDistance, m[8] * cameraDistance];
|
|
400
439
|
const light = isLight(background);
|
|
401
440
|
const faces = this.visibleFaces(m, origin, pose, boxes, dotWidth, dotHeight, light);
|
|
402
441
|
if (faces.length === 0)
|
|
@@ -430,9 +469,9 @@ class LogoRaster {
|
|
|
430
469
|
const sx = (face.minX + 0.5 - centerX) / scale;
|
|
431
470
|
for (let dotY = face.minY; dotY <= face.maxY; dotY++) {
|
|
432
471
|
const sy = (dotY + 0.5 - centerY) / scale;
|
|
433
|
-
let directionA = m[a] * sx + m[3 + a] * sy - m[6 + a] *
|
|
434
|
-
let directionU = m[u] * sx + m[3 + u] * sy - m[6 + u] *
|
|
435
|
-
let directionV = m[v] * sx + m[3 + v] * sy - m[6 + v] *
|
|
472
|
+
let directionA = m[a] * sx + m[3 + a] * sy - m[6 + a] * cameraDistance;
|
|
473
|
+
let directionU = m[u] * sx + m[3 + u] * sy - m[6 + u] * cameraDistance;
|
|
474
|
+
let directionV = m[v] * sx + m[3 + v] * sy - m[6 + v] * cameraDistance;
|
|
436
475
|
let index = dotY * dotWidth + face.minX;
|
|
437
476
|
for (let dotX = face.minX; dotX <= face.maxX; dotX++, index++) {
|
|
438
477
|
const t = (face.plane - originA) / directionA;
|
|
@@ -466,7 +505,7 @@ class LogoRaster {
|
|
|
466
505
|
continue;
|
|
467
506
|
const face = faces[id - 1];
|
|
468
507
|
// Points farther from the camera fade slightly toward the background for depth.
|
|
469
|
-
const fog = clamp01(0.15 -
|
|
508
|
+
const fog = clamp01(0.15 - cameraDistance * (1 - depth[index]) * 0.12) * (light ? 0.5 : 1);
|
|
470
509
|
faceIds[index] = 0;
|
|
471
510
|
depth[index] = Infinity;
|
|
472
511
|
const cellX = dotX >> 1;
|
|
@@ -586,6 +625,7 @@ class LogoRaster {
|
|
|
586
625
|
}
|
|
587
626
|
visibleFaces(m, origin, pose, boxes, dotWidth, dotHeight, lightBackground) {
|
|
588
627
|
const faces = [];
|
|
628
|
+
const cameraDistance = this.cameraDistance;
|
|
589
629
|
for (const box of boxes) {
|
|
590
630
|
for (let a = 0; a < 3; a++) {
|
|
591
631
|
const u = (a + 1) % 3;
|
|
@@ -617,7 +657,7 @@ class LogoRaster {
|
|
|
617
657
|
const x = m[0] * corner[0] + m[1] * corner[1] + m[2] * corner[2];
|
|
618
658
|
const y = m[3] * corner[0] + m[4] * corner[1] + m[5] * corner[2];
|
|
619
659
|
const z = m[6] * corner[0] + m[7] * corner[1] + m[8] * corner[2];
|
|
620
|
-
const perspective = (pose.scale *
|
|
660
|
+
const perspective = (pose.scale * cameraDistance) / (cameraDistance - z);
|
|
621
661
|
const screenX = pose.centerX + x * perspective;
|
|
622
662
|
const screenY = pose.centerY + y * perspective;
|
|
623
663
|
minX = Math.min(minX, screenX);
|
|
@@ -672,7 +712,7 @@ let playing = false;
|
|
|
672
712
|
* Show the animation as a fullscreen overlay until it is dismissed. The overlay takes focus and mouse input and
|
|
673
713
|
* returns focus when hidden, so the rest of the UI keeps running underneath untouched.
|
|
674
714
|
*/
|
|
675
|
-
export async function
|
|
715
|
+
export async function playEasterEgg3d(tui, screen, egg) {
|
|
676
716
|
if (playing)
|
|
677
717
|
return;
|
|
678
718
|
playing = true;
|
|
@@ -688,15 +728,18 @@ export async function playPiLogoAnimation(tui, options) {
|
|
|
688
728
|
playing = false;
|
|
689
729
|
return;
|
|
690
730
|
}
|
|
691
|
-
const
|
|
731
|
+
const model = egg.kind === "armin" ? arminModel(toRgb(theme.colors.accent)) : piLogoModel({ column: egg.column, row: egg.row });
|
|
732
|
+
const animation = new EasterEgg3dAnimation(tui, screen, model, colors, () => {
|
|
692
733
|
playing = false;
|
|
693
734
|
overlay.hide();
|
|
694
735
|
});
|
|
695
736
|
const overlay = tui.showOverlay(animation, { anchor: "top-left", width: "100%", maxHeight: "100%" });
|
|
696
737
|
}
|
|
697
|
-
export class
|
|
738
|
+
export class EasterEgg3dAnimation {
|
|
698
739
|
tui;
|
|
699
|
-
|
|
740
|
+
/** The screen to dissolve, as rendered lines. */
|
|
741
|
+
screen;
|
|
742
|
+
model;
|
|
700
743
|
foreground;
|
|
701
744
|
background;
|
|
702
745
|
onDone;
|
|
@@ -711,10 +754,12 @@ export class PiLogoAnimation {
|
|
|
711
754
|
stars = [];
|
|
712
755
|
cells = [];
|
|
713
756
|
ansiCache = new Map();
|
|
714
|
-
|
|
715
|
-
constructor(tui,
|
|
757
|
+
raster;
|
|
758
|
+
constructor(tui, screen, model, colors, onDone) {
|
|
716
759
|
this.tui = tui;
|
|
717
|
-
this.
|
|
760
|
+
this.screen = screen;
|
|
761
|
+
this.model = model;
|
|
762
|
+
this.raster = new BlockRaster(model.cameraDistance);
|
|
718
763
|
this.foreground = colors.foreground;
|
|
719
764
|
this.background = colors.background;
|
|
720
765
|
this.onDone = onDone;
|
|
@@ -799,19 +844,20 @@ export class PiLogoAnimation {
|
|
|
799
844
|
starAlpha = this.starAlpha(time);
|
|
800
845
|
offsets = this.blockOffsets(time);
|
|
801
846
|
}
|
|
802
|
-
const
|
|
847
|
+
const { columns, rows } = this.model;
|
|
848
|
+
const boxes = this.model.blocks.map((block, index) => {
|
|
803
849
|
const [dx, dy, dz] = offsets[index];
|
|
804
|
-
const x = block.home[0] - 2 + dx;
|
|
805
|
-
const y = block.home[1] - 2 + dy;
|
|
850
|
+
const x = block.home[0] - columns / 2 + dx;
|
|
851
|
+
const y = block.home[1] - rows / 2 + dy;
|
|
806
852
|
return { min: [x, y, dz - DEPTH / 2], max: [x + 1, y + 1, dz + DEPTH / 2], color: block.color };
|
|
807
853
|
});
|
|
808
|
-
const
|
|
809
|
-
|
|
854
|
+
const raster = this.raster;
|
|
855
|
+
raster.render(width, height, pose, boxes, background, isLight(background) ? LIGHT_HALO_STRENGTH : 0);
|
|
810
856
|
const halo = (index, base) => {
|
|
811
|
-
const amount =
|
|
857
|
+
const amount = raster.haloAmount[index];
|
|
812
858
|
if (amount <= 0)
|
|
813
859
|
return base;
|
|
814
|
-
const color =
|
|
860
|
+
const color = raster.haloRgb;
|
|
815
861
|
return mix(base ?? background, [color[index * 3], color[index * 3 + 1], color[index * 3 + 2]], amount);
|
|
816
862
|
};
|
|
817
863
|
const backgroundFade = smooth(dissolveTime / BACKGROUND_FADE);
|
|
@@ -865,11 +911,11 @@ export class PiLogoAnimation {
|
|
|
865
911
|
emit(hint.text[offset], offset < hint.keyLength ? hint.keyColor : hint.color, cellBg);
|
|
866
912
|
continue;
|
|
867
913
|
}
|
|
868
|
-
const dots =
|
|
914
|
+
const dots = raster.bits[index];
|
|
869
915
|
if (dots) {
|
|
870
916
|
// Quantized so neighboring cells on one face usually share a color and its escape sequence.
|
|
871
|
-
const step = LOGO_COLOR_STEP *
|
|
872
|
-
const rgb =
|
|
917
|
+
const step = LOGO_COLOR_STEP * raster.counts[index];
|
|
918
|
+
const rgb = raster.rgb;
|
|
873
919
|
const color = [
|
|
874
920
|
Math.round(rgb[index * 3] / step) * LOGO_COLOR_STEP,
|
|
875
921
|
Math.round(rgb[index * 3 + 1] / step) * LOGO_COLOR_STEP,
|
|
@@ -884,7 +930,7 @@ export class PiLogoAnimation {
|
|
|
884
930
|
continue;
|
|
885
931
|
}
|
|
886
932
|
const dust = (dissolveTime - cell.delay) / DUST_DURATION;
|
|
887
|
-
if (dust < 0 && cell.width === 2 && !
|
|
933
|
+
if (dust < 0 && cell.width === 2 && !raster.bits[index + 1]) {
|
|
888
934
|
emit(cell.text, mix(cell.fg, background, textFade), cellBg);
|
|
889
935
|
column++;
|
|
890
936
|
continue;
|
|
@@ -929,18 +975,19 @@ export class PiLogoAnimation {
|
|
|
929
975
|
this.timer = undefined;
|
|
930
976
|
this.onDone();
|
|
931
977
|
}
|
|
932
|
-
/** Each block's displacement from its place in the
|
|
978
|
+
/** Each block's displacement from its place in the model at `time`, in grid units. */
|
|
933
979
|
blockOffsets(time) {
|
|
934
|
-
const
|
|
980
|
+
const blocks = this.model.blocks;
|
|
981
|
+
const home = () => blocks.map(() => [0, 0, 0]);
|
|
935
982
|
if (time < PUZZLE_START)
|
|
936
983
|
return home();
|
|
937
984
|
const cycle = Math.floor((time - PUZZLE_START) / PUZZLE_CYCLE);
|
|
938
985
|
const local = time - PUZZLE_START - cycle * PUZZLE_CYCLE;
|
|
939
986
|
if (this.shuffle?.cycle !== cycle)
|
|
940
|
-
this.shuffle = { cycle, steps: shuffleSteps(cycle) };
|
|
987
|
+
this.shuffle = { cycle, steps: shuffleSteps(this.model, cycle) };
|
|
941
988
|
const steps = this.shuffle.steps;
|
|
942
989
|
const offset = (position, index) => {
|
|
943
|
-
const block =
|
|
990
|
+
const block = blocks[index].home;
|
|
944
991
|
return [position[0] - block[0], position[1] - block[1], position[2] - block[2]];
|
|
945
992
|
};
|
|
946
993
|
const shuffleEnd = PUZZLE_STEP * PUZZLE_STEPS;
|
|
@@ -975,14 +1022,16 @@ export class PiLogoAnimation {
|
|
|
975
1022
|
return smooth((time - FLY_START - FLY_DURATION) / HINT_FADE);
|
|
976
1023
|
}
|
|
977
1024
|
pose(width, height, progress, time) {
|
|
978
|
-
|
|
979
|
-
const
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
const
|
|
983
|
-
const
|
|
1025
|
+
const { columns, rows, cameraDistance, radius, widthShare, origin } = this.model;
|
|
1026
|
+
const reach = radius * (cameraDistance / (cameraDistance - radius));
|
|
1027
|
+
// Lifting off, the front face must cover exactly the half-block cells (2x2 dots per pixel) despite the
|
|
1028
|
+
// perspective.
|
|
1029
|
+
const startScale = origin ? (2 * (cameraDistance - DEPTH / 2)) / cameraDistance : START_SCALE;
|
|
1030
|
+
const endScale = Math.max(startScale, Math.min(width * 2 * widthShare, (height * 4 - 8) * 0.48) / reach);
|
|
984
1031
|
const endX = width;
|
|
985
1032
|
const endY = height * 2 - 2;
|
|
1033
|
+
const startX = origin ? origin.column * 2 + columns : endX;
|
|
1034
|
+
const startY = origin ? origin.row * 4 + rows : endY;
|
|
986
1035
|
return {
|
|
987
1036
|
centerX: startX + (endX - startX) * progress,
|
|
988
1037
|
centerY: startY + (endY - startY) * progress,
|
|
@@ -1028,10 +1077,12 @@ export class PiLogoAnimation {
|
|
|
1028
1077
|
return stars;
|
|
1029
1078
|
}
|
|
1030
1079
|
prepareCells(width, foreground) {
|
|
1031
|
-
const cells = parseScreen(this.
|
|
1032
|
-
const centerX = this.options.logoColumn + 2;
|
|
1033
|
-
const centerY = this.options.logoRow + 1;
|
|
1080
|
+
const cells = parseScreen(this.screen, width, foreground, this.background);
|
|
1034
1081
|
const height = Math.max(1, this.tui.terminal.rows);
|
|
1082
|
+
const { columns, rows, origin } = this.model;
|
|
1083
|
+
// The dust spreads out from where the model starts.
|
|
1084
|
+
const centerX = origin ? origin.column + columns / 2 : width / 2;
|
|
1085
|
+
const centerY = origin ? origin.row + rows / 4 : height / 2;
|
|
1035
1086
|
const farthest = Math.hypot(Math.max(centerX, width - centerX), Math.max(centerY, height - centerY) * 2);
|
|
1036
1087
|
for (let row = 0; row < cells.length; row++) {
|
|
1037
1088
|
const line = cells[row];
|
|
@@ -1051,18 +1102,20 @@ export class PiLogoAnimation {
|
|
|
1051
1102
|
cell.ink = glyphInk(owner.text, seed);
|
|
1052
1103
|
}
|
|
1053
1104
|
}
|
|
1054
|
-
// The 3D
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
cell
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1105
|
+
// The 3D model replaces its half-block rendering on screen.
|
|
1106
|
+
if (origin) {
|
|
1107
|
+
for (let row = origin.row; row < origin.row + Math.ceil(rows / 2); row++) {
|
|
1108
|
+
const line = cells[row];
|
|
1109
|
+
if (!line)
|
|
1110
|
+
continue;
|
|
1111
|
+
for (let column = origin.column; column < origin.column + columns; column++) {
|
|
1112
|
+
const cell = line[column];
|
|
1113
|
+
if (cell) {
|
|
1114
|
+
cell.text = " ";
|
|
1115
|
+
cell.width = 1;
|
|
1116
|
+
cell.ink = 0;
|
|
1117
|
+
cell.bg = undefined;
|
|
1118
|
+
}
|
|
1066
1119
|
}
|
|
1067
1120
|
}
|
|
1068
1121
|
}
|
|
@@ -1083,4 +1136,4 @@ export class PiLogoAnimation {
|
|
|
1083
1136
|
return value;
|
|
1084
1137
|
}
|
|
1085
1138
|
}
|
|
1086
|
-
//# sourceMappingURL=
|
|
1139
|
+
//# sourceMappingURL=easter-egg-3d.js.map
|