Compare commits
22
Commits
blue/team-shell
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9cd26e3ee | ||
|
|
a93d68a05c | ||
|
|
33e4edcd8e | ||
|
|
2c79ff36ae | ||
|
|
48c5832fca | ||
|
|
75c5d5b5d0 | ||
|
|
7d9f0fa2a4 | ||
|
|
da50042d5e | ||
|
|
e4700d2c30 | ||
|
|
22b7f62f89 | ||
|
|
44a822f106 | ||
|
|
441ce04497 | ||
|
|
e97433f553 | ||
|
|
f74b440384 | ||
|
|
60c24492f1 | ||
|
|
81e2103f30 | ||
|
|
011441eb5a | ||
|
|
e05c06e404 | ||
|
|
3b3f9d6826 | ||
|
|
d804cb3db1 | ||
|
|
b3a0dc5861 | ||
|
|
d313a71bee |
@@ -1,102 +0,0 @@
|
||||
name: sync
|
||||
|
||||
# Carries pushed refs to git.hanzo.ai, the canonical forge, and does nothing
|
||||
# else: build, test and release run natively there.
|
||||
#
|
||||
# Repo-agnostic. owner/name come from ${{ github.repository }}, so this file is
|
||||
# dropped into every repo unchanged as .github/workflows/sync.yml and is the
|
||||
# only workflow GitHub is permitted to run.
|
||||
|
||||
on:
|
||||
push:
|
||||
# '**' matches slashes; '*' does not, and would skip feature/foo.
|
||||
branches: ['**']
|
||||
tags: ['**']
|
||||
workflow_dispatch:
|
||||
|
||||
# Serialize per ref. Never cancel: a cancelled run drops that ref and nothing
|
||||
# reconciles it afterwards.
|
||||
concurrency:
|
||||
group: sync-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
# .github/workflows is also on the forge's own workflow search path, so this
|
||||
# file executes there too in any repo without .hanzo/workflows, where it
|
||||
# would push the forge to itself. server_url is the forge's AppURL, which
|
||||
# makes this the test for "running on GitHub".
|
||||
if: github.server_url == 'https://github.com'
|
||||
# GitHub-hosted on purpose. This is the one workflow that must run ON GitHub,
|
||||
# and the self-hosted pool that used to serve it is retired — a job asking for
|
||||
# it now queues forever against a label nothing answers. Carrying refs is a
|
||||
# push, not a build, so it does not belong on the build fabric anyway: builds
|
||||
# run natively on git.hanzo.ai, which is the whole point of this file.
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
persist-credentials: false
|
||||
|
||||
- name: Push refs to git.hanzo.ai
|
||||
env:
|
||||
FORGE: https://git.hanzo.ai
|
||||
GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if [ -z "${GIT_TOKEN}" ]; then
|
||||
echo "::error::GIT_TOKEN is unset, so this push never reached the canonical forge. Add the org secret."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
REMOTE="${FORGE}/${GITHUB_REPOSITORY}.git"
|
||||
|
||||
# The forge reads the token from the basic-auth password and ignores
|
||||
# the username. base64 of a secret is not masked automatically.
|
||||
AUTH="$(printf 'x-access-token:%s' "${GIT_TOKEN}" | base64 -w0)"
|
||||
echo "::add-mask::${AUTH}"
|
||||
# Host-scoped, so the forge token is never sent to github.com.
|
||||
git config --local "http.${FORGE}/.extraheader" "Authorization: Basic ${AUTH}"
|
||||
|
||||
# Reachability is the only transient failure here, so it is the only
|
||||
# thing retried. ls-remote exits 0 on an existing empty repo and
|
||||
# non-zero when the forge is down or the repo is absent.
|
||||
ERR="$(mktemp)"
|
||||
REACHED=""
|
||||
for attempt in 1 2 3; do
|
||||
if git ls-remote "${REMOTE}" >/dev/null 2>"${ERR}"; then
|
||||
REACHED=1
|
||||
break
|
||||
fi
|
||||
echo "forge probe failed (attempt ${attempt}/3): $(cat "${ERR}")"
|
||||
if [ "${attempt}" -lt 3 ]; then
|
||||
sleep $((attempt * 5))
|
||||
fi
|
||||
done
|
||||
if [ -z "${REACHED}" ]; then
|
||||
echo "::error::git.hanzo.ai is unreachable or ${GITHUB_REPOSITORY} does not exist there. $(cat "${ERR}")"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Additive and fast-forward-only: no leading '+', no --force, no
|
||||
# --prune, no --mirror. Tags go every run so a tag that missed its own
|
||||
# event still lands; git refuses to move one that already differs.
|
||||
REFS=('refs/tags/*:refs/tags/*')
|
||||
case "${GITHUB_REF}" in
|
||||
refs/heads/*) REFS+=("${GITHUB_REF}:${GITHUB_REF}") ;;
|
||||
esac
|
||||
|
||||
# Not --atomic: a diverged ref must not hold back the refs that are
|
||||
# clean. Rejections still fail the job.
|
||||
if ! git push "${REMOTE}" "${REFS[@]}"; then
|
||||
echo "::error::git.hanzo.ai rejected a ref for ${GITHUB_REPOSITORY}. The forge is canonical and is never force-updated from here; reconcile the diverged branch or tag by hand."
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,121 +0,0 @@
|
||||
name: Publish @hanzo/ai
|
||||
|
||||
# Surgical publish of ONLY the @hanzo/ai SDK (pkgs/ai). The desktop apps
|
||||
# (hanzo/zoo/lux) render `<HanzoAI/>` from this package; it must be on npm
|
||||
# for a clean `npm i` to resolve it.
|
||||
#
|
||||
# Triggered only on `release/ai-v*` tags. A version bump alone does NOT
|
||||
# publish — the tag gates the run. Tag scheme:
|
||||
# release/ai-v0.1.1 -> @hanzo/ai@0.1.1
|
||||
#
|
||||
# NPM_TOKEN resolution order (same shape as publish-gui.yml):
|
||||
# 1. KMS via Universal Auth (KMS_CLIENT_ID/SECRET -> short-lived token)
|
||||
# 2. KMS via long-lived HANZO_API_KEY (legacy bootstrap)
|
||||
# 3. Direct repo secret NPM_TOKEN (fallback until KMS is provisioned)
|
||||
#
|
||||
# Idempotent: skips if @hanzo/ai@<version> is already on npm.
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'release/ai-v*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
confirm:
|
||||
description: 'Type "publish" to publish @hanzo/ai at its current on-disk version'
|
||||
required: true
|
||||
default: ''
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
# A transitive puppeteer dep tries to download Chromium in its postinstall,
|
||||
# which fails on the runner and kills `bun install` (run #7). The publish
|
||||
# never drives a browser, so skip every browser-binary download.
|
||||
env:
|
||||
PUPPETEER_SKIP_DOWNLOAD: '1'
|
||||
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: '1'
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
|
||||
PUPPETEER_SKIP_CHROME_DOWNLOAD: '1'
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Build, smoke-test, publish @hanzo/ai
|
||||
# Same runner publish-gui.yml uses. @hanzo/ai is arch-independent JS, and the
|
||||
# arm64 label this asked for is answered by one frequently-offline host — a job
|
||||
# requesting a label nothing answers queues silently instead of failing, which
|
||||
# is the worst outcome for a publisher. KMS is reachable from either pool.
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
if: github.event_name == 'push' || github.event.inputs.confirm == 'publish'
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
# Shallow: the publish only builds pkgs/ai — it needs no git history.
|
||||
# A full clone (fetch-depth 0) of this monorepo's history OOMs git
|
||||
# index-pack on the runner and fails checkout (runs #5/#6 died here).
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Install
|
||||
uses: ./.github/actions/install
|
||||
|
||||
# Canonical, deterministic KMS auth — the ONE way every hanzo/lux/zoo repo
|
||||
# loads secrets (see hanzoai/universe/.github/actions/kms-action). OIDC-
|
||||
# first: GitHub's id-token proves this repo's identity to KMS, so NOTHING
|
||||
# is stored — only non-secret `vars.*` config. NPM_TOKEN is exported into
|
||||
# the job env at `secret-path`. No repo NPM_TOKEN secret, no client_id/
|
||||
# secret. Set KMS_IDENTITY_ID + KMS_PROJECT_ID once at the org level.
|
||||
- name: Load NPM_TOKEN from Hanzo KMS (OIDC)
|
||||
uses: hanzoai/universe/.github/actions/kms-action@main
|
||||
with:
|
||||
identity-id: ${{ vars.KMS_IDENTITY_ID }}
|
||||
project-id: ${{ vars.KMS_PROJECT_ID }}
|
||||
environment: ${{ vars.KMS_ENVIRONMENT || 'prod' }}
|
||||
secret-path: ${{ vars.KMS_PUBLISH_SECRET_PATH || '/publish' }}
|
||||
export-type: env
|
||||
|
||||
- name: Configure npm registry
|
||||
run: |
|
||||
# NPM_TOKEN is now in the job env (kms-action export-type=env).
|
||||
test -n "${NPM_TOKEN:-}" || { echo "::error::KMS did not yield NPM_TOKEN at the publish path"; exit 1; }
|
||||
echo 'registry=https://registry.npmjs.org/' > ~/.npmrc
|
||||
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" >> ~/.npmrc
|
||||
|
||||
- name: Build @hanzo/ai (dist + dist-desktop)
|
||||
# pkgs/ai's own build runs BOTH vite passes: dist/ (web entry,
|
||||
# ai.css) and dist-desktop/ (the <HanzoAI/> desktop bundle the apps
|
||||
# import). Build it explicitly so both trees exist before publish.
|
||||
working-directory: pkgs/ai
|
||||
run: bun run build
|
||||
|
||||
- name: Smoke-test dist trees + exports
|
||||
run: |
|
||||
set -eu
|
||||
cd pkgs/ai
|
||||
test -f dist/index.js || (echo "::error::dist/index.js missing"; exit 1)
|
||||
test -f dist/ai.css || (echo "::error::dist/ai.css missing"; exit 1)
|
||||
test -f dist-desktop/index.js || (echo "::error::dist-desktop/index.js missing"; exit 1)
|
||||
# Every file referenced from the exports map must exist.
|
||||
node -e "
|
||||
const fs=require('fs');
|
||||
const pkg=require('./package.json');
|
||||
const walk=(v)=>{
|
||||
if (typeof v==='string') { if (v.startsWith('./') && !fs.existsSync(v)) { console.error('missing:',v); process.exitCode=1; } }
|
||||
else if (v && typeof v==='object') { for (const k of Object.keys(v)) walk(v[k]); }
|
||||
};
|
||||
walk(pkg.exports||{}); walk(pkg.main); walk(pkg.module); walk(pkg.types);
|
||||
"
|
||||
echo "smoke OK: $(node -p "require('./package.json').name + '@' + require('./package.json').version")"
|
||||
|
||||
- name: Publish @hanzo/ai
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ env.NPM_TOKEN }}
|
||||
working-directory: pkgs/ai
|
||||
run: |
|
||||
set -eu
|
||||
version=$(node -p "require('./package.json').version")
|
||||
if npm view "@hanzo/ai@${version}" version >/dev/null 2>&1; then
|
||||
echo "@hanzo/ai@${version} already on npm - skipping."
|
||||
exit 0
|
||||
fi
|
||||
# pkgs/ai has no workspace:* deps, so a direct publish is clean.
|
||||
npm publish --access public
|
||||
@@ -103,7 +103,7 @@ jobs:
|
||||
# Targeted publish of just the @hanzogui/* closure + hanzogui +
|
||||
# @hanzo/gui, at the current on-disk version. Avoids release.ts's
|
||||
# whole-workspace fan-out (which fails packing unrelated
|
||||
# @hanzo_network/* packages) and its fragile tarball-rename step.
|
||||
# unrelated workspace packages) and its fragile tarball-rename step.
|
||||
# Idempotent — skips any name@version already on npm.
|
||||
env:
|
||||
NPM_TOKEN: ${{ steps.token.outputs.npm_token }}
|
||||
|
||||
@@ -16,7 +16,6 @@ One way, and it runs on our own stack:
|
||||
.hanzo/workflows/checks.yaml check, lint, unit tests
|
||||
.hanzo/workflows/publish-gui.yml hanzogui + @hanzo/gui
|
||||
.hanzo/workflows/publish-gui-all.yml every @hanzogui/* primitive
|
||||
.hanzo/workflows/publish-ai.yml @hanzo/ai
|
||||
.hanzo/workflows/build.yml ghcr.io/hanzoai/gui
|
||||
|
||||
**git.hanzo.ai is canonical; GitHub is a mirror.** `.github/workflows/` holds
|
||||
@@ -35,19 +34,15 @@ every case — a version bump alone never ships.
|
||||
| --- | --- | --- |
|
||||
| `release/gui-v*` | `publish-gui.yml` | `hanzogui` + the `@hanzo/gui` alias |
|
||||
| `release/gui-all-v*` (or dispatch) | `publish-gui-all.yml` | every `@hanzogui/*` primitive + both umbrellas |
|
||||
| `release/ai-v*` | `publish-ai.yml` | `@hanzo/ai` from `pkgs/ai` |
|
||||
|
||||
`NPM_TOKEN` comes from KMS with a repo-secret fallback in the two `publish-gui*`
|
||||
workflows, and from `hanzoai/universe/.github/actions/kms-action` over OIDC in
|
||||
`publish-ai.yml`. The forge has to be able to resolve that cross-repo action, so
|
||||
`hanzoai/universe` must exist there; if it does not, the KMS step is the thing that
|
||||
fails.
|
||||
`NPM_TOKEN` comes from KMS with a repo-secret fallback in both `publish-gui*`
|
||||
workflows.
|
||||
|
||||
`publish-ai.yml` asked for `runs-on: [self-hosted, linux, arm64]`, which is one
|
||||
frequently-offline host. A job requesting a label nothing answers queues silently
|
||||
instead of failing, which is the worst possible outcome for a publisher, so it now
|
||||
uses `hanzo-build-linux-amd64` like its two siblings — `@hanzo/ai` is
|
||||
arch-independent JS.
|
||||
**This repo publishes primitives only.** The AI app that used to live at
|
||||
`pkgs/ai` — `@hanzo/app`, plus the seven `@hanzo_network/*` libraries it is
|
||||
built from — moved to `hanzo-js/app` on 2026-07-28, and publishes itself from
|
||||
there. An application inside the primitives repo is the layering mistake the
|
||||
owner named: `@hanzo/gui` (primitives) -> `@hanzo/ui` (widgets) -> apps.
|
||||
|
||||
## Known dead ends
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"rootDir": ".",
|
||||
"paths": {
|
||||
"~/*": ["./*"]
|
||||
@@ -11,7 +10,6 @@
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"declaration": true,
|
||||
"downlevelIteration": true,
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"inlineSourceMap": true,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"target": "esnext",
|
||||
|
||||
@@ -34,7 +34,7 @@ interface Endpoints {
|
||||
userinfo: string
|
||||
}
|
||||
|
||||
// Casdoor/IAM defaults, used when discovery is unreachable.
|
||||
// IAM's endpoint paths, used when discovery is unreachable.
|
||||
const FALLBACK: Endpoints = {
|
||||
authorization: `${ISSUER}/login/oauth/authorize`,
|
||||
token: `${ISSUER}/api/login/oauth/access_token`,
|
||||
@@ -43,7 +43,7 @@ const FALLBACK: Endpoints = {
|
||||
|
||||
let endpointsCache: Endpoints | undefined
|
||||
|
||||
/** Resolve OIDC endpoints from the discovery document, falling back to Casdoor paths. */
|
||||
/** Resolve OIDC endpoints from the discovery document, falling back to IAM's paths. */
|
||||
export async function discover(): Promise<Endpoints> {
|
||||
if (endpointsCache !== undefined) return endpointsCache
|
||||
try {
|
||||
@@ -58,7 +58,7 @@ export async function discover(): Promise<Endpoints> {
|
||||
return endpointsCache
|
||||
}
|
||||
} catch {
|
||||
// discovery blocked — fall through to the known Casdoor paths
|
||||
// discovery blocked — fall through to the known IAM paths
|
||||
}
|
||||
endpointsCache = FALLBACK
|
||||
return endpointsCache
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"target": "esnext",
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"declaration": true,
|
||||
"downlevelIteration": true,
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"inlineSourceMap": true,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
// for jumping to things properly in vscode
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"hanzogui": [
|
||||
"../ui/gui"
|
||||
@@ -29,7 +27,7 @@
|
||||
"./data/*"
|
||||
],
|
||||
"react-native": [
|
||||
"react-native-web"
|
||||
"./react-native-web"
|
||||
]
|
||||
},
|
||||
"target": "es2021",
|
||||
@@ -42,7 +40,7 @@
|
||||
"allowJs": true,
|
||||
"noEmit": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"moduleResolution": "bundler",
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"noImplicitThis": true,
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"rootDir": ".",
|
||||
"importHelpers": true,
|
||||
"allowJs": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"downlevelIteration": true,
|
||||
"esModuleInterop": true,
|
||||
"preserveSymlinks": true,
|
||||
"incremental": true,
|
||||
@@ -26,7 +24,15 @@
|
||||
"strictNullChecks": true,
|
||||
"target": "es2020",
|
||||
"types": ["node"],
|
||||
"lib": ["dom", "esnext"]
|
||||
"lib": [
|
||||
"dom",
|
||||
"esnext"
|
||||
],
|
||||
"paths": {
|
||||
"*": [
|
||||
"./*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exclude": ["_"],
|
||||
"typeAcquisition": {
|
||||
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
// TypeScript 7 reports TS2882 for a side-effect import with no type
|
||||
// declaration ("Cannot find module or type declarations for side-effect import
|
||||
// of './globals.css'"). TS 5.x accepted these silently.
|
||||
//
|
||||
// Stylesheets are resolved by the bundler (Next/Vite/webpack), never by the
|
||||
// TypeScript module resolver, so this declares them legitimate rather than
|
||||
// giving them a shape.
|
||||
declare module '*.css';
|
||||
+1
-1
@@ -208,4 +208,4 @@
|
||||
"exports": {
|
||||
"./package.json": "./package.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
# Live chat — VERIFIED working end-to-end (web, local) 2026-06-05
|
||||
|
||||
The web app at `:1500` chats with **zen-coder-24b** through a real **hanzo-node**,
|
||||
fully in the browser. Verified: prompt "Reply with exactly: E2E CHAT OK" →
|
||||
zen-coder generated `E2E CHAT OK` (7 tokens), streamed back to the chat UI.
|
||||
|
||||
The full path:
|
||||
|
||||
```
|
||||
browser :1500 ─(vite proxy /v1,/v2,/ws)→ hanzo-node :3700 ─job→ zen_engine provider
|
||||
→ responses-proxy :36906 (rewrites /v1/engine/responses → /v1/responses)
|
||||
→ hanzo-engine :36902 (zen-coder-24b) → "E2E CHAT OK" → streamed back to the UI
|
||||
```
|
||||
|
||||
## The non-obvious fixes that made it work
|
||||
|
||||
1. **The node speaks the OpenAI _Responses_ API, the engine serves it at a
|
||||
different path.** The node's `OpenAI` provider POSTs to
|
||||
`<url>/v1/engine/responses` (`{input:[...], max_output_tokens, ...}`), but
|
||||
hanzo-engine serves the Responses API at **`/v1/responses`** (`/v1/engine/...`
|
||||
→ 404 → the node reports "AI Provider API Error: Unknown error"). A tiny proxy
|
||||
rewrites the path (and injects `enable_thinking:false`). See
|
||||
`local-runtime/responses-proxy.py`.
|
||||
2. **Use `127.0.0.1`, never `localhost`.** `localhost` resolves to IPv6 `::1`
|
||||
here; the engine/embeddings bind IPv4 only → instant connection failure.
|
||||
3. **CORS:** the vite dev server proxies the app's own origin (`/v1`,`/v2`,`/ws`)
|
||||
to the node, so the app's `nodeAddress` is just `http://<host>:1500`.
|
||||
4. **Two migration import bugs** had to be fixed for the chat screens to render:
|
||||
`main-layout.tsx` was missing `Box`/`Boxes`/`Coins` lucide imports, and the
|
||||
merge pulled `react-resizable-panels@4` (renamed exports) — pinned to `^3.0.2`
|
||||
in `pkgs/net-ui` (the app uses `PanelGroup`/`PanelResizeHandle`).
|
||||
|
||||
## Reproduce it
|
||||
|
||||
```bash
|
||||
# 1. engine + embeddings already running: :36902 (zen-coder), :11436 (embed)
|
||||
# 2. responses-proxy (path rewrite):
|
||||
python3 pkgs/ai/local-runtime/responses-proxy.py & # :36906 → :36902
|
||||
# 3. WS proxy (the app derives ws://host:1501 behind the vite proxy):
|
||||
node pkgs/ai/local-runtime/ws-proxy.js & # :1501 → node WS :3701
|
||||
# 4. a /v1 hanzo-node wired to the proxy (fresh storage, no reg code):
|
||||
bash pkgs/ai/local-runtime/run-node.sh & # API :3700, zen_engine → :36906
|
||||
# 5. web app pointed at the node + engine (engine-api proxy avoids CORS):
|
||||
cd pkgs/ai/web && VITE_NODE_API=http://127.0.0.1:3700 VITE_NODE_WS=ws://127.0.0.1:3701 \
|
||||
VITE_ENGINE_BASE_URL=/engine-api VITE_ENGINE_API=http://127.0.0.1:36906 \
|
||||
bun x vite --config vite.config.ts # :1500
|
||||
# 6. open http://spark.local:1500 → agree → Quick Connect (node address = same
|
||||
# origin http://localhost:1500) → /home → chat. Reply streams from zen-coder.
|
||||
```
|
||||
|
||||
## Console — clean (the e2e asserts it)
|
||||
|
||||
The earlier web console errors are fixed and guarded by `e2e/chat.e2e.test.ts`:
|
||||
react-query "data cannot be undefined" (web `invoke` returns null), the
|
||||
`isPermissionGranted`/notification gap, the `:36900/v1/engine/models` CORS
|
||||
(routed via the `/engine-api` proxy), the `ws://…:1501` failure (ws-proxy), the
|
||||
`available_models` 401 (gated `useGetLLMProviders` on auth), and the framer
|
||||
`motion()` deprecation. Remaining: a rare, intermittent static-asset `404` and a
|
||||
node-side `Embedding "Query is not read-only"` warning — neither affects chat.
|
||||
@@ -1,80 +0,0 @@
|
||||
# @hanzo/ai — one app surface, every platform
|
||||
|
||||
The Hanzo / Zoo / Lux AI app is **one** thing: `@hanzo/ai`. Web, desktop and
|
||||
mobile are not three apps — they are the same app with two axes injected.
|
||||
|
||||
```
|
||||
┌────────────────────────── @hanzo/ai ──────────────────────────┐
|
||||
│ src/app/ (the migrated shinkai-fork app — ONE copy, one place) │
|
||||
│ + net-* libs (ui, state, i18n, message-ts, brand-config, logo, │
|
||||
│ artifacts) + src/host/* (the @tauri-apps shim surface) │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
▲ ▲ ▲
|
||||
host = web (default) host = tauriHost host = expoHost
|
||||
brand = getBrand() brand = getBrand() brand = getBrand()
|
||||
│ │ │
|
||||
hanzo.app / .chat hanzoai/desktop @hanzo/gui
|
||||
zoo.cloud / lux.cloud zooai/app · luxfi/app (Expo mobile)
|
||||
```
|
||||
|
||||
## The two orthogonal axes (decomplected)
|
||||
|
||||
| Axis | What it is | How it's injected | Default |
|
||||
|---|---|---|---|
|
||||
| **Brand** | hanzo / zoo / lux identity, cloud endpoints, chain, IAM | `getBrand()` (driven by `VITE_BRAND` or hostname) **or** spread as props `<HanzoAI {...brand}/>` | `getBrand()` → hostname → HANZO |
|
||||
| **Platform** | how native calls (`invoke`, `listen`, fs, window…) resolve | `host` prop — a `HostAdapter` | web no-ops |
|
||||
|
||||
Everything else — the entire app — is shared. A new app is a ~10-line shim:
|
||||
|
||||
```tsx
|
||||
import HanzoAI, { getBrand } from '@hanzo/ai';
|
||||
import { tauriHost } from './tauri-host'; // desktop only; web omits it
|
||||
createRoot(root).render(
|
||||
<HanzoAI {...getBrand()} host={tauriHost} features={{ chat, wallet, agents }} />
|
||||
);
|
||||
```
|
||||
|
||||
## Web ⇄ desktop parity
|
||||
|
||||
The web shim and the desktop shim are **byte-identical except one prop**
|
||||
(`host={tauriHost}`). They import the same `@hanzo/ai`, which is the same
|
||||
`src/app`. So the rendered React tree — every screen, route and component — is
|
||||
identical. The only runtime difference is what `HostAdapter` does:
|
||||
|
||||
| Call | web (default host) | desktop (tauriHost) |
|
||||
|---|---|---|
|
||||
| `invoke(cmd, args)` | no-op, returns `undefined` (logs in dev) | Tauri IPC → Rust |
|
||||
| `listen(event, cb)` | no-op unlisten | Tauri event bus |
|
||||
| `getCurrentWindow()` | no-op Window (emit/listen/geometry) | real Tauri window |
|
||||
| fs / shell / process / updater | no-op / empty | Tauri plugins |
|
||||
|
||||
So on the web the app renders 1:1 with desktop; native-only actions (open a
|
||||
folder, auto-update, tray) simply do nothing instead of crashing. Cloud
|
||||
inference + chat work on both (they go over HTTP to the brand's
|
||||
`inferenceEndpoint`, not through `invoke`).
|
||||
|
||||
## Verification (2026-06-04)
|
||||
|
||||
| Surface | Build | Render |
|
||||
|---|---|---|
|
||||
| web dev server (`pkgs/ai/web`, imports `src`) | ✅ | ✅ Hanzo onboarding |
|
||||
| web prod (unminified, 55M) | ✅ 40s | ✅ |
|
||||
| web prod (minified, 13M / gzip 3.75M) | ✅ 44s | ✅ |
|
||||
| library `dist` (self-contained, react external) | ✅ | — |
|
||||
| **external shim** (`examples/web`, only react provided, app from `dist`) | ✅ | ✅ — proves SDK consumption |
|
||||
|
||||
The render blocker that had to be cleared first: app modules imported
|
||||
`useBrand` from the **`@hanzo/ai` package name**, which resolved to the built
|
||||
`dist` — pulling a second React + a second app copy into the source graph (a
|
||||
cycle → `useContext` of null). Fix: the brand store lives in
|
||||
`@hanzo_network/brand-config` (plain getters, not hooks), aliased to `src`;
|
||||
app code never imports the `@hanzo/ai` package by name.
|
||||
|
||||
## Turning the real repos into shims
|
||||
|
||||
`luxfi/app`, `zooai/app`, `hanzoai/desktop`, `hanzoai/app` each become the
|
||||
shim above. Per repo: depend on `@hanzo/ai` (+ `react`, `react-dom`, and for
|
||||
desktop `@tauri-apps/api`), set `VITE_BRAND`, drop all app source. The
|
||||
Dockerfile/Tauri config builds from the pinned SDK — exactly how
|
||||
`zooai/exchange` builds from `@luxfi/exchange`. (`@hanzo/ai` is not yet
|
||||
published to npm; until then the shims consume it via the workspace.)
|
||||
@@ -1,51 +0,0 @@
|
||||
# Testing @hanzo/ai — and TDD going forward
|
||||
|
||||
## Run
|
||||
|
||||
```bash
|
||||
bun run --cwd pkgs/ai test # unit + regression (vitest, jsdom) — fast, no external deps
|
||||
bun run --cwd pkgs/ai test:watch # vitest watch mode (use while writing a fix)
|
||||
bun run --cwd pkgs/ai test:e2e # Playwright e2e — needs the local runtime up (skips if not)
|
||||
```
|
||||
|
||||
`test` runs everything under `src/**/*.{test,spec}.{ts,tsx}` in jsdom. Config:
|
||||
`vitest.config.ts` mirrors the web build's alias graph (so `@/`, `@tauri-apps/*`
|
||||
→ host shims, `@hanzo_network/*` → src resolve), stubs the streamdown markdown
|
||||
stack (esbuild can't resolve its nested micromark), and loads
|
||||
`@testing-library/jest-dom` via `src/__tests__/setup.ts`. Pyodide /
|
||||
python-code-runner tests are excluded (30 MB wasm + network).
|
||||
|
||||
## What's covered
|
||||
|
||||
**Unit + regression** (`src/__tests__/`) — each guards a bug this stack actually hit:
|
||||
- `brand-store.test.ts` — `useBrand`/`getBrand` are plain getters callable
|
||||
outside render (the invalid-hook-call that blocked the whole web app).
|
||||
- `host-shims.test.ts` — `getCurrentWindow().emit` etc. exist; the injected host
|
||||
adapter switches web↔tauri (the "emit is not a function" mount crash).
|
||||
- `resizable.test.tsx` — `react-resizable-panels` v3 API present, net-ui
|
||||
resizable renders (the v4 "Element type is invalid" chat-view crash).
|
||||
- `no-missing-imports.test.ts` — critical screens import every `<Component>`
|
||||
they use (the dropped `Box`/`Boxes`/`Coins` lucide imports → "Box is not defined").
|
||||
|
||||
**App tests** (pre-existing, `src/app/**`) — engine client, machine-state queries,
|
||||
mining page, playground utils. **E2E** (`e2e/chat.e2e.test.ts`) — drives the real
|
||||
browser: onboarding renders, then connect → register → send → assert zen-coder's
|
||||
reply (and assert no "is not defined" / "Element type is invalid" page errors).
|
||||
|
||||
## TDD workflow (going forward)
|
||||
|
||||
Every fix and feature starts with a failing test:
|
||||
|
||||
1. **Red** — write a test that reproduces the bug or specifies the behavior, in
|
||||
`src/__tests__/` (unit) or `e2e/` (flow). Run `test:watch`; confirm it fails.
|
||||
2. **Green** — make the smallest change that passes it.
|
||||
3. **Refactor** — clean up with the test green.
|
||||
4. Run `bun run --cwd pkgs/ai test` before committing; keep it green.
|
||||
|
||||
Most bugs this session were import/dep/runtime issues that *static render checks
|
||||
missed but a test would have caught* — prefer a regression test over a manual
|
||||
re-check. For anything touching the chat path, add/extend the e2e.
|
||||
|
||||
The local runtime for `test:e2e` is in `pkgs/ai/local-runtime/` (see `CHAT.md`):
|
||||
restart `run-node.sh` (fresh storage) before a run so Quick Connect registers as
|
||||
the first device.
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-47
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-31
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,268 +0,0 @@
|
||||
import { P as withPath, Q as pi, S as cos, T as sin, V as constant, W as halfPi, X as epsilon, Y as tau, Z as sqrt, $ as min, a0 as abs, a1 as atan2, a2 as max, a3 as asin, a4 as acos } from './index-CjXUZi2M.js';
|
||||
|
||||
function arcInnerRadius(d) {
|
||||
return d.innerRadius;
|
||||
}
|
||||
|
||||
function arcOuterRadius(d) {
|
||||
return d.outerRadius;
|
||||
}
|
||||
|
||||
function arcStartAngle(d) {
|
||||
return d.startAngle;
|
||||
}
|
||||
|
||||
function arcEndAngle(d) {
|
||||
return d.endAngle;
|
||||
}
|
||||
|
||||
function arcPadAngle(d) {
|
||||
return d && d.padAngle; // Note: optional!
|
||||
}
|
||||
|
||||
function intersect(x0, y0, x1, y1, x2, y2, x3, y3) {
|
||||
var x10 = x1 - x0, y10 = y1 - y0,
|
||||
x32 = x3 - x2, y32 = y3 - y2,
|
||||
t = y32 * x10 - x32 * y10;
|
||||
if (t * t < epsilon) return;
|
||||
t = (x32 * (y0 - y2) - y32 * (x0 - x2)) / t;
|
||||
return [x0 + t * x10, y0 + t * y10];
|
||||
}
|
||||
|
||||
// Compute perpendicular offset line of length rc.
|
||||
// http://mathworld.wolfram.com/Circle-LineIntersection.html
|
||||
function cornerTangents(x0, y0, x1, y1, r1, rc, cw) {
|
||||
var x01 = x0 - x1,
|
||||
y01 = y0 - y1,
|
||||
lo = (cw ? rc : -rc) / sqrt(x01 * x01 + y01 * y01),
|
||||
ox = lo * y01,
|
||||
oy = -lo * x01,
|
||||
x11 = x0 + ox,
|
||||
y11 = y0 + oy,
|
||||
x10 = x1 + ox,
|
||||
y10 = y1 + oy,
|
||||
x00 = (x11 + x10) / 2,
|
||||
y00 = (y11 + y10) / 2,
|
||||
dx = x10 - x11,
|
||||
dy = y10 - y11,
|
||||
d2 = dx * dx + dy * dy,
|
||||
r = r1 - rc,
|
||||
D = x11 * y10 - x10 * y11,
|
||||
d = (dy < 0 ? -1 : 1) * sqrt(max(0, r * r * d2 - D * D)),
|
||||
cx0 = (D * dy - dx * d) / d2,
|
||||
cy0 = (-D * dx - dy * d) / d2,
|
||||
cx1 = (D * dy + dx * d) / d2,
|
||||
cy1 = (-D * dx + dy * d) / d2,
|
||||
dx0 = cx0 - x00,
|
||||
dy0 = cy0 - y00,
|
||||
dx1 = cx1 - x00,
|
||||
dy1 = cy1 - y00;
|
||||
|
||||
// Pick the closer of the two intersection points.
|
||||
// TODO Is there a faster way to determine which intersection to use?
|
||||
if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1;
|
||||
|
||||
return {
|
||||
cx: cx0,
|
||||
cy: cy0,
|
||||
x01: -ox,
|
||||
y01: -oy,
|
||||
x11: cx0 * (r1 / r - 1),
|
||||
y11: cy0 * (r1 / r - 1)
|
||||
};
|
||||
}
|
||||
|
||||
function d3arc() {
|
||||
var innerRadius = arcInnerRadius,
|
||||
outerRadius = arcOuterRadius,
|
||||
cornerRadius = constant(0),
|
||||
padRadius = null,
|
||||
startAngle = arcStartAngle,
|
||||
endAngle = arcEndAngle,
|
||||
padAngle = arcPadAngle,
|
||||
context = null,
|
||||
path = withPath(arc);
|
||||
|
||||
function arc() {
|
||||
var buffer,
|
||||
r,
|
||||
r0 = +innerRadius.apply(this, arguments),
|
||||
r1 = +outerRadius.apply(this, arguments),
|
||||
a0 = startAngle.apply(this, arguments) - halfPi,
|
||||
a1 = endAngle.apply(this, arguments) - halfPi,
|
||||
da = abs(a1 - a0),
|
||||
cw = a1 > a0;
|
||||
|
||||
if (!context) context = buffer = path();
|
||||
|
||||
// Ensure that the outer radius is always larger than the inner radius.
|
||||
if (r1 < r0) r = r1, r1 = r0, r0 = r;
|
||||
|
||||
// Is it a point?
|
||||
if (!(r1 > epsilon)) context.moveTo(0, 0);
|
||||
|
||||
// Or is it a circle or annulus?
|
||||
else if (da > tau - epsilon) {
|
||||
context.moveTo(r1 * cos(a0), r1 * sin(a0));
|
||||
context.arc(0, 0, r1, a0, a1, !cw);
|
||||
if (r0 > epsilon) {
|
||||
context.moveTo(r0 * cos(a1), r0 * sin(a1));
|
||||
context.arc(0, 0, r0, a1, a0, cw);
|
||||
}
|
||||
}
|
||||
|
||||
// Or is it a circular or annular sector?
|
||||
else {
|
||||
var a01 = a0,
|
||||
a11 = a1,
|
||||
a00 = a0,
|
||||
a10 = a1,
|
||||
da0 = da,
|
||||
da1 = da,
|
||||
ap = padAngle.apply(this, arguments) / 2,
|
||||
rp = (ap > epsilon) && (padRadius ? +padRadius.apply(this, arguments) : sqrt(r0 * r0 + r1 * r1)),
|
||||
rc = min(abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments)),
|
||||
rc0 = rc,
|
||||
rc1 = rc,
|
||||
t0,
|
||||
t1;
|
||||
|
||||
// Apply padding? Note that since r1 ≥ r0, da1 ≥ da0.
|
||||
if (rp > epsilon) {
|
||||
var p0 = asin(rp / r0 * sin(ap)),
|
||||
p1 = asin(rp / r1 * sin(ap));
|
||||
if ((da0 -= p0 * 2) > epsilon) p0 *= (cw ? 1 : -1), a00 += p0, a10 -= p0;
|
||||
else da0 = 0, a00 = a10 = (a0 + a1) / 2;
|
||||
if ((da1 -= p1 * 2) > epsilon) p1 *= (cw ? 1 : -1), a01 += p1, a11 -= p1;
|
||||
else da1 = 0, a01 = a11 = (a0 + a1) / 2;
|
||||
}
|
||||
|
||||
var x01 = r1 * cos(a01),
|
||||
y01 = r1 * sin(a01),
|
||||
x10 = r0 * cos(a10),
|
||||
y10 = r0 * sin(a10);
|
||||
|
||||
// Apply rounded corners?
|
||||
if (rc > epsilon) {
|
||||
var x11 = r1 * cos(a11),
|
||||
y11 = r1 * sin(a11),
|
||||
x00 = r0 * cos(a00),
|
||||
y00 = r0 * sin(a00),
|
||||
oc;
|
||||
|
||||
// Restrict the corner radius according to the sector angle. If this
|
||||
// intersection fails, it’s probably because the arc is too small, so
|
||||
// disable the corner radius entirely.
|
||||
if (da < pi) {
|
||||
if (oc = intersect(x01, y01, x00, y00, x11, y11, x10, y10)) {
|
||||
var ax = x01 - oc[0],
|
||||
ay = y01 - oc[1],
|
||||
bx = x11 - oc[0],
|
||||
by = y11 - oc[1],
|
||||
kc = 1 / sin(acos((ax * bx + ay * by) / (sqrt(ax * ax + ay * ay) * sqrt(bx * bx + by * by))) / 2),
|
||||
lc = sqrt(oc[0] * oc[0] + oc[1] * oc[1]);
|
||||
rc0 = min(rc, (r0 - lc) / (kc - 1));
|
||||
rc1 = min(rc, (r1 - lc) / (kc + 1));
|
||||
} else {
|
||||
rc0 = rc1 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Is the sector collapsed to a line?
|
||||
if (!(da1 > epsilon)) context.moveTo(x01, y01);
|
||||
|
||||
// Does the sector’s outer ring have rounded corners?
|
||||
else if (rc1 > epsilon) {
|
||||
t0 = cornerTangents(x00, y00, x01, y01, r1, rc1, cw);
|
||||
t1 = cornerTangents(x11, y11, x10, y10, r1, rc1, cw);
|
||||
|
||||
context.moveTo(t0.cx + t0.x01, t0.cy + t0.y01);
|
||||
|
||||
// Have the corners merged?
|
||||
if (rc1 < rc) context.arc(t0.cx, t0.cy, rc1, atan2(t0.y01, t0.x01), atan2(t1.y01, t1.x01), !cw);
|
||||
|
||||
// Otherwise, draw the two corners and the ring.
|
||||
else {
|
||||
context.arc(t0.cx, t0.cy, rc1, atan2(t0.y01, t0.x01), atan2(t0.y11, t0.x11), !cw);
|
||||
context.arc(0, 0, r1, atan2(t0.cy + t0.y11, t0.cx + t0.x11), atan2(t1.cy + t1.y11, t1.cx + t1.x11), !cw);
|
||||
context.arc(t1.cx, t1.cy, rc1, atan2(t1.y11, t1.x11), atan2(t1.y01, t1.x01), !cw);
|
||||
}
|
||||
}
|
||||
|
||||
// Or is the outer ring just a circular arc?
|
||||
else context.moveTo(x01, y01), context.arc(0, 0, r1, a01, a11, !cw);
|
||||
|
||||
// Is there no inner ring, and it’s a circular sector?
|
||||
// Or perhaps it’s an annular sector collapsed due to padding?
|
||||
if (!(r0 > epsilon) || !(da0 > epsilon)) context.lineTo(x10, y10);
|
||||
|
||||
// Does the sector’s inner ring (or point) have rounded corners?
|
||||
else if (rc0 > epsilon) {
|
||||
t0 = cornerTangents(x10, y10, x11, y11, r0, -rc0, cw);
|
||||
t1 = cornerTangents(x01, y01, x00, y00, r0, -rc0, cw);
|
||||
|
||||
context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01);
|
||||
|
||||
// Have the corners merged?
|
||||
if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, atan2(t0.y01, t0.x01), atan2(t1.y01, t1.x01), !cw);
|
||||
|
||||
// Otherwise, draw the two corners and the ring.
|
||||
else {
|
||||
context.arc(t0.cx, t0.cy, rc0, atan2(t0.y01, t0.x01), atan2(t0.y11, t0.x11), !cw);
|
||||
context.arc(0, 0, r0, atan2(t0.cy + t0.y11, t0.cx + t0.x11), atan2(t1.cy + t1.y11, t1.cx + t1.x11), cw);
|
||||
context.arc(t1.cx, t1.cy, rc0, atan2(t1.y11, t1.x11), atan2(t1.y01, t1.x01), !cw);
|
||||
}
|
||||
}
|
||||
|
||||
// Or is the inner ring just a circular arc?
|
||||
else context.arc(0, 0, r0, a10, a00, cw);
|
||||
}
|
||||
|
||||
context.closePath();
|
||||
|
||||
if (buffer) return context = null, buffer + "" || null;
|
||||
}
|
||||
|
||||
arc.centroid = function() {
|
||||
var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2,
|
||||
a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - pi / 2;
|
||||
return [cos(a) * r, sin(a) * r];
|
||||
};
|
||||
|
||||
arc.innerRadius = function(_) {
|
||||
return arguments.length ? (innerRadius = typeof _ === "function" ? _ : constant(+_), arc) : innerRadius;
|
||||
};
|
||||
|
||||
arc.outerRadius = function(_) {
|
||||
return arguments.length ? (outerRadius = typeof _ === "function" ? _ : constant(+_), arc) : outerRadius;
|
||||
};
|
||||
|
||||
arc.cornerRadius = function(_) {
|
||||
return arguments.length ? (cornerRadius = typeof _ === "function" ? _ : constant(+_), arc) : cornerRadius;
|
||||
};
|
||||
|
||||
arc.padRadius = function(_) {
|
||||
return arguments.length ? (padRadius = _ == null ? null : typeof _ === "function" ? _ : constant(+_), arc) : padRadius;
|
||||
};
|
||||
|
||||
arc.startAngle = function(_) {
|
||||
return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant(+_), arc) : startAngle;
|
||||
};
|
||||
|
||||
arc.endAngle = function(_) {
|
||||
return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant(+_), arc) : endAngle;
|
||||
};
|
||||
|
||||
arc.padAngle = function(_) {
|
||||
return arguments.length ? (padAngle = typeof _ === "function" ? _ : constant(+_), arc) : padAngle;
|
||||
};
|
||||
|
||||
arc.context = function(_) {
|
||||
return arguments.length ? ((context = _ == null ? null : _), arc) : context;
|
||||
};
|
||||
|
||||
return arc;
|
||||
}
|
||||
|
||||
export { d3arc as d };
|
||||
@@ -1,270 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const index = require('./index-DKX_wW-G.cjs');
|
||||
|
||||
function arcInnerRadius(d) {
|
||||
return d.innerRadius;
|
||||
}
|
||||
|
||||
function arcOuterRadius(d) {
|
||||
return d.outerRadius;
|
||||
}
|
||||
|
||||
function arcStartAngle(d) {
|
||||
return d.startAngle;
|
||||
}
|
||||
|
||||
function arcEndAngle(d) {
|
||||
return d.endAngle;
|
||||
}
|
||||
|
||||
function arcPadAngle(d) {
|
||||
return d && d.padAngle; // Note: optional!
|
||||
}
|
||||
|
||||
function intersect(x0, y0, x1, y1, x2, y2, x3, y3) {
|
||||
var x10 = x1 - x0, y10 = y1 - y0,
|
||||
x32 = x3 - x2, y32 = y3 - y2,
|
||||
t = y32 * x10 - x32 * y10;
|
||||
if (t * t < index.epsilon) return;
|
||||
t = (x32 * (y0 - y2) - y32 * (x0 - x2)) / t;
|
||||
return [x0 + t * x10, y0 + t * y10];
|
||||
}
|
||||
|
||||
// Compute perpendicular offset line of length rc.
|
||||
// http://mathworld.wolfram.com/Circle-LineIntersection.html
|
||||
function cornerTangents(x0, y0, x1, y1, r1, rc, cw) {
|
||||
var x01 = x0 - x1,
|
||||
y01 = y0 - y1,
|
||||
lo = (cw ? rc : -rc) / index.sqrt(x01 * x01 + y01 * y01),
|
||||
ox = lo * y01,
|
||||
oy = -lo * x01,
|
||||
x11 = x0 + ox,
|
||||
y11 = y0 + oy,
|
||||
x10 = x1 + ox,
|
||||
y10 = y1 + oy,
|
||||
x00 = (x11 + x10) / 2,
|
||||
y00 = (y11 + y10) / 2,
|
||||
dx = x10 - x11,
|
||||
dy = y10 - y11,
|
||||
d2 = dx * dx + dy * dy,
|
||||
r = r1 - rc,
|
||||
D = x11 * y10 - x10 * y11,
|
||||
d = (dy < 0 ? -1 : 1) * index.sqrt(index.max(0, r * r * d2 - D * D)),
|
||||
cx0 = (D * dy - dx * d) / d2,
|
||||
cy0 = (-D * dx - dy * d) / d2,
|
||||
cx1 = (D * dy + dx * d) / d2,
|
||||
cy1 = (-D * dx + dy * d) / d2,
|
||||
dx0 = cx0 - x00,
|
||||
dy0 = cy0 - y00,
|
||||
dx1 = cx1 - x00,
|
||||
dy1 = cy1 - y00;
|
||||
|
||||
// Pick the closer of the two intersection points.
|
||||
// TODO Is there a faster way to determine which intersection to use?
|
||||
if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1;
|
||||
|
||||
return {
|
||||
cx: cx0,
|
||||
cy: cy0,
|
||||
x01: -ox,
|
||||
y01: -oy,
|
||||
x11: cx0 * (r1 / r - 1),
|
||||
y11: cy0 * (r1 / r - 1)
|
||||
};
|
||||
}
|
||||
|
||||
function d3arc() {
|
||||
var innerRadius = arcInnerRadius,
|
||||
outerRadius = arcOuterRadius,
|
||||
cornerRadius = index.constant(0),
|
||||
padRadius = null,
|
||||
startAngle = arcStartAngle,
|
||||
endAngle = arcEndAngle,
|
||||
padAngle = arcPadAngle,
|
||||
context = null,
|
||||
path = index.withPath(arc);
|
||||
|
||||
function arc() {
|
||||
var buffer,
|
||||
r,
|
||||
r0 = +innerRadius.apply(this, arguments),
|
||||
r1 = +outerRadius.apply(this, arguments),
|
||||
a0 = startAngle.apply(this, arguments) - index.halfPi,
|
||||
a1 = endAngle.apply(this, arguments) - index.halfPi,
|
||||
da = index.abs(a1 - a0),
|
||||
cw = a1 > a0;
|
||||
|
||||
if (!context) context = buffer = path();
|
||||
|
||||
// Ensure that the outer radius is always larger than the inner radius.
|
||||
if (r1 < r0) r = r1, r1 = r0, r0 = r;
|
||||
|
||||
// Is it a point?
|
||||
if (!(r1 > index.epsilon)) context.moveTo(0, 0);
|
||||
|
||||
// Or is it a circle or annulus?
|
||||
else if (da > index.tau - index.epsilon) {
|
||||
context.moveTo(r1 * index.cos(a0), r1 * index.sin(a0));
|
||||
context.arc(0, 0, r1, a0, a1, !cw);
|
||||
if (r0 > index.epsilon) {
|
||||
context.moveTo(r0 * index.cos(a1), r0 * index.sin(a1));
|
||||
context.arc(0, 0, r0, a1, a0, cw);
|
||||
}
|
||||
}
|
||||
|
||||
// Or is it a circular or annular sector?
|
||||
else {
|
||||
var a01 = a0,
|
||||
a11 = a1,
|
||||
a00 = a0,
|
||||
a10 = a1,
|
||||
da0 = da,
|
||||
da1 = da,
|
||||
ap = padAngle.apply(this, arguments) / 2,
|
||||
rp = (ap > index.epsilon) && (padRadius ? +padRadius.apply(this, arguments) : index.sqrt(r0 * r0 + r1 * r1)),
|
||||
rc = index.min(index.abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments)),
|
||||
rc0 = rc,
|
||||
rc1 = rc,
|
||||
t0,
|
||||
t1;
|
||||
|
||||
// Apply padding? Note that since r1 ≥ r0, da1 ≥ da0.
|
||||
if (rp > index.epsilon) {
|
||||
var p0 = index.asin(rp / r0 * index.sin(ap)),
|
||||
p1 = index.asin(rp / r1 * index.sin(ap));
|
||||
if ((da0 -= p0 * 2) > index.epsilon) p0 *= (cw ? 1 : -1), a00 += p0, a10 -= p0;
|
||||
else da0 = 0, a00 = a10 = (a0 + a1) / 2;
|
||||
if ((da1 -= p1 * 2) > index.epsilon) p1 *= (cw ? 1 : -1), a01 += p1, a11 -= p1;
|
||||
else da1 = 0, a01 = a11 = (a0 + a1) / 2;
|
||||
}
|
||||
|
||||
var x01 = r1 * index.cos(a01),
|
||||
y01 = r1 * index.sin(a01),
|
||||
x10 = r0 * index.cos(a10),
|
||||
y10 = r0 * index.sin(a10);
|
||||
|
||||
// Apply rounded corners?
|
||||
if (rc > index.epsilon) {
|
||||
var x11 = r1 * index.cos(a11),
|
||||
y11 = r1 * index.sin(a11),
|
||||
x00 = r0 * index.cos(a00),
|
||||
y00 = r0 * index.sin(a00),
|
||||
oc;
|
||||
|
||||
// Restrict the corner radius according to the sector angle. If this
|
||||
// intersection fails, it’s probably because the arc is too small, so
|
||||
// disable the corner radius entirely.
|
||||
if (da < index.pi) {
|
||||
if (oc = intersect(x01, y01, x00, y00, x11, y11, x10, y10)) {
|
||||
var ax = x01 - oc[0],
|
||||
ay = y01 - oc[1],
|
||||
bx = x11 - oc[0],
|
||||
by = y11 - oc[1],
|
||||
kc = 1 / index.sin(index.acos((ax * bx + ay * by) / (index.sqrt(ax * ax + ay * ay) * index.sqrt(bx * bx + by * by))) / 2),
|
||||
lc = index.sqrt(oc[0] * oc[0] + oc[1] * oc[1]);
|
||||
rc0 = index.min(rc, (r0 - lc) / (kc - 1));
|
||||
rc1 = index.min(rc, (r1 - lc) / (kc + 1));
|
||||
} else {
|
||||
rc0 = rc1 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Is the sector collapsed to a line?
|
||||
if (!(da1 > index.epsilon)) context.moveTo(x01, y01);
|
||||
|
||||
// Does the sector’s outer ring have rounded corners?
|
||||
else if (rc1 > index.epsilon) {
|
||||
t0 = cornerTangents(x00, y00, x01, y01, r1, rc1, cw);
|
||||
t1 = cornerTangents(x11, y11, x10, y10, r1, rc1, cw);
|
||||
|
||||
context.moveTo(t0.cx + t0.x01, t0.cy + t0.y01);
|
||||
|
||||
// Have the corners merged?
|
||||
if (rc1 < rc) context.arc(t0.cx, t0.cy, rc1, index.atan2(t0.y01, t0.x01), index.atan2(t1.y01, t1.x01), !cw);
|
||||
|
||||
// Otherwise, draw the two corners and the ring.
|
||||
else {
|
||||
context.arc(t0.cx, t0.cy, rc1, index.atan2(t0.y01, t0.x01), index.atan2(t0.y11, t0.x11), !cw);
|
||||
context.arc(0, 0, r1, index.atan2(t0.cy + t0.y11, t0.cx + t0.x11), index.atan2(t1.cy + t1.y11, t1.cx + t1.x11), !cw);
|
||||
context.arc(t1.cx, t1.cy, rc1, index.atan2(t1.y11, t1.x11), index.atan2(t1.y01, t1.x01), !cw);
|
||||
}
|
||||
}
|
||||
|
||||
// Or is the outer ring just a circular arc?
|
||||
else context.moveTo(x01, y01), context.arc(0, 0, r1, a01, a11, !cw);
|
||||
|
||||
// Is there no inner ring, and it’s a circular sector?
|
||||
// Or perhaps it’s an annular sector collapsed due to padding?
|
||||
if (!(r0 > index.epsilon) || !(da0 > index.epsilon)) context.lineTo(x10, y10);
|
||||
|
||||
// Does the sector’s inner ring (or point) have rounded corners?
|
||||
else if (rc0 > index.epsilon) {
|
||||
t0 = cornerTangents(x10, y10, x11, y11, r0, -rc0, cw);
|
||||
t1 = cornerTangents(x01, y01, x00, y00, r0, -rc0, cw);
|
||||
|
||||
context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01);
|
||||
|
||||
// Have the corners merged?
|
||||
if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, index.atan2(t0.y01, t0.x01), index.atan2(t1.y01, t1.x01), !cw);
|
||||
|
||||
// Otherwise, draw the two corners and the ring.
|
||||
else {
|
||||
context.arc(t0.cx, t0.cy, rc0, index.atan2(t0.y01, t0.x01), index.atan2(t0.y11, t0.x11), !cw);
|
||||
context.arc(0, 0, r0, index.atan2(t0.cy + t0.y11, t0.cx + t0.x11), index.atan2(t1.cy + t1.y11, t1.cx + t1.x11), cw);
|
||||
context.arc(t1.cx, t1.cy, rc0, index.atan2(t1.y11, t1.x11), index.atan2(t1.y01, t1.x01), !cw);
|
||||
}
|
||||
}
|
||||
|
||||
// Or is the inner ring just a circular arc?
|
||||
else context.arc(0, 0, r0, a10, a00, cw);
|
||||
}
|
||||
|
||||
context.closePath();
|
||||
|
||||
if (buffer) return context = null, buffer + "" || null;
|
||||
}
|
||||
|
||||
arc.centroid = function() {
|
||||
var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2,
|
||||
a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - index.pi / 2;
|
||||
return [index.cos(a) * r, index.sin(a) * r];
|
||||
};
|
||||
|
||||
arc.innerRadius = function(_) {
|
||||
return arguments.length ? (innerRadius = typeof _ === "function" ? _ : index.constant(+_), arc) : innerRadius;
|
||||
};
|
||||
|
||||
arc.outerRadius = function(_) {
|
||||
return arguments.length ? (outerRadius = typeof _ === "function" ? _ : index.constant(+_), arc) : outerRadius;
|
||||
};
|
||||
|
||||
arc.cornerRadius = function(_) {
|
||||
return arguments.length ? (cornerRadius = typeof _ === "function" ? _ : index.constant(+_), arc) : cornerRadius;
|
||||
};
|
||||
|
||||
arc.padRadius = function(_) {
|
||||
return arguments.length ? (padRadius = _ == null ? null : typeof _ === "function" ? _ : index.constant(+_), arc) : padRadius;
|
||||
};
|
||||
|
||||
arc.startAngle = function(_) {
|
||||
return arguments.length ? (startAngle = typeof _ === "function" ? _ : index.constant(+_), arc) : startAngle;
|
||||
};
|
||||
|
||||
arc.endAngle = function(_) {
|
||||
return arguments.length ? (endAngle = typeof _ === "function" ? _ : index.constant(+_), arc) : endAngle;
|
||||
};
|
||||
|
||||
arc.padAngle = function(_) {
|
||||
return arguments.length ? (padAngle = typeof _ === "function" ? _ : index.constant(+_), arc) : padAngle;
|
||||
};
|
||||
|
||||
arc.context = function(_) {
|
||||
return arguments.length ? ((context = _ == null ? null : _), arc) : context;
|
||||
};
|
||||
|
||||
return arc;
|
||||
}
|
||||
|
||||
exports.d3arc = d3arc;
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,3 +0,0 @@
|
||||
var __viteBrowserExternal = {};
|
||||
|
||||
export { __viteBrowserExternal as default };
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,7 +0,0 @@
|
||||
const lang = Object.freeze(JSON.parse("{\"displayName\":\"Berry\",\"name\":\"berry\",\"patterns\":[{\"include\":\"#controls\"},{\"include\":\"#strings\"},{\"include\":\"#comment-block\"},{\"include\":\"#comments\"},{\"include\":\"#keywords\"},{\"include\":\"#function\"},{\"include\":\"#member\"},{\"include\":\"#identifier\"},{\"include\":\"#number\"},{\"include\":\"#operator\"}],\"repository\":{\"comment-block\":{\"begin\":\"#-\",\"end\":\"-#\",\"name\":\"comment.berry\",\"patterns\":[{}]},\"comments\":{\"begin\":\"#\",\"end\":\"\\\\n\",\"name\":\"comment.line.berry\",\"patterns\":[{}]},\"controls\":{\"patterns\":[{\"match\":\"\\\\b(if|elif|else|for|while|do|end|break|continue|return|try|except|raise)\\\\b\",\"name\":\"keyword.control.berry\"}]},\"function\":{\"patterns\":[{\"match\":\"\\\\b([A-Z_a-z][0-9A-Z_a-z]*(?=\\\\s*\\\\())\",\"name\":\"entity.name.function.berry\"}]},\"identifier\":{\"patterns\":[{\"match\":\"\\\\b[A-Z_a-z]\\\\w+\\\\b\",\"name\":\"identifier.berry\"}]},\"keywords\":{\"patterns\":[{\"match\":\"\\\\b(var|static|def|class|true|false|nil|self|super|import|as|_class)\\\\b\",\"name\":\"keyword.berry\"}]},\"member\":{\"patterns\":[{\"captures\":{\"0\":{\"name\":\"entity.other.attribute-name.berry\"}},\"match\":\"\\\\.([A-Z_a-z][0-9A-Z_a-z]*)\"}]},\"number\":{\"patterns\":[{\"match\":\"0x\\\\h+|\\\\d+|(\\\\d+\\\\.?|\\\\.\\\\d)\\\\d*([Ee][-+]?\\\\d+)?\",\"name\":\"constant.numeric.berry\"}]},\"operator\":{\"patterns\":[{\"match\":\"[-\\\\]!%\\\\&(-+./:<=>\\\\[^|~]\",\"name\":\"keyword.operator.berry\"}]},\"strings\":{\"patterns\":[{\"begin\":\"f(?=[\\\"'])\",\"patterns\":[{\"begin\":\"\\\"\",\"end\":\"\\\"\",\"name\":\"string.quoted.other.berry\",\"patterns\":[{\"match\":\"(\\\\\\\\x\\\\h{2})|(\\\\\\\\[0-7]{3})|(\\\\\\\\\\\\\\\\)|(\\\\\\\\\\\")|(\\\\\\\\')|(\\\\\\\\a)|(\\\\\\\\b)|(\\\\\\\\f)|(\\\\\\\\n)|(\\\\\\\\r)|(\\\\\\\\t)|(\\\\\\\\v)\",\"name\":\"constant.character.escape.berry\"},{\"match\":\"\\\\{\\\\{[^}]*}}\",\"name\":\"string.quoted.other.berry\"},{\"begin\":\"\\\\{\",\"end\":\"}\",\"name\":\"keyword.other.unit.berry\",\"patterns\":[{\"include\":\"#keywords\"},{\"include\":\"#numbers\"},{\"include\":\"#identifier\"},{\"include\":\"#operator\"},{\"include\":\"#member\"},{\"include\":\"#function\"}]}]},{\"begin\":\"'\",\"end\":\"'\",\"name\":\"string.quoted.other.berry\",\"patterns\":[{\"match\":\"(\\\\\\\\x\\\\h{2})|(\\\\\\\\[0-7]{3})|(\\\\\\\\\\\\\\\\)|(\\\\\\\\\\\")|(\\\\\\\\')|(\\\\\\\\a)|(\\\\\\\\b)|(\\\\\\\\f)|(\\\\\\\\n)|(\\\\\\\\r)|(\\\\\\\\t)|(\\\\\\\\v)\",\"name\":\"constant.character.escape.berry\"},{\"match\":\"\\\\{\\\\{[^}]*}}\",\"name\":\"string.quoted.other.berry\"},{\"begin\":\"\\\\{\",\"end\":\"}\",\"name\":\"keyword.other.unit.berry\",\"patterns\":[{\"include\":\"#keywords\"},{\"include\":\"#numbers\"},{\"include\":\"#identifier\"},{\"include\":\"#operator\"},{\"include\":\"#member\"},{\"include\":\"#function\"}]}]}],\"while\":\"\\\\G|^[\\\\t ]*(?=[\\\"'])\"},{\"begin\":\"([\\\"'])\",\"end\":\"\\\\1\",\"name\":\"string.quoted.double.berry\",\"patterns\":[{\"match\":\"(\\\\\\\\x\\\\h{2})|(\\\\\\\\[0-7]{3})|(\\\\\\\\\\\\\\\\)|(\\\\\\\\\\\")|(\\\\\\\\')|(\\\\\\\\a)|(\\\\\\\\b)|(\\\\\\\\f)|(\\\\\\\\n)|(\\\\\\\\r)|(\\\\\\\\t)|(\\\\\\\\v)\",\"name\":\"constant.character.escape.berry\"}]}]}},\"scopeName\":\"source.berry\",\"aliases\":[\"be\"]}"));
|
||||
|
||||
const berry = [
|
||||
lang
|
||||
];
|
||||
|
||||
export { berry as default };
|
||||
@@ -1,11 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
||||
|
||||
const lang = Object.freeze(JSON.parse("{\"displayName\":\"Berry\",\"name\":\"berry\",\"patterns\":[{\"include\":\"#controls\"},{\"include\":\"#strings\"},{\"include\":\"#comment-block\"},{\"include\":\"#comments\"},{\"include\":\"#keywords\"},{\"include\":\"#function\"},{\"include\":\"#member\"},{\"include\":\"#identifier\"},{\"include\":\"#number\"},{\"include\":\"#operator\"}],\"repository\":{\"comment-block\":{\"begin\":\"#-\",\"end\":\"-#\",\"name\":\"comment.berry\",\"patterns\":[{}]},\"comments\":{\"begin\":\"#\",\"end\":\"\\\\n\",\"name\":\"comment.line.berry\",\"patterns\":[{}]},\"controls\":{\"patterns\":[{\"match\":\"\\\\b(if|elif|else|for|while|do|end|break|continue|return|try|except|raise)\\\\b\",\"name\":\"keyword.control.berry\"}]},\"function\":{\"patterns\":[{\"match\":\"\\\\b([A-Z_a-z][0-9A-Z_a-z]*(?=\\\\s*\\\\())\",\"name\":\"entity.name.function.berry\"}]},\"identifier\":{\"patterns\":[{\"match\":\"\\\\b[A-Z_a-z]\\\\w+\\\\b\",\"name\":\"identifier.berry\"}]},\"keywords\":{\"patterns\":[{\"match\":\"\\\\b(var|static|def|class|true|false|nil|self|super|import|as|_class)\\\\b\",\"name\":\"keyword.berry\"}]},\"member\":{\"patterns\":[{\"captures\":{\"0\":{\"name\":\"entity.other.attribute-name.berry\"}},\"match\":\"\\\\.([A-Z_a-z][0-9A-Z_a-z]*)\"}]},\"number\":{\"patterns\":[{\"match\":\"0x\\\\h+|\\\\d+|(\\\\d+\\\\.?|\\\\.\\\\d)\\\\d*([Ee][-+]?\\\\d+)?\",\"name\":\"constant.numeric.berry\"}]},\"operator\":{\"patterns\":[{\"match\":\"[-\\\\]!%\\\\&(-+./:<=>\\\\[^|~]\",\"name\":\"keyword.operator.berry\"}]},\"strings\":{\"patterns\":[{\"begin\":\"f(?=[\\\"'])\",\"patterns\":[{\"begin\":\"\\\"\",\"end\":\"\\\"\",\"name\":\"string.quoted.other.berry\",\"patterns\":[{\"match\":\"(\\\\\\\\x\\\\h{2})|(\\\\\\\\[0-7]{3})|(\\\\\\\\\\\\\\\\)|(\\\\\\\\\\\")|(\\\\\\\\')|(\\\\\\\\a)|(\\\\\\\\b)|(\\\\\\\\f)|(\\\\\\\\n)|(\\\\\\\\r)|(\\\\\\\\t)|(\\\\\\\\v)\",\"name\":\"constant.character.escape.berry\"},{\"match\":\"\\\\{\\\\{[^}]*}}\",\"name\":\"string.quoted.other.berry\"},{\"begin\":\"\\\\{\",\"end\":\"}\",\"name\":\"keyword.other.unit.berry\",\"patterns\":[{\"include\":\"#keywords\"},{\"include\":\"#numbers\"},{\"include\":\"#identifier\"},{\"include\":\"#operator\"},{\"include\":\"#member\"},{\"include\":\"#function\"}]}]},{\"begin\":\"'\",\"end\":\"'\",\"name\":\"string.quoted.other.berry\",\"patterns\":[{\"match\":\"(\\\\\\\\x\\\\h{2})|(\\\\\\\\[0-7]{3})|(\\\\\\\\\\\\\\\\)|(\\\\\\\\\\\")|(\\\\\\\\')|(\\\\\\\\a)|(\\\\\\\\b)|(\\\\\\\\f)|(\\\\\\\\n)|(\\\\\\\\r)|(\\\\\\\\t)|(\\\\\\\\v)\",\"name\":\"constant.character.escape.berry\"},{\"match\":\"\\\\{\\\\{[^}]*}}\",\"name\":\"string.quoted.other.berry\"},{\"begin\":\"\\\\{\",\"end\":\"}\",\"name\":\"keyword.other.unit.berry\",\"patterns\":[{\"include\":\"#keywords\"},{\"include\":\"#numbers\"},{\"include\":\"#identifier\"},{\"include\":\"#operator\"},{\"include\":\"#member\"},{\"include\":\"#function\"}]}]}],\"while\":\"\\\\G|^[\\\\t ]*(?=[\\\"'])\"},{\"begin\":\"([\\\"'])\",\"end\":\"\\\\1\",\"name\":\"string.quoted.double.berry\",\"patterns\":[{\"match\":\"(\\\\\\\\x\\\\h{2})|(\\\\\\\\[0-7]{3})|(\\\\\\\\\\\\\\\\)|(\\\\\\\\\\\")|(\\\\\\\\')|(\\\\\\\\a)|(\\\\\\\\b)|(\\\\\\\\f)|(\\\\\\\\n)|(\\\\\\\\r)|(\\\\\\\\t)|(\\\\\\\\v)\",\"name\":\"constant.character.escape.berry\"}]}]}},\"scopeName\":\"source.berry\",\"aliases\":[\"be\"]}"));
|
||||
|
||||
const berry = [
|
||||
lang
|
||||
];
|
||||
|
||||
exports.default = berry;
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,14 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
||||
|
||||
const python = require('./python-R5Doq6Ap.cjs');
|
||||
|
||||
const lang = Object.freeze(JSON.parse("{\"displayName\":\"Cairo\",\"name\":\"cairo\",\"patterns\":[{\"begin\":\"\\\\b(if).*\\\\(\",\"beginCaptures\":{\"1\":{\"name\":\"keyword.control.if\"},\"2\":{\"name\":\"entity.name.condition\"}},\"contentName\":\"source.cairo0\",\"end\":\"}\",\"endCaptures\":{\"0\":{\"name\":\"keyword.control.end\"}},\"name\":\"meta.control.if\",\"patterns\":[{\"include\":\"source.cairo0\"}]},{\"begin\":\"\\\\b(with)\\\\s+(.+)\\\\s*\\\\{\",\"beginCaptures\":{\"1\":{\"name\":\"keyword.control.with\"},\"2\":{\"name\":\"entity.name.identifiers\"}},\"contentName\":\"source.cairo0\",\"end\":\"}\",\"endCaptures\":{\"0\":{\"name\":\"keyword.control.end\"}},\"name\":\"meta.control.with\",\"patterns\":[{\"include\":\"source.cairo0\"}]},{\"begin\":\"\\\\b(with_attr)\\\\s+([A-Z_a-z][0-9A-Z_a-z]*)\\\\s*[({]\",\"beginCaptures\":{\"1\":{\"name\":\"keyword.control.with_attr\"},\"2\":{\"name\":\"entity.name.function\"}},\"contentName\":\"source.cairo0\",\"end\":\"}\",\"endCaptures\":{\"0\":{\"name\":\"keyword.control.end\"}},\"name\":\"meta.control.with_attr\",\"patterns\":[{\"include\":\"source.cairo0\"}]},{\"match\":\"\\\\belse\\\\b\",\"name\":\"keyword.control.else\"},{\"match\":\"\\\\b(call|jmp|ret|abs|rel|if)\\\\b\",\"name\":\"keyword.other.opcode\"},{\"match\":\"\\\\b([af]p)\\\\b\",\"name\":\"keyword.other.register\"},{\"match\":\"\\\\b(const|let|local|tempvar|felt|as|from|import|static_assert|return|assert|cast|alloc_locals|with|with_attr|nondet|dw|codeoffset|new|using|and)\\\\b\",\"name\":\"keyword.other.meta\"},{\"match\":\"\\\\b(SIZE(?:OF_LOCALS|))\\\\b\",\"name\":\"markup.italic\"},{\"match\":\"//[^\\\\n]*\\\\n\",\"name\":\"comment.line.sharp\"},{\"match\":\"\\\\b[A-Z_a-z][0-9A-Z_a-z]*:\\\\s*$\",\"name\":\"entity.name.function\"},{\"begin\":\"\\\\b(func)\\\\s+([A-Z_a-z][0-9A-Z_a-z]*)\\\\s*[({]\",\"beginCaptures\":{\"1\":{\"name\":\"storage.type.function.cairo\"},\"2\":{\"name\":\"entity.name.function\"}},\"contentName\":\"source.cairo0\",\"end\":\"}\",\"endCaptures\":{\"0\":{\"name\":\"storage.type.function.cairo\"}},\"name\":\"meta.function.cairo\",\"patterns\":[{\"include\":\"source.cairo0\"}]},{\"begin\":\"\\\\b(struct|namespace)\\\\s+([A-Z_a-z][0-9A-Z_a-z]*)\\\\s*\\\\{\",\"beginCaptures\":{\"1\":{\"name\":\"storage.type.function.cairo\"},\"2\":{\"name\":\"entity.name.function\"}},\"contentName\":\"source.cairo0\",\"end\":\"}\",\"endCaptures\":{\"0\":{\"name\":\"storage.type.function.cairo\"}},\"name\":\"meta.function.cairo\",\"patterns\":[{\"include\":\"source.cairo0\"}]},{\"match\":\"\\\\b[-+]?[0-9]+\\\\b\",\"name\":\"constant.numeric.decimal\"},{\"match\":\"\\\\b[-+]?0x\\\\h+\\\\b\",\"name\":\"constant.numeric.hexadecimal\"},{\"match\":\"'[^']*'\",\"name\":\"string.quoted.single\"},{\"match\":\"\\\"[^\\\"]*\\\"\",\"name\":\"string.quoted.double\"},{\"begin\":\"%\\\\{\",\"beginCaptures\":{\"0\":{\"name\":\"punctuation.section.embedded.begin.python\"}},\"contentName\":\"source.python\",\"end\":\"%}\",\"endCaptures\":{\"0\":{\"name\":\"punctuation.section.embedded.end.python\"},\"1\":{\"name\":\"source.python\"}},\"name\":\"meta.embedded.block.python\",\"patterns\":[{\"include\":\"source.python\"}]}],\"scopeName\":\"source.cairo0\",\"embeddedLangs\":[\"python\"]}"));
|
||||
|
||||
const cairo = [
|
||||
...python.default,
|
||||
lang
|
||||
];
|
||||
|
||||
exports.default = cairo;
|
||||
@@ -1,10 +0,0 @@
|
||||
import python from './python-CRy0opiU.js';
|
||||
|
||||
const lang = Object.freeze(JSON.parse("{\"displayName\":\"Cairo\",\"name\":\"cairo\",\"patterns\":[{\"begin\":\"\\\\b(if).*\\\\(\",\"beginCaptures\":{\"1\":{\"name\":\"keyword.control.if\"},\"2\":{\"name\":\"entity.name.condition\"}},\"contentName\":\"source.cairo0\",\"end\":\"}\",\"endCaptures\":{\"0\":{\"name\":\"keyword.control.end\"}},\"name\":\"meta.control.if\",\"patterns\":[{\"include\":\"source.cairo0\"}]},{\"begin\":\"\\\\b(with)\\\\s+(.+)\\\\s*\\\\{\",\"beginCaptures\":{\"1\":{\"name\":\"keyword.control.with\"},\"2\":{\"name\":\"entity.name.identifiers\"}},\"contentName\":\"source.cairo0\",\"end\":\"}\",\"endCaptures\":{\"0\":{\"name\":\"keyword.control.end\"}},\"name\":\"meta.control.with\",\"patterns\":[{\"include\":\"source.cairo0\"}]},{\"begin\":\"\\\\b(with_attr)\\\\s+([A-Z_a-z][0-9A-Z_a-z]*)\\\\s*[({]\",\"beginCaptures\":{\"1\":{\"name\":\"keyword.control.with_attr\"},\"2\":{\"name\":\"entity.name.function\"}},\"contentName\":\"source.cairo0\",\"end\":\"}\",\"endCaptures\":{\"0\":{\"name\":\"keyword.control.end\"}},\"name\":\"meta.control.with_attr\",\"patterns\":[{\"include\":\"source.cairo0\"}]},{\"match\":\"\\\\belse\\\\b\",\"name\":\"keyword.control.else\"},{\"match\":\"\\\\b(call|jmp|ret|abs|rel|if)\\\\b\",\"name\":\"keyword.other.opcode\"},{\"match\":\"\\\\b([af]p)\\\\b\",\"name\":\"keyword.other.register\"},{\"match\":\"\\\\b(const|let|local|tempvar|felt|as|from|import|static_assert|return|assert|cast|alloc_locals|with|with_attr|nondet|dw|codeoffset|new|using|and)\\\\b\",\"name\":\"keyword.other.meta\"},{\"match\":\"\\\\b(SIZE(?:OF_LOCALS|))\\\\b\",\"name\":\"markup.italic\"},{\"match\":\"//[^\\\\n]*\\\\n\",\"name\":\"comment.line.sharp\"},{\"match\":\"\\\\b[A-Z_a-z][0-9A-Z_a-z]*:\\\\s*$\",\"name\":\"entity.name.function\"},{\"begin\":\"\\\\b(func)\\\\s+([A-Z_a-z][0-9A-Z_a-z]*)\\\\s*[({]\",\"beginCaptures\":{\"1\":{\"name\":\"storage.type.function.cairo\"},\"2\":{\"name\":\"entity.name.function\"}},\"contentName\":\"source.cairo0\",\"end\":\"}\",\"endCaptures\":{\"0\":{\"name\":\"storage.type.function.cairo\"}},\"name\":\"meta.function.cairo\",\"patterns\":[{\"include\":\"source.cairo0\"}]},{\"begin\":\"\\\\b(struct|namespace)\\\\s+([A-Z_a-z][0-9A-Z_a-z]*)\\\\s*\\\\{\",\"beginCaptures\":{\"1\":{\"name\":\"storage.type.function.cairo\"},\"2\":{\"name\":\"entity.name.function\"}},\"contentName\":\"source.cairo0\",\"end\":\"}\",\"endCaptures\":{\"0\":{\"name\":\"storage.type.function.cairo\"}},\"name\":\"meta.function.cairo\",\"patterns\":[{\"include\":\"source.cairo0\"}]},{\"match\":\"\\\\b[-+]?[0-9]+\\\\b\",\"name\":\"constant.numeric.decimal\"},{\"match\":\"\\\\b[-+]?0x\\\\h+\\\\b\",\"name\":\"constant.numeric.hexadecimal\"},{\"match\":\"'[^']*'\",\"name\":\"string.quoted.single\"},{\"match\":\"\\\"[^\\\"]*\\\"\",\"name\":\"string.quoted.double\"},{\"begin\":\"%\\\\{\",\"beginCaptures\":{\"0\":{\"name\":\"punctuation.section.embedded.begin.python\"}},\"contentName\":\"source.python\",\"end\":\"%}\",\"endCaptures\":{\"0\":{\"name\":\"punctuation.section.embedded.end.python\"},\"1\":{\"name\":\"source.python\"}},\"name\":\"meta.embedded.block.python\",\"patterns\":[{\"include\":\"source.python\"}]}],\"scopeName\":\"source.cairo0\",\"embeddedLangs\":[\"python\"]}"));
|
||||
|
||||
const cairo = [
|
||||
...python,
|
||||
lang
|
||||
];
|
||||
|
||||
export { cairo as default };
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user