diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index c95cd52dda4..491d0cac794 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -112,7 +112,6 @@ All stable kinds (`patch`, `minor`, `major`) are computed off the latest _stable The scheduled 2x/day RC cron in [`release-rc.yml`](../../actions/workflows/release-rc.yml) is independent and continues to run automatically from `main`. - ## Release Channels The public Homebrew cask tracks stable desktop releases: diff --git a/.github/actions/install-node-dependencies/action.yml b/.github/actions/install-node-dependencies/action.yml index b67ce13cc1b..8b7801881eb 100644 --- a/.github/actions/install-node-dependencies/action.yml +++ b/.github/actions/install-node-dependencies/action.yml @@ -87,7 +87,9 @@ runs: run: | pnpm install --frozen-lockfile --ignore-scripts # Job containers can run composite steps from a source mirror without .git. - git -C "$GITHUB_WORKSPACE" diff --exit-code -- package.json pnpm-lock.yaml + if [ "$(git -C "$GITHUB_WORKSPACE" rev-parse --is-inside-work-tree 2>/dev/null)" = true ]; then + git -C "$GITHUB_WORKSPACE" diff --exit-code -- package.json pnpm-lock.yaml + fi # Why cached: `--ignore-scripts` leaves node-pty without build/Release, so # ensure-native-runtime node-gyp-compiles it in every job that asks for a runtime. diff --git a/.github/workflows/daily-mac-build.yml b/.github/workflows/daily-mac-build.yml index e445045e67c..2ef3baed2fd 100644 --- a/.github/workflows/daily-mac-build.yml +++ b/.github/workflows/daily-mac-build.yml @@ -308,7 +308,6 @@ jobs: echo "tag=$TAG" >>"$GITHUB_OUTPUT" echo "notes_file=$notes_file" >>"$GITHUB_OUTPUT" - - name: Publish daily macOS artifacts if: steps.freshness.outputs.should_build == 'true' uses: nick-fields/retry@v4 diff --git a/.github/workflows/hourly-mac-build.yml b/.github/workflows/hourly-mac-build.yml index 16e2f05579c..e072b0f9a10 100644 --- a/.github/workflows/hourly-mac-build.yml +++ b/.github/workflows/hourly-mac-build.yml @@ -290,7 +290,6 @@ jobs: including back to Stable, works in-app from there." echo "tag=$TAG" >>"$GITHUB_OUTPUT" - - name: Publish hourly macOS artifacts if: steps.freshness.outputs.should_build == 'true' uses: nick-fields/retry@v4 diff --git a/.github/workflows/node-next-compat.yml b/.github/workflows/node-next-compat.yml new file mode 100644 index 00000000000..eb9de70dab7 --- /dev/null +++ b/.github/workflows/node-next-compat.yml @@ -0,0 +1,20 @@ +name: Node next compatibility + +on: + schedule: + # Full future-runtime coverage is useful, but not worth doubling every PR matrix. + - cron: '0 10 * * *' + workflow_dispatch: + +concurrency: + group: node-next-compat + cancel-in-progress: true + +permissions: + contents: read + +jobs: + test: + uses: ./.github/workflows/unit-tests.yml + with: + node_versions: '["26"]' diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 9f3b14f5903..338bcda7dbd 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -444,16 +444,12 @@ jobs: src/shared/posix-command-path-lookup.test.ts # Cache-key input changes would otherwise make every shard compile the same - # native addon concurrently. Prime each Node ABI once before the matrix fans out. + # native addon concurrently. Prime the supported Node ABI before the matrix fans out. test_native_cache: - name: prepare test native cache node ${{ matrix.node }} + name: prepare test native cache node 24 needs: [code_paths] if: needs.code_paths.outputs.native_cache_changed == 'true' runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - node: ['24', '26'] steps: - name: Checkout @@ -464,58 +460,17 @@ jobs: - uses: ./.github/actions/install-node-dependencies with: native-runtime: node - node-version: ${{ matrix.node }} + node-version: '24' test: - name: tests node ${{ matrix.node }} ${{ matrix.shard }}/${{ matrix.shard_total }} needs: [code_paths, test_native_cache] if: >- always() && needs.code_paths.outputs.test == 'true' && (needs.test_native_cache.result == 'success' || needs.test_native_cache.result == 'skipped') - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - node: ['24', '26'] - shard: [1, 2, 3, 4, 5, 6, 7, 8] - shard_total: [8] - - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - persist-credentials: false - - - uses: ./.github/actions/install-node-dependencies - with: - native-runtime: node - node-version: ${{ matrix.node }} - - - name: Install Electron package binary for tests - run: node config/scripts/install-electron-package-binary.mjs - - - name: Test shard - run: | - pnpm exec vitest run --config config/vitest.config.ts \ - --exclude=src/main/daemon/repro-13767-shell-ready-marker-lost-to-exec.test.ts \ - --exclude=src/main/daemon/shell-ready.test.ts \ - --exclude=src/main/daemon/node-pty-fd-leak.test.ts \ - --exclude=src/main/providers/local-pty-shell-ready-zsh-launch-environment.test.ts \ - --exclude=src/main/providers/__tests__/shell-ready-framework-example.test.ts \ - --exclude=src/main/pty/omp-shell-wrapper.node-pty.test.ts \ - --exclude=src/main/shell-startup-feature-channel.test.ts \ - --exclude=src/main/terminal-history-fish-session.node-pty.test.ts \ - --exclude=src/main/zsh-scoped-histfile.live-shell.test.ts \ - --exclude=src/main/zsh-startup-hook-user-config-equivalence.live-shell.test.ts \ - --exclude=src/main/zsh-wrapper-version-mismatch.live-shell.test.ts \ - --exclude=src/renderer/src/components/terminal-pane/fish-color-scheme-child-stdin.node-pty.test.ts \ - --exclude=src/shared/fish-query-reply-child-stdin.node-pty.test.ts \ - --exclude=src/shared/pty-reply-echo-shapes.node-pty.test.ts \ - --exclude=src/shared/startup-shell-portability.live-shell.test.ts \ - --exclude=src/shared/posix-command-path-lookup.test.ts \ - --exclude=tests/e2e/cross-version-wire/** \ - --shard=${{ matrix.shard }}/${{ matrix.shard_total }} + uses: ./.github/workflows/unit-tests.yml + with: + node_versions: '["24"]' # Why a separate job: the test needs a real Chrome, and the sharded `test` matrix # would pay for it on every shard to run one file in whichever shard it landed in. diff --git a/.github/workflows/pullfrog.yml b/.github/workflows/pullfrog.yml index 19401170c0e..d5030252f88 100644 --- a/.github/workflows/pullfrog.yml +++ b/.github/workflows/pullfrog.yml @@ -34,8 +34,7 @@ jobs: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - GOOGLE_GENERATIVE_AI_API_KEY: - ${{ secrets.GOOGLE_GENERATIVE_AI_API_KEY }} + GOOGLE_GENERATIVE_AI_API_KEY: ${{ secrets.GOOGLE_GENERATIVE_AI_API_KEY }} GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} XAI_API_KEY: ${{ secrets.XAI_API_KEY }} DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} diff --git a/.github/workflows/release-cut.yml b/.github/workflows/release-cut.yml index c035080a203..af0fe9b45ba 100644 --- a/.github/workflows/release-cut.yml +++ b/.github/workflows/release-cut.yml @@ -844,6 +844,16 @@ jobs: with: ref: refs/tags/${{ needs.cut.outputs.tag }} + - name: Restore golden test harness from the workflow ref + shell: bash + env: + WORKFLOW_SHA: ${{ github.workflow_sha }} + run: | + git fetch --no-tags --depth=1 origin "$WORKFLOW_SHA" + git checkout "$WORKFLOW_SHA" -- \ + tests/e2e/golden-source-control-open-diff.spec.ts \ + tests/e2e/golden-terminal-file-link.spec.ts + - name: Install native build tools if: runner.os == 'Linux' run: sudo apt-get update && sudo apt-get install -y build-essential python3 xvfb @@ -946,6 +956,16 @@ jobs: with: ref: refs/tags/${{ needs.cut.outputs.tag }} + - name: Restore skill-sharing test harness from the workflow ref + shell: bash + env: + WORKFLOW_SHA: ${{ github.workflow_sha }} + run: | + git fetch --no-tags --depth=1 origin "$WORKFLOW_SHA" + git checkout "$WORKFLOW_SHA" -- \ + src/main/skills/skill-freshness-inventory.test.ts \ + src/main/skills/skill-provider-runtime-roots.test.ts + - uses: ./.github/actions/install-node-dependencies with: native-runtime: node @@ -978,13 +998,26 @@ jobs: steps: - name: Install Ubuntu 20.04 prerequisites - run: apt-get update && apt-get install -y build-essential ca-certificates git python3 + run: apt-get update && apt-get install -y build-essential ca-certificates git python3 unzip - name: Checkout uses: actions/checkout@v6 with: ref: refs/tags/${{ needs.cut.outputs.tag }} + - name: Trust the checked-out workspace in the job container + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + + - name: Restore skill-sharing test harness from the workflow ref + shell: bash + env: + WORKFLOW_SHA: ${{ github.workflow_sha }} + run: | + git fetch --no-tags --depth=1 origin "$WORKFLOW_SHA" + git checkout "$WORKFLOW_SHA" -- \ + src/main/skills/skill-freshness-inventory.test.ts \ + src/main/skills/skill-provider-runtime-roots.test.ts + - uses: ./.github/actions/install-node-dependencies with: native-runtime: node diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml new file mode 100644 index 00000000000..9906b569324 --- /dev/null +++ b/.github/workflows/unit-tests.yml @@ -0,0 +1,59 @@ +name: Unit tests + +on: + workflow_call: + inputs: + node_versions: + description: JSON array of Node.js major versions to test. + required: true + type: string + +permissions: + contents: read + +jobs: + test: + name: tests node ${{ matrix.node }} ${{ matrix.shard }}/${{ matrix.shard_total }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node: ${{ fromJSON(inputs.node_versions) }} + shard: [1, 2, 3, 4, 5, 6, 7, 8] + shard_total: [8] + + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + persist-credentials: false + + - uses: ./.github/actions/install-node-dependencies + with: + native-runtime: node + node-version: ${{ matrix.node }} + + - name: Install Electron package binary for tests + run: node config/scripts/install-electron-package-binary.mjs + + - name: Test shard + run: | + pnpm exec vitest run --config config/vitest.config.ts \ + --exclude=src/main/daemon/repro-13767-shell-ready-marker-lost-to-exec.test.ts \ + --exclude=src/main/daemon/shell-ready.test.ts \ + --exclude=src/main/daemon/node-pty-fd-leak.test.ts \ + --exclude=src/main/providers/local-pty-shell-ready-zsh-launch-environment.test.ts \ + --exclude=src/main/providers/__tests__/shell-ready-framework-example.test.ts \ + --exclude=src/main/pty/omp-shell-wrapper.node-pty.test.ts \ + --exclude=src/main/shell-startup-feature-channel.test.ts \ + --exclude=src/main/terminal-history-fish-session.node-pty.test.ts \ + --exclude=src/main/zsh-scoped-histfile.live-shell.test.ts \ + --exclude=src/main/zsh-startup-hook-user-config-equivalence.live-shell.test.ts \ + --exclude=src/main/zsh-wrapper-version-mismatch.live-shell.test.ts \ + --exclude=src/renderer/src/components/terminal-pane/fish-color-scheme-child-stdin.node-pty.test.ts \ + --exclude=src/shared/fish-query-reply-child-stdin.node-pty.test.ts \ + --exclude=src/shared/pty-reply-echo-shapes.node-pty.test.ts \ + --exclude=src/shared/startup-shell-portability.live-shell.test.ts \ + --exclude=src/shared/posix-command-path-lookup.test.ts \ + --exclude=tests/e2e/cross-version-wire/** \ + --shard=${{ matrix.shard }}/${{ matrix.shard_total }} diff --git a/AGENTS.md b/AGENTS.md index 307c975d219..1fbce202438 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,13 +7,15 @@ All UI work — layout, color, typography, spacing, component selection, UX beha Use the `$electron` skill and Playwright CDP for rendered Orca UI checks. Do not use computer-use for Orca UI validation. # Style + ## Reuse Before Reimplementing Before writing new logic at any scale — a function, component, IPC channel, state store, or whole subsystem/flow — check whether an existing implementation already does the job (or nearly does). Extend or generalize it instead of building a parallel version; only write from scratch when nothing fits. Keep the check proportionate: a quick search for trivial code, a real one before building anything substantial. ## Concise/Brief Non-obvious Comments ONLY - * DO NOT: be verbose, explain the obvious, walk through the code ("WHY not HOW") - * BE CONCISE. 1 LINE if possible + +- DO NOT: be verbose, explain the obvious, walk through the code ("WHY not HOW") +- BE CONCISE. 1 LINE if possible ## Lint Rules: Do Not Disable Max Lines @@ -32,6 +34,7 @@ Never use vague names like `helpers`, `utils`, `common`, `misc`, or `shared-stuf - **Lint**: `oxlint`, or `pnpm run check:code-quality:changed` for changed files (full `pnpm lint` is slow); format with `pnpm format` # Considerations + ## Worktree Safety Always use the primary working directory (the worktree) for all file reads and edits. Never follow absolute paths from subagent results that point to the main repo. diff --git a/README.md b/README.md index 8de0ff8d2b9..82dfdbaa5c9 100644 --- a/README.md +++ b/README.md @@ -262,6 +262,7 @@ Want to contribute or run locally? See our [CONTRIBUTING.md](.github/CONTRIBUTIN
## Signed Builds + Windows code signing sponored/provided by [SignPath.io](https://signpath.io), certificate by [SignPath Foundation](https://signpath.org). ## License diff --git a/config/patches/@vscode__windows-process-tree@0.8.0.patch b/config/patches/@vscode__windows-process-tree@0.8.0.patch index 8e6e8e86e25..65cf2be5ce4 100644 --- a/config/patches/@vscode__windows-process-tree@0.8.0.patch +++ b/config/patches/@vscode__windows-process-tree@0.8.0.patch @@ -1,26 +1,31 @@ diff --git a/binding.gyp b/binding.gyp -index 855bd4b86f0a3c18c7594212c0e42b6e35bc4001..5f15551cb1520af996f216500a83ac68b57f5104 100644 +index 855bd4b86f0a3c18c7594212c0e42b6e35bc4001..33774e7ae296f0de39dd94156673c9e773638bf4 100644 --- a/binding.gyp +++ b/binding.gyp -@@ -3,7 +3,7 @@ +@@ -3,7 +3,6 @@ { "target_name": "windows_process_tree", "dependencies": [ - "h2.deleteProgram(this._program))),this._projectionLocation=(0,n.throwIfFalsy)(h2.getUniformLocation(this._program,"u_projection")),this._resolutionLocation=(0,n.throwIfFalsy)(h2.getUniformLocation(this._program,"u_resolution")),this._textureLocation=(0,n.throwIfFalsy)(h2.getUniformLocation(this._program,"u_texture"));const T2=this._vertexArrayObject=h2.createVertexArray();this._register((0,a.toDisposable)(()=>h2.deleteVertexArray(T2))),h2.bindVertexArray(T2);const l2=new Float32Array([0,0,1,0,0,1,1,1]),d2=h2.createBuffer();this._register((0,a.toDisposable)(()=>h2.deleteBuffer(d2))),h2.bindBuffer(h2.ARRAY_BUFFER,d2),h2.bufferData(h2.ARRAY_BUFFER,l2,h2.STATIC_DRAW),h2.enableVertexAttribArray(0),h2.vertexAttribPointer(0,2,this._gl.FLOAT,false,0,0);const c=new Uint8Array([0,1,2,3]),_=h2.createBuffer();this._register((0,a.toDisposable)(()=>h2.deleteBuffer(_))),h2.bindBuffer(h2.ELEMENT_ARRAY_BUFFER,_),h2.bufferData(h2.ELEMENT_ARRAY_BUFFER,c,h2.STATIC_DRAW),this._attributesBuffer=(0,n.throwIfFalsy)(h2.createBuffer()),this._register((0,a.toDisposable)(()=>h2.deleteBuffer(this._attributesBuffer))),h2.bindBuffer(h2.ARRAY_BUFFER,this._attributesBuffer),h2.enableVertexAttribArray(2),h2.vertexAttribPointer(2,2,h2.FLOAT,false,44,0),h2.vertexAttribDivisor(2,1),h2.enableVertexAttribArray(3),h2.vertexAttribPointer(3,2,h2.FLOAT,false,44,2*Float32Array.BYTES_PER_ELEMENT),h2.vertexAttribDivisor(3,1),h2.enableVertexAttribArray(4),h2.vertexAttribPointer(4,1,h2.FLOAT,false,44,4*Float32Array.BYTES_PER_ELEMENT),h2.vertexAttribDivisor(4,1),h2.enableVertexAttribArray(5),h2.vertexAttribPointer(5,2,h2.FLOAT,false,44,5*Float32Array.BYTES_PER_ELEMENT),h2.vertexAttribDivisor(5,1),h2.enableVertexAttribArray(6),h2.vertexAttribPointer(6,2,h2.FLOAT,false,44,7*Float32Array.BYTES_PER_ELEMENT),h2.vertexAttribDivisor(6,1),h2.enableVertexAttribArray(1),h2.vertexAttribPointer(1,2,h2.FLOAT,false,44,9*Float32Array.BYTES_PER_ELEMENT),h2.vertexAttribDivisor(1,1),h2.useProgram(this._program);const Q=new Int32Array(s2.TextureAtlas.maxAtlasPages);for(let t4=0;t4>1&1,d=o&1;h!==0||d!==0?T.setTransform(new DOMMatrix().translateSelf(-h,-d)):T.setTransform(new DOMMatrix),i.fillStyle=T,i.fillRect(t,r,s,a)}function Wt(i,e,t,r,s,a,o,n,L){i.save(),i.beginPath(),i.rect(t,r,s,a),i.clip(),i.beginPath();let T;if(typeof e=="function"){let _=.15/a*s;T=e(.15,_)}else T=e;let h={currentX:0,currentY:0,lastControlX:0,lastControlY:0,lastCommand:""};for(let d of T.split(" ")){let _=d[0];if(_==="Z"){i.closePath(),h.lastCommand=_;continue}let p=je[_];if(!p){n.error(`Could not find drawing instructions for "${_}"`);continue}let c=d.substring(1).split(",");!c[0]||!c[1]||(p(i,Je(c,s,a,t,r,!0,o,0,0,!1),h),h.lastCommand=_)}L!==void 0?(i.strokeStyle=i.fillStyle,i.lineWidth=o*L,i.stroke()):i.fill(),i.closePath(),i.restore()}function Vt(i,e,t,r,s,a){i.beginPath();for(let o of e.split(" ")){let n=o[0];if(n==="Z"){i.closePath();continue}let L=o.substring(1).split(",");if(!L[0]||!L[1])continue;let T=t+parseFloat(L[0])*s,h=r+parseFloat(L[1])*a;n==="M"?i.moveTo(T,h):n==="L"&&i.lineTo(T,h)}i.clip()}function $t(i,e,t,r,s,a,o,n,L){let T=new Path2D;T.rect(t,r,s,a),i.clip(T),i.beginPath();let h=o/12;i.lineWidth=n*h;let d={currentX:0,currentY:0,lastControlX:0,lastControlY:0,lastCommand:""};for(let _ of e.d.split(" ")){let p=_[0];if(p==="Z"){i.closePath(),d.lastCommand=p;continue}let c=je[p];if(!c){L.error(`Could not find drawing instructions for "${p}"`);continue}let u=_.substring(1).split(",");!u[0]||!u[1]||(c(i,Je(u,s,a,t,r,!1,n,(e.leftPadding??0)*(h/2),(e.rightPadding??0)*(h/2)),d),d.lastCommand=p)}e.type===1?(i.strokeStyle=i.fillStyle,i.stroke()):i.fill(),i.closePath()}function Ye(i,e,t=0){return Math.max(Math.min(i,e),t)}var je={C:(i,e,t)=>{i.bezierCurveTo(e[0],e[1],e[2],e[3],e[4],e[5]),t.lastControlX=e[2],t.lastControlY=e[3],t.currentX=e[4],t.currentY=e[5]},L:(i,e,t)=>{i.lineTo(e[0],e[1]),t.lastControlX=t.currentX=e[0],t.lastControlY=t.currentY=e[1]},M:(i,e,t)=>{i.moveTo(e[0],e[1]),t.lastControlX=t.currentX=e[0],t.lastControlY=t.currentY=e[1]},Q:(i,e,t)=>{i.quadraticCurveTo(e[0],e[1],e[2],e[3]),t.lastControlX=e[0],t.lastControlY=e[1],t.currentX=e[2],t.currentY=e[3]},T:(i,e,t)=>{let r,s;t.lastCommand==="Q"||t.lastCommand==="T"?(r=2*t.currentX-t.lastControlX,s=2*t.currentY-t.lastControlY):(r=t.currentX,s=t.currentY),i.quadraticCurveTo(r,s,e[0],e[1]),t.lastControlX=r,t.lastControlY=s,t.currentX=e[0],t.currentY=e[1]}};function Je(i,e,t,r,s,a,o,n=0,L=0,T=!0){let h=i.map(d=>parseFloat(d)||parseInt(d));if(h.length<2)throw new Error("Too few arguments for instruction");for(let d=0;d >1&1,d=o&1;h!==0||d!==0?T.setTransform(new DOMMatrix().translateSelf(-h,-d)):T.setTransform(new DOMMatrix),i5.fillStyle=T,i5.fillRect(t,r,s,a)}function Wt(i5,e,t,r,s,a,o,n,L){;i5.save(),i5.beginPath(),i5.rect(t,r,s,a),i5.clip(),i5.beginPath();let T;if(typeof e=="function"){let _=.15/a*s;T=e(.15,_)}else T=e;let h={currentX:0,currentY:0,lastControlX:0,lastControlY:0,lastCommand:""};for(let d of T.split(" ")){let _=d[0];if(_==="Z"){;i5.closePath(),h.lastCommand=_;continue}let p=je[_];if(!p){n.error(`Could not find drawing instructions for "${_}"`);continue}let c=d.substring(1).split(",");!c[0]||!c[1]||(p(i5,Je(c,s,a,t,r,true,o,0,0,false),h),h.lastCommand=_)};L!==void 0?(i5.strokeStyle=i5.fillStyle,i5.lineWidth=o*L,i5.stroke()):i5.fill(),i5.closePath(),i5.restore()}function Vt(i5,e,t,r,s,a){i5.beginPath();for(let o of e.split(" ")){let n=o[0];if(n==="Z"){i5.closePath();continue}let L=o.substring(1).split(",");if(!L[0]||!L[1])continue;let T=t+parseFloat(L[0])*s,h=r+parseFloat(L[1])*a;n==="M"?i5.moveTo(T,h):n==="L"&&i5.lineTo(T,h)}i5.clip()}function $t(i5,e,t,r,s,a,o,n,L){let T=new Path2D;T.rect(t,r,s,a),i5.clip(T),i5.beginPath();let h=o/12;i5.lineWidth=n*h;let d={currentX:0,currentY:0,lastControlX:0,lastControlY:0,lastCommand:""};for(let _ of e.d.split(" ")){let p=_[0];if(p==="Z"){;i5.closePath(),d.lastCommand=p;continue}let c=je[p];if(!c){L.error(`Could not find drawing instructions for "${p}"`);continue}let u=_.substring(1).split(",");!u[0]||!u[1]||(c(i5,Je(u,s,a,t,r,false,n,(e.leftPadding??0)*(h/2),(e.rightPadding??0)*(h/2)),d),d.lastCommand=p)};e.type===1?(i5.strokeStyle=i5.fillStyle,i5.stroke()):i5.fill(),i5.closePath()}function Ye(i5,e,t=0){return Math.max(Math.min(i5,e),t)}var je={C:(i5,e,t)=>{;i5.bezierCurveTo(e[0],e[1],e[2],e[3],e[4],e[5]),t.lastControlX=e[2],t.lastControlY=e[3],t.currentX=e[4],t.currentY=e[5]},L:(i5,e,t)=>{;i5.lineTo(e[0],e[1]),t.lastControlX=t.currentX=e[0],t.lastControlY=t.currentY=e[1]},M:(i5,e,t)=>{;i5.moveTo(e[0],e[1]),t.lastControlX=t.currentX=e[0],t.lastControlY=t.currentY=e[1]},Q:(i5,e,t)=>{;i5.quadraticCurveTo(e[0],e[1],e[2],e[3]),t.lastControlX=e[0],t.lastControlY=e[1],t.currentX=e[2],t.currentY=e[3]},T:(i5,e,t)=>{let r,s;t.lastCommand==="Q"||t.lastCommand==="T"?(r=2*t.currentX-t.lastControlX,s=2*t.currentY-t.lastControlY):(r=t.currentX,s=t.currentY),i5.quadraticCurveTo(r,s,e[0],e[1]),t.lastControlX=r,t.lastControlY=s,t.currentX=e[0],t.currentY=e[1]}};function Je(i5,e,t,r,s,a,o,n=0,L=0,T=true){let h=i5.map(d=>parseFloat(d)||parseInt(d));if(h.length<2)throw new Error("Too few arguments for instruction");for(let d=0;d >1&1,d=o&1;h!==0||d!==0?T.setTransform(new DOMMatrix().translateSelf(-h,-d)):T.setTransform(new DOMMatrix),i5.fillStyle=T,i5.fillRect(t,r,s,a)}function Wt(i5,e,t,r,s,a,o,n,L){;i5.save(),i5.beginPath(),i5.rect(t,r,s,a),i5.clip(),i5.beginPath();let T;if(typeof e=="function"){let _=.15/a*s;T=e(.15,_)}else T=e;let h={currentX:0,currentY:0,lastControlX:0,lastControlY:0,lastCommand:""};for(let d of T.split(" ")){let _=d[0];if(_==="Z"){;i5.closePath(),h.lastCommand=_;continue}let p=je[_];if(!p){n.error(`Could not find drawing instructions for "${_}"`);continue}let c=d.substring(1).split(",");!c[0]||!c[1]||(p(i5,Je(c,s,a,t,r,true,o,0,0,false),h),h.lastCommand=_)};L!==void 0?(i5.strokeStyle=i5.fillStyle,i5.lineWidth=o*L,i5.stroke()):i5.fill(),i5.closePath(),i5.restore()}function Vt(i5,e,t,r,s,a){i5.beginPath();for(let o of e.split(" ")){let n=o[0];if(n==="Z"){i5.closePath();continue}let L=o.substring(1).split(",");if(!L[0]||!L[1])continue;let T=t+parseFloat(L[0])*s,h=r+parseFloat(L[1])*a;n==="M"?i5.moveTo(T,h):n==="L"&&i5.lineTo(T,h)}i5.clip()}function $t(i5,e,t,r,s,a,o,n,L){let T=new Path2D;T.rect(t,r,s,a),i5.clip(T),i5.beginPath();let h=o/12;i5.lineWidth=n*h;let d={currentX:0,currentY:0,lastControlX:0,lastControlY:0,lastCommand:""};for(let _ of e.d.split(" ")){let p=_[0];if(p==="Z"){;i5.closePath(),d.lastCommand=p;continue}let c=je[p];if(!c){L.error(`Could not find drawing instructions for "${p}"`);continue}let u=_.substring(1).split(",");!u[0]||!u[1]||(c(i5,Je(u,s,a,t,r,false,n,(e.leftPadding??0)*(h/2),(e.rightPadding??0)*(h/2)),d),d.lastCommand=p)};e.type===1?(i5.strokeStyle=i5.fillStyle,i5.stroke()):i5.fill(),i5.closePath()}function Ye(i5,e,t=0){return Math.max(Math.min(i5,e),t)}var je={C:(i5,e,t)=>{;i5.bezierCurveTo(e[0],e[1],e[2],e[3],e[4],e[5]),t.lastControlX=e[2],t.lastControlY=e[3],t.currentX=e[4],t.currentY=e[5]},L:(i5,e,t)=>{;i5.lineTo(e[0],e[1]),t.lastControlX=t.currentX=e[0],t.lastControlY=t.currentY=e[1]},M:(i5,e,t)=>{;i5.moveTo(e[0],e[1]),t.lastControlX=t.currentX=e[0],t.lastControlY=t.currentY=e[1]},Q:(i5,e,t)=>{;i5.quadraticCurveTo(e[0],e[1],e[2],e[3]),t.lastControlX=e[0],t.lastControlY=e[1],t.currentX=e[2],t.currentY=e[3]},T:(i5,e,t)=>{let r,s;t.lastCommand==="Q"||t.lastCommand==="T"?(r=2*t.currentX-t.lastControlX,s=2*t.currentY-t.lastControlY):(r=t.currentX,s=t.currentY),i5.quadraticCurveTo(r,s,e[0],e[1]),t.lastControlX=r,t.lastControlY=s,t.currentX=e[0],t.currentY=e[1]}};function Je(i5,e,t,r,s,a,o,n=0,L=0,T=true){let h=i5.map(d=>parseFloat(d)||parseInt(d));if(h.length<2)throw new Error("Too few arguments for instruction");for(let d=0;d