From 1c4b29794107e420ff8c46764e5b833cf02ad548 Mon Sep 17 00:00:00 2001
From: Bret Comnes
Date: Sun, 13 Sep 2026 15:30:01 -0700
Subject: [PATCH 1/5] refactor: scope DOMStack data and migrate tooling to
TypeScript
---
.github/workflows/docs-audit.yml | 7 +-
.github/workflows/pages.yml | 13 +-
README.md | 40 +-
docs/migration.md | 13 +-
package.json | 3 +-
playwright.config.ts | 2 +-
runtime/tools/audit-site.py | 212 -
runtime/tools/generate-js-api-reference.py | 318 --
runtime/tools/generate-js-api-reference.ts | 132 +
runtime/tools/js-api-reference-content.ts | 409 ++
runtime/tools/js_api_reference_content.py | 1256 -----
sage/tools/audit-site.py | 32 -
silk/tools/audit-site.py | 130 -
silk/tools/audit-stdlib-docs.py | 134 -
silk/tools/sync-from-silk-docs.py | 767 ---
silk/tools/sync-from-silk-docs.ts | 88 +
slg/tools/audit-site.py | 32 -
src/artifacts.template.ts | 86 +-
src/docs/client.ts | 3 +
src/global.data.ts | 49 +-
src/globals/global.client.js | 214 -
src/globals/global.client.ts | 2 +
src/globals/global.css | 4158 +++++------------
src/layouts/docs.layout.client.ts | 21 +-
src/layouts/docs.layout.ts | 97 +-
src/layouts/learn.layout.client.ts | 4 +-
src/layouts/learn.layout.css | 732 +++
src/layouts/product.layout.client.ts | 1 -
src/layouts/root.layout.ts | 41 +-
src/layouts/runtime-docs.layout.ts | 10 +
src/layouts/sage-docs.layout.ts | 10 +
src/layouts/silk-docs.layout.ts | 10 +
src/layouts/silkWiki-docs.layout.ts | 10 +
src/layouts/slg-docs.layout.ts | 10 +
src/layouts/spec.layout.client.ts | 3 +
src/layouts/spec.layout.ts | 21 +-
src/layouts/virtnosis-docs.layout.ts | 10 +
src/lib/article-client.ts | 2 +-
src/lib/artifacts.ts | 28 +
src/lib/ask-ai-client.ts | 165 +
src/lib/{clipboard.js => clipboard.ts} | 2 +-
src/lib/docs.ts | 78 +-
src/lib/learn-client.js | 151 -
src/lib/learn-client.ts | 127 +
src/lib/markdown.ts | 14 +-
src/lib/navigation.ts | 6 +-
src/lib/rendering.ts | 69 +
src/lib/{silk.js => silk.ts} | 6 +-
src/lib/{tabs.js => tabs.ts} | 18 +-
src/lib/{toml.js => toml.ts} | 4 +-
src/lib/{zig.js => zig.ts} | 4 +-
src/markdown-it.settings.ts | 2 +
src/runtime-index.template.ts | 6 +
src/runtime-llms.template.ts | 6 +
src/runtime-search.template.ts | 6 +
src/runtime-sources.template.ts | 6 +
src/runtime/docs/cli/build/page.md | 2 +-
src/runtime/docs/cli/config/page.md | 2 +-
src/runtime/docs/cli/env/page.md | 2 +-
src/runtime/docs/cli/help/page.md | 2 +-
src/runtime/docs/cli/init/page.md | 2 +-
src/runtime/docs/cli/install-app/page.md | 2 +-
src/runtime/docs/cli/list-devices/page.md | 2 +-
src/runtime/docs/cli/mcp/page.md | 2 +-
src/runtime/docs/cli/oroc/page.md | 2 +-
src/runtime/docs/cli/print-build-dir/page.md | 2 +-
src/runtime/docs/cli/run/page.md | 2 +-
src/runtime/docs/cli/setup/page.md | 2 +-
src/runtime/docs/cli/update/bundle/page.md | 2 +-
src/runtime/docs/cli/update/extract/page.md | 2 +-
src/runtime/docs/cli/update/info/page.md | 2 +-
src/runtime/docs/cli/update/init/page.md | 2 +-
src/runtime/docs/cli/update/keygen/page.md | 2 +-
src/runtime/docs/cli/update/page.md | 2 +-
src/runtime/docs/cli/update/server/page.md | 2 +-
src/runtime/docs/cli/update/sign/page.md | 2 +-
src/runtime/docs/cli/update/validate/page.md | 2 +-
src/runtime/docs/cli/update/verify/page.md | 2 +-
src/runtime/docs/cli/version/page.md | 2 +-
src/runtime/docs/cli/versions/page.md | 2 +-
src/runtime/docs/config/copy-map/page.md | 2 +-
src/runtime/docs/config/overview/page.md | 2 +-
src/runtime/docs/config/reference/page.md | 2 +-
.../docs/guides/build-and-package/page.md | 2 +-
.../docs/guides/calling-http-apis/page.md | 2 +-
.../configure-your-runtime-project/page.md | 2 +-
.../custom-protocols-and-routing/page.md | 2 +-
.../docs/guides/desktop-integrations/page.md | 2 +-
.../docs/guides/files-and-sandboxing/page.md | 2 +-
.../guides/from-zero-to-first-run/page.md | 2 +-
.../guides/frontend-build-pipeline/page.md | 2 +-
src/runtime/docs/guides/hello-world/page.md | 2 +-
.../lifecycle-hooks-and-deep-links/page.md | 2 +-
.../guides/local-data-with-sqlite/page.md | 2 +-
.../guides/mcp-and-agent-automation/page.md | 2 +-
.../page.md | 2 +-
.../docs/guides/native-extensions/page.md | 2 +-
.../page.md | 2 +-
.../docs/guides/project-layout/page.md | 2 +-
.../page.md | 2 +-
.../secure-storage-and-sessions/page.md | 2 +-
.../shared-workers-across-windows/page.md | 2 +-
.../guides/testing-and-diagnostics/page.md | 2 +-
.../docs/guides/windows-and-messaging/page.md | 2 +-
.../worker-threads-for-heavy-work/page.md | 2 +-
src/runtime/docs/javascript/ai/page.md | 2 +-
.../docs/javascript/all-modules/page.md | 2 +-
.../docs/javascript/application/page.md | 2 +-
src/runtime/docs/javascript/asn1/page.md | 2 +-
src/runtime/docs/javascript/assert/page.md | 2 +-
src/runtime/docs/javascript/async/page.md | 2 +-
.../docs/javascript/async_hooks/page.md | 2 +-
.../docs/javascript/background/page.md | 2 +-
src/runtime/docs/javascript/buffer/page.md | 2 +-
src/runtime/docs/javascript/cdp/page.md | 2 +-
.../docs/javascript/child_process/page.md | 2 +-
src/runtime/docs/javascript/clipboard/page.md | 2 +-
src/runtime/docs/javascript/commonjs/page.md | 2 +-
src/runtime/docs/javascript/conduit/page.md | 2 +-
src/runtime/docs/javascript/console/page.md | 2 +-
src/runtime/docs/javascript/constants/page.md | 2 +-
src/runtime/docs/javascript/cookies/page.md | 2 +-
src/runtime/docs/javascript/crypto/page.md | 2 +-
src/runtime/docs/javascript/dbus/page.md | 2 +-
src/runtime/docs/javascript/dgram/page.md | 2 +-
.../docs/javascript/diagnostics/page.md | 2 +-
src/runtime/docs/javascript/did/page.md | 2 +-
src/runtime/docs/javascript/dns/page.md | 2 +-
.../docs/javascript/enumeration/page.md | 2 +-
src/runtime/docs/javascript/errno/page.md | 2 +-
src/runtime/docs/javascript/errors/page.md | 2 +-
src/runtime/docs/javascript/events/page.md | 2 +-
src/runtime/docs/javascript/extension/page.md | 2 +-
src/runtime/docs/javascript/fetch/page.md | 2 +-
src/runtime/docs/javascript/fs/page.md | 2 +-
src/runtime/docs/javascript/gc/page.md | 2 +-
src/runtime/docs/javascript/hci/page.md | 2 +-
src/runtime/docs/javascript/hooks/page.md | 2 +-
src/runtime/docs/javascript/http/page.md | 2 +-
src/runtime/docs/javascript/https/page.md | 2 +-
src/runtime/docs/javascript/i18n/page.md | 2 +-
src/runtime/docs/javascript/ip/page.md | 2 +-
src/runtime/docs/javascript/ipc/page.md | 2 +-
src/runtime/docs/javascript/ipfs/page.md | 2 +-
src/runtime/docs/javascript/iroh/page.md | 2 +-
src/runtime/docs/javascript/language/page.md | 2 +-
src/runtime/docs/javascript/latica/page.md | 2 +-
src/runtime/docs/javascript/location/page.md | 2 +-
src/runtime/docs/javascript/mcp/page.md | 2 +-
src/runtime/docs/javascript/mime/page.md | 2 +-
src/runtime/docs/javascript/module/page.md | 2 +-
.../docs/javascript/navigation/page.md | 2 +-
src/runtime/docs/javascript/net/page.md | 2 +-
src/runtime/docs/javascript/network/page.md | 2 +-
.../docs/javascript/notification/page.md | 2 +-
src/runtime/docs/javascript/npm/page.md | 2 +-
src/runtime/docs/javascript/os/page.md | 2 +-
src/runtime/docs/javascript/overview/page.md | 2 +-
src/runtime/docs/javascript/path/page.md | 2 +-
src/runtime/docs/javascript/process/page.md | 2 +-
.../docs/javascript/protocol-handlers/page.md | 2 +-
.../docs/javascript/querystring/page.md | 2 +-
.../docs/javascript/secure-storage/page.md | 2 +-
src/runtime/docs/javascript/semver/page.md | 2 +-
.../docs/javascript/service-worker/page.md | 2 +-
.../docs/javascript/shared-worker/page.md | 2 +-
src/runtime/docs/javascript/signal/page.md | 2 +-
src/runtime/docs/javascript/sqlite/page.md | 2 +-
src/runtime/docs/javascript/stream/page.md | 2 +-
.../docs/javascript/string_decoder/page.md | 2 +-
src/runtime/docs/javascript/tar/page.md | 2 +-
src/runtime/docs/javascript/tcp/page.md | 2 +-
src/runtime/docs/javascript/test/page.md | 2 +-
src/runtime/docs/javascript/timers/page.md | 2 +-
src/runtime/docs/javascript/tls/page.md | 2 +-
src/runtime/docs/javascript/toml/page.md | 2 +-
src/runtime/docs/javascript/tty/page.md | 2 +-
src/runtime/docs/javascript/url/page.md | 2 +-
src/runtime/docs/javascript/usb/page.md | 2 +-
src/runtime/docs/javascript/util/page.md | 2 +-
src/runtime/docs/javascript/vm/page.md | 2 +-
src/runtime/docs/javascript/window/page.md | 2 +-
src/runtime/docs/javascript/worker/page.md | 2 +-
.../docs/javascript/worker_threads/page.md | 2 +-
src/runtime/docs/javascript/xpc/page.md | 2 +-
src/runtime/docs/javascript/zlib/page.md | 2 +-
src/runtime/docs/page.md | 2 +-
src/sage-index.template.ts | 6 +
src/sage-llms.template.ts | 6 +
src/sage-search.template.ts | 6 +
src/sage-sources.template.ts | 6 +
src/sage/docs/cli/configuration/page.md | 2 +-
src/sage/docs/cli/diagnostics/page.md | 2 +-
.../docs/cli/environment-and-files/page.md | 2 +-
src/sage/docs/cli/inputs-and-modes/page.md | 2 +-
src/sage/docs/cli/keys-and-commands/page.md | 2 +-
src/sage/docs/cli/options/page.md | 2 +-
src/sage/docs/cli/sage/page.md | 2 +-
src/sage/docs/cli/syntax-and-cache/page.md | 2 +-
src/sage/docs/guides/getting-started/page.md | 2 +-
src/sage/docs/guides/pager-workflows/page.md | 2 +-
.../docs/guides/plugins-quickstart/page.md | 2 +-
src/sage/docs/javascript/all-modules/page.md | 2 +-
src/sage/docs/javascript/crypto/page.md | 2 +-
src/sage/docs/javascript/dom/page.md | 2 +-
src/sage/docs/javascript/env/page.md | 2 +-
src/sage/docs/javascript/fetch/page.md | 2 +-
src/sage/docs/javascript/fs/page.md | 2 +-
src/sage/docs/javascript/module-index/page.md | 2 +-
src/sage/docs/javascript/navigator/page.md | 2 +-
src/sage/docs/javascript/overview/page.md | 2 +-
src/sage/docs/javascript/path/page.md | 2 +-
src/sage/docs/javascript/performance/page.md | 2 +-
src/sage/docs/javascript/process/page.md | 2 +-
src/sage/docs/javascript/runtime/page.md | 2 +-
src/sage/docs/javascript/url/page.md | 2 +-
src/sage/docs/javascript/uuid/page.md | 2 +-
src/sage/docs/javascript/web/page.md | 2 +-
src/sage/docs/man/sage-1/page.md | 2 +-
src/sage/docs/man/sage-7/page.md | 2 +-
src/sage/docs/man/sage-plugin-api-3/page.md | 2 +-
src/sage/docs/page.md | 2 +-
src/silk-index.template.ts | 6 +
src/silk-llms.template.ts | 6 +
src/silk-search.template.ts | 6 +
src/silk-sources.template.ts | 6 +
src/silk/docs/compiler/abi-libsilk/page.md | 2 +-
src/silk/docs/compiler/architecture/page.md | 2 +-
src/silk/docs/compiler/async-runtime/page.md | 2 +-
src/silk/docs/compiler/backend-amdgpu/page.md | 2 +-
src/silk/docs/compiler/backend-gpu/page.md | 2 +-
src/silk/docs/compiler/backend-nvidia/page.md | 2 +-
src/silk/docs/compiler/backend-wasm/page.md | 2 +-
src/silk/docs/compiler/build-scripts/page.md | 2 +-
src/silk/docs/compiler/builtin-deps/page.md | 2 +-
src/silk/docs/compiler/cache-silk/page.md | 2 +-
src/silk/docs/compiler/cli-silk/page.md | 2 +-
src/silk/docs/compiler/diagnostics/page.md | 2 +-
.../compiler/implementation-status/page.md | 2 +-
src/silk/docs/compiler/ir-overview/page.md | 2 +-
.../docs/compiler/libsilk-quickstart/page.md | 2 +-
src/silk/docs/compiler/limits/page.md | 2 +-
src/silk/docs/compiler/lsp-silk/page.md | 2 +-
.../compiler/package-distribution/page.md | 2 +-
.../docs/compiler/package-manifests/page.md | 2 +-
.../docs/compiler/stdlib-integration/page.md | 2 +-
.../docs/compiler/testing-strategy/page.md | 2 +-
src/silk/docs/compiler/vendored-deps/page.md | 2 +-
src/silk/docs/compiler/zig-api/page.md | 2 +-
src/silk/docs/guides/cli/page.md | 2 +-
src/silk/docs/guides/formal-silk/page.md | 2 +-
src/silk/docs/guides/hello-world/page.md | 2 +-
src/silk/docs/guides/language-tour/page.md | 2 +-
.../docs/guides/modules-and-packages/page.md | 2 +-
.../guides/practical-logger-module/page.md | 2 +-
.../docs/guides/project-dependencies/page.md | 2 +-
src/silk/docs/guides/purpose/page.md | 2 +-
src/silk/docs/guides/standard-library/page.md | 2 +-
src/silk/docs/guides/testing/page.md | 2 +-
src/silk/docs/language/asm/page.md | 2 +-
src/silk/docs/language/atomics/page.md | 2 +-
src/silk/docs/language/attributes/page.md | 2 +-
src/silk/docs/language/borrow-checker/page.md | 2 +-
src/silk/docs/language/buffers/page.md | 2 +-
src/silk/docs/language/build-metadata/page.md | 2 +-
src/silk/docs/language/cheat-sheet/page.md | 2 +-
src/silk/docs/language/concurrency/page.md | 2 +-
.../docs/language/const-functions/page.md | 2 +-
src/silk/docs/language/conventions/page.md | 2 +-
.../docs/language/dependent-types/page.md | 2 +-
src/silk/docs/language/doc-comments/page.md | 2 +-
.../docs/language/duration-instant/page.md | 2 +-
src/silk/docs/language/enums/page.md | 2 +-
src/silk/docs/language/errors/page.md | 2 +-
src/silk/docs/language/ext/page.md | 2 +-
.../language/flow-blocks-statements/page.md | 2 +-
src/silk/docs/language/flow-break/page.md | 2 +-
src/silk/docs/language/flow-continue/page.md | 2 +-
.../flow-expression-statements/page.md | 2 +-
src/silk/docs/language/flow-for/page.md | 2 +-
src/silk/docs/language/flow-if-else/page.md | 2 +-
src/silk/docs/language/flow-loop/page.md | 2 +-
src/silk/docs/language/flow-match/page.md | 2 +-
src/silk/docs/language/flow-overview/page.md | 2 +-
src/silk/docs/language/flow-return/page.md | 2 +-
src/silk/docs/language/flow-while/page.md | 2 +-
.../docs/language/formal-verification/page.md | 2 +-
.../language/function-disciplines/page.md | 2 +-
src/silk/docs/language/generics/page.md | 2 +-
src/silk/docs/language/gpu-execution/page.md | 2 +-
.../docs/language/gpu-launch-blocks/page.md | 2 +-
src/silk/docs/language/grammar/page.md | 2 +-
src/silk/docs/language/interfaces/page.md | 2 +-
.../docs/language/literals-aggregate/page.md | 2 +-
.../docs/language/literals-boolean/page.md | 2 +-
.../docs/language/literals-character/page.md | 2 +-
.../docs/language/literals-duration/page.md | 2 +-
.../docs/language/literals-numeric/page.md | 2 +-
.../docs/language/literals-overview/page.md | 2 +-
.../docs/language/literals-regexp/page.md | 2 +-
.../docs/language/literals-string/page.md | 2 +-
src/silk/docs/language/memory-model/page.md | 2 +-
src/silk/docs/language/mutability/page.md | 2 +-
src/silk/docs/language/operators/page.md | 2 +-
src/silk/docs/language/optional/page.md | 2 +-
.../language/packages-imports-exports/page.md | 2 +-
src/silk/docs/language/regions/page.md | 2 +-
.../docs/language/struct-requirements/page.md | 2 +-
.../language/structs-impls-layout/page.md | 2 +-
src/silk/docs/language/syntax-tour/page.md | 2 +-
.../docs/language/target-metadata/page.md | 2 +-
src/silk/docs/language/testing/page.md | 2 +-
.../docs/language/toolchain-metadata/page.md | 2 +-
src/silk/docs/language/type-unions/page.md | 2 +-
src/silk/docs/language/typed-errors/page.md | 2 +-
src/silk/docs/language/types/page.md | 2 +-
src/silk/docs/language/using/page.md | 2 +-
src/silk/docs/language/varargs/page.md | 2 +-
src/silk/docs/man/libsilk.7/page.md | 2 +-
src/silk/docs/man/silk-build.1/page.md | 2 +-
src/silk/docs/man/silk-cache.1/page.md | 2 +-
src/silk/docs/man/silk-cc.1/page.md | 2 +-
src/silk/docs/man/silk-check.1/page.md | 2 +-
src/silk/docs/man/silk-codesign.1/page.md | 2 +-
src/silk/docs/man/silk-devices.1/page.md | 2 +-
src/silk/docs/man/silk-doc.1/page.md | 2 +-
src/silk/docs/man/silk-env.1/page.md | 2 +-
src/silk/docs/man/silk-error.1/page.md | 2 +-
src/silk/docs/man/silk-format.1/page.md | 2 +-
src/silk/docs/man/silk-graph.1/page.md | 2 +-
src/silk/docs/man/silk-guide.1/page.md | 2 +-
src/silk/docs/man/silk-help.1/page.md | 2 +-
src/silk/docs/man/silk-lsp.1/page.md | 2 +-
src/silk/docs/man/silk-man.1/page.md | 2 +-
src/silk/docs/man/silk-package.1/page.md | 2 +-
src/silk/docs/man/silk-proto.1/page.md | 2 +-
src/silk/docs/man/silk-repl.1/page.md | 2 +-
src/silk/docs/man/silk-size.1/page.md | 2 +-
src/silk/docs/man/silk-targets.1/page.md | 2 +-
src/silk/docs/man/silk-test.1/page.md | 2 +-
src/silk/docs/man/silk.1/page.md | 2 +-
src/silk/docs/man/silk.7/page.md | 2 +-
.../docs/man/silk_abi_get_version.3/page.md | 2 +-
.../page.md | 2 +-
src/silk/docs/man/silk_bytes.3/page.md | 2 +-
src/silk/docs/man/silk_compiler.3/page.md | 2 +-
src/silk/docs/man/silk_error.3/page.md | 2 +-
src/silk/docs/man/slc.1/page.md | 2 +-
src/silk/docs/man/slcc.1/page.md | 2 +-
src/silk/docs/page.md | 2 +-
src/silk/docs/std/abort-controller/page.md | 2 +-
src/silk/docs/std/abort_controller/page.md | 2 +-
src/silk/docs/std/algorithms/page.md | 2 +-
src/silk/docs/std/args/page.md | 2 +-
src/silk/docs/std/arrays/page.md | 2 +-
src/silk/docs/std/atomic/page.md | 2 +-
src/silk/docs/std/bits/page.md | 2 +-
src/silk/docs/std/boolean/page.md | 2 +-
src/silk/docs/std/boxed-values/page.md | 2 +-
src/silk/docs/std/buffer/page.md | 2 +-
src/silk/docs/std/build/page.md | 2 +-
src/silk/docs/std/bytes/page.md | 2 +-
src/silk/docs/std/conventions/page.md | 2 +-
src/silk/docs/std/crypto-aead/page.md | 2 +-
src/silk/docs/std/crypto-box/page.md | 2 +-
src/silk/docs/std/crypto-hash/page.md | 2 +-
src/silk/docs/std/crypto-random/page.md | 2 +-
src/silk/docs/std/crypto-secretbox/page.md | 2 +-
src/silk/docs/std/crypto-sign/page.md | 2 +-
src/silk/docs/std/crypto/page.md | 2 +-
src/silk/docs/std/dylib/page.md | 2 +-
src/silk/docs/std/env/page.md | 2 +-
src/silk/docs/std/ffi-c/page.md | 2 +-
src/silk/docs/std/ffi-c_owned/page.md | 2 +-
src/silk/docs/std/filesystem/page.md | 2 +-
src/silk/docs/std/flag/page.md | 2 +-
src/silk/docs/std/fmt/page.md | 2 +-
src/silk/docs/std/formal/page.md | 2 +-
src/silk/docs/std/fs-stream/page.md | 2 +-
src/silk/docs/std/fs/page.md | 2 +-
src/silk/docs/std/function/page.md | 2 +-
src/silk/docs/std/ggml/page.md | 2 +-
src/silk/docs/std/gpu-device/page.md | 2 +-
src/silk/docs/std/gpu-isa/page.md | 2 +-
src/silk/docs/std/gpu/page.md | 2 +-
src/silk/docs/std/graphics-metal/page.md | 2 +-
src/silk/docs/std/graphics-opengl/page.md | 2 +-
src/silk/docs/std/graphics-opengles/page.md | 2 +-
src/silk/docs/std/graphics-vulkan/page.md | 2 +-
src/silk/docs/std/graphics-window/page.md | 2 +-
src/silk/docs/std/graphics/page.md | 2 +-
src/silk/docs/std/http/page.md | 2 +-
src/silk/docs/std/https/page.md | 2 +-
src/silk/docs/std/idl-web/page.md | 2 +-
src/silk/docs/std/image-color/page.md | 2 +-
src/silk/docs/std/image-jpeg/page.md | 2 +-
src/silk/docs/std/image-png/page.md | 2 +-
src/silk/docs/std/image/page.md | 2 +-
src/silk/docs/std/interfaces/page.md | 2 +-
src/silk/docs/std/io-async/page.md | 2 +-
src/silk/docs/std/io-stream/page.md | 2 +-
src/silk/docs/std/io/page.md | 2 +-
src/silk/docs/std/js-ecma/page.md | 2 +-
src/silk/docs/std/json/page.md | 2 +-
src/silk/docs/std/limits/page.md | 2 +-
src/silk/docs/std/list/page.md | 2 +-
src/silk/docs/std/map/page.md | 2 +-
src/silk/docs/std/math-matrix/page.md | 2 +-
src/silk/docs/std/math-vector/page.md | 2 +-
src/silk/docs/std/math/page.md | 2 +-
src/silk/docs/std/memory/page.md | 2 +-
src/silk/docs/std/mime/page.md | 2 +-
src/silk/docs/std/module-catalog/page.md | 2 +-
src/silk/docs/std/net-stream/page.md | 2 +-
src/silk/docs/std/net/page.md | 2 +-
src/silk/docs/std/networking/page.md | 2 +-
src/silk/docs/std/number/page.md | 2 +-
src/silk/docs/std/optional/page.md | 2 +-
src/silk/docs/std/os/page.md | 2 +-
src/silk/docs/std/overview/page.md | 2 +-
src/silk/docs/std/package-structure/page.md | 2 +-
src/silk/docs/std/path/page.md | 2 +-
src/silk/docs/std/process-child/page.md | 2 +-
src/silk/docs/std/process/page.md | 2 +-
src/silk/docs/std/protobuf/page.md | 2 +-
src/silk/docs/std/queue/page.md | 2 +-
src/silk/docs/std/range/page.md | 2 +-
src/silk/docs/std/readline/page.md | 2 +-
src/silk/docs/std/regex/page.md | 2 +-
src/silk/docs/std/result/page.md | 2 +-
src/silk/docs/std/runtime-atomic/page.md | 2 +-
src/silk/docs/std/runtime-build/page.md | 2 +-
src/silk/docs/std/runtime-dylib/page.md | 2 +-
src/silk/docs/std/runtime-env/page.md | 2 +-
src/silk/docs/std/runtime-event_loop/page.md | 2 +-
src/silk/docs/std/runtime-fs/page.md | 2 +-
src/silk/docs/std/runtime-globals/page.md | 2 +-
src/silk/docs/std/runtime-gpu/page.md | 2 +-
.../docs/std/runtime-graphics-metal/page.md | 2 +-
src/silk/docs/std/runtime-io/page.md | 2 +-
.../docs/std/runtime-linux-event_loop/page.md | 2 +-
src/silk/docs/std/runtime-mem/page.md | 2 +-
src/silk/docs/std/runtime-net/page.md | 2 +-
src/silk/docs/std/runtime-number/page.md | 2 +-
.../docs/std/runtime-posix-atomic/page.md | 2 +-
src/silk/docs/std/runtime-posix-dylib/page.md | 2 +-
src/silk/docs/std/runtime-posix-env/page.md | 2 +-
.../docs/std/runtime-posix-event_loop/page.md | 2 +-
src/silk/docs/std/runtime-posix-fs/page.md | 2 +-
src/silk/docs/std/runtime-posix-io/page.md | 2 +-
src/silk/docs/std/runtime-posix-mem/page.md | 2 +-
src/silk/docs/std/runtime-posix-net/page.md | 2 +-
.../docs/std/runtime-posix-process/page.md | 2 +-
.../docs/std/runtime-posix-signal/page.md | 2 +-
src/silk/docs/std/runtime-posix-sync/page.md | 2 +-
src/silk/docs/std/runtime-posix-task/page.md | 2 +-
src/silk/docs/std/runtime-posix-time/page.md | 2 +-
src/silk/docs/std/runtime-process/page.md | 2 +-
src/silk/docs/std/runtime-readline/page.md | 2 +-
src/silk/docs/std/runtime-regex/page.md | 2 +-
src/silk/docs/std/runtime-signal/page.md | 2 +-
src/silk/docs/std/runtime-sync/page.md | 2 +-
src/silk/docs/std/runtime-task/page.md | 2 +-
src/silk/docs/std/runtime-time/page.md | 2 +-
src/silk/docs/std/runtime-unicode/page.md | 2 +-
src/silk/docs/std/runtime-wasi-cwd/page.md | 2 +-
src/silk/docs/std/runtime-wasi-env/page.md | 2 +-
.../docs/std/runtime-wasi-event_loop/page.md | 2 +-
src/silk/docs/std/runtime-wasi-fs/page.md | 2 +-
src/silk/docs/std/runtime-wasi-io/page.md | 2 +-
src/silk/docs/std/runtime-wasi-mem/page.md | 2 +-
src/silk/docs/std/runtime-wasi-net/page.md | 2 +-
.../docs/std/runtime-wasi-preopen/page.md | 2 +-
.../docs/std/runtime-wasi-process/page.md | 2 +-
src/silk/docs/std/runtime-wasi-signal/page.md | 2 +-
src/silk/docs/std/runtime-wasi-sync/page.md | 2 +-
src/silk/docs/std/runtime-wasi-task/page.md | 2 +-
src/silk/docs/std/runtime-wasi-time/page.md | 2 +-
src/silk/docs/std/runtime-window/page.md | 2 +-
src/silk/docs/std/runtime-z3/page.md | 2 +-
src/silk/docs/std/runtime/page.md | 2 +-
src/silk/docs/std/semver/page.md | 2 +-
src/silk/docs/std/set/page.md | 2 +-
src/silk/docs/std/signal/page.md | 2 +-
src/silk/docs/std/sqlite/page.md | 2 +-
src/silk/docs/std/ssh/page.md | 2 +-
src/silk/docs/std/ssh2/page.md | 2 +-
src/silk/docs/std/stack/page.md | 2 +-
src/silk/docs/std/stream/page.md | 2 +-
src/silk/docs/std/strings/page.md | 2 +-
src/silk/docs/std/sync/page.md | 2 +-
src/silk/docs/std/tar-async/page.md | 2 +-
src/silk/docs/std/tar/page.md | 2 +-
src/silk/docs/std/task/page.md | 2 +-
src/silk/docs/std/temporal/page.md | 2 +-
src/silk/docs/std/test/page.md | 2 +-
src/silk/docs/std/time/page.md | 2 +-
src/silk/docs/std/tls/page.md | 2 +-
src/silk/docs/std/toml/page.md | 2 +-
src/silk/docs/std/unicode/page.md | 2 +-
src/silk/docs/std/url/page.md | 2 +-
src/silk/docs/std/uuid-random/page.md | 2 +-
src/silk/docs/std/uuid/page.md | 2 +-
src/silk/docs/std/vector/page.md | 2 +-
src/silk/docs/std/wasm/page.md | 2 +-
src/silk/docs/std/websocket/page.md | 2 +-
src/silk/docs/std/window-gtk/page.md | 2 +-
src/silk/docs/std/window-ios/page.md | 2 +-
src/silk/docs/std/window-macos/page.md | 2 +-
src/silk/docs/std/window/page.md | 2 +-
src/silk/docs/std/xml/page.md | 2 +-
src/silk/docs/std/z3/page.md | 2 +-
src/silk/docs/usage/cli-examples/page.md | 2 +-
src/silk/docs/usage/editor-coc-nvim/page.md | 2 +-
src/silk/docs/usage/editor-ctags/page.md | 2 +-
src/silk/docs/usage/editor-textmate/page.md | 2 +-
src/silk/docs/usage/editor-vim/page.md | 2 +-
src/silk/docs/usage/getting-started/page.md | 2 +-
src/silk/docs/usage/github-actions/page.md | 2 +-
src/silk/docs/usage/github-linguist/page.md | 2 +-
src/silk/docs/usage/guide-examples/page.md | 2 +-
.../usage/howto-custom-stdlib-root/page.md | 2 +-
.../usage/howto-dynamic-libraries/page.md | 2 +-
src/silk/docs/usage/howto-lumen-trail/page.md | 2 +-
.../docs/usage/howto-run-wasi-node/page.md | 2 +-
src/silk/docs/usage/pure-silk-gpu-ml/page.md | 2 +-
src/silk/docs/usage/pure-silk-gpu/page.md | 2 +-
.../usage/tutorials/01-first-program/page.md | 2 +-
.../tutorials/02-structs-and-impls/page.md | 2 +-
.../tutorials/03-arrays-and-slices/page.md | 2 +-
.../usage/tutorials/04-filesystem/page.md | 2 +-
.../usage/tutorials/05-concurrency/page.md | 2 +-
.../06-async-io-streams-abort/page.md | 2 +-
.../usage/tutorials/07-formal-silk/page.md | 2 +-
src/silk/wiki/language/borrow-checker/page.md | 2 +-
src/silk/wiki/language/buffers/page.md | 2 +-
src/silk/wiki/language/cheat-sheet/page.md | 2 +-
src/silk/wiki/language/concurrency/page.md | 2 +-
.../wiki/language/const-functions/page.md | 2 +-
src/silk/wiki/language/conventions/page.md | 2 +-
.../wiki/language/dependent-types/page.md | 2 +-
src/silk/wiki/language/doc-comments/page.md | 2 +-
.../wiki/language/duration-instant/page.md | 2 +-
src/silk/wiki/language/enums/page.md | 2 +-
src/silk/wiki/language/errors/page.md | 2 +-
src/silk/wiki/language/ext/page.md | 2 +-
.../language/flow-blocks-statements/page.md | 2 +-
src/silk/wiki/language/flow-break/page.md | 2 +-
src/silk/wiki/language/flow-continue/page.md | 2 +-
.../flow-expression-statements/page.md | 2 +-
src/silk/wiki/language/flow-for/page.md | 2 +-
src/silk/wiki/language/flow-if-else/page.md | 2 +-
src/silk/wiki/language/flow-loop/page.md | 2 +-
src/silk/wiki/language/flow-match/page.md | 2 +-
src/silk/wiki/language/flow-overview/page.md | 2 +-
src/silk/wiki/language/flow-return/page.md | 2 +-
src/silk/wiki/language/flow-while/page.md | 2 +-
.../wiki/language/formal-verification/page.md | 2 +-
.../language/function-disciplines/page.md | 2 +-
src/silk/wiki/language/generics/page.md | 2 +-
src/silk/wiki/language/grammar/page.md | 2 +-
src/silk/wiki/language/interfaces/page.md | 2 +-
.../wiki/language/literals-aggregate/page.md | 2 +-
.../wiki/language/literals-boolean/page.md | 2 +-
.../wiki/language/literals-character/page.md | 2 +-
.../wiki/language/literals-duration/page.md | 2 +-
.../wiki/language/literals-numeric/page.md | 2 +-
.../wiki/language/literals-overview/page.md | 2 +-
.../wiki/language/literals-string/page.md | 2 +-
src/silk/wiki/language/memory-model/page.md | 2 +-
src/silk/wiki/language/mutability/page.md | 2 +-
src/silk/wiki/language/operators/page.md | 2 +-
src/silk/wiki/language/optional/page.md | 2 +-
.../language/packages-imports-exports/page.md | 2 +-
src/silk/wiki/language/regions/page.md | 2 +-
.../wiki/language/struct-requirements/page.md | 2 +-
.../language/structs-impls-layout/page.md | 2 +-
src/silk/wiki/language/syntax-tour/page.md | 2 +-
src/silk/wiki/language/testing/page.md | 2 +-
src/silk/wiki/language/typed-errors/page.md | 2 +-
src/silk/wiki/language/types/page.md | 2 +-
src/silk/wiki/language/varargs/page.md | 2 +-
src/silk/wiki/page.md | 2 +-
src/silk/wiki/std/algorithms/page.md | 2 +-
src/silk/wiki/std/args/page.md | 2 +-
src/silk/wiki/std/arrays/page.md | 2 +-
src/silk/wiki/std/bits/page.md | 2 +-
src/silk/wiki/std/buffer/page.md | 2 +-
src/silk/wiki/std/conventions/page.md | 2 +-
src/silk/wiki/std/crypto/page.md | 2 +-
src/silk/wiki/std/env/page.md | 2 +-
src/silk/wiki/std/filesystem/page.md | 2 +-
src/silk/wiki/std/fmt/page.md | 2 +-
src/silk/wiki/std/formal/page.md | 2 +-
src/silk/wiki/std/interfaces/page.md | 2 +-
src/silk/wiki/std/io/page.md | 2 +-
src/silk/wiki/std/json/page.md | 2 +-
src/silk/wiki/std/map/page.md | 2 +-
src/silk/wiki/std/memory/page.md | 2 +-
src/silk/wiki/std/networking/page.md | 2 +-
src/silk/wiki/std/overview/page.md | 2 +-
src/silk/wiki/std/package-structure/page.md | 2 +-
src/silk/wiki/std/path/page.md | 2 +-
src/silk/wiki/std/process/page.md | 2 +-
src/silk/wiki/std/result/page.md | 2 +-
src/silk/wiki/std/runtime/page.md | 2 +-
src/silk/wiki/std/semver/page.md | 2 +-
src/silk/wiki/std/set/page.md | 2 +-
src/silk/wiki/std/strings/page.md | 2 +-
src/silk/wiki/std/sync/page.md | 2 +-
src/silk/wiki/std/task/page.md | 2 +-
src/silk/wiki/std/temporal/page.md | 2 +-
src/silk/wiki/std/tls/page.md | 2 +-
src/silk/wiki/std/toml/page.md | 2 +-
src/silk/wiki/std/url/page.md | 2 +-
src/silk/wiki/std/uuid/page.md | 2 +-
src/silk/wiki/std/vector/page.md | 2 +-
src/silkWiki-index.template.ts | 6 +
src/silkWiki-search.template.ts | 6 +
src/silkWiki-sources.template.ts | 6 +
src/sitemap.template.ts | 6 +
src/slg-index.template.ts | 6 +
src/slg-llms.template.ts | 6 +
src/slg-search.template.ts | 6 +
src/slg-sources.template.ts | 6 +
src/slg/docs/cli/diagnostics/page.md | 2 +-
.../docs/cli/input-types-and-parsing/page.md | 2 +-
src/slg/docs/cli/options-reference/page.md | 2 +-
.../docs/cli/output-and-exit-codes/page.md | 2 +-
src/slg/docs/cli/parallelism/page.md | 2 +-
.../docs/cli/patterns-and-matching/page.md | 2 +-
src/slg/docs/cli/slg/page.md | 2 +-
.../docs/cli/traversal-and-ignores/page.md | 2 +-
src/slg/docs/guides/getting-started/page.md | 2 +-
src/slg/docs/guides/search-recipes/page.md | 2 +-
src/slg/docs/man/slg-1/page.md | 2 +-
src/slg/docs/page.md | 2 +-
src/style.css | 1228 +++++
src/virtnosis-index.template.ts | 6 +
src/virtnosis-llms.template.ts | 6 +
src/virtnosis-search.template.ts | 6 +
src/virtnosis-sources.template.ts | 6 +
.../docs/cli/virtnosis-agent/page.md | 2 +-
src/virtnosis/docs/cli/vnactl/page.md | 2 +-
src/virtnosis/docs/guides/deployment/page.md | 2 +-
.../docs/guides/getting-started/page.md | 2 +-
.../docs/guides/install-and-package/page.md | 2 +-
.../docs/guides/operator-guide/page.md | 2 +-
.../docs/guides/output-and-automation/page.md | 2 +-
src/virtnosis/docs/guides/systemd/page.md | 2 +-
src/virtnosis/docs/man/virtnosis-7/page.md | 2 +-
.../docs/man/virtnosis-agent-1/page.md | 2 +-
.../man/virtnosis-agent-protocol-7/page.md | 2 +-
.../docs/man/virtnosis-scan-report-7/page.md | 2 +-
src/virtnosis/docs/man/vnactl-1/page.md | 2 +-
src/virtnosis/docs/page.md | 2 +-
.../docs/reference/architecture/page.md | 2 +-
.../docs/reference/development/page.md | 2 +-
src/virtnosis/docs/reference/overview/page.md | 2 +-
.../docs/reference/repository-docs/page.md | 2 +-
.../docs/reference/scan-analysis/page.md | 2 +-
tests/browser/client.spec.ts | 72 +
tests/docs-data.test.ts | 60 +
tests/docs-watch.test.ts | 93 +
tests/ingestion-fixtures.json | 410 ++
tests/ingestion-import.test.ts | 80 +
tests/ingestion.test.ts | 127 +-
tests/test_ingestion.py | 80 -
tests/tooling/content-audit-baseline.json | 37 +
tests/tooling/content-audit.test.ts | 71 +
tools/audit-content.ts | 30 +
tools/audits/common.ts | 176 +
tools/audits/runtime.ts | 65 +
tools/audits/silk.ts | 75 +
tools/import-public.ts | 2 +-
tools/migration/content.ts | 2 +-
tools/migration/migrate.ts | 2 +-
.../{public-copy.js => public-copy.ts} | 29 +-
tools/serve-output.ts | 67 +
tools/silk-editorial.ts | 192 +
tools/silk-ingestion-data.ts | 231 +
tools/site_audit_common.py | 315 --
tools/source-pages.py | 43 -
tools/source-pages.ts | 74 +
tsconfig.json | 5 +-
virtnosis/tools/audit-site.py | 32 -
686 files changed, 7051 insertions(+), 7615 deletions(-)
delete mode 100644 runtime/tools/audit-site.py
delete mode 100644 runtime/tools/generate-js-api-reference.py
create mode 100644 runtime/tools/generate-js-api-reference.ts
create mode 100644 runtime/tools/js-api-reference-content.ts
delete mode 100644 runtime/tools/js_api_reference_content.py
delete mode 100644 sage/tools/audit-site.py
delete mode 100644 silk/tools/audit-site.py
delete mode 100644 silk/tools/audit-stdlib-docs.py
delete mode 100644 silk/tools/sync-from-silk-docs.py
create mode 100644 silk/tools/sync-from-silk-docs.ts
delete mode 100644 slg/tools/audit-site.py
create mode 100644 src/docs/client.ts
delete mode 100644 src/globals/global.client.js
create mode 100644 src/globals/global.client.ts
create mode 100644 src/layouts/learn.layout.css
delete mode 100644 src/layouts/product.layout.client.ts
create mode 100644 src/layouts/runtime-docs.layout.ts
create mode 100644 src/layouts/sage-docs.layout.ts
create mode 100644 src/layouts/silk-docs.layout.ts
create mode 100644 src/layouts/silkWiki-docs.layout.ts
create mode 100644 src/layouts/slg-docs.layout.ts
create mode 100644 src/layouts/virtnosis-docs.layout.ts
create mode 100644 src/lib/artifacts.ts
create mode 100644 src/lib/ask-ai-client.ts
rename src/lib/{clipboard.js => clipboard.ts} (89%)
delete mode 100644 src/lib/learn-client.js
create mode 100644 src/lib/learn-client.ts
create mode 100644 src/lib/rendering.ts
rename src/lib/{silk.js => silk.ts} (97%)
rename src/lib/{tabs.js => tabs.ts} (85%)
rename src/lib/{toml.js => toml.ts} (94%)
rename src/lib/{zig.js => zig.ts} (94%)
create mode 100644 src/runtime-index.template.ts
create mode 100644 src/runtime-llms.template.ts
create mode 100644 src/runtime-search.template.ts
create mode 100644 src/runtime-sources.template.ts
create mode 100644 src/sage-index.template.ts
create mode 100644 src/sage-llms.template.ts
create mode 100644 src/sage-search.template.ts
create mode 100644 src/sage-sources.template.ts
create mode 100644 src/silk-index.template.ts
create mode 100644 src/silk-llms.template.ts
create mode 100644 src/silk-search.template.ts
create mode 100644 src/silk-sources.template.ts
create mode 100644 src/silkWiki-index.template.ts
create mode 100644 src/silkWiki-search.template.ts
create mode 100644 src/silkWiki-sources.template.ts
create mode 100644 src/sitemap.template.ts
create mode 100644 src/slg-index.template.ts
create mode 100644 src/slg-llms.template.ts
create mode 100644 src/slg-search.template.ts
create mode 100644 src/slg-sources.template.ts
create mode 100644 src/style.css
create mode 100644 src/virtnosis-index.template.ts
create mode 100644 src/virtnosis-llms.template.ts
create mode 100644 src/virtnosis-search.template.ts
create mode 100644 src/virtnosis-sources.template.ts
create mode 100644 tests/browser/client.spec.ts
create mode 100644 tests/docs-data.test.ts
create mode 100644 tests/docs-watch.test.ts
create mode 100644 tests/ingestion-fixtures.json
create mode 100644 tests/ingestion-import.test.ts
delete mode 100644 tests/test_ingestion.py
create mode 100644 tests/tooling/content-audit-baseline.json
create mode 100644 tests/tooling/content-audit.test.ts
create mode 100644 tools/audit-content.ts
create mode 100644 tools/audits/common.ts
create mode 100644 tools/audits/runtime.ts
create mode 100644 tools/audits/silk.ts
rename tools/migration/{public-copy.js => public-copy.ts} (96%)
create mode 100644 tools/serve-output.ts
create mode 100644 tools/silk-editorial.ts
create mode 100644 tools/silk-ingestion-data.ts
delete mode 100644 tools/site_audit_common.py
delete mode 100644 tools/source-pages.py
create mode 100644 tools/source-pages.ts
delete mode 100644 virtnosis/tools/audit-site.py
diff --git a/.github/workflows/docs-audit.yml b/.github/workflows/docs-audit.yml
index 7c818c5a..361f4724 100644
--- a/.github/workflows/docs-audit.yml
+++ b/.github/workflows/docs-audit.yml
@@ -2,8 +2,7 @@ name: Docs Audit
on:
pull_request:
- push:
- branches: [main, master]
+
workflow_dispatch:
permissions:
@@ -18,9 +17,7 @@ jobs:
with:
node-version: '24'
cache: npm
- - uses: actions/setup-python@v5
- with:
- python-version: '3.12'
+
- run: npm ci
- run: npx playwright install --with-deps chromium
- run: npm run check
diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml
index 699f94ca..16e7a962 100644
--- a/.github/workflows/pages.yml
+++ b/.github/workflows/pages.yml
@@ -21,13 +21,20 @@ jobs:
with:
node-version: '24'
cache: npm
- - uses: actions/setup-python@v5
- with:
- python-version: '3.12'
+
- run: npm ci
# Upstream ingestion is manual. Validate only the committed public sources.
- run: npx playwright install --with-deps chromium
- run: npm run check
+ - name: Verify source files remain unchanged
+ run: |
+ git diff --check
+ git diff --exit-code
+ - uses: actions/upload-artifact@v4
+ if: failure()
+ with:
+ name: browser-test-results
+ path: test-results/
- uses: actions/upload-pages-artifact@v3
with:
path: public
diff --git a/README.md b/README.md
index 90440daf..ab9e912f 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,8 @@ chapters, and documentation for Runtime, Silk, Virtnosis, Sage, and slg.
## Local development
-Use Node 24 and Python 3.12. No sibling checkout is needed to build the site.
+Use Node 24. Builds, ingestion tools, audits, and tests run in TypeScript on Node;
+Python is not required. No sibling checkout is needed to build the site.
```sh
npm ci
@@ -21,16 +22,25 @@ are output files; do not commit `public/`.
- `src/**/page.html`: authored page fragments, with metadata in `page.vars.ts`.
- `src/**/page.md`: public documentation, including YAML frontmatter.
- `src/layouts/`: shared page chrome, navigation, and progressive browser clients.
-- `src/globals/global.css`: Oro styles, based on `docs/branding/`.
+- `src/globals/global.css`: shared Oro styles, based on `docs/branding/`.
+ Homepage styles live in `src/style.css`; learn styles in `src/layouts/learn.layout.css`.
- `src/lib/`: rendering, URL, collection, and navigation helpers.
-- `src/global.data.ts`: collection data derived from source pages.
-- `src/artifacts.template.ts`: navigation/search JSON, raw Markdown, LLM packs,
- and sitemap output.
+- `src/global.data.ts`: validated collection metadata and precomputed navigation,
+ search, and source-export projections derived from source pages.
+- `src/layouts/*-docs.layout.ts`: thin collection-specific data
+ consumers sharing the docs renderer and its assets.
+- `src/*-{index,search,sources,llms}.template.ts`: dependency-scoped collection
+ artifacts; `src/sitemap.template.ts`: sitemap output;
+ `src/artifacts.template.ts`: site-wide LLM directory and `.nojekyll`.
Consult `docs/branding/` before changing visual design or copy tone. The approved
logo originals remain in `docs/branding/assets/`; their checked mirrors in `src/docs/branding/assets/` retain
the same public URLs. Update both when replacing an asset. Handlebars is disabled globally so literal `{{ ... }}` code
-examples remain intact.
+examples remain intact. The Markdown parser deliberately keeps the existing
+plugin policy: tables, strikethrough, HTML, linkification, GitHub alerts, syntax
+highlighting, and legacy heading IDs. DOMStack's other default extensions are
+not implicitly enabled; review fragment and rendered-output compatibility before
+adding plugins.
Each documentation page declares `title`, `description`, `docsCollection`,
`section`, `order`, `sourcePath`, `githubRepo`, and `githubRef`. Preserve
@@ -38,6 +48,9 @@ Each documentation page declares `title`, `description`, `docsCollection`,
The `start` document lives at its collection root; the specification lives at
`src/silk/spec/2026/page.md`. Order is explicit and does not depend on filenames.
New imported pages are appended; review their section and order after syncing.
+Use the collection-specific layout, such as `runtime-docs` or `silkWiki-docs`,
+for new documentation pages (`spec` for the specification). The shared `docs`
+layout is an asset-bearing parent, not a standalone article renderer.
Use canonical directory links such as `/runtime/docs/guides/hello-world/`.
Collection roots redirect legacy `?p=` links while preserving fragments. Static
@@ -52,8 +65,8 @@ checkouts, preserve the established curated/website-owned content, and write
committed DOMStack Markdown pages. Review and commit their changes before deploying.
```sh
-python3 silk/tools/sync-from-silk-docs.py --silk-repo /path/to/silk
-python3 runtime/tools/generate-js-api-reference.py --runtime-repo /path/to/runtime
+node silk/tools/sync-from-silk-docs.ts --silk-repo /path/to/silk
+node runtime/tools/generate-js-api-reference.ts --runtime-repo /path/to/runtime
npm run build
npm run audit
npm run audit:content
@@ -69,8 +82,10 @@ linked API headings become plain-text titles when a page changes. The reference
catalog includes new pages in the same import batch. Runtime's generated
reference markers retain surrounding prose.
-The Python index and LLM exporters have been replaced by DOMStack templates.
-Content audits inspect `public/` by default; set `ORO_SITE_OUTPUT` to inspect
+The generators call the TypeScript importer directly; they never run during a
+DOMStack build. JSON indexes and LLM exports are DOMStack templates, not ingestion
+outputs. Content audits run through `tools/audit-content.ts` and inspect `public/`
+by default; set `ORO_SITE_OUTPUT` to inspect
another output directory. Set `ORO_RUNTIME_REPO` explicitly to additionally audit
against a particular upstream Runtime checkout; ordinary checks are independent
of whatever happens to be checked out next door.
@@ -117,7 +132,10 @@ update the relevant assertions and redirects together.
## Deployment and dependency updates
-GitHub Actions builds and uploads `public/` and deploys with the Pages environment.
+Pull requests run the Docs Audit workflow. Production-branch pushes run the
+Pages workflow, which validates once, uploads that checked `public/` artifact,
+and deploys with the Pages environment. Both workflows use Node only and retain
+browser failure artifacts.
The repository's **Settings → Pages → Source** must be **GitHub Actions**. Merging
these changes does not itself change that repository setting. The output contains
`CNAME`, `.nojekyll`, branding assets, raw Markdown, and `llms.txt` packs.
diff --git a/docs/migration.md b/docs/migration.md
index 08b0bfb4..5d052595 100644
--- a/docs/migration.md
+++ b/docs/migration.md
@@ -14,9 +14,18 @@ remains reproducible. Production cutover is complete; deployment evidence is rec
| Page chrome | Headers, footers, metadata, product navigation, learn chapter bars, documentation sidebars, breadcrumbs, previous/next links, and heading lists render at build time. |
| Progressive features | Search, keyboard tabs, copy controls, Ask AI, mobile sidebar controls, active headings, and tab fragments work with browser JavaScript. |
| Artifacts | One build emits HTML, indexes, search data, raw Markdown, LLM packs, the sitemap, CNAME, and `.nojekyll`. |
-| Ingestion | Silk and Runtime retain their upstream/editorial ownership rules and accept explicit upstream checkout paths. Unchanged public pages retain exact Markdown and metadata. |
+| Ingestion | Native TypeScript tools retain Silk and Runtime upstream/editorial ownership rules and explicit checkout paths. Unchanged public pages retain exact Markdown and metadata. |
| Cleanup | The browser Markdown renderers, vendored rendering libraries, old HTML shells, duplicate Python exporters, generated source-tree indexes, and Jekyll configuration are retired. |
-| CI | Audit and deployment workflows use Node 24, Python 3.12, and the same `npm run check` gate. |
+| CI | Node 24 runs ingestion, audits, browser serving, and the `npm run check` gate without Python. Pull requests validate in Docs Audit; production pushes validate and deploy the same artifact in Pages, without a duplicate push audit. |
+
+Collection layouts subscribe to lightweight, collection-specific navigation keys.
+Search and raw-source/LLM templates have separate dependencies, so document-body
+edits do not invalidate unrelated navigation consumers. Shared article transforms
+remain page-local rather than storing rendered articles in global data. Markdown
+keeps the existing alerts, highlighting, and legacy heading-ID policy; enabling
+additional DOMStack Markdown plugins is an explicit compatibility decision.
+Homepage and learn styles are scoped to their consumers, and progressive clients
+are TypeScript modules rather than unchecked global initializers.
The route inventory is in `tools/migration/baseline.json`; original Markdown
links and heading text are in `tools/migration/link-inventory.json`. The migration
diff --git a/package.json b/package.json
index 0f39c367..610cda61 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
"scripts": {
"clean": "node tools/clean.ts",
"build": "npm run clean && domstack",
+ "start": "domstack --serve",
"dev": "domstack --watch",
"preview": "domstack --serve",
"typecheck": "tsc --noEmit",
@@ -16,7 +17,7 @@
"test:site": "node --test tests/site/*.test.ts",
"test:browser": "playwright test",
"test:reproducibility": "node tools/reproducibility.ts",
- "audit:content": "python3 runtime/tools/audit-site.py && python3 silk/tools/audit-site.py && python3 silk/tools/audit-stdlib-docs.py && python3 sage/tools/audit-site.py && python3 slg/tools/audit-site.py && python3 virtnosis/tools/audit-site.py",
+ "audit:content": "node tools/audit-content.ts",
"test:tooling": "node --test tests/tooling/*.test.ts",
"check": "npm run typecheck && npm test && npm run build && npm run audit && npm run audit:content && npm run test:site && npm run test:browser && npm run test:tooling && npm run test:reproducibility"
},
diff --git a/playwright.config.ts b/playwright.config.ts
index 28713868..ab6dddd3 100644
--- a/playwright.config.ts
+++ b/playwright.config.ts
@@ -9,7 +9,7 @@ export default defineConfig({
: {},
},
webServer: {
- command: 'python3 -m http.server 9877 --bind 127.0.0.1 --directory public',
+ command: 'node tools/serve-output.ts',
url: 'http://127.0.0.1:9877',
reuseExistingServer: !process.env.CI,
},
diff --git a/runtime/tools/audit-site.py b/runtime/tools/audit-site.py
deleted file mode 100644
index c183de2c..00000000
--- a/runtime/tools/audit-site.py
+++ /dev/null
@@ -1,212 +0,0 @@
-#!/usr/bin/env python3
-
-from __future__ import annotations
-
-import re
-import sys
-import os
-from pathlib import Path
-
-
-SITE_ROOT = Path(__file__).resolve().parents[2]
-RUNTIME_ROOT = Path(os.environ.get("ORO_SITE_OUTPUT", str(SITE_ROOT / "public"))) / "runtime"
-REPO_ROOT = RUNTIME_ROOT.parent
-JAVASCRIPT_DOCS = RUNTIME_ROOT / "docs" / "source" / "javascript"
-CLI_DOCS = RUNTIME_ROOT / "docs" / "source" / "cli"
-UPSTREAM_RUNTIME_ROOT = Path(os.environ["ORO_RUNTIME_REPO"]) if "ORO_RUNTIME_REPO" in os.environ else None
-UPSTREAM_RUNTIME_INDEX = UPSTREAM_RUNTIME_ROOT / "api" / "index.d.ts" if UPSTREAM_RUNTIME_ROOT else None
-UPSTREAM_RUNTIME_CLI = UPSTREAM_RUNTIME_ROOT / "api" / "CLI.md" if UPSTREAM_RUNTIME_ROOT else None
-
-sys.path.insert(0, str(SITE_ROOT / "tools"))
-
-from site_audit_common import SiteAuditConfig, run_site_audit
-
-
-CURATED_FILES_BY_FAMILY = {
- "oro:ai": "ai.md",
- "oro:application": "application.md",
- "oro:extension": "extension.md",
- "oro:fs": "fs.md",
- "oro:hooks": "hooks.md",
- "oro:mcp": "mcp.md",
- "oro:notification": "notification.md",
- "oro:secure-storage": "secure-storage.md",
- "oro:window": "window.md",
-}
-
-
-EXCLUDED_PUBLIC_FAMILIES = {
- "oro:bootstrap",
- "oro:external",
- "oro:internal",
- "oro:node",
- "oro:node-esm-loader",
-}
-
-
-MODULE_START = re.compile(r"^declare module ['\"](?Poro:[^'\"]+)['\"]", re.M)
-EXAMPLE_HEADING_RE = re.compile(r"^\s*##\s+.*examples?\b", re.I | re.M)
-
-CLI_SECTION_TO_DOC = {
- "oroc": "cli/oroc.md",
- "oroc help": "cli/help.md",
- "oroc update": "cli/update.md",
- "oroc update-init": "cli/update/init.md",
- "oroc update-server": "cli/update/server.md",
- "oroc update-info": "cli/update/info.md",
- "oroc update-keygen": "cli/update/keygen.md",
- "oroc update-sign": "cli/update/sign.md",
- "oroc update-verify": "cli/update/verify.md",
- "oroc update-validate": "cli/update/validate.md",
- "oroc update-bundle": "cli/update/bundle.md",
- "oroc update-extract": "cli/update/extract.md",
- "oroc build": "cli/build.md",
- "oroc run": "cli/run.md",
- "oroc list-devices": "cli/list-devices.md",
- "oroc env": "cli/env.md",
- "oroc mcp": "cli/mcp.md",
- "oroc init": "cli/init.md",
- "oroc install-app": "cli/install-app.md",
- "oroc print-build-dir": "cli/print-build-dir.md",
- "oroc setup": "cli/setup.md",
- "oroc config": "cli/config.md",
- "oroc versions": "cli/versions.md",
- "oroc version": "cli/version.md",
-}
-
-
-def family_reference_re(family: str) -> re.Pattern[str]:
- return re.compile(rf"`?{re.escape(family)}(?:/\*|/[A-Za-z0-9._-]+(?:/[A-Za-z0-9._-]+)*)`?")
-
-
-def parse_runtime_specifiers() -> tuple[set[str], set[str]]:
- if UPSTREAM_RUNTIME_INDEX is None or not UPSTREAM_RUNTIME_INDEX.exists():
- return set(), set()
- text = UPSTREAM_RUNTIME_INDEX.read_text(encoding="utf-8")
- specifiers = set(MODULE_START.findall(text))
- families = {spec.split("/", 1)[0] for spec in specifiers}
- return families, specifiers
-
-
-def parse_runtime_cli_sections() -> set[str]:
- if UPSTREAM_RUNTIME_CLI is None or not UPSTREAM_RUNTIME_CLI.exists():
- return set()
- text = UPSTREAM_RUNTIME_CLI.read_text(encoding="utf-8")
- return set(re.findall(r"^##\s+([^\n]+)$", text, re.M))
-
-
-def iter_runtime_doc_issues() -> list[str]:
- issues: list[str] = []
-
- if (JAVASCRIPT_DOCS / "module-index.md").exists():
- issues.append(
- f"{(JAVASCRIPT_DOCS / 'module-index.md').relative_to(REPO_ROOT)}: remove this page; link JavaScript API entry points to javascript/overview."
- )
-
- for path in sorted(JAVASCRIPT_DOCS.glob("*.md")):
- text = path.read_text(encoding="utf-8")
- rel = path.relative_to(REPO_ROOT)
-
- if "console.log(Object.keys(api))" in text:
- issues.append(f"{rel}: remove generic Object.keys(api) examples; document a real usage flow instead.")
-
- if (
- path.stem not in {"overview", "all-modules"}
- and path.name not in set(CURATED_FILES_BY_FAMILY.values())
- and not EXAMPLE_HEADING_RE.search(text)
- ):
- issues.append(f"{rel}: missing an Examples section.")
-
- families, specifiers = parse_runtime_specifiers()
- if families and specifiers:
- public_families = {family for family in families if family not in EXCLUDED_PUBLIC_FAMILIES}
- public_specifiers = {
- spec for spec in specifiers if spec.split("/", 1)[0] not in EXCLUDED_PUBLIC_FAMILIES
- }
-
- for family in sorted(public_families):
- expected = CURATED_FILES_BY_FAMILY.get(family, f"{family.removeprefix('oro:')}.md")
- if not (JAVASCRIPT_DOCS / expected).exists():
- issues.append(
- f"{(JAVASCRIPT_DOCS / expected).relative_to(REPO_ROOT)}: missing docs page for published module family {family}."
- )
-
- for family in sorted(EXCLUDED_PUBLIC_FAMILIES):
- excluded_path = JAVASCRIPT_DOCS / f"{family.removeprefix('oro:')}.md"
- if excluded_path.exists():
- issues.append(
- f"{excluded_path.relative_to(REPO_ROOT)}: excluded private module family {family} should not have a public docs page."
- )
-
- all_modules = (JAVASCRIPT_DOCS / "all-modules.md").read_text(encoding="utf-8")
-
- for family in sorted(EXCLUDED_PUBLIC_FAMILIES):
- family_ref = family_reference_re(family)
- if family_ref.search(all_modules):
- issues.append(
- f"{(JAVASCRIPT_DOCS / 'all-modules.md').relative_to(REPO_ROOT)}: excluded private module family {family} should not appear in the public module listing."
- )
-
- for spec in sorted(public_specifiers):
- if spec not in all_modules:
- issues.append(
- f"{(JAVASCRIPT_DOCS / 'all-modules.md').relative_to(REPO_ROOT)}: missing published module specifier {spec}."
- )
-
- cli_sections = parse_runtime_cli_sections()
- if cli_sections:
- for section, rel_path in sorted(CLI_SECTION_TO_DOC.items()):
- if section not in cli_sections:
- continue
- path = RUNTIME_ROOT / "docs" / "source" / rel_path
- if not path.exists():
- issues.append(
- f"{path.relative_to(REPO_ROOT)}: missing website docs page for upstream CLI section `{section}`."
- )
-
- stale_repo_patterns = (
- (re.compile(r"oro-computer/" + r"oro" + r"-runtime"), "deprecated runtime repository alias"),
- (re.compile(r"oro-computer/" + r"legacy" + r"-runtime"), "deprecated runtime repository alias"),
- (re.compile(r"\blegacy" + r"-runtime\b"), "deprecated runtime repository alias"),
- )
- for path in (
- REPO_ROOT / "index.html",
- RUNTIME_ROOT / "index.html",
- RUNTIME_ROOT / "docs" / "index.html",
- ):
- text = path.read_text(encoding="utf-8")
- for pattern, label in stale_repo_patterns:
- if pattern.search(text):
- issues.append(f"{path.relative_to(REPO_ROOT)}: stale runtime repository reference ({label}).")
-
- return issues
-
-
-def main() -> int:
- rc = run_site_audit(
- SiteAuditConfig(
- project_root=RUNTIME_ROOT,
- docs_source=RUNTIME_ROOT / "docs" / "source",
- docs_index=RUNTIME_ROOT / "docs" / "index.json",
- missing_index_message="Missing Runtime docs/index.json; run build scripts first.",
- success_message="OK: runtime site audit passed",
- )
- )
- if rc != 0:
- return rc
-
- issues = iter_runtime_doc_issues()
- if issues:
- for issue in issues[:200]:
- print(issue, file=sys.stderr)
- if len(issues) > 200:
- print(f"... and {len(issues) - 200} more", file=sys.stderr)
- print(f"FAIL: {len(issues)} runtime docs issues", file=sys.stderr)
- return 1
-
- print("OK: runtime docs audit passed")
- return 0
-
-
-if __name__ == "__main__":
- raise SystemExit(main())
diff --git a/runtime/tools/generate-js-api-reference.py b/runtime/tools/generate-js-api-reference.py
deleted file mode 100644
index 726f7e73..00000000
--- a/runtime/tools/generate-js-api-reference.py
+++ /dev/null
@@ -1,318 +0,0 @@
-#!/usr/bin/env python3
-
-from __future__ import annotations
-
-import argparse
-import re
-import sys
-import tempfile
-from dataclasses import dataclass
-from pathlib import Path
-
-sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "tools"))
-from importlib import import_module
-source_pages = import_module("source-pages")
-
-from js_api_reference_content import (
- DEFAULT_SEE_ALSO,
- DESCRIPTION_BY_FAMILY,
- EXAMPLES_BY_FAMILY,
- GUIDE_REFS_BY_FAMILY,
-)
-
-
-MODULE_START = re.compile(r"^declare module ['\"](?Poro:[^'\"]+)['\"]\s*\{\s*$")
-
-
-CURATED_FAMILIES = {
- "oro:ai",
- "oro:application",
- "oro:extension",
- "oro:fs",
- "oro:hooks",
- "oro:mcp",
- "oro:notification",
- "oro:secure-storage",
- "oro:window",
-}
-
-
-EXCLUDED_PUBLIC_FAMILIES = {
- "oro:bootstrap",
- "oro:external",
- "oro:internal",
- "oro:node",
- "oro:node-esm-loader",
-}
-
-
-CURATED_FILES_BY_FAMILY = {
- "oro:ai": "ai.md",
- "oro:application": "application.md",
- "oro:extension": "extension.md",
- "oro:fs": "fs.md",
- "oro:hooks": "hooks.md",
- "oro:mcp": "mcp.md",
- "oro:notification": "notification.md",
- "oro:secure-storage": "secure-storage.md",
- "oro:window": "window.md",
-}
-
-
-REF_START = ""
-REF_END = ""
-
-
-@dataclass(frozen=True)
-class ModuleBlock:
- spec: str
- block: str
-
-
-def repo_root() -> Path:
- return Path(__file__).resolve().parents[2]
-
-
-def read_text(path: Path) -> str:
- return path.read_text(encoding="utf-8")
-
-
-def write_text_if_changed(path: Path, text: str) -> bool:
- path.parent.mkdir(parents=True, exist_ok=True)
- prev = path.read_text(encoding="utf-8") if path.exists() else None
- if prev == text:
- return False
- path.write_text(text, encoding="utf-8")
- return True
-
-
-def parse_index_d_ts(text: str) -> dict[str, ModuleBlock]:
- lines = text.splitlines()
- out: dict[str, ModuleBlock] = {}
- i = 0
-
- while i < len(lines):
- m = MODULE_START.match(lines[i])
- if not m:
- i += 1
- continue
-
- spec = m.group("name").strip()
- buf: list[str] = [lines[i].rstrip()]
- i += 1
-
- while i < len(lines):
- line = lines[i].rstrip()
- buf.append(line)
- if line == "}" and not lines[i].startswith((" ", "\t")):
- break
- i += 1
-
- block = "\n".join(buf).rstrip() + "\n"
- out[spec] = ModuleBlock(spec=spec, block=block)
- i += 1
-
- if not out:
- raise SystemExit("No `declare module 'oro:*' { ... }` blocks found.")
-
- return out
-
-
-def family_of(spec: str) -> str:
- return spec.split("/", 1)[0]
-
-
-def sort_specs_in_family(family: str, specs: list[str]) -> list[str]:
- return sorted(specs, key=lambda s: (s != family, s))
-
-
-def preferred_import_spec(family: str, specs: list[str], blocks: dict[str, ModuleBlock]) -> str:
- if family in blocks:
- return family
- for s in specs:
- if s.endswith("/index"):
- return s
- return specs[0]
-
-
-def family_title(family: str, blocks: dict[str, ModuleBlock]) -> str:
- if family in blocks:
- return f"`{family}`"
- return f"`{family}/*`"
-
-
-def family_intro(family: str) -> str:
- return DESCRIPTION_BY_FAMILY.get(
- family,
- f"This page documents the exported JavaScript surface for `{family}` as declared by the runtime’s published TypeScript definitions.\n",
- )
-
-
-def render_examples_section(family: str) -> str:
- body = EXAMPLES_BY_FAMILY.get(family)
- if not body:
- raise SystemExit(f"Missing generated examples for {family}")
- return f"## Examples\n\n{body.rstrip()}\n"
-
-
-def render_see_also_section() -> str:
- lines = ["## See also", ""]
- for label, doc_id in DEFAULT_SEE_ALSO:
- lines.append(f"- [{label}](?p={doc_id})")
- lines.append("")
- return "\n".join(lines)
-
-
-def render_related_guides_section(family: str) -> str:
- refs = GUIDE_REFS_BY_FAMILY.get(family, ())
- if not refs:
- return ""
-
- lines = ["## Related guides", ""]
- for label, doc_id in refs:
- lines.append(f"- [{label}](?p={doc_id})")
- lines.append("")
- return "\n".join(lines)
-
-
-def render_reference_section(family: str, specs: list[str], blocks: dict[str, ModuleBlock]) -> str:
- specs_sorted = sort_specs_in_family(family, specs)
-
- lines: list[str] = []
- lines.append("## API reference")
- lines.append("")
- lines.append(REF_START)
- lines.append("")
- lines.append("### Module specifiers")
- lines.append("")
- lines.append("```text")
- for s in specs_sorted:
- lines.append(s)
- lines.append("```")
- lines.append("")
-
- lines.append("### TypeScript declarations")
- lines.append("")
- lines.append("These declarations are generated from the runtime's published TypeScript surface.")
- lines.append("")
- for s in specs_sorted:
- lines.append(f"#### `{s}`")
- lines.append("")
- lines.append("```ts")
- lines.extend(blocks[s].block.rstrip().splitlines())
- lines.append("```")
- lines.append("")
-
- lines.append(REF_END)
- lines.append("")
- return "\n".join(lines)
-
-
-def update_curated_page(path: Path, family: str, specs: list[str], blocks: dict[str, ModuleBlock]) -> bool:
- text = read_text(path)
-
- if REF_START in text and REF_END in text:
- before, rest = text.split(REF_START, 1)
- _, after = rest.split(REF_END, 1)
- before = re.sub(r"(?:\n## API reference\s*)+\Z", "\n", before.rstrip())
- # Keep content outside the generated region; regenerate the region itself.
- next_text = before.rstrip() + "\n\n" + render_reference_section(family, specs, blocks) + after.lstrip()
- else:
- next_text = text.rstrip() + "\n\n" + render_reference_section(family, specs, blocks)
-
- if "\n## See also\n" not in next_text and not next_text.startswith("## See also\n"):
- next_text = next_text.rstrip() + "\n\n" + render_see_also_section()
-
- # Ensure trailing newline
- next_text = next_text.rstrip() + "\n"
- return write_text_if_changed(path, next_text)
-
-
-def render_generated_page(path: Path, family: str, specs: list[str], blocks: dict[str, ModuleBlock]) -> str:
- title = family_title(family, blocks)
- specs_sorted = sort_specs_in_family(family, specs)
-
- lines: list[str] = []
- lines.append(f"# {title}")
- lines.append("")
- lines.append(family_intro(family).rstrip())
- lines.append("")
- related = render_related_guides_section(family)
- if related:
- lines.append(related.rstrip())
- lines.append("")
- lines.append(render_examples_section(family).rstrip())
- lines.append("")
- lines.append(render_reference_section(family, specs_sorted, blocks).rstrip())
- lines.append("")
- lines.append(render_see_also_section().rstrip())
- lines.append("")
- return "\n".join(lines).rstrip() + "\n"
-
-
-def main() -> None:
- root = repo_root()
- parser = argparse.ArgumentParser(description="Refresh Runtime API Markdown while preserving curated prose.")
- parser.add_argument("--runtime-repo", type=Path, default=root.parent / "runtime")
- args = parser.parse_args()
- index_path = args.runtime_repo.resolve() / "api" / "index.d.ts"
- if not index_path.exists():
- raise SystemExit(f"Missing {index_path}")
-
- blocks = parse_index_d_ts(read_text(index_path))
-
- families: dict[str, list[str]] = {}
- for spec in blocks.keys():
- families.setdefault(family_of(spec), []).append(spec)
-
- staging = tempfile.TemporaryDirectory(prefix="oro-runtime-import-")
- staged = Path(staging.name)
- source_pages.stage_collection(root, "runtime", staged)
- out_dir = staged / "javascript"
- out_dir.mkdir(parents=True, exist_ok=True)
-
- written: list[Path] = []
- removed: list[Path] = []
-
- for family in sorted(families.keys()):
- if family in EXCLUDED_PUBLIC_FAMILIES:
- continue
-
- specs = families[family]
-
- if family in CURATED_FAMILIES:
- rel = CURATED_FILES_BY_FAMILY[family]
- path = out_dir / rel
- if not path.exists():
- raise SystemExit(f"Expected curated file to exist: {path}")
- if update_curated_page(path, family, specs, blocks):
- written.append(path)
- continue
-
- name = family.removeprefix("oro:")
- path = out_dir / f"{name}.md"
- text = render_generated_page(path, family, specs, blocks)
- if write_text_if_changed(path, text):
- written.append(path)
-
- for family in sorted(EXCLUDED_PUBLIC_FAMILIES):
- stale_path = out_dir / f"{family.removeprefix('oro:')}.md"
- if stale_path.exists():
- stale_path.unlink()
- removed.append(stale_path)
-
- source_pages.import_collection(root, "runtime", staged)
- staging.cleanup()
-
- if written or removed:
- print("Wrote:")
- for p in written:
- print(f"- {root / 'src/runtime/docs/javascript' / p.stem / 'page.md'}")
- for p in removed:
- print(f"- removed {root / 'src/runtime/docs/javascript' / p.stem / 'page.md'}")
- else:
- print("Unchanged: generated JavaScript API reference pages")
-
-
-if __name__ == "__main__":
- main()
diff --git a/runtime/tools/generate-js-api-reference.ts b/runtime/tools/generate-js-api-reference.ts
new file mode 100644
index 00000000..6e13325d
--- /dev/null
+++ b/runtime/tools/generate-js-api-reference.ts
@@ -0,0 +1,132 @@
+#!/usr/bin/env node
+import { access, mkdir, mkdtemp, rm, unlink } from 'node:fs/promises'
+import { tmpdir } from 'node:os'
+import { resolve, join, basename } from 'node:path'
+import { fileURLToPath, pathToFileURL } from 'node:url'
+import { parseArgs } from 'node:util'
+import { stageCollection, importCollection, readText, writeTextIfChanged, splitLines } from '../../tools/source-pages.ts'
+import { DEFAULT_SEE_ALSO, DESCRIPTION_BY_FAMILY, EXAMPLES_BY_FAMILY, GUIDE_REFS_BY_FAMILY } from './js-api-reference-content.ts'
+
+export const CURATED_FILES_BY_FAMILY: Record = Object.fromEntries(
+ ['ai', 'application', 'extension', 'fs', 'hooks', 'mcp', 'notification', 'secure-storage', 'window'].map(name => [`oro:${name}`, `${name}.md`]))
+export const EXCLUDED_PUBLIC_FAMILIES = new Set(['oro:bootstrap', 'oro:external', 'oro:internal', 'oro:node', 'oro:node-esm-loader'])
+export const REF_START = ''
+export const REF_END = ''
+export interface ModuleBlock { spec: string; block: string }
+export function parseIndexDTS(text: string): Record {
+ const lines = splitLines(text)
+ const blocks: Record = {}
+ for (let i = 0; i < lines.length; i++) {
+ const match = /^declare module ['"](oro:[^'"]+)['"]\s*\{\s*$/.exec(lines[i])
+ if (!match) continue
+ const spec = match[1].trim()
+ const buf = [lines[i].trimEnd()]
+ while (++i < lines.length) {
+ const line = lines[i].trimEnd()
+ buf.push(line)
+ if (line === '}' && !/^[ \t]/.test(lines[i])) break
+ }
+ blocks[spec] = { spec, block: buf.join('\n').trimEnd() + '\n' }
+ }
+ if (!Object.keys(blocks).length) throw new Error("No `declare module 'oro:*' { ... }` blocks found.")
+ return blocks
+}
+export function familyOf(spec: string): string { return spec.split('/', 1)[0] }
+export function sortSpecsInFamily(family: string, specs: string[]): string[] {
+ return [...specs].sort().sort((a, b) => Number(a !== family) - Number(b !== family))
+}
+export function preferredImportSpec(family: string, specs: string[], blocks: Record): string {
+ return family in blocks ? family : specs.find(s => s.endsWith('/index')) ?? specs[0]
+}
+export function familyTitle(family: string, blocks: Record): string {
+ return '`' + family + (family in blocks ? '' : '/*') + '`'
+}
+export function familyIntro(family: string): string {
+ return DESCRIPTION_BY_FAMILY[family] ?? `This page documents the exported JavaScript surface for \`${family}\` as declared by the runtime’s published TypeScript definitions.\n`
+}
+export function renderExamplesSection(family: string): string {
+ const body = EXAMPLES_BY_FAMILY[family]
+ if (!body) throw new Error(`Missing generated examples for ${family}`)
+ return `## Examples\n\n${body.trimEnd()}\n`
+}
+export function renderSeeAlsoSection(): string {
+ return ['## See also', '', ...DEFAULT_SEE_ALSO.map(([label, id]) => `- [${label}](?p=${id})`), ''].join('\n')
+}
+export function renderRelatedGuidesSection(family: string): string {
+ const refs = GUIDE_REFS_BY_FAMILY[family] ?? []
+ return refs.length ? ['## Related guides', '', ...refs.map(([label, id]) => `- [${label}](?p=${id})`), ''].join('\n') : ''
+}
+export function renderReferenceSection(family: string, specs: string[], blocks: Record): string {
+ const sorted = sortSpecsInFamily(family, specs)
+ const lines = ['## API reference', '', REF_START, '', '### Module specifiers', '', '```text', ...sorted, '```', '',
+ '### TypeScript declarations', '', "These declarations are generated from the runtime's published TypeScript surface.", '']
+ for (const spec of sorted) lines.push(`#### \`${spec}\``, '', '```ts', ...splitLines(blocks[spec].block.trimEnd()), '```', '')
+ return [...lines, REF_END, ''].join('\n')
+}
+export async function updateCuratedPage(path: string, family: string, specs: string[], blocks: Record): Promise {
+ const text = await readText(path)
+ let next: string
+ if (text.includes(REF_START) && text.includes(REF_END)) {
+ const start = text.indexOf(REF_START)
+ const end = text.indexOf(REF_END, start + REF_START.length)
+ if (end < 0) throw new Error(`Reversed generated reference markers in ${path}`)
+ const before = text.slice(0, start).trimEnd().replace(/(?:\n## API reference\s*)+$/, '\n')
+ next = before.trimEnd() + '\n\n' + renderReferenceSection(family, specs, blocks) + text.slice(end + REF_END.length).trimStart()
+ } else next = text.trimEnd() + '\n\n' + renderReferenceSection(family, specs, blocks)
+ if (!next.includes('\n## See also\n') && !next.startsWith('## See also\n')) next = next.trimEnd() + '\n\n' + renderSeeAlsoSection()
+ return writeTextIfChanged(path, next.trimEnd() + '\n')
+}
+export function renderGeneratedPage(_path: string, family: string, specs: string[], blocks: Record): string {
+ const lines = [`# ${familyTitle(family, blocks)}`, '', familyIntro(family).trimEnd(), '']
+ const related = renderRelatedGuidesSection(family)
+ if (related) lines.push(related.trimEnd(), '')
+ lines.push(renderExamplesSection(family).trimEnd(), '', renderReferenceSection(family, specs, blocks).trimEnd(), '', renderSeeAlsoSection().trimEnd(), '')
+ return lines.join('\n').trimEnd() + '\n'
+}
+export async function generateReference(site: string, runtimeRepo: string): Promise {
+ const index = resolve(runtimeRepo, 'api/index.d.ts')
+ try { await access(index) } catch { throw new Error(`Missing ${index}`) }
+ const blocks = parseIndexDTS(await readText(index))
+ const families: Record = {}
+ for (const spec of Object.keys(blocks)) (families[familyOf(spec)] ??= []).push(spec)
+ const staged = await mkdtemp(join(tmpdir(), 'oro-runtime-import-'))
+ const written: string[] = [], removed: string[] = []
+ try {
+ await stageCollection('runtime', staged, site)
+ const out = join(staged, 'javascript')
+ await mkdir(out, { recursive: true })
+ for (const family of Object.keys(families).sort()) {
+ if (EXCLUDED_PUBLIC_FAMILIES.has(family)) continue
+ const curated = CURATED_FILES_BY_FAMILY[family]
+ const path = join(out, curated ?? `${family.slice(4)}.md`)
+ if (curated) {
+ try { await access(path) } catch { throw new Error(`Expected curated file to exist: ${path}`) }
+ if (await updateCuratedPage(path, family, families[family], blocks)) written.push(path)
+ } else if (await writeTextIfChanged(path, renderGeneratedPage(path, family, families[family], blocks))) written.push(path)
+ }
+ for (const family of [...EXCLUDED_PUBLIC_FAMILIES].sort()) {
+ const path = join(out, `${family.slice(4)}.md`)
+ try { await unlink(path); removed.push(path) } catch (error) {
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error
+ }
+ }
+ await importCollection('runtime', staged, site)
+ } finally { await rm(staged, { recursive: true, force: true }) }
+ if (written.length || removed.length) {
+ console.log('Wrote:')
+ for (const path of written) console.log(`- ${join(site, 'src/runtime/docs/javascript', basename(path, '.md'), 'page.md')}`)
+ for (const path of removed) console.log(`- removed ${join(site, 'src/runtime/docs/javascript', basename(path, '.md'), 'page.md')}`)
+ } else console.log('Unchanged: generated JavaScript API reference pages')
+}
+export async function main(args = process.argv.slice(2)): Promise {
+ const { values } = parseArgs({ args, options: { 'runtime-repo': { type: 'string' }, help: { type: 'boolean', short: 'h' } } })
+ if (values.help) { console.log('Refresh Runtime API Markdown while preserving curated prose.\n\nUsage: node runtime/tools/generate-js-api-reference.ts [--runtime-repo PATH]'); return }
+ const site = fileURLToPath(new URL('../../', import.meta.url))
+ await generateReference(site, values['runtime-repo'] ?? resolve(site, '../runtime'))
+}
+if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
+ main().catch(error => {
+ console.error(error.message)
+ process.exitCode = error.code?.startsWith('ERR_PARSE_ARGS_') ? 2 : 1
+ })
+}
diff --git a/runtime/tools/js-api-reference-content.ts b/runtime/tools/js-api-reference-content.ts
new file mode 100644
index 00000000..06280c58
--- /dev/null
+++ b/runtime/tools/js-api-reference-content.ts
@@ -0,0 +1,409 @@
+// Evaluated content from the original Python generator; preserve strings exactly.
+export const DESCRIPTION_BY_FAMILY: Record = {
+ "oro:asn1": "`oro:asn1` parses ASN.1 source text and files into structured module data.",
+ "oro:assert": "`oro:assert` provides Node-compatible assertion helpers for tests, runtime checks, and invariants.",
+ "oro:async": "`oro:async` exposes async context propagation, deferred values, and resource tracking primitives.",
+ "oro:async_hooks": "`oro:async_hooks` exposes hook-based async lifecycle inspection compatible with Node-style instrumentation.",
+ "oro:background": "`oro:background` lets you inspect and coordinate the runtime background task surface.",
+ "oro:buffer": "`oro:buffer` provides `Buffer`, `Blob`, and related byte-oriented helpers.",
+ "oro:cdp": "`oro:cdp` controls the runtime Chrome DevTools Protocol endpoint used for inspection and debugging.",
+ "oro:child_process": "`oro:child_process` launches and manages subprocesses with a Node-compatible API.",
+ "oro:clipboard": "`oro:clipboard` reads and writes clipboard text from the current application context.",
+ "oro:commonjs": "`oro:commonjs/*` exposes the runtime’s CommonJS loader, cache, and resolution helpers.",
+ "oro:conduit": "`oro:conduit` manages the runtime WebSocket conduit used for internal cross-surface messaging.",
+ "oro:console": "`oro:console` exposes the runtime console implementation and patch helpers.",
+ "oro:constants": "`oro:constants` publishes the runtime’s platform, errno, and low-level constant tables.",
+ "oro:cookies": "`oro:cookies` reads and writes cookies for application-controlled origins.",
+ "oro:crypto": "`oro:crypto` exposes hashing, random bytes, and bundled sodium-backed helpers.",
+ "oro:dbus": "`oro:dbus` connects to DBus and lets JavaScript code call methods, watch signals, and manage names.",
+ "oro:dgram": "`oro:dgram` provides UDP sockets and multicast helpers with a Node-compatible API.",
+ "oro:diagnostics": "`oro:diagnostics` exposes channels, metrics, and runtime/window diagnostic queries.",
+ "oro:did": "`oro:did` parses and manipulates decentralized identifiers and DID URLs.",
+ "oro:dns": "`oro:dns` provides DNS lookup helpers and promise-based resolution APIs.",
+ "oro:enumeration": "`oro:enumeration` is a small typed-set helper used throughout the runtime surface.",
+ "oro:errno": "`oro:errno` converts errno values to names and messages.",
+ "oro:errors": "`oro:errors` exports DOM-style and runtime-specific error classes and constants.",
+ "oro:events": "`oro:events` provides `EventEmitter`, `EventTarget`, and event primitives used across the runtime.",
+ "oro:extension": "`oro:extension` loads native and wasm extensions and reports extension runtime state.",
+ "oro:fetch": "`oro:fetch` exposes the runtime fetch stack and standard request/response primitives.",
+ "oro:gc": "`oro:gc` lets you register finalizers and control object retention hooks.",
+ "oro:hci": "`oro:hci` exposes host controller interface helpers for low-level Bluetooth adapter access.",
+ "oro:http": "`oro:http` provides Node-compatible HTTP client and server APIs.",
+ "oro:https": "`oro:https` provides HTTPS client and server APIs on top of the runtime TLS stack.",
+ "oro:i18n": "`oro:i18n` resolves UI languages, localized messages, and locale-aware metadata.",
+ "oro:ip": "`oro:ip` normalizes and validates IP address inputs.",
+ "oro:ipc": "`oro:ipc` exposes low-level runtime IPC helpers used to talk to the native host process.",
+ "oro:ipfs": "`oro:ipfs` manages the embedded IPFS node and common content operations.",
+ "oro:iroh": "`oro:iroh` exposes the runtime’s Iroh transport bindings for peer-to-peer connections and streams.",
+ "oro:language": "`oro:language` resolves language names, tags, and related metadata.",
+ "oro:latica": "`oro:latica/*` exposes the runtime’s Latica networking primitives, packet helpers, and crypto utilities.",
+ "oro:location": "`oro:location` normalizes the current runtime location and origin semantics across platforms.",
+ "oro:mime": "`oro:mime` looks up MIME types, extensions, and content-type metadata.",
+ "oro:module": "`oro:module` exposes module-loader helpers and builtin-module inspection.",
+ "oro:navigation": "`oro:navigation` surfaces the Navigation API state that the runtime makes available to application code.",
+ "oro:net": "`oro:net` provides Node-compatible TCP clients and servers.",
+ "oro:network": "`oro:network` exposes a higher-level networking surface built on the Latica stack.",
+ "oro:npm": "`oro:npm/*` supports package-resolution and service-worker plumbing for NPM-backed modules.",
+ "oro:os": "`oro:os` exposes platform, CPU, temporary-directory, and system metadata helpers.",
+ "oro:path": "`oro:path` provides cross-platform path manipulation utilities.",
+ "oro:process": "`oro:process` exposes runtime process state, env access, signals, and scheduling helpers.",
+ "oro:protocol-handlers": "`oro:protocol-handlers` exposes runtime helpers for custom protocol and service-worker routing.",
+ "oro:querystring": "`oro:querystring` parses and serializes URL query strings.",
+ "oro:semver": "`oro:semver` parses, validates, and compares semantic versions.",
+ "oro:service-worker": "`oro:service-worker/*` exposes the service-worker environment, lifecycle objects, and helpers.",
+ "oro:shared-worker": "`oro:shared-worker/*` exposes the shared-worker environment and runtime helpers.",
+ "oro:signal": "`oro:signal` exposes process-signal constants, conversion helpers, and event listeners.",
+ "oro:sqlite": "`oro:sqlite` opens SQLite databases and exposes sync and async query helpers.",
+ "oro:stream": "`oro:stream` exposes Node-style streams, Web streams, and pipeline helpers.",
+ "oro:string_decoder": "`oro:string_decoder` decodes split byte streams into text without corrupting multibyte characters.",
+ "oro:tar": "`oro:tar` opens, creates, streams, and extracts tar archives.",
+ "oro:tcp": "`oro:tcp` exposes lower-level TCP client and server helpers alongside the higher-level `oro:net` API.",
+ "oro:test": "`oro:test` provides the runtime test harness used by application and module tests.",
+ "oro:timers": "`oro:timers` exposes timer primitives, promise-based sleeps, and scheduler helpers.",
+ "oro:tls": "`oro:tls` exposes TLS sockets, servers, and certificate-pin helpers.",
+ "oro:toml": "`oro:toml` parses and serializes TOML documents.",
+ "oro:tty": "`oro:tty` exposes terminal streams and TTY detection helpers.",
+ "oro:url": "`oro:url` exposes the WHATWG URL implementation used by the runtime.",
+ "oro:usb": "`oro:usb` installs and exposes the runtime WebUSB surface for window contexts.",
+ "oro:util": "`oro:util` exposes inspection, debug, and type-checking helpers.",
+ "oro:vm": "`oro:vm` runs JavaScript in isolated runtime contexts and manages context windows and workers.",
+ "oro:worker": "`oro:worker` re-exports the runtime worker classes for dedicated, shared, and service workers.",
+ "oro:worker_threads": "`oro:worker_threads` exposes worker-thread primitives, ports, and environment data helpers.",
+ "oro:xpc": "`oro:xpc` connects to macOS XPC services and encodes structured XPC payloads.",
+ "oro:zlib": "`oro:zlib` compresses and decompresses buffers and exposes zlib stream helpers."
+}
+export const EXAMPLES_BY_FAMILY: Record = {
+ "oro:asn1": "Parse ASN.1 source text and inspect the discovered modules:\n\n```js\nimport { parse } from 'oro:asn1'\n\nconst document = await parse(`\nDemo DEFINITIONS ::= BEGIN\n serialNumber INTEGER ::= 42\nEND\n`)\n\nconsole.log(document.modulesCount)\nconsole.log(document.modules[0]?.name)\n```\n",
+ "oro:assert": "Use assertion helpers for runtime invariants or tests:\n\n```js\nimport { ok, strictEqual, deepEqual } from 'oro:assert'\n\nconst payload = { id: 7, tags: ['runtime', 'docs'] }\n\nok(payload.id > 0)\nstrictEqual(payload.tags.length, 2)\ndeepEqual(payload.tags, ['runtime', 'docs'])\n```\n",
+ "oro:async": "Propagate request-scoped state across async boundaries:\n\n```js\nimport { AsyncLocalStorage } from 'oro:async'\n\nconst store = new AsyncLocalStorage()\n\nawait store.run({ requestId: 'req-42' }, async () => {\n await Promise.resolve()\n console.log(store.getStore().requestId)\n})\n```\n",
+ "oro:async_hooks": "Inspect async-resource lifecycles as work is scheduled:\n\n```js\nimport { createHook, executionAsyncId } from 'oro:async_hooks'\n\nconst hook = createHook({\n init(asyncId, type, triggerAsyncId) {\n console.log({ asyncId, type, triggerAsyncId })\n }\n})\n\nhook.enable()\nqueueMicrotask(() => {\n console.log(executionAsyncId())\n hook.disable()\n})\n```\n",
+ "oro:background": "Check whether background execution is available before scheduling work:\n\n```js\nimport background from 'oro:background'\n\nif (background.available) {\n console.log(await background.status())\n}\n```\n",
+ "oro:buffer": "Create byte buffers and wrap them in higher-level binary containers:\n\n```js\nimport Buffer, { Blob } from 'oro:buffer'\n\nconst bytes = Buffer.from('hello runtime', 'utf8')\nconst payload = new Blob([bytes], { type: 'text/plain' })\n\nconsole.log(bytes.length)\nconsole.log(payload.size)\n```\n",
+ "oro:cdp": "Inspect the current DevTools state and start the endpoint when needed:\n\n```js\nimport { status, listen, close } from 'oro:cdp'\n\nconsole.log(await status())\nawait listen({ host: '127.0.0.1', port: 9222 })\n\n// ...connect a DevTools client...\n\nawait close()\n```\n",
+ "oro:child_process": "Spawn a subprocess and consume its stdout stream:\n\n```js\nimport { spawn } from 'oro:child_process'\n\nconst child = spawn('echo', ['hello from runtime'])\n\nchild.stdout.on('data', (chunk) => {\n console.log(chunk.toString())\n})\n\nawait new Promise((resolve, reject) => {\n child.once('close', resolve)\n child.once('error', reject)\n})\n```\n",
+ "oro:clipboard": "Read and write plain-text clipboard content:\n\n```js\nimport { writeText, readText, canWriteText } from 'oro:clipboard'\n\nif (canWriteText()) {\n await writeText('Copied from Oro Runtime')\n console.log(await readText())\n}\n```\n",
+ "oro:commonjs": "Resolve CommonJS modules from the current ES module:\n\n```js\nimport { Module, isBuiltin } from 'oro:commonjs'\n\nconst require = Module.createRequire(import.meta.url)\n\nconsole.log(isBuiltin('fs'))\nconsole.log(require('./package.json').name)\n```\n",
+ "oro:conduit": "Connect to the runtime conduit and inspect its current status:\n\n```js\nimport { Conduit } from 'oro:conduit'\n\nconst conduit = new Conduit({ id: 'docs' })\nawait conduit.connect()\n\nconsole.log(await Conduit.status())\n\nawait conduit.close()\n```\n",
+ "oro:console": "Patch the global console so application logs use the runtime console implementation:\n\n```js\nimport { patchGlobalConsole } from 'oro:console'\n\npatchGlobalConsole(globalThis.console)\n\nconsole.info('runtime console is active')\n```\n",
+ "oro:constants": "Use named constants instead of hard-coding errno or signal values:\n\n```js\nimport { EACCES, SIGTERM } from 'oro:constants'\n\nconsole.log({ EACCES, SIGTERM })\n```\n",
+ "oro:cookies": "Set, read, and remove cookies for an application-controlled origin:\n\n```js\nimport { set, get, remove } from 'oro:cookies'\n\nawait set('https://app.example', 'session=abc123; Path=/; HttpOnly')\nconsole.log(await get('https://app.example'))\nawait remove('https://app.example', 'session')\n```\n",
+ "oro:crypto": "Generate random bytes and hash them with the runtime digest helper:\n\n```js\nimport { randomBytes, createDigest, murmur3 } from 'oro:crypto'\n\nconst nonce = randomBytes(16)\nconst digest = await createDigest('sha256', nonce)\n\nconsole.log(digest.toString('hex'))\nconsole.log(murmur3('hello-runtime'))\n```\n",
+ "oro:dbus": "Check DBus availability and open a session-bus connection:\n\n```js\nimport { availability, connect, BUS } from 'oro:dbus'\n\nconst state = await availability()\n\nif (state.available) {\n const bus = await connect({ bus: BUS.SESSION })\n console.log(bus.id)\n await bus.close()\n}\n```\n",
+ "oro:dgram": "Create a UDP socket and send a datagram:\n\n```js\nimport Buffer from 'oro:buffer'\nimport { createSocket } from 'oro:dgram'\n\nconst socket = createSocket('udp4')\n\nsocket.on('message', (message, rinfo) => {\n console.log(rinfo.address, message.toString())\n})\n\nsocket.bind(41234)\nsocket.send(Buffer.from('ping'), 41235, '127.0.0.1')\n```\n",
+ "oro:diagnostics": "Publish and subscribe to a diagnostics channel:\n\n```js\nimport { channel } from 'oro:diagnostics'\n\nconst buildChannel = channel('build')\n\nbuildChannel.subscribe('step', (message) => {\n console.log(message.step)\n})\n\nawait buildChannel.publish('step', { step: 'bundle-ready' })\n```\n",
+ "oro:did": "Parse a DID and a DID URL:\n\n```js\nimport { parse, parseUrl } from 'oro:did'\n\nconst did = parse('did:key:z6Mkkx...')\nconst url = parseUrl('did:key:z6Mkkx...#keys-1')\n\nconsole.log(did.method)\nconsole.log(url.fragment)\n```\n",
+ "oro:dns": "Resolve hostnames with the promise-based DNS helpers:\n\n```js\nimport { lookup } from 'oro:dns/promises'\n\nconst record = await lookup('oro.computer')\n\nconsole.log(record.address)\nconsole.log(record.family)\n```\n",
+ "oro:enumeration": "Use `Enumeration` when you want a typed, readable set of allowed values:\n\n```js\nimport Enumeration from 'oro:enumeration'\n\nconst states = new Enumeration(['draft', 'ready', 'shipped'])\n\nconsole.log(states.has('ready'))\nconsole.log([...states])\n```\n",
+ "oro:errno": "Convert errno values to names and human-readable messages:\n\n```js\nimport { EACCES, getName, getMessage } from 'oro:errno'\n\nconsole.log(getName(EACCES))\nconsole.log(getMessage(EACCES))\n```\n",
+ "oro:errors": "Construct runtime error types directly when surfacing failures:\n\n```js\nimport { AbortError, NotFoundError } from 'oro:errors'\n\nconst abort = new AbortError('request cancelled')\nconst missing = new NotFoundError('resource not found')\n\nconsole.log(abort.name, missing.name)\n```\n",
+ "oro:events": "Use `EventEmitter` for familiar event-based application code:\n\n```js\nimport EventEmitter from 'oro:events'\n\nconst bus = new EventEmitter()\n\nbus.on('ready', (value) => console.log(value))\nbus.emit('ready', { ok: true })\n```\n",
+ "oro:extension": "Load a runtime extension and inspect the resulting binding:\n\n```js\nimport { load, stats } from 'oro:extension'\n\nconst extension = await load('image-tools', { allow: ['resize'] })\n\nconsole.log(extension.type)\nconsole.log(await stats())\n\nawait extension.unload()\n```\n",
+ "oro:fetch": "Use the runtime fetch stack exactly like standard web fetch:\n\n```js\nimport fetch from 'oro:fetch'\n\nconst response = await fetch('https://example.com/api/status')\nconst data = await response.json()\n\nconsole.log(data)\n```\n",
+ "oro:gc": "Register a finalizer on an object that owns runtime resources:\n\n```js\nimport { ref, unref } from 'oro:gc'\n\nconst resource = {\n [Symbol.for('oro.runtime.gc.finalize')]() {\n console.log('cleaning up resource')\n }\n}\n\nref(resource)\nunref(resource)\n```\n",
+ "oro:hci": "Inspect local Bluetooth adapters before opening a low-level HCI socket:\n\n```js\nimport { listAdapters, getAdapter } from 'oro:hci'\n\nconst adapters = listAdapters()\nconsole.log(adapters)\n\nif (adapters.length > 0) {\n console.log(getAdapter(adapters[0].devId))\n}\n```\n",
+ "oro:http": "Start a small HTTP server with the Node-compatible API:\n\n```js\nimport { createServer } from 'oro:http'\n\nconst server = createServer((req, res) => {\n res.writeHead(200, { 'content-type': 'text/plain' })\n res.end('hello from oro:http')\n})\n\nserver.listen(8080, '127.0.0.1')\n```\n",
+ "oro:https": "Issue an HTTPS request with the familiar client helper:\n\n```js\nimport process from 'oro:process'\nimport { get } from 'oro:https'\n\nget('https://example.com', (res) => {\n res.on('data', (chunk) => process.stdout.write(chunk))\n})\n```\n",
+ "oro:i18n": "Resolve the current UI language and look up a localized message:\n\n```js\nimport { getUILanguage, getMessage } from 'oro:i18n'\n\nconsole.log(getUILanguage())\nconsole.log(getMessage('menu.file.open'))\n```\n",
+ "oro:ip": "Normalize user-provided IP input before using it in socket code:\n\n```js\nimport { normalizeIPv4, isIPv4 } from 'oro:ip'\n\nconst address = normalizeIPv4('127.000.000.001')\n\nconsole.log(address)\nconsole.log(isIPv4(address))\n```\n",
+ "oro:ipc": "Wait for the runtime IPC bridge and post a structured message:\n\n```js\nimport { ready, postMessage, debug } from 'oro:ipc'\n\ndebug(true)\nawait ready()\n\npostMessage({ type: 'docs-demo', at: Date.now() })\n```\n",
+ "oro:ipfs": "Ensure the embedded IPFS node is running before adding local content:\n\n```js\nimport { ensureStarted, add, status } from 'oro:ipfs'\n\nawait ensureStarted({ repoPath: './ipfs-repo' })\nconsole.log(await status())\n\nconst { cid } = await add('/absolute/path/to/report.txt')\nconsole.log(cid)\n```\n",
+ "oro:iroh": "Initialize the Iroh transport and create an endpoint:\n\n```js\nimport { ensureInitialized, Endpoint } from 'oro:iroh'\n\nawait ensureInitialized()\n\nconst endpoint = await Endpoint.create()\nawait endpoint.bind()\n\nconsole.log(await endpoint.nodeAddr())\n\nawait endpoint.close()\n```\n",
+ "oro:language": "Resolve language tags and metadata from user input:\n\n```js\nimport { lookup, describe } from 'oro:language'\n\nconsole.log(lookup('en-US'))\nconsole.log(describe('English'))\n```\n",
+ "oro:latica": "Use Latica helpers directly when you are working at the packet and transport layer:\n\n```js\nimport { sha256 } from 'oro:latica'\n\nconst topic = await sha256(new TextEncoder().encode('docs-demo'))\n\nconsole.log(topic)\n```\n",
+ "oro:location": "Inspect the runtime-normalized location values for the current context:\n\n```js\nimport location from 'oro:location'\n\nconsole.log(location.href)\nconsole.log(location.origin)\n```\n",
+ "oro:mime": "Look up MIME metadata for a type or extension:\n\n```js\nimport { lookupSync } from 'oro:mime'\n\nconsole.log(lookupSync('application/json'))\nconsole.log(lookupSync('.png'))\n```\n",
+ "oro:module": "Inspect builtin modules and create a `require()` function when you need one:\n\n```js\nimport Module, { builtinModules, isBuiltin, createRequire } from 'oro:module'\n\nconst require = createRequire(import.meta.url)\n\nconsole.log(isBuiltin('oro:path'))\nconsole.log(Object.keys(builtinModules).length)\nconsole.log(typeof Module)\nconsole.log(typeof require)\n```\n",
+ "oro:navigation": "Read the current navigation entry and listen for browser-style navigation events:\n\n```js\nimport navigation from 'oro:navigation'\n\nconsole.log(navigation.currentEntry?.url)\n\nnavigation.addEventListener('navigate', (event) => {\n console.log(event.destination.url)\n})\n```\n",
+ "oro:net": "Build a TCP echo server with the Node-compatible net API:\n\n```js\nimport { createServer } from 'oro:net'\n\nconst server = createServer({}, (socket) => {\n socket.on('data', (chunk) => socket.write(chunk))\n})\n\nserver.listen(4040, '127.0.0.1')\n```\n",
+ "oro:network": "Start the higher-level networking surface and attach event listeners:\n\n```js\nimport network from 'oro:network'\n\nconst bus = await network({})\n\nbus.on('error', console.error)\nbus.on('message', (message) => console.log(message))\n```\n",
+ "oro:npm": "Resolve an NPM specifier to the runtime module URL it will load:\n\n```js\nimport { resolve } from 'oro:npm/module'\n\nconst resolved = resolve('react', import.meta.url)\n\nconsole.log(resolved?.url)\nconsole.log(resolved?.type)\n```\n",
+ "oro:os": "Inspect the current platform and locate scratch space:\n\n```js\nimport { arch, platform, tmpdir, cpus } from 'oro:os'\n\nconsole.log(platform(), arch())\nconsole.log(tmpdir())\nconsole.log(cpus().length)\n```\n",
+ "oro:path": "Compose paths without branching on platform separators:\n\n```js\nimport { resolve, join, dirname, basename } from 'oro:path'\n\nconst absolute = resolve('dist', 'assets', 'logo.svg')\n\nconsole.log(absolute)\nconsole.log(dirname(absolute))\nconsole.log(basename(join('/tmp', 'report.txt')))\n```\n",
+ "oro:process": "Inspect process state and schedule work on the next tick:\n\n```js\nimport process, { env, hrtime, nextTick } from 'oro:process'\n\nconsole.log(env.PATH)\nconsole.log(hrtime())\n\nnextTick(() => {\n console.log(process.pid)\n})\n```\n",
+ "oro:protocol-handlers": "Ask the runtime which service worker is currently handling a custom scheme:\n\n```js\nimport { getServiceWorker } from 'oro:protocol-handlers'\n\nconst worker = await getServiceWorker({ scheme: 'npm' })\n\nconsole.log(worker)\n```\n",
+ "oro:querystring": "Parse and serialize query strings with the Node-compatible helpers:\n\n```js\nimport { stringify, parse } from 'oro:querystring'\n\nconst query = stringify({ q: 'oro runtime', page: 2 })\n\nconsole.log(query)\nconsole.log(parse(query))\n```\n",
+ "oro:semver": "Validate and compare versions before running upgrade logic:\n\n```js\nimport { valid, compare, gt } from 'oro:semver'\n\nconsole.log(valid('1.4.0'))\nconsole.log(compare('1.4.0', '1.3.9'))\nconsole.log(gt('2.0.0', '1.9.0'))\n```\n",
+ "oro:service-worker": "Use the runtime service-worker helpers from inside a service-worker scope:\n\n```js\nimport { env } from 'oro:service-worker'\n\nif (env) {\n self.addEventListener('fetch', (event) => {\n event.respondWith(fetch(event.request))\n })\n}\n```\n",
+ "oro:shared-worker": "Launch a shared worker and communicate through its shared port:\n\n```js\nimport { SharedWorker } from 'oro:shared-worker'\n\nconst worker = new SharedWorker(new URL('./shared.js', import.meta.url))\n\nworker.port.start()\nworker.port.postMessage({ type: 'warmup' })\n```\n",
+ "oro:signal": "Listen for runtime process signals using the signal helper surface:\n\n```js\nimport signal from 'oro:signal'\n\nsignal.addEventListener('SIGTERM', (event) => {\n console.log(event.type)\n})\n```\n",
+ "oro:sqlite": "Open an in-memory database and run a few synchronous queries:\n\n```js\nimport { open, OPEN_CREATE, OPEN_MEMORY } from 'oro:sqlite'\n\nconst db = open(':memory:', { flags: OPEN_CREATE | OPEN_MEMORY })\n\ndb.exec('create table notes (id integer primary key, title text)')\ndb.exec(\"insert into notes (title) values ('hello')\")\n\nconsole.log(db.query('select * from notes'))\n```\n",
+ "oro:stream": "Connect streams together with the pipeline helper:\n\n```js\nimport process from 'oro:process'\nimport { Readable, Writable, pipelinePromise } from 'oro:stream'\n\nconst source = Readable.from(['oro', ' ', 'runtime'])\nconst sink = new Writable({\n write(chunk, _encoding, done) {\n process.stdout.write(chunk)\n done()\n }\n})\n\nawait pipelinePromise(source, sink)\n```\n",
+ "oro:string_decoder": "Decode chunked UTF-8 data without splitting multibyte characters:\n\n```js\nimport Buffer from 'oro:buffer'\nimport StringDecoder from 'oro:string_decoder'\n\nconst decoder = new StringDecoder('utf8')\nconst chunks = [Buffer.from([0xe2, 0x82]), Buffer.from([0xac])]\n\nconsole.log(decoder.write(chunks[0]))\nconsole.log(decoder.write(chunks[1]))\n```\n",
+ "oro:tar": "Build a tar archive in memory and inspect its entries:\n\n```js\nimport Buffer from 'oro:buffer'\nimport { createInMemory } from 'oro:tar'\n\nconst archive = await createInMemory()\n\nawait archive.append({ path: 'hello.txt' }, Buffer.from('hello'))\nawait archive.finalize()\n\nconsole.log(await archive.entries())\n```\n",
+ "oro:tcp": "Open a lower-level TCP server when you want more direct socket control:\n\n```js\nimport { createServer } from 'oro:tcp'\n\nconst server = createServer((socket) => {\n socket.write(Buffer.from('connected'))\n})\n\nserver.listen({ port: 5050, host: '127.0.0.1' })\n```\n",
+ "oro:test": "Write runtime tests with the built-in test harness:\n\n```js\nimport test from 'oro:test'\n\ntest('querystring roundtrip', async (t) => {\n t.equal('a=1', new URLSearchParams({ a: '1' }).toString())\n})\n```\n",
+ "oro:timers": "Sleep without writing your own timeout wrapper:\n\n```js\nimport { sleep } from 'oro:timers'\n\nawait sleep(250)\n\nconsole.log('quarter second elapsed')\n```\n",
+ "oro:tls": "Configure TLS pins before opening pinned connections:\n\n```js\nimport { setTlsPins, getTlsPins, clearTlsPins } from 'oro:tls'\n\nawait setTlsPins('sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')\nconsole.log(await getTlsPins())\nawait clearTlsPins()\n```\n",
+ "oro:toml": "Parse configuration and emit updated TOML back to disk:\n\n```js\nimport { parse, stringify } from 'oro:toml'\n\nconst config = parse('name = \"oro\"\\nport = 8080')\nconfig.debug = true\n\nconsole.log(stringify(config))\n```\n",
+ "oro:tty": "Detect interactive terminals before enabling richer output:\n\n```js\nimport { isatty } from 'oro:tty'\n\nconsole.log(isatty(0))\nconsole.log(isatty(1))\n```\n",
+ "oro:url": "Use the WHATWG URL implementation for robust URL parsing:\n\n```js\nimport URL from 'oro:url'\n\nconst url = new URL('https://oro.computer/docs?project=runtime')\n\nconsole.log(url.pathname)\nconsole.log(url.searchParams.get('project'))\n```\n",
+ "oro:usb": "Install the runtime WebUSB surface and request a device in window contexts:\n\n```js\nimport { installNavigatorUSB } from 'oro:usb'\n\nconst usb = installNavigatorUSB()\n\nif (usb) {\n console.log(await usb.getDevices())\n // const device = await usb.requestDevice({ filters: [{ vendorId: 0x1209 }] })\n}\n```\n",
+ "oro:util": "Use the runtime debug helper and common type predicates:\n\n```js\nimport { debug, isTypedArray } from 'oro:util'\n\nconst log = debug('docs')\nconst payload = new Uint8Array([1, 2, 3])\n\nlog('payload ready')\nconsole.log(isTypedArray(payload))\n```\n",
+ "oro:vm": "Run a script inside an isolated context object:\n\n```js\nimport { runInContext } from 'oro:vm'\n\nconst context = { answer: 41 }\nconst result = await runInContext('answer + 1', context)\n\nconsole.log(result)\n```\n",
+ "oro:worker": "Launch a dedicated worker through the runtime worker surface:\n\n```js\nimport Worker from 'oro:worker'\n\nconst worker = new Worker(new URL('./worker.js', import.meta.url), {\n workerData: { job: 'thumbnail' }\n})\n\nworker.postMessage({ type: 'start' })\n```\n",
+ "oro:worker_threads": "Pass environment data into worker threads and communicate with message channels:\n\n```js\nimport { MessageChannel, setEnvironmentData } from 'oro:worker_threads'\n\nsetEnvironmentData('traceId', 'req-42')\n\nconst { port1, port2 } = new MessageChannel()\nport1.onmessage = (event) => console.log(event.data)\nport2.postMessage({ ok: true })\n```\n",
+ "oro:xpc": "Open an XPC connection, send a request, and close it cleanly:\n\n```js\nimport { availability, connect } from 'oro:xpc'\n\nconst state = await availability()\n\nif (state.available) {\n const connection = await connect({ service: 'com.example.agent' })\n await connection.sendAndForget({ type: 'ping' })\n await connection.close()\n}\n```\n",
+ "oro:zlib": "Compress and decompress buffers with the promise-based helpers:\n\n```js\nimport { gzip, gunzip } from 'oro:zlib'\n\nconst compressed = await gzip('hello runtime')\nconst restored = await gunzip(compressed)\n\nconsole.log(restored.toString())\n```\n"
+}
+export const GUIDE_REFS_BY_FAMILY: Record = {
+ "oro:application": [
+ [
+ "Windows and messaging",
+ "guides/windows-and-messaging"
+ ],
+ [
+ "Lifecycle hooks and deep links",
+ "guides/lifecycle-hooks-and-deep-links"
+ ],
+ [
+ "Desktop integrations",
+ "guides/desktop-integrations"
+ ]
+ ],
+ "oro:assert": [
+ [
+ "Testing and diagnostics",
+ "guides/testing-and-diagnostics"
+ ]
+ ],
+ "oro:cdp": [
+ [
+ "Testing and diagnostics",
+ "guides/testing-and-diagnostics"
+ ]
+ ],
+ "oro:child_process": [
+ [
+ "Configure your runtime project",
+ "guides/configure-your-runtime-project"
+ ],
+ [
+ "Testing and diagnostics",
+ "guides/testing-and-diagnostics"
+ ]
+ ],
+ "oro:clipboard": [
+ [
+ "Desktop integrations",
+ "guides/desktop-integrations"
+ ]
+ ],
+ "oro:console": [
+ [
+ "Testing and diagnostics",
+ "guides/testing-and-diagnostics"
+ ]
+ ],
+ "oro:crypto": [
+ [
+ "Secure storage and sessions",
+ "guides/secure-storage-and-sessions"
+ ]
+ ],
+ "oro:diagnostics": [
+ [
+ "Testing and diagnostics",
+ "guides/testing-and-diagnostics"
+ ]
+ ],
+ "oro:errors": [
+ [
+ "Testing and diagnostics",
+ "guides/testing-and-diagnostics"
+ ]
+ ],
+ "oro:extension": [
+ [
+ "Native extensions",
+ "guides/native-extensions"
+ ]
+ ],
+ "oro:fetch": [
+ [
+ "Calling HTTP APIs",
+ "guides/calling-http-apis"
+ ],
+ [
+ "Offline-first with service workers",
+ "guides/offline-first-with-service-workers"
+ ]
+ ],
+ "oro:fs": [
+ [
+ "Files and sandboxing",
+ "guides/files-and-sandboxing"
+ ],
+ [
+ "Project layout",
+ "guides/project-layout"
+ ]
+ ],
+ "oro:hooks": [
+ [
+ "Lifecycle hooks and deep links",
+ "guides/lifecycle-hooks-and-deep-links"
+ ],
+ [
+ "Windows and messaging",
+ "guides/windows-and-messaging"
+ ]
+ ],
+ "oro:http": [
+ [
+ "Calling HTTP APIs",
+ "guides/calling-http-apis"
+ ]
+ ],
+ "oro:https": [
+ [
+ "Calling HTTP APIs",
+ "guides/calling-http-apis"
+ ]
+ ],
+ "oro:ipc": [
+ [
+ "Native extensions",
+ "guides/native-extensions"
+ ],
+ [
+ "Windows and messaging",
+ "guides/windows-and-messaging"
+ ]
+ ],
+ "oro:location": [
+ [
+ "Lifecycle hooks and deep links",
+ "guides/lifecycle-hooks-and-deep-links"
+ ]
+ ],
+ "oro:mcp": [
+ [
+ "MCP and agent automation",
+ "guides/mcp-and-agent-automation"
+ ]
+ ],
+ "oro:navigation": [
+ [
+ "Lifecycle hooks and deep links",
+ "guides/lifecycle-hooks-and-deep-links"
+ ],
+ [
+ "Windows and messaging",
+ "guides/windows-and-messaging"
+ ]
+ ],
+ "oro:notification": [
+ [
+ "Desktop integrations",
+ "guides/desktop-integrations"
+ ],
+ [
+ "Lifecycle hooks and deep links",
+ "guides/lifecycle-hooks-and-deep-links"
+ ]
+ ],
+ "oro:path": [
+ [
+ "Files and sandboxing",
+ "guides/files-and-sandboxing"
+ ],
+ [
+ "Project layout",
+ "guides/project-layout"
+ ]
+ ],
+ "oro:process": [
+ [
+ "Configure your runtime project",
+ "guides/configure-your-runtime-project"
+ ]
+ ],
+ "oro:protocol-handlers": [
+ [
+ "Custom protocols and routing",
+ "guides/custom-protocols-and-routing"
+ ],
+ [
+ "Offline-first with service workers",
+ "guides/offline-first-with-service-workers"
+ ]
+ ],
+ "oro:secure-storage": [
+ [
+ "Secure storage and sessions",
+ "guides/secure-storage-and-sessions"
+ ]
+ ],
+ "oro:service-worker": [
+ [
+ "Offline-first with service workers",
+ "guides/offline-first-with-service-workers"
+ ],
+ [
+ "Custom protocols and routing",
+ "guides/custom-protocols-and-routing"
+ ]
+ ],
+ "oro:shared-worker": [
+ [
+ "Shared workers across windows",
+ "guides/shared-workers-across-windows"
+ ],
+ [
+ "Windows and messaging",
+ "guides/windows-and-messaging"
+ ]
+ ],
+ "oro:sqlite": [
+ [
+ "Local data with SQLite",
+ "guides/local-data-with-sqlite"
+ ],
+ [
+ "Files and sandboxing",
+ "guides/files-and-sandboxing"
+ ]
+ ],
+ "oro:test": [
+ [
+ "Testing and diagnostics",
+ "guides/testing-and-diagnostics"
+ ]
+ ],
+ "oro:worker": [
+ [
+ "Worker threads for heavy work",
+ "guides/worker-threads-for-heavy-work"
+ ],
+ [
+ "Shared workers across windows",
+ "guides/shared-workers-across-windows"
+ ]
+ ],
+ "oro:worker_threads": [
+ [
+ "Worker threads for heavy work",
+ "guides/worker-threads-for-heavy-work"
+ ]
+ ],
+ "oro:window": [
+ [
+ "Windows and messaging",
+ "guides/windows-and-messaging"
+ ],
+ [
+ "Desktop integrations",
+ "guides/desktop-integrations"
+ ]
+ ]
+}
+export const DEFAULT_SEE_ALSO: readonly (readonly [string, string])[] = [
+ [
+ "JavaScript APIs overview",
+ "javascript/overview"
+ ],
+ [
+ "All module specifiers",
+ "javascript/all-modules"
+ ]
+]
diff --git a/runtime/tools/js_api_reference_content.py b/runtime/tools/js_api_reference_content.py
deleted file mode 100644
index cca22c85..00000000
--- a/runtime/tools/js_api_reference_content.py
+++ /dev/null
@@ -1,1256 +0,0 @@
-from __future__ import annotations
-
-from textwrap import dedent
-
-
-def md(text: str) -> str:
- return dedent(text).strip() + "\n"
-
-
-DESCRIPTION_BY_FAMILY: dict[str, str] = {
- "oro:asn1": "`oro:asn1` parses ASN.1 source text and files into structured module data.",
- "oro:assert": "`oro:assert` provides Node-compatible assertion helpers for tests, runtime checks, and invariants.",
- "oro:async": "`oro:async` exposes async context propagation, deferred values, and resource tracking primitives.",
- "oro:async_hooks": "`oro:async_hooks` exposes hook-based async lifecycle inspection compatible with Node-style instrumentation.",
- "oro:background": "`oro:background` lets you inspect and coordinate the runtime background task surface.",
- "oro:buffer": "`oro:buffer` provides `Buffer`, `Blob`, and related byte-oriented helpers.",
- "oro:cdp": "`oro:cdp` controls the runtime Chrome DevTools Protocol endpoint used for inspection and debugging.",
- "oro:child_process": "`oro:child_process` launches and manages subprocesses with a Node-compatible API.",
- "oro:clipboard": "`oro:clipboard` reads and writes clipboard text from the current application context.",
- "oro:commonjs": "`oro:commonjs/*` exposes the runtime’s CommonJS loader, cache, and resolution helpers.",
- "oro:conduit": "`oro:conduit` manages the runtime WebSocket conduit used for internal cross-surface messaging.",
- "oro:console": "`oro:console` exposes the runtime console implementation and patch helpers.",
- "oro:constants": "`oro:constants` publishes the runtime’s platform, errno, and low-level constant tables.",
- "oro:cookies": "`oro:cookies` reads and writes cookies for application-controlled origins.",
- "oro:crypto": "`oro:crypto` exposes hashing, random bytes, and bundled sodium-backed helpers.",
- "oro:dbus": "`oro:dbus` connects to DBus and lets JavaScript code call methods, watch signals, and manage names.",
- "oro:dgram": "`oro:dgram` provides UDP sockets and multicast helpers with a Node-compatible API.",
- "oro:diagnostics": "`oro:diagnostics` exposes channels, metrics, and runtime/window diagnostic queries.",
- "oro:did": "`oro:did` parses and manipulates decentralized identifiers and DID URLs.",
- "oro:dns": "`oro:dns` provides DNS lookup helpers and promise-based resolution APIs.",
- "oro:enumeration": "`oro:enumeration` is a small typed-set helper used throughout the runtime surface.",
- "oro:errno": "`oro:errno` converts errno values to names and messages.",
- "oro:errors": "`oro:errors` exports DOM-style and runtime-specific error classes and constants.",
- "oro:events": "`oro:events` provides `EventEmitter`, `EventTarget`, and event primitives used across the runtime.",
- "oro:extension": "`oro:extension` loads native and wasm extensions and reports extension runtime state.",
- "oro:fetch": "`oro:fetch` exposes the runtime fetch stack and standard request/response primitives.",
- "oro:gc": "`oro:gc` lets you register finalizers and control object retention hooks.",
- "oro:hci": "`oro:hci` exposes host controller interface helpers for low-level Bluetooth adapter access.",
- "oro:http": "`oro:http` provides Node-compatible HTTP client and server APIs.",
- "oro:https": "`oro:https` provides HTTPS client and server APIs on top of the runtime TLS stack.",
- "oro:i18n": "`oro:i18n` resolves UI languages, localized messages, and locale-aware metadata.",
- "oro:ip": "`oro:ip` normalizes and validates IP address inputs.",
- "oro:ipc": "`oro:ipc` exposes low-level runtime IPC helpers used to talk to the native host process.",
- "oro:ipfs": "`oro:ipfs` manages the embedded IPFS node and common content operations.",
- "oro:iroh": "`oro:iroh` exposes the runtime’s Iroh transport bindings for peer-to-peer connections and streams.",
- "oro:language": "`oro:language` resolves language names, tags, and related metadata.",
- "oro:latica": "`oro:latica/*` exposes the runtime’s Latica networking primitives, packet helpers, and crypto utilities.",
- "oro:location": "`oro:location` normalizes the current runtime location and origin semantics across platforms.",
- "oro:mime": "`oro:mime` looks up MIME types, extensions, and content-type metadata.",
- "oro:module": "`oro:module` exposes module-loader helpers and builtin-module inspection.",
- "oro:navigation": "`oro:navigation` surfaces the Navigation API state that the runtime makes available to application code.",
- "oro:net": "`oro:net` provides Node-compatible TCP clients and servers.",
- "oro:network": "`oro:network` exposes a higher-level networking surface built on the Latica stack.",
- "oro:npm": "`oro:npm/*` supports package-resolution and service-worker plumbing for NPM-backed modules.",
- "oro:os": "`oro:os` exposes platform, CPU, temporary-directory, and system metadata helpers.",
- "oro:path": "`oro:path` provides cross-platform path manipulation utilities.",
- "oro:process": "`oro:process` exposes runtime process state, env access, signals, and scheduling helpers.",
- "oro:protocol-handlers": "`oro:protocol-handlers` exposes runtime helpers for custom protocol and service-worker routing.",
- "oro:querystring": "`oro:querystring` parses and serializes URL query strings.",
- "oro:semver": "`oro:semver` parses, validates, and compares semantic versions.",
- "oro:service-worker": "`oro:service-worker/*` exposes the service-worker environment, lifecycle objects, and helpers.",
- "oro:shared-worker": "`oro:shared-worker/*` exposes the shared-worker environment and runtime helpers.",
- "oro:signal": "`oro:signal` exposes process-signal constants, conversion helpers, and event listeners.",
- "oro:sqlite": "`oro:sqlite` opens SQLite databases and exposes sync and async query helpers.",
- "oro:stream": "`oro:stream` exposes Node-style streams, Web streams, and pipeline helpers.",
- "oro:string_decoder": "`oro:string_decoder` decodes split byte streams into text without corrupting multibyte characters.",
- "oro:tar": "`oro:tar` opens, creates, streams, and extracts tar archives.",
- "oro:tcp": "`oro:tcp` exposes lower-level TCP client and server helpers alongside the higher-level `oro:net` API.",
- "oro:test": "`oro:test` provides the runtime test harness used by application and module tests.",
- "oro:timers": "`oro:timers` exposes timer primitives, promise-based sleeps, and scheduler helpers.",
- "oro:tls": "`oro:tls` exposes TLS sockets, servers, and certificate-pin helpers.",
- "oro:toml": "`oro:toml` parses and serializes TOML documents.",
- "oro:tty": "`oro:tty` exposes terminal streams and TTY detection helpers.",
- "oro:url": "`oro:url` exposes the WHATWG URL implementation used by the runtime.",
- "oro:usb": "`oro:usb` installs and exposes the runtime WebUSB surface for window contexts.",
- "oro:util": "`oro:util` exposes inspection, debug, and type-checking helpers.",
- "oro:vm": "`oro:vm` runs JavaScript in isolated runtime contexts and manages context windows and workers.",
- "oro:worker": "`oro:worker` re-exports the runtime worker classes for dedicated, shared, and service workers.",
- "oro:worker_threads": "`oro:worker_threads` exposes worker-thread primitives, ports, and environment data helpers.",
- "oro:xpc": "`oro:xpc` connects to macOS XPC services and encodes structured XPC payloads.",
- "oro:zlib": "`oro:zlib` compresses and decompresses buffers and exposes zlib stream helpers.",
-}
-
-
-EXAMPLES_BY_FAMILY: dict[str, str] = {
- "oro:asn1": md(
- """
- Parse ASN.1 source text and inspect the discovered modules:
-
- ```js
- import { parse } from 'oro:asn1'
-
- const document = await parse(`
- Demo DEFINITIONS ::= BEGIN
- serialNumber INTEGER ::= 42
- END
- `)
-
- console.log(document.modulesCount)
- console.log(document.modules[0]?.name)
- ```
- """
- ),
- "oro:assert": md(
- """
- Use assertion helpers for runtime invariants or tests:
-
- ```js
- import { ok, strictEqual, deepEqual } from 'oro:assert'
-
- const payload = { id: 7, tags: ['runtime', 'docs'] }
-
- ok(payload.id > 0)
- strictEqual(payload.tags.length, 2)
- deepEqual(payload.tags, ['runtime', 'docs'])
- ```
- """
- ),
- "oro:async": md(
- """
- Propagate request-scoped state across async boundaries:
-
- ```js
- import { AsyncLocalStorage } from 'oro:async'
-
- const store = new AsyncLocalStorage()
-
- await store.run({ requestId: 'req-42' }, async () => {
- await Promise.resolve()
- console.log(store.getStore().requestId)
- })
- ```
- """
- ),
- "oro:async_hooks": md(
- """
- Inspect async-resource lifecycles as work is scheduled:
-
- ```js
- import { createHook, executionAsyncId } from 'oro:async_hooks'
-
- const hook = createHook({
- init(asyncId, type, triggerAsyncId) {
- console.log({ asyncId, type, triggerAsyncId })
- }
- })
-
- hook.enable()
- queueMicrotask(() => {
- console.log(executionAsyncId())
- hook.disable()
- })
- ```
- """
- ),
- "oro:background": md(
- """
- Check whether background execution is available before scheduling work:
-
- ```js
- import background from 'oro:background'
-
- if (background.available) {
- console.log(await background.status())
- }
- ```
- """
- ),
- "oro:buffer": md(
- """
- Create byte buffers and wrap them in higher-level binary containers:
-
- ```js
- import Buffer, { Blob } from 'oro:buffer'
-
- const bytes = Buffer.from('hello runtime', 'utf8')
- const payload = new Blob([bytes], { type: 'text/plain' })
-
- console.log(bytes.length)
- console.log(payload.size)
- ```
- """
- ),
- "oro:cdp": md(
- """
- Inspect the current DevTools state and start the endpoint when needed:
-
- ```js
- import { status, listen, close } from 'oro:cdp'
-
- console.log(await status())
- await listen({ host: '127.0.0.1', port: 9222 })
-
- // ...connect a DevTools client...
-
- await close()
- ```
- """
- ),
- "oro:child_process": md(
- """
- Spawn a subprocess and consume its stdout stream:
-
- ```js
- import { spawn } from 'oro:child_process'
-
- const child = spawn('echo', ['hello from runtime'])
-
- child.stdout.on('data', (chunk) => {
- console.log(chunk.toString())
- })
-
- await new Promise((resolve, reject) => {
- child.once('close', resolve)
- child.once('error', reject)
- })
- ```
- """
- ),
- "oro:clipboard": md(
- """
- Read and write plain-text clipboard content:
-
- ```js
- import { writeText, readText, canWriteText } from 'oro:clipboard'
-
- if (canWriteText()) {
- await writeText('Copied from Oro Runtime')
- console.log(await readText())
- }
- ```
- """
- ),
- "oro:commonjs": md(
- """
- Resolve CommonJS modules from the current ES module:
-
- ```js
- import { Module, isBuiltin } from 'oro:commonjs'
-
- const require = Module.createRequire(import.meta.url)
-
- console.log(isBuiltin('fs'))
- console.log(require('./package.json').name)
- ```
- """
- ),
- "oro:conduit": md(
- """
- Connect to the runtime conduit and inspect its current status:
-
- ```js
- import { Conduit } from 'oro:conduit'
-
- const conduit = new Conduit({ id: 'docs' })
- await conduit.connect()
-
- console.log(await Conduit.status())
-
- await conduit.close()
- ```
- """
- ),
- "oro:console": md(
- """
- Patch the global console so application logs use the runtime console implementation:
-
- ```js
- import { patchGlobalConsole } from 'oro:console'
-
- patchGlobalConsole(globalThis.console)
-
- console.info('runtime console is active')
- ```
- """
- ),
- "oro:constants": md(
- """
- Use named constants instead of hard-coding errno or signal values:
-
- ```js
- import { EACCES, SIGTERM } from 'oro:constants'
-
- console.log({ EACCES, SIGTERM })
- ```
- """
- ),
- "oro:cookies": md(
- """
- Set, read, and remove cookies for an application-controlled origin:
-
- ```js
- import { set, get, remove } from 'oro:cookies'
-
- await set('https://app.example', 'session=abc123; Path=/; HttpOnly')
- console.log(await get('https://app.example'))
- await remove('https://app.example', 'session')
- ```
- """
- ),
- "oro:crypto": md(
- """
- Generate random bytes and hash them with the runtime digest helper:
-
- ```js
- import { randomBytes, createDigest, murmur3 } from 'oro:crypto'
-
- const nonce = randomBytes(16)
- const digest = await createDigest('sha256', nonce)
-
- console.log(digest.toString('hex'))
- console.log(murmur3('hello-runtime'))
- ```
- """
- ),
- "oro:dbus": md(
- """
- Check DBus availability and open a session-bus connection:
-
- ```js
- import { availability, connect, BUS } from 'oro:dbus'
-
- const state = await availability()
-
- if (state.available) {
- const bus = await connect({ bus: BUS.SESSION })
- console.log(bus.id)
- await bus.close()
- }
- ```
- """
- ),
- "oro:dgram": md(
- """
- Create a UDP socket and send a datagram:
-
- ```js
- import Buffer from 'oro:buffer'
- import { createSocket } from 'oro:dgram'
-
- const socket = createSocket('udp4')
-
- socket.on('message', (message, rinfo) => {
- console.log(rinfo.address, message.toString())
- })
-
- socket.bind(41234)
- socket.send(Buffer.from('ping'), 41235, '127.0.0.1')
- ```
- """
- ),
- "oro:diagnostics": md(
- """
- Publish and subscribe to a diagnostics channel:
-
- ```js
- import { channel } from 'oro:diagnostics'
-
- const buildChannel = channel('build')
-
- buildChannel.subscribe('step', (message) => {
- console.log(message.step)
- })
-
- await buildChannel.publish('step', { step: 'bundle-ready' })
- ```
- """
- ),
- "oro:did": md(
- """
- Parse a DID and a DID URL:
-
- ```js
- import { parse, parseUrl } from 'oro:did'
-
- const did = parse('did:key:z6Mkkx...')
- const url = parseUrl('did:key:z6Mkkx...#keys-1')
-
- console.log(did.method)
- console.log(url.fragment)
- ```
- """
- ),
- "oro:dns": md(
- """
- Resolve hostnames with the promise-based DNS helpers:
-
- ```js
- import { lookup } from 'oro:dns/promises'
-
- const record = await lookup('oro.computer')
-
- console.log(record.address)
- console.log(record.family)
- ```
- """
- ),
- "oro:enumeration": md(
- """
- Use `Enumeration` when you want a typed, readable set of allowed values:
-
- ```js
- import Enumeration from 'oro:enumeration'
-
- const states = new Enumeration(['draft', 'ready', 'shipped'])
-
- console.log(states.has('ready'))
- console.log([...states])
- ```
- """
- ),
- "oro:errno": md(
- """
- Convert errno values to names and human-readable messages:
-
- ```js
- import { EACCES, getName, getMessage } from 'oro:errno'
-
- console.log(getName(EACCES))
- console.log(getMessage(EACCES))
- ```
- """
- ),
- "oro:errors": md(
- """
- Construct runtime error types directly when surfacing failures:
-
- ```js
- import { AbortError, NotFoundError } from 'oro:errors'
-
- const abort = new AbortError('request cancelled')
- const missing = new NotFoundError('resource not found')
-
- console.log(abort.name, missing.name)
- ```
- """
- ),
- "oro:events": md(
- """
- Use `EventEmitter` for familiar event-based application code:
-
- ```js
- import EventEmitter from 'oro:events'
-
- const bus = new EventEmitter()
-
- bus.on('ready', (value) => console.log(value))
- bus.emit('ready', { ok: true })
- ```
- """
- ),
- "oro:extension": md(
- """
- Load a runtime extension and inspect the resulting binding:
-
- ```js
- import { load, stats } from 'oro:extension'
-
- const extension = await load('image-tools', { allow: ['resize'] })
-
- console.log(extension.type)
- console.log(await stats())
-
- await extension.unload()
- ```
- """
- ),
- "oro:fetch": md(
- """
- Use the runtime fetch stack exactly like standard web fetch:
-
- ```js
- import fetch from 'oro:fetch'
-
- const response = await fetch('https://example.com/api/status')
- const data = await response.json()
-
- console.log(data)
- ```
- """
- ),
- "oro:gc": md(
- """
- Register a finalizer on an object that owns runtime resources:
-
- ```js
- import { ref, unref } from 'oro:gc'
-
- const resource = {
- [Symbol.for('oro.runtime.gc.finalize')]() {
- console.log('cleaning up resource')
- }
- }
-
- ref(resource)
- unref(resource)
- ```
- """
- ),
- "oro:hci": md(
- """
- Inspect local Bluetooth adapters before opening a low-level HCI socket:
-
- ```js
- import { listAdapters, getAdapter } from 'oro:hci'
-
- const adapters = listAdapters()
- console.log(adapters)
-
- if (adapters.length > 0) {
- console.log(getAdapter(adapters[0].devId))
- }
- ```
- """
- ),
- "oro:http": md(
- """
- Start a small HTTP server with the Node-compatible API:
-
- ```js
- import { createServer } from 'oro:http'
-
- const server = createServer((req, res) => {
- res.writeHead(200, { 'content-type': 'text/plain' })
- res.end('hello from oro:http')
- })
-
- server.listen(8080, '127.0.0.1')
- ```
- """
- ),
- "oro:https": md(
- """
- Issue an HTTPS request with the familiar client helper:
-
- ```js
- import process from 'oro:process'
- import { get } from 'oro:https'
-
- get('https://example.com', (res) => {
- res.on('data', (chunk) => process.stdout.write(chunk))
- })
- ```
- """
- ),
- "oro:i18n": md(
- """
- Resolve the current UI language and look up a localized message:
-
- ```js
- import { getUILanguage, getMessage } from 'oro:i18n'
-
- console.log(getUILanguage())
- console.log(getMessage('menu.file.open'))
- ```
- """
- ),
- "oro:ip": md(
- """
- Normalize user-provided IP input before using it in socket code:
-
- ```js
- import { normalizeIPv4, isIPv4 } from 'oro:ip'
-
- const address = normalizeIPv4('127.000.000.001')
-
- console.log(address)
- console.log(isIPv4(address))
- ```
- """
- ),
- "oro:ipc": md(
- """
- Wait for the runtime IPC bridge and post a structured message:
-
- ```js
- import { ready, postMessage, debug } from 'oro:ipc'
-
- debug(true)
- await ready()
-
- postMessage({ type: 'docs-demo', at: Date.now() })
- ```
- """
- ),
- "oro:ipfs": md(
- """
- Ensure the embedded IPFS node is running before adding local content:
-
- ```js
- import { ensureStarted, add, status } from 'oro:ipfs'
-
- await ensureStarted({ repoPath: './ipfs-repo' })
- console.log(await status())
-
- const { cid } = await add('/absolute/path/to/report.txt')
- console.log(cid)
- ```
- """
- ),
- "oro:iroh": md(
- """
- Initialize the Iroh transport and create an endpoint:
-
- ```js
- import { ensureInitialized, Endpoint } from 'oro:iroh'
-
- await ensureInitialized()
-
- const endpoint = await Endpoint.create()
- await endpoint.bind()
-
- console.log(await endpoint.nodeAddr())
-
- await endpoint.close()
- ```
- """
- ),
- "oro:language": md(
- """
- Resolve language tags and metadata from user input:
-
- ```js
- import { lookup, describe } from 'oro:language'
-
- console.log(lookup('en-US'))
- console.log(describe('English'))
- ```
- """
- ),
- "oro:latica": md(
- """
- Use Latica helpers directly when you are working at the packet and transport layer:
-
- ```js
- import { sha256 } from 'oro:latica'
-
- const topic = await sha256(new TextEncoder().encode('docs-demo'))
-
- console.log(topic)
- ```
- """
- ),
- "oro:location": md(
- """
- Inspect the runtime-normalized location values for the current context:
-
- ```js
- import location from 'oro:location'
-
- console.log(location.href)
- console.log(location.origin)
- ```
- """
- ),
- "oro:mime": md(
- """
- Look up MIME metadata for a type or extension:
-
- ```js
- import { lookupSync } from 'oro:mime'
-
- console.log(lookupSync('application/json'))
- console.log(lookupSync('.png'))
- ```
- """
- ),
- "oro:module": md(
- """
- Inspect builtin modules and create a `require()` function when you need one:
-
- ```js
- import Module, { builtinModules, isBuiltin, createRequire } from 'oro:module'
-
- const require = createRequire(import.meta.url)
-
- console.log(isBuiltin('oro:path'))
- console.log(Object.keys(builtinModules).length)
- console.log(typeof Module)
- console.log(typeof require)
- ```
- """
- ),
- "oro:navigation": md(
- """
- Read the current navigation entry and listen for browser-style navigation events:
-
- ```js
- import navigation from 'oro:navigation'
-
- console.log(navigation.currentEntry?.url)
-
- navigation.addEventListener('navigate', (event) => {
- console.log(event.destination.url)
- })
- ```
- """
- ),
- "oro:net": md(
- """
- Build a TCP echo server with the Node-compatible net API:
-
- ```js
- import { createServer } from 'oro:net'
-
- const server = createServer({}, (socket) => {
- socket.on('data', (chunk) => socket.write(chunk))
- })
-
- server.listen(4040, '127.0.0.1')
- ```
- """
- ),
- "oro:network": md(
- """
- Start the higher-level networking surface and attach event listeners:
-
- ```js
- import network from 'oro:network'
-
- const bus = await network({})
-
- bus.on('error', console.error)
- bus.on('message', (message) => console.log(message))
- ```
- """
- ),
- "oro:npm": md(
- """
- Resolve an NPM specifier to the runtime module URL it will load:
-
- ```js
- import { resolve } from 'oro:npm/module'
-
- const resolved = resolve('react', import.meta.url)
-
- console.log(resolved?.url)
- console.log(resolved?.type)
- ```
- """
- ),
- "oro:os": md(
- """
- Inspect the current platform and locate scratch space:
-
- ```js
- import { arch, platform, tmpdir, cpus } from 'oro:os'
-
- console.log(platform(), arch())
- console.log(tmpdir())
- console.log(cpus().length)
- ```
- """
- ),
- "oro:path": md(
- """
- Compose paths without branching on platform separators:
-
- ```js
- import { resolve, join, dirname, basename } from 'oro:path'
-
- const absolute = resolve('dist', 'assets', 'logo.svg')
-
- console.log(absolute)
- console.log(dirname(absolute))
- console.log(basename(join('/tmp', 'report.txt')))
- ```
- """
- ),
- "oro:process": md(
- """
- Inspect process state and schedule work on the next tick:
-
- ```js
- import process, { env, hrtime, nextTick } from 'oro:process'
-
- console.log(env.PATH)
- console.log(hrtime())
-
- nextTick(() => {
- console.log(process.pid)
- })
- ```
- """
- ),
- "oro:protocol-handlers": md(
- """
- Ask the runtime which service worker is currently handling a custom scheme:
-
- ```js
- import { getServiceWorker } from 'oro:protocol-handlers'
-
- const worker = await getServiceWorker({ scheme: 'npm' })
-
- console.log(worker)
- ```
- """
- ),
- "oro:querystring": md(
- """
- Parse and serialize query strings with the Node-compatible helpers:
-
- ```js
- import { stringify, parse } from 'oro:querystring'
-
- const query = stringify({ q: 'oro runtime', page: 2 })
-
- console.log(query)
- console.log(parse(query))
- ```
- """
- ),
- "oro:semver": md(
- """
- Validate and compare versions before running upgrade logic:
-
- ```js
- import { valid, compare, gt } from 'oro:semver'
-
- console.log(valid('1.4.0'))
- console.log(compare('1.4.0', '1.3.9'))
- console.log(gt('2.0.0', '1.9.0'))
- ```
- """
- ),
- "oro:service-worker": md(
- """
- Use the runtime service-worker helpers from inside a service-worker scope:
-
- ```js
- import { env } from 'oro:service-worker'
-
- if (env) {
- self.addEventListener('fetch', (event) => {
- event.respondWith(fetch(event.request))
- })
- }
- ```
- """
- ),
- "oro:shared-worker": md(
- """
- Launch a shared worker and communicate through its shared port:
-
- ```js
- import { SharedWorker } from 'oro:shared-worker'
-
- const worker = new SharedWorker(new URL('./shared.js', import.meta.url))
-
- worker.port.start()
- worker.port.postMessage({ type: 'warmup' })
- ```
- """
- ),
- "oro:signal": md(
- """
- Listen for runtime process signals using the signal helper surface:
-
- ```js
- import signal from 'oro:signal'
-
- signal.addEventListener('SIGTERM', (event) => {
- console.log(event.type)
- })
- ```
- """
- ),
- "oro:sqlite": md(
- """
- Open an in-memory database and run a few synchronous queries:
-
- ```js
- import { open, OPEN_CREATE, OPEN_MEMORY } from 'oro:sqlite'
-
- const db = open(':memory:', { flags: OPEN_CREATE | OPEN_MEMORY })
-
- db.exec('create table notes (id integer primary key, title text)')
- db.exec(\"insert into notes (title) values ('hello')\")
-
- console.log(db.query('select * from notes'))
- ```
- """
- ),
- "oro:stream": md(
- """
- Connect streams together with the pipeline helper:
-
- ```js
- import process from 'oro:process'
- import { Readable, Writable, pipelinePromise } from 'oro:stream'
-
- const source = Readable.from(['oro', ' ', 'runtime'])
- const sink = new Writable({
- write(chunk, _encoding, done) {
- process.stdout.write(chunk)
- done()
- }
- })
-
- await pipelinePromise(source, sink)
- ```
- """
- ),
- "oro:string_decoder": md(
- """
- Decode chunked UTF-8 data without splitting multibyte characters:
-
- ```js
- import Buffer from 'oro:buffer'
- import StringDecoder from 'oro:string_decoder'
-
- const decoder = new StringDecoder('utf8')
- const chunks = [Buffer.from([0xe2, 0x82]), Buffer.from([0xac])]
-
- console.log(decoder.write(chunks[0]))
- console.log(decoder.write(chunks[1]))
- ```
- """
- ),
- "oro:tar": md(
- """
- Build a tar archive in memory and inspect its entries:
-
- ```js
- import Buffer from 'oro:buffer'
- import { createInMemory } from 'oro:tar'
-
- const archive = await createInMemory()
-
- await archive.append({ path: 'hello.txt' }, Buffer.from('hello'))
- await archive.finalize()
-
- console.log(await archive.entries())
- ```
- """
- ),
- "oro:tcp": md(
- """
- Open a lower-level TCP server when you want more direct socket control:
-
- ```js
- import { createServer } from 'oro:tcp'
-
- const server = createServer((socket) => {
- socket.write(Buffer.from('connected'))
- })
-
- server.listen({ port: 5050, host: '127.0.0.1' })
- ```
- """
- ),
- "oro:test": md(
- """
- Write runtime tests with the built-in test harness:
-
- ```js
- import test from 'oro:test'
-
- test('querystring roundtrip', async (t) => {
- t.equal('a=1', new URLSearchParams({ a: '1' }).toString())
- })
- ```
- """
- ),
- "oro:timers": md(
- """
- Sleep without writing your own timeout wrapper:
-
- ```js
- import { sleep } from 'oro:timers'
-
- await sleep(250)
-
- console.log('quarter second elapsed')
- ```
- """
- ),
- "oro:tls": md(
- """
- Configure TLS pins before opening pinned connections:
-
- ```js
- import { setTlsPins, getTlsPins, clearTlsPins } from 'oro:tls'
-
- await setTlsPins('sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=')
- console.log(await getTlsPins())
- await clearTlsPins()
- ```
- """
- ),
- "oro:toml": md(
- """
- Parse configuration and emit updated TOML back to disk:
-
- ```js
- import { parse, stringify } from 'oro:toml'
-
- const config = parse('name = \"oro\"\\nport = 8080')
- config.debug = true
-
- console.log(stringify(config))
- ```
- """
- ),
- "oro:tty": md(
- """
- Detect interactive terminals before enabling richer output:
-
- ```js
- import { isatty } from 'oro:tty'
-
- console.log(isatty(0))
- console.log(isatty(1))
- ```
- """
- ),
- "oro:url": md(
- """
- Use the WHATWG URL implementation for robust URL parsing:
-
- ```js
- import URL from 'oro:url'
-
- const url = new URL('https://oro.computer/docs?project=runtime')
-
- console.log(url.pathname)
- console.log(url.searchParams.get('project'))
- ```
- """
- ),
- "oro:usb": md(
- """
- Install the runtime WebUSB surface and request a device in window contexts:
-
- ```js
- import { installNavigatorUSB } from 'oro:usb'
-
- const usb = installNavigatorUSB()
-
- if (usb) {
- console.log(await usb.getDevices())
- // const device = await usb.requestDevice({ filters: [{ vendorId: 0x1209 }] })
- }
- ```
- """
- ),
- "oro:util": md(
- """
- Use the runtime debug helper and common type predicates:
-
- ```js
- import { debug, isTypedArray } from 'oro:util'
-
- const log = debug('docs')
- const payload = new Uint8Array([1, 2, 3])
-
- log('payload ready')
- console.log(isTypedArray(payload))
- ```
- """
- ),
- "oro:vm": md(
- """
- Run a script inside an isolated context object:
-
- ```js
- import { runInContext } from 'oro:vm'
-
- const context = { answer: 41 }
- const result = await runInContext('answer + 1', context)
-
- console.log(result)
- ```
- """
- ),
- "oro:worker": md(
- """
- Launch a dedicated worker through the runtime worker surface:
-
- ```js
- import Worker from 'oro:worker'
-
- const worker = new Worker(new URL('./worker.js', import.meta.url), {
- workerData: { job: 'thumbnail' }
- })
-
- worker.postMessage({ type: 'start' })
- ```
- """
- ),
- "oro:worker_threads": md(
- """
- Pass environment data into worker threads and communicate with message channels:
-
- ```js
- import { MessageChannel, setEnvironmentData } from 'oro:worker_threads'
-
- setEnvironmentData('traceId', 'req-42')
-
- const { port1, port2 } = new MessageChannel()
- port1.onmessage = (event) => console.log(event.data)
- port2.postMessage({ ok: true })
- ```
- """
- ),
- "oro:xpc": md(
- """
- Open an XPC connection, send a request, and close it cleanly:
-
- ```js
- import { availability, connect } from 'oro:xpc'
-
- const state = await availability()
-
- if (state.available) {
- const connection = await connect({ service: 'com.example.agent' })
- await connection.sendAndForget({ type: 'ping' })
- await connection.close()
- }
- ```
- """
- ),
- "oro:zlib": md(
- """
- Compress and decompress buffers with the promise-based helpers:
-
- ```js
- import { gzip, gunzip } from 'oro:zlib'
-
- const compressed = await gzip('hello runtime')
- const restored = await gunzip(compressed)
-
- console.log(restored.toString())
- ```
- """
- ),
-}
-
-
-DEFAULT_SEE_ALSO: tuple[tuple[str, str], ...] = (
- ("JavaScript APIs overview", "javascript/overview"),
- ("All module specifiers", "javascript/all-modules"),
-)
-
-
-GUIDE_REFS_BY_FAMILY: dict[str, tuple[tuple[str, str], ...]] = {
- "oro:application": (
- ("Windows and messaging", "guides/windows-and-messaging"),
- ("Lifecycle hooks and deep links", "guides/lifecycle-hooks-and-deep-links"),
- ("Desktop integrations", "guides/desktop-integrations"),
- ),
- "oro:assert": (
- ("Testing and diagnostics", "guides/testing-and-diagnostics"),
- ),
- "oro:cdp": (
- ("Testing and diagnostics", "guides/testing-and-diagnostics"),
- ),
- "oro:child_process": (
- ("Configure your runtime project", "guides/configure-your-runtime-project"),
- ("Testing and diagnostics", "guides/testing-and-diagnostics"),
- ),
- "oro:clipboard": (
- ("Desktop integrations", "guides/desktop-integrations"),
- ),
- "oro:console": (
- ("Testing and diagnostics", "guides/testing-and-diagnostics"),
- ),
- "oro:crypto": (
- ("Secure storage and sessions", "guides/secure-storage-and-sessions"),
- ),
- "oro:diagnostics": (
- ("Testing and diagnostics", "guides/testing-and-diagnostics"),
- ),
- "oro:errors": (
- ("Testing and diagnostics", "guides/testing-and-diagnostics"),
- ),
- "oro:extension": (
- ("Native extensions", "guides/native-extensions"),
- ),
- "oro:fetch": (
- ("Calling HTTP APIs", "guides/calling-http-apis"),
- ("Offline-first with service workers", "guides/offline-first-with-service-workers"),
- ),
- "oro:fs": (
- ("Files and sandboxing", "guides/files-and-sandboxing"),
- ("Project layout", "guides/project-layout"),
- ),
- "oro:hooks": (
- ("Lifecycle hooks and deep links", "guides/lifecycle-hooks-and-deep-links"),
- ("Windows and messaging", "guides/windows-and-messaging"),
- ),
- "oro:http": (
- ("Calling HTTP APIs", "guides/calling-http-apis"),
- ),
- "oro:https": (
- ("Calling HTTP APIs", "guides/calling-http-apis"),
- ),
- "oro:ipc": (
- ("Native extensions", "guides/native-extensions"),
- ("Windows and messaging", "guides/windows-and-messaging"),
- ),
- "oro:location": (
- ("Lifecycle hooks and deep links", "guides/lifecycle-hooks-and-deep-links"),
- ),
- "oro:mcp": (
- ("MCP and agent automation", "guides/mcp-and-agent-automation"),
- ),
- "oro:navigation": (
- ("Lifecycle hooks and deep links", "guides/lifecycle-hooks-and-deep-links"),
- ("Windows and messaging", "guides/windows-and-messaging"),
- ),
- "oro:notification": (
- ("Desktop integrations", "guides/desktop-integrations"),
- ("Lifecycle hooks and deep links", "guides/lifecycle-hooks-and-deep-links"),
- ),
- "oro:path": (
- ("Files and sandboxing", "guides/files-and-sandboxing"),
- ("Project layout", "guides/project-layout"),
- ),
- "oro:process": (
- ("Configure your runtime project", "guides/configure-your-runtime-project"),
- ),
- "oro:protocol-handlers": (
- ("Custom protocols and routing", "guides/custom-protocols-and-routing"),
- ("Offline-first with service workers", "guides/offline-first-with-service-workers"),
- ),
- "oro:secure-storage": (
- ("Secure storage and sessions", "guides/secure-storage-and-sessions"),
- ),
- "oro:service-worker": (
- ("Offline-first with service workers", "guides/offline-first-with-service-workers"),
- ("Custom protocols and routing", "guides/custom-protocols-and-routing"),
- ),
- "oro:shared-worker": (
- ("Shared workers across windows", "guides/shared-workers-across-windows"),
- ("Windows and messaging", "guides/windows-and-messaging"),
- ),
- "oro:sqlite": (
- ("Local data with SQLite", "guides/local-data-with-sqlite"),
- ("Files and sandboxing", "guides/files-and-sandboxing"),
- ),
- "oro:test": (
- ("Testing and diagnostics", "guides/testing-and-diagnostics"),
- ),
- "oro:worker": (
- ("Worker threads for heavy work", "guides/worker-threads-for-heavy-work"),
- ("Shared workers across windows", "guides/shared-workers-across-windows"),
- ),
- "oro:worker_threads": (
- ("Worker threads for heavy work", "guides/worker-threads-for-heavy-work"),
- ),
- "oro:window": (
- ("Windows and messaging", "guides/windows-and-messaging"),
- ("Desktop integrations", "guides/desktop-integrations"),
- ),
-}
diff --git a/sage/tools/audit-site.py b/sage/tools/audit-site.py
deleted file mode 100644
index 3ca6450f..00000000
--- a/sage/tools/audit-site.py
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env python3
-
-from __future__ import annotations
-
-import sys
-import os
-from pathlib import Path
-
-
-SITE_ROOT = Path(__file__).resolve().parents[2]
-SAGE_ROOT = Path(os.environ.get("ORO_SITE_OUTPUT", str(SITE_ROOT / "public"))) / "sage"
-REPO_ROOT = SAGE_ROOT.parent
-
-sys.path.insert(0, str(SITE_ROOT / "tools"))
-
-from site_audit_common import SiteAuditConfig, run_site_audit
-
-
-def main() -> int:
- return run_site_audit(
- SiteAuditConfig(
- project_root=SAGE_ROOT,
- docs_source=SAGE_ROOT / "docs" / "source",
- docs_index=SAGE_ROOT / "docs" / "index.json",
- missing_index_message="Missing Sage docs/index.json; run build scripts first.",
- success_message="OK: sage site audit passed",
- )
- )
-
-
-if __name__ == "__main__":
- raise SystemExit(main())
diff --git a/silk/tools/audit-site.py b/silk/tools/audit-site.py
deleted file mode 100644
index 64228e25..00000000
--- a/silk/tools/audit-site.py
+++ /dev/null
@@ -1,130 +0,0 @@
-#!/usr/bin/env python3
-
-from __future__ import annotations
-
-import re
-import sys
-import os
-from pathlib import Path
-
-
-SITE_ROOT = Path(__file__).resolve().parents[2]
-SILK_ROOT = Path(os.environ.get("ORO_SITE_OUTPUT", str(SITE_ROOT / "public"))) / "silk"
-REPO_ROOT = SILK_ROOT.parent
-
-DOCS_SOURCE = SILK_ROOT / "docs" / "source"
-WIKI_SOURCE = SILK_ROOT / "wiki" / "source"
-
-DOCS_INDEX = SILK_ROOT / "docs" / "index.json"
-WIKI_INDEX = SILK_ROOT / "wiki" / "index.json"
-
-sys.path.insert(0, str(SITE_ROOT / "tools"))
-
-from site_audit_common import (
- Issue,
- check_doclike_links_in_tree,
- check_index_files_exist_at,
- check_no_editorial_status_framing_in_tree,
- check_no_raw_manpage_refs_in_tree,
- check_no_raw_viewer_refs_in_tree,
- check_p_links_in_tree,
- load_ids,
-)
-
-
-def check_no_arena_identifiers(source_root: Path) -> list[Issue]:
- issues: list[Issue] = []
- banned = re.compile(
- r"\bconst\s+region\s+arena\b"
- r"|\bexport\s+const\s+region\s+arena\b"
- r"|\bwith\s+arena\b\s*\{"
- r"|\bfrom\s+arena\b"
- r"|\bglobal_arena\b",
- flags=re.I,
- )
- for md in source_root.rglob("*.md"):
- text = md.read_text(encoding="utf-8")
- if banned.search(text):
- issues.append(Issue(md, "Found arena identifier in examples (use regions + neutral names)."))
- return issues
-
-
-def check_no_works_today_labels(source_root: Path) -> list[Issue]:
- issues: list[Issue] = []
- banned = re.compile(
- r"\bWorks today\b|\bWhat works today\b|\(Works today\)|Syntax\s*\(Selected\)",
- flags=re.I,
- )
- for md in source_root.rglob("*.md"):
- text = md.read_text(encoding="utf-8")
- if banned.search(text):
- issues.append(Issue(md, "Found deprecated 'Works today' / 'Syntax (Selected)' labeling."))
- return issues
-
-
-def check_spec_has_no_repo_internal_paths(spec_path: Path) -> list[Issue]:
- issues: list[Issue] = []
- if not spec_path.exists():
- return issues
-
- text = spec_path.read_text(encoding="utf-8")
- banned = re.compile(
- r"\bchecker\.[A-Za-z0-9_]+\b"
- r"|\bsrc/[A-Za-z0-9_./-]+"
- r"|\bc-tests/"
- r"|\btests/silk/",
- flags=re.I,
- )
- if banned.search(text):
- issues.append(Issue(spec_path, "Spec contains repo-internal paths/names (remove or generalize)."))
- return issues
-
-
-def main() -> int:
- if not DOCS_INDEX.exists() or not WIKI_INDEX.exists():
- print("Missing Silk index.json files; run build scripts first.", file=sys.stderr)
- return 2
-
- docs_ids = load_ids(DOCS_INDEX)
- wiki_ids = load_ids(WIKI_INDEX)
- ids_by_kind = {"docs": docs_ids, "wiki": wiki_ids}
- source_roots = {
- "docs": DOCS_SOURCE,
- "wiki": WIKI_SOURCE,
- "spec": DOCS_SOURCE / "spec",
- }
-
- issues: list[Issue] = []
- issues += check_index_files_exist_at(DOCS_INDEX, DOCS_SOURCE)
- issues += check_index_files_exist_at(WIKI_INDEX, WIKI_SOURCE)
- issues += check_p_links_in_tree(DOCS_SOURCE, "docs", ids_by_kind, include_kind_in_message=True)
- issues += check_p_links_in_tree(WIKI_SOURCE, "wiki", ids_by_kind, include_kind_in_message=True)
- issues += check_doclike_links_in_tree(DOCS_SOURCE, REPO_ROOT, source_roots)
- issues += check_doclike_links_in_tree(WIKI_SOURCE, REPO_ROOT, source_roots)
- issues += check_no_arena_identifiers(DOCS_SOURCE)
- issues += check_no_arena_identifiers(WIKI_SOURCE)
- issues += check_no_works_today_labels(DOCS_SOURCE)
- issues += check_no_works_today_labels(WIKI_SOURCE)
- issues += check_no_raw_viewer_refs_in_tree(DOCS_SOURCE)
- issues += check_no_raw_viewer_refs_in_tree(WIKI_SOURCE)
- issues += check_no_raw_manpage_refs_in_tree(DOCS_SOURCE)
- issues += check_no_raw_manpage_refs_in_tree(WIKI_SOURCE)
- issues += check_no_editorial_status_framing_in_tree(DOCS_SOURCE)
- issues += check_no_editorial_status_framing_in_tree(WIKI_SOURCE)
- issues += check_spec_has_no_repo_internal_paths(DOCS_SOURCE / "spec" / "2026.md")
-
- if issues:
- for issue in issues[:200]:
- rel = issue.path.relative_to(REPO_ROOT)
- print(f"{rel}: {issue.message}", file=sys.stderr)
- if len(issues) > 200:
- print(f"... and {len(issues) - 200} more", file=sys.stderr)
- print(f"FAIL: {len(issues)} issues", file=sys.stderr)
- return 1
-
- print("OK: silk site audit passed")
- return 0
-
-
-if __name__ == "__main__":
- raise SystemExit(main())
diff --git a/silk/tools/audit-stdlib-docs.py b/silk/tools/audit-stdlib-docs.py
deleted file mode 100644
index f784f6d2..00000000
--- a/silk/tools/audit-stdlib-docs.py
+++ /dev/null
@@ -1,134 +0,0 @@
-#!/usr/bin/env python3
-
-from __future__ import annotations
-
-import os
-import re
-import sys
-from dataclasses import dataclass
-from pathlib import Path
-
-
-SILK_ROOT = Path(__file__).resolve().parents[1]
-STD_DOCS = Path(os.environ.get("ORO_SITE_OUTPUT", str(SILK_ROOT.parent / "public"))) / "silk" / "docs" / "source" / "std"
-
-ORDER = [
- "Description",
- "Exported API",
- "Examples",
- "Considerations",
- "Design goals",
- "See also",
-]
-
-BANNED_HEADINGS = (
- "API",
- "Current API",
- "Implemented API",
- "Public API",
- "High-Level API",
- "Intended Surface",
- "Current Surface",
- "Current Grammar Coverage",
- "Goals",
- "Design Goals",
- "Future Work",
- "Future work",
- "Follow-ups",
- "Remaining Follow-Ups",
- "Important Limitations",
- "Current Limitations",
- "Notes and Limitations",
-)
-
-BANNED_HEADING_PATTERNS = (
- re.compile(r"\bInitial Design\b", flags=re.I),
- re.compile(r"\bCurrent Scope\b", flags=re.I),
- re.compile(r"\bScope \(Current\)\b", flags=re.I),
- re.compile(r"\bMVP\b", flags=re.I),
-)
-
-BANNED_STATUS_PATTERNS = (
- re.compile(r"active expansion", flags=re.I),
- re.compile(r"current snapshot", flags=re.I),
- re.compile(r"Partially implemented", flags=re.I),
- re.compile(r"initial std wrapper", flags=re.I),
- re.compile(r"expansion path", flags=re.I),
- re.compile(r"\bMVP\b", flags=re.I),
-)
-
-
-@dataclass(frozen=True)
-class Issue:
- path: Path
- message: str
-
-
-def collect_h2s(path: Path) -> list[str]:
- headings: list[str] = []
- for line in path.read_text(encoding="utf-8").splitlines():
- if line.startswith("## "):
- headings.append(line[3:].strip())
- return headings
-
-
-def check_status(path: Path, text: str) -> list[Issue]:
- issues: list[Issue] = []
- for pattern in BANNED_STATUS_PATTERNS:
- if pattern.search(text):
- issues.append(Issue(path, f"Status/body uses banned transitional wording: {pattern.pattern}"))
- return issues
-
-
-def check_headings(path: Path, headings: list[str]) -> list[Issue]:
- issues: list[Issue] = []
-
- for heading in headings:
- for banned in BANNED_HEADINGS:
- if heading == banned:
- issues.append(Issue(path, f"Banned heading: {heading}"))
- for pattern in BANNED_HEADING_PATTERNS:
- if pattern.search(heading):
- issues.append(Issue(path, f"Banned heading pattern: {heading}"))
-
- positions = {heading: index for index, heading in enumerate(headings) if heading in ORDER}
- last = -1
- seen: list[str] = []
- for heading in ORDER:
- pos = positions.get(heading)
- if pos is None:
- continue
- if pos < last:
- issues.append(
- Issue(
- path,
- f"Section order violation: {' -> '.join(seen + [heading])}",
- )
- )
- break
- last = pos
- seen.append(heading)
-
- return issues
-
-
-def main() -> int:
- issues: list[Issue] = []
-
- for path in sorted(STD_DOCS.glob("*.md")):
- text = path.read_text(encoding="utf-8")
- issues.extend(check_status(path, text))
- issues.extend(check_headings(path, collect_h2s(path)))
-
- if issues:
- for issue in issues:
- rel = issue.path.relative_to(SILK_ROOT.parent)
- print(f"{rel}: {issue.message}")
- return 1
-
- print("Stdlib doc audit passed.")
- return 0
-
-
-if __name__ == "__main__":
- raise SystemExit(main())
diff --git a/silk/tools/sync-from-silk-docs.py b/silk/tools/sync-from-silk-docs.py
deleted file mode 100644
index 5c6840a8..00000000
--- a/silk/tools/sync-from-silk-docs.py
+++ /dev/null
@@ -1,767 +0,0 @@
-#!/usr/bin/env python3
-
-from __future__ import annotations
-
-import argparse
-import re
-import shutil
-import sys
-import tempfile
-from dataclasses import dataclass
-from pathlib import Path
-from typing import Callable
-
-sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "tools"))
-from importlib import import_module
-source_pages = import_module("source-pages")
-
-
-EXCLUDE_BASENAMES = {
- # Arenas were removed from the user-facing surface; use regions instead.
- "arenas.md",
- "README.md",
- "PLAN.md",
- "STATUS.md",
- "llms.txt",
- "style-guide.md",
- "TEMPLATE.md",
- "_template-language.md",
- "_template-stdlib.md",
-}
-
-
-KEEP_DOCS_PREFIXES = (
- "guides/",
-)
-
-
-KEEP_DOCS_FILES = {
- # Website-owned landing/start.
- "start.md",
- "compiler/implementation-status.md",
- # Website-owned, downstream-facing rewrites.
- "usage/cli-examples.md",
- "usage/howto-custom-stdlib-root.md",
- "usage/howto-run-wasi-node.md",
- "usage/editor-coc-nvim.md",
- "usage/editor-ctags.md",
- "usage/editor-vim.md",
- "usage/editor-textmate.md",
- "usage/getting-started.md",
- "usage/github-linguist.md",
- # Website-owned tutorial rewrites (avoid repo-build steps).
- "usage/tutorials/01-first-program.md",
- "usage/tutorials/02-structs-and-impls.md",
- "usage/tutorials/03-arrays-and-slices.md",
- "usage/tutorials/04-filesystem.md",
- "usage/tutorials/05-concurrency.md",
- "usage/tutorials/06-async-io-streams-abort.md",
- "usage/tutorials/07-formal-silk.md",
- # Website-owned copy edits to avoid repo-internal wording/refs.
- "compiler/backend-wasm.md",
- "compiler/testing-strategy.md",
- "compiler/libsilk-quickstart.md",
- "compiler/zig-api.md",
-}
-
-
-KEEP_WIKI_FILES = {
- # Website-owned wiki landing/start.
- "start.md",
-}
-
-
-@dataclass(frozen=True)
-class SyncStats:
- copied: int = 0
- skipped: int = 0
- deleted: int = 0
-
-
-def should_skip(rel: str, keep_files: set[str], keep_prefixes: tuple[str, ...]) -> bool:
- name = Path(rel).name
- if name in EXCLUDE_BASENAMES:
- return True
- if rel in keep_files:
- return True
- for prefix in keep_prefixes:
- if rel.startswith(prefix):
- return True
- return False
-
-
-def normalize_region_identifiers(markdown: str) -> str:
- out = markdown
- replacements = (
- (r"\bexport const region global_arena\b", "export const region global_region_buf"),
- (r"\bconst region arena\b", "const region region_buf"),
- (r"\bwith arena\b", "with region_buf"),
- (r"\bfrom arena\b", "from region_buf"),
- (r"\bglobal_arena\b", "global_region_buf"),
- (r"\barena\[", "region_buf["),
- (r"`arena`", "`region_buf`"),
- (r"`global_arena`", "`global_region_buf`"),
- )
- for pattern, replacement in replacements:
- out = re.sub(pattern, replacement, out)
- return out
-
-
-def normalize_shared_markdown(markdown: str) -> str:
- markdown = markdown.replace("https://oro.computer/silk/docs/?p=", "?p=")
- markdown = markdown.replace("https://oro.computer/silk/wiki/?p=", "wiki/?p=")
- markdown = markdown.replace("?p=docs/", "?p=")
- markdown = markdown.replace("docs/?p=", "?p=")
- markdown = markdown.replace("?p=wiki/", "wiki/?p=")
- markdown = re.sub(
- r"(?m)^(\s*//\s*)Works today\b",
- r"\1Supported",
- markdown,
- )
-
- markdown = re.sub(r"(?m)^(\s*#{2,6}\s+)Syntax\s+\(Selected\)\s*$", r"\1Syntax", markdown)
- markdown = re.sub(
- r"(?m)^(\s*#{2,6}\s+)(Example|Examples)\s+\(Works today\)(?::\s*(.+))?\s*$",
- lambda m: f"{m.group(1)}{m.group(2)}" + (f": {m.group(3)}" if m.group(3) else ""),
- markdown,
- )
- markdown = re.sub(
- r"(?m)^(\s*#{2,6}\s+)Works today(?::\s*(.+))?\s*$",
- lambda m: f"{m.group(1)}Example" + (f": {m.group(2)}" if m.group(2) else ""),
- markdown,
- )
- markdown = re.sub(
- r"(?m)^(\s*#{2,6}\s+)What works today(?:\s*\([^)]*\))?\s*$",
- r"\1Current subset",
- markdown,
- )
- markdown = re.sub(r"(?m)^Works today\b", "Current subset", markdown)
- markdown = markdown.replace("examples labeled “Works today”", "examples labeled “Example”")
- markdown = normalize_region_identifiers(markdown)
- return markdown
-
-
-def normalize_user_facing_links(markdown: str) -> str:
- def link_label(target: str) -> str:
- stem = target.rsplit("/", 1)[-1]
- return stem.replace("-", " ").replace("_", " ")
-
- def doc_link_target(target: str) -> str | None:
- target = target.removesuffix(".md")
- for prefix in ("docs/", "wiki/"):
- if target.startswith(prefix):
- target = target[len(prefix):]
- target = {
- "language/README": "start",
- "usage/README": "start",
- "usage/tutorials/README": "start",
- "std/runtime-event-loop": "std/runtime",
- }.get(target, target)
- if target in {"std/foo", "std/foo-bar", "man/my-app.7"}:
- return None
- return target
-
- def normalize_line(line: str) -> str:
- line = re.sub(r"`?\?p=\.\.\.`?", "docs links", line)
- line = re.sub(
- r"`\?p=(?P[a-zA-Z0-9_./-]+)`",
- lambda m: f"[{link_label(m.group('target'))}](?p={m.group('target')})",
- line,
- )
-
- def replace_raw_viewer_ref(match: re.Match[str]) -> str:
- prefix = match.group("prefix") or ""
- target = doc_link_target(match.group("target"))
- if prefix == "[" or target is None:
- return match.group(0)
- return f"{prefix}[{link_label(target)}](?p={target})"
-
- line = re.sub(
- r"(?P^|[\s:;,(])(?[a-zA-Z0-9_./-]+)",
- replace_raw_viewer_ref,
- line,
- )
-
- def replace_man_ref(match: re.Match[str]) -> str:
- prefix = match.group("prefix") or ""
- name = match.group("name")
- section = match.group("section")
- if name.startswith("std::"):
- target = name.replace("::", "/")
- return f"{prefix}[`{name}({section})`](?p={target})"
- return f"{prefix}[`{name}({section})`](?p=man/{name}.{section})"
-
- line = re.sub(
- r"(?P^|[\s:;,(])(?[A-Za-z0-9_:+.-]+)`\s+\((?P[137])\)",
- replace_man_ref,
- line,
- )
-
- def replace_markdown_docs_link(match: re.Match[str]) -> str:
- target = doc_link_target(match.group("target"))
- if target is None:
- return match.group(0)
- return f"[{link_label(target)}](?p={target})"
-
- line = re.sub(
- r"\[`?docs/(?P[a-zA-Z0-9_./-]+\.md)`?\]\([^)]+\)",
- replace_markdown_docs_link,
- line,
- )
-
- def replace_docs_path(match: re.Match[str]) -> str:
- prefix = match.group("prefix") or ""
- target = doc_link_target(match.group("target"))
- if target is None:
- return match.group(0)
- return f"{prefix}[{link_label(target)}](?p={target})"
-
- line = re.sub(
- r"(?P^|[\s:;,(])`?docs/(?P[a-zA-Z0-9_./-]+\.md)`?",
- replace_docs_path,
- line,
- )
- return line
-
- out_lines: list[str] = []
- in_code = False
- for raw in markdown.splitlines():
- stripped = raw.lstrip()
- if stripped.startswith("```") or stripped.startswith("~~~"):
- in_code = not in_code
- out_lines.append(raw.rstrip())
- continue
- out_lines.append(raw.rstrip() if in_code else normalize_line(raw.rstrip()))
- return "\n".join(out_lines) + ("\n" if markdown.endswith("\n") else "")
-
-
-def normalize_editorial_framing(markdown: str) -> str:
- replacements = (
- (r"\bStatus:\s*\*\*([^*]+)\*\*\.\s*", ""),
- (r"\bStatus:\s*\*\*([^*]+)\*\*\s*", ""),
- (r"\bStatus:\s*", ""),
- (r"\bImplementation status\s*\([^)]*\)\s*:\s*", ""),
- (r"\bImplementation status\s*:\s*", ""),
- (r"Returning readers typically want the “Implementation Status” section near the top", "Returning readers typically want the notes near the top"),
- (r"see “Implementation Status” below", "see the notes below"),
- (r"Implementation Status” sections", "notes sections"),
- (r"\bCurrent supported contexts include\b", "Supported contexts include"),
- (r"\bcurrent supported\b", "supported"),
- (r"\bCurrent compiler subset restriction\b", "Restriction"),
- (r"\bthe current compiler subset\b", "Silk currently"),
- (r"\bThe current compiler subset\b", "Silk currently"),
- (r"\bcurrent compiler subset\b", "current implementation"),
- (r"\bCurrent subset\b", "Supported forms"),
- (r"\bcurrent subset\b", "supported forms"),
- (r"\bInitial implementation target\b", "Implementation target"),
- (r"\binitial implementation target\b", "implementation target"),
- (r"\binitial implementation\b", "implementation"),
- (r"\bInitial implementation\b", "Implementation"),
- (r"\bImplemented-subset notes\b", "Notes"),
- (r"\bImplemented-subset details\b", "Reference details"),
- (r"\bImplemented-subset\b", "Reference"),
- (r"\bImplemented subset notes\b", "Notes"),
- (r"\bimplemented subset notes\b", "notes"),
- (r"\bImplemented subset\b", "Supported forms"),
- (r"\bimplemented subset\b", "supported forms"),
- (r"\bImplemented Subset\b", "Supported Forms"),
- (r"\bInitial Implemented Subset\b", "Supported Forms"),
- (r"\bCurrent Implemented Subset\b", "Supported Forms"),
- (r"\bexamples that exercise the supported forms\b", "examples"),
- (r"\bExamples that exercise the supported forms\b", "Examples"),
- (r"\bactive expansion\b", "current module surface"),
- (r"\bPartially implemented\b", "Implemented"),
- (r"\bpartially implemented\b", "implemented"),
- (r"\bin progress\b", "available"),
- (r"\bIn progress\b", "Available"),
- )
-
- out_lines: list[str] = []
- in_code = False
- for raw in markdown.splitlines():
- stripped = raw.lstrip()
- if stripped.startswith("```") or stripped.startswith("~~~"):
- in_code = not in_code
- out_lines.append(raw.rstrip())
- continue
- if in_code:
- out_lines.append(raw.rstrip())
- continue
-
- line = raw
- heading = re.match(r"^(?P\s*#{1,6}\s+)(?P.+?)\s*$", line)
- if heading:
- title = heading.group("title").strip()
- title = re.sub(
- r"\s+\((?:Initial\s+)?(?:Current\s+)?(?:Implemented|supported|compiler|initial|selected|planned|current|implementation status)[^)]*\)",
- "",
- title,
- flags=re.I,
- )
- title = re.sub(r"\s+\(Planned[^)]*\)", "", title, flags=re.I)
- if re.fullmatch(r"Status(?:\s*\([^)]*\))?|Implementation Status(?:\s*\([^)]*\))?|Status and Future Work|Future Work|Follow-ups", title, flags=re.I):
- title = "Notes"
- elif re.fullmatch(r"Notes and Limitations", title, flags=re.I):
- title = "Considerations"
- elif re.fullmatch(r"Current API(?:\s*\([^)]*\))?|Implemented API|Public API|API\s*\([^)]*\)|API\s*\(current\)", title, flags=re.I):
- title = "Exported API"
- elif re.fullmatch(r"Current Implemented Subset|Implemented Subset|Implemented subset", title, flags=re.I):
- title = "Notes"
- title = re.sub(r"\s{2,}", " ", title).strip()
- line = f"{heading.group('prefix')}{title}"
-
- for pattern, replacement in replacements:
- line = re.sub(pattern, replacement, line, flags=re.I)
- line = re.sub(r"\s{2,}", " ", line).rstrip()
- out_lines.append(line)
-
- return "\n".join(out_lines) + ("\n" if markdown.endswith("\n") else "")
-
-
-def normalize_trailing_newlines(markdown: str) -> str:
- return markdown.rstrip() + "\n"
-
-
-def normalize_headings_for_context(path: Path, markdown: str) -> str:
- markdown = markdown.replace("`docs/language/mutability.md`", "the mutability docs")
- markdown = markdown.replace("`docs/language/literals-duration.md`", "the duration literal docs")
- markdown = markdown.replace("?p=std/runtime-event-loop", "?p=std/runtime")
-
- if "/wiki/source/" in path.as_posix():
- markdown = re.sub(r"\]\(\?p=(compiler|man|usage|guides)/", r"](../docs/?p=\1/", markdown)
-
- if path.parent.name == "std":
- markdown = re.sub(r"(?m)^## API$", "## Exported API", markdown)
- markdown = re.sub(r"(?m)^## High-Level API$", "## Exported API", markdown)
- markdown = re.sub(r"(?m)^## Intended Surface$", "## Exported API", markdown)
- markdown = re.sub(r"(?m)^## Current Surface$", "## Exported API", markdown)
- markdown = re.sub(r"(?m)^## Current Grammar Coverage$", "## Grammar coverage", markdown)
- markdown = re.sub(r"(?m)^## Goals$", "## Design goals", markdown)
- markdown = re.sub(r"(?m)^## Design Goals$", "## Design goals", markdown)
- markdown = re.sub(r"(?m)^## Important Limitations$", "## Considerations", markdown)
- markdown = re.sub(r"(?m)^## Remaining Follow-Ups$", "## Considerations", markdown)
- markdown = re.sub(r"(?m)^## Implemented (`std::interfaces` surface)$", r"## \1", markdown)
- markdown = markdown.replace("current API surface", "exported API surface")
- markdown = markdown.replace("API surface (current):", "API surface:")
- markdown = move_section_to_end(markdown, "Design goals")
-
- if path.parent.name == "language":
- markdown = re.sub(r"(?m)^## Goals$", "## Semantics", markdown)
- markdown = re.sub(r"(?m)^## Design Goals$", "## Model", markdown)
- markdown = re.sub(r"(?m)^### Important Limitations$", "### Considerations", markdown)
-
- if path.name == "flow-for.md":
- markdown = markdown.replace(
- "element.\n\n: integer range iteration",
- "element.\n\nSupported forms include integer range iteration",
- )
- markdown = re.sub(
- r"\n## Semantics\n\n- Provide a readable, structured loop construct for iteration\.\n- Avoid .+? hidden allocation\.\n\n## Surface Syntax",
- "\n## Surface Syntax",
- markdown,
- flags=re.S,
- )
-
- return markdown
-
-
-def sanitize_wiki_markdown(rel: str, markdown: str) -> str:
- """
- The upstream Silk wiki is written for repo contributors and sometimes
- references internal tracker files (STATUS.md, PLAN.md) that don't exist (or
- aren't meaningful) on the public website.
-
- This function keeps the useful parts of those lines while removing the
- internal-only references.
- """
-
- markdown = normalize_shared_markdown(markdown)
-
- out_lines: list[str] = []
-
- drop_whole_line = re.compile(
- r"^\s*[-*+]\s*(End-to-end support snapshot|Implemented-subset notes)\s*:\s*`?(STATUS|PLAN)\.md`?\s*$",
- flags=re.I,
- )
-
- in_code = False
-
- for raw in markdown.splitlines():
- trimmed = raw.lstrip()
- if trimmed.startswith("```"):
- in_code = not in_code
- out_lines.append(raw.rstrip())
- continue
-
- if in_code:
- # Never rewrite inside fenced code blocks; preserve indentation and
- # exact spelling.
- out_lines.append(raw.rstrip())
- continue
-
- line = raw
-
- if drop_whole_line.match(line):
- continue
-
- # Rewrite a few common wiki-only phrasings.
- line = re.sub(r"^(\s*[-*+]\s+)Relevant fixtures:\s*", r"\1Fixtures: ", line, flags=re.I)
- line = re.sub(
- r"^\s*Status:\s*implemented for the current front-end \+\s*native backend subset\.\s*$",
- "Implemented in the reference compiler (front-end + native backend subset).",
- line,
- flags=re.I,
- )
-
- # Remove internal tracker refs while preserving surrounding prose.
- for basename in ("STATUS.md", "PLAN.md"):
- # Common: "... and `STATUS.md`"
- line = re.sub(rf"\s+(?:and|&)\s+`?{re.escape(basename)}`?\s*$", "", line, flags=re.I)
- # Strip any remaining mention.
- line = re.sub(rf"`?{re.escape(basename)}`?", "", line, flags=re.I)
-
- # If we removed a reference, clean up dangling conjunctions.
- line = re.sub(r"\s+(?:and|&)\s*$", "", line, flags=re.I)
-
- # Tidy up extra spaces introduced by stripping (but preserve indentation).
- leading = re.match(r"^\s*", line).group(0)
- body = line[len(leading) :]
- body = re.sub(r"[ \t]{2,}", " ", body)
- line = (leading + body).rstrip()
-
- # Drop empty bullets like "- Details:" after stripping.
- if re.match(r"^\s*[-*+]\s*[^A-Za-z0-9`]*\s*$", line):
- continue
-
- out_lines.append(line)
-
- cleaned = "\n".join(out_lines) + ("\n" if markdown.endswith("\n") else "")
- cleaned = normalize_editorial_framing(cleaned)
- cleaned = normalize_user_facing_links(cleaned)
- return normalize_trailing_newlines(cleaned)
-
-
-def sanitize_docs_markdown(rel: str, markdown: str) -> str:
- """
- The upstream Silk docs are written for the Silk compiler repository and may
- use ambiguous phrasing like "this repository" or project-internal jargon
- like "repo dependency workflow". When we sync into the website, we rewrite
- a few phrases to be clearer to downstream readers.
- """
-
- def drop_named_section(md: str, heading: str) -> str:
- target = heading.strip().lower()
- out_lines: list[str] = []
- in_code = False
- skip_level: int | None = None
-
- for raw in md.splitlines():
- trimmed = raw.lstrip()
- if trimmed.startswith("```"):
- in_code = not in_code
- if skip_level is None:
- out_lines.append(raw.rstrip())
- continue
-
- if not in_code and skip_level is not None:
- heading_match = re.match(r"^(#{1,6})\s+(.+?)\s*$", raw)
- if not heading_match:
- continue
- level = len(heading_match.group(1))
- if level > skip_level:
- continue
- skip_level = None
-
- if not in_code:
- heading_match = re.match(r"^(#{1,6})\s+(.+?)\s*$", raw)
- if heading_match and heading_match.group(2).strip().lower() == target:
- skip_level = len(heading_match.group(1))
- continue
-
- if skip_level is None:
- out_lines.append(raw.rstrip())
-
- return "\n".join(out_lines) + ("\n" if md.endswith("\n") else "")
-
- def rewrite_outside_inline_code(text: str) -> str:
- out = text
-
- # Make references explicit.
- out = re.sub(r"\bThe repository\b", "The Silk compiler repository", out)
- out = re.sub(r"\bthe repository\b", "the Silk compiler repository", out)
- out = re.sub(r"\bThis repository\b", "The Silk compiler repository", out)
- out = re.sub(r"\bthis repository\b", "the Silk compiler repository", out)
-
- # Prefer a clearer name for the dependency build flow.
- out = re.sub(
- r"\brepo dependency workflow\b",
- "Silk compiler repository’s vendored dependency workflow",
- out,
- flags=re.I,
- )
- out = re.sub(r"\bwhat works today\b", "current implementation notes", out, flags=re.I)
-
- return out
-
- def sanitize_std_markdown(text: str) -> str:
- out = text
- out = re.sub(r"(?m)^Status:\s*\*\*[^*]+\*\*\.\s*", "", out)
- out = re.sub(r"(?m)^Status:\s*\*\*[^*]+\*\*\.\s*$", "", out)
- out = re.sub(r"(?m)^##\s+Current API\s*$", "## Exported API", out)
- out = re.sub(r"(?m)^##\s+Implemented API\s*$", "## Exported API", out)
- out = re.sub(r"(?m)^##\s+Public API\s*$", "## Exported API", out)
- out = re.sub(
- r"(?m)^##\s+(Future Work|Future work|Follow-ups|Current Limitations|Notes and Limitations)\s*$",
- "## Considerations",
- out,
- )
- out = re.sub(r"(?m)^##\s+Current Scope\s*$", "## Considerations", out)
- out = re.sub(r"(?m)^##\s+(.+?)\s+\((?:Initial Design|MVP|Current)\)\s*$", r"## \1", out)
- out = re.sub(r"active expansion", "current module surface", out, flags=re.I)
- out = re.sub(r"current snapshot", "current implementation", out, flags=re.I)
- out = re.sub(r"Partially implemented", "Implemented", out, flags=re.I)
- out = re.sub(r"initial std wrapper", "stdlib wrapper", out, flags=re.I)
- out = re.sub(r"\bMVP\b", "baseline", out)
- return out
-
- def sanitize_spec_markdown(text: str) -> str:
- out = drop_named_section(text, "Silk Proposal Process (TC39-Inspired)")
- out = out.replace("// Works today", "// Supported")
- out = out.replace("checker.checkModuleSetWithImports", "the module-set import helper")
- out = re.sub(
- r"CLI output:\s*`+silk` CLI`?\s+and\s+`+silk`\s+\(1\)\s+—\s+Silk Language Compiler`?\s+\(`silk --version`\)",
- "CLI output: the `silk` CLI and [`silk(1)`](?p=man/silk.1) (`silk --version`)",
- out,
- )
- return out
-
- markdown = normalize_shared_markdown(markdown)
- markdown = markdown.replace(
- "`STATUS.md`",
- "[implementation status](?p=compiler/implementation-status)",
- )
- markdown = markdown.replace(
- "[implementation status](?p=compiler/implementation-status) (implementation status)",
- "[implementation status](?p=compiler/implementation-status)",
- )
- markdown = re.sub(
- r"\s*\(tracked[^)]*`PLAN\.md`\)",
- "",
- markdown,
- flags=re.I,
- )
- markdown = drop_named_section(markdown, "Arenas")
- markdown = drop_named_section(markdown, "Tests")
- if rel.startswith("std/"):
- markdown = sanitize_std_markdown(markdown)
- if rel == "spec/2026.md":
- markdown = sanitize_spec_markdown(markdown)
- if rel == "compiler/vendored-deps.md":
- markdown = markdown.replace(
- "# Built-In Dependencies\n",
- "# Built-In Dependencies (Legacy Link)\n",
- 1,
- )
-
- out_lines: list[str] = []
- in_code = False
-
- for raw in markdown.splitlines():
- trimmed = raw.lstrip()
- if trimmed.startswith("```"):
- in_code = not in_code
- out_lines.append(raw.rstrip())
- continue
-
- if in_code:
- out_lines.append(raw.rstrip())
- continue
-
- # Preserve inline-code spans (single-backtick) while rewriting prose.
- parts = raw.split("`")
- for i in range(0, len(parts), 2):
- parts[i] = rewrite_outside_inline_code(parts[i])
- out_lines.append("`".join(parts).rstrip())
-
- cleaned = "\n".join(out_lines) + ("\n" if markdown.endswith("\n") else "")
- cleaned = normalize_editorial_framing(cleaned)
- cleaned = normalize_user_facing_links(cleaned)
- return normalize_trailing_newlines(cleaned)
-
-
-
-def move_section_to_end(markdown: str, heading: str) -> str:
- lines = markdown.splitlines()
- section: list[str] = []
- out: list[str] = []
- in_code = False
- capture = False
- capture_level = 0
-
- for raw in lines:
- stripped = raw.lstrip()
- if stripped.startswith("```") or stripped.startswith("~~~"):
- in_code = not in_code
-
- if not in_code:
- m = re.match(r"^(#{1,6})\s+(.+?)\s*$", raw)
- if m:
- level = len(m.group(1))
- title = m.group(2).strip()
- if capture and level <= capture_level:
- capture = False
- if not capture and title.lower() == heading.lower():
- capture = True
- capture_level = level
-
- if capture:
- section.append(raw.rstrip())
- else:
- out.append(raw.rstrip())
-
- if not section:
- return markdown
- while out and not out[-1].strip():
- out.pop()
- while section and not section[-1].strip():
- section.pop()
- return "\n".join(out + ["", *section]) + "\n"
-
-
-def postprocess_tree(root: Path, *, keep_files: set[str] = frozenset(), keep_prefixes: tuple[str, ...] = ()) -> None:
- for path in sorted(root.rglob("*.md")):
- if should_skip(path.relative_to(root).as_posix(), keep_files, keep_prefixes):
- continue
- text = path.read_text(encoding="utf-8")
- def normalize(body):
- body = normalize_editorial_framing(body)
- body = normalize_user_facing_links(body)
- body = normalize_headings_for_context(path, body)
- return normalize_trailing_newlines(body)
- next_text = source_pages.preserve_fences(text, normalize)
- if next_text != text:
- path.write_text(next_text, encoding="utf-8")
-
-
-def sync_tree(
- src_root: Path,
- dst_root: Path,
- *,
- keep_files: set[str],
- keep_prefixes: tuple[str, ...],
- sanitize: Callable[[str, str], str] | None = None,
-) -> SyncStats:
- copied: set[str] = set()
- skipped = 0
- deleted = 0
-
- for path in sorted(src_root.rglob("*")):
- if not path.is_file():
- continue
- if path.suffix not in (".md", ".txt"):
- continue
-
- rel = path.relative_to(src_root).as_posix()
- if should_skip(rel, keep_files, keep_prefixes):
- skipped += 1
- continue
-
- dst = dst_root / rel
- dst.parent.mkdir(parents=True, exist_ok=True)
-
- if sanitize and path.suffix == ".md":
- dst.write_text(sanitize(rel, path.read_text(encoding="utf-8")), encoding="utf-8")
- else:
- shutil.copyfile(path, dst)
- copied.add(rel)
-
- # Prune any previously-synced files that no longer exist upstream.
- for path in sorted(dst_root.rglob("*")):
- if not path.is_file():
- continue
- if path.suffix not in (".md", ".txt"):
- continue
-
- if path.name in EXCLUDE_BASENAMES:
- path.unlink()
- deleted += 1
- continue
-
- rel = path.relative_to(dst_root).as_posix()
- if should_skip(rel, keep_files, keep_prefixes):
- continue
- if rel in copied:
- continue
- path.unlink()
- deleted += 1
-
- return SyncStats(copied=len(copied), skipped=skipped, deleted=deleted)
-
-
-def main() -> None:
- parser = argparse.ArgumentParser(
- description="Sync Silk docs/wiki from the repo (silk/docs) into the website copies (website/silk)."
- )
- parser.add_argument(
- "--repo-root",
- type=Path,
- default=None,
- help="Path to repo root (auto-detected by default).",
- )
- parser.add_argument("--silk-repo", type=Path, help="Path to the upstream Silk checkout.")
- args = parser.parse_args()
-
- site = Path(__file__).resolve().parents[2]
- silk_repo = args.silk_repo or ((args.repo_root / "silk") if args.repo_root else site.parent / "silk")
- src_docs = silk_repo.resolve() / "docs"
- src_wiki = src_docs / "wiki"
-
- staging = tempfile.TemporaryDirectory(prefix="oro-silk-import-")
- dst_docs = Path(staging.name) / "docs"
- dst_wiki = Path(staging.name) / "wiki"
- source_pages.stage_collection(site, "silk", dst_docs)
- source_pages.stage_collection(site, "silkWiki", dst_wiki)
-
- if not src_docs.exists():
- raise SystemExit(f"Missing source docs at {src_docs}")
-
- # Docs: copy everything except wiki/ subtree.
- docs_stats = sync_tree(
- src_docs,
- dst_docs,
- keep_files=KEEP_DOCS_FILES,
- keep_prefixes=KEEP_DOCS_PREFIXES + ("wiki/",),
- sanitize=lambda rel, text: source_pages.preserve_fences(text, lambda body: sanitize_docs_markdown(rel, body)),
- )
-
- # Wiki: copy everything under docs/wiki into website wiki source.
- if src_wiki.exists():
- wiki_stats = sync_tree(
- src_wiki,
- dst_wiki,
- keep_files=KEEP_WIKI_FILES,
- keep_prefixes=(),
- sanitize=lambda rel, text: source_pages.preserve_fences(text, lambda body: sanitize_wiki_markdown(rel, body)),
- )
- else:
- wiki_stats = SyncStats()
-
- postprocess_tree(dst_docs, keep_files=KEEP_DOCS_FILES, keep_prefixes=KEEP_DOCS_PREFIXES)
- postprocess_tree(dst_wiki, keep_files=KEEP_WIKI_FILES)
-
- source_pages.import_collection(site, "silk", dst_docs)
- if src_wiki.exists():
- source_pages.import_collection(site, "silkWiki", dst_wiki)
- staging.cleanup()
-
- print("Synced Silk docs to website:")
- print(
- f"- Docs copied: {docs_stats.copied} (skipped: {docs_stats.skipped}, deleted: {docs_stats.deleted})"
- )
- print(
- f"- Wiki copied: {wiki_stats.copied} (skipped: {wiki_stats.skipped}, deleted: {wiki_stats.deleted})"
- )
-
-
-if __name__ == "__main__":
- main()
diff --git a/silk/tools/sync-from-silk-docs.ts b/silk/tools/sync-from-silk-docs.ts
new file mode 100644
index 00000000..17a6eaba
--- /dev/null
+++ b/silk/tools/sync-from-silk-docs.ts
@@ -0,0 +1,88 @@
+#!/usr/bin/env node
+import { access, copyFile, mkdir, mkdtemp, rm, unlink, writeFile } from 'node:fs/promises'
+import { tmpdir } from 'node:os'
+import { basename, dirname, extname, join, relative, resolve } from 'node:path'
+import { fileURLToPath, pathToFileURL } from 'node:url'
+import { parseArgs } from 'node:util'
+import { files, readText, preserveFences, stageCollection, importCollection } from '../../tools/source-pages.ts'
+import { EXCLUDE_BASENAMES, KEEP_DOCS_FILES, KEEP_DOCS_PREFIXES, KEEP_WIKI_FILES } from '../../tools/silk-ingestion-data.ts'
+import { sanitizeDocsMarkdown, sanitizeWikiMarkdown, normalizeEditorialFraming, normalizeUserFacingLinks, normalizeHeadingsForContext, normalizeTrailingNewlines } from '../../tools/silk-editorial.ts'
+export { EXCLUDE_BASENAMES, KEEP_DOCS_FILES, KEEP_DOCS_PREFIXES, KEEP_WIKI_FILES }
+export * from '../../tools/silk-editorial.ts'
+export interface Ownership { keepFiles?: readonly string[]; keepPrefixes?: readonly string[] }
+export interface SyncStats { copied: number; skipped: number; deleted: number }
+export function shouldSkip(rel: string, { keepFiles = [], keepPrefixes = [] }: Ownership): boolean {
+ return EXCLUDE_BASENAMES.includes(basename(rel)) || keepFiles.includes(rel) || keepPrefixes.some(prefix => rel.startsWith(prefix))
+}
+export async function syncTree(src: string, dst: string, options: Ownership & { sanitize?: (rel: string, text: string) => string }): Promise {
+ const copied = new Set()
+ let skipped = 0, deleted = 0
+ for (const path of await files(src)) {
+ if (!['.md', '.txt'].includes(extname(path))) continue
+ const rel = relative(src, path)
+ if (shouldSkip(rel, options)) { skipped++; continue }
+ const dest = join(dst, rel)
+ await mkdir(dirname(dest), { recursive: true })
+ if (options.sanitize && extname(path) === '.md') await writeFile(dest, options.sanitize(rel, await readText(path)))
+ else await copyFile(path, dest)
+ copied.add(rel)
+ }
+ for (const path of await files(dst)) {
+ if (!['.md', '.txt'].includes(extname(path))) continue
+ const rel = relative(dst, path)
+ if (!EXCLUDE_BASENAMES.includes(basename(path)) && (shouldSkip(rel, options) || copied.has(rel))) continue
+ await unlink(path); deleted++
+ }
+ return { copied: copied.size, skipped, deleted }
+}
+export async function postprocessTree(root: string, options: Ownership = {}): Promise {
+ for (const path of await files(root)) {
+ if (extname(path) !== '.md' || shouldSkip(relative(root, path), options)) continue
+ const text = await readText(path)
+ const next = preserveFences(text, body => normalizeTrailingNewlines(normalizeHeadingsForContext(path, normalizeUserFacingLinks(normalizeEditorialFraming(body)))))
+ if (next !== text) await writeFile(path, next)
+ }
+}
+export async function syncFromSilkDocs(site: string, silkRepo: string): Promise {
+ const srcDocs = resolve(silkRepo, 'docs'), srcWiki = join(srcDocs, 'wiki')
+ try { await access(srcDocs) } catch { throw new Error(`Missing source docs at ${srcDocs}`) }
+ const staged = await mkdtemp(join(tmpdir(), 'oro-silk-import-'))
+ try {
+ const dstDocs = join(staged, 'docs'), dstWiki = join(staged, 'wiki')
+ // Empty collections still need a staging directory for the importer.
+ await mkdir(dstDocs); await mkdir(dstWiki)
+ await stageCollection('silk', dstDocs, site)
+ await stageCollection('silkWiki', dstWiki, site)
+ const docsStats = await syncTree(srcDocs, dstDocs, {
+ keepFiles: KEEP_DOCS_FILES, keepPrefixes: [...KEEP_DOCS_PREFIXES, 'wiki/'],
+ sanitize: (rel, text) => preserveFences(text, body => sanitizeDocsMarkdown(rel, body)),
+ })
+ let hasWiki = true
+ try { await access(srcWiki) } catch (error) {
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error
+ hasWiki = false
+ }
+ const wikiStats = hasWiki ? await syncTree(srcWiki, dstWiki, {
+ keepFiles: KEEP_WIKI_FILES, sanitize: (rel, text) => preserveFences(text, body => sanitizeWikiMarkdown(rel, body)),
+ }) : { copied: 0, skipped: 0, deleted: 0 }
+ await postprocessTree(dstDocs, { keepFiles: KEEP_DOCS_FILES, keepPrefixes: KEEP_DOCS_PREFIXES })
+ await postprocessTree(dstWiki, { keepFiles: KEEP_WIKI_FILES })
+ await importCollection('silk', dstDocs, site)
+ if (hasWiki) await importCollection('silkWiki', dstWiki, site)
+ console.log('Synced Silk docs to website:')
+ console.log(`- Docs copied: ${docsStats.copied} (skipped: ${docsStats.skipped}, deleted: ${docsStats.deleted})`)
+ console.log(`- Wiki copied: ${wikiStats.copied} (skipped: ${wikiStats.skipped}, deleted: ${wikiStats.deleted})`)
+ } finally { await rm(staged, { recursive: true, force: true }) }
+}
+export async function main(args = process.argv.slice(2)): Promise {
+ const { values } = parseArgs({ args, options: { 'repo-root': { type: 'string' }, 'silk-repo': { type: 'string' }, help: { type: 'boolean', short: 'h' } } })
+ if (values.help) { console.log('Sync Silk docs/wiki from the repo (silk/docs) into the website copies (website/silk).\n\nUsage: node silk/tools/sync-from-silk-docs.ts [--repo-root PATH] [--silk-repo PATH]'); return }
+ const site = fileURLToPath(new URL('../../', import.meta.url))
+ await syncFromSilkDocs(site, values['silk-repo'] ?? resolve(values['repo-root'] ?? resolve(site, '..'), 'silk'))
+}
+if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
+ main().catch(error => {
+ console.error(error.message)
+ process.exitCode = error.code?.startsWith('ERR_PARSE_ARGS_') ? 2 : 1
+ })
+}
diff --git a/slg/tools/audit-site.py b/slg/tools/audit-site.py
deleted file mode 100644
index f0f9d0d2..00000000
--- a/slg/tools/audit-site.py
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env python3
-
-from __future__ import annotations
-
-import sys
-import os
-from pathlib import Path
-
-
-SITE_ROOT = Path(__file__).resolve().parents[2]
-SLG_ROOT = Path(os.environ.get("ORO_SITE_OUTPUT", str(SITE_ROOT / "public"))) / "slg"
-REPO_ROOT = SLG_ROOT.parent
-
-sys.path.insert(0, str(SITE_ROOT / "tools"))
-
-from site_audit_common import SiteAuditConfig, run_site_audit
-
-
-def main() -> int:
- return run_site_audit(
- SiteAuditConfig(
- project_root=SLG_ROOT,
- docs_source=SLG_ROOT / "docs" / "source",
- docs_index=SLG_ROOT / "docs" / "index.json",
- missing_index_message="Missing Slg docs/index.json; run build scripts first.",
- success_message="OK: slg site audit passed",
- )
- )
-
-
-if __name__ == "__main__":
- raise SystemExit(main())
diff --git a/src/artifacts.template.ts b/src/artifacts.template.ts
index 3b9e24cf..327ff8f6 100644
--- a/src/artifacts.template.ts
+++ b/src/artifacts.template.ts
@@ -1,80 +1,12 @@
import type { TemplateFunction } from '@domstack/static/types.js'
import { collections } from './lib/collections.ts'
-import { rawUrl, type DocsData, type Doc } from './lib/docs.ts'
-export const dataDeps = ['docs', 'routes']
-const artifacts: TemplateFunction, DocsData> = ({
- data,
-}) => {
- const outputs: { outputName: string; content: string }[] = []
- const emit = (name: string, content: string) =>
- outputs.push({ outputName: name.replace(/^\//, ''), content })
- const json = (name: string, value: unknown) =>
- emit(name, JSON.stringify(value, null, 2) + '\n')
- const packs: Record = {}
- for (const [key, c] of Object.entries(collections)) {
- const docs = data.docs[key as keyof typeof collections]
- const sections = [...new Set(docs.map((d) => d.section))].map((name) => ({
- name,
- items: docs
- .filter((d) => d.section === name)
- .map((d) => ({
- id: d.id,
- title: d.title,
- file: d.sourcePath,
- url: d.url,
- })),
- }))
- json(c.base + 'index.json', {
- kind: key === 'silkWiki' ? 'wiki' : 'docs',
- count: docs.length,
- sections,
- })
- json(c.base + 'search.json', {
- kind: key === 'silkWiki' ? 'wiki' : 'docs',
- count: docs.length,
- items: docs.map((d) => ({
- id: d.id,
- title: d.title,
- file: d.sourcePath,
- url: d.url,
- section: d.section,
- summary: d.description,
- text: d.searchText,
- })),
- })
- for (const doc of docs) emit(rawUrl(doc), doc.markdown)
- ;(packs[c.product] ||= []).push(...docs)
- }
- for (const [product, docs] of Object.entries(packs))
- emit(
- `/${product}/llms.txt`,
- `# ${product} documentation\n\n` +
- docs
- .map(
- (d) =>
- `## ${d.title}\n\nURL: https://oro.computer${d.url}\nSource: https://oro.computer${rawUrl(d)}\n\n${d.markdown}`,
- )
- .join('\n---\n\n'),
- )
- emit(
- '/llms.txt',
- '# Oro Computer\n\n' +
- Object.keys(packs)
- .map(
- (p) => `- [${p} documentation](https://oro.computer/${p}/llms.txt)`,
- )
- .join('\n') +
- '\n',
- )
- emit(
- '/sitemap.xml',
- '\n' +
- data.routes
- .map((url) => `https://oro.computer${url}`)
- .join('') +
- '\n',
- )
- emit('/.nojekyll', '')
- return outputs
-}
+import { output } from './lib/artifacts.ts'
+export const dataDeps = []
+const artifacts: TemplateFunction<{ siteUrl: string }> = ({ vars }) => [
+ output('/llms.txt', '# Oro Computer\n\n' +
+ [...new Set(Object.values(collections).map(c => c.product))]
+ .map(p => `- [${p} documentation](${new URL(`/${p}/llms.txt`, vars.siteUrl).href})`)
+ .join('\n') + '\n'),
+ output('/.nojekyll', ''),
+]
export default artifacts
diff --git a/src/docs/client.ts b/src/docs/client.ts
new file mode 100644
index 00000000..e949ecf9
--- /dev/null
+++ b/src/docs/client.ts
@@ -0,0 +1,3 @@
+import { initAskAiMenu } from '../lib/ask-ai-client.ts'
+
+initAskAiMenu()
diff --git a/src/global.data.ts b/src/global.data.ts
index b522c90d..58321bae 100644
--- a/src/global.data.ts
+++ b/src/global.data.ts
@@ -1,32 +1,37 @@
import type { GlobalDataFunction } from '@domstack/static/types.js'
import { collections, type Collection } from './lib/collections.ts'
-import { plainText, type Doc, type DocsData } from './lib/docs.ts'
-const globalData: GlobalDataFunction = async ({ pages }) => {
- const docs = Object.fromEntries(
- Object.keys(collections).map((k) => [k, []]),
- ) as unknown as Record
+import { editUrl, plainText, projectCollection, validateDocVars, type Doc, type DocsData } from './lib/docs.ts'
+const globalData: GlobalDataFunction, string> = async ({ pages }) => {
+ const docs: Record = {
+ runtime: [], silk: [], silkWiki: [], virtnosis: [], sage: [], slg: [],
+ }
for (const page of pages) {
- const v = page.vars
- if (!v.docsCollection) continue
- const collection = v.docsCollection as Collection
- const markdown = await page.readMarkdownContent()
- docs[collection].push({
- collection,
+ if (page.vars.docsCollection === undefined) continue
+ const source = page.pageInfo.pageFile.relname
+ const v = validateDocVars(page.vars, source)
+ if (page.pageInfo.type !== 'md') throw new Error(`${source}: documentation must be Markdown`)
+ docs[v.docsCollection].push({
+ collection: v.docsCollection,
id: v.sourcePath.replace(/\.(md|txt)$/, ''),
- title: v.title,
- description: v.description,
- section: v.section,
- order: v.order,
- url: page.pageInfo.url,
- sourcePath: v.sourcePath,
- markdown,
+ title: v.title, description: v.description, section: v.section, order: v.order,
+ url: page.pageInfo.url, sourcePath: v.sourcePath,
+ markdown: await page.readMarkdownContent(),
searchText: plainText(await page.renderInnerPage()),
- githubRepo: v.githubRepo,
- githubRef: v.githubRef,
+ githubRepo: v.githubRepo, githubRef: v.githubRef,
+ editUrl: editUrl(source),
})
}
- for (const list of Object.values(docs))
+ const data = { routes: pages.map(p => p.pageInfo.url).sort() } as DocsData
+ for (const collection of Object.keys(collections) as Collection[]) {
+ const list = docs[collection]
list.sort((a, b) => a.order - b.order || a.id.localeCompare(b.id, 'en'))
- return { docs, routes: pages.map((p) => p.pageInfo.url).sort() }
+ data[`${collection}Navigation`] = projectCollection(list)
+ data[`${collection}Exports`] = list.map(({ collection, id, title, sourcePath, url, markdown }) => ({ collection, id, title, sourcePath, url, markdown }))
+ data[`${collection}Search`] = {
+ kind: collection === 'silkWiki' ? 'wiki' : 'docs', count: list.length,
+ items: list.map(d => ({ id: d.id, title: d.title, file: d.sourcePath, url: d.url, section: d.section, summary: d.description, text: d.searchText })),
+ }
+ }
+ return data
}
export default globalData
diff --git a/src/globals/global.client.js b/src/globals/global.client.js
deleted file mode 100644
index 6d471a75..00000000
--- a/src/globals/global.client.js
+++ /dev/null
@@ -1,214 +0,0 @@
-import { writeClipboard } from "../lib/clipboard.js";
-(() => {
- const yearNode = document.querySelector("[data-year]");
- if (yearNode) yearNode.textContent = String(new Date().getFullYear());
- const source = document.querySelector("[data-markdown]");
- if (source) globalThis.oroPageMarkdown = {url:new URL(source.dataset.markdown, location.href).href};
- function getSiteRootUrl() {
- const brand = document.querySelector("a.brand[href]");
- if (brand && brand.href) {
- try {
- return new URL(brand.href);
- } catch {}
- }
- try {
- return new URL("./", globalThis.location.href);
- } catch {
- return new URL("https://example.invalid/");
- }
- }
-
- function buildChatGPTUrl(targetUrl) {
- const prompt = `Hi ChatGPT! Can you please read [this page](${targetUrl}) and prepare to answer questions about it?`;
- const params = new URLSearchParams({ prompt });
- return `https://chatgpt.com/?${params.toString()}`;
- }
-
- function buildClaudeUrl(targetUrl) {
- const prompt = `Hi Claude! Can you please read [this page](${targetUrl}) and prepare to answer questions about it?`;
- const params = new URLSearchParams({ q: prompt });
- return `https://claude.ai/new?${params.toString()}`;
- }
-
- function inferLlmsUrl() {
- const root = getSiteRootUrl();
- const path = String(globalThis.location.pathname || "");
- if (path.includes("/silk/") || path.endsWith("/silk")) {
- return new URL("silk/llms.txt", root).toString();
- }
- if (path.includes("/runtime/") || path.endsWith("/runtime")) {
- return new URL("runtime/llms.txt", root).toString();
- }
- if (path.includes("/sage/") || path.endsWith("/sage")) {
- return new URL("sage/llms.txt", root).toString();
- }
- if (path.includes("/slg/") || path.endsWith("/slg")) {
- return new URL("slg/llms.txt", root).toString();
- }
- if (path.includes("/virtnosis/") || path.endsWith("/virtnosis")) {
- return new URL("virtnosis/llms.txt", root).toString();
- }
- return new URL("llms.txt", root).toString();
- }
-
- function shouldShowAskAiMenu() {
- const body = document.body;
- const path = String(globalThis.location.pathname || "");
- if (body?.dataset?.askAi === "true") return true;
- if (body?.classList?.contains("docs-page")) return true;
- if (body?.classList?.contains("docs-landing-page")) return true;
- return /(^|\/)docs(\/|$)/.test(path);
- }
-
- function initAskAiMenu() {
- if (!shouldShowAskAiMenu()) return;
-
- const nav = document.querySelector(".nav");
- if (!nav) return;
- if (nav.querySelector("[data-ask-ai]")) return;
-
- const menu = document.createElement("details");
- menu.className = "menu ask-ai-menu";
- menu.dataset.askAi = "true";
-
- const summary = document.createElement("summary");
- summary.className = "button button-quiet";
- summary.textContent = "Ask AI";
- menu.appendChild(summary);
-
- const panel = document.createElement("div");
- panel.className = "menu-panel";
-
- function itemLink(label, hint) {
- const a = document.createElement("a");
- a.className = "menu-item";
- a.target = "_blank";
- a.rel = "noreferrer";
- const left = document.createElement("span");
- left.textContent = label;
- const right = document.createElement("small");
- right.textContent = hint || "";
- a.appendChild(left);
- a.appendChild(right);
- return a;
- }
-
- function itemButton(label, hint) {
- const b = document.createElement("button");
- b.className = "menu-item";
- b.type = "button";
- const left = document.createElement("span");
- left.textContent = label;
- const right = document.createElement("small");
- right.textContent = hint || "";
- b.appendChild(left);
- b.appendChild(right);
- return b;
- }
-
- function sep() {
- const d = document.createElement("div");
- d.className = "menu-sep";
- return d;
- }
-
- const chatgpt = itemLink("ChatGPT", "New tab");
- const claude = itemLink("Claude", "New tab");
- const viewMd = itemLink("View Markdown", "Raw");
- viewMd.dataset.askAiViewMarkdown = "true";
- const copyMd = itemButton("Copy Markdown", "Clipboard");
- copyMd.dataset.askAiCopyMarkdown = "true";
- const llms = itemLink("Open llms.txt", "Pack");
-
- const hint = document.createElement("div");
- hint.className = "menu-hint";
- hint.textContent = "Uses page Markdown when available.";
-
- panel.appendChild(chatgpt);
- panel.appendChild(claude);
- panel.appendChild(sep());
- panel.appendChild(viewMd);
- panel.appendChild(copyMd);
- panel.appendChild(sep());
- panel.appendChild(llms);
- panel.appendChild(hint);
- menu.appendChild(panel);
-
- const primaryButton = nav.querySelector(".button.button-primary");
- if (primaryButton && primaryButton.parentElement === nav) {
- nav.insertBefore(menu, primaryButton);
- } else {
- nav.appendChild(menu);
- }
-
- function getContext() {
- const md = globalThis.oroPageMarkdown;
- const markdownUrl = md && md.url ? String(md.url) : "";
- const markdownText = md && md.text ? String(md.text) : "";
- const pageUrl = String(globalThis.location.href || "");
- const llmsUrl = inferLlmsUrl();
- return { markdownUrl, markdownText, pageUrl, llmsUrl };
- }
-
- function updateMenu() {
- const ctx = getContext();
- const target = ctx.markdownUrl || ctx.pageUrl;
- chatgpt.href = buildChatGPTUrl(target);
- claude.href = buildClaudeUrl(target);
- llms.href = ctx.llmsUrl;
-
- const hasMd = Boolean(ctx.markdownUrl);
- viewMd.hidden = !hasMd;
- copyMd.hidden = !hasMd;
- if (hasMd) viewMd.href = ctx.markdownUrl;
- }
-
- async function onCopyMarkdown() {
- const ctx = getContext();
- if (!ctx.markdownUrl) return;
-
- let text = ctx.markdownText;
- if (!text) {
- try {
- const res = await fetch(ctx.markdownUrl);
- if (res.ok) text = await res.text();
- } catch {}
- }
-
- const ok = await writeClipboard(text);
- const right = copyMd.querySelector("small");
- if (!right) return;
- const prev = right.textContent;
- right.textContent = ok ? "Copied" : "Failed";
- globalThis.setTimeout(() => {
- right.textContent = prev || "Clipboard";
- }, 900);
- }
-
- copyMd.addEventListener("click", onCopyMarkdown);
-
- panel.addEventListener("click", (event) => {
- const a = event.target && event.target.closest ? event.target.closest("a.menu-item") : null;
- if (!a) return;
- menu.open = false;
- });
-
- document.addEventListener("click", (event) => {
- if (!menu.open) return;
- const t = event.target;
- if (t && menu.contains(t)) return;
- menu.open = false;
- });
-
- document.addEventListener("keydown", (event) => {
- if (!menu.open) return;
- if (event.key === "Escape") menu.open = false;
- });
-
- globalThis.addEventListener("oro:page-markdown", updateMenu);
- updateMenu();
- }
-
- initAskAiMenu();
-
-})();
diff --git a/src/globals/global.client.ts b/src/globals/global.client.ts
new file mode 100644
index 00000000..17b562d6
--- /dev/null
+++ b/src/globals/global.client.ts
@@ -0,0 +1,2 @@
+const yearNode = document.querySelector('[data-year]')
+if (yearNode) yearNode.textContent = String(new Date().getFullYear())
diff --git a/src/globals/global.css b/src/globals/global.css
index b492fd12..42d1616d 100644
--- a/src/globals/global.css
+++ b/src/globals/global.css
@@ -363,7 +363,8 @@ pre {
position: relative;
}
-.menu summary {
+.menu summary,
+.menu .menu-trigger {
list-style: none;
}
@@ -371,7 +372,8 @@ pre {
display: none;
}
-.menu summary::after {
+.menu summary::after,
+.menu .menu-trigger::after {
content: "";
display: inline-block;
width: 0.42rem;
@@ -384,7 +386,8 @@ pre {
transition: transform 140ms ease;
}
-.menu[open] summary::after {
+.menu[open] summary::after,
+.menu .menu-trigger[aria-expanded="true"]::after {
transform: rotate(225deg) translateY(-0.02em);
}
@@ -1591,3443 +1594,1627 @@ pre code {
}
-/* Home redesign */
.container-wide {
max-width: var(--container-wide);
}
-.home-page {
+/* Oro repositioning */
+.site-dark {
--color-bg: #11100f;
--color-fg: #f7f3ea;
- --color-muted: rgba(247, 243, 234, 0.68);
+ --color-muted: rgba(247, 243, 234, 0.66);
--color-border: rgba(250, 248, 244, 0.12);
--color-border-strong: rgba(250, 248, 244, 0.22);
- --color-surface: rgba(250, 248, 244, 0.06);
- --color-surface-2: rgba(250, 248, 244, 0.1);
- --color-surface-3: rgba(250, 248, 244, 0.14);
- --color-code-bg: #171616;
- --color-code-bg-2: #211b2a;
- --shadow: 0 18px 42px rgba(0, 0, 0, 0.26);
+ --color-surface: rgba(250, 248, 244, 0.055);
+ --color-surface-2: rgba(250, 248, 244, 0.085);
+ --color-surface-3: rgba(250, 248, 244, 0.12);
+ --color-code-bg: #151414;
+ --color-code-bg-2: #1f1928;
+ --shadow: 0 18px 42px rgba(0, 0, 0, 0.28);
--shadow-elevated: 0 30px 80px rgba(0, 0, 0, 0.34);
- background: var(--color-bg);
+ background: #11100f;
color: var(--color-fg);
}
-.home-page::before {
+.site-dark::before {
background:
linear-gradient(rgba(250, 248, 244, 0.035) 1px, transparent 1px),
linear-gradient(90deg, rgba(250, 248, 244, 0.035) 1px, transparent 1px),
- linear-gradient(180deg, rgba(91, 59, 162, 0.12), transparent 44%),
- linear-gradient(140deg, rgba(246, 201, 69, 0.06), transparent 38%);
+ linear-gradient(180deg, rgba(91, 59, 162, 0.12), transparent 42%),
+ linear-gradient(140deg, rgba(62, 155, 101, 0.08), transparent 46%);
background-size: 72px 72px, 72px 72px, auto, auto;
- mask-image: linear-gradient(to bottom, black 0%, black 62%, transparent 100%);
opacity: 1;
}
-.home-page a {
- color: #d9cbff;
- text-decoration-color: rgba(246, 201, 69, 0.35);
-}
-
-.home-page .site-top {
- background: rgba(17, 16, 15, 0.82);
- border-bottom-color: rgba(250, 248, 244, 0.08);
+.site-dark .site-top {
+ background: rgba(17, 16, 15, 0.9);
+ border-bottom-color: rgba(250, 248, 244, 0.1);
box-shadow: none;
}
-.home-page .brand,
-.home-page .nav a,
-.home-page .footer-inner strong {
+.site-dark .brand,
+.site-dark .footer-inner strong {
color: var(--color-fg);
}
-.home-page .brand-mark {
- border-color: rgba(250, 248, 244, 0.16);
+.site-dark .brand-mark {
+ border-color: rgba(250, 248, 244, 0.18);
background: rgba(250, 248, 244, 0.08);
box-shadow: 0 0 0 1px rgba(246, 201, 69, 0.08);
}
-.home-page .nav {
- color: rgba(247, 243, 234, 0.66);
+.site-dark .nav {
+ color: rgba(247, 243, 234, 0.64);
+}
+
+.site-dark .nav a {
+ padding: 0.35rem 0;
+}
+
+.site-dark .nav a:hover,
+.site-dark .nav a:focus-visible {
+ color: var(--color-fg);
}
-.home-page .nav a:hover,
-.home-page .nav a:focus-visible {
+.site-dark .nav a[aria-current="page"] {
color: var(--color-fg);
- border-color: rgba(250, 248, 244, 0.12);
- background: rgba(250, 248, 244, 0.06);
}
-.home-page .button {
+.site-dark .button {
border-color: rgba(250, 248, 244, 0.14);
background: rgba(250, 248, 244, 0.06);
color: var(--color-fg);
}
-.home-page .button:hover,
-.home-page .button:focus-visible {
+.site-dark .button:hover,
+.site-dark .button:focus-visible {
border-color: rgba(246, 201, 69, 0.42);
background: rgba(250, 248, 244, 0.1);
}
-.home-page .button-primary {
+.site-dark .button-primary {
border-color: rgba(246, 201, 69, 0.68);
background: linear-gradient(135deg, rgba(246, 201, 69, 0.92), rgba(244, 122, 40, 0.72));
color: #1f1f1f;
box-shadow: 0 16px 40px rgba(246, 201, 69, 0.12);
}
-.home-page .button-primary:hover,
-.home-page .button-primary:focus-visible {
+.site-dark .button-primary:hover,
+.site-dark .button-primary:focus-visible {
background: linear-gradient(135deg, #f6c945, #f47a28);
color: #1f1f1f;
}
-.home-header {
- padding: 0.72rem 0;
+.site-dark .menu-panel {
+ border-color: rgba(250, 248, 244, 0.12);
+ background: rgba(20, 18, 23, 0.96);
+ box-shadow: 0 24px 64px rgba(0, 0, 0, 0.34);
}
-.home-header .nav {
- gap: 0.35rem;
- font-size: 0.86rem;
+.site-dark .menu-item {
+ color: rgba(247, 243, 234, 0.82);
}
-.home-header .nav a {
- padding: 0.45rem 0.68rem;
+.site-dark .menu-item:hover,
+.site-dark .menu-item:focus-visible {
+ border-color: rgba(246, 201, 69, 0.24);
+ background: rgba(250, 248, 244, 0.08);
+ color: var(--color-fg);
}
-.home-header .nav .button {
- padding: 0.62rem 0.82rem;
+.site-dark .menu-item small,
+.site-dark .menu-hint {
+ color: rgba(247, 243, 234, 0.54);
}
-.home-hero {
- position: relative;
- min-height: 780px;
- display: flex;
- align-items: center;
- padding: 5.5rem 0 4.5rem;
- overflow: hidden;
- border-bottom: 1px solid rgba(250, 248, 244, 0.08);
+.site-dark .menu-sep {
+ background: rgba(250, 248, 244, 0.1);
}
-.home-hero::after {
- content: "";
- position: absolute;
- inset: auto 0 0;
- height: 34%;
- pointer-events: none;
- background: linear-gradient(to bottom, transparent, var(--color-bg));
+.site-dark .site-subnav {
+ border-top-color: rgba(250, 248, 244, 0.08);
}
-.home-hero-grid {
- position: absolute;
- inset: 0;
- pointer-events: none;
- background:
- linear-gradient(rgba(250, 248, 244, 0.055) 1px, transparent 1px),
- linear-gradient(90deg, rgba(250, 248, 244, 0.055) 1px, transparent 1px);
- background-size: 88px 88px;
- opacity: 0.42;
- mask-image: linear-gradient(to bottom, black 0%, black 72%, transparent 100%);
+.site-dark .site-subnav-label {
+ color: rgba(247, 243, 234, 0.52);
}
-.home-hero-layout {
- position: relative;
- z-index: 2;
- display: grid;
- grid-template-columns: minmax(0, 0.86fr) minmax(0, 1.14fr);
- gap: 3rem;
- align-items: center;
+.site-dark .site-subnav-parent {
+ border-color: rgba(250, 248, 244, 0.16);
+ background: rgba(250, 248, 244, 0.07);
+ color: var(--color-fg);
}
-.home-hero-copy {
- max-width: 700px;
+.site-dark .site-subnav-links a {
+ color: rgba(247, 243, 234, 0.62);
+ background: rgba(250, 248, 244, 0.04);
}
-.home-eyebrow {
- display: inline-flex;
- align-items: center;
- gap: 0.65rem;
- margin-bottom: 1.1rem;
- color: rgba(247, 243, 234, 0.76);
- font-size: 0.78rem;
- font-weight: 750;
- text-transform: uppercase;
- letter-spacing: 0.08em;
+.site-dark .site-subnav-links a:hover,
+.site-dark .site-subnav-links a:focus-visible,
+.site-dark .site-subnav-links a[aria-current="page"] {
+ color: var(--color-fg);
+ border-color: rgba(246, 201, 69, 0.28);
+ background: rgba(250, 248, 244, 0.08);
}
-.home-eyebrow-mark {
- width: 12px;
- height: 12px;
- border-radius: 50%;
+.site-dark .hero::before {
background:
- linear-gradient(90deg, transparent 42%, var(--color-gold) 42% 58%, transparent 58%),
- linear-gradient(0deg, transparent 42%, var(--color-green) 42% 58%, transparent 58%),
- var(--color-purple);
- box-shadow: 0 0 0 5px rgba(246, 201, 69, 0.09);
+ linear-gradient(rgba(250, 248, 244, 0.035) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(250, 248, 244, 0.035) 1px, transparent 1px),
+ linear-gradient(120deg, rgba(91, 59, 162, 0.14), transparent 52%);
+ background-size: 84px 84px, 84px 84px, auto;
}
-.home-hero-copy h1 {
- max-width: 700px;
- margin: 0;
- font-size: 5.6rem;
- line-height: 0.95;
- letter-spacing: 0;
- text-wrap: balance;
+.site-dark .hero-kicker {
+ border-color: rgba(250, 248, 244, 0.14);
+ background: rgba(250, 248, 244, 0.06);
+ color: rgba(247, 243, 234, 0.74);
}
-.home-lede {
- max-width: 640px;
- margin: 1.25rem 0 0;
- color: var(--color-muted);
- font-size: 1.18rem;
- line-height: 1.62;
- text-wrap: balance;
+.site-dark .panel,
+.site-dark .card,
+.site-dark .list {
+ border-color: rgba(250, 248, 244, 0.12);
+ background: rgba(250, 248, 244, 0.045);
+ box-shadow: var(--shadow);
}
-.home-actions {
- justify-content: flex-start;
- margin-top: 1.65rem;
+.site-dark .card::before {
+ background:
+ linear-gradient(135deg, rgba(246, 201, 69, 0.14), transparent 48%),
+ linear-gradient(210deg, rgba(91, 59, 162, 0.18), transparent 56%);
+ opacity: 1;
}
-.home-actions-left {
- margin-top: 1.35rem;
+.site-dark .panel-header {
+ border-bottom-color: rgba(250, 248, 244, 0.1);
+ background: rgba(250, 248, 244, 0.045);
}
-.home-metrics {
- display: grid;
- grid-template-columns: repeat(3, minmax(0, 1fr));
- gap: 0.8rem;
- max-width: 650px;
- margin-top: 2.2rem;
+.site-dark .card-title,
+.site-dark .list-title,
+.site-dark .panel-title {
+ color: var(--color-fg);
}
-.home-metrics div {
- min-width: 0;
- padding: 0.9rem 0;
- border-top: 1px solid rgba(250, 248, 244, 0.14);
+.site-dark .card-meta,
+.site-dark .list-desc,
+.site-dark p {
+ color: var(--color-muted);
}
-.home-metrics strong,
-.home-metrics span {
- display: block;
+.site-dark .list-row {
+ border-top-color: rgba(250, 248, 244, 0.1);
}
-.home-metrics strong {
- color: var(--color-fg);
- font-family: var(--font-display);
- font-size: 1.08rem;
- line-height: 1.2;
+.site-dark .list-row:hover,
+.site-dark .list-row:focus-visible {
+ background: rgba(250, 248, 244, 0.06);
}
-.home-metrics span {
+.site-dark .list-meta,
+.site-dark .footer-inner {
color: rgba(247, 243, 234, 0.54);
- font-size: 0.9rem;
- line-height: 1.35;
}
-.home-orbit {
- position: relative;
- min-height: 610px;
- perspective: 1100px;
+.site-dark code {
+ background: rgba(246, 201, 69, 0.1);
+ color: rgba(247, 243, 234, 0.92);
}
-.home-orbit::before {
- content: "";
- position: absolute;
- inset: 4% 2% 0;
- border: 1px solid rgba(91, 59, 162, 0.26);
- transform: rotateX(61deg) rotateZ(-28deg);
- transform-origin: center;
- background:
- linear-gradient(rgba(91, 59, 162, 0.16) 1px, transparent 1px),
- linear-gradient(90deg, rgba(91, 59, 162, 0.16) 1px, transparent 1px);
- background-size: 74px 74px;
- opacity: 0.92;
+.site-dark pre code {
+ background: transparent;
+ color: inherit;
}
-.home-orbit-plane {
- position: absolute;
- inset: 0;
- transform: rotateX(61deg) rotateZ(-28deg);
- transform-style: preserve-3d;
+.site-dark pre {
+ border-color: rgba(250, 248, 244, 0.12);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04), 0 18px 44px rgba(0, 0, 0, 0.24);
}
-.home-tile {
- position: absolute;
- width: 178px;
- min-height: 74px;
- padding: 0.75rem 0.85rem;
- border-radius: 8px;
- border: 1px solid rgba(250, 248, 244, 0.13);
- background: linear-gradient(180deg, rgba(91, 59, 162, 0.5), rgba(23, 20, 30, 0.9));
- color: var(--color-fg);
- box-shadow: 0 20px 30px rgba(0, 0, 0, 0.24);
- transform: translateZ(28px);
+.site-dark .site-footer {
+ border-top-color: rgba(250, 248, 244, 0.1);
}
-.home-tile::before {
- content: "";
- position: absolute;
- inset: 0;
- border-radius: inherit;
- background: linear-gradient(135deg, rgba(246, 201, 69, 0.18), transparent 48%);
- pointer-events: none;
+.section-heading {
+ max-width: 760px;
}
-.home-tile span,
-.home-tile small {
- position: relative;
- display: block;
+.section-heading h2,
+.docs-hub-tech-copy h2,
+.coming-soon-copy h1,
+.docs-hub-copy h1,
+.tech-hero-copy h1 {
+ letter-spacing: 0;
}
-.home-tile span {
+.section-kicker {
+ margin-bottom: 0.8rem;
+ color: var(--color-gold);
+ font-size: 0.78rem;
font-weight: 800;
- line-height: 1.15;
+ text-transform: uppercase;
+ letter-spacing: 0.1em;
}
-.home-tile small {
- margin-top: 0.24rem;
- color: rgba(247, 243, 234, 0.58);
- font-size: 0.74rem;
- line-height: 1.15;
+.tech-hero,
+.docs-hub-hero,
+.coming-soon {
+ position: relative;
+ overflow: hidden;
+ min-height: 760px;
+ display: flex;
+ align-items: center;
+ border-bottom: 1px solid rgba(250, 248, 244, 0.08);
}
-.tile-runtime {
- left: 14%;
- top: 26%;
+.tech-hero-grid,
+.docs-hub-grid,
+.coming-soon-grid {
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+ background:
+ linear-gradient(rgba(250, 248, 244, 0.05) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(250, 248, 244, 0.05) 1px, transparent 1px);
+ background-size: 88px 88px;
+ opacity: 0.4;
+ mask-image: linear-gradient(to bottom, black 0%, black 72%, transparent 100%);
}
-.tile-silk {
- left: 40%;
- top: 18%;
- border-color: rgba(246, 201, 69, 0.36);
+.tech-system-map {
+ position: absolute;
+ inset: 8rem max(5vw, calc((100vw - var(--container)) / 2)) 6rem auto;
+ width: min(390px, 28vw);
+ pointer-events: none;
}
-.tile-sage {
- left: 62%;
- top: 31%;
+.system-map-frame {
+ position: relative;
+ height: 100%;
+ min-height: 460px;
}
-.tile-slg {
- left: 28%;
- top: 52%;
- border-color: rgba(244, 122, 40, 0.32);
+.system-map-line {
+ position: absolute;
+ background: rgba(250, 248, 244, 0.16);
}
-.tile-virtnosis {
- left: 55%;
- top: 58%;
- border-color: rgba(62, 155, 101, 0.34);
+.system-map-line-main {
+ left: 50%;
+ top: 0;
+ width: 1px;
+ height: 100%;
+ background: linear-gradient(180deg, transparent, rgba(246, 201, 69, 0.34), rgba(62, 155, 101, 0.24), transparent);
}
-.tile-edge {
- left: 73%;
- top: 52%;
+.system-map-line-runtime,
+.system-map-line-silk,
+.system-map-line-virtnosis {
+ left: 21%;
+ width: 58%;
+ height: 1px;
}
-.tile-policy {
- left: 5%;
- top: 47%;
- width: 138px;
+.system-map-line-runtime {
+ top: 22%;
}
-.tile-packages {
- left: 78%;
- top: 20%;
- width: 148px;
+.system-map-line-silk {
+ top: 50%;
}
-.tile-center {
- left: 43%;
- top: 40%;
- width: 152px;
- min-height: 96px;
- display: grid;
- place-items: center;
- text-align: center;
- background: linear-gradient(180deg, rgba(246, 201, 69, 0.24), rgba(91, 59, 162, 0.6));
- border-color: rgba(246, 201, 69, 0.48);
- transform: translateZ(64px);
-}
-
-.tile-center img {
- width: 34px;
- height: 34px;
- border-radius: 10px;
-}
-
-.tile-center span {
- margin-top: 0.35rem;
+.system-map-line-virtnosis {
+ top: 78%;
}
-.home-strip {
- padding: 2.5rem 0 3.5rem;
+.system-node {
+ position: absolute;
+ display: grid;
+ grid-template-columns: minmax(0, 1fr);
+ row-gap: 0.1rem;
+ width: min(245px, 100%);
+ padding: 0.95rem 0 0.95rem 1rem;
+ border-left: 1px solid rgba(250, 248, 244, 0.22);
+ background: linear-gradient(90deg, rgba(250, 248, 244, 0.07), transparent 82%);
+ color: rgba(247, 243, 234, 0.9);
}
-.home-project-strip {
- display: grid;
- grid-template-columns: repeat(5, minmax(0, 1fr));
- gap: 1rem;
- align-items: center;
+.system-node strong {
+ font-size: 1rem;
}
-.home-project-strip a {
- display: flex;
- align-items: center;
- justify-content: center;
- min-height: 76px;
- border: 1px solid rgba(250, 248, 244, 0.1);
- border-radius: 8px;
- background: rgba(250, 248, 244, 0.035);
- color: rgba(247, 243, 234, 0.62);
- text-decoration: none;
- font-family: var(--font-display);
- font-size: 1.08rem;
- font-weight: 800;
+.system-node small {
+ color: rgba(247, 243, 234, 0.52);
+ font-size: 0.82rem;
}
-.home-project-strip a:hover,
-.home-project-strip a:focus-visible {
- border-color: rgba(246, 201, 69, 0.34);
- color: var(--color-fg);
- background: rgba(250, 248, 244, 0.065);
+.node-runtime {
+ left: 0;
+ top: 16%;
}
-.home-proof {
- padding: 4.5rem 0 5rem;
- border-top: 1px solid rgba(250, 248, 244, 0.08);
- border-bottom: 1px solid rgba(250, 248, 244, 0.08);
+.node-silk {
+ right: 0;
+ top: 44%;
+ border-left-color: rgba(91, 59, 162, 0.5);
}
-.home-proof-grid {
- display: grid;
- grid-template-columns: minmax(0, 0.78fr) minmax(0, 1.22fr);
- gap: 4rem;
- align-items: center;
+.node-virtnosis {
+ left: 10%;
+ top: 72%;
+ border-left-color: rgba(62, 155, 101, 0.46);
}
-.home-proof-copy {
- display: grid;
- gap: 1.15rem;
+.tech-hero-copy,
+.docs-hub-copy,
+.coming-soon-copy {
+ position: relative;
+ z-index: 2;
+ padding: 7rem 0;
}
-.home-rule-item {
- padding: 0.3rem 0 0.35rem 1.25rem;
- border-left: 2px solid rgba(250, 248, 244, 0.14);
+.tech-hero-copy {
+ max-width: 720px;
}
-.home-rule-item[data-active="true"] {
- border-left-color: var(--color-gold);
+.docs-hub-copy,
+.coming-soon-copy {
+ max-width: 820px;
}
-.home-rule-item h2,
-.home-rule-item h3 {
+.tech-hero-copy h1 {
+ max-width: 720px;
margin: 0;
- line-height: 1.1;
- letter-spacing: 0;
+ color: var(--color-fg);
+ font-size: 4.75rem;
+ line-height: 1;
}
-.home-rule-item h2 {
- font-size: 2.4rem;
+.docs-hub-copy h1,
+.coming-soon-copy h1 {
+ max-width: 820px;
+ margin: 0;
+ color: var(--color-fg);
+ font-size: 5.1rem;
+ line-height: 0.98;
}
-.home-rule-item h3 {
- font-size: 1.35rem;
- color: rgba(247, 243, 234, 0.78);
+.tech-hero-copy p,
+.docs-hub-copy p,
+.coming-soon-copy p {
+ max-width: 680px;
+ margin-top: 1.25rem;
+ font-size: 1.16rem;
+ line-height: 1.62;
}
-.home-rule-item p {
- max-width: 460px;
- margin-top: 0.65rem;
- color: var(--color-muted);
- font-size: 1rem;
+.technology-section,
+.docs-hub-section {
+ padding: 5.5rem 0;
}
-.home-device-map {
+.technology-section {
position: relative;
- min-height: 390px;
- border: 1px solid rgba(91, 59, 162, 0.28);
- border-radius: 8px;
- background:
- linear-gradient(rgba(250, 248, 244, 0.04) 1px, transparent 1px),
- linear-gradient(90deg, rgba(250, 248, 244, 0.04) 1px, transparent 1px),
- rgba(250, 248, 244, 0.025);
- background-size: 64px 64px;
+ border-top: 1px solid rgba(250, 248, 244, 0.08);
overflow: hidden;
}
-.map-root,
-.map-node {
+.technology-section::before {
+ content: "";
position: absolute;
- display: grid;
- place-items: center;
- border-radius: 8px;
- border: 1px solid rgba(250, 248, 244, 0.14);
- background: rgba(17, 16, 15, 0.76);
- color: var(--color-fg);
- font-weight: 800;
+ inset: 0;
+ pointer-events: none;
+ background:
+ linear-gradient(rgba(250, 248, 244, 0.035) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(250, 248, 244, 0.035) 1px, transparent 1px);
+ background-size: 88px 88px;
+ opacity: 0.42;
+ mask-image: linear-gradient(90deg, transparent 0%, black 18%, black 82%, transparent 100%);
}
-.map-root {
- left: 50%;
- top: 18%;
- width: 132px;
- height: 48px;
- transform: translateX(-50%);
- border-color: rgba(246, 201, 69, 0.5);
- color: var(--color-gold);
+.technology-section-silk {
+ background: rgba(91, 59, 162, 0.035);
}
-.map-node {
- width: 122px;
- height: 72px;
- color: rgba(247, 243, 234, 0.76);
+.technology-section-virtnosis {
+ background: rgba(62, 155, 101, 0.026);
}
-.node-runtime {
- left: 12%;
- top: 62%;
+.technology-layout {
+ position: relative;
+ z-index: 1;
+ display: grid;
+ grid-template-columns: minmax(0, 0.95fr) minmax(360px, 0.85fr);
+ gap: 5rem;
+ align-items: center;
}
-.node-silk {
- left: 36%;
- top: 62%;
+.technology-layout-reverse {
+ grid-template-columns: minmax(360px, 0.85fr) minmax(0, 0.95fr);
}
-.node-slg {
- left: 60%;
- top: 62%;
+.technology-layout-reverse .technology-copy {
+ order: 2;
}
-.node-edge {
- left: 80%;
- top: 62%;
+.technology-copy h2 {
+ max-width: 760px;
+ margin: 0;
+ color: var(--color-fg);
+ font-size: 3.4rem;
+ line-height: 1.04;
+ letter-spacing: 0;
}
-.map-line {
- position: absolute;
- left: 18%;
- right: 12%;
- top: 40%;
- height: 1px;
- background: rgba(91, 59, 162, 0.75);
+.technology-copy p {
+ max-width: 680px;
+ margin-top: 1.15rem;
+ font-size: 1.06rem;
+ line-height: 1.65;
}
-.map-line::before,
-.map-line::after {
- content: "";
- position: absolute;
- width: 1px;
- height: 92px;
- top: 0;
- background: rgba(91, 59, 162, 0.75);
+.technology-feature-list {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 0.75rem;
+ max-width: 680px;
+ margin-top: 1.6rem;
+ padding: 0;
+ list-style: none;
}
-.map-line::before {
- left: 16%;
+.technology-feature-list li {
+ position: relative;
+ min-width: 0;
+ min-height: 86px;
+ padding: 0.86rem 0.95rem 0.88rem;
+ overflow: hidden;
+ border: 1px solid rgba(250, 248, 244, 0.12);
+ border-radius: 8px;
+ background:
+ linear-gradient(135deg, rgba(250, 248, 244, 0.072), rgba(250, 248, 244, 0.022)),
+ linear-gradient(90deg, rgba(246, 201, 69, 0.14), transparent 32%);
+ transition:
+ border-color 160ms ease,
+ background-color 160ms ease,
+ transform 160ms ease;
}
-.map-line::after {
- right: 18%;
+.technology-feature-list li::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: 0.8rem;
+ bottom: 0.8rem;
+ width: 2px;
+ border-radius: 999px;
+ background: var(--color-gold);
}
-.map-line-b {
- left: 42%;
- right: auto;
- width: 1px;
- height: 92px;
+.technology-feature-list li:nth-child(2)::before {
+ background: var(--color-purple);
}
-.map-line-c {
- left: 66%;
- right: auto;
- width: 1px;
- height: 92px;
+.technology-feature-list li:nth-child(3)::before {
+ background: var(--color-green);
}
-.map-line-b::before,
-.map-line-b::after,
-.map-line-c::before,
-.map-line-c::after {
- display: none;
+.technology-feature-list li:nth-child(4)::before {
+ background: var(--color-orange);
}
-.home-quote {
- position: relative;
- padding: 4.75rem 0;
- overflow: hidden;
- background:
- linear-gradient(115deg, rgba(91, 59, 162, 0.22) 0 48%, rgba(246, 201, 69, 0.08) 48% 49%, rgba(17, 16, 15, 0.4) 49%),
- #151311;
- border-bottom: 1px solid rgba(250, 248, 244, 0.08);
+.technology-feature-list li:hover {
+ border-color: rgba(246, 201, 69, 0.32);
+ background-color: rgba(250, 248, 244, 0.035);
+ transform: translateY(-1px);
}
-.home-quote blockquote {
- max-width: 920px;
- margin: 0 auto;
+.technology-feature-list strong {
+ display: block;
color: var(--color-fg);
- font-family: var(--font-display);
- font-size: 1.45rem;
- font-weight: 800;
- line-height: 1.35;
- text-align: center;
+ font-size: 0.96rem;
+ line-height: 1.25;
}
-.home-build {
- padding: 5.5rem 0 6rem;
+.technology-feature-list small {
+ display: block;
+ margin-top: 0.28rem;
+ color: rgba(247, 243, 234, 0.58);
+ font-size: 0.82rem;
+ line-height: 1.45;
}
-.home-build-grid {
- display: grid;
- grid-template-columns: minmax(0, 0.78fr) minmax(0, 1.22fr);
- gap: 4rem;
- align-items: center;
+.technology-feature-list code {
+ padding: 0.08rem 0.28rem;
+ font-size: 0.78em;
+}
+
+.technology-feature-list span {
+ min-width: 0;
+ padding: 0.62rem 0.8rem;
+ border: 1px solid rgba(250, 248, 244, 0.11);
+ border-radius: 999px;
+ background: rgba(250, 248, 244, 0.04);
+ color: rgba(247, 243, 234, 0.78);
+ font-size: 0.93rem;
+ font-weight: 700;
}
-.home-build-copy h2 {
+.technology-code pre {
margin: 0;
- font-size: 3rem;
- line-height: 1;
- letter-spacing: 0;
+ font-size: 0.9rem;
}
-.home-build-copy p {
- max-width: 460px;
- margin-top: 1rem;
- color: var(--color-muted);
+.technology-code {
+ min-width: 0;
}
-.home-build-mark {
- width: 34px;
- height: 34px;
+.runtime-example {
display: grid;
- place-items: center;
- margin-bottom: 1.1rem;
- border-radius: 50%;
- border: 1px solid rgba(244, 122, 40, 0.5);
- color: var(--color-orange);
- font-weight: 900;
+ gap: 0.8rem;
}
-.home-code-card {
- border: 1px solid rgba(250, 248, 244, 0.12);
+.runtime-code-tabs {
+ border-color: rgba(250, 248, 244, 0.13);
border-radius: 8px;
- background: rgba(250, 248, 244, 0.04);
- box-shadow: var(--shadow-elevated);
- overflow: hidden;
+ background: rgba(18, 15, 22, 0.86);
+ box-shadow: 0 18px 44px rgba(0, 0, 0, 0.24);
}
-.home-code-title {
- padding: 0.75rem 1rem;
- border-bottom: 1px solid rgba(250, 248, 244, 0.1);
- color: rgba(247, 243, 234, 0.78);
- font-family: var(--font-mono);
+.runtime-code-tabs .tabs-list {
+ gap: 0.35rem;
+ padding: 0.42rem;
+ border-bottom-color: rgba(250, 248, 244, 0.1);
+ background: rgba(250, 248, 244, 0.035);
+}
+
+.runtime-code-tabs .tabs-tab {
+ min-height: 34px;
+ padding: 0.42rem 0.72rem;
+ color: rgba(247, 243, 234, 0.58);
font-size: 0.82rem;
- font-weight: 800;
}
-.home-code-card pre {
+.runtime-code-tabs .tabs-tab:hover,
+.runtime-code-tabs .tabs-tab:focus-visible {
+ border-color: rgba(246, 201, 69, 0.32);
+ background: rgba(250, 248, 244, 0.07);
+ color: var(--color-fg);
+}
+
+.runtime-code-tabs .tabs-tab[aria-selected="true"] {
+ border-color: rgba(246, 201, 69, 0.42);
+ background: rgba(246, 201, 69, 0.12);
+ color: var(--color-fg);
+}
+
+.runtime-code-tabs .tabs-panel {
+ padding: 0;
+}
+
+.runtime-code-tabs pre {
+ max-height: 360px;
+ overflow: auto;
border: 0;
border-radius: 0;
+ background: transparent;
box-shadow: none;
- background: linear-gradient(180deg, #171616 0%, #1d1727 100%);
- font-size: 0.95rem;
}
-.home-section {
- position: relative;
- padding: 4rem 0 4.5rem;
+.technology-section-silk .technology-code pre {
+ max-height: 560px;
+ overflow: auto;
}
-.contact-panel {
- display: flex;
- justify-content: space-between;
- gap: 2rem;
+.technology-section-virtnosis .technology-code pre {
+ max-height: 520px;
+ overflow: auto;
+}
+
+.runtime-command {
+ display: grid;
+ grid-template-columns: auto minmax(0, 1fr);
+ gap: 0.8rem;
align-items: center;
- padding: 1.25rem 0;
- border-top: 1px solid rgba(250, 248, 244, 0.12);
+ padding: 0.62rem 0.75rem;
+ border: 1px solid rgba(250, 248, 244, 0.12);
+ border-radius: 8px;
+ background: rgba(250, 248, 244, 0.045);
}
-.contact-panel h2 {
- margin: 0;
- font-size: 1.8rem;
- line-height: 1;
- letter-spacing: 0;
+.runtime-command span {
+ color: rgba(247, 243, 234, 0.58);
+ font-size: 0.76rem;
+ font-weight: 800;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ white-space: nowrap;
+}
+
+.runtime-command pre {
+ padding: 0.35rem 0;
+ border: 0;
+ background: transparent;
+ box-shadow: none;
}
-.contact-links {
+.technology-links,
+.docs-selector {
display: flex;
flex-wrap: wrap;
- justify-content: flex-end;
gap: 0.55rem;
}
-.contact-links a {
- padding: 0.65rem 0.85rem;
- border-radius: 999px;
+.technology-links {
+ margin-top: 1.55rem;
+}
+
+.technology-links a,
+.docs-selector a,
+.docs-link-group a {
+ display: inline-flex;
+ align-items: center;
+ min-height: 42px;
+ padding: 0.58rem 0.75rem;
border: 1px solid rgba(250, 248, 244, 0.12);
- background: rgba(250, 248, 244, 0.06);
+ border-radius: 999px;
+ background: rgba(250, 248, 244, 0.045);
color: var(--color-fg);
text-decoration: none;
font-weight: 700;
}
-.contact-links a:hover,
-.contact-links a:focus-visible {
- border-color: rgba(246, 201, 69, 0.34);
- background: rgba(250, 248, 244, 0.1);
+.technology-links a:hover,
+.technology-links a:focus-visible,
+.docs-selector a:hover,
+.docs-selector a:focus-visible,
+.docs-link-group a:hover,
+.docs-link-group a:focus-visible {
+ border-color: rgba(246, 201, 69, 0.4);
+ background: rgba(250, 248, 244, 0.08);
}
-.home-footer {
- border-top-color: rgba(250, 248, 244, 0.08);
+.coming-soon {
+ min-height: 840px;
}
-.home-footer .footer-inner {
- color: rgba(247, 243, 234, 0.56);
+.blog-lanes {
+ position: relative;
+ z-index: 2;
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 1rem;
+ margin-top: 1.5rem;
}
-@media (max-width: 1180px) {
- .home-hero-layout,
- .home-proof-grid,
- .home-build-grid {
- grid-template-columns: 1fr;
- }
-
- .home-hero-copy {
- max-width: 780px;
- }
-
- .home-hero-copy h1 {
- max-width: 780px;
- font-size: 4.2rem;
- }
-
- .home-orbit {
- min-height: 500px;
- }
+.blog-lanes article,
+.blog-placeholder,
+.docs-link-group {
+ border: 1px solid rgba(250, 248, 244, 0.12);
+ border-radius: 8px;
+ background: rgba(250, 248, 244, 0.045);
}
-@media (max-width: 820px) {
- .home-hero {
- min-height: auto;
- padding: 4.25rem 0 3rem;
- }
-
- .home-hero-copy h1 {
- font-size: 3rem;
- line-height: 1;
- }
-
- .home-lede {
- font-size: 1.05rem;
- }
-
- .home-metrics,
- .home-project-strip {
- grid-template-columns: 1fr;
- }
+.blog-lanes article {
+ padding: 1rem;
+}
- .home-orbit {
- min-height: 360px;
- margin-top: 1rem;
- overflow: hidden;
- }
+.blog-lanes span {
+ color: var(--color-gold);
+ font-size: 0.78rem;
+ font-weight: 800;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+}
- .home-orbit::before,
- .home-orbit-plane {
- left: -30%;
- right: -22%;
- transform: rotateX(58deg) rotateZ(-24deg) scale(0.78);
- }
+.blog-lanes h2 {
+ margin-top: 0.6rem;
+ font-size: 1.35rem;
+}
- .home-tile {
- width: 150px;
- min-height: 68px;
- }
+.blog-lanes p {
+ margin-top: 0.55rem;
+ font-size: 0.98rem;
+}
- .home-proof,
- .home-build {
- padding: 3.75rem 0;
- }
+.blog-placeholder {
+ position: relative;
+ z-index: 2;
+ display: grid;
+ grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
+ gap: 2rem;
+ margin-top: 1rem;
+ padding: 1.2rem;
+}
- .home-rule-item h2,
- .home-build-copy h2 {
- font-size: 2.35rem;
- }
+.blog-placeholder h2 {
+ font-size: 1.7rem;
+}
- .home-device-map {
- min-height: 330px;
- }
+.docs-hub-hero {
+ min-height: 600px;
+}
- .map-root {
- top: 10%;
- }
+.docs-selector {
+ position: relative;
+ z-index: 2;
+ margin-top: 1.8rem;
+}
- .map-node {
- width: 112px;
- height: 60px;
- font-size: 0.84rem;
- }
+.docs-hub-tech {
+ display: grid;
+ grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
+ gap: 3rem;
+ align-items: start;
+}
- .node-runtime {
- left: 6%;
- top: 62%;
- }
+.docs-hub-tech-copy h2 {
+ max-width: 680px;
+ font-size: 3rem;
+ line-height: 1.04;
+}
- .node-silk {
- left: 35%;
- top: 62%;
- }
+.docs-hub-tech-copy p {
+ max-width: 620px;
+ margin-top: 1rem;
+}
- .node-slg {
- left: 64%;
- top: 62%;
- }
+.docs-link-groups {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 0.9rem;
+}
- .node-edge {
- display: none;
- }
+.docs-link-group {
+ display: flex;
+ flex-direction: column;
+ gap: 0.55rem;
+ min-width: 0;
+ padding: 0.95rem;
+}
- .contact-panel {
- align-items: flex-start;
- flex-direction: column;
- }
+.docs-link-group h3 {
+ color: var(--color-fg);
+ font-size: 1.1rem;
+ letter-spacing: 0;
+}
- .contact-links {
- justify-content: flex-start;
- }
+.docs-link-group a {
+ justify-content: flex-start;
+ border-radius: 8px;
}
-@media (max-width: 520px) {
- .home-hero-copy h1 {
- font-size: 2.45rem;
- }
+.docs-flow {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) 44px minmax(0, 1fr) 44px minmax(0, 1fr) 44px minmax(0, 1fr);
+ gap: 0.35rem;
+ align-items: center;
+ margin-top: 2rem;
+}
- .home-actions {
- align-items: stretch;
- flex-direction: column;
- }
+.docs-flow div {
+ display: grid;
+ min-height: 74px;
+ place-items: center;
+ border: 1px solid rgba(250, 248, 244, 0.12);
+ border-radius: 8px;
+ background: rgba(250, 248, 244, 0.045);
+ color: var(--color-fg);
+ font-weight: 800;
+}
- .home-actions .button {
- width: 100%;
- }
+.docs-flow span {
+ height: 1px;
+ background: rgba(246, 201, 69, 0.5);
}
+.docs-flow-silk span {
+ background: rgba(62, 155, 101, 0.5);
+}
-/* Oro repositioning */
-.site-dark {
- --color-bg: #11100f;
- --color-fg: #f7f3ea;
- --color-muted: rgba(247, 243, 234, 0.66);
- --color-border: rgba(250, 248, 244, 0.12);
- --color-border-strong: rgba(250, 248, 244, 0.22);
- --color-surface: rgba(250, 248, 244, 0.055);
- --color-surface-2: rgba(250, 248, 244, 0.085);
- --color-surface-3: rgba(250, 248, 244, 0.12);
- --color-code-bg: #151414;
- --color-code-bg-2: #1f1928;
- --shadow: 0 18px 42px rgba(0, 0, 0, 0.28);
- --shadow-elevated: 0 30px 80px rgba(0, 0, 0, 0.34);
+.docs-landing-page {
background: #11100f;
- color: var(--color-fg);
}
-.site-dark::before {
+.docs-landing-main {
+ position: relative;
+ border-top: 1px solid rgba(250, 248, 244, 0.08);
background:
linear-gradient(rgba(250, 248, 244, 0.035) 1px, transparent 1px),
linear-gradient(90deg, rgba(250, 248, 244, 0.035) 1px, transparent 1px),
- linear-gradient(180deg, rgba(91, 59, 162, 0.12), transparent 42%),
- linear-gradient(140deg, rgba(62, 155, 101, 0.08), transparent 46%);
- background-size: 72px 72px, 72px 72px, auto, auto;
- opacity: 1;
-}
-
-.site-dark .site-top {
- background: rgba(17, 16, 15, 0.9);
- border-bottom-color: rgba(250, 248, 244, 0.1);
- box-shadow: none;
+ radial-gradient(circle at 22% 12%, rgba(246, 201, 69, 0.08), transparent 32%),
+ radial-gradient(circle at 88% 18%, rgba(91, 59, 162, 0.12), transparent 34%),
+ #11100f;
+ background-size: 48px 48px, 48px 48px, auto, auto, auto;
}
-.site-dark .brand,
-.site-dark .footer-inner strong {
- color: var(--color-fg);
+.docs-landing-shell {
+ position: relative;
+ z-index: 1;
+ display: grid;
+ grid-template-columns: 280px minmax(0, 920px) 220px;
+ gap: 2.75rem;
+ width: min(100%, 1500px);
+ margin: 0 auto;
+ padding: 3rem var(--gutter) 5rem;
}
-.site-dark .brand-mark {
- border-color: rgba(250, 248, 244, 0.18);
- background: rgba(250, 248, 244, 0.08);
- box-shadow: 0 0 0 1px rgba(246, 201, 69, 0.08);
+.docs-landing-sidebar,
+.docs-landing-toc {
+ position: sticky;
+ top: 112px;
+ align-self: start;
}
-.site-dark .nav {
- color: rgba(247, 243, 234, 0.64);
+.docs-landing-sidebar {
+ max-height: calc(100vh - 140px);
+ overflow: auto;
+ padding-right: 1.25rem;
+ border-right: 1px solid rgba(250, 248, 244, 0.1);
}
-.site-dark .nav a {
- padding: 0.35rem 0;
+.docs-sidebar-home {
+ display: inline-flex;
+ min-height: 36px;
+ align-items: center;
+ color: var(--color-fg);
+ font-weight: 800;
+ text-decoration: none;
}
-.site-dark .nav a:hover,
-.site-dark .nav a:focus-visible {
- color: var(--color-fg);
+.docs-sidebar-nav {
+ display: grid;
+ gap: 1.8rem;
+ margin-top: 1.5rem;
}
-.site-dark .nav a[aria-current="page"] {
- color: var(--color-fg);
+.docs-sidebar-nav section {
+ display: grid;
+ gap: 0.22rem;
}
-.site-dark .button {
- border-color: rgba(250, 248, 244, 0.14);
- background: rgba(250, 248, 244, 0.06);
- color: var(--color-fg);
+.docs-sidebar-nav h2,
+.docs-landing-toc p,
+.docs-landing-kicker {
+ color: rgba(250, 248, 244, 0.62);
+ font-size: 0.72rem;
+ font-weight: 900;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
}
-.site-dark .button:hover,
-.site-dark .button:focus-visible {
- border-color: rgba(246, 201, 69, 0.42);
- background: rgba(250, 248, 244, 0.1);
+.docs-sidebar-nav a,
+.docs-landing-toc a {
+ display: flex;
+ min-height: 34px;
+ align-items: center;
+ border-radius: 8px;
+ color: rgba(250, 248, 244, 0.74);
+ text-decoration: none;
}
-.site-dark .button-primary {
- border-color: rgba(246, 201, 69, 0.68);
- background: linear-gradient(135deg, rgba(246, 201, 69, 0.92), rgba(244, 122, 40, 0.72));
- color: #1f1f1f;
- box-shadow: 0 16px 40px rgba(246, 201, 69, 0.12);
+.docs-sidebar-nav a {
+ padding: 0.28rem 0.55rem;
}
-.site-dark .button-primary:hover,
-.site-dark .button-primary:focus-visible {
- background: linear-gradient(135deg, #f6c945, #f47a28);
- color: #1f1f1f;
+.docs-sidebar-nav a:hover,
+.docs-sidebar-nav a:focus-visible,
+.docs-landing-toc a:hover,
+.docs-landing-toc a:focus-visible {
+ background: rgba(250, 248, 244, 0.06);
+ color: var(--color-fg);
}
-.site-dark .menu-panel {
- border-color: rgba(250, 248, 244, 0.12);
- background: rgba(20, 18, 23, 0.96);
- box-shadow: 0 24px 64px rgba(0, 0, 0, 0.34);
+.docs-landing-content {
+ min-width: 0;
}
-.site-dark .menu-item {
- color: rgba(247, 243, 234, 0.82);
+.docs-landing-intro {
+ padding: 1.2rem 0 2.5rem;
+ border-bottom: 1px solid rgba(250, 248, 244, 0.1);
}
-.site-dark .menu-item:hover,
-.site-dark .menu-item:focus-visible {
- border-color: rgba(246, 201, 69, 0.24);
- background: rgba(250, 248, 244, 0.08);
+.docs-landing-content h1 {
+ max-width: 760px;
+ margin-top: 0.85rem;
color: var(--color-fg);
+ font-size: 3.45rem;
+ line-height: 1.03;
}
-.site-dark .menu-item small,
-.site-dark .menu-hint {
- color: rgba(247, 243, 234, 0.54);
+.docs-landing-content h2 {
+ max-width: 700px;
+ color: var(--color-fg);
+ font-size: 2.3rem;
+ line-height: 1.08;
}
-.site-dark .menu-sep {
- background: rgba(250, 248, 244, 0.1);
+.docs-landing-content p {
+ color: rgba(250, 248, 244, 0.7);
+ line-height: 1.65;
}
-.site-dark .site-subnav {
- border-top-color: rgba(250, 248, 244, 0.08);
+.docs-landing-intro > p:not(.docs-landing-kicker) {
+ max-width: 760px;
+ margin-top: 1rem;
+ font-size: 1.05rem;
}
-.site-dark .site-subnav-label {
- color: rgba(247, 243, 234, 0.52);
+.docs-quick-select {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 0.8rem;
+ margin-top: 2rem;
}
-.site-dark .site-subnav-parent {
- border-color: rgba(250, 248, 244, 0.16);
- background: rgba(250, 248, 244, 0.07);
+.docs-quick-select a {
+ display: grid;
+ gap: 0.35rem;
+ min-height: 112px;
+ align-content: center;
+ padding: 1rem;
+ border: 1px solid rgba(250, 248, 244, 0.12);
+ border-radius: 8px;
+ background: rgba(250, 248, 244, 0.045);
color: var(--color-fg);
+ text-decoration: none;
}
-.site-dark .site-subnav-links a {
- color: rgba(247, 243, 234, 0.62);
- background: rgba(250, 248, 244, 0.04);
-}
-
-.site-dark .site-subnav-links a:hover,
-.site-dark .site-subnav-links a:focus-visible,
-.site-dark .site-subnav-links a[aria-current="page"] {
- color: var(--color-fg);
- border-color: rgba(246, 201, 69, 0.28);
- background: rgba(250, 248, 244, 0.08);
+.docs-quick-select a:first-child {
+ border-color: rgba(246, 201, 69, 0.26);
}
-.site-dark .hero::before {
- background:
- linear-gradient(rgba(250, 248, 244, 0.035) 1px, transparent 1px),
- linear-gradient(90deg, rgba(250, 248, 244, 0.035) 1px, transparent 1px),
- linear-gradient(120deg, rgba(91, 59, 162, 0.14), transparent 52%);
- background-size: 84px 84px, 84px 84px, auto;
+.docs-quick-select a:last-child {
+ border-color: rgba(91, 59, 162, 0.34);
}
-.site-dark .hero-kicker {
- border-color: rgba(250, 248, 244, 0.14);
- background: rgba(250, 248, 244, 0.06);
- color: rgba(247, 243, 234, 0.74);
+.docs-quick-select a:hover,
+.docs-quick-select a:focus-visible,
+.docs-section-action:hover,
+.docs-section-action:focus-visible,
+.docs-link-block a:hover,
+.docs-link-block a:focus-visible {
+ border-color: rgba(246, 201, 69, 0.5);
+ background: rgba(250, 248, 244, 0.075);
}
-.site-dark .panel,
-.site-dark .card,
-.site-dark .list {
- border-color: rgba(250, 248, 244, 0.12);
- background: rgba(250, 248, 244, 0.045);
- box-shadow: var(--shadow);
+.docs-quick-select span {
+ font-weight: 900;
}
-.site-dark .card::before {
- background:
- linear-gradient(135deg, rgba(246, 201, 69, 0.14), transparent 48%),
- linear-gradient(210deg, rgba(91, 59, 162, 0.18), transparent 56%);
- opacity: 1;
+.docs-quick-select small,
+.docs-link-block small {
+ color: rgba(250, 248, 244, 0.58);
+ line-height: 1.45;
}
-.site-dark .panel-header {
- border-bottom-color: rgba(250, 248, 244, 0.1);
- background: rgba(250, 248, 244, 0.045);
+.docs-tech-section {
+ padding: 3rem 0;
+ border-bottom: 1px solid rgba(250, 248, 244, 0.1);
}
-.site-dark .card-title,
-.site-dark .list-title,
-.site-dark .panel-title {
- color: var(--color-fg);
+.docs-tech-heading {
+ display: flex;
+ gap: 1.5rem;
+ align-items: flex-start;
+ justify-content: space-between;
}
-.site-dark .card-meta,
-.site-dark .list-desc,
-.site-dark p {
- color: var(--color-muted);
+.docs-tech-heading h2 {
+ margin-top: 0.7rem;
}
-.site-dark .list-row {
- border-top-color: rgba(250, 248, 244, 0.1);
+.docs-section-action {
+ display: inline-flex;
+ min-width: 150px;
+ min-height: 42px;
+ align-items: center;
+ justify-content: center;
+ padding: 0.55rem 0.9rem;
+ border: 1px solid rgba(246, 201, 69, 0.34);
+ border-radius: 999px;
+ background: rgba(246, 201, 69, 0.08);
+ color: var(--color-fg);
+ font-weight: 800;
+ text-decoration: none;
+ white-space: nowrap;
}
-.site-dark .list-row:hover,
-.site-dark .list-row:focus-visible {
- background: rgba(250, 248, 244, 0.06);
+.docs-tech-summary {
+ max-width: 780px;
+ margin-top: 1rem;
}
-.site-dark .list-meta,
-.site-dark .footer-inner {
- color: rgba(247, 243, 234, 0.54);
+.docs-link-grid {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 1.1rem;
+ margin-top: 2rem;
}
-.site-dark code {
- background: rgba(246, 201, 69, 0.1);
- color: rgba(247, 243, 234, 0.92);
+.docs-link-block {
+ min-width: 0;
+ padding-top: 1rem;
+ border-top: 1px solid rgba(250, 248, 244, 0.12);
}
-.site-dark pre code {
- background: transparent;
- color: inherit;
+.docs-link-block h3 {
+ color: var(--color-fg);
+ font-size: 1rem;
}
-.site-dark pre {
- border-color: rgba(250, 248, 244, 0.12);
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04), 0 18px 44px rgba(0, 0, 0, 0.24);
+.docs-link-block a {
+ display: grid;
+ gap: 0.18rem;
+ margin-top: 0.7rem;
+ min-height: 72px;
+ padding: 0.82rem 0.9rem;
+ border: 1px solid rgba(250, 248, 244, 0.1);
+ border-radius: 8px;
+ background: rgba(250, 248, 244, 0.035);
+ color: var(--color-fg);
+ text-decoration: none;
}
-.site-dark .site-footer {
- border-top-color: rgba(250, 248, 244, 0.1);
+.docs-link-block span {
+ font-weight: 800;
}
-.section-heading {
- max-width: 760px;
+.docs-link-block code {
+ background: rgba(246, 201, 69, 0.12);
+ color: var(--color-gold);
}
-.section-heading h2,
-.docs-hub-tech-copy h2,
-.coming-soon-copy h1,
-.docs-hub-copy h1,
-.tech-hero-copy h1 {
- letter-spacing: 0;
+.docs-landing-toc {
+ display: grid;
+ gap: 0.18rem;
+ padding-left: 1rem;
+ border-left: 1px solid rgba(250, 248, 244, 0.1);
}
-.section-kicker {
- margin-bottom: 0.8rem;
- color: var(--color-gold);
- font-size: 0.78rem;
- font-weight: 800;
- text-transform: uppercase;
- letter-spacing: 0.1em;
+.docs-landing-toc p {
+ margin-bottom: 0.35rem;
+ color: var(--color-fg);
}
-.tech-hero,
-.docs-hub-hero,
-.coming-soon {
- position: relative;
- overflow: hidden;
- min-height: 760px;
- display: flex;
- align-items: center;
- border-bottom: 1px solid rgba(250, 248, 244, 0.08);
+.docs-landing-toc a {
+ padding: 0.25rem 0.5rem;
+ font-size: 0.92rem;
}
-.tech-hero-grid,
-.docs-hub-grid,
-.coming-soon-grid {
- position: absolute;
- inset: 0;
- pointer-events: none;
- background:
- linear-gradient(rgba(250, 248, 244, 0.05) 1px, transparent 1px),
- linear-gradient(90deg, rgba(250, 248, 244, 0.05) 1px, transparent 1px);
- background-size: 88px 88px;
- opacity: 0.4;
- mask-image: linear-gradient(to bottom, black 0%, black 72%, transparent 100%);
+.product-page .hero h1 {
+ max-width: 760px;
+ font-size: 4.4rem;
+ line-height: 1;
+ letter-spacing: 0;
}
-.tech-system-map {
- position: absolute;
- inset: 8rem max(5vw, calc((100vw - var(--container)) / 2)) 6rem auto;
- width: min(390px, 28vw);
- pointer-events: none;
+.product-page .hero {
+ padding: 5.25rem 0;
+ border-bottom: 1px solid rgba(250, 248, 244, 0.08);
}
-.system-map-frame {
- position: relative;
- height: 100%;
- min-height: 460px;
+.product-page .section {
+ border-top: 1px solid rgba(250, 248, 244, 0.06);
}
-.system-map-line {
- position: absolute;
- background: rgba(250, 248, 244, 0.16);
+.learn-copy {
+ position: relative;
+ flex: 0 0 auto;
+ width: 36px;
+ height: 36px;
+ border: 1px solid rgba(250, 248, 244, 0.28);
+ border-radius: 6px;
+ background: rgba(250, 248, 244, 0.08);
+ color: rgba(250, 248, 244, 0.92);
+ cursor: pointer;
+ -webkit-appearance: none;
+ appearance: none;
}
-.system-map-line-main {
- left: 50%;
- top: 0;
- width: 1px;
- height: 100%;
- background: linear-gradient(180deg, transparent, rgba(246, 201, 69, 0.34), rgba(62, 155, 101, 0.24), transparent);
+.learn-copy::before,
+.learn-copy::after {
+ content: "";
+ position: absolute;
+ width: 11px;
+ height: 13px;
+ border: 2px solid currentColor;
+ border-radius: 2px;
}
-.system-map-line-runtime,
-.system-map-line-silk,
-.system-map-line-virtnosis {
- left: 21%;
- width: 58%;
- height: 1px;
+.learn-copy::before {
+ left: 10px;
+ top: 8px;
+ opacity: 0.72;
}
-.system-map-line-runtime {
- top: 22%;
+.learn-copy::after {
+ left: 14px;
+ top: 12px;
+ background: rgba(6, 5, 7, 0.92);
}
-.system-map-line-silk {
- top: 50%;
+.learn-copy:hover,
+.learn-copy:focus-visible {
+ border-color: rgba(246, 201, 69, 0.62);
+ background: rgba(250, 248, 244, 0.14);
+ color: #fffdf9;
}
-.system-map-line-virtnosis {
- top: 78%;
+.learn-copy[data-copy-state="copied"] {
+ border-color: rgba(62, 155, 101, 0.58);
+ color: #8fd4aa;
}
-.system-node {
- position: absolute;
- display: grid;
- grid-template-columns: minmax(0, 1fr);
- row-gap: 0.1rem;
- width: min(245px, 100%);
- padding: 0.95rem 0 0.95rem 1rem;
- border-left: 1px solid rgba(250, 248, 244, 0.22);
- background: linear-gradient(90deg, rgba(250, 248, 244, 0.07), transparent 82%);
- color: rgba(247, 243, 234, 0.9);
+.learn-copy[data-copy-state="failed"] {
+ border-color: rgba(244, 122, 40, 0.58);
+ color: #ffb17f;
}
-.system-node strong {
- font-size: 1rem;
+.docs-page .section {
+ padding: 2.2rem 0 4.5rem;
}
-.system-node small {
- color: rgba(247, 243, 234, 0.52);
- font-size: 0.82rem;
+.docs-page .docs-layout {
+ gap: 2.75rem;
}
-.node-runtime {
- left: 0;
- top: 16%;
+.docs-page .docs-layout[data-has-toc="true"] {
+ grid-template-columns: 300px minmax(0, 900px) 260px;
}
-.node-silk {
- right: 0;
- top: 44%;
- border-left-color: rgba(91, 59, 162, 0.5);
+.docs-page .docs-sidebar,
+.docs-page .docs-toc {
+ border-color: rgba(250, 248, 244, 0.12);
+ background: rgba(250, 248, 244, 0.035);
+ box-shadow: none;
}
-.node-virtnosis {
- left: 10%;
- top: 72%;
- border-left-color: rgba(62, 155, 101, 0.46);
+.docs-page .docs-sidebar {
+ border-radius: 0;
+ border-width: 0 1px 0 0;
+ padding-right: 1.2rem;
}
-.tech-hero-copy,
-.docs-hub-copy,
-.coming-soon-copy {
- position: relative;
- z-index: 2;
- padding: 7rem 0;
+.docs-page .docs-toc {
+ border-radius: 0;
+ border-width: 0 0 0 1px;
+ padding-left: 1.2rem;
}
-.tech-hero-copy {
- max-width: 720px;
+.docs-page .docs-sidebar-header {
+ border-bottom-color: rgba(250, 248, 244, 0.1);
}
-.docs-hub-copy,
-.coming-soon-copy {
- max-width: 820px;
+.docs-page .docs-sidebar-title,
+.docs-page .docs-toc-title,
+.docs-page .docs-search-result-title,
+.docs-page .docs-prevnext-title,
+.docs-page .docs-content h1,
+.docs-page .docs-content h2,
+.docs-page .docs-content h3,
+.docs-page .docs-content h4 {
+ color: var(--color-fg);
}
-.tech-hero-copy h1 {
- max-width: 720px;
- margin: 0;
- color: var(--color-fg);
- font-size: 4.75rem;
- line-height: 1;
-}
-
-.docs-hub-copy h1,
-.coming-soon-copy h1 {
- max-width: 820px;
- margin: 0;
+.docs-page .docs-search {
+ border-color: rgba(250, 248, 244, 0.14);
+ background: rgba(250, 248, 244, 0.055);
color: var(--color-fg);
- font-size: 5.1rem;
- line-height: 0.98;
-}
-
-.tech-hero-copy p,
-.docs-hub-copy p,
-.coming-soon-copy p {
- max-width: 680px;
- margin-top: 1.25rem;
- font-size: 1.16rem;
- line-height: 1.62;
-}
-
-.technology-section,
-.docs-hub-section {
- padding: 5.5rem 0;
-}
-
-.technology-section {
- position: relative;
- border-top: 1px solid rgba(250, 248, 244, 0.08);
- overflow: hidden;
-}
-
-.technology-section::before {
- content: "";
- position: absolute;
- inset: 0;
- pointer-events: none;
- background:
- linear-gradient(rgba(250, 248, 244, 0.035) 1px, transparent 1px),
- linear-gradient(90deg, rgba(250, 248, 244, 0.035) 1px, transparent 1px);
- background-size: 88px 88px;
- opacity: 0.42;
- mask-image: linear-gradient(90deg, transparent 0%, black 18%, black 82%, transparent 100%);
}
-.technology-section-silk {
- background: rgba(91, 59, 162, 0.035);
+.docs-page .docs-search::placeholder {
+ color: rgba(247, 243, 234, 0.44);
}
-.technology-section-virtnosis {
- background: rgba(62, 155, 101, 0.026);
+.docs-page .docs-nav-section,
+.docs-page .docs-search-result-meta,
+.docs-page .docs-breadcrumb,
+.docs-page .docs-prevnext-label,
+.docs-page .spec-toc-title {
+ color: rgba(247, 243, 234, 0.48);
}
-.technology-layout {
- position: relative;
- z-index: 1;
- display: grid;
- grid-template-columns: minmax(0, 0.95fr) minmax(360px, 0.85fr);
- gap: 5rem;
- align-items: center;
+.docs-page .docs-nav a,
+.docs-page .docs-toc-list a,
+.docs-page .spec-toc a {
+ color: rgba(247, 243, 234, 0.62);
}
-.technology-layout-reverse {
- grid-template-columns: minmax(360px, 0.85fr) minmax(0, 0.95fr);
+.docs-page .docs-nav a:hover,
+.docs-page .docs-nav a:focus-visible,
+.docs-page .docs-nav a[data-active="true"],
+.docs-page .docs-toc-list a:hover,
+.docs-page .docs-toc-list a:focus-visible,
+.docs-page .spec-toc a:hover,
+.docs-page .spec-toc a:focus-visible,
+.docs-page .spec-toc a[data-active="true"] {
+ color: var(--color-fg);
+ background: rgba(250, 248, 244, 0.07);
}
-.technology-layout-reverse .technology-copy {
- order: 2;
+.docs-page .docs-content .prose,
+.docs-page .spec-prose {
+ max-width: 92ch;
}
-.technology-copy h2 {
- max-width: 760px;
- margin: 0;
- color: var(--color-fg);
- font-size: 3.4rem;
- line-height: 1.04;
+.docs-page .docs-content .prose > h1:first-child,
+.docs-page .spec-prose > h1:first-child {
+ font-size: 3.05rem;
+ line-height: 1.05;
letter-spacing: 0;
}
-.technology-copy p {
- max-width: 680px;
- margin-top: 1.15rem;
- font-size: 1.06rem;
- line-height: 1.65;
+.docs-page .docs-content p,
+.docs-page .docs-content li,
+.docs-page .docs-content dd,
+.docs-page .spec-prose p,
+.docs-page .spec-prose li,
+.docs-page .spec-prose dd {
+ color: rgba(247, 243, 234, 0.68);
}
-.technology-feature-list {
+.docs-layout .prose ul,
+.docs-layout .prose ol,
+.spec-prose ul,
+.spec-prose ol {
display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: 0.75rem;
- max-width: 680px;
- margin-top: 1.6rem;
+ gap: 0.55rem;
+ margin: 1.1rem 0;
padding: 0;
list-style: none;
}
-.technology-feature-list li {
+.docs-layout .prose li,
+.spec-prose li {
position: relative;
- min-width: 0;
- min-height: 86px;
- padding: 0.86rem 0.95rem 0.88rem;
- overflow: hidden;
- border: 1px solid rgba(250, 248, 244, 0.12);
+ margin: 0;
+ padding: 0.72rem 0.86rem 0.72rem 1rem;
+ border: 1px solid rgba(31, 31, 31, 0.1);
+ border-left: 2px solid rgba(246, 201, 69, 0.44);
border-radius: 8px;
- background:
- linear-gradient(135deg, rgba(250, 248, 244, 0.072), rgba(250, 248, 244, 0.022)),
- linear-gradient(90deg, rgba(246, 201, 69, 0.14), transparent 32%);
- transition:
- border-color 160ms ease,
- background-color 160ms ease,
- transform 160ms ease;
+ background: rgba(255, 255, 255, 0.6);
}
-.technology-feature-list li::before {
- content: "";
- position: absolute;
- left: 0;
- top: 0.8rem;
- bottom: 0.8rem;
- width: 2px;
- border-radius: 999px;
- background: var(--color-gold);
+.docs-layout .prose li + li,
+.spec-prose li + li {
+ margin-top: 0;
}
-.technology-feature-list li:nth-child(2)::before {
- background: var(--color-purple);
+.docs-layout .prose ol,
+.spec-prose ol {
+ counter-reset: docs-ordered-list;
}
-.technology-feature-list li:nth-child(3)::before {
- background: var(--color-green);
+.docs-layout .prose ol > li,
+.spec-prose ol > li {
+ counter-increment: docs-ordered-list;
+ padding-left: 2.75rem;
}
-.technology-feature-list li:nth-child(4)::before {
- background: var(--color-orange);
+.docs-layout .prose ol > li::before,
+.spec-prose ol > li::before {
+ content: counter(docs-ordered-list);
+ position: absolute;
+ left: 0.78rem;
+ top: 0.72rem;
+ display: inline-grid;
+ place-items: center;
+ min-width: 1.32rem;
+ height: 1.32rem;
+ padding: 0 0.18rem;
+ border: 1px solid rgba(246, 201, 69, 0.38);
+ border-radius: 999px;
+ background: rgba(246, 201, 69, 0.12);
+ color: var(--color-gold);
+ font-family: var(--font-mono);
+ font-size: 0.7rem;
+ font-weight: 800;
+ line-height: 1;
}
-.technology-feature-list li:hover {
- border-color: rgba(246, 201, 69, 0.32);
- background-color: rgba(250, 248, 244, 0.035);
- transform: translateY(-1px);
+.docs-layout .prose li > p:first-child,
+.spec-prose li > p:first-child {
+ margin-top: 0;
}
-.technology-feature-list strong {
- display: block;
- color: var(--color-fg);
- font-size: 0.96rem;
- line-height: 1.25;
+.docs-layout .prose li > p:last-child,
+.spec-prose li > p:last-child {
+ margin-bottom: 0;
}
-.technology-feature-list small {
- display: block;
- margin-top: 0.28rem;
- color: rgba(247, 243, 234, 0.58);
- font-size: 0.82rem;
- line-height: 1.45;
+.docs-layout .prose li > ul,
+.docs-layout .prose li > ol,
+.spec-prose li > ul,
+.spec-prose li > ol {
+ gap: 0.42rem;
+ margin: 0.64rem 0 0;
}
-.technology-feature-list code {
- padding: 0.08rem 0.28rem;
- font-size: 0.78em;
+.docs-layout .prose li > ul > li,
+.docs-layout .prose li > ol > li,
+.spec-prose li > ul > li,
+.spec-prose li > ol > li {
+ padding-top: 0.56rem;
+ padding-bottom: 0.56rem;
+ background: rgba(31, 31, 31, 0.035);
}
-.technology-feature-list span {
- min-width: 0;
- padding: 0.62rem 0.8rem;
- border: 1px solid rgba(250, 248, 244, 0.11);
- border-radius: 999px;
- background: rgba(250, 248, 244, 0.04);
- color: rgba(247, 243, 234, 0.78);
- font-size: 0.93rem;
- font-weight: 700;
+.site-dark .docs-layout .prose li,
+.site-dark .spec-prose li,
+.docs-page .docs-layout .prose li,
+.docs-page .spec-prose li {
+ border-color: rgba(250, 248, 244, 0.1);
+ border-left-color: rgba(246, 201, 69, 0.46);
+ background: rgba(250, 248, 244, 0.045);
}
-.technology-code pre {
- margin: 0;
- font-size: 0.9rem;
+.site-dark .docs-layout .prose li > ul > li,
+.site-dark .docs-layout .prose li > ol > li,
+.site-dark .spec-prose li > ul > li,
+.site-dark .spec-prose li > ol > li,
+.docs-page .docs-layout .prose li > ul > li,
+.docs-page .docs-layout .prose li > ol > li,
+.docs-page .spec-prose li > ul > li,
+.docs-page .spec-prose li > ol > li {
+ background: rgba(250, 248, 244, 0.035);
}
-.technology-code {
- min-width: 0;
+.docs-page .docs-inline-ref {
+ color: var(--color-fg);
+ text-decoration-color: rgba(246, 201, 69, 0.35);
}
-.runtime-example {
- display: grid;
- gap: 0.8rem;
-}
-
-.runtime-code-tabs {
- border-color: rgba(250, 248, 244, 0.13);
- border-radius: 8px;
- background: rgba(18, 15, 22, 0.86);
- box-shadow: 0 18px 44px rgba(0, 0, 0, 0.24);
-}
-
-.runtime-code-tabs .tabs-list {
- gap: 0.35rem;
- padding: 0.42rem;
- border-bottom-color: rgba(250, 248, 244, 0.1);
- background: rgba(250, 248, 244, 0.035);
-}
-
-.runtime-code-tabs .tabs-tab {
- min-height: 34px;
- padding: 0.42rem 0.72rem;
- color: rgba(247, 243, 234, 0.58);
- font-size: 0.82rem;
-}
-
-.runtime-code-tabs .tabs-tab:hover,
-.runtime-code-tabs .tabs-tab:focus-visible {
- border-color: rgba(246, 201, 69, 0.32);
- background: rgba(250, 248, 244, 0.07);
- color: var(--color-fg);
-}
-
-.runtime-code-tabs .tabs-tab[aria-selected="true"] {
- border-color: rgba(246, 201, 69, 0.42);
- background: rgba(246, 201, 69, 0.12);
- color: var(--color-fg);
-}
-
-.runtime-code-tabs .tabs-panel {
- padding: 0;
-}
-
-.runtime-code-tabs pre {
- max-height: 360px;
- overflow: auto;
- border: 0;
- border-radius: 0;
- background: transparent;
- box-shadow: none;
-}
-
-.technology-section-silk .technology-code pre {
- max-height: 560px;
- overflow: auto;
-}
-
-.technology-section-virtnosis .technology-code pre {
- max-height: 520px;
- overflow: auto;
-}
-
-.runtime-command {
- display: grid;
- grid-template-columns: auto minmax(0, 1fr);
- gap: 0.8rem;
- align-items: center;
- padding: 0.62rem 0.75rem;
- border: 1px solid rgba(250, 248, 244, 0.12);
- border-radius: 8px;
- background: rgba(250, 248, 244, 0.045);
-}
-
-.runtime-command span {
- color: rgba(247, 243, 234, 0.58);
- font-size: 0.76rem;
- font-weight: 800;
- letter-spacing: 0.08em;
- text-transform: uppercase;
- white-space: nowrap;
-}
-
-.runtime-command pre {
- padding: 0.35rem 0;
- border: 0;
- background: transparent;
- box-shadow: none;
-}
-
-.technology-links,
-.docs-selector {
- display: flex;
- flex-wrap: wrap;
- gap: 0.55rem;
-}
-
-.technology-links {
- margin-top: 1.55rem;
-}
-
-.technology-links a,
-.docs-selector a,
-.docs-link-group a {
- display: inline-flex;
- align-items: center;
- min-height: 42px;
- padding: 0.58rem 0.75rem;
- border: 1px solid rgba(250, 248, 244, 0.12);
- border-radius: 999px;
- background: rgba(250, 248, 244, 0.045);
- color: var(--color-fg);
- text-decoration: none;
- font-weight: 700;
-}
-
-.technology-links a:hover,
-.technology-links a:focus-visible,
-.docs-selector a:hover,
-.docs-selector a:focus-visible,
-.docs-link-group a:hover,
-.docs-link-group a:focus-visible {
- border-color: rgba(246, 201, 69, 0.4);
+.docs-page .docs-inline-code,
+.docs-page .prose code {
background: rgba(250, 248, 244, 0.08);
+ color: rgba(247, 243, 234, 0.92);
}
-.coming-soon {
- min-height: 840px;
-}
-
-.blog-lanes {
- position: relative;
- z-index: 2;
- display: grid;
- grid-template-columns: repeat(3, minmax(0, 1fr));
- gap: 1rem;
- margin-top: 1.5rem;
-}
-
-.blog-lanes article,
-.blog-placeholder,
-.docs-link-group {
- border: 1px solid rgba(250, 248, 244, 0.12);
- border-radius: 8px;
- background: rgba(250, 248, 244, 0.045);
+.docs-page .prose pre {
+ padding: 0;
}
-.blog-lanes article {
+.docs-page .prose pre code,
+.docs-page .prose pre code.hljs {
+ display: block;
padding: 1rem;
+ background: transparent;
}
-.blog-lanes span {
- color: var(--color-gold);
- font-size: 0.78rem;
- font-weight: 800;
- text-transform: uppercase;
- letter-spacing: 0.08em;
-}
-
-.blog-lanes h2 {
- margin-top: 0.6rem;
- font-size: 1.35rem;
-}
-
-.blog-lanes p {
- margin-top: 0.55rem;
- font-size: 0.98rem;
-}
-
-.blog-placeholder {
- position: relative;
- z-index: 2;
- display: grid;
- grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
- gap: 2rem;
- margin-top: 1rem;
- padding: 1.2rem;
-}
-
-.blog-placeholder h2 {
- font-size: 1.7rem;
-}
-
-.docs-hub-hero {
- min-height: 600px;
-}
-
-.docs-selector {
- position: relative;
- z-index: 2;
- margin-top: 1.8rem;
-}
-
-.docs-hub-tech {
- display: grid;
- grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
- gap: 3rem;
- align-items: start;
-}
-
-.docs-hub-tech-copy h2 {
- max-width: 680px;
- font-size: 3rem;
- line-height: 1.04;
-}
-
-.docs-hub-tech-copy p {
- max-width: 620px;
- margin-top: 1rem;
-}
-
-.docs-link-groups {
- display: grid;
- grid-template-columns: repeat(3, minmax(0, 1fr));
- gap: 0.9rem;
-}
-
-.docs-link-group {
- display: flex;
- flex-direction: column;
- gap: 0.55rem;
- min-width: 0;
- padding: 0.95rem;
-}
-
-.docs-link-group h3 {
- color: var(--color-fg);
- font-size: 1.1rem;
- letter-spacing: 0;
-}
-
-.docs-link-group a {
- justify-content: flex-start;
- border-radius: 8px;
-}
-
-.docs-flow {
- display: grid;
- grid-template-columns: minmax(0, 1fr) 44px minmax(0, 1fr) 44px minmax(0, 1fr) 44px minmax(0, 1fr);
- gap: 0.35rem;
- align-items: center;
- margin-top: 2rem;
-}
-
-.docs-flow div {
- display: grid;
- min-height: 74px;
- place-items: center;
- border: 1px solid rgba(250, 248, 244, 0.12);
- border-radius: 8px;
- background: rgba(250, 248, 244, 0.045);
- color: var(--color-fg);
- font-weight: 800;
-}
-
-.docs-flow span {
- height: 1px;
- background: rgba(246, 201, 69, 0.5);
-}
-
-.docs-flow-silk span {
- background: rgba(62, 155, 101, 0.5);
-}
-
-.docs-landing-page {
- background: #11100f;
-}
-
-.docs-landing-main {
- position: relative;
- border-top: 1px solid rgba(250, 248, 244, 0.08);
- background:
- linear-gradient(rgba(250, 248, 244, 0.035) 1px, transparent 1px),
- linear-gradient(90deg, rgba(250, 248, 244, 0.035) 1px, transparent 1px),
- radial-gradient(circle at 22% 12%, rgba(246, 201, 69, 0.08), transparent 32%),
- radial-gradient(circle at 88% 18%, rgba(91, 59, 162, 0.12), transparent 34%),
- #11100f;
- background-size: 48px 48px, 48px 48px, auto, auto, auto;
-}
-
-.docs-landing-shell {
- position: relative;
- z-index: 1;
- display: grid;
- grid-template-columns: 280px minmax(0, 920px) 220px;
- gap: 2.75rem;
- width: min(100%, 1500px);
- margin: 0 auto;
- padding: 3rem var(--gutter) 5rem;
-}
-
-.docs-landing-sidebar,
-.docs-landing-toc {
- position: sticky;
- top: 112px;
- align-self: start;
-}
-
-.docs-landing-sidebar {
- max-height: calc(100vh - 140px);
- overflow: auto;
- padding-right: 1.25rem;
- border-right: 1px solid rgba(250, 248, 244, 0.1);
-}
-
-.docs-sidebar-home {
- display: inline-flex;
- min-height: 36px;
- align-items: center;
- color: var(--color-fg);
- font-weight: 800;
- text-decoration: none;
-}
-
-.docs-sidebar-nav {
- display: grid;
- gap: 1.8rem;
- margin-top: 1.5rem;
-}
-
-.docs-sidebar-nav section {
- display: grid;
- gap: 0.22rem;
-}
-
-.docs-sidebar-nav h2,
-.docs-landing-toc p,
-.docs-landing-kicker {
- color: rgba(250, 248, 244, 0.62);
- font-size: 0.72rem;
- font-weight: 900;
- letter-spacing: 0.12em;
- text-transform: uppercase;
-}
-
-.docs-sidebar-nav a,
-.docs-landing-toc a {
- display: flex;
- min-height: 34px;
- align-items: center;
- border-radius: 8px;
- color: rgba(250, 248, 244, 0.74);
- text-decoration: none;
-}
-
-.docs-sidebar-nav a {
- padding: 0.28rem 0.55rem;
-}
-
-.docs-sidebar-nav a:hover,
-.docs-sidebar-nav a:focus-visible,
-.docs-landing-toc a:hover,
-.docs-landing-toc a:focus-visible {
- background: rgba(250, 248, 244, 0.06);
- color: var(--color-fg);
-}
-
-.docs-landing-content {
- min-width: 0;
-}
-
-.docs-landing-intro {
- padding: 1.2rem 0 2.5rem;
- border-bottom: 1px solid rgba(250, 248, 244, 0.1);
-}
-
-.docs-landing-content h1 {
- max-width: 760px;
- margin-top: 0.85rem;
- color: var(--color-fg);
- font-size: 3.45rem;
- line-height: 1.03;
-}
-
-.docs-landing-content h2 {
- max-width: 700px;
- color: var(--color-fg);
- font-size: 2.3rem;
- line-height: 1.08;
-}
-
-.docs-landing-content p {
- color: rgba(250, 248, 244, 0.7);
- line-height: 1.65;
-}
-
-.docs-landing-intro > p:not(.docs-landing-kicker) {
- max-width: 760px;
- margin-top: 1rem;
- font-size: 1.05rem;
-}
-
-.docs-quick-select {
- display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: 0.8rem;
- margin-top: 2rem;
-}
-
-.docs-quick-select a {
- display: grid;
- gap: 0.35rem;
- min-height: 112px;
- align-content: center;
- padding: 1rem;
- border: 1px solid rgba(250, 248, 244, 0.12);
- border-radius: 8px;
- background: rgba(250, 248, 244, 0.045);
- color: var(--color-fg);
- text-decoration: none;
-}
-
-.docs-quick-select a:first-child {
- border-color: rgba(246, 201, 69, 0.26);
-}
-
-.docs-quick-select a:last-child {
- border-color: rgba(91, 59, 162, 0.34);
-}
-
-.docs-quick-select a:hover,
-.docs-quick-select a:focus-visible,
-.docs-section-action:hover,
-.docs-section-action:focus-visible,
-.docs-link-block a:hover,
-.docs-link-block a:focus-visible {
- border-color: rgba(246, 201, 69, 0.5);
- background: rgba(250, 248, 244, 0.075);
-}
-
-.docs-quick-select span {
- font-weight: 900;
-}
-
-.docs-quick-select small,
-.docs-link-block small {
- color: rgba(250, 248, 244, 0.58);
- line-height: 1.45;
-}
-
-.docs-tech-section {
- padding: 3rem 0;
- border-bottom: 1px solid rgba(250, 248, 244, 0.1);
-}
-
-.docs-tech-heading {
- display: flex;
- gap: 1.5rem;
- align-items: flex-start;
- justify-content: space-between;
-}
-
-.docs-tech-heading h2 {
- margin-top: 0.7rem;
-}
-
-.docs-section-action {
- display: inline-flex;
- min-width: 150px;
- min-height: 42px;
- align-items: center;
- justify-content: center;
- padding: 0.55rem 0.9rem;
- border: 1px solid rgba(246, 201, 69, 0.34);
- border-radius: 999px;
- background: rgba(246, 201, 69, 0.08);
- color: var(--color-fg);
- font-weight: 800;
- text-decoration: none;
- white-space: nowrap;
-}
-
-.docs-tech-summary {
- max-width: 780px;
- margin-top: 1rem;
-}
-
-.docs-link-grid {
- display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: 1.1rem;
- margin-top: 2rem;
-}
-
-.docs-link-block {
- min-width: 0;
- padding-top: 1rem;
- border-top: 1px solid rgba(250, 248, 244, 0.12);
-}
-
-.docs-link-block h3 {
- color: var(--color-fg);
- font-size: 1rem;
-}
-
-.docs-link-block a {
- display: grid;
- gap: 0.18rem;
- margin-top: 0.7rem;
- min-height: 72px;
- padding: 0.82rem 0.9rem;
- border: 1px solid rgba(250, 248, 244, 0.1);
- border-radius: 8px;
- background: rgba(250, 248, 244, 0.035);
- color: var(--color-fg);
- text-decoration: none;
-}
-
-.docs-link-block span {
- font-weight: 800;
-}
-
-.docs-link-block code {
- background: rgba(246, 201, 69, 0.12);
- color: var(--color-gold);
-}
-
-.docs-landing-toc {
- display: grid;
- gap: 0.18rem;
- padding-left: 1rem;
- border-left: 1px solid rgba(250, 248, 244, 0.1);
-}
-
-.docs-landing-toc p {
- margin-bottom: 0.35rem;
- color: var(--color-fg);
-}
-
-.docs-landing-toc a {
- padding: 0.25rem 0.5rem;
- font-size: 0.92rem;
-}
-
-.product-page .hero h1 {
- max-width: 760px;
- font-size: 4.4rem;
- line-height: 1;
- letter-spacing: 0;
-}
-
-.product-page .hero {
- padding: 5.25rem 0;
- border-bottom: 1px solid rgba(250, 248, 244, 0.08);
-}
-
-.product-page .section {
- border-top: 1px solid rgba(250, 248, 244, 0.06);
-}
-
-.learn-page .hero {
- padding: clamp(4rem, 7vw, 6.5rem) 0 3rem;
-}
-
-.learn-hero {
- position: relative;
- z-index: 1;
- max-width: 780px;
- margin: 0 auto;
- text-align: center;
-}
-
-.learn-hero h1 {
- max-width: none;
- margin-top: 1rem;
- font-size: clamp(3.1rem, 7vw, 5.7rem);
- line-height: 0.98;
- letter-spacing: 0;
-}
-
-.learn-hero .lede {
- max-width: 62ch;
- margin: 1.15rem auto 0;
-}
-
-.learn-hero-actions {
- justify-content: center;
-}
-
-.learn-hero-art {
- position: relative;
- display: grid;
- place-items: center;
- min-height: clamp(18rem, 31vw, 28rem);
- margin-top: clamp(2rem, 5vw, 4rem);
- overflow: hidden;
- border-bottom: 1px solid rgba(250, 248, 244, 0.08);
- background:
- linear-gradient(rgba(250, 248, 244, 0.035) 1px, transparent 1px),
- linear-gradient(90deg, rgba(250, 248, 244, 0.035) 1px, transparent 1px),
- radial-gradient(60% 80% at 50% 110%, rgba(62, 155, 101, 0.28), transparent 58%),
- radial-gradient(80% 90% at 50% 115%, rgba(91, 59, 162, 0.18), transparent 64%);
- background-size: 56px 56px, 56px 56px, auto, auto;
-}
-
-.learn-course-panel {
- position: relative;
- width: min(100%, 560px);
- padding: 1.4rem;
- border: 1px solid rgba(250, 248, 244, 0.12);
- border-radius: 8px;
- background: rgba(12, 11, 14, 0.86);
- box-shadow: 0 28px 80px rgba(0, 0, 0, 0.36);
-}
-
-.learn-course-mark {
- display: grid;
- place-items: center;
- width: 72px;
- height: 72px;
- margin: -3rem auto 1.1rem;
- border: 1px solid rgba(250, 248, 244, 0.16);
- border-radius: 8px;
- background:
- linear-gradient(135deg, rgba(250, 248, 244, 0.96), rgba(250, 248, 244, 0.78)),
- linear-gradient(135deg, rgba(246, 201, 69, 0.3), rgba(91, 59, 162, 0.22));
-}
-
-.learn-course-mark img {
- width: 42px;
- height: 42px;
-}
-
-.learn-course-panel h2 {
- text-align: center;
-}
-
-.learn-course-panel p {
- max-width: 44ch;
- margin: 0.55rem auto 0;
- text-align: center;
-}
-
-.learn-current-row {
- display: grid;
- grid-template-columns: minmax(0, 1fr) auto;
- gap: 1rem;
- align-items: center;
- margin: 1.4rem auto 0;
- padding: 0.82rem 0.95rem;
- max-width: 380px;
- border: 1px solid rgba(250, 248, 244, 0.1);
- border-radius: 999px;
- background: rgba(250, 248, 244, 0.06);
- text-align: left;
-}
-
-.learn-current-row strong {
- display: block;
- color: var(--color-fg);
- line-height: 1.2;
-}
-
-.learn-current-row span {
- display: block;
- margin-top: 0.18rem;
- color: rgba(247, 243, 234, 0.56);
- font-size: 0.85rem;
- line-height: 1.35;
-}
-
-.learn-current-dot {
- width: 28px;
- height: 28px;
- border: 1px solid rgba(246, 201, 69, 0.34);
- border-radius: 999px;
- background: rgba(246, 201, 69, 0.12);
-}
-
-.learn-section-heading {
- display: flex;
- align-items: baseline;
- justify-content: center;
- gap: 1rem;
- flex-wrap: wrap;
- text-align: center;
-}
-
-.learn-section-heading p {
- font-size: 1rem;
-}
-
-.learn-chapter-grid {
- display: grid;
- grid-template-columns: repeat(3, minmax(0, 1fr));
- gap: 0.8rem;
-}
-
-.learn-chapter {
- display: grid;
- grid-template-columns: auto minmax(0, 1fr);
- gap: 0.78rem;
- min-height: 112px;
- padding: 1rem;
- border: 1px solid rgba(250, 248, 244, 0.12);
- border-radius: 8px;
- background: rgba(250, 248, 244, 0.045);
- color: inherit;
- text-decoration: none;
- transition: transform 140ms ease, background 140ms ease, border-color 140ms ease;
-}
-
-.learn-chapter:hover,
-.learn-chapter:focus-visible {
- border-color: rgba(246, 201, 69, 0.36);
- background: rgba(250, 248, 244, 0.07);
- transform: translateY(-1px);
-}
-
-.learn-chapter-number {
- display: inline-grid;
- place-items: center;
- width: 1.7rem;
- height: 1.7rem;
- margin-top: 0;
- border-radius: 999px;
- background: rgba(246, 201, 69, 0.16);
- color: var(--color-gold);
- font-family: var(--font-mono);
- font-size: 0.78rem;
- font-weight: 800;
- line-height: 1;
- text-align: center;
-}
-
-.learn-chapter:nth-child(3n + 2) .learn-chapter-number {
- background: rgba(62, 155, 101, 0.16);
- color: #8fd4aa;
-}
-
-.learn-chapter:nth-child(3n) .learn-chapter-number {
- background: rgba(244, 122, 40, 0.16);
- color: #ffb17f;
-}
-
-.learn-chapter strong {
- display: block;
- color: var(--color-fg);
- line-height: 1.25;
-}
-
-.learn-chapter > span:not(.learn-chapter-number) {
- display: block;
- min-width: 0;
-}
-
-.learn-chapter > span:not(.learn-chapter-number) > span {
- display: block;
- margin-top: 0.38rem;
- color: rgba(247, 243, 234, 0.62);
- font-size: 0.92rem;
- line-height: 1.45;
-}
-
-.learn-info-grid {
- display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: 1rem;
-}
-
-.learn-info {
- padding: 1.15rem;
- border: 1px solid rgba(250, 248, 244, 0.12);
- border-radius: 8px;
- background: rgba(250, 248, 244, 0.04);
-}
-
-.learn-info h3 {
- font-size: 1.2rem;
-}
-
-.learn-info p {
- margin-top: 0.45rem;
-}
-
-.learn-lesson-page .section {
- padding-top: 2.2rem;
-}
-
-.learn-lesson-page {
- --learn-lesson-bar-top: 126px;
-}
-
-.learn-lesson-shell {
- max-width: 920px;
- margin: 0 auto;
-}
-
-.learn-lesson-bar {
- position: sticky;
- top: var(--learn-lesson-bar-top);
- z-index: 80;
- display: flex;
- gap: 0.75rem;
- align-items: center;
- margin-bottom: 2rem;
- padding: 0.55rem 0.72rem;
- border: 1px solid rgba(250, 248, 244, 0.12);
- border-radius: 999px;
- background: rgba(10, 9, 12, 0.88);
- backdrop-filter: blur(16px);
-}
-
-.learn-lesson-menu {
- width: 36px;
- height: 36px;
- border: 1px solid rgba(250, 248, 244, 0.1);
- border-radius: 999px;
- background: rgba(250, 248, 244, 0.045);
- position: relative;
-}
-
-.learn-lesson-menu::before,
-.learn-lesson-menu::after {
- content: "";
- position: absolute;
- left: 10px;
- width: 14px;
- height: 1px;
- background: rgba(247, 243, 234, 0.72);
-}
-
-.learn-lesson-menu::before {
- top: 13px;
- box-shadow: 0 5px 0 rgba(247, 243, 234, 0.72);
-}
-
-.learn-lesson-menu::after {
- top: 23px;
-}
-
-.learn-lesson-title {
- display: flex;
- align-items: center;
- gap: 0.7rem;
- min-width: 0;
- flex: 0 1 auto;
-}
-
-.learn-lesson-title img {
- width: 30px;
- height: 30px;
- border-radius: 8px;
- border: 1px solid rgba(250, 248, 244, 0.12);
- background: rgba(250, 248, 244, 0.08);
-}
-
-.learn-lesson-title strong,
-.learn-lesson-title span {
- display: block;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.learn-lesson-title strong {
- color: var(--color-fg);
- font-size: 0.9rem;
- line-height: 1.15;
-}
-
-.learn-lesson-title span {
- color: rgba(247, 243, 234, 0.55);
- font-size: 0.78rem;
- line-height: 1.25;
-}
-
-.learn-lesson-top {
- margin-left: auto;
- padding-left: 0.9rem;
- border-left: 1px solid rgba(250, 248, 244, 0.1);
- color: rgba(247, 243, 234, 0.68);
- font-size: 0.82rem;
- font-weight: 650;
- text-decoration: none;
-}
-
-.learn-lesson-nav {
- display: flex;
- align-items: center;
- gap: 0.4rem;
- margin-left: auto;
- padding-left: 0.85rem;
- border-left: 1px solid rgba(250, 248, 244, 0.1);
-}
-
-.learn-lesson-nav a,
-.learn-lesson-nav span {
- display: inline-flex;
- align-items: center;
- min-height: 2rem;
- padding: 0 0.68rem;
- border-radius: 999px;
- color: rgba(247, 243, 234, 0.68);
- font-size: 0.78rem;
- font-weight: 700;
- line-height: 1;
- text-decoration: none;
- white-space: nowrap;
-}
-
-.learn-lesson-nav a {
- border: 1px solid rgba(250, 248, 244, 0.1);
- background: rgba(250, 248, 244, 0.045);
-}
-
-.learn-lesson-nav a:hover,
-.learn-lesson-nav a:focus-visible {
- border-color: rgba(246, 201, 69, 0.28);
- color: var(--color-fg);
- background: rgba(250, 248, 244, 0.08);
-}
-
-.learn-lesson-nav span {
- color: rgba(247, 243, 234, 0.5);
-}
-
-.learn-lesson-heading {
- display: grid;
- grid-template-columns: auto minmax(0, 1fr);
- gap: 1.2rem;
- align-items: center;
- padding-bottom: 2rem;
- border-bottom: 1px solid rgba(250, 248, 244, 0.1);
-}
-
-.learn-lesson-number {
- display: grid;
- place-items: center;
- width: 68px;
- height: 68px;
- border-radius: 999px;
- background: rgba(246, 201, 69, 0.14);
- color: var(--color-gold);
- font-family: var(--font-mono);
- font-size: 2rem;
- font-weight: 800;
-}
-
-.learn-lesson-heading small {
- color: rgba(247, 243, 234, 0.56);
- font-weight: 700;
-}
-
-.learn-lesson-heading h1 {
- margin-top: 0.2rem;
- font-size: clamp(2.5rem, 5.5vw, 4rem);
- line-height: 1;
-}
-
-.learn-prose {
- max-width: none;
-}
-
-.learn-prose > * + * {
- margin-top: 1.2rem;
-}
-
-.learn-prose h2 {
- margin-top: 3.2rem;
- padding-top: 0.2rem;
- font-size: 1.7rem;
-}
-
-.learn-prose ul {
- margin: 1rem 0;
- padding-left: 1.25rem;
-}
-
-.learn-prose li {
- color: rgba(247, 243, 234, 0.7);
-}
-
-.learn-code {
- margin: 1rem 0 1.4rem;
- overflow: hidden;
- border: 1px solid rgba(250, 248, 244, 0.13);
- border-radius: 8px;
- background: rgba(6, 5, 7, 0.92);
-}
-
-.learn-code-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 1rem;
- min-height: 46px;
- padding: 0.55rem 0.68rem 0.55rem 0.9rem;
- border-bottom: 1px solid rgba(250, 248, 244, 0.1);
- background: rgba(250, 248, 244, 0.025);
-}
-
-.learn-code-title {
- display: inline-flex;
- align-items: center;
- gap: 0.55rem;
- min-width: 0;
- color: rgba(247, 243, 234, 0.68);
- font-size: 0.82rem;
- font-weight: 650;
-}
-
-.learn-code-title::before {
- content: "";
- flex: 0 0 auto;
- width: 16px;
- height: 16px;
- border: 1px solid rgba(250, 248, 244, 0.48);
- border-radius: 3px;
-}
-
-.learn-code[data-code-kind="terminal"] .learn-code-title::before {
- border: 0;
- border-radius: 0;
- background:
- linear-gradient(45deg, transparent 44%, rgba(250, 248, 244, 0.72) 46%, rgba(250, 248, 244, 0.72) 58%, transparent 60%) 0 2px / 8px 10px no-repeat,
- linear-gradient(rgba(250, 248, 244, 0.72), rgba(250, 248, 244, 0.72)) 10px 12px / 8px 1.5px no-repeat;
-}
-
-.learn-copy {
- position: relative;
- flex: 0 0 auto;
- width: 36px;
- height: 36px;
- border: 1px solid rgba(250, 248, 244, 0.28);
- border-radius: 6px;
- background: rgba(250, 248, 244, 0.08);
- color: rgba(250, 248, 244, 0.92);
- cursor: pointer;
- -webkit-appearance: none;
- appearance: none;
-}
-
-.learn-copy::before,
-.learn-copy::after {
- content: "";
- position: absolute;
- width: 11px;
- height: 13px;
- border: 2px solid currentColor;
- border-radius: 2px;
-}
-
-.learn-copy::before {
- left: 10px;
- top: 8px;
- opacity: 0.72;
-}
-
-.learn-copy::after {
- left: 14px;
- top: 12px;
- background: rgba(6, 5, 7, 0.92);
-}
-
-.learn-copy:hover,
-.learn-copy:focus-visible {
- border-color: rgba(246, 201, 69, 0.62);
- background: rgba(250, 248, 244, 0.14);
- color: #fffdf9;
-}
-
-.learn-copy[data-copy-state="copied"] {
- border-color: rgba(62, 155, 101, 0.58);
- color: #8fd4aa;
-}
-
-.learn-copy[data-copy-state="failed"] {
- border-color: rgba(244, 122, 40, 0.58);
- color: #ffb17f;
-}
-
-.learn-code pre {
- padding: 1rem 1.1rem;
- border: 0;
- border-radius: 0;
- background: transparent;
- box-shadow: none;
-}
-
-.learn-code pre code {
- display: block;
- min-width: max-content;
- font-size: 0.9rem;
- line-height: 1.6;
-}
-
-.learn-callout {
- padding: 1rem 1.1rem;
- border: 1px solid rgba(246, 201, 69, 0.24);
- border-radius: 8px;
- background: rgba(246, 201, 69, 0.07);
-}
-
-.learn-complete {
- margin-top: 3rem;
- padding: 2rem;
- border: 1px solid rgba(250, 248, 244, 0.12);
- border-radius: 8px;
- background: rgba(250, 248, 244, 0.04);
- text-align: center;
-}
-
-.learn-complete h2 {
- margin-top: 0;
-}
-
-.learn-complete .button {
- margin-top: 1.25rem;
-}
-
-.docs-page .section {
- padding: 2.2rem 0 4.5rem;
-}
-
-.docs-page .docs-layout {
- gap: 2.75rem;
-}
-
-.docs-page .docs-layout[data-has-toc="true"] {
- grid-template-columns: 300px minmax(0, 900px) 260px;
-}
-
-.docs-page .docs-sidebar,
-.docs-page .docs-toc {
- border-color: rgba(250, 248, 244, 0.12);
- background: rgba(250, 248, 244, 0.035);
- box-shadow: none;
-}
-
-.docs-page .docs-sidebar {
- border-radius: 0;
- border-width: 0 1px 0 0;
- padding-right: 1.2rem;
-}
-
-.docs-page .docs-toc {
- border-radius: 0;
- border-width: 0 0 0 1px;
- padding-left: 1.2rem;
-}
-
-.docs-page .docs-sidebar-header {
- border-bottom-color: rgba(250, 248, 244, 0.1);
-}
-
-.docs-page .docs-sidebar-title,
-.docs-page .docs-toc-title,
-.docs-page .docs-search-result-title,
-.docs-page .docs-prevnext-title,
-.docs-page .docs-content h1,
-.docs-page .docs-content h2,
-.docs-page .docs-content h3,
-.docs-page .docs-content h4 {
- color: var(--color-fg);
-}
-
-.docs-page .docs-search {
- border-color: rgba(250, 248, 244, 0.14);
- background: rgba(250, 248, 244, 0.055);
- color: var(--color-fg);
-}
-
-.docs-page .docs-search::placeholder {
- color: rgba(247, 243, 234, 0.44);
-}
-
-.docs-page .docs-nav-section,
-.docs-page .docs-search-result-meta,
-.docs-page .docs-breadcrumb,
-.docs-page .docs-prevnext-label,
-.docs-page .spec-toc-title {
- color: rgba(247, 243, 234, 0.48);
-}
-
-.docs-page .docs-nav a,
-.docs-page .docs-toc-list a,
-.docs-page .spec-toc a {
- color: rgba(247, 243, 234, 0.62);
-}
-
-.docs-page .docs-nav a:hover,
-.docs-page .docs-nav a:focus-visible,
-.docs-page .docs-nav a[data-active="true"],
-.docs-page .docs-toc-list a:hover,
-.docs-page .docs-toc-list a:focus-visible,
-.docs-page .spec-toc a:hover,
-.docs-page .spec-toc a:focus-visible,
-.docs-page .spec-toc a[data-active="true"] {
- color: var(--color-fg);
- background: rgba(250, 248, 244, 0.07);
-}
-
-.docs-page .docs-content .prose,
-.docs-page .spec-prose {
- max-width: 92ch;
-}
-
-.docs-page .docs-content .prose > h1:first-child,
-.docs-page .spec-prose > h1:first-child {
- font-size: 3.05rem;
- line-height: 1.05;
- letter-spacing: 0;
-}
-
-.docs-page .docs-content p,
-.docs-page .docs-content li,
-.docs-page .docs-content dd,
-.docs-page .spec-prose p,
-.docs-page .spec-prose li,
-.docs-page .spec-prose dd {
- color: rgba(247, 243, 234, 0.68);
-}
-
-.docs-layout .prose ul,
-.docs-layout .prose ol,
-.spec-prose ul,
-.spec-prose ol {
- display: grid;
- gap: 0.55rem;
- margin: 1.1rem 0;
- padding: 0;
- list-style: none;
-}
-
-.docs-layout .prose li,
-.spec-prose li {
- position: relative;
- margin: 0;
- padding: 0.72rem 0.86rem 0.72rem 1rem;
- border: 1px solid rgba(31, 31, 31, 0.1);
- border-left: 2px solid rgba(246, 201, 69, 0.44);
- border-radius: 8px;
- background: rgba(255, 255, 255, 0.6);
-}
-
-.docs-layout .prose li + li,
-.spec-prose li + li {
- margin-top: 0;
-}
-
-.docs-layout .prose ol,
-.spec-prose ol {
- counter-reset: docs-ordered-list;
-}
-
-.docs-layout .prose ol > li,
-.spec-prose ol > li {
- counter-increment: docs-ordered-list;
- padding-left: 2.75rem;
-}
-
-.docs-layout .prose ol > li::before,
-.spec-prose ol > li::before {
- content: counter(docs-ordered-list);
- position: absolute;
- left: 0.78rem;
- top: 0.72rem;
- display: inline-grid;
- place-items: center;
- min-width: 1.32rem;
- height: 1.32rem;
- padding: 0 0.18rem;
- border: 1px solid rgba(246, 201, 69, 0.38);
- border-radius: 999px;
- background: rgba(246, 201, 69, 0.12);
- color: var(--color-gold);
- font-family: var(--font-mono);
- font-size: 0.7rem;
- font-weight: 800;
- line-height: 1;
-}
-
-.docs-layout .prose li > p:first-child,
-.spec-prose li > p:first-child {
- margin-top: 0;
-}
-
-.docs-layout .prose li > p:last-child,
-.spec-prose li > p:last-child {
- margin-bottom: 0;
-}
-
-.docs-layout .prose li > ul,
-.docs-layout .prose li > ol,
-.spec-prose li > ul,
-.spec-prose li > ol {
- gap: 0.42rem;
- margin: 0.64rem 0 0;
-}
-
-.docs-layout .prose li > ul > li,
-.docs-layout .prose li > ol > li,
-.spec-prose li > ul > li,
-.spec-prose li > ol > li {
- padding-top: 0.56rem;
- padding-bottom: 0.56rem;
- background: rgba(31, 31, 31, 0.035);
-}
-
-.site-dark .docs-layout .prose li,
-.site-dark .spec-prose li,
-.docs-page .docs-layout .prose li,
-.docs-page .spec-prose li {
- border-color: rgba(250, 248, 244, 0.1);
- border-left-color: rgba(246, 201, 69, 0.46);
- background: rgba(250, 248, 244, 0.045);
-}
-
-.site-dark .docs-layout .prose li > ul > li,
-.site-dark .docs-layout .prose li > ol > li,
-.site-dark .spec-prose li > ul > li,
-.site-dark .spec-prose li > ol > li,
-.docs-page .docs-layout .prose li > ul > li,
-.docs-page .docs-layout .prose li > ol > li,
-.docs-page .spec-prose li > ul > li,
-.docs-page .spec-prose li > ol > li {
- background: rgba(250, 248, 244, 0.035);
-}
-
-.docs-page .docs-inline-ref {
- color: var(--color-fg);
- text-decoration-color: rgba(246, 201, 69, 0.35);
-}
-
-.docs-page .docs-inline-code,
-.docs-page .prose code {
- background: rgba(250, 248, 244, 0.08);
- color: rgba(247, 243, 234, 0.92);
-}
-
-.docs-page .prose pre {
- padding: 0;
-}
-
-.docs-page .prose pre code,
-.docs-page .prose pre code.hljs {
- display: block;
- padding: 1rem;
- background: transparent;
-}
-
-.docs-page .prose h1 code,
-.docs-page .prose h2 code,
-.docs-page .prose h3 code,
-.docs-page .prose h4 code,
-.docs-page .docs-nav code,
-.docs-page .docs-breadcrumb code,
-.docs-page .docs-search-result-title code,
-.docs-page .docs-prevnext-title code {
- background: transparent;
- color: var(--color-gold);
-}
-
-.docs-page .prose table {
- border-color: rgba(250, 248, 244, 0.12);
- background: rgba(250, 248, 244, 0.035);
-}
-
-.docs-page .prose th,
-.docs-page .prose td {
- border-color: rgba(250, 248, 244, 0.1);
-}
-
-.docs-page .prose th {
- background: rgba(246, 201, 69, 0.08);
-}
-
-.docs-page .prose tbody tr:nth-child(even) td {
- background: rgba(250, 248, 244, 0.025);
-}
-
-.docs-page .prose blockquote {
- border-left-color: rgba(246, 201, 69, 0.42);
-}
-
-.docs-page .prose details {
- border-color: rgba(250, 248, 244, 0.12);
- background: rgba(250, 248, 244, 0.045);
-}
-
-.docs-page .prose summary:hover,
-.docs-page .prose summary:focus-visible {
- background: rgba(250, 248, 244, 0.07);
-}
-
-.docs-page .docs-prevnext {
- border-top-color: rgba(250, 248, 244, 0.1);
-}
-
-.docs-page .docs-prevnext-link {
- border-color: rgba(250, 248, 244, 0.12);
- background: rgba(250, 248, 244, 0.045);
-}
-
-.docs-page .docs-prevnext-link:hover,
-.docs-page .docs-prevnext-link:focus-visible {
- background: rgba(250, 248, 244, 0.08);
-}
-
-@media (max-width: 980px) {
- .learn-chapter-grid {
- grid-template-columns: repeat(2, minmax(0, 1fr));
- }
-}
-
-@media (max-width: 1180px) {
- .tech-hero-copy h1,
- .docs-hub-copy h1,
- .coming-soon-copy h1,
- .product-page .hero h1 {
- font-size: 3.7rem;
- }
-
- .tech-system-map {
- opacity: 0.26;
- width: 38vw;
- }
-
- .technology-layout,
- .technology-layout-reverse,
- .docs-hub-tech,
- .blog-placeholder {
- grid-template-columns: 1fr;
- }
-
- .technology-layout-reverse .technology-copy {
- order: 0;
- }
-
- .docs-link-groups,
- .blog-lanes {
- grid-template-columns: repeat(2, minmax(0, 1fr));
- }
-
- .docs-page .docs-layout,
- .docs-page .docs-layout[data-has-toc="true"] {
- grid-template-columns: 300px minmax(0, 1fr);
- }
-
- .docs-page .docs-toc {
- display: none;
- }
-
- .docs-landing-shell {
- grid-template-columns: 250px minmax(0, 1fr);
- gap: 2rem;
- }
-
- .docs-landing-toc {
- display: none;
- }
-}
-
-@media (max-width: 820px) {
- .tech-hero,
- .docs-hub-hero,
- .coming-soon {
- min-height: auto;
- }
-
- .tech-hero-copy,
- .docs-hub-copy,
- .coming-soon-copy {
- padding: 4.25rem 0 3rem;
- }
-
- .tech-hero-copy h1,
- .docs-hub-copy h1,
- .coming-soon-copy h1,
- .product-page .hero h1,
- .docs-page .docs-content .prose > h1:first-child,
- .docs-page .spec-prose > h1:first-child {
- font-size: 2.75rem;
- line-height: 1.04;
- }
-
- .tech-system-map {
- display: none;
- }
-
- .technology-section,
- .docs-hub-section {
- padding: 3.5rem 0;
- }
-
- .technology-copy h2,
- .docs-hub-tech-copy h2 {
- font-size: 2.25rem;
- }
-
- .technology-feature-list,
- .docs-link-groups,
- .blog-lanes,
- .docs-flow {
- grid-template-columns: 1fr;
- }
-
- .docs-flow span {
- width: 1px;
- height: 28px;
- justify-self: center;
- }
-
- .docs-page .docs-sidebar,
- .docs-page .docs-toc {
- border-width: 1px;
- border-radius: 8px;
- }
-
- .docs-page .docs-layout,
- .docs-page .docs-layout[data-has-toc="true"] {
- grid-template-columns: minmax(0, 1fr);
- }
-
- .docs-landing-shell {
- display: block;
- padding: 2rem var(--gutter) 3.5rem;
- }
-
- .docs-landing-sidebar {
- display: none;
- }
-
- .docs-landing-content h1 {
- font-size: 2.65rem;
- }
-
- .docs-landing-content h2 {
- font-size: 2rem;
- }
-
- .docs-quick-select,
- .docs-link-grid {
- grid-template-columns: minmax(0, 1fr);
- }
-
- .docs-tech-heading {
- display: block;
- }
-
- .docs-section-action {
- margin-top: 1rem;
- }
-
- .learn-hero-art {
- margin-inline: calc(var(--gutter) * -1);
- }
-
- .learn-section-heading {
- display: block;
- }
-
- .learn-section-heading p {
- margin-top: 0.45rem;
- }
-
- .learn-chapter-grid,
- .learn-info-grid {
- grid-template-columns: minmax(0, 1fr);
- }
-
- .learn-lesson-bar {
- position: static;
- flex-wrap: wrap;
- border-radius: 8px;
- }
-
- .learn-lesson-title {
- flex: 1 1 0;
- }
-
- .learn-lesson-nav {
- flex: 1 0 100%;
- margin-left: 0;
- padding: 0.55rem 0 0;
- border-top: 1px solid rgba(250, 248, 244, 0.1);
- border-left: 0;
- }
-
- .learn-lesson-top {
- display: none;
- }
-
- .learn-lesson-heading {
- grid-template-columns: minmax(0, 1fr);
- }
-}
-
-@media (max-width: 520px) {
- .tech-hero-copy h1,
- .docs-hub-copy h1,
- .coming-soon-copy h1,
- .product-page .hero h1 {
- font-size: 2.35rem;
- }
-
- .technology-feature-list li,
- .technology-feature-list span,
- .blog-placeholder,
- .docs-link-group {
- padding: 0.9rem;
- }
-
- .runtime-command {
- grid-template-columns: minmax(0, 1fr);
- }
-
- .runtime-code-tabs pre {
- max-height: 300px;
- }
-
- .docs-sidebar-nav {
- grid-template-columns: minmax(0, 1fr);
- }
-
- .docs-landing-content h1 {
- font-size: 2.25rem;
- }
-
- .docs-landing-content h2 {
- font-size: 1.8rem;
- }
-
- .learn-page .hero {
- padding-top: 3rem;
- }
-
- .learn-course-panel {
- width: calc(100% - 2rem);
- padding: 1.1rem;
- }
-
- .learn-current-row {
- grid-template-columns: minmax(0, 1fr);
- border-radius: 8px;
- }
-
- .learn-current-dot {
- display: none;
- }
-
- .learn-chapter {
- grid-template-columns: minmax(0, 1fr);
- }
-
- .learn-lesson-number {
- width: 56px;
- height: 56px;
- font-size: 1.55rem;
- }
-
- .learn-code pre code {
- font-size: 0.82rem;
- }
-}
-
-/* Oro editorial homepage */
-.editorial-layout {
- --editorial-rail: 220px;
- --editorial-column-gap: 4rem;
- --editorial-page-max: 1440px;
- min-height: 100vh;
- display: grid;
- grid-template-columns: var(--editorial-rail) minmax(0, 1fr);
- grid-template-rows: minmax(0, 1fr) auto;
- column-gap: var(--editorial-column-gap);
- max-width: var(--editorial-page-max);
- margin: 0 auto;
- padding: 0 2rem;
- background: linear-gradient(180deg, #fffdf9 0%, var(--color-bg) 52%, #f5eee1 100%);
- color: var(--color-fg);
-}
-
-.editorial-layout::before {
- content: none;
-}
-
-.editorial-layout .site-top {
- grid-column: 1;
- grid-row: 1;
- position: sticky;
- top: 0;
- align-self: start;
- height: fit-content;
- padding-top: 6rem;
- background: transparent;
- border-bottom: 0;
- box-shadow: none;
- backdrop-filter: none;
-}
-
-.editorial-layout .site-top .container,
-.editorial-layout .site-top .container-wide {
- max-width: none;
- padding: 0;
-}
-
-.editorial-layout .site-header {
- flex-direction: column;
- align-items: flex-start;
- justify-content: flex-start;
- gap: 4rem;
- min-height: 0;
-}
-
-.editorial-layout .brand {
- color: var(--color-fg);
-}
-
-.editorial-layout .brand-mark {
- width: 30px;
- height: 30px;
- border-color: rgba(31, 31, 31, 0.14);
- background: rgba(255, 255, 255, 0.72);
-}
-
-.editorial-layout .brand-name {
- font-size: 1.48rem;
-}
-
-.editorial-layout .nav {
- flex-direction: column;
- align-items: flex-start;
- gap: 0.4rem;
- margin-left: 0;
- color: rgba(31, 31, 31, 0.55);
- font-size: 0.88rem;
- line-height: 1.2;
-}
-
-.editorial-layout .nav a {
- padding: 0.22rem 0;
-}
-
-.editorial-layout .nav a::after {
- display: none;
-}
-
-.editorial-layout .nav a:hover,
-.editorial-layout .nav a:focus-visible {
- color: var(--color-fg);
-}
-
-.editorial-main {
- grid-column: 2;
- grid-row: 1;
- min-width: 0;
- padding: 6rem 0 0;
-}
-
-.editorial-layout .section-kicker {
- margin: 0;
- color: var(--color-purple);
- font-size: 0.78rem;
- font-weight: 760;
- letter-spacing: 0.08em;
-}
-
-.editorial-hero {
- max-width: 760px;
- padding-bottom: 5rem;
-}
-
-.editorial-hero h1 {
- max-width: 760px;
- margin: 0;
- font-family: var(--font-display);
- font-size: 5.6rem;
- font-weight: 820;
- line-height: 0.95;
- letter-spacing: 0;
-}
-
-.editorial-hero p:not(.section-kicker) {
- max-width: 650px;
- margin: 1.35rem 0 0;
- color: var(--color-muted);
- font-size: 1.1rem;
- line-height: 1.65;
-}
-
-.editorial-actions {
- display: flex;
- flex-wrap: wrap;
- gap: 0.75rem;
- margin-top: 1.8rem;
-}
-
-.editorial-layout .button {
- min-height: 44px;
- border-radius: var(--radius);
- font-size: 0.94rem;
-}
-
-.editorial-layout .button-primary {
- border-color: rgba(31, 31, 31, 0.9);
- background: var(--color-fg);
- color: var(--color-bg);
- box-shadow: 0 14px 32px rgba(31, 31, 31, 0.14);
+.docs-page .prose h1 code,
+.docs-page .prose h2 code,
+.docs-page .prose h3 code,
+.docs-page .prose h4 code,
+.docs-page .docs-nav code,
+.docs-page .docs-breadcrumb code,
+.docs-page .docs-search-result-title code,
+.docs-page .docs-prevnext-title code {
+ background: transparent;
+ color: var(--color-gold);
}
-.editorial-layout .button-primary:hover,
-.editorial-layout .button-primary:focus-visible {
- border-color: rgba(31, 31, 31, 0.9);
- background: #000;
- color: var(--color-bg);
+.docs-page .prose table {
+ border-color: rgba(250, 248, 244, 0.12);
+ background: rgba(250, 248, 244, 0.035);
}
-.editorial-layout .button-quiet {
- border-color: rgba(31, 31, 31, 0.14);
- background: rgba(255, 255, 255, 0.54);
- color: var(--color-fg);
+.docs-page .prose th,
+.docs-page .prose td {
+ border-color: rgba(250, 248, 244, 0.1);
}
-.editorial-layout .button-quiet:hover,
-.editorial-layout .button-quiet:focus-visible {
- border-color: rgba(31, 31, 31, 0.24);
- background: rgba(255, 255, 255, 0.84);
+.docs-page .prose th {
+ background: rgba(246, 201, 69, 0.08);
}
-.editorial-products {
- padding: 0 0 4.5rem;
+.docs-page .prose tbody tr:nth-child(even) td {
+ background: rgba(250, 248, 244, 0.025);
}
-.editorial-section-heading {
- margin-bottom: 1.4rem;
+.docs-page .prose blockquote {
+ border-left-color: rgba(246, 201, 69, 0.42);
}
-.editorial-section-heading h2 {
- margin: 0;
- font-family: var(--font-display);
- font-size: 2.65rem;
- font-weight: 740;
- line-height: 1.08;
- letter-spacing: 0;
+.docs-page .prose details {
+ border-color: rgba(250, 248, 244, 0.12);
+ background: rgba(250, 248, 244, 0.045);
}
-.editorial-product {
- display: grid;
- grid-template-columns: 64px minmax(0, 760px);
- gap: 2rem;
- align-items: start;
- padding: 1.8rem 0;
- border-top: 1px solid rgba(31, 31, 31, 0.12);
+.docs-page .prose summary:hover,
+.docs-page .prose summary:focus-visible {
+ background: rgba(250, 248, 244, 0.07);
}
-.editorial-product:last-child {
- border-bottom: 1px solid rgba(31, 31, 31, 0.12);
+.docs-page .docs-prevnext {
+ border-top-color: rgba(250, 248, 244, 0.1);
}
-.editorial-product-index {
- color: rgba(91, 59, 162, 0.84);
- font-family: var(--font-mono);
- font-size: 0.84rem;
- font-weight: 720;
- line-height: 1.9;
+.docs-page .docs-prevnext-link {
+ border-color: rgba(250, 248, 244, 0.12);
+ background: rgba(250, 248, 244, 0.045);
}
-.editorial-product h3 {
- margin: 0;
- font-family: var(--font-display);
- font-size: 1.55rem;
- font-weight: 740;
- line-height: 1.15;
+.docs-page .docs-prevnext-link:hover,
+.docs-page .docs-prevnext-link:focus-visible {
+ background: rgba(250, 248, 244, 0.08);
}
-.editorial-product p {
- max-width: 560px;
- margin: 0.55rem 0 0;
- color: var(--color-muted);
- line-height: 1.62;
-}
+@media (max-width: 1180px) {
+ .tech-hero-copy h1,
+ .docs-hub-copy h1,
+ .coming-soon-copy h1,
+ .product-page .hero h1 {
+ font-size: 3.7rem;
+ }
-.editorial-product-links {
- display: flex;
- justify-content: flex-end;
- gap: 0.5rem;
- flex-wrap: wrap;
-}
+ .tech-system-map {
+ opacity: 0.26;
+ width: 38vw;
+ }
-.editorial-product-links a {
- display: inline-flex;
- align-items: center;
- min-height: 34px;
- padding: 0.42rem 0.68rem;
- border: 1px solid rgba(31, 31, 31, 0.12);
- border-radius: 999px;
- background: rgba(255, 255, 255, 0.46);
- color: rgba(31, 31, 31, 0.72);
- text-decoration: none;
- font-size: 0.82rem;
- font-weight: 680;
- line-height: 1.1;
-}
+ .technology-layout,
+ .technology-layout-reverse,
+ .docs-hub-tech,
+ .blog-placeholder {
+ grid-template-columns: 1fr;
+ }
-.editorial-product-links a:hover,
-.editorial-product-links a:focus-visible {
- border-color: rgba(91, 59, 162, 0.34);
- background: rgba(255, 255, 255, 0.78);
- color: var(--color-fg);
-}
+ .technology-layout-reverse .technology-copy {
+ order: 0;
+ }
-.editorial-footer {
- grid-column: 1 / -1;
- display: grid;
- grid-template-columns: var(--editorial-rail) minmax(0, 1fr) auto;
- gap: var(--editorial-column-gap);
- align-items: center;
- padding: 2.5rem 0 4rem;
- border-top: 1px solid rgba(31, 31, 31, 0.12);
- color: rgba(31, 31, 31, 0.58);
- font-size: 0.88rem;
- line-height: 1.35;
-}
+ .docs-link-groups,
+ .blog-lanes {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
-.editorial-footer-contact {
- display: grid;
- gap: 0.4rem;
-}
+ .docs-page .docs-layout,
+ .docs-page .docs-layout[data-has-toc="true"] {
+ grid-template-columns: 300px minmax(0, 1fr);
+ }
-.editorial-footer-contact span {
- color: var(--color-fg);
- font-weight: 720;
-}
+ .docs-page .docs-toc {
+ display: none;
+ }
-.editorial-footer a {
- color: rgba(31, 31, 31, 0.62);
- text-decoration: none;
-}
+ .docs-landing-shell {
+ grid-template-columns: 250px minmax(0, 1fr);
+ gap: 2rem;
+ }
-.editorial-footer a:hover,
-.editorial-footer a:focus-visible {
- color: var(--color-fg);
+ .docs-landing-toc {
+ display: none;
+ }
}
-.editorial-footer-links {
- display: flex;
- flex-wrap: wrap;
- gap: 0.95rem;
- align-items: center;
-}
+@media (max-width: 820px) {
+ .tech-hero,
+ .docs-hub-hero,
+ .coming-soon {
+ min-height: auto;
+ }
-.editorial-footer-links:last-child {
- justify-content: flex-end;
-}
+ .tech-hero-copy,
+ .docs-hub-copy,
+ .coming-soon-copy {
+ padding: 4.25rem 0 3rem;
+ }
-@media (max-width: 1080px) {
- .editorial-layout {
- --editorial-rail: 180px;
- --editorial-column-gap: 2.5rem;
+ .tech-hero-copy h1,
+ .docs-hub-copy h1,
+ .coming-soon-copy h1,
+ .product-page .hero h1,
+ .docs-page .docs-content .prose > h1:first-child,
+ .docs-page .spec-prose > h1:first-child {
+ font-size: 2.75rem;
+ line-height: 1.04;
}
- .editorial-hero h1 {
- font-size: 4.6rem;
+ .tech-system-map {
+ display: none;
}
- .editorial-product {
- grid-template-columns: 48px minmax(0, 1fr);
+ .technology-section,
+ .docs-hub-section {
+ padding: 3.5rem 0;
}
-}
-@media (max-width: 940px) {
- .editorial-layout {
- display: block;
- padding: 0;
+ .technology-copy h2,
+ .docs-hub-tech-copy h2 {
+ font-size: 2.25rem;
}
- .editorial-layout .site-top {
- position: sticky;
- top: 0;
- padding-top: 0;
- background: rgba(250, 248, 244, 0.92);
- border-bottom: 1px solid rgba(31, 31, 31, 0.08);
- backdrop-filter: blur(16px);
+ .technology-feature-list,
+ .docs-link-groups,
+ .blog-lanes,
+ .docs-flow {
+ grid-template-columns: 1fr;
}
- .editorial-layout .site-top .container,
- .editorial-layout .site-top .container-wide {
- max-width: none;
- padding: 0 var(--gutter);
+ .docs-flow span {
+ width: 1px;
+ height: 28px;
+ justify-self: center;
}
- .editorial-layout .site-header {
- min-height: 68px;
- flex-direction: row;
- align-items: center;
- gap: 1rem;
+ .docs-page .docs-sidebar,
+ .docs-page .docs-toc {
+ border-width: 1px;
+ border-radius: 8px;
}
- .editorial-layout .nav {
- flex: 1 1 100%;
- flex-direction: row;
- flex-wrap: nowrap;
- gap: 0.72rem;
- width: 100%;
- padding: 0 0 0.75rem;
- overflow-x: auto;
- font-size: 0.82rem;
- white-space: nowrap;
- scrollbar-width: none;
+ .docs-page .docs-layout,
+ .docs-page .docs-layout[data-has-toc="true"] {
+ grid-template-columns: minmax(0, 1fr);
}
- .editorial-layout .nav::-webkit-scrollbar {
- display: none;
+ .docs-landing-shell {
+ display: block;
+ padding: 2rem var(--gutter) 3.5rem;
}
- .editorial-main {
- padding: 3.75rem var(--gutter) 0;
+ .docs-landing-sidebar {
+ display: none;
}
- .editorial-hero {
- padding-bottom: 3.5rem;
+ .docs-landing-content h1 {
+ font-size: 2.65rem;
}
- .editorial-hero h1 {
- font-size: 3.8rem;
+ .docs-landing-content h2 {
+ font-size: 2rem;
}
- .editorial-section-heading,
- .editorial-product {
+ .docs-quick-select,
+ .docs-link-grid {
grid-template-columns: minmax(0, 1fr);
- gap: 0.8rem;
}
- .editorial-footer {
- display: grid;
- grid-template-columns: minmax(0, 1fr);
- gap: 1.35rem;
- padding: 2rem var(--gutter) 3rem;
+ .docs-tech-heading {
+ display: block;
}
- .editorial-footer-links:last-child {
- justify-content: flex-start;
+ .docs-section-action {
+ margin-top: 1rem;
}
}
-@media (max-width: 560px) {
- .editorial-main {
- padding-top: 3rem;
+@media (max-width: 520px) {
+ .tech-hero-copy h1,
+ .docs-hub-copy h1,
+ .coming-soon-copy h1,
+ .product-page .hero h1 {
+ font-size: 2.35rem;
}
- .editorial-hero h1 {
- font-size: 3rem;
- line-height: 1.02;
+ .technology-feature-list li,
+ .technology-feature-list span,
+ .blog-placeholder,
+ .docs-link-group {
+ padding: 0.9rem;
}
- .editorial-hero p:not(.section-kicker) {
- font-size: 1rem;
+ .runtime-command {
+ grid-template-columns: minmax(0, 1fr);
}
- .editorial-actions .button {
- width: 100%;
+ .runtime-code-tabs pre {
+ max-height: 300px;
+ }
+
+ .docs-sidebar-nav {
+ grid-template-columns: minmax(0, 1fr);
}
- .editorial-section-heading h2 {
- font-size: 2.1rem;
+ .docs-landing-content h1 {
+ font-size: 2.25rem;
}
- .editorial-product {
- padding: 1.45rem 0;
+ .docs-landing-content h2 {
+ font-size: 1.8rem;
}
}
@@ -5155,7 +3342,6 @@ body:not(.editorial-layout) .hero-grid {
}
body:not(.editorial-layout) .hero-copy,
-body:not(.editorial-layout) .learn-hero,
body:not(.editorial-layout) .coming-soon-copy {
max-width: 840px;
padding: 0;
@@ -5189,7 +3375,6 @@ body:not(.editorial-layout) .docs-landing-content h1 {
}
body:not(.editorial-layout) .hero-copy p,
-body:not(.editorial-layout) .learn-hero p,
body:not(.editorial-layout) .docs-landing-intro > p:not(.docs-landing-kicker),
body:not(.editorial-layout) .docs-tech-summary {
max-width: 680px;
@@ -5255,13 +3440,7 @@ body:not(.editorial-layout) .docs-sidebar,
body:not(.editorial-layout) .docs-toc,
body:not(.editorial-layout) .docs-prevnext-link,
body:not(.editorial-layout) .docs-quick-select a,
-body:not(.editorial-layout) .docs-link-block,
-body:not(.editorial-layout) .learn-course-panel,
-body:not(.editorial-layout) .learn-info,
-body:not(.editorial-layout) .learn-current-row,
-body:not(.editorial-layout) .learn-lesson-bar,
-body:not(.editorial-layout) .learn-code,
-body:not(.editorial-layout) .learn-callout {
+body:not(.editorial-layout) .docs-link-block {
border-color: rgba(31, 31, 31, 0.12);
background: rgba(255, 255, 255, 0.5);
box-shadow: none;
@@ -5441,10 +3620,6 @@ body:not(.editorial-layout).docs-page .spec-prose > h1:first-child {
color: var(--color-fg);
}
-body:not(.editorial-layout) .learn-hero-art {
- display: none;
-}
-
body:not(.editorial-layout) .button,
body:not(.editorial-layout) .site-subnav-parent,
body:not(.editorial-layout) .site-subnav-links a,
@@ -5458,13 +3633,7 @@ body:not(.editorial-layout) .docs-search-result,
body:not(.editorial-layout) .docs-prevnext-link,
body:not(.editorial-layout) .docs-quick-select a,
body:not(.editorial-layout) .docs-link-block a,
-body:not(.editorial-layout) .docs-section-action,
-body:not(.editorial-layout) .learn-course-panel,
-body:not(.editorial-layout) .learn-info,
-body:not(.editorial-layout) .learn-current-row,
-body:not(.editorial-layout) .learn-lesson-bar,
-body:not(.editorial-layout) .learn-code,
-body:not(.editorial-layout) .learn-callout {
+body:not(.editorial-layout) .docs-section-action {
border-radius: var(--radius);
}
@@ -5486,14 +3655,12 @@ body:not(.editorial-layout) pre {
body:not(.editorial-layout) pre code,
body:not(.editorial-layout) pre code.hljs,
body:not(.editorial-layout).docs-page .prose pre code,
-body:not(.editorial-layout).docs-page .prose pre code.hljs,
-body:not(.editorial-layout) .learn-code pre code {
+body:not(.editorial-layout).docs-page .prose pre code.hljs {
background: transparent;
color: inherit;
}
-body:not(.editorial-layout).docs-page .prose pre,
-body:not(.editorial-layout) .learn-code pre {
+body:not(.editorial-layout).docs-page .prose pre {
border-radius: var(--radius);
background: linear-gradient(180deg, #171513 0%, #241d31 100%);
color: rgba(250, 248, 244, 0.94);
@@ -5573,16 +3740,6 @@ body:not(.editorial-layout) .docs-tech-heading h2,
body:not(.editorial-layout) .docs-quick-select span,
body:not(.editorial-layout) .docs-link-block h3,
body:not(.editorial-layout) .docs-link-block span,
-body:not(.editorial-layout) .learn-section-heading h2,
-body:not(.editorial-layout) .learn-course-panel h2,
-body:not(.editorial-layout) .learn-current-row strong,
-body:not(.editorial-layout) .learn-chapter strong,
-body:not(.editorial-layout) .learn-info h3,
-body:not(.editorial-layout) .learn-lesson-heading h1,
-body:not(.editorial-layout) .learn-lesson-title strong,
-body:not(.editorial-layout) .learn-prose h2,
-body:not(.editorial-layout) .learn-prose h3,
-body:not(.editorial-layout) .learn-complete h2,
body:not(.editorial-layout).docs-page .docs-sidebar-title,
body:not(.editorial-layout).docs-page .docs-toc-title,
body:not(.editorial-layout).docs-page .docs-search-result-title,
@@ -5609,19 +3766,6 @@ body:not(.editorial-layout) .docs-landing-content p,
body:not(.editorial-layout) .docs-tech-summary,
body:not(.editorial-layout) .docs-quick-select small,
body:not(.editorial-layout) .docs-link-block small,
-body:not(.editorial-layout) .learn-hero .lede,
-body:not(.editorial-layout) .learn-section-heading p,
-body:not(.editorial-layout) .learn-course-panel p,
-body:not(.editorial-layout) .learn-current-row span,
-body:not(.editorial-layout) .learn-chapter > span:not(.learn-chapter-number) > span,
-body:not(.editorial-layout) .learn-info p,
-body:not(.editorial-layout) .learn-lesson-title span,
-body:not(.editorial-layout) .learn-lesson-heading small,
-body:not(.editorial-layout) .learn-prose p,
-body:not(.editorial-layout) .learn-prose li,
-body:not(.editorial-layout) .learn-callout,
-body:not(.editorial-layout) .learn-callout p,
-body:not(.editorial-layout) .learn-complete p,
body:not(.editorial-layout).docs-page .docs-content p,
body:not(.editorial-layout).docs-page .docs-content li,
body:not(.editorial-layout).docs-page .docs-content dd,
@@ -5642,10 +3786,7 @@ body:not(.editorial-layout).docs-page .spec-toc-title {
body:not(.editorial-layout).docs-page .docs-nav a,
body:not(.editorial-layout).docs-page .docs-toc-list a,
-body:not(.editorial-layout).docs-page .spec-toc a,
-body:not(.editorial-layout) .learn-lesson-top,
-body:not(.editorial-layout) .learn-lesson-nav a,
-body:not(.editorial-layout) .learn-lesson-nav span {
+body:not(.editorial-layout).docs-page .spec-toc a {
color: rgba(31, 31, 31, 0.64);
}
@@ -5656,11 +3797,7 @@ body:not(.editorial-layout).docs-page .docs-toc-list a:hover,
body:not(.editorial-layout).docs-page .docs-toc-list a:focus-visible,
body:not(.editorial-layout).docs-page .spec-toc a:hover,
body:not(.editorial-layout).docs-page .spec-toc a:focus-visible,
-body:not(.editorial-layout).docs-page .spec-toc a[data-active="true"],
-body:not(.editorial-layout) .learn-lesson-top:hover,
-body:not(.editorial-layout) .learn-lesson-top:focus-visible,
-body:not(.editorial-layout) .learn-lesson-nav a:hover,
-body:not(.editorial-layout) .learn-lesson-nav a:focus-visible {
+body:not(.editorial-layout).docs-page .spec-toc a[data-active="true"] {
background: rgba(31, 31, 31, 0.05);
color: var(--color-fg);
}
@@ -5674,10 +3811,6 @@ body:not(.editorial-layout).product-page .section,
body:not(.editorial-layout) .docs-landing-intro,
body:not(.editorial-layout) .docs-tech-section,
body:not(.editorial-layout) .docs-link-block,
-body:not(.editorial-layout) .learn-hero-art,
-body:not(.editorial-layout) .learn-lesson-heading,
-body:not(.editorial-layout) .learn-lesson-top,
-body:not(.editorial-layout) .learn-lesson-nav,
body:not(.editorial-layout).docs-page .docs-sidebar-header,
body:not(.editorial-layout).docs-page .docs-prevnext {
border-color: rgba(31, 31, 31, 0.1);
@@ -5691,14 +3824,6 @@ body:not(.editorial-layout) .docs-link-group a,
body:not(.editorial-layout) .blog-lanes article,
body:not(.editorial-layout) .blog-placeholder,
body:not(.editorial-layout) .docs-flow div,
-body:not(.editorial-layout) .learn-course-panel,
-body:not(.editorial-layout) .learn-current-row,
-body:not(.editorial-layout) .learn-chapter,
-body:not(.editorial-layout) .learn-info,
-body:not(.editorial-layout) .learn-lesson-bar,
-body:not(.editorial-layout) .learn-lesson-menu,
-body:not(.editorial-layout) .learn-lesson-nav a,
-body:not(.editorial-layout) .learn-complete,
body:not(.editorial-layout).docs-page .docs-search,
body:not(.editorial-layout).docs-page .docs-search-result,
body:not(.editorial-layout).docs-page .docs-prevnext-link,
@@ -5707,32 +3832,6 @@ body:not(.editorial-layout).docs-page .prose details {
background: rgba(255, 255, 255, 0.5);
}
-body:not(.editorial-layout) .learn-lesson-menu::before,
-body:not(.editorial-layout) .learn-lesson-menu::after {
- background: rgba(31, 31, 31, 0.72);
-}
-
-body:not(.editorial-layout) .learn-lesson-menu::before {
- box-shadow: 0 5px 0 rgba(31, 31, 31, 0.72);
-}
-
-body:not(.editorial-layout) .learn-lesson-title img {
- border-color: rgba(31, 31, 31, 0.12);
- background: rgba(255, 255, 255, 0.56);
-}
-
-body:not(.editorial-layout) .learn-code {
- border-color: rgba(246, 201, 69, 0.22);
- background: linear-gradient(180deg, #171513 0%, #241d31 100%);
- color: rgba(250, 248, 244, 0.94);
-}
-
-body:not(.editorial-layout) .learn-code-header {
- border-bottom-color: rgba(250, 248, 244, 0.1);
- background: rgba(250, 248, 244, 0.035);
-}
-
-body:not(.editorial-layout) .learn-code-title,
body:not(.editorial-layout) .learn-copy {
color: rgba(250, 248, 244, 0.72);
}
@@ -5791,38 +3890,38 @@ body:not(.editorial-layout).docs-page .prose tbody tr:nth-child(even) td {
background: rgba(31, 31, 31, 0.035);
}
-body:not(.editorial-layout) .nav .menu-panel {
+body:not(.editorial-layout) .site-header .menu-panel {
border-color: rgba(31, 31, 31, 0.12);
background: rgba(255, 255, 255, 0.96);
color: var(--color-fg);
box-shadow: 0 18px 42px rgba(31, 31, 31, 0.12);
}
-body:not(.editorial-layout) .nav .menu-item {
+body:not(.editorial-layout) .site-header .menu-item {
position: relative;
padding: 0.54rem 0.62rem;
border-radius: var(--radius);
color: var(--color-fg);
}
-body:not(.editorial-layout) .nav .menu-item::after {
+body:not(.editorial-layout) .site-header .menu-item::after {
content: none;
display: none;
}
-body:not(.editorial-layout) .nav .menu-item:hover,
-body:not(.editorial-layout) .nav .menu-item:focus-visible {
+body:not(.editorial-layout) .site-header .menu-item:hover,
+body:not(.editorial-layout) .site-header .menu-item:focus-visible {
border-color: rgba(31, 31, 31, 0.12);
background: rgba(31, 31, 31, 0.06);
color: var(--color-fg);
}
-body:not(.editorial-layout) .nav .menu-item small,
-body:not(.editorial-layout) .nav .menu-hint {
+body:not(.editorial-layout) .site-header .menu-item small,
+body:not(.editorial-layout) .site-header .menu-hint {
color: rgba(31, 31, 31, 0.58);
}
-body:not(.editorial-layout) .nav .menu-sep {
+body:not(.editorial-layout) .site-header .menu-sep {
background: rgba(31, 31, 31, 0.1);
}
@@ -5847,3 +3946,14 @@ body:not(.editorial-layout) .nav .menu-sep {
.markdown-alert-warning, .markdown-alert-caution { border-color: var(--color-orange); }
.docs-tab-anchor { display: block; scroll-margin-top: var(--site-top-offset, 144px); }
+
+/* The Ask AI panel is a sibling of the scrolling nav, anchored by its client. */
+.ask-ai-panel {
+ font-size: 0.82rem;
+ font-weight: 650;
+ right: auto;
+ min-width: min(224px, calc(100vw - 16px));
+ max-width: calc(100vw - 16px);
+ max-height: calc(100dvh - 160px);
+ overflow-y: auto;
+}
diff --git a/src/layouts/docs.layout.client.ts b/src/layouts/docs.layout.client.ts
index 04294aab..c0aa4770 100644
--- a/src/layouts/docs.layout.client.ts
+++ b/src/layouts/docs.layout.client.ts
@@ -1,7 +1,8 @@
+import { initAskAiMenu } from '../lib/ask-ai-client.ts'
import { legacyTarget } from '../lib/legacy.ts'
import { enhanceSidebar } from '../lib/sidebar-client.ts'
-import '../lib/learn-client.js'
-import { renderTabs } from '../lib/tabs.js'
+import { initLearn } from '../lib/learn-client.ts'
+import { renderTabs } from '../lib/tabs.ts'
import { enhanceArticle } from '../lib/article-client.ts'
const app = document.querySelector('[data-docs-app]')
if (app) {
@@ -20,10 +21,7 @@ if (app) {
}
const content = app.querySelector('[data-docs-content]')!
renderTabs(content)
- const oro = globalThis as typeof globalThis & {
- oroInitTabs?: (root: HTMLElement) => void
- }
- oro.oroInitTabs?.(content)
+ initLearn(content)
enhanceArticle(app)
enhanceSidebar(app)
const input = app.querySelector('[data-docs-search]')!
@@ -51,7 +49,13 @@ if (app) {
return r.json()
})
.then((data) => data.items)
- const docs = await index
+ const pending = index
+ // Evict failures even when the query was cleared while fetching. An older
+ // request must never evict a replacement cached by a later input event.
+ void pending.catch(() => {
+ if (index === pending) index = undefined
+ })
+ const docs = await pending
if (current !== version) return
const scored = docs
.map((d) => ({
@@ -88,7 +92,6 @@ if (app) {
results.textContent =
'Search is unavailable. Browse the navigation below.'
nav.hidden = false
- index = undefined
}
}
})
@@ -109,3 +112,5 @@ function revealHashTarget() {
}
revealHashTarget()
addEventListener('hashchange', revealHashTarget)
+
+initAskAiMenu()
diff --git a/src/layouts/docs.layout.ts b/src/layouts/docs.layout.ts
index 54d767b6..2865bec7 100644
--- a/src/layouts/docs.layout.ts
+++ b/src/layouts/docs.layout.ts
@@ -1,68 +1,15 @@
import { html, raw, render } from 'fragtml'
-import { load } from 'cheerio'
+import { article } from '../lib/rendering.ts'
import type { LayoutFunction } from '@domstack/static/types.js'
import {
collections,
sectionTitle,
- type Collection,
} from '../lib/collections.ts'
-import { rawUrl, type Doc, type DocsData } from '../lib/docs.ts'
+import { rawUrl, type DocLink, type Navigation, type DocVars } from '../lib/docs.ts'
export const parentLayout = 'root'
-export const vars = { dataDeps: ['docs'] }
-export function article(
- content: string,
- spec = false,
-): { body: string; toc: string } {
- const $ = load(content, {}, false)
- const headings: { id: string; title: string; level: string }[] = []
- $('h2,h3,h4,h5').each((_i, h) => {
- const id = $(h).attr('id') || ''
- const title = $(h).text()
- if (id) {
- headings.push({ id, title, level: h.tagName })
- $(h).append(
- render(
- html`#`,
- ),
- )
- }
- })
- return {
- body: $.html(),
- toc: render(
- spec
- ? html`Table of contents
-
- ${headings.map(
- (h) =>
- html`
${h.title}`,
- )}
-
`
- : html`On this page
-
- ${headings
- .filter((h) => ['h2', 'h3'].includes(h.level))
- .map(
- (h) =>
- html`-
- ${h.title}
-
`,
- )}
-
`,
- ),
- }
-}
-function prevNext(doc: Doc | undefined, label: string): string {
+// Asset-bearing parent: collection wrappers render the documentation shell.
+const docsLayout: LayoutFunction, string> = ({ children }) => children
+function prevNext(doc: DocLink | null, label: string): string {
return doc
? render(
html`,
- string,
- string,
- DocsData
-> = ({ vars: v, children, data }) => {
- const collection = v.docsCollection as Collection
- const c = collections[collection]
- const docs = data.docs[collection]
- const idx = docs.findIndex((d) => d.sourcePath === v.sourcePath)
- const doc = docs[idx]
- const sections = [...new Set(docs.map((d) => d.section))]
+export function renderDocs(v: DocVars, children: string, navigation: Navigation): string {
+ const c = collections[v.docsCollection]
+ const doc = navigation.bySource[v.sourcePath]
+ if (!doc) throw new Error(`Missing navigation document: ${v.docsCollection}/${v.sourcePath}`)
+ const sections = navigation.sections
const { body, toc } = article(children)
- const legacy =
- doc.id === 'start'
- ? JSON.stringify(Object.fromEntries(docs.map((d) => [d.id, d.url])))
- : ''
+ const legacy = doc.id === 'start' ? JSON.stringify(navigation.legacy) : ''
return render(
html`
@@ -116,11 +53,9 @@ const docsLayout: LayoutFunction<
- ${raw(prevNext(docs[idx - 1], 'Previous'))}
+ ${raw(prevNext(doc.previous, 'Previous'))}
- ${raw(prevNext(docs[idx + 1], 'Next'))}
+ ${raw(prevNext(doc.next, 'Next'))}
diff --git a/src/layouts/learn.layout.client.ts b/src/layouts/learn.layout.client.ts
index 31da9879..61abbd96 100644
--- a/src/layouts/learn.layout.client.ts
+++ b/src/layouts/learn.layout.client.ts
@@ -1 +1,3 @@
-import '../lib/learn-client.js'
+import { initLearn } from '../lib/learn-client.ts'
+
+initLearn()
diff --git a/src/layouts/learn.layout.css b/src/layouts/learn.layout.css
new file mode 100644
index 00000000..e60e1a7a
--- /dev/null
+++ b/src/layouts/learn.layout.css
@@ -0,0 +1,732 @@
+/* Learn layout rules, in their original cascade order. Shared tabs and copy controls remain global. */
+.learn-page .hero {
+ padding: clamp(4rem, 7vw, 6.5rem) 0 3rem;
+}
+
+.learn-hero {
+ position: relative;
+ z-index: 1;
+ max-width: 780px;
+ margin: 0 auto;
+ text-align: center;
+}
+
+.learn-hero h1 {
+ max-width: none;
+ margin-top: 1rem;
+ font-size: clamp(3.1rem, 7vw, 5.7rem);
+ line-height: 0.98;
+ letter-spacing: 0;
+}
+
+.learn-hero .lede {
+ max-width: 62ch;
+ margin: 1.15rem auto 0;
+}
+
+.learn-hero-actions {
+ justify-content: center;
+}
+
+.learn-hero-art {
+ position: relative;
+ display: grid;
+ place-items: center;
+ min-height: clamp(18rem, 31vw, 28rem);
+ margin-top: clamp(2rem, 5vw, 4rem);
+ overflow: hidden;
+ border-bottom: 1px solid rgba(250, 248, 244, 0.08);
+ background:
+ linear-gradient(rgba(250, 248, 244, 0.035) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(250, 248, 244, 0.035) 1px, transparent 1px),
+ radial-gradient(60% 80% at 50% 110%, rgba(62, 155, 101, 0.28), transparent 58%),
+ radial-gradient(80% 90% at 50% 115%, rgba(91, 59, 162, 0.18), transparent 64%);
+ background-size: 56px 56px, 56px 56px, auto, auto;
+}
+
+.learn-course-panel {
+ position: relative;
+ width: min(100%, 560px);
+ padding: 1.4rem;
+ border: 1px solid rgba(250, 248, 244, 0.12);
+ border-radius: 8px;
+ background: rgba(12, 11, 14, 0.86);
+ box-shadow: 0 28px 80px rgba(0, 0, 0, 0.36);
+}
+
+.learn-course-mark {
+ display: grid;
+ place-items: center;
+ width: 72px;
+ height: 72px;
+ margin: -3rem auto 1.1rem;
+ border: 1px solid rgba(250, 248, 244, 0.16);
+ border-radius: 8px;
+ background:
+ linear-gradient(135deg, rgba(250, 248, 244, 0.96), rgba(250, 248, 244, 0.78)),
+ linear-gradient(135deg, rgba(246, 201, 69, 0.3), rgba(91, 59, 162, 0.22));
+}
+
+.learn-course-mark img {
+ width: 42px;
+ height: 42px;
+}
+
+.learn-course-panel h2 {
+ text-align: center;
+}
+
+.learn-course-panel p {
+ max-width: 44ch;
+ margin: 0.55rem auto 0;
+ text-align: center;
+}
+
+.learn-current-row {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto;
+ gap: 1rem;
+ align-items: center;
+ margin: 1.4rem auto 0;
+ padding: 0.82rem 0.95rem;
+ max-width: 380px;
+ border: 1px solid rgba(250, 248, 244, 0.1);
+ border-radius: 999px;
+ background: rgba(250, 248, 244, 0.06);
+ text-align: left;
+}
+
+.learn-current-row strong {
+ display: block;
+ color: var(--color-fg);
+ line-height: 1.2;
+}
+
+.learn-current-row span {
+ display: block;
+ margin-top: 0.18rem;
+ color: rgba(247, 243, 234, 0.56);
+ font-size: 0.85rem;
+ line-height: 1.35;
+}
+
+.learn-current-dot {
+ width: 28px;
+ height: 28px;
+ border: 1px solid rgba(246, 201, 69, 0.34);
+ border-radius: 999px;
+ background: rgba(246, 201, 69, 0.12);
+}
+
+.learn-section-heading {
+ display: flex;
+ align-items: baseline;
+ justify-content: center;
+ gap: 1rem;
+ flex-wrap: wrap;
+ text-align: center;
+}
+
+.learn-section-heading p {
+ font-size: 1rem;
+}
+
+.learn-chapter-grid {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 0.8rem;
+}
+
+.learn-chapter {
+ display: grid;
+ grid-template-columns: auto minmax(0, 1fr);
+ gap: 0.78rem;
+ min-height: 112px;
+ padding: 1rem;
+ border: 1px solid rgba(250, 248, 244, 0.12);
+ border-radius: 8px;
+ background: rgba(250, 248, 244, 0.045);
+ color: inherit;
+ text-decoration: none;
+ transition: transform 140ms ease, background 140ms ease, border-color 140ms ease;
+}
+
+.learn-chapter:hover,
+.learn-chapter:focus-visible {
+ border-color: rgba(246, 201, 69, 0.36);
+ background: rgba(250, 248, 244, 0.07);
+ transform: translateY(-1px);
+}
+
+.learn-chapter-number {
+ display: inline-grid;
+ place-items: center;
+ width: 1.7rem;
+ height: 1.7rem;
+ margin-top: 0;
+ border-radius: 999px;
+ background: rgba(246, 201, 69, 0.16);
+ color: var(--color-gold);
+ font-family: var(--font-mono);
+ font-size: 0.78rem;
+ font-weight: 800;
+ line-height: 1;
+ text-align: center;
+}
+
+.learn-chapter:nth-child(3n + 2) .learn-chapter-number {
+ background: rgba(62, 155, 101, 0.16);
+ color: #8fd4aa;
+}
+
+.learn-chapter:nth-child(3n) .learn-chapter-number {
+ background: rgba(244, 122, 40, 0.16);
+ color: #ffb17f;
+}
+
+.learn-chapter strong {
+ display: block;
+ color: var(--color-fg);
+ line-height: 1.25;
+}
+
+.learn-chapter > span:not(.learn-chapter-number) {
+ display: block;
+ min-width: 0;
+}
+
+.learn-chapter > span:not(.learn-chapter-number) > span {
+ display: block;
+ margin-top: 0.38rem;
+ color: rgba(247, 243, 234, 0.62);
+ font-size: 0.92rem;
+ line-height: 1.45;
+}
+
+.learn-info-grid {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 1rem;
+}
+
+.learn-info {
+ padding: 1.15rem;
+ border: 1px solid rgba(250, 248, 244, 0.12);
+ border-radius: 8px;
+ background: rgba(250, 248, 244, 0.04);
+}
+
+.learn-info h3 {
+ font-size: 1.2rem;
+}
+
+.learn-info p {
+ margin-top: 0.45rem;
+}
+
+.learn-lesson-page .section {
+ padding-top: 2.2rem;
+}
+
+.learn-lesson-page {
+ --learn-lesson-bar-top: 126px;
+}
+
+.learn-lesson-shell {
+ max-width: 920px;
+ margin: 0 auto;
+}
+
+.learn-lesson-bar {
+ position: sticky;
+ top: var(--learn-lesson-bar-top);
+ z-index: 80;
+ display: flex;
+ gap: 0.75rem;
+ align-items: center;
+ margin-bottom: 2rem;
+ padding: 0.55rem 0.72rem;
+ border: 1px solid rgba(250, 248, 244, 0.12);
+ border-radius: 999px;
+ background: rgba(10, 9, 12, 0.88);
+ backdrop-filter: blur(16px);
+}
+
+.learn-lesson-menu {
+ width: 36px;
+ height: 36px;
+ border: 1px solid rgba(250, 248, 244, 0.1);
+ border-radius: 999px;
+ background: rgba(250, 248, 244, 0.045);
+ position: relative;
+}
+
+.learn-lesson-menu::before,
+.learn-lesson-menu::after {
+ content: "";
+ position: absolute;
+ left: 10px;
+ width: 14px;
+ height: 1px;
+ background: rgba(247, 243, 234, 0.72);
+}
+
+.learn-lesson-menu::before {
+ top: 13px;
+ box-shadow: 0 5px 0 rgba(247, 243, 234, 0.72);
+}
+
+.learn-lesson-menu::after {
+ top: 23px;
+}
+
+.learn-lesson-title {
+ display: flex;
+ align-items: center;
+ gap: 0.7rem;
+ min-width: 0;
+ flex: 0 1 auto;
+}
+
+.learn-lesson-title img {
+ width: 30px;
+ height: 30px;
+ border-radius: 8px;
+ border: 1px solid rgba(250, 248, 244, 0.12);
+ background: rgba(250, 248, 244, 0.08);
+}
+
+.learn-lesson-title strong,
+.learn-lesson-title span {
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.learn-lesson-title strong {
+ color: var(--color-fg);
+ font-size: 0.9rem;
+ line-height: 1.15;
+}
+
+.learn-lesson-title span {
+ color: rgba(247, 243, 234, 0.55);
+ font-size: 0.78rem;
+ line-height: 1.25;
+}
+
+.learn-lesson-top {
+ margin-left: auto;
+ padding-left: 0.9rem;
+ border-left: 1px solid rgba(250, 248, 244, 0.1);
+ color: rgba(247, 243, 234, 0.68);
+ font-size: 0.82rem;
+ font-weight: 650;
+ text-decoration: none;
+}
+
+.learn-lesson-nav {
+ display: flex;
+ align-items: center;
+ gap: 0.4rem;
+ margin-left: auto;
+ padding-left: 0.85rem;
+ border-left: 1px solid rgba(250, 248, 244, 0.1);
+}
+
+.learn-lesson-nav a,
+.learn-lesson-nav span {
+ display: inline-flex;
+ align-items: center;
+ min-height: 2rem;
+ padding: 0 0.68rem;
+ border-radius: 999px;
+ color: rgba(247, 243, 234, 0.68);
+ font-size: 0.78rem;
+ font-weight: 700;
+ line-height: 1;
+ text-decoration: none;
+ white-space: nowrap;
+}
+
+.learn-lesson-nav a {
+ border: 1px solid rgba(250, 248, 244, 0.1);
+ background: rgba(250, 248, 244, 0.045);
+}
+
+.learn-lesson-nav a:hover,
+.learn-lesson-nav a:focus-visible {
+ border-color: rgba(246, 201, 69, 0.28);
+ color: var(--color-fg);
+ background: rgba(250, 248, 244, 0.08);
+}
+
+.learn-lesson-nav span {
+ color: rgba(247, 243, 234, 0.5);
+}
+
+.learn-lesson-heading {
+ display: grid;
+ grid-template-columns: auto minmax(0, 1fr);
+ gap: 1.2rem;
+ align-items: center;
+ padding-bottom: 2rem;
+ border-bottom: 1px solid rgba(250, 248, 244, 0.1);
+}
+
+.learn-lesson-number {
+ display: grid;
+ place-items: center;
+ width: 68px;
+ height: 68px;
+ border-radius: 999px;
+ background: rgba(246, 201, 69, 0.14);
+ color: var(--color-gold);
+ font-family: var(--font-mono);
+ font-size: 2rem;
+ font-weight: 800;
+}
+
+.learn-lesson-heading small {
+ color: rgba(247, 243, 234, 0.56);
+ font-weight: 700;
+}
+
+.learn-lesson-heading h1 {
+ margin-top: 0.2rem;
+ font-size: clamp(2.5rem, 5.5vw, 4rem);
+ line-height: 1;
+}
+
+.learn-prose {
+ max-width: none;
+}
+
+.learn-prose > * + * {
+ margin-top: 1.2rem;
+}
+
+.learn-prose h2 {
+ margin-top: 3.2rem;
+ padding-top: 0.2rem;
+ font-size: 1.7rem;
+}
+
+.learn-prose ul {
+ margin: 1rem 0;
+ padding-left: 1.25rem;
+}
+
+.learn-prose li {
+ color: rgba(247, 243, 234, 0.7);
+}
+
+.learn-code {
+ margin: 1rem 0 1.4rem;
+ overflow: hidden;
+ border: 1px solid rgba(250, 248, 244, 0.13);
+ border-radius: 8px;
+ background: rgba(6, 5, 7, 0.92);
+}
+
+.learn-code-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1rem;
+ min-height: 46px;
+ padding: 0.55rem 0.68rem 0.55rem 0.9rem;
+ border-bottom: 1px solid rgba(250, 248, 244, 0.1);
+ background: rgba(250, 248, 244, 0.025);
+}
+
+.learn-code-title {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.55rem;
+ min-width: 0;
+ color: rgba(247, 243, 234, 0.68);
+ font-size: 0.82rem;
+ font-weight: 650;
+}
+
+.learn-code-title::before {
+ content: "";
+ flex: 0 0 auto;
+ width: 16px;
+ height: 16px;
+ border: 1px solid rgba(250, 248, 244, 0.48);
+ border-radius: 3px;
+}
+
+.learn-code[data-code-kind="terminal"] .learn-code-title::before {
+ border: 0;
+ border-radius: 0;
+ background:
+ linear-gradient(45deg, transparent 44%, rgba(250, 248, 244, 0.72) 46%, rgba(250, 248, 244, 0.72) 58%, transparent 60%) 0 2px / 8px 10px no-repeat,
+ linear-gradient(rgba(250, 248, 244, 0.72), rgba(250, 248, 244, 0.72)) 10px 12px / 8px 1.5px no-repeat;
+}
+
+.learn-code pre {
+ padding: 1rem 1.1rem;
+ border: 0;
+ border-radius: 0;
+ background: transparent;
+ box-shadow: none;
+}
+
+.learn-code pre code {
+ display: block;
+ min-width: max-content;
+ font-size: 0.9rem;
+ line-height: 1.6;
+}
+
+.learn-callout {
+ padding: 1rem 1.1rem;
+ border: 1px solid rgba(246, 201, 69, 0.24);
+ border-radius: 8px;
+ background: rgba(246, 201, 69, 0.07);
+}
+
+.learn-complete {
+ margin-top: 3rem;
+ padding: 2rem;
+ border: 1px solid rgba(250, 248, 244, 0.12);
+ border-radius: 8px;
+ background: rgba(250, 248, 244, 0.04);
+ text-align: center;
+}
+
+.learn-complete h2 {
+ margin-top: 0;
+}
+
+.learn-complete .button {
+ margin-top: 1.25rem;
+}
+
+@media (max-width: 980px) {
+ .learn-chapter-grid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }}
+
+@media (max-width: 820px) {
+
+ .learn-hero-art {
+ margin-inline: calc(var(--gutter) * -1);
+ }
+
+ .learn-section-heading {
+ display: block;
+ }
+
+ .learn-section-heading p {
+ margin-top: 0.45rem;
+ }
+
+ .learn-chapter-grid,
+ .learn-info-grid {
+ grid-template-columns: minmax(0, 1fr);
+ }
+
+ .learn-lesson-bar {
+ position: static;
+ flex-wrap: wrap;
+ border-radius: 8px;
+ }
+
+ .learn-lesson-title {
+ flex: 1 1 0;
+ }
+
+ .learn-lesson-nav {
+ flex: 1 0 100%;
+ margin-left: 0;
+ padding: 0.55rem 0 0;
+ border-top: 1px solid rgba(250, 248, 244, 0.1);
+ border-left: 0;
+ }
+
+ .learn-lesson-top {
+ display: none;
+ }
+
+ .learn-lesson-heading {
+ grid-template-columns: minmax(0, 1fr);
+ }}
+
+@media (max-width: 520px) {
+
+ .learn-page .hero {
+ padding-top: 3rem;
+ }
+
+ .learn-course-panel {
+ width: calc(100% - 2rem);
+ padding: 1.1rem;
+ }
+
+ .learn-current-row {
+ grid-template-columns: minmax(0, 1fr);
+ border-radius: 8px;
+ }
+
+ .learn-current-dot {
+ display: none;
+ }
+
+ .learn-chapter {
+ grid-template-columns: minmax(0, 1fr);
+ }
+
+ .learn-lesson-number {
+ width: 56px;
+ height: 56px;
+ font-size: 1.55rem;
+ }
+
+ .learn-code pre code {
+ font-size: 0.82rem;
+ }}
+
+body:not(.editorial-layout) .learn-hero {
+ max-width: 840px;
+ padding: 0;
+}
+
+body:not(.editorial-layout) .learn-hero p {
+ max-width: 680px;
+ margin-top: 1.25rem;
+ color: var(--color-muted);
+ font-size: 1.08rem;
+ line-height: 1.65;
+}
+
+body:not(.editorial-layout) .learn-course-panel,
+body:not(.editorial-layout) .learn-info,
+body:not(.editorial-layout) .learn-current-row,
+body:not(.editorial-layout) .learn-lesson-bar,
+body:not(.editorial-layout) .learn-code,
+body:not(.editorial-layout) .learn-callout {
+ border-color: rgba(31, 31, 31, 0.12);
+ background: rgba(255, 255, 255, 0.5);
+ box-shadow: none;
+}
+
+body:not(.editorial-layout) .learn-hero-art {
+ display: none;
+}
+
+body:not(.editorial-layout) .learn-course-panel,
+body:not(.editorial-layout) .learn-info,
+body:not(.editorial-layout) .learn-current-row,
+body:not(.editorial-layout) .learn-lesson-bar,
+body:not(.editorial-layout) .learn-code,
+body:not(.editorial-layout) .learn-callout {
+ border-radius: var(--radius);
+}
+
+body:not(.editorial-layout) .learn-code pre code {
+ background: transparent;
+ color: inherit;
+}
+
+body:not(.editorial-layout) .learn-code pre {
+ border-radius: var(--radius);
+ background: linear-gradient(180deg, #171513 0%, #241d31 100%);
+ color: rgba(250, 248, 244, 0.94);
+}
+
+/* Light-theme contrast cleanup for components inherited from the dark presentation. */
+body:not(.editorial-layout) .learn-section-heading h2,
+body:not(.editorial-layout) .learn-course-panel h2,
+body:not(.editorial-layout) .learn-current-row strong,
+body:not(.editorial-layout) .learn-chapter strong,
+body:not(.editorial-layout) .learn-info h3,
+body:not(.editorial-layout) .learn-lesson-heading h1,
+body:not(.editorial-layout) .learn-lesson-title strong,
+body:not(.editorial-layout) .learn-prose h2,
+body:not(.editorial-layout) .learn-prose h3,
+body:not(.editorial-layout) .learn-complete h2 {
+ color: var(--color-fg);
+}
+
+body:not(.editorial-layout) .learn-hero .lede,
+body:not(.editorial-layout) .learn-section-heading p,
+body:not(.editorial-layout) .learn-course-panel p,
+body:not(.editorial-layout) .learn-current-row span,
+body:not(.editorial-layout) .learn-chapter > span:not(.learn-chapter-number) > span,
+body:not(.editorial-layout) .learn-info p,
+body:not(.editorial-layout) .learn-lesson-title span,
+body:not(.editorial-layout) .learn-lesson-heading small,
+body:not(.editorial-layout) .learn-prose p,
+body:not(.editorial-layout) .learn-prose li,
+body:not(.editorial-layout) .learn-callout,
+body:not(.editorial-layout) .learn-callout p,
+body:not(.editorial-layout) .learn-complete p {
+ color: var(--color-muted);
+}
+
+body:not(.editorial-layout) .learn-lesson-top,
+body:not(.editorial-layout) .learn-lesson-nav a,
+body:not(.editorial-layout) .learn-lesson-nav span {
+ color: rgba(31, 31, 31, 0.64);
+}
+
+body:not(.editorial-layout) .learn-lesson-top:hover,
+body:not(.editorial-layout) .learn-lesson-top:focus-visible,
+body:not(.editorial-layout) .learn-lesson-nav a:hover,
+body:not(.editorial-layout) .learn-lesson-nav a:focus-visible {
+ background: rgba(31, 31, 31, 0.05);
+ color: var(--color-fg);
+}
+
+body:not(.editorial-layout) .learn-hero-art,
+body:not(.editorial-layout) .learn-lesson-heading,
+body:not(.editorial-layout) .learn-lesson-top,
+body:not(.editorial-layout) .learn-lesson-nav {
+ border-color: rgba(31, 31, 31, 0.1);
+}
+
+body:not(.editorial-layout) .learn-course-panel,
+body:not(.editorial-layout) .learn-current-row,
+body:not(.editorial-layout) .learn-chapter,
+body:not(.editorial-layout) .learn-info,
+body:not(.editorial-layout) .learn-lesson-bar,
+body:not(.editorial-layout) .learn-lesson-menu,
+body:not(.editorial-layout) .learn-lesson-nav a,
+body:not(.editorial-layout) .learn-complete {
+ border-color: rgba(31, 31, 31, 0.12);
+ background: rgba(255, 255, 255, 0.5);
+}
+
+body:not(.editorial-layout) .learn-lesson-menu::before,
+body:not(.editorial-layout) .learn-lesson-menu::after {
+ background: rgba(31, 31, 31, 0.72);
+}
+
+body:not(.editorial-layout) .learn-lesson-menu::before {
+ box-shadow: 0 5px 0 rgba(31, 31, 31, 0.72);
+}
+
+body:not(.editorial-layout) .learn-lesson-title img {
+ border-color: rgba(31, 31, 31, 0.12);
+ background: rgba(255, 255, 255, 0.56);
+}
+
+body:not(.editorial-layout) .learn-code {
+ border-color: rgba(246, 201, 69, 0.22);
+ background: linear-gradient(180deg, #171513 0%, #241d31 100%);
+ color: rgba(250, 248, 244, 0.94);
+}
+
+body:not(.editorial-layout) .learn-code-header {
+ border-bottom-color: rgba(250, 248, 244, 0.1);
+ background: rgba(250, 248, 244, 0.035);
+}
+
+body:not(.editorial-layout) .learn-code-title {
+ color: rgba(250, 248, 244, 0.72);
+}
diff --git a/src/layouts/product.layout.client.ts b/src/layouts/product.layout.client.ts
deleted file mode 100644
index 31da9879..00000000
--- a/src/layouts/product.layout.client.ts
+++ /dev/null
@@ -1 +0,0 @@
-import '../lib/learn-client.js'
diff --git a/src/layouts/root.layout.ts b/src/layouts/root.layout.ts
index 80c191b4..c69f94e7 100644
--- a/src/layouts/root.layout.ts
+++ b/src/layouts/root.layout.ts
@@ -1,10 +1,23 @@
-import { load } from 'cheerio'
-import { highlight } from '../lib/markdown.ts'
+import { rootContent } from '../lib/rendering.ts'
import { html, raw, render } from 'fragtml'
import type { LayoutFunction } from '@domstack/static/types.js'
import { header, footer } from '../lib/navigation.ts'
import { collections, type Collection } from '../lib/collections.ts'
-const root: LayoutFunction, string> = ({
+interface RootVars {
+ title: string
+ siteUrl: string
+ siteName?: string
+ lang?: string
+ layout: string
+ docsCollection?: Collection
+ description?: string
+ bodyClass?: string
+ product?: string
+ redirect?: string
+ footerLabel?: string
+ bodyAttrs?: { 'data-ask-ai'?: string | boolean }
+}
+const root: LayoutFunction = ({
vars: v,
children,
page,
@@ -12,30 +25,18 @@ const root: LayoutFunction, string> = ({
styles = [],
}) => {
const collection = v.docsCollection
- ? collections[v.docsCollection as Collection]
+ ? collections[v.docsCollection]
: undefined
const editorial = (v.bodyClass || '').includes('editorial-layout')
const product = collection?.product || v.product || ''
const title = collection
- ? `${v.title} · ${v.layout === 'spec' ? 'Silk Spec' : collection.title} · Oro Computer`
+ ? `${v.title} · ${v.layout === 'spec' ? 'Silk Spec' : collection.title} · ${v.siteName ?? 'Oro Computer'}`
: v.title
- if (!collection && children.includes(' {
- const language = $(el)
- .attr('class')
- ?.match(/language-([\w+-]+)/)?.[1]
- if (language) {
- const highlighted = highlight($(el).text(), language)
- if (highlighted) $(el).html(highlighted).addClass('hljs')
- }
- })
- children = $.html()
- }
+ if (!collection) children = rootContent(children)
const canonical = new URL(v.redirect || page.url, v.siteUrl).href
return render(
html`
-
+
@@ -49,7 +50,7 @@ const root: LayoutFunction, string> = ({
>
+// DOMStack beta.5 reads layout subscriptions from vars.
+export const vars = { dataDeps }
+const layout: LayoutFunction> = ({ vars, children, data }) =>
+ renderDocs(vars, children, data.runtimeNavigation)
+export default layout
diff --git a/src/layouts/sage-docs.layout.ts b/src/layouts/sage-docs.layout.ts
new file mode 100644
index 00000000..886b4126
--- /dev/null
+++ b/src/layouts/sage-docs.layout.ts
@@ -0,0 +1,10 @@
+import type { DataDeps, LayoutFunction } from '@domstack/static/types.js'
+import type { DocVars, DocsData } from '../lib/docs.ts'
+import { renderDocs } from './docs.layout.ts'
+export const parentLayout = 'docs'
+export const dataDeps = ['sageNavigation'] satisfies DataDeps>
+// DOMStack beta.5 reads layout subscriptions from vars.
+export const vars = { dataDeps }
+const layout: LayoutFunction> = ({ vars, children, data }) =>
+ renderDocs(vars, children, data.sageNavigation)
+export default layout
diff --git a/src/layouts/silk-docs.layout.ts b/src/layouts/silk-docs.layout.ts
new file mode 100644
index 00000000..43395692
--- /dev/null
+++ b/src/layouts/silk-docs.layout.ts
@@ -0,0 +1,10 @@
+import type { DataDeps, LayoutFunction } from '@domstack/static/types.js'
+import type { DocVars, DocsData } from '../lib/docs.ts'
+import { renderDocs } from './docs.layout.ts'
+export const parentLayout = 'docs'
+export const dataDeps = ['silkNavigation'] satisfies DataDeps>
+// DOMStack beta.5 reads layout subscriptions from vars.
+export const vars = { dataDeps }
+const layout: LayoutFunction> = ({ vars, children, data }) =>
+ renderDocs(vars, children, data.silkNavigation)
+export default layout
diff --git a/src/layouts/silkWiki-docs.layout.ts b/src/layouts/silkWiki-docs.layout.ts
new file mode 100644
index 00000000..c6afcf6c
--- /dev/null
+++ b/src/layouts/silkWiki-docs.layout.ts
@@ -0,0 +1,10 @@
+import type { DataDeps, LayoutFunction } from '@domstack/static/types.js'
+import type { DocVars, DocsData } from '../lib/docs.ts'
+import { renderDocs } from './docs.layout.ts'
+export const parentLayout = 'docs'
+export const dataDeps = ['silkWikiNavigation'] satisfies DataDeps>
+// DOMStack beta.5 reads layout subscriptions from vars.
+export const vars = { dataDeps }
+const layout: LayoutFunction> = ({ vars, children, data }) =>
+ renderDocs(vars, children, data.silkWikiNavigation)
+export default layout
diff --git a/src/layouts/slg-docs.layout.ts b/src/layouts/slg-docs.layout.ts
new file mode 100644
index 00000000..83e23bcc
--- /dev/null
+++ b/src/layouts/slg-docs.layout.ts
@@ -0,0 +1,10 @@
+import type { DataDeps, LayoutFunction } from '@domstack/static/types.js'
+import type { DocVars, DocsData } from '../lib/docs.ts'
+import { renderDocs } from './docs.layout.ts'
+export const parentLayout = 'docs'
+export const dataDeps = ['slgNavigation'] satisfies DataDeps>
+// DOMStack beta.5 reads layout subscriptions from vars.
+export const vars = { dataDeps }
+const layout: LayoutFunction> = ({ vars, children, data }) =>
+ renderDocs(vars, children, data.slgNavigation)
+export default layout
diff --git a/src/layouts/spec.layout.client.ts b/src/layouts/spec.layout.client.ts
index bd301014..24bac85c 100644
--- a/src/layouts/spec.layout.client.ts
+++ b/src/layouts/spec.layout.client.ts
@@ -1,3 +1,4 @@
+import { initAskAiMenu } from '../lib/ask-ai-client.ts'
import { enhanceSidebar } from '../lib/sidebar-client.ts'
import { enhanceArticle } from '../lib/article-client.ts'
const app = document.querySelector('[data-spec-app]')
@@ -16,3 +17,5 @@ if (app) {
if (p && !/^spec\/2026(?:\.md)?$/.test(p))
location.replace('/silk/docs/?p=' + encodeURIComponent(p) + location.hash)
}
+
+initAskAiMenu()
diff --git a/src/layouts/spec.layout.ts b/src/layouts/spec.layout.ts
index 2d6f6c65..a32e767b 100644
--- a/src/layouts/spec.layout.ts
+++ b/src/layouts/spec.layout.ts
@@ -1,9 +1,18 @@
import { html, raw, render } from 'fragtml'
import type { LayoutFunction } from '@domstack/static/types.js'
-import { article } from './docs.layout.ts'
+import { article } from '../lib/rendering.ts'
+import { collections } from '../lib/collections.ts'
+import { rawUrl, type DocVars } from '../lib/docs.ts'
export const parentLayout = 'root'
-const spec: LayoutFunction, string> = ({ children }) => {
+type SpecVars = DocVars & { specLabel?: string }
+const spec: LayoutFunction = ({ vars: v, children }) => {
const { body, toc } = article(children, true)
+ const markdownUrl = rawUrl({ collection: v.docsCollection, sourcePath: v.sourcePath })
+ const year = v.sourcePath.match(/(?:^|\/)(\d{4})\.(?:md|txt)$/)?.[1]
+ const productLabel = collections[v.docsCollection].title.replace(/ (?:Docs|Wiki)$/, '')
+ const label = v.specLabel ?? (year ? `${productLabel} Spec (${year})` : v.title)
+ const upstreamPath = v.sourcePath.split('/').map(encodeURIComponent).join('/')
+ const upstreamUrl = `https://github.com/${v.githubRepo}/blob/${encodeURIComponent(v.githubRef)}/docs/${upstreamPath}`
return render(
html`
@@ -11,14 +20,14 @@ const spec: LayoutFunction, string> = ({ children }) => {